How to check this condition and then send to server when refresh token?
App deletes Instance ID
App is restored on a new device
User uninstalls/reinstall the app
User clears app data
Here is my code to send server. But is it send all time when app login ? How to send when above condition?
#Override
public void onNewToken(String s) {
super.onNewToken(s);
Log.e("NEW_TOKEN", s);
sendRegistrationToServer(s);
}
private void sendRegistrationToServer(String token) {
APIInterface apiInterface = APIClient.getClient().create(APIInterface.class);
Call<Fcm> call = apiInterface.postFCMToken(user_id, newToken);
call.enqueue(new Callback<Fcm>() {
#Override
public void onResponse(Call<Fcm> call, Response<Fcm> response) {
}
#Override
public void onFailure(Call<Fcm> call, Throwable t) {
}
});
}
In general, you should expect that an app installation is unable to reliably detect when an FCM token changes. As such, any new token should be unconditionally reported to the backend, as a potential target for messaging for the given user.
The server side of your app can remove invalid tokens by looking at the error response when attempting to send a message to a token that's no longer valid. If you're using the Admin SDK to send a message to a device token, you can detect then when a token is invalid because the API will generate the error messaging/registration-token-not-registered, according to the documentation.
Related
I am using Firebase FCM to send notifications from my server to the users.
When the user installs the App for the first time, i catch the fresh and new token at MessagingService.java:
#Override
public void onNewToken(#NonNull String tkn) {
super.onNewToken(tkn);
sendTokenToServer(tkn);
}
Here comes the problem, when the user closes session (without uninstalling the app), SharedPreferences are deleted. A new session is started; but onNewToken() is not called. So, i must manually retrieve the Token inside my MainActivity in order to send it to the server. I am getting the updated token with this piece of code:
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(MainActivity.this, new OnSuccessListener<InstanceIdResult>() {
#Override
public void onSuccess(InstanceIdResult instanceIdResult) {
sendTokenToServer(instanceIdResult.getToken());
}
});
As you know, that code is deprecated and should be avoided. Instead, i tried to replace it with this piece of code with no success:
FirebaseInstallations.getInstance().getToken(true).addOnCompleteListener(new OnCompleteListener<InstallationTokenResult>() {
#Override
public void onComplete(#NonNull Task<InstallationTokenResult> task) {
if(task.isSuccessful()) {
String token = task.getResult().getToken();
}
}
});
The Token length obtained at onNewToken() is 163.
The token length obtained at deprecated call is 163 (perfect, but deprtecated).
The token length obtained at FirebaseInstallations is 316.
My firebase API at server side fails to send a notification using the code of 316 length.
Any one knows what i am doing wrong? Or why i get those different length tokens?
Update:
Server side python, retrieves token from database and sends the notification like this. Please note this code is working when token len is 163.
from pyfcm import FCMNotification
push_service = FCMNotification(api_key=" ... ")
push_service.notify_single_device(registration_id=token, data_message=datamessage, time_to_live=1296000)
When trying to send a notification with long token this is the message I get:
{'multicast_ids': [8149274412512777031], 'success': 0, 'failure': 1, 'canonical_ids': 0, 'results': [{'error': 'InvalidRegistration'}], 'topic_message_id': None}
From the documentation for FirebaseInstanceId:
This class is deprecated. Firebase Instance ID has been replaced with
FirebaseInstallations for app instance identifiers and
FirebaseMessaging.getToken() for FCM registration tokens.
Looks like you need FirebaseMessaging.getToken() not FirebaseInstallations.getInstance().getToken(true) as you want a FCM registration token.
These APIs provide different tokens for different purposes.
So in your example it would be:
FirebaseMessaging.getInstance()
.getToken()
.addOnCompleteListener(new OnCompleteListener<String>() {
#Override public void onComplete(#NonNull Task<String> token) {
}
}
);
I am trying to implement notifications and can send a notification using the firebase console to all devices of the app, however I am having problem trying to retrieve the token of a device so i can send the notification to one particular device. I have a service that extends FirebaseMessagingService and includes the method onNewtoken seen below. I have added this service to my manifest and tried running the app but still unable to find the token. Is there something im doing wrong?
#Override
public void onNewToken(#NonNull String s) {
super.onNewToken(s);
Log.d("NEW_TOKEN",s);
}
OnNewtoken will trigger only by following below scenarios
The app deletes Instance ID
The app is restored on a new device
The user uninstalls/reinstalls the app The user clears app data.
you can get user token by below code as well
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(new OnSuccessListener<InstanceIdResult>() {
#Override
public void onSuccess(InstanceIdResult instanceIdResult) {
String token = instanceIdResult.getToken();
// send it to server
}
});
You can get token forcefully like this u can call this code from onCreate() as sometimes when app has already generated to token it does not call onNewToken
FirebaseInstanceId.getInstance().getInstanceId()
.addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() {
#Override
public void onComplete(#NonNull Task<InstanceIdResult> task) {
if (!task.isSuccessful()) {
KLog.w("getInstanceId failed", task.getException());
return;
}
// Get new Instance ID token
if (task.getResult() != null) {
String token = task.getResult().getToken();
}
}
});
i was try to create login session with session key, the session key always generate new key either we do Login/registration, i can retrieve the data from my gson
LoginService loginService = retrofit.create(LoginService.class);
Observable<LoginResponse> obs = loginService.logins(emai,pass,"1", Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID), Build.MODEL, Build.VERSION.RELEASE);
obs.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).
subscribe(new Observer<LoginResponse>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
public void onNext(LoginResponse loginResponse) {
int responses = loginResponse.getCODE();
String texts="";
if(responses == 1)
{
User user = loginResponse.getDATALIST().get(0);
setPrefIsLogin(true);
setPrefSessionUid(user.getSESSIONKEY(),user.getUSERID());
nextActivity();
}
else{
}
}
});
the question is, how to make handler to handle the save session check if there is another login activity with the same account?
You should never assign two accessToken/Session for one user. You will send the same accessToken to the other instance of the new user. Plus side, user won't be able to duplicate his/her work by two accessToken.
If you want to force the other/first one AUTO-LOGOUT, you can use Firebase notification feature to send a notification to that particular deviceID and force it to stop. You can check firebase's tutorial to see how they work.
Another slow procedure is to check before login & everyother server side work to check if there are instance of same user. You will send an error and user end will receive it and show the error accompanying by logging out the user.
Okay so I have an app which on first start takes you through a few welcoming slides, then takes you to a login/register page and then to MainActivity.
I have just implemented FCM and the services generate a token before any of those pages have been seen by the user. How could I make it so that the service runs after I get to MainActivity?
The problem is I'm trying to send the token as soon as it is refreshed to the MySQL DB to the appropriate user account, but since the user hasn't signed in yet, that is null and my message to the server fails. What's a good way to design this? I thought of saving the token in SharedPreferences and sending it to the server after the user has logged in but that creates lots of complications when the token is refreshed at some later point?!
Possible solution:
I'm not sure I completely understand how the 2 services run but say in onTokenRefresh I just save the token into SharedPreferences and in MainActivity I get the value from SP and then I send it to the server. In that case when the token is refreshed the new value will immediately go into SharedPreferences again. But I would still need to check if it's a new value in SP and then reupload it to the server. This is confusing!
Note that you can always retrieve the token with:
FirebaseInstanceID.getInstance().getToken();
This will return null if the token has not yet been generated or the token if it has been generated. In your case it is very likely that the token will be generated by the time the user has signed in. So you should be able to send it to your app server as soon as the user has signed in. If it is not available then you would send it in the onTokenRefresh callback as Chintan Soni mentioned.
Edit
Using the new Firebase SDK (21.0.0) , you will get your token this way :
FirebaseInstallations.getInstance().getToken(false).addOnCompleteListener(new OnCompleteListener<InstallationTokenResult>() {
#Override
public void onComplete(#NonNull Task<InstallationTokenResult> task) {
if(!task.isSuccessful()){
return;
}
// Get new Instance ID token
String token = task.getResult().getToken();
}
});
You better add a listener for more handling on the response .
Yes FCM token is generated automatically. But try to see this in a different angle.
This is how I handled it.
Let FCM generate token as soon as your app starts. OnTokenRefresh will be called and you just save it in your preferences as:
#Override
public void onTokenRefresh() {
// Get updated InstanceID token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Refreshed token: " + refreshedToken);
sendRegistrationToServer(refreshedToken);
}
private void sendRegistrationToServer(String token) {
// Add custom implementation, as needed.
SharedPreferenceUtils.getInstance(this).setValue(getString(R.string.firebase_cloud_messaging_token), token);
// To implement: Only if user is registered, i.e. UserId is available in preference, update token on server.
int userId = SharedPreferenceUtils.getInstance(this).getIntValue(getString(R.string.user_id), 0);
if(userId != 0){
// Implement code to update registration token to server
}
}
Hope you are clear with the way. Ask if you need more clearance on it.
Edit
Using the new Firebase SDK (21.0.0) , you need to override onNewToken() method instead of onTokenRefresh()
#Override
public void onNewToken(#NonNull String s) {
super.onNewToken(s);
sendRegistrationToServer(s);
}
We handled it like this:
Our server create/update the token value against a user id (primary key)
Use 2 SharedPreferences
String - token String
Boolean (updated) - whether token is updated on server or not.
In case of token refresh we update the token string and set the boolean to false.
Later whenever user login each time we check for boolean (updated), if that is false - we attach the current token to his id and send it to server and set updated to true.
December 2020 update : Using the new Firebase SDK (21.0.0) you can get it by overriding onNewToken() method :
#Override
public void onNewToken(#NonNull String s) {
super.onNewToken(s);
sendRegistrationToServer(s);
}
Or by FirebaseInstallations.getInstance() within your scope :
FirebaseInstallations.getInstance().getToken(false).addOnCompleteListener(new OnCompleteListener<InstallationTokenResult>() {
#Override
public void onComplete(#NonNull Task<InstallationTokenResult> task) {
if(!task.isSuccessful()){
return;
}
// Get new Instance ID token
String token = task.getResult().getToken();
}
});
It would be great if you share some knowledge with me! Here is my problem - we have an Android app. and a server. For some of the calls the client needs to send a previously obtained token from the server which is legit for a limited amount of time. If it happens that the token is not valid any more, an error is returned from the sever, a new token needs to be obtained and we need to retry the request.
But how can I handle such behavior with Retrofit? Any thoughts?
Thank you in advance!
How about you try and make a request with a callback, if the callback is a failure and the error message is that the token needs to be obtained, then you do a request to get the new token for example:
connectionInterface.getSomeStuff(object, new Callback<ObjectPOJO>() {
#Override
public void success(ObjectPOJO objectPOJO, Response response) {
//success
}
#Override
public void failure(RetrofitError error) {
if (error.getLocalizedMessage().equals("Token expire error")
connectionInterface.getToken();
}
Then in your retrofit getToken callback, if it is a success you will redo the getSomeStuff method and if it is a failure, you let the user know. For example:
connectionInterface.getToken(token, new Callback<token>() {
#Override
public void success(token token, Response response) {
//success and token has been added
//add the token to your request somehow...
connectionInterface.updateToken(token);
connectionInterface.getSomeStuff();
}
#Override
public void failure(RetrofitError error) {
//error let user know
}
Let me know if you have any other question. I think this will be the easiest way of doing it.