Saturday, June 12, 2010

Download Count Problems

Something is apparently wrong in the Android Market. We are getting multiple reports of erroneous download counts. The right people are aware of the situation and are working on it.

[Update, Monday morning: The fix was rolled in early Sunday and it seems as though app developers have their missing downloads back.]

Friday, June 11, 2010

Blogging Round the World

It seems that once or twice a week, I run across an Android-developer-oriented site that I hadn�t previously noticed. There are already a few aggregators and directories, and I think we�re going to need more. But for the moment, here are three pieces of bloggy Android goodness, from Florida, Odessa (Ukraine!), and Sydney. What they have in common is that I previously hadn't encountered any of them.

Font Magic

This is from Florida-based Jeff Vera�s Musings of the Bare Bones Coder, which, although it advertises itself as being about �Coding and managing in the .NET space�, recently ran the excellent Android Development � Using Custom Fonts. You�ve always been able to use your own fonts in your own apps, but the how-to coverage has been light.

How Hot Is It?

Ivan Memruk from Odessa, Ukraine, brings us Mind The Robot, which has a refreshing concern for visual elegance. Speaking of which, soak up the analog steampunk tastiness of Android Custom UI: Making a Vintage Thermometer.

Aussie Rules

In this case, I mean rules for getting your Android project set up for use both via Eclipse and command-line Ant. Daniel Ostermeier and Jason Sankey from Sydney run the Android-dense a little madness, and lay the rules out in Setting Up An Android Project Build. Lots of steps; but very handy for a command-line guy like me.

Wednesday, June 9, 2010

Making Sense of Multitouch




[This post is by Adam Powell, one of our more touchy-feely Android engineers. � Tim Bray]




The word �multitouch� gets thrown around quite a bit and it�s not always clear what people are referring to. For some it�s about hardware capability, for others it refers to specific gesture support in software. Whatever you decide to call it, today we�re going to look at how to make your apps and views behave nicely with multiple fingers on the screen.



This post is going to be heavy on code examples. It will cover creating a custom View that responds to touch events and allows the user to manipulate an object drawn within it. To get the most out of the examples you should be familiar with setting up an Activity and the basics of the Android UI system. Full project source will be linked at the end.



We�ll begin with a new View class that draws an object (our application icon) at a given position:


public class TouchExampleView extends View {
private Drawable mIcon;
private float mPosX;
private float mPosY;

private float mLastTouchX;
private float mLastTouchY;

public TouchExampleView(Context context) {
this(context, null, 0);
}

public TouchExampleView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public TouchExampleView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mIcon = context.getResources().getDrawable(R.drawable.icon);
mIcon.setBounds(0, 0, mIcon.getIntrinsicWidth(), mIcon.getIntrinsicHeight());
}

@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);

canvas.save();
canvas.translate(mPosX, mPosY);
mIcon.draw(canvas);
canvas.restore();
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
// More to come here later...
return true;
}
}


MotionEvent



The Android framework�s primary point of access for touch data is the android.view.MotionEvent class. Passed to your views through the onTouchEvent and onInterceptTouchEvent methods, MotionEvent contains data about �pointers,� or active touch points on the device�s screen. Through a MotionEvent you can obtain X/Y coordinates as well as size and pressure for each pointer. MotionEvent.getAction() returns a value describing what kind of motion event occurred.



One of the more common uses of touch input is letting the user drag an object around the screen. We can accomplish this in our View class from above by implementing onTouchEvent as follows:


@Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();

// Remember where we started
mLastTouchX = x;
mLastTouchY = y;
break;
}

case MotionEvent.ACTION_MOVE: {
final float x = ev.getX();
final float y = ev.getY();

// Calculate the distance moved
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;

// Move the object
mPosX += dx;
mPosY += dy;

// Remember this touch position for the next move event
mLastTouchX = x;
mLastTouchY = y;

// Invalidate to request a redraw
invalidate();
break;
}
}

return true;
}


The code above has a bug on devices that support multiple pointers. While dragging the image around the screen, place a second finger on the touchscreen then lift the first finger. The image jumps! What�s happening? We�re calculating the distance to move the object based on the last known position of the default pointer. When the first finger is lifted, the second finger becomes the default pointer and we have a large delta between pointer positions which our code dutifully applies to the object�s location.



If all you want is info about a single pointer�s location, the methods MotionEvent.getX() and MotionEvent.getY() are all you need. MotionEvent was extended in Android 2.0 (Eclair) to report data about multiple pointers and new actions were added to describe multitouch events. MotionEvent.getPointerCount() returns the number of active pointers. getX and getY now accept an index to specify which pointer�s data to retrieve.



Index vs. ID



At a higher level, touchscreen data from a snapshot in time may not be immediately useful since touch gestures involve motion over time spanning many motion events. A pointer index does not necessarily match up across complex events, it only indicates the data�s position within the MotionEvent. However this is not work that your app has to do itself. Each pointer also has an ID mapping that stays persistent across touch events. You can retrieve this ID for each pointer using MotionEvent.getPointerId(index) and find an index for a pointer ID using MotionEvent.findPointerIndex(id).



Feeling Better?



Let�s fix the example above by taking pointer IDs into account.


private static final int INVALID_POINTER_ID = -1;

// The �active pointer� is the one currently moving our object.
private int mActivePointerId = INVALID_POINTER_ID;

// Existing code ...

@Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();

mLastTouchX = x;
mLastTouchY = y;

// Save the ID of this pointer
mActivePointerId = ev.getPointerId(0);
break;
}

case MotionEvent.ACTION_MOVE: {
// Find the index of the active pointer and fetch its position
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);

final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;

mPosX += dx;
mPosY += dy;

mLastTouchX = x;
mLastTouchY = y;

invalidate();
break;
}

case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}

case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}

case MotionEvent.ACTION_POINTER_UP: {
// Extract the index of the pointer that left the touch sensor
final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastTouchX = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
}
break;
}
}

return true;
}


There are a few new elements at work here. We�re switching on action & MotionEvent.ACTION_MASK now rather than just action itself, and we�re using a new MotionEvent action constant, MotionEvent.ACTION_POINTER_UP. ACTION_POINTER_DOWN and ACTION_POINTER_UP are fired whenever a secondary pointer goes down or up. If there is already a pointer on the screen and a new one goes down, you will receive ACTION_POINTER_DOWN instead of ACTION_DOWN. If a pointer goes up but there is still at least one touching the screen, you will receive ACTION_POINTER_UP instead of ACTION_UP.



The ACTION_POINTER_DOWN and ACTION_POINTER_UP events encode extra information in the action value. ANDing it with MotionEvent.ACTION_MASK gives us the action constant while ANDing it with ACTION_POINTER_INDEX_MASK gives us the index of the pointer that went up or down. In the ACTION_POINTER_UP case our example extracts this index and ensures that our active pointer ID is not referring to a pointer that is no longer touching the screen. If it was, we select a different pointer to be active and save its current X and Y position. Since this saved position is used in the ACTION_MOVE case to calculate the distance to move the onscreen object, we will always calculate the distance to move using data from the correct pointer.



This is all the data that you need to process any sort of gesture your app may require. However dealing with this low-level data can be cumbersome when working with more complex gestures. Enter GestureDetectors.



GestureDetectors



Since apps can have vastly different needs, Android does not spend time cooking touch data into higher level events unless you specifically request it. GestureDetectors are small filter objects that consume MotionEvents and dispatch higher level gesture events to listeners specified during their construction. The Android framework provides two GestureDetectors out of the box, but you should also feel free to use them as examples for implementing your own if needed. GestureDetectors are a pattern, not a prepacked solution. They�re not just for complex gestures such as drawing a star while standing on your head, they can even make simple gestures like fling or double tap easier to work with.



android.view.GestureDetector generates gesture events for several common single-pointer gestures used by Android including scrolling, flinging, and long press. For Android 2.2 (Froyo) we�ve also added android.view.ScaleGestureDetector for processing the most commonly requested two-finger gesture: pinch zooming.



Gesture detectors follow the pattern of providing a method public boolean onTouchEvent(MotionEvent). This method, like its namesake in android.view.View, returns true if it handles the event and false if it does not. In the context of a gesture detector, a return value of true implies that there is an appropriate gesture currently in progress. GestureDetector and ScaleGestureDetector can be used together when you want a view to recognize multiple gestures.



To report detected gesture events, gesture detectors use listener objects passed to their constructors. ScaleGestureDetector uses ScaleGestureDetector.OnScaleGestureListener. ScaleGestureDetector.SimpleOnScaleGestureListener is offered as a helper class that you can extend if you don�t care about all of the reported events.



Since we are already supporting dragging in our example, let�s add support for scaling. The updated example code is shown below:


