Разработка под Windows Phone: Alarms и Reminders

Так ли нужна одновременная работа нескольких приложений в фоновом режиме? На самом деле, в большинстве случаев, нам нужно, чтобы приложение имело возможность оповестить пользователя о том, что что-то изменилось или необходимы какие-то действия с его стороны. И в этой части серии статей мы познакомимся с возможностями системы, которые позволяют нам оповещать пользователя, напоминать ему или даже предоставлять саму востребованную информацию без необходимости запускать приложение!

Предупреждения (Alarm) и напоминания (Reminder)

Приложения в Windows Phone могут использовать оповещения двух типов: предупреждения (Alarms) и напоминания (Reminders), которые будут отображаться пользователю в виде диалоговых окон, по расписанию. Интерфейс оповещений и напоминаний соответствует тому, который используют системные приложения. Это позволяет сторонним приложениям общаться с пользователем на знакомом ему языке системного интерфейса.

Объекты Alarm и Reminder наследуются от класса ScheduledNotification и имеют достаточно много общего, однако, есть и определенный отличия. Классический вариант оповещения Alarm – это будильник, а оповещения Reminder – напоминание о событии в календаре.

Обратите внимание, что каждое приложение может зарегистрировать до 50 оповещений, а точность отображения пользователю – около минуты.

Давайте кратко рассмотрим основные параметры оповещений.

Предупреждения (Alarm)

img_6_6_1

Предупреждение (Alarm) отображается пользователю в виде диалогового окна с 2-мя кнопками отложить (snooze) и закрыть (dismiss), а также тремя текстовыми блоками. Верхний блок – название приложения, которое зарегистрировало предупреждение. Далее, текстовый блок, который отображается всегда и не может быть изменён с надписью Будильник (Alarm) и, самый последний блок – текст, который был указан при создании предупреждения.

Предупреждение (Alarm) позволяет указать звуковой файл, который будет проигран при отображении его пользователю, он проигрывается с нарастающей громкостью.

Если пользователь коснётся любой области оповещения, кроме кнопок, будет загружено основное приложение и отобразится его начальная страница, так, как будто пользователь запустил его из списка программ.

Напоминание (Reminder)

img_6_7_1

Напоминание (Reminder) отображается пользователю в виде диалогового окна с 2-мя кнопками, выпадающим списком для выбора, на сколько отложить оповещение и тремя текстовыми блоками.

Верхний блок – название приложения, которое зарегистрировало предупреждение. Далее, текстовый блок, который отображает заголовок оповещения, который был указан при его создании, и, самый последний блок – текст – содержание напоминания, указанный при создании.

Если пользователь коснётся любой области оповещения, кроме кнопок, будет загружено основное приложение, при этом, разработчик, при создании напоминания может указать URI страницы с параметрами, на которые перейдёт пользователь.

В отличие от предупреждения (Alarm) будет вегда использоваться звук для оповещения установленный в настройках устройства.

Пример работы с Alarm и Reminder

Давайте теперь на практике попробуем разобраться с оповещениями.

Создадим новое приложение на базе стандартного шаблона Windows Phone Application и назовём приложение SimpleNotificationManager.

После того, как проект приложения будет создан, добавьте в приложение поддержку Silverlight for Windows Phone Toolkit.

Убедитесь, что у вас установлен NuGet Package Manager (Tools -> Extension Manager …)

img_6_8

При необходимости – установите.

После этого в меню Visual Studio выберите, Tools -> Library Package Manager -> Manage NuGet Packages …

img_6_2

Откроется графическая утилита установки пактов NuGet. Найдите и установите пакет Silverlight for Windows Phone Toolkit.

img_6_3

После этого, ваш проект будет выглядеть следующим образом.

img_6_4

Перед тем, как продолжить, для всех картинок в папке Toolkit.Content установите тип сборки (Build Action) в Content (по умолчанию стоит Resource).

Итак, Silverlight for Windows Phone Toolkit подключён к проекту. Давайте добавим, как мы это уже делали ранее, пространство имён toolkit в XAML файл MainPage.xaml, чтобы использовать элементы управления из Silverlight for Windows Phone Toolkit на нашей главной странице:

img_6_5

Мы добавили следующую строку:

 xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"

И в результате XAML будет выглядеть следующим образом:

 <phone:PhoneApplicationPage 
    x:Class="SimpleNotificationManager.MainPage"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
    mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    shell:SystemTray.IsVisible="True">

