From 284dc8602f79d425bbcbfb85ea474cc606de39fd Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 10 Dec 2019 18:00:51 +0100 Subject: [PATCH] InvalidToken: Regular Signed out screen --- .../api/session/signout/SignOutService.kt | 7 +- .../session/signout/DefaultSignOutService.kt | 8 +- .../internal/session/signout/SignOutTask.kt | 15 +++- .../android/internal/session/sync/SyncTask.kt | 17 +--- vector/src/main/AndroidManifest.xml | 1 + .../riotx/core/platform/VectorBaseActivity.kt | 26 +++++- .../im/vector/riotx/features/MainActivity.kt | 84 ++++++++++++------- .../features/link/LinkHandlerActivity.kt | 2 +- .../riotx/features/main/SignedOutActivity.kt | 52 ++++++++++++ .../main/res/layout/activity_signed_out.xml | 51 +++++++++++ vector/src/main/res/values/strings_riotX.xml | 4 + 11 files changed, 212 insertions(+), 55 deletions(-) create mode 100644 vector/src/main/java/im/vector/riotx/features/main/SignedOutActivity.kt create mode 100644 vector/src/main/res/layout/activity_signed_out.xml diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/signout/SignOutService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/signout/SignOutService.kt index 5a0638fb6e..1ee176c9e4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/signout/SignOutService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/signout/SignOutService.kt @@ -17,6 +17,7 @@ package im.vector.matrix.android.api.session.signout import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.util.Cancelable /** * This interface defines a method to sign out. It's implemented at the session level. @@ -24,7 +25,9 @@ import im.vector.matrix.android.api.MatrixCallback interface SignOutService { /** - * Sign out + * Sign out, and release the session, clear all the session data, including crypto data + * @param sigOutFromHomeserver true if the sign out request has to be done */ - fun signOut(callback: MatrixCallback) + fun signOut(sigOutFromHomeserver: Boolean, + callback: MatrixCallback): Cancelable } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/DefaultSignOutService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/DefaultSignOutService.kt index b48ac2c78a..40a408889d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/DefaultSignOutService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/DefaultSignOutService.kt @@ -18,6 +18,7 @@ package im.vector.matrix.android.internal.session.signout import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.signout.SignOutService +import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith import javax.inject.Inject @@ -25,9 +26,10 @@ import javax.inject.Inject internal class DefaultSignOutService @Inject constructor(private val signOutTask: SignOutTask, private val taskExecutor: TaskExecutor) : SignOutService { - override fun signOut(callback: MatrixCallback) { - signOutTask - .configureWith { + override fun signOut(sigOutFromHomeserver: Boolean, + callback: MatrixCallback): Cancelable { + return signOutTask + .configureWith(SignOutTask.Params(sigOutFromHomeserver)) { this.callback = callback } .executeBy(taskExecutor) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/SignOutTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/SignOutTask.kt index 7bff2936fd..d0ad77c8e4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/SignOutTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/SignOutTask.kt @@ -34,7 +34,11 @@ import timber.log.Timber import java.io.File import javax.inject.Inject -internal interface SignOutTask : Task +internal interface SignOutTask : Task { + data class Params( + val sigOutFromHomeserver: Boolean + ) +} internal class DefaultSignOutTask @Inject constructor(private val context: Context, @UserId private val userId: String, @@ -49,10 +53,13 @@ internal class DefaultSignOutTask @Inject constructor(private val context: Conte @CryptoDatabase private val realmCryptoConfiguration: RealmConfiguration, @UserMd5 private val userMd5: String) : SignOutTask { - override suspend fun execute(params: Unit) { + override suspend fun execute(params: SignOutTask.Params) { Timber.d("SignOut: send request...") - executeRequest { - apiCall = signOutAPI.signOut() + + if (params.sigOutFromHomeserver) { + executeRequest { + apiCall = signOutAPI.signOut() + } } Timber.d("SignOut: release session...") diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTask.kt index d38bf91f28..9f9e67bd2e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTask.kt @@ -17,8 +17,6 @@ package im.vector.matrix.android.internal.session.sync import im.vector.matrix.android.R -import im.vector.matrix.android.api.failure.Failure -import im.vector.matrix.android.api.failure.MatrixError import im.vector.matrix.android.internal.auth.SessionParamsStore import im.vector.matrix.android.internal.di.UserId import im.vector.matrix.android.internal.network.executeRequest @@ -67,19 +65,8 @@ internal class DefaultSyncTask @Inject constructor(private val syncAPI: SyncAPI, initialSyncProgressService.endAll() initialSyncProgressService.startTask(R.string.initial_sync_start_importing_account, 100) } - val syncResponse = try { - executeRequest { - apiCall = syncAPI.sync(requestParams) - } - } catch (throwable: Throwable) { - // Intercept 401 - // TODO Remove? - //if (throwable is Failure.ServerError - // && throwable.error.code == MatrixError.M_UNKNOWN_TOKEN - // && !throwable.error.isSoftLogout) { - // sessionParamsStore.delete(userId) - //} - throw throwable + val syncResponse = executeRequest { + apiCall = syncAPI.sync(requestParams) } syncResponseHandler.handleResponse(syncResponse, token) syncTokenStore.saveToken(syncResponse.nextBatch) diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml index 5f1687c9c9..53df10468e 100644 --- a/vector/src/main/AndroidManifest.xml +++ b/vector/src/main/AndroidManifest.xml @@ -98,6 +98,7 @@ + TODO() + is GlobalError.InvalidToken -> + handleInvalidToken(globalError) is GlobalError.ConsentNotGivenError -> consentNotGivenHelper.displayDialog(globalError.consentUri, activeSessionHolder.getActiveSession().sessionParams.homeServerConnectionConfig.homeServerUri.host ?: "") } } + protected open fun handleInvalidToken(globalError: GlobalError.InvalidToken) { + Timber.w("Invalid token event received") + if(mainActivityStarted) { + return + } + + mainActivityStarted = true + + MainActivity.restartApp(this, + MainActivityArgs( + clearCache = true, + clearCredentials = !globalError.softLogout, + isUserLoggedOut = true, + isSoftLogout = globalError.softLogout + ) + ) + } + override fun onDestroy() { super.onDestroy() unBinder?.unbind() diff --git a/vector/src/main/java/im/vector/riotx/features/MainActivity.kt b/vector/src/main/java/im/vector/riotx/features/MainActivity.kt index 6b9ce66980..d875edba7e 100644 --- a/vector/src/main/java/im/vector/riotx/features/MainActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/MainActivity.kt @@ -23,6 +23,7 @@ import android.os.Parcelable import androidx.appcompat.app.AlertDialog import com.bumptech.glide.Glide import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.failure.GlobalError import im.vector.riotx.R import im.vector.riotx.core.di.ActiveSessionHolder import im.vector.riotx.core.di.ScreenComponent @@ -31,6 +32,7 @@ import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.core.utils.deleteAllFiles import im.vector.riotx.features.home.HomeActivity import im.vector.riotx.features.login.LoginActivity +import im.vector.riotx.features.main.SignedOutActivity import kotlinx.android.parcel.Parcelize import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope @@ -62,7 +64,8 @@ class MainActivity : VectorBaseActivity() { } } - private var args: MainActivityArgs? = null + private lateinit var args: MainActivityArgs + @Inject lateinit var sessionHolder: ActiveSessionHolder @Inject lateinit var errorFormatter: ErrorFormatter @@ -72,44 +75,61 @@ class MainActivity : VectorBaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - args = intent.getParcelableExtra(EXTRA_ARGS) - - val clearCache = args?.clearCache ?: false - val clearCredentials = args?.clearCredentials ?: false + args = parseArgs() // Handle some wanted cleanup - if (clearCache || clearCredentials) { - doCleanUp(clearCache, clearCredentials) + if (args.clearCache || args.clearCredentials) { + doCleanUp() } else { start() } } - private fun doCleanUp(clearCache: Boolean, clearCredentials: Boolean) { + private fun parseArgs(): MainActivityArgs { + val argsFromIntent: MainActivityArgs? = intent.getParcelableExtra(EXTRA_ARGS) + Timber.w("Starting MainActivity with $argsFromIntent") + + return MainActivityArgs( + clearCache = argsFromIntent?.clearCache ?: false, + clearCredentials = argsFromIntent?.clearCredentials ?: false, + isUserLoggedOut = argsFromIntent?.isUserLoggedOut ?: false, + isSoftLogout = argsFromIntent?.isSoftLogout ?: false + ) + } + + private fun doCleanUp() { when { - clearCredentials -> sessionHolder.getActiveSession().signOut(object : MatrixCallback { - override fun onSuccess(data: Unit) { - Timber.w("SIGN_OUT: success, start app") - sessionHolder.clearActiveSession() - doLocalCleanupAndStart() - } + args.clearCredentials -> sessionHolder.getActiveSession().signOut( + !args.isUserLoggedOut, + object : MatrixCallback { + override fun onSuccess(data: Unit) { + Timber.w("SIGN_OUT: success, start app") + sessionHolder.clearActiveSession() + doLocalCleanupAndStart() + } - override fun onFailure(failure: Throwable) { - displayError(failure, clearCache, clearCredentials) - } - }) - clearCache -> sessionHolder.getActiveSession().clearCache(object : MatrixCallback { - override fun onSuccess(data: Unit) { - doLocalCleanupAndStart() - } + override fun onFailure(failure: Throwable) { + displayError(failure) + } + }) + args.clearCache -> sessionHolder.getActiveSession().clearCache( + object : MatrixCallback { + override fun onSuccess(data: Unit) { + doLocalCleanupAndStart() + } - override fun onFailure(failure: Throwable) { - displayError(failure, clearCache, clearCredentials) - } - }) + override fun onFailure(failure: Throwable) { + displayError(failure) + } + }) } } + override fun handleInvalidToken(globalError: GlobalError.InvalidToken) { + // No op here + Timber.w("Ignoring invalid token global error") + } + private fun doLocalCleanupAndStart() { GlobalScope.launch(Dispatchers.Main) { // On UI Thread @@ -126,11 +146,11 @@ class MainActivity : VectorBaseActivity() { start() } - private fun displayError(failure: Throwable, clearCache: Boolean, clearCredentials: Boolean) { + private fun displayError(failure: Throwable) { AlertDialog.Builder(this) .setTitle(R.string.dialog_title_error) .setMessage(errorFormatter.toHumanReadable(failure)) - .setPositiveButton(R.string.global_retry) { _, _ -> doCleanUp(clearCache, clearCredentials) } + .setPositiveButton(R.string.global_retry) { _, _ -> doCleanUp() } .setNegativeButton(R.string.cancel) { _, _ -> start() } .setCancelable(false) .show() @@ -140,7 +160,13 @@ class MainActivity : VectorBaseActivity() { val intent = if (sessionHolder.hasActiveSession()) { HomeActivity.newIntent(this) } else { - LoginActivity.newIntent(this, null) + // Check if we've been signed out + if (args.isUserLoggedOut) { + // TODO Soft logout + SignedOutActivity.newIntent(this) + } else { + LoginActivity.newIntent(this, null) + } } startActivity(intent) finish() diff --git a/vector/src/main/java/im/vector/riotx/features/link/LinkHandlerActivity.kt b/vector/src/main/java/im/vector/riotx/features/link/LinkHandlerActivity.kt index 90ed466695..f1782018a0 100644 --- a/vector/src/main/java/im/vector/riotx/features/link/LinkHandlerActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/link/LinkHandlerActivity.kt @@ -87,7 +87,7 @@ class LinkHandlerActivity : VectorBaseActivity() { .setMessage(R.string.error_user_already_logged_in) .setCancelable(false) .setPositiveButton(R.string.logout) { _, _ -> - sessionHolder.getSafeActiveSession()?.signOut(object : MatrixCallback { + sessionHolder.getSafeActiveSession()?.signOut(true, object : MatrixCallback { override fun onFailure(failure: Throwable) { displayError(failure) } diff --git a/vector/src/main/java/im/vector/riotx/features/main/SignedOutActivity.kt b/vector/src/main/java/im/vector/riotx/features/main/SignedOutActivity.kt new file mode 100644 index 0000000000..02feba4019 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/main/SignedOutActivity.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotx.features.main + +import android.content.Context +import android.content.Intent +import butterknife.OnClick +import im.vector.matrix.android.api.failure.GlobalError +import im.vector.riotx.R +import im.vector.riotx.core.platform.VectorBaseActivity +import im.vector.riotx.features.MainActivity +import im.vector.riotx.features.MainActivityArgs +import timber.log.Timber + +/** + * In this screen, the user is viewing a message informing that he has been logged out + */ +class SignedOutActivity : VectorBaseActivity() { + + override fun getLayoutRes() = R.layout.activity_signed_out + + @OnClick(R.id.signedOutSubmit) + fun submit() { + // All is already cleared when we are here + MainActivity.restartApp(this, MainActivityArgs()) + } + + companion object { + fun newIntent(context: Context): Intent { + return Intent(context, SignedOutActivity::class.java) + } + } + + override fun handleInvalidToken(globalError: GlobalError.InvalidToken) { + // No op here + Timber.w("Ignoring invalid token global error") + } +} diff --git a/vector/src/main/res/layout/activity_signed_out.xml b/vector/src/main/res/layout/activity_signed_out.xml new file mode 100644 index 0000000000..cfe9316677 --- /dev/null +++ b/vector/src/main/res/layout/activity_signed_out.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml index f259a34e44..58ccb02f38 100644 --- a/vector/src/main/res/values/strings_riotX.xml +++ b/vector/src/main/res/values/strings_riotX.xml @@ -140,4 +140,8 @@ Seen by + You’re signed out + It can be due to various reasons:\n\n• You’ve changed your password on another device.\n\n• You have deleted this device from another device.\n\n• The administrator of your server has invalidated your access for security reason. + Sign in again +