31 Jul 2010

feedAndroid Forums

Root Motorolla Cliq on a Mac

Can someone please help? I can't find anything in the search. Is there a way to root this on a Mac? Thanks

31 Jul 2010 3:08pm GMT

Apad video chat

Hi, does anyone know of a good program to use for Apad android video chat? I've heard of Movicha but can't find the file to download. Does Skype lite...

31 Jul 2010 1:35pm GMT

[HELP]Hero can not boot!

Hi all,I am a new user of android. Last night, I installed the wrong "radio" to my phone. After that,The phone can not boot, when I press the POWER...

31 Jul 2010 1:25pm GMT

is there a gnome-do like application for android?

In linux, for gnome desktop environment, there is an application to quick launch desired application. This saves much time one spends to browse...

31 Jul 2010 1:20pm GMT

Android 3.0 expectations

What would we like to see in Android 3.0 , I know we are just wrapping our head around 2.2 froyo, but with 3.0 slated from october release, what are...

31 Jul 2010 12:43pm GMT

Trivia app

Hi, Im the developer of the trivia app test. Dumbometer. Please try this game and let us know your reviews. Dumbometer is a simple...

31 Jul 2010 11:53am GMT

[YouTube]Enjoy Every Single day of Life with Dolphin Add-ons

I had found the wonderful video since I subscrible dolphinbrowser channel in YouTube. I WANT SHARE WIYH YOU. ...

31 Jul 2010 11:53am GMT

Dumbometer

Dumbometer is a simple addictive test based on months of research, study and analysis of human brain response time. It is the mother of all quiz and...

31 Jul 2010 11:39am GMT

No privacy on software

Hi , Currently Google said that there is not privacy on android phone marketplace. And google admits that many developers have asked to speak to...

31 Jul 2010 9:30am GMT

Help pls!!!

I bought an HTC Hero from carphonewarehouse with orange contract. The firmware version is 1.5 and the ROM version is 2.73.405.5. I heard that HTC has...

31 Jul 2010 8:53am GMT

feedGoogle Android News Android Forums

LogMeIn for Android Giveaway Contest Closed

Well, guys, the contest for winning LogMeIn's Ignition app for Android has now come to a close. Thanks to all those that submitted responses in our forum thread, and to all those that responded incorrectly… sorry, folks, but your entries wont be considered. Lots of entries came in on the app! Looks like everyone is excited [...]

For more information on Android and the current Android mobile phones, check out our Android Guides

LogMeIn for Android Giveaway Contest Closed

31 Jul 2010 5:01am GMT

Motorola releases Droid X antenna, quality FAQs

Remember Motorola's release of the "No Jacket Required" ad, taking a stab at Apple's Antenna deisgn problem? Well now, they have released a FAQ (Frequently Asked Questions) section to their site, along with specifications on their antenna design. A few of the highlights in the FAQ: Q: When Motorola is designing a new phone, how does [...]

For more information on Android and the current Android mobile phones, check out our Android Guides

Motorola releases Droid X antenna, quality FAQs

31 Jul 2010 2:58am GMT

Froyo coming out to unlocked European HTC Desire handests

Who isn't getting Froyo? Well, it looks like unlocked HTC Desire's in Europe are no longer in the ignore list, as there is now an official press release that states that they will be getting the long awaited 2.2 update. The upgrade is set to include: 720P Video Recording HTC Sense Updates App Share Improved Keyboard HTC's new phone features Sync features [...]

For more information on Android and the current Android mobile phones, check out our Android Guides

Froyo coming out to unlocked European HTC Desire handests

31 Jul 2010 2:37am GMT

McAfee acquires tenCube, makers of WaveSecure

Awhile back, we did a review and giveaway of WaveSecure, an app that allows you to remotelyloss and theft of your Android device. Now, tenCube, maker of WaveSecure, is set to be acquired by McAfee. According to the official announcement on McAfee's website, they say that the acquirement is set to: Further establish itself as the leader in mobile [...]

For more information on Android and the current Android mobile phones, check out our Android Guides

McAfee acquires tenCube, makers of WaveSecure

31 Jul 2010 1:39am GMT

30 Jul 2010

feedGoogle Android News Android Forums

Motorola Droid to receive Android 2.2 next week

According to an article over at engadget, they have received official word from Verizon that Froyo will be headed to the original Motorola Droid next week. It's entirely possible that this could be a hasty response to HTC's notice that the Evo will also be getting 2.2 (officially) on 8/3, but however it came about, [...]

For more information on Android and the current Android mobile phones, check out our Android Guides

Motorola Droid to receive Android 2.2 next week

30 Jul 2010 9:52pm GMT

Motorola M511 Flips into China

Adding to an impressive stable of phones and carriers, Motorola will be releasing the M511 (known as the FlipOut in Europe) onto Chinese carriers by the end of this month. In other words, today. The M511 will run Android 2.1 with MotoBlur, and will offer a full QWERY keyboard to perfectly complement its social network integration, which [...]

For more information on Android and the current Android mobile phones, check out our Android Guides

Motorola M511 Flips into China

30 Jul 2010 8:19pm GMT

Droid X source code released

Is today source code release day? Well not really but it sure seems like it considering Samsung released their Captivate source code today as well. Truth be told, Motorola actually released the source code two days ago, just nobody noticed, lol. Anyway, if you'd like to crawl through what makes the Droid X tick, then hit [...]

For more information on Android and the current Android mobile phones, check out our Android Guides

Droid X source code released

30 Jul 2010 8:03pm GMT

Samsung Captivate Code Goes Open Source

Developers anxious for the full open source coding of the Samsung Captivate were joyous today as they received the following message in their inboxes: Dear _____________, You can download the source code of SGH-I897 on this site in Mobile Category, SGH-I897 model. Thank you. Short, sweet, and to the point. This is excellent news for those intrepid ROM developers already [...]

For more information on Android and the current Android mobile phones, check out our Android Guides

Samsung Captivate Code Goes Open Source

30 Jul 2010 8:01pm GMT

Sony Ericsson Xperia X10 Mini & Mini Pro get rooted

Another one bites the dust! The Sony Ericsson Xperia X10 Mini & Mini Pro have been rooted. We'll skip right to the goodies and give you the instructions: You will need ADB from Android SDK and USB drivers for X10 mini (part of Sony Ericsson PC Companion). Enable USB debugging on the phone in Settings>Applications>Development and connect the phone [...]

For more information on Android and the current Android mobile phones, check out our Android Guides

Sony Ericsson Xperia X10 Mini & Mini Pro get rooted

30 Jul 2010 7:47pm GMT

Huawei releases 2 new Android phones

Despite some pretty harsh claims against the company, Huawei has moved forward with it's business, andis now officially launching two Android phones. The U8300: and the U8500: There is very little known about the specification of these two devices, other than that they are both 3G enabled and have Qualcomm chips.. Independantly, the 8300 is capable of 3G [...]

For more information on Android and the current Android mobile phones, check out our Android Guides

Huawei releases 2 new Android phones

