How to Set an Animated Gif File as Live Wallpaper in Android

Is it possible to set an animated gif file as live wallpaper in android?

This is the basic wallpaper service (as supplied in the Live Wallpaper Tutorial) hacked to display an animated gif.

First - create a project & set up your manifest as a Live wallpaper.

Then - download a gif, like this one

   nyan nyan

Save that gif in res/raw/nyan.gif in your project.

Create a live wallpaper service, like shown in this example.

public class NyanNyanService extends WallpaperService {
static final String TAG = "NYAN";
static final Handler mNyanHandler = new Handler();

/**
* @see android.service.wallpaper.WallpaperService#onCreate()
*/
@Override
public void onCreate() {
super.onCreate();
}

/**
* @see android.service.wallpaper.WallpaperService#onCreateEngine()
*/
@Override
public Engine onCreateEngine() {
try {
return new NyanEngine();
} catch (IOException e) {
Log.w(TAG, "Error creating NyanEngine", e);
stopSelf();
return null;
}
}

class NyanEngine extends Engine {
private final Movie mNyan;
private final int mNyanDuration;
private final Runnable mNyanNyan;
float mScaleX;
float mScaleY;
int mWhen;
long mStart;

NyanEngine() throws IOException {
InputStream is = getResources().openRawResource(R.raw.nyan);
if (is != null) {
try {
mNyan = Movie.decodeStream(is);
mNyanDuration = mNyan.duration();
} finally {
is.close();
}
} else {
throw new IOException("Unable to open R.raw.nyan");
}

mWhen = -1;
mNyanNyan = new Runnable() {
public void run() {
nyan();
}
};
}

@Override
public void onDestroy() {
super.onDestroy();
mNyanHandler.removeCallbacks(mNyanNyan);
}

@Override
public void onVisibilityChanged(boolean visible) {
super.onVisibilityChanged(visible);
if (visible) {
nyan();
} else {
mNyanHandler.removeCallbacks(mNyanNyan);
}
}

@Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
mScaleX = width / (1f * mNyan.width());
mScaleY = height / (1f * mNyan.height());
nyan();
}

@Override
public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep,
float yOffsetStep, int xPixelOffset, int yPixelOffset) {
super.onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixelOffset, yPixelOffset);
nyan();
}

void nyan() {
tick();
SurfaceHolder surfaceHolder = getSurfaceHolder();
Canvas canvas = null;
try {
canvas = surfaceHolder.lockCanvas();
if (canvas != null) {
nyanNyan(canvas);
}
} finally {
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
mNyanHandler.removeCallbacks(mNyanNyan);
if (isVisible()) {
mNyanHandler.postDelayed(mNyanNyan, 1000L/25L);
}
}

void tick() {
if (mWhen == -1L) {
mWhen = 0;
mStart = SystemClock.uptimeMillis();
} else {
long mDiff = SystemClock.uptimeMillis() - mStart;
mWhen = (int) (mDiff % mNyanDuration);
}
}

void nyanNyan(Canvas canvas) {
canvas.save();
canvas.scale(mScaleX, mScaleY);
mNyan.setTime(mWhen);
mNyan.draw(canvas, 0, 0);
canvas.restore();
}
}
}

This will basically scale the nyan-nyan cat to fit the screen and animate it perpetually.

A Live wallpaper manifest looks sort-of-like this (this example does not contain a configuration activity):

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="your.nyan.nyan.package"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="10" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/application_nyan" >
<service
android:label="@string/wallpaper_nyan"
android:name=".NyanNyanService"
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/nyan" />
</service>
</application>
</manifest>

The AndroidManifest.xml has a reference to a file in res/xml, in this case named "nyan.xml":

<?xml version="1.0" encoding="utf-8"?>
<wallpaper xmlns:android="http://schemas.android.com/apk/res/android" />

Live wallpaper using animated gif ( with centercrop scaletype)

Your activity file

public class GIFWallpaperService extends WallpaperService {

@Override
public WallpaperService.Engine onCreateEngine() {
try {
Movie movie = Movie.decodeStream(
getResources().getAssets().open("owlinsnow.gif"));

return new GIFWallpaperEngine(movie);
}catch(IOException e){
Log.d("GIF", "Could not load asset");
return null;
}
}

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;
}

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(2f, 2f);
movie.draw(canvas, -100, 0);
canvas.restore();
holder.unlockCanvasAndPost(canvas);
movie.setTime((int) (System.currentTimeMillis() % movie.duration()));

handler.removeCallbacks(drawGIF);
handler.postDelayed(drawGIF, frameDuration);
}
}

@Override
public void onVisibilityChanged(boolean visible) {
this.visible = visible;
if (visible) {
handler.post(drawGIF);
} else {
handler.removeCallbacks(drawGIF);
}
}

@Override
public void onDestroy() {
super.onDestroy();
handler.removeCallbacks(drawGIF);
}
}
}

Manifest file

<application android:allowBackup="true" android:label="@string/app_name"
android:icon="@drawable/owl" android:theme="@style/AppTheme">

<service
android:name=".GIFWallpaperService"
android:enabled="true"
android:label="Owl in Snow"
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>

</application>

<uses-feature
android:name="android.software.live_wallpaper"
android:required="true" >
</uses-feature>

Create xml directory under res and wallpaper.xml inside it.

<?xml version="1.0" encoding="UTF-8"?>
<wallpaper
xmlns:android="http://schemas.android.com/apk/res/android"
android:label="GIF Wallpaper"
android:thumbnail="@drawable/owl">
</wallpaper>

Make sure you place the gif image inside assets folder. To create an asset folder click on file->New->Folder->assets

How to fit content of GIF animation in View and in live wallpaper?

OK I think I got how to scale the content. Not sure though why the app still crashes upon orientation change sometimes, and why the app doesn't show the preview right away sometimes.

Project is available here.

For center-inside, the code is:

    private fun draw() {
if (!isVisible)
return
val canvas = holder!!.lockCanvas() ?: return
canvas.save()
//center-inside
val scale = Math.min(canvas.width.toFloat() / movie.width().toFloat(), canvas.height.toFloat() / movie.height().toFloat());
val x = (canvas.width.toFloat() / 2f) - (movie.width().toFloat() / 2f) * scale;
val y = (canvas.height.toFloat() / 2f) - (movie.height().toFloat() / 2f) * scale;
canvas.translate(x, y)
canvas.scale(scale, scale)
movie.draw(canvas, 0f, 0f)
canvas.restore()
holder!!.unlockCanvasAndPost(canvas)
movie.setTime((System.currentTimeMillis() % movie.duration()).toInt())
handler.removeCallbacks(drawGIF)
handler.postDelayed(drawGIF, frameDuration.toLong())
}

For center-crop, the code is:

    private fun draw() {
if (!isVisible)
return
val canvas = holder!!.lockCanvas() ?: return
canvas.save()
//center crop
val scale = Math.max(canvas.width.toFloat() / movie.width().toFloat(), canvas.height.toFloat() / movie.height().toFloat());
val x = (canvas.width.toFloat() / 2f) - (movie.width().toFloat() / 2f) * scale;
val y = (canvas.height.toFloat() / 2f) - (movie.height().toFloat() / 2f) * scale;
canvas.translate(x, y)
canvas.scale(scale, scale)
movie.draw(canvas, 0f, 0f)
canvas.restore()
holder!!.unlockCanvasAndPost(canvas)
movie.setTime((System.currentTimeMillis() % movie.duration()).toInt())
handler.removeCallbacks(drawGIF)
handler.postDelayed(drawGIF, frameDuration.toLong())
}

for fit-center, I can use this:

                    val canvasWidth = canvas.width.toFloat()
val canvasHeight = canvas.height.toFloat()
val bitmapWidth = curBitmap.width.toFloat()
val bitmapHeight = curBitmap.height.toFloat()
val scaleX = canvasWidth / bitmapWidth
val scaleY = canvasHeight / bitmapHeight
scale = if (scaleX * curBitmap.height > canvas.height) scaleY else scaleX
x = (canvasWidth / 2f) - (bitmapWidth / 2f) * scale
y = (canvasHeight / 2f) - (bitmapHeight / 2f) * scale
...

Set Animated .GIF As Background Android

As Martin says, Android does not support GIFs. As a workaround, Android offers Animation List/AnimationDrawable. You will need to convert the GIF into individual frames [.png files]. I use GIMP for the conversion

Original GIF image

This GIF can be broken down into its frames:

GIF frames in GIMP

Save them as frame01.png, frame02.png, and so on, and create an animation-list XML file, say, progress_animation.xml

<animation-list android:id="@+id/selected" android:oneshot="false">
<item android:drawable="@drawable/frame01" android:duration="50" />
<item android:drawable="@drawable/frame02" android:duration="50" />
<item android:drawable="@drawable/frame03" android:duration="50" />
....
<item android:drawable="@drawable/frameN" android:duration="50" />

To begin the animation, you need to cast the image to an AnimationDrawable, and call start() on it

AnimationDrawable progressAnimation = (AnimationDrawable) yourImageView.getBackground();
progressAnimation.start();

Using .gif file in android live wallpaper

I have tried the example it works for me. Also i have tried this with different .gif images and it doesn't seems that their is any problem. My code for that is .
{

 /**
* Method to init suitable wallpaper according to time.
*/
private void initWallpaperAccordingtoTime(InputStream inputStream) {

if (inputStream != null) {
try {
wallpaperGifStream = Movie.decodeStream(inputStream);
if (wallpaperGifStream != null) {
duration = wallpaperGifStream.duration();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

}

Call to this method will be as follows.

 initWallpaperAccordingtoTime(getResources().openRawResource(
R.raw.android_apple));

}



Related Topics



Leave a reply



Submit