I wanted a solution for the following problem:
I want to send notification specific to user like thank you message for one user and job posted say another user. I am looking for a way so that I can send separate messages depending on user id and registration id.
I tried the following method ie.
When a new user registers then, that users userid gets created on the database. I thought if I also create a registration id at the same time then I can save that in a separate table on the server. which will contain user id and registration_id and device type
But I noticed that at the time of GCM registration it is the Asynchronous task so I obtain it but till the user gets registered the id is still null. So it fails to get into the database.
If anyone has ideas on this particular problem. Or also has solved similar issue. Then do let me know.
Thanks for reading.
Actually the problem is GCM registration often reuires some time to register. So there is a simple way to get your id registered in your database. One is, if you are using splash screen in your app, then try to register for GCM there and in onRegistered method of your GCMIntentService save your registrationId in preferences like this
#Override
protected void onRegistered(Context arg0, String regId) {
Log.v("registrationId", regId);
prefs = getSharedPreferences("filename", 0);
prefs.edit().putString("regid", regId).commit();
}
Now at the time of login you should try to get your registrationId from the prefs where you have stored in onRegistered method. But still there is 1-2 % chance that you will not get you regId here. so for that, you can make a separate api for GCM Registration, that you will hit((if your regid is not registered at Login time)) in Activity next to your LoginActivity to get the id registered to your database.
Secondally, if you are not using splash screen, then you have to make separate api for GCM Registration because there is more chance that you will not get your registrationId during login.
Related
I have app that allows user to login with credentials issued by the server, lets say simple login with password and usename.
So user starts the app, enter username and password, server authentificates the user, save this information. (details are really don't play any role in this case).
What I need to implement ?
There some events when my server need to notify user. Lets consider order status changed. If so I need to notify user about this, with notification in the status bar.
I have decided to use GCM for this task.
I read about this, and got some questions.
When user gets registration ID from GCM service in android, is it constant or it can be changed. As I understand there is no guarantee that it be always the same. So how can I handle refreshes of this ID.
Or if I will not call GoogleCloudMessaging.getInstance(applicationContext).registerit will stay the same until I register new or even if I call register method it can return the same ID. May I save this ID in SharedPreferences for example to use it for a long time ? Or it can be become invalid after some period of time ?
The main problem is that I need to handle multiple users on the same device. As far as I know, registration id issued by gcm service it tied to the device and app. So if new user logged out, and logged in with new credentials (new user account) how can I handle this ? I can for example store this ID in my database on the server and use it until user logout from the account inside my app, and after that just remove it from the database, and if user on this device will login with another account registration ID can be used to notify another user ? Will it work ? Or I have missed something.
How can I handle user multiple device. My group notifaction key will be some unique data from user account (email,username..). If I understand correctly, I need to get registration ID on all devices where user logins with its my server account and map this to one notification key. Should I handle notification dismiss by myself and than send upstream message to the GCM server in order to notify other user devices that notification has been dismissed ?
Please help with understanding of this service.
Thanks in advance.
I have implemented this in one of our app. In this we send the notification to users whenever new listing get added.
User can login from multiple devices same time. Device may be android or iOS anything.
Also multiple users can login from same device (After logging out previous users).
Whenever user log in from Android device will get the registration ID from GCM. This GCM id is not constant for one device, it gets changed whenever you again request it from GCM server.
We send the notifications to multiple devices using notification key, it contains GCM register ids of all devices. If you lose a notification key, you can’t get it back. To make it worse, You can’t regenerate a new notification key with a previously-used notification key name.
Never use unique data from user account (email, username..) as notification key name. Always generate some random string as notification key name.
To overcome this follow following steps:
Create one table to store the following details
Columns :
id
userId
deviceGCMId
deviceId
notificationKey
notificationKeyName
whenever user logs in sent the following parameters to the server.
GCMId, deviceId, userId
First check that userId and deviceId exists or not.
If not exists then go to step 5 else go to step 6
From given userId get the notificationKey and notificationKeyName from table. If not found then generate new notificationKeyName and from that key generate new notificationKey. Use current GCMRegId to generate the key. Store the keys in variables.
Add new entry in table with notificationKey and keyname generated in step 4. Also add the GCM id under notification key.
Check the deviceId is same and GCM id is different then add update the GCM id in table and add that GCM id under notification key. If device id and GCM id same then no need to do anything
Sometimes notification key get discarded from GCM server and it shows the key not found error. In this case create the new notificationKey from new key name and add GCM ids against that key.
For more info go through following useful links
https://medium.com/appunite-edu-collection/notifications-d7df385b0ff4
https://medium.com/#Bicx/adventures-in-android-user-notifications-e6568871d9be
Hope this will help
Is there something wrong in the fact that every time I log in to my android application, the first thing I do is to register to GCM.
I know it's not necessary to do this, but in order to check if the registration id is refreshed to a new one or it s still the same, I plan to re-register.
final String regId = GCMRegistrar.getRegistrationId(this);
if (regId.equals("")) {
GCMRegistrar.register(this, SENDER_ID);
} else {
Log.v(TAG, "Already registered");
}
I would also like to know if by re-registering to GCM I am somehow forcing the registrationID to expire.
Thank you!!!
Part 1:
In order to check if the registration id is refreshed to a new one or it is still the same, I plan to re-register.
Now, if you see the second point under the heading Enable GCM on Architectural Overview page, it says:
Note that Google may periodically refresh the registration ID, so you should design your Android application with the understanding that the com.google.android.c2dm.intent.REGISTRATION intent may be called multiple times. Your Android application needs to be able to respond accordingly.
So, Google automatically sends you this broadcast, when it renews the ID. You might not necessarily send check the emptyness of the ID. When Google changes the ID, then also it won't be empty, right? So if you want to handle the renewal/refreshing of the ID then you may do the following:
You should have a Broadcast Listener which could handle com.google.android.c2dm.intent.REGISTRATION intent, which Google send to the app when it has to refresh the registration ID. The broadcast receiver will have the onReceive method with an Intent. From the intent you can get the Bundle using which you can extract the new registration ID from Google. You can save that and send it to the 3rd part server to replace your previous registered ID for that user.
Also you may see this answer on the question In GoogleCloudMessaging API, how to handle the renewal or expiration of registration ID?.
Part 2:
Answering your second part of question:
You may want to read Unregistration here. The docs says If you unregister and then re-register, GCM may return the same ID or a different ID—there's no guarantee either way.
I think that whenever an ID will be about to expire/renew, Google will send you the new ID. Then the response from Google will contain a Canonical Registration ID (which is the new registration ID). This response indicates that your server should delete the old registration ID and use only the new one. ( Source : Answer at Unregistering and re-registering for GCM messages causes two regId's to be valid. Is this as intended? question by #Eran)
Hope this helps you understand how to handle it.
In the docs on Google Cloud Messaging, it states:
The Android application should store this ID for later use (for
instance, to check on onCreate() if it is already registered). Note
that Google may periodically refresh the registration ID, so you
should design your Android application with the understanding that the
com.google.android.c2dm.intent.REGISTRATION intent may be called
multiple times. Your Android application needs to be able to respond
accordingly.
I register my device using the following code:
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(context);
String regID = gcm.register(senderID);
The GoogleCloudMessaging class encapsulates the registration process. So how am I suppose to handle com.google.android.c2dm.intent.REGISTRATION since handling that is done internally by the GoogleCloudMessaging class?
That's an interesting question.
Google encourage you to switch to the new registration process :
An Android application running on a mobile device registers to receive messages by calling the GoogleCloudMessaging method register(senderID...). This method registers the application for GCM and returns the registration ID. This streamlined approach replaces the previous GCM registration process.
The note that says Google may periodically refresh the registration ID only appears on the page that still shows the old registration process, so it's possible that this note is no longer relevant.
If you want to be safe, you can still use the old registration process. Or you can use the new process, but have in addition the code that handles the com.google.android.c2dm.intent.REGISTRATION intent, in order to make sure you are covered if Google do decide to refresh the registration ID.
That said, I never experienced such a refresh, and even when I did experience a change in the registration ID (usually as a result of sending a notification after un-installing the app and then re-installing it), the old registration ID still worked (resulting in a canonical registration ID sent in the response from Google), so no harm was done.
EDIT (06.06.2013) :
Google changed their Demo App to use the new interface. They refresh the registration ID by setting an expiration date on the value persisted locally by the app. When the app starts, they load their locally stored registration id. If it is "expired" (which in the demo means it was received from GCM over 7 days ago), they call gcm.register(senderID) again.
This doesn't handle the hypothetical scenario in which a registration ID is refreshed by Google for an app that hasn't been launched for a long time. In that case, the app won't be aware of the change, and neither will the 3rd party server.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mDisplay = (TextView) findViewById(R.id.display);
context = getApplicationContext();
regid = getRegistrationId(context);
if (regid.length() == 0) {
registerBackground();
}
gcm = GoogleCloudMessaging.getInstance(this);
}
/**
* Gets the current registration id for application on GCM service.
* <p>
* If result is empty, the registration has failed.
*
* #return registration id, or empty string if the registration is not
* complete.
*/
private String getRegistrationId(Context context) {
final SharedPreferences prefs = getGCMPreferences(context);
String registrationId = prefs.getString(PROPERTY_REG_ID, "");
if (registrationId.length() == 0) {
Log.v(TAG, "Registration not found.");
return "";
}
// check if app was updated; if so, it must clear registration id to
// avoid a race condition if GCM sends a message
int registeredVersion = prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE);
int currentVersion = getAppVersion(context);
if (registeredVersion != currentVersion || isRegistrationExpired()) {
Log.v(TAG, "App version changed or registration expired.");
return "";
}
return registrationId;
}
/**
* Checks if the registration has expired.
*
* <p>To avoid the scenario where the device sends the registration to the
* server but the server loses it, the app developer may choose to re-register
* after REGISTRATION_EXPIRY_TIME_MS.
*
* #return true if the registration has expired.
*/
private boolean isRegistrationExpired() {
final SharedPreferences prefs = getGCMPreferences(context);
// checks if the information is not stale
long expirationTime =
prefs.getLong(PROPERTY_ON_SERVER_EXPIRATION_TIME, -1);
return System.currentTimeMillis() > expirationTime;
}
EDIT (08.14.2013) :
Google changed their Demo App again (two days ago). This time they removed the logic that considers the Registration ID to be expired after 7 days. Now they only refresh the Registration ID when a new version of the app it installed.
EDIT (04.24.2014) :
For the sake of completeness, here are the words of Costin Manolache (taken from here), a Google developer involved in the development of GCM, on the matter :
The 'periodical' refresh never happened, and the registration refresh
is not included in the new GCM library.
The only known cause for registration ID change is the old bug of apps
getting unregistered automatically if they receive a message while
getting upgraded. Until this bug is fixed apps still need to call
register() after upgrade, and so far the registration ID may change in
this case. Calling unregister() explicitly usually changes the
registration ID too.
The suggestion/workaround is to generate your own random identifier,
saved as a shared preference for example. On each app upgrade you can
upload the identifier and the potentially new registration ID. This
may also help tracking and debugging the upgrade and registration
changes on server side.
This explains the current implementation of the official GCM Demo application.
com.google.android.c2dm.intent.REGISTRATION should never be handled when using the GoogleCloudMessaging class to register.
Reading the new InstanceID API, I found more info on when the token might change:
Your app can request tokens from the Instance ID service as needed
using the getToken() method, and like InstanceID, your app can also
store tokens on your own server. All tokens issued to your app belong
to the app's InstanceID.
Tokens are unique and secure, but your app or the Instance ID service
may need to refresh tokens in the event of a security issue or when a
user uninstalls and reinstalls your app during device restoration.
Your app must implement a listener to respond to token refresh
requests from the Instance ID service.
More details:
The Instance ID service initiates callbacks periodically (for example,
every 6 months), requesting that your app refreshes its tokens. It may
also initiate callbacks when:
There are security issues; for example, SSL or platform issues.
Device information is no longer valid; for example, backup and restore.
The Instance ID service is otherwise affected.
Sources:
https://developers.google.com/instance-id/
https://developers.google.com/instance-id/guides/android-implementation
After scrubbing through tonnes of misleading answers across the net, including SO, the only place I found a complete answer was as remarked by Eran's answer and here:
While automatic registration refresh might or might never have happened, google describes a simiple algorithm to handle the canocical_ids by parsing successful response:
If the value of failure and canonical_ids is 0, it's not necessary to parse the remainder of the response. Otherwise, we recommend that you iterate through the results field and do the following for each object in that list:
If message_id is set, check for registration_id:
If registration_id is set, replace the original ID with the new value (canonical ID) in your server database. Note that the original ID is not part of the result, so you need to obtain it from the list of code>registration_ids passed in the request (using the same index).
Otherwise, get the value of error:
If it is Unavailable, you could retry to send it in another request.
If it is NotRegistered, you should remove the registration ID from your server database because the application was uninstalled from the device or it does not have a broadcast receiver configured to receive com.google.android.c2dm.intent.RECEIVE intents.
Otherwise, there is something wrong in the registration ID passed in the request; it is probably a non-recoverable error that will also require removing the registration from the server database. See Interpreting an error response for all possible error values.
From aforementioned link.
I'm developing a small application using GCM Service.
Before, I tried to send to my self a message, but (server side) the answer has been:
"success":0,"failure":1,"canonical_ids":0,"results":[{"error":"NotRegistered"}]}
But at the beggining of my app i check this:
final String regId = GCMRegistrar.getRegistrationId(this); if (regId.equals(""))
{
// Register
GCMRegistrar.register(this, SENDER_ID);
}
else ...
It seems evident that getRegistrationId() works fine only locally, (SharedPreferences ?)..
The nice thing is that i never did Unregister my app,just reinstall it, not changing version number (because it is in test,still) so in this case my account has expired, in those cases google should not send me another id that i could catch here:
#Override
protected void onRegistered
???
However there is a safe way to understand if my app is registered GCM server side?
Thanks!
EDIT:
I'm thinking this:
When i reinstall my application through Eclipse, there is a moment where my application is not installed, if GCM server send me a message in that moment there is no receiver and so google unvalidates my ID.
In your opinion is this idea, a stupid idea?
follow these steps http://developer.android.com/google/gcm/gs.html.
It could happen because some time gcm registartion id gets expired so everytime you should check that is it registered or not if not register it
You should only register the device one time. Save the registrationId in some way (shared preferences is a good one) and use it.
From the documentation:
Register the application for GCM and return the registration ID. You must call this once, when your application is installed, and send the returned registration ID to the server.
In some tests i've made, the registration id returned by gcm.register(id) isn't always the same for the device,app pair. However, Google says:
Repeated calls to this method will return the original registration ID.
If the method gcm.register(id) returns an id, it will be valid ever since.
Best.
I have configured GCM in my application. I want to unregister device from GCM whenever user will uninstall application.
I got the code as
Intent unregIntent = new Intent("com.google.android.c2dm.intent.UNREGISTER");
unregIntent.putExtra("app", PendingIntent.getBroadcast(this, 0, new Intent(), 0));
startService(unregIntent);
but where we have to put this code..?
Thank You.
You cannot call unregister from GCM while uninstalling, because there is no method called while user uninstalls the application.
when you send a push notification, GCM will check if the user has your application, if the user has uninstalled the application GCM will note the same and inform you as part of reply for the push.
You have to check this on your server. You cannot do it from the application code since there is no way of knowing when the user is uninstalling the application.
See: Implement Canonical IDs.
Reference: https://developers.google.com/cloud-messaging/http#request
A canonical registration ID is defined to be the ID of the last registration requested by your application. This is the ID that the server should use when sending messages to the device.
If later on you try to send a message using a different registration ID, GCM will process the request as usual, but it will include the canonical registration ID in the registration_id field of the response. Make sure to replace the registration ID stored in your server with this canonical ID, as eventually the ID you're using will stop working.
Reference: https://stuff.mit.edu/afs/sipb/project/android/docs/google/gcm/adv.html#canonical
If the Canonical ID is not 0, then you have a duplicate registration.
Say for instance, you have 2 registrations in your database:
registration_A
registration_B
When you send a push notification, your server will get respond with something that looks like this:
{"multicast_id":########,"success":1,"failure":0,"canonical_ids":1,"results":
[{"registration_id":"new_id_registration_id","message_id":"0:########"}]}
{"multicast_id":######### ,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:################"}]}
Store this data in an array. Notice that the first one has a "canonical_ids":1. This means there was a duplicate. So to know which record in your database is the old one. Just search for "registration_id" in the above and save the index value. This index value points to the old record in your database.
In the above example, registration_A would be the old registration_id.
Get all the records from your database. Then delete it based on the index value that you retrieved. OR you can update it. That depends on how you set up your database.
Good luck!