[TUT] Preloaded images set as phone wallpaper

This is the first request that I’ve done!

This tutorial will show you how to display some images within your application from the resources folder, you can browse through these images and choose to set one as your phones wallpaper.

Tutorial will go like this:

  • Create XML layout to let you browse your images
  • Create main activity to load these images
  • Create helper to do the hard work of setting the wallpaper
  • Add permissions to manifest

So here .. we .. go

The aim of this is to be able to browse through some pictures you have in your resources folder. Therefore your XML layout needs an ImageView to show the picture and some buttons to go backwards forwards and set it as the wallpaper. I’ve used weighting on the buttons don’t worry if you haven’t seen it before it’s easy. The three buttons are in a linearlayout that is the width of the screen, so thats 100% width. Each button’s width is set to 0dip this means ithe buttons width is zero! However when you then add the layout_weight attribute it’s width becomes a percentage. So the previous and next buttons take up 25% of the width and the set wallpaper button the other 50%! Told you it was easy.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <Button
            android:layout_width="0dip"
            android:layout_height="wrap_content"
            android:layout_weight="25"
            android:onClick="gotoPreviousImage"
            android:padding="10dip"
            android:text="&lt;" />

        <Button
            android:layout_width="0dip"
            android:layout_height="wrap_content"
            android:layout_weight="50"
            android:onClick="setAsWallpaper"
            android:padding="10dip"
            android:text="Set as Background" />

        <Button
            android:layout_width="0dip"
            android:layout_height="wrap_content"
            android:layout_weight="25"
            android:onClick="gotoNextImage"
            android:padding="10dip"
            android:text=">" />
    </LinearLayout>

    <ImageView
        android:id="@+id/backgroundPreview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:contentDescription="Preview of the wallpaper to choose"
        android:scaleType="fitXY" />

</LinearLayout>

Next comes your Main activity. This will be in charge of letting you browse through the images, displaying them on the screen. For this tutorial the images haven’t been cached in the activity, only the resource Id has, this means each time you browse to a new image the system has to go fetch it out of your resource folder. The benefits of this are you don’t have a massive memory footprint in your activity (a big list holding images), the downside is .. you don’t have a big list holding images. So it’s give and take, the more images you want to browse through, the more the current way it is written makes sense. The other special item in this activity is the callback mechanism from the heavy lifter. Our heavy lifter is there so we don’t do too many complex operations on the main thread, they are delegated to another thread so the user can carry on browsing. When these threads have finished their task they need to inform the activity, this is where the handler comes in. It’s all commented so your bound to get it:

MainActivity.java

package com.blundell.tutorial.ui.phone;

import static com.blundell.tutorial.util.HeavyLifter.FAIL;
import static com.blundell.tutorial.util.HeavyLifter.SUCCESS;

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

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;

import com.blundell.tutorial.R;
import com.blundell.tutorial.util.HeavyLifter;

public class MainActivity extends Activity {
	/** A list containing the resource identifiers for all of our selectable backgrounds */
	private static final List<Integer> backgrounds = new ArrayList<Integer>();
	/** The total number of backgrounds in the list */
	private static final int TOTAL_IMAGES;
	/** Instantiate the list statically, so this will be done once on app load, also calculate the total number of backgrounds */
	static {
		backgrounds.add(R.drawable.background1);
		backgrounds.add(R.drawable.background2);
		backgrounds.add(R.drawable.background3);
		
		// We -1 as lists are zero indexed (0-2 is a size of 3) - we'll mke use of this to implement a browsing loop
		TOTAL_IMAGES = (backgrounds.size() - 1);
	}
	
	
	/** the state of what wallpaper is currently being previewed */
	private int currentPosition = 0;
	/** our image wallpaper preview */
	private ImageView backgroundPreview;
	/** A helper class that will do the heavy work of decoding images and actually setting the wallpaper */
	private HeavyLifter chuckNorris;
	
	
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		backgroundPreview = (ImageView) findViewById(R.id.backgroundPreview);
		
		// Set the default image to be shown to start with
		changePreviewImage(currentPosition);
		
		// Load are heavy lifter (goes and does work on another thread), to get a response after the lifters thread
		// has finished we pass in a Handler that will be notified when it completes
		chuckNorris = new HeavyLifter(this, chuckFinishedHandler);
	}
	
	/**
	 * Called from XML when the previous button is pressed
	 * Decrement the current state position
	 * If the position is now less than 0 loop round and show the last image (the array size)
	 * @param v
	 */
	public void gotoPreviousImage(View v) {
		int positionToMoveTo = currentPosition;
		positionToMoveTo--;
		if(positionToMoveTo < 0){
			positionToMoveTo = TOTAL_IMAGES;
		}
		changePreviewImage(positionToMoveTo);
	}

	/**
	 * Called from XML when the set wallpaper button is pressed
	 * Thie retrieves the id of the current image from our list
	 * It then asks chuck to set it as a wallpaper!
	 * The chuckHandler will be called when this operation is complete
	 * @param v
	 */
	public void setAsWallpaper(View v) {
		int resourceId = backgrounds.get(currentPosition);
		chuckNorris.setResourceAsWallpaper(resourceId);
	}

	/**
	 * Called from XML when the next button is pressed
	 * Increment the current state position
	 * If the position is now greater than are array size loop round and show the first image again
	 * @param v
	 */
	public void gotoNextImage(View v) {
		int positionToMoveTo = currentPosition;
		positionToMoveTo++;
		if(currentPosition == TOTAL_IMAGES){
			positionToMoveTo = 0;
		} 

		changePreviewImage(positionToMoveTo);
	}
	
	/**
	 * Change the currently showing image on the screen
	 * This is quite an expensive operation as each time the system
	 * has to decode the image from our resources - alternatives are possible (a list of drawables created at app start)
	 * @param pos the position in {@link MainActivity#backgrounds} to select the image from
	 */
	public void changePreviewImage(int pos) {
		currentPosition = pos;
		backgroundPreview.setImageResource(backgrounds.get(pos));
		Log.d("Main", "Current position: "+pos);
	}
	
	/**
	 * This is the handler that is notified when are HeavyLifter is finished doing an operation
	 */
	private Handler chuckFinishedHandler = new Handler(){
		@Override
		public void handleMessage(Message msg) {
			switch(msg.what){
			case SUCCESS:
				Toast.makeText(MainActivity.this, "Wallpaper set", Toast.LENGTH_SHORT).show();
				break;
			case FAIL:
				Toast.makeText(MainActivity.this, "Uh oh, can't do that right now", Toast.LENGTH_SHORT).show();
				break;
			default:
				super.handleMessage(msg);
			}
		}
	};
	
}

Once we have an activity we want to create the lifter so we can actually set the wallpaper. The heavy lifter ( I couldn’t think of a better name), gets a hold of the systems wallpaper manager and uses this to set the wallpaper. We do it in a separate thread for the reasons discussed above.

HeavyLifter.java

package com.blundell.tutorial.util;

import java.io.IOException;

import android.app.WallpaperManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.util.Log;

/**
 * <b>This class uses Threads</b>
 * An alternative to this class would be to use an ASyncTask
 * @author blundell
 *
 */
public class HeavyLifter {

	public static final int SUCCESS = 0;
	public static final int FAIL = 1;

	private final Context context;
	private final Handler callback;
	private WallpaperManager manager;

	/**
	 * Setup the HeavyLifter
	 * @param context the context we are running in - typically an activity
	 * @param callback the handler you want to be notified when we finish doing an operation
	 */
	public HeavyLifter(Context context, Handler callback) {
		this.context = context;
		this.callback = callback;
		this.manager = (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE);
	}

	/**
	 * Takes a resource id of a file within our /res/drawable/ folder<br/>
	 * It then spawns a new thread to do its work on<br/>
	 * The reource is decoded and converted to a byte array<br/>
	 * This array is passed to the system which can use it to set the phones wallpaper<br/>
	 * Upon completion the callback handler is sent a message with eith {@link HeavyLifter#SUCCESS} or {@link HeavyLifter#FAIL}
	 * 
	 * @param resourceId id of a file within our /res/drawable/ folder
	 */
	public void setResourceAsWallpaper(final int resourceId) {
		new Thread() {
			@Override
			public void run() {
				try {
					manager.setBitmap(getImage(resourceId));
					callback.sendEmptyMessage(SUCCESS);
				} catch (IOException e) {
					Log.e("Main", "Cant set wallpaper");
					callback.sendEmptyMessage(FAIL);
				}
			}
		}.start();
	}

	/**
	 * Decodes a resource into a bitmap, here it uses the convenience method 'BitmapFactory.decodeResource', but you can decode
	 * using alternatives these will give you more control over the size and quality of the resource.
	 * You may need certain size resources within each of your /hdpi/ /mdpi/ /ldpi/ folders in order
	 * to have the quality and look you want on each different phone.
	 */
	private Bitmap getImage(int resourceId) {
		Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resourceId, null);
		Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, manager.getDesiredMinimumWidth(), manager.getDesiredMinimumHeight(), true);
		bitmap.recycle();
		bitmap = null;
		return scaledBitmap;
	}
}

Yayy all done, wait … it doesn’t work! Ah those bleeding manifest permissions! Never forget 🙂

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="com.blundell.tutorial"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="8" />

    <uses-permission android:name="android.permission.SET_WALLPAPER" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".ui.phone.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Here’s the source project code: Downlod Set Wallpaper Tutorial Source Code

Hope you enjoyed it, please say thanks if your using it.

14 thoughts on “[TUT] Preloaded images set as phone wallpaper

  1. this.manager = (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE);

Comments are closed.