I'm building an app that uses c2dm.
I think that I'm supposed to ask for a registration ID whenever my app's main "intent" starts up. I've been doing this, but it seems that each request results in a new string, so I end up piling up numerous registration IDs in my database for the same device. I presume that only one of them will work. However, I don't want to just delete the old ones because I want my user to be able to receive notifications on more than one device if they own more than one.
How should I handle this?
Never to refresh ID untill your app is installed on your Device
I have implemented C2DM. If you have more than one registration ID for one device then only latest ID will work for sending notification.
When you register your device to google server then it generate a String(As you already know) corresponding to that device ID.But if you again register using same device then previous one is useless.
So i will suggest better to keep information that device is already registered or not.If registered then no need to re-register again.As you will get notification on the base of your old R_ID. simply send this R_ID to your server for notification purpose and save flag in your data base to keep track of registeration
Your only suppose to request a registration ID when you don't have one and send it to your server(the one that will send C2DM messages). It's strange that you keep getting a new id, in my own testing every time I register and unregister this same app on the same device I get the same id. I haven't done a lot of testing yet, but I would assume the id would change for every device and app combo.
I am saving the registration_id with the device_id in the DB. That way I have only one entry for each device. Also the reg id is saved on the device as a shared preference so that I don't have to request a new one each time...
But I would like to know to if there is some way to find out if the reg id the user has on it's device is expired or not.
Any ideas? Is there an intent for it?
If you register with the cloud, the returned registration ID will be valid until you receive the intent with the "unregistered" string extra (which will only occur if your application consciously unregisters with the service) OR Google sends another Registration intent with a new registration ID, in which case the system you have setup should work fine.
Your server will know if the registration ID has expired if it receives a 200 response with data "Error=NotRegistered". In this case, you would remove that entry from your DB.
http://code.google.com/android/c2dm/#server
The registration ID for the device should be requested from the C2DM server only the first time the app is run after installation and then saved in the database( preferably along with the device ID so that it is unique).
The registration ID is valid as long as C2DM does not send the device a new registration ID, or the user uninstalls the app. I'm assuming you know how to handle the latter case.
Coming back to the first case. It is possible that during the lifetime of the application in the device the C2DM server may send the device a new registration ID[It will do so on its own and you do not have to request again for it]. Thus you should have a listener for the same, and you can update the registration ID of that device in your database.
Hope it helps.
Related
I want to build a notification system in my Android app where I can send a notification to someone upon an event. For example, my app is about a social system where it uses Firestore database (Firebase) to save its users (which means I have a 'users' collection in there), and I have an 'Add as Friend' feature.
Everything works just fine, but I would like to add a feature where when user1 adds user2 as a friend, I would like to send user2 a notification about that.
I have searched in google and saw this post: https://www.geeksforgeeks.org/how-to-push-notification-in-android-using-firebase-cloud-messaging/
Now, from what I understand, this wouldn't help me much cause it only sends the notification from FCM by me (and not to a specific user).
The problem is, I don't really know what to search for (or what to type in google) in order to find what I'm looking for.
Some ideas I thought:
to make a collection in firestore called 'Notifications' where it holds usernames as documentIDs
when user1 sends a friend request to user2, I would add data (like title/message) to 'Notifications/user2/'
But I'm kind of stuck here, I would like to know ideas or if you guys have threads on how do I implement this, I really don't know what should I look for.
Thank you very much and sorry for the long post.
Firebase creates a userID for every user and a usertoken which is a temporary identifier for the device which is connected to this userID. You need to save the usertoken when there is a new one being created in a database(firestore) along with the user with whom you identify this person.
When someone adds this person as a friend you can add a notification in the database that there is a request from person1 to person2.
This change in the database can trigger a cloud function that reacts to changes in the database.
The triggered cloud function sends an FCM to the person which is being added as a friend. This FCM can either be a pure notification or you send data and let your app handle how this data is being handled(saved in a local database or displayed as a custom notification).
Keywords are: FCM, Notification, Firestore, cloud functions, and firebase token
Edit (Answer to 1. comment): Firebase userID and usertoken are created on the device. In the tutorial you implement FCM(I didn't read it complete) and a class called FirebaseMessagingService, from which you can inherit. This class is called, when a message is sent to the device and contains a method called onNewToken. This message is called, when the Firebase Server creates a token for your device and sends it to the device. In this method you might want to send the new token to your database and save it as an identifier for the user of this device. The Tokens are used by firebase to identify devices, to which messages are being sent(You can read about FCM in detail)
Cloud functions are something you can deploy along your firebase project, which get triggered when an event occurs. For example a http call, or a specific change in your database. Cloud functions are written in JS and need to be deployed for your firebase project. (Cloud functions are not for free and you need to pay for each call, but you have some thousand uses for free each month.)
You send a FCM to a specific person by creating a firebase message which is directed to a specific token, tokens or user groups. To identify the person to which you want to send a message you need to have the token for this user, that is why you are saving the tokens from the 1. part in your database and always update the token, when the token changes on the device.
Keep in mind, that a user might use multiple devices. You should have an identifier for you, such as username and along this username you save multiple firebaseIds and Firebase tokens. A single FCM can be directed to up to 100 tokens.
My app sends notifications using Firebase Cloud Messaging FCM. For every user, I'm storing the device token in database and I fetch it when I want to notify him. I'm using FirebaseMessagingService with the overridden method onNewToken that updates my database with new tokens. I suppose that this method is called every 1 hour to check token's update, but I was expecting it to be also called when the service is initialized for the first time (after installing and running the app on device). However this is not the case. To remedy this, I could call onNewToken each time the user log in But I would like to know if this is an acceptable way or there is a better one.
To avoid abuse, I leave here extra information on my case :
I run my app on Android Studio emulator and I check the stored token in database, let's call it TOKEN-1.
Now I install the app on my phone and I show the token with String token = FirebaseInstanceId.getInstance().getToken(); Toast.makeText(MainActivity.this, token, Toast.LENGTH_LONG).show();
The token is different that the first one TOKEN-1, and TOKEN-1 is still stored in my database. This means that I can receive notifications only on emulator and not my phone.
Sorry for my long text and looking forward to reading your suggestions.
The FCM SDK and server work together to manage the token in the background, and listening to onNewToken ensures that you get notified when the token changes. For this reason you should always be listening to onNewToken, and update it in your own database whenever it changes.
There is no guarantee that your FCM token will be refreshed every hour (or even ever) though, as you seem to expect. Given the 1 hour interval, you might be thinking of Firebase Authentication ID tokens, which are short-lived and are indeed refreshed every hour.
Finally: the token doesn't get refreshed when you attach a listener. In fact: if the token was already generated before you attach a listener, your listener won't be called. For this reason, you'll typically also want to grab the current token in your main activity when the app starts, and store it in the database at that point.
This last code is mostly necessary during development, as that's where you're most likely to have the scenario where the token gets generated when you don't have an onNewToken listener yet. So instead of putting code in the main activity, you can also uninstall/reinstall the app after adding your onNewToken listener, as FCM will generate a new token upon installing the app in that case - and thus call your onNewToken with this initial token.
for my first question on StackOverflow I'm gonna ask about Google Cloud Messaging service, and in particular Loopback's implementation.
So, I'm developing an app and started to work on a different branch to introduce Loopback's push notification handling and it's various tools for REST Api. Even if this topic is gonna cover strictly Loopback's way to handle GCM, the question is also related to the original way as is described on Google docs.
So, the main idea behind GCM's kick-off is to check whether the device is registered or not.
This is done by a simple check on a SharedPreferences variable, a name used to store our RegistrationID value.
final LocalInstallation installation = new LocalInstallation(context, adapter);
If this is found, the device has to notify the server, communicating the token.
Else, a registration to GCM has to be done.
Once this is done, the device notifies the server. ( registerInBackground(installation) will eventually call saveInstallation(installation) after retrieving RegistrationId )
if (installation.getDeviceToken() != null) {
saveInstallation(installation);
} else {
registerInBackground(installation);
}
If communication is successful, the device saves RegistrationId using SharedPreferences as described above. (NOTE : getDeviceToken() is Loopback's way to handle via API the value in SharedPreferences)
Let's say this "GCM-Check" is being done every time my MainActivity is being created (so, during the onCreate method).
We also know GCM is sometimes messy, and wants to refresh my app's RegistrationId or some other stuff that, to be honest, is not completely clear to me right now. In short terms, GCM invalidates the token of my app. This causes an error-message when the server send a push-notification using the Token bound to my device-app.
An error similar to
{"multicast_id":0123456789012345678,"success":0,"failure":1,"canonical_ids":0,"results":[{"error":"NotRegistered"}]}
You can see, "failure":1 and "results":[{"error":"NotRegistered"}]
Loopback reacts just as Google docs say, by having the server remove the record of the device linked to the faulty RegistrationId .
Comprehensible.
Back to our device. Launching my app again and loading MainActivity, causes the same "GCM-check" procedure. This time the app can find RegistrationId using SharedPreferences, and can directly notify the server, which creates a record with the given RegistrationId.
No new registration is being handled by the device-app.
You can see the loop in here. Device will have no knowledge of it's token invalidity and will continue to tell the server the same data, which will keep sending information to the wrong registrationId, thus removing it after receiving the related error.
The problem is that the app has to rely on data which is created once and never gets modified. To remove the old data I should send a notification to the device, which is obviously not possible as I can't reach it from GCM. Other solutions possible is notify the user by sending an email or sms, and ask him to click a button for example, but I'd rather have a more "automated" approach to the problem.
A VERY BAD SOLUTION I'VE FOUND
As to my knowledge the only error-info is returned from GCM to the server during a push-notification, I've made a little hack on the device.
The idea is simple: create a POST request to GCM Servers, using the headers my server should use to authenticate. This causes the error to be given to the device itself, which can parse the JSON and notice what happened. From here the app can forge a new registration process, fixing the issue.
What is bad about this? The fact that to authenticate the device as the server, I have to hard-code the ServerKey and distribute it in every app. The ServerKey should be used only on the server, making this solution a very bad idea.
This being said, the idea of simply letting the device know its state using a SharedPreference value is not so great, as it would only tell me if I ever registered at least once, without letting me know my current status.
On other apps I've developed which use GCM just as well, I've solved in different ways, like having a Button to be clicked by the user or reading some specials SMS send by the server, which then enable GoogleCloudMessaging.unregister() at first and eventually GoogleCloudMessaging.register()
So, asking for forgiveness for such a wall-of-text, how have you solved this NotRegistered thing?
Thanks for your effort and time in reading and, hopefully, in answering : )
As an addendum to my comments, since it helped and I have more space here:
Instead of just checking whether or not the token exists inside your SharedPreferences, you should also check to see if the version of your app that token is for matches the version that is currently running the check (that was a suggestion from Google's docs).
If the device version do not match, you should request a valid token (which could actually be the same, but is not guaranteed). Now, you may also want to check for the PACKAGE_REPLACED broadcast (in case you're not incrementing the version in your manifest each time you install for tests, which I'm very guilty of) which, if triggered, should also force you to request a new token.
As for why there's sometimes a change in the token and sometimes not: I couldn't agree more that it's totally sporadic and can't help feeling like there's something going on we don't really know about.
Sometimes the new request after PACKAGE_REPLACED returns the same key; sometimes it doesn't. What happens in between those that makes it weird? God, I would LOVE to know, and I wish I had more info on it. But that's part of why I mentioned trying to catch that broadcast: forcing the check when it happens should ensure that you're always getting a new valid one (in the event that the version check passes when it shouldn't) if it's available.
Hopefully this helps~
Due to a bug on our system, we mistakenly stored multiple registration ids per device per user by appending to their profile, rather than replacing after an upgrade and the like.
We can obviously now correct that by handling the canonical ids from the response we get, but for some users (like myself, for instance...) they would get multiple notifications, instead of just one per device.
I've read what can be sent via,
http://developer.android.com/google/gcm/http.html
and
http://developer.android.com/google/gcm/server.html#params
But there doesn't seem to be a perfect solution. I've just checked there, and I could send a 'dry_run' request first, handle the multiple registration ids, replace what needs replacing, remove what needs removing, then send the second (pruned) request.
This certainly could be a solution, but I can't imagine it'd be friendly to our api quotas (though I don't really know). Is there no other property that can be set on the HTTP equest so as to only send to an individual device?
GCM is completely free no matter how big your messaging needs are, and there are no quotas.
(quote from here).
There are no quotas to GCM, so you can try your dry_run approach without fear. The only question is whether dry_run mode actually returns Canonical Registration ID, or simply returns immediately with just some fake message ID.
Using the Canonical Registration ID response is the only way you can use to clean your DB (other than deleting your DB and re-building it from scratch).
There is a small optimization you can make in your cleanup process. If you can fetch the registration IDs from your DB in the order they were inserted (from the oldest to the latest), you will likely get Canonical Registration ID responses for the first registration IDs you try. For each of them you'll know what the current (canonical) registration ID is, and you'll mark it, so you don't send to it during your cleanup process. This will prevent sending duplicate messages for all devices that have up to 2 registration IDs in your DB (and will reduce by one the number of duplicate messages for the other devices).
In which time frequency the GCM server refresh the registration ID. If it is changed how to get regId changed event in my mobile?
My Application registers on GCM on first time launch and store the regId Id in Sahred Preference file. Then it will get regId from shared Pref and send to our server when user done some event manually.
My Problem is,How to find the reg Id refreshed in GCM Server. Then only i can create a event to update on server to update reg Id.
When the regid changes on the GCM server, then it is your server which is responsible for updating the regid in its database.
It is only possible because the old regid still works (at least once anyway) and tells your server in its response to the message sent with the old regid by means of the canonical_id, which will contain the new regid that you should use in future. Have a look at the dev docs and advanced topics for a full explanation
See http://developer.android.com/google/gcm/gs.html.
You need to add the BroadcastReceiver to your application as per the directions on this page. You then create a GCMIntentService class, which extends GCMBaseIntentService. Have that service upload the registration ID to your app server (or store it in shared preferences for later).
As per http://developer.android.com/google/gcm/client.html#sample-register, unlike previous versions of GCM and C2DM, Google itself does not refresh the registration. Once you have the registration id from the initial registration you are good to go, except for one case: you do still need to re-register when the user upgrades to a new version (this case is also handled in the example in the link above).
When an application is updated, it should invalidate its existing registration ID, as it is not guaranteed to work with the new version. Because there is no lifecycle method called when the application is updated, the best way to achieve this validation is by storing the current application version when a registration ID is stored.