Sunday, December 20, 2015

Understanding Android's Live Wallpaper Chooser

Ever since Android API 7 (2.1 Eclair) was released in January of 2010, developers had the option to produce live wallpaper applications for phones. This was great because this had not yet been implemented for iOS devices, and to my knowledge, still hasn't.* On those first devices that got API 7, if you wanted to change the wallpaper to a live one, you'd have to go through the phone's settings to do it. It was a couple of extra steps, but well worth it if you liked the feature. Later on in API 16 (4.1 Jelly Bean) developers could access the intent for changing the live wallpaper. This was great because users didn't have to go through the phone's settings to set the wallpaper anymore. Instead, they could press the 'OPEN' button on the app's page from the Google Play Store. It looked like this:

                                                 

Unfortunately for me, I didn't know this. Because I had stopped focus on my personal apps, there was a great length of time where users would install my live wallpaper app and all they would see is this:

                                                 

Users had become so accustomed to just pressing the 'OPEN' button to go to the wallpaper settings, that they had forgotten (or just didn't know) you could change the wallpaper from the phone's settings. This resulted in a lot of bad reviews for me :(

So, let's see what it takes to get the 'OPEN' button to appear for a live wallpaper app in the Google Play Store. Any normal Android app you download from the Play Store has a main activity (i.e. the screen you first see when it launches). The way this is set is in the manifest with the action tag and android.intent.action.MAIN set as its name. For example:
<activity
    android:name=".MainActivity"
    android:label="@string/app_name" >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
    </intent-filter>
</activity>

Live wallpaper applications run on a service, and guess what? Live wallpaper services need the action tag to be specific. It has to look like this:
<service
    android:name=".AnimatedWallpaperService"
    android:permission="android.permission.BIND_WALLPAPER" >
    <intent-filter>
        <action android:name="android.service.wallpaper.WallpaperService" />
    </intent-filter>
</service>

So what do we do? Well, we must make a separate activity (our main activity that is) that has the action tag. When this activity is run, it starts the ACTION_CHANGE_LIVE_WALLPAPER intent. This intent must be supplied with a Component Name object that has our specific wallpaper service attached to it. Essentially, this will start the live wallpaper chooser screen (intent), and because our wallpaper service is attached to it, it will look as if the user had already selected our wallpaper from the wallpaper chooser screen and is at the step to change it's settings or set it as the current wallpaper.

Let me show you:

First, create a new activity, this will be our Main Activity for the 'OPEN' button. Inside of that activity, include this code:
@Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    try {
        Intent intent = new Intent(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER);
        intent.putExtra(WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT,
                new ComponentName(MainActivity.this, AnimatedWallpaperService.class));
        startActivity(intent);
        finish();
    } catch(Exception e) {
        Intent intent = new Intent();
        intent.setAction(WallpaperManager.ACTION_LIVE_WALLPAPER_CHOOSER);
        startActivity(intent);
        finish();
    }
}

For the ComponentName object, you must supply your wallpaper service class (don't include mine). This code does pretty much what I said in the last paragraph, but let me reiterate. First, we create the intent we want, which in this case is the Action Change Live Wallpaper intent supplied by the WallpaperManager class. We supply an extra to this intent, and that extra has our specific wallpaper class. That is the part where it knows to open the live wallpaper chooser and to choose our live wallpaper over any other one that might be on the user's phone. Finally, we start that activity and finish this activity (if we don't finish and the user presses the back button, it will come back to this activity which might result in a loop). But like all intents that that are external to your app, you must include a catch for any exception that could occur (i.e. the wallpaper service you included isn't found, the device isn't the right API for this intent, the device is bootleg, etc etc). In our situation, if the wallpaper can't be found or if it's the wrong API, we just make the activity open the Wallpaper Chooser and hope that the user chooses our wallpaper.

But we aren't finished! The manifest still needs to know to open this activity. Coincidentally, I already put that code up above, but I forgot to mention one part, the category tag. Our activity tag must have the action tag so that the phone knows which activity to start when the user selects to open the app from their app drawer or the Play Store, but how does the app get there? Well that's all in the category tag. All regular apps that you open from the app drawer have the category tag, and that category tag tells the phone top include the app in the app drawer when it is downloaded. It's also the tag that tells the Play Store to include the 'OPEN' button. So, make your activity tag in the manifest look more like this:
<activity
    android:name=".MainActivity"
    android:label="@string/app_name" >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.INFO" />
    </intent-filter>
</activity>

Now that your activity tag has the necessary category tag, with the correct name, it can now be accessed from your app drawer and from the Play Store.

* iOS 7 has dynamic wallpapers, but these aren't the same as Android's "live wallpapers." They are supplied on the phone or can be made from the user's pictures. There is not an API for developers to make their own dynamic wallpapers.

No comments: