Libgdx Livewallpapers are here

Some nice guy called Patrick Nagelschmidt contributed a wallpaper backend. After much refucktoring, i was able to make it work seamlessly with the rest of libgdx. Here are the instructions. Note that input is still a bit of a WIP, and i didn’t test on a multitude of devices yet. Maybe you guys can help :) The nightlies should be ready in half an hour, they contain the new classes in the Android backend. Also, i extended gdx-tests-android to also install a livewallpaper that displays the WaterRipples test, see LiveWallpaper.java, LiveWallpaperSettings.java which is just dummy for a settings Activity, livewallpaper.xml which declares metadata for the wallpaper, and finally the modified AndroidManifest.xml.

The starter class for a livewallpaper is called AndroidLiveWallpaperService, here’s an example:

package com.mypackage;
 
// imports snipped for brevity 
 
public class LiveWallpaper extends AndroidLiveWallpaperService {
	@Override
	public ApplicationListener createListener () {
		return new MyApplicationListener();
	}
 
	@Override
	public AndroidApplicationConfiguration createConfig () {
		return new AndroidApplicationConfiguration();
	}
 
	@Override
	public void offsetChange (ApplicationListener listener, float xOffset, float yOffset, float xOffsetStep, float yOffsetStep,
		int xPixelOffset, int yPixelOffset) {
		Gdx.app.log("LiveWallpaper", "offset changed: " + xOffset + ", " + yOffset);
	}
}

The methods createListener() and createConfig() will be called when your livewallpaper is shown in the picker or when it is created to be displayed on the home screen.

The offsetChange() method is scaled when the user swipes through screens on the home screen and tells you by how much the screen is offset from the center screen. This method will be called on the rendering thread, so you don’t have to synchronize anything.

In addition to a starter class, you also have to create an XML file describing your wallpaper. Let’s call that livewallpaper.xml. Create a folder called xml/ in your Android project’s res/ folder and put the file in there (res/xml/livewallpaper.xml). Here’s what to put into that file:

<?xml version="1.0" encoding="UTF-8"?>
<wallpaper
       xmlns:android="http://schemas.android.com/apk/res/android"  
       android:thumbnail="@drawable/ic_launcher"
       android:description="@string/description"
       android:settingsActivity="com.mypackage.LivewallpaperSettings"/>

This defines the thumbnail to be displayed for your LWP in the picker, the description and an Activity that will be displayed when the user hits “Settings” in the LWP picker. This should be just a standard Activity that has a few widgets to change settings such as the background color and similar things. You can store those settings in SharedPreferences and load them later in your LWPs ApplicationListener via Gdx.app.getPreferences().

Finally, you’ll need to add things to your AndroidManifest.xml file. Here’s an example for an LWP with a simple settings Activity:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.mypackage"
      android:versionCode="1"
      android:versionName="1.0"
      android:installLocation="preferExternal">
	<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="14"/>	
	<uses-feature android:name="android.software.live_wallpaper" />
 
	<application android:icon="@drawable/icon" android:label="@string/app_name">
		<activity android:name=".LivewallpaperSettings" 
				  android:label="Livewallpaper Settings"/>
 
		<service android:name=".LiveWallpaper"
            android:label="@string/app_name"
            android:icon="@drawable/icon"
            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/livewallpaper" />
        </service>				  	
	</application>
</manifest>

The manifest defines:

  • it uses the live wallpaper feature, see .
  • a permission to be allowed to bind the wallpaper, see android:permission
  • the settings activity
  • the livewallpaper service, pointing at the livewallpaper.xml file, see meta-data

Note that livewallpapers are only supported starting from Android 2.1 (SDK level 7).

LWPs have some limitations concerning touch input. In general only tap/drop will be reported. If you want full touch you can see the AndroidApplicationConfiguration#getTouchEventsForLiveWallpaper flag to true to receive full multi-touch events.

Please test this thing and report any issues you have. Chances are that OpenGL context and surfaceview handling might be broken on some devices, which seems to be a problem of all OpenGL based LWP implementations. I couldn’t find an issue yet.

  • https://www.happydroids.com Phil Plante

    Can we use the non-continuous rendering to help save on battery life? Live Wallpapers via OpenGL seem like a great way to suck away precious battery life.

  • semtiko

    Thank you very much!
    I have some free lwp (about 50-60 installs per day) and i will try port it to libgdx right now and collect bug reports :D Maybe this will help.

  • http://badlogicgames.com Mario

    Yes, non-continuous rendering should work (haven’t tested it yet).

  • Silverwolf

    Thankyou, I was just discussing live wallpaper ideas with my wife a couple of days ago. It may be my next project.

  • http://alkalinelabs.net Alkaline Labs

    This will be awesome!

  • MadGorilla

    Seems that I can’t get it to run with opengl es 2.0. Tested on Samsung Galaxy S and SII. Has anyone gotten it to work with opengl es 2.0?

  • MadGorilla

    My bad, got it to work

  • http://theshank.tumblr.com Shashank Sharma

    This is friggin awesome !! been waiting for this for some time… Now i should get back to that LWP idea in my head :)
    Thank you so much

  • http://www.teamblubee.com blubee

    this is a really nice addition, just might be what i needed to bring me back to the dark side?
    Let’s get the testing!

  • dosse91

    hi, how do i know if i’m in preview mode? there’s no isPreview method, and offsetChange xOffset reports 0 when in preview, screwing up the preview

  • ghd214

    I will have a try , thanks very much

  • majlan

    Hello, could someone provide complete project example? I don’t understand these pieces of code and when I looked at the tests it didn’t help me too. I can start the app but all I see is black screen and name of the app. It seems that my main project isn’t called.

    I have an Android starter class which calls startService (should be activity? but LiveWallpaper isn’t subclass of Activity) on LiveWallpaper which creates listener like return new TestWallpaper();

    Example here http://code.google.com/p/test-wallpaper/source/browse/

  • majlan

    EDIT: So I made it work now – I run the app and I can see some graphics but how to make live wallpaper from it?

  • majlan

    I’ve finally made it work, you can find new version in repository from first post but I still have one problem – when I start the Wallpaper, following exception occurs:

    [code]01-05 20:31:53.102: ERROR/AndroidRuntime(3681): FATAL EXCEPTION: main
    java.lang.RuntimeException: Unable to resume activity {com.reinto.wallpaper.test/com.reinto.wallpaper.test.AndroidStarter}: java.lang.NullPointerException
    at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2823)
    at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2862)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2245)
    at android.app.ActivityThread.access$600(ActivityThread.java:139)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1262)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:156)
    at android.app.ActivityThread.main(ActivityThread.java:4977)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
    at dalvik.system.NativeStart.main(Native Method)
    Caused by: java.lang.NullPointerException
    at com.badlogic.gdx.backends.android.AndroidApplication.onResume(AndroidApplication.java:247)
    at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1236)
    at android.app.Activity.performResume(Activity.java:4620)
    at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2807)
    ... 12 more
    [/code]

    But I can continue with selecting the wallpaper and I can make my wallpaper active. However this error is bothering.

  • llonni

    This live wallpaper backend has a problem noted in: http://code.google.com/p/libgdx/issues/detail?id=721

    Test
    Go to “live wallpapers” list, select the live wallpaper and press set before the preview is loaded

    Description (copy&paste)

    The problems that arise when creating a GLSurfaceView for the livewallpaper are related with how the LWP service works.

    You have a wallpaper service and a engine.
    When the LWP is selected in the list of wallpapers, the service creates the first engine in preview mode (can be exactly the same as non-preview). A surfaceHolder is given to the engine, that has to create a glview accordingly using that surfaceholder. When the wallpaper is finally selected as final background, the same service is still running and it ask to create another engine with a different surfaceholder, while the previous is in the process of being terminated.
    For this reason, any static variables “inside” the engine and its variables will probably conflict between both engines, you must take that into account. The surfaceHolder and the static variables are things that currently most GLSurfaceView implementations will fail to suffice.

    I like your work, please try to fix the problem.

  • http://gplus.to/mars3142 Peter Siegmund

    Thanks for this advice. Now, I know, that I can’t create a really cool LWP, because of this issue. Maybe, I’ll try it later again…