chat-sdk / chat-sdk-android

Chat SDK Android - Open Source Mobile Messenger

Home Page:https://chatsdk.co

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Logout problems on production version of app

GDMobileTeam opened this issue · comments

Hi,
I'm having a problem with your library that only occurs on the signed productionRelease of my app.

ChatSDK version used is 5.1.1

The problem is with the Logout function ChatSDK.auth().logout()

Implemented in my app like this:

  if (ChatSDK.auth().isAuthenticated) {
      Log.d("CHAT_SDK_AUTH", "Chat Logout")
      ChatSDK.auth().logout().observeOn(RX.main()).subscribe ({
        Log.d("CHAT_SDK_AUTH", "Logout Success")
        ChatSDK.auth().authenticate(AccountDetails.token(session.firebaseToken)).subscribe({
          Log.d("CHAT_SDK_AUTH", "Auth Success")
        }, {
          Log.d("CHAT_SDK_AUTH", "Auth Error" + it.localizedMessage)
        })
      },
      {
        Log.d("CHAT_SDK_AUTH", "Logout Error" + it.localizedMessage)
      })
  }

Running this bit of code with any debugger (tried debugging the signed release version with debuggable true with no success) passes seamlessly so I'm not able to detect where exactly it gets stuck.
And also it depends on the phone used, on some it takes between 2 - 10 seconds, and on other (like my OnePlus 6T) it takes to over 2 minutes:

01-21 15:05:50.038 12052 12052 D CHAT_SDK_AUTH: Chat Logout
01-21 15:08:03.009 12052 12052 D CHAT_SDK_AUTH: Logout Success

Could you point me in any direction to why that could be happening?
Thank you in advance for your help

Hi @GDMobileTeam can you see if this happens with the standard unmodified version of the app? If you've made customizations, it's not really possible for me to help you since I can't see which changes you've made. However, if this is a bug with the framework, I will do my best to fix it.

Thank you for your response @bensmiley
So I tried building the demo app as a signedRelease version and everything works as intended.
Now I'm using chatSDK version 5.1.1

I'm using the library in the way of just using its functions without the UI and I built my UI around it then to fit in my app

What I've used and how:

The code I pasted in my previous message is the main auth function I use
As I have guest mode in my app I need logout the guest user before I can log him in again, also if the user logs out it goes back to the guest user so it again uses that function.

In my SplashActivity I check if the user is logged in to my app as following:

     if (ChatSDK.auth() != null && ChatSDK.auth().isAuthenticatedThisSession) {
       fetch()
     } else if (ChatSDK.auth() != null && (ChatSDK.auth().isAuthenticated || ChatSDK.auth().isAuthenticating)) {
       ChatSDK.auth().authenticate()
         .observeOn(RX.main())
         .subscribe(
           {
             Log.d("CHAT_SDK_AUTH", "Splash VM Login Session Success")
             fetch()
           }
         ) {
             Log.d("CHAT_SDK_AUTH", "Splash VM Login Error : ${it.localizedMessage}")
             fetch()
           }
     } else {
       val token = authRepository.getFirebaseToken()
       val details = AccountDetails.token(token)
       val d = ChatSDK.auth().authenticate(details).subscribe({
         Log.d("CHAT_SDK_AUTH", "Splash VM Chat Auth Success")
         authRepository.saveChatUserDataForExistingUser()
         fetch()
       }, {
         Log.d("CHAT_SDK_AUTH", "Splash VM Chat Auth Error : ${it.localizedMessage}")
         fetch()
       })
     }

Where this is invoked if the user is already logged into my app only, else the block I described before is invoked to login him as a guest user

The function that saves the users data is saveChatUserDataForExistingUser (this function is used on the previous message also but I thought its irrelevant at that time):

  if (ChatSDK.auth() != null && (ChatSDK.auth().isAuthenticated || ChatSDK.auth().isAuthenticating)) {
      val chatUser = ChatSDK.currentUser()
      chatUser.name = session.user?.name
      chatUser.avatarURL = session.user?.avatar
      chatUser.setMetaValue("name-lowercase", (session.user?.name ?: "").toLowerCase(Locale.ROOT))

      ChatSDK.core().pushUser().subscribe({
        Log.d("CHAT_SDK_USER", "User Successfully updated : ${chatUser.entityID}")
      }, {
        Log.d("CHAT_SDK_USER", "User update error: ${it.message} - for user : ${chatUser.entityID}")
      })
  }

