How to send push notifications to the Windows Universal app

    We make a service for students whose main task is to alert classmates about various events. To do this, we primarily use the push notification mechanism. An application developed for iOS and Android will work both on tablets and phones with these operating systems, and the mechanism for sending push notifications does not depend on the device class. Until recently, Windows had to write two separate applications for Windows and Windows Phone, 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 the progress and developed the Universal Windows app, the sending of push notifications to which we also wanted to make universal in terms of code.



    The Edusty service runs on ASP.NET MVC WebAPI and is hosted on 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 were faced with 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 in this way. Since we did not wait for a quick update of the library, it was decided to write the code for sending push notifications on our own.
    Prior to the arrival of universal Windows applications, push notifications for Windows Phone were sent via the Microsoft Push Notification Service (MPNS), while a new Windows Notification Service (WNS) was created for Windows 8.0 applications. Universal Windows applications brought WNS to Windows Phone 8.1 as well. Thus, sending push notifications for both platforms has become possible in one way.

    Generation of notification content


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

    
                 <...>
           

    
                 <...>
           

     
           <...>
    

    The visual tag is required for toast and tile. It is impossible to combine different types into one xml, so you have to do three dispatches for three types. For toast and tile, there are many templates that allow you to have different pop-up notifications and live tiles. 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 toast and tile, the binding tag should be embedded inside the visual tag indicating the selected template, and several templates can be combined in the tile to support different tile sizes. A complete list of templates can be found here and here .

    text1text2

    text1text2text1text2

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


    Obtaining authorization keys


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

    The service receives the notification channel Uri from the client, and in order to get the access token, you need to send a POST request to the address https://login.live.com/accesstoken.srf . The request should contain the heading “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 "). For client_id and client_secret values, see the Microsoft Developer Centeron the page of your application, where client_id looks like "ms-app: // s-1-15 -....", and client_secret looks like "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 generate a POST request by Uri of the notification channel received from the client device. The following headers must be added to the request:
    • “X-WNS-Type”, which can take 3 different values: “wns / toast”, “wns / tile”, “wns / badge”,
    • "ContentType" with a value of "text / xml",
    • "Authorization" with the value "Bearer your access token ."
    Add the previously generated XML to the request body and send. Separate requests must be made for each type of notifications (the access token can not be re-requested if these requests are in a row for a short time).

    Conclusion


    Thus, we decided to issue universal push notification for devices running Windows 8.1 and Windows Phone 8.1. Some time after that, we found out that in Windows Azure there is such a wonderful service as the Azure Notification Hub , in which all this has already been implemented, including sending for iOS, Android and other platforms. Since everything works with us, we decided not to use this feature of Windows Azure for now.

    Especially for lazy people
    var accessToken = GetAccessToken("Z9qiptL...","ms-app://s-1-15...");
                                        var xml = @"" + text1 + @"" + text2 + @"";
                                        byte[] content = Encoding.UTF8.GetBytes(xml);
                                        SendWindowsPush(pushDevice, accessToken, content, "wns/toast");
                                        xml = @"" + text1 + @"" + text2 + @"" + text1 + @"" + text2 + @"";
                                        content = Encoding.UTF8.GetBytes(xml);
                                        SendWindowsPush(pushDevice, accessToken, content, "wns/tile");
                                        xml = @"";
                                        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>
                {
                    new KeyValuePair("grant_type","client_credentials"),
                    new KeyValuePair("client_id",sid),
                    new KeyValuePair("client_secret",secret),
                    new KeyValuePair("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();
            }
    

    Also popular now: