Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions app/src/main/java/to/bitkit/ui/ContentView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import androidx.navigation.NavController
import androidx.navigation.NavDeepLink
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.NavOptions
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
Expand Down Expand Up @@ -80,6 +81,7 @@ import to.bitkit.ui.settings.SecuritySettingsScreen
import to.bitkit.ui.settings.SettingsScreen
import to.bitkit.ui.settings.backups.BackupWalletScreen
import to.bitkit.ui.settings.backups.RestoreWalletScreen
import to.bitkit.ui.settings.pin.DisablePinScreen
import to.bitkit.ui.utils.screenScaleIn
import to.bitkit.ui.utils.screenScaleOut
import to.bitkit.ui.utils.screenSlideIn
Expand Down Expand Up @@ -231,6 +233,7 @@ fun ContentView(
nodeState(walletViewModel, navController)
generalSettings(navController)
securitySettings(navController)
disablePin(navController)
defaultUnitSettings(currencyViewModel, navController)
localCurrencySettings(currencyViewModel, navController)
backupSettings(navController)
Expand Down Expand Up @@ -475,14 +478,19 @@ private fun NavGraphBuilder.generalSettings(navController: NavHostController) {
}

private fun NavGraphBuilder.securitySettings(navController: NavHostController) {
composableWithDefaultTransitions<Routes.SecuritySettings> { backStackEntry ->
composableWithDefaultTransitions<Routes.SecuritySettings> {
SecuritySettingsScreen(
navController = navController,
savedStateHandle = backStackEntry.savedStateHandle,
)
}
}

private fun NavGraphBuilder.disablePin(navController: NavHostController) {
composableWithDefaultTransitions<Routes.DisablePin> {
DisablePinScreen(navController)
}
}

private fun NavGraphBuilder.defaultUnitSettings(
currencyViewModel: CurrencyViewModel,
navController: NavHostController,
Expand Down Expand Up @@ -688,18 +696,24 @@ fun NavController.navigateToSecuritySettings() = navigate(
route = Routes.SecuritySettings,
)

fun NavController.navigateToDisablePin() = navigate(
route = Routes.DisablePin,
)

fun NavController.navigateToAuthCheck(
showLogoOnPin: Boolean = false,
requirePin: Boolean = false,
requireBiometrics: Boolean = false,
onSuccessActionId: String,
navOptions: NavOptions? = null,
) = navigate(
route = Routes.AuthCheck(
showLogoOnPin = showLogoOnPin,
requirePin = requirePin,
requireBiometrics = requireBiometrics,
onSuccessActionId = onSuccessActionId,
),
navOptions = navOptions,
)

fun NavController.navigateToDefaultUnitSettings() = navigate(
Expand Down Expand Up @@ -795,6 +809,9 @@ object Routes {
@Serializable
data object SecuritySettings

@Serializable
data object DisablePin

@Serializable
data class AuthCheck(
val showLogoOnPin: Boolean = false,
Expand Down
31 changes: 22 additions & 9 deletions app/src/main/java/to/bitkit/ui/components/AuthCheckScreen.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package to.bitkit.ui.components

import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavController
import to.bitkit.ui.Routes
import to.bitkit.ui.appViewModel
Expand All @@ -12,26 +14,37 @@ fun AuthCheckScreen(
) {
val app = appViewModel ?: return

val isPinOnLaunchEnabled by app.isPinOnLaunchEnabled.collectAsStateWithLifecycle()
val isBiometricEnabled by app.isBiometricEnabled.collectAsStateWithLifecycle()

AuthCheckView(
showLogoOnPin = route.showLogoOnPin,
appViewModel = app,
requireBiometrics = route.requireBiometrics,
requirePin = route.requirePin,
onSuccess = {
navController.previousBackStackEntry
?.savedStateHandle
?.set(AuthCheckAction.KEY, route.onSuccessActionId)
when (route.onSuccessActionId) {
AuthCheckAction.TOGGLE_BIOMETRICS -> {
app.setIsBiometricEnabled(!isBiometricEnabled)
}

AuthCheckAction.TOGGLE_PIN_ON_LAUNCH -> {
app.setIsPinOnLaunchEnabled(!isPinOnLaunchEnabled)
}

AuthCheckAction.DISABLE_PIN -> {
app.removePin()
}
}

navController.popBackStack()
},
onBack = { navController.popBackStack() },
)
}

object AuthCheckAction {
const val KEY = "auth_check_action_key"

object Id {
const val TOGGLE_PIN_ON_LAUNCH = "toggle_pin_on_launch"
const val TOGGLE_BIOMETRICS = "toggle_biometrics"
}
const val TOGGLE_PIN_ON_LAUNCH = "toggle_pin_on_launch"
const val TOGGLE_BIOMETRICS = "toggle_biometrics"
const val DISABLE_PIN = "disable_pin"
}
15 changes: 13 additions & 2 deletions app/src/main/java/to/bitkit/ui/components/AuthCheckView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import to.bitkit.R
import to.bitkit.env.Env
import to.bitkit.ui.scaffold.AppTopBar
import to.bitkit.ui.theme.AppThemeSurface
import to.bitkit.ui.theme.Colors
import to.bitkit.ui.utils.rememberBiometricAuthSupported
Expand All @@ -41,6 +42,7 @@ fun AuthCheckView(
requireBiometrics: Boolean = false,
requirePin: Boolean = false,
onSuccess: (() -> Unit)? = null,
onBack: (() -> Unit)? = null,
) {
val isBiometricsEnabled by appViewModel.isBiometricEnabled.collectAsStateWithLifecycle()
val attemptsRemaining by appViewModel.pinAttemptsRemaining.collectAsStateWithLifecycle()
Expand All @@ -54,6 +56,7 @@ fun AuthCheckView(
requirePin = requirePin,
validatePin = appViewModel::validatePin,
onSuccess = onSuccess,
onBack = onBack,
)
}

Expand All @@ -67,6 +70,7 @@ private fun AuthCheckViewContent(
requirePin: Boolean = false,
validatePin: (String) -> Boolean,
onSuccess: (() -> Unit)? = null,
onBack: (() -> Unit)?,
) {
var showBio by rememberSaveable { mutableStateOf(isBiometricsEnabled) }

Expand All @@ -89,10 +93,11 @@ private fun AuthCheckViewContent(
PinPad(
showLogo = showLogoOnPin,
validatePin = validatePin,
onSuccess = onSuccess,
attemptsRemaining = attemptsRemaining,
allowBiometrics = isBiometricsEnabled && isBiometrySupported && !requirePin,
onShowBiometrics = { showBio = true },
onSuccess = onSuccess,
onBack = onBack,
)
}
}
Expand All @@ -102,10 +107,11 @@ private fun AuthCheckViewContent(
private fun PinPad(
showLogo: Boolean = false,
validatePin: (String) -> Boolean,
onSuccess: (() -> Unit)?,
attemptsRemaining: Int,
allowBiometrics: Boolean,
onShowBiometrics: () -> Unit,
onSuccess: (() -> Unit)?,
onBack: (() -> Unit)? = null,
) {
var pin by remember { mutableStateOf("") }
val isLastAttempt = attemptsRemaining == 1
Expand All @@ -122,6 +128,7 @@ private fun PinPad(
Column(
horizontalAlignment = Alignment.CenterHorizontally,
) {
AppTopBar(titleText = " ", onBackClick = onBack)
Box(
contentAlignment = Alignment.BottomCenter,
modifier = Modifier.weight(1f)
Expand Down Expand Up @@ -200,6 +207,7 @@ private fun PreviewBio() {
AppThemeSurface {
AuthCheckViewContent(
onSuccess = {},
onBack = {},
isBiometricsEnabled = true,
isBiometrySupported = true,
showLogoOnPin = true,
Expand All @@ -215,6 +223,7 @@ private fun PreviewPin() {
AppThemeSurface {
AuthCheckViewContent(
onSuccess = {},
onBack = {},
isBiometricsEnabled = false,
isBiometrySupported = true,
showLogoOnPin = true,
Expand All @@ -230,6 +239,7 @@ private fun PreviewPinAttempts() {
AppThemeSurface {
AuthCheckViewContent(
onSuccess = {},
onBack = null,
isBiometricsEnabled = false,
isBiometrySupported = true,
showLogoOnPin = false,
Expand All @@ -245,6 +255,7 @@ private fun PreviewPinAttemptLast() {
AppThemeSurface {
AuthCheckViewContent(
onSuccess = {},
onBack = {},
isBiometricsEnabled = false,
isBiometrySupported = true,
showLogoOnPin = true,
Expand Down
15 changes: 10 additions & 5 deletions app/src/main/java/to/bitkit/ui/scaffold/AppTopBar.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,16 @@ import to.bitkit.ui.components.Title
@OptIn(ExperimentalMaterial3Api::class)
fun AppTopBar(
titleText: String,
onBackClick: () -> Unit,
onBackClick: (() -> Unit)?,
icon: Painter? = null,
navigationIcon: @Composable () -> Unit = backNavIcon(onBackClick),
actions: @Composable (RowScope.() -> Unit) = {},
) {
CenterAlignedTopAppBar(
navigationIcon = navigationIcon,
navigationIcon = {
if (onBackClick != null) {
BackNavIcon(onBackClick)
}
},
title = {
Row(
verticalAlignment = Alignment.CenterVertically,
Expand All @@ -60,8 +63,10 @@ fun AppTopBar(
)
}

private fun backNavIcon(onBackClick: () -> Unit) = @Composable {
IconButton(onClick = onBackClick) {
// TODO use everywhere
@Composable
fun BackNavIcon(onClick: () -> Unit) {
IconButton(onClick = onClick) {
Icon(
imageVector = Icons.AutoMirrored.Default.ArrowBack,
contentDescription = stringResource(R.string.common__back),
Expand Down
31 changes: 5 additions & 26 deletions app/src/main/java/to/bitkit/ui/settings/SecuritySettingsScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
Expand All @@ -14,7 +13,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavController
import to.bitkit.R
Expand All @@ -24,19 +22,19 @@ import to.bitkit.ui.components.BodyS
import to.bitkit.ui.components.settings.SettingsButtonRow
import to.bitkit.ui.components.settings.SettingsSwitchRow
import to.bitkit.ui.navigateToAuthCheck
import to.bitkit.ui.navigateToDisablePin
import to.bitkit.ui.navigateToHome
import to.bitkit.ui.scaffold.AppTopBar
import to.bitkit.ui.scaffold.CloseNavIcon
import to.bitkit.ui.scaffold.ScreenColumn
import to.bitkit.ui.settings.pin.PinNavigationSheet
import to.bitkit.ui.theme.AppThemeSurface
import to.bitkit.ui.theme.Colors
import to.bitkit.ui.utils.rememberBiometricAuthSupported
import to.bitkit.ui.settings.pin.PinNavigationSheet

@Composable
fun SecuritySettingsScreen(
navController: NavController,
savedStateHandle: SavedStateHandle,
) {
val app = appViewModel ?: return

Expand All @@ -45,24 +43,6 @@ fun SecuritySettingsScreen(
val isPinOnLaunchEnabled by app.isPinOnLaunchEnabled.collectAsStateWithLifecycle()
val isBiometricEnabled by app.isBiometricEnabled.collectAsStateWithLifecycle()

LaunchedEffect(savedStateHandle) {
savedStateHandle.getStateFlow<String?>(AuthCheckAction.KEY, null)
.collect { actionId ->
if (actionId != null) {
when (actionId) {
AuthCheckAction.Id.TOGGLE_BIOMETRICS -> {
app.setIsBiometricEnabled(!isBiometricEnabled)
}
AuthCheckAction.Id.TOGGLE_PIN_ON_LAUNCH -> {
app.setIsPinOnLaunchEnabled(!isPinOnLaunchEnabled)
}
}
// cleanup
savedStateHandle.remove<String>(AuthCheckAction.KEY)
}
}
}

PinNavigationSheet(
showSheet = showPinSheet,
showLaterButton = false,
Expand All @@ -77,19 +57,18 @@ fun SecuritySettingsScreen(
if (!isPinEnabled) {
showPinSheet = true
} else {
// TODO show Disable Pin screen
app.removePin()
navController.navigateToDisablePin()
}
},
onPinOnLaunchClick = {
navController.navigateToAuthCheck(
onSuccessActionId = AuthCheckAction.Id.TOGGLE_PIN_ON_LAUNCH,
onSuccessActionId = AuthCheckAction.TOGGLE_PIN_ON_LAUNCH,
)
},
onUseBiometricsClick = {
navController.navigateToAuthCheck(
requireBiometrics = true,
onSuccessActionId = AuthCheckAction.Id.TOGGLE_BIOMETRICS,
onSuccessActionId = AuthCheckAction.TOGGLE_BIOMETRICS,
)
},
onBackClick = { navController.popBackStack() },
Expand Down
Loading