ionic-team / ionic-cli

The Ionic command-line interface
MIT License
2k stars 641 forks source link

Ionic resource icon for Android push notifications #608

Open schoettler opened 8 years ago

schoettler commented 8 years ago

Hey Ionic!

Would be nice if we could automatically set an icon for multiple resolutions as a custom push notification icon, as an addition to the app's icon & splash screen.

elloboblanco commented 8 years ago

Second! Just looking for a way to do this too.

awebdeveloper commented 8 years ago

+1

ghost commented 8 years ago

I can't figure out a way to display an icon on my received push notifications.

adyz commented 8 years ago

+1

I use phonegap-plugin-push plugin https://github.com/phonegap/phonegap-plugin-push for push notifications

I generate "old" icons with ionic resources command even if I don't use ionic in my project at all.

After that I generate a new png with a new name with this tool: http://romannurik.github.io/AndroidAssetStudio/icons-notification.html and paste all the generated folders in the platforms/android/res folder with the "merge" options. The notification icon must have a different name, not icon.png and when I init the push plugin I use the name of my new file here:

    "android": {
        senderID: vv.params.googleGCMProjectNumber,
        icon: "ic_stat_icon_material",
        iconColor: "grey"
    },

and the large icon from the JSON sent as push:

    "GCM": {
        title: "Large Icon", 
        message: "Loaded from drawables folder", 
        image: "icon"
    }

where icon is the name of the file generated by ionic.

Tested on two devices so far. Works fine.

RafaelMCarvalho commented 8 years ago

+1

igordeoliveirasa commented 8 years ago

+1

MazzMazz commented 8 years ago

+1

gauravarora commented 8 years ago

+1

ronycohen commented 8 years ago

+1

Samox commented 8 years ago

+1

awebdeveloper commented 8 years ago

Here is a solution

  1. Create a folder called push_icon in resources/android/
  2. Create the files in the format drawable-hopi-icon.png like you see in icon folder
  3. Create a after_prepare hook
#!/usr/bin/env node

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

var inputFolder = path.join(__dirname, '../../', 'resources/android/push_icon/');
var outputFolder = path.join(__dirname, '../../', '/platforms/android/res/');

console.log('------------------------------------------------------------------------------------------');
console.log("Running hook: "+path.basename(process.env.CORDOVA_HOOK));
console.log('------------------------------------------------------------------------------------------');

fs.readdir(inputFolder, function(err, list) {
    list.forEach(function(file){
        if (file.indexOf('drawable') === 0) {
            var destFolder = file.replace('-icon.png','');
            fs.createReadStream(inputFolder+file)
              .pipe(fs.createWriteStream(outputFolder + destFolder + '/notify.png'));
            console.log('# ' + file + ' --> ' + destFolder);
        }
    });

    console.log('-----------------------------------------------------------------------------------------');
});
jakub-g commented 8 years ago

+1. please let us know if you consider the feature valuable, so maybe someone from community will send a PR for that

If you're unfamiliar with the context: in Android 5.0+, push notification icon has to be two-color: transparent background + white foreground; otherwise the default app icon is taken, and anything non-transparent is displayed as white (so very likely, the user will see a white square)

Now, to prevent that, you can have a separate icon only for push notifications, and pass that name to the cordova push plugin.

I'm for now using a combination of solutions from @adyz and @awebdeveloper i.e. generate icons via a website, commit them to git to a custom folder, then copy the images to platform/android/res in a hook.

mainalikishan commented 8 years ago

+1

alejandromagnorsky commented 8 years ago

+1

Remrem84 commented 8 years ago

+1

pawurb commented 8 years ago

+1

wli commented 8 years ago

For anyone here for ionic2, you can:

  1. Use this generator: http://romannurik.github.io/AndroidAssetStudio/icons-notification.html
  2. Put your icons in resources/android/ic_notification/. It should look like this:

    - resources/android/ic_notification/drawable-hdpi-v11/ic_notification.png
    - resources/android/ic_notification/drawable-hdpi/ic_notification.png
    - resources/android/ic_notification/drawable-mdpi-v11/ic_notification.png
    - resources/android/ic_notification/drawable-mdpi/ic_notification.png
    - resources/android/ic_notification/drawable-xhdpi-v11/ic_notification.png
    - resources/android/ic_notification/drawable-xhdpi/ic_notification.png
    - resources/android/ic_notification/drawable-xxhdpi-v11/ic_notification.png
    - resources/android/ic_notification/drawable-xxhdpi/ic_notification.png
    - resources/android/ic_notification/drawable-xxxhdpi-v11/ic_notification.png
    - resources/android/ic_notification/drawable-xxxhdpi/ic_notification.png
  3. stick this into gulpfile.js

    gulp.task('ic_notification', function() {
       gulp.src('./resources/android/ic_notification/**')
           .pipe(gulp.dest('./platforms/android/res'));
       });
  4. Add the 'ic_notification' task to the runSequence calls for 'build' and 'watch':

    gulp.task('watch', ['clean'], function(done){
     runSequence(
      ['sass', 'html', 'fonts', 'scripts', 'ic_notification'],
       function(){
         gulpWatch('app/**/*.scss', function(){ gulp.start('sass'); });
         gulpWatch('app/**/*.html', function(){ gulp.start('html'); });
         buildBrowserify({ watch: true }).on('end', done);
       }
     );
    });
    
    gulp.task('build', ['clean'], function(done){
     runSequence(
      ['sass', 'html', 'fonts', 'scripts', 'ic_notification'],
       function(){
         buildBrowserify().on('end', done);
       }
     );
    });

I think this whole process can be generalized and put into a plugin that should be included in https://github.com/driftyco/ionic-gulp-tasks.

boyfunky commented 8 years ago

am i the only one experiencing this but all the icons generated via http://romannurik.github.io/AndroidAssetStudio/icons-notification.html are just all white circles. i dont see my icon at all. @wli

wli commented 8 years ago

@boyfunky notification icons are only two colors - white or transparent. The white circle implies that your icon has a circle shape. You probably want some kind of transparency so you get some kind of shape.

shyamal890 commented 8 years ago

+1

saguilerao commented 8 years ago

+1

saguilerao commented 7 years ago

Another way to prevent notification icon to be white-transparent is to set the Target SDK to any value below 20.. (lollipop)..

pke commented 7 years ago

Without hackery like lowering the target SDK just save the icon in 8bit Grayscale with Alpha-Channel.

lucasbasquerotto commented 7 years ago

I created the hook below, based on this code, that you can set as an after prepare hook, without the need of external libraries:

#!/usr/bin/env node
var ROOT_DIR = process.argv[2];
var DIRS_TO_COPY = [{
    srcDir: "resources/other/android/res/",
    destDir: "platforms/android/res/"
}];

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

function copyFileSync(srcFile, target) {
    var destFile = target;

    //if target is a directory a new file with the same name will be created inside it
    if (fs.existsSync(target)) {
        if (fs.lstatSync(target).isDirectory()) {
            destFile = path.join(target, path.basename(srcFile));
        }
    }

    //console.log('copying ' + srcFile + ' to ' + destFile);
    fs.writeFileSync(destFile, fs.readFileSync(srcFile));
}

function copyFolderRecursiveSync(sourceFolder, targetFolder) {
    var files = null;

    if (!fs.existsSync(targetFolder)) {
        fs.mkdirSync(targetFolder);
    }

    //copy
    if (fs.lstatSync(sourceFolder).isDirectory()) {
        files = fs.readdirSync(sourceFolder) || [];

        files.forEach(function (curSource) {
            var curSourceFull = path.join(sourceFolder, curSource);

            if (fs.lstatSync(curSourceFull).isDirectory()) {
                var curTargetFolder = path.join(targetFolder, path.basename(curSource));
                copyFolderRecursiveSync(curSourceFull, curTargetFolder);
            } else {
                copyFileSync(curSourceFull, targetFolder);
            }
        });
    }
}

DIRS_TO_COPY.forEach(function (dirInfo) {
    var srcDirFull = path.join(ROOT_DIR, dirInfo.srcDir);
    var destDirFull = path.join(ROOT_DIR, dirInfo.destDir);
    copyFolderRecursiveSync(srcDirFull, destDirFull);
});

Just create a .js file with the above code called, for instance, my_hook.js, and put it inside the [YOUR_PROJECT]/hooks/after_prepare/ folder (it works just like that with Ionic2). I don't know if Ionic1 or a non-ionic cordova project will run the hook automatically, but you can add it to the config.xml in these cases:

<hook type="after_prepare" src="hooks/after_prepare/my_hook.js" />

The hook copies all files and directories recursively inside resources/other/android/res/ (I created this folder, where I put all drawables I use, except the app icon and splash screen that are already generated by Ionic) into platforms/android/res/ (where all drawables are put in android). I don't need to specify each file to be copied, because it will copy the entire folder.

Just make sure that the icons are in the correct directories. The utility below already does that for you (it creates a zip file with the correct folders, just paste it inside resources/other/android/res/). You can add another directory for iOS too (in the DIRS_TO_COPYarray).

You can create a notification icon for android in https://romannurik.github.io/AndroidAssetStudio/icons-generic.html.

lincolnthree commented 7 years ago

Don't forget that once you add these icons to the res/ folder, you need to configure the push plugin to use them:

E.g. If you have res/other/android/res/:

./drawable-hdpi/ic_notify.png
./drawable-mdpi/ic_notify.png
./drawable-xhdpi/ic_notify.png
./drawable-xxhdpi/ic_notify.png
./drawable-xxxhdpi/ic_notify.png

You'd still need need:

const cloudSettings: CloudSettings = {
    'core': {
        'app_id': '...'
    },
    'push': {
        'sender_id': '...',
        'pluginConfig': {
            'ios': {
                'badge': true,
                'sound': true,
            },
            'android': {
                'iconColor': '#343434',
                'icon': 'ic_notify', <--- this is required to specify which icon to use - otherwise it will be blank.
            }
        }
    }
};
shyamal890 commented 7 years ago

+1

sbozzie commented 7 years ago

Hi Guys,

I'm having some issues, and can't seem to get the combination of images/folders and config right here to get the small icon working in the notification bar of Android - I'm simply shown a square,

In my app.js I have:

$ionicCloudProvider.init({
   "core": {
     "app_id": "APPIDHERE"
   },
    "push": {
      "sender_id": "SENDERIDHERE",
      "pluginConfig": {
        "ios": {
          "badge": true,
          "sound": true
        },
        "android": {
            "iconColor": "#00AEED",
            "icon":"ic_notify"
        }
      }
    }
 });`

I can see that this config is being used, by changing "iconColor" I am able to get a little blue or red square!

Within my \android\res\drawable-resolution folders, I have the png's as generated by the AndroidAssetStudio generator tool, as white on alpha png's

image

What am I missing?

lincolnthree commented 7 years ago

The folders containing the icon files need to end up here:

platforms/android/res/

Make sure your build is putting them there. I use this 'after_prepare' hook script:

#!/usr/bin/env node

var ROOT_DIR = process.argv[2];
var DIRS_TO_COPY = [{
    srcDir: "resources/other/android/res/",
    destDir: "platforms/android/res/"
}];

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

function copyFileSync(srcFile, target) {
    var destFile = target;

    //if target is a directory a new file with the same name will be created inside it
    if (fs.existsSync(target)) {
        if (fs.lstatSync(target).isDirectory()) {
            destFile = path.join(target, path.basename(srcFile));
        }
    }

    //console.log('copying ' + srcFile + ' to ' + destFile);
    fs.writeFileSync(destFile, fs.readFileSync(srcFile));
}

function copyFolderRecursiveSync(sourceFolder, targetFolder) {
    var files = null;

    if (!fs.existsSync(targetFolder)) {
        fs.mkdirSync(targetFolder);
    }

    //copy
    if (fs.lstatSync(sourceFolder).isDirectory()) {
        files = fs.readdirSync(sourceFolder) || [];

        files.forEach(function(curSource) {
            var curSourceFull = path.join(sourceFolder, curSource);

            if (fs.lstatSync(curSourceFull).isDirectory()) {
                var curTargetFolder = path.join(targetFolder, path.basename(curSource));
                copyFolderRecursiveSync(curSourceFull, curTargetFolder);
            } else {
                copyFileSync(curSourceFull, targetFolder);
            }
        });
    }
}

DIRS_TO_COPY.forEach(function(dirInfo) {
    var srcDirFull = path.join(ROOT_DIR, dirInfo.srcDir);
    var destDirFull = path.join(ROOT_DIR, dirInfo.destDir);
    copyFolderRecursiveSync(srcDirFull, destDirFull);
});
lincolnthree commented 7 years ago

It's a real pain to get right. And the error messaging is non-existent. Lots of trial and error.

sbozzie commented 7 years ago

Is just manually putting them into location not enough? Would a Gulp task do the job?

Jharilela commented 7 years ago

I think that the hook file does a good job in copying the files after execution but i think the real problem lies in the android firmware unable to detect the location of the icon from its root

dovk commented 6 years ago

If your only problem is with the notifications icons appearing correctly on Android, the following worked for me - take the drawable-xhdpi-icon icon (size 96x96), rename it icon.png and place it in two places:
   /src/assets/img    /platforms/android/res/drawable

The drawable folder is a new folder which can be created by copying platforms/android/res/mipmap-xhdpi to platforms/android/res/drawable manually or with the aid of a hook. In your code, the local or geofence notification is referenced as follows:

      smallIcon: 'res://icon',
      icon: 'file://assets/img/icon.png'

If ionic cordova resources is part of the problem, you can do your own one-time setup by taking your largest icon and, with the help of a resizing tool such as resizeimage.net, create a set of icons for iOS and Android.
The Excel here https://github.com/dovk/howto_resources-folder has a list of the sizes and names of the .png files to create. You then place them in their respective resources folder just like ionic cordova resources would have done - for example in resources/android/icon, resources/ios/splash and so on.
If you do so, then ionic cordova platform add android or ionic cordova platform add ios should not be used anymore, as this also does ionic cordova resources - What you need to do is cordova platform add ... --save (without the ionic in the beginning).

image image image

marcojr commented 6 years ago

I can't make this work...My recipe...

1) used https://romannurik.github.io/AndroidAssetStudio/icons-generic.html to generate the icons 2) Placed the icons in this way...(ic_notify, in white)

captura de tela 2017-09-19 as 12 22 40

3) Created android_icons at hooks/after_prepare

4) Added on the config.xml after the

5) Executed ionic cordova run android

6) got an error: Running command: _/Users/marcojr/Projects/munkat/app/hooks/after_prepare/android_icons.js /Users/marcojr/Projects/munkat/app fs.js:994 binding.lstat(pathModule._makeLong(path), statValues); ^

    Error: ENOENT: no such file or directory, lstat 
    '/Users/marcojr/Projects/munkat/app/resources/other/android/res/'_

7) Changed line 5 of the hook file from srcDir: "resources/other/android/res/" to srcDir: "resources/android/"

8) Executed ionic cordova run android

9) Got another error : Error: ENOENT: no such file or directory, mkdir 'run/platforms/android/res/'

10) Dropped the hook from the config.xml, got another error: /Users/marcojr/Projects/munkat/app/platforms/android/res/icon/drawable-hdpi-ic_notify.png:1:1: Error: O conteúdo não é permitido no prólogo. (The content is not allowed on prologue)

11) Decided to try awebdeveloper's solution. Got an error: Error: Path must be a string. Received undefined

any suggestions ?

lucasbasquerotto commented 6 years ago

@marcojr I recommend you not rename the files generated at https://romannurik.github.io/AndroidAssetStudio/icons-generic.html , but keep them with the same name and inside the created folders. What should be done is copying all the folders inside srcDirto destDir.

I defined srcDir as resources/other/android/res/ instead of resources/android/res/ because ionic creates the icon and splashscreen inside resources/android/ and that might (possibly) cause some conflicts, or erase the contents inside this folder. You could use another folder instead of resources/other/, but I recommend not to use resources/android/ (I'm not certain, but your last error may be because of this).

Like I said before, the contents inside srcDir should be folders with icons, the folders names should be like drawable-hdpi, drawable-mdpi, and so on... The notification icon should have the same name across the folders (like ic_notif.png, the same name for all icons, the utility in the link above (to create the icons) already does all that, creating the folders with the correct name and the icons, you just need to copy and paste it inside your srcDir). Look at the location of my icons, their names and the folder names:

icons

My notification icon is ic_99.png, the other icons are for other reasons.

The hook just copy the contents inside srcDir to destDir, so it create those folders inside platforms/android/res/:

icons2

First I'd recommend to use the same paths as mine to see if it works (if it works you can move your folders to another loaction and change the srcDir path accordingly).

The only error you got that I don't know why is that with mkdir 'run/platforms/android/res/', it should be mkdir 'platforms/android/res/', I've never got this error, don't know why this happened.

I don't recommend putting the icons manually inside the platforms folder, because the hook is basically to avoid that (the platforms folder should be such that you could delete it and generate it again without doing any manual changes).

marcojr commented 6 years ago

Ty, guys ! But looks like the solution provided by dovk really works and it's insanely simple !