From 1026a296e33cf27b5dffa13b108469699e185027 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 29 Aug 2024 11:47:22 +0200 Subject: [PATCH 1/5] Make LogoutUseCase.logout return the result of the SDK method. --- .../lockscreen/impl/unlock/PinUnlockPresenterTest.kt | 2 +- .../io/element/android/features/logout/api/LogoutUseCase.kt | 5 +++-- .../android/features/logout/impl/DefaultLogoutUseCase.kt | 6 +++--- .../android/features/logout/test/FakeLogoutUseCase.kt | 4 ++-- .../impl/developer/DeveloperSettingsPresenterTest.kt | 2 +- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt index 8299b5a1ab..9aa43ee646 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt @@ -106,7 +106,7 @@ class PinUnlockPresenterTest { @Test fun `present - forgot pin flow`() = runTest { - val signOutLambda = lambdaRecorder { "" } + val signOutLambda = lambdaRecorder { "" } val signOut = FakeLogoutUseCase(signOutLambda) val presenter = createPinUnlockPresenter(this, logoutUseCase = signOut) moleculeFlow(RecompositionMode.Immediate) { diff --git a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/LogoutUseCase.kt b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/LogoutUseCase.kt index a06b7117b1..692074dafb 100644 --- a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/LogoutUseCase.kt +++ b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/LogoutUseCase.kt @@ -23,9 +23,10 @@ interface LogoutUseCase { /** * Log out the current user and then perform any needed cleanup tasks. * @param ignoreSdkError if true, the SDK error will be ignored and the user will be logged out anyway. - * @return the session id of the logged out user. + * @return an optional URL. When the URL is there, it should be presented to the user after logout for + * Relying Party (RP) initiated logout on their account page. */ - suspend fun logout(ignoreSdkError: Boolean): String + suspend fun logout(ignoreSdkError: Boolean): String? interface Factory { fun create(sessionId: String): LogoutUseCase diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/DefaultLogoutUseCase.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/DefaultLogoutUseCase.kt index 8e5f08a87a..ffd8d11406 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/DefaultLogoutUseCase.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/DefaultLogoutUseCase.kt @@ -35,9 +35,9 @@ class DefaultLogoutUseCase @AssistedInject constructor( override fun create(sessionId: String): DefaultLogoutUseCase } - override suspend fun logout(ignoreSdkError: Boolean): String { + override suspend fun logout(ignoreSdkError: Boolean): String? { val matrixClient = matrixClientProvider.getOrRestore(SessionId(sessionId)).getOrThrow() - matrixClient.logout(ignoreSdkError = ignoreSdkError) - return sessionId + val result = matrixClient.logout(ignoreSdkError = ignoreSdkError) + return result } } diff --git a/features/logout/test/src/main/kotlin/io/element/android/features/logout/test/FakeLogoutUseCase.kt b/features/logout/test/src/main/kotlin/io/element/android/features/logout/test/FakeLogoutUseCase.kt index bbc67765d1..452d466032 100644 --- a/features/logout/test/src/main/kotlin/io/element/android/features/logout/test/FakeLogoutUseCase.kt +++ b/features/logout/test/src/main/kotlin/io/element/android/features/logout/test/FakeLogoutUseCase.kt @@ -20,9 +20,9 @@ import io.element.android.features.logout.api.LogoutUseCase import io.element.android.tests.testutils.lambda.lambdaError class FakeLogoutUseCase( - var logoutLambda: (Boolean) -> String = lambdaError() + var logoutLambda: (Boolean) -> String? = { lambdaError() } ) : LogoutUseCase { - override suspend fun logout(ignoreSdkError: Boolean): String { + override suspend fun logout(ignoreSdkError: Boolean): String? { return logoutLambda(ignoreSdkError) } } diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt index 9fb1c8b37f..75c1a19f39 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt @@ -166,7 +166,7 @@ class DeveloperSettingsPresenterTest { @Test fun `present - toggling simplified sliding sync changes the preferences and logs out the user`() = runTest { - val logoutCallRecorder = lambdaRecorder { "" } + val logoutCallRecorder = lambdaRecorder { "" } val logoutUseCase = FakeLogoutUseCase(logoutLambda = logoutCallRecorder) val preferences = InMemoryAppPreferencesStore() val presenter = createDeveloperSettingsPresenter(preferencesStore = preferences, logoutUseCase = logoutUseCase) From a6e2a5d81f1c9db9682ad610b175dc37c70dab00 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 29 Aug 2024 11:49:57 +0200 Subject: [PATCH 2/5] Change PinUnlockState.signOutAction type to AsyncAction. --- .../features/lockscreen/impl/unlock/PinUnlockPresenter.kt | 5 +++-- .../features/lockscreen/impl/unlock/PinUnlockState.kt | 3 ++- .../lockscreen/impl/unlock/PinUnlockStateProvider.kt | 5 +++-- .../android/features/lockscreen/impl/unlock/PinUnlockView.kt | 3 ++- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt index b563b3ac70..c2f3b81873 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt @@ -30,6 +30,7 @@ import io.element.android.features.lockscreen.impl.pin.PinCodeManager import io.element.android.features.lockscreen.impl.pin.model.PinEntry import io.element.android.features.lockscreen.impl.unlock.keypad.PinKeypadModel import io.element.android.features.logout.api.LogoutUseCase +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runCatchingUpdatingState @@ -61,7 +62,7 @@ class PinUnlockPresenter @Inject constructor( mutableStateOf(false) } val signOutAction = remember { - mutableStateOf>(AsyncData.Uninitialized) + mutableStateOf>(AsyncAction.Uninitialized) } var biometricUnlockResult by remember { mutableStateOf(null) @@ -177,7 +178,7 @@ class PinUnlockPresenter @Inject constructor( } } - private fun CoroutineScope.signOut(signOutAction: MutableState>) = launch { + private fun CoroutineScope.signOut(signOutAction: MutableState>) = launch { suspend { logoutUseCase.logout(ignoreSdkError = true) }.runCatchingUpdatingState(signOutAction) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockState.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockState.kt index cfe371eb3f..53a44609ef 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockState.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockState.kt @@ -19,6 +19,7 @@ package io.element.android.features.lockscreen.impl.unlock import io.element.android.features.lockscreen.impl.biometric.BiometricUnlock import io.element.android.features.lockscreen.impl.biometric.BiometricUnlockError import io.element.android.features.lockscreen.impl.pin.model.PinEntry +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData data class PinUnlockState( @@ -26,7 +27,7 @@ data class PinUnlockState( val showWrongPinTitle: Boolean, val remainingAttempts: AsyncData, val showSignOutPrompt: Boolean, - val signOutAction: AsyncData, + val signOutAction: AsyncAction, val showBiometricUnlock: Boolean, val isUnlocked: Boolean, val biometricUnlockResult: BiometricUnlock.AuthenticationResult?, diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockStateProvider.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockStateProvider.kt index 151c3079ee..8cd55f141d 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockStateProvider.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockStateProvider.kt @@ -19,6 +19,7 @@ package io.element.android.features.lockscreen.impl.unlock import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.lockscreen.impl.biometric.BiometricUnlock import io.element.android.features.lockscreen.impl.pin.model.PinEntry +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData open class PinUnlockStateProvider : PreviewParameterProvider { @@ -30,7 +31,7 @@ open class PinUnlockStateProvider : PreviewParameterProvider { aPinUnlockState(showSignOutPrompt = true), aPinUnlockState(showBiometricUnlock = false), aPinUnlockState(showSignOutPrompt = true, remainingAttempts = 0), - aPinUnlockState(signOutAction = AsyncData.Loading()), + aPinUnlockState(signOutAction = AsyncAction.Loading), ) } @@ -42,7 +43,7 @@ fun aPinUnlockState( showBiometricUnlock: Boolean = true, biometricUnlockResult: BiometricUnlock.AuthenticationResult? = null, isUnlocked: Boolean = false, - signOutAction: AsyncData = AsyncData.Uninitialized, + signOutAction: AsyncAction = AsyncAction.Uninitialized, ) = PinUnlockState( pinEntry = AsyncData.Success(pinEntry), showWrongPinTitle = showWrongPinTitle, diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt index 70b6a6c634..89d313df9b 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt @@ -56,6 +56,7 @@ import io.element.android.features.lockscreen.impl.components.PinEntryTextField import io.element.android.features.lockscreen.impl.pin.model.PinDigit import io.element.android.features.lockscreen.impl.pin.model.PinEntry import io.element.android.features.lockscreen.impl.unlock.keypad.PinKeypad +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtom import io.element.android.libraries.designsystem.components.ProgressDialog @@ -91,7 +92,7 @@ fun PinUnlockView( onDismiss = { state.eventSink(PinUnlockEvents.ClearSignOutPrompt) }, ) } - if (state.signOutAction is AsyncData.Loading) { + if (state.signOutAction == AsyncAction.Loading) { ProgressDialog(text = stringResource(id = R.string.screen_signout_in_progress_dialog_content)) } if (state.showBiometricUnlockError) { From cfbd0c4c02951b2a552fcf583d52287d35b2ba22 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 29 Aug 2024 12:16:38 +0200 Subject: [PATCH 3/5] Ensure success logout url is opened in all cases. --- .../lockscreen/impl/unlock/PinUnlockNode.kt | 5 +++ .../lockscreen/impl/unlock/PinUnlockView.kt | 21 +++++++++++-- .../impl/unlock/activity/PinUnlockActivity.kt | 7 ++++- features/logout/api/build.gradle.kts | 1 + .../android/features/logout/api/util/Util.kt | 31 +++++++++++++++++++ .../features/logout/impl/LogoutNode.kt | 10 +----- .../impl/root/PreferencesRootNode.kt | 9 +----- 7 files changed, 64 insertions(+), 20 deletions(-) create mode 100644 features/logout/api/src/main/kotlin/io/element/android/features/logout/api/util/Util.kt diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt index f357869375..bb0c86d5a2 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt @@ -16,9 +16,11 @@ package io.element.android.features.lockscreen.impl.unlock +import android.app.Activity import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin @@ -26,6 +28,7 @@ import com.bumble.appyx.core.plugin.plugins import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode +import io.element.android.features.logout.api.util.onSuccessLogout import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -47,6 +50,7 @@ class PinUnlockNode @AssistedInject constructor( @Composable override fun View(modifier: Modifier) { val state = presenter.present() + val activity = LocalContext.current as Activity LaunchedEffect(state.isUnlocked) { if (state.isUnlocked) { onUnlock() @@ -57,6 +61,7 @@ class PinUnlockNode @AssistedInject constructor( // UnlockNode is only used for in-app unlock, so we can safely set isInAppUnlock to true. // It's set to false in PinUnlockActivity. isInAppUnlock = true, + onSuccessLogout = { onSuccessLogout(activity, it) }, modifier = modifier ) } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt index 89d313df9b..494135e6c2 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt @@ -39,7 +39,9 @@ import androidx.compose.material.icons.filled.Lock import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester @@ -75,6 +77,7 @@ import io.element.android.libraries.ui.strings.CommonStrings fun PinUnlockView( state: PinUnlockState, isInAppUnlock: Boolean, + onSuccessLogout: (logoutUrlResult: String?) -> Unit, modifier: Modifier = Modifier, ) { OnLifecycleEvent { _, event -> @@ -92,9 +95,21 @@ fun PinUnlockView( onDismiss = { state.eventSink(PinUnlockEvents.ClearSignOutPrompt) }, ) } - if (state.signOutAction == AsyncAction.Loading) { - ProgressDialog(text = stringResource(id = R.string.screen_signout_in_progress_dialog_content)) + when (state.signOutAction) { + AsyncAction.Loading -> { + ProgressDialog(text = stringResource(id = R.string.screen_signout_in_progress_dialog_content)) + } + is AsyncAction.Success -> { + val latestOnSuccessLogout by rememberUpdatedState(onSuccessLogout) + LaunchedEffect(state) { + latestOnSuccessLogout(state.signOutAction.data) + } + } + AsyncAction.Confirming, + is AsyncAction.Failure, + AsyncAction.Uninitialized -> Unit } + if (state.showBiometricUnlockError) { ErrorDialog( content = state.biometricUnlockErrorMessage ?: "", @@ -364,6 +379,7 @@ internal fun PinUnlockViewInAppPreview(@PreviewParameter(PinUnlockStateProvider: PinUnlockView( state = state, isInAppUnlock = true, + onSuccessLogout = {}, ) } } @@ -375,6 +391,7 @@ internal fun PinUnlockViewPreview(@PreviewParameter(PinUnlockStateProvider::clas PinUnlockView( state = state, isInAppUnlock = false, + onSuccessLogout = {}, ) } } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/activity/PinUnlockActivity.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/activity/PinUnlockActivity.kt index 7b7b16790f..cbb05909a1 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/activity/PinUnlockActivity.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/activity/PinUnlockActivity.kt @@ -29,6 +29,7 @@ import io.element.android.features.lockscreen.api.LockScreenService import io.element.android.features.lockscreen.impl.unlock.PinUnlockPresenter import io.element.android.features.lockscreen.impl.unlock.PinUnlockView import io.element.android.features.lockscreen.impl.unlock.di.PinUnlockBindings +import io.element.android.features.logout.api.util.onSuccessLogout import io.element.android.libraries.architecture.bindings import io.element.android.libraries.designsystem.theme.ElementThemeApp import io.element.android.libraries.preferences.api.store.AppPreferencesStore @@ -53,7 +54,11 @@ class PinUnlockActivity : AppCompatActivity() { setContent { ElementThemeApp(appPreferencesStore) { val state = presenter.present() - PinUnlockView(state = state, isInAppUnlock = false) + PinUnlockView( + state = state, + isInAppUnlock = false, + onSuccessLogout = { onSuccessLogout(this, it) }, + ) } } lifecycleScope.launch { diff --git a/features/logout/api/build.gradle.kts b/features/logout/api/build.gradle.kts index 85532f5617..655ab29ee5 100644 --- a/features/logout/api/build.gradle.kts +++ b/features/logout/api/build.gradle.kts @@ -22,6 +22,7 @@ android { } dependencies { + implementation(projects.libraries.androidutils) implementation(projects.libraries.architecture) implementation(projects.libraries.designsystem) implementation(projects.libraries.uiStrings) diff --git a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/util/Util.kt b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/util/Util.kt new file mode 100644 index 0000000000..6b59c2d0ef --- /dev/null +++ b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/util/Util.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 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 + * + * https://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 io.element.android.features.logout.api.util + +import android.app.Activity +import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab +import timber.log.Timber + +fun onSuccessLogout( + activity: Activity, + url: String?, +) { + Timber.d("Success logout with result url: $url") + url?.let { + activity.openUrlInChromeCustomTab(null, false, it) + } +} diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt index 4f5bab10b9..c4f1fd9e71 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt @@ -28,9 +28,8 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.features.logout.api.LogoutEntryPoint -import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab +import io.element.android.features.logout.api.util.onSuccessLogout import io.element.android.libraries.di.SessionScope -import timber.log.Timber @ContributesNode(SessionScope::class) class LogoutNode @AssistedInject constructor( @@ -42,13 +41,6 @@ class LogoutNode @AssistedInject constructor( plugins().forEach { it.onChangeRecoveryKeyClick() } } - private fun onSuccessLogout(activity: Activity, url: String?) { - Timber.d("Success logout with result url: $url") - url?.let { - activity.openUrlInChromeCustomTab(null, false, it) - } - } - @Composable override fun View(modifier: Modifier) { val state = presenter.present() diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt index 106f07ecc2..16642029e4 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt @@ -30,10 +30,10 @@ import io.element.android.anvilannotations.ContributesNode import io.element.android.compound.theme.ElementTheme import io.element.android.features.logout.api.direct.DirectLogoutEvents import io.element.android.features.logout.api.direct.DirectLogoutView +import io.element.android.features.logout.api.util.onSuccessLogout import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.user.MatrixUser -import timber.log.Timber @ContributesNode(SessionScope::class) class PreferencesRootNode @AssistedInject constructor( @@ -94,13 +94,6 @@ class PreferencesRootNode @AssistedInject constructor( } } - private fun onSuccessLogout(activity: Activity, url: String?) { - Timber.d("Success (direct) logout with result url: $url") - url?.let { - activity.openUrlInChromeCustomTab(null, false, it) - } - } - private fun onOpenNotificationSettings() { plugins().forEach { it.onOpenNotificationSettings() } } From 460e095e78b4e18232e23cbf8d414b1437f1380f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 29 Aug 2024 12:23:39 +0200 Subject: [PATCH 4/5] Provide dark theme parameter. --- .../android/features/lockscreen/impl/unlock/PinUnlockNode.kt | 4 +++- .../lockscreen/impl/unlock/activity/PinUnlockActivity.kt | 4 +++- .../io/element/android/features/logout/api/util/Util.kt | 3 ++- .../io/element/android/features/logout/impl/LogoutNode.kt | 4 +++- .../features/preferences/impl/root/PreferencesRootNode.kt | 2 +- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt index bb0c86d5a2..e1f87a3c61 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt @@ -28,6 +28,7 @@ import com.bumble.appyx.core.plugin.plugins import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode +import io.element.android.compound.theme.ElementTheme import io.element.android.features.logout.api.util.onSuccessLogout import io.element.android.libraries.di.SessionScope @@ -51,6 +52,7 @@ class PinUnlockNode @AssistedInject constructor( override fun View(modifier: Modifier) { val state = presenter.present() val activity = LocalContext.current as Activity + val isDark = ElementTheme.isLightTheme.not() LaunchedEffect(state.isUnlocked) { if (state.isUnlocked) { onUnlock() @@ -61,7 +63,7 @@ class PinUnlockNode @AssistedInject constructor( // UnlockNode is only used for in-app unlock, so we can safely set isInAppUnlock to true. // It's set to false in PinUnlockActivity. isInAppUnlock = true, - onSuccessLogout = { onSuccessLogout(activity, it) }, + onSuccessLogout = { onSuccessLogout(activity, isDark, it) }, modifier = modifier ) } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/activity/PinUnlockActivity.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/activity/PinUnlockActivity.kt index cbb05909a1..88e8daf9db 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/activity/PinUnlockActivity.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/activity/PinUnlockActivity.kt @@ -24,6 +24,7 @@ import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.lifecycleScope +import io.element.android.compound.theme.ElementTheme import io.element.android.features.lockscreen.api.LockScreenLockState import io.element.android.features.lockscreen.api.LockScreenService import io.element.android.features.lockscreen.impl.unlock.PinUnlockPresenter @@ -54,10 +55,11 @@ class PinUnlockActivity : AppCompatActivity() { setContent { ElementThemeApp(appPreferencesStore) { val state = presenter.present() + val isDark = ElementTheme.isLightTheme.not() PinUnlockView( state = state, isInAppUnlock = false, - onSuccessLogout = { onSuccessLogout(this, it) }, + onSuccessLogout = { onSuccessLogout(this, isDark, it) }, ) } } diff --git a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/util/Util.kt b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/util/Util.kt index 6b59c2d0ef..81d224bdf1 100644 --- a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/util/Util.kt +++ b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/util/Util.kt @@ -22,10 +22,11 @@ import timber.log.Timber fun onSuccessLogout( activity: Activity, + darkTheme: Boolean, url: String?, ) { Timber.d("Success logout with result url: $url") url?.let { - activity.openUrlInChromeCustomTab(null, false, it) + activity.openUrlInChromeCustomTab(null, darkTheme, it) } } diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt index c4f1fd9e71..8c6948b227 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt @@ -27,6 +27,7 @@ import com.bumble.appyx.core.plugin.plugins import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode +import io.element.android.compound.theme.ElementTheme import io.element.android.features.logout.api.LogoutEntryPoint import io.element.android.features.logout.api.util.onSuccessLogout import io.element.android.libraries.di.SessionScope @@ -45,10 +46,11 @@ class LogoutNode @AssistedInject constructor( override fun View(modifier: Modifier) { val state = presenter.present() val activity = LocalContext.current as Activity + val isDark = ElementTheme.isLightTheme.not() LogoutView( state = state, onChangeRecoveryKeyClick = ::onChangeRecoveryKeyClick, - onSuccessLogout = { onSuccessLogout(activity, it) }, + onSuccessLogout = { onSuccessLogout(activity, isDark, it) }, onBackClick = ::navigateUp, modifier = modifier, ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt index 16642029e4..b05031661b 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt @@ -146,7 +146,7 @@ class PreferencesRootNode @AssistedInject constructor( directLogoutView.Render( state = state.directLogoutState, onSuccessLogout = { - onSuccessLogout(activity, it) + onSuccessLogout(activity, isDark, it) } ) } From aae44875bdb0adb4041394d536a8f0fd5b2c2e7c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 29 Aug 2024 12:38:58 +0200 Subject: [PATCH 5/5] Fix test. --- .../lockscreen/impl/unlock/PinUnlockPresenterTest.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt index 9aa43ee646..3bb8998663 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt @@ -29,6 +29,7 @@ import io.element.android.features.lockscreen.impl.pin.model.PinEntry import io.element.android.features.lockscreen.impl.pin.model.assertText import io.element.android.features.lockscreen.impl.unlock.keypad.PinKeypadModel import io.element.android.features.logout.test.FakeLogoutUseCase +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.tests.testutils.lambda.assert import io.element.android.tests.testutils.lambda.lambdaRecorder @@ -51,7 +52,7 @@ class PinUnlockPresenterTest { assertThat(state.showWrongPinTitle).isFalse() assertThat(state.showSignOutPrompt).isFalse() assertThat(state.isUnlocked).isFalse() - assertThat(state.signOutAction).isInstanceOf(AsyncData.Uninitialized::class.java) + assertThat(state.signOutAction).isInstanceOf(AsyncAction.Uninitialized::class.java) assertThat(state.remainingAttempts).isInstanceOf(AsyncData.Uninitialized::class.java) } awaitItem().also { state -> @@ -133,7 +134,7 @@ class PinUnlockPresenterTest { } skipItems(2) awaitItem().also { state -> - assertThat(state.signOutAction).isInstanceOf(AsyncData.Success::class.java) + assertThat(state.signOutAction).isInstanceOf(AsyncAction.Success::class.java) } assert(signOutLambda).isCalledOnce() }