Check out Jim O'Neil's blog here: http://blogs.msdn.com/b/jimoneil/
Also, check out our Windows 8 sponsored section.
Hopefully, you’ve had a chance to review my previous post on Local Notifications as it
sets the foundation nicely for taking the next step to scheduling notifications
to occur at some point in the future – when the application itself may or may
not be actually running.
Bookmark-worthy References for Scheduled
Notifications
Time-specific "local" notifications
Taking the step from a local notification to a
scheduled one is pretty simple; a scheduled notification is more or less a
local notification with a specific delivery time attached to it. Given that
there is no guarantee that the application will be running when that time
arrives, a scheduled notification can actually fire when the application itself
isn’t running.
Any time-management application is an obvious
candidate for incorporating scheduled notifications, but don’t hesitate to
think outside-of-the-box. Reminders may be equally valuable in many
applications that don’t sport a calendar as the user interface; a financial
management application, for instance, could alert you regarding upcoming tax or
other filing deadlines.
Design Considerations for Scheduled
Notifications Badges? We Don’t Need No Stinkin’ Badges
Badges can’t be a target for scheduled
notifications; only tiles and toast are supported.
To Schedule or Not to Schedule
In my previous post on Local Notifications, I
mentioned that you can detect whether tile or toast notifications are enabled and perhaps decide whether or not to bother sending them. With scheduled
notifications, there’s a bit of a twist, because the user’s settings could
change between the point your code schedules the notification and when it’s
received.
Of course, notifications that do get scheduled
may not appear because the user subsequently either opted out of tile updates
via the app bar or turned off all notifications via PC Settings . Conversely,
if your application uses the setting property to decide not to
schedule a notification, say for a sports league practice application, you may
confuse a user that subsequently opted in to notifications, because whether a
given notification appears depends on the state of the app at the point the
notification was constructed (or not) - all quite opaque to the user!
Does Anyone Really Know What Time it Is?
Whenever time is involved, things get
complicated! What time zone? What about Daylight Savings Time, British Summer
Time, and other similar wrinkles?
When you schedule a notification, the delivery
time (and expiration time for tiles) that you specify is localized, but the
underlying notification mechanism converts it to Coordinated
Universal Time (UTC). The time of the notification is
set at the point the notification is constructed, which yields a scenario like
this:
- You’re in Boston on Monday, and your
application schedules a reminder for Tuesday at 3 p.m.
- You fly to San Francisco Monday afternoon,
and upon arrival update your system clock to reflect that you’re now on
Pacific Time.
- On Tuesday, your reminder appears at noon,
since there’s a time zone differential of three hours between Boston and
San Francisco).
In most cases, this is what you want, but what
if you want more of a locale independent reminder – you have medication you
need to take at noon and would like to have a toast notification alert you at
noon, regardless of what time zone you happen to be in? That’s takes a little
more work because the notification time isn’t absolute but rather is dependent
on your local settings. Here’s one way you could go about this:
- Schedule the notification normally; it will
occur at noon ("wall time") as long as you don’t change the system clock.
- Store a notification id, the scheduled time for the
notification (noon), and the details of the notification (perhaps even
the full XML template) in application data storage.
- Setup a background task that will run when
the time zone changes on the device. (For more information on background
tasks, check out the whitepaper and a sample at dev.windows.com which includes
a background task specifically tied to the timeZoneChange SystemTriggerType.)
- In that background task,
If It’s Tuesday, This Must be
Belgium
You can schedule up to 4096 notifications for
an application, so for recurring tasks – say a reminder to fill out a timecard
every Friday – you could just schedule a year or so worth of reminders ahead of
time and then maybe include a separate "meta-reminder" in a year to schedule
the next years’ worth, but there’s a much better way!
With a MaintenanceTrigger you can set up a background
task to run at a regular interval, let’s say every week, by setting the freshnessTime, the number of minutes that
elapse until the background task is scheduled. In the background task itself,
you’d then schedule the next weekly reminder. There are a few constraints to be
aware of though
- The system has up to 15 minutes from when
the freshnessTime elapses to schedule the
background task, so leave an adequate bufffer to ensure the notification
gets scheduled before it actually occurs!
- A background task associated with a MaintenanceTrigger only fires when the
machine is on battery power. The triggered tasks are, however, buffered
so they will fire the next time the machine is plugged in.
The Best Laid Plans...
change! Be
sure to include a mechanism in your app for cleaning up notifications that are
no longer relevant – like a reminder for a meeting that’s been cancelled.
Consider using the id property to link a notification with a
content item if changes to that item may require modifying the notification. An
id is a 16 character string of your choosing, so you could certainly devise an
encoding scheme to create such a mapping.
Finding a notification with a given id
requires traversing the list of notifications, which you can obtain via a call
to GetScheduledTileNotifications or GetScheduledToastNofifications, and then
checking each of the notification ids against whatever encoding scheme you’ve
devised. A matching notification can be easily removed via TileUpdater.removeFromSchedule or ToastNotifier.removeFromSchedule.
Lastly, if you’ve engaged a MaintenanceTrigger for a recurring
notification and you no longer want the notifications to occur, be sure to unregister the background task.
The APIs
Constructing the Notification
You’ll use one of two classes for the
notification – one for tile and one for toast; each of the class constructors
expects two parameters: a completed notification template (see the previous post for a primer) and the localized
notification delivery time.
A ScheduledTileNotification includes two
additional properties over its local counterpart
- id is used as a key to identify a given
scheduled notification in the list of pending notifications; it’s a
string of your choosing of up to 16 characters that you’d use to find a
to-be-delivered notification for deletion or modification.
- deliveryTime (readonly) is the time the
notification will occur, expressed relative to Coordinated Universal Time
(UTC) and is set using local time semantics when the class is
constructed, having been provided as the second argument to the
constructor
Each ScheduledToastNofitication likewise includes id
and deliveryTime properties, but it also offers ‘snooze’ settings, to
provide up to five repetitions of the toast at a defined interval:
- maximumSnoozeCount is the maximum number
of times the toast will appear – up to five – and is set when the
notification is created,
- snoozeInterval is the amount of time
that should elapse between each showing of the toast (up to the maximumSnoozeCount), the range is from
one minute to on hour, inclusive, and also set when the notification is
created.
Scheduling the Notification
Once the scheduled notification instance has
been constructed, it’s a simple call to the addToSchedule method of the
appropriate notifier class, either ToastNotifier or TileUpdater. Remember, badges can’t be updated
via scheduled notifications.
For recurring notifications, as mentioned above, you may want to leverage a
MaintenanceTrigger to schedule the next
notification in the sequence. Full coverage of background tasks is a bit out of
scope for this post, but the SDK background task code sample and whitepaper should provide insight into how to
use task in conjunction with notifications.
Clearing the Notification
Once the scheduled notification has ‘hit’, the
same semantics for clearing it that apply to local notifications apply here. Specifically, you can reset (or more technically clear) a tile to the state specified in the
application’s manifest, and you can programmatically hide a toast, keeping in
mind the guidelines discourage doing so.
More interesting (and challenging) is clearing
notifications that are scheduled to occur but should be removed since they are
no longer relevant or have otherwise been overcome by events. I’ve already
broached that subject in the design consideration section above, but here’s a
itemization of the key API classes and properties you’d leverage to do so:
- the id property of a ScheduledToastNotification or ScheduledTileNotification can be set to
an meaningful string (of up to 16 characters) to get a handle to a
notification that might be modified. If needed, you could use that id
value as a unique index to a persistent data store that records
additional context about the notification.
- use the
removeFromSchedule method
(on ToastNotifier or TileUpdater) to cancel a pending
notification that you’ve identified should no longer be delivered.
In more sophisticated scenarios, when you’ve
engaged a background task to set up a recurring toast or tile notification,
you’ll need to cancel the background task itself as well. I’d recommend doing
this first and then cleaning up any notifications that may have been scheduled
as a follow on step. To disable the background task, you’ll leverage a few of
the methods and properties of BackgroundTaskRegistration:
- allTasks is a collection (a static
dictionary to be exact) of the background tasks that the application has
registered, via a call to BackgroundTaskBuilder.register
- since each task has a name property assigned at creation, you
can identify the specific task of interest by testing each of the
elements of the allTasks collection for the name you
assigned to the background task when it was initially registered.
- when the background task to be cancelled
has been identified, pass its reference to unregister along with a Boolean
parameter indicating whether you want any currently running instances of
the task to be aborted as well. In the context of scheduling a
notification, not cancelling it is reasonable. Just be aware that one
last notification might have gotten scheduled, but if you cancel the task
first and then
removeFromSchedule any last second additional
notifications, you should be all set.
Jim is a Technology Evangelist for Microsoft who covers the Northeast District, namely, New England and upstate New York. He is focused on engaging with the development community in the area through user groups, code camps, BarCamps, Microsoft-sponsored events, and on-line. Currently Jim's focus is on mobile applications and their integration with cloud services.
Jim joined Microsoft in April 2008 after nearly 12 years working for Sybase in a support and sales consultant role for its developer tools, specifically PowerBuilder and EAServer. Prior to that he worked on various DoD projects at MITRE (Bedford, MA) and BDM International (in McLean, VA, now subsumed by Northrop Grumman). Jim received a B.S. in Mathematics and Computer Science from Austin Peay State University and his M.S. in Computer Science from Duke University.