diff --git a/app/src/main/kotlin/io/element/android/x/MainActivity.kt b/app/src/main/kotlin/io/element/android/x/MainActivity.kt index 7109743052..7fcae7040e 100644 --- a/app/src/main/kotlin/io/element/android/x/MainActivity.kt +++ b/app/src/main/kotlin/io/element/android/x/MainActivity.kt @@ -32,6 +32,7 @@ import androidx.core.view.WindowCompat import com.bumble.appyx.core.integration.NodeHost import com.bumble.appyx.core.integrationpoint.NodeActivity import com.bumble.appyx.core.plugin.NodeReadyObserver +import io.element.android.features.lockscreen.api.handleSecureFlag import io.element.android.libraries.architecture.bindings import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.designsystem.utils.snackbar.LocalSnackbarDispatcher @@ -53,6 +54,7 @@ class MainActivity : NodeActivity() { installSplashScreen() super.onCreate(savedInstanceState) appBindings = bindings() + appBindings.lockScreenService().handleSecureFlag(this) WindowCompat.setDecorFitsSystemWindows(window, false) setContent { MainContent(appBindings) diff --git a/app/src/main/kotlin/io/element/android/x/di/AppBindings.kt b/app/src/main/kotlin/io/element/android/x/di/AppBindings.kt index 185b3834c5..512b5093d0 100644 --- a/app/src/main/kotlin/io/element/android/x/di/AppBindings.kt +++ b/app/src/main/kotlin/io/element/android/x/di/AppBindings.kt @@ -17,6 +17,7 @@ package io.element.android.x.di import com.squareup.anvil.annotations.ContributesTo +import io.element.android.features.lockscreen.api.LockScreenService import io.element.android.features.rageshake.api.reporter.BugReporter import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.di.AppScope @@ -27,4 +28,5 @@ interface AppBindings { fun snackbarDispatcher(): SnackbarDispatcher fun tracingService(): TracingService fun bugReporter(): BugReporter + fun lockScreenService(): LockScreenService } 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 7d80fd7413..b1abee90bc 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 @@ -120,7 +120,7 @@ class DefaultFtueState @Inject constructor( private fun shouldDisplayLockscreenSetup(): Boolean { return runBlocking { - lockScreenService.isSetupRequired() + lockScreenService.isSetupRequired().first() } } diff --git a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/DefaultFtueStateTests.kt b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/DefaultFtueStateTests.kt index b84bd93d3b..13de50c6b4 100644 --- a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/DefaultFtueStateTests.kt +++ b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/DefaultFtueStateTests.kt @@ -57,6 +57,7 @@ class DefaultFtueStateTests { val analyticsService = FakeAnalyticsService() val migrationScreenStore = InMemoryMigrationScreenStore() val permissionStateProvider = FakePermissionStateProvider(permissionGranted = true) + val lockScreenService = FakeLockScreenService() val coroutineScope = CoroutineScope(coroutineContext + SupervisorJob()) val state = createState( @@ -64,13 +65,15 @@ class DefaultFtueStateTests { welcomeState = welcomeState, analyticsService = analyticsService, migrationScreenStore = migrationScreenStore, - permissionStateProvider = permissionStateProvider + permissionStateProvider = permissionStateProvider, + lockScreenService = lockScreenService, ) welcomeState.setWelcomeScreenShown() analyticsService.setDidAskUserConsent() migrationScreenStore.setMigrationScreenShown(A_SESSION_ID) permissionStateProvider.setPermissionGranted() + lockScreenService.setIsPinSetup(true) state.updateState() assertThat(state.shouldDisplayFlow.value).isFalse() @@ -85,6 +88,7 @@ class DefaultFtueStateTests { val analyticsService = FakeAnalyticsService() val migrationScreenStore = InMemoryMigrationScreenStore() val permissionStateProvider = FakePermissionStateProvider(permissionGranted = false) + val lockScreenService = FakeLockScreenService() val coroutineScope = CoroutineScope(coroutineContext + SupervisorJob()) val state = createState( @@ -92,7 +96,8 @@ class DefaultFtueStateTests { welcomeState = welcomeState, analyticsService = analyticsService, migrationScreenStore = migrationScreenStore, - permissionStateProvider = permissionStateProvider + permissionStateProvider = permissionStateProvider, + lockScreenService = lockScreenService, ) val steps = mutableListOf() @@ -108,7 +113,11 @@ class DefaultFtueStateTests { steps.add(state.getNextStep(steps.lastOrNull())) permissionStateProvider.setPermissionGranted() - // Fourth step, analytics opt in + // Fourth step, notifications opt in + steps.add(state.getNextStep(steps.lastOrNull())) + lockScreenService.setIsPinSetup(true) + + // Fifth step, analytics opt in steps.add(state.getNextStep(steps.lastOrNull())) analyticsService.setDidAskUserConsent() @@ -119,6 +128,7 @@ class DefaultFtueStateTests { FtueStep.MigrationScreen, FtueStep.WelcomeScreen, FtueStep.NotificationsOptIn, + FtueStep.LockscreenSetup, FtueStep.AnalyticsOptIn, null, // Final state ) @@ -133,18 +143,20 @@ class DefaultFtueStateTests { val analyticsService = FakeAnalyticsService() val migrationScreenStore = InMemoryMigrationScreenStore() val permissionStateProvider = FakePermissionStateProvider(permissionGranted = false) - + val lockScreenService = FakeLockScreenService() val state = createState( coroutineScope = coroutineScope, analyticsService = analyticsService, migrationScreenStore = migrationScreenStore, permissionStateProvider = permissionStateProvider, + lockScreenService = lockScreenService, ) - // Skip first 3 steps + // Skip first 4 steps migrationScreenStore.setMigrationScreenShown(A_SESSION_ID) state.setWelcomeScreenShown() permissionStateProvider.setPermissionGranted() + lockScreenService.setIsPinSetup(true) assertThat(state.getNextStep()).isEqualTo(FtueStep.AnalyticsOptIn) @@ -160,18 +172,21 @@ class DefaultFtueStateTests { val coroutineScope = CoroutineScope(coroutineContext + SupervisorJob()) val analyticsService = FakeAnalyticsService() val migrationScreenStore = InMemoryMigrationScreenStore() + val lockScreenService = FakeLockScreenService() val state = createState( sdkIntVersion = Build.VERSION_CODES.M, coroutineScope = coroutineScope, analyticsService = analyticsService, migrationScreenStore = migrationScreenStore, + lockScreenService = lockScreenService, ) migrationScreenStore.setMigrationScreenShown(A_SESSION_ID) assertThat(state.getNextStep()).isEqualTo(FtueStep.WelcomeScreen) - state.setWelcomeScreenShown() + lockScreenService.setIsPinSetup(true) + assertThat(state.getNextStep()).isEqualTo(FtueStep.AnalyticsOptIn) analyticsService.setDidAskUserConsent() diff --git a/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenService.kt b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenService.kt index c6fe444119..e0fe432ede 100644 --- a/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenService.kt +++ b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenService.kt @@ -16,7 +16,14 @@ package io.element.android.features.lockscreen.api +import android.os.Build +import android.view.WindowManager +import androidx.activity.ComponentActivity +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach interface LockScreenService { /** @@ -28,5 +35,33 @@ interface LockScreenService { * 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 + fun isSetupRequired(): Flow + + /** + * Check if pin is setup. + * @return true if the pin is setup, false otherwise. + */ + fun isPinSetup(): Flow +} + +/** + * Makes sure the secure flag is set on the activity if the pin is setup. + * @param activity the activity to set the flag on. + */ +fun LockScreenService.handleSecureFlag(activity: ComponentActivity) { + isPinSetup() + .onEach { isPinSetup -> + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + activity.setRecentsScreenshotEnabled(!isPinSetup) + } else { + if (isPinSetup) { + activity.window.setFlags( + WindowManager.LayoutParams.FLAG_SECURE, + WindowManager.LayoutParams.FLAG_SECURE + ) + } else { + activity.window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE) + } + } + }.launchIn(activity.lifecycleScope) } 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 index f4cc699907..96cf7a655b 100644 --- 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 @@ -35,8 +35,12 @@ import io.element.android.services.appnavstate.api.AppForegroundStateService import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import javax.inject.Inject import kotlin.time.Duration @@ -113,14 +117,23 @@ class DefaultLockScreenService @Inject constructor( } } - override suspend fun isSetupRequired(): Boolean { - return lockScreenConfig.isPinMandatory - && featureFlagService.isFeatureEnabled(FeatureFlags.PinUnlock) - && !pinCodeManager.isPinCodeAvailable() + override fun isPinSetup(): Flow { + return combine( + featureFlagService.isFeatureEnabledFlow(FeatureFlags.PinUnlock), + pinCodeManager.hasPinCode() + ) { isEnabled, hasPinCode -> + isEnabled && hasPinCode + } + } + + override fun isSetupRequired(): Flow { + return isPinSetup().map { isPinSetup -> + !isPinSetup && lockScreenConfig.isPinMandatory + } } private fun CoroutineScope.lockIfNeeded(gracePeriod: Duration = Duration.ZERO) = launch { - if (featureFlagService.isFeatureEnabled(FeatureFlags.PinUnlock) && pinCodeManager.isPinCodeAvailable()) { + if (isPinSetup().first()) { delay(gracePeriod) _lockScreenState.value = LockScreenLockState.Locked } 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 a256562c43..c08513c537 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,7 @@ import io.element.android.libraries.cryptography.api.EncryptionResult import io.element.android.libraries.cryptography.api.SecretKeyRepository import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.SingleIn +import kotlinx.coroutines.flow.Flow import java.util.concurrent.CopyOnWriteArrayList import javax.inject.Inject @@ -46,7 +47,7 @@ class DefaultPinCodeManager @Inject constructor( callbacks.remove(callback) } - override suspend fun isPinCodeAvailable(): Boolean { + override fun hasPinCode(): Flow { return lockScreenStore.hasPinCode() } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/PinCodeManager.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/PinCodeManager.kt index 21e7281dc8..ae3519759f 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/PinCodeManager.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/PinCodeManager.kt @@ -16,6 +16,8 @@ package io.element.android.features.lockscreen.impl.pin +import kotlinx.coroutines.flow.Flow + /** * This interface is the main interface to manage the pin code. * Implementation should take care of encrypting the pin code and storing it. @@ -55,7 +57,7 @@ interface PinCodeManager { /** * @return true if a pin code is available. */ - suspend fun isPinCodeAvailable(): Boolean + fun hasPinCode(): Flow /** * @return the size of the saved pin code. diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsFlowNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsFlowNode.kt index 82f5cafbe7..975d049cdb 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsFlowNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsFlowNode.kt @@ -42,6 +42,7 @@ import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.SessionScope +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize @@ -90,9 +91,11 @@ class LockScreenSettingsFlowNode @AssistedInject constructor( } } - init { + override fun onBuilt() { + super.onBuilt() lifecycleScope.launch { - if (pinCodeManager.isPinCodeAvailable()) { + val hasPinCode = pinCodeManager.hasPinCode().first() + if (hasPinCode) { backstack.newRoot(NavTarget.Unlock) } else { backstack.newRoot(NavTarget.Setup) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsPresenter.kt index 2c086ea92a..b60b472f72 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsPresenter.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsPresenter.kt @@ -17,11 +17,10 @@ package io.element.android.features.lockscreen.impl.settings import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import io.element.android.appconfig.LockScreenConfig @@ -43,23 +42,15 @@ class LockScreenSettingsPresenter @Inject constructor( @Composable override fun present(): LockScreenSettingsState { - var triggerComputation by remember { - mutableIntStateOf(0) - } - var showRemovePinOption by remember { - mutableStateOf(false) - } - var showToggleBiometric by remember { - mutableStateOf(false) + val showRemovePinOption by produceState(initialValue = false) { + pinCodeManager.hasPinCode().collect { hasPinCode -> + value = !lockScreenConfig.isPinMandatory && hasPinCode + } } val isBiometricEnabled by lockScreenStore.isBiometricUnlockAllowed().collectAsState(initial = false) var showRemovePinConfirmation by remember { mutableStateOf(false) } - LaunchedEffect(triggerComputation) { - showRemovePinOption = !lockScreenConfig.isPinMandatory && pinCodeManager.isPinCodeAvailable() - showToggleBiometric = biometricUnlockManager.isDeviceSecured - } fun handleEvents(event: LockScreenSettingsEvents) { when (event) { @@ -69,7 +60,6 @@ class LockScreenSettingsPresenter @Inject constructor( if (showRemovePinConfirmation) { showRemovePinConfirmation = false pinCodeManager.deletePinCode() - triggerComputation++ } } } @@ -86,7 +76,7 @@ class LockScreenSettingsPresenter @Inject constructor( showRemovePinOption = showRemovePinOption, isBiometricEnabled = isBiometricEnabled, showRemovePinConfirmation = showRemovePinConfirmation, - showToggleBiometric = showToggleBiometric, + showToggleBiometric = biometricUnlockManager.isDeviceSecured, eventSink = ::handleEvents ) } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/storage/EncryptedPinCodeStorage.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/storage/EncryptedPinCodeStorage.kt index 7f19346cec..d2a5ba4204 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/storage/EncryptedPinCodeStorage.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/storage/EncryptedPinCodeStorage.kt @@ -16,6 +16,8 @@ package io.element.android.features.lockscreen.impl.storage +import kotlinx.coroutines.flow.Flow + /** * Should be implemented by any class that provides access to the encrypted PIN code. * All methods are suspending in case there are async IO operations involved. @@ -39,5 +41,6 @@ interface EncryptedPinCodeStorage { /** * Returns whether the PIN code is stored or not. */ - suspend fun hasPinCode(): Boolean + fun hasPinCode(): Flow + } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/storage/PreferencesLockScreenStore.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/storage/PreferencesLockScreenStore.kt index 4b01b6e62a..cfdf081986 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/storage/PreferencesLockScreenStore.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/storage/PreferencesLockScreenStore.kt @@ -85,10 +85,10 @@ class PreferencesLockScreenStore @Inject constructor( } } - override suspend fun hasPinCode(): Boolean { + override fun hasPinCode(): Flow { return context.dataStore.data.map { preferences -> preferences[pinCodeKey] != null - }.first() + } } override fun isBiometricUnlockAllowed(): Flow { diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManagerTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManagerTest.kt index 3c65620084..d3af36d820 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManagerTest.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManagerTest.kt @@ -16,6 +16,7 @@ package io.element.android.features.lockscreen.impl.pin +import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.lockscreen.impl.pin.storage.InMemoryLockScreenStore import io.element.android.libraries.cryptography.impl.AESEncryptionDecryptionService @@ -32,10 +33,13 @@ class DefaultPinCodeManagerTest { @Test fun `given a pin code when create and delete assert no pin code left`() = runTest { - pinCodeManager.createPinCode("1234") - assertThat(pinCodeManager.isPinCodeAvailable()).isTrue() - pinCodeManager.deletePinCode() - assertThat(pinCodeManager.isPinCodeAvailable()).isFalse() + pinCodeManager.hasPinCode().test { + assertThat(awaitItem()).isFalse() + pinCodeManager.createPinCode("1234") + assertThat(awaitItem()).isTrue() + pinCodeManager.deletePinCode() + assertThat(awaitItem()).isFalse() + } } @Test diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/storage/InMemoryLockScreenStore.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/storage/InMemoryLockScreenStore.kt index 5d1af46ae5..8f622acfe4 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/storage/InMemoryLockScreenStore.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/storage/InMemoryLockScreenStore.kt @@ -24,7 +24,12 @@ private const val DEFAULT_REMAINING_ATTEMPTS = 3 class InMemoryLockScreenStore : LockScreenStore { + private val hasPinCode = MutableStateFlow(false) private var pinCode: String? = null + set(value) { + field = value + hasPinCode.value = value != null + } private var remainingAttempts: Int = DEFAULT_REMAINING_ATTEMPTS private var isBiometricUnlockAllowed = MutableStateFlow(false) @@ -52,8 +57,8 @@ class InMemoryLockScreenStore : LockScreenStore { pinCode = null } - override suspend fun hasPinCode(): Boolean { - return pinCode != null + override fun hasPinCode(): Flow { + return hasPinCode } override fun isBiometricUnlockAllowed(): Flow { diff --git a/features/lockscreen/test/src/main/kotlin/io/element/android/features/lockscreen/test/FakeLockScreenService.kt b/features/lockscreen/test/src/main/kotlin/io/element/android/features/lockscreen/test/FakeLockScreenService.kt index 012c8e9a5c..11dc4ee20f 100644 --- a/features/lockscreen/test/src/main/kotlin/io/element/android/features/lockscreen/test/FakeLockScreenService.kt +++ b/features/lockscreen/test/src/main/kotlin/io/element/android/features/lockscreen/test/FakeLockScreenService.kt @@ -18,21 +18,27 @@ package io.element.android.features.lockscreen.test import io.element.android.features.lockscreen.api.LockScreenLockState import io.element.android.features.lockscreen.api.LockScreenService +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map class FakeLockScreenService : LockScreenService { - private var isSetupRequired: Boolean = false + private var isPinSetup = MutableStateFlow(false) private val _lockState: MutableStateFlow = MutableStateFlow(LockScreenLockState.Locked) override val lockState: StateFlow = _lockState - override suspend fun isSetupRequired(): Boolean { - return isSetupRequired + override fun isSetupRequired(): Flow { + return isPinSetup.map { !it } } - fun setIsSetupRequired(isSetupRequired: Boolean) { - this.isSetupRequired = isSetupRequired + fun setIsPinSetup(isPinSetup: Boolean) { + this.isPinSetup.value = isPinSetup + } + + override fun isPinSetup(): Flow { + return isPinSetup } fun setLockState(lockState: LockScreenLockState) {