Now I don't know if any of the other things is of importance here but I'll mention it
Next to that I am observing ChatSDK.events().sourceOnMain()
Getting threads with:

    thread = ChatSDK.db().fetchThreadWithEntityID(name)
    if (thread == null) {
      compositeDisposable.add(ChatSDK.publicThread().createPublicThreadWithName(name, name)
        .observeOn(RX.main())
        .subscribe(
          {
            thread = it
            initData()
          },
          {
            Log.d("CHAT_SDK", "Failed to get thread for name: $name")
          }
        ))
    } else {
      initData()
    }

And sending messages with:

   if (ChatSDK.currentUser() != null) {
      val message = ChatSDK.db().createEntity(Message::class.java)
      message.text = messageText
      message.thread = thread
      message.type = MessageType.Text
      message.sender = ChatSDK.currentUser()
      val meta = mutableMapOf<String, Any>()
      if (replyMessageID != "") {
        meta["reply"] = replyMessageID
      }
      message.setMetaValues(meta)
      ChatSDK.thread().sendMessage(message).subscribe()
  }

I know this is a lot to read, but I'm really out of ideas to why this could be happening.
If you could just point me in a direction to what I'm missing here would be much appreciated.

Thank you for your time!

Hi @GDMobileTeam from what I understand, you want a guest mode that can be upgraded to a full login. I can see how trying to do this would lead to issues.

I would recommend taking another approach. When the user is in guest mode, you could use anonymous login. Then, when it is time to upgrade their account, you could like their "real" account with the anonymous account using this:

https://firebase.google.com/docs/auth/android/account-linking

This has a number of benefits:

  1. They can keep all the data they generated while in guest mode
  2. You don't need complex logic to log them out and back in
  3. You don't end up with many abandoned user profiles in Firebase

Overall, this is a much more robust way to go about implementing guest mode.

Hello again @bensmiley, thank you for the recommendation, this has already been done when the user registers, but I did not know there's a possibility of merging multiple accounts ill have to look into that!

But the logic behind my code doesn't change that much, I'll still need to make an anonymous login just after the logout and it's your logout function that takes time. I am able to see the code behind that function but as I am unable to debug the app I would really appreciate it if you, you're much more familiar with the code, would have any idea which part of it could take so long to execute and only in the signed release version.

Either way I hope to hear from you again.
Thank you for your help!

Hi @GDMobileTeam, from the documentation, it doesn't look like you have to log out. You stay signed in as anonymous and start a new sign up flow (this would have to be through Firebase not the Chat SDK). At the end of that process, you would have a credential which you link to the existing user.

1. Sign in the user using any authentication provider or method.

2. Complete the sign-in flow for the new authentication provider up to, but not including, calling one of the FirebaseAuth.signInWith methods. For example, get the user's Google ID token, Facebook access token, or email and password.

3. Get a AuthCredential for the new authentication provider:

Signing out and back in again is what you should try to avoid.

Addressing your issue, however, if logout is taking a long time it's because some of your other code is causing issues. For me logout completes in under 1 second.

I'm happy to provide pointers. But when it comes to custom code, there is very little I can do without fully reviewing and testing that particular code (which would require you to pay for priority support time).

So I would recommend that you either - start deactivating parts of your code to simplify the auth flow and test it to see which part is causing the delay. Or the alternative, use the Firebase linking I suggested above. This could be done without signing the user out and wouldn't require any interaction with Chat SDK. It would just link another account and the next time, the user could sign in with that account.

Hello @bensmiley
You are right, I will see to refactor my code following what you said

I finally figured out what is causing my problem and why its only on my production, I have 2 separate databases for development and for production so different data

My app has quite a lot of users so my chats have reached over 2.5 thousand treads (most public, growing by the day)
On login/logout firebase database references are added/removed i.e. public Observable<DocumentChange> on(Query ref) in RXRealtime.java

So with having 2000 chats it impacts the performance quite drastically...
To counter it I disabled loading all public threads through ChatSDK and manually call those who i need for the user

Thank you again for all your help!

Yes, having a lot of public threads can impact the performance because it means the app has to do a lot of synchronisation. It's best to limit the number because of that and because having thousands of public threads isn't good for the user experience either.