I've integrated the Smooch/Sunshine Conversations SDK into our app.
On the most part, it works. However I've got a bit of an issue in a failure scenario:
User is logged in (both to our service, and smooch)
Our serverside dies for whatever reason, meaning JWT temporarily can't be fetched
Conversation view shows "Cannot connect to server" (as expected)
Our serverside recovers ... valid JWT's returned on request
User tries to trigger a conversation in the app, and they continue to see "Cannot connect to server" indefinitely (even after moving back from the conversation activity and back into it).
The Smooch SDK never recovers from this. The only way to solve it is to kill and restart the app.
I'm using the latest SDK version 7.0.3, and the vanilla ConversationActivity (I've not subclassed this or anything)
I've tried the following:
Re initialising Smooch immediately before moving into the ConversationActivity
Calling login immediately before moving into the ConversationActivity
Any ideas?
Code:
// This is in the Application class, as recommended
fun initialiseSmooch(application: Application) {
GlobalScope.launch {
Log.i(TAG, "Initialising Smooch")
val settings = Settings("INTEGRATION_ID")
settings.authenticationDelegate = getAuthenticationDelegate()
Smooch.init(application, settings, getInitialisationCallback())
}
}
private fun getInitialisationCallback(): (SmoochCallback.Response<InitializationStatus>) -> Unit {
return { response ->
if (response.data === InitializationStatus.SUCCESS) {
Log.i(TAG, "Smooch initialised successfully")
} else {
Log.e(TAG, "Smooch initialization failed: ${response.error}")
}
}
}
/**
* This basically tells the Smooch SDK what to do if the JWT is rejected. Basically it goes
* and fetches a new token from our API.
*/
private fun getAuthenticationDelegate(): AuthenticationDelegate {
return AuthenticationDelegate(function = { authenticationError, authenticationCallback ->
if (authenticationError != null && authenticationError.data != null) {
Log.w(TAG, authenticationError.data)
}
if(AppResources.repository.getUserId() == null){
Log.i(TAG, "Authentication error. User isn't logged in, so shouldn't be logged in to Smooch either.")
logoutSmoochUser()
} else {
Log.i(TAG, "Authentication error. Getting new Smooch token.")
getSmoochToken { token -> authenticationCallback.updateToken(token) }
}
})
}
private fun getSmoochToken(callback: (String) -> Unit) {
// Fetches token from API. If successful, callback is called
// If unsuccessful, callback isn't called. This won't hang forever, it has a timeout.
}
// And to start the conversation
private fun proceedToConversation() {
ConversationActivity.builder().show(this)
}
Please ensure you've implemented authentication delegates to automatically handle cases where JWTs are expired: https://docs.smooch.io/guide/authenticating-users/#expiring-jwts-on-sdks
Once your backend issuing the JWTs comes back up, I'd start recovery attempts with a login() call.
Finally, 10s JWT expiry is very short indeed.
After a successful login, I am unable to get the id token using the getIdToken() method.
This method returns a task and the task never completes. Using basic email id/password authentication using Firebase Auth UI in an android app.
new AuthUI.IdpConfig.EmailBuilder().build()
Here is the library version:
implementation 'com.firebaseui:firebase-ui-auth:6.1.0'
Here is how I am fetching the token:
public String getIdToken(){
String token = null;
Task<GetTokenResult> task = FirebaseAuth.getInstance().getCurrentUser().getIdToken(true);
while(!task.isComplete()){
}
if(task.isSuccessful()){
token = task.getResult().getToken();
}else{
token = "token_gen_failed";
}
return token;
}
The while loop never ends as the task never completes. How to wait for this task?
This code used to work when for firebase-UI-auth:3.2.2. Now I am trying to upgrade this library to
6.1.0.
Any help here please, tried so many things.
All I see in the log is this:
D/FirebaseAuth: Notifying id token listeners about user ( xxxxxxxxxx ).
You can do it like
task.addOnCompleteListener(task -> {
if(task.isSuccessful() & task.getResult() != null){
token = task.getResult().getToken();
}else{
token = "token_gen_failed";
}
//do here whatever you want to do with token;
}
If you want to use this as a separate function then let me know. You have to do some extra task for it.
I'm trying to use Amazon Cognito Sync to remotely store and retrieve information about my user, and for that information to be synced across all devices that that user is logged into.
I'm following the tutorial here which shows how to create Dataset objects and how to use its get(), put(), and synchronize() methods.
After getting that working, I tried following the tutorial here which shows how to register a device for push notifications and subsequently subscribe to a Dataset that you want to keep synced. However, when I call
cognitoSyncManager.subscribeAll()
I get the following exception:
com.amazonaws.mobileconnectors.cognito.exceptions.SubscribeFailedException: Failed to subscribe to dataset
at com.amazonaws.mobileconnectors.cognito.internal.storage.CognitoSyncStorage.subscribeToDataset(CognitoSyncStorage.java:360)
at com.amazonaws.mobileconnectors.cognito.DefaultDataset.subscribe(DefaultDataset.java:604)
at com.amazonaws.mobileconnectors.cognito.CognitoSyncManager.subscribe(CognitoSyncManager.java:332)
at com.amazonaws.mobileconnectors.cognito.CognitoSyncManager.subscribeAll(CognitoSyncManager.java:319)
Caused by: com.amazonaws.services.cognitosync.model.ResourceNotFoundException: Failed to subscribe to dataset USER_INFORMATION, endpointArns do not exist (Service: AmazonCognitoSync; Status Code: 404; Error Code: ResourceNotFoundException; Request ID: 7e681e01-a872-11e7-9e5f-01c7f0419773)
at com.amazonaws.http.AmazonHttpClient.handleErrorResponse(AmazonHttpClient.java:712)
at com.amazonaws.http.AmazonHttpClient.executeHelper(AmazonHttpClient.java:388)
at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:199)
at com.amazonaws.services.cognitosync.AmazonCognitoSyncClient.invoke(AmazonCognitoSyncClient.java:864)
at com.amazonaws.services.cognitosync.AmazonCognitoSyncClient.subscribeToDataset(AmazonCognitoSyncClient.java:663)
at com.amazonaws.mobileconnectors.cognito.internal.storage.CognitoSyncStorage.subscribeToDataset(CognitoSyncStorage.java:357)
at com.amazonaws.mobileconnectors.cognito.DefaultDataset.subscribe(DefaultDataset.java:604)
at com.amazonaws.mobileconnectors.cognito.CognitoSyncManager.subscribe(CognitoSyncManager.java:332)
at com.amazonaws.mobileconnectors.cognito.CognitoSyncManager.subscribeAll(CognitoSyncManager.java:319)
In my Android app, I'm authenticating the user using Google Sign-In, which gives me the token that I need when creating my Cognito Credentials Provider, and I'm using Firebase Cloud Messaging for getting the token needed by Cognito Sync Manager. Here's the snippet of my code which results in the exceptions:
new Thread(new Runnable()
{
#Override public void run()
{
CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(getContext(), Utilities.getString(R.string.aws_cognito_identity_pool_id), Regions.US_EAST_1);
Map<String, String> loginsMap = new HashMap<>();
loginsMap.put("accounts.google.com", GoogleLoginManager.getInstance().getToken());
credentialsProvider.setLogins(loginsMap);
credentialsProvider.refresh();
cognitoId = credentialsProvider.getIdentityId();
isLoggedIn = !cognitoId.equals("");
if(isLoggedIn)
{
CognitoSyncManager cognitoSyncManager = new CognitoSyncManager(getContext(), Regions.US_EAST_1, credentialsProvider);
try
{
cognitoSyncManager.registerDevice("GCM", FirebaseInstanceId.getInstance().getId());
}
catch(RegistrationFailedException exception)
{
Log.e(exception);
}
catch(AmazonClientException exception)
{
Log.e(exception);
}
if(cognitoSyncManager.isDeviceRegistered())
{
try
{
cognitoSyncManager.subscribeAll();
}
catch(SubscribeFailedException exception)
{
Log.e(exception);
}
catch(AmazonClientException exception)
{
Log.e(exception);
}
}
}
}
}).start();
Any idea what I'm doing wrong? Following the tutorials and navigating the developer console have been a bit of an enigma for me, and I feel like there must be some core concept that I'm just not getting.
I finally figured it out. When calling cognitoSyncManager.registerDevice() I was passing FirebaseInstanceId.getInstance().getId() as the token instead of FirebaseInstanceId.getInstance().getToken(). Bleh.
Also, before calling cognitoSyncManager.registerDevice("GCM", FirebaseInstanceId.getInstance().getId()); I now call cognitoSyncManager.unregisterDevice(). Apparently, if you call registerDevice() in a previous run of the app and the registration wasn't actually successful, the CognitoSyncManager class still stores in SharedPreferences that it's registered, and thus will block all future calls to registerDevice() until unregisterDevice() is called.
I've been trying to figure out how to authenticate users for my android app. It is based on a website which already has a developed api, using JWT to authenticate.
I have come against the problem of refreshing tokens. Let's say I want to fetch something from the API and I need the auth token for that. I check my current auth token. If it is expired, I need to get a new one using some sort of refresh token.
However, it seems like almost no matter how I think of trying to implement it, I run into a few problems:
I don't want the UI thread to wait while I get a new token
I would prefer that I don't have to explicitly check whether the token
is there (and then refresh it) before making any API call
I've come up with one solution that solves #1 and at least minimizes the pain of #2. I can have some sort of getToken method. As an example, using JS style promises because they're easier for me to understand:
function getToken() {
return new Promise((resolve) => {
// Check for token, and return if valid.
// Otherwise, go to the server and get a new one
...
resolve(token)
}
}
// When making an API call
getToken().then((token) => {
// Call API
})
I think I can work this out so that the request will never be running on the UI thread, which solves #1, and as far as #2, it's at least bearable.
My question is this: is there a better way to do this? It kind of seems like AccountManager might be able to handle this sort of thing for me, but the documentation for it is subpar at best, so I'm not sure how I would even implement it. If AccountManager can do it and you know of a good tutorial for it, please comment with that.
A way to accomplish this is intercept a 401 status code and refresh token.
If you are using Volley, you can extend Request class and override parseNetworkEror(VolleyError error) method. If need be, schedule a Job which will refresh the token (JobDispatcher) and trigger an event to communicate UI about the change (EventBus).
The following example is using OAuth authentication, but can be easily changed to implement JWT.
#Override
protected VolleyError parseNetworkError(VolleyError volleyError) {
if (getDataAccess().shouldRefreshToken(volleyError)) {
if (!EventBus.getDefault().hasSubscriberForEvent(TokenRefreshedEvent.class)) {
EventBus.getDefault().register(this);
}
CSApplication app = CSApplication.getInstance();
FirebaseJobDispatcher dispatcher = app.getJobDispatcher(app.getApplicationContext());
Job myJob = dispatcher.newJobBuilder()
.setService(JobRefreshToken.class)
.setTag("REFRESH_TOKEN")
.setTrigger(Trigger.NOW)
.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
.setConstraints(Constraint.ON_ANY_NETWORK)
.build();
int result = dispatcher.schedule(myJob);
if (result == FirebaseJobDispatcher.SCHEDULE_RESULT_SUCCESS) {
LogUtils.log(LogUtils.Type.JOB, GsonRequest.class, "Scheduling job refresh token");
} else {
LogUtils.log(LogUtils.Type.JOB, GsonRequest.class, "Error on schedule refresh token");
}
}
return super.parseNetworkError(volleyError);
}
public boolean shouldRefreshToken(VolleyError error) {
boolean shouldRefreshToken = error.networkResponse != null && error.networkResponse.statusCode == 401;
if (shouldRefreshToken) {
Map<String, String> headers = error.networkResponse.headers;
if (headers.containsKey("WWW-Authenticate")) {
String value = headers.get("WWW-Authenticate");
boolean issuerInvalid = value.contains("The issuer is invalid");
shouldRefreshToken = !issuerInvalid;
if (issuerInvalid) {
log(LogUtils.Type.VOLLEY, DataAccess.class, "Issuer do token é inválido");
}
}
}
return shouldRefreshToken;
}
Job Code
getDataAccess().refreshToken(getApplicationContext(), new VolleyCallback<Void>() {
#Override
public void onSuccess(Void aVoid) {
EventBus.getDefault().post(new TokenRefreshedEvent(true));
job.jobFinished(params, false);
log(LogUtils.Type.JOB, JobRefreshToken.class, "Refresh Token job finished");
}
#Override
public void onError(VolleyError error) {
super.onError(error);
EventBus.getDefault().post(new TokenRefreshedEvent(false));
job.jobFinished(params, false);
}
});
return true;
}
What I ended up doing was creating a method getToken which either returns the current token or gets a new one (blocking). With this strategy, I need to make sure that it never gets called from the UI thread. I created a Retrofit2 interceptor which calls getToken. The benefit of this method is that I can just call my Retrofit methods without worrying about the token at all, and it checks for expiration and gets a new one as necessary.
How do I handle situation, when user logs out of my application and I no longer want him to receive notifications to the device.
I tried
FirebaseInstanceId.getInstance().deleteToken(FirebaseInstanceId.getInstance().getId(), FirebaseMessaging.INSTANCE_ID_SCOPE)
But I still receive the notifications to my device's registration_id.
I also made sure that this is the token I should delete:
FirebaseInstanceId.getInstance().getToken(FirebaseInstanceId.getInstance().getId(), FirebaseMessaging.INSTANCE_ID_SCOPE)
or simply FirebaseInstanceId.getInstance().getToken()).
I also tried FirebaseInstanceId.getInstance().deleteInstanceId(), but then the next time I call FirebaseInstanceId.getInstance.getToken I receive null (it works on the second try).
I guess, after deleteInstanceId I could immediately call getToken() again, but it looks like a hack. And also there's this answer which states that it shouldn't be done, but it proposes deleting the token which apparently doesn't work.
So what is the right method to handle this?
Okay. So I managed to do some testing and have concluded the following:
deleteToken() is the counterpart of getToken(String, String), but not for getToken().
It only works if the Sender ID you are passing is a different Sender ID (not the same ID that can be seen in your google-services.json). For example, you want to allow a different Server to send to your app, you call getToken("THEIR_SENDER_ID", "FCM") to give them authorization to send to your app. This will return a different registration token that corresponds only to that specific sender.
In the future, if you chose to remove their authorization to send to your app, you'll then have to make use of deleteToken("THEIR_SENDER_ID", "FCM"). This will invalidate the corresponding token, and when the Sender attempts to send a message, as the intended behavior, they will receive a NotRegistered error.
In order to delete the token for your own Sender, the correct handling is to use deleteInstanceId().
Special mentioning this answer by #Prince, specifically the code sample for helping me with this.
As #MichałK already doing in his post, after calling the deleteInstanceId(), getToken() should be called in order to send a request for a new token. However, you don't have to call it the second time. So long as onTokenRefresh() onNewToken() is implemented, it should automatically trigger providing you the new token.
For short, deleteInstanceId() > getToken() > check onTokenRefresh() onNewToken().
Note: Calling deleteInstanceId() will not only delete the token for your own app. It will delete all topic subscriptions and all other tokens associated with the app instance.
Are you positive you're calling deleteToken() properly? The value for audience should be (also seen from my answer that you linked) is "set to the app server's sender ID". You're passing the getId() value which is not the same as the Sender ID (it contains the app instance id value). Also, how are you sending the message (App Server or Notifications Console)?
getToken() and getToken(String, String) returns different tokens. See my answer here.
I also tried FirebaseInstanceId.getInstance().deleteInstanceId(), but then the next time I call FirebaseInstanceId.getInstance.getToken I receive null (it works on the second try).
It's probably because the first time you're calling the getToken(), it's still being generated. It's just the intended behavior.
I guess, after deleteInstanceId I could immediately call getToken() again, but it looks like a hack.
Not really. It's how you'll get the new generated (provided that it is already generated) token. So I think it's fine.
I did a brief research on what would be the most elegant solution to get back the full control (subscribe and unsubscribe to FCM) as before. Enable and disable the FCM after the user logged in or out.
Step 1. - Prevent auto initialization
Firebase now handle the InstanceID and everything else which need to generate a registration token. First of all you need to prevent auto initialization. Based on the official set-up documentation you need to add these meta-data values to your AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<application>
<!-- FCM: Disable auto-init -->
<meta-data android:name="firebase_messaging_auto_init_enabled"
android:value="false" />
<meta-data android:name="firebase_analytics_collection_enabled"
android:value="false" />
<!-- FCM: Receive token and messages -->
<service android:name=".FCMService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
</application>
Now you disabled the automatic token request process. At the same time you have an option to enable it again at runtime by code.
Step 2. - Implement enableFCM() and disableFCM() functions
If you enable the auto initialization again then you received a new token immediately, so this is a perfect way to implement the enableFCM() method.
All subscribe information assigned to InstanceID, so when you delete it then initiate to unsubscribe all topic. On this way you able to implement disableFCM() method, just turn back off auto-init before you delete it.
public class FCMHandler {
public void enableFCM(){
// Enable FCM via enable Auto-init service which generate new token and receive in FCMService
FirebaseMessaging.getInstance().setAutoInitEnabled(true);
}
public void disableFCM(){
// Disable auto init
FirebaseMessaging.getInstance().setAutoInitEnabled(false);
new Thread(() -> {
try {
// Remove InstanceID initiate to unsubscribe all topic
// TODO: May be a better way to use FirebaseMessaging.getInstance().unsubscribeFromTopic()
FirebaseInstanceId.getInstance().deleteInstanceId();
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
Step 3. - FCMService implementation - token and message receiving
In the last step you need to receive the new token and send direct to your server.
Other hand you'll receive your data message and just do it what you want.
public class FCMService extends FirebaseMessagingService {
#Override
public void onNewToken(String token) {
super.onNewToken(token);
// TODO: send your new token to the server
}
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
String from = remoteMessage.getFrom();
Map data = remoteMessage.getData();
if (data != null) {
// TODO: handle your message and data
sendMessageNotification(message, messageId);
}
}
private void sendMessageNotification(String msg, long messageId) {
// TODO: show notification using NotificationCompat
}
}
I think this solution is clear, simple and transparent. I tested in a production environment and it's works. I hope it was helpful.
I was working on the same problem, when I had done my logout() from my application. But the problem was that after logging out, I was still getting push notifications from Firebase. I tried to delete the Firebase token. But after deleting the token in my logout() method, it is null when I query for it in my login() method. After working 2 days I finally got a solution.
In your logout() method, delete the Firebase token in the background because you can not delete Firebase token from the main thread
new AsyncTask<Void,Void,Void>() {
#Override
protected Void doInBackground(Void... params) {
try
{
FirebaseInstanceId.getInstance().deleteInstanceId();
} catch (IOException e)
{
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
// Call your Activity where you want to land after log out
}
}.execute();
In your login() method, generate the Firebase token again.
new AsyncTask<Void,Void,Void>() {
#Override
protected Void doInBackground(Void... params) {
String token = FirebaseInstanceId.getInstance().getToken();
// Used to get firebase token until its null so it will save you from null pointer exeption
while(token == null) {
token = FirebaseInstanceId.getInstance().getToken();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
}
}.execute();
Developers should never unregister the client app as a mechanism for
logout or for switching between users, for the following reasons:
A registration token isn't associated with a particular logged in user. If the client app unregisters and then re-registers, the app can
receive the same registration token or a different registration token.
Unregistration and re-registration may each take up to five minutes to propagate. During this time messages may be rejected due to the
unregistered state, and messages may go to the wrong user. To make
sure that messages go to the intended user:
The app server can maintain a mapping between the current user and the registration token.
The client app can then check to ensure that messages it receives match the logged in user.
this quote is from a deprecated google documentation
But there is reasons to believe this is still true - even if the documentation above is deprecated.
You can observe this here - check out how they do it in this codelab https://github.com/firebase/functions-samples/blob/master/fcm-notifications/functions/index.js
and here
https://github.com/firebase/friendlychat-web/blob/master/cloud-functions/public/scripts/main.js
Since the getToken() is deprecated, use getInstanceId() to regenerate new token instead. It has same effect.
public static void resetInstanceId() {
new Thread(new Runnable() {
#Override
public void run() {
try {
FirebaseInstanceId.getInstance().deleteInstanceId();
FirebaseInstanceId.getInstance().getInstanceId();
Helper.log(TAG, "InstanceId removed and regenerated.");
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
Use this methods.
This is my solution, and I referred this at here
When you sign-up, use initFirebaseMessage,. and when log-out or delete
use removeFirebaseMessage().
private fun removeFirebaseMessage(){
CoroutineScope(Dispatchers.Default).launch {
FirebaseMessaging.getInstance().isAutoInitEnabled = false
FirebaseInstallations.getInstance().delete()
FirebaseMessaging.getInstance().deleteToken()
}
}
private fun initFirebaseMessage(){
val fcm = FirebaseMessaging.getInstance()
fcm.isAutoInitEnabled = true
fcm.subscribeToTopic("all")
fcm.subscribeToTopic("")
}
Another handy way to clear the firebase token and regenerated a new one using FirebaseMessaging.getInstance()
fun clearFirebaseToken() {
FirebaseMessaging.getInstance().apply {
deleteToken().addOnCompleteListener { it ->
Log.d("TAG++", "firebase token deleted ${it.result}")
token.addOnCompleteListener {
Log.d("TAG++", "firebase token generated ${it.result}")
if (it.result != null) saveTokenGenerated(it.result!!)
}
}
}
}
Just call deleteToken method on a background Thread upon Logout:
https://firebase.google.com/docs/reference/android/com/google/firebase/iid/FirebaseInstanceId.html#public-void-deletetoken-string-senderid,-string-scope
FirebaseInstanceId.getInstance().deleteToken(getString(R.string.gcm_defaultSenderId), "FCM")
The first argument takes the SenderID as it is defined in your FireBaseConsole
It takes a few seconds to update - and after that, you will no longer get FCM notifications.
I know I am late for the party. deleteInstanceId() should be called from the background thread since it's a blocking call. Just check the method deleteInstanceId() in FirebaseInstanceId() class.
#WorkerThread
public void deleteInstanceId() throws IOException {
if (Looper.getMainLooper() == Looper.myLooper()) {
throw new IOException("MAIN_THREAD");
} else {
String var1 = zzh();
this.zza(this.zzal.deleteInstanceId(var1));
this.zzl();
}
}
You can start an IntentService to delete the instance id and the data associated with it.
The firebase.iid package that contains FirebaseInstanceId is now deprecated. Auto-initialization has been migrated from Firebase Instance ID to Firebase Cloud Messaging. Also its behaviour has slighly changed. Before, a call to deleteInstanceId() would automatically generate a new token if auto-initialization was enabled. Now, the new token is only generated on the next app-start or if getToken() is called explicitly.
private suspend fun loginFCM() = withContext(Dispatchers.Default) {
val fcm = FirebaseMessaging.getInstance()
fcm.isAutoInitEnabled = true
fcm.token.await()
}
private suspend fun logoutFCM() = withContext(Dispatchers.Default) {
val fcm = FirebaseMessaging.getInstance()
fcm.isAutoInitEnabled = false // To prevent a new token to be generated automatically in the next app-start (remove if you don't care)
fcm.deleteToken().await()
}
If you want to logout completely from Firebase you can just delete the whole installation afterwards:
private suspend fun logoutFirebase() = withContext(Dispatchers.Default) {
logoutFCM()
val firebase = FirebaseInstallations.getInstance()
firebase.delete().await()
}
To wrap it all up, use background thread to delete the instanceID, the next time you login keep an eye on the Firestore/Realtime DB (if you save your tokens there), they will refresh
public void Logout() {
new Thread(){
#Override
public void run() {
super.run();
try {
FirebaseInstanceId.getInstance().deleteInstanceId();
FirebaseInstanceId.getInstance().getInstanceId();
} catch (final IOException e) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(Flags.this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
}
}.start();
FirebaseMessaging.getInstance().setAutoInitEnabled(false);
FirebaseAuth.getInstance().signOut();
SharedPreferences sharedPreferences = getDefaultSharedPreferences(Flags.this);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.clear();
editor.apply();
startActivity(new Intent(Flags.this, MainActivity.class));
Flags.this.finish();
}
This code below I used it and it helps me, and I used Kotlin coroutine instead of Thread(Runnable{}).start() because it less cost than creating a new thread object
private fun logoutFromFCM() {
GlobalScope.launch(Dispatchers.IO) {
FirebaseInstallations.getInstance().delete()
FirebaseMessaging.getInstance().deleteToken()
FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task ->
if (!task.isSuccessful) {
Log.w(TAG, "Fetching FCM registration token failed", task.exception)
return#OnCompleteListener
}
// Get new FCM registration token
val token = task.result
saveFirebaseToken(token)
Log.w(TAG, "Token Updated - newToken> $token")
})
}
}
For many situations where the notifications requirements are simple, the issue of handling log out can be implemented much more easily. For example, in my case every user is subscribed to only two topics:
Global alerts topic
User specific topic defined as the users email (with replacement of # with - because # is not allowed in topic string)
For such simple scenarios simply unsubscribe from the unwanted topics on log out:
Future<void> signOut() async {
messaging.unsubscribeFromTopic(emailToTopic(_firebaseAuth.currentUser.email));
await _firebaseAuth.signOut();
}
And of course subscribe to topics only on successful log in or sign up:
Future<String> signIn({String email, String password}) async {
try {
await _firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
messaging.subscribeToTopic(emailToTopic(email));
return "Signed in";
} on FirebaseAuthException catch (e) {
return e.message;
}
}