Push Notifications в Windows Phone 7 (toast)

Рассматривая возможности Windows Phone 7 нельзя не обратить внимания на механизм Push Notification, которого не было в предыдущих версиях Windows Phone. Этот механизм сам по себе очень удобный для разработчиков и пользователей и позволяет асинхронно уведомлять пользователя о происходящих событиях. Например, сервис из облака может сообщить пользователю о том, что произошло какое-то событие, на которое нужно как-то отреагировать. Давайте разберемся как устроен этот механизм.

В структуре Push Notifications существует специальный сервис в облаке (размещенный за счет мощностей Microsoft), который делает возможность работы уведомлений. Когда устройство хочет получать уведомления от обращается к этому сервису и устанавливает с ним постоянное соединение. В ответ на это сервис выдает этому устройству уникальный URI, используя который можно отправить по протоколу HTTP некоторое сообщение. Это сообщение получает тот самый сервис в облаке и пересылает его соответствующему устройству. Логично, что для каждого устройства и приложения этот URI будет уникальный. Таким образом, работу уведомлений в Windows Phone 7 можно описать так:

  1. Устройство устанавливает соединение с сервером Microsoft и получает уникальный HTTP-адрес;
  2. Устройство передает этот HTTP-адрес тому, кто будет уведомлять устройство о чем-либо;
  3. Когда необходимо уведомить устройство о чем-либо, приложение, которому был передан HTTP-адрес отправляет специальное HTTP-сообщение на этот адрес;
  4. Сервис Microsoft получает это сообщение и по возможности доставляет его на устройство. Если доставить сообщение на устройство не удалось, то отправитель получит соответствующее сообщение.

Схематически этот алгоритм можно изобразить следующим образом.

Существуют три разных вида уведомлений для Windows Phone 7:

  • Tile Notifications — при отправке уведомления указывается URL некоторого изображения, расположенного в сети. Когда устройство получает уведомление оно самостоятельно загружает изображение из сети и обновляет его на рабочем столе устройства. Поскольку в Windows Phone OS CTP, доступной на данный момент размещение своих элементов на рабочем столе недоступно, то мы пока не будем рассматривать этот тип уведомлений и вернемся к нему позже.
  • Toast Notifications — при получении этого типа уведомления на экране устройства всплывает небольшое сообщение, содержащее текст уведомления. При этом приложение в данный момент может быть неактивным. При нажатии на это сообщение пользователь может быстро переключиться к соответствующему уведомлению.
  • Raw Notifications — при получении этого типа уведомления его содержимое передается непосредственно приложению. Приложение в праве обработать содержимое уведомление так, как считает нужным по своему собственному алгоритму. Это наиболее гибкий способ получения уведомлений от внешних сервисов.

Toast Notifications

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

Для начала создадим новое приложение Windows Phone 7 и добавим на форму элемент управления, который будет отображать текущий адрес для отправки уведомлений.

<phoneNavigation:PhoneApplicationPage 
    x:Class="ToastNotification.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phoneNavigation="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Navigation"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="800"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}">

    <Grid x:Name="LayoutRoot">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.Background>
            <ImageBrush ImageSource="FormBackground.jpg" Stretch="UniformToFill"/>
        </Grid.Background>

        <!--TitleGrid is the name of the application and page title-->
        <Grid x:Name="TitleGrid" Grid.Row="0">
            <TextBlock Text="MY APPLICATION" x:Name="textBlockPageTitle" Style="{StaticResource PhoneTextPageTitle1Style}"/>
            <TextBlock Text="Notifications" x:Name="textBlockListTitle" Style="{StaticResource PhoneTextPageTitle2Style}"/>
        </Grid>

        <!--ContentGrid is empty. Place new content here-->
        <Grid x:Name="ContentGrid" Grid.Row="1">
            
            <Border VerticalAlignment="Center" HorizontalAlignment="Center" Margin="20" Padding="20" CornerRadius="5">
                <Border.Background>
                    <SolidColorBrush Color="Black" Opacity="0.2"/>
                </Border.Background>
                <TextBlock Name="ChannelUri"
                           TextWrapping="Wrap"
                           FontSize="30"/>
            </Border>
            
        </Grid>
    </Grid>
    
</phoneNavigation:PhoneApplicationPage>

Теперь при старте приложения следует соединиться с сервисом уведомлений и получить адрес для отправки уведомлений. Для этих целей следует подключить сборку Microsoft.Phone.Notification и использовать объект HttpNotificationChannel. Если мы еще не обращались к сервису уведомлений, то необходимо использовать метод Open() и установить соединение; если ранее мы уже обращались к сервису, то нужно просто заново соединиться с сервисом, используя метод Find(). При установлении соединения мы должны указать уникальное имя канала и/или сервиса. Эти параметры задаются в конструкторе объекта HttpNotificationChannel.

После установления соединения мы можем обратиться к свойству ChannelUri объекта HttpNotificationChannel и получить текущий адрес для уведомлений. Однако, сервис уведомлений может самостоятельно изменить этот адрес по своему усмотрению. Для того, чтобы быть в курсе таких изменений следует подписаться на событие ChannelUriUpdated объекта HttpNotificationChannel и обрабатывать изменения адреса там.

После запуска приложения выведем текущий адрес для уведомлений на экран, а также в консоль отладки (чтобы этот адрес можно было скопировать в буфер обмена). Таким образом мы получим небольшой фрагмент кода для приложения Windows Phone 7.

public partial class MainPage : PhoneApplicationPage
{
    private HttpNotificationChannel notifications;
    private const string notificationServiceName = "Some test";

    public MainPage()
    {
        InitializeComponent();

        SupportedOrientations = SupportedPageOrientation.Portrait | SupportedPageOrientation.Landscape;

        InitializePushChannel();
    }

    private void InitializePushChannel()
    {
        if (notifications != null)
        {
            ShowUri(notifications.ChannelUri);
        }
        else
        {
            try
            {
                notifications = new HttpNotificationChannel(Application.Current.ToString(), notificationServiceName);
                notifications.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(notifications_ChannelUriUpdated);
                notifications.Open();
                ShowUri(notifications.ChannelUri);

                notifications.BindToShellNotification();
            }
            catch (NotificationChannelExistsException)
            {
                notifications = HttpNotificationChannel.Find(Application.Current.ToString());
                notifications.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(notifications_ChannelUriUpdated);
                ShowUri(notifications.ChannelUri);
            }
        }
    }

    private void ShowUri(Uri uri)
    {
        if (uri != null)
        {
            Dispatcher.BeginInvoke(() => ChannelUri.Text = uri.ToString());
            Debug.WriteLine(uri.ToString());
        }
    }

    private void notifications_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
    {
        ShowUri(e.ChannelUri);
    }
}

На этом создание приложения для устройства завершено. Теперь нам необходимо отправить специальное HTTP-сообщение по адресу для уведомлений. Для этих целей можно использовать различные сторонние утилиты, например, Fiddler. Однако, давайте пройдем путь до конца и создадим приложение, которое будет отправлять соответствующие уведомления по указанному адресу.

Для того, чтобы отправить Toast-уведомление следует использовать следующий формат сообщения:

X-WindowsPhone-Target: toast

<?xml version=""1.0"" encoding=""utf-8""?>
<wp:PushNotification xmlns:wp=""WindowsPhonePushNotification"">
    <wp:Toast>
        <wp:Text1>Header</wp:Text1>
        <wp:Text2>Text</wp:Text2>
    </wp:Toast>
</wp:PushNotification>

В данном случае Header и Text – это те значения, которые пользователь увидит у себя на экране. Кроме того, HTTP-сообщение должно иметь заголовок X-NotificationClass, которое отражает важность доставляемого сообщения. Более подробно о формате сообщений и классе важности сообщений можно почитать здесь и здесь.

В ответ на отправляемый HTTP-запрос сервис вернет ответ, в котором содержится два важных HTTP-заголовка — X-DeviceConnectionStatus и X-NotificationStatus. Заголовок X-DeviceConnectionStatus отражает состояние подключения устройства к сервису, а заголовок X-NotificationStatus позволяет понять успешно ли доставлено сообщение.

Теперь, все, что осталось сделать – это отправить соответствующий HTTP-запрос по указанному адресу и обработать результат. Сделаем небольшое консольное приложение в которое поместим следующий код.

class Program
{
    private const string Url = @"http://sn1.notify.live.net/throttledthirdparty/01.00/AAFTkavjFpAuT4i1fIKvueDiAgoOs1ADAgAAAAQOMDAwAAAAAAAAAAAAAAA";

    static void Main(string[] args)
    {
        Console.Write("Header: ");
        string header = Console.ReadLine();
        Console.Write("Text: ");
        string text = Console.ReadLine();
        SendToast(Url, header, text);
        Console.ReadLine();
    }

    static void SendToast(string uri, string header, string text)
    {
        var client = new WebClient();

        client.Headers.Add("Content-Type", "text/html");
        client.Headers.Add("X-NotificationClass", "2");

        var message = "X-WindowsPhone-Target: toast" + Environment.NewLine + Environment.NewLine +
            @"<?xml version=""1.0"" encoding=""utf-8""?>
                <wp:PushNotification xmlns:wp=""WindowsPhonePushNotification"">
                    <wp:Toast>
                        <wp:Text1>{0}</wp:Text1>
                        <wp:Text2>{1}</wp:Text2>
                    </wp:Toast>
                </wp:PushNotification>";



        var result = client.UploadString(uri, "POST", String.Format(message, header, text));
        Console.WriteLine(result);
        Console.WriteLine("Device status is {0}", client.ResponseHeaders["X-DeviceConnectionStatus"]);
        Console.WriteLine("Notification status is {0}", client.ResponseHeaders["X-NotificationStatus"]);
    }
}

Теперь при запуске мобильного приложения из консоли отладки получим нужный URI для отправки уведомлений и вставим его в полученное консольное приложение. При запуске консольного приложения укажем значения Header и Text. Если все прошло успешно, в ответ на этот запрос увидим сообщение о доставке уведомления.

Если теперь взглянуть на экран эмулятора Windows Phone 7, то можно увидеть, что там появилось соответствующее уведомление.

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

Как видно, создать свое приложение, использующее уведомления очень просто. Ну а в следующий раз я расскажу о том, как использовать Raw Notifications.