Теперь нужно сделать интерфейс создания оповещений. Нужен выбор типа, выбор времени оповещения, заголовка (в случае напоминания) и содержания. Я сделал простой интерфейс на базе комбинации элементов управления StackPanel:

img_6_9

При этом, XAML код страницы MainPage.xaml (внутри тега phone:PhoneApplicationPage) выглядит следующим образом:

 <!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <!--TitlePanel contains the name of the application and page title-->
    <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
        <TextBlock x:Name="ApplicationTitle" Text="МЕНЕДЖЕР ОПОВЕЩЕН�Й" 
                    Style="{StaticResource PhoneTextNormalStyle}"/>
        <TextBlock x:Name="PageTitle" Text="установка" Margin="9,-7,0,0" 
                    Style="{StaticResource PhoneTextTitle1Style}"/>
    </StackPanel>

    <!--ContentPanel - place additional content here-->
    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
        <StackPanel>
            <RadioButton Content="предупреждение" GroupName="NotificationType" 
                            Name="AlarmType" IsChecked="True" />
            <RadioButton Content="напоминание" GroupName="NotificationType" 
                            Name="ReminderType" />
            <StackPanel Orientation="Horizontal" Margin="15,0,0,0">
                <TextBlock Text="дата и время"  FontSize="22.667" 
                            VerticalAlignment="Center"/>
                <toolkit:DatePicker Name="NotificationDate"/>
                <toolkit:TimePicker Name="NotificationTime"/>
            </StackPanel>
            <StackPanel  Margin="15,15,0,0">
                <TextBlock Name="NotificationTitleCaption" Text="заголовок оповещения"  
                            FontSize="22.667" VerticalAlignment="Center" Visibility="Collapsed"/>
                <TextBox Name="NotificationTitle" FontSize="22.667" 
                            Margin="-10,0,0,0"  Visibility="Collapsed"/>
                <TextBlock Text="текст оповещения"  FontSize="22.667" 
                            VerticalAlignment="Center"/>
                <TextBox Name="NotificationText" FontSize="22.667" Margin="-10,0,0,0"/>
            </StackPanel>
            <Button Content="установить" Name="SetNotification" FontSize="22.667" Width="450"/>
            <Button Content="посмотреть список" Name="CheckNotification" FontSize="22.667" Width="450" />
            </StackPanel>
    </Grid>
</Grid>

В зависимости от того, какой тип оповещения создаётся, поле ввода для заголовка оповещения и соответствующий текстовый блок с названием поля либо отображаются, либо нет. По умолчанию, выбрано предупреждение и эти элементы управления не отображаются. Нам необходимо обрабатывать изменения типа создаваемого оповещения и либо отображать, либо не отображать этот блок.

Если мы назначим обработчики изменения состояний радио-кнопок в XAML коде, может сложиться такая ситуация, что они сработают ещё до того, как создадутся элементы управления, которые мы хотим скрывать/показывать. Поэтому, мы добавим в код обработчик события Loaded страницы, а уже в него добавим регистрацию обработчиков изменения состояния радио-кнопок:

 public MainPage()
{
    InitializeComponent();

    this.Loaded += new RoutedEventHandler(MainPage_Loaded);
}

void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    AlarmType.Checked +=new RoutedEventHandler(AlarmType_Checked);
    ReminderType.Checked +=new RoutedEventHandler(ReminderType_Checked);
}

И напишем обработчики событий радио-кнопок:

 private void AlarmType_Checked(object sender, RoutedEventArgs e)
{
             
    NotificationTitleCaption.Visibility = System.Windows.Visibility.Collapsed;
    NotificationTitle.Visibility = System.Windows.Visibility.Collapsed;
            
}

private void ReminderType_Checked(object sender, RoutedEventArgs e)
{
    NotificationTitleCaption.Visibility = System.Windows.Visibility.Visible;
    NotificationTitle.Visibility = System.Windows.Visibility.Visible;
}

Теперь осталось добавим код, который собственно и создаёт предупреждения и напоминания.

Для упрощения кода, разрешим нашему приложению в каждый момент времени иметь только одно предупреждение и одно напоминание. Зададим для них имена (должны быть уникальными среди всех, создаваемых приложением оповещений) в виде констант в классе:

 const string MY_ALARM = "My Alarm";
