Pull to refresh
0

Как отправлять push-уведомления на Windows Universal app

Reading time 6 min
Views 9K
Мы делаем сервис для студентов, основной задачей которого является оповещение одногруппников о различных событиях. Для этого в первую очередь мы используем механизм push-уведомлений. Приложение, разработанное для iOS и Android, будет работать как на планшетах, так и на телефонах с этими операционными системами и механизм отправки push-уведомлений не зависит от класса устройтва. До недавнего времени для Windows и Windows Phone приходилось писать два раздельных приложения, однако теперь есть возможность создавать Universal Windows app — универсальные приложения, которые работают как на Windows 8.1, так и на Windows Phone 8.1. Мы решили не отставать от прогресса и разработали Universal Windows app, отправку push-уведомлений на которое мы также хотели сделать универсальной с точки зрения кода.



Сервис Edusty работает на ASP .NET MVC WebAPI и располагается в Windows Azure. Изначально для отправки push-уведомлений мы решили использовать библиотеку PushSharp. Для iOS и Android она работает отлично, тоже самое касается Windows Phone 7.5/8.0, а также Windows 8.0/8.1. Когда же мы решили писать Universal Windows app, то столкнулись с проблемой отправки push-уведомлений на устройства с Windows Phone 8.1. Максимальная версия Windows Phone, которую PushSharp поддерживал на тот момент была 8.0. На устройства с Windows Phone 8.1 удавалось отправить push-уведомления только через класс, предназначеный для Windows 8.х, но живые плитки таким образом не работали. Так как быстрого обновления библиотеки мы не дождались, то было принято решение написать код отправки push-уведомлений самостоятельно.
До прихода универсальных Windows приложений push-уведомления для Windows Phone отправлялись через Microsoft Push Notification Service (MPNS), тогда как для Windows 8.0 приложений был создан новый сервис Windows Notification Service (WNS). Универсальные приложения Windows принесли WNS и для Windows Phone 8.1. Тем самым, отправлять push-уведомления для обеих платформ стало возможным одним способом.

Формирование контента уведомления


Универсальное приложение Windows поддерживает три типа уведомлений: toast (всплывающее уведомление), live tile (живая плитка), badge (бляха с цифрой).Push-уведомление формируется XML-разметкой, корневой тег которой определяет тип уведомления.

<toast>
       <visual>
             <...>
       </visual>
</toast>

<tile>
       <visual>
             <...>
       </visual>
</tile>

<badge> 
       <...>
</badge>

Тег visual обязателен для toast и tile. Объединять разные типы в один xml нельзя, так что придётся делать три отправки для трёх типов. Для toast и tile существует множество шаблонов, которые позволяют по-разному оформить всплывающие уведомления и живые плитки. Некоторые шаблоны поддерживаются только на Windows, другие только на Windows Phone, а есть те, которые поддерживаются на обеих платформах — среди них мы и выбирали.

Для toast и tile внутри тега visual должен быть вложен тег binding с указанием выбранного шаблона, причём в tile можно объединять несколько шаблонов для поддержки разных размеров плиток. Полный список шаблонов можно взять здесь и здесь.

<toast>
       <visual>
             <binding template="ToastText02">
                        <text id="1">text1</text>
                        <text id="2">text2</text>
             </binding>
       </visual>
</toast>

<tile>
       <visual>
           <binding template="TileSquare150x150Text02" fallback="TileSquareText02">
                     <text id="1">text1</text>
                     <text id="2">text2</text>
           </binding>
           <binding template="TileWide310x150Text09" fallback="TileWideText09">
                     <text id="1">text1</text>
                     <text id="2">text2</text>
           </binding>    
       </visual>
</tile>

Для тега badge необходимо указать лишь параметр value, значениями которого являются числа или глифы, отображаемые на плитке приложения.

<badge value="1"> 
</badge>

Получение ключей авторизации


Перед тем, как отправить push-уведомление, нам необходимо получить Uri канала уведомлений (PushNotificationChannel.Uri) и токен доступа (access_token).

Uri канала уведомлений сервис получает от клиента, а чтобы получить токен доступа, необходимо отправить POST-запрос по адресу https://login.live.com/accesstoken.srf. В запросе должен присутствовать заголовок «Content-Type: application/x-www-form-urlencoded», а тело содержать следующие параметры: grant_type (значение всегда «client_credentials»), client_id, client_secret, scope (значение всегда «notify.windows.com»). Значения client_id и client_secret можно узнать в центре разработки для учётных записей Майкрософт на странице вашего приложения, где client_id имеет вид «ms-app://s-1-15-....», а client_secret имеет вид «Z9qiptLV.....». В ответ на запрос придёт json с двумя полями: access_token и token_type, где первое поле и является необходимым нам токеном доступа.

Отправка push-уведомления


Теперь можно начинать отправлять push-уведомления. Для этого сформируем POST-запрос по Uri канала уведомлений, полученному с устройства клиента. В запрос необходимо добавить следующие заголовки:
  • «X-WNS-Type», который может принимать 3 различных значения: «wns/toast», «wns/tile», «wns/badge»,
  • «ContentType» со значением «text/xml»,
  • «Authorization» со значением «Bearer ваш токен доступа».
Добавляем в тело запроса сформированный ранее XML и отправляем. Для каждого типа уведомлений нужно делать отдельные запросы (токен доступа можно не перезапрашивать, если эти запросы идут подряд в течение короткого времени).

Заключение


Таким образом мы решили вопрос универсальной отправки push-уведомлений для устройств под управлением Windows 8.1 и Windows Phone 8.1. Через некоторое время после этого мы узнали, что в Windows Azure есть такой замечатльный сервис как Azure Notification Hub, в котором это всё уже реализовано, в том числе отправка для iOS, Android и других платформ. Поскольку у нас и так всё работает, мы решили пока не пользоваться этой функцией Windows Azure.

Специально для лентяев
var accessToken = GetAccessToken("Z9qiptL...","ms-app://s-1-15...");

                                    var xml = @"<toast>
                                                <visual>
                                                    <binding template=""ToastText02"">
                                                        <text id=""1"">" + text1 + @"</text>
                                                        <text id=""2"">" + text2 + @"</text>
                                                    </binding>
                                                </visual>
                                            </toast>";
                                    byte[] content = Encoding.UTF8.GetBytes(xml);
                                    SendWindowsPush(pushDevice, accessToken, content, "wns/toast");

                                    xml = @"<tile>
                                            <visual version=""2"">
                                                <binding template=""TileSquare150x150Text02"" fallback=""TileSquareText02"">
                                                    <text id=""1"">" + text1 + @"</text>
                                                    <text id=""2"">" + text2 + @"</text>
                                                </binding>
                                                <binding template=""TileWide310x150Text09"" fallback=""TileWideText09"">
                                                   <text id=""1"">" + text1 + @"</text>
                                                   <text id=""2"">" + text2 + @"</text>
                                                </binding>    
                                            </visual>                                          
                                        </tile>";

                                    content = Encoding.UTF8.GetBytes(xml);
                                    SendWindowsPush(pushDevice, accessToken, content, "wns/tile");

                                    xml = @"<badge value=""" + pushDevice.User.UnreadMessagesCount + @"""/>";
                                    content = Encoding.UTF8.GetBytes(xml);
                                    SendWindowsPush(pushDevice, accessToken, content, "wns/badge");

private static OAuthToken GetAccessToken(string secret, string sid)
        {
            HttpContent content = new FormUrlEncodedContent(new List<KeyValuePair<string, string>>
            {
                new KeyValuePair<string, string>("grant_type","client_credentials"),
                new KeyValuePair<string, string>("client_id",sid),
                new KeyValuePair<string, string>("client_secret",secret),
                new KeyValuePair<string, string>("scope","notify.windows.com")
            });
            var client = new HttpClient();
            client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/x-www-form-urlencoded");
            var response = client.PostAsync(new Uri("https://login.live.com/accesstoken.srf"), content).Result;
            var json = response.Content.ReadAsStringAsync().Result;
            return GetOAuthTokenFromJson(json);
        }

private static OAuthToken GetOAuthTokenFromJson(string jsonString)
        {
            using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(jsonString)))
            {
                var ser = new DataContractJsonSerializer(typeof(OAuthToken));
                var oAuthToken = (OAuthToken)ser.ReadObject(ms);
                return oAuthToken;
            }
        }

 [DataContract]
    public class OAuthToken
    {
        [DataMember(Name = "access_token")]
        public string AccessToken { get; set; }
        [DataMember(Name = "token_type")]
        public string TokenType { get; set; }
    }

 private static void SendWindowsPush(PushDevice pushDevice, OAuthToken accessToken, byte[] content, string type)
        {
            var request = HttpWebRequest.Create(pushDevice.PushCode) as HttpWebRequest;
            request.Method = "POST";
            request.Headers.Add("X-WNS-Type", type);
            request.ContentType = "text/xml";
            request.Headers.Add("Authorization", String.Format("Bearer {0}", accessToken.AccessToken));

            using (Stream requestStream = request.GetRequestStream())
                requestStream.Write(content, 0, content.Length);
            var result = request.GetResponse();
            result.GetResponseStream();


        }
Tags:
Hubs:
+13
Comments 6
Comments Comments 6

Articles

Information

Website
edusty.ru
Registered
Founded
Employees
2–10 employees
Location
Россия