private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;

// Existing code ...

public TouchExampleView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mIcon = context.getResources().getDrawable(R.drawable.icon);
mIcon.setBounds(0, 0, mIcon.getIntrinsicWidth(), mIcon.getIntrinsicHeight());

// Create our ScaleGestureDetector
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
// Let the ScaleGestureDetector inspect all events.
mScaleDetector.onTouchEvent(ev);

final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();

mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = ev.getPointerId(0);
break;
}

case MotionEvent.ACTION_MOVE: {
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);

// Only move if the ScaleGestureDetector isn't processing a gesture.
if (!mScaleDetector.isInProgress()) {
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;

mPosX += dx;
mPosY += dy;

invalidate();
}

mLastTouchX = x;
mLastTouchY = y;

break;
}

case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}

case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}

case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastTouchX = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
}
break;
}
}

return true;
}

@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);

canvas.save();
canvas.translate(mPosX, mPosY);
canvas.scale(mScaleFactor, mScaleFactor);
mIcon.draw(canvas);
canvas.restore();
}

private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();

// Don't let the object get too small or too large.
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));

invalidate();
return true;
}
}


This example merely scratches the surface of what ScaleGestureDetector offers. The listener methods receive a reference to the detector itself as a parameter that can be queried for extended information about the gesture in progress. See the ScaleGestureDetector API documentation for more details.



Now our example app allows a user to drag with one finger, scale with two, and it correctly handles passing active pointer focus between fingers as they contact and leave the screen. You can download the final sample project at http://code.google.com/p/android-touchexample/. It requires the Android 2.2 SDK (API level 8) to build and a 2.2 (Froyo) powered device to run.



From Example to Application



In a real app you would want to tweak the details about how zooming behaves. When zooming, users will expect content to zoom about the focal point of the gesture as reported by ScaleGestureDetector.getFocusX() and getFocusY(). The specifics of this will vary depending on how your app represents and draws its content.



Different touchscreen hardware may have different capabilities; some panels may only support a single pointer, others may support two pointers but with position data unsuitable for complex gestures, and others may support precise positioning data for two pointers and beyond. You can query what type of touchscreen a device has at runtime using PackageManager.hasSystemFeature().



As you design your user interface keep in mind that people use their mobile devices in many different ways and not all Android devices are created equal. Some apps might be used one-handed, making multiple-finger gestures awkward. Some users prefer using directional pads or trackballs to navigate. Well-designed gesture support can put complex functionality at your users� fingertips, but also consider designing alternate means of accessing application functionality that can coexist with gestures.

Tuesday, June 8, 2010

Application Visibility Issues

Recently we became aware that some Android applications were not visible on the Android Market. While we were internally troubleshooting and qualifying the fix and communicating with our hardware partners, developers were trying hard to get our help through various technical support sites. Regrettably, we fell short of our own standard for customer support by not communicating the issue to our developers and how we were working to resolve it.

We�re pleased to say that the issue looks to be resolved with a patch, and to our best knowledge, all apps that were previously impacted are up and visible again. Again, apologies for the delay and inconvenience this created.

Wednesday, June 2, 2010

Allowing applications to play nice(r) with each other: Handling remote control buttons

[This post is by Jean-Michel Trivi, an engineer working on the Android Media framework, whose T-shirt of the day reads �all your media buttons are belong to you�. � Tim Bray]

Many Android devices come with the Music application used to play audio files stored on the device. Some devices ship with a wired headset that features transport control buttons, so users can for instance conveniently pause and restart music playback, directly from the headset.

But a user might use one application for music listening, and another for listening to podcasts, both of which should be controlled by the headset remote control.

If your media playback application creates a media playback service, just like Music, that responds to the media button events, how will the user know where those events are going to? Music, or your new application?

In this article, we�ll see how to handle this properly in Android 2.2. We�ll first see how to set up intents to receive �MEDIA_BUTTON� intents. We�ll then describe how your application can appropriately become the preferred media button responder in Android 2.2. Since this feature relies on a new API, we�ll revisit the use of reflection to prepare your app to take advantage of Android 2.2, without restricting it to API level 8 (Android 2.2).

An example of the handling of media button intents

In our AndroidManifest.xml for this package we declare the class RemoteControlReceiver to receive MEDIA_BUTTON intents:

<receiver android:name="RemoteControlReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>

Our class to handle those intents can look something like this:

public class RemoteControlReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
/* handle media button intent here by reading contents */
/* of EXTRA_KEY_EVENT to know which key was pressed */
}
}
}

In a media playback application, this is used to react to headset button presses when your activity doesn�t have the focus. For when it does, we override the Activity.onKeyDown() or onKeyUp() methods for the user interface to trap the headset button-related events.

However, this is problematic in the scenario we mentioned earlier. When the user presses �play�, what application should start playing? The Music application? The user�s preferred podcast application?

Becoming the �preferred� media button responder

In Android 2.2, we are introducing two new methods in android.media.AudioManager to declare your intention to become the �preferred� component to receive media button events: registerMediaButtonEventReceiver() and its counterpart, unregisterMediaButtonEventReceiver(). Once the registration call is placed, the designated component will exclusively receive the ACTION_MEDIA_BUTTON intent just as in the example above.

In the activity below were are creating an instance of AudioManager with which we will register our component. We therefore create a ComponentName instance that references our intended media button event responder.

public class MyMediaPlaybackActivity extends Activity {
private AudioManager mAudioManager;
private ComponentName mRemoteControlResponder;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAudioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
mRemoteControlResponder = new ComponentName(getPackageName(),
RemoteControlReceiver.class.getName());
}

The system handles the media button registration requests in a �last one wins� manner. This means we need to select where it makes sense for the user to make this request. In a media playback application, proper uses of the registration are for instance:

  • when the UI is displayed: the user is interacting with that application, so (s)he expects it to be the one that will respond to the remote control,


  • when content starts playing (e.g. content finished downloading, or another application caused your service to play content)


Registering is here performed for instance when our UI comes to the foreground:

    @Override
public void onResume() {
super.onResume();
mAudioManager.registerMediaButtonEventReceiver(
mRemoteControlResponder);
}

If we had previously registered our receiver, registering it again will push it up the stack, and doesn�t cause any duplicate registration.

Additionally, it may make sense for your registered component not to be called when your service or application is destroyed (as illustrated below), or under conditions that are specific to your application. For instance, in an application that reads to the user her/his appointments of the day, it could unregister when it�s done speaking the calendar entries of the day.

    @Override
public void onDestroy() {
super.onDestroy();
mAudioManager.unregisterMediaButtonEventReceiver(
mRemoteControlResponder);
}

After �unregistering�, the previous component that requested to receive the media button intents will once again receive them.

Preparing your code for Android 2.2 without restricting it to Android 2.2

While you may appreciate the benefit this new API offers to the users, you might not want to restrict your application to devices that support this feature. Andy McFadden shows us how to use reflection to take advantage of features that are not available on all devices. Let�s use what we learned then to enable your application to use the new media button mechanism when it runs on devices that support this feature.

First we declare in our Activity the two new methods we have used previously for the registration mechanism:

    private static Method mRegisterMediaButtonEventReceiver;
private static Method mUnregisterMediaButtonEventReceiver;

We then add a method that will use reflection on the android.media.AudioManager class to find the two methods when the feature is supported:

private static void initializeRemoteControlRegistrationMethods() {
try {
if (mRegisterMediaButtonEventReceiver == null) {
mRegisterMediaButtonEventReceiver = AudioManager.class.getMethod(
"registerMediaButtonEventReceiver",
new Class[] { ComponentName.class } );
}
if (mUnregisterMediaButtonEventReceiver == null) {
mUnregisterMediaButtonEventReceiver = AudioManager.class.getMethod(
"unregisterMediaButtonEventReceiver",
new Class[] { ComponentName.class } );
}
/* success, this device will take advantage of better remote */
/* control event handling */
} catch (NoSuchMethodException nsme) {
/* failure, still using the legacy behavior, but this app */
/* is future-proof! */
}
}

The method fields will need to be initialized when our Activity class is loaded:

    static {
initializeRemoteControlRegistrationMethods();
}

We�re almost done. Our code will be easier to read and maintain if we wrap the use of our methods initialized through reflection by the following. Note in bold the actual method invocation on our AudioManager instance:

    private void registerRemoteControl() {
try {
if (mRegisterMediaButtonEventReceiver == null) {
return;
}
mRegisterMediaButtonEventReceiver.invoke(mAudioManager,
mRemoteControlResponder);

} catch (InvocationTargetException ite) {
/* unpack original exception when possible */
Throwable cause = ite.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
} else {
/* unexpected checked exception; wrap and re-throw */
throw new RuntimeException(ite);
}
} catch (IllegalAccessException ie) {
Log.e(�MyApp�, "unexpected " + ie);
}
}

