How do I know when the FirebaseInstanceId is refreshed? - android

I'm using the following line of code to retrieve the FirebaseInstanceId:
FirebaseInstanceId.getInstance().id
I'm using it as a unique app id and want to know when it changes, because I save it on the server side + I want store it in my shared preferences, so I need to know when it is refreshed.
Do I do this in FirebaseMessagingService's onNewToken(token: String), even though this method is meant for the FCM token? To my understanding, this the instance id is tightly coupled with the FCM token, and is in fact the prefix of it. Can there be a situation where the instance id is refreshed, but the FCM token remains the same (or vice versa)?
If so, what do I do in case my app doesn't need FCM, but I still want to store it? Implement it anyway?

Documentation on FirebaseInstanceId states the following:
Instance ID is stable except when:
App deletes Instance ID
App is restored on a new device
User uninstalls/reinstall the app
User clears app data
So, the way I see it, you should be reasonably safe because that id can be either removed by your own code (via Firebase#deleteInstanceId()) or by the users when they restore/reinstall/uninstall their apps. There're no other cases where this id could be changed throughout the app's life.

Related

When should we refresh android device token?

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.

When is FirebaseInstanceId.getInstance().getToken() = null?

From where and when is the token retrieved and when is it available?
Is it a synchronous call to the Firebase server? If I call it too soon in the app lifecycle, might it not have been populated yet?
From where and when is the token retrieved and when is it available?
The token is generated by the FCM Instance ID service in the background, which starts as soon as your app runs. The details on how the token gets generated is unclear, but how I see it is that the device needs a decent connection to the internet in order for it to communicate with the FCM servers for the token.
Is it a synchronous call to the Firebase server?
Technically speaking, no. As mentioned in the docs:
FirebaseInstanceID.getToken() returns null if the token has not yet been generated.
At this time, if the token is null, you should expect a trigger in your onNewToken() where you could then call getToken() which should now contain the token.
If I call it too soon in the app lifecycle, might it not have been populated yet?
It's usually okay to call getToken() as soon as possible -- in your app's MainActivity -- in most cases, by the time your app reaches that point, it already has a value. But then again, you should still handle it properly if it is null.

Realm - Does the .currentUser() deliver the current logged in on the mobile device or ist there another meaning to it?

I have checked the class SyncUser and the definition for the method currentUser() is: Returns the current user that is logged in and still valid.
What I want to know is, what exactly is delivered, when this method is executed? How reliable is the delivered value and what is the limitation regarding the definition of "current user".
Thanks for any information!
SyncUser.currentUser() is just a convenience method for retrieving a single SyncUser that have logged in on that particular device using either SyncUser.login() or SyncUser.loginAsync(). It only works if a single user is logged in, otherwise, you need to use SyncUser.allUsers().
This is commonly needed when the app is restarted. You can then check if the SyncUser already exists and thus there is no need for them to log in again. This can be useful in the case when the device is offline and logging in is not possible.
The SyncUser is represented by what is called an access token, which has a default lifetime of 10 years, so valid in this context just means "Access token granted by the Server that has not expired yet".
Of course, that information cannot be fully validated on the client, e.g if the Server was restored from a backup losing information about a particular token, then the Client might think the token is valid while the server will reject it. In that case, you will receive a callback on the SyncConfiguration.Builder.errorHandler(..) with an event you can react to and log the user in again.
You can see an example of this in practice in this example here: https://github.com/realm/realm-java/tree/master/examples/objectServerExample

Issues with GCM Push Notifications

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~

Parse anonymous User ID doesn't persist on Android

I'm using anonymous users with parse but user ID keeps changing every time the app is restarted. I can create a serious of records, tied to a user and see them in backend / the app itself. Then kill the app, restart it and see a new user in Parse backend. Obviously, all records are no longer displayed because this is a "new user".
How can I make anonymous user id persist?
Turns out, if you're using Parse SDK and their anonymous users, you need to check if user is logged in and then run anonymous user initialization only in that case. Otherwise, each time the app is restarted, a new anonymous user is created. Makes sense but wasn't in their docs.
When you 'create' a new user, i think that you are getting an implied 'logon' of that new user.
Note that the response to the logon includes a session token ( no expiration on token ).
Using 'sharedPreferences' or some other android persistence technique, when you restart the app you can just retrieve the token from your persistence layer and - at least in REST API - just include that token in your headers along with AppId, and REST key and you will get that users data.
If you are using the SDK and not rest api, there may be some other way to get back to a reference for the user.

Categories

Resources