Thursday, September 1, 2011

Playing with the new ViewPager class

Some days ago we released our latest work for, a magazine for mobile devices. The application already existed in ios so we did a port to Android.

Since we wanted portability we decided to create an hybrid application. An hybrid application is a mixture of native code and WebViews with HTML+JS+CSS. During the development of the application we found some android issues and some interesting implementation problems which I will describe in the following lines.

Btw the application is ready to download in this link.

APK size limitation
While in the ios world the ipa size limit is 2 GBs in the Android market is just 50MBs. After zipping our resources we ended up with a 200MB zip file.

To solve this we create a splash screen activity where the user get a dialog asking him if he wants to start the download. When the download starts, a Service is created and the download starts.

To avoid the download to halt we decided to add a partial wake lock with the following code:

PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
WakeLock wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);

In the docs, it says:
Wake lock that ensures that the CPU is running. The screen might not be on.

Even thought the docs don't say if the wifi stays on, using a full wake lock didn't make a lot of sense. I tested on a nexus one and a nexus S and everything went fine, BUT yesterday I was talking to Christopher and he mentioned that his download stopped when the screen turned off. He tested on a Desire Z, running 2.3.3 (CyanogenMod 7.0.3). Let me quote his research:

There doesn't really seem to be a disconnection when the screen is off for a short time. Using "netstat" shows that the HTTP connection is still open; it's just not transferring anything. Usually when I turn the screen back on, the transfer will continue. Probably if it's idle for a long time, the connection is dropped.

I just tested this behaviour between WLAN and 3G. Network activity continues when the screen is turned off and the device is on a mobile network. If the device is on WLAN, the network activity pauses when the screen is turned off.

The Android Market shows the same behaviour on my device. It uses a partial wakelock for its DownloadManager but most network traffic pauses while the screen is off, if using WLAN. When the screen is turned on again soon, the traffic resumes.

Having to create a download Service from scratch was quite a pain. I know there is already a DownloadManager but it's only available in Android >2.3. It would be nice to have something similar to JakeWharton's ActionBarSherlock but with the DownloadManager.

If you never heard about the ViewPager go and read Rich Hyndman's blogpost.

I made a small demo with some stuff I found.
You can get the source code from this link and the apk from here.
I will explain what I am trying to show in each test.

The magazine app was meant to be portrait only but it did have some landscape resources.
For example:

Portrait version

Landscape version

So, the Activity holding the ViewPager needed to be portrait only / portrait-landscape depending on the opened page. In the example every page index %2 == 0 has a landscape version. The test is called Restarting because every time we rotate the device, it will recreate the whole view.

This tests shows how to deal with WebViews. Nothing strange.

WebViews Accelerated
I would say this is an android bug. If you place WebViews and you make your Activity hardware accelerated you will notice that the WebViews will start to invalidate themselves in a wrong way.

Unfortunately we couldn't find a solution for this and ended up leaving the app without hardware acceleration :(

Here's a screenshot of the issue:
WebViews accelerated

When moving through pages you will see some blue boxes for my blog since the background is blue.

We tried really hard to make the HTML5 video tag work in android but we failed.
You can try this link .
The video used in the demo is from that site, I hope the author doesn't get mad about that :)

The first test doesn't say much, just shows VideoViews inside a ViewPager. All the pages are portrait only.

VideoView Resizable
This second VideoView test is more interesting. It tries to reproduce youtube's app behavior.
You can read about this issue in the following stackoverflow's question.

Instead of using the technique in the "Restarting" test we handle the change of orientation by hand changing the videoView's layout params.

VideoView Resizable with Margins
Similar to the last one but with the possibility of using margins. I just placed this test because when testing in the real app when the videoView got resized it didn't end up with the original size. I couldn't reproduce it :(

You will still notice that there is also a bug when you start changing from portrait to landscape. Here's an screenshot:
Media controller in the wrong place

Android 3.x has bugs and some of them will make you wonder how they could make the slip. On the other hand the introduction of the compatibility library brought an excellent weapon to fight fragmentation. For instance, the demo's min sdk is 4. I can be sure that I am leaving just a small group of users out.

PS: Talking about android 3.x bugs/issues. If you try running the demo with target sdk 13 and compile against 3.2 onConfigurationChanged() will never get called. The WTF per second is increasing drastically :)