private void unregisterRemoteControl() {
try {
if (mUnregisterMediaButtonEventReceiver == null) {
return;
}
mUnregisterMediaButtonEventReceiver.invoke(mAudioManager,
mRemoteControlResponder);

} catch (InvocationTargetException ite) {
/* unpack original exception when possible */
Throwable cause = ite.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
} else {
/* unexpected checked exception; wrap and re-throw */
throw new RuntimeException(ite);
}
} catch (IllegalAccessException ie) {
System.err.println("unexpected " + ie);
}
}

We are now ready to use our two new methods, registerRemoteControl() and unregisterRemoteControl() in a project that runs on devices supporting API level 1, while still taking advantage of the features found in devices running Android 2.2.

Monday, May 31, 2010

On Android Compatibility

[This post is by Dan Morrill, Open Source & Compatibility Program Manager. � Tim Bray]

At Google I/O 2010, we announced that there are over 60 Android models now, selling 100,000 units a day. When I wear my open-source hat, this is exciting: every day the equivalent of the entire population of my old home city starts using open-source software, possibly for the first time. When I put on my hat for Android Compatibility, this is humbling: that�s a whole lotta phones that can all share the same apps.

Another thing we launched at Google I/O was an upgraded and expanded source.android.com. The new version has refreshed info on the Android Open-Source Project, and some new tips and tools for device manufacturers � useful if you�re an OEM. However, it also has details on the Android compatibility program, now. This is also aimed mostly at OEMs, but Tim Bray suggested that developers might be interested in a peek at how we keep those 100,000 devices marching to the same beat, every day. So here I am, back on the blog.

The F-word, or, Remember remember the fifth of November

I remember sitting with my colleagues in a conference room in Building 44 on November 5, 2007, listening to Andy Rubin and Eric Schmidt announce Android to the world. I remember a lot of the press stories, too. For instance, Android was �just words on paper� which was especially entertaining since I knew we were getting ready to launch the first early-look SDK a mere week later.

Another meme I remember is... yes, �fragmentation�. Literally before the close of business on the same day we announced Android (4:46pm to be precise), I saw the first article about Android �fragmentation.� The first day wasn�t even over yet, and the press had already decided that Android would have a �fragmentation� problem.

The thing is, nobody ever defined �fragmentation� � or rather, everybody has a different definition. Some people use it to mean too many mobile operating systems; others to refer to optional APIs causing inconsistent platform implementations; still others use it to refer to �locked down� devices, or even to the existence of multiple versions of the software at the same time. I�ve even seen it used to refer to the existence of different UI skins. Most of these definitions don�t even have any impact on whether apps can run!

Because it means everything, it actually means nothing, so the term is useless. Stories on �fragmentation� are dramatic and they drive traffic to pundits� blogs, but they have little to do with reality. �Fragmentation� is a bogeyman, a red herring, a story you tell to frighten junior developers. Yawn.

Compatibility

Now, that�s not to say that there aren�t real challenges in making sure that Android devices are compatible with each other, or that there aren�t very real concerns that keep app developers awake at night. There definitely are, and I spend a great deal of time indeed thinking about them and addressing them. The trick is to define them clearly.

We define �Android compatibility� to be the ability of a device to properly run apps written with the Android SDK. This is a deceptively simple way to frame it, because there are a number of things that can go wrong. Here are a few:

  • Bugs - devices might simply have bugs, such as a buggy Bluetooth driver or an incorrectly implemented GPS API.


  • Missing components - devices might omit hardware (such as a camera) that apps expect, and attempt to �fake� or stub out the corresponding API.


  • Added or altered APIs - devices might add or alter APIs that aren�t part of standard Android. Done correctly this is innovation; done poorly and it�s �embrace and extend�.


Each of these is an example of something that can make an app not run properly on a device. They might run, but they won�t run properly. These are the things that I spend my time preventing.

How It Works

As stewards of the platform we realize that it�s vital to allow only compatible devices to participate in the Android ecosystem. So, we make compatibility a strict prerequisite for access to Android Market and the right to use the Android name. This means that developers can rely on the fact that Android Market � the keystone of the Android ecosystem � will only allow their apps to run on compatible devices. It�s pretty self-evident that a single app ecosystem is better than many small ones, so OEMs are generally pretty motivated to ship compatible devices.

