locked
Push Notification in windows store app

    Question

  • Hi,

    I am new in windows store app development. 

    Trying to develop an app , in that app i want to include push notification.

    Please explain step by step to do so in windows store app .

    Thanx in advance!!

    Monday, April 08, 2013 11:33 AM

Answers

  • For the full story, I recommend the series of posts I have on the Windows 8 Developer Blog:

    Part 1 will give you the background of push notifications in the context of all other options, and Part 3 shows how to use them both in their raw form and via Azure Mobile Services, the latter of which greatly simplifies implementation.

    Just to note, I wrote these posts to expand on Chapter 13 of my book linked below.

    Kraig

    Author, Programming Windows 8 Apps with HTML, CSS, and JavaScript, a free ebook from Microsoft Press


    Monday, April 08, 2013 2:47 PM
  • The code you show here is not creating proper XML--you just copy-pasted the "schema" which will not work.

    A "schema" is a generalized definition of what can be expressed in the XML, but the XML itself has to be properly formed for Windows to recognize the notification as valid.

    To learn more about schemas, see http://www.w3schools.com/Schema/default.asp. 

    For toast notifications in general, the Toast template catalog I linked to before shows you what the actual (final) XML should look like. For the template you indicate, ToastImageAndText01, the XML string needs to look like this:

    <toast>
        <visual>
            <binding template="ToastImageAndText01">
                <image id="1" src="<uri>" alt="<alt text>"/>
                <text id="1">Your toast text goes here</text>
            </binding>  
        </visual>
    </toast>

    Where you *must* replace <uri> with a URI to the image, <alt text> must describe the image, and "Your toast text goes here" you replace with the text you want.

    For more examples of this, please study the Toast notifications sample in the SDK. This shows how to issue toasts locally, but makes it easy to work see how the XML should be formed. You might look specifically at the C# version of the sample as that code will translate better to an ASP.NET service.

    The bottom line is that you must have properly-formed XML for the push notification to work, and you haven't yet created that XML. To do this correctly, (a) work out the XML you need using the Toast notifications sample using local updates, (b) use the Notifications Extensions library to build the XML, which will reduce the chances of making errors.

    Friday, April 19, 2013 4:20 PM

All replies

  • For the full story, I recommend the series of posts I have on the Windows 8 Developer Blog:

    Part 1 will give you the background of push notifications in the context of all other options, and Part 3 shows how to use them both in their raw form and via Azure Mobile Services, the latter of which greatly simplifies implementation.

    Just to note, I wrote these posts to expand on Chapter 13 of my book linked below.

    Kraig

    Author, Programming Windows 8 Apps with HTML, CSS, and JavaScript, a free ebook from Microsoft Press


    Monday, April 08, 2013 2:47 PM
  • Hi Kraig Brockschmidt,

    Thanx for helping Reply,

    I go through all the link which you provide.

    I have done Push Notification in Windows phone app and it is working properly. But in the case of windows store app. Whole functionality is so different.

    I have associate my app with developer account and get the Package Security Identifier (SID) and Client secret.

    after that i am not able to create our cloud service which will send the notification request to WNS Server.

    Please help me to create that service and Why this Push Notification thing is different to Windows phone push nofication.

    Waiting for your reply!!

    Thanking You!!

    Thursday, April 18, 2013 7:24 AM
  • Hi Krang,

    I have tried to create push notification client and a server Component  which will communicate with WNS server. 

    I am posting my code Please Review it i am not able to get any notification :(

    default.js

    // For an introduction to the Blank template, see the following documentation:
    // http://go.microsoft.com/fwlink/?LinkId=232509
    (function () {
        "use strict";

        WinJS.Binding.optimizeBindingReferences = true;

        var app = WinJS.Application;
        var activation = Windows.ApplicationModel.Activation;

        app.onactivated = function (args) {
            if (args.detail.kind === activation.ActivationKind.launch) {
                if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
                    // TODO: This application has been newly launched. Initialize
                    // your application here.
                } else {
                    // TODO: This application has been reactivated from suspension.
                    // Restore application state here.
                }
                args.setPromise(WinJS.UI.processAll());
            }
        };

        app.oncheckpoint = function (args) {
            // TODO: This application is about to be suspended. Save any state
            // that needs to persist across suspensions here. You might use the
            // WinJS.Application.sessionState object, which is automatically
            // saved and restored across suspension. If you need to complete an
            // asynchronous operation before your application is suspended, call
            // args.setPromise().
        };


        Windows.Networking.PushNotifications.PushNotificationChannelManager
            .createPushNotificationChannelForApplicationAsync()
            .done(function (channel) {
                //Typically save the channel URI to appdata here.
                WinJS.xhr({
                    type: "POST", url: "http://localhost:63919/receiveuri.aspx",  // Service which i have deploy on my local server
                    headers: { "Content-Type": "application/x-www-form-urlencoded" },
                    data: "channelUri=" + encodeURIComponent(channel.uri)       // HERE I AM ABLE TO GET channelUri .. My app is register with Developer Account
                        + "&itemId=" + "mainAppTile"
                }).done(function (request) {
                    //Typically update the channel URI in app data here.
                }, function (e) {
                    //Error handler
                });
            });
        app.start();
    })();

    receiveuri.aspx.cs 

    protected void Page_Load(object sender, EventArgs e)
            {
                 //Output page header
                   Response.Write("<!DOCTYPE html>\n<head>\n<title>Register Channel URI</title>\n</head>\n<html>\n<body>");
        
        //If called with HTTP GET (as from a browser), just show a message.
        if (Request.HttpMethod == "GET")
        {
            Response.StatusCode = 400;
            Response.Write("<p>This page is set up to receive channel URIs from a push notification client app.</p>");
            Response.Write("</body></html>");
            return;
        }

        if (Request.HttpMethod != "POST") {
            Response.StatusCode = 400;
            Response.Status = "400 This page only accepts POSTs.";
            Response.Write("<p>This page only accepts POSTs.</p>");
            Response.Write("</body></html>");        
            return;
        }
        
        //Otherwise assume a POST and check for parameters    
        try
        {
            //channelUri and itemId are the values posted from the Push and Periodic Notifications Sample in the Windows 8 SDK
            if (Request.Params["channelUri"] != null && Request.Params["itemId"] != null)
            {
                // Obtain the values, along with the user string
                string uri = Request.Params["channelUri"];
                string itemId = Request.Params["itemId"];
                string user = Request.Params["LOGON_USER"];
                     
                //TODO: validate the parameters and return 400 if not.
                
                //Output in response
                Response.Write("<p>Saved channel data:</p><p>channelUri = " + uri + "<br/>" + "itemId = " + itemId + "user = " + user + "</p>");
                //The service should save the URI and itemId here, along with any other unique data from the app such as the user;
                SaveChannel(uri, itemId, user);
                Response.Write("</body></html>");
            }
        }
        catch (Exception ex)
        {
            Trace.Write(ex.Message);
            Response.StatusCode = 500;
            Response.StatusDescription = ex.Message; 
            Response.End();
        }
    }
            protected void SaveChannel(String uri, String itemId, String user) // This function able to save Channel URI in file ,... Working Properly 
            {
                //Typically this would be saved to a database of some kind; to keep this demonstration very simple, we'll just use
                //the complete hack of writing the data to a file, paying no heed to overwriting previous data.

                //If running in the debugger on localhost, this will save to the project folder
                string saveLocation = Server.MapPath(".") + "\\" + "channeldata_aspx.txt";
                string data = uri + "~" + itemId + "~" + user;

                System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
                byte[] dataBytes = encoding.GetBytes(data);

                using (System.IO.FileStream fs = new System.IO.FileStream(saveLocation, System.IO.FileMode.Create))
                {
                    fs.Write(dataBytes, 0, data.Length);
                }

                return;
            }
            }

    SendBadgeToWNS.aspx.cs   // To authenticate and to send request of notification to WNS to

            protected void Page_Load(object sender, EventArgs e)
            {
                //Load our data that was previously saved. A real service would do a database lookup here
            //with user- or tile-specific criteria.
            string loadLocation = Server.MapPath(".") + "\\" + "channeldata_aspx.txt";
            byte[] dataBytes;
            
            using (System.IO.FileStream fs = new System.IO.FileStream(loadLocation, System.IO.FileMode.Open))
            {
                dataBytes = new byte[fs.Length];
                fs.Read(dataBytes, 0, dataBytes.Length);
            }

            if (dataBytes.Length == 0)
            {
                return;
            }
            
            System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();

            string data = encoding.GetString(dataBytes);
            string[] values = data.Split(new Char[] { '~' });
            string uri = values[0]; //Channel URI
            string secret = "MY SECRET KEY";
            string sid = "MY SID";
            //Create some simple XML for a badge update
            string xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>";
            xml += "<badge value='alert'/>";
                        
           // PostToWns(secret, sid, uri, xml, "wns/badge");
            PostToWns(secret, sid, uri, xml, "wns/toast", "text/xml");
        }
     public string PostToWns(string secret, string sid, string uri, string xml, string notificationType, string contentType)
    {
        try
        {
            // You should cache this access token.
            var accessToken = GetAccessToken(secret, sid);

            byte[] contentInBytes = Encoding.UTF8.GetBytes(xml);

            HttpWebRequest request = HttpWebRequest.Create(uri) as HttpWebRequest;
            request.Method = "POST";
            request.Headers.Add("X-WNS-Type", notificationType);
            request.ContentType = contentType;
           
            request.Headers.Add("Authorization", String.Format("Bearer {0}", accessToken.AccessToken));

            using (Stream requestStream = request.GetRequestStream())
                requestStream.Write(contentInBytes, 0, contentInBytes.Length);

            using (HttpWebResponse webResponse = (HttpWebResponse)request.GetResponse())
                return webResponse.StatusCode.ToString(); // THIS IS COMING "OK"
        }
        catch (WebException webException)
        {
            string exceptionDetails = webException.Response.Headers["WWW-Authenticate"];
            if (exceptionDetails.Contains("Token expired"))
            {
                GetAccessToken(secret, sid);

                // We suggest that you implement a maximum retry policy.
                return PostToWns(uri, xml, secret, sid, notificationType, contentType);
            }
            else
            {
                // Log the response
                return "EXCEPTION: " + webException.Message;
            }
        }
        catch (Exception ex)
        {
            return "EXCEPTION: " + ex.Message;
        }
    }

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


    private 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;
        }
    }

    protected OAuthToken GetAccessToken(string secret, string sid)
    {
        var urlEncodedSecret = HttpUtility.UrlEncode(secret);
        var urlEncodedSid = HttpUtility.UrlEncode(sid);

        var body = String.Format("grant_type=client_credentials&client_id={0}&client_secret={1}&scope=notify.windows.com", 
                                 urlEncodedSid, 
                                 urlEncodedSecret);

        string response;
        using (var client = new WebClient())
        {
            client.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
            response = client.UploadString("https://login.live.com/accesstoken.srf", body);
        }
        return GetOAuthTokenFromJson(response);
    }


    webResponse.StatusCode.ToString() is coming OK but i am not getting any PushNotification on my Application.

    Please help me to solve this issue..

    Thanking You,

    Thursday, April 18, 2013 12:44 PM
  • I see that you changed the notification type with this line from a badge to a toast:

            PostToWns(secret, sid, uri, xml, "wns/toast", "text/xml");

    However, you're still sending the XML for a badge update, which is created in the lines immediately preceding the one above:

            //Create some simple XML for a badge update
            string xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>";
            xml += "<badge value='alert'/>";

    As a result, Windows is receiving the push notification, but because the XML is invalid for a toast notification, it's rejecting (that is, ignoring) the notification, so nothing appears.

    For toasts, make sure that you're sending XML that matches the toast schema as described in Part 1 of the blog series, where your options are described on the  Toast template catalog.

    Kraig

    Author, Programming Windows 8 Apps with HTML, CSS, and JavaScript, a free ebook from Microsoft Press


    Thursday, April 18, 2013 4:00 PM
  • Hi Kraig,

    Thank you for your valuable suggestion.

    i have change my service like 

       xml="<toast launch? = \"string\" duration? = \"long\"><visual version? =\"integer\" lang? = \"string\" baseUri? = \"anyURI\" branding? = \"none\" addImageQuery? = \"boolean\" >";

       xml=xml+"<binding template  = \"ToastImageAndText01\" =\"AMIT\" fallback? = \"string\"  lang? = \"string\" baseUri? = \"anyURI\" branding? = \"none\" addImageQuery? = \"boolean\" >";
       xml = xml + "<image id  = \"integer\" src = \"string\" alt = \"string\" addImageQuery? = \"boolean\" /><text id = \"integer\" lang? = \"string\" /></binding></visual></toast>";

    and have changed my PostTOwns function to  

    PostToWns(secret, sid, uri, xml, "wns/toast");

    And added  Toast Capablilty TO YES in appxmenifest file .

    Still having the same problem. not able to get any notification in application and  still 

    webResponse.StatusCode.ToString() is coming OK .

    In there any thing which i have to add in the client app (some additional code apart from channel request etc.) and my internet capabilities is also yes.

    Please help me to figure out this problem !!

    Waiting for your valuable response !!

    Thanking you!!

    Friday, April 19, 2013 7:29 AM
  • The code you show here is not creating proper XML--you just copy-pasted the "schema" which will not work.

    A "schema" is a generalized definition of what can be expressed in the XML, but the XML itself has to be properly formed for Windows to recognize the notification as valid.

    To learn more about schemas, see http://www.w3schools.com/Schema/default.asp. 

    For toast notifications in general, the Toast template catalog I linked to before shows you what the actual (final) XML should look like. For the template you indicate, ToastImageAndText01, the XML string needs to look like this:

    <toast>
        <visual>
            <binding template="ToastImageAndText01">
                <image id="1" src="<uri>" alt="<alt text>"/>
                <text id="1">Your toast text goes here</text>
            </binding>  
        </visual>
    </toast>

    Where you *must* replace <uri> with a URI to the image, <alt text> must describe the image, and "Your toast text goes here" you replace with the text you want.

    For more examples of this, please study the Toast notifications sample in the SDK. This shows how to issue toasts locally, but makes it easy to work see how the XML should be formed. You might look specifically at the C# version of the sample as that code will translate better to an ASP.NET service.

    The bottom line is that you must have properly-formed XML for the push notification to work, and you haven't yet created that XML. To do this correctly, (a) work out the XML you need using the Toast notifications sample using local updates, (b) use the Notifications Extensions library to build the XML, which will reduce the chances of making errors.

    Friday, April 19, 2013 4:20 PM
  • Hi Kraig Brockschmidt,

    The XML which i was sending was not proper so windows was not able to fine.

    Now i am able to get notification in my device.

    Thank you for your help.

    Tuesday, April 23, 2013 6:38 AM
  • Hi Kraig,

    Now i am able to send notification using service which is running on local server code is written in C#.

    Actually my app is already consuming some service which are written in java and hosted in Apache server.

    Can i write same service to call WNS server of microsoft to send PushNotification in JAVA code so that i can just one more service in my server to handle that thing.

    I do not want to have 2 server , 1 specially IIS server for push notification and one Apache to handle all other service.

    Please help me to figure out this problem..

    Thankin you 

    Tuesday, April 23, 2013 9:13 AM
  • You can write a service in any language you want. Sending a notification to WNS is nothing more than doing an HTTP POST to the channel URI. You can look at the PostToWns function in the documentation for how that POST is generated. I don't know of an example of this written in Java, but you should be able to translate it. Again, WNS works entirely through HTTP POST so it's not particular to any language.
    Tuesday, April 23, 2013 4:35 PM