Showing posts with label professional android application development. Show all posts
Showing posts with label professional android application development. Show all posts

Monday, January 10, 2011

How To: Use the Gyroscope API and Remain Android 1.1 Compatible

Making your app backwards compatible is the process of ensuring it degrades gracefully. 

Every new Android SDK release introduces a variety of new APIs

It takes time for each new OS version to percolate through the ecosystem, so while new functionality is cool, but it introduces a dilemma - use the new hotness or support the majority of users?

The answer of course is to do both! Make use of useful new APIs where they're available, and fall back to an earlier alternative (or disable functionality) when necessary.

Let's look at a practical example
My Nexus S has a gyroscope sensor that I can use to stabilize my artificial horizon / compass app (New Horizons). I'll save most of the implementation details for a separate post, and focus instead on how I can adjust my code to go from supporting only Android 2.3, to supporting everything from Android 1.0 up.
The platform version distribution shows 99.9% of devices are now running at least Android 1.5, and more than 75% are running 2.1+, so in this case achieving 1.0 compatibility is a contrivance that lets me demonstrate a number of useful techniques within a single example.
I'll start by reacting to orientation changes using the gyroscope in 2.3 and then remove hardware and API features until we end up compatible with a factory-fresh HTC G1.

Using the gyroscope sensor 

You'll need code that looks a little like this:

private void hookupSensorListener() {
  SensorManager sm = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
  sm.registerListener(sel,
    sm.getDefaultSensor(Sensor.TYPE_GYROSCOPE),
    SensorManager.SENSOR_DELAY_UI);
}

private final SensorEventListener sel = new SensorEventListener() {
  public void onSensorChanged(SensorEvent event) {
    if (event.sensor.getType() == Sensor.TYPE_GYROSCOPE) {
      updateOrientation(event.values[0], event.values[1], event.values[2]);
    }
  }
  
  public void onAccuracyChanged(Sensor sensor, int accuracy) {}
};

private void updateOrientation(float heading, float pitch, float roll) {
  // Update the UI
}


Within the updateOrientation method you react to each orientation change (for a gyro that's going to mean integrating the changes in angular velocity) and eventually extract values you'll use to update the UI.

Case 1: What if there is no gyroscope?

Can we implement the same functionality with different hardware? In this case yes, we can use the accelerometers to determine our orientation.

Start by creating a listener interface that can listen for orientation changes:

public interface OrientationChangeListener {
  public void onUpdate(float heading, float pitch, float roll);
  public void onAccuraryChanged(int accuracy);
}


Then define another interface that can be implemented to handle orientation updates from different sensor hardware:

public interface IOrientationSensorListener {
  public void setOrientationChangeListener(OrientationChangeListener l);
  public void registerListener(SensorManager sensorManager);
  public void unregisterListener(SensorManager sensorManager);
}


Create two implementations of the IOrientationSensorListener class, one that uses the gyro and the other that uses accelerometers:
  • GyroOrientationSensorListener
  • AccOrientationSensorListener
The orientation updates are encapsulated in their hardware-specific implementations, so we need to implement an OrientationChangeListener that will receive their orientation updates and update the UI accordingly:

final OrientationChangeListener ocl = new OrientationChangeListener() {
  public void onUpdate(float heading, float pitch, float roll,) {
    updateOrientation(heading, pitch, roll);
  }

  public void onAccuraryChanged(int accuracy) {
    updateAccuracy(accuracy);
  }
});


Now we determine if the gyro is available:

PackageManager paM = getPackageManager();
boolean gyroExists = paM.hasSystemFeature(PackageManager.FEATURE_SENSOR_GYROSCOPE);


And instantiate the appropriate IOrientationSensorListener implementation:

IOrientationSensorListener mySensorListener;
if (gyroExists)
  mySensorListener = new GyroOrientationSensorListener();
else
  mySensorListener = new AccOrientationSensorListener();


Before hooking up the orientation change listener:

mySensorListener.setOrientationChangeListener(ocl);
For cases where there is no viable alternative hardware, you can either disable that functionality completely, or specify the required hardware as a mandatory feature:

<uses-feature android:name="android.hardware.sensor.gyroscope"/>

Note that this will prevent your app from being visible on the Market for devices without the required hardware.

Case 2: What if the APIs you're using are missing?

While your Android 2.3 app will install and run on any version of the Android OS, it will crash the moment it tries to use a class or method that doesn't exist on that device. To figure out what needs to be fixed, you can modify your project's build target to the lowest OS version you intend to support and see what won't compile.

Looking at my code above, I can see the following constants, methods, and classes were introduced since Android 1.0:
  • API Level 9: PackageManager.FEATURE_SENSOR_GYROSCOPE
  • API Level 6: PackageManager.hasSystemFeature()
  • API Level 3: Sensor, SensorEvent, and SensorEventListener
Dealing with new constants

