📜 ⬆️ ⬇️

How to send push notifications to the Windows Universal app

We are doing a service for students, whose main task is to alert classmates about various events. To do this, we first use the mechanism of push-notifications. An application developed for iOS and Android will work both on tablets and on phones with these operating systems and the mechanism for sending push notifications does not depend on the class of the device. Until recently, Windows and Windows Phone had to write two separate applications, but now it is possible to create a Universal Windows app — universal applications that work on both Windows 8.1 and Windows Phone 8.1. We decided to keep up with progress and developed the Universal Windows app, which we also wanted to make push notifications universal in terms of code.



The Edusty service runs on ASP .NET MVC WebAPI and resides in Windows Azure. Initially, we decided to use the PushSharp library to send push notifications. For iOS and Android, it works fine, the same goes for Windows Phone 7.5 / 8.0, as well as Windows 8.0 / 8.1. When we decided to write the Universal Windows app, we encountered the problem of sending push notifications to devices with Windows Phone 8.1. The maximum version of Windows Phone that PushSharp supported at that time was 8.0. On devices with Windows Phone 8.1, it was possible to send push notifications only through a class designed for Windows 8.x, but live tiles did not work this way. Since we did not wait for a quick update of the library, it was decided to write a code for sending push notifications on our own.
Before the arrival of universal Windows applications, push notifications for Windows Phone were sent through the Microsoft Push Notification Service (MPNS), while for Windows 8.0 applications a new Windows Notification Service (WNS) was created. Universal Windows applications brought WNS and for Windows Phone 8.1. Thereby, it is possible to send push notifications for both platforms in one way.

Notification content generation


The universal Windows application supports three types of notifications: toast (pop-up notification), live tile (live tile), badge (plaque with a number). A push notification is generated by XML markup, the root tag of which defines the type of notification.
')
<toast> <visual> <...> </visual> </toast> 

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

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

The visual tag is required for toast and tile. You cannot combine different types into one xml, so you have to make three shipments for three types. For toast and tile there are many templates that allow you to arrange pop-up notifications and live tiles in different ways. Some templates are supported only on Windows, others only on Windows Phone, and there are those that are supported on both platforms - we chose among them.

For the toast and the tile inside the visual tag, a binding tag must be attached with an indication of the selected template, and several templates can be combined into the tile to support different tile sizes. A full list of templates can be found here and here .

 <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> 

For the badge tag, you only need to specify the value parameter, the values ​​of which are numbers or glyphs displayed on the application tile.

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

Getting authorization keys


Before we send a push notification, we need to get a notification channel Uri (PushNotificationChannel.Uri) and an access token (access_token).

The notification channel receives the Uri of the service from the client, and in order to receive the access token, you need to send a POST request to https://login.live.com/accesstoken.srf . The request should contain the header “Content-Type: application / x-www-form-urlencoded”, and the body should contain the following parameters: grant_type (the value is always “client_credentials”), client_id, client_secret, scope (the value is always “notify.windows.com "). The values ​​of client_id and client_secret can be found in the development center for Microsoft accounts on the page of your application, where client_id has the form "ms-app: // s-1-15 -....", and client_secret has the form "Z9qiptLV ... .. " In response to the request, json will come with two fields: access_token and token_type, where the first field is the access token we need.

Sending push notifications


Now you can start sending push notifications. To do this, we will create a POST request for the Uri of the notification channel received from the client device. The request must add the following headers:Add the previously generated XML to the request body and send it. For each type of notification, separate requests should be made (the access token should not be re-requested if these requests go in a row for a short time).

Conclusion


Thus, we solved the issue of universal push notifications for devices running Windows 8.1 and Windows Phone 8.1. Some time later, we learned that in Windows Azure there is such a great service as Azure Notification Hub , in which all this has already been implemented, including sending for iOS, Android and other platforms. Since everything is working with us, we decided not to use this feature of Windows Azure yet.

Especially for lazy people
 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(); } 

Source: https://habr.com/ru/post/243383/


All Articles