My Android app doesn't receive push notifications in the background and it should according to the documentation.
An Android application on an Android device doesn't need to be running
to receive messages. The system will wake up the Android application
via Intent broadcast when the message arrives, as long as the
application is set up with the proper broadcast receiver and
permissions.
Trying different notifications discovered that it does receives the push notifications while it is closed if and only if the notifications contains the attribute "message", if not, it just discards it. (Push notifications are just JSON objects).
My notifications contain all kind of attributes including "alert", "id" and "title", but only "message" makes Android to wake up the app.
Example notification that doesn't work:
{ event: 'message',
from: '947957531940',
collapse_key: 'do_not_collapse',
foreground: true,
payload:
{ alert: 'Mensaje de Prueba',
title: 'Titulo Mensaje de Prueba' } }
Example notification that works:
{ event: 'message',
from: '947957531940',
message: 'Contenido del mensaje de prueba.',
collapse_key: 'do_not_collapse',
foreground: true,
payload:
{ alert: 'Mensaje de Prueba',
title: 'Titulo Mensaje de Prueba',
message: 'Contenido del mensaje de prueba.' } }
Is this an Android standard by design or am I doing something wrong in my app?
My app was developed using Ionic with Cordova.
PD: Excuse my english.
EDIT:
This is the Android push code inside the .run module in app.js, as ng-cordova instructions specify:
if (ionic.Platform.isAndroid()){
var androidConfig = {
"senderID": "94*************",
"ecb": "window.casosPush"
};
try{
var pushNotification = window.plugins.pushNotification;
} catch (ex){
}
// Llamada en caso de exito
var successfn = function(result){
//alert("Success: " + result);
};
// Llamada en caso de error
var errorfn = function(result){
window.alert("Error: " + result);
};
// Llamada de casos de notificacion push
window.casosPush = function(notification){
switch (notification.event){
case 'registered':
if (notification.regid.length > 0){
$rootScope.data.token = notification.regid;
//alert('registration ID = ' + notification.regid);
}
break;
case 'message':
$rootScope.mensajes.unshift(notification.payload);
$localstorage.setArray('mensajes', $rootScope.mensajes);
alert(JSON.stringify(notification));
break;
case 'error':
alert('GCM error = ' + notification.msg);
break;
default:
alert('An unknown GCM event has occurred');
break;
}
};
try{
// Llamada de registro con la plataforma GCM
pushNotification.register(successfn,errorfn,androidConfig);
} catch(notification){
}
}
After searching all questions in stackoverflow involving Cordova/Phonegap Push Plugin I found de source of the problem. Is written in the source code of the plugin, and it shouldn't.
The problem was that the plugin source code requires the push notification to have "message" field in order to trigger a notification while being on the background. It is especified in the GCMIntentService.java file.
if (extras.getString("message") != null && extras.getString("message").length() != 0) {
createNotification(context, extras);
}
The most helpful posts are this answer and this issue in the plugin repository. So, because I have no power over the push server action I can't have specified a "message" field in the push notifications, I had no alternative but to rewrite the source code.
So I rewrote te code like this:
@Override
protected void onMessage(Context context, Intent intent) {
Log.d(TAG, "onMessage - context: " + context);
// Extract the payload from the message
Bundle extras = intent.getExtras();
if (extras != null)
{
// if we are in the foreground, just surface the payload, else post it to the statusbar
if (PushPlugin.isInForeground()) {
extras.putBoolean("foreground", true);
PushPlugin.sendExtras(extras);
}
else {
extras.putBoolean("foreground", false);
// Send a notification if there is a message
//if (extras.getString("message") != null && extras.getString("message").length() != 0) {
if (extras.getString("alert") != null && extras.getString("alert").length() != 0) {
createNotification(context, extras);
}
}
}
}
That way the plugin triggers a local notification if it receives a push notifications with the field "alert" instead of "message".
Then, I changed the code for the local notification itself:
public void createNotification(Context context, Bundle extras)
{
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
String appName = getAppName(this);
Intent notificationIntent = new Intent(this, PushHandlerActivity.class);
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
notificationIntent.putExtra("pushBundle", extras);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
int defaults = Notification.DEFAULT_ALL;
if (extras.getString("defaults") != null) {
try {
defaults = Integer.parseInt(extras.getString("defaults"));
} catch (NumberFormatException e) {}
}
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(context)
.setDefaults(defaults)
.setSmallIcon(context.getApplicationInfo().icon)
.setWhen(System.currentTimeMillis())
//.setContentTitle(extras.getString("title"))
.setContentTitle(extras.getString("alert"))
//.setTicker(extras.getString("title"))
.setTicker(extras.getString("alert"))
.setContentIntent(contentIntent)
.setAutoCancel(true);
/*
String message = extras.getString("message");
if (message != null) {
mBuilder.setContentText(message);
} else {
mBuilder.setContentText("<missing message content>");
}
*/
// Agregado mensaje predefinido
mBuilder.setContentText("Haz clic para más información");
String msgcnt = extras.getString("msgcnt");
if (msgcnt != null) {
mBuilder.setNumber(Integer.parseInt(msgcnt));
}
int notId = 0;
try {
notId = Integer.parseInt(extras.getString("notId"));
}
catch(NumberFormatException e) {
Log.e(TAG, "Number format exception - Error parsing Notification ID: " + e.getMessage());
}
catch(Exception e) {
Log.e(TAG, "Number format exception - Error parsing Notification ID" + e.getMessage());
}
mNotificationManager.notify((String) appName, notId, mBuilder.build());
}
Now the notification in the notification bar will show first the field "alert" instead of "title" and second a predefined message instead of the field "message" that doesn't exist in my push notifications.
Then I just recompile the code with:
ionic platform rm android
ionic platform add android
ionic run android
So kids, this is what happens when you write a plugin for everyone but you don't consider generic cases and you don't document well. That plugin is only useful for people with the field "message" and "title" in their push notifications.
I lost two days of my internship because of this, I took my time to write this so others won't have to.
You are right @David prieto.
The problem was that the plugin source code requires the push notification to have "message" field in order to trigger a notification while being on the background.
There is one way to put "message" field in push notification and its simple.
Payload array which has data of notification, which is send through an API. It should have an "message" field in it. So the push notification will automatically have a message field in it and it will get detected in background as well as foreground.