const string MY_REMINDER = "My Reminder";

Обратите внимание, что для использования объектов Alarm и Reminder необходимо добавить следующую запись в блок using:

 using Microsoft.Phone.Scheduler;

Теперь напишем две простые функции, которые будут создавать Alarm и Reminder соответственно.

 private void CreateAlarm()
{
    DateTime date = (DateTime)NotificationDate.Value;
    DateTime time = (DateTime)NotificationTime.Value;
    DateTime beginTime = date + time.TimeOfDay;

    if (beginTime < DateTime.Now)
    {
        MessageBox.Show("Указанное время оповещения уже прошло!");
        return;
    }
            
    if (ScheduledActionService.Find(MY_ALARM) != null)
        ScheduledActionService.Remove(MY_ALARM);

    Alarm myAlarm = new Alarm(MY_ALARM);
            
    myAlarm.Content = NotificationText.Text;
                        
            
    myAlarm.RecurrenceType = RecurrenceInterval.None;
    myAlarm.BeginTime = beginTime;
    myAlarm.ExpirationTime = beginTime.AddMinutes(5);

    ScheduledActionService.Add(myAlarm);
            
}


private void CreateReminder()
{
    DateTime date = (DateTime)NotificationDate.Value;
    DateTime time = (DateTime)NotificationTime.Value;
    DateTime beginTime = date + time.TimeOfDay;

    if (beginTime < DateTime.Now)
    {
        MessageBox.Show("Указанное время оповещения уже прошло!");
        return;
    }
            
    if (ScheduledActionService.Find(MY_REMINDER) != null)
        ScheduledActionService.Remove(MY_REMINDER);
            
    Reminder myReminder = new Reminder(MY_REMINDER);
    myReminder.Title = NotificationTitle.Text;
    myReminder.Content = NotificationText.Text;

            
    myReminder.RecurrenceType = RecurrenceInterval.None;
    myReminder.BeginTime = beginTime;
    myReminder.ExpirationTime = beginTime.AddMinutes(5);
    myReminder.NavigationUri = new Uri("/NotificationList.xaml",UriKind.RelativeOrAbsolute);



    ScheduledActionService.Add(myReminder);

}

Давайте взглянем на код функций поближе.

Перед созданием мы выполняем простую проверку, что время, на которые мы хотим назначить оповещение еще не прошло:

 DateTime date = (DateTime)NotificationDate.Value;
DateTime time = (DateTime)NotificationTime.Value;
DateTime beginTime = date + time.TimeOfDay;

if (beginTime < DateTime.Now)
{
    MessageBox.Show("Указанное время оповещения уже прошло!");
    return;
}

Затем, поскольку по дизайну приложения, мы можем иметь только одно предупреждение и одно оповещение, мы ищем, присутствует ли уже ранее созданное оповещение и если оно присутствует, удаляем его.

Ниже, для примера приведён пример для Alarm:

 if (ScheduledActionService.Find(MY_ALARM) != null)
                ScheduledActionService.Remove(MY_ALARM);

Дальше, в обоих функциях создаётся однократное оповещение, которое закончится через 5 минут, после указанного времени.

При этом, для типа Reminder мы задаём заголовок и URI перехода, при нажатии на область уведомления.

 Reminder myReminder = new Reminder(MY_REMINDER);
myReminder.Title = NotificationTitle.Text;
myReminder.Content = NotificationText.Text;
            
myReminder.RecurrenceType = RecurrenceInterval.None;
myReminder.BeginTime = beginTime;
myReminder.ExpirationTime = beginTime.AddMinutes(5);
myReminder.NavigationUri = new Uri("/NotificationList.xaml",
                                   UriKind.RelativeOrAbsolute);

ScheduledActionService.Add(myReminder);

Теперь осталось добавить в XAML код ссылку на обработчик события Click:

 <Button Content="установить" Name="SetNotification" FontSize="22.667" 
        Width="450" Click="SetNotification_Click" />

И добавить в код простой обработчик:

 private void SetNotification_Click(object sender, RoutedEventArgs e)
{
    if ((bool)AlarmType.IsChecked)
        CreateAlarm();
 
    if ((bool)ReminderType.IsChecked)
        CreateReminder();
}

Соберите приложение, запустите его (F5) и проверьте, как оно работает.