Friday, December 14, 2012

How to Build Live Wallpaper with Canvas on Android

This will be a very basic canvas live wallpaper with android. Just to get people started.
Just read the comments on the code to understand how everything works.

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="org.altlimit.samplelivewallpaper"
          android:versionCode="1"
          android:versionName="1.0">
    <uses-sdk android:minSdkVersion="15"/>
    <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
        <activity android:name="MyActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <!--The service that will be drawing your wallpaper-->
        <service android:name=".SampleService"
                 android:label="@string/app_name"
                 android:permission="android.permission.BIND_WALLPAPER"
                 android:icon="@drawable/ic_launcher">

            <intent-filter>
                <action android:name="android.service.wallpaper.WallpaperService" />
            </intent-filter>
            <!--To tell your wallpaper which settings activity it will launch-->
            <meta-data android:name="android.service.wallpaper"
                       android:resource="@xml/meta" />

        </service>
        <!--The activity declaration settings for your wallpaper-->
        <activity android:label="Settings"
                  android:name=".SampleSettings"
                  android:exported="true"
                  android:icon="@drawable/ic_launcher">
        </activity>

    </application>

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

</manifest>

res/xml/settings.xml - this is a normal preference activity
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
                  android:title="Sample Live Wallpaper Settings"
                  android:key="samplelive_wallpaper_settings">

    <PreferenceCategory
            android:title="Settings">

        <CheckBoxPreference
                android:key="pref_sample_checkbox"
                android:defaultValue="false"
                android:title="Sample Checkbox"
                />
        <EditTextPreference
                android:key="pref_count"
                android:defaultValue="10"
                android:numeric="integer"
                android:title="Write Me This amount"
                />
        <EditTextPreference
                android:key="pref_text"
                android:defaultValue="Hello World"
                android:title="What to write"
                />
    </PreferenceCategory>

</PreferenceScreen>


SampleSettings.java - normal preference activity
package org.altlimit.samplelivewallpaper;

import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceActivity;

public class SampleSettings extends PreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener {

    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {

    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        addPreferencesFromResource(R.xml.settings);
    }
}


MyActivity.java
package org.altlimit.samplelivewallpaper;

import android.app.Activity;
import android.app.WallpaperManager;
import android.content.Intent;
import android.os.Bundle;

public class MyActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        // Call the live wallpaper picker when started your app
        Intent intent = new Intent(WallpaperManager.ACTION_LIVE_WALLPAPER_CHOOSER);
        startActivity(intent);

    }

    @Override
    protected void onResume() {
        super.onResume();    //To change body of overridden methods use File | Settings | File Templates.
        finish();
    }
}


SampleService.java
package org.altlimit.samplelivewallpaper;

import android.content.SharedPreferences;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.service.wallpaper.WallpaperService;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.Log;
import android.view.SurfaceHolder;

public class SampleService extends WallpaperService {


    /**
     * A wallpaper service is actually just another android service.
     * You just need to implement the onCreateEngine to get started and return your engine
     */
    @Override
    public Engine onCreateEngine() {
        return new SampleEngine();
    }

    class SampleEngine extends Engine {
        /**
         * Create variables for your settings
         */
        private boolean checkedSettings = false;
        private String textToWrite = null;
        private int timesToWrite = 0;
        private boolean isVisible = false;

        private Paint paint;
        private StaticLayout staticLayout;
        private Handler redrawHandler = new Handler();
        private Runnable redrawRunnable = new Runnable() {
            @Override
            public void run() {
                draw();
            }
        };

        // Here is where all the drawing stuff happens
        private void draw() {
            SurfaceHolder holder = getSurfaceHolder();
            Canvas c = null;
            try {
                c = holder.lockCanvas();
                if (c != null) {
                    // Resets your canvas to black surface
                    c.drawColor(Color.WHITE);
                    int cW = c.getWidth();
                    int cH = c.getHeight();

                    if (textToWrite != null && timesToWrite > 0) {
                        paint = new Paint();

                        String multipleText = "";
                        for (int i = 0; i < timesToWrite; i ++) {
                            multipleText = multipleText.concat(" ").concat(textToWrite);
                        }

                        paint.setColor(Color.BLACK);
                        staticLayout = new StaticLayout(multipleText, new TextPaint(paint), cW - 10, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
                        // Move top left where to write the text
                        c.translate(10, cH / 3);
                        staticLayout.draw(c);

                        // if you want to write more stuff to canvas
                        c.restore();
                    }

                }
            } finally {
                if (c != null)
                    holder.unlockCanvasAndPost(c);
            }
            // If you have changing data then here is how you redraw
            if (isVisible) {
                // if you have a longer delay you probably want to clear other in queue to avoid multiple draws
                redrawHandler.removeCallbacks(redrawRunnable);
                redrawHandler.postDelayed(redrawRunnable, 500); // you add delay if you dont change oftent
            }
        }

        @Override
        public void onVisibilityChanged(boolean visible) {
            super.onVisibilityChanged(visible);
            isVisible = visible;
            // Called when your wallpaper is viewed or not so load settings if it is to show changes instantly
            if (isVisible) {
                final SharedPreferences preference = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
                checkedSettings = preference.getBoolean("pref_sample_checkbox", false);
                textToWrite = preference.getString("pref_text", null);
                timesToWrite = Integer.valueOf(preference.getString("pref_count", "0"));
                // Now we update the canvas
                draw();
            }
        }
    }
}


That's all of the basic foundation of creating live wallpaper with canvas. You can implement more method for the engine and the service to have more control. But this will basically give you all the initial starting point. Download the full project here