My previous post lamented my ignorance about setting up Android icons for Local Notifications in an Ionic 1 project. I found a lot of speculation in my Google search as to how to handle them but not a lot of explanation or examples. In this post I am presenting the results of the research I completed on this topic during a recent project.

Understanding How Notifications Work on Android

The Android platform a notification requires two icons. Lets take a look at Figure 1 to identify them:

Figure 1: Where the Android notification icons appear


As you can see, there is an icon (1) that appears in the status bar as well as in the notification listing (2) along side another larger icon (3).

Lets take a look at the typical notification code that might appear in a service in your application:

var setUpNotification = function( note ) {
    var deferred = $q.defer();
    
    $cordovaLocalNotification.schedule({
        id: note.id,
        at: note.at,
        text: note.text,
        title: note.title,
        sound: 'file://audio/notification.wav',
        badge: 1,
        data: 'hello=world',
        autoClear: false,
        icon: 'res://icon.png',
        smallIcon: 'res://ic_stat_notify.png'
    }).then(function ( result ) {
        console.log(note.title  + ' Notification Set');
        deferred.resolve( result );
    }, function ( error ) {
        console.log( 'setUpNotification ERROR: ' + error );
        deferred.reject( error );
    });
            
    return deferred.promise;
};

In the code above the object being sent to the plugin has an icon and an smallIcon property.

The icon is the large icon that appears in the notifications listing. Typically this icon is the same as your application’s icon so that there is a visual association between the notification and the application that scheduled it.

The smallIcon property sets the small icon that appears in two places: in the status bar when a notification occurs and next to the larger icon in the notifications listing to visually associate the icon in the status bar with both the notification and the application.

Where Do These Icons Go?

In that code sample, notice that the links to these icons are prefixed with res://. That means these icons are located in the platforms/android/res folder which is where the icons for each screen density reside. The res folder has several folders in it with names like drawable-hdpi and inside each of these folders there should already be a file named icon.png. When you build the Android platform using the Ionic CLI, images located resources/android/icon folder renamed and copied into the proper drawable folder. When your application runs on Android, these folders are used to provide the proper icons for the device’s resolution.

Let me try to make this more succinct:

  • The images for the icon property are in your resources/android/icons folder, automatically created for you by Ionic. You should update those images for your application and when you use the ionic build android command, those files are copied to the correct folder.

  • The images for the smallIcon property need to be created.

How Big Are The Status Bar Icons?

The status bar icons need to be created for each Android screen pixel density. With a little research I was able to find out what sizes are required:

22 × 22 area in 24 × 24 (mdpi)
33 × 33 area in 36 × 36 (hdpi)
44 × 44 area in 48 × 48 (xhdpi)
66 × 66 area in 72 × 72 (xxhdpi)
88 × 88 area in 96 × 96 (xxxhdpi)

Reference: Icon Handbook

The above reference is telling us that, for instance, for the xxxhdpi screen density a 96 x 96 pixel icon file using an area 88 x 88 pixels need to be created. Basically, center your 88 x 88 pixel graphic in your 96 x 96 pixel image.

Per the Android guidelines, you should use the prefix ic_stat_notify_ (“ic” for icon, “stat” for status bar, “notify” for notification) for the image name. These files should be white on transparency and should be saved in the PNG file format. For the purpose of this example, I simply used the name ic_stat_notify as I was only using one file for every notification. Each file should be placed into the proper screen density folder. For instance, the 36 x 36 icon image for the hdpi screen density should go into the platforms/android/res/drawable-hdpi folder.

Please Note: You can put these files into the platforms/android/res/ folders, but if you ever need to run the ionic platform remove android command, you’ll find that those images have disappeared. Also, if you are working with a team, since the platform folder normally isn’t stored in your code repository each team member will need to manually move these files into place. Guess what happens if the icons change. A pain, right? So don’t do it and continue reading.

How Can I Get The Icons There Automagically?

As you can see from the notice above, manually moving these files into place might cause all kinds of troubles for you and your team.

During my research for this feature I found this little nugget. Below you will find code that will assist with placing these files into the proper places automatically.

This Javascript file should be placed into your hooks/after_prepare/ folder. I don’t think it matters how the file is named (the ReadMe says “Any scripts you add to these directories will be executed before and after the commands corresponding to the directory name.”) but the files in this folder are processed when you build your project. To find out more about how this works, take a look at the hooks/ReadMe.md file.

This code assumes you have files named “ic_stat_notify.png” inside a folder at the root of your project named ic_status_images/{screen density}/. Set up your folder structure any way you like and edit this code to reflect those changes.

/*      file: hooks/after_prepare/add_platform_class.js     */


#!/usr/bin/env node

var filestocopy = [
    {
        "ic_status_images/mdpi/ic_stat_notify.png":
        "platforms/android/res/drawable-mdpi/ic_stat_notify.png"
    },
    {
        "ic_status_images/hdpi/ic_stat_notify.png":
        "platforms/android/res/drawable-hdpi/ic_stat_notify.png"
    },
    {
        "ic_status_images/xhdpi/ic_stat_notify.png":
        "platforms/android/res/drawable-xhdpi/ic_stat_notify.png"
    },
    {
        "ic_status_images/xxhdpi/ic_stat_notify.png":
        "platforms/android/res/drawable-xxhdpi/ic_stat_notify.png"
    },
    {
        "ic_status_images/xxxhdpi/ic_stat_notify.png":
        "platforms/android/res/drawable-xxxhdpi/ic_stat_notify.png"
    }
];

var fs = require('fs');
var path = require('path');

// no need to configure below
var rootdir = process.argv[2];
console.log("");
console.log("~~~~ Start Copying Notification Status Icons");
filestocopy.forEach(function (obj) {
    Object.keys(obj).forEach(function (key) {
        var val = obj[key];
        var srcfile = path.join(rootdir, key);
        var destfile = path.join(rootdir, val);
        console.log("copying: " + srcfile);
        console.log("     to: " + destfile);
        var destdir = path.dirname(destfile);
        if (fs.existsSync(srcfile) && fs.existsSync(destdir)) {
            fs.createReadStream(srcfile).pipe(
                fs.createWriteStream(destfile));
        }
    });
});
console.log("~~~~ End Copying Notification Status Icons");
console.log("");

Summary

I hope this article assists folks with using Local Notifications on Android. Let me know if you have further insights.