Android Live Wallpaper tutorial
Posted by Tonio | Filed under Tutorial
Live Wallpaper is a new feature added in Android 2.1. Live wallpapers are animated and interactive backgrounds that can be added to your Android home. In this tutorial we’ll se how to build an interactive animated live wallpaper properly.
A live wallpaper is an Android application that includes a WallpaperService. This service must wrap a WallpaperService.Engine. The Engine is the bridge between the user, the surface, and the system. It owns the surface in wich the wallpaper is drawn.
First of all, a WallpaperService class must be created with its inner Engine class. This service must be declared in the AndroidManifest.xml with the android.service.wallpaper.WallpaperService Intent so that it would be recognized as a live wallpaper on the device. The android.permission.BIND_WALLPAPER permission is required to attach a live wallpaper to the Android Home application :
<service android:name="LiveWallpaperService" android:enabled="true" android:icon="@drawable/icon" android:label="@string/app_name" android:permission="android.permission.BIND_WALLPAPER"> <intent-filter android:priority="1" > <action android:name="android.service.wallpaper.WallpaperService" /> </intent-filter> <meta-data android:name="android.service.wallpaper" android:resource="@xml/wallpaper" /> </service>
A xml file should be placed in the /res/xml/ directory of your application. It’s used to described your live wallpaper.
<?xml version="1.0" encoding="UTF-8"?> <wallpaper xmlns:android="http://schemas.android.com/apk/res/android" android:thumbnail="@drawable/thumbnail" android:description="@string/description" android:settingsActivity="PreferenceActivity"/>
As described in the attrs.xml Android file, the allowed attributes and there meanings are as follow :
<declare-styleable name="Wallpaper"> <!-- Component name of an activity that allows the user to modify the current settings for this wallpaper. --> <attr name="settingsActivity" /> <!-- Reference to a the wallpaper's thumbnail bitmap. --> <attr name="thumbnail" format="reference" /> <!-- Name of the author of this component, e.g. Google. --> <attr name="author" format="reference" /> <!-- Short description of the component's purpose or behavior. --> <attr name="description" /> </declare-styleable>
The live wallpaper archetype is as follow :
package net.androgames.blog.sample.livewallpaper; import android.content.SharedPreferences; import android.service.wallpaper.WallpaperService; import android.view.MotionEvent; import android.view.SurfaceHolder; /** * Android Live Wallpaper Archetype * @author antoine vianey * under GPL v3 : http://www.gnu.org/licenses/gpl-3.0.html */ public class LiveWallpaperService extends WallpaperService { @Override public Engine onCreateEngine() { return new SampleEngine(); } @Override public void onCreate() { super.onCreate(); } @Override public void onDestroy() { super.onDestroy(); } public class SampleEngine extends Engine { private LiveWallpaperPainting painting; SampleEngine() { SurfaceHolder holder = getSurfaceHolder(); painting = new LiveWallpaperPainting(holder, getApplicationContext()); } @Override public void onCreate(SurfaceHolder surfaceHolder) { super.onCreate(surfaceHolder); // register listeners and callbacks here setTouchEventsEnabled(true); } @Override public void onDestroy() { super.onDestroy(); // remove listeners and callbacks here painting.stopPainting(); } @Override public void onVisibilityChanged(boolean visible) { if (visible) { // register listeners and callbacks here painting.resumePainting(); } else { // remove listeners and callbacks here painting.pausePainting(); } } @Override public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) { super.onSurfaceChanged(holder, format, width, height); painting.setSurfaceSize(width, height); } @Override public void onSurfaceCreated(SurfaceHolder holder) { super.onSurfaceCreated(holder); // start painting painting.start(); } @Override public void onSurfaceDestroyed(SurfaceHolder holder) { super.onSurfaceDestroyed(holder); boolean retry = true; painting.stopPainting(); while (retry) { try { painting.join(); retry = false; } catch (InterruptedException e) {} } } @Override public void onOffsetsChanged(float xOffset, float yOffset, float xStep, float yStep, int xPixels, int yPixels) { } @Override public void onTouchEvent(MotionEvent event) { super.onTouchEvent(event); painting.doTouchEvent(event); } } }
The Engine’s methods onCreate, onDestroy, onVisibilityChanged, onSurfaceChanged, onSurfaceCreated and onSurfaceDestroyed are called when the wallpaper visibility, state or size changes. With this methods, the live wallpaper could be animated only if necessary. Touch events are activated through setTouchEventsEnabled(true) and handled with the onTouchEvent(MotionEvent event) callback.
To do the actual painting of the wallpaper, a separate painting thread is used :
package net.androgames.blog.sample.livewallpaper; import android.content.Context; import android.graphics.Canvas; import android.view.MotionEvent; import android.view.SurfaceHolder; /** * Android Live Wallpaper painting thread Archetype * @author antoine vianey * GPL v3 : http://www.gnu.org/licenses/gpl-3.0.html */ public class LiveWallpaperPainting extends Thread { /** Reference to the View and the context */ private SurfaceHolder surfaceHolder; private Context context; /** State */ private boolean wait; private boolean run; /** Dimensions */ private int width; private int height; /** Time tracking */ private long previousTime; private long currentTime; public LiveWallpaperPainting(SurfaceHolder surfaceHolder, Context context) { // keep a reference of the context and the surface // the context is needed if you want to inflate // some resources from your livewallpaper .apk this.surfaceHolder = surfaceHolder; this.context = context; // don't animate until surface is created and displayed this.wait = true; } /** * Pauses the live wallpaper animation */ public void pausePainting() { this.wait = true; synchronized(this) { this.notify(); } } /** * Resume the live wallpaper animation */ public void resumePainting() { this.wait = false; synchronized(this) { this.notify(); } } /** * Stop the live wallpaper animation */ public void stopPainting() { this.run = false; synchronized(this) { this.notify(); } } @Override public void run() { this.run = true; Canvas c = null; while (run) { try { c = this.surfaceHolder.lockCanvas(null); synchronized (this.surfaceHolder) { currentTime = System.currentTimeMillis(); updatePhysics(); doDraw(c); previousTime = currentTime; } } finally { if (c != null) { this.surfaceHolder.unlockCanvasAndPost(c); } } // pause if no need to animate synchronized (this) { if (wait) { try { wait(); } catch (Exception e) {} } } } } /** * Invoke when the surface dimension change */ public void setSurfaceSize(int width, int height) { this.width = width; this.height = height; synchronized(this) { this.notify(); } } /** * Invoke while the screen is touched */ public void doTouchEvent(MotionEvent event) { // handle the event here // if there is something to animate // then wake up this.wait = false; synchronized(this) { notify(); } } /** * Do the actual drawing stuff */ private void doDraw(Canvas canvas) {} /** * Update the animation, sprites or whatever. * If there is nothing to animate set the wait * attribute of the thread to true */ private void updatePhysics() { // if nothing was updated : // this.wait = true; } }
This class is optimized in the sens that it draws the canvas only if the wallpaper surface is visible and if there is something new to draw. If there is nothing to animate anymore, then the updatePhysics should tell the thread to wait. If so, the canvas needs to be drawn one last time because of the SurfaceView behaviour that use two Canvas alternating…
To allow configuration of a live wallpaper, just create a PreferenceActivity and link it into the wallpaper xml declaration file described previously. Preference values can be retrieved through a SharedPreference object.
You can browse the full source code of the Eclipse project here : SampleLiveWallpaper.
Enjoy !
Tags: live wallpaper
4 Responses to “Android Live Wallpaper tutorial”
-
unixseb Says:
January 28th, 2010 at 10:20 amThanks a lot, verry usefull !!
-
Dan Says:
January 29th, 2010 at 6:22 amThanks for this, it’s very helpful.
I copied the files to test and play around with the code. Although, I’m getting an error in the: LiveWallpaperSettings.java file at line 15:
addPreferencesFromResource(R.xml.settings);
Error: R.xml cannot be resolved…
The R.java file is an automated file, it comes up with 2 quick fixes in Eclipse, either create the field “xml” in R or create the constant “xml” in R…
Any help would be appreciated, Cheers!
-
Tonio Says:
January 29th, 2010 at 7:15 amHi Dan,
It’s a common Eclipse issue,
Just try to change the Java Compiler Settings for the project or simply change the android SDK set for your project and it should work just fine then… -
Dan Says:
January 29th, 2010 at 3:06 pmAh, cheers! That did it, although after I switched API from 2.1 to 2.0.1 then back. The R.java file was removed… so I remade another R.java file under the default package (just under gen/ folder) and re-opened the project and it remade the correct R.java file for my package.
Then just had to remove the default one and refresh the project.