Java – Setting live wallpaper does not work

Setting live wallpaper does not work… here is a solution to the problem.

Setting live wallpaper does not work

I’ve asked this question, but it’s migrated to android.stackexchange.com and closed there as an offtopic.
So here we go again:

I made a simple live wallpaper. I can see it in the preview, but if I try to set it as my live wallpaper, Android keeps the old wallpaper.

Any ideas?

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.localfotos"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="7" />

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

<application android:icon="@drawable/icon" android:label="@string/app_name">

<service android:name=". MyWallpaperService"
            android:label="@string/app_name"
            android:icon="@drawable/icon">

<intent-filter>
                <action android:name="android.service.wallpaper.WallpaperService" />
            </intent-filter>
            <meta-data android:name="android.service.wallpaper"
                android:resource="@xml/livewallpaper" />

</service>

</application>
</manifest>

Live wallpaper .xml

<wallpaper xmlns:android="http://schemas.android.com/apk/res/android"
/>

My wallpaper service .java

package com.localfotos;

import android.service.wallpaper.WallpaperService;
import android.view.MotionEvent;
import android.view.SurfaceHolder;

public class MyWallpaperService extends WallpaperService {

@Override
    public Engine onCreateEngine() {
        return new MyEngine();
    }

@Override
    public void onCreate() {
        super.onCreate();
    }

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

public class MyEngine extends Engine {

private MyWallpaperPainting painting;

MyEngine() {
            SurfaceHolder holder = getSurfaceHolder();
            painting = new MyWallpaperPainting(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);
        }

}

}

MyWallpaperPainting.java

package com.localfotos;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;

public class MyWallpaperPainting extends Thread {
    private static final String TAG = "MyWallpaperPainting";

/** 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 MyWallpaperPainting(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) {}
                }
            }

synchronized (this){    
            try{
                wait(1000);
            }catch (InterruptedException 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
         */

Paint mPaint = new Paint();
          mPaint.setDither(true);
          mPaint.setColor(0xFFFFFF00);
          mPaint.setStyle(Paint.Style.STROKE);
          mPaint.setStrokeJoin(Paint.Join.ROUND);
          mPaint.setStrokeCap(Paint.Cap.ROUND);
          mPaint.setStrokeWidth(3);

Path path = new Path();
          path.lineTo(10, 100);

canvas.drawColor(0xFFFFFF00);

Log.d(TAG, "doDraw");

}

private void updatePhysics() {
         if nothing was updated :
         this.wait = true;
    }

}

Solution

Your list is missing BIND_WALLPAPER permissions. See Cube samples in the SDK.

android:permission="android.permission.BIND_WALLPAPER">

Related Problems and Solutions