Have you ever seen a beautiful animated GIF that loops seamlessly and wondered if you could use it as a live wallpaper on your Android device? Well, you can, and in this tutorial I am going to show you how.
Introduction
Creating an interesting and beautiful live wallpaper from scratch using only math and code to generate the graphics can be tedious and time-consuming. It also requires lots of creativity. On the other hand, creating an animated GIF or finding one online is a lot easier. In this tutorial, you are going to learn how to convert any animated GIF into a live wallpaper.
Prerequisites
Ensure that you have the latest version of Android Studio set up. You can get it from the Android Developer website.
Even though any animated GIF will do, I suggest that you download a good cinemagraph. A cinemagraph is nothing but an animated GIF—usually created from a video—that loops seamlessly. You can find lots of good ones on Flickr.
For this tutorial, I am using a cinemagraph created by Flickr user djandyw.com as it is available under a Creative Commons license.
1. Create a New Project
Start Android Studio, create a new project, and name the project GIFWallpaper. Pick a unique package name if you plan to publish this app on Google Play.
Set the minimum SDK to API 8: Android 2.2 (Froyo).
Our app is not going to have an Activity
, so choose Add No Activity and click Finish.
2. Describe the Wallpaper
A live wallpaper needs a file that describes it. Create a new XML file named res/xml/wallpaper.xml and replace its contents with the following XML:
<?xml version="1.0" encoding="UTF-8"?><wallpaper xmlns:android="http://schemas.android.com/apk/res/android" android:label="GIF Wallpaper" android:thumbnail="@drawable/ic_launcher"></wallpaper>
The label and thumbnail are particularly important as they will be used when the wallpaper shows up in the list of the wallpapers available on your device.
3. Edit the Manifest
To run as a live wallpaper, our app needs only one permission, android.permission.BIND_WALLPAPER
.
A live wallpaper runs as a Service
that can receive the android.service.wallpaper.WallpaperService
intent action. Name the Service
GIFWallpaperService and add it to the project's manifest, AndroidManifest.xml.
<service android:name=".GIFWallpaperService" android:enabled="true" android:label="GIF Wallpaper" android:permission="android.permission.BIND_WALLPAPER" ><intent-filter><action android:name="android.service.wallpaper.WallpaperService"/></intent-filter><meta-data android:name="android.service.wallpaper" android:resource="@xml/wallpaper" ></meta-data></service>
Next, to make sure that the app can be installed only on devices that can run live wallpapers, add the following snippet to the manifest:
<uses-feature android:name="android.software.live_wallpaper" android:required="true" ></uses-feature>
4. Add Animated GIF
Copy the animated GIF you downloaded from Flickr to the assets folder of the project. I've named the GIF girl.gif.
5. Create the Service
Create a new Java class and name it GIFWallpaperService.java. This class should extend the WallpaperService
class.
public class GIFWallpaperService extends WallpaperService { }
Because WallpaperService
is an abstract class, you have to override its onCreateEngine
method and return an instance of your own Engine
, which can render the frames of the GIF.
To use the animated GIF, you first have to convert it into a Movie
object. You can use the Movie
class's decodeStream
method to do so. Once the Movie
object has been created, pass it as a parameter to the constructor of the custom Engine
.
This is what the onCreateEngine
method should look like:
@Override public WallpaperService.Engine onCreateEngine() { try { Movie movie = Movie.decodeStream( getResources().getAssets().open("girl.gif")); return new GIFWallpaperEngine(movie); }catch(IOException e){ Log.d("GIF", "Could not load asset"); return null; } }
6. Create the Engine
Let's start working on the Engine
now. Create a class named GIFWallpaperEngine inside the GIFWallpaperService
class and make it extend WallpaperService.Engine
.
Add the following fields to this new class:
frameDuration
: This integer represents the delay between re-draw operations. A value of 20 gives you 50 frames per second.visible
: This boolean lets the engine know if the live wallpaper is currently visible on the screen. This is important, because we should not be drawing the wallpaper when it isn't visible.movie
: This is the animated GIF in the form of aMovie
object.holder
: This refers to theSurfaceHolder
object available to the engine. It has to be initialized by overriding theonCreate
method.handler
: This is aHandler
object that will be used to start aRunnable
that is responsible for actually drawing the wallpaper.
Your class should now look like this:
private class GIFWallpaperEngine extends WallpaperService.Engine { private final int frameDuration = 20; private SurfaceHolder holder; private Movie movie; private boolean visible; private Handler handler; public GIFWallpaperEngine(Movie movie) { this.movie = movie; handler = new Handler(); } @Override public void onCreate(SurfaceHolder surfaceHolder) { super.onCreate(surfaceHolder); this.holder = surfaceHolder; } }
Next, create a method named draw
that draws the contents of the animated GIF. Let's break this method down:
- We first check if the
visible
variable is set totrue
. We only continue if it is. - Use the
SurfaceHolder
'slockCanvas
method to get aCanvas
to draw on. - Draw a frame of the animated GIF on the
Canvas
after scaling and positioning it. - Once all the drawing is done, pass the
Canvas
back to theSurfaceHolder
. - Update the current frame of the animated GIF using the
Movie
object'ssetTime
method. - Call the method again using the
handler
after waiting forframeDuration
milliseconds.
The draw
method is never called directly. It is always called using a Handler
and a Runnable
object. Therefore, let's make the Runnable
object a field of the class and call it drawGIF
.
Add the following code to the GIFWallpaperService
class:
private Runnable drawGIF = new Runnable() { public void run() { draw(); } }; private void draw() { if (visible) { Canvas canvas = holder.lockCanvas(); canvas.save(); // Adjust size and position so that // the image looks good on your screen canvas.scale(3f, 3f); movie.draw(canvas, -100, 0); canvas.restore(); holder.unlockCanvasAndPost(canvas); movie.setTime((int) (System.currentTimeMillis() % movie.duration())); handler.removeCallbacks(drawGIF); handler.postDelayed(drawGIF, frameDuration); } }
The onVisibilityChanged
method is automatically called whenever the visibility of the wallpaper changes. We need to override it and, based on the value of the visible
argument, either start or stop drawGIF
. The removeCallbacks
method of the Handler
is used to stop any pending drawGIF
runs.
@Override public void onVisibilityChanged(boolean visible) { this.visible = visible; if (visible) { handler.post(drawGIF); } else { handler.removeCallbacks(drawGIF); } }
Finally, override the onDestroy
method of the Engine
to stop any pending drawGIF
runs if the wallpaper is deactivated.
@Override public void onDestroy() { super.onDestroy(); handler.removeCallbacks(drawGIF); }
7. Compile and Install
Your live wallpaper is now ready. Compile it and install it on your Android device. Once installed, you should be able to find the wallpaper in the list of available wallpapers.
Most launchers give you an option to change the wallpaper after a long tap gesture. Alternatively, you can go to the display settings to change the wallpaper.
If the GIF looks too small or it isn't positioned correctly, then go back to the draw
method and adjust the scale and position.
Conclusion
You now know how to use an animated GIF to create a live wallpaper. Feel free to experiment with more GIFs. If you plan to publish your live wallpaper on Google Play, make sure you have the creator's permission to use the animated GIF commercially. Visit the Android Developer website to learn more about the WallpaperService
class.