Saturday, August 27, 2011

How to create LIve Wallpaper in Android ?

1.1. Live Wallpapers

Live Wallpapers are animated, interactive backgrounds for the Android homescreen. A live wallpaper is similar to other Android applications and can use most of the same functionality.
To create a live wallpaper, you need to create a XML file which describes your wallpaper. This file should contain a description of the application and can contain a preview and a link to a preference activity which allow to customize the live wallpaper.
You also maintain a service which must extend the abstract class "WallpaperService". "WallpaperService"is the base class for all live wallpapers in the system. You must implement the method onCreateEngine() and return an object of type "Engine". This engine will handle the life cycle events, animations and drawings of the wallpaper. Here you find life cycle hooks, as for example onCreate(), onSurfaceCreated(), onVisibilityChanged(), onOffsetsChanged(), onTouchEvent() and onCommand() .
This service requires the permission "android.permission.BIND_WALLPAPER" and must be registered via an intent-filter to the action "android.service.wallpaper.WallpaperService".
The service returns in its method onCreateEngine() an instance "android.service.wallpaper.WallpaperService.Engine". This class is responsible for handling the Live Wallpaper.
You should also maintain in your "AndroidManifest.xml" that your application uses the feature "android.software.live_wallpaper" in "AndroidManifest.xml". This will prevent that your wallpaper can be installed on devices which do not support live wallpapers.

2. Android Wallpaper Example

Create a new project "de.vogella.android.wallpaper". Do not create an Activity.
Create the folder "/res/xml" and maintain the following file "mywallpaper.xml. This file contains a description of your wallpaper and a preview graphic. You can also maintain a link to an activity which allow to configure your wallpaper. This resource file will be linked to from the "AndroidManifest.xml". You could also include the "android:thumbnail attribute" which would point to a drawable which gives a smaller image of the running wallpaper.


<?xml version="1.0" encoding="UTF-8"?>
<wallpaper 
xmlns:android="http://schemas.android.com/apk/res/android"
android:thumbnail="@drawable/icon"
android:description="@string/wallpaper_description"  
android:settingsActivity="de.vogella.android.wallpaper.MyPreferencesActivity"/>
 
	
Change your "AndroidManifest.xml" to the following to define your service ""MyWallpaperService" and to maintain the uses-feature. 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
	package="de.vogella.android.wallpaper" android:versionCode="1"
	android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">


<service android:name="MyWallpaperService" android:enabled="true"
android:permission="android.permission.BIND_WALLPAPER"
android:label="Wallpaper Example ">
	<intent-filter>
<action android:name="android.service.wallpaper.WallpaperService"></action>
		</intent-filter>
	<meta-data android:name="android.service.wallpaper"
		android:resource="@xml/mywallpaper"></meta-data>
</service>
<activity android:label="@string/app_name" android:name=".MyPreferencesActivity"
	android:theme="@android:style/Theme.Light.WallpaperSettings"
	android:exported="true">
	</activity>
	</application>
	<uses-sdk android:minSdkVersion="10" />
	<uses-feature android:name="android.software.live_wallpaper"
		android:required="true"></uses-feature>

</manifest> 
 
We create the class "MyPoint" to save the elements we have drawn. 
 
				
package de.vogella.android.wallpaper;

public class MyPoint {
	String text;
	private int x;
	private int y;

	public MyPoint(String text, int x, int y) {
		this.text = text;
		this.x = x;
		this.y = y;
	}
}

			
		
Create a preference activity . Create the preference value files "prefs.xml" in the folder "res/xml".

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
	<CheckBoxPreference android:key="touch"
		android:title="Enable Touch"></CheckBoxPreference>
	<EditTextPreference android:key="numberOfCircles"
		android:title="Number of Circles"></EditTextPreference>
</PreferenceScreen>
 
 
Create the following coding for your preference activity. 
 
 
package de.vogella.android.wallpaper;

import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.PreferenceActivity;
import android.widget.Toast;

public class MyPreferencesActivity extends PreferenceActivity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		addPreferencesFromResource(R.xml.prefs);

	// We want to add a validator to the number of circles so that it only
	// accepts numbers
	Preference circlePreference = getPreferenceScreen().findPreference(
			"numberOfCircles");

		// Add the validator
	circlePreference.setOnPreferenceChangeListener(numberCheckListener);
	}

	/**
	 * Checks that a preference is a valid numerical value
	 */
	Preference.OnPreferenceChangeListener numberCheckListener = 
new OnPreferenceChangeListener() {

	@Override
	public boolean onPreferenceChange(Preference preference,
Object newValue) {
		// Check that the string is an integer
		if (newValue != null && newValue.toString().length() > 0
				&& newValue.toString().matches("\\d*")) {
			return true;
		}
		// If now create a message to the user
		Toast.makeText(MyPreferencesActivity.this, "Invalid Input",
				Toast.LENGTH_SHORT).show();
			return false;
	}
	};

} 
 
Create the following coding for the Wallpaper Service. 
 
package de.vogella.android.wallpaper;

import java.util.ArrayList;
import java.util.List;

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.view.MotionEvent;
import android.view.SurfaceHolder;

public class MyWallpaperService extends WallpaperService {

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

	private class MyWallpaperEngine extends Engine {
		private final Handler handler = new Handler();
		private final Runnable drawRunner = new Runnable() {
			@Override
			public void run() {
				draw();
			}

		};
		private List<MyPoint> circles;
		private Paint paint = new Paint();
		private int width;
		int height;
		private boolean visible = true;
		private int maxNumber;
		private boolean touchEnabled;

		public MyWallpaperEngine() {
	SharedPreferences prefs = PreferenceManager
		.getDefaultSharedPreferences(MyWallpaperService.this);
		maxNumber = Integer
			.valueOf(prefs.getString("numberOfCircles", "4"));
			touchEnabled = prefs.getBoolean("touch", false);
			circles = new ArrayList<MyPoint>();
			paint.setAntiAlias(true);
			paint.setColor(Color.WHITE);
			paint.setStyle(Paint.Style.STROKE);
			paint.setStrokeJoin(Paint.Join.ROUND);
			paint.setStrokeWidth(10f);
			handler.post(drawRunner);
		}

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

		

}
 
			@Override
		public void onSurfaceDestroyed(SurfaceHolder holder) {
			super.onSurfaceDestroyed(holder);
			this.visible = false;
			handler.removeCallbacks(drawRunner);
		}

		@Override
		public void onSurfaceChanged(SurfaceHolder holder, int format,
				int width, int height) {
			this.width = width;
			this.height = height;
			super.onSurfaceChanged(holder, format, width, height);
		}

		@Override
		public void onTouchEvent(MotionEvent event) {
			if (touchEnabled) {

				float x = event.getX();
				float y = event.getY();
				SurfaceHolder holder = getSurfaceHolder();
				Canvas canvas = null;
				try {
					canvas = holder.lockCanvas();
			if (canvas != null) {
				canvas.drawColor(Color.BLACK);
				circles.clear();
				circles.add(new MyPoint(
				String.valueOf(circles.size() + 1), x, y));
				drawCircles(canvas, circles);
				}
			} finally {
				if (canvas != null)
				holder.unlockCanvasAndPost(canvas);
			}
			super.onTouchEvent(event);
		}
	}
		private void draw() {
	SurfaceHolder holder = getSurfaceHolder();
	Canvas canvas = null;
		try {
			canvas = holder.lockCanvas();
			if (canvas != null) {
				if (circles.size() >= maxNumber) {
					circles.clear();
				}
			int x = (int) (width * Math.random());
			int y = (int) (height * Math.random());
	circles.add(new MyPoint(String.valueOf(circles.size() + 1),
						x, y));
					drawCircles(canvas, circles);
				}
			} finally {
				if (canvas != null)
					holder.unlockCanvasAndPost(canvas);
			}
			handler.removeCallbacks(drawRunner);
			if (visible) {
				handler.postDelayed(drawRunner, 5000);
			}
		}

		// Surface view requires that all elements are drawn completely
		private void drawCircles(Canvas canvas, List<MyPoint> circles) {
			canvas.drawColor(Color.BLACK);
			for (MyPoint point : circles) {
			canvas.drawCircle(point.x, point.y, 20.0f, paint);
			}
		}
	}
 
 
If you start your application and set this wallpaper as
background your emulator should similar to the following. If you have
Touch enabled via the preferences you can click on the screen to
remove the existing circles, also via the settings you can define the
number of circles which should be displayed.
	 
 
 


No comments:

Post a Comment