Friday, January 14, 2011

Adding suport for Android SDK <2.2

 Making your application work across multiple Android SDK versions can be non-trivial if you are using features that depend on newly introduced API. One such example is the YuvImage class in SDK v2.2 (Froyo). We use YuvImage.compressToJpeg method to convert YUV data obtained from the Android camera, to a Jpeg image which you can save to disk, dispatch over the network, pickle it, or what you will. However, for API version <2.2, we need to do something else:

int[] rgb = new int[previewWidth * previewHeight]; 
// This method decodes YUV to RGB.
// imageData is byte[] obtained from the Camera.
decodeYUV(rgb, imageData, previewWidth, previewHeight);
Bitmap bm = Bitmap.createBitmap(rgb, previewWidth, previewHeight, Config.RGB_565);
bm.compress(Bitmap.CompressFormat.JPEG, mPreviewQuality, out);

However, to produce an app that works across different platform versions, this is not enough. We still want to use the speed of native Jpeg compression done by YuvImage in 2.2+ SDK. With the target SDK version of the app set to 2.1, we need to use reflection in order to use the YuvImage class.

Class<?>[] yuvArgsClass = new Class[] {
  byte[].class, int.class, int.class, int.class, int[].class };
Class<?> yuvParamTypes[] = new Class[] { Rect.class, int.class, OutputStream.class};
Class<?> c = Class.forName("android.graphics.YuvImage");
Constructor<?> yuvImageConstructor = c.getConstructor(yuvArgsClass);
Object[] args = new Object[] {
  imageData, previewFormat, previewWidth, previewHeight, null};
Object yuvImage = yuvImageConstructor.newInstance(args);
Object params[] = new Object[] {previewRect, mPreviewQuality, out};
Method compress = c.getMethod("compressToJpeg", yuvParamTypes);
compress.invoke(yuvImage, params);

In the above code, imageData is the byte[] data obtained from the Camera. previewFormat, previewWidth, and previewHeight are obtained from Camera.getParameters().
previewRect = new Rect(0, 0, previewWidth, previewHeight);

We were unable to get the Java-based YUV -> Bitmap conversion to work on the G1 phones due to OutOfMemory problems. G1 phones have a 16 MB heap which seems to run out pretty quick.

Sunday, January 2, 2011

ProGuard Issues

Android recommends using ProGuard to obfuscate the APKs. However, sometimes ProGuard ends up obfuscating something the app needs, thus causing crashes with exceptions as ClassNotFoundException, NoClassDefFound and such. I ran into this issue recently while releasing the Baby Monitor app.

As suggested here, adding -keep to the progcfg.txt file works. The trick to figure out which classes to keep from obfuscating. If your app implements a Service, make sure that the interfaces are not obfuscated. To do this, add the following line to progcfg.txt:

-keep public interface * extends android.os.IInterface