Wednesday, March 2, 2011

Android Programming Tips

In this post, I'll share some android programming tips I picked up building Say Cheese.

Tip # 1 Android 3.0 clipping dynamic views.

Few camera apps, if any besides Say Cheese, support all the devices possible picture sizes. Square 400x400 snapshots, for example. So I built a class, extending ViewGroup, with a basic FrameLayout where I add my camera SurfaceView.

public class CameraViewGroup extends ViewGroup {

then I override the layout method so I can change the size and position of my camera SurfaceFiew holder, or any other child objects in that group, like a zoom rectangle.

protected void onLayout(boolean changed, int l, int t, int r, int b)
MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY));
mCameraFrame.layout(newx, newy, neww, newh);

if (mSizeListener != null)


All is working great, when along comes Android 3.0 and the Xoom. Suddenly my Dynamic surface holder is being chopped off for no good reason.

Solution: The solutions is simple, but almost impossible to figure out. This now works on all Android devices and OS versions. So in your layout.xml file include
the android:clipChildren="false" tag in your custom ViewGroup definition. This fixes an incompatibility issue with Android 3.0 but still works with earlier versions.

Tip # 2 Front Facing Camera.

From what I can determine, Android does not support the front facing camera in versions below 2.3 (gingerbread). I had to use a special library from Sprint to support the front camera on the EVO. Sprint and Verizon are promising updates to the OS very soon so the best solution is to code in anticipation of those using Java Reflection. Here is a routine that will open the front camera if the phone has 2.3 or higher installed, and will not crash otherwise.

public boolean usingFrontCamera = false;
public boolean frontCameraFailed = false;
private Camera tryOpenFrontFacingCamera() {
Camera camera = null;
// Look for front-facing camera, using the Gingerbread API.
// Java reflection is used for backwards compatibility with pre-Gingerbread APIs.
try {
Class cameraClass = Class.forName("android.hardware.Camera");
Object cameraInfo = null;
Field field = null;
int cameraCount = 0;
Method getNumberOfCamerasMethod = cameraClass.getMethod( "getNumberOfCameras" );
if ( getNumberOfCamerasMethod != null ) {
cameraCount = (Integer) getNumberOfCamerasMethod.invoke( null, (Object[]) null );
Class cameraInfoClass = Class.forName("android.hardware.Camera$CameraInfo");
if ( cameraInfoClass != null ) {
cameraInfo = cameraInfoClass.newInstance();
if ( cameraInfo != null ) {
field = cameraInfo.getClass().getField( "facing" );
Method getCameraInfoMethod = cameraClass.getMethod( "getCameraInfo", Integer.TYPE, cameraInfoClass );
if ( getCameraInfoMethod != null && cameraInfoClass != null && field != null ) {
for ( int camIdx = 0; camIdx < cameraCount; camIdx++ ) {
getCameraInfoMethod.invoke( null, camIdx, cameraInfo );
int facing = field.getInt( cameraInfo );
if ( facing == 1 ) { // Camera.CameraInfo.CAMERA_FACING_FRONT
try {
Method cameraOpenMethod = cameraClass.getMethod( "open", Integer.TYPE );
if ( cameraOpenMethod != null ) {
camera = (Camera) cameraOpenMethod.invoke( null, camIdx );
} catch (RuntimeException e) {
Log.e(TAG, "Camera failed to open: " + e.getLocalizedMessage());
// Ignore the bevy of checked exceptions the Java Reflection API throws - if it fails, who cares.
catch ( ClassNotFoundException e ) {Log.e(TAG, "ClassNotFoundException" + e.getLocalizedMessage());}
catch ( NoSuchMethodException e ) {Log.e(TAG, "NoSuchMethodException" + e.getLocalizedMessage());}
catch ( NoSuchFieldException e ) {Log.e(TAG, "NoSuchFieldException" + e.getLocalizedMessage());}
catch ( IllegalAccessException e ) {Log.e(TAG, "IllegalAccessException" + e.getLocalizedMessage());}
catch ( InvocationTargetException e ) {Log.e(TAG, "InvocationTargetException" + e.getLocalizedMessage());}
catch ( InstantiationException e ) {Log.e(TAG, "InstantiationException" + e.getLocalizedMessage());}
catch ( SecurityException e ) {Log.e(TAG, "SecurityException" + e.getLocalizedMessage());}

usingFrontCamera = true;
if ( camera == null ) {
// Try using the pre-Gingerbread APIs to open the camera.
try {
camera =;
usingFrontCamera = false;
frontCameraFailed = true;
} catch (RuntimeException e) {
Log.e(TAG, "Camera failed to open: " + e.getLocalizedMessage());
return camera;
Tip # 3 Activity as Dialog without Title Bar.

I wanted to include a pop-up help feature in Say Cheese as its own activity and have a custom title with a return button. You would think this would be simple but it's not. You need to theme the activity as a dialog and then hide the title bar. Here's the trick. First you use a dialog theme in the manifest file for this activity. For example:

activity android:name=".help" android:theme="@android:style/Theme.Dialog"

But you can only have one theme for the activity so you need to remove the title bar in your code. Just add one line of code (see sample below) to your onCreate override for your activity to turn off the title bar. The net result is a nice popup dialog activity with no title bar.

public void onCreate(Bundle savedInstanceState) {

Tip # 4 Add runtime un-handled exception reporting

With all the different devices and Android OS versions, you need to know when something goes wrong and where in your code the problem occurs. From what I can determine, you can't do anything about the force close error window that pops up when you have an un-handled exception. But what you can do is create an error report and have the user email you the report if your program crashes. This will let you fix the problem without testing your code on every device and OS and every condition in debug mode on your own computer.. 

Tip # 5 InApp Billing. Fix the dangling service.

The Google Dungeons sample leaves a dangling service. Some people have blogged about this. 

Here's a fix.

In add the following code

    public int onStartCommand(Intent intent, int flags, int startId)
        handleCommand(intent, startId);
        return START_NOT_STICKY;

Then when you exit your application in your OnDestroy function add these two lines of code as your last lines of code in your OnDestroy


1 comment:

  1. I've been trying to solve this too thanks for posting.