Introduction
FlexiPush is basically developed for Developer and Tester who will be developing and testing the apps with push notification features. The team (Developer and Tester) wanted to verify certain use cases which will depends on the server or push notification.
Why Push Notification is useful to us?
If something interesting or important happens and you wish to inform the user know about this, even though application is not running on the user’s device at that time Push Notification comes in picture. Let’s say, maybe the user received a new tweet when their favorite team won the match, or their ticket is booked, or someone sent you a message.
Luckily, Android, iOS and Windows provide solution to this. Instead of your app continuously checking these kind of stuff in background we can write a code on server to do the job instead of doing at client side.
What is Problem
Following are few examples where we can use FlexiPush:
- If application is in background and notification comes?
- Notification comes while traversing app by user, then how notification behaves?
- User tap on notification then will it redirect to specific page or not?
- Application will be able to get the required parameters on the respected page?
If you are not using FlexiPush then you will have to wait till notification comes from the server, and the waiting time may be in Minutes, Hours or a Days.
Solution for the Problem
FlexiPush is a Quick, Easy and Flexible way to send a Push Notifications to the different platforms like Android, iOS and Windows instantly with your custom Payload as well.
Technology Stack
ASP.NET is a development framework for building web pages and web sites with HTML, CSS, JavaScript and server scripting. ASP.NET supports three different development models:
- Web Pages
- MVC (Model View Controller)
- Web Forms
Website: ASP.NET MVC 4
A server-side library for sending Push Notifications to iOS (iPhone/iPad APNS), Android (C2DM and GCM - Google Cloud Message), Windows Phone, Windows 8, Amazon, Blackberry, and (soon) FirefoxOS devices!
Website: Redth/PushSharp
ASP.NET SignalR is a new library for ASP.NET developers that makes developing real-time web functionality easy. SignalR allows bi-directional communication between server and client. Servers can now push content to connected clients instantly as it becomes available.
Website: Getting Started with SignalR 2
Bootstrap is the most popular HTML, CSS, and JS framework for developing responsive, mobile first projects on the web.
Website: Bootstrap
Code Overview
- Model Class Diagram
- Home Screen
On Home page, User will have option to choose Platform and Application Mode.
If Beginner mode is selected then user will be able to send only few parameters value to the notification center. This mode will be generally used by those users who does not know the custom JSON Paloyad and its type.
If Expert mode is selected then user will be able to send all possible JSON Payload. This feature will be generally used by those users who know each and every parameters of custom JSON Payload. Make sure at this point application will not responsible for verify each fields and attributes value or does not perform any validation.
If user has entered invalid payload then they will not receive notification on the device because of improper payload.
- Send Push Notification to Android Phone
- Screen
- Server Code in Controllers/NotificationController.cs
[HttpPost]
public ActionResult SendToAndroid(AndroidPushNotificationModel model, string ClientID, string JSONPayload)
{
responseResult result = new responseResult();
try
{
NotificationController.SocketClientID = ClientID;
updateProcessStatusToClient(100, "Initializing", "Initializing GCM Push Channel Settings...");
var push = new PushBroker();
push.RegisterGcmService(new GcmPushChannelSettings(model.APIKey));
string JSONData = (isExpertMode()) ? JSONPayload : "{\"message\":\"" + model.NotificationText + "\",\"badge\":\"" + model.NotificationBadge + "\",\"sound\":\"" + model.SoundFile + "\"}";
push.QueueNotification(new GcmNotification().ForDeviceRegistrationId(model.DeviceRegistrationID).WithJson(JSONData));
}
catch (Exception ex)
{
updateProcessStatusToClient(110, "Error in sending notification", ex.Message);
}
return Json(result, JsonRequestBehavior.AllowGet);
}
- Send Push Notification to iOS Phone
- Screen
- Server Code in Controllers/NotificationController.cs
First step is Uploading Certificate on server
[HttpPost]
public void UploadFiles(string ClientID)
{
responseResult result = new responseResult();
try
{
NotificationController.SocketClientID = ClientID;
updateProcessStatusToClient(98, "Uploading Certificate...", "Uploading certificate on server");
HttpPostedFileBase CertificateFile = null;
foreach (string file in Request.Files)
{
CertificateFile = Request.Files[file] as HttpPostedFileBase;
if (CertificateFile.ContentLength == 0) continue;
string uploadFileName = System.IO.Path.GetFileName(CertificateFile.FileName);
string uploadFilePathAndName = System.IO.Path.Combine(Server.MapPath(NotificationController.SaveCertificateFolder), uploadFileName);
if (System.IO.File.Exists(uploadFilePathAndName)) System.IO.File.Delete(uploadFilePathAndName);
CertificateFile.SaveAs(uploadFilePathAndName);
}
updateProcessStatusToClient(99, "Certificate Uploaded", "Certificate uploaded on server");
}
catch (Exception ex)
{
result.status = Status.Error;
result.message = string.Format(RESULT_ERROR, ex.Message);
updateProcessStatusToClient(104, "Notification Failed", ex.Message);
}
}
Second step is sending notification to the server
[HttpPost]
public ActionResult SendToiOS(iOSPushNotificationModel model, string ClientID, string JSONPayload)
{
model.isDisableCertificateCheck = false;
responseResult result = new responseResult();
try
{
NotificationController.SocketClientID = ClientID;
updateProcessStatusToClient(100, "Initializing", "Initializing Push Channel Settings...");
string uploadFileName = "";
string certificateFile = "";
if (model.UseDefaultCertificate == "0")
{
certificateFile = System.IO.Path.Combine(Server.MapPath(NotificationController.DefaultCertificateFolder), DefaultCertificateFile);
}
else
{
uploadFileName = System.IO.Path.GetFileName(model.CertificateFile);
certificateFile = System.IO.Path.Combine(Server.MapPath(NotificationController.SaveCertificateFolder), uploadFileName);
}
var push = new PushBroker();
bool isProduction = (model.PublishType == PublishType.Development) ? false : true;
var appleCert = System.IO.File.ReadAllBytes(certificateFile);
int badge = (String.IsNullOrEmpty(model.NotificationBadge)) ? 0 : int.Parse(model.NotificationBadge);
push.RegisterAppleService(new ApplePushChannelSettings(isProduction, appleCert, model.CertificatePassword, model.isDisableCertificateCheck));
if (isExpertMode()) {
push.QueueNotification(new AppleNotification().ForDeviceToken(model.DeviceToken).WithJSON(JSONPayload));
}
else
push.QueueNotification(new AppleNotification().ForDeviceToken(model.DeviceToken).WithAlert(model.NotificationText).WithBadge(badge));
}
catch (Exception ex)
{
result.status = Status.Error;
result.message = string.Format(RESULT_ERROR, ex.Message);
updateProcessStatusToClient(104, "Notification Failed", ex.Message);
}
return Json(result, JsonRequestBehavior.AllowGet);
}
I have modified following files (PushSharp Library) to achieve the expert mode functionality.
- Added following method in AppleFluentNotification.cs
public static AppleNotification WithJSON(this AppleNotification n, string json)
{
if (n.Payload == null)
n.Payload = new AppleNotificationPayload();
n.Payload.JSON = json;
return n;
}
- Added property in AppleNotificationAlert.cs
public string JSON
{
get;
set;
}
- Added property in AppleNotificationPayload.cs
public string JSON { get; set; }
And updated method ToJson() with two lines of code.
public string ToJson()
{
if (!string.IsNullOrEmpty(this.JSON))
return this.JSON;
}
- Send Push Notification to Windows Phone
- Screen
- Server Code in Controllers/NotificationController.cs
PushSharp library does not support to send custom Payload in XML format for windows platform when application mode is Expert, hence I have written code in pure .Net to send XML Payload to windows devices for Tile and Toast notifications.
Following is the method to send notification for windows platform.
[HttpPost]
public ActionResult SendToWindows(WindowsPushNotificationModel model, string ClientID)
{
updateProcessStatusToClient(100, "Initializing", "Initializing Notification Request...");
string WindowsPhoneTarget = (model.NotificationType == Models.NotificationType.Toast) ? "toast" : "token";
string NotificationClass = (model.NotificationType == Models.NotificationType.Toast) ? "2" : "1";
string subscriptionUri = model.ChannelURI;
updateProcessStatusToClient(97, "Preparing Channel Headers", "Preparing headers for Windows phone channel");
HttpWebRequest sendNotificationRequest = (HttpWebRequest)WebRequest.Create(subscriptionUri);
sendNotificationRequest.Method = "POST";
byte[] notificationMessage = Encoding.Default.GetBytes(model.JSONPayload);
sendNotificationRequest.ContentLength = notificationMessage.Length;
sendNotificationRequest.ContentType = "text/xml";
sendNotificationRequest.Headers.Add("X-WindowsPhone-Target", WindowsPhoneTarget);
sendNotificationRequest.Headers.Add("X-NotificationClass", NotificationClass);
using (Stream requestStream = sendNotificationRequest.GetRequestStream())
{
requestStream.Write(notificationMessage, 0, notificationMessage.Length);
}
HttpWebResponse response = (HttpWebResponse)sendNotificationRequest.GetResponse();
string notificationStatus = response.Headers["X-NotificationStatus"];
string notificationChannelStatus = response.Headers["X-SubscriptionStatus"];
string deviceConnectionStatus = response.Headers["X-DeviceConnectionStatus"];
switch (notificationStatus.ToUpper())
{
case "RECEIVED":
case "QUEUEFULL":
case "SUPPRESSED":
updateProcessStatusToClient(101, "Notification Sent", string.Format("Notification Status:{0}, Channel Status:{1}, Device Connection Status:{2}", notificationStatus, notificationChannelStatus, deviceConnectionStatus));
break;
case "N/A":
updateProcessStatusToClient(104, "Notification Failed", "Coloud service sends a notification request with a bad XML document or malformed notification URI");
break;
default:
updateProcessStatusToClient(104, "Notification Failed", "The Push Notification Service is unable to process the request");
break;
}
updateProcessStatusToClient(100, "Initializing", "Initializing Push Channel Settings...");
int badge = (String.IsNullOrEmpty(model.NotificationBadge)) ? 0 : int.Parse(model.NotificationBadge);
var push = new PushBroker();
push.RegisterWindowsPhoneService();
if (model.NotificationType == Models.NotificationType.Tile)
{
push.QueueNotification(new WindowsPhoneTileNotification()
.ForEndpointUri(new Uri(model.ChannelURI))
.ForOSVersion(WindowsPhoneDeviceOSVersion.Eight)
.WithBatchingInterval(BatchingInterval.Immediate)
.WithTitle(model.NotificationText)
.WithCount(badge));
}
else if (model.NotificationType == Models.NotificationType.Toast)
{
push.QueueNotification(new WindowsPhoneToastNotification()
.ForEndpointUri(new Uri(model.ChannelURI))
.ForOSVersion(WindowsPhoneDeviceOSVersion.Eight)
.WithBatchingInterval(BatchingInterval.Immediate)
.WithNavigatePath(model.NavigatePath)
.WithText1(model.NotificationText)
.WithText2(model.NotificationText2));
}
}
- SignalR Code Review
- Installation
- Run the following command in the Package Manager Console
PM> Install-Package Microsoft.AspNet.SignalR -Version 2.0.3
- Create Folder and File, then replace complete code from existing file.
\Hubs\ProcessStatusNotifyHub.cs
- Include SignalR javascript files in file.
<script src="~/Scripts/jquery.signalR-2.1.2.min.js">
</script><script src="~/signalr/hubs"></script>
- Initialize SignalR.
$(function () {
var chat = $.connection.processStatusNotifyHub;
chat.client.notify = function (response) {
}
$.connection.hub.start().done(function () {
$('#ClientID').val($.connection.hub.id);
$('#' + platform + '-overlay').hide().find('div.log').removeClass('loading').addClass('log-' + platform);
});
});
- Notify Client for Process Status using Server Code
Following method will be notifying client for every process step executed on server side, so user will get acknowledge from the server whether notification sent or not.
static public void updateProcessStatusToClient(int status, string title, string message)
{
if (prevStatus != status)
{
prevStatus = status;
string clientid = NotificationController.SocketClientID;
var hubContext = GlobalHost.ConnectionManager.GetHubContext<processstatusnotifyhub>();
SocketResponse response = new SocketResponse() { ClientID = clientid, Status = status, Title = title, Message = message, Time = DateTime.Now.ToString("hh:mm:ss tt") };
var json = new JavaScriptSerializer().Serialize(response);
hubContext.Clients.All.notify(json);
}
}
</processstatusnotifyhub>
- Display Process Status using Client Code
var res = JSON.parse(response);
if (res.ClientID === $('#ClientID').val()) {
var push = $('#' + platform + '-notification');
writeProcessLog(res.Time, res.Title);
switch (res.Status) {
case 100:
trackTimeout = window.setTimeout(function () {
writeProcessLog('', 'Request timeouts.
Please verify you have entered the correct values. Please try again!');
$("div.close-log").show();
}, failureTimeout);
break;
case 101:
clearTimeout(trackTimeout);
window.setTimeout(function () {
$('#' + platform + '-overlay').hide();
if (push.css('display') == 'none') {
$('#notificationText').text(notificationText);
push.show().removeClass(NOTIFICATION_SHOW).addClass(NOTIFICATION_SHOW);
}
}, 1500);
break;
case 103:
case 104:
case 105:
case 106:
case 107:
case 108:
case 110:
clearTimeout(trackTimeout);
$("div.close-log").show();
break;
}
}
- JavaScript Code Snippet
- Field Validation
function checkValidation(fields) {
var _cook = readCookie('ExpertMode');
var _val = (_cook == null || _cook == "" || _cook == "false") ? false : true
var fields, isValid = true;
var msg = $('#NotificationText').val();
switch (platform) {
case 'android':
fields = [$('#APIKey'), $('#DeviceRegistrationID')];
if (_val) {
fields.push($('#JSONPayload'));
notificationText = notificationExpertText;
}
else {
fields.push($('#NotificationText'));
notificationText = msg;
}
break;
case 'windows':
fields = [$('#NotificationType'), $('#ChannelURI')];
if (_val) {
fields.push($('#JSONPayload'));
notificationText = notificationExpertText;
}
else {
fields.push($('#NotificationText'));
notificationText = msg;
}
break;
case 'ios':
fields = [$('#PublishType'), $('#DeviceToken'), $('#UseDefaultCertificate'), $('#CertificatePassword')];
if (_val) {
fields.push($('#JSONPayload'));
notificationText = notificationExpertText;
}
else {
fields.push($('#NotificationText'));
notificationText = msg;
}
break;
}
fields.forEach(function (field) {
if (field.attr('id') == 'UseDefaultCertificate' && field.val() == "1" && $.trim($('#CertificateFile').val()) == "") {
$('#CertificateFile').addClass('required');
isValid = false;
}
else if (field.attr('id') == 'UseDefaultCertificate' && field.val() != "1") {
$('#CertificateFile').removeClass('required');
}
if ($.trim(field.val()) == "") {
field.addClass('required');
isValid = false;
}
else
field.removeClass('required');
});
return isValid;
}
- Upload Certificate File (for iOS only)
var fd = new FormData();
fd.append("fileCertificate", $("#fileCertificate")[0].files[0]);
fd.append("ClientID", $("#ClientID").val());
$.ajax({
url: '/Notification/UploadFiles',
data: fd,
processData: false,
contentType: false,
type: 'POST',
success: function (data) {
submitFormData();
},
error: function (er) {
hideProcessLog('Error in Certificate Upload. Please try again!');
}
});
- Submit Data to the Server
function submitFormData() {
var form = $('#formSend');
var data = form.serialize();
$.ajax({
url: form.attr('action'),
type: 'POST',
data: data,
dataType: 'json',
success: function (result) {
if (result.status == 'Warning') {
hideProcessLog('Warning: ' + result.message);
}
},
error: function (er) {
hideProcessLog('Error: ' + er);
}
});
}
- Help and Notification Show/Hide Animation
For showing animated help on the screen using below code:
$('#notificationText').text(notificationText);
push.show().removeClass(NOTIFICATION_SHOW).addClass(NOTIFICATION_SHOW);
$("span.help-field").on('click', function (e) {
var index = Number($(this).data('index')) - 1;
$('#' + platform + '-fieldhelp').html(help[index]).show().removeClass(HELP_SHOW).addClass(HELP_SHOW);
});
For hiding animated Help and Notification from the screen using below code:
$('#android-notification, #android-fieldhelp, #windows-notification, #windows-fieldhelp, #ios-notification, #ios-fieldhelp').bind("animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd", function () {
var obj = $(this);
var className = obj.attr('class');
if (className.indexOf('flipOutX') > -1) {
obj.removeClass(NOTIFICATION_HIDE).hide();
}
else if (className.indexOf('zoomOut') > -1) {
obj.removeClass(HELP_HIDE).hide();
}
});
-
Important Key Points
-
Android
- iOS
- Make sure you use the right Push certificate.
- Apple allows you to generate one for connecting to Sandbox, and one for connecting to Production. You must use the right one, to match the provisioning profile you build your application with.For more details How to create APNS certificates.
- Windows
- Toast Notifications
Toast Notifications are short text-based messages it consist of two strings of text, and appear at the top of the phone's display area
- Tile Notifications
Tile Notification can modify the appearance of app tiles that a phone user has pinned on their device. Tile notifications can specify a title, a count, and a background image that can be either a local or remote resource.
- Raw Notifications
Row Notification are similar to tile and toast notifications, except they do not have a particular display style or predefined payload format. The data contained in a raw notification is determined by the sender, and the app running on the phone must understand the content. Because the underlying operating system does not handle raw notifications, the app for which the notification is designed must be running in order to handle this type of notification. As of now this type of notification is not supported.
-
Important Fields Details
-
Android
- API Key: The API KEY comes from your Google APIs Console App, under the API Access section, by choosing “Create new Server key...”.
- Device Registration ID: Use your own Registration Id here that gets generated within your Android App itself.
-
iOS
- Publish Type: If you are using a Development provisioning Profile, you must use the Sandbox push notification server. So you have to select Publish Type as “Development”.
If you are using an AdHoc or AppStore provisioning profile, you must use the Production push notification server. So you have to select Publish Type as “Production”. - Device Token: The device token contains information that enables APNs (Apple Push Notification) to locate the device on which the client App is installed.
- Select Certificate: This will provide an option to the user, for selecting existing Pre Defined certificate or upload new certificate.
- New Certificate: To configure push notification for this iOS App ID, a Client SSL Certificate that allows your notification server to connect to the APN Service is required. Each iOS App ID requires its own Client SSL Certificate.
- Certificate Password: SSL Certificate password for selected certificate file.
- Windows
- Notification Type: There are two types of alert will be supported
Tile: Which are the Start screen tiles that represent and launch your app.
Toast: Which are app-specific pop-up notifications. - Channel URI: Use your own Endpoint Uri here that gets generated within your Windows Phone App itself.
- Navigate Path: It will be redirecting to particular page when user tap on push notification. e.g. (/MainPage.xaml)
- Notification Text2: A notification text2 is a short message, which will be displayed beside the title.
- XML Payload: This will be visible only when Application Mode is set to Expert. Allow you to add custom data to the payload. Custom values must use the XML structured including <?xml version="1.0" encoding="utf-8"?>
- Common
- Notification Text: A notification text is a short message, which will be displayed at notification tray on which the client App is installed.
- Notification Badge: The number to display as the badge of the app icon.
- JSON Payload: This will be visible only when Application Mode is set to Expert. Allow you to add custom data to the payload. Custom values must use the JSON structured and primitive types: dictionary (object), array, string, number, and Boolean.
Feedback
Please fill free to ask me if you would require any help for the same. Your valuable feedback, comment, suggestion and VOTE are highly appreciated.