From f1988e3093b29f4effa0eea50fa870232b309366 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 24 Oct 2023 18:13:41 +0200 Subject: [PATCH] PIN : branch the mandatory flow --- .../android/appconfig/LockScreenConfig.kt | 7 +- .../android/appnav/LoggedInFlowNode.kt | 22 +--- features/ftue/impl/build.gradle.kts | 1 + .../features/ftue/impl/FtueFlowNode.kt | 15 ++- .../ftue/impl/state/DefaultFtueState.kt | 27 +++-- ...kScreenState.kt => LockScreenLockState.kt} | 6 +- ...enStateService.kt => LockScreenService.kt} | 14 ++- features/lockscreen/impl/build.gradle.kts | 4 + .../impl/DefaultLockScreenService.kt | 108 ++++++++++++++++++ .../lockscreen/impl/LockScreenFlowNode.kt | 2 - .../impl/pin/DefaultPinCodeManager.kt | 2 + .../state/DefaultLockScreenStateService.kt | 74 ------------ .../impl/unlock/PinUnlockPresenterTest.kt | 4 +- 13 files changed, 176 insertions(+), 110 deletions(-) rename features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/{LockScreenState.kt => LockScreenLockState.kt} (83%) rename features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/{LockScreenStateService.kt => LockScreenService.kt} (67%) create mode 100644 features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenService.kt delete mode 100644 features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/state/DefaultLockScreenStateService.kt diff --git a/appconfig/src/main/kotlin/io/element/android/appconfig/LockScreenConfig.kt b/appconfig/src/main/kotlin/io/element/android/appconfig/LockScreenConfig.kt index 5dd903b484..c809e0c10e 100644 --- a/appconfig/src/main/kotlin/io/element/android/appconfig/LockScreenConfig.kt +++ b/appconfig/src/main/kotlin/io/element/android/appconfig/LockScreenConfig.kt @@ -21,7 +21,7 @@ object LockScreenConfig { /** * Whether the PIN is mandatory or not. */ - const val IS_PIN_MANDATORY: Boolean = false + const val IS_PIN_MANDATORY: Boolean = true /** * Some PINs are blacklisted. @@ -37,4 +37,9 @@ object LockScreenConfig { * Number of attempts before the user is logged out. */ const val MAX_PIN_CODE_ATTEMPTS_NUMBER_BEFORE_LOGOUT = 3 + + /** + * Time period before locking the app once backgrounded. + */ + const val GRACE_PERIOD_IN_MILLIS = 90 * 1000L } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt index 754ab167d1..8669738fc6 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -49,8 +49,8 @@ import io.element.android.features.ftue.api.FtueEntryPoint import io.element.android.features.ftue.api.state.FtueState import io.element.android.features.invitelist.api.InviteListEntryPoint import io.element.android.features.lockscreen.api.LockScreenEntryPoint -import io.element.android.features.lockscreen.api.LockScreenState -import io.element.android.features.lockscreen.api.LockScreenStateService +import io.element.android.features.lockscreen.api.LockScreenLockState +import io.element.android.features.lockscreen.api.LockScreenService import io.element.android.features.networkmonitor.api.NetworkMonitor import io.element.android.features.networkmonitor.api.NetworkStatus import io.element.android.features.preferences.api.PreferencesEntryPoint @@ -94,7 +94,7 @@ class LoggedInFlowNode @AssistedInject constructor( private val notificationDrawerManager: NotificationDrawerManager, private val ftueState: FtueState, private val lockScreenEntryPoint: LockScreenEntryPoint, - private val lockScreenStateService: LockScreenStateService, + private val lockScreenStateService: LockScreenService, private val matrixClient: MatrixClient, snackbarDispatcher: SnackbarDispatcher, ) : BackstackNode( @@ -134,16 +134,6 @@ class LoggedInFlowNode @AssistedInject constructor( backstack.push(NavTarget.Ftue) } }, - onResume = { - coroutineScope.launch { - lockScreenStateService.entersForeground() - } - }, - onPause = { - coroutineScope.launch { - lockScreenStateService.entersBackground() - } - }, onStop = { coroutineScope.launch { //Counterpart startSync is done in observeSyncStateAndNetworkStatus method. @@ -347,9 +337,9 @@ class LoggedInFlowNode @AssistedInject constructor( @Composable override fun View(modifier: Modifier) { Box(modifier = modifier) { - val lockScreenState by lockScreenStateService.state.collectAsState() + val lockScreenState by lockScreenStateService.lockState.collectAsState() when (lockScreenState) { - LockScreenState.Unlocked -> { + LockScreenLockState.Unlocked -> { Children( navModel = backstack, modifier = Modifier, @@ -361,7 +351,7 @@ class LoggedInFlowNode @AssistedInject constructor( PermanentChild(permanentNavModel = permanentNavModel, navTarget = NavTarget.LoggedInPermanent) } } - LockScreenState.Locked -> { + LockScreenLockState.Locked -> { MoveActivityToBackgroundBackHandler() PermanentChild(permanentNavModel = permanentNavModel, navTarget = NavTarget.LockPermanent) } diff --git a/features/ftue/impl/build.gradle.kts b/features/ftue/impl/build.gradle.kts index 42fe8dade5..acdb97b517 100644 --- a/features/ftue/impl/build.gradle.kts +++ b/features/ftue/impl/build.gradle.kts @@ -43,6 +43,7 @@ dependencies { implementation(projects.libraries.testtags) implementation(projects.features.analytics.api) implementation(projects.services.analytics.api) + implementation(projects.features.lockscreen.api) implementation(projects.libraries.permissions.api) implementation(projects.libraries.permissions.noop) implementation(projects.services.toolbox.api) diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/FtueFlowNode.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/FtueFlowNode.kt index ab6bf94a69..a8206ed927 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/FtueFlowNode.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/FtueFlowNode.kt @@ -39,6 +39,7 @@ import io.element.android.features.ftue.impl.notifications.NotificationsOptInNod import io.element.android.features.ftue.impl.state.DefaultFtueState import io.element.android.features.ftue.impl.state.FtueStep import io.element.android.features.ftue.impl.welcome.WelcomeNode +import io.element.android.features.lockscreen.api.LockScreenEntryPoint import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.createNode @@ -60,11 +61,12 @@ class FtueFlowNode @AssistedInject constructor( private val ftueState: DefaultFtueState, private val analyticsEntryPoint: AnalyticsEntryPoint, private val analyticsService: AnalyticsService, + private val lockScreenEntryPoint: LockScreenEntryPoint, ) : BackstackNode( backstack = BackStack( initialElement = NavTarget.Placeholder, savedStateMap = buildContext.savedStateMap, - backPressHandler = NoOpBackstackHandlerStrategy(), + backPressHandler = NoOpBackstackHandlerStrategy(), ), buildContext = buildContext, plugins = plugins, @@ -85,6 +87,9 @@ class FtueFlowNode @AssistedInject constructor( @Parcelize data object AnalyticsOptIn : NavTarget + + @Parcelize + data object LockScreenSetup : NavTarget } private val callback = plugins.filterIsInstance().firstOrNull() @@ -139,6 +144,11 @@ class FtueFlowNode @AssistedInject constructor( NavTarget.AnalyticsOptIn -> { analyticsEntryPoint.createNode(this, buildContext) } + NavTarget.LockScreenSetup -> { + lockScreenEntryPoint.nodeBuilder(this, buildContext) + .target(LockScreenEntryPoint.Target.Setup) + .build() + } } } @@ -156,6 +166,9 @@ class FtueFlowNode @AssistedInject constructor( FtueStep.AnalyticsOptIn -> { backstack.replace(NavTarget.AnalyticsOptIn) } + FtueStep.LockscreenSetup -> { + backstack.newRoot(NavTarget.LockScreenSetup) + } null -> callback?.onFtueFlowFinished() } } diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/state/DefaultFtueState.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/state/DefaultFtueState.kt index 3247d7faf8..07d2372344 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/state/DefaultFtueState.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/state/DefaultFtueState.kt @@ -23,6 +23,7 @@ import com.squareup.anvil.annotations.ContributesBinding import io.element.android.features.ftue.api.state.FtueState import io.element.android.features.ftue.impl.migration.MigrationScreenStore import io.element.android.features.ftue.impl.welcome.state.WelcomeScreenState +import io.element.android.features.lockscreen.api.LockScreenService import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.permissions.api.PermissionStateProvider @@ -44,6 +45,7 @@ class DefaultFtueState @Inject constructor( private val welcomeScreenState: WelcomeScreenState, private val migrationScreenStore: MigrationScreenStore, private val permissionStateProvider: PermissionStateProvider, + private val lockScreenService: LockScreenService, private val matrixClient: MatrixClient, ) : FtueState { @@ -72,10 +74,13 @@ class DefaultFtueState @Inject constructor( FtueStep.MigrationScreen -> if (shouldDisplayWelcomeScreen()) FtueStep.WelcomeScreen else getNextStep( FtueStep.WelcomeScreen ) - FtueStep.WelcomeScreen -> if (shouldAskNotificationPermissions()) FtueStep.NotificationsOptIn else getNextStep( + FtueStep.WelcomeScreen -> if (shouldAskNotificationPermissions()) FtueStep.NotificationsOptIn else getNextStep( FtueStep.NotificationsOptIn ) - FtueStep.NotificationsOptIn -> if (needsAnalyticsOptIn()) FtueStep.AnalyticsOptIn else getNextStep( + FtueStep.NotificationsOptIn -> if (shouldDisplayLockscreenSetup()) FtueStep.LockscreenSetup else getNextStep( + FtueStep.LockscreenSetup + ) + FtueStep.LockscreenSetup -> if (needsAnalyticsOptIn()) FtueStep.AnalyticsOptIn else getNextStep( FtueStep.AnalyticsOptIn ) FtueStep.AnalyticsOptIn -> null @@ -83,11 +88,12 @@ class DefaultFtueState @Inject constructor( private fun isAnyStepIncomplete(): Boolean { return listOf( - shouldDisplayMigrationScreen(), - shouldDisplayWelcomeScreen(), - shouldAskNotificationPermissions(), - needsAnalyticsOptIn() - ).any { it } + { shouldDisplayMigrationScreen() }, + { shouldDisplayWelcomeScreen() }, + { shouldAskNotificationPermissions() }, + { needsAnalyticsOptIn() }, + { shouldDisplayLockscreenSetup() }, + ).any { it -> it() } } private fun shouldDisplayMigrationScreen(): Boolean { @@ -112,6 +118,12 @@ class DefaultFtueState @Inject constructor( } else false } + private fun shouldDisplayLockscreenSetup(): Boolean { + return runBlocking { + lockScreenService.isSetupRequired() + } + } + fun setWelcomeScreenShown() { welcomeScreenState.setWelcomeScreenShown() updateState() @@ -128,4 +140,5 @@ sealed interface FtueStep { data object WelcomeScreen : FtueStep data object NotificationsOptIn : FtueStep data object AnalyticsOptIn : FtueStep + data object LockscreenSetup : FtueStep } diff --git a/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenState.kt b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenLockState.kt similarity index 83% rename from features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenState.kt rename to features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenLockState.kt index d1e53cfdcc..e107729454 100644 --- a/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenState.kt +++ b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenLockState.kt @@ -16,7 +16,7 @@ package io.element.android.features.lockscreen.api -sealed interface LockScreenState { - data object Unlocked : LockScreenState - data object Locked : LockScreenState +sealed interface LockScreenLockState { + data object Unlocked : LockScreenLockState + data object Locked : LockScreenLockState } diff --git a/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenStateService.kt b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenService.kt similarity index 67% rename from features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenStateService.kt rename to features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenService.kt index 299d7bbc15..c6fe444119 100644 --- a/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenStateService.kt +++ b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenService.kt @@ -18,9 +18,15 @@ package io.element.android.features.lockscreen.api import kotlinx.coroutines.flow.StateFlow -interface LockScreenStateService { - val state: StateFlow +interface LockScreenService { + /** + * The current lock state of the app. + */ + val lockState: StateFlow - suspend fun entersForeground() - suspend fun entersBackground() + /** + * Check if setting up the lock screen is required. + * @return true if the lock screen is mandatory and not setup yet, false otherwise. + */ + suspend fun isSetupRequired(): Boolean } diff --git a/features/lockscreen/impl/build.gradle.kts b/features/lockscreen/impl/build.gradle.kts index a3657ccff3..05e9ce20f3 100644 --- a/features/lockscreen/impl/build.gradle.kts +++ b/features/lockscreen/impl/build.gradle.kts @@ -43,6 +43,8 @@ dependencies { implementation(projects.libraries.featureflag.api) implementation(projects.libraries.cryptography.api) implementation(projects.libraries.uiStrings) + implementation(projects.libraries.sessionStorage.api) + implementation(projects.services.appnavstate.api) testImplementation(libs.test.junit) testImplementation(libs.coroutines.test) @@ -54,4 +56,6 @@ dependencies { testImplementation(projects.libraries.cryptography.test) testImplementation(projects.libraries.cryptography.impl) testImplementation(projects.libraries.featureflag.test) + implementation(projects.libraries.sessionStorage.test) + implementation(projects.services.appnavstate.test) } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenService.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenService.kt new file mode 100644 index 0000000000..b949183892 --- /dev/null +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenService.kt @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.lockscreen.impl + +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.appconfig.LockScreenConfig +import io.element.android.features.lockscreen.api.LockScreenLockState +import io.element.android.features.lockscreen.api.LockScreenService +import io.element.android.features.lockscreen.impl.pin.PinCodeManager +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.di.SingleIn +import io.element.android.libraries.featureflag.api.FeatureFlagService +import io.element.android.libraries.featureflag.api.FeatureFlags +import io.element.android.libraries.sessionstorage.api.observer.SessionListener +import io.element.android.libraries.sessionstorage.api.observer.SessionObserver +import io.element.android.services.appnavstate.api.AppForegroundStateService +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +@SingleIn(AppScope::class) +@ContributesBinding(AppScope::class) +class DefaultLockScreenService @Inject constructor( + private val featureFlagService: FeatureFlagService, + private val pinCodeManager: PinCodeManager, + private val coroutineScope: CoroutineScope, + private val sessionObserver: SessionObserver, + private val appForegroundStateService: AppForegroundStateService, +) : LockScreenService { + + private val _lockScreenState = MutableStateFlow(LockScreenLockState.Unlocked) + override val lockState: StateFlow = _lockScreenState + + private var lockJob: Job? = null + + init { + pinCodeManager.addCallback(object : PinCodeManager.Callback { + override fun onPinCodeVerified() { + _lockScreenState.value = LockScreenLockState.Unlocked + } + }) + coroutineScope.lockIfNeeded() + observeAppForegroundState() + observeSessionsState() + } + + /** + * Makes sure to delete the pin code when the session is deleted. + */ + private fun observeSessionsState() { + sessionObserver.addListener(object : SessionListener { + + override suspend fun onSessionCreated(userId: String) = Unit + + override suspend fun onSessionDeleted(userId: String) { + //TODO handle multi session at some point + pinCodeManager.deletePinCode() + } + }) + } + + /** + * Makes sure to lock the app if it goes in background for a certain amount of time. + */ + private fun observeAppForegroundState() { + coroutineScope.launch { + appForegroundStateService.start() + appForegroundStateService.isInForeground.collect { isInForeground -> + if (isInForeground) { + lockJob?.cancel() + } else { + lockJob = lockIfNeeded(delayInMillis = LockScreenConfig.GRACE_PERIOD_IN_MILLIS) + } + } + } + } + + override suspend fun isSetupRequired(): Boolean { + return LockScreenConfig.IS_PIN_MANDATORY + && featureFlagService.isFeatureEnabled(FeatureFlags.PinUnlock) + && !pinCodeManager.isPinCodeAvailable() + } + + private fun CoroutineScope.lockIfNeeded(delayInMillis: Long = 0L) = launch { + if (featureFlagService.isFeatureEnabled(FeatureFlags.PinUnlock) && pinCodeManager.isPinCodeAvailable()) { + delay(delayInMillis) + _lockScreenState.value = LockScreenLockState.Locked + } + } +} diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/LockScreenFlowNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/LockScreenFlowNode.kt index b3de0f81ac..a978b4d649 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/LockScreenFlowNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/LockScreenFlowNode.kt @@ -24,12 +24,10 @@ import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.navmodel.backstack.BackStack -import com.bumble.appyx.navmodel.backstack.operation.push import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.features.lockscreen.impl.settings.LockScreenSettingsFlowNode -import io.element.android.features.lockscreen.impl.settings.LockScreenSettingsNode import io.element.android.features.lockscreen.impl.setup.SetupPinNode import io.element.android.features.lockscreen.impl.unlock.PinUnlockNode import io.element.android.libraries.architecture.BackstackNode diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManager.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManager.kt index d7674c76a6..b7b861199e 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManager.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManager.kt @@ -23,6 +23,8 @@ import io.element.android.libraries.cryptography.api.EncryptionResult import io.element.android.libraries.cryptography.api.SecretKeyProvider import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.SingleIn +import io.element.android.libraries.sessionstorage.api.observer.SessionObserver +import kotlinx.coroutines.launch import java.util.concurrent.CopyOnWriteArrayList import javax.inject.Inject diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/state/DefaultLockScreenStateService.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/state/DefaultLockScreenStateService.kt deleted file mode 100644 index 601ae1b68a..0000000000 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/state/DefaultLockScreenStateService.kt +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2023 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 io.element.android.features.lockscreen.impl.state - -import com.squareup.anvil.annotations.ContributesBinding -import io.element.android.features.lockscreen.api.LockScreenState -import io.element.android.features.lockscreen.api.LockScreenStateService -import io.element.android.features.lockscreen.impl.pin.PinCodeManager -import io.element.android.libraries.di.AppScope -import io.element.android.libraries.di.SingleIn -import io.element.android.libraries.featureflag.api.FeatureFlagService -import io.element.android.libraries.featureflag.api.FeatureFlags -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.launch -import javax.inject.Inject - -//private const val GRACE_PERIOD_IN_MILLIS = 90 * 1000L - -@SingleIn(AppScope::class) -@ContributesBinding(AppScope::class) -class DefaultLockScreenStateService @Inject constructor( - private val featureFlagService: FeatureFlagService, - private val pinCodeManager: PinCodeManager, - private val coroutineScope: CoroutineScope, -) : LockScreenStateService { - - private val _lockScreenState = MutableStateFlow(LockScreenState.Unlocked) - override val state: StateFlow = _lockScreenState - - private var lockJob: Job? = null - - init { - pinCodeManager.addCallback(object : PinCodeManager.Callback { - override fun onPinCodeVerified() { - _lockScreenState.value = LockScreenState.Unlocked - } - }) - coroutineScope.lockIfNeeded() - } - - override suspend fun entersForeground() { - lockJob?.cancel() - } - - override suspend fun entersBackground() = coroutineScope { - lockJob = lockIfNeeded() - } - - private fun CoroutineScope.lockIfNeeded(delayInMillis: Long = 0L) = launch { - if (featureFlagService.isFeatureEnabled(FeatureFlags.PinUnlock) && pinCodeManager.isPinCodeAvailable()) { - delay(delayInMillis) - _lockScreenState.value = LockScreenState.Locked - } - } -} 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 02919edce0..391a51e692 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 @@ -22,7 +22,7 @@ import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.lockscreen.impl.pin.model.assertEmpty import io.element.android.features.lockscreen.impl.pin.model.assertText -import io.element.android.features.lockscreen.impl.state.DefaultLockScreenStateService +import io.element.android.features.lockscreen.impl.DefaultLockScreenService import io.element.android.features.lockscreen.impl.unlock.keypad.PinKeypadModel import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.test.FakeFeatureFlagService @@ -80,7 +80,7 @@ class PinUnlockPresenterTest { val featureFlagService = FakeFeatureFlagService().apply { setFeatureEnabled(FeatureFlags.PinUnlock, true) } - val lockScreenStateService = DefaultLockScreenStateService(featureFlagService) + val lockScreenStateService = DefaultLockScreenService(featureFlagService) return PinUnlockPresenter( lockScreenStateService, scope,