But motivation alone doesn�t get us very far without tools to actually ensure compatibility, which is where the Android compatibility program comes in. This program is like a stool with three legs: the Android source code, the Compatibility Definition Document, and the Compatibility Test Suite.

It all starts with the Android source code. Android is not a specification, or a distribution in the traditional Linux sense. It�s not a collection of replaceable components. Android is a chunk of software that you port to a device. For the most part, Android devices are running the same code. The fact that all Android devices run the same core Android code goes a long way toward making sure those devices all work the same way.

However, this doesn�t solve the problems of missing components or altered APIs, because the source code can always be tweaked. This is where the Compatibility Definition Document (or CDD) comes in. The CDD defines in gory detail exactly what is expected of Android devices. It clearly states, for example, that devices may not omit most components, and that the official Android APIs may not be altered. In a nutshell, the CDD exists to remove ambiguities around what�s required of an Android device.

Of course, none of that overcomes the simple reality of human error � bugs. This is where the Compatibility Test Suite comes in. The CTS is a collection of more than 20,000 test cases that check Android device implementations for known issues. Device makers run the CTS on their devices throughout the development process, and use it to identify and fix bugs early. This helps ensure that the builds they finally ship are as bug-free as possible.

Keeping Up with the Times

We�ve been operating this compatibility process with our OEM partners for over a year now, and it�s largely responsible for those 60+ device models being interoperable. However no process is ever perfect and no test suite is ever 100% comprehensive, and sometimes bugs get through. What happens then?

Well, we have great relationships with our OEMs, and like I said, they�re motivated to be compatible. Whenever we hear about a serious bug affecting apps, we report it to our partners, and they typically prepare a bugfix release and get it out to end users. We will also typically add a test case to the CTS to catch that problem for future devices. It�s an ongoing process, but generally our partners are as interested as we are in the user experience of the devices they sell.

The mobile industry today is �very exciting�, which is code for �changes painfully fast�. We believe that the only way Android will be a success is to keep up with that change, and ultimately drive that change. This means that over time, the CDD will also change. We�ll add new text to handle problem cases we encounter, and the actual requirements will change to accommodate the innovations happening in the field. For example, in the 2.1/Eclair CDD, we tweaked the CDD slightly to make telephony optional, which allows Android to ship compatibly on non-phone handheld devices. Whenever we do this, of course, we�ll make corresponding changes to the Android APIs and Android Market to make sure that your apps are protected from ill effects.

On a somewhat related note, a lot of ink has been spilled on the fact that there are multiple versions of Android out there in users� hands at the same time. While it�s true that devices without the latest software can�t run some of the latest apps, Android is 100% forward compatible � apps written properly for older versions also run on the newest versions. The choice is in app developers� hands as to whether they want to live on the bleeding edge for the flashiest features, or stay on older versions for the largest possible audience. And in the long term, as the mobile industry gets more accustomed to the idea of upgradeable phone software, more and more devices will be be upgraded.

What It Means for You

All that is great � but what does it mean for developers? Well, we put together a page in the SDK Documentation to explain this, so you should take a look there. But really it boils down to this:

  1. As a developer, you simply decide what features your app requires, and list them in your app�s AndroidManifest.xml.


  2. The Android compatibility program ensures that only compatible devices have access to Android Market.


  3. Android Market makes sure your app is only visible to those devices where it will run correctly, by filtering your app from devices which don�t have the features you listed.


That�s all!

There almost certainly will be devices that have access to Android Market that probably weren�t quite what you had in mind when you wrote your app. But this is a very good thing � it increases the size of the potential audience for your app. As long as you accurately list your app�s requirements, we�ll do the rest and make sure that your app won�t be accessible to a device where it won�t run properly. After all, we�re app developers ourselves, and we know how painful it is to deal with users upset about an app not working on a device it wasn�t designed for.

Now, that�s not to say that we think our current solution is perfect � no solution is. But we�re continuously working on improvements to the SDK tools and Android Market to make your life as an Android developer even easier. Keep an eye on this blog and on the Android Market itself for the latest info.

Thanks for reading, and happy coding!

Thursday, May 27, 2010

Android Cloud To Device Messaging

[This post is by Wei Huang, who helped implement this feature. � Tim Bray]

In the just-launched Android 2.2, we�ve added a new service to help developers send data from servers to their applications on Android phones. Android Cloud to Device Messaging (C2DM) makes it easier for mobile applications to sync data with servers.

Most of the useful applications on your mobile phone use the Internet to keep users connected. Traditionally, many apps use polling to fetch data periodically. POP mail clients, for example, connect to the server every 15 minutes or so. Polling is fairly easy to implement, and works well in many situations. It�s tricky, though, to select the frequency: Poll too often, you may not see any new data, and create unnecessary stress on the server and network. Poll too rarely and the data on the device may become stale. Polling is especially problematic on mobile devices, because it consumes precious network bandwidth and battery life.

Having the server push messages to the client asynchronously may be a superior choice for getting the latest data to your applications, resulting in fresher data and more efficient use of the network and your battery. However, it�s also tricky to implement a good push solution, and it isn�t free as there is some overhead in maintaining the required connection. On a mobile device like an Android phone, implementing applications to receive these messages is tricky; you have to worry about patchy network coverage and zombie connections created when the wireless carrier�s routers time out connections that are idle for too long.

Many of the Google applications on Android already use push to keep their data fresh, for example Gmail, Contacts, and Calendar. Starting with Android 2.2, C2DM allows third-party developers to use the same service the Google apps do.

Here are a few basic things to know about C2DM:

  • It requires Android 2.2; C2DM uses Google services which are present on any device running the Android Market.


  • It uses existing connections for Google services. This requires the users to sign into their Google account on Android.


  • It allows 3rd party servers to send lightweight data messages to their apps. The C2DM service is not designed for pushing a lot of user content; rather it should be used like a �tickle�, to tell the app that there is new data on the server, so the app can fetch it.


  • An application doesn�t need to be running to receive data messages. The system will wake up the app via an Intent broadcast when the the data message arrives, so long as the app is set up with the proper Intent Receiver and permissions.


  • No user interface is required for receiving the data messages. The app can post a notification (or display other UI) if it desires.


It�s easy to use the C2DM API. Here is how it works:

  • To enable C2DM, an application on the device registers with Google and get a registration ID, and sends the ID to its server.


  • When the server needs to push a message to the app on the device, it posts the message via HTTP to Google�s C2DM servers.


  • The C2DM servers route the message to the device, and an Intent broadcast is sent to the app.


  • The app is woken up to process the message in its Intent Receiver.


  • The app can unregister with C2DM when the user no longer wants messages to be pushed to it.


That�s about it! All you need is a server that knows to talk HTTP, and an Android app that knows how to use the Intent APIs. Below are some code samples:

// Use the Intent API to get a registration ID
// Registration ID is compartmentalized per app/device
Intent regIntent = new Intent(
"com.google.android.c2dm.intent.REGISTER");
// Identify your app
regIntent.putExtra("app",
PendingIntent.getBroadcast(this /* your activity */,
0, new Intent(), 0);
// Identify role account server will use to send
regIntent.putExtra("sender", emailOfSender);
// Start the registration process
startService(regIntent);

The registration ID will be delivered back to your app via an intent broadcast with the Intent Action com.google.android.c2dm.intent.REGISTRATION. Here is a code sample to receive the registration ID.

// Registration ID received via an Intent
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (�com.google.android.c2dm.intent.REGISTRATION�.equals(action)) {
handleRegistration(context, intent);
}
}

public void handleRegistration(Context context, Intent intent) {
String id = intent.getExtra(�registration_id�);
if ((intent.getExtra(�error�) != null) {
// Registration failed. Try again later, with backoff.
} else if (id != null) {
// Send the registration ID to the app�s server.
// Be sure to do this in a separate thread.
}
}

On the server side, your server needs to get a ClientLogin Auth token in order to talk to the C2DM servers. When it wants to push a message to the device, it can send an authenticated http POST with:

  • Authorization: GoogleLogin auth=<auth token>


  • URL encoded parameters including the registration id, the data key/value pairs, a �collapse key� used for overriding old messages with the same key on the Google C2DM servers, and a few other optional params.


When you use the C2DM service, you no longer need to worry about dealing with flaky mobile data connections, or when the user isn�t connected to the internet (i.e. Airplane mode). C2DM keeps the messages in the server store, and delivers them when the device comes back online. Basically, you can leave all the hard work of designing a robust push service to Google. Your application can take advantage of push infrastructure we�ve already built and tested, and stay more connected to the internet. Best of all, you won�t ruin the users� battery life.

Information about how to build C2DM enabled applications on Android is online at the code lab, and more will be coming as we approach general release.