30 Jul 2010 7:32pm GMT

27 Jul 2010

feedAndroid Developers Blog

Licensing Service Technology Highlights

We've just announced the introduction of a licensing server for Android Market. This should address one of the concerns we've heard repeatedly from the Android developer community.

The impact and intent, as outlined in the announcement, are straightforward. If you want to enable your app to use the licensing server, there's no substitute for reading the authoritative documentation: Licensing Your Applications. Here are some technical highlights.

We think this is a major improvement over the copy-protection option we've offered up to this point, and look forward to feedback from developers.

27 Jul 2010 9:00pm GMT

Licensing Service For Android Applications

[This post is by Eric Chu, Android Developer Ecosystem. - Tim Bray]

In my conversations with Android developers, I often hear that you'd like better protection against unauthorized use of your applications. So today, I'm pleased to announce the release of a licensing service for applications in Android Market.

This simple and free service provides a secure mechanism to manage access to all Android Market paid applications targeting Android 1.5 or higher. At run time, with the inclusion of a set of libraries provided by us, your application can query the Android Market licensing server to determine the license status of your users. It returns information on whether your users are authorized to use the app based on stored sales records.

This licensing service operating real time over the network provides more flexibility in choosing license-enforcement strategies, and a more secure approach in protecting your applications from unauthorized use, than copy protection.

The licensing service is available now; our plan is for it to replace the current Android Market copy-protection mechanism over the next few months. I encourage you to check out the Licensing Your Applications section of our Developer Guide and the Android Market Help Center to learn how you can take advantage of this new service immediately.

27 Jul 2010 5:00pm GMT

24 Jul 2010

feedAndroid Developers Blog

Adjustment to Market Legals

Please note that we have updated the Android Market Developer Distribution Agreement (DDA). This is in preparation for some work we're doing on introducing new payment options, which we think developers will like.

In the spirit of transparency, we wanted to highlight the changes:

These new terms apply immediately to anyone joining Android Market as a new publisher. Existing publishers have been notified of this change via email; they have up to 30 days to sign into the Android Market developer console to accept the new terms.

24 Jul 2010 12:24am GMT

19 Jul 2010

feedAndroid Developers Blog

Multithreading For Performance

[This post is by Gilles Debunne, an engineer in the Android group who loves to get multitasked. - Tim Bray]

A good practice in creating responsive applications is to make sure your main UI thread does the minimum amount of work. Any potentially long task that may hang your application should be handled in a different thread. Typical examples of such tasks are network operations, which involve unpredictable delays. Users will tolerate some pauses, especially if you provide feedback that something is in progress, but a frozen application gives them no clue.

In this article, we will create a simple image downloader that illustrates this pattern. We will populate a ListView with thumbnail images downloaded from the internet. Creating an asynchronous task that downloads in the background will keep our application fast.

An Image downloader

Downloading an image from the web is fairly simple, using the HTTP-related classes provided by the framework. Here is a possible implementation:

static Bitmap downloadBitmap(String url) {
    final AndroidHttpClient client = AndroidHttpClient.newInstance("Android");
    final HttpGet getRequest = new HttpGet(url);

    try {
        HttpResponse response = client.execute(getRequest);
        final int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode != HttpStatus.SC_OK) { 
            Log.w("ImageDownloader", "Error " + statusCode + " while retrieving bitmap from " + url); 
            return null;
        }
        
        final HttpEntity entity = response.getEntity();
        if (entity != null) {
            InputStream inputStream = null;
            try {
                inputStream = entity.getContent(); 
                final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                return bitmap;
            } finally {
                if (inputStream != null) {
                    inputStream.close();  
                }
                entity.consumeContent();
            }
        }
    } catch (Exception e) {
        // Could provide a more explicit error message for IOException or IllegalStateException
        getRequest.abort();
        Log.w("ImageDownloader", "Error while retrieving bitmap from " + url, e.toString());
    } finally {
        if (client != null) {
            client.close();
        }
    }
    return null;
}

A client and an HTTP request are created. If the request succeeds, the response entity stream containing the image is decoded to create the resulting Bitmap. Your applications' manifest must ask for the INTERNET to make this possible.

Note: a bug in the previous versions of BitmapFactory.decodeStream may prevent this code from working over a slow connection. Decode a new FlushedInputStream(inputStream) instead to fix the problem. Here is the implementation of this helper class:

static class FlushedInputStream extends FilterInputStream {
    public FlushedInputStream(InputStream inputStream) {
        super(inputStream);
    }

    @Override
    public long skip(long n) throws IOException {
        long totalBytesSkipped = 0L;
        while (totalBytesSkipped < n) {
            long bytesSkipped = in.skip(n - totalBytesSkipped);
            if (bytesSkipped == 0L) {
                  int byte = read();
                  if (byte < 0) {
                      break;  // we reached EOF
                  } else {
                      bytesSkipped = 1; // we read one byte
                  }
           }
            totalBytesSkipped += bytesSkipped;
        }
        return totalBytesSkipped;
    }
}

This ensures that skip() actually skips the provided number of bytes, unless we reach the end of file.

If you were to directly use this method in your ListAdapter's getView method, the resulting scrolling would be unpleasantly jaggy. Each display of a new view has to wait for an image download, which prevents smooth scrolling.

Indeed, this is such a bad idea that the AndroidHttpClient does not allow itself to be started from the main thread. The above code will display "This thread forbids HTTP requests" error messages instead. Use the DefaultHttpClient instead if you really want to shoot yourself in the foot.

Introducing asynchronous tasks

The AsyncTask class provides one of the simplest ways to fire off a new task from the UI thread. Let's create an ImageDownloader class which will be in charge of creating these tasks. It will provide a download method which will assign an image downloaded from its URL to an ImageView:

public class ImageDownloader {

    public void download(String url, ImageView imageView) {
            BitmapDownloaderTask task = new BitmapDownloaderTask(imageView);
            task.execute(url);
        }
    }

    /* class BitmapDownloaderTask, see below */
}

The BitmapDownloaderTask is the AsyncTask which will actually download the image. It is started using execute, which returns immediately hence making this method really fast which is the whole purpose since it will be called from the UI thread. Here is the implementation of this class:

class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
    private String url;
    private final WeakReference<ImageView> imageViewReference;

    public BitmapDownloaderTask(ImageView imageView) {
        imageViewReference = new WeakReference<ImageView>(imageView);
    }

    @Override
    // Actual download method, run in the task thread
    protected Bitmap doInBackground(String... params) {
         // params comes from the execute() call: params[0] is the url.
         return downloadBitmap(params[0]);
    }

    @Override
    // Once the image is downloaded, associates it to the imageView
    protected void onPostExecute(Bitmap bitmap) {
        if (isCancelled()) {
            bitmap = null;
        }

        if (imageViewReference != null) {
            ImageView imageView = imageViewReference.get();
            if (imageView != null) {
                imageView.setImageBitmap(bitmap);
            }
        }
    }
}