New constants will work without any changes. The compiler will convert them into the value they represent and the methods into which you pass them should be able to handle with unrecognized values cleanly.

In this instance I pass a string constant into the hasSystemFeature method that assumes any feature it doesn't recognize isn't available and returns false.

We're now compatible with API Level 6 (Android 2.0.1)!

Dealing with missing methods

Missing methods are a little more complicated. In this case the PackageManager class has been extended to include the hasSystemFeature method. If you call this method on a device running Android 1.6 (or below) it will throw an exception and crash.

To avoid this you can:
  • Use reflection at runtime to determine if the method exists.
  • Replace the method with an alternative that works on previous OS versions.
Reflection is expensive, so first we'll check if there's a cheaper alternative that doesn't add significant complexity or cost.

In this case I can use SensorManager.getDefaultSensor to check if there is a default gyroscope sensor available.

boolean gyroExists = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) != null;

We're now compatible with API Level 3 (Android 1.5), and given the distribution of Android OS versions in the wild, for most cases we'd be done.

We still haven't looked at dealing with missing classes though, so for the sake of completeness let's go one step further and ensure support for Android 1.0.

Dealing with missing classes

The Sensor, SensorEvent, and SensorEventListener classes were all added in Android 1.5 (along with a number of useful SensorManager methods including getDefaultSensor used above).

Let's start by using reflection to see if the new Sensor classes and methods are available. This can be expensive so it's important to perform the check only once per application launch. We'll also assume that the existence of one 1.5+ class implies the existence of the rest.

private static boolean sensorListenerExists = false;
 
private static void checkSensorEventListenerExists() throws NoClassDefFoundError {
  sensorEventListenerExists = SensorEventListener.class != null;
}
 
static {
  try {
    checkSensorEventListenerExists();
  } catch (NoClassDefFoundError e) {
    sensorEventListenerExists = false;
  }
}


To avoid an exception on calling getDefaultSensor we need to move this call into a new class that's only used if the new method is available. Let's expose the functionality using a static method to avoid the need to create any new objects:

public class SensorEventHasGyro {
  public static boolean gyroExists(SensorManager sensorManager) {
    return sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) != null;
  }
}


Gyroscopes weren't supported prior to Android 1.5, so we can check for a gyroscope sensor using the following code:

boolean gyroExists = sensorListenerExists && 
                     SensorEventHasGyro.gyroExists(sensorManager);

Finally, we create a new implementation of the OrientationSensorListener that uses the old technique for calculating orientation from accelerometers (which is so deprecated it's not worth repeating here), then modify the code used to select and instantiate the correct implementation.

if (gyroExists)
  mySensorListener = new GyroOrientationSensorListener();
else if (sensorListenerExists)
  mySensorListener = new AccOrientationSensorListener();
else 
  mySensorListener = new AccOldOrientationSensorListener();

Summary

While providing support for Android 1.0 as done in this example is a little contrived, the same techniques will work in cases like the Download Manager or SIP stack where the majority of users don't have access to the newer versions of the OS.

Why wait until everyone is using Android 2.3 to make use of the gyroscope or download manager? Using these techniques you can build it in right now, and simply provide an alternative implementation for users who aren't fortunate enough to have the latest hardware / software.

Monday, February 22, 2010

Professional Android 2 Application Development: This Time It's Personal

Professional Android 2 Application Development, started shipping today (Monday) from Amazon US - so those of you who pre-ordered should be seeing your copies in a couple of days. I'm really excited and can’t wait to find out what people think.

A lot has happened in the year since Professional Android was released. In November of 2008 version 1.0 of the Android SDK had only just been released. There was one handset (the G1) on one carrier (T-Mobile) in one country (the US).

What's new?

As Professional Android 2 rolls off the presses there have been 6 new platform releases (up to Android 2.1), and Android is now available on 26 handsets in 48 countries on 58 carriers.

Understandably much of the original Professional Android material is now out-of-date. Accordingly, Professional Android 2 has been totally revised and expanded to cover all the changed and new API features introduced in the past year up to and including Android 2.1.

That includes the new Bluetooth API, Widgets and Live Wallpaper, the updated contacts and Sensor APIs, the new ASyncTasks and Services framework, as well as expanded sections covering using the camera, microphone, and media framework.

In all there are an extra five chapters and over 100 more pages.

Oh, and there's a new cover image that is so awesome it's almost enough to justify replacing the older copy by itself.

Where to buy


If you're interested in a copy the best place for you to buy a paper copy of the book is from Amazon using one of these links:
I'm also particularly excited to say that you can also purchase Professional Android 2 as DRM-free e-book PDF from the Wrox website:
Every concept in the book is supported with code snippets and a bunch of detailed step-by-step examples -- all of which you can download from the Wrox Open Source site.

Android development conversation

I'll be looking to answer Android questions at the Wrox P2P forums, Stack Overflow, and the Android Google Groups.

For more information on Android and the book you can follow me on Twitter or Buzz. I'll tweet any 'bugs' and changes to the text or code samples, as well as updates on any SDK releases that cause book code samples to break.

Friday, February 27, 2009

Earthquake! for Android

A couple of years ago, the real-time earthquake display on display at the Natural History Museum in New York impressed me so much I stole copied created my own version of their concentric circle filled goodness.

Lately I've been focussing on mobile applications, so I figured why not move the same compelling UI to my Android handset?



Like the exhibit that inspired it, Earthquake! (Applications > News & Weather) shows not just the epicentres, but also an approximated 'damage zone' (inner circle, dark shading) and 'rumble zone' (outer circle, lighter shading) to give an impression of the areas likely to be affected by each earthquake. Zoom in to see which cities and suburbs will feel the tremor, and which are at risk for property damage.

Being a mobile app invites some personalization features not possible on a web site or museum display. Earthquake! lets you configure notification for new earthquakes that cause the phone to vibrate in proportion to the size of the detected quake. Small, magnitude 3 quakes barely shake your phone, but Big One's at 8 or 9 on the Richter scale will vibrate for up to 20 seconds.

Using My Location you can filter notifications to only alert you to earthquakes nearby, or for which you're within the expected 'rumble zone'.

So now if you've got an Android powered phone, you can have you own mobile real-time earthquake display. Then if you wake up in the deserted ruins of a post-apocalyptic nightmare you won't have to wonder whether or not the damage was caused by a magnitude nine earthquake splitting the Earth's crust in two.
The code used to create Earthquake! is a polished version of the ongoing example code shown in Professional Android Application Development, so if you like the idea pick up a copy and see how it's done.

Saturday, November 15, 2008

Professional Android Application Development: Out Now!

Professional Android Application Development ships tomorrow (Tuesday) from Amazon US, so those of you who pre-ordered should be seeing your copies in a couple of days. I'm really excited and can’t wait to find out what people think.

Where to buy

If you're interested in a copy the best place for me, for you to buy the book is Amazon following one of these links:
Free stuff and resources

If you'd prefer not to fork out without knowing a little more about what you're paying for (or just don't want to fork out at all), here's some useful resources that come free of charge:

Chapter 1 is available as a free PDF download [pdf] from the Wrox site if you want to learn more about Android before you commit to a 400 page tome. The TOC and index are there too. Chapter 7 [pdf ] -- which focusses on maps, location-based services, and the geocoder -- is available too.

Every concept in the book is supported with code snippets and a bunch of detailed step-by-step examples -- all of which you can download from the Wrox Open Source site.

If you're looking for details on something specific, Amazon's "Search Inside" is a surprisingly useful resource.

Android development conversation

I'll be looking to answer Android questions at the Wrox P2P forums, StackOverflow, and the Android Google Groups.

You can follow me on Twitter or FriendFeed, or just get book related news from the books Twitter feed and Friend Feed Room. I'll tweet any 'bugs' and changes to the text or code samples, as well as updates on any SDK releases that cause book code samples to break.

Still to come

To celebrate the book release I'll be posting a bunch of stuff about Android, including tutorials, walk throughs, and open source projects -- both here and other places online.

As luck would have it I got my G1 last week, so I can finally test and tweek my Android applications, so expect to see announcements about them in the coming days and weeks.

Tuesday, August 19, 2008

New Android SDK Beta Released

The eagerly (if not patiently) awaited update to the Android SDK was released yesterday. You can see the release notes here, or skip all that and download the good stuff from here.

It's a substantial release, as you'd expect after such a long gap since the previous build (M5); I've listed some of the more notable changes below.

The executive summary? Excellent new release that adds some maturity and stability to the early look version we've been playing with for so long. There's a lot of work for people porting existing apps over from M5, but the biggest downside is the loss two of the more interesting optional libraries (Gtalk and Bluetooth).

The Good News
  • Future SDK releases up to version 1.0 shouldn't have significant breaking changes.
  • The maps, location-based services, and geocoding APIs have been much improved and tidied up.
  • Interacting with the phone hardware (getting incoming call number etc.) is improved.
  • The accelerometer and compass APIs are much more mature.
  • Context menus and dialog boxes are included and improved respectively.
  • The developer tools are much slicker.
  • The new home screen and native applications are much nicer than M5, as are a lot of the default controls (Views).
The Bad News
  • GTalkService is out for 1.0
  • Bluetooth is similarly missing for 1.0
  • There have been a lot of breaking changes to behavior, required permissions, class names, and method signatures. Such is the price of progress.
The loss of GTalk and Bluetooth is a bit of a blow, some of the most exciting mobile application possibilities come from those APIs.

The signals coming from Google suggest this is "for now" not "forever". Most likely scenario is the security implications for both these APIs it was cheaper / easier to just leave 'em out for version 1 and come back to them later.

For those of you interested, Professional Android Application Development will be fully up-to-date on the (at least) this latest Android Beta.