Lock settings : branch the flow

This commit is contained in:
ganfra
2023-10-23 18:11:02 +02:00
parent eb15ca3901
commit afe6d5a870
15 changed files with 100 additions and 15 deletions

View File

@@ -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<LoggedInNode>(buildContext)
}
NavTarget.LockPermanent -> {
lockScreenEntryPoint.createNode(this, buildContext)
lockScreenEntryPoint.nodeBuilder(this, buildContext)
.target(LockScreenEntryPoint.Target.Unlock)
.build()
}
NavTarget.RoomList -> {
val callback = object : RoomListEntryPoint.Callback {

View File

@@ -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
}
}

View File

@@ -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<LockScreenFlowNode>(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<LockScreenFlowNode>(buildContext, listOf(inputs))
}
}
}
}

View File

@@ -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<Plugin>,
) : BackstackNode<LockScreenFlowNode.NavTarget>(
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<SetupPinNode>(buildContext)
}
NavTarget.Settings -> {
createNode<LockScreenSettingsNode>(buildContext)
}
}
}

View File

@@ -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<LockScreenSe
@Composable
override fun present(): LockScreenSettingsState {
var isLockMandatory by remember {
mutableStateOf(false)
}
var isBiometricEnabled by remember {
mutableStateOf(false)
}
@@ -50,7 +48,7 @@ class LockScreenSettingsPresenter @Inject constructor() : Presenter<LockScreenSe
}
return LockScreenSettingsState(
isLockMandatory = isLockMandatory,
isPinMandatory = LockScreenConfig.IS_PIN_MANDATORY,
isBiometricEnabled = isBiometricEnabled,
showRemovePinConfirmation = showRemovePinConfirmation,
eventSink = ::handleEvents

View File

@@ -17,7 +17,7 @@
package io.element.android.features.lockscreen.impl.settings
data class LockScreenSettingsState(
val isLockMandatory: Boolean,
val isPinMandatory: Boolean,
val isBiometricEnabled: Boolean,
val showRemovePinConfirmation: Boolean,
val eventSink: (LockScreenSettingsEvents) -> Unit

View File

@@ -32,7 +32,7 @@ fun aLockScreenSettingsState(
isBiometricEnabled: Boolean = false,
showRemovePinConfirmation: Boolean = false,
) = LockScreenSettingsState(
isLockMandatory = isLockMandatory,
isPinMandatory = isLockMandatory,
isBiometricEnabled = isBiometricEnabled,
showRemovePinConfirmation = showRemovePinConfirmation,
eventSink = {}

View File

@@ -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,

View File

@@ -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)

View File

@@ -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<Plugin>,
private val lockScreenEntryPoint: LockScreenEntryPoint,
) : BackstackNode<PreferencesFlowNode.NavTarget>(
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<EditUserProfileNode>(buildContext, listOf(inputs))
}
NavTarget.LockScreenSettings -> {
lockScreenEntryPoint.nodeBuilder(this, buildContext)
.target(LockScreenEntryPoint.Target.Settings)
.build()
}
}
}

View File

@@ -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<Callback>().forEach { it.onOpenNotificationSettings() }
}
private fun onOpenLockScreenSettings() {
plugins<Callback>().forEach { it.onOpenLockScreenSettings() }
}
private fun onOpenUserProfile(matrixUser: MatrixUser) {
plugins<Callback>().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,
)
}

View File

@@ -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,
)
}

View File

@@ -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?,
)

View File

@@ -30,5 +30,6 @@ fun aPreferencesRootState() = PreferencesRootState(
showAnalyticsSettings = true,
showDeveloperSettings = true,
showNotificationSettings = true,
showLockScreenSettings = true,
snackbarMessage = SnackbarMessage(CommonStrings.common_verification_complete),
)

View File

@@ -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 = {},
)
}