Android Tutorial Android Live Wallpaper tutorial

Using the new LiveWallpaper library you can now create your own live wallpapers. The user can set live wallpapers by long pressing on the home screen and choosing Wallpapers - Live Wallpapers.

Creating a live wallpaper is not too difficult.

A service is responsible for handling the wallpaper events and drawing the wallpaper.

There can be several instances of the same wallpaper at the same time. For example the user can set your wallpaper as the home screen wallpaper and also see a demo of your wallpaper in the wallpaper preview dialog.

LiveWallpaper library contains two objects: LWManager and LWEngine.
LWManager is responsible for raising the events.
The first parameter of each event is of type LWEngine. LWEngine represents a specific wallpaper instance.
LWEngine includes a Canvas property which is used to draw the wallpaper.
When you finish drawing you must call LWEngine.Refresh or LWEngine.RefreshAll. Otherwise the drawing will not appear.

The following example draws a blue background. Red circles are drawn when the user touches the screen:
B4X:
Sub Process_Globals
   Dim lwm As LWManager
End Sub

Sub Service_Create
   lwm.Initialize("lwm", True)
End Sub

Sub LWM_SizeChanged (Engine As LWEngine)
   Engine.Canvas.DrawColor(Colors.Blue)
   Engine.RefreshAll
End Sub

Sub LWM_Touch (Engine As LWEngine, Action As Int, X As Float, Y As Float)
   Engine.Canvas.DrawCircle(X, Y, 20dip, Colors.Red, True, 0)
   Engine.Rect.Left = X - 20dip
   Engine.Rect.Right = X + 20dip
   Engine.Rect.Top = Y - 20dip
   Engine.Rect.Bottom = Y + 20dip
   Engine.Refresh(Engine.Rect)
End Sub
SS-2011-11-15_11.11.45.png


As there could be several different engines, it is convenient to work with the local engine.
LWEngine includes a Tag property which you can use to store data specific to that engine.

LWManager includes an internal timer that you can use if you need to do animations. The Tick event will only be raised for visible wallpapers. This is important to conserve battery.

For example the following code draws the time on the wallpaper:
B4X:
Sub Process_Globals
   Dim lwm As LWManager
End Sub

Sub Service_Create
   lwm.Initialize("lwm", True)
   lwm.StartTicking(1000) 'tick every second
End Sub

Sub LWM_Tick (Engine As LWEngine)
   Engine.Canvas.DrawColor(Colors.Black) 'Erase the background
   Engine.Canvas.DrawText(DateTime.Time(DateTime.Now), _
      300dip, 100dip, Typeface.DEFAULT_BOLD, 40, Colors.White, "LEFT")
   Engine.RefreshAll
End Sub
SS-2011-11-15_11.23.06.png


Offsets
On most devices the wallpaper virtual size is wider than a single screen. When the user moves to a different screen the offset changes.
You can use the OffsetChanged event to handle those changes.
LWEngine.FullWallpaperWidth / Height return the full size of the wallpaper.
LWEngine.CurrentOffsetX / Y return the current position.
Note that the wallpaper scrolls less than the foreground layer with the icons.

The LiveWallpaperImage demonstrates how to use those properties to display an image over the full wallpaper.

LWManager events

SizeChanged - Raised when the engine is first ready and when the screen size changes (for example when the orientation changes).
VisibilityChanged - Raised when a wallpaper becomes visible or invisible.
Touch - Raised when the user touches the wallpaper.
Tick - The internal timer tick event.
OffsetChanged - Raised when the wallpaper offsets change.
EngineDestroyed - Raised when an engine is destroyed.

Configuration
Live wallpapers require some configuration.
1. You should add the inner service declaration to the manifest editor (Project -> Manifest Editor):
B4X:
AddApplicationText(
<!-- ******** Add the internal service declaration - you can change android:label  ******* -->
<service
        android:label="My Livewallpaper"
        android:name="anywheresoftware.b4a.objects.WallpaperInternalService"
        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/wallpaper" />
</service>
)
It is also recommended to change minSdkVersion to 7 (at the beginning of the manifest editor code).

2. Add wallpaper.xml to Objects\res\xml folder and set it to be readonly (otherwise it will be deleted during compilation).
You can find this file in both attached examples.

3. Your service must be named WallpaperService. Note that there is an additional service inside the library named anywheresoftware.b4a.objects.WallpaperInternalService. The internal service connects to your service.

Examples
LiveWallpaperImage - The user can select an image from the activity. This image is set as the wallpaper. When the user changes the wallpaper offset the image is updated accordingly.

LiveWallpaperBall - A bouncing smiley. Clicking on the smiley will change its direction.
Before running the examples you need to set Object\res\xml\wallpaper.xml to be readonly. Otherwise it will be deleted.

Note that it is recommended to test live wallpapers in Release mode (the wallpaper will fail in debug mode when the process is started by the OS).
 

Attachments

  • LiveWallpaperBall.zip
    8.3 KB · Views: 2,235
  • LiveWallpaperImage.zip
    8 KB · Views: 2,446
Last edited:

mcmanu

Active Member
Licensed User
Longtime User
Issue 4.0.4

When i Programm a Livewallpaper and want to set it, it crashes every time under Ics 4.0.4
How can i fix that?
 

mcmanu

Active Member
Licensed User
Longtime User
Check the logs and find the error message.


Thats the Example that you have Posted -->

Logs


LogCat connected to: B4A-Bridge: samsung GT-I9100-359585041223105
--------- beginning of /dev/log/main
--------- beginning of /dev/log/system
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
** Service (service1) Create **
** Service (service1) Start **
Connected to B4A-Bridge (Wifi)
Installing file.
** Activity (main) Pause, UserClosed = false **
PackageAdded: package:anywheresoftware.b4a.samples.livewallpaperball
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
** Activity (main) Pause, UserClosed = true **
** Activity (main) Resume **
** Activity (main) Pause, UserClosed = true **
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
** Activity (main) Pause, UserClosed = true **
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
** Activity (main) Pause, UserClosed = true **
** Service (wallpaperservice) Create **
wallpaperservice_service_create (java line: 234)
java.lang.NoSuchFieldError: anywheresoftware.b4a.BA.isService
at anywheresoftware.b4a.objects.WallpaperInternalService$LWManager.Initialize(WallpaperInternalService.java:62)
at anywheresoftware.b4a.samples.livewallpaperball.wallpaperservice._service_create(wallpaperservice.java:234)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:165)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:149)
at anywheresoftware.b4a.samples.livewallpaperball.wallpaperservice.onCreate(wallpaperservice.java:38)
at android.app.ActivityThread.handleCreateService(ActivityThread.java:2264)
at android.app.ActivityThread.access$1600(ActivityThread.java:127)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1212)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4511)
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:980)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:747)
at dalvik.system.NativeStart.main(Native Method)
** Service (wallpaperservice) Create **
wallpaperservice_service_create (java line: 234)
java.lang.NoSuchFieldError: anywheresoftware.b4a.BA.isService
at anywheresoftware.b4a.objects.WallpaperInternalService$LWManager.Initialize(WallpaperInternalService.java:62)
at anywheresoftware.b4a.samples.livewallpaperball.wallpaperservice._service_create(wallpaperservice.java:234)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:165)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:149)
at anywheresoftware.b4a.samples.livewallpaperball.wallpaperservice.onCreate(wallpaperservice.java:38)
at android.app.ActivityThread.handleCreateService(ActivityThread.java:2264)
at android.app.ActivityThread.access$1600(ActivityThread.java:127)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1212)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4511)
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:980)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:747)
at dalvik.system.NativeStart.main(Native Method)
 

bloxa69

Active Member
Licensed User
Longtime User
Erel,
I'm still struggling to make LWP smooth and anti-aliased. Even though I load bitmaps without scaling, they are still not smooth enough, especially the ones I need to rotate. I've tried this, but it doesn't work:
B4X:
Sub SetAntiAlias (c As Canvas)
    Dim r As Reflector
    Dim NativeCanvas As Object
    r.Target = c
    NativeCanvas = r.GetField("canvas")
    Dim PaintFlagsDrawFilter As Object
    PaintFlagsDrawFilter = r.CreateObject2("android.graphics.PaintFlagsDrawFilter", _
        Array As Object(0, 1), Array As String("java.lang.int", "java.lang.int"))
    r.Target = NativeCanvas
    r.RunMethod4("setDrawFilter", Array As Object(PaintFlagsDrawFilter), Array As String("android.graphics.DrawFilter"))
End Sub

I found a piece of Java code to enable antialiasing on LWP. I don't know if there is a way to translate it to B4A script anyhow?

B4X:
package com.mydomain.wallpapers.mywallpaper;

import rajawali.wallpaper.Wallpaper;
import android.content.Context;

public class MyWallpaper extends Wallpaper {
    private MyWallpaperRenderer mRenderer;

    public Engine onCreateEngine() {
        mRenderer = new MyWallpaperRenderer(this);

        // -- the last parameter (true) indicates that we want to use multi-sampling / anti-aliasing
        return new WallpaperEngine(this.getSharedPreferences(SHARED_PREFS_NAME,
                Context.MODE_PRIVATE), getBaseContext(), mRenderer, true);
    }
}
 

trebun

Member
Licensed User
Longtime User
Mh.

I will implement a settings page for my Live Wallpaper.

I read this tiny help:

http://www.b4x.com/forum/basic4andr...roid-live-wallpaper-tutorial-4.html#post77786

I do:

1.) Create a wallpaper.xml:
B4X:
<?xml version="1.0" encoding="utf-8"?>
<wallpaper xmlns:android="http://schemas.android.com/apk/res/android"
  android:thumbnail="@drawable/icon" android:description="@string/description"
  android:settingsActivity="de.duzka.livewallpaper.settings" 
/>

2.)Make the wallpaper.xml read-only!

3.) Create a new activity module, called "Settings"

4.) Load my layout:

B4X:
Sub Activity_Create(FirstTime As Boolean)
   'Do not forget to load the layout file created with the visual designer. For example:
   Activity.LoadLayout("settingsLayout")

End Sub

See the attachment, my wallpaper gives a force close... :(

Whats wrong...?

Oh, I use LiveWallpaper 1.01 :)

Regards,
trebun
 

Attachments

  • lwp.jpg
    lwp.jpg
    63.7 KB · Views: 343

trebun

Member
Licensed User
Longtime User
Look at the unfiltered logs on the IDE to see the cause of that error.

Mh.

Its hard to understand:

Permission Denial: starting Intent { cmp=net.flyercircle.livewallpaper/.settings (has extras) [...]

Maybe my error, but how can I fix it?

Full Error:
START u0 {cmp=net.flyercircle.livewallpaper/.settings (has extras)} from pid 4927
Permission denied: checkComponentPermission() owningUid=10104
Shutting down VM
threadid=1: thread exiting with uncaught exception (group=0x40d84930)
Permission Denial: starting Intent { cmp=net.flyercircle.livewallpaper/.settings (has extras) } from ProcessRecord{4258cb70 4927:com.android.wallpaper.livepicker/u0a10031} (pid=4927, uid=10031) not exported from uid 10104
FATAL EXCEPTION: main
java.lang.IllegalStateException: Could not execute method of the activity
at android.view.View$1.onClick(View.java:3597)
at android.view.View.performClick(View.java:4202)
at android.view.View$PerformClick.run(View.java:17340)
at android.os.Handler.handleCallback(Handler.java:725)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5039)
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:793)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at android.view.View$1.onClick(View.java:3592)
... 11 more
Caused by: java.lang.SecurityException: Permission Denial: starting Intent { cmp=net.flyercircle.livewallpaper/.settings (has extras) } from ProcessRecord{4258cb70 4927:com.android.wallpaper.livepicker/u0a10031} (pid=4927, uid=10031) not exported from uid 10104
at android.os.Parcel.readException(Parcel.java:1425)
at android.os.Parcel.readException(Parcel.java:1379)
at android.app.ActivityManagerProxy.startActivity(ActivityManagerNative.java:1870)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1412)
at android.app.Activity.startActivityForResult(Activity.java:3370)
at android.app.Activity.startActivityForResult(Activity.java:3331)
at android.app.Activity.startActivity(Activity.java:3566)
at android.app.Activity.startActivity(Activity.java:3534)
at com.android.wallpaper.livepicker.LiveWallpaperPreview.configureLiveWallpaper(LiveWallpaperPreview.java:117)
... 14 more
Force finishing activity com.android.wallpaper.livepicker/.LiveWallpaperPreview

GC_CONCURRENT freed 448K, 5% free 11353K/11832K, paused 4ms+6ms, total 39ms
Activity pause timeout for ActivityRecord{41cb7bd0 u0 com.android.wallpaper.livepicker/.LiveWallpaperPreview}

Process com.google.android.apps.maps:FriendService (pid 4697) has died.

Sending signal. PID: 4927 SIG: 9

Process com.android.wallpaper.livepicker (pid 4927) has died.

WIN DEATH: Window{41d92260 u0 com.android.wallpaper.livepicker/com.android.wallpaper.livepicker.LiveWallpaperPreview}
WIN DEATH: Window{41c70330 u0 com.android.wallpaper.livepicker/com.android.wallpaper.livepicker.LiveWallpaperActivity}
channel '41c70330 com.android.wallpaper.livepicker/com.android.wallpaper.livepicker.LiveWallpaperActivity (server)' ~ Consumer closed input channel or an error occurred. events=0x9
channel '41c70330 com.android.wallpaper.livepicker/com.android.wallpaper.livepicker.LiveWallpaperActivity (server)' ~ Channel is unrecoverably broken and will be disposed!
Attempted to unregister already unregistered input channel '41c70330 com.android.wallpaper.livepicker/com.android.wallpaper.livepicker.LiveWallpaperActivity (server)'
WIN DEATH: Window{41d94520 u0 Media:com.android.wallpaper.livepicker/com.android.wallpaper.livepicker.LiveWallpaperPreview}
Start proc com.android.wallpaper.livepicker for activity com.android.wallpaper.livepicker/.LiveWallpaperActivity: pid=4967 uid=10031 gids={50031, 1028}

channel '41deaef0 anywheresoftware.b4a.objects.WallpaperInternalService (client)' ~ Publisher closed input channel or an error occurred. events=0x9
Force-removing child win Window{41deaef0 u0 anywheresoftware.b4a.objects.WallpaperInternalService} from container Window{41d92260 u0 com.android.wallpaper.livepicker/com.android.wallpaper.livepicker.LiveWallpaperPreview}
GC_CONCURRENT freed 81K, 2% free 9214K/9328K, paused 6ms+2ms, total 27ms

Have anyone a solution for me?

Regards,
trebun

//Edit: Another question: Is it the right way to do every hour something with "LWM.StartTicking(3600000)" or is another solution better?
 
Last edited:

Erel

B4X founder
Staff member
Licensed User
Longtime User
Add this line to the manifest editor:
B4X:
SetActivityAttribute(settings, android:exported, true)

Another question: Is it the right way to do every hour something with "LWM.StartTicking(3600000)" or is another solution better?
It will work as long as Android doesn't kill your process. It will probably be better to use a service and call StartServiceAt.
 

trebun

Member
Licensed User
Longtime User
Add this line to the manifest editor:
B4X:
SetActivityAttribute(settings, android:exported, true)

:sign0098:

Thank you, it works! :)

It will work as long as Android doesn't kill your process. It will probably be better to use a service and call StartServiceAt.

Ok - I will try!

Big thanks and "Guten Rutsch" [ I don't know what it is in english ;-) ]
 

trebun

Member
Licensed User
Longtime User
@Erel

StartServiceAt is my friend - Thank you! :)

Another question about the preview: If I change some settings from my settingscreen, how can I update the preview"-view" from the live wallpaper?

Regards,
trebun
 

FJS

Active Member
Licensed User
Longtime User
Access Wallpaper menu directly

Hello,
Is there a way to access directly the livewallpaper menu through a button and not having to go over the hold finger on the screen --> LiveWallpapers --> Pick Wallpaper... process? I've seen some LiveWallpapers on the market that get you to the set as wallpaper screen directly through a button. Skipping all the Wallpaper selection process.
Thanks,
Jorge
 

trebun

Member
Licensed User
Longtime User
Hello,
Is there a way to access directly the livewallpaper menu through a button and not having to go over the hold finger on the screen --> LiveWallpapers --> Pick Wallpaper... process? I've seen some LiveWallpapers on the market that get you to the set as wallpaper screen directly through a button. Skipping all the Wallpaper selection process.
Thanks,
Jorge

Yes,
take a look:

http://www.b4x.com/forum/basic4andr...id-live-wallpaper-tutorial-11.html#post124053

http://www.b4x.com/forum/basic4andr...id-live-wallpaper-tutorial-11.html#post124080
 

trebun

Member
Licensed User
Longtime User
Mkay, I have some troubles to stretch the wallpaper background.

The LiveWallpaperImage example does the same effect - but I don't know about the usage from OffsetX,Y... :/

Maybe there is an example to scale the wallpaper with the right ratio?

My Code (from the LiveWallpaperImage):
B4X:
Sub LWM_OffsetChanged (Engine As LWEngine)
   If Flyer.IsInitialized  Then
      Engine.Rect.Left = -Engine.CurrentOffsetX 
      Engine.Rect.Top = -Engine.CurrentOffsetY 
      Engine.Rect.Right = -Engine.CurrentOffsetX + Engine.FullWallpaperWidth 
      Engine.Rect.Bottom = -Engine.CurrentOffsetY + Engine.FullWallpaperHeight
      Engine.Canvas.DrawBitmap(Flyer, Null, Engine.Rect)
      
      Log("Engine.Rect.Left: " & Engine.CurrentOffsetX)
      Log("Engine.Rect.Top: " & Engine.CurrentOffsetY)
      Log("Engine.Rect.Right: " & (Engine.CurrentOffsetX + Engine.FullWallpaperWidth))
      Log("Engine.Rect.Bottom: " & (Engine.CurrentOffsetY + Engine.FullWallpaperHeight))
   Else
      Engine.Canvas.DrawColor(Colors.Black)
      Engine.Canvas.DrawText(AResMap.Get("other_placeholder_on_screen"), 120dip, 120dip, Typeface.DEFAULT_BOLD, 30, Colors.White, "LEFT")
   End If
   
   Engine.RefreshAll
End Sub

Called when a new flyer is downloaded. :/

Regards,
trebun
 

Attachments

  • stretch_wallpaper.jpg
    stretch_wallpaper.jpg
    92.3 KB · Views: 354
Top