Meteor-Community-Packages / raix-push

DEPRECATED: Push notifications for cordova (ios, android) browser (Chrome, Safari, Firefox)
https://atmospherejs.com/raix/push
MIT License
514 stars 197 forks source link

Notifications to Android 8.0+ are blocked because notification channels are required #341

Open TheRealNate opened 5 years ago

TheRealNate commented 5 years ago

According to https://developer.android.com/training/notify-user/channels

To register a notification channel, this code is executed:

private void createNotificationChannel() {
    // Create the NotificationChannel, but only on API 26+ because
    // the NotificationChannel class is new and not in the support library
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        CharSequence name = getString(R.string.channel_name);
        String description = getString(R.string.channel_description);
        int importance = NotificationManager.IMPORTANCE_DEFAULT;
        NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
        channel.setDescription(description);
        // Register the channel with the system; you can't change the importance
        // or other notification behaviors after this
        NotificationManager notificationManager = getSystemService(NotificationManager.class);
        notificationManager.createNotificationChannel(channel);
    }
}

It's java that needs to be directly integrated into Cordova, or there needs to be a Cordova package allowing us to execute this from the JavaScript.

Note that the guide says:

Creating an existing notification channel with its original values performs no operation, so it's safe to call this code when starting an app.

batcode007 commented 5 years ago

This might be of help :

PushNotification.createChannel(
  () => {
    console.log('success');
  },
  () => {
    console.log('error');
  },
  {
    id: 'testchannel1',
    description: 'My first test channel',
    importance: 3,
    vibration: true
  }
);

https://github.com/phonegap/phonegap-plugin-push/blob/master/docs/API.md#pushnotificationcreatechannel

batcode007 commented 5 years ago

Any updates? Did you get it resolved?

TheRealNate commented 5 years ago

Haven't gotten a chance to attempt implementing this fix yet, though it seems promising. Will update once I've tried it.

TheRealNate commented 5 years ago

Follow up question. Once the channel is implemented, how do you specify the channel when sending the notification?

TheRealNate commented 5 years ago

Code works to register a channel. Now need to figure out how to send a notification to a specific channel.

da314pc commented 5 years ago

@TheRealNate I think I've made progress, so in the payload you have to include the channel id: "data": { "title": "Hello Bob!", "message": "Phonegap is awesome!", "android_channel_id": "testchannel2" }

we have to change:

Allow user to set payload:

var data = (notification.payload) ? { ejson: EJSON.stringify(notification.payload) } : {};

data.title = notification.title; data.message = notification.text;

add something like:

data.android_channel_id = notification.channel_id

https://github.com/raix/push/blob/464d82469ca15b8579b469b7156a6c9d16874d9b/lib/server/push.api.js#L307-L310

TheRealNate commented 5 years ago

I've made a fork for this repo with @da314pc's changes. I'll try testing it asap.

TheRealNate commented 5 years ago

Would this be correct usage?

Push.send({ from: 'push', title: 'Hello World!', text: 'Lorem ipsum', android_channel_id: 'testchannel1', query: {} });

da314pc commented 5 years ago

@TheRealNate I think you can use android_channel_id or whatever variable you want to call it, as long as set in push.api.js "data.android_channel_id = notification.android_channel_id"

I'll try testing it when I get home.

TheRealNate commented 5 years ago

It seems to be removing the argument android_channel_id before it gets to the code around line 309

da314pc commented 5 years ago

@TheRealNate your right, it will be removed because of the this: https://github.com/raix/push/blob/464d82469ca15b8579b469b7156a6c9d16874d9b/lib/server/push.api.js#L282

so we have to add a new field android_channel_id to the collection:

https://github.com/raix/push/blob/464d82469ca15b8579b469b7156a6c9d16874d9b/lib/common/notifications.js#L11-L49

TheRealNate commented 5 years ago

Done, https://github.com/TheRealNate/push/blob/master/lib/common/notifications.js

Testing these changes. Is it enough to allow the argument?

TheRealNate commented 5 years ago

I'm no longer getting the error message, so the channel id seems to be making it to the code in push/lib/server/push.api.js, yet notifications still don't seem to be working.

da314pc commented 5 years ago

@TheRealNate Is the device token still registered in the database? In the debug settings does it say if it's been sent to any android devices.

If you registered the device: We may have to check the "node-gcm" library. I have been doing some digging, but I'll keep checking to make sure the payload has the android channel Id available or it gets lost before it reaches the device.

in the readme: data: { key1: 'message1', key2: 'message2' }, notification: { title: "Hello, World", icon: "ic_launcher", body: "This is a notification that will be displayed if your app is in the background." }

but in:

https://github.com/TheRealNate/push/blob/41c7a8d8b535d9999475dded2d04a3c81da1970e/lib/server/push.api.js#L345-L352

It looks like we are adding formatting it slight differently.

TheRealNate commented 5 years ago

The logs do confirm when Push.debug=true that it's reaching:

Push: Sent message "Hello World!" to 0 ios apps 8 android apps

And

ANDROID: Result of sender: {"multicast_id":xxx,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"xxx"}]}
TheRealNate commented 5 years ago

As for node-gcm, you seem to be right. I cannot find any mention of notification channels anywhere in the GitHub repo (https://github.com/ToothlessGear/node-gcm/search?q=channel&unscoped_q=channel)

Perhaps the official API (https://firebase.google.com/docs/reference/admin/) could work?

TheRealNate commented 5 years ago

Here's another weird thing. I am unable to send notifications directly from the Firebase Console. Maybe some changes to the manifest need to be made (https://firebase.google.com/docs/cloud-messaging/android/client)

da314pc commented 5 years ago

Yea I noticed that too. Even when the notifications were working they were never integrated into firebase. It always showed blank.

da314pc commented 5 years ago

So this is the correct format which is similar: https://github.com/phonegap/phonegap-plugin-push/blob/master/docs/PAYLOAD.md#notification-vs-data-payloads

My recommended format for your push payload when using this plugin (while it differs from Google's docs) works 100% of the time:

{ "data": { "title": "Test Notification", "body": "This offer expires at 11:30 or whatever", "notId": 10, "surveyID": "ewtawgreg-gragrag-rgarhthgbad" } }

In the FCM-NODE:

Warning: on February 2, 2017, the Firebase Team released the admin.messaging() service to their node.js admin module. This new service makes this module kind of deprecated

TheRealNate commented 5 years ago

@da314pc do you have any idea where they are obtaining surveyID? Also, how would you do this using Push.send? Would everything in data go inside payload?

da314pc commented 5 years ago

not sure

da314pc commented 5 years ago

@TheRealNate one more thing to check,

in your Push.send: did you remember to include the android_channel_id?

TheRealNate commented 5 years ago

Where would I put it in the arguments object? At root level, in gcm, in data, or in payload.

On Mon, Aug 13, 2018, 2:14 PM da314pc notifications@github.com wrote:

@TheRealNate https://github.com/TheRealNate one more thing to check,

in your Push.send: did you remember to include the android_channel_id?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/raix/push/issues/341#issuecomment-412613453, or mute the thread https://github.com/notifications/unsubscribe-auth/APg4Q_qW1NMKTdf4LYB9Y1gxR1HZkIYlks5uQcH2gaJpZM4VO0Sw .

da314pc commented 5 years ago

So everything looks fine, but when you send the notification from the server:

Push.send({ from: 'test', title: 'test', text: "Hello, World"',
query: { userId: "user1" }, // android_channel_id: "testchannel1" //channel you put in the client });

TheRealNate commented 5 years ago

I'm doing exactly that except I'm leaving the query as {}.

da314pc commented 5 years ago

That's probably the problem, the device token is registered with the userId, so if the userId is blank, the Push notification can't find the device token in the database,

Push looks up the collection by userId ("im pretty sure")

Its all database driven, so for the test, make sure in the "db._raix_push_app_tokens", there is a token with the userId,

then send the userId in query from the server,

so you kind of have to have a user logged in for this to work.

TheRealNate commented 5 years ago

I'll try that, however I've noticed that the server still outputs that it successfully sent the notification to x devices.

TheRealNate commented 5 years ago

Even running this:

    Meteor.users.find().forEach(function (doc){
      console.log(`Sending notf to ${doc.username}(${doc._id})`);
      Push.send({
        from: 'push',
        title: 'Hello World!',
        text: 'Lorem ipsum',
        android_channel_id: 'testchannel1',
        gcm:{
          android_channel_id: 'testchannel1'
        },
        date:{
          android_channel_id: 'testchannel1'
        },
        payload:{
          android_channel_id: 'testchannel1'
        },
        query: {"userId":doc._id}
      });    
    });

it isn't working. A few things I noticed:

  1. It says Sent message "Hello World!" to 0 ios apps 0 android apps, which means with the usedId query it isn't even finding the app
  2. Every time I open the app on my device I get the Push: Settings userId "abc" for app: xyz but with a different app id
  3. I added a console.log to data.android_channel_id in push.api.js and it is getting there, so it's being packaged into the data object sent to gcm.Message as android_channel_id. Perhaps the syntax is wrong?
TheRealNate commented 5 years ago

From https://github.com/ToothlessGear/node-gcm/issues/135 it seems that the syntax needs to be

{ ...
  data:
   { message:
      { ...
        channelId: 'channel-ship-devs',
        ...
      }
   }
 }
TheRealNate commented 5 years ago

Is it possible we're getting the syntax wrong somewhere else? Maybe when we actually show the notification, we're not passing the channel id there?

da314pc commented 5 years ago

I think the syntax is correct. There was a comment on the plugin push to put everything in data:""

I believe somewhere the android channel Id is getting lost when sent from the server

TheRealNate commented 5 years ago

It gets all the way to the gcm.message in push.api.js, and is then passed to gcm.sender.send.

lokiribeiro commented 5 years ago

any updates on this? is raix/push already working on Android Oreo?

Thanks

TheRealNate commented 5 years ago

@lokiribeiro No. We still need to figure out how to pass the channel name from the server to the actual Android Java code that displays the notification on the device.

CineXMike commented 5 years ago

@TheRealNate Any place where i can start looking for that implementation?

da314pc commented 5 years ago

@TheRealNate I checked the node-gcm library and it seems it should work.

https://firebase.google.com/docs/cloud-messaging/http-server-ref

I think our payload is off. In your fork change this line: https://github.com/TheRealNate/push/blob/f81a3079e44349965c22d2e400860855c270d8dc/lib/server/push.api.js#L322

to: data.message.android_channel_id= notification.android_channel_id;

Also remove https://github.com/TheRealNate/push/blob/f81a3079e44349965c22d2e400860855c270d8dc/lib/server/push.api.js#L355-L358

The plugin says we should include the message inside the data struct. https://github.com/phonegap/phonegap-plugin-push/blob/master/docs/PAYLOAD.md#notification-vs-data-payloads

But we may just have trial and error. I'll try it today.

CineXMike commented 5 years ago

@da314pc Found anything usefull?

da314pc commented 5 years ago

@CineXMike not just yet. Installing version 2 of the plugin causes some gradle build errors. Soon as I get through that stuff I can try to see whats going on in the logs.

CineXMike commented 5 years ago

@da314pc Thanks for keeping me updated!

da314pc commented 5 years ago

@CineXMike @TheRealNate I think I got it working. I'm going to publish the changes I made. Let me test with ios, now.

CineXMike commented 5 years ago

@da314pc Let me know once we can test it aswell.

da314pc commented 5 years ago

@CineXMike @TheRealNate You may need to update meteor, The version of the plugin needs Cordova android 6.3.0,

This is what I am using so far for Android: Meteor Version: 1.6.1

I usually run meteor run android device without a device connected, once the build throws no device, I open up android studio

Make sure to add "google-services.json" in the platforms/android root folder. You can get this file from Firebase,

I did have this multi-dex issue compiling,: https://github.com/phonegap/phonegap-plugin-push/blob/master/docs/INSTALLATION.md#multidex

just force a specific version on the support library if its causing a problem in your gradle build

In my cordova plugins file I have: phonegap-plugin-push@2.1.2

I also bumped this version to: 2.1.2 https://github.com/raix/push/blob/464d82469ca15b8579b469b7156a6c9d16874d9b/package.js#L15

In my meteor app, I have this code after startup, PushNotification.createChannel( () => { console.log('success'); }, () => { console.log('error'); }, { id: Meteor.userId(), description: 'Android Channel', importance: 3, vibration: true } );
//Each channel needs a unique Id, so I just use userId,

I added the library here, I renamed the package so you can just change it or add the name I'm using, https://github.com/da314pc/push

I added this tweak here: https://github.com/da314pc/push/blob/8aa389071e711ee777497934ddd90912a9a36644/lib/server/server.js#L26

I was losing tokens I always keep a copy of the userId and previous token before It gets removed, there also may be more tweaks in there if you find it,

the channel Id is added to the notification collection: https://github.com/da314pc/push/blob/8aa389071e711ee777497934ddd90912a9a36644/lib/common/notifications.js#L44

Here we copy the channel Id so it can be sent in the payload: https://github.com/da314pc/push/blob/8aa389071e711ee777497934ddd90912a9a36644/lib/server/push.api.js#L317

Then on the server, when you call the Push Method:

Push.send({ from: 'test...', title: '..', text: 'test' android_channel_id userId, //userId that matches the channel userId
query: { // Ex. send to a specific user if using accounts: userId: userId //userId that matches the channel userId
}, payload:payloadData });

let me know if something doesn't work

I tried ios on version 2.1.2, I did the pod setup, pod install, built the project,

but for some reason I wasn't getting notifications, I'll keep trying. We should have at least a fix for android.

TheRealNate commented 5 years ago

@da314pc

This is amazing! Testing your fix using meteor@latest. Following these steps:

  1. Creating <appRoot>/platforms/android folder and adding google-services.json from Firebase console
  2. Clone https://github.com/da314pc/push in <appRoot>/packages directory
  3. meteor add raix:pushinternal
  4. Add your Push.send code (I don't have a payloadData, so leaving as {})

I'm testing this now. Anything I've missed?

da314pc commented 5 years ago

You can put anything in payload, I just use the payload to redirect users with FlowRouter (or any routers) once the app opens,

TheRealNate commented 5 years ago

@da314pc Doesn't seem to be working. Have I missed any installation steps?

Here's the full code:

In it's only file in client:

if(Meteor.isCordova) {
    try {
        PushNotification.createChannel(
            () => {
                alert('success');
            },
            () => {
                alert('error');
            },
            {
                id:Meteor.userId(),
                description: 'Android Channel',
                importance: 3,
                vibration: true
            }
        );
    }
    catch(e) {
        alert(e)
    }
}

On server:

let payloadData = {};
Push.debug = true;

Meteor.methods({

    testPush: function () {
        console.log("Sending...");
        Push.send({
            from: 'test...',
            title: '..',
            text: 'test',
            android_channel_id:Meteor.userId(),
            query: {
                userId:Meteor.userId()
            },
            payload:payloadData
        });
    }

});
da314pc commented 5 years ago

what version of meteor are you on?

you installed the right version of the plugin?

TheRealNate commented 5 years ago

Meteor 1.7.0.5

cordova:cordova-plugin-device  1.1.5
cordova:phonegap-plugin-push   2.1.2

Do I need to wrap the createChannel code in a Meteor.startUp or Accounts.onLogin

Also, even though I set Push.Debug to true all I'm seeing in console is Sending.... No error messages, and none of the verbose messages usually output by the push plugin.

Even running:

        Push.send({
            "from":'test...',
            "title": '..',
            "text": 'test',
            "android_channel_id":'test',
            "query": {
                "userId":'test'
            },
            "payload":{}
        });

Outputs 0 messages in server log

When I use your version of push, how should I be adding it?

da314pc commented 5 years ago

Change the query userId: to the userId of the user on the device your sending:
Push.send({ "from":'test...', "title": '..', "text": 'test', "android_channel_id":'test', "query": { "userId":'test' //change ID }, "payload":{} });

I put the createChannel code, in my layouts-js file. So after the user logs in, in the main layout create the channel.

My Plugins: cordova-plugin-android-permissions@0.11.0 cordova-plugin-app-event@1.2.1 cordova-plugin-app-version@0.1.9 cordova-plugin-camera@4.0.2 cordova-plugin-contacts@3.0.1 cordova-plugin-device@2.0.1 cordova-plugin-dialogs@2.0.1 cordova-plugin-file@6.0.1 cordova-plugin-file-transfer@1.7.1 cordova-plugin-geolocation@4.0.1 cordova-plugin-inappbrowser@2.0.2 cordova-plugin-inapppurchase@1.2.0 cordova-plugin-media-capture@3.0.1 cordova-plugin-meteor-webapp@1.6.0 cordova-plugin-navigationbar-color@0.0.8 cordova-plugin-simple-image-resizer@0.2.0 cordova-plugin-vibration@3.0.1 cordova-plugin-video-editor@1.1.3 de.appplant.cordova.plugin.local-notification@0.8.5 ionic-plugin-keyboard@2.2.1 phonegap-plugin-push@2.1.2

Push.debug is set on the server?

Add this on the client it helps debug:

Push.addListener('token', function(currentToken, newToken) { //Token is { apn: 'xxxx' } or { gcm: 'xxxx' } console.log("currentToken",JSON.stringify(currentToken)); console.log("newToken",JSON.stringify(newToken));

});

TheRealNate commented 5 years ago

Push.debug is set on the server. I'll try the listener.

Here's my full package/plugin list, am I missing anything?

accounts-base                  1.4.2  A user account system
accounts-password              1.5.1  Password support for accounts
accounts-ui                    1.3.0  Simple templates to add login widgets to an app
anti:modals                    0.4.0  Modals and dialogs: the Meteor way
blaze-html-templates           1.1.2  Compile HTML templates into reactive UI with Meteor Blaze
brentjanderson:buzz            1.1.10  The fantastic Buzz javascript sound library repackaged for...
check                          1.3.1  Check whether a value matches a pattern
cordova:cordova-plugin-device  1.1.5
cordova:phonegap-plugin-push   2.1.2
ecmascript                     0.11.1  Compiler plugin that supports ES2015+ in all .js files
es5-shim                       4.8.0  Shims and polyfills to improve ECMAScript 5 support
force-ssl                      1.1.0  Require this application to use HTTPS
iron:router                    1.1.2  Routing specifically designed for Meteor
meteor-base                    1.4.0  Packages that every Meteor app needs
mobile-experience              1.0.5  Packages for a great mobile user experience
momentjs:moment                2.22.2  Moment.js (official): parse, validate, manipulate, and dis...
mongo                          1.5.1  Adaptor for using MongoDB and Minimongo over DDP
msavin:sjobs                   2.0.1* The simple jobs queue that just works [synced, schedule, ta...
raix:push                      0.0.1* Meteor push server for IOS and android
raix:pushinternal              0.0.0-semantic-release+ Isomorphic Push notifications for APN and GCM
random                         1.1.0  Random number generator and utilities
reactive-var                   1.0.11  Reactive variable
session                        1.1.8  Session variable
shell-server                   0.3.1  Server-side component of the `meteor shell` command.
standard-minifier-css          1.4.1  Standard css minifier used with Meteor apps by default.
standard-minifier-js           2.3.4  Standard javascript minifiers used with Meteor apps by defa...
themeteorchef:bert             2.1.3  A client side, multi-style alerts system for Meteor.
tracker                        1.2.0  Dependency tracker to allow reactive callbacks
underscore                     1.0.10  Collection of small helpers: _.map, _.each, ...

I'm thinking it may be a server issue since Push.debug is set but I'm not getting any output.