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 2f7eabb582..754ab167d1 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -48,11 +48,11 @@ import io.element.android.features.createroom.api.CreateRoomEntryPoint 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.networkmonitor.api.NetworkMonitor -import io.element.android.features.networkmonitor.api.NetworkStatus 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.networkmonitor.api.NetworkMonitor +import io.element.android.features.networkmonitor.api.NetworkStatus import io.element.android.features.preferences.api.PreferencesEntryPoint import io.element.android.features.roomlist.api.RoomListEntryPoint import io.element.android.features.verifysession.api.VerifySessionEntryPoint @@ -218,7 +218,9 @@ class LoggedInFlowNode @AssistedInject constructor( createNode(buildContext) } NavTarget.LockPermanent -> { - lockScreenEntryPoint.createNode(this, buildContext) + lockScreenEntryPoint.nodeBuilder(this, buildContext) + .target(LockScreenEntryPoint.Target.Unlock) + .build() } NavTarget.RoomList -> { val callback = object : RoomListEntryPoint.Callback { diff --git a/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenEntryPoint.kt b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenEntryPoint.kt index 3c9aceb2c8..fce3706dce 100644 --- a/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenEntryPoint.kt +++ b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenEntryPoint.kt @@ -16,6 +16,22 @@ package io.element.android.features.lockscreen.api -import io.element.android.libraries.architecture.SimpleFeatureEntryPoint +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.libraries.architecture.FeatureEntryPoint -interface LockScreenEntryPoint : SimpleFeatureEntryPoint +interface LockScreenEntryPoint : FeatureEntryPoint { + + fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder + + interface NodeBuilder { + fun target(target: Target): NodeBuilder + fun build(): Node + } + + enum class Target { + Settings, + Setup, + Unlock + } +} diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenEntryPoint.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenEntryPoint.kt index 736be374cd..289f11113a 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenEntryPoint.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenEntryPoint.kt @@ -27,7 +27,26 @@ import javax.inject.Inject @ContributesBinding(AppScope::class) class DefaultLockScreenEntryPoint @Inject constructor() : LockScreenEntryPoint { - override fun createNode(parentNode: Node, buildContext: BuildContext): Node { - return parentNode.createNode(buildContext) + override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): LockScreenEntryPoint.NodeBuilder { + + var innerTarget: LockScreenEntryPoint.Target = LockScreenEntryPoint.Target.Unlock + + return object : LockScreenEntryPoint.NodeBuilder { + override fun target(target: LockScreenEntryPoint.Target): LockScreenEntryPoint.NodeBuilder { + innerTarget = target + return this + } + + override fun build(): Node { + val inputs = LockScreenFlowNode.Inputs( + when (innerTarget) { + LockScreenEntryPoint.Target.Unlock -> LockScreenFlowNode.NavTarget.Unlock + LockScreenEntryPoint.Target.Setup -> LockScreenFlowNode.NavTarget.Setup + LockScreenEntryPoint.Target.Settings -> LockScreenFlowNode.NavTarget.Settings + } + ) + return parentNode.createNode(buildContext, listOf(inputs)) + } + } } } 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 fa3b88e18a..962a133424 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 @@ -27,9 +27,11 @@ import com.bumble.appyx.navmodel.backstack.BackStack import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode +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 +import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.AppScope @@ -41,19 +43,26 @@ class LockScreenFlowNode @AssistedInject constructor( @Assisted plugins: List, ) : BackstackNode( backstack = BackStack( - initialElement = NavTarget.Unlock, + initialElement = plugins.filterIsInstance(Inputs::class.java).first().initialNavTarget, savedStateMap = buildContext.savedStateMap, ), buildContext = buildContext, plugins = plugins, ) { + data class Inputs( + val initialNavTarget: NavTarget = NavTarget.Unlock, + ) : NodeInputs + sealed interface NavTarget : Parcelable { @Parcelize data object Unlock : NavTarget @Parcelize data object Setup : NavTarget + + @Parcelize + data object Settings : NavTarget } override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { @@ -64,6 +73,9 @@ class LockScreenFlowNode @AssistedInject constructor( NavTarget.Setup -> { createNode(buildContext) } + NavTarget.Settings -> { + createNode(buildContext) + } } } 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 2f02bb3982..4ac1d1c863 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 @@ -21,6 +21,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import io.element.android.appconfig.LockScreenConfig import io.element.android.libraries.architecture.Presenter import javax.inject.Inject @@ -29,9 +30,6 @@ class LockScreenSettingsPresenter @Inject constructor() : Presenter Unit diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsStateProvider.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsStateProvider.kt index 3c4ce83452..320ec1aa8b 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsStateProvider.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsStateProvider.kt @@ -32,7 +32,7 @@ fun aLockScreenSettingsState( isBiometricEnabled: Boolean = false, showRemovePinConfirmation: Boolean = false, ) = LockScreenSettingsState( - isLockMandatory = isLockMandatory, + isPinMandatory = isLockMandatory, isBiometricEnabled = isBiometricEnabled, showRemovePinConfirmation = showRemovePinConfirmation, eventSink = {} diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsView.kt index 57c07213ce..55df5632f1 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsView.kt @@ -48,7 +48,7 @@ fun LockScreenSettingsView( } ) PreferenceDivider() - if (!state.isLockMandatory) { + if (!state.isPinMandatory) { PreferenceText( title = stringResource(id = R.string.screen_app_lock_settings_remove_pin), tintColor = ElementTheme.colors.textCriticalPrimary, diff --git a/features/preferences/impl/build.gradle.kts b/features/preferences/impl/build.gradle.kts index 4fcc69ff6b..e4ec04c263 100644 --- a/features/preferences/impl/build.gradle.kts +++ b/features/preferences/impl/build.gradle.kts @@ -50,6 +50,7 @@ dependencies { implementation(projects.libraries.mediaupload.api) implementation(projects.libraries.permissions.api) implementation(projects.features.rageshake.api) + implementation(projects.features.lockscreen.api) implementation(projects.features.analytics.api) implementation(projects.features.ftue.api) implementation(projects.features.logout.api) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt index 6cf0390db2..5144cfeb3f 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt @@ -29,6 +29,7 @@ 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.api.LockScreenEntryPoint import io.element.android.features.preferences.api.PreferencesEntryPoint import io.element.android.features.preferences.impl.about.AboutNode import io.element.android.features.preferences.impl.advanced.AdvancedSettingsNode @@ -50,6 +51,7 @@ import kotlinx.parcelize.Parcelize class PreferencesFlowNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, + private val lockScreenEntryPoint: LockScreenEntryPoint, ) : BackstackNode( backstack = BackStack( initialElement = NavTarget.Root, @@ -81,6 +83,9 @@ class PreferencesFlowNode @AssistedInject constructor( @Parcelize data object NotificationSettings : NavTarget + @Parcelize + data object LockScreenSettings : NavTarget + @Parcelize data class EditDefaultNotificationSetting(val isOneToOne: Boolean) : NavTarget @@ -116,6 +121,10 @@ class PreferencesFlowNode @AssistedInject constructor( backstack.push(NavTarget.NotificationSettings) } + override fun onOpenLockScreenSettings() { + backstack.push(NavTarget.LockScreenSettings) + } + override fun onOpenAdvancedSettings() { backstack.push(NavTarget.AdvancedSettings) } @@ -162,6 +171,11 @@ class PreferencesFlowNode @AssistedInject constructor( val inputs = EditUserProfileNode.Inputs(navTarget.matrixUser) createNode(buildContext, listOf(inputs)) } + NavTarget.LockScreenSettings -> { + lockScreenEntryPoint.nodeBuilder(this, buildContext) + .target(LockScreenEntryPoint.Target.Settings) + .build() + } } } 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 407832627b..7ea1fa2e8a 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 @@ -47,6 +47,7 @@ class PreferencesRootNode @AssistedInject constructor( fun onOpenAbout() fun onOpenDeveloperSettings() fun onOpenNotificationSettings() + fun onOpenLockScreenSettings() fun onOpenAdvancedSettings() fun onOpenUserProfile(matrixUser: MatrixUser) } @@ -93,6 +94,10 @@ class PreferencesRootNode @AssistedInject constructor( plugins().forEach { it.onOpenNotificationSettings() } } + private fun onOpenLockScreenSettings() { + plugins().forEach { it.onOpenLockScreenSettings() } + } + private fun onOpenUserProfile(matrixUser: MatrixUser) { plugins().forEach { it.onOpenUserProfile(matrixUser) } } @@ -115,6 +120,7 @@ class PreferencesRootNode @AssistedInject constructor( onSuccessLogout = { onSuccessLogout(activity, it) }, onManageAccountClicked = { onManageAccountClicked(activity, it, isDark) }, onOpenNotificationSettings = this::onOpenNotificationSettings, + onOpenLockScreenSettings = this::onOpenLockScreenSettings, onOpenUserProfile = this::onOpenUserProfile, ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt index 200785e03d..64132db982 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt @@ -68,6 +68,10 @@ class PreferencesRootPresenter @Inject constructor( LaunchedEffect(Unit) { showNotificationSettings.value = featureFlagService.isFeatureEnabled(FeatureFlags.NotificationSettings) } + val showLockScreenSettings = remember { mutableStateOf(false) } + LaunchedEffect(Unit) { + showLockScreenSettings.value = featureFlagService.isFeatureEnabled(FeatureFlags.PinUnlock) + } // We should display the 'complete verification' option if the current session can be verified val showCompleteVerification by sessionVerificationService.canVerifySessionFlow.collectAsState(false) @@ -95,6 +99,7 @@ class PreferencesRootPresenter @Inject constructor( showAnalyticsSettings = hasAnalyticsProviders, showDeveloperSettings = showDeveloperSettings, showNotificationSettings = showNotificationSettings.value, + showLockScreenSettings = showLockScreenSettings.value, snackbarMessage = snackbarMessage, ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt index f61ea5890d..d6ff4855de 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt @@ -29,6 +29,7 @@ data class PreferencesRootState( val devicesManagementUrl: String?, val showAnalyticsSettings: Boolean, val showDeveloperSettings: Boolean, + val showLockScreenSettings: Boolean, val showNotificationSettings: Boolean, val snackbarMessage: SnackbarMessage?, ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt index 467ca4c6d6..07dd6240bf 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt @@ -30,5 +30,6 @@ fun aPreferencesRootState() = PreferencesRootState( showAnalyticsSettings = true, showDeveloperSettings = true, showNotificationSettings = true, + showLockScreenSettings = true, snackbarMessage = SnackbarMessage(CommonStrings.common_verification_complete), ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt index 2999ddea94..34a4890348 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt @@ -20,6 +20,7 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Lock import androidx.compose.material.icons.outlined.InsertChart import androidx.compose.material.icons.outlined.VerifiedUser import androidx.compose.runtime.Composable @@ -53,6 +54,7 @@ fun PreferencesRootView( onManageAccountClicked: (url: String) -> Unit, onOpenAnalytics: () -> Unit, onOpenRageShake: () -> Unit, + onOpenLockScreenSettings: ()->Unit, onOpenAbout: () -> Unit, onOpenDeveloperSettings: () -> Unit, onOpenAdvancedSettings: () -> Unit, @@ -116,6 +118,13 @@ fun PreferencesRootView( iconResourceId = CommonDrawables.ic_compound_info, onClick = onOpenAbout, ) + if (state.showLockScreenSettings) { + PreferenceText( + title = stringResource(id = CommonStrings.common_screen_lock), + icon = Icons.Default.Lock, + onClick = onOpenLockScreenSettings, + ) + } HorizontalDivider() if (state.devicesManagementUrl != null) { PreferenceText( @@ -183,6 +192,7 @@ private fun ContentToPreview(matrixUser: MatrixUser) { onSuccessLogout = {}, onManageAccountClicked = {}, onOpenNotificationSettings = {}, + onOpenLockScreenSettings = {}, onOpenUserProfile = {}, ) }