The doInBackground method is the one which is actually run in its own process by the task. It simply uses the downloadBitmap method we implemented at the beginning of this article.

onPostExecute is run in the calling UI thread when the task is finished. It takes the resulting Bitmap as a parameter, which is simply associated with the imageView that was provided to download and was stored in the BitmapDownloaderTask. Note that this ImageView is stored as a WeakReference, so that a download in progress does not prevent a killed activity's ImageView from being garbage collected. This explains why we have to check that both the weak reference and the imageView are not null (i.e. were not collected) before using them in onPostExecute.

This simplified example illustrates the use on an AsyncTask, and if you try it, you'll see that these few lines of code actually dramatically improved the performance of the ListView which now scrolls smoothly. Read Painless threading for more details on AsyncTasks.

However, a ListView-specific behavior reveals a problem with our current implementation. Indeed, for memory efficiency reasons, ListView recycles the views that are displayed when the user scrolls. If one flings the list, a given ImageView object will be used many times. Each time it is displayed the ImageView correctly triggers an image download task, which will eventually change its image. So where is the problem? As with most parallel applications, the key issue is in the ordering. In our case, there's no guarantee that the download tasks will finish in the order in which they were started. The result is that the image finally displayed in the list may come from a previous item, which simply happened to have taken longer to download. This is not an issue if the images you download are bound once and for all to given ImageViews, but let's fix it for the common case where they are used in a list.

Handling concurrency

To solve this issue, we should remember the order of the downloads, so that the last started one is the one that will effectively be displayed. It is indeed sufficient for each ImageView to remember its last download. We will add this extra information in the ImageView using a dedicated Drawable subclass, which will be temporarily bind to the ImageView while the download is in progress. Here is the code of our DownloadedDrawable class:

static class DownloadedDrawable extends ColorDrawable {
    private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference;

    public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) {
        super(Color.BLACK);
        bitmapDownloaderTaskReference =
            new WeakReference<BitmapDownloaderTask>(bitmapDownloaderTask);
    }

    public BitmapDownloaderTask getBitmapDownloaderTask() {
        return bitmapDownloaderTaskReference.get();
    }
}

This implementation is backed by a ColorDrawable, which will result in the ImageView displaying a black background while its download is in progress. One could use a "download in progress" image instead, which would provide feedback to the user. Once again, note the use of a WeakReference to limit object dependencies.

Let's change our code to take this new class into account. First, the download method will now create an instance of this class and associate it with the imageView:

public void download(String url, ImageView imageView) {
     if (cancelPotentialDownload(url, imageView)) {
         BitmapDownloaderTask task = new BitmapDownloaderTask(imageView);
         DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task);
         imageView.setImageDrawable(downloadedDrawable);
         task.execute(url, cookie);
     }
}

The cancelPotentialDownload method will stop the possible download in progress on this imageView since a new one is about to start. Note that this is not sufficient to guarantee that the newest download is always displayed, since the task may be finished, waiting in its onPostExecute method, which may still may be executed after the one of this new download.

private static boolean cancelPotentialDownload(String url, ImageView imageView) {
    BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);

    if (bitmapDownloaderTask != null) {
        String bitmapUrl = bitmapDownloaderTask.url;
        if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {
            bitmapDownloaderTask.cancel(true);
        } else {
            // The same URL is already being downloaded.
            return false;
        }
    }
    return true;
}

cancelPotentialDownload uses the cancel method of the AsyncTask class to stop the download in progress. It returns true most of the time, so that the download can be started in download. The only reason we don't want this to happen is when a download is already in progress on the same URL in which case we let it continue. Note that with this implementation, if an ImageView is garbage collected, its associated download is not stopped. A RecyclerListener might be used for that.

This method uses a helper getBitmapDownloaderTask function, which is pretty straigthforward:

private static BitmapDownloaderTask getBitmapDownloaderTask(ImageView imageView) {
    if (imageView != null) {
        Drawable drawable = imageView.getDrawable();
        if (drawable instanceof DownloadedDrawable) {
            DownloadedDrawable downloadedDrawable = (DownloadedDrawable)drawable;
            return downloadedDrawable.getBitmapDownloaderTask();
        }
    }
    return null;
}

Finally, onPostExecute has to be modified so that it will bind the Bitmap only if this ImageView is still associated with this download process:

if (imageViewReference != null) {
    ImageView imageView = imageViewReference.get();
    BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
    // Change bitmap only if this process is still associated with it
    if (this == bitmapDownloaderTask) {
        imageView.setImageBitmap(bitmap);
    }
}

With these modifications, our ImageDownloader class provides the basic services we expect from it. Feel free to use it or the asynchronous pattern it illustrates in your applications to ensure their responsiveness.

Demo

The source code of this article is available online on Google Code. You can switch between and compare the three different implementations that are described in this article (no asynchronous task, no bitmap to task association and the final correct version). Note that the cache size has been limited to 10 images to better demonstrate the issues.

Future work

This code was simplified to focus on its parallel aspects and many useful features are missing from our implementation. The ImageDownloader class would first clearly benefit from a cache, especially if it is used in conjuction with a ListView, which will probably display the same image many times as the user scrolls back and forth. This can easily be implemented using a Least Recently Used cache backed by a LinkedHashMap of URL to Bitmap SoftReferences. More involved cache mechanism could also rely on a local disk storage of the image. Thumbnails creation and image resizing could also be added if needed.

Download errors and time-outs are correctly handled by our implementation, which will return a null Bitmap in these case. One may want to display an error image instead.

Our HTTP request is pretty simple. One may want to add parameters or cookies to the request as required by certain web sites.

The AsyncTask class used in this article is a really convenient and easy way to defer some work from the UI thread. You may want to use the Handler class to have a finer control on what you do, such as controlling the total number of download threads which are running in parallel in this case.

19 Jul 2010 6:41pm GMT

16 Jul 2010

feedAndroid Developers Blog

Market Statistics Adjustments

If you look closely today, you'll notice that some per-app Android Market statistics have lower values; not big differences, but noticeable in a few cases. We discovered last week that, starting in early June, certain events had been double-counted: installs, uninstalls, impressions, and so on. The most obvious symptom was (for paid apps) a discrepancy between the number of installs and the number of reported sales through Checkout.

The underlying problem has been corrected and following data repair, the reported statistics should now be accurate. Our apologies for the glitch.

16 Jul 2010 5:52am GMT

Android Market Welcomes Korea!

As of today, Android Market is open for business to application buyers in the Republic of Korea. We hope that this will make the outstanding Android devices now available in that nation even more useful and fun. We welcome the people of Korea, acknowledged everywhere as one of the world's most-wired societies, to the world of Android.

16 Jul 2010 4:33am GMT

12 Jul 2010

feedAndroid Developers Blog

How to have your (Cup)cake and eat it too

[This post is by Adam Powell, his second touchy-feely outing in just a few weeks. I asked him to send me a better picture than we ran last time, and got this in response. Photo by our own Romain Guy. - Tim Bray]

Android developers concerned with targeting every last device with their apps are no doubt familiar with this chart:

On July 1, 2010 this was the breakdown of active devices running different versions of the Android platform. With all of the new platform features added to the Android SDK in each version, this chart has many developers shouting the F-word when they are forced to choose between integrating newer platform features and providing their app to the widest possible audience.

Savvy Android developers already know that these two options aren't really mutually exclusive, but that straddling between them can be painful. In this post I'm going to show you that it doesn't have to be that way.

Several weeks ago we took a look at how to handle multitouch on Android 2.0 (Eclair) and above, and by the end we had a simple demo app. That app uses features exclusive to Android 2.2 (Froyo) which as of this writing hasn't had a chance to reach many devices yet. In this post we're going to refactor that demo to run on devices all the way back to Android 1.5 (Cupcake). If you'd like to follow along, start off by grabbing the code in the trunk of the android-touchexample project on Google Code.

The problem manifests

The uses-sdk tag in your AndroidManifest.xml can specify both a minSdkVersion and a targetSdkVersion. You can use this to declare that while your app is prepared to run on an older version of the platform, it knows about newer versions. Your app can now build against newer SDKs. However, if your code accesses newer platform functionality directly you will probably see something like this in the system log of devices running an older version of Android:

E/dalvikvm(  792): Could not find method android.view.MotionEvent.getX, referenced from method com.example.android.touchexample.TouchExampleView.onTouchEvent
W/dalvikvm(  792): VFY: unable to resolve virtual method 17: Landroid/view/MotionEvent;.getX (I)F
W/dalvikvm(  792): VFY:  rejecting opcode 0x6e at 0x0006
W/dalvikvm(  792): VFY:  rejected Lcom/example/android/touchexample/TouchExampleView;.onTouchEvent (Landroid/view/MotionEvent;)Z
W/dalvikvm(  792): Verifier rejected class Lcom/example/android/touchexample/TouchExampleView;
D/AndroidRuntime(  792): Shutting down VM
W/dalvikvm(  792): threadid=3: thread exiting with uncaught exception (group=0x4000fe70)

We broke the contract of minSdkVersion, and here is the result. When we build our app against SDK 8 (Froyo) but declare minSdkVersion="3" (Cupcake) we promise the system that we know what we're doing and we won't try to access anything that doesn't exist. If we mess this up, we see the above, and our users see an ugly error message.

Cue a lot of frustrated users and one-star ratings on Market. We need a safe way of accessing newer platform functionality without making the verifier angry on older platform versions.

Stop and reflect

Many Android developers are already familiar with the practice of accomplishing this through reflection. Reflection lets your code interface with the runtime, detect when certain methods or classes are present, and invoke or instantiate them without touching them directly.

The prospect of querying each platform feature individually and conditionally invoking it using reflection isn't pretty. It's ugly. It's slow. It's cumbersome. Most of all, heavy use can turn your app's codebase into an unmaintainable mess. What if I said there is a way to write Android apps that target Android 1.5 (Cupcake) through 2.2 (Froyo) and beyond with a single codebase and no reflection at all?

Lazy Loading

Computer science researcher Bill Pugh published and popularized a method of writing singletons in Java that takes advantage of the laziness of ClassLoaders. Wikipedia explains his solution further. The code looks like this:

public class Singleton {
  // Private constructor prevents instantiation from other classes
  private Singleton() {}

  /**
   * SingletonHolder is loaded on the first execution of Singleton.getInstance() 
   * or the first access to SingletonHolder.INSTANCE, not before.
   */
  private static class SingletonHolder { 
    private static final Singleton INSTANCE = new Singleton();
  }

  public static Singleton getInstance() {
    return SingletonHolder.INSTANCE;
  }
}

There is a very important guaranteed behavior at work here explained by the comment above SingletonHolder. Java classes are loaded and initialized on first access - instantiating the class or accessing one of its static fields or methods for the first time. This is relevant to us because classes are verified by the VM when they are loaded, not before. We now have everything we need to write Android apps that span versions without reflection.

Designing for compatibility

As it turns out this is fairly simple to apply. You generally will want your app to degrade gracefully on older platform versions, dropping features or providing alternate functionality when the platform support isn't available. Since Android platform features are tied to the API level you have only one axis to consider when designing for compatibility.

In most cases this version support can be expressed as a simple class hierarchy. You can design your app to access version-sensitive functionality through a version-independent interface or abstract class. Subclasses of that interface intended to run on newer platform versions will support newer platform features, and subclasses intended for older versions might need to present alternate ways for your users to access app functionality.

Your app can use a factory method, abstract factory, or other object creation pattern to instantiate the proper subclass at runtime based on the information exposed by android.os.Build.VERSION. This last step insures that the system will never attempt to load a class it can't verify, preserving compatibility.

The principle in practice

At the beginning of this post I said that we are going to refactor the touch example app from Making Sense of Multitouch to be compatible from API level 3 (Cupcake) on through API level 8 (Froyo). In that post I pointed out that GestureDetectors can be a useful pattern for abstracting the processing of touch events. At the time I didn't realize how soon that statement would be put to the test. We can refactor the version-specific elements of the demo app's touch handling into an abstract GestureDetector.

Before we begin the real work, we need to change our manifest to declare that we support API level 3 devices with minSdkVersion in the uses-sdk tag. Keep in mind that we're still targeting SDK 8, both with targetSdkVersion in our manifest and in our project configuration. Our manifest now looks like this:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.example.android.touchexample"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".TouchExampleActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    <uses-sdk android:minSdkVersion="3" android:targetSdkVersion="8" />
</manifest>

Our TouchExampleView class isn't compatible with Android versions prior to Froyo thanks to its use of ScaleGestureDetector, and it isn't compatible with versions prior to Eclair thanks to its use of the newer MotionEvent methods that return multitouch data. We need to abstract that functionality out into classes that will not be loaded on versions of the platform that don't support it. To do this, we will create the abstract class VersionedGestureDetector.

The example app allows the user to perform two gestures, drag and scale. VersionedGestureDetector will therefore publish two events to an attached listener, onDrag and onScale. TouchExampleView will obtain a VersionedGestureDetector instance appropriate to the platform version, filter incoming touch events through it, and respond to the resulting onDrag and onScale events accordingly.

The first pass of VersionedGestureDetector looks like this:

public abstract class VersionedGestureDetector {
    OnGestureListener mListener;
    
    public abstract boolean onTouchEvent(MotionEvent ev);
    
    public interface OnGestureListener {
        public void onDrag(float dx, float dy);
        public void onScale(float scaleFactor);
    }
}

We'll start with the simplest functionality first, the VersionedGestureDetector for Cupcake. For simplicity's sake in this example we will implement each version as a private static inner class of VersionedGestureDetector. You can organize this however you please, of course, as long as you use the lazy loading technique shown above or some equivalent. Don't touch any class that directly accesses functionality not supported by your platform version.

private static class CupcakeDetector extends VersionedGestureDetector {
    float mLastTouchX;
    float mLastTouchY;

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN: {
            mLastTouchX = ev.getX();
            mLastTouchY = ev.getY();
            break;
        }
        case MotionEvent.ACTION_MOVE: {
            final float x = ev.getX();
            final float y = ev.getY();

            mListener.onDrag(x - mLastTouchX, y - mLastTouchY);
                
            mLastTouchX = x;
            mLastTouchY = y;
            break;
        }
        }
        return true;
    }
}

This simple implementation dispatches onDrag events whenever a pointer is dragged across the touchscreen. The values it passes are the X and Y distances traveled by the pointer.

In Eclair and later we will need to properly track pointer IDs during drags so that our draggable object doesn't jump around as extra pointers enter and leave the touchscreen. The base implementation of onTouchEvent in CupcakeDetector can handle drag events for us with a few tweaks. We'll add the methods getActiveX and getActiveY to fetch the appropriate touch coordinates and override them in EclairDetector to get the coordinates from the correct pointer:

private static class CupcakeDetector extends VersionedGestureDetector {
    float mLastTouchX;
    float mLastTouchY;
        
    float getActiveX(MotionEvent ev) {
        return ev.getX();
    }

    float getActiveY(MotionEvent ev) {
        return ev.getY();
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN: {
            mLastTouchX = getActiveX(ev);
            mLastTouchY = getActiveY(ev);
            break;
        }
        case MotionEvent.ACTION_MOVE: {
            final float x = getActiveX(ev);
            final float y = getActiveY(ev);

            mListener.onDrag(x - mLastTouchX, y - mLastTouchY);
                
            mLastTouchX = x;
            mLastTouchY = y;
            break;
        }
        }
        return true;
    }
}

And now EclairDetector, overriding the new getActiveX and getActiveY methods. Most of this code should be familiar from the original touch example:

private static class EclairDetector extends CupcakeDetector {
    private static final int INVALID_POINTER_ID = -1;
    private int mActivePointerId = INVALID_POINTER_ID;
    private int mActivePointerIndex = 0;

    @Override
    float getActiveX(MotionEvent ev) {
        return ev.getX(mActivePointerIndex);
    }

    @Override
    float getActiveY(MotionEvent ev) {
        return ev.getY(mActivePointerIndex);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        final int action = ev.getAction();
        switch (action & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
            mActivePointerId = ev.getPointerId(0);
            break;
        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_UP:
            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;
                mActivePointerId = ev.getPointerId(newPointerIndex);
                mLastTouchX = ev.getX(newPointerIndex);
                mLastTouchY = ev.getY(newPointerIndex);
            }
            break;
        }

        mActivePointerIndex = ev.findPointerIndex(mActivePointerId);
        return super.onTouchEvent(ev);
    }
}

EclairDetector calls super.onTouchEvent after determining the active pointer index and lets CupcakeDetector take care of dispatching the drag event. Supporting multiple platform versions doesn't have to mean code duplication.

Finally, let's add scale gesture support for Froyo devices that have ScaleGestureDetector. We'll need a couple more changes to CupcakeDetector first; we don't want to drag normally while scaling. Some devices have touchscreens that don't deal well with it, and we would want to handle it differently on devices that do anyway. We'll add a shouldDrag method to CupcakeDetector that we'll check before dispatching onDrag events.

The final CupcakeDetector:

private static class CupcakeDetector extends VersionedGestureDetector {
    float mLastTouchX;
    float mLastTouchY;
        
    float getActiveX(MotionEvent ev) {
        return ev.getX();
    }

    float getActiveY(MotionEvent ev) {
        return ev.getY();
    }
        
    boolean shouldDrag() {
        return true;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN: {
            mLastTouchX = getActiveX(ev);
            mLastTouchY = getActiveY(ev);
            break;
        }
        case MotionEvent.ACTION_MOVE: {
            final float x = getActiveX(ev);
            final float y = getActiveY(ev);
                
            if (shouldDrag()) {
                mListener.onDrag(x - mLastTouchX, y - mLastTouchY);
            }
                
            mLastTouchX = x;
            mLastTouchY = y;
            break;
        }
        }
        return true;
    }
}

EclairDetector remains unchanged. FroyoDetector is below. shouldDrag will return true as long as we do not have a scale gesture in progress:

private static class FroyoDetector extends EclairDetector {
    private ScaleGestureDetector mDetector;

    public FroyoDetector(Context context) {
        mDetector = new ScaleGestureDetector(context,
                new ScaleGestureDetector.SimpleOnScaleGestureListener() {
                    @Override public boolean onScale(ScaleGestureDetector detector) {
                        mListener.onScale(detector.getScaleFactor());
                        return true;
                    }
                });
    }

    @Override
    boolean shouldDrag() {
        return !mDetector.isInProgress();
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        mDetector.onTouchEvent(ev);
        return super.onTouchEvent(ev);
    }
}

Now that we have our detector implementations in order we need a way to create them. Let's add a factory method to VersionedGestureDetector:

public static VersionedGestureDetector newInstance(Context context,
        OnGestureListener listener) {
    final int sdkVersion = Integer.parseInt(Build.VERSION.SDK);
    VersionedGestureDetector detector = null;
    if (sdkVersion < Build.VERSION_CODES.ECLAIR) {
        detector = new CupcakeDetector();
    } else if (sdkVersion < Build.VERSION_CODES.FROYO) {
        detector = new EclairDetector();
    } else {
        detector = new FroyoDetector(context);
    }

    detector.mListener = listener;

    return detector;
}

Since we're targeting Cupcake, we don't have access to Build.VERSION.SDK_INT yet. We have to parse the now-deprecated Build.VERSION.SDK instead. But why is accessing Build.VERSION_CODES.ECLAIR and Build.VERSION_CODES.FROYO safe? As primitive static final int constants, these are inlined by the compiler at build time.

Our VersionedGestureDetector is ready. Now we just need to hook it up to TouchExampleView, which has become considerably shorter:

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

    private VersionedGestureDetector mDetector;
    private float mScaleFactor = 1.f;

    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());
        
        mDetector = VersionedGestureDetector.newInstance(context, new GestureCallback());
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        mDetector.onTouchEvent(ev);
        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 GestureCallback implements VersionedGestureDetector.OnGestureListener {
        public void onDrag(float dx, float dy) {
            mPosX += dx;
            mPosY += dy;
            invalidate();
        }

        public void onScale(float scaleFactor) {
            mScaleFactor *= scaleFactor;

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

            invalidate();
        }
    }
}

Wrapping up

We've now adapted the touch example app to work from Android 1.5 on through the latest and greatest, taking advantage of newer platform features as available without a single reflective call. The same principles shown here can apply to any new Android feature that you want to use while still allowing your app to run on older platform versions:

To see the final cross-version touch example app, check out the "cupcake" branch of the android-touchexample project on Google Code.

Extra Credit

In this example we didn't provide another way for pre-Froyo users to zoom since ScaleGestureDetector was only added as a public API for 2.2. For a real app we would want to offer some alternate affordance to users. Traditionally Android offers a set of small tappable zoom buttons along the bottom of the screen. The ZoomControls and ZoomButtonsController classes in the framework can help you present these controls to the user in a standard way. Implementing this is left as an exercise for the reader.

12 Jul 2010 10:29pm GMT

08 Jul 2010

feedAndroid Developers Blog

Apps on SD Card: The Details

[This post is by Suchi Amalapurapu, an engineer who worked on this feature. - Tim Bray]

Android 2.2 supports application installation on external storage devices like the SD card. This should give users room for many more apps, and will also benefit certain categories, like games, that need huge assets.

(Note that not all of the contents of an "SD-card-resident" APK are actually on the card; the dex files, private data directories, and native shared libraries remain in internal storage.)

The "Manage Applications" screen in the Settings app now has an "On SD Card" tab. The sizes listed in Manage Applications only include the space taken by the application on internal storage.

The Application Info screen now has either a "move to SD card" or "move to phone" button, but this is often disabled. Copy-protected apps and updates to system apps can't be moved to the SD card, nor can those which are don't specify that they work on the SD card.

Controlling Installation Preference

SD-card installation is an optional feature on Android 2.2, is under the control of the developer not the user, and will not affect any applications built prior to Android 2.2.

Application developers can set the field android:installLocation in the root manifest element to one of these values:

If the installLocation is not specified (as is the case for all applications prior to Android 2.2), the application is installed on internal storage. Application updates will by default try to retain their install location, but application developers may change the installLocation field in an update. Installing an application with this new attribute on older devices will not break compatibility and these applications will be installed on internal storage only.

Application developers can also explicitly install under-development code on external storage via adb install flags, which override the installLocation field: -f for internal storage, and -s for external storage. They can also override the default install location to verify and test application installation on SD card with an adb shell pm command:

adb shell pm setInstallLocation option

Where option is one of:

The current install location can be retrieved via
adb shell pm getInstallLocation
Note that changing this default can cause applications to misbehave if they're not prepared to live on the SD card.

USB Mass Storage interactions

The Android system stores the SD-card-resident applications' APKs in a secure application-specific container. A new flag FLAG_EXTERNAL_STORAGE in ApplicationInfo object indicates that an application is currently installed on the SD card. This storage is removed when an SD-card-resident app is uninstalled.

When an Android device's SD card is unmounted, applications installed on it are no longer available. Their internal storage is still there and such apps can be uninstalled without replacing the SD card.

The Android framework provides several broadcast intents to support the (small) class of applications, such as launchers, that access other applications' resources:

Applications that handle broadcast intents like ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED may also want to handle these additional notifications.

When an application gets moved between internal to external storage and vice versa, the application is disabled first (which includes broadcasting intent ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE), the asset resources are copied, and then the application is enabled (which includes broadcasting intent ACTION_EXTERNAL_APPLICATIONS_AVAILABLE).

Security and Performance Implications

Applications on SD card are mounted via Linux's loopback interface and encrypted via a device-specific key, so they cannot be decrypted on any other device. Note that this is security measure and does not provide copy protection; the apps are world readable just like the resources of normal applications installed on the device's internal storage. Copy-protected applications cannot be installed on external media. In the interests of stability, updates to system applications also cannot be installed on the external media. The application resources stored on external storage are read-only and hence there are no performance issues with loading or launching applications on SD card.

Swapping or Transferring SD Card Contents

It has always been the case that when you swap SD cards on an Android device, if you physically copy the contents of the old card to the new one, the system will use the data on the new card as if nothing had changed. This is also true of apps which have been installed on the SD card.

When not to install on SD card?

The advantage of installing on SD card is easy to understand: contention for storage space is reduced. There are costs, the most obvious being that your app is disabled when the SD card is either removed or in USB Mass Storage mode; this includes running Services, not just interactive Activities. Aside from this, device removal disables an application's Widgets, Input methods, Account Managers, Device administrators, Live wallpapers, and Live folders, and may require explicit user action to re-enable them.

08 Jul 2010 10:07pm GMT

Android 2.2 SDK refresh

As you may have noticed, the source code for Android 2.2, which we call Froyo, has been released.

The Android 2.2 SDK that was released at Google I/O contained a preview of the Froyo system image and today, we are releasing an update to bring it into sync with the system image pushed to Nexus One devices.

I encourage all developers to use the SDK manager to update to this version.

08 Jul 2010 6:00pm GMT

07 Jul 2010

feedAndroid Developers Blog

Android Love at OSCON

Android developers who aren't already going should take a moment to check out the OSCON 2010 schedule, and give serious thought to a trip to Portland in a couple of weeks. OSCON is one of the world's premiere events for those who care about Open Source, and one of my personal favorite conferences, with a powerful community vibe. And this year, the Android drums are sounding.

There are a bunch of mobile and Android-related sessions, both pure and extremely impure; for example, I bet both Steve Jobs and I are dubious about Cross-Compiling Android Applications to the iPhone. There's a full-dress Android for Java Developers tutorial. And (the one I helped cook up) there's the Android Hands-On; last I heard, registration for that is approaching 200 and, since O'Reilly found us a big room, it's not full up; but it will be.

On top of which, there are going to be a herd of Googlers at OSCON, and a sub-herd of Androiders, including Dan Morrill and Justin Mattson and me. This isn't surprising; what does surprise me is that OSCON hasn't previously been an Android love-fest. Because if you're interested in mobile devices and have a hankering for Open Source, Android is for you.

07 Jul 2010 8:39pm GMT

24 Jun 2010

feedAndroid Developers Blog

Exercising Our Remote Application Removal Feature

[This post is by Rich Cannings, Android Security Lead. - Tim Bray]

Every now and then, we remove applications from Android Market due to violations of our Android Market Developer Distribution Agreement or Content Policy. In cases where users may have installed a malicious application that poses a threat, we've also developed technologies and processes to remotely remove an installed application from devices. If an application is removed in this way, users will receive a notification on their phone.

Recently, we became aware of two free applications built by a security researcher for research purposes. These applications intentionally misrepresented their purpose in order to encourage user downloads, but they were not designed to be used maliciously, and did not have permission to access private data - or system resources beyond permission.INTERNET. As the applications were practically useless, most users uninstalled the applications shortly after downloading them.

After the researcher voluntarily removed these applications from Android Market, we decided, per the Android Market Terms of Service, to exercise our remote application removal feature on the remaining installed copies to complete the cleanup.

The remote application removal feature is one of many security controls Android possesses to help protect users from malicious applications. In case of an emergency, a dangerous application could be removed from active circulation in a rapid and scalable manner to prevent further exposure to users. While we hope to not have to use it, we know that we have the capability to take swift action on behalf of users' safety when needed.

This remote removal functionality - along with Android's unique Application Sandbox and Permissions model, Over-The-Air update system, centralized Market, developer registrations, user-submitted ratings, and application flagging - provides a powerful security advantage to help protect Android users in our open environment.

24 Jun 2010 5:35am GMT

23 Jun 2010

feedAndroid Developers Blog

Android Market Problem

Earlier today we had a brief outage in Android Market. For a period of about thirty minutes, some users were unable to find any apps. The problem was detected and corrected, and we believe the user experience is now back to normal. We apologize for the outage.

23 Jun 2010 10:08pm GMT

The Froyo Code Drop

[This post is by Jean-Baptiste Queru, who moves truck-loads of source code in and out of the Googleplex. - Tim Bray]

Today is one of those days that has my heart racing; we've just released the source code for Android 2.2. This is a big step forward for the entire Android ecosystem. Please don't melt the servers down again while trying to download that latest source code.

This blog typically talks about developing Android applications using the SDK and NDK. However, the skills of a platform contributor aren't fundamentally different from those of an application developer. Those are simply different roles using the same skill set. I'm providing an update here to the experienced Android programmers all around the world on some of the recent developments in the Android Open-Source Project.

For Google engineers working on Android, releases are mostly known by their code names which are chosen alphabetically after tasty treats. I'll call Android 2.2 "Froyo" throughout this post, since that was its code name. Raw version numbers don't make me salivate as much as the thought of a cold dessert in the California summer.

Let's have a look at some cool aspects of the new Froyo source, and let's then take a few steps back to look at other noteworthy aspects of the Android Open-Source Project.

I had been increasingly involved in all previous open-source releases of Android, from testing the initial code drop to doing all the open-source-related git-level work in Eclair. Following that path, Froyo is the first release where my primary focus has been the Android Open-Source Project from start to finish. I thank the entire Android team for helping me all along with much of that work. Here are some aspects of Froyo that I am proud of, and that kept me busy for the last few months:

Besides the Froyo source code release, I wanted to mention several other improvements in the Android Open-Source Project:

I believe that those last two aspects are important to application developers. If you're an application developer and you'd like to improve the tools that you and your fellow developers use, the process to make changes in that area is now a lot more transparent. Similarly, if during application development you find incompatibilities between devices and believe that those incompatibilities aren't within the letter or the spirit of Android compatibility, you can help improve the situation by contributing a CTS test for that area.

With Android 2.2 now being available to the open-source world, and with the review process working smoothly, I'm looking forward to seeing a lot more high-quality contributions that will be used to build future versions of Android. My sweetest dream, which is also my worst nightmare, is to have so many contributions that I can't keep up with them. Please don't wake me up.

23 Jun 2010 3:35pm GMT

22 Jun 2010

feedAndroid Developers Blog

Hands-on at OSCON

This year at OSCON we and O'Reilly are co-presenting Android Hands-on. The event is on the evening of Wednesday, July 21 after the Expo-hall reception. Led by Google Android experts, the Hands-on will run from 7:00 pm-10:00 pm, and will be intense, technical, and structured. The goal is that you leave the room with foundation skills for writing interesting code for an open-source stack that runs on a pocket-sized Internet-connected device.

Some specific topics we'll cover:

Sign-up in advance is required, and is restricted to registered full conference attendees and speakers. Spaces are limited and will be given out on a first-come-first-served basis.

If you're considering participating, you might want to keep these things in mind:

22 Jun 2010 9:22pm GMT

21 Jun 2010

feedAndroid Developers Blog

Future-Proofing Your App

[This post is by Reto Meier AKA @retomeier, who wrote the book on Android App development. - Tim Bray]

As a developer, I'm excited by Android's potential as a single development platform that can make my apps available on a wide range of devices. From smartphones to televisions, Android is now being used on an increasingly diverse collection of hardware.

Last year's Android SDK 1.6 release was the first to introduce support for variations in device hardware, paving the way for devices like the HTC Tattoo - a small screen device with a non-autofocus camera. Future devices, like Google TV, may not include some of the hardware features that we now expect, such a accelerometers and telephony.

We all want our apps available on as many devices as possible, but on some hardware they might just not make sense, so it's important that apps are available only on the devices where they do.

Android Market Rule #1: Don't let existing applications break on new devices

As curators of the Android Market, one of our most important responsibilities is ensuring consumers and developers can trust the Market to only deliver applications to devices capable of running them.

The Android SDK includes built-in support for specifying which hardware features your application needs, ensuring that when we see more hardware variations, the Market will make sure your apps are available everywhere (and only where) they make sense.

Specify the hardware your app needs using the application Manifest

That includes the target and minimum SDK versions, supported screen sizes, and the required hardware features without which your app will "break". You can specify the hardware features your app requires by adding a uses-feature node to your manifest.

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

By updating your manifest now to include all the hardware features you require, you effectively opt out of future hardware that won't be capable of properly supporting your app.

Android Market Rule #2: Don't let existing applications break on new devices

In extreme cases - such as the introduction of small screen sizes in Android 1.6 - developers will be required to explicitly opt in their apps before they will be made visible in the Market on these new devices.

In other cases the Android Market will analyze the permissions requested by an app to determine if it implies a dependence on any particular hardware. For example, requiring the CALL_PHONE permission strongly implies the need for telephony hardware.

Until we provide a more convenient tool, you can use AAPT in the SDK to analyze your apps (2.2 SDK required) and see which device requirements are being implicitly added to your application:

aapt dump badging myApp.apk

Where your app uses a particular hardware feature, but you know (and have tested) that it will still work without it, you can specify it as optional by setting the required attribute to false.

<uses-feature android:name="android.hardware.telephony" android:required="false" />

Ensure your application manifest correctly identifies what hardware your app needs, and what is optional

With the uses-feature name strings now available, you can ensure right now that your app appears in the Market, where appropriate, on current and future hardware devices rather than waiting for the devices to be released.

It's in your interest as a developer to ensure your apps work well, and are available, on as many devices as possible and appropriate. Now is the time to test your applications and update your Manifest to opt in to all hardware configurations which you support, and opt out of those that don't make sense.

21 Jun 2010 5:30pm GMT

15 Jun 2010

feedAndroid Developers Blog

Game Development for Android: A Quick Primer

[This post is by Chris Pruett, an outward-facing Androider who focuses on the world of games. - Tim Bray]

If you attended Google I/O this year, you might have noticed the large number of game developers showing off their stuff in the Android part of the Developer Sandbox. Unity, EA, Com2Us, Polarbit, Laminar Research, and several others demonstrated high-end games running on Android devices. Part of my role as a Game Developer Advocate for Android is to field requests for information about Android from game developers, and in the last six months the number of requests has gone through the roof. Since there's clearly a huge amount of interest in Android game development, here's an overview of how Android games work and what you as a developer should know.

Step One: Decide on a Target Device Class

There are basically two types of devices running Android that you should consider: lower-end devices like the G1 (which I'll call "first generation" devices), and high-end devices like the Nexus One ("second generation" devices). Though there are a lot of different Android phones on the market, they fall rather neatly into these two classes when it comes to CPU and graphics performance, which are the variables that game developers usually care the most about.

First generation devices are generally phones with HVGA screens, running Android 1.5 or 1.6 (though a few are starting to make their way to 2.1), typically with an 500mhz CPU and hardware accelerated OpenGL ES 1.0 backend. A large number of devices sport internals similar to the G1 (Qualcomm MSM7K CPU/GPU at ~500mhz), so the G1 is representative of this class (and can be safely considered the low end of the spectrum). Based on my tests, these devices can push about 5000 textured, colored, unlit vertices per frame and still maintain 30 frames per second. Using OpenGL ES to draw, I can get just over 250 animating sprites on the screen at 30 frames per second (at 60 fps I can draw just over 100 sprites per frame). These aren't hard numbers; my benchmarks are fairly primitive, and I'm sure that they can be improved (for example, I haven't done tests using the GL_OES_point_sprite extension, which the G1 supports). But they should give you an idea of what the first generation class of devices can do.

Second generation devices generally have WVGA screens, much faster CPUs and GPUs, and support for OpenGL ES 2.0. The Nexus One and Verizon Droid by Motorola are both good examples of this class. These devices seem to be about 5x faster than the first generation devices when it comes to raw OpenGL 1.0 performance (I can get at least 27,000 textured unlit colored vertices per frame at 30 frames per second on all of the second generation devices I've tested). Using OpenGL ES 2.0 can be even faster, as these devices typically incur some overhead translating OpenGL ES 1.0 commands to their 2.0-native graphics hardware. However, the large screens on these devices often mean that they are fill-bound: the cost of filling the screen with pixels is high enough that it's often not possible to draw faster than 30 frames per second, regardless of scene complexity.

Since there is a pretty wide performance delta between the first generation class of devices and the second, you should be careful when selecting a target. Based on our metrics about Android versions, first generation devices represent over half of all of the Android phones on the market (as of this writing, anyway; 2.0 devices are growing very quickly). Those games that are able to scale between the first and second generation devices have the largest audience.

Step Two: Pick a Language

If you're an Android app programmer who's thinking about getting into game development, chances are you are planning on writing code in Java. If you're a game development veteran who's thinking of bringing games to Android, it's likely that you prefer to do everything in C++.

The side-scrolling action game that I wrote, Replica Island, is entirely Java. It uses OpenGL ES 1.0 to draw and is backwards compatible to Android 1.5. It runs at a good frame rate (close to 60 fps on the G1) across almost all Android devices. In fact, many of the popular games on Android Market were written in Java, so if you're the type of person who finds coding in C++ like speaking in tongues, you can rest easy in the knowledge that Java on Android is perfectly viable for games.

That said, native code is the way to go if your game needs to run as fast as possible. We've just released the fourth revision of our Native Development Kit for Android, and it includes a number of improvements that are particularly useful to game developers. Using the NDK, you can compile your code into a shared library, wrap it in a thin Java shell to manage input and lifecycle events, and do all of the heavy lifting in C++ with regular OpenGL ES APIs. As of Revision 4, you can also draw directly into Java Bitmap pixel buffers from native code, which should be faster than loading bitmaps as GL textures every frame for 2D games that want to do their own scene compositing. Revision 4 also (finally!) includes gdb support for debugging your native code on the device.

You should know that when using the NDK, you don't have access to Android Framework APIs. There's no way, for example, to play audio from C++ (though we announced at Google I/O our intention to support OpenSL ES in the future). Some developers use the AudioTrack API to share a direct buffer with native code that mixes and generates a PCM stream on the fly, and many call from C++ into the Java SoundPool interface. Just be aware that for this type of work, a jump through JNI back into Java code is required.

Step Three: Carefully Design the Best Game Ever

Once you have a target system spec and have decided on a development environment, you're off and running. But before you get too deep into your epic ragdoll physics-based space marine action online RPG with branching endings and a morality system, take a minute to think about your end users. Specifically, there are two areas that require consideration for Android games that you might not be used to: texture compression and input systems.

Texture compression is a way to (surprise!) compress your texture data. It can improve draw performance and let you pack more texture into vram. The problem with texture compression is that different graphics card vendors support different texture formats. The G1 and other MSM7k devices support ATI's ATITC compression format. The Droid supports PowerVR's PVRTC format. Nvidia's Tegra2 platform supports the DXT format. The bad news is, these formats are not compatible. The good news is, all OpenGL ES 2.0 devices (including the Snapdragon-based Nexus One, the OMAP3-based Droid, and Tegra2 devices) support a common format called ETC1. ETC1 isn't the best texture format (it lacks support for alpha channels), and it isn't supported on the first generation devices, but it's the most common format supported (the Android SDK provides a compressor utility (see sdk/tools/etc1tool) and runtime tools for this format).

The bottom line is that if you compress your textures, you'll need to somehow provide different versions of those textures compressed with different formats. You could do this all in a single apk, or you could download textures from a web site over HTTP, or you could use ETC1 and restrict yourself to only OpenGL ES 2.0 devices. For Replica Island, I just chose not to compress my textures at all and had no problems. You can query the GL_EXTENSIONS string to see what the device you are currently running on supports.

String extensions = " " + gl.glGetString(GL10.GL_EXTENSIONS) + " ";
String version = gl.glGetString(GL10.GL_VERSION);
String renderer = gl.glGetString(GL10.GL_RENDERER);

boolean isSoftwareRenderer = renderer.contains("PixelFlinger");

// On 1.6 and newer, we could use ActivityManager.getDeviceConfigurationInfo() to get the GL version.
// To include 1.5, I'll use the GL version string.
boolean isOpenGL10 = version.contains(" 1.0");  
boolean supportsDrawTexture =
 extensions.contains(" GL_OES_draw_texture "); // draw_texture extension
boolean supportsETC1 =
 extensions.contains(" GL_OES_compressed_ETC1_RGB8_texture ");  // standard ETC1 support extension

// VBOs are guaranteed in GLES1.1, but they were an extension under 1.0.
// There's no point in using VBOs when using the software renderer (though they are supported).

boolean supportsVBOs =
 !isSoftwareRenderer && (!isOpenGL10 || extensions.contains("vertex_buffer_object "));

You should also think carefully about how your game will be played. Some phones have a trackball, some have a directional pad, some have a hardware keyboard, some support multitouch screens. Others have none of those things. Per the Compatibility Definition Document, all Android devices that have Android Market are required to have a touch screen and three-axis accelerometer, so if you can get away with just tilt and single touch, you don't need to worry about input much. If you want to take advantage of the various input devices that these phones support (which, based on several thousand comments on Android Market about Replica Island, I wholeheartedly recommend), the Android API will package the events up for you in a standard way.

That said, one of the most dramatic lessons I learned after shipping Replica Island is that users want customizable controls. Even if you have added perfect support for every phone, many users will want to go in and tweak it. Or they prefer the hardware keyboard over their phone's dpad. Or they prefer tilt controls over trackball controls. My advice: plan on providing customizable controls, both so that you can support phones that have input configurations that you didn't consider, and also so that you can allow users to tweak the experience to match their preferences.

Step Four: Profit!

The rest is up to you. But before you go, here are a few resources that might come in handy:

15 Jun 2010 5:33pm GMT