Phone 8 dynamic fractal lock screen background

Tags: Windows Phone 8, WP8, WinRT, Fractal, Mandelbrot

A new feature of Windows Phone 8 is you can have application that can change the lock screen background.

To tell the OS your app is a lock screen background provider, you have to add an Extension in the WMAppManifest.xml, just after the <Token> area :

<Extensions>
      <Extension ExtensionName="LockScreen_Background" ConsumerID="{111DFF24-AA15-4A96-8006-2BFF8122084F}" 
TaskID="_default" /> </Extensions>

Now your app should appears as background provider for the lock screen :

 

To set the background, your application must be called. You can see if it was called from the settings screen by checking if the NavigationContext querystring contains a key “WallpaperSettings” with value 1.

if (NavigationContext.QueryString.ContainsKey("WallpaperSettings") 
      && (NavigationContext.QueryString["WallpaperSettings"] == "1"))
{
    // called from settings screen, do something if needed
}

So you can, for instance, navigate automatically to a special screen (like a background settings screen) if your app is called from settings.

When your application is launched, you can also check if it is the current lock screen provider. And if not, you can ask the user to set it as lock screen provider.

var isprovider = LockScreenManager.IsProvidedByCurrentApplication;

if (!isprovider
{   
    isprovider = await LockScreenManager.RequestAccessAsync() == LockScreenRequestResult.Granted;
}

if (!isprovider)
    return;

SetBackground();

So I am checking if I am the current provider. If not, I ask user to set me as the provider. If it is accepted, I set the lockscreen background to a picture in the package.

Change the background

To change the background, you must use LockScreen.SetImageUri. The URI must be an absolute path to an image in the isolated storage or the application install path.

LockScreen.SetImageUri("ms-appdata:///local/MyBackground.jpg");

You can have the current lock screen background using LockScreen.GetImageUri :

var currentimageuri = LockScreen.GetImageUri();

Default background

Maybe, for whatever reason, your app doesn’t have a background ready when the phone is asking for it. Or maybe the application wasn’t launched. Hopefully, you can set a default wallpaper that will be used in those cases. Just add a jpeg called “DefaultLockScreen.jpg” in the application root. This image will be used as background when your application is selected as provider until the application changes it :

Be a good citizen

You can’t “undo” being a lock screen background provider yourself. So it is good practice to have a button on your app redirecting to the setting screen, so the user can select another provider. Here is how to do so :

await Windows.System.Launcher.LaunchUriAsync(new Uri("ms-settings-lock:"));

Dynamic background

It is nice but…what if you want, like the Bing background, change the background regularly ?

Here comes Periodic Agent !

The idea is your main application will launch a periodic agent that will change the background.

Add a new Periodic Agent

In the agent OnInvoke method, get or create your wallpaper, save it in your local folder and change the lock screen background URI.

Example from my sample at the end of the post :

private readonly string _fileName1 = "Background1.jpg";
private readonly string _fileName2 = "Background2.jpg";

private static int _width = 480;
private static int _height = 800;

private readonly string _uriBase = "ms-appdata:///local/{0}";

protected override async void OnInvoke(ScheduledTask task)
{
  try
  {
    // read the file. To be sure latest color read.
    await ReadSettings();

    if (LockScreenManager.IsProvidedByCurrentApplication)
    {
       var olduri = LockScreen.GetImageUri();

       var filename = olduri.OriginalString.Contains(_fileName1) ? _fileName2 : _fileName1;


       Deployment.Current.Dispatcher.BeginInvoke(async () =>
       {
         try
         {
           if (_mandelbrot == null)
              _mandelbrot = new Mandelbrot(_width, _height, true, _mandelColor);

           var img = _mandelbrot.RandomImage();

           // Save image in localfolder
           var storagefile = await ApplicationData.Current.LocalFolder.
                 CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting);


           var randomaccessstream = await storagefile.OpenAsync(FileAccessMode.ReadWrite);

           var stream = randomaccessstream.AsStreamForWrite();

           img.SaveJpeg(stream, _width, _height, 0, 80);

           await stream.FlushAsync();

           await randomaccessstream.FlushAsync();

           randomaccessstream.Dispose();
           
           // Set lock background URI
           LockScreen.SetImageUri(new Uri(string.Format(_uriBase, filename), UriKind.Absolute));
        }
        catch (Exception e)
        {
           // Do something
        }
        finally
        {
           NotifyComplete();
        }});
   }
   else
   {
      // Not provided by our application, so stop periodic agent
      Abort();
   }
  }
  catch (Exception e)
  {
     //do something
  }
}

I create a Mandelbrot instance then call the RandomImage that returns a Bitmap. Then I save it in the local folder.

I am switching between two different names (“Background1.jpg” and “Background2.jpg”), in order to not erase the one that is currently used (I retrieve the current background URI using GetImageURI).

Then create the new URI and I set with SetImageURI.

And now, every 30 min the agent is called and set a new fractal background.

Sample app

OK, the fractals are not very beautiful. The problem is I had to reduce the number of iterations (from 4000 to 50 !) used to calculate the image otherwise it will take more than 25s, which is the maximum a Periodic Agent can run.

The fractal code is a modification of this one : http://dev.illys.com/mandelbrot/

Sample application is here

Comments powered by Disqus