//----------------------------------------------------------------------------- // PushNotificationSender.cs // // Microsoft XNA Community Game Platform // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- using System; using System.Text; using System.Net; using System.IO; namespace PushNotificationSender { /// /// A utility class for sending the three different types of push notifications. /// public class PushNotificationSender { public enum NotificationType { Tile = 1, Toast = 2, Raw = 3 } public delegate void SendCompletedEventHandler(PushNotificationCallbackArgs args); public event SendCompletedEventHandler NotificationSendCompleted; public const string MESSAGE_ID_HEADER = "X-MessageID"; public const string NOTIFICATION_CLASS_HEADER = "X-NotificationClass"; public const string NOTIFICATION_STATUS_HEADER = "X-NotificationStatus"; public const string DEVICE_CONNECTION_STATUS_HEADER = "X-DeviceConnectionStatus"; public const string SUBSCRIPTION_STATUS_HEADER = "X-SubscriptionStatus"; public const string WINDOWSPHONE_TARGET_HEADER = "X-WindowsPhone-Target"; public const int MAX_PAYLOAD_LENGTH = 1024; /// /// Sends a raw notification, which is just a byte payload. /// public void SendRawNotification(Uri deviceUri, byte[] payload) { SendNotificationByType(deviceUri, payload, NotificationType.Raw); } /// /// Sends a tile notification, which is a title, a count, and an image URI. /// public void SendTileNotification(Uri deviceUri, string title, int count, string backgroundImage) { // Malformed push notifications cause exceptions to be thrown on the receiving end // so make sure we have valid data to start with. if (string.IsNullOrEmpty(title)) { throw new InvalidOperationException("Tile notifications require title text"); } // Set up the XML string msg = "" + "" + ""; if (!string.IsNullOrEmpty(backgroundImage)) { msg += "" + backgroundImage + ""; } msg += "" + count.ToString() + "" + "" + title + "" + "" + ""; byte[] payload = new UTF8Encoding().GetBytes(msg); SendNotificationByType(deviceUri, payload, NotificationType.Tile); } /// /// Sends a toast notification, which is two lines of text. /// public void SendToastNotification(Uri deviceUri, string text1, string text2) { // Malformed push notifications cause exceptions to be thrown on the receiving end // so make sure we have valid data to start with. if (string.IsNullOrEmpty(text1) && string.IsNullOrEmpty(text2)) { throw new InvalidOperationException("toast notifications must have at least 1 valid string"); } // Set up the XML string msg = "" + "" + "" + "" + text1 + "" + "" + text2 + "" + "" + ""; byte[] payload = new UTF8Encoding().GetBytes(msg); SendNotificationByType(deviceUri, payload, NotificationType.Toast); } /// /// helper function to set up the request headers based on type and send the notification payload. /// private void SendNotificationByType(Uri channelUri, byte[] payload, NotificationType notificationType) { // Check the length of the payload and reject it if too long. if (payload.Length > MAX_PAYLOAD_LENGTH) throw new ArgumentOutOfRangeException("Payload is too long. Maximum payload size shouldn't exceed " + MAX_PAYLOAD_LENGTH.ToString() + " bytes"); try { // Create and initialize the request object. HttpWebRequest request = (HttpWebRequest)WebRequest.Create(channelUri); request.Method = WebRequestMethods.Http.Post; request.ContentLength = payload.Length; request.Headers[MESSAGE_ID_HEADER] = Guid.NewGuid().ToString(); // Each type of push notification uses a different code in its X-NotificationClass // header to specify its delivery priority. The three priorities are: // Realtime. The notification is delivered as soon as possible. // Priority. The notification is delivered within 450 seconds. // Regular. The notification is delivered within 900 seconds. // Realtime Priority Regular // Raw 3-10 13-20 23-31 // Tile 1 11 21 // Toast 2 12 22 switch (notificationType) { case NotificationType.Tile: // the notification type for a tile notification is "token". request.Headers[WINDOWSPHONE_TARGET_HEADER] = "token"; request.ContentType = "text/xml"; // Request real-time delivery for tile notifications. request.Headers[NOTIFICATION_CLASS_HEADER] = "1"; break; case NotificationType.Toast: request.Headers[WINDOWSPHONE_TARGET_HEADER] = "toast"; request.ContentType = "text/xml"; // Request real-time delivery for toast notifications. request.Headers[NOTIFICATION_CLASS_HEADER] = "2"; break; case NotificationType.Raw: // Request real-time delivery for raw notifications. request.Headers[NOTIFICATION_CLASS_HEADER] = "3"; break; default: throw new ArgumentException("Unknown notification type", "notificationType"); } Stream requestStream = request.GetRequestStream(); requestStream.Write(payload, 0, payload.Length); requestStream.Close(); HttpWebResponse response = request.GetResponse() as HttpWebResponse; if (NotificationSendCompleted != null && response != null) { PushNotificationCallbackArgs args = new PushNotificationCallbackArgs(notificationType, (HttpWebResponse)response); NotificationSendCompleted(args); } } catch (WebException ex) { // Notify the caller on exception as well. if (NotificationSendCompleted != null) { if (null != ex.Response) { PushNotificationCallbackArgs args = new PushNotificationCallbackArgs(notificationType, (HttpWebResponse)ex.Response); NotificationSendCompleted(args); } } } } } }