Exercise

This lab is based on the Tidy application. The Tidy application allows users to manage projects containing tasks where each task has a due date. Users can add a notification for each task to alert them of its impending due date (or for any other reason). This lab starts with a copy of the application that has no notification-related capabilities. You can find this version in the lab installation folder under Source\Begin. After performing the lab, you should end up with a version that is functionally identical to the one contained in Source\End.

Task 1 – Adding Support for Notification Management

While the Windows Phone Codenamed Mango API for working with notifications is not complex, it can still be cumbersome in the Tidy application’s context that ties notifications to specific tasks. In this task, we add a set of methods to assist us with managing notifications in the application. These methods will demonstrate how to use the new Microsoft.Phone.Scheduler.ScheduledActionService class in order to manage reminders.

  1. Open the starter solution file, Todo.sln, located at the lab installation folder under Source\Begin.
  2. Examine the solution and locate the Todo project. Expand the project’s Misc folder and open Utils.cs. This file contains several utility classes, including the currently empty NotificationHelper static class, which you will work on in the following steps.
  3. Add the following extension method, which we will use to tie a notification of a specific type to a specific task using a combination of the task’s ID and the notification’s type:

    C#

    public static string GetNotificationName<T>(this Guid taskId) where T : ScheduledNotification { return taskId.ToString() + typeof(T).Name; }
  4. Add the following method to the NotificationHelper class:

    C#

    public static T GetExistingNotification<T>(Guid taskID) where T : ScheduledNotification { return ScheduledActionService.Find(taskID.GetNotificationName<T>()) as T; }

    The above method will allow us to retrieve the reminder or alarm associated with a specific task by using the ScheduledActionService’s Find method with a unique generated from the task’s identifier using the method we added in step 3.

  5. Add an additional method that checks whether or not a certain task has a notification set up:

    C#

    public static bool HasNotification<T>(Guid taskID) where T : ScheduledNotification { if (ScheduledActionService.Find(taskID.GetNotificationName<T>()) != null) { return true; } else { return false; } }
  6. Add a method for removing a task’s notification if it exists:

    C#

    public static void RemoveNotification<T>(Guid taskID) where T : ScheduledNotification { if (HasNotification<T>(taskID)) { ScheduledActionService.Remove(taskID.GetNotificationName<T>()); } }

    Note again, how the ScheduledActionService identifies notifications using a unique name.

  7. Finally, add the only method still missing, the one that defines a new notification and associates it with a task:

    C#

    public static T AddNotification<T>(Task task, DateTime date, bool removeOld = true, string body = null) where T : ScheduledNotification { Guid taskID = new Guid(task.Id.ToString()); if (HasNotification<T>(taskID)) { if (removeOld) { RemoveNotification<T>(taskID); } else { return null; } } try { if (typeof(T) == typeof(Reminder)) { return AddReminder(task, date, body, taskID) as T; } if (typeof(T) == typeof(Alarm)) { return AddAlarm(date, body, taskID) as T; } throw new InvalidOperationException("Unsupported notification type."); } catch (Exception e) { MessageBox.Show(e.Message); return null; } }

    Before creating the new notification, the above code first tries to remove any existing notification of the same type that is associated with the task, unless the caller specifies otherwise. The above method uses two helper methods for creating either a reminder or an alarm.

  8. Add the two helper methods used in the previous step by using the following code:

    C#

    private static Reminder AddReminder(Task task, DateTime date, string body, Guid taskID) { Reminder reminder = new Reminder(taskID.GetNotificationName<Reminder>()); reminder.Title = task.Title; reminder.RecurrenceType = Microsoft.Phone.Scheduler.RecurrenceInterval.None; reminder.NavigationUri = UIConstants.MakeReminderUri(task); reminder.Content = body; reminder.BeginTime = date; ScheduledActionService.Add(reminder); return reminder; } private static Alarm AddAlarm(DateTime date, string body, Guid taskID) { Alarm Alarm = new Alarm(taskID.GetNotificationName<Alarm>()); Alarm.RecurrenceType = Microsoft.Phone.Scheduler.RecurrenceInterval.None; Alarm.Sound = new Uri("/Sounds/sound.wav", UriKind.Relative); Alarm.Content = body; Alarm.BeginTime = date; ScheduledActionService.Add(Alarm); return Alarm; }

    Note the highlighted sections in the code above, where we specify the reminder’s deep link URI, and the alarm’s custom sound.

Task 2 – Managing Notifications Through the User Interface

In the previous task, you laid the groundwork for managing task notifications. In this task, you will alter the application’s interface to allow adding, removing, and updating task notifications.

  1. Compile and run the application.
  2. Create a new task or click on an existing one.
  3. In the task view, you should see a toggle button for adding or removing a task’s reminder, and another for managing the task’s associated alarm. The buttons have no effect currently:

    Figure 1

    The task view with non functional notification UI

    The notification management UI (most of which is currently hidden) is already implemented and all that is left is to wire it up properly. We do this by altering the task editing ViewModel.

  4. Examine the Todo project.
  5. Expand the project’s ViewModels folder and open TaskEditViewModel.cs. This file implements the ViewModel used by the task editing view. We will fill out some placeholders contained in the file in order to allow managing reminders.
  6. Navigate to the HasSystemReminder property in the TaskEditViewModel class and change it to the following:

    C#

    public bool HasSystemReminder { get { return NotificationHelper.HasNotification<Reminder>(task.Id); } }

    The above getter simply uses the NotificationHelper we implemented to return whether or not the task viewed has a reminder.

  7. Similarly, change the HasSystemAlarm property to the following:

    C#

    public bool HasSystemAlarm { get { return NotificationHelper.HasNotification<Alarm>(task.Id); } }
  8. Find the IsReminderSettingsEnabled property and change its setter to contain the following code:

    C#

    if (isReminderSettingEnabled != value) { isReminderSettingEnabled = value; base.OnPropertyChanged("IsReminderSettingEnabled"); if (!isReminderSettingEnabled) { NotificationHelper.RemoveNotification<Reminder>(task.Id); ReminderDate = null; ReminderTime = null; ReminderContent = string.Empty; OnPropertyChanged("HasSystemReminder"); } }

    This setter cleans the current task’s reminder if the toggle switch is set to off.

  9. Similarly, change the IsAlarmSettingsEnabled property to the following:

    C#

    public bool IsAlarmSettingEnabled { get { return isAlarmSettingEnabled; } set { if (isAlarmSettingEnabled != value) { isAlarmSettingEnabled = value; base.OnPropertyChanged("IsAlarmSettingEnabled"); if (!isAlarmSettingEnabled) { NotificationHelper.RemoveNotification<Alarm>(task.Id); AlarmDate = null; AlarmTime = null; AlarmContent = string.Empty; OnPropertyChanged("HasSystemAlarm"); } } } }
  10. Find the InitializeReminder method and change it using the following snippet:

    C#

    void InitializeReminder() { //Cache the reminder to prevent too many lookups since a lot of fields //need it if (reminder == null && notSearchedForReminder) { reminder = NotificationHelper.GetExistingNotification<Reminder>(task.Id); notSearchedForReminder = false; } if (reminder != null) { reminderContent = reminder.Content; reminderTime = reminder.BeginTime; reminderDate = reminder.BeginTime; IsReminderSettingEnabled = true; } }

    The above method gets the task’s existing reminder and initializes some fields accordingly. This allows the page to display an existing reminder’s data.

  11. Now change the InitializeAlarm method to initialize the page according to an existing alarm:

    C#

    void InitializeAlarm() { //Cache the alarm to prevent too many lookups since a lot of fields need it if (alarm == null && notSearchedForAlarm) { alarm = NotificationHelper.GetExistingNotification<Alarm>(task.Id); notSearchedForAlarm = false; } if (alarm != null) { alarmContent = alarm.Content; alarmTime = alarm.BeginTime; alarmDate = alarm.BeginTime; IsAlarmSettingEnabled = true; } }
  12. The UI will allow the user to set a reminder’s text, date and time. We need a method to take the information provided by the user and use it to create a reminder for the current task, or update its existing reminder. Change the CheckAndSaveReminder method using the following code:

    C#

    void CheckAndSaveReminder() { if ( HasValidReminder) { DateTime fullDate = new DateTime ( ReminderDate.Value.Year, ReminderDate.Value.Month, ReminderDate.Value.Day, ReminderTime.Value.Hour, ReminderTime.Value.Minute, ReminderTime.Value.Second ); if (DateTime.Now.AddMinutes(3) > fullDate) { MessageBox.Show(ApplicationStrings.ReminderNotFarEnough); return; } NotificationHelper.AddNotification<Reminder>(task, fullDate, true, ReminderContent); OnPropertyChanged("HasValidReminder"); OnPropertyChanged("HasSystemReminder"); } }
  13. And update the similar CheckAndSaveAlarm as well:

    C#

    void CheckAndSaveAlarm() { if (HasValidAlarm) { DateTime fullDate = new DateTime(AlarmDate.Value.Year, AlarmDate.Value.Month, AlarmDate.Value.Day, AlarmTime.Value.Hour, AlarmTime.Value.Minute, AlarmTime.Value.Second); if (DateTime.Now.AddMinutes(1) > fullDate) { MessageBox.Show(ApplicationStrings.AlarmNotFarEnough); return; } NotificationHelper.AddNotification<Alarm>(task, fullDate, true, AlarmContent); OnPropertyChanged("HasValidAlarm"); OnPropertyChanged("HasSystemAlarm"); } }
  14. Compile and launch the application. You should now be able to add reminders and alarms to your tasks, and then access the task by clicking its reminder once it pops up, or launch the application by clicking an alarm

    Note:
    For alarms and reminders to display properly, be sure to navigate away from the application before they are supposed appear. This is most easily done by pressing the Windows button.

    Figure 2

    Adding a reminder to a task

    Figure 3

    The reminder after popping up

    Figure 4

    Adding an alarm to a task

    Figure 5

    The alarm after popping up

  15. This completes the exercise and the lab.