In this tutorial, we're going to focus on live apps. Live apps are one of the core concepts in Windows Phone development, and to properly create a quality experience, many factors are involved, like notifications, agents, and Tiles.
The Multitasking Approach
As we’ve seen in the application life cycle discussed earlier in this series, applications are suspended when they are not in the foreground. Every running process is terminated, so the application can’t execute operations while in the background.
There are three ways to overcome this limitation:
- Push notifications, which are sent by a remote service using an HTTP channel. This approach is used to send notifications to users, update a Tile, or warn users that something has happened.
- Background agents, which are services connected to our application that can run from time to time under specific conditions. These services can also be used for push notification scenarios—in this case, remote services are not involved—but they can also perform other tasks as long as they use supported APIs.
- Alarms and reminders, which display reminders to the user at specific dates and times.
Let’s see in detail how they work.
Push Notifications
Push notifications are messages sent to the phone that can react in many ways based on the notification type. There are three types of push notifications:
- Raw notifications can store any type of information, but they can be received only if the associated application is in the foreground.
- Toast notifications are the most intrusive ones, since they display a message at the top of the screen, along with a sound and a vibration. Text messages are a good example of toast notifications.
- Tile notifications can be used to update the application’s Tile.
There are three factors involved in the push notification architecture:
- The Windows Phone application, which acts as a client to receive notifications.
- The server application, which can be a web application or a service, that takes care of sending the notifications. Usually, the server stores a list of all the devices that are registered to receive notifications.
- The Microsoft Push Notification Service (MPNS), which is a cloud service offered by Microsoft that is able to receive notifications from the server application and route them to the Windows Phone clients.
Every Windows Phone application receives push notifications using a channel, which is identified by a unique URI. The server application will send notifications to the registered clients by sending an XML string to this URI using a POST command. The MPNS will take care of routing the requests to the proper devices.
Here is a sample of a URI that represents a channel:
http://sn1.notify.live.net/throttledthirdparty/01.00/AAhsLicyiJgtTaidbJoSgm-
Note: MPNS usage is free, but limited to 500 notifications per day per single device. If you need to exceed this limitation, you have to buy a TLS digital certificate, which you’ll need to submit during the certification process and to digitally sign your server’s application. This way, you’ll also be able to support SSL to encrypt the notification’s channel.
Sending a Notification: The Server
As already mentioned, notifications are sent using an HTTP channel with a POST command. The benefit is that it relies on standard technology, so you’ll be able to create a server application with any development platform.
The HTTP request that represents a notification has the following features:
- It’s defined using XML, so the content type of the request should be
text/xml
. - A custom header called
X-WindowsPhone-Target
, which contains the notification’s type (toast, Tile, or raw). - A custom header called
X-NotificationClass
, which is the notification’s priority (we’ll discuss this more in-depth later).
Let’s see how the different push notifications are structured in detail.
Toast Notifications
The following sample shows the XML needed to send a toast notification:
<?xml version="1.0" encoding="utf-8"?> <wp:Notification xmlns:wp="WPNotification"> <wp:Toast> <wp:Text1>Title</wp:Text1> <wp:Text2>Text</wp:Text2> <wp:Param>/MainPage.xaml?ID=1</wp:Param> </wp:Toast> </wp:Notification>
There are three parameters to set:
-
wp:Text1
is the notification’s title. -
wp:Text2
is the notification’s text. -
wp:Param
is the optional notification deep link; when this is set, the application is opened automatically on the specified page with one or more query string parameters that can be used to identify the notification’s context.
When you prepare the request to send over HTTP, the X-WindowsPhone-Target
header should be set to toast
, while the X-NotificationClass
header supports the following values:
-
2
to send the notification immediately. -
12
to send the notification after 450 seconds. -
22
to send the notification after 900 seconds.
Tile Notifications
Tile notifications are used to update either the main Tile or one of the secondary Tiles of the application. We won’t describe the XML needed to send the notification here: Tiles are more complex than the other notification types since Windows Phone 8 supports many templates and sizes. We’ll look at the XML that describes Tile notifications later in the Tiles section of the article.
To send a Tile notification, the X-WindowsPhone-Target
header of the HTTP request should be set to tile
, while the X-NotificationClass
header supports the following values:
-
1
to send the notification immediately. -
11
to send the notification after 450 seconds. -
21
to send the notification after 900 seconds.
Raw Notifications
Raw notifications don’t have a specific XML definition since they can deliver any kind of data, so we can include our own definition.
To send a raw notification, the X-WindowsPhone-Target
header of the HTTP request should be set to raw
, while the X-NotificationClass
header supports the following values:
-
3
to send the notification immediately. -
13
to send the notification after 450 seconds. -
23
to send the notification after 900 seconds.
Sending the Request and Managing the Response
The following sample code shows an example of how to send a toast notification using the HttpWebRequest
class, one of the basic .NET Framework classes for performing network operations:
string toastNotificationPayloadXml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + "<wp:Notification xmlns:wp=\"WPNotification\">" + "<wp:Toast>" + "<wp:Text1> title </wp:Text1>" + "<wp:Text2> text </wp:Text2>" + "</wp:Toast> " + "</wp:Notification>"; byte[] payload = Encoding.UTF8.GetBytes(toastNotificationPayloadXml); var pushNotificationWebRequest = (HttpWebRequest)WebRequest.Create("http://sn1.notify.live.net/throttledthirdparty/01.00/AAEqbi-clyknR6iysF1QNBFpAgAAAAADAQAAAAQUZm52OkJCMjg1QTg1QkZDMkUxREQ"); pushNotificationWebRequest.Method = "POST"; pushNotificationWebRequest.ContentType = "text/xml"; var messageId = Guid.NewGuid(); pushNotificationWebRequest.Headers.Add("X-MessageID", messageId.ToString()); pushNotificationWebRequest.Headers.Add("X-WindowsPhone-Target", "toast"); pushNotificationWebRequest.Headers.Add("X-NotificationClass", "2"); pushNotificationWebRequest.ContentLength = payload.Length; using (var notificationRequestStream = pushNotificationWebRequest.GetRequestStream()) { notificationRequestStream.Write(payload, 0, payload.Length); } using (var pushNotificationWebResponse = (HttpWebResponse)pushNotificationWebRequest.GetResponse()) { //Check the status of the response. }
The XML definition is simply stored in a string
. We’re going to change just the node values that store the notification’s title and text. Then, we start to prepare the HTTP request by using the HttpWebRequest
class. We add the custom headers, define the content’s length and type (text/xml
), and specify the method to use (POST
).
In the end, by using the GetRequestStream()
method, we get the stream location to write the request’s content, which is the notification’s XML. Then we send it by calling the GetResponse()
method, which returns the status of the request. By analyzing the response we are able to tell whether or not the operation was successful.
The response’s analysis involves the status code and three custom headers:
- The response’s status code returns generic information that tells you whether the request has been received. It’s based on the standard HTTP status codes. For example,
200 OK
means that the request has been successfully received, while404 Not Found
means that the URI was invalid. - The
X-NotificationStatus
header tells you if the MPNS has received the request using the valuesReceived
,Dropped
,QueueFull
, andSupressed
. - The
X-DeviceConnectionStatus
header returns the device status when the request is sent:Connected
,Inactive
,Disconnected
, orTempDisconnected
. - The
X-SubscriptionStatus
header returns if the channel is still valid (Active
) or not (Expired
). In the second case, we shouldn’t try to send it again, since it doesn’t exist anymore.
The combination of these parameters will help you understand the real status of the operation. The MSDN documentation features descriptions of all the possible combinations.
It’s important to correctly manage the notifications because MPNS doesn’t offer any automatic retry mechanism. If a notification is not delivered, MPSN won’t try again to send it, even if the operation failed for a temporary reason (for example, the device wasn’t connected to the Internet). It’s up to you to implement a retry mechanism based on the response.
PushSharp: A Push Notification Helper Library
As you can see, sending push notifications is a little bit tricky since it requires you to manually set headers, XML strings, etc. Some developers have worked on wrappers that hide the complexity of manually defining the notification by exposing high-level APIs so that you can work with classes and objects.
One of the most interesting wrappers is called PushSharp, which can be simply installed on your server project using NuGet. The biggest benefits of this library are:
- It’s a generic .NET library that supports not only Windows Phone, but the most common platforms that use push notifications, like Windows Store apps, iOS, Android, and Blackberry. If you have a cross-platform application, it will make your life easier in managing a single-server application that is able to send notifications to different kinds of devices.
- It’s totally compatible with Windows Phone 8, so it supports not only toast and raw notifications, but also all the new Tile templates and sizes.
The following sample shows how simple it is to send a toast notification using this library:
WindowsPhoneToastNotification notification = new WindowsPhoneToastNotification(); notification.Text1 = "Title"; notification.Text2 = "Text"; notification.EndPointUrl = "http://sn1.notify.live.net/throttledthirdparty/01.00/AQHcej5duTcJRqnn779soTA1AgAAAAADAQAAAAQUZm52OkJCMjg1QTg1QkZDMkUxREQFBkxFR0FDWQ"; notification.NotificationClass=BatchingInterval.Immediate; PushBroker broker = new PushBroker(); broker.RegisterWindowsPhoneService(); broker.QueueNotification(notification);
Every notification type is represented by a specific class, which exposes a property for every notification feature. In the previous sample, the WindowsPhoneToastNotification
class offers properties to set the notification’s title, text, and deep link.
The channel URI location to send the notification is set in the EndPointUrl
property. Once everything is set, you can send it by creating a PushBroker
object, which represents the dispatcher that takes care of sending notifications. First, you have to register for the kind of notifications you want to send. Since we’re working with Windows Phone, we use the RegisterWindowsPhoneService()
method. Then, we can queue the notification by simply passing it to the QueueNotification()
method. It will be automatically sent with the priority you’ve set.
The approach is the same if you want to send a Tile. You have three different classes based on the Tile’s template, WindowsPhoneCycleTileNotification
, WindowsPhoneFlipTileNotification
, and WindowsPhoneIconicTileNotification
; or WindowsPhoneRawNotification
for a raw notification.
In the end, the PushBroker
class exposes many events to control the notification life cycle, like OnNotificationSent
which is triggered when a notification is successfully sent, or OnNotificationFailed
which is triggered when the sending operation has failed.
Receiving Push Notifications: The Client
The base class that identifies a push notification channel is called HttpNotificationChannel
and exposes many methods and events that are triggered when something connected to the channel happens.
Note: To receive push notifications you’ll need to enable the ID_CAP_PUSH_NOTIFICATION
capability in the manifest file.
Every application has a single unique channel, identified by a keyword. For this reason, it should be created only the first time the application subscribes to receive notifications; if you try to create a channel that already exists, you’ll get an exception. To avoid this scenario, the HttpNotificationChannel
class offers the Find()
method, which returns a reference to the channel.
private void OnRegisterChannelClicked(object sender, RoutedEventArgs e) { HttpNotificationChannel channel = HttpNotificationChannel.Find("TestChannel"); if (channel == null) { channel = new HttpNotificationChannel("TestChannel"); channel.Open(); } }
In the previous sample, the channel is created only if the Find()
method fails and returns a null object. The HttpNotificationChannel
class exposes many methods to start interacting with push notifications; they should be called only if the channel doesn’t already exist. In the sample we see the Open()
method which should be called to effectively create the channel, and which automatically subscribes to raw notifications.
If we want to be able to receive toast and Tile notifications, we need to use two other methods offered by the class: BindToShellToast()
and BindToShellTile()
. The following sample shows a complete initialization:
private void OnRegisterChannelClicked(object sender, RoutedEventArgs e) { HttpNotificationChannel channel = HttpNotificationChannel.Find("TestChannel"); if (channel == null) { channel = new HttpNotificationChannel("TestChannel"); channel.Open(); channel.BindToShellToast(); channel.BindToShellTile(); } }
Beyond offering methods, the HttpNotificationChannel
class also offers some events to manage different conditions that can be triggered during the channel life cycle.
The most important one is called ChannelUriUpdated
, which is triggered when the channel creation operation is completed and the MPNS has returned the URI that identifies it. This is the event in which, in a regular application, we will send the URI to the server application so that it can store it for later use. It’s important to subscribe to this event whether the channel has just been created, or already exists and has been retrieved using the Find()
method. From time to time, the URI that identifies the channel can expire. In this case, the ChannelUriUpdated
event is triggered again to return the new URI.
The following sample shows a full client initialization:
private void OnRegisterChannelClicked(object sender, RoutedEventArgs e) { HttpNotificationChannel channel = HttpNotificationChannel.Find("TestChannel"); if (channel == null) { channel = new HttpNotificationChannel("TestChannel"); channel.Open(); channel.BindToShellToast(); channel.BindToShellTile(); } channel.ChannelUriUpdated += channel_ChannelUriUpdated; } void channel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e) { MessageBox.Show(e.ChannelUri); }
As you can see, the ChannelUriUpdated
event returns a parameter with the ChannelUri
property, which contains the information we need. In the previous sample, we just display the URI channel to the user.
There are two other events offered by the HttpNotificationChannel
class that can be useful:
-
HttpNotificationReceived
is triggered when the application has received a raw notification. -
ShellToastNotificationReceived
is triggered when the application receives a toast notification while it is open. By default, toast notifications are not displayed if the associated application is in the foreground.
The HttpNotificationReceived
event receives, in the parameters, the object that identifies the notification. The content is stored in the Body
property, which is a stream since raw notifications can store any type of data. In the following sample, we assume that the raw notification contains text and display it when it’s received:
void Channel_HttpNotificationReceived(object sender, HttpNotificationEventArgs e) { using (StreamReader reader = new StreamReader(e.Notification.Body)) { string message = reader.ReadToEnd(); Dispatcher.BeginInvoke(() => MessageBox.Show(message)); } }
The ShellNotificationReceived
event, instead, returns in the parameters a Collection
object, which contains all the XML nodes that are part of the notification. The following sample shows you how to extract the title and the description of the notification, and how to display them to the user:
void Channel_ShellToastNotificationReceived(object sender, NotificationEventArgs e) { string title = e.Collection["wp:Text1"]; string message = e.Collection["wp:Text2"]; Dispatcher.BeginInvoke(() => MessageBox.Show(title + " " + message)); }
Managing Errors
If something goes wrong when you open a notification channel, you can subscribe to the ErrorOccurred
event of the HttpNotificationChannel
class to discover what’s happened.
The event returns a parameter that contains information about the error, like ErrorType
, ErrorCode
, ErrorAdditionalData
, and Message
.
The following list includes the most common conditions that can lead to a failure during the channel opening:
- To preserve battery life and performance, Windows Phone limits the maximum number of channels that are kept alive at the same time. If the limit has been reached and you try to open a new channel, you’ll get the value
ChannelOpenFailed
asErrorType
. - The received notification can contain a message which is badly formatted; in this case the
ErrorType
will beMessageBadContent
. - You can send too many notifications at the same time; in this case, they are rejected with the
NotificationRateTooHigh
error. - To preserve battery power, notifications can be received only if the battery isn’t critical; in this case, you’ll get a
PowerLevelChanged
error.
The ErrorAdditionalData
property can contain additional information about the error. For example, if you get a PowerLevelChanged
error, you’ll be informed of the current battery level (low, critical, or normal).
void channel_ErrorOccurred(object sender, NotificationChannelErrorEventArgs e) { if (e.ErrorType == ChannelErrorType.PowerLevelChanged) { ChannelPowerLevel level = (ChannelPowerLevel) e.ErrorAdditionalData; switch (level) { case ChannelPowerLevel.LowPowerLevel: MessageBox.Show("Battery is low"); break; case ChannelPowerLevel.CriticalLowPowerLevel: MessageBox.Show("Battery is critical"); break; } } }
Background Agents
Push notifications are the best way to interact with the user when the application is not running since they offer the best experience and, at the same time, preserve battery life. However, the experience is limited to notifications: you can’t execute any other operation, like fetching data from a web service or reading a file from the local storage. Moreover, for certain scenarios in which you don’t require instant notifications, creating the required server infrastructure can be too expensive. Think, for example, of a weather application: it’s not critical that the forecast is updated immediately when the forecasts change.
For all these scenarios, Windows Phone 7.5 has introduced background agents, which are special services periodically executed by Windows Phone, even when the application is not running. There are two types of periodic background agents: periodic and audio. In the New Project section of Visual Studio, you’ll find many templates for all the supported agent types. In this section we’ll see how periodic agents work in detail.
Tip: Even if a background agent is a separate Visual Studio project, it shares the same resources with the foreground application. For example, they share the same local storage, so you’re able to read data created by the application in the agent, and vice versa.
Agent Limits
There are some limitations that background agents have to satisfy. The most important one is connected to timing, since agents can run only in a specific time frame for a limited amount of time. We’ll discuss this limitation later since there are some differences according to the background agent type you’re going to use.
The first limitation concerns supported APIs: only a limited number of APIs can be used in a background agent. Basically, all the APIs that are related to the user interface are prohibited since agents can’t interact with the application interface. You can find the complete list of unsupported APIs in the MSDN documentation.
The second limitation is about memory: a background agent can’t use more than 11 MB of memory, otherwise it will be terminated. It’s important to highlight that during the testing process (when the Visual Studio debugger is attached), the memory limit will be disabled, and the background agent won’t be terminated if it has used more than 11 MB. You’ll have to test it in a real environment if you want to make sure the limit isn’t reached.
The third and final limitation is about timing: a background agent is automatically disabled 14 days after it has been initialized by the connected application. There are two ways to overcome this limitation:
- The user keeps using the application; the agent can be renewed for another 14 days every time the application is opened.
- The agent is used to send notifications to update the main application’s Tile or the lock screen; every time the agent sends a notification it will be automatically renewed for another 14 days.
It’s important to keep in mind that if the background agent execution consecutively fails twice (because it exceeded the memory limit or raised an unmanaged exception), it’s automatically disabled; the application will have to reenable it when it’s launched.
Periodic Agents
Periodic agents are used when you need to execute small operations frequently. They are typically executed every 30 minutes (the execution interval can sometimes be shortened to every 10 minutes to coincide with other background processes to save battery life), and they can run for up to 25 seconds. Users are able to manage periodic agents from the Settings panel and disable the ones they don’t need. Periodic agents are automatically disabled if the phone is running in Battery Saver mode; they’ll be automatically restored when sufficient battery power is available.
Periodic agents are identified by the PeriodicTask
class, which belongs to the Microsoft.Phone.Scheduler
namespace.
Resource Intensive Agents
Resource intensive agents have been created for the opposite scenario: long-running tasks that are executed occasionally. They can run for up to 10 minutes, but only if the phone is connected to a Wi-Fi network and an external power source.
These agents are perfect for tasks like data synchronization. In fact, they are typically executed during the night, when the phone charging. Other than the previous conditions, in fact, the phone shouldn’t be in use. The lock screen should be activated and no other operations (like phone calls) should be performing.
Resource intensive agents are identified by the ResourceIntensiveTask
, which is also part of the Microsoft.Phone.Scheduler
namespace.
Creating a Background Agent
As already mentioned, background agents are defined in a project separate from the front-end application. Periodic agents share the same template and architecture, and the Windows Phone application will decide to register them as PeriodicTask
or ResourceIntensiveTask
objects.
To create a background agent, you’ll have to add a new project to the solution that contains your Windows Phone application. In the Add New Project window you’ll find a template called Windows Phone Scheduled Task Agent
in the Windows Phone section.
The project already contains the class that will manage the agent; it’s called ScheduledAgent
and it inherits from the ScheduledTaskAgent
class. The class already implements a method and an event handler.
The method, called OnInvoke()
, is the most important one. It’s the method that is triggered when the background agent is executed, so it contains the logic that performs the operations we need. The following sample shows how to send a toast notification from a background agent:
protected override void OnInvoke(ScheduledTask task) { ShellToast toast = new ShellToast(); toast.Title = "Title"; toast.Content = "Text"; toast.Show(); NotifyComplete(); }
It’s important to highlight the NotifyComplete()
method, which should be called as soon as the agent has completed all the operations. It notifies the operating system that the task has completed its job and that the next scheduled task can be executed. The NotifyComplete()
method determines the task’s status. If it’s not called within the assigned time—25 seconds for periodic tasks or 10 minutes for resource intensive tasks—the execution is interrupted.
There’s another way to complete the agent’s execution: Abort()
. This method is called when something goes wrong (for example, the required conditions to execute the agent are not satisfied) and the user needs to open the application to fix the problem.
The event handler is called UnhandledException
and is triggered when an unexpected exception is raised. You can use it, for example, to log the error.
The previous sample shows you how to send local toast notifications. A toast notification is identified by the ShellToast
class. You simply have to set all the supported properties (Title
, Content
, and optionally NavigationUri
, which is the deep link). In the end, you have to call the Show()
method to display it.
Like remote notifications, local toasts are supported only if the application is in the background. The previous code works only inside a background agent. If it’s executed by a foreground application, nothing happens.
Registering the Agent
The background agent is defined in a separate project, but is registered in the application. The registration should be done when the application starts, or in the settings page if we give users the option to enable or disable it within the application.
The base class to use when working with background agents is ScheduledActionService
, which represents the phone’s scheduler. It takes care of registering all the background agents and maintaining them during their life cycle.
The first step is to define which type of agent you want to use. As previously mentioned, the background agent architecture is always the same; the type (periodic or resource intensive) is defined by the application.
In the first case you’ll need to create a PeriodicTask
object, and in the second case, a ResourceIntensive
task object. Regardless of the type, it’s important to set the Description
property, which is text displayed to users in the Settings page. It’s used to explain the purpose of the agent so users can decide whether or not to keep it enabled.
PeriodicTask periodicTask = new PeriodicTask("PeriodicTask"); periodicTask.Description = "This is a periodic task"; ResourceIntensiveTask resourceIntensiveTask = new ResourceIntensiveTask("ResourceIntensiveTask"); resourceIntensiveTask.Description = "This is a resource intensive task";
In both cases, background agents are identified by a name, which is passed as a parameter of the class. This name should be unique across all the tasks registered using the PhoneApplicationService
class; otherwise you’ll get an exception.
The basic operation to add a task is very simple:
public void ScheduleAgent() { ScheduledAction action = ScheduledActionService.Find("Agent"); if (action == null || !action.IsScheduled) { if (action != null) { ScheduledActionService.Remove("Agent"); } PeriodicTask task = new PeriodicTask("Agent"); task.Description = "This is a periodic agent"; ScheduledActionService.Add(task); #if DEBUG ScheduledActionService.LaunchForTest("Agent", TimeSpan.FromSeconds(10)); #endif } }
The first operation checks whether the agent is already scheduled by using the Find()
method of the ScheduledActionService
class, which requires the task’s unique name. This operation is required if we want to extend the agent’s lifetime. If the agent does not exist yet or is not scheduled (the IsScheduled
property is false), we first remove it from the scheduler and then add it since the ScheduledActionService
class doesn’t offer a method to simply update a registered task. The add operation is done using the Add()
method, which accepts either a PeriodicTask
or a ResourceIntensiveTask
object.
Now the task is scheduled and will be executed when the appropriate conditions are satisfied. If you’re in the testing phase, you’ll find the LaunchForTest()
method useful; it forces the execution of an agent after a fixed amount of time. In the previous sample, the agent identified by the name PeriodicTask
is launched after five seconds. The LaunchForTest()
method can also be executed in the OnInvoke()
event inside the background agent, allowing you to easily simulate multiple executions.
In the previous sample you can see that we’ve used conditional compilation to execute the LaunchForTest()
method only when the application is launched in debug mode. This way, we make sure that when the application is compiled in release mode for publication to the Windows Store, the method won’t be executed; otherwise, you’ll get an exception if the method is called by an application installed from the Store.
Managing Errors
Background agents are good examples of the philosophy behind Windows Phone:
- Users are always in control; they can disable whatever background agents they aren’t interested in through the Settings page.
- Performance and battery life are two crucial factors; Windows Phone limits the maximum number of registered background agents.
For these reasons, the agent registration process can fail, so we need to manage both scenarios. The following code shows a more complete sample of a background agent’s initialization:
public void ScheduleAgent() { ScheduledAction action = ScheduledActionService.Find("Agent"); if (action == null || !action.IsScheduled) { if (action != null) { ScheduledActionService.Remove("Agent"); } try { PeriodicTask task = new PeriodicTask("Agent"); task.Description = "This is a periodic agent"; ScheduledActionService.Add(task); } catch (InvalidOperationException exception) { if (exception.Message.Contains("BNS Error: The action is disabled")) { // No user action required. } if (exception.Message.Contains("BNS Error: The maximum number of ScheduledActions of this type have already been added.")) { // No user action required. } } } }
The difference in the previous sample is that the Add()
operation is executed inside a try / catch
block. This way, we are ready to catch the InvalidOperationException
error that might be raised.
We can identify the scenario by the exception message:
- BNS Error: The action is disabled. The user has disabled the agent connected to our application in the Settings page. In this case, we have to warn the user to enable it again in the Settings page before trying to register it.
- BSN Error: The maximum number of ScheduledActions of this type have already been added. The user has reached the maximum number of agents allowed to be installed on phone. In this case, we don’t have to do anything; Windows Phone will display a proper warning message.
Moreover, the ScheduledTask
class (which is the base class that PeriodicTask
and ResourceIntensiveTask
inherit from) offers some properties for understanding the status of the last execution, such as LastScheduledTime
which contains the date and time of the last execution, and LastExitReason
which stores the last execution status.
Specifically, LastExitReason
is very useful for knowing if the last execution completed successfully (Completed
), if it exceeded the memory limit (MemoryQuotaExceeded
) or the time limit (ExecutionTimeExceeded
), or if an unhandled exception occurred (UnhandledException
).
Background Audio Agent
There’s a special kind of background agent that works differently than periodic agents: audio agents, which are used in audio-related applications to keep playing audio when the app is closed. The goal is to offer a similar experience to the native Music + Videos Hub; even when the app is not in the foreground, users are able to keep listening to their music library.
Again, the background agent is defined in a different project than the foreground application. However:
- The agent doesn’t need to be initialized in the foreground application using the
ScheduledActionService
class like we did for periodic agents. - There aren’t time limitations. The agent runs every time users interact with the music controls, and it never expires. The only limitation is that the triggered operation should complete within 30 seconds.
- There is a memory limitation, but the cap is higher: 20 MB (keep in mind that the memory limit isn’t activated when the Visual Studio debugger is connected).
- In this scenario, the background agent is not just a companion, but the core of the application; it manages all interactions with the music playback, regardless of whether they occur in the foreground application or the native embedded player.
Interacting With the Audio
The core class to reproduce background audio is called BackgroundAudioPlayer
, which identifies the built-in Windows Phone audio player. There’s just one instance of the player within the system, and it can’t be shared. If users launch another application that uses a background audio agent (including the native Music + Videos Hub), it takes control over the audio reproduction. As we’re going to see soon, the BackgroundAudioPlayer
class is used both in the foreground app and in the background agent to interact with the music playback.
The audio tracks played by a background audio agent are represented by the AudioTrack
class. Each track, other than the resource to play, contains all the metadata like the title, artist, and album title.
The track’s path is set in the Source
property, which can be either a remote file or a file stored in the local storage. However, most of the properties can be set directly when the AudioTrack
object is created, like in the following sample:
AudioTrack track = new AudioTrack(new Uri("http://www.windowsphonelounge.net/issues/wpl_issue_01.mp3", UriKind.Absolute), "Episode 1", "Igor & Matteo", "Windows Phone Lounge", null);
With the previous code, in addition to setting the source file, we also immediately set information like the title, the artist, and the album. A useful available property is called PlayerControls
, which can be used to set which controls (Play, Pause, Forward, etc.) are available for the track. This way, if you’re developing an application connected to an online radio, for example, you can automatically block options that are not supported (like the skip track button).
Creating the Agent
Visual Studio offers two templates to create background audio agents: Windows Phone Audio Playback Agent and Windows Phone Audio Streaming agent. They share the same purpose; their difference is that the Windows Phone Audio Streaming agent is required for working with media streaming codecs that are not natively supported by the platform.
A background audio agent’s project already comes with a class called AudioAgent
, which inherits from the AudioPlayerAgent
class. As we saw with periodic agents, the class automatically implements some methods that are used to interact with the agent. The most important ones are OnUserAction()
and OnPlayStateChanged()
.
OnUserAction()
is triggered every time users manually interact with the music playback, such as pausing a track or pressing the skip track button in the foreground application or the background player.
The method returns some parameters that can be used to understand the context and perform the appropriate operations:
- a
BackgroundAudioPlayer
object, which is a reference to the background audio player - an
AudioTrack
object, which is a reference to the track currently playing - a
UserAction
object, which is the action triggered by the user
The following sample shows a typical implementation of the OnUserAction()
method:
protected override void OnUserAction(BackgroundAudioPlayer player, AudioTrack track, UserAction action, object param) { switch (action) { case UserAction.Pause: { player.Pause(); break; } case UserAction.Play: { player.Play(); break; } case UserAction.SkipNext: { //Play next track. break; } case UserAction.SkipPrevious: { //Play previous track. break; } } NotifyComplete(); }
Usually with a switch
statement, you’ll monitor every supported user interaction, which is stored in the UserAction
object. Then, you respond using the methods exposed by the BackgroundAudioPlayer
class. Play
and Pause
are the simplest states to manage; SkipNext
and SkipPrevious
usually require more logic, since you have to get the previous or next track to play in the list from your library.
Note that background audio agents also require the NotifyComplete()
method execution as soon as we’ve finished to manage the operation; it should be called within 30 seconds to avoid termination.
The OnPlayStateChanged()
method is triggered automatically every time the music playback state is changed, but not as a direct consequence of a manual action. For example, when the current track ends, the agent should automatically start playing the next track in the list.
The method’s structure is very similar to the OnUserAction()
method. In addition to a reference to the background player and the current track in this case, you’ll get a PlayState
object, which notifies you about what’s going on.
The following sample shows a typical implementation of the method:
protected override void OnPlayStateChanged(BackgroundAudioPlayer player, AudioTrack track, PlayState playState) { if (playState == PlayState.TrackEnded) //Play next track. NotifyComplete(); }
Tip: Background audio agents are not kept in memory all the time, but instead are launched only when the music playback state changes. If you need to persist some data across the different executions, you’ll need to rely on the local storage.
The Foreground Application
We’ve seen how all the main playback logic is managed directly by the background agent. The foreground application, in most of the cases, is just a visual front end for the agent.
To understand the playback state (and to properly update the UI) we need to use, again, the BackgroundAudioPlayer
class we’ve seen. The difference is that, in the foreground application, we need to use the Instance
singleton to get access to it.
The methods exposed by the class are the same, so we can use it to play, pause, or change the music playback state (for example, if we want to connect these operations to input controls like buttons).
The BackgroundAudioPlayer
exposes an important event called PlayStateChanged
, which is triggered every time the playback state changes. We can use it to update the visual interface (for example, if we want to display the track currently playing).
The following sample shows how the PlayStateChanged
event is used to change the behavior of the play/pause button and to display to some metadata about the currently playing track:
public partial class MainPage : PhoneApplicationPage { private BackgroundAudioPlayer player; // Constructor public MainPage() { InitializeComponent(); player = BackgroundAudioPlayer.Instance; player.PlayStateChanged += new EventHandler(player_PlayStateChanged); } private void OnPlayClicked(object sender, RoutedEventArgs e) { player.Play(); } void player_PlayStateChanged(object sender, EventArgs e) { if (player.PlayerState == PlayState.Playing) Dispatcher.BeginInvoke(() => btnPlay.Content = "Pause"); else Dispatcher.BeginInvoke(() => btnPlay.Content = "Play"); if (player.PlayerState == PlayState.Playing) { Dispatcher.BeginInvoke(() => { txtTitle.Text = player.Track.Title; txtArtist.Text = player.Track.Artist; txtAlbum.Text = player.Track.Album; }); } } }
The previous code should be familiar; you have access to all the properties we’ve seen in the background agent, like PlayerState
to identify the current playback state, or Track
to identify the currently playing track. Track
isn’t just a read-only property. If we want to set a new track to play in the application, we can simply assign a new AudioTrack
object to the Track
property of the BackgroundAudioPlayer
instance.
Alarms and Reminders
Alarms and reminders are simple ways to show reminders to users at a specified date and time, as the native Alarm and Calendar applications do.
They work in the same way. The APIs belong to the Microsoft.Phone.Scheduler
namespace, and they inherit from the base ScheduledNotification
class. There are some properties in common between the two APIs:
-
Content
: The reminder description. -
BeginTime
: The date and time the reminder should be displayed. -
RecurrenceType
: Sets whether it’s a recurrent or one-time reminder. -
ExpirationTime
: The date and time a recurrent reminder expires.
Every reminder is identified by a name, which should be unique across all the alarms and reminders created by the application. They work like background agents; their life cycle is controlled by the ScheduledActionService
class, which takes care of adding, updating, and removing them.
Alarms are identified by the Alarm
class and used when to show a reminder that doesn’t have a specific context. Users will be able to snooze or dismiss it. A feature specific to alarms is that they can play a custom sound, which is set in the Sound
property.
The following sample shows how to create and schedule an alarm:
private void OnSetAlarmClicked(object sender, RoutedEventArgs e) { Alarm alarm = new Alarm("Alarm") { BeginTime = DateTime.Now.AddSeconds(15), Content = "It's time!", RecurrenceType = RecurrenceInterval.None, Sound = new Uri("/Assets/CustomSound.mp3", UriKind.Relative) }; ScheduledActionService.Add(alarm); }
The sample creates an alarm that is scheduled 15 seconds after the current date and time, and uses a custom sound that is an MP3 file inside the Visual Studio project.
Reminders, instead, are identified by the Reminder
class and are used when the notification is connected to a specific context, in a similar way that calendar reminders are connected to an appointment.
The context is managed using the NavigationUri
property, which supports a deep link. It’s the page (with optional query string parameters) that is opened when users tap the reminder’s title.
private void OnSetReminderClicked(object sender, RoutedEventArgs e) { Reminder reminder = new Reminder("Reminder") { BeginTime = DateTime.Now.AddSeconds(15), Title = "Reminder", Content = "Meeting", RecurrenceType = RecurrenceInterval.None, NavigationUri = new Uri("/DetailPage.xaml?id=1", UriKind.Relative) }; ScheduledActionService.Add(reminder); }
The previous code schedules a reminder that opens a page called DetailPage.xaml
. Using the navigation events described earlier in this series, you’ll be able to get the query string parameters and load the requested data. Notice also that the Reminder
class offers a Title
property, which is not supported by alarms.
Live Tiles
Live Tiles are, without a doubt, the most unique Windows Phone feature, and one you won’t find on any other platform. They are called Live Tiles because they aren’t simply shortcuts to open applications; they can be updated with local or remote notifications to display information without forcing users to open the application. Many kinds of applications take advantage of this feature, like weather apps that display the forecast, news apps that display the latest headlines, and movie apps that display upcoming movie titles.
Windows Phone 8 has introduced many new features regarding Tiles, like new templates and new sizes.
An application can use three different sizes for Tiles: small, medium, and wide. As developers, we’ll be able to customize the Tile’s content according to the size so that, for example, the wide Tile can display more info than the small Tile.
Windows Phone 8 has also introduced three different templates to customize a Tile: flip, cycle, and iconic. It’s important to note that you can choose only one template for your application; it must be declared in the manifest file, in the Application UI section. Once you’ve set it, you won’t be able to change it at run time, and all the Tiles you’re going to create or update will have to use that template. In addition, you can choose the features (Tiles, pictures, etc.) to use for the main Tile in the Application UI section; this information will be used until a notification updates it.
In the following sections we’ll examine every available template in detail. For each one we’ll discuss the architecture and code needed to update it with a notification. For remote notifications, we’ll see the required XML. For local notifications, we’ll look at the APIs to use in the application or in a background agent.
In both cases, all the fields that define a Tile are optional. If you don’t set some of them, those properties will simply be ignored. On the other hand, if a field that was previously set is not updated with a notification, the old value will be kept.
Flip Template
Flip is the standard Windows Phone template, and the only one that was already available in Windows Phone 7. With this template you can display text, counters, and images on the front of the Tile. Periodically, the Tile will rotate or “flip” to show the opposite side, which can display different text or images.
As you can see from the previous figure, you can customize both front and rear sides of the Tile. If you want to include an image, you have to use one of the following sizes:
- Small: 159 × 159
- Medium: 336 × 336
- Wide: 691 × 336
A flip template Tile is identified by the FlipTileData
class. The following sample shows how to use it to define a Tile that can be managed by code.
private void OnCreateFlipTileClicked(object sender, RoutedEventArgs e) { FlipTileData data = new FlipTileData { SmallBackgroundImage = new Uri("Assets/Tiles/FlipCycleTileSmall.png", UriKind.Relative), BackgroundImage = new Uri("Assets/Tiles/FlipCycleTileMedium.png", UriKind.Relative), WideBackgroundImage = new Uri("Assets/Tiles/FlipCycleTileLarge.png", UriKind.Relative), Title = "Flip tile", BackTitle = "Back flip tile", BackContent = "This is a flip tile", WideBackContent = "This is a flip tile with wide content", Count = 5 }; }
The following code shows how the same Tile is represented using the XML definition needed for remote notifications:
<?xml version="1.0" encoding="utf-8"?> <wp:Notification xmlns:wp="WPNotification" Version="2.0"> <wp:Tile Id="[Tile ID]" Template="FlipTile"> <wp:SmallBackgroundImage Action="Clear">[small Tile size URI]</wp:SmallBackgroundImage> <wp:WideBackgroundImage Action="Clear">[front of wide Tile size URI]</wp:WideBackgroundImage> <wp:WideBackBackgroundImage Action="Clear">[back of wide Tile size URI]</wp:WideBackBackgroundImage> <wp:WideBackContent Action="Clear">[back of wide Tile size content]</wp:WideBackContent> <wp:BackgroundImage Action="Clear">[front of medium Tile size URI]</wp:BackgroundImage> <wp:Count Action="Clear">[count]</wp:Count> <wp:Title Action="Clear">[title]</wp:Title> <wp:BackBackgroundImage Action="Clear">[back of medium Tile size URI]</wp:BackBackgroundImage> <wp:BackTitle Action="Clear">[back of Tile title]</wp:BackTitle> <wp:BackContent Action="Clear">[back of medium Tile size content]</wp:BackContent> </wp:Tile> </wp:Notification>
Notice the Action
attribute that is set for many nodes. If you set it without assigning a value to the node, it will simply erase the previous value so that it reverts to the default.
Cycle Template
The cycle template can be used to create a visual experience similar to the one offered by the Photos Hub. Up to nine pictures can cycle on the front side of the Tile.
The cycle template offers fewer ways to customize the Tile than the other two templates since its focus is the images. The image sizes are the same as those used for the flip template:
- Small: 159 × 159
- Medium: 336 × 336
- Wide: 691 × 336
A cycle template is identified by the CycleTileData
class, as shown in the following sample:
private void OnCreateCycleTileClicked(object sender, RoutedEventArgs e) { CycleTileData data = new CycleTileData() { Count = 5, SmallBackgroundImage = new Uri("Assets/Tiles/FlipCycleTileSmall.png", UriKind.Relative), Title = "Cycle tile", CycleImages = new List<Uri> { new Uri("Assets/Tiles/Tile1.png", UriKind.Relative), new Uri("Assets/Tiles/Tile2.png", UriKind.Relative), new Uri("Assets/Tiles/Tile3.png", UriKind.Relative) } }; }
The following XML can used to send remote notifications to update Tiles based on the cycle template:
<?xml version="1.0" encoding="utf-8"?> <wp:Notification xmlns:wp="WPNotification" Version="2.0"> <wp:Tile Id="[Tile ID]" Template="CycleTile"> <wp:SmallBackgroundImage Action="Clear">[small Tile size URI]</wp:SmallBackgroundImage> <wp:CycleImage1 Action="Clear">[photo 1 URI]</wp:CycleImage1> <wp:CycleImage2 Action="Clear">[photo 2 URI]</wp:CycleImage2> <wp:CycleImage3 Action="Clear">[photo 3 URI]</wp:CycleImage3> <wp:Count Action="Clear">[count]</wp:Count> <wp:Title Action="Clear">[title]</wp:Title> </wp:Tile> </wp:Notification>
Iconic Template
The iconic template is used to create Tiles that emphasize the counter. Many native applications such as Mail, Messaging, and Phone use this template. In this template, the counter is bigger and easier to see.
The iconic template features two main differences from the flip and cycle templates. The first is that full size images are not supported; instead, you can specify an icon image, which is displayed near the counter. There are just two images sizes required:
- Small and Wide Tiles: 110 × 110
- Medium Tile: 202 × 202
The other difference is that it’s possible to customize the background color (the only way to do this with the other templates is to use an image with the background color you prefer). If you don’t set a background color, the template will automatically use the phone’s theme.
An iconic Tile is represented by the IconicTileData
template, as shown in the following sample:
private void OnCreateIconicTileClicked(object sender, RoutedEventArgs e) { IconicTileData data = new IconicTileData() { SmallIconImage = new Uri("/Assets/Tiles/IconicTileSmall.png", UriKind.Relative), IconImage = new Uri("/Assets/Tiles/IconicTileMediumLarge.png", UriKind.Relative), Title = "My App", Count = 5, WideContent1 = "First line", WideContent2 = "Second line", WideContent3 = "Third line" }; }
The following sample is the XML representation for remote push notifications in a Tile that uses the iconic template:
<?xml version="1.0" encoding="utf-8"?> <wp:Notification xmlns:wp="WPNotification" Version="2.0"> <wp:Tile Id="[Tile ID]" Template="IconicTile"> <wp:SmallIconImage Action="Clear">[small Tile size URI]</wp:SmallIconImage> <wp:IconImage Action="Clear">[medium/wide Tile size URI]</wp:IconImage> <wp:WideContent1 Action="Clear">[1st row of content]</wp:WideContent1> <wp:WideContent2 Action="Clear">[2nd row of content]</wp:WideContent2> <wp:WideContent3 Action="Clear">[3rd row of content]</wp:WideContent3> <wp:Count Action="Clear">[count]</wp:Count> <wp:Title Action="Clear">[title]</wp:Title> <wp:BackgroundColor Action="Clear">[hex ARGB format color]</wp:BackgroundColor> </wp:Tile> </wp:Notification>
Working With Multiple Tiles
The previous code, in addition to being supported in the application or in a background agent to update the main Tile, can also be used to create multiple Tiles—a feature introduced in Windows Phone 7.5. Secondary Tiles behave like the main ones: they can be updated by notifications and moved or deleted from the Start screen.
The difference is that secondary Tiles have a unique ID, which is the Tile’s deep link. The main Tile always opens the application’s main page, while secondary Tiles can open another page of the application and include one or more query string parameters to identify the context. For example, a weather application can create Tiles for the user’s favorite cities, and every Tile will redirect the user to the forecast page for the selected city.
The base class to interact with Tiles is called ShellTile
, which belongs to the Microsoft.Phone.Shell
namespace.
Creating a secondary Tile is simple: you call the Create()
method by passing the Tile’s deep link and the Tile itself, using one of the classes we’ve seen before. The following sample shows how to create a secondary Tile using the flip template:
private void OnCreateFlipTileClicked(object sender, RoutedEventArgs e) { FlipTileData data = new FlipTileData { SmallBackgroundImage = new Uri("Assets/Tiles/FlipCycleTileSmall.png", UriKind.Relative), BackgroundImage = new Uri(“Assets/Tiles/FlipCycleTileMedium.png”, UriKind.Relative), WideBackgroundImage = new Uri(“Assets/Tiles/FlipCycleTileLarge.png”, UriKind.Relative), Title = “Flip tile”, BackTitle = “Back flip tile”, BackContent = “This is a flip tile”, WideBackContent = “This is a flip tile with wide content”, Count = 5 }; ShellTile.Create(new Uri(“/MainPage.xaml?id=1”, UriKind.Relative), data, true); }
When the application is opened using this Tile, you’ll be able to understand the context and display the proper information using the OnNavigatedTo
method and the NavigationContext
class we used earlier in this series.
Note: To avoid inappropriate usage of secondary Tiles, every time you create a new Tile the application will be closed to immediately display it to the user.
Deleting a secondary Tile requires working with the ShellTile
class again. It exposes a collection called ActiveTiles
, which contains all the Tiles that belong to the application, including the main one. It’s sufficient to get a reference to the Tile we want to delete (using the deep link as an identifier) and call the Delete()
method on it.
private void OnDeleteTileClicked(object sender, RoutedEventArgs e) { Uri deepLink = new Uri(“/MainPage.xaml?id=1”, UriKind.Relative); ShellTile tile = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri == deepLink); if (tile != null) { tile.Delete(); } }
The previous sample deletes the Tile identified by the deep link /MainPage.xaml?id=1
. Unlike when the Tile is created, the application won’t be closed.
Tip: Remember to always check that a Tile exists before removing it. Like every other Tile, in fact, users can also delete one on the main page by tapping and holding Tile and then tapping the Unpin icon.
Tiles can also be updated. Updates can be performed not only by the main application but also in the background by a background agent.
The approach is similar to the one we’ve seen for the delete operation. First we have to retrieve a reference to the Tile we want to update, and then we call the Update()
method, passing the Tile object as a parameter. The following sample shows how to update a Tile that uses the flip template:
private void OnUpdateMainTileClicked(object sender, RoutedEventArgs e) { FlipTileData data = new FlipTileData { Title = “Updated Flip tile”, BackTitle = “Updated Back flip tile”, BackContent = “This is an updated flip tile”, WideBackContent = “This is an updated flip tile with wide content”, Count = 5 }; Uri deepLink = new Uri(“/MainPage.xaml?id=1”, UriKind.Relative); ShellTile tile = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri == deepLink); if (tile != null) { tile.Update(data); } }
The Update()
method can also be used to update the application’s main Tile. It’s always stored as the first element of the ActiveTiles
collection, so it’s enough to call the Update()
method on it as in the following sample:
private void OnUpdateMainTileClicked(object sender, RoutedEventArgs e) { FlipTileData data = new FlipTileData { Title = “Updated Flip tile”, BackTitle = “Updated Back flip tile”, BackContent = “This is an updated flip tile”, WideBackContent = “This is an updated flip tile with wide content”, Count = 5 }; ShellTile.ActiveTiles.FirstOrDefault().Update(data); }
The previous code will always work, even if the main Tile is not pinned to the Start screen. If the user decides to pin it, the Tile will already be updated with the latest notification.
Tip: You can invite users to pin the main Tile on the Start screen, but you can’t force it by code.
Interacting With the Lock Screen
Windows Phone 8 has introduced a new way for applications to interact with users, thanks to the lock screen support. There are two ways to interact with it:
- Display notifications in the same way the Messaging and Mail apps display the number of unread messages.
- Change the lock screen image; specifically, the application can become a lock screen provider and occasionally update the image using a background agent.
Let’s see in detail how to support both scenarios.
Notifications
In the Settings page, users can choose up to five applications that are able to display counter notifications, and only one application that is able to display text notifications.
To support both scenarios in our application, we need to manually add a new declaration in the manifest file (remember to use the View code option in the context menu since it’s not supported by the visual editor):
<Extensions> <Extension ExtensionName=“LockScreen_Notification_IconCount” ConsumerID=“{111DFF24-AA15-4A96-8006-2BFF8122084F}” TaskID=“_default” /> <Extension ExtensionName=“LockScreen_Notification_TextField” ConsumerID=“{111DFF24-AA15-4A96-8006-2BFF8122084F}” TaskID=“_default” /> </Extensions>
The first extension is used to support counter notifications, while the second one is used for text notifications. You can declare just one of them or both, according to your requirements.
If you want to support counter notifications, there’s another modification to apply to the manifest file: inside the Tokens section, you’ll find the tags that define the main Tile’s basic properties. One of them is called DeviceLockImageURI
, which you need to set with the path of the image that will be used as an icon for the notifications.
<DeviceLockImageURI IsRelative=“true” IsResource=“false”>Assets/WinLogo.png</DeviceLockImageURI>
The image should have the following properties:
- The supported resolution is 38 × 38.
- It has to be in PNG format.
- It can contain only transparent or white pixels. No other colors are supported.
Once your application is set, you don’t have to do anything special to display lock screen notifications. In fact, they are based on Tile notifications, so you’ll be able to update both the Tile and the lock screen with just one notification.
- If your application supports counter notifications, you need to send a Tile notification with the number stored in the
Count
property. - If your application supports text notifications, you need to send a Tile notification with the text stored in the
WideBackContent
property for the flip template or theWideContent1
property for an iconic template. The cycle template doesn’t support text notifications.
Lock Screen Image
The starting point for supporting lock screen images is, again, the manifest file. The following sample is the declaration that should be added in the Extensions section:
<Extensions> <Extension ExtensionName=“LockScreen_Background” ConsumerID=“{111DFF24-AA15-4A96-8006-2BFF8122084F}” TaskID=“_default” /> </Extensions>
Starting now, your application will be listed as a wallpaper provider in the Settings page. If the user chooses your application as a provider, you’ll be able to update the lock screen image, both from the foreground app and using a background agent.
The APIs allow you to check whether the application has already been set as a provider, or you can ask the user. If the application is set as a provider, you will be able to effectively change the wallpaper; otherwise you’ll get an exception.
Two classes are part of the Windows.Phone.System.UserProfile
namespace: LockScreenManager
can be used to detect the current provider status, and LockScreen
can effectively perform operations on the lock screen.
private async void OnSetWallpaperClicked(object sender, RoutedEventArgs e) { Uri wallpaper = new Uri(“ms-appx:///Assets/Wallpapers/Wallpaper1.jpg”, UriKind.RelativeOrAbsolute); bool isProvider = LockScreenManager.IsProvidedByCurrentApplication; if (isProvider) { LockScreen.SetImageUri(wallpaper); } else { LockScreenRequestResult lockScreenRequestResult = await LockScreenManager.RequestAccessAsync(); if (lockScreenRequestResult == LockScreenRequestResult.Granted) { LockScreen.SetImageUri(wallpaper); } } }
The first step is to check if the current application is set as a provider by using the IsProvidedByCurrentApplication
property of the LockScreenManager
class. Otherwise, we ask for the user’s permission by calling the RequestAccessAsync()
method. In return, we receive the user’s choice, which can be positive (LockScreenRequestResult.Granted
) or negative (LockScreenRequestResult.Denied
).
In both cases, only if the application has been set as provider can we effectively change the lock screen image using the SetImageUri()
method, which requires the picture’s path as a parameter. The picture can be either part of the project (as in the previous sample where we use the ms-appx:///
prefix) or stored in the local storage (in this case, we have to use the ms-appdata:///Local/
prefix). Remote images are not directly supported; they must be downloaded before using them as a lock screen background.
The previous code can also be used for the background agent. The difference is that you’ll be able to check whether the application is set as a provider and, eventually, change the image. You won’t be able to ask to the user for permission to use your app as a provider since background agents can’t interact with the UI.
Conclusion
In this tutorial, we have seen that live apps are one of the core concepts in Windows Phone development and, to properly create a quality experience, many factors are involved, like notifications, agents, and Tiles.
The following list details what we’ve learned:
- Push notifications are the best way to notify users of something even if the application is in the background. An app can either send toast notifications or update Tiles. We’ve seen how to create the required architecture for reach, both on the client side and the server side.
- Push notifications offer the best approach for optimizing battery life and performance, but they support limited scenarios and require a server application to send them. For this reason, Windows Phone has introduced background agents, which we can periodically execute to send notifications or carry out general purpose operations, even when the application is not in the foreground.
- Windows Phone offers a special background agent type called audio background agent that is used in audio playback scenarios. Applications are able to play audio even when they are not running, like the native Music + Videos Hub.
- Alarms and reminders are a simple way to show reminders to users when the application is not running.
- Live Tiles are one of the distinctive features of the platform. We’ve learned how to customize them by choosing between different templates and sizes.
- We’ve seen another new feature introduced in Windows Phone 8, lock screen support: applications are now able to interact with the lock screen by displaying notifications and changing the wallpaper.
This tutorial represents a chapter from Windows Phone 8 Succinctly, a free eBook from the team at Syncfusion.
Comments