Cordova-plugin-firebase: Changing push notification icon - blank android icon issue in 2018

Created on 6 Jul 2018  ·  22Comments  ·  Source: arnesson/cordova-plugin-firebase

Hi everybody,

For people using this plugin with cordova 8+ you might find that changing your notification icon wont work and you hit the old 'notification icon is blank square' issue on android. This happens because android notification icons have to be simple 1 color shape on transparent background and if you haven't provided such icon android will use application icon instead. Those are most likely a full and colourful squares without any transparent background.

This is very annoying when setting new Ionic 3 project and trying to use this plugin.

Readme here atm does not describe process of changing your icon correctly. Here are the steps you need to take to change the icon:

0) Stop googling around and pulling your hair out. Follow this instruction step by step and you will get it to work, trust me.
1) Prepare version of your application icon that is only a 1 color shape on transparent background
2) Go here: http://romannurik.github.io/AndroidAssetStudio/icons-notification.html#source.type=clipart&source.clipart=ac_unit&source.space.trim=1&source.space.pad=0&name=notification_icon press "image" on top left, upload your icon, trim it/add padding if need be
3) If it looks good press arrow in top right to download the 'drawable' folders. It will give you a zip with res folder inside and 5 drawable-xxx folders each one with different size of your icon
4) Copy those drawable folders somewhere into your project. For me I copied them next to icon and splash screen resources: project-root/resources/android/notification_icon
5) Configure your config.xml file to copy those files inside your android app using <resource-file /> directive. Note that since crodova 8.x the correct target path is app/src/main/res. On cordova < 8 it was just res (as previously the resource files were kept in platforms/android/res directory and now they are in platforms/android/app/main/res). Most of the 'solved' issues online on blank android notification icon do not mention that.

<platform name="android">
(...) 
  <resource-file src="resources/android/notification_icon/drawable-mdpi/notification_icon.png" target="app/src/main/res/drawable-mdpi/notification_icon.png" />
  <resource-file src="resources/android/notification_icon/drawable-hdpi/notification_icon.png" target="app/src/main/res/drawable-hdpi/notification_icon.png" />
  <resource-file src="resources/android/notification_icon/drawable-xhdpi/notification_icon.png" target="app/src/main/res/drawable-xhdpi/notification_icon.png" />
  <resource-file src="resources/android/notification_icon/drawable-xxhdpi/notification_icon.png" target="app/src/main/res/drawable-xxhdpi/notification_icon.png" />
  <resource-file src="resources/android/notification_icon/drawable-xxxhdpi/notification_icon.png" target="app/src/main/res/drawable-xxxhdpi/notification_icon.png" />
</platform>

6) Now that you have your icon in the application as drawable resource you need to force your application to use it. Regardless of what's written in readme here automatic discovery of drawable resource called 'notification_icon' did not work for me. I'm not exactly sure why because it seems to be in the codebase here: https://github.com/arnesson/cordova-plugin-firebase/blob/master/src/android/FirebasePluginMessagingService.java#L140 but I'm guessing there is something changed with resource discovery on latest versions of android.
7) Because of that the only other solution I could use was sending icon parameter from the backend when sending push notification to firebase. Note that their api is probably undergoing some changes because it has issues with correctly merging the notification and android parts of the payload. For that reason you have to add the icon param to the notification part of the json. Using android specfic params wont work.

So notification payload that you send to firebase from your backend has to look somewhat like this: THIS IS THE ONLY VERSION THAT WORKS

{
  notification: {
    body: 'Imma push your notification if you know what i mean!',
    icon: 'notification_icon',
  },
  data: { someExtraData: 'goes_here' }
}

Let me reiterate - USING ANDROID SPECIFIC PARAMS DOES NOT WORK WITH FIREBASE AS OF July 2018 - BELOW WONT WORK AND YOUR NOTIFICATION ICON WONT CHANGE REGARDLESS OF WHAT THEIR DOCUMENTATION SAYS:

{
  notification: {
    body: 'Imma push your notification if you know what i mean!'
  },
  data: { someExtraData: 'goes_here_if_you_need_it' },
  android: { 
    icon: 'notification_icon'
  } 
}

8) With all steps completed you are now ready to test your new icon. I strongly suggest testing it inside android emulator - some of the android skins take over the notification bar and use application icon regardless of what you set (i.e. MIUI from xiaomi). I've also found sometimes the icons get cached. For that reason testing it in simulator on freshly created AVD is your safest bet. Remove your app manually in between the test runs and restart the simluator / device to avoid caching issues.

I hope this long write up will help others in the future, as the amount of hurdle one has to go through to change small 10x10 icon is simply to damn high...

Most helpful comment

Please test if this code added to config.xml <platform name="android"> tag will add the icon as default notification icon.

<config-file parent="/manifest/application/" target="app/src/main/AndroidManifest.xml">
    <meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/notification_icon" />
</config-file>

This works for me sending by Firebase Cloud Messaging panel or back-end with no icon on payload.
Please post here the feedback.
If it works, your tutorial added to this code will help a lot of people and should open a PR to add it to the README.

All 22 comments

Please test if this code added to config.xml <platform name="android"> tag will add the icon as default notification icon.

<config-file parent="/manifest/application/" target="app/src/main/AndroidManifest.xml">
    <meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/notification_icon" />
</config-file>

This works for me sending by Firebase Cloud Messaging panel or back-end with no icon on payload.
Please post here the feedback.
If it works, your tutorial added to this code will help a lot of people and should open a PR to add it to the README.

Thank you @madsheep !! you saved my life !!

Hi, thanks for the moral support, and giving me the feeling that I am not the only one who ended up in this mess. However, unfortunately, I gravitated back to pre-step 0, losing hair on the way.

When I try to send the notification through the http/post method, I get errors from the firebase api that the json is invalid. I've tried both ways:

This is what I get when including the icon asmessage: { notification: { icon: 'notification_icon', ... }, ... }

Error code=400, message=Invalid JSON payload received. Unknown name "icon" at 'message.notification': Cannot find field., status=INVALID_ARGUMENT, details=[@type=type.googleapis.com/google.rpc.BadRequest, fieldViolations=[field=message.notification, description=Invalid JSON payload received. Unknown name "icon" at 'message.notification': Cannot find field.]]

This is the message I get when including the icon asmessage: { android: { icon: 'notification_icon' }, ... }

Error code=400, message=Invalid JSON payload received. Unknown name "icon" at 'message.android': Cannot find field., status=INVALID_ARGUMENT, details=[@type=type.googleapis.com/google.rpc.BadRequest, fieldViolations=[field=message.android, description=Invalid JSON payload received. Unknown name "icon" at 'message.android': Cannot find field.]]

but maybe I am using this in the wrong context (our backend cloud code sending a notification to client apps using firebase cloud messaging).

this worked fine for me (I send notifications using firebase functions) :
const payload = { notification: { title: ........, body: ........., icon: 'notification_icon' } };

Thanks, that results in the first of the two errors for me. I am using the REST service via a javascript / request library, and send according to the format https://firebase.google.com/docs/cloud-messaging/js/first-message . I suppose the message/notification format is described in https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages#Notification .

In fact, when I read this, this suggest that the icon should be in message.android.notification.icon, i.e., one level deeper.

I just tried, and this actually works for me! So I suppose it depends on serverprotocol how the payload should be structured...

Thanks, everyone, for this discussion. It has finally resulted in me stopping pulling my hair out...

closing as resolved

@madsheep It is not working. After adding 'icon' under notification in cloud function, it gives following error:

{ Error: Invalid JSON payload received. Unknown name "icon" at 'message.notification': Cannot find field

errorInfo:
{ code: 'messaging/invalid-argument',
message: 'Invalid JSON payload received. Unknown name "icon" at 'message.notification': Cannot find field.' },
codePrefix: 'messaging' }

try message.android.notification.icon instead of message.notification.icon.

@madsheep @guilhermehtk It worked for me! Thx.

Please test if this code added to config.xml <platform name="android"> tag will add the icon as default notification icon.

<config-file parent="/manifest/application/" target="app/src/main/AndroidManifest.xml">
    <meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/notification_icon" />
</config-file>

This works for me sending by Firebase Cloud Messaging panel or back-end with no icon on payload.
Please post here the feedback.
If it works, your tutorial added to this code will help a lot of people and should open a PR to add it to the README.

You Just made my day,

if any one face problem in build like

AAPT: error: unbound prefix.

then try to add The

xmlns:android="http://schemas.android.com/apk/res/android"

attribute should be added to the root tag inside the config.xml

you saved my day!

For anyone facing this problem in 2019 with android build 7.1.1, the resources folder has changed it seems!!

By checking Android studio and the generated files here's what worked for me:

<resource-file src="resources/android/notification/notification_icon.png" target="app/src/main/res/drawable/notification_icon.png" />
<config-file parent="/manifest/application/" target="app/src/main/AndroidManifest.xml">
            <meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/notification_icon" />
</config-file>

The 1st line is the important part, since that was wrong for me.

@alarv The icon is not showing for some android phone, any help on this please.

Hey @bioyeneye what models? What version of Android are they running? Stock or other?

@bioyeneye I have another solution for Ionic 4 to get the icons working on all android devices.
I'm not using a png for icon but XML. I'm using a hook to include this in the following dirs:

'res/drawable-hdpi',
'res/drawable-mdpi',
'res/drawable-xhdpi',
'res/drawable-xxhdpi',
'res/drawable-xxxhdpi',
'res/mipmap-hdpi',
'res/mipmap-mdpi',
'res/mipmap-xhdpi',
'res/mipmap-xxhdpi',
'res/mipmap-xxxhdpi'

create a file example:
android_notification_resources.js

In this new hook file add:


// BEGIN
const fs = require("fs");
const path = require("path");
const Q = require("q");

var sourceDir = 'resources/android/notification_icon'; // this is where your icon is
var platformDir = 'platforms/android/app/src/main/';
var resourceDirs = [
'res/drawable-hdpi',
'res/drawable-mdpi',
'res/drawable-xhdpi',
'res/drawable-xxhdpi',
'res/drawable-xxxhdpi',
'res/mipmap-hdpi',
'res/mipmap-mdpi',
'res/mipmap-xhdpi',
'res/mipmap-xxhdpi',
'res/mipmap-xxxhdpi'
];

module.exports = function(ctx) {

if (ctx.opts.platforms.indexOf('android') < 0) {
return;
}

var deferred = Q.defer();
var androidPlatformDir = path.join(ctx.opts.projectRoot, platformDir);
var customResourcesDir = path.join(ctx.opts.projectRoot, sourceDir);

function copy(src, dest) {
var deferred = Q.defer();

fs.stat(src, function(err, stats) {
  if (err || !stats.isFile()) {
    return deferred.reject(err);
  }

  fs.stat(path.dirname(dest), function(err, stats) {
    if (err || !stats.isDirectory()) {
      return deferred.reject(err);
    }

    var rs = fs.createReadStream(src);

    rs.on('error', function(err) {
      console.error(err.stack);
      deferred.reject(err);
    });

    var ws = fs.createWriteStream(dest);

    ws.on('error', function(err) {
      console.error(err.stack);
      deferred.reject(err);
    });

    ws.on('close', function() {
      deferred.resolve();
    });

    rs.pipe(ws);
  });
});

return deferred.promise;

}

fs.stat(customResourcesDir, function(err, stats) {
if (err || !stats.isDirectory()) {
return deferred.resolve();
}

fs.readdir(customResourcesDir, function(err, files) {
  var copies = [];

  for (var i in files) {
    for (var j in resourceDirs) {
      var filePath = path.join(ctx.opts.projectRoot, sourceDir, files[i]);
      var destPath = path.join(androidPlatformDir, resourceDirs[j], files[i]);

      copies.push([filePath, destPath]);
    }
  }

  copies.map(function(args) {
    return copy.apply(copy, args);
  });

  Q.all(copies).then(function(r) {
    deferred.resolve();
  }, function(err) {
    console.error(err.stack);
    deferred.reject(err);
  });
});

});

return deferred.promise;
}
// END


in your config.xml add this line to include your hook:

What the hook will do is copy your icon from your resources folder and add it in different drawables folders. I did this to make sure it works for all devices. So far so good.

Let me know if it worked for you! cheers

@RobinGiel Thanks, I am using ionic 3, will this work?

Hello @Flucadetena
Android 9
Cordova Platforms : android 7.1.1

@bioyeneye I'm using ionic 3 too. I'll check everything and let you know. ;)

@bioyeneye Yes it will , you will have to modify a few lines from the hook.

var sourceDir = 'resources/android/notification_icon';
var platformDir = 'platforms/android';

and maybe instead of using const Q = require("q"); remove that line and use it here.. example:

module.exports = function(ctx) {

if (ctx.opts.platforms.indexOf('android') < 0) {
return;
}

var Q = ctx.requireCordovaModule('q'); // use it here
var deferred = Q.defer();
var androidPlatformDir = path.join(ctx.opts.projectRoot, platformDir);
var customResourcesDir = path.join(ctx.opts.projectRoot, sourceDir);


Add in the config.xml calling the same hook before_build and before_run example:

<hook type="before_run"         src="hooks/android_notification_resources.js" />
<hook type="before_build"       src="hooks/android_notification_resources.js" />

Please test if this code added to config.xml <platform name="android"> tag will add the icon as default notification icon.

<config-file parent="/manifest/application/" target="app/src/main/AndroidManifest.xml">
    <meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/notification_icon" />
</config-file>

This works for me sending by Firebase Cloud Messaging panel or back-end with no icon on payload.
Please post here the feedback.
If it works, your tutorial added to this code will help a lot of people and should open a PR to add it to the README.

You Just made my day,

if any one face problem in build like

AAPT: error: unbound prefix.

then try to add The

xmlns:android="http://schemas.android.com/apk/res/android"

attribute should be added to the root tag inside the config.xml

This here works for me, but the icon seems to be incredibly small, what's the proper size of the file we should be using?

Edit: I've added a 256x256 yet still shows a tiny dot on the status bar. dragging the status bar to the bottom shows a slightly bigger one

É

You are my life saver!! Thank you!!!
Notes:
I add color: '#e50012' in notification section to change to color of icon.( change it to whatever your color is).
ex:
{
"name": "my_notification",
"notification": {
"body": "Notification body",
"title": "Notification title",
"color": "#e50012"
},
"data":{ ... }
}

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rlz picture rlz  ·  4Comments

ghost picture ghost  ·  3Comments

dhavalsoni2001 picture dhavalsoni2001  ·  5Comments

eilian92 picture eilian92  ·  4Comments

jdla1990 picture jdla1990  ·  4Comments