Monday, September 26, 2011

Segmented Button Control in Android

For an Android project I did a little while back I needed a pair of so-called segmented buttons, as they are named in iOS (see for example the official iOS API for it).

The idea is that when you select one of the buttons, it usually shows just about the same related data but in a different way (at least that's my translation of what it should do).
The Android version of it which I provide below looks currently most like the third and fourth example in the above image. But of course you can change the code to style it like the others too.

Android does not have this control out of the box. Luckily, I found a reference to the source of the Foursquared app.
In there, a segmented button control is created! Big thanks for the creators of Foursquared for sharing the code and thus of the segmented button control :)

Since the Foursquared app is quite big, I extracted the segmented buttons code from it and made it an Eclipse project, for your and my convenience. It's now just one Activity and the widget.
You can get it from here and use it anyway you like, following the Foursquared source Apache License 2.0 rules of course.
Here's two screenshots of what it will show:


Above shows at startup by default the Recent button is selected


Above shows after clicking on the Nearby button

Please not that I kept it as basic as possible; for example you'll keep seeing the progress indicator from layout/empty_loading.xml, which of course should be replaced with actual data. I also did not go through all the drawables etc to see which ones are needed and which are not, so you'll still have to clean those up.
Code has been tested on 2.1-update1.

Saturday, August 27, 2011

Using dependency injection and annotations in Android for cleaner code

Interesting overview of possibilities for cleaner code using dependency injection and annotations by using the RoboGuice and/or


Android Annotations


frameworks:

Clean code in Android applications

Definitely interesting possibilities, but these frameworks are quite young. And is it really worth the effort, since Activities and Intents can also be used quite effectively to keep the app loosely-coupled? Also the libraries will increase the app's size.

Not convinced yet :)

Friday, August 19, 2011

Houston we got them: Android Mini Collectible Special Edition Summer 2011



1st: "Greentooth is a master of mind control technology."
"One shake of his hand and you are under his command."

2nd: "The Hidden Task waits in the shadows, always prepared to set things right."
"The Hidden Task makes no sounds and leaves no traces."

3rd: "El Poderoso fights for justice, equality.. and fancy belts."
"El Poderoso is ready to wrestle the worst villans and pin down the biggest kingpings."

4th: "Cycle-On is a power-hungry Android who will stop at nothing."
"Cycle-On's tough metal casing makes it a formidable opponent."

Thursday, August 18, 2011

Android project lessons learned

Quick post with some bullet points of lessons learned during my last Android project:

  • To be able to keep strings in strings.xml apart, prefix them with the name of the Activity they belong to. So listOrdersOrderNr instead of just orderNr.

  • If you inflate() a view without the parent, it won't take into account the parent's attributes like padding! Sadly apparently that trick does not seem to work in the getView() method. For that you'd have to compute the padding in dp and then set the padding of the view. E.g:

    
    
    float scale = getContext().getResources().getDisplayMetrics().density;
    float dpSize = 10 * scale; // So 10 would be the number of dp you want
    buttonRow.setPadding((int) dpSize, (int) dpSize, (int) dpSize, (int) dpSize);



  • When using a RelativeLayout, make sure you refer to other elements using "@id/someId", and not "@+id/someId". If you use the latter, Android will just create a new id named "someId" and will place the layout starting from the top of your Activity. Pretty obvious this one (the docs say the "+" creates a new id) but still an easy one to miss... So don't do:

    
    
    android:layout_below="@+id/feedbackEmailLogsSeparator"


    But use:

    
    
    android:layout_below="@id/feedbackEmailLogsSeparator"



Monday, July 4, 2011

Android pro tips for advanced expert Android developers

Short summary of the Google I/O 2011 session "Android Protips: Advanced Topics for Expert Android App Developers". Full presentation can be found here.

6:35: the parallel Activity pattern: based on SDK version, start another "parallel" code line. Easy check to see what your SDK version is so you can distinguish use of more advanced APIs as for example available since HoneyComb.
8:20: use interfaces for backward compatibility
9:09: get real user feedback before launching. Note you can also of course use Analytics to log an exception!
10:38: example of doing A/B testing
11:35: use Android Market for beta testing. It seems there's no special facility for this in the market, you just have to control it yourself... e.g password protect, obscure the name.
12:31: But don't forget to protect your package name before someone else takes it! So upload and save it but don't publish it.
13:43: don't assume the default (natural) orientation on a device is portrait, e.g most tablets start in landscape. The orientation sensor's default is also 90degrees "off"
15:00: Don't track devices to detect unique installations. Give your app a unique id using a UUID, store it in the prefs.
20:00: the app's data has to be always fresh if possible, the user does not have to wait. See also this recent post: http://android-developers.blogspot.com/2011/06/deep-dive-into-location.html
25:51: how to get fresher data
32:00: make your services intelligent like do everything async, let it die asap
33:13: make your app psychic: leverage what the phone already knows, remembers what the user has told it. Like the Account Manager. Preferences should follow the user across devices and resets (which means storing them offline). Give users the ability to delete their preferences, using the Backup Manager to backup shared preferences.
38:21: intercept links using an Intent Receiver
39:25: make your apps adaptive. E.g: behave as expected, optimize for different user experiences. You can specify the IME keyboard behavior by setting the appropriate action (Go, Search, Send, Done etc). Use android:imeOptions for that
43:20: handle volume and playback buttons icw requesting audio focus for playback
47:10: provide translations for your app and title description to have it better found in people's native language
48:00: make your app smooth: make everything async. Loader and CursorLoader are available since HoneyComb.

Wednesday, June 29, 2011

Recommended Android Market screenshot format

In the Android Developer Console you can upload the screenshots of your app (max 8 currently) in several sizes, like 320x480, 480x800, 480x854 and 1280x800.

But it turns out that sometimes (always?) if you upload in the higher resolutions (like 480x800), the images get cropped in a weird way in the market on your phone (at least the Nexus One). The images will look fine in the online Android market, but not on a physical device. Actually, when you look closely, the dev-console gives a thumbnail of the screenshots and there you can already see that the images are cropped strangely sometimes; like the left 20-50 pixels just cut off. And that's actually how they also appear in the market on devices!
After some experimenting, it turns out this does not happen when you upload in 320x480 format.
Thus the recommended resolution to upload screenshots seems to be 320x480! Doesn't matter if you use a device (like the Galaxy S2) or emulator to take the screenshots.

Note I didnt check if the browser market image quality decreased when going from 480x800 to 320x480. Though clicking on an image in the webmarket shows the 320x480 uploaded image quite fine.

Android Bluetooth bugs on Galaxy S2

Just a quick post regarding Bluetooth on the Samsung Galaxy S II.

Recently I've been creating an app that uses Bluetooth for sending data between devices (not FTP just strings). A pretty good introduction can be found here, though some things are missing from it. Like an example how you can register a BroadcastReceiver for the ACTION_SCAN_MODE_CHANGE. Just in case you are looking for it, here it is:



// Register the BroadcastReceiver
IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy

...

// Creates a BroadcastReceiver for ACTION_SCAN_MODE_CHANGED for BT discovery
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {

public void onReceive(Context context, Intent intent) {
if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(action)) {
int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE,
BluetoothAdapter.ERROR);
String strMode = "";
switch(mode){
case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
strMode = "mode changed: SCAN_MODE_CONNECTABLE_DISCOVERABLE";
break;
case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
break;
case BluetoothAdapter.SCAN_MODE_NONE:
strMode = "mode changed: SCAN_MODE_NONE";
break;
default:
strMode = "mode changed: unknown mode " + mode;
break;
}
Log.d(TAG, strMode);

}


Basically a device can be made discoverable by others, or a device can be set to find other devices (discover). Being discoverable is configurable up to 300 seconds max, finding other devices is not configurable and about 12 seconds.

Making a device discoverable entails the following code:


Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120);
startActivityForResult(discoverableIntent, REQUEST_MAKE_BT_DISCOVERABLE);


That should enable Bluetooth if it isn't already and make the device discoverable for 120 seconds, and the result of the intent comes in at onActivityResult(). For receiving Bluetooth state changes you can register a BroadcastReceiver. This all works fine on a Nexus One.
But not on all devices, their Bluetooth stack contains bugs and behaves differently:

Samsung Galaxy S II
  • When you make the device discoverable, its state should become SCAN_MODE_CONNECTABLE_DISCOVERABLE, i.e value 23, meaning this device is both discoverable and connectable from remote Bluetooth devices.
    On the Nexus One I see the device.getScanMode() always return 23, as it should be. But on the Galaxy S2 it sometimes just changes to scanmode SCAN_MODE_CONNECTABLE (21), making it not discoverable anymore! And indeed, other devices don't see it anymore when discovering. In the S2 phone's settings I also see in that case that the discoverable checkbox is not checked.
    Workaround: the only workaround that always works seems to be to always first turn on the Bluetooth, and after that send the above discoverableIntent. Thus first do a


    Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableIntent, REQUEST_ENABLE_BT);


    then the


    Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
    discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120);
    startActivityForResult(discoverableIntent, REQUEST_MAKE_BT_DISCOVERABLE);



  • When you start the discoverable intent with any number different from 120, say 300, the popup that asks for user confirmation always shows 120, instead of the different number, say 300. The discoverability actually stays on for the specified amount (300 seconds), so functionally it works (phew), but the display is just incorrect and you can't change it. Confusing for the user.



Other bugs
I also found a few other more generic bugs reported by others and found by myself. Maybe they were SDK or device related (my app runs on Android 2.0+, tested on N1, Wildfire and Galaxy S2):

Sunday, April 17, 2011

Horizontally sliding grid of images in Android

For my Android app I wanted a swipeable and horizontally slideable 3x3 grid of images. If there were more than 9 images, a swipe should show the next 3x3 grid. Another requirement was that all should be in a ScrollView because there would be more on the screen above the 3x3 grid.
The first solutions that directly came to my mind were of course the GalleryView or a GridView or a horizontal ListView.
But the final solution was a ViewFlipper with TableLayouts as its children!
Below you can find my "journey" figuring out, experimenting and getting to the solution.

List View

Luckily for the horizontal ListView there was already a good example here. The result:


It really does work as a horizontal ListView, but it doesn't support the paging I was looking for; it is just a continuous list.
Sadly the code wasn't downloadable in one block (update: seems I just missed it at the top!), and it also contained typos (the layout tags were all lowercase), so it took me a while to get it all fixed and useable in an Eclipse project.
For people looking for the complete source code, you can find it here.
Note that the getView() has not been optimized yet, it creates a new view each time!
Also weird in the original code was the TextView within the ImageView, I changed the code to have them below each eachother.
An advantage of this implementation is that there is no "center locking" of the image anymore.

Gallery View

So the horizontal ListView didn't cut it, let's try a GalleryView (g1 in the code) with per item a standard GridView. That worked reasonably, except that the swiping didn't work anymore! Keypad scrolling still worked, kind of:


Luckily I found a fix for that, implementing a custom GridView that passes the fling on to the parent gallery (g2 in the code).



But then I still had to put it in a ScrollView... And then the trouble started, look at the vertical scrollbars that suddenly appear:


Bummer!

I ended up with quite some trial code which you can find here. Disclaimer: this is quite messy trial code so you are warned and I'm not supporting this code in any way; you are on your own here :)

Grid View

Making a GridView scroll horizontally in sets of 3x3 images just gave me headaches from the beginning. For example you can't set the number of rows, so it might sometimes show more rows than 3. And a GridView inside a ScrollView is also strongly warned against. Which btw also applies to ListView within a ScrollView. In general it even seems: a scrolling View inside another scrolling View is not a good idea and even strongly discouraged!
Furthermore, on a xlarge screen (WXGA) the gridview had almost its own will for laying out, making it look very different than on a HVGA screen.

The solution

After some more thinking I came across the solution: put Table Layouts of 3x3 images inside a ViewFlipper! Per table you can control how many images should be shown and it's quite easy to add swiping! Paging is then done per table. You can swipe left and right.
Page 1:


Page 2 after swiping to the left:


The source code of the final solution you can find here.
Note the top left icon in the grid changes when you swipe to the left the first time (so you can tell that the 2nd page is showing).
Note also the use of the FixedViewFlipper to handle rotation correctly.
A future improvement: keep track of the "page" you're on so with the example of 2 tables, don't allow the swipe/fling to "go round", i.e swiping to the left on the 2nd table shows the first table again...

PS: of course you could also create your custom component, but I want to avoid that as much as possible to minimize (custom) maintenance.

Misc

These links I also stumbled upon but didn't do anything with:

Hope this helps somebody some time :)

Monday, February 14, 2011

Upgrade to iTunes 10.1.2.17 iOS 4.2.1 on Windows XP 64bit

Over a year ago I managed to get iTunes 9.x.y working on my Windows XP 64 bit machine.
I had to try so many stuff, that I didn't really keep track of what I did :( The only things I wrote down were:


But now (february 2011) I wanted to upgrade my iTunes 9 to iTunes 10 because my OS on my iPod Touch was still at 3.x, and more and more apps are not running on 3.x anymore, they require 4.x and higher.
I knew it was going to be a nightmare, remembering the iTunes 9 installation. But this time I decided to keep track of it as detailed as possible. Hopefully it helps somebody...
I'll write down the steps as how I did them, so read through them all before trying yourself, you might be able to skip certain steps because they didn't work... WARNING: it is definitely not a clean walk-through!

  1. Goal is to follow the steps from http://skunkwerks.wordpress.com/2008/04/13/getting-itunes-to-work-with-windows-xp-x64

  2. Downloaded the 64b Vista version iTunes64Setup.exe from the Apple site

  3. First thought I could just try to run the .exe, since it's just an upgrade. But after some thought, thought I needed to modify the .msi file(s), since they do a check at startup. And, since all I need is an updated iTunes I don't think I even need to run all .msi files, only the modified iTunes64.msi (with the edits from the first post, NOT the deletion of entries as in this post).

  4. Installer starts successful, see screenshot (sorry for the Dutch):


  5. Let it install where the 9.x.y was already installed: C:\Program Files (x86)\iTunes

  6. A popup showed saying itunes+quicktime needs sp2 or higher. See screenshot:



    Just hit OK, install continues afterwards and it said successfully installed.

  7. And iTunes starts fine! Note that I still manually have to start the Mobile Apple service. I do that before starting iTunes, then plugin the iPod (with popup see screenshot), Then start iTunes.



    I also still do always get the popup about not being able to burn CDs. No problem.

  8. But now i get popup saying "itunes requires a newer version of Apple Mobile Support. Please uninstall both Apple Mobile Device Support and iTunes, then install iTunes again.". See screenshot:



    So it seems I do need to run AppleMobileDeviceSupport64.msi.
    But first edit it also. And instead of deleting entries, also here I just change 600 to 501.
    Starting AppleMobileDeviceSupport64.msi gives again needs SP2 or higher.
    So I also changed (VersionNT=501 And ServicePackLevel>=2) Or VersionNT>=600
    to (VersionNT=501 And ServicePackLevel>=2) Or VersionNT>=501.
    Yup .msi starts now, installs update in C:\Program Files (x86)\Common Files\Apple\Mobile Device Support
    Got popup saying it didn't pass windows logo testing. Continue anyway.
    Again for USB driver.
    And again. ipod goes on/off. Shows popup again that tries to open 2 programs when inserting ipod.
    Successful!! Note that it didn't require a restart and that I did not re-install iTunes.
    Started iTunes
    Yeah: ipod lights up, sync in progress

  9. It now asks for 4.2 upgrade of iPod software! yeah! See screenshot:


  10. Hit Download and update, then it says automatically checking for updates is disabled. If you hit cancel, nothing happens. So you have to select "Check this time":


    Then iTunes says my iPod isn't synced, are you sure to contiue? Canceled and did that sync first, making sure all my apps etc are in iTunes too.

    Selected Upgrade again, yup started downloading 4.2.1, see screenshot:


  11. Crap, got error 1603/1604, while popup Find new hardware wizard was suddenly showing up!

    Apple support link with some more info: http://support.apple.com/kb/TS1275
    Crap, can't find a driver (usbaapl). Seems it didn't get installed?
    So did this order of .msi running:
    1) AppleApplicationSupport.msi
    2) AppleMobileDeviceSupport64.msi
    3) iTunes64.msi
    Selected "recover" each time.
    Still didn't work, got 1604 error or something.
    Note that iTunes does see that the phone needs to be recovered, but it just fails after a long time with a 1603 or 1604 error. That indicates something is wrong with the USB connection.

  12. Then I manually got the driver from AppleMobileDeviceSupport64.msi as described here (using lessmsi*.exe): http://discussions.apple.com/thread.jspa?messageID=5619588
    Yeah but which one do I need, the 64b or 32b? Guess the 64b, that's named usbaapl64.
    Note that you need to do Have disk as in http://support.apple.com/kb/TS1538
    Ah now got 1604 error (device was sometimes recognised, then windows says new device found again etc)
    Did what is says here for error 1604 (turn if off etc):
    Showing Preparing iPod for restore... for a long time... minutes... Again 1604 error.
    So the USB driver is just not set up correctly.
    You hear when you try the restore (if the device is recognised in iTunes), the USB ping "rings" twice, meaning something is happening twice or something.
    Don't think that should happen. I also see the ipod flash shortly, then it's just dark.

  13. I did read somewhere that in this case you're kindof lost, so you need to restore the iPod OS via another PC/Mac for which everything is working. After that's on the iPod, you can restore from the 64b iTunes...
    Luckily I have such a PC around: XP 32bit. Downloaded the latest iTunes 10, installed, and plugged in the iPod. iTunes detected it needed restoring so it put 4.2.1 on it! Note that I didn't give my device a name at the end of the restoring (thinking that should come from the XP 64b iTunes anyway...).

  14. So after this, plugged it back in on the XP 64b. But it was not seen/recognised by iTunes! Though in the Windows Device Manager it said Apple iPod!

  15. Then I did update the driver as in here in the part Troubleshooting iTunes <-> iPhone Link/Sync
    After that, the Device Manager says Apple Mobile Device USB driver! See screenshot:


  16. And (since) iTunes was already started, it started immediately restoring apps! Yeah!! See screenshot progress:


    Darn! An error occurred restoring this ipod (-402620395):


  17. Before hitting OK, I removed any provisioning files I had on the ipod as said here in the post "bkynaston post: Posted: Jun 23, 2010 6:45 PM"

    Hit Ok. After this it tries to sync again.

    Yes! It says: iPod sync is complete. OK to disconnect.

  18. And yes, finally, finally, after about 1 day of working on this, my iPod is ok again! Even the language is back to English!
    Finally! Hope I never have to do this again...



Misc
Here's some other knowledgebase tip(s) that I looked at and tried

Saturday, February 12, 2011

Lessons learned webservices project

At a recent project we used the following tools & frameworks:

  • Java 6

  • Spring 3.0

  • Hibernate 3.2.7

  • JMS

  • JBoss ESB 4.5/5.1

  • SOAP 1.1

  • JAXB

  • JAX-WS

  • Apache CXF

  • Maven2

  • Jetty 6.1

  • Oracle 10g

  • Eclipse Mylyn

  • SVN

  • JUnit

  • Bamboo

  • Jira

  • Crucible

  • Nexus

Below are a couple of lessons learned which I remembered to write down:

JUnit
  • To skip a testclass (e.g Util.java) during a test in a maven project: put @Ignore above it.



Java
  • Throw checked exceptions in case of functionality errors. To not have to return those errors as return-values of the methods and making them checked, makes the interface clearer.



SOAP
  • Should you use the service response for functional errors, and SOAP fault only for technical errors?
    Nothing in the SOAP 1.1 spec says you should do that or are not allowed to put functional errors in the <detail>.
    Makes sense to me to do that: otherwise you can get errors in the response AND in the soap fault. More complex to handle in the client...
    Less of an issue if you say when there are SOAP faults we just give up, only if there's a response we try to see what's going on.

  • Apparent best practice: no underscores in xml/xsd/wsdl for element nor attributes, for better readability



ESB
  • JBoss ESB 4.5 JMS queue had loads of problems with high load and would sometimes just completely drop the queue. We moved to JBoss ESB 5.1 with ActiveMQ, that proved much more stable.



Transactional atomic file manipulation and file locking
Bunch of interesting articles related to this:

Related to file locking:


Maven
  • With Maven you can also have it put the sources + javadocs in the repository.
    Running 'mvn eclipse:eclipse' after that, and refresh the project in Eclipse, within Eclipse you can then see the javadocs and debug the sources.
    Just make sure you set <downloadSources> and <downloadJavadocs> to true.


Misc
  • If you have an action that can't be made transactional (e.g a file move), do that as last thing in your steps. More precisely: start a (e.g database) transaction, move the file. If that move succeeds, update status that move was successful. If that move fails, rollback the transaction, and thus is the status of that file still in "to move".

Number of downloaded apps/installs of an android app

Online android market website gives more details on the number of downloads/installs of an app than the market on a device (phone)!
Check for example 3D Light Racer Basic. On my Nexus One it says it has > 250000 downloads. But on the website it says 500000 - 1000000 installs! See the red arrow.


Can be pretty useful for competitor analysis for example :)

Friday, November 12, 2010

My experience with Android Market Licensing and LVL

This post describes my lessons learned with the Android Market Licensing service and the accompanying License Verification Library (LVL). I used it the first time for a completely new application, never published on the market before.


Getting started
From the description and instructions page you can read:
"Android Market offers a licensing service that lets you enforce licensing policies for paid applications that you publish through Android Market. With Android Market Licensing, your applications can query Android Market at run time to obtain their licensing status for the current user, then allow or disallow further use as appropriate".
"The License Verification Library (LVL) handles all of the licensing-related communication with the Android Market client and the licensing service. With the LVL integrated, your application can determine its licensing status for the current user by simply calling a library checker method and implementing a callback that receives the status".


That page is quite elaborate and definitely a good starting point for integrating the LVL into your project. Note that your app needs to support SDK 1.5 or higher (API level 3 and onwards).

The LVL I did not set up as a Library Project in Eclipse because it messed up my Subversion. It got totally confused, indicating stuff wasn't checked in, but I also could not check anything in even after a cleanup. Or it kept indicating there were waiting changes to check in, even though I had it checked in. Maybe it's Eclipse Ganymede, maybe it's me :). So I just imported the source code in my project. Disadvantage is I'll have to update each project that uses the LVL when an update of the LVL arrives.

Furthermore I used the ServerManagedPolicy and changed the salt for the AESObfuscator (which is used to securely obfuscate data written to and read from storage).
Note that the AESObfuscator has been reported to be quite slow.

Setting up a test response for license checks works fine for brand spanking new apps like mine was. On my emulator I used my developer account, on the real device (a Nexus One) another gmail account as test-account.

Quite essential in the documentation is the line "Signing in using a publisher account offers the advantage of letting your applications receive static test responses even before the applications are uploaded to the publisher site".
It took me (and apparently others) quite a while to figure out that that means that the test accounts only get the Test response when you've uploaded the app to the market and saved it! Otherwise they will get ERROR_NOT_MARKET_MANAGED.


To obfuscate or not to obfuscate
I decided not to obfuscate my application. Main reasons being:

  • It won't prevent hackers from decompiling your code for 100% sure.

  • It gets a real pain in the @ss to figure out the real line of the code when you get an error report or exception.

  • You should do a full regression test after obfuscation. Of course :) you have that all automated but still...

Note also that the post explaining ProGuard obfuscation requires a fix (it obfuscates too much). It might be updated now.
Here's a tip when you're using Proguard obfuscation to easily indicate which classes shouldn't be obfuscated (in short: by letting those implement a specificly named interface and put that as a keep-rule in the proguard config).

Great tip here to even more minimize the number of classes/interfaces you shouldn't obfuscate.

For another nice introduction and more in-depth details on why you could use Proguard for obfuscation, shrinking and optimization, see this post.


Server Response Extras
During testing on the emulator I found out that the Server Response Extras are never set for the test responses, so it's quite hard to test different behaviour here on different values. On the other hand, the default implementation seems to handle it quite ok. If you don't like it, you'll have to dive into the LVL code...


Testing new versions of an app
I also found out that if you increase the versionCode of your app, and try to test that app on a real phone with a test account, you'll get back ERROR_NOT_MARKET_MANAGED. Most likely this is because of the new versionCode, which the market licensing service does not recognize/accept. I have not seen the ability to just upload a new version of an app and only save it, it seems you can only publish it... And this thus means your test accounts can't test the app before release...

Looking at a reply in this post it seems you don't even want to save a newer version because the current version of your app disappears from the market! Another report of this here.

As mentioned in the first post, the only way to test a newer version seems to set the versionCode to the currently published version and use that during testing on a real device with test accounts. Sounds doable, there's not too much risk of forgetting to increase the versionCode when publishing: the market won't let you upload an app with the same versionCode as already published.

Note: Maybe the new "draft upload" feature fixes the above issues (see sixth item in this post), didn't try that out yet.

Some answers from Trevor Johns in the post clarify things re: responses a bit but not all:

"If an app is not published AND not draft, then you'll get
ERROR_NOT_MARKET_MANAGED."

Yes, but if your testing with your developer account on the emulator, you get back whatever is set in the test response. Even for an app with a newer versionCode.

"If an app is in draft (never published), then we send LICENSED for all requests for that app."
Is it? It seems it sends back to test accounts on a real device what's set in the Test response. Not tested yet for increased versionCode.
The reply on 6aug10 from Trevor Johns in this thread says it too. Unless I did something wrong, I noticed different behaviour.

"If an app is published (or has been published then unpublished), then
the response is driven by the dev console settings for the developer/testers, and purely by purchase history for everyone else."

Yes, but if you have increased the versionCode, test accounts on a real phone get back ERROR_NOT_MARKET_MANAGED.

See also other responses in this post.

I also wanted to test what happens when I publish a new version, would people with an older purchased version of the app still get verified successfully, and what does the provided LVL code do when it gets back LICENSED_OLD_KEY?
My conclusion: that combination seems only testable on the emulator for the developer account before publishing the app (note I didn't try the scenario on a real device with the developer account). After really publishing the app you can test it of course, but that's a bit late, you want to try it before publishing, before the world can download it.
For an app never published before, you can test the LICENSED_OLD_KEY as described here.


More securing your implementation
Of course I followed also most steps described in Securing Android LVL Applications. I did not add a CRC check. Tips on how to do that can also be found here and here.


Wishes
What would be great to have available in the developer console is to see how many verification requests failed, and with what error. This way you can get an indication on how many people try to illegally use your app, and get an indication whether something might be competely wrong with your app (e.g nobody gets correctly verified anymore).
You could then also see how many people haven't upgraded yet via distinct the OLD_LICENSE_KEY responses.
As long as this kind of statistics is not provided by the market, but you still want to know about these kind of failures, what you could do is report any error results to your own separate server (though of course a skilled hacker could prevent that from being sent).

Finally: don't forget to add any issues you find to the market licensing issues page.

Saturday, October 16, 2010

Saving precious Android Market description text space

Pretty annoying that you only have 325 characters in the description field when submitting an app right? You really have to use the space wisely.

One of the things you usually want to put in what you have added/changed in the current release, so people who want to update know what to expect.
That takes up precious space of those 325 characters. But from now on you don't need to worry about that anymore because... it turns out you can put any text in the android:versionName attribute in the AndroidManifest.xml!

So what I usually do is put in versionName the digits from the android:versionCode attribute separated by dots, then a space and then a short description of what has changed since the last version. For example, suppose android:versionCode="103" then androidName becomes:

android:versionName="1.0.3 Preferences are now correctly stored"

So what does it look like? Well here's first a screenshot of an app that only puts in a version number in the android:versionName attribute:


And here's an example of an app that fully uses that attribute:


I do not know what the maximum lenght is of the versionName field, but apparently you can put quite some text in there, as can be seen from above screenshot.

Update: well, putting the version information in the versionName is not necessary anymore, since from now on there's a separate field to put the recent changes into. What now quickly comes to mind to put into the versionName could be the minimal SDK version number your app supports. That can be useful for people who want/need to get your app on non-Google markets. This way they get at least an indication whether the app can run on their phone or not.

Update: and recently (definitely since 23 Feb 2011) they have limited the length of the versionName field to 30 characters, because if you exceed that, at apk-upload time you'll see: "The versionName in AndroidManifest.xml exceeds maximum (30)."

Sunday, October 3, 2010

JBoss Application Server 5.1/ESB 4.5 GA and ActiveMQ 5.4 integration

On one of my last projects I had to integrate JBoss ESB 4.5 GA with ActiveMQ 5.4.0 and Spring 3. NOTE: the fixes below also work for integration with JBoss 5.1 Application Server.



My starting point was of course this post: Integrating Apache ActiveMQ with JBoss. (Or is this the original?)
Part of the requirements mentioned in that article (Apache ActiveMQ 4.0.1+ and JBoss 4.0.4+) got me worried that the steps described might not work for my version of JBoss and ActiveMQ. Even though there's a '+' after the version numbers :)

First I had quite a hard time finding the .rar file! Finally found it in \lib\optional in the ActiveMQ zip: activemq-rar-5.4.0.rar

After that I followed the steps including up to 'Configuring JBoss'. In there you also have to start JBoss again.
I didn't see any exceptions fly around so thought all was fine, so I started the consumer ('ant consumer').
But there I got:



[java] javax.jms.JMSException: Could not connect to broker URL:
tcp://localhost:61616. Reason: java.net.ConnectException:
Connection re[Thread-2] Caught: javax.jms.JMSException: Could not
connect to broker URL: tcp://localhost:61616. Reason:
java.net Connection Exception : connection refused


Strrrange, because I thought the server started fine. After quite some searching, I figured out the error message probably indicates that there's just nothing listening at localhost:61616.
After carefully checking the startup log (searching for 'activemq') I found out it hadn't started!



17:17:31,566 INFO [XBeanXmlBeanDefinitionReader] Loading XML bean
definitions from class path resource [broker-config.xml]
17:17:31,768 WARN [ActiveMQResourceAdapter] Could not start up embeded
ActiveMQ Broker 'xbean:broker-config.xml': Line 29 in XML document from
class path resource [broker-config.xml] is invalid; nested exception is
org.xml.sax.SAXParseException: cvc-elt.1: Cannot find the declaration
of element 'beans'.


That's weird, some namespace problem?
Searching on the biggg internet I found this page (via), explaining that the namespaces are different from 5.1 onwards.

Since I'm using AMQ 5.4.0 I tried in broker-config.xml:



<beans xmlns="http://activemq.apache.org/schema/core">


But that still gave:



17:23:38,638 WARN [ActiveMQResourceAdapter] Could not start up embeded
ActiveMQ Broker 'xbean:broker-config.xml': Line 29 in XML document from
class path resource [broker-config.xml] is invalid; nested exception is
org.xml.sax.SAXParseException: cvc-elt.1: Cannot find the declaration
of element 'beans'.


So now I just tried all the namespace definitions:



<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:amq="http://activemq.apache.org/schema/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://activemq.apache.org/schema/core
http://activemq.apache.org/schema/core/activemq-core-5.2.0.xsd
">
</beans>


Darn, still not ok:



17:27:42,265 WARN [ActiveMQResourceAdapter] Could not start up embeded
ActiveMQ Broker 'xbean:broker-config.xml': Line 49 in XML document from
class path resource [broker-config.xml] is invalid; nested exception is
org.xml.sax.SAXParseException: cvc-complex-type.2.4.c: The matching wildcard
is strict, but no declaration can be found for element 'persistenceAdapter'.


But that was easy, missing the namespace prefix. So I modified:



<amq:persistenceAdapter>
<amq:journaledJDBC journalLogFiles="5" dataDirectory="activemq-data"/>
<!-- To use a different datasource, use th following syntax : -->
<!--
<journaledJDBC journalLogFiles="5" dataDirectory="../data" dataSource="#postgres-ds"/>
-->
</amq:persistenceAdapter>


But again an error:



17:29:50,840 WARN [ActiveMQResourceAdapter] Could not start up embeded
ActiveMQ Broker 'xbean:broker-config.xml': Line 50 in XML document from
class path resource [broker-config.xml] is invalid; nested exception is
org.xml.sax.SAXParseException: cvc-complex-type.2.4.a: Invalid content was
found starting with element 'amq:journaledJDBC'. One of
'{"http://activemq.apache.org/schema/core":amqPersistenceAdapter,
"http://activemq.apache.org/schema/core":jdbcPersistenceAdapter,
"http://activemq.apache.org/schema/core":journalPersistenceAdapter,
"http://activemq.apache.org/schema/core":kahaDB,
"http://activemq.apache.org/schema/core":kahaPersistenceAdapter,
"http://activemq.apache.org/schema/core":memoryPersistenceAdapter,
WC[##other:"http://activemq.apache.org/schema/core"]}' is expected.


So journaledJDBC is unknown. There must be something with a wrong namespace version or something. From the XSDs from here it seemed there was an element in the wrong place. So I just removed the whole persistenceAdapter element.

I also noted that there is a persistenceFactory element in the AMQ 450 broker-config.xml, which is not present in the Integrating Apache ActiveMQ with JBoss post.
So I also changed that one's dataDirectory to be the same as in the (just removed) persistenceAdapter:



<amq:persistenceFactory>
<amq:journalPersistenceAdapterFactory journalLogFiles="5" dataDirectory="${jboss.server.data.dir}/activemq"/>
</amq:persistenceFactory>


Yes that did it! See the log part below:



17:45:01,215 INFO [XBeanXmlBeanDefinitionReader] Loading XML bean definitions
from classpath resource [broker-config.xml]
17:45:01,542 INFO [DefaultListableBeanFactory] Pre-instantiating singletons
in org.springframework.beans.factory.support.DefaultListableBeanFactory@157402b:
defining beans [org.apache.activemq.xbean.XBeanBrokerService#0]; root of
factory hierarchy
17:45:01,697 INFO [PListStore] PListStore:activemq-data\bruce.broker1\tmp_storage
started

... stuff deleted ...

17:45:02,896 INFO [BrokerService] ActiveMQ 5.4.0 JMS Message Broker
(bruce.broker1) is starting
17:45:02,911 INFO [BrokerService] For help or more information please
see: http://activemq.apache.org/
17:45:03,083 INFO [SchedulerBroker] Scheduler using directory:
activemq-data\scheduler
17:45:03,145 INFO [JournalPersistenceAdapter] Journal Recovery Started from:
Active Journal: using 5 x 20.0 Megs at: C:\jbossesb-server-4.5.GA\bin\${jboss.server.data.dir}\activemq\journal
17:45:03,176 INFO [JournalPersistenceAdapter] Journal Recovered: 0 message(s)
in transactions recovered.
17:45:03,207 INFO [TransportServerThreadSupport] Listening for connections at:
tcp://localhost:61616
17:45:03,223 INFO [TransportConnector] Connector bruce.broker1 Started
17:45:03,223 INFO [BrokerService] ActiveMQ JMS Message Broker (bruce.broker1,
ID:PC555-4930-1389432149-0:0) started


Also after that the consumer and producer worked fine.

And as a final optimization to not having to change the namespace for each new ActiveMQ version, I removed the version from the activemq-core xsd:



<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:amq="http://activemq.apache.org/schema/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://activemq.apache.org/schema/core
http://activemq.apache.org/schema/core/activemq-core.xsd
">


To summarize, this is the final broker-config.xml (some comments deleted for space):



<?xml version="1.0" encoding="UTF-8"?>
<!-- START SNIPPET: xbean -->
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:amq="http://activemq.apache.org/schema/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://activemq.apache.org/schema/core
http://activemq.apache.org/schema/core/activemq-core.xsd
">

<!-- shutdown hook is disabled as RAR classloader may be gone at shutdown -->
<amq:broker useJmx="true" useShutdownHook="false" brokerName="bruce.broker1">

<amq:managementContext>
<!-- use appserver provided context instead of creating one,
for jboss use: -Djboss.platform.mbeanserver -->
<amq:managementContext createConnector="false"/>
</amq:managementContext>

<amq:persistenceFactory>
<amq:journalPersistenceAdapterFactory journalLogFiles="5" dataDirectory="${jboss.server.data.dir}/activemq"/>
</amq:persistenceFactory>

<amq:transportConnectors>
<amq:transportConnector name="bruce.broker1" uri="tcp://localhost:61616" discoveryUri="multicast://default"/>
</amq:transportConnectors>

</amq:broker>
</beans>


JBoss 5.1 AS specific stuff:

I just copied over the above created .rar dir to the jboss-5.1.0.GA/server/default/deploy/activemq-ra.rar/META-INF directory and started it.

Got one error trying:



18:00:48,611 ERROR [AbstractKernelController] Error installing to Parse:
name=vfsfile:/C:/jboss-5.1.0.GA/server/default/deploy/activemq-ra.rar/
state=Not Installed mode=Manual requiredState=Parse
org.jboss.deployers.spi.DeploymentException: Error creating managed object
for vfsfile:/C:/jboss-5.1.0.GA/server/default/deploy/activemq-ra.rar/
at org.jboss.deployers.spi.DeploymentException.rethrowAsDeploymentException(
DeploymentException.java:49)
...
Caused by: org.jboss.xb.binding.JBossXBException: Failed to parse source:
cvc-complex-type.2.4.d: Invalid content was found starting with element
'config-property-value'. No child element is expected at this point. @
vfsfile:/C:/jboss-5.1.0.GA/server/default/deploy/
activemq-ra.rar/META-INF/ra.xml[100,36]


Ah for some reason I had an empty in ra.xml. Removing that made the 5.1 server start fine and the producer produce and the consumer consume!

PS: the example consumer & producer now send out 2000 messages instead of the 10 you see in the Integrating Apache ActiveMQ with JBoss post.

Hope this might help somebody some time :)

Changing format of blog

This blogpost is the last of the 'Best of this Week Summary' series.
Looking at the number of subscribers, I decided it's too low to be worth the effort.

Therefore, from now on I'll only post "real" blogposts written by myself, no more weekly summaries.
Those posts will contain lessons learned, tutorials etc.
This change of format also implies that the posts will be less frequent but hopefully much more interesting and relevant to a greater audience.

For the fans, here's the last set of items for "Best of this Week Summary", 27 September - 3 October 2010:


PS: I'll change my mind if I get at least 50 (valid, unique) requests in the comments to keep posting my weekly summaries :)

Sunday, September 26, 2010

Best of this Week Summary 14 September - 26 September 2010

Sunday, September 19, 2010

Best of this Week Summary 13 September - 19 September 2010

  • Interesting (sort of inside) view on how Google controls what's shipped with Android on mobile phones, how smart their usage of open source is, and why Android probably got such a big momentum quickly.
    The controls are: private branches, closed review process, speed of evolution of the platform versions, incomplete public source code, gated development community, anti-fragmentation agreement, private roadmap and Android trademark.

  • Good list of lessons learned when building social sites.

  • Atomiq could be handy tool to check for duplicate code in Java, C#, VB.Net, ASPX, Ruby, Python, C, C++, ActionScript and XAML. A related similar tool is the Copy Paste Detector in PMD.


Sunday, September 12, 2010

Best of this Week Summary 06 September - 12 September 2010

  • Small tuturial for sending emails with Spring Framework, which provides some abstraction from the underlying mailing system.

  • Nice: "HTML5 Boilerplate is the professional badass's base HTML/CSS/JS template for a fast, robust and future-proof site.
    After more than two years in iterative development, you get the best of the best practices baked in: cross-browser normalization, performance optimizations, even optional features like cross-domain ajax and flash. A starter apache .htaccess config file hooks you the eff up with caching rules and preps your site to serve HTML5 video, use @font-face, and get your gzip zipple on.
    Boilerplate is not a framework, nor does it prescribe any philosophy of development, it's just got some tricks to get your project off the ground quickly and right-footed."

  • Different existing logging strategies described and a new one: grammar based event logging.

  • Be cautious to not over-engineer your software solutions...


  • Are Spock, Geb and WebDriver the future of functional web testing?

Sunday, September 5, 2010

Best of this Week Summary 30 August - 05 September 2010

  • A "series of (currently 5) articles introduces Contexts and Dependency Injection for Java EE (CDI), a key part of the Java EE 6 platform. Standardized via JSR 299, CDI is the de-facto API for comprehensive next-generation type-safe dependency injection as well as robust context management for Java EE. Led by Gavin King, JSR 299 aims to synthesize the best-of-breed features from solutions like Seam, Guice and Spring while adding many useful innovations of its own".

  • An extensive tutorial on creating a Spring MVC based mobile web application using MyEclipse. The IDE has already an iPhone skinned version built in, this tutorial shows how to add other mobile handsets like Android and Palm Pre. Basically: providing another style :)


  • How to use Apache ActiveMQ in combination with Spring.

  • Why ACID is hard to scale. and an argument that NoSQL/NoACID is the lazy way around these difficulties. Here's a post considering the performance argument for switching to a NoSQL database.

  • Great interesting series on the progress of converting the native code BBC News iPhone/iPad app to a web app using HTML5.

Sunday, August 29, 2010

Best of this Week Summary 23 August - 29 August 2010

Wednesday, August 25, 2010

Endless flashing TextView animation in Android

A little while ago I needed a flashing text in Android.


Pretty easy you'd think: stick two animations in an AnimationSet, set the repeatMode to Animation.RESTART and repeatCount to Animation.INFITITE like this:

XML


<set xmlns:android="http://schemas.android.com/apk/res/android"
<alpha
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromAlpha="0.0"
    android:toAlpha="1.0"
    android:duration="1000"
/>
<alpha
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromAlpha="0.9"
    android:toAlpha="0.0"
    android:duration="1000"
    android:startOffset="1000"
/>
</set>



Activity code

AnimationSet c = (AnimationSet) AnimationUtils.loadAnimation(this, R.anim.flash);
c.setRepeatMode(Animation.RESTART);
c.setRepeatCount(Animation.INFINITE);
TextView tv = (TextView) findViewById(R.id.flashingTextView);
tv.clearAnimation();
tv.startAnimation(c);



and you're done.
But no! It only runs one time, or 1.5 times or whatever; definitely not endlessly.

This pretty basic requirement of looping a set of animations indefinitely is apparently a known bug. I haven't been able to find out if it's still an outstanding issue today (August 2010) but unless I implemented it incorrectly, it is definitely present in SDK 1.6.
Based on the answers in that thread and the example code here, I came up with the following solution:

XML
A fadein.xml and a fadeout.xml. See the end of this post for the complete code.

Activity code


    // Get ref to what we're going to animate (fade-in and fade-out indefinitely)
    tv = (TextView) findViewById(R.id.flashingTextView);

    // Setup fadein/out animations
   fadeIn = AnimationUtils.loadAnimation(this, R.anim.fadein);
   fadeIn.setAnimationListener( myFadeInAnimationListener );
   fadeOut = AnimationUtils.loadAnimation(this, R.anim.fadeout);
   fadeOut.setAnimationListener( myFadeOutAnimationListener );
   
   // And start with the fade in
   launchInAnimation();

    public void launchInAnimation() {
       tv.startAnimation(fadeIn);
    }    

    public void launchOutAnimation() {
       // Launch the second animation :
       tv = (TextView) findViewById(R.id.flashingTextView);
       tv.startAnimation(fadeOut);
    }    
    



Two listeners which start the other animation at the end of an animation:

    private class LocalFadeInAnimationListener implements AnimationListener {
       public void onAnimationEnd(Animation animation) {
           tv.post(mLaunchFadeOutAnimation);
        }
       public void onAnimationRepeat(Animation animation) {
       }
       public void onAnimationStart(Animation animation) {
       }
    };
    
    private class LocalFadeOutAnimationListener implements AnimationListener {
       public void onAnimationEnd(Animation animation) {
           tv.post(mLaunchFadeInAnimation);
      }
       public void onAnimationRepeat(Animation animation) {
      }
      public void onAnimationStart(Animation animation) {
      }
    };
    
    private LocalFadeInAnimationListener myFadeInAnimationListener = new LocalFadeInAnimationListener();
    private LocalFadeOutAnimationListener myFadeOutAnimationListener = new LocalFadeOutAnimationListener();




Runnables that do the fading

    private Runnable mLaunchFadeOutAnimation = new Runnable() {
       public void run() {
            launchOutAnimation();
       }
    };    
    
    private Runnable mLaunchFadeInAnimation = new Runnable() {
       public void run() {
        launchInAnimation();
       }
    };    



Note that I improved efficiency of the mentioned example code by only setting the animations one time and getting the TextView to animate only once.

That's it!

Note: in the logcat I do get these messages (2-3 times in about 5 minutes):


08-22 15:33:28.078: WARN/SurfaceComposerClient(252): lock_layer timed out (is the CPU pegged?) layer=0, lcblk=0x41048020, 
state=00000002 (was 00000043)
08-22 15:33:28.078: WARN/SurfaceComposerClient(252): lock_layer() timed out but didn't appear to need to be locked and 
we recovered (layer=0, lcblk=0x41048020, state=00000002)



Pretty strange, the CPU doesn't seem to be pegged... It says it recovers, but I guess the warning is there for a reason. Can the code be modified to avoid these warnings?
It might be a platform bug. Indeed I do see it appear in 1.6 but not in 2.1.

Full code (tested on 1.6) can be downloaded here.