From 4c4699a9aaf9a5e61a2decd68d43ff5ad79eacf9 Mon Sep 17 00:00:00 2001 From: jvsena42 Date: Fri, 20 Mar 2026 07:24:41 -0300 Subject: [PATCH 1/7] feat: implement settings tab bar WIP --- app/src/main/java/to/bitkit/ui/ContentView.kt | 70 +- .../to/bitkit/ui/components/DrawerMenu.kt | 10 + .../components/settings/SettingsSwitchRow.kt | 25 + .../java/to/bitkit/ui/settings/AboutScreen.kt | 109 --- .../ui/settings/AdvancedSettingsScreen.kt | 205 ------ .../ui/settings/BackupSettingsScreen.kt | 37 +- .../ui/settings/SecuritySettingsScreen.kt | 225 ------ .../to/bitkit/ui/settings/SettingsScreen.kt | 692 ++++++++++++++---- .../settings/general/GeneralSettingsScreen.kt | 182 ----- .../settings/general/WidgetsSettingsScreen.kt | 52 +- .../ui/settings/pin/ChangePinResultScreen.kt | 2 +- .../ui/settings/pin/DisablePinScreen.kt | 96 --- .../ui/settings/pin/PinManagementScreen.kt | 157 ++++ .../ui/settings/support/SupportScreen.kt | 127 +++- .../to/bitkit/viewmodels/SettingsViewModel.kt | 6 + app/src/main/res/values/strings.xml | 28 +- 16 files changed, 960 insertions(+), 1063 deletions(-) delete mode 100644 app/src/main/java/to/bitkit/ui/settings/AboutScreen.kt delete mode 100644 app/src/main/java/to/bitkit/ui/settings/AdvancedSettingsScreen.kt delete mode 100644 app/src/main/java/to/bitkit/ui/settings/SecuritySettingsScreen.kt delete mode 100644 app/src/main/java/to/bitkit/ui/settings/general/GeneralSettingsScreen.kt delete mode 100644 app/src/main/java/to/bitkit/ui/settings/pin/DisablePinScreen.kt create mode 100644 app/src/main/java/to/bitkit/ui/settings/pin/PinManagementScreen.kt diff --git a/app/src/main/java/to/bitkit/ui/ContentView.kt b/app/src/main/java/to/bitkit/ui/ContentView.kt index b504d0fa6..009f9ffbc 100644 --- a/app/src/main/java/to/bitkit/ui/ContentView.kt +++ b/app/src/main/java/to/bitkit/ui/ContentView.kt @@ -122,8 +122,6 @@ import to.bitkit.ui.screens.widgets.suggestions.SuggestionsViewModel import to.bitkit.ui.screens.widgets.weather.WeatherEditScreen import to.bitkit.ui.screens.widgets.weather.WeatherPreviewScreen import to.bitkit.ui.screens.widgets.weather.WeatherViewModel -import to.bitkit.ui.settings.AboutScreen -import to.bitkit.ui.settings.AdvancedSettingsScreen import to.bitkit.ui.settings.BackupSettingsScreen import to.bitkit.ui.settings.BlocktankRegtestScreen import to.bitkit.ui.settings.CJitDetailScreen @@ -132,7 +130,6 @@ import to.bitkit.ui.settings.LanguageSettingsScreen import to.bitkit.ui.settings.LogDetailScreen import to.bitkit.ui.settings.LogsScreen import to.bitkit.ui.settings.OrderDetailScreen -import to.bitkit.ui.settings.SecuritySettingsScreen import to.bitkit.ui.settings.SettingsScreen import to.bitkit.ui.settings.advanced.AddressTypePreferenceScreen import to.bitkit.ui.settings.advanced.AddressViewerScreen @@ -144,7 +141,6 @@ import to.bitkit.ui.settings.backgroundPayments.BackgroundPaymentsIntroScreen import to.bitkit.ui.settings.backgroundPayments.BackgroundPaymentsSettings import to.bitkit.ui.settings.backups.ResetAndRestoreScreen import to.bitkit.ui.settings.general.DefaultUnitSettingsScreen -import to.bitkit.ui.settings.general.GeneralSettingsScreen import to.bitkit.ui.settings.general.LocalCurrencySettingsScreen import to.bitkit.ui.settings.general.TagsSettingsScreen import to.bitkit.ui.settings.general.WidgetsSettingsScreen @@ -156,7 +152,7 @@ import to.bitkit.ui.settings.pin.ChangePinConfirmScreen import to.bitkit.ui.settings.pin.ChangePinNewScreen import to.bitkit.ui.settings.pin.ChangePinResultScreen import to.bitkit.ui.settings.pin.ChangePinScreen -import to.bitkit.ui.settings.pin.DisablePinScreen +import to.bitkit.ui.settings.pin.PinManagementScreen import to.bitkit.ui.settings.quickPay.QuickPayIntroScreen import to.bitkit.ui.settings.quickPay.QuickPaySettingsScreen import to.bitkit.ui.settings.support.ReportIssueResultScreen @@ -527,12 +523,10 @@ private fun RootNavHost( comingSoon(navController) profile(navController, settingsViewModel) shop(navController, settingsViewModel, appViewModel) - generalSettings(navController) - advancedSettings(navController) - aboutSettings(navController) + generalSettingsSubScreens(navController) + advancedSettingsSubScreens(navController) transactionSpeedSettings(navController) - securitySettings(navController) - disablePin(navController) + pinManagement(navController) changePin(navController) changePinNew(navController) changePinConfirm(navController) @@ -972,11 +966,7 @@ private fun NavGraphBuilder.shop( } } -private fun NavGraphBuilder.generalSettings(navController: NavHostController) { - composableWithDefaultTransitions { - GeneralSettingsScreen(navController) - } - +private fun NavGraphBuilder.generalSettingsSubScreens(navController: NavHostController) { composableWithDefaultTransitions { WidgetsSettingsScreen(navController) } @@ -1000,10 +990,7 @@ private fun NavGraphBuilder.generalSettings(navController: NavHostController) { } } -private fun NavGraphBuilder.advancedSettings(navController: NavHostController) { - composableWithDefaultTransitions { - AdvancedSettingsScreen(navController) - } +private fun NavGraphBuilder.advancedSettingsSubScreens(navController: NavHostController) { composableWithDefaultTransitions { CoinSelectPreferenceScreen(navController) } @@ -1024,16 +1011,6 @@ private fun NavGraphBuilder.advancedSettings(navController: NavHostController) { } } -private fun NavGraphBuilder.aboutSettings(navController: NavHostController) { - composableWithDefaultTransitions { - AboutScreen( - onBack = { - navController.popBackStack() - } - ) - } -} - private fun NavGraphBuilder.transactionSpeedSettings(navController: NavHostController) { composableWithDefaultTransitions { TransactionSpeedSettingsScreen(navController) @@ -1043,15 +1020,9 @@ private fun NavGraphBuilder.transactionSpeedSettings(navController: NavHostContr } } -private fun NavGraphBuilder.securitySettings(navController: NavHostController) { - composableWithDefaultTransitions { - SecuritySettingsScreen(navController = navController) - } -} - -private fun NavGraphBuilder.disablePin(navController: NavHostController) { - composableWithDefaultTransitions { - DisablePinScreen(navController) +private fun NavGraphBuilder.pinManagement(navController: NavHostController) { + composableWithDefaultTransitions { + PinManagementScreen(navController) } } @@ -1517,11 +1488,7 @@ inline fun NavController.navigateTo( } } -fun NavController.navigateToGeneralSettings() = navigateTo(Routes.GeneralSettings) - -fun NavController.navigateToSecuritySettings() = navigateTo(Routes.SecuritySettings) - -fun NavController.navigateToDisablePin() = navigateTo(Routes.DisablePin) +fun NavController.navigateToPinManagement() = navigateTo(Routes.PinManagement) fun NavController.navigateToChangePin() = navigateTo(Routes.ChangePin) @@ -1592,9 +1559,6 @@ fun NavController.navigateToTagsSettings() = navigateTo(Routes.TagsSettings) fun NavController.navigateToLanguageSettings() = navigateTo(Routes.LanguageSettings) -fun NavController.navigateToAdvancedSettings() = navigateTo(Routes.AdvancedSettings) - -fun NavController.navigateToAboutSettings() = navigateTo(Routes.AboutSettings) // endregion @Stable @@ -1614,9 +1578,6 @@ sealed interface Routes { @Serializable data object NodeInfo : Routes - @Serializable - data object GeneralSettings : Routes - @Serializable data object TransactionSpeedSettings : Routes @@ -1626,9 +1587,6 @@ sealed interface Routes { @Serializable data object TagsSettings : Routes - @Serializable - data object AdvancedSettings : Routes - @Serializable data object CoinSelectPreference : Routes @@ -1644,17 +1602,11 @@ sealed interface Routes { @Serializable data object AddressViewer : Routes - @Serializable - data object AboutSettings : Routes - @Serializable data object CustomFeeSettings : Routes @Serializable - data object SecuritySettings : Routes - - @Serializable - data object DisablePin : Routes + data object PinManagement : Routes @Serializable data object ChangePin : Routes diff --git a/app/src/main/java/to/bitkit/ui/components/DrawerMenu.kt b/app/src/main/java/to/bitkit/ui/components/DrawerMenu.kt index 4e5c4b176..759e8731b 100644 --- a/app/src/main/java/to/bitkit/ui/components/DrawerMenu.kt +++ b/app/src/main/java/to/bitkit/ui/components/DrawerMenu.kt @@ -199,6 +199,16 @@ private fun Menu( modifier = Modifier.testTag("DrawerShop") ) + DrawerItem( + label = stringResource(R.string.wallet__drawer__support), + iconRes = R.drawable.ic_settings_support, + onClick = { + rootNavController.navigateIfNotCurrent(Routes.Support) + scope.launch { drawerState.close() } + }, + modifier = Modifier.testTag("DrawerSupport") + ) + DrawerItem( label = stringResource(R.string.wallet__drawer__settings), iconRes = R.drawable.ic_settings, diff --git a/app/src/main/java/to/bitkit/ui/components/settings/SettingsSwitchRow.kt b/app/src/main/java/to/bitkit/ui/components/settings/SettingsSwitchRow.kt index 2b512440f..797ab09c3 100644 --- a/app/src/main/java/to/bitkit/ui/components/settings/SettingsSwitchRow.kt +++ b/app/src/main/java/to/bitkit/ui/components/settings/SettingsSwitchRow.kt @@ -3,18 +3,25 @@ package to.bitkit.ui.components.settings import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon import androidx.compose.material3.Switch import androidx.compose.material3.SwitchColors import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import to.bitkit.R import to.bitkit.ui.components.BodyM import to.bitkit.ui.components.BodyS import to.bitkit.ui.shared.modifiers.clickableAlpha @@ -29,6 +36,8 @@ fun SettingsSwitchRow( onClick: () -> Unit, modifier: Modifier = Modifier, subtitle: String? = null, + iconRes: Int? = null, + iconTint: Color = Color.Unspecified, colors: SwitchColors = AppSwitchDefaults.colors, ) { Column( @@ -42,6 +51,16 @@ fun SettingsSwitchRow( .clickableAlpha { onClick() } .padding(vertical = 16.dp) ) { + if (iconRes != null) { + Icon( + painter = painterResource(iconRes), + contentDescription = null, + tint = iconTint, + modifier = Modifier.size(32.dp), + ) + Spacer(modifier = Modifier.width(10.dp)) + } + Column( modifier = Modifier .weight(1f) @@ -79,6 +98,12 @@ private fun Preview() { isChecked = false, onClick = {}, ) + SettingsSwitchRow( + title = "With Icon", + isChecked = true, + iconRes = R.drawable.ic_eye, + onClick = {}, + ) } } } diff --git a/app/src/main/java/to/bitkit/ui/settings/AboutScreen.kt b/app/src/main/java/to/bitkit/ui/settings/AboutScreen.kt deleted file mode 100644 index c2ee445a2..000000000 --- a/app/src/main/java/to/bitkit/ui/settings/AboutScreen.kt +++ /dev/null @@ -1,109 +0,0 @@ -package to.bitkit.ui.settings - -import android.content.Intent -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.HorizontalDivider -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.core.net.toUri -import to.bitkit.BuildConfig -import to.bitkit.R -import to.bitkit.env.Env -import to.bitkit.ui.components.BodyM -import to.bitkit.ui.components.VerticalSpacer -import to.bitkit.ui.components.settings.Links -import to.bitkit.ui.components.settings.SettingsButtonRow -import to.bitkit.ui.scaffold.AppTopBar -import to.bitkit.ui.scaffold.DrawerNavIcon -import to.bitkit.ui.scaffold.ScreenColumn -import to.bitkit.ui.shared.util.shareText -import to.bitkit.ui.theme.AppThemeSurface -import to.bitkit.ui.theme.Colors - -@Composable -fun AboutScreen( - onBack: () -> Unit, -) { - val context = LocalContext.current - - ScreenColumn { - AppTopBar( - titleText = stringResource(R.string.settings__about__title), - onBackClick = onBack, - actions = { DrawerNavIcon() }, - ) - - Column( - modifier = Modifier.padding(horizontal = 16.dp) - ) { - VerticalSpacer(32.dp) - - BodyM(text = stringResource(R.string.settings__about__text), color = Colors.White64) - - VerticalSpacer(32.dp) - - SettingsButtonRow(title = stringResource(R.string.settings__about__legal), onClick = { - val intent = Intent(Intent.ACTION_VIEW, Env.TERMS_OF_USE_URL.toUri()) - context.startActivity(intent) - }) - - SettingsButtonRow(title = stringResource(R.string.settings__about__share), onClick = { - shareText( - context, - context.getString(R.string.settings__about__shareText) - .replace("{appStoreUrl}", Env.APP_STORE_URL) - .replace("{playStoreUrl}", Env.PLAY_STORE_URL) - ) - }) - - VerticalSpacer(14.dp) - - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween - ) { - BodyM(text = stringResource(R.string.settings__about__version)) - BodyM(text = "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})", color = Colors.White50) - } - - VerticalSpacer(14.dp) - - HorizontalDivider() - - Image( - painter = painterResource(R.drawable.bitkit_logo), - contentDescription = null, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 32.dp) - .weight(1f) - .testTag("AboutLogo") - ) - - Links(modifier = Modifier.fillMaxWidth()) - - VerticalSpacer(16.dp) - } - } -} - -@Preview -@Composable -private fun Preview() { - AppThemeSurface { - AboutScreen( - onBack = {}, - ) - } -} diff --git a/app/src/main/java/to/bitkit/ui/settings/AdvancedSettingsScreen.kt b/app/src/main/java/to/bitkit/ui/settings/AdvancedSettingsScreen.kt deleted file mode 100644 index db5235cca..000000000 --- a/app/src/main/java/to/bitkit/ui/settings/AdvancedSettingsScreen.kt +++ /dev/null @@ -1,205 +0,0 @@ -package to.bitkit.ui.settings - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -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.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.navigation.NavController -import to.bitkit.R -import to.bitkit.ui.Routes -import to.bitkit.ui.components.VerticalSpacer -import to.bitkit.ui.components.settings.SectionHeader -import to.bitkit.ui.components.settings.SettingsButtonRow -import to.bitkit.ui.components.settings.SettingsButtonValue -import to.bitkit.ui.navigateTo -import to.bitkit.ui.navigateToHome -import to.bitkit.ui.scaffold.AppAlertDialog -import to.bitkit.ui.scaffold.AppTopBar -import to.bitkit.ui.scaffold.DrawerNavIcon -import to.bitkit.ui.scaffold.ScreenColumn -import to.bitkit.ui.theme.AppThemeSurface - -@Composable -fun AdvancedSettingsScreen( - navController: NavController, - viewModel: AdvancedSettingsViewModel = hiltViewModel(), -) { - var showResetSuggestionsDialog by remember { mutableStateOf(false) } - val selectedAddressTypeName by viewModel.selectedAddressTypeName.collectAsStateWithLifecycle() - - Content( - showResetSuggestionsDialog = showResetSuggestionsDialog, - selectedAddressTypeName = selectedAddressTypeName, - onBack = { navController.popBackStack() }, - onCoinSelectionClick = { - navController.navigateTo(Routes.CoinSelectPreference) - }, - onAddressTypePreferenceClick = { - navController.navigateTo(Routes.AddressTypePreference) - }, - onLightningConnectionsClick = { - navController.navigateTo(Routes.LightningConnections) - }, - onLightningNodeClick = { - navController.navigateTo(Routes.NodeInfo) - }, - onElectrumServerClick = { - navController.navigateTo(Routes.ElectrumConfig) - }, - onRgsServerClick = { - navController.navigateTo(Routes.RgsServer) - }, - onAddressViewerClick = { - navController.navigateTo(Routes.AddressViewer) - }, - onSuggestionsResetClick = { showResetSuggestionsDialog = true }, - onResetSuggestionsDialogConfirm = { - viewModel.resetSuggestions() - showResetSuggestionsDialog = false - navController.navigateToHome() - }, - onResetSuggestionsDialogCancel = { showResetSuggestionsDialog = false }, - ) -} - -@Composable -private fun Content( - showResetSuggestionsDialog: Boolean, - selectedAddressTypeName: String = "", - onBack: () -> Unit = {}, - onCoinSelectionClick: () -> Unit = {}, - onAddressTypePreferenceClick: () -> Unit = {}, - onLightningConnectionsClick: () -> Unit = {}, - onLightningNodeClick: () -> Unit = {}, - onElectrumServerClick: () -> Unit = {}, - onRgsServerClick: () -> Unit = {}, - onAddressViewerClick: () -> Unit = {}, - onSuggestionsResetClick: () -> Unit = {}, - onResetSuggestionsDialogConfirm: () -> Unit = {}, - onResetSuggestionsDialogCancel: () -> Unit = {}, -) { - ScreenColumn { - AppTopBar( - titleText = stringResource(R.string.settings__advanced_title), - onBackClick = onBack, - actions = { DrawerNavIcon() }, - ) - Column( - modifier = Modifier - .padding(horizontal = 16.dp) - .fillMaxSize() - .verticalScroll(rememberScrollState()) - .testTag("advanced_settings_screen") - ) { - // Payments Section - SectionHeader(title = stringResource(R.string.settings__adv__section_payments)) - - SettingsButtonRow( - title = stringResource(R.string.settings__addr_type__title), - value = if (selectedAddressTypeName.isNotEmpty()) { - SettingsButtonValue.StringValue(selectedAddressTypeName) - } else { - SettingsButtonValue.None - }, - onClick = onAddressTypePreferenceClick, - modifier = Modifier.testTag("AddressTypePreference"), - ) - - SettingsButtonRow( - title = stringResource(R.string.settings__adv__coin_selection), - onClick = onCoinSelectionClick, - modifier = Modifier.testTag("CoinSelectPreference"), - ) - - // Networks Section - SectionHeader(title = stringResource(R.string.settings__adv__section_networks)) - - SettingsButtonRow( - title = stringResource(R.string.settings__adv__lightning_connections), - onClick = onLightningConnectionsClick, - modifier = Modifier.testTag("Channels"), - ) - - SettingsButtonRow( - title = stringResource(R.string.settings__adv__lightning_node), - onClick = onLightningNodeClick, - modifier = Modifier.testTag("LightningNodeInfo"), - ) - - SettingsButtonRow( - title = stringResource(R.string.settings__adv__electrum_server), - onClick = onElectrumServerClick, - modifier = Modifier.testTag("ElectrumConfig"), - ) - - SettingsButtonRow( - title = stringResource(R.string.settings__adv__rgs_server), - onClick = onRgsServerClick, - modifier = Modifier.testTag("RGSServer"), - ) - - // Other Section - SectionHeader(title = stringResource(R.string.settings__adv__section_other)) - - SettingsButtonRow( - title = stringResource(R.string.settings__adv__address_viewer), - onClick = onAddressViewerClick, - modifier = Modifier.testTag("AddressViewer"), - ) - - SettingsButtonRow( - title = stringResource(R.string.settings__adv__suggestions_reset), - onClick = onSuggestionsResetClick, - modifier = Modifier.testTag("ResetSuggestions"), - ) - - VerticalSpacer(32.dp) - } - - if (showResetSuggestionsDialog) { - AppAlertDialog( - title = stringResource(R.string.settings__adv__reset_title), - text = stringResource(R.string.settings__adv__reset_desc), - confirmText = stringResource(R.string.settings__adv__reset_confirm), - onConfirm = onResetSuggestionsDialogConfirm, - onDismiss = onResetSuggestionsDialogCancel, - modifier = Modifier.testTag("reset_suggestions_dialog"), - ) - } - } -} - -@Preview -@Composable -private fun Preview() { - AppThemeSurface { - Content( - showResetSuggestionsDialog = false, - selectedAddressTypeName = "Taproot", - ) - } -} - -@Preview -@Composable -private fun PreviewDialog() { - AppThemeSurface { - Content( - showResetSuggestionsDialog = true, - selectedAddressTypeName = "Taproot", - ) - } -} diff --git a/app/src/main/java/to/bitkit/ui/settings/BackupSettingsScreen.kt b/app/src/main/java/to/bitkit/ui/settings/BackupSettingsScreen.kt index cbafab83e..b872ec3d8 100644 --- a/app/src/main/java/to/bitkit/ui/settings/BackupSettingsScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/BackupSettingsScreen.kt @@ -31,23 +31,15 @@ import to.bitkit.env.Env import to.bitkit.ext.toRelativeTimeString import to.bitkit.models.BackupCategory import to.bitkit.models.BackupItemStatus -import to.bitkit.ui.Routes -import to.bitkit.ui.appViewModel import to.bitkit.ui.backupsViewModel -import to.bitkit.ui.components.AuthCheckAction import to.bitkit.ui.components.BodyMSB import to.bitkit.ui.components.Caption13Up import to.bitkit.ui.components.CaptionB import to.bitkit.ui.components.FillWidth -import to.bitkit.ui.components.Sheet import to.bitkit.ui.components.VerticalSpacer -import to.bitkit.ui.components.settings.SettingsButtonRow -import to.bitkit.ui.navigateTo -import to.bitkit.ui.navigateToAuthCheck import to.bitkit.ui.scaffold.AppTopBar import to.bitkit.ui.scaffold.DrawerNavIcon import to.bitkit.ui.scaffold.ScreenColumn -import to.bitkit.ui.settingsViewModel import to.bitkit.ui.shared.modifiers.clickableAlpha import to.bitkit.ui.theme.AppThemeSurface import to.bitkit.ui.theme.Colors @@ -60,23 +52,12 @@ import kotlin.time.ExperimentalTime fun BackupSettingsScreen( navController: NavController, ) { - val app = appViewModel ?: return - val settings = settingsViewModel ?: return val viewModel = backupsViewModel ?: return - val isPinEnabled by settings.isPinEnabled.collectAsStateWithLifecycle() val uiState by viewModel.uiState.collectAsStateWithLifecycle() BackupSettingsScreenContent( uiState = uiState, - onBackupClick = { app.showSheet(Sheet.Backup()) }, - onResetAndRestoreClick = { - if (isPinEnabled) { - navController.navigateToAuthCheck(onSuccessActionId = AuthCheckAction.NAV_TO_RESET) - } else { - navController.navigateTo(Routes.ResetAndRestoreSettings) - } - }, onRetryBackup = { category -> viewModel.retryBackup(category) }, onBack = { navController.popBackStack() }, ) @@ -85,15 +66,13 @@ fun BackupSettingsScreen( @Composable private fun BackupSettingsScreenContent( uiState: BackupStatusUiState, - onBackupClick: () -> Unit, - onResetAndRestoreClick: () -> Unit, onRetryBackup: (BackupCategory) -> Unit, onBack: () -> Unit, ) { val allSynced = uiState.categories.all { !it.status.isRequired } ScreenColumn { AppTopBar( - titleText = stringResource(R.string.settings__backup__title), + titleText = stringResource(R.string.settings__backup__data), onBackClick = onBack, actions = { DrawerNavIcon() }, ) @@ -103,17 +82,7 @@ private fun BackupSettingsScreenContent( .verticalScroll(rememberScrollState()) .testTag("BackupScrollView") ) { - SettingsButtonRow( - title = stringResource(R.string.settings__backup__wallet), - onClick = onBackupClick, - modifier = Modifier.testTag("BackupWallet"), - ) - SettingsButtonRow( - title = stringResource(R.string.settings__backup__reset), - onClick = onResetAndRestoreClick, - modifier = Modifier.testTag("ResetAndRestore"), - ) - VerticalSpacer(28.dp) + VerticalSpacer(16.dp) Row(verticalAlignment = Alignment.CenterVertically) { Caption13Up( @@ -263,8 +232,6 @@ private fun Preview() { AppThemeSurface { BackupSettingsScreenContent( uiState = BackupStatusUiState(categories = categories), - onBackupClick = {}, - onResetAndRestoreClick = {}, onRetryBackup = {}, onBack = {}, ) diff --git a/app/src/main/java/to/bitkit/ui/settings/SecuritySettingsScreen.kt b/app/src/main/java/to/bitkit/ui/settings/SecuritySettingsScreen.kt deleted file mode 100644 index 510baa93b..000000000 --- a/app/src/main/java/to/bitkit/ui/settings/SecuritySettingsScreen.kt +++ /dev/null @@ -1,225 +0,0 @@ -package to.bitkit.ui.settings - -import androidx.compose.foundation.layout.Column -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.getValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.navigation.NavController -import to.bitkit.R -import to.bitkit.ui.appViewModel -import to.bitkit.ui.components.AuthCheckAction -import to.bitkit.ui.components.BodyS -import to.bitkit.ui.components.Sheet -import to.bitkit.ui.components.settings.SettingsButtonRow -import to.bitkit.ui.components.settings.SettingsButtonValue -import to.bitkit.ui.components.settings.SettingsSwitchRow -import to.bitkit.ui.navigateToAuthCheck -import to.bitkit.ui.navigateToChangePin -import to.bitkit.ui.navigateToDisablePin -import to.bitkit.ui.scaffold.AppTopBar -import to.bitkit.ui.scaffold.DrawerNavIcon -import to.bitkit.ui.scaffold.ScreenColumn -import to.bitkit.ui.settingsViewModel -import to.bitkit.ui.theme.AppThemeSurface -import to.bitkit.ui.theme.Colors -import to.bitkit.ui.utils.rememberBiometricAuthSupported - -@Composable -fun SecuritySettingsScreen( - navController: NavController, -) { - val settings = settingsViewModel ?: return - val app = appViewModel ?: return - - val isPinEnabled by settings.isPinEnabled.collectAsStateWithLifecycle() - val isBiometricEnabled by settings.isBiometricEnabled.collectAsStateWithLifecycle() - val isPinForPaymentsEnabled by settings.isPinForPaymentsEnabled.collectAsStateWithLifecycle() - val enableSwipeToHideBalance by settings.enableSwipeToHideBalance.collectAsStateWithLifecycle() - val hideBalanceOnOpen by settings.hideBalanceOnOpen.collectAsStateWithLifecycle() - val enableAutoReadClipboard by settings.enableAutoReadClipboard.collectAsStateWithLifecycle() - val enableSendAmountWarning by settings.enableSendAmountWarning.collectAsStateWithLifecycle() - - Content( - isPinEnabled = isPinEnabled, - isBiometricEnabled = isBiometricEnabled, - isPinForPaymentsEnabled = isPinForPaymentsEnabled, - enableSwipeToHideBalance = enableSwipeToHideBalance, - hideBalanceOnOpen = hideBalanceOnOpen, - enableAutoReadClipboard = enableAutoReadClipboard, - enableSendAmountWarning = enableSendAmountWarning, - isBiometrySupported = rememberBiometricAuthSupported(), - onPinClick = { - if (!isPinEnabled) { - app.showSheet(Sheet.Pin()) - } else { - navController.navigateToDisablePin() - } - }, - onChangePinClick = { - navController.navigateToChangePin() - }, - onPinForPaymentsClick = { - navController.navigateToAuthCheck( - onSuccessActionId = AuthCheckAction.TOGGLE_PIN_FOR_PAYMENTS, - ) - }, - onUseBiometricsClick = { - navController.navigateToAuthCheck( - requireBiometrics = true, - onSuccessActionId = AuthCheckAction.TOGGLE_BIOMETRICS, - ) - }, - onSwipeToHideBalanceClick = { - settings.setEnableSwipeToHideBalance(!enableSwipeToHideBalance) - }, - onHideBalanceOnOpenClick = { - settings.setHideBalanceOnOpen(!hideBalanceOnOpen) - }, - onAutoReadClipboardClick = { - settings.setEnableAutoReadClipboard(!enableAutoReadClipboard) - }, - onSendAmountWarningClick = { - settings.setEnableSendAmountWarning(!enableSendAmountWarning) - }, - onBackClick = { navController.popBackStack() }, - ) -} - -@Composable -private fun Content( - isPinEnabled: Boolean, - isBiometricEnabled: Boolean, - isPinForPaymentsEnabled: Boolean, - enableSwipeToHideBalance: Boolean, - hideBalanceOnOpen: Boolean, - enableAutoReadClipboard: Boolean, - enableSendAmountWarning: Boolean, - isBiometrySupported: Boolean, - onPinClick: () -> Unit = {}, - onChangePinClick: () -> Unit = {}, - onPinForPaymentsClick: () -> Unit = {}, - onUseBiometricsClick: () -> Unit = {}, - onSwipeToHideBalanceClick: () -> Unit = {}, - onHideBalanceOnOpenClick: () -> Unit = {}, - onAutoReadClipboardClick: () -> Unit = {}, - onSendAmountWarningClick: () -> Unit = {}, - onBackClick: () -> Unit = {}, -) { - ScreenColumn( - modifier = Modifier - .verticalScroll(rememberScrollState()) - ) { - AppTopBar( - titleText = stringResource(R.string.settings__security_title), - onBackClick = onBackClick, - actions = { DrawerNavIcon() }, - ) - Column( - modifier = Modifier.padding(horizontal = 16.dp) - ) { - SettingsSwitchRow( - title = stringResource(R.string.settings__security__swipe_balance_to_hide), - isChecked = enableSwipeToHideBalance, - onClick = onSwipeToHideBalanceClick, - modifier = Modifier.testTag("SwipeBalanceToHide"), - ) - - if (enableSwipeToHideBalance) { - SettingsSwitchRow( - title = stringResource(R.string.settings__security__hide_balance_on_open), - isChecked = hideBalanceOnOpen, - onClick = onHideBalanceOnOpenClick, - modifier = Modifier.testTag("HideBalanceOnOpen"), - ) - } - - SettingsSwitchRow( - title = stringResource(R.string.settings__security__clipboard), - isChecked = enableAutoReadClipboard, - onClick = onAutoReadClipboardClick, - modifier = Modifier.testTag("AutoReadClipboard"), - ) - - SettingsSwitchRow( - title = stringResource(R.string.settings__security__warn_100), - isChecked = enableSendAmountWarning, - onClick = onSendAmountWarningClick, - modifier = Modifier.testTag("SendAmountWarning"), - ) - - SettingsButtonRow( - title = stringResource(R.string.settings__security__pin), - value = SettingsButtonValue.StringValue( - stringResource( - if (isPinEnabled) { - R.string.settings__security__pin_enabled - } else { - R.string.settings__security__pin_disabled - } - ) - ), - onClick = onPinClick, - modifier = Modifier.testTag("PINCode"), - ) - if (isPinEnabled) { - SettingsButtonRow( - title = stringResource(R.string.settings__security__pin_change), - onClick = onChangePinClick, - modifier = Modifier.testTag("PINChange"), - ) - SettingsSwitchRow( - title = stringResource(R.string.settings__security__pin_payments), - isChecked = isPinForPaymentsEnabled, - onClick = onPinForPaymentsClick, - modifier = Modifier.testTag("EnablePinForPayments"), - ) - } - if (isPinEnabled && isBiometrySupported) { - SettingsSwitchRow( - title = run { - val bioTypeName = stringResource(R.string.security__bio) - stringResource(R.string.settings__security__use_bio).replace("{biometryTypeName}", bioTypeName) - }, - isChecked = isBiometricEnabled, - onClick = onUseBiometricsClick, - modifier = Modifier.testTag("UseBiometryInstead"), - ) - } - if (isPinEnabled && isBiometrySupported) { - BodyS( - text = run { - val bioTypeName = stringResource(R.string.security__bio) - stringResource(R.string.settings__security__footer).replace("{biometryTypeName}", bioTypeName) - }, - color = Colors.White64, - modifier = Modifier.padding(vertical = 16.dp) - ) - } - } - } -} - -@Preview -@Composable -private fun Preview() { - AppThemeSurface { - Content( - isPinEnabled = true, - isBiometricEnabled = false, - isPinForPaymentsEnabled = false, - enableSwipeToHideBalance = true, - hideBalanceOnOpen = false, - enableAutoReadClipboard = true, - enableSendAmountWarning = true, - isBiometrySupported = true, - ) - } -} diff --git a/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt b/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt index 587df3ef9..f4001e640 100644 --- a/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt @@ -1,203 +1,629 @@ package to.bitkit.ui.settings -import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height 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.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.hapticfeedback.HapticFeedbackType -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController import to.bitkit.R -import to.bitkit.models.Toast +import to.bitkit.models.PrimaryDisplay +import to.bitkit.models.TransactionSpeed +import to.bitkit.models.transactionSpeedUiText +import to.bitkit.ui.LocalCurrencies import to.bitkit.ui.Routes import to.bitkit.ui.appViewModel +import to.bitkit.ui.components.AuthCheckAction +import to.bitkit.ui.components.Sheet +import to.bitkit.ui.components.VerticalSpacer +import to.bitkit.ui.components.settings.SectionHeader import to.bitkit.ui.components.settings.SettingsButtonRow +import to.bitkit.ui.components.settings.SettingsButtonValue +import to.bitkit.ui.components.settings.SettingsSwitchRow import to.bitkit.ui.navigateTo -import to.bitkit.ui.navigateToAboutSettings -import to.bitkit.ui.navigateToAdvancedSettings -import to.bitkit.ui.navigateToBackupSettings +import to.bitkit.ui.navigateToAuthCheck +import to.bitkit.ui.navigateToDefaultUnitSettings import to.bitkit.ui.navigateToDevSettings -import to.bitkit.ui.navigateToGeneralSettings -import to.bitkit.ui.navigateToSecuritySettings +import to.bitkit.ui.navigateToLanguageSettings +import to.bitkit.ui.navigateToLocalCurrencySettings +import to.bitkit.ui.navigateToPinManagement +import to.bitkit.ui.navigateToQuickPaySettings +import to.bitkit.ui.navigateToTagsSettings +import to.bitkit.ui.navigateToTransactionSpeedSettings +import to.bitkit.ui.navigateToWidgetsSettings import to.bitkit.ui.scaffold.AppTopBar import to.bitkit.ui.scaffold.DrawerNavIcon import to.bitkit.ui.scaffold.ScreenColumn +import to.bitkit.ui.screens.wallets.activity.components.CustomTabRowWithSpacing +import to.bitkit.ui.screens.wallets.activity.components.TabItem import to.bitkit.ui.settingsViewModel -import to.bitkit.ui.shared.modifiers.clickableAlpha import to.bitkit.ui.theme.AppThemeSurface +import to.bitkit.ui.utils.rememberBiometricAuthSupported +import to.bitkit.viewmodels.LanguageViewModel -private const val DEV_MODE_TAP_THRESHOLD = 5 +private enum class SettingsTab(private val titleRes: Int) : TabItem { + General(R.string.settings__general_title), + Security(R.string.settings__security_title), + Advanced(R.string.settings__advanced_title); + + override val uiText @Composable get() = stringResource(titleRes) +} @Composable fun SettingsScreen( navController: NavController, + advancedViewModel: AdvancedSettingsViewModel = hiltViewModel(), + languageViewModel: LanguageViewModel = hiltViewModel(), ) { val app = appViewModel ?: return val settings = settingsViewModel ?: return + val currencies = LocalCurrencies.current + + // General tab state + val defaultTransactionSpeed by settings.defaultTransactionSpeed.collectAsStateWithLifecycle() + val lastUsedTags by settings.lastUsedTags.collectAsStateWithLifecycle() + val quickPayIntroSeen by settings.quickPayIntroSeen.collectAsStateWithLifecycle() + val bgPaymentsIntroSeen by settings.bgPaymentsIntroSeen.collectAsStateWithLifecycle() + val notificationsGranted by settings.notificationsGranted.collectAsStateWithLifecycle() + val languageUiState by languageViewModel.uiState.collectAsStateWithLifecycle() + + // Security tab state + val isPinEnabled by settings.isPinEnabled.collectAsStateWithLifecycle() + val isBiometricEnabled by settings.isBiometricEnabled.collectAsStateWithLifecycle() + val isPinForPaymentsEnabled by settings.isPinForPaymentsEnabled.collectAsStateWithLifecycle() + val enableSwipeToHideBalance by settings.enableSwipeToHideBalance.collectAsStateWithLifecycle() + val hideBalanceOnOpen by settings.hideBalanceOnOpen.collectAsStateWithLifecycle() + val enableAutoReadClipboard by settings.enableAutoReadClipboard.collectAsStateWithLifecycle() + val enableSendAmountWarning by settings.enableSendAmountWarning.collectAsStateWithLifecycle() + + // Advanced tab state val isDevModeEnabled by settings.isDevModeEnabled.collectAsStateWithLifecycle() - var enableDevModeTapCount by remember { mutableIntStateOf(0) } - val haptic = LocalHapticFeedback.current - val context = LocalContext.current + val selectedAddressTypeName by advancedViewModel.selectedAddressTypeName.collectAsStateWithLifecycle() - SettingsScreenContent( - isDevModeEnabled = isDevModeEnabled, - onGeneralClick = { navController.navigateToGeneralSettings() }, - onSecurityClick = { navController.navigateToSecuritySettings() }, - onBackupClick = { navController.navigateToBackupSettings() }, - onAdvancedClick = { navController.navigateToAdvancedSettings() }, - onSupportClick = { navController.navigateTo(Routes.Support) }, - onAboutClick = { navController.navigateToAboutSettings() }, - onDevClick = { navController.navigateToDevSettings() }, - onBackClick = { navController.popBackStack() }, - onCogTap = { - haptic.performHapticFeedback(HapticFeedbackType.Confirm) - enableDevModeTapCount = enableDevModeTapCount + 1 - - if (enableDevModeTapCount >= DEV_MODE_TAP_THRESHOLD) { - val newValue = !isDevModeEnabled - settings.setIsDevModeEnabled(newValue) - haptic.performHapticFeedback(HapticFeedbackType.LongPress) - - app.toast( - type = Toast.ToastType.SUCCESS, - title = context.getString( - if (newValue) { - R.string.settings__dev_enabled_title - } else { - R.string.settings__dev_disabled_title - } - ), - description = context.getString( - if (newValue) { - R.string.settings__dev_enabled_message - } else { - R.string.settings__dev_disabled_message - } - ), - ) - enableDevModeTapCount = 0 + LaunchedEffect(Unit) { languageViewModel.fetchLanguageInfo() } + + SettingsContent( + // General + selectedCurrency = currencies.selectedCurrency, + primaryDisplay = currencies.primaryDisplay, + defaultTransactionSpeed = defaultTransactionSpeed, + selectedLanguage = languageUiState.selectedLanguage.displayName, + showTagsButton = lastUsedTags.isNotEmpty(), + notificationsGranted = notificationsGranted, + onLanguageClick = { navController.navigateToLanguageSettings() }, + onLocalCurrencyClick = { navController.navigateToLocalCurrencySettings() }, + onDefaultUnitClick = { navController.navigateToDefaultUnitSettings() }, + onWidgetsClick = { navController.navigateToWidgetsSettings() }, + onTagsClick = { navController.navigateToTagsSettings() }, + onTransactionSpeedClick = { navController.navigateToTransactionSpeedSettings() }, + onQuickPayClick = { navController.navigateToQuickPaySettings(quickPayIntroSeen) }, + onBgPaymentsClick = { + if (bgPaymentsIntroSeen || notificationsGranted) { + navController.navigateTo(Routes.BackgroundPaymentsSettings) + } else { + navController.navigateTo(Routes.BackgroundPaymentsIntro) + } + }, + // Security + isPinEnabled = isPinEnabled, + isBiometricEnabled = isBiometricEnabled, + isPinForPaymentsEnabled = isPinForPaymentsEnabled, + enableSwipeToHideBalance = enableSwipeToHideBalance, + hideBalanceOnOpen = hideBalanceOnOpen, + enableAutoReadClipboard = enableAutoReadClipboard, + enableSendAmountWarning = enableSendAmountWarning, + isBiometrySupported = rememberBiometricAuthSupported(), + onBackupWalletClick = { app.showSheet(Sheet.Backup()) }, + onDataBackupsClick = { navController.navigateTo(Routes.BackupSettings) }, + onResetWalletClick = { + if (isPinEnabled) { + navController.navigateToAuthCheck(onSuccessActionId = AuthCheckAction.NAV_TO_RESET) + } else { + navController.navigateTo(Routes.ResetAndRestoreSettings) } }, + onPinClick = { navController.navigateToPinManagement() }, + onPinForPaymentsClick = { + navController.navigateToAuthCheck( + onSuccessActionId = AuthCheckAction.TOGGLE_PIN_FOR_PAYMENTS, + ) + }, + onUseBiometricsClick = { + navController.navigateToAuthCheck( + requireBiometrics = true, + onSuccessActionId = AuthCheckAction.TOGGLE_BIOMETRICS, + ) + }, + onSwipeToHideBalanceClick = { settings.setEnableSwipeToHideBalance(!enableSwipeToHideBalance) }, + onHideBalanceOnOpenClick = { settings.setHideBalanceOnOpen(!hideBalanceOnOpen) }, + onAutoReadClipboardClick = { settings.setEnableAutoReadClipboard(!enableAutoReadClipboard) }, + onSendAmountWarningClick = { settings.setEnableSendAmountWarning(!enableSendAmountWarning) }, + // Advanced + isDevModeEnabled = isDevModeEnabled, + selectedAddressTypeName = selectedAddressTypeName, + onDevSettingsClick = { navController.navigateToDevSettings() }, + onAddressTypeClick = { navController.navigateTo(Routes.AddressTypePreference) }, + onCoinSelectionClick = { navController.navigateTo(Routes.CoinSelectPreference) }, + onAddressViewerClick = { navController.navigateTo(Routes.AddressViewer) }, + onLightningConnectionsClick = { navController.navigateTo(Routes.LightningConnections) }, + onLightningNodeClick = { navController.navigateTo(Routes.NodeInfo) }, + onElectrumServerClick = { navController.navigateTo(Routes.ElectrumConfig) }, + onRgsServerClick = { navController.navigateTo(Routes.RgsServer) }, + // Navigation + onBackClick = { navController.popBackStack() }, ) } +@Suppress("LongParameterList", "LongMethod") @Composable -fun SettingsScreenContent( - isDevModeEnabled: Boolean, - onGeneralClick: () -> Unit, - onSecurityClick: () -> Unit, - onBackupClick: () -> Unit, - onAdvancedClick: () -> Unit, - onSupportClick: () -> Unit, - onAboutClick: () -> Unit, - onDevClick: () -> Unit, - onCogTap: () -> Unit, - onBackClick: () -> Unit, +private fun SettingsContent( + // General + selectedCurrency: String = "USD", + primaryDisplay: PrimaryDisplay = PrimaryDisplay.BITCOIN, + defaultTransactionSpeed: TransactionSpeed = TransactionSpeed.Medium, + selectedLanguage: String = "", + showTagsButton: Boolean = false, + notificationsGranted: Boolean = false, + onLanguageClick: () -> Unit = {}, + onLocalCurrencyClick: () -> Unit = {}, + onDefaultUnitClick: () -> Unit = {}, + onWidgetsClick: () -> Unit = {}, + onTagsClick: () -> Unit = {}, + onTransactionSpeedClick: () -> Unit = {}, + onQuickPayClick: () -> Unit = {}, + onBgPaymentsClick: () -> Unit = {}, + // Security + isPinEnabled: Boolean = false, + isBiometricEnabled: Boolean = false, + isPinForPaymentsEnabled: Boolean = false, + enableSwipeToHideBalance: Boolean = false, + hideBalanceOnOpen: Boolean = false, + enableAutoReadClipboard: Boolean = true, + enableSendAmountWarning: Boolean = true, + isBiometrySupported: Boolean = false, + onBackupWalletClick: () -> Unit = {}, + onDataBackupsClick: () -> Unit = {}, + onResetWalletClick: () -> Unit = {}, + onPinClick: () -> Unit = {}, + onPinForPaymentsClick: () -> Unit = {}, + onUseBiometricsClick: () -> Unit = {}, + onSwipeToHideBalanceClick: () -> Unit = {}, + onHideBalanceOnOpenClick: () -> Unit = {}, + onAutoReadClipboardClick: () -> Unit = {}, + onSendAmountWarningClick: () -> Unit = {}, + // Advanced + isDevModeEnabled: Boolean = false, + selectedAddressTypeName: String = "", + onDevSettingsClick: () -> Unit = {}, + onAddressTypeClick: () -> Unit = {}, + onCoinSelectionClick: () -> Unit = {}, + onAddressViewerClick: () -> Unit = {}, + onLightningConnectionsClick: () -> Unit = {}, + onLightningNodeClick: () -> Unit = {}, + onElectrumServerClick: () -> Unit = {}, + onRgsServerClick: () -> Unit = {}, + // Navigation + onBackClick: () -> Unit = {}, ) { + var selectedTab by remember { mutableStateOf(SettingsTab.General) } + val tabs = remember { SettingsTab.entries } + ScreenColumn { AppTopBar( titleText = stringResource(R.string.settings__settings), onBackClick = onBackClick, actions = { DrawerNavIcon() }, ) - Column( - modifier = Modifier - .padding(horizontal = 16.dp) - .fillMaxSize() - .verticalScroll(rememberScrollState()) - ) { - SettingsButtonRow( - title = stringResource(R.string.settings__general_title), - iconRes = R.drawable.ic_settings_general, - onClick = onGeneralClick, - modifier = Modifier.testTag("GeneralSettings") - ) - SettingsButtonRow( - title = stringResource(R.string.settings__security_title), - iconRes = R.drawable.ic_settings_security, - onClick = onSecurityClick, - modifier = Modifier.testTag("SecuritySettings") + + CustomTabRowWithSpacing( + tabs = tabs, + currentTabIndex = tabs.indexOf(selectedTab), + onTabChange = { selectedTab = it }, + modifier = Modifier.padding(horizontal = 16.dp), + ) + + when (selectedTab) { + SettingsTab.General -> GeneralTabContent( + selectedCurrency = selectedCurrency, + primaryDisplay = primaryDisplay, + defaultTransactionSpeed = defaultTransactionSpeed, + selectedLanguage = selectedLanguage, + showTagsButton = showTagsButton, + notificationsGranted = notificationsGranted, + onLanguageClick = onLanguageClick, + onLocalCurrencyClick = onLocalCurrencyClick, + onDefaultUnitClick = onDefaultUnitClick, + onWidgetsClick = onWidgetsClick, + onTagsClick = onTagsClick, + onTransactionSpeedClick = onTransactionSpeedClick, + onQuickPayClick = onQuickPayClick, + onBgPaymentsClick = onBgPaymentsClick, ) - SettingsButtonRow( - title = stringResource(R.string.settings__backup_title), - iconRes = R.drawable.ic_settings_backup, - onClick = onBackupClick, - modifier = Modifier.testTag("BackupSettings") + + SettingsTab.Security -> SecurityTabContent( + isPinEnabled = isPinEnabled, + isBiometricEnabled = isBiometricEnabled, + isPinForPaymentsEnabled = isPinForPaymentsEnabled, + enableSwipeToHideBalance = enableSwipeToHideBalance, + hideBalanceOnOpen = hideBalanceOnOpen, + enableAutoReadClipboard = enableAutoReadClipboard, + enableSendAmountWarning = enableSendAmountWarning, + isBiometrySupported = isBiometrySupported, + onBackupWalletClick = onBackupWalletClick, + onDataBackupsClick = onDataBackupsClick, + onResetWalletClick = onResetWalletClick, + onPinClick = onPinClick, + onPinForPaymentsClick = onPinForPaymentsClick, + onUseBiometricsClick = onUseBiometricsClick, + onSwipeToHideBalanceClick = onSwipeToHideBalanceClick, + onHideBalanceOnOpenClick = onHideBalanceOnOpenClick, + onAutoReadClipboardClick = onAutoReadClipboardClick, + onSendAmountWarningClick = onSendAmountWarningClick, ) - SettingsButtonRow( - title = stringResource(R.string.settings__advanced_title), - iconRes = R.drawable.ic_settings_advanced, - onClick = onAdvancedClick, - modifier = Modifier.testTag("AdvancedSettings") + + SettingsTab.Advanced -> AdvancedTabContent( + isDevModeEnabled = isDevModeEnabled, + selectedAddressTypeName = selectedAddressTypeName, + onDevSettingsClick = onDevSettingsClick, + onAddressTypeClick = onAddressTypeClick, + onCoinSelectionClick = onCoinSelectionClick, + onAddressViewerClick = onAddressViewerClick, + onLightningConnectionsClick = onLightningConnectionsClick, + onLightningNodeClick = onLightningNodeClick, + onElectrumServerClick = onElectrumServerClick, + onRgsServerClick = onRgsServerClick, ) + } + } +} + +@Composable +private fun GeneralTabContent( + selectedCurrency: String, + primaryDisplay: PrimaryDisplay, + defaultTransactionSpeed: TransactionSpeed, + selectedLanguage: String, + showTagsButton: Boolean, + notificationsGranted: Boolean, + onLanguageClick: () -> Unit, + onLocalCurrencyClick: () -> Unit, + onDefaultUnitClick: () -> Unit, + onWidgetsClick: () -> Unit, + onTagsClick: () -> Unit, + onTransactionSpeedClick: () -> Unit, + onQuickPayClick: () -> Unit, + onBgPaymentsClick: () -> Unit, +) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 16.dp) + .verticalScroll(rememberScrollState()) + ) { + // Interface section + SectionHeader(title = stringResource(R.string.settings__general__section_interface)) + + SettingsButtonRow( + title = stringResource(R.string.settings__language_title), + iconRes = R.drawable.ic_warning, + value = SettingsButtonValue.StringValue(selectedLanguage), + onClick = onLanguageClick, + modifier = Modifier.testTag("LanguageSettings"), + ) + SettingsButtonRow( + title = stringResource(R.string.settings__general__currency_local), + iconRes = R.drawable.ic_coins, + value = SettingsButtonValue.StringValue(selectedCurrency), + onClick = onLocalCurrencyClick, + modifier = Modifier.testTag("CurrenciesSettings"), + ) + SettingsButtonRow( + title = stringResource(R.string.settings__general__unit), + iconRes = R.drawable.ic_coins, + value = SettingsButtonValue.StringValue( + when (primaryDisplay) { + PrimaryDisplay.BITCOIN -> stringResource(R.string.settings__general__unit_bitcoin) + PrimaryDisplay.FIAT -> selectedCurrency + } + ), + onClick = onDefaultUnitClick, + modifier = Modifier.testTag("UnitSettings"), + ) + SettingsButtonRow( + title = stringResource(R.string.settings__widgets__nav_title), + iconRes = R.drawable.ic_warning, + onClick = onWidgetsClick, + modifier = Modifier.testTag("WidgetsSettings"), + ) + if (showTagsButton) { SettingsButtonRow( - title = stringResource(R.string.settings__support_title), - iconRes = R.drawable.ic_settings_support, - onClick = onSupportClick, - modifier = Modifier.testTag("Support") + title = stringResource(R.string.settings__general__tags), + iconRes = R.drawable.ic_tag, + onClick = onTagsClick, + modifier = Modifier.testTag("TagsSettings"), ) - SettingsButtonRow( - title = stringResource(R.string.settings__about_title), - iconRes = R.drawable.ic_settings_about, - onClick = onAboutClick, - modifier = Modifier.testTag("About") + } + + // Payments section + SectionHeader( + title = stringResource(R.string.settings__general__section_payments), + padding = androidx.compose.foundation.layout.PaddingValues(top = 16.dp), + ) + + SettingsButtonRow( + title = stringResource(R.string.settings__general__speed), + iconRes = R.drawable.ic_speed_normal, + value = SettingsButtonValue.StringValue(defaultTransactionSpeed.transactionSpeedUiText()), + onClick = onTransactionSpeedClick, + modifier = Modifier.testTag("TransactionSpeedSettings"), + ) + SettingsButtonRow( + title = stringResource(R.string.settings__quickpay__nav_title), + iconRes = R.drawable.ic_warning, + onClick = onQuickPayClick, + modifier = Modifier.testTag("QuickpaySettings"), + ) + SettingsButtonRow( + title = stringResource(R.string.settings__bg__title), + iconRes = R.drawable.ic_bell, + value = SettingsButtonValue.StringValue( + stringResource(if (notificationsGranted) R.string.settings__bg__on else R.string.settings__bg__off) + ), + onClick = onBgPaymentsClick, + modifier = Modifier.testTag("BackgroundPaymentSettings"), + ) + + VerticalSpacer(32.dp) + } +} + +@Suppress("LongParameterList") +@Composable +private fun SecurityTabContent( + isPinEnabled: Boolean, + isBiometricEnabled: Boolean, + isPinForPaymentsEnabled: Boolean, + enableSwipeToHideBalance: Boolean, + hideBalanceOnOpen: Boolean, + enableAutoReadClipboard: Boolean, + enableSendAmountWarning: Boolean, + isBiometrySupported: Boolean, + onBackupWalletClick: () -> Unit, + onDataBackupsClick: () -> Unit, + onResetWalletClick: () -> Unit, + onPinClick: () -> Unit, + onPinForPaymentsClick: () -> Unit, + onUseBiometricsClick: () -> Unit, + onSwipeToHideBalanceClick: () -> Unit, + onHideBalanceOnOpenClick: () -> Unit, + onAutoReadClipboardClick: () -> Unit, + onSendAmountWarningClick: () -> Unit, +) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 16.dp) + .verticalScroll(rememberScrollState()) + ) { + // Back up or reset section + SectionHeader(title = stringResource(R.string.settings__security__section_backup)) + + SettingsButtonRow( + title = stringResource(R.string.settings__backup__wallet), + iconRes = R.drawable.ic_warning, + onClick = onBackupWalletClick, + modifier = Modifier.testTag("BackupWallet"), + ) + SettingsButtonRow( + title = stringResource(R.string.settings__backup__data), + iconRes = R.drawable.ic_warning, + onClick = onDataBackupsClick, + modifier = Modifier.testTag("BackupSettings"), + ) + SettingsButtonRow( + title = stringResource(R.string.settings__backup__reset), + iconRes = R.drawable.ic_warning, + onClick = onResetWalletClick, + modifier = Modifier.testTag("ResetAndRestore"), + ) + + // Safety section + SectionHeader( + title = stringResource(R.string.settings__security__section_safety), + padding = androidx.compose.foundation.layout.PaddingValues(top = 16.dp), + ) + + SettingsButtonRow( + title = stringResource(R.string.settings__security__pin), + iconRes = R.drawable.ic_warning, + value = SettingsButtonValue.StringValue( + stringResource( + if (isPinEnabled) { + R.string.settings__security__pin_enabled + } else { + R.string.settings__security__pin_disabled + } + ) + ), + onClick = onPinClick, + modifier = Modifier.testTag("PINCode"), + ) + + if (isPinEnabled) { + SettingsSwitchRow( + title = stringResource(R.string.settings__security__pin_payments), + iconRes = R.drawable.ic_coins, + isChecked = isPinForPaymentsEnabled, + onClick = onPinForPaymentsClick, + modifier = Modifier.testTag("EnablePinForPayments"), ) - if (isDevModeEnabled) { - SettingsButtonRow( - title = stringResource(R.string.settings__dev_title), - iconRes = R.drawable.ic_settings_dev, - onClick = onDevClick, - modifier = Modifier.testTag("DevSettings") + + if (isBiometrySupported) { + SettingsSwitchRow( + title = run { + val bioTypeName = stringResource(R.string.security__bio) + stringResource(R.string.settings__security__use_bio) + .replace("{biometryTypeName}", bioTypeName) + }, + iconRes = R.drawable.ic_warning, + isChecked = isBiometricEnabled, + onClick = onUseBiometricsClick, + modifier = Modifier.testTag("UseBiometryInstead"), ) } - Spacer(Modifier.weight(1f)) - Image( - painter = painterResource(R.drawable.cog), - contentDescription = null, - modifier = Modifier - .fillMaxWidth() - .height(256.dp) - .clickableAlpha(1f) { onCogTap() } - .testTag("DevOptions") + } + + SettingsSwitchRow( + title = stringResource(R.string.settings__security__warn_100), + iconRes = R.drawable.ic_warning, + isChecked = enableSendAmountWarning, + onClick = onSendAmountWarningClick, + modifier = Modifier.testTag("SendAmountWarning"), + ) + + // Privacy section + SectionHeader( + title = stringResource(R.string.settings__security__section_privacy), + padding = androidx.compose.foundation.layout.PaddingValues(top = 16.dp), + ) + + SettingsSwitchRow( + title = stringResource(R.string.settings__security__swipe_balance_to_hide), + iconRes = R.drawable.ic_warning, + isChecked = enableSwipeToHideBalance, + onClick = onSwipeToHideBalanceClick, + modifier = Modifier.testTag("SwipeBalanceToHide"), + ) + SettingsSwitchRow( + title = stringResource(R.string.settings__security__hide_balance_on_open), + iconRes = R.drawable.ic_warning, + isChecked = hideBalanceOnOpen, + onClick = onHideBalanceOnOpenClick, + modifier = Modifier.testTag("HideBalanceOnOpen"), + ) + SettingsSwitchRow( + title = stringResource(R.string.settings__security__clipboard), + iconRes = R.drawable.ic_clipboard_text, + isChecked = enableAutoReadClipboard, + onClick = onAutoReadClipboardClick, + modifier = Modifier.testTag("AutoReadClipboard"), + ) + + VerticalSpacer(32.dp) + } +} + +@Composable +private fun AdvancedTabContent( + isDevModeEnabled: Boolean, + selectedAddressTypeName: String, + onDevSettingsClick: () -> Unit, + onAddressTypeClick: () -> Unit, + onCoinSelectionClick: () -> Unit, + onAddressViewerClick: () -> Unit, + onLightningConnectionsClick: () -> Unit, + onLightningNodeClick: () -> Unit, + onElectrumServerClick: () -> Unit, + onRgsServerClick: () -> Unit, +) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 16.dp) + .verticalScroll(rememberScrollState()) + .testTag("advanced_settings_screen") + ) { + // Debug section (only if dev mode enabled) + if (isDevModeEnabled) { + SectionHeader(title = stringResource(R.string.settings__adv__section_debug)) + + SettingsButtonRow( + title = stringResource(R.string.settings__dev_title), + iconRes = R.drawable.ic_settings_dev, + onClick = onDevSettingsClick, + modifier = Modifier.testTag("DevSettings"), ) - Spacer(Modifier.weight(1f)) } + + // Payments section + SectionHeader(title = stringResource(R.string.settings__adv__section_payments)) + + SettingsButtonRow( + title = stringResource(R.string.settings__addr_type__title), + iconRes = R.drawable.ic_warning, + value = if (selectedAddressTypeName.isNotEmpty()) { + SettingsButtonValue.StringValue(selectedAddressTypeName) + } else { + SettingsButtonValue.None + }, + onClick = onAddressTypeClick, + modifier = Modifier.testTag("AddressTypePreference"), + ) + SettingsButtonRow( + title = stringResource(R.string.settings__adv__coin_selection), + iconRes = R.drawable.ic_coins, + onClick = onCoinSelectionClick, + modifier = Modifier.testTag("CoinSelectPreference"), + ) + SettingsButtonRow( + title = stringResource(R.string.settings__adv__address_viewer), + iconRes = R.drawable.ic_eye, + onClick = onAddressViewerClick, + modifier = Modifier.testTag("AddressViewer"), + ) + + // Networks section + SectionHeader( + title = stringResource(R.string.settings__adv__section_networks), + padding = androidx.compose.foundation.layout.PaddingValues(top = 16.dp), + ) + + SettingsButtonRow( + title = stringResource(R.string.settings__adv__lightning_connections), + iconRes = R.drawable.ic_git_branch, + onClick = onLightningConnectionsClick, + modifier = Modifier.testTag("Channels"), + ) + SettingsButtonRow( + title = stringResource(R.string.settings__adv__lightning_node), + iconRes = R.drawable.ic_git_branch, + onClick = onLightningNodeClick, + modifier = Modifier.testTag("LightningNodeInfo"), + ) + SettingsButtonRow( + title = stringResource(R.string.settings__adv__electrum_server), + iconRes = R.drawable.ic_warning, + onClick = onElectrumServerClick, + modifier = Modifier.testTag("ElectrumConfig"), + ) + SettingsButtonRow( + title = stringResource(R.string.settings__adv__rgs_server), + iconRes = R.drawable.ic_broadcast, + onClick = onRgsServerClick, + modifier = Modifier.testTag("RGSServer"), + ) + + VerticalSpacer(32.dp) } } @Preview(showBackground = true) @Composable -private fun Preview() { +private fun PreviewGeneral() { AppThemeSurface { - SettingsScreenContent( - isDevModeEnabled = true, - onGeneralClick = {}, - onSecurityClick = {}, - onBackupClick = {}, - onAdvancedClick = {}, - onSupportClick = {}, - onAboutClick = {}, - onDevClick = {}, - onCogTap = {}, - onBackClick = {}, - ) + SettingsContent() } } diff --git a/app/src/main/java/to/bitkit/ui/settings/general/GeneralSettingsScreen.kt b/app/src/main/java/to/bitkit/ui/settings/general/GeneralSettingsScreen.kt deleted file mode 100644 index 1bafb0693..000000000 --- a/app/src/main/java/to/bitkit/ui/settings/general/GeneralSettingsScreen.kt +++ /dev/null @@ -1,182 +0,0 @@ -package to.bitkit.ui.settings.general - -import androidx.compose.foundation.layout.Column -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.ui.Modifier -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.navigation.NavController -import to.bitkit.R -import to.bitkit.models.Language -import to.bitkit.models.PrimaryDisplay -import to.bitkit.models.TransactionSpeed -import to.bitkit.models.transactionSpeedUiText -import to.bitkit.ui.LocalCurrencies -import to.bitkit.ui.Routes -import to.bitkit.ui.components.settings.SettingsButtonRow -import to.bitkit.ui.components.settings.SettingsButtonValue -import to.bitkit.ui.navigateTo -import to.bitkit.ui.navigateToDefaultUnitSettings -import to.bitkit.ui.navigateToLanguageSettings -import to.bitkit.ui.navigateToLocalCurrencySettings -import to.bitkit.ui.navigateToQuickPaySettings -import to.bitkit.ui.navigateToTagsSettings -import to.bitkit.ui.navigateToTransactionSpeedSettings -import to.bitkit.ui.navigateToWidgetsSettings -import to.bitkit.ui.scaffold.AppTopBar -import to.bitkit.ui.scaffold.DrawerNavIcon -import to.bitkit.ui.scaffold.ScreenColumn -import to.bitkit.ui.settingsViewModel -import to.bitkit.ui.theme.AppThemeSurface -import to.bitkit.viewmodels.LanguageViewModel - -@Composable -fun GeneralSettingsScreen( - navController: NavController, - languageViewModel: LanguageViewModel = hiltViewModel(), -) { - val settings = settingsViewModel ?: return - val currencies = LocalCurrencies.current - val defaultTransactionSpeed by settings.defaultTransactionSpeed.collectAsStateWithLifecycle() - val lastUsedTags by settings.lastUsedTags.collectAsStateWithLifecycle() - val quickPayIntroSeen by settings.quickPayIntroSeen.collectAsStateWithLifecycle() - val bgPaymentsIntroSeen by settings.bgPaymentsIntroSeen.collectAsStateWithLifecycle() - val notificationsGranted by settings.notificationsGranted.collectAsStateWithLifecycle() - val languageUiState by languageViewModel.uiState.collectAsStateWithLifecycle() - - LaunchedEffect(Unit) { languageViewModel.fetchLanguageInfo() } - - GeneralSettingsContent( - selectedCurrency = currencies.selectedCurrency, - primaryDisplay = currencies.primaryDisplay, - defaultTransactionSpeed = defaultTransactionSpeed, - showTagsButton = lastUsedTags.isNotEmpty(), - onBackClick = { navController.popBackStack() }, - onLocalCurrencyClick = { navController.navigateToLocalCurrencySettings() }, - onDefaultUnitClick = { navController.navigateToDefaultUnitSettings() }, - onTransactionSpeedClick = { navController.navigateToTransactionSpeedSettings() }, - onWidgetsClick = { navController.navigateToWidgetsSettings() }, - onQuickPayClick = { navController.navigateToQuickPaySettings(quickPayIntroSeen) }, - onTagsClick = { navController.navigateToTagsSettings() }, - onLanguageSettingsClick = { navController.navigateToLanguageSettings() }, - onBgPaymentsClick = { - if (bgPaymentsIntroSeen || notificationsGranted) { - navController.navigateTo(Routes.BackgroundPaymentsSettings) - } else { - navController.navigateTo(Routes.BackgroundPaymentsIntro) - } - }, - selectedLanguage = languageUiState.selectedLanguage.displayName, - notificationsGranted = notificationsGranted - ) -} - -@Composable -private fun GeneralSettingsContent( - selectedCurrency: String, - primaryDisplay: PrimaryDisplay, - defaultTransactionSpeed: TransactionSpeed, - selectedLanguage: String, - notificationsGranted: Boolean, - showTagsButton: Boolean = false, - onBackClick: () -> Unit = {}, - onLocalCurrencyClick: () -> Unit = {}, - onDefaultUnitClick: () -> Unit = {}, - onTransactionSpeedClick: () -> Unit = {}, - onWidgetsClick: () -> Unit = {}, - onQuickPayClick: () -> Unit = {}, - onLanguageSettingsClick: () -> Unit = {}, - onTagsClick: () -> Unit = {}, - onBgPaymentsClick: () -> Unit = {}, -) { - ScreenColumn { - AppTopBar( - titleText = stringResource(R.string.settings__general_title), - onBackClick = onBackClick, - actions = { DrawerNavIcon() }, - ) - Column( - modifier = Modifier - .padding(horizontal = 16.dp) - .verticalScroll(rememberScrollState()) - ) { - SettingsButtonRow( - title = stringResource(R.string.settings__language_title), - value = SettingsButtonValue.StringValue(selectedLanguage), - onClick = onLanguageSettingsClick, - modifier = Modifier.testTag("LanguageSettings") - ) - SettingsButtonRow( - title = stringResource(R.string.settings__general__currency_local), - value = SettingsButtonValue.StringValue(selectedCurrency), - onClick = onLocalCurrencyClick, - modifier = Modifier.testTag("CurrenciesSettings") - ) - SettingsButtonRow( - title = stringResource(R.string.settings__general__unit), - value = SettingsButtonValue.StringValue( - when (primaryDisplay) { - PrimaryDisplay.BITCOIN -> stringResource(R.string.settings__general__unit_bitcoin) - PrimaryDisplay.FIAT -> selectedCurrency - } - ), - onClick = onDefaultUnitClick, - modifier = Modifier.testTag("UnitSettings") - ) - SettingsButtonRow( - title = stringResource(R.string.settings__general__speed), - value = SettingsButtonValue.StringValue(defaultTransactionSpeed.transactionSpeedUiText()), - onClick = onTransactionSpeedClick, - modifier = Modifier.testTag("TransactionSpeedSettings") - ) - if (showTagsButton) { - SettingsButtonRow( - title = stringResource(R.string.settings__general__tags), - onClick = onTagsClick, - modifier = Modifier.testTag("TagsSettings") - ) - } - SettingsButtonRow( - title = stringResource(R.string.settings__widgets__nav_title), - onClick = onWidgetsClick, - modifier = Modifier.testTag("WidgetsSettings") - ) - SettingsButtonRow( - title = stringResource(R.string.settings__quickpay__nav_title), - onClick = onQuickPayClick, - modifier = Modifier.testTag("QuickpaySettings") - ) - SettingsButtonRow( - title = stringResource(R.string.settings__bg__title), - onClick = onBgPaymentsClick, - value = SettingsButtonValue.StringValue( - stringResource(if (notificationsGranted) R.string.settings__bg__on else R.string.settings__bg__off) - ), - modifier = Modifier.testTag("BackgroundPaymentSettings") - ) - } - } -} - -@Preview(showBackground = true) -@Composable -private fun Preview() { - AppThemeSurface { - GeneralSettingsContent( - selectedCurrency = "USD", - primaryDisplay = PrimaryDisplay.BITCOIN, - defaultTransactionSpeed = TransactionSpeed.Medium, - selectedLanguage = Language.SYSTEM_DEFAULT.displayName, - notificationsGranted = true, - ) - } -} diff --git a/app/src/main/java/to/bitkit/ui/settings/general/WidgetsSettingsScreen.kt b/app/src/main/java/to/bitkit/ui/settings/general/WidgetsSettingsScreen.kt index 9a860baa4..bcf01e555 100644 --- a/app/src/main/java/to/bitkit/ui/settings/general/WidgetsSettingsScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/general/WidgetsSettingsScreen.kt @@ -2,16 +2,25 @@ package to.bitkit.ui.settings.general import androidx.compose.foundation.layout.Column 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.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController import to.bitkit.R +import to.bitkit.ui.components.settings.SectionHeader +import to.bitkit.ui.components.settings.SettingsButtonRow import to.bitkit.ui.components.settings.SettingsSwitchRow +import to.bitkit.ui.scaffold.AppAlertDialog import to.bitkit.ui.scaffold.AppTopBar import to.bitkit.ui.scaffold.DrawerNavIcon import to.bitkit.ui.scaffold.ScreenColumn @@ -33,17 +42,23 @@ fun WidgetsSettingsScreen( showWidgetTitles = showWidgetTitles, onShowWidgetsClick = { settings.setShowWidgets(!showWidgets) }, onShowWidgetTitlesClick = { settings.setShowWidgetTitles(!showWidgetTitles) }, + onResetSuggestionsClick = { + settings.resetDismissedSuggestions() + }, ) } @Composable private fun WidgetsSettingsContent( - onBackClick: () -> Unit = {}, showWidgets: Boolean, - onShowWidgetsClick: () -> Unit = {}, showWidgetTitles: Boolean, + onBackClick: () -> Unit = {}, + onShowWidgetsClick: () -> Unit = {}, onShowWidgetTitlesClick: () -> Unit = {}, + onResetSuggestionsClick: () -> Unit = {}, ) { + var showResetSuggestionsDialog by remember { mutableStateOf(false) } + ScreenColumn { AppTopBar( titleText = stringResource(R.string.settings__widgets__nav_title), @@ -51,8 +66,13 @@ private fun WidgetsSettingsContent( actions = { DrawerNavIcon() }, ) Column( - modifier = Modifier.padding(horizontal = 16.dp), + modifier = Modifier + .padding(horizontal = 16.dp) + .verticalScroll(rememberScrollState()), ) { + // Display section + SectionHeader(title = stringResource(R.string.settings__widgets__section_display)) + SettingsSwitchRow( title = stringResource(R.string.settings__widgets__showWidgets), isChecked = showWidgets, @@ -63,6 +83,32 @@ private fun WidgetsSettingsContent( isChecked = showWidgetTitles, onClick = onShowWidgetTitlesClick, ) + + // Reset section + SectionHeader( + title = stringResource(R.string.settings__widgets__section_reset), + padding = androidx.compose.foundation.layout.PaddingValues(top = 16.dp), + ) + + SettingsButtonRow( + title = stringResource(R.string.settings__widgets__reset_suggestions), + onClick = { showResetSuggestionsDialog = true }, + modifier = Modifier.testTag("ResetSuggestions"), + ) + } + + if (showResetSuggestionsDialog) { + AppAlertDialog( + title = stringResource(R.string.settings__adv__reset_title), + text = stringResource(R.string.settings__adv__reset_desc), + confirmText = stringResource(R.string.settings__adv__reset_confirm), + onConfirm = { + onResetSuggestionsClick() + showResetSuggestionsDialog = false + }, + onDismiss = { showResetSuggestionsDialog = false }, + modifier = Modifier.testTag("reset_suggestions_dialog"), + ) } } } diff --git a/app/src/main/java/to/bitkit/ui/settings/pin/ChangePinResultScreen.kt b/app/src/main/java/to/bitkit/ui/settings/pin/ChangePinResultScreen.kt index 8913265f2..020703c3b 100644 --- a/app/src/main/java/to/bitkit/ui/settings/pin/ChangePinResultScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/pin/ChangePinResultScreen.kt @@ -32,7 +32,7 @@ fun ChangePinResultScreen( ) { ChangePinResultContent( onOkClick = { - navController.popBackStack(inclusive = false) + navController.popBackStack(inclusive = false) }, onBackClick = { navController.popBackStack() diff --git a/app/src/main/java/to/bitkit/ui/settings/pin/DisablePinScreen.kt b/app/src/main/java/to/bitkit/ui/settings/pin/DisablePinScreen.kt deleted file mode 100644 index 1c68a0e10..000000000 --- a/app/src/main/java/to/bitkit/ui/settings/pin/DisablePinScreen.kt +++ /dev/null @@ -1,96 +0,0 @@ -package to.bitkit.ui.settings.pin - -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.navigation.NavController -import to.bitkit.R -import to.bitkit.ui.Routes -import to.bitkit.ui.components.AuthCheckAction -import to.bitkit.ui.components.BodyM -import to.bitkit.ui.components.PrimaryButton -import to.bitkit.ui.navigateToAuthCheck -import to.bitkit.ui.scaffold.AppTopBar -import to.bitkit.ui.scaffold.DrawerNavIcon -import to.bitkit.ui.scaffold.ScreenColumn -import to.bitkit.ui.theme.AppThemeSurface -import to.bitkit.ui.theme.Colors - -@Composable -fun DisablePinScreen( - navController: NavController, -) { - DisablePinContent( - onDisableClick = { - navController.navigateToAuthCheck( - requirePin = true, - onSuccessActionId = AuthCheckAction.DISABLE_PIN, - ) { popUpTo(Routes.SecuritySettings) } - }, - onBackClick = { navController.popBackStack() }, - ) -} - -@Composable -private fun DisablePinContent( - onDisableClick: () -> Unit = {}, - onBackClick: () -> Unit = {}, -) { - ScreenColumn { - AppTopBar( - titleText = stringResource(R.string.security__pin_disable_title), - onBackClick = onBackClick, - actions = { DrawerNavIcon() }, - ) - Column( - modifier = Modifier.padding(horizontal = 16.dp), - ) { - BodyM( - text = stringResource(R.string.security__pin_disable_text), - color = Colors.White64, - ) - - Box( - contentAlignment = Alignment.Center, - modifier = Modifier - .fillMaxWidth() - .weight(1f) - ) { - Image( - painter = painterResource(R.drawable.shield), - contentDescription = null, - modifier = Modifier.size(256.dp) - ) - } - - PrimaryButton( - text = stringResource(R.string.security__pin_disable_button), - onClick = onDisableClick, - modifier = Modifier.testTag("DisablePin") - ) - - Spacer(modifier = Modifier.height(16.dp)) - } - } -} - -@Preview(showSystemUi = true) -@Composable -private fun Preview() { - AppThemeSurface { - DisablePinContent() - } -} diff --git a/app/src/main/java/to/bitkit/ui/settings/pin/PinManagementScreen.kt b/app/src/main/java/to/bitkit/ui/settings/pin/PinManagementScreen.kt new file mode 100644 index 000000000..fa02b892d --- /dev/null +++ b/app/src/main/java/to/bitkit/ui/settings/pin/PinManagementScreen.kt @@ -0,0 +1,157 @@ +package to.bitkit.ui.settings.pin + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.navigation.NavController +import to.bitkit.R +import to.bitkit.ui.Routes +import to.bitkit.ui.appViewModel +import to.bitkit.ui.components.AuthCheckAction +import to.bitkit.ui.components.BodyM +import to.bitkit.ui.components.PrimaryButton +import to.bitkit.ui.components.SecondaryButton +import to.bitkit.ui.components.Sheet +import to.bitkit.ui.navigateToAuthCheck +import to.bitkit.ui.navigateToChangePin +import to.bitkit.ui.scaffold.AppTopBar +import to.bitkit.ui.scaffold.DrawerNavIcon +import to.bitkit.ui.scaffold.ScreenColumn +import to.bitkit.ui.settingsViewModel +import to.bitkit.ui.theme.AppThemeSurface +import to.bitkit.ui.theme.Colors + +@Composable +fun PinManagementScreen( + navController: NavController, +) { + val app = appViewModel ?: return + val settings = settingsViewModel ?: return + val isPinEnabled by settings.isPinEnabled.collectAsStateWithLifecycle() + + Content( + isPinEnabled = isPinEnabled, + onEnablePinClick = { app.showSheet(Sheet.Pin()) }, + onChangePinClick = { navController.navigateToChangePin() }, + onDisablePinClick = { + navController.navigateToAuthCheck( + requirePin = true, + onSuccessActionId = AuthCheckAction.DISABLE_PIN, + ) { popUpTo(Routes.Settings) } + }, + onBackClick = { navController.popBackStack() }, + ) +} + +@Composable +private fun Content( + isPinEnabled: Boolean, + onEnablePinClick: () -> Unit = {}, + onChangePinClick: () -> Unit = {}, + onDisablePinClick: () -> Unit = {}, + onBackClick: () -> Unit = {}, +) { + ScreenColumn { + AppTopBar( + titleText = stringResource( + if (isPinEnabled) { + R.string.security__pin_disable_title + } else { + R.string.settings__security__pin + } + ), + onBackClick = onBackClick, + actions = { DrawerNavIcon() }, + ) + Column( + modifier = Modifier.padding(horizontal = 16.dp), + ) { + BodyM( + text = stringResource( + if (isPinEnabled) { + R.string.security__pin_disable_text + } else { + R.string.security__pin_security_text + } + ), + color = Colors.White64, + ) + + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .fillMaxWidth() + .weight(1f) + ) { + Image( + painter = painterResource(R.drawable.shield), + contentDescription = null, + modifier = Modifier.size(256.dp), + ) + } + + if (isPinEnabled) { + Row( + horizontalArrangement = Arrangement.spacedBy(16.dp), + modifier = Modifier.fillMaxWidth(), + ) { + SecondaryButton( + text = stringResource(R.string.settings__security__pin_change), + onClick = onChangePinClick, + modifier = Modifier + .weight(1f) + .testTag("ChangePIN"), + ) + PrimaryButton( + text = stringResource(R.string.security__pin_disable_button), + onClick = onDisablePinClick, + modifier = Modifier + .weight(1f) + .testTag("DisablePin"), + ) + } + } else { + PrimaryButton( + text = stringResource(R.string.security__pin_enable_button), + onClick = onEnablePinClick, + modifier = Modifier.testTag("EnablePin"), + ) + } + + Spacer(modifier = Modifier.height(16.dp)) + } + } +} + +@Preview(showSystemUi = true) +@Composable +private fun PreviewDisabled() { + AppThemeSurface { + Content(isPinEnabled = false) + } +} + +@Preview(showSystemUi = true) +@Composable +private fun PreviewEnabled() { + AppThemeSurface { + Content(isPinEnabled = true) + } +} diff --git a/app/src/main/java/to/bitkit/ui/settings/support/SupportScreen.kt b/app/src/main/java/to/bitkit/ui/settings/support/SupportScreen.kt index d56085155..81d8b823f 100644 --- a/app/src/main/java/to/bitkit/ui/settings/support/SupportScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/support/SupportScreen.kt @@ -7,34 +7,58 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height 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.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.core.net.toUri +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController +import to.bitkit.BuildConfig import to.bitkit.R import to.bitkit.env.Env +import to.bitkit.models.Toast import to.bitkit.ui.Routes +import to.bitkit.ui.appViewModel import to.bitkit.ui.components.BodyM +import to.bitkit.ui.components.BodyS +import to.bitkit.ui.components.VerticalSpacer import to.bitkit.ui.components.settings.Links import to.bitkit.ui.components.settings.SettingsButtonRow +import to.bitkit.ui.components.settings.SettingsButtonValue import to.bitkit.ui.navigateTo import to.bitkit.ui.scaffold.AppTopBar import to.bitkit.ui.scaffold.DrawerNavIcon import to.bitkit.ui.scaffold.ScreenColumn +import to.bitkit.ui.settingsViewModel +import to.bitkit.ui.shared.util.shareText import to.bitkit.ui.theme.AppThemeSurface import to.bitkit.ui.theme.Colors +private const val DEV_MODE_TAP_THRESHOLD = 5 + @Composable fun SupportScreen( navController: NavController, ) { val context = LocalContext.current + val app = appViewModel ?: return + val settings = settingsViewModel ?: return + val isDevModeEnabled by settings.isDevModeEnabled.collectAsStateWithLifecycle() + var devModeTapCount by remember { mutableIntStateOf(0) } + val haptic = LocalHapticFeedback.current Content( onBack = { navController.popBackStack() }, @@ -44,6 +68,47 @@ fun SupportScreen( context.startActivity(intent) }, onClickAppStatus = { navController.navigateTo(Routes.AppStatus) }, + onClickLegal = { + val intent = Intent(Intent.ACTION_VIEW, Env.TERMS_OF_USE_URL.toUri()) + context.startActivity(intent) + }, + onClickShare = { + shareText( + context, + context.getString(R.string.settings__about__shareText) + .replace("{appStoreUrl}", Env.APP_STORE_URL) + .replace("{playStoreUrl}", Env.PLAY_STORE_URL) + ) + }, + onClickVersion = { + haptic.performHapticFeedback(HapticFeedbackType.Confirm) + devModeTapCount += 1 + + if (devModeTapCount >= DEV_MODE_TAP_THRESHOLD) { + val newValue = !isDevModeEnabled + settings.setIsDevModeEnabled(newValue) + haptic.performHapticFeedback(HapticFeedbackType.LongPress) + + app.toast( + type = Toast.ToastType.SUCCESS, + title = context.getString( + if (newValue) { + R.string.settings__dev_enabled_title + } else { + R.string.settings__dev_disabled_title + } + ), + description = context.getString( + if (newValue) { + R.string.settings__dev_enabled_message + } else { + R.string.settings__dev_disabled_message + } + ), + ) + devModeTapCount = 0 + } + }, ) } @@ -53,7 +118,12 @@ private fun Content( onClickReportIssue: () -> Unit = {}, onClickHelpCenter: () -> Unit = {}, onClickAppStatus: () -> Unit = {}, + onClickLegal: () -> Unit = {}, + onClickShare: () -> Unit = {}, + onClickVersion: () -> Unit = {}, ) { + val appVersion = "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})" + ScreenColumn { AppTopBar( titleText = stringResource(R.string.settings__support_title), @@ -62,33 +132,72 @@ private fun Content( ) Column( - modifier = Modifier.padding(horizontal = 16.dp) + modifier = Modifier + .padding(horizontal = 16.dp) + .verticalScroll(rememberScrollState()) ) { - Spacer(modifier = Modifier.height(32.dp)) + VerticalSpacer(16.dp) BodyM(text = stringResource(R.string.settings__support__text), color = Colors.White64) - Spacer(modifier = Modifier.height(32.dp)) + VerticalSpacer(16.dp) - SettingsButtonRow(title = stringResource(R.string.settings__support__report), onClick = onClickReportIssue) - SettingsButtonRow(title = stringResource(R.string.settings__support__help), onClick = onClickHelpCenter) + SettingsButtonRow( + title = stringResource(R.string.settings__support__report), + iconRes = R.drawable.ic_warning, + onClick = onClickReportIssue, + ) + SettingsButtonRow( + title = stringResource(R.string.settings__support__help), + iconRes = R.drawable.ic_warning, + onClick = onClickHelpCenter, + ) SettingsButtonRow( title = stringResource(R.string.settings__support__status), + iconRes = R.drawable.ic_settings_support, onClick = onClickAppStatus, - modifier = Modifier.testTag("AppStatus") + modifier = Modifier.testTag("AppStatus"), + ) + SettingsButtonRow( + title = stringResource(R.string.settings__about__legal), + iconRes = R.drawable.ic_warning, + onClick = onClickLegal, ) + SettingsButtonRow( + title = stringResource(R.string.settings__about__share), + iconRes = R.drawable.ic_share, + onClick = onClickShare, + ) + SettingsButtonRow( + title = stringResource(R.string.settings__about__version), + iconRes = R.drawable.ic_stack, + value = SettingsButtonValue.StringValue(appVersion), + onClick = onClickVersion, + modifier = Modifier.testTag("DevOptions"), + ) + + Spacer(modifier = Modifier.weight(1f)) Image( - painter = painterResource(R.drawable.question_mark), + painter = painterResource(R.drawable.bitkit_logo), contentDescription = null, modifier = Modifier .fillMaxWidth() - .weight(1f) + .padding(horizontal = 32.dp, vertical = 16.dp) + .height(82.dp) + .testTag("AboutLogo"), ) Links(modifier = Modifier.fillMaxWidth()) - Spacer(modifier = Modifier.height(16.dp)) + VerticalSpacer(16.dp) + + BodyS( + text = stringResource(R.string.settings__support__copyright), + color = Colors.White64, + ) + + VerticalSpacer(32.dp) } } } diff --git a/app/src/main/java/to/bitkit/viewmodels/SettingsViewModel.kt b/app/src/main/java/to/bitkit/viewmodels/SettingsViewModel.kt index ba3e42286..022fc0ef9 100644 --- a/app/src/main/java/to/bitkit/viewmodels/SettingsViewModel.kt +++ b/app/src/main/java/to/bitkit/viewmodels/SettingsViewModel.kt @@ -166,6 +166,12 @@ class SettingsViewModel @Inject constructor( } } + fun resetDismissedSuggestions() { + viewModelScope.launch { + settingsStore.update { it.copy(dismissedSuggestions = emptyList()) } + } + } + val lastUsedTags = settingsStore.data.map { it.lastUsedTags } .asStateFlow(initialValue = emptyList()) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5e85b445b..57b657b71 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -484,6 +484,7 @@ Choose 4-Digit PIN Please use a PIN you will remember. If you forget your PIN you can reset it, but that will require restoring your wallet. Disable PIN + Enable PIN PIN code is currently enabled. If you want to disable your PIN, you need to enter your current PIN code first. Disable PIN Please enter your PIN code @@ -570,6 +571,7 @@ Are you sure you want to reset the suggestions? They will reappear in case you have removed them from your Bitkit wallet overview. Reset Suggestions? Rapid-Gossip-Sync + Debug Networks Other Payments @@ -587,7 +589,8 @@ Bitkit failed to back up wallet data. Retrying in {interval, plural, one {# minute} other {# minutes}}. Data Backup Failure latest data backups - Reset and restore wallet + Data backups + Reset wallet Failed Backup: {time} Running Latest Backup: {time} @@ -653,7 +656,9 @@ Classic (₿ 0.00010000) Bitcoin denomination Modern (₿ 10 000) - Transaction speed + Interface + Payments + Transaction Speed Default Transaction Speed Set Custom Fee ₿ {feeSats} for the average transaction @@ -680,6 +685,9 @@ You may need to restart the app once or twice for this change to take effect. Rapid-Gossip-Sync Server Updated Read clipboard for ease of use + Back up or reset + Privacy + Safety When enabled, you can use {biometryTypeName} instead of your PIN code to unlock your wallet or send payments. Hide balance on open PIN Code @@ -688,9 +696,9 @@ Enabled Require PIN for payments Swipe balance to hide - Use {biometryTypeName} instead + {biometryTypeName} instead of PIN Warn when sending over $100 - Security and Privacy + Security Settings Failed to complete a full backup Backing up... @@ -722,7 +730,8 @@ Report Issue Please describe the issue you are experiencing or ask a general question. App Status - Need help? Report your issue from within Bitkit, visit the help center, check the status, or reach out to us on our social channels. + Bitkit was crafted by Synonym Software, S.A. DE C.V. ©2025. All rights reserved. + Need help? Report your issue from within Bitkit or visit our help center. Send Thank you for contacting us! We will try to get back to you as soon as possible. OK @@ -734,7 +743,13 @@ Support Widgets Show Widget Titles - Widgets + Reset Suggestions Cards + Reset Widgets + Are you sure you want to reset the widgets? The default widget set with default configurations will be displayed. + Reset Widgets? + Display + Reset To Defaults + Show Widgets Own your\n<accent>profile</accent> Set up your public profile and links, so your Bitkit contacts can reach you or pay you anytime, anywhere. Profile @@ -848,6 +863,7 @@ Profile Settings Shop + Support App Status Wallet Widgets From ac561b0d6909a9256b418a6e5cfbecd7985ae25a Mon Sep 17 00:00:00 2001 From: jvsena42 Date: Fri, 20 Mar 2026 07:55:53 -0300 Subject: [PATCH 2/7] feat: implement swipe --- .../to/bitkit/ui/settings/SettingsScreen.kt | 113 +++++++++--------- 1 file changed, 59 insertions(+), 54 deletions(-) diff --git a/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt b/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt index f4001e640..e5f22e351 100644 --- a/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt @@ -3,14 +3,15 @@ package to.bitkit.ui.settings import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState 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 -import androidx.compose.runtime.setValue +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource @@ -19,6 +20,7 @@ import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController +import kotlinx.coroutines.launch import to.bitkit.R import to.bitkit.models.PrimaryDisplay import to.bitkit.models.TransactionSpeed @@ -218,8 +220,9 @@ private fun SettingsContent( // Navigation onBackClick: () -> Unit = {}, ) { - var selectedTab by remember { mutableStateOf(SettingsTab.General) } val tabs = remember { SettingsTab.entries } + val pagerState = rememberPagerState(pageCount = { tabs.size }) + val scope = rememberCoroutineScope() ScreenColumn { AppTopBar( @@ -230,62 +233,64 @@ private fun SettingsContent( CustomTabRowWithSpacing( tabs = tabs, - currentTabIndex = tabs.indexOf(selectedTab), - onTabChange = { selectedTab = it }, + currentTabIndex = pagerState.currentPage, + onTabChange = { scope.launch { pagerState.animateScrollToPage(tabs.indexOf(it)) } }, modifier = Modifier.padding(horizontal = 16.dp), ) - when (selectedTab) { - SettingsTab.General -> GeneralTabContent( - selectedCurrency = selectedCurrency, - primaryDisplay = primaryDisplay, - defaultTransactionSpeed = defaultTransactionSpeed, - selectedLanguage = selectedLanguage, - showTagsButton = showTagsButton, - notificationsGranted = notificationsGranted, - onLanguageClick = onLanguageClick, - onLocalCurrencyClick = onLocalCurrencyClick, - onDefaultUnitClick = onDefaultUnitClick, - onWidgetsClick = onWidgetsClick, - onTagsClick = onTagsClick, - onTransactionSpeedClick = onTransactionSpeedClick, - onQuickPayClick = onQuickPayClick, - onBgPaymentsClick = onBgPaymentsClick, - ) + HorizontalPager(state = pagerState) { page -> + when (tabs[page]) { + SettingsTab.General -> GeneralTabContent( + selectedCurrency = selectedCurrency, + primaryDisplay = primaryDisplay, + defaultTransactionSpeed = defaultTransactionSpeed, + selectedLanguage = selectedLanguage, + showTagsButton = showTagsButton, + notificationsGranted = notificationsGranted, + onLanguageClick = onLanguageClick, + onLocalCurrencyClick = onLocalCurrencyClick, + onDefaultUnitClick = onDefaultUnitClick, + onWidgetsClick = onWidgetsClick, + onTagsClick = onTagsClick, + onTransactionSpeedClick = onTransactionSpeedClick, + onQuickPayClick = onQuickPayClick, + onBgPaymentsClick = onBgPaymentsClick, + ) - SettingsTab.Security -> SecurityTabContent( - isPinEnabled = isPinEnabled, - isBiometricEnabled = isBiometricEnabled, - isPinForPaymentsEnabled = isPinForPaymentsEnabled, - enableSwipeToHideBalance = enableSwipeToHideBalance, - hideBalanceOnOpen = hideBalanceOnOpen, - enableAutoReadClipboard = enableAutoReadClipboard, - enableSendAmountWarning = enableSendAmountWarning, - isBiometrySupported = isBiometrySupported, - onBackupWalletClick = onBackupWalletClick, - onDataBackupsClick = onDataBackupsClick, - onResetWalletClick = onResetWalletClick, - onPinClick = onPinClick, - onPinForPaymentsClick = onPinForPaymentsClick, - onUseBiometricsClick = onUseBiometricsClick, - onSwipeToHideBalanceClick = onSwipeToHideBalanceClick, - onHideBalanceOnOpenClick = onHideBalanceOnOpenClick, - onAutoReadClipboardClick = onAutoReadClipboardClick, - onSendAmountWarningClick = onSendAmountWarningClick, - ) + SettingsTab.Security -> SecurityTabContent( + isPinEnabled = isPinEnabled, + isBiometricEnabled = isBiometricEnabled, + isPinForPaymentsEnabled = isPinForPaymentsEnabled, + enableSwipeToHideBalance = enableSwipeToHideBalance, + hideBalanceOnOpen = hideBalanceOnOpen, + enableAutoReadClipboard = enableAutoReadClipboard, + enableSendAmountWarning = enableSendAmountWarning, + isBiometrySupported = isBiometrySupported, + onBackupWalletClick = onBackupWalletClick, + onDataBackupsClick = onDataBackupsClick, + onResetWalletClick = onResetWalletClick, + onPinClick = onPinClick, + onPinForPaymentsClick = onPinForPaymentsClick, + onUseBiometricsClick = onUseBiometricsClick, + onSwipeToHideBalanceClick = onSwipeToHideBalanceClick, + onHideBalanceOnOpenClick = onHideBalanceOnOpenClick, + onAutoReadClipboardClick = onAutoReadClipboardClick, + onSendAmountWarningClick = onSendAmountWarningClick, + ) - SettingsTab.Advanced -> AdvancedTabContent( - isDevModeEnabled = isDevModeEnabled, - selectedAddressTypeName = selectedAddressTypeName, - onDevSettingsClick = onDevSettingsClick, - onAddressTypeClick = onAddressTypeClick, - onCoinSelectionClick = onCoinSelectionClick, - onAddressViewerClick = onAddressViewerClick, - onLightningConnectionsClick = onLightningConnectionsClick, - onLightningNodeClick = onLightningNodeClick, - onElectrumServerClick = onElectrumServerClick, - onRgsServerClick = onRgsServerClick, - ) + SettingsTab.Advanced -> AdvancedTabContent( + isDevModeEnabled = isDevModeEnabled, + selectedAddressTypeName = selectedAddressTypeName, + onDevSettingsClick = onDevSettingsClick, + onAddressTypeClick = onAddressTypeClick, + onCoinSelectionClick = onCoinSelectionClick, + onAddressViewerClick = onAddressViewerClick, + onLightningConnectionsClick = onLightningConnectionsClick, + onLightningNodeClick = onLightningNodeClick, + onElectrumServerClick = onElectrumServerClick, + onRgsServerClick = onRgsServerClick, + ) + } } } } From c57a54fcb30ba13986c8c5b348954d9f6fc50d64 Mon Sep 17 00:00:00 2001 From: jvsena42 Date: Fri, 20 Mar 2026 09:12:23 -0300 Subject: [PATCH 3/7] feat: icon color --- .../to/bitkit/ui/settings/SettingsScreen.kt | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt b/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt index e5f22e351..33d9ed16c 100644 --- a/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt @@ -53,6 +53,7 @@ import to.bitkit.ui.screens.wallets.activity.components.CustomTabRowWithSpacing import to.bitkit.ui.screens.wallets.activity.components.TabItem import to.bitkit.ui.settingsViewModel import to.bitkit.ui.theme.AppThemeSurface +import to.bitkit.ui.theme.Colors import to.bitkit.ui.utils.rememberBiometricAuthSupported import to.bitkit.viewmodels.LanguageViewModel @@ -324,6 +325,7 @@ private fun GeneralTabContent( SettingsButtonRow( title = stringResource(R.string.settings__language_title), iconRes = R.drawable.ic_warning, + iconTint = Colors.Brand, value = SettingsButtonValue.StringValue(selectedLanguage), onClick = onLanguageClick, modifier = Modifier.testTag("LanguageSettings"), @@ -331,6 +333,7 @@ private fun GeneralTabContent( SettingsButtonRow( title = stringResource(R.string.settings__general__currency_local), iconRes = R.drawable.ic_coins, + iconTint = Colors.Brand, value = SettingsButtonValue.StringValue(selectedCurrency), onClick = onLocalCurrencyClick, modifier = Modifier.testTag("CurrenciesSettings"), @@ -338,6 +341,7 @@ private fun GeneralTabContent( SettingsButtonRow( title = stringResource(R.string.settings__general__unit), iconRes = R.drawable.ic_coins, + iconTint = Colors.Brand, value = SettingsButtonValue.StringValue( when (primaryDisplay) { PrimaryDisplay.BITCOIN -> stringResource(R.string.settings__general__unit_bitcoin) @@ -350,6 +354,7 @@ private fun GeneralTabContent( SettingsButtonRow( title = stringResource(R.string.settings__widgets__nav_title), iconRes = R.drawable.ic_warning, + iconTint = Colors.Brand, onClick = onWidgetsClick, modifier = Modifier.testTag("WidgetsSettings"), ) @@ -357,6 +362,7 @@ private fun GeneralTabContent( SettingsButtonRow( title = stringResource(R.string.settings__general__tags), iconRes = R.drawable.ic_tag, + iconTint = Colors.Brand, onClick = onTagsClick, modifier = Modifier.testTag("TagsSettings"), ) @@ -371,6 +377,7 @@ private fun GeneralTabContent( SettingsButtonRow( title = stringResource(R.string.settings__general__speed), iconRes = R.drawable.ic_speed_normal, + iconTint = Colors.Brand, value = SettingsButtonValue.StringValue(defaultTransactionSpeed.transactionSpeedUiText()), onClick = onTransactionSpeedClick, modifier = Modifier.testTag("TransactionSpeedSettings"), @@ -378,12 +385,14 @@ private fun GeneralTabContent( SettingsButtonRow( title = stringResource(R.string.settings__quickpay__nav_title), iconRes = R.drawable.ic_warning, + iconTint = Colors.Brand, onClick = onQuickPayClick, modifier = Modifier.testTag("QuickpaySettings"), ) SettingsButtonRow( title = stringResource(R.string.settings__bg__title), iconRes = R.drawable.ic_bell, + iconTint = Colors.Brand, value = SettingsButtonValue.StringValue( stringResource(if (notificationsGranted) R.string.settings__bg__on else R.string.settings__bg__off) ), @@ -429,18 +438,21 @@ private fun SecurityTabContent( SettingsButtonRow( title = stringResource(R.string.settings__backup__wallet), iconRes = R.drawable.ic_warning, + iconTint = Colors.Brand, onClick = onBackupWalletClick, modifier = Modifier.testTag("BackupWallet"), ) SettingsButtonRow( title = stringResource(R.string.settings__backup__data), iconRes = R.drawable.ic_warning, + iconTint = Colors.Brand, onClick = onDataBackupsClick, modifier = Modifier.testTag("BackupSettings"), ) SettingsButtonRow( title = stringResource(R.string.settings__backup__reset), iconRes = R.drawable.ic_warning, + iconTint = Colors.Brand, onClick = onResetWalletClick, modifier = Modifier.testTag("ResetAndRestore"), ) @@ -454,6 +466,7 @@ private fun SecurityTabContent( SettingsButtonRow( title = stringResource(R.string.settings__security__pin), iconRes = R.drawable.ic_warning, + iconTint = Colors.Brand, value = SettingsButtonValue.StringValue( stringResource( if (isPinEnabled) { @@ -471,6 +484,7 @@ private fun SecurityTabContent( SettingsSwitchRow( title = stringResource(R.string.settings__security__pin_payments), iconRes = R.drawable.ic_coins, + iconTint = Colors.Brand, isChecked = isPinForPaymentsEnabled, onClick = onPinForPaymentsClick, modifier = Modifier.testTag("EnablePinForPayments"), @@ -484,6 +498,7 @@ private fun SecurityTabContent( .replace("{biometryTypeName}", bioTypeName) }, iconRes = R.drawable.ic_warning, + iconTint = Colors.Brand, isChecked = isBiometricEnabled, onClick = onUseBiometricsClick, modifier = Modifier.testTag("UseBiometryInstead"), @@ -494,6 +509,7 @@ private fun SecurityTabContent( SettingsSwitchRow( title = stringResource(R.string.settings__security__warn_100), iconRes = R.drawable.ic_warning, + iconTint = Colors.Brand, isChecked = enableSendAmountWarning, onClick = onSendAmountWarningClick, modifier = Modifier.testTag("SendAmountWarning"), @@ -508,6 +524,7 @@ private fun SecurityTabContent( SettingsSwitchRow( title = stringResource(R.string.settings__security__swipe_balance_to_hide), iconRes = R.drawable.ic_warning, + iconTint = Colors.Brand, isChecked = enableSwipeToHideBalance, onClick = onSwipeToHideBalanceClick, modifier = Modifier.testTag("SwipeBalanceToHide"), @@ -515,6 +532,7 @@ private fun SecurityTabContent( SettingsSwitchRow( title = stringResource(R.string.settings__security__hide_balance_on_open), iconRes = R.drawable.ic_warning, + iconTint = Colors.Brand, isChecked = hideBalanceOnOpen, onClick = onHideBalanceOnOpenClick, modifier = Modifier.testTag("HideBalanceOnOpen"), @@ -522,6 +540,7 @@ private fun SecurityTabContent( SettingsSwitchRow( title = stringResource(R.string.settings__security__clipboard), iconRes = R.drawable.ic_clipboard_text, + iconTint = Colors.Brand, isChecked = enableAutoReadClipboard, onClick = onAutoReadClipboardClick, modifier = Modifier.testTag("AutoReadClipboard"), @@ -558,6 +577,7 @@ private fun AdvancedTabContent( SettingsButtonRow( title = stringResource(R.string.settings__dev_title), iconRes = R.drawable.ic_settings_dev, + iconTint = Colors.Brand, onClick = onDevSettingsClick, modifier = Modifier.testTag("DevSettings"), ) @@ -569,6 +589,7 @@ private fun AdvancedTabContent( SettingsButtonRow( title = stringResource(R.string.settings__addr_type__title), iconRes = R.drawable.ic_warning, + iconTint = Colors.Brand, value = if (selectedAddressTypeName.isNotEmpty()) { SettingsButtonValue.StringValue(selectedAddressTypeName) } else { @@ -580,12 +601,14 @@ private fun AdvancedTabContent( SettingsButtonRow( title = stringResource(R.string.settings__adv__coin_selection), iconRes = R.drawable.ic_coins, + iconTint = Colors.Brand, onClick = onCoinSelectionClick, modifier = Modifier.testTag("CoinSelectPreference"), ) SettingsButtonRow( title = stringResource(R.string.settings__adv__address_viewer), iconRes = R.drawable.ic_eye, + iconTint = Colors.Brand, onClick = onAddressViewerClick, modifier = Modifier.testTag("AddressViewer"), ) @@ -599,24 +622,28 @@ private fun AdvancedTabContent( SettingsButtonRow( title = stringResource(R.string.settings__adv__lightning_connections), iconRes = R.drawable.ic_git_branch, + iconTint = Colors.Brand, onClick = onLightningConnectionsClick, modifier = Modifier.testTag("Channels"), ) SettingsButtonRow( title = stringResource(R.string.settings__adv__lightning_node), iconRes = R.drawable.ic_git_branch, + iconTint = Colors.Brand, onClick = onLightningNodeClick, modifier = Modifier.testTag("LightningNodeInfo"), ) SettingsButtonRow( title = stringResource(R.string.settings__adv__electrum_server), iconRes = R.drawable.ic_warning, + iconTint = Colors.Brand, onClick = onElectrumServerClick, modifier = Modifier.testTag("ElectrumConfig"), ) SettingsButtonRow( title = stringResource(R.string.settings__adv__rgs_server), iconRes = R.drawable.ic_broadcast, + iconTint = Colors.Brand, onClick = onRgsServerClick, modifier = Modifier.testTag("RGSServer"), ) From e55e01c9c85fddaa7df68969e41fc9c2cd5a477a Mon Sep 17 00:00:00 2001 From: jvsena42 Date: Fri, 20 Mar 2026 09:22:52 -0300 Subject: [PATCH 4/7] feat: selected tab color --- app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt b/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt index 33d9ed16c..c91f61626 100644 --- a/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt @@ -235,6 +235,7 @@ private fun SettingsContent( CustomTabRowWithSpacing( tabs = tabs, currentTabIndex = pagerState.currentPage, + selectedColor = Colors.White, onTabChange = { scope.launch { pagerState.animateScrollToPage(tabs.indexOf(it)) } }, modifier = Modifier.padding(horizontal = 16.dp), ) From 85fa8c83cf6f68c8d01fb7d18c9a0011d8912b8d Mon Sep 17 00:00:00 2001 From: jvsena42 Date: Fri, 20 Mar 2026 10:02:23 -0300 Subject: [PATCH 5/7] feat: set icons --- .../components/settings/SettingsButtonRow.kt | 84 ++++++++++++++++--- 1 file changed, 74 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/to/bitkit/ui/components/settings/SettingsButtonRow.kt b/app/src/main/java/to/bitkit/ui/components/settings/SettingsButtonRow.kt index 4e2fadc25..680a7ee49 100644 --- a/app/src/main/java/to/bitkit/ui/components/settings/SettingsButtonRow.kt +++ b/app/src/main/java/to/bitkit/ui/components/settings/SettingsButtonRow.kt @@ -34,7 +34,6 @@ import to.bitkit.ui.shared.modifiers.clickableAlpha import to.bitkit.ui.theme.AppThemeSurface import to.bitkit.ui.theme.Colors -@Suppress("CyclomaticComplexMethod") @Composable fun SettingsButtonRow( title: String, @@ -49,6 +48,77 @@ fun SettingsButtonRow( enabled: Boolean = true, loading: Boolean = false, onClick: () -> Unit, +) { + SettingsButtonRowCore( + title = title, + hasIcon = iconRes != null, + subtitle = subtitle, + value = value, + description = description, + maxLinesSubtitle = maxLinesSubtitle, + enabled = enabled, + loading = loading, + onClick = onClick, + icon = if (iconRes != null) { + { + Icon( + painter = painterResource(iconRes), + contentDescription = null, + tint = iconTint, + modifier = Modifier + .size(iconSize) + .padding(end = 10.dp), + ) + } + } else { + null + }, + modifier = modifier, + ) +} + +@Composable +fun SettingsButtonRow( + title: String, + icon: @Composable () -> Unit, + modifier: Modifier = Modifier, + subtitle: String? = null, + value: SettingsButtonValue = SettingsButtonValue.None, + description: String? = null, + maxLinesSubtitle: Int = Int.MAX_VALUE, + enabled: Boolean = true, + loading: Boolean = false, + onClick: () -> Unit, +) { + SettingsButtonRowCore( + title = title, + hasIcon = true, + icon = { icon() }, + subtitle = subtitle, + value = value, + description = description, + maxLinesSubtitle = maxLinesSubtitle, + enabled = enabled, + loading = loading, + onClick = onClick, + modifier = modifier, + ) +} + +@Suppress("CyclomaticComplexMethod") +@Composable +private fun SettingsButtonRowCore( + title: String, + hasIcon: Boolean, + modifier: Modifier = Modifier, + icon: (@Composable () -> Unit)? = null, + subtitle: String? = null, + value: SettingsButtonValue = SettingsButtonValue.None, + description: String? = null, + maxLinesSubtitle: Int = Int.MAX_VALUE, + enabled: Boolean = true, + loading: Boolean = false, + onClick: () -> Unit, ) { val alphaModifier = Modifier.then(if (!enabled) Modifier.alpha(0.5f) else Modifier) Column( @@ -57,7 +127,7 @@ fun SettingsButtonRow( ) { Column(modifier = alphaModifier) { val rowHeight = when { - subtitle != null && iconRes != null -> 90.dp + subtitle != null && hasIcon -> 90.dp subtitle != null -> 74.dp else -> 52.dp } @@ -67,14 +137,8 @@ fun SettingsButtonRow( .fillMaxWidth() .heightIn(min = rowHeight) ) { - if (iconRes != null) { - Icon( - painter = painterResource(iconRes), - contentDescription = null, - tint = iconTint, - modifier = Modifier.size(iconSize), - ) - Spacer(modifier = Modifier.width(10.dp)) + if (icon != null) { + icon() } Column( verticalArrangement = Arrangement.Center, From 1e96e0f74ddadd2d6db106212bd1e7e9c8d4fcf9 Mon Sep 17 00:00:00 2001 From: jvsena42 Date: Fri, 20 Mar 2026 11:28:44 -0300 Subject: [PATCH 6/7] feat: set icons --- .../components/settings/SettingsSwitchRow.kt | 64 +++++++++-- .../to/bitkit/ui/settings/SettingsScreen.kt | 105 +++++++++--------- .../ui/settings/support/SupportScreen.kt | 4 +- .../drawable/ic_arrow_counter_clockwise.xml | 14 +++ app/src/main/res/drawable/ic_bell.xml | 22 ++-- .../main/res/drawable/ic_bitcoin_modern.xml | 9 ++ .../res/drawable/ic_caret_double_right.xml | 14 +++ .../main/res/drawable/ic_clipboard_text.xml | 29 ++--- app/src/main/res/drawable/ic_database.xml | 18 +++ .../res/drawable/ic_device_mobile_speaker.xml | 19 ++++ app/src/main/res/drawable/ic_eye_slash.xml | 26 +++++ app/src/main/res/drawable/ic_file_text.xml | 9 ++ .../main/res/drawable/ic_hand_pointing.xml | 14 +++ app/src/main/res/drawable/ic_hard_drives.xml | 20 ++++ app/src/main/res/drawable/ic_lightning.xml | 17 +-- app/src/main/res/drawable/ic_list_dashes.xml | 30 +++++ app/src/main/res/drawable/ic_lock_key.xml | 22 ++++ app/src/main/res/drawable/ic_question.xml | 9 ++ app/src/main/res/drawable/ic_shield.xml | 14 +++ app/src/main/res/drawable/ic_smiley.xml | 20 ++++ app/src/main/res/drawable/ic_stack.xml | 15 +-- app/src/main/res/drawable/ic_stop_circle.xml | 14 +++ app/src/main/res/drawable/ic_translate.xml | 30 +++++ 23 files changed, 427 insertions(+), 111 deletions(-) create mode 100644 app/src/main/res/drawable/ic_arrow_counter_clockwise.xml create mode 100644 app/src/main/res/drawable/ic_bitcoin_modern.xml create mode 100644 app/src/main/res/drawable/ic_caret_double_right.xml create mode 100644 app/src/main/res/drawable/ic_database.xml create mode 100644 app/src/main/res/drawable/ic_device_mobile_speaker.xml create mode 100644 app/src/main/res/drawable/ic_eye_slash.xml create mode 100644 app/src/main/res/drawable/ic_file_text.xml create mode 100644 app/src/main/res/drawable/ic_hand_pointing.xml create mode 100644 app/src/main/res/drawable/ic_hard_drives.xml create mode 100644 app/src/main/res/drawable/ic_list_dashes.xml create mode 100644 app/src/main/res/drawable/ic_lock_key.xml create mode 100644 app/src/main/res/drawable/ic_question.xml create mode 100644 app/src/main/res/drawable/ic_shield.xml create mode 100644 app/src/main/res/drawable/ic_smiley.xml create mode 100644 app/src/main/res/drawable/ic_stop_circle.xml create mode 100644 app/src/main/res/drawable/ic_translate.xml diff --git a/app/src/main/java/to/bitkit/ui/components/settings/SettingsSwitchRow.kt b/app/src/main/java/to/bitkit/ui/components/settings/SettingsSwitchRow.kt index 797ab09c3..bb8a86b7e 100644 --- a/app/src/main/java/to/bitkit/ui/components/settings/SettingsSwitchRow.kt +++ b/app/src/main/java/to/bitkit/ui/components/settings/SettingsSwitchRow.kt @@ -39,6 +39,60 @@ fun SettingsSwitchRow( iconRes: Int? = null, iconTint: Color = Color.Unspecified, colors: SwitchColors = AppSwitchDefaults.colors, +) { + SettingsSwitchRowCore( + title = title, + isChecked = isChecked, + onClick = onClick, + subtitle = subtitle, + colors = colors, + icon = if (iconRes != null) { + { + Icon( + painter = painterResource(iconRes), + contentDescription = null, + tint = iconTint, + modifier = Modifier.size(32.dp), + ) + Spacer(modifier = Modifier.width(10.dp)) + } + } else { + null + }, + modifier = modifier, + ) +} + +@Composable +fun SettingsSwitchRow( + title: String, + isChecked: Boolean, + icon: @Composable () -> Unit, + onClick: () -> Unit, + modifier: Modifier = Modifier, + subtitle: String? = null, + colors: SwitchColors = AppSwitchDefaults.colors, +) { + SettingsSwitchRowCore( + title = title, + isChecked = isChecked, + onClick = onClick, + subtitle = subtitle, + colors = colors, + icon = { icon() }, + modifier = modifier, + ) +} + +@Composable +private fun SettingsSwitchRowCore( + title: String, + isChecked: Boolean, + onClick: () -> Unit, + modifier: Modifier = Modifier, + subtitle: String? = null, + icon: (@Composable () -> Unit)? = null, + colors: SwitchColors = AppSwitchDefaults.colors, ) { Column( modifier = modifier.heightIn(min = 52.dp), @@ -51,14 +105,8 @@ fun SettingsSwitchRow( .clickableAlpha { onClick() } .padding(vertical = 16.dp) ) { - if (iconRes != null) { - Icon( - painter = painterResource(iconRes), - contentDescription = null, - tint = iconTint, - modifier = Modifier.size(32.dp), - ) - Spacer(modifier = Modifier.width(10.dp)) + if (icon != null) { + icon() } Column( diff --git a/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt b/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt index c91f61626..d4ed365cf 100644 --- a/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt @@ -1,19 +1,27 @@ package to.bitkit.ui.settings +import androidx.annotation.DrawableRes +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Icon import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -29,6 +37,7 @@ import to.bitkit.ui.LocalCurrencies import to.bitkit.ui.Routes import to.bitkit.ui.appViewModel import to.bitkit.ui.components.AuthCheckAction +import to.bitkit.ui.components.HorizontalSpacer import to.bitkit.ui.components.Sheet import to.bitkit.ui.components.VerticalSpacer import to.bitkit.ui.components.settings.SectionHeader @@ -325,24 +334,21 @@ private fun GeneralTabContent( SettingsButtonRow( title = stringResource(R.string.settings__language_title), - iconRes = R.drawable.ic_warning, - iconTint = Colors.Brand, + icon = { SettingsIcon(R.drawable.ic_translate) }, value = SettingsButtonValue.StringValue(selectedLanguage), onClick = onLanguageClick, modifier = Modifier.testTag("LanguageSettings"), ) SettingsButtonRow( title = stringResource(R.string.settings__general__currency_local), - iconRes = R.drawable.ic_coins, - iconTint = Colors.Brand, + icon = { SettingsIcon(R.drawable.ic_coins) }, value = SettingsButtonValue.StringValue(selectedCurrency), onClick = onLocalCurrencyClick, modifier = Modifier.testTag("CurrenciesSettings"), ) SettingsButtonRow( title = stringResource(R.string.settings__general__unit), - iconRes = R.drawable.ic_coins, - iconTint = Colors.Brand, + icon = { SettingsIcon(R.drawable.ic_bitcoin_modern) }, value = SettingsButtonValue.StringValue( when (primaryDisplay) { PrimaryDisplay.BITCOIN -> stringResource(R.string.settings__general__unit_bitcoin) @@ -354,16 +360,14 @@ private fun GeneralTabContent( ) SettingsButtonRow( title = stringResource(R.string.settings__widgets__nav_title), - iconRes = R.drawable.ic_warning, - iconTint = Colors.Brand, + icon = { SettingsIcon(R.drawable.ic_stack) }, onClick = onWidgetsClick, modifier = Modifier.testTag("WidgetsSettings"), ) if (showTagsButton) { SettingsButtonRow( title = stringResource(R.string.settings__general__tags), - iconRes = R.drawable.ic_tag, - iconTint = Colors.Brand, + icon = { SettingsIcon(R.drawable.ic_tag) }, onClick = onTagsClick, modifier = Modifier.testTag("TagsSettings"), ) @@ -377,23 +381,20 @@ private fun GeneralTabContent( SettingsButtonRow( title = stringResource(R.string.settings__general__speed), - iconRes = R.drawable.ic_speed_normal, - iconTint = Colors.Brand, + icon = { SettingsIcon(R.drawable.ic_speed_normal) }, value = SettingsButtonValue.StringValue(defaultTransactionSpeed.transactionSpeedUiText()), onClick = onTransactionSpeedClick, modifier = Modifier.testTag("TransactionSpeedSettings"), ) SettingsButtonRow( title = stringResource(R.string.settings__quickpay__nav_title), - iconRes = R.drawable.ic_warning, - iconTint = Colors.Brand, + icon = { SettingsIcon(R.drawable.ic_caret_double_right) }, onClick = onQuickPayClick, modifier = Modifier.testTag("QuickpaySettings"), ) SettingsButtonRow( title = stringResource(R.string.settings__bg__title), - iconRes = R.drawable.ic_bell, - iconTint = Colors.Brand, + icon = { SettingsIcon(R.drawable.ic_bell) }, value = SettingsButtonValue.StringValue( stringResource(if (notificationsGranted) R.string.settings__bg__on else R.string.settings__bg__off) ), @@ -438,22 +439,19 @@ private fun SecurityTabContent( SettingsButtonRow( title = stringResource(R.string.settings__backup__wallet), - iconRes = R.drawable.ic_warning, - iconTint = Colors.Brand, + icon = { SettingsIcon(R.drawable.ic_lock_key) }, onClick = onBackupWalletClick, modifier = Modifier.testTag("BackupWallet"), ) SettingsButtonRow( title = stringResource(R.string.settings__backup__data), - iconRes = R.drawable.ic_warning, - iconTint = Colors.Brand, + icon = { SettingsIcon(R.drawable.ic_database) }, onClick = onDataBackupsClick, modifier = Modifier.testTag("BackupSettings"), ) SettingsButtonRow( title = stringResource(R.string.settings__backup__reset), - iconRes = R.drawable.ic_warning, - iconTint = Colors.Brand, + icon = { SettingsIcon(R.drawable.ic_arrow_counter_clockwise) }, onClick = onResetWalletClick, modifier = Modifier.testTag("ResetAndRestore"), ) @@ -466,8 +464,7 @@ private fun SecurityTabContent( SettingsButtonRow( title = stringResource(R.string.settings__security__pin), - iconRes = R.drawable.ic_warning, - iconTint = Colors.Brand, + icon = { SettingsIcon(R.drawable.ic_shield) }, value = SettingsButtonValue.StringValue( stringResource( if (isPinEnabled) { @@ -484,8 +481,7 @@ private fun SecurityTabContent( if (isPinEnabled) { SettingsSwitchRow( title = stringResource(R.string.settings__security__pin_payments), - iconRes = R.drawable.ic_coins, - iconTint = Colors.Brand, + icon = { SettingsIcon(R.drawable.ic_coins) }, isChecked = isPinForPaymentsEnabled, onClick = onPinForPaymentsClick, modifier = Modifier.testTag("EnablePinForPayments"), @@ -498,8 +494,7 @@ private fun SecurityTabContent( stringResource(R.string.settings__security__use_bio) .replace("{biometryTypeName}", bioTypeName) }, - iconRes = R.drawable.ic_warning, - iconTint = Colors.Brand, + icon = { SettingsIcon(R.drawable.ic_smiley) }, isChecked = isBiometricEnabled, onClick = onUseBiometricsClick, modifier = Modifier.testTag("UseBiometryInstead"), @@ -509,8 +504,7 @@ private fun SecurityTabContent( SettingsSwitchRow( title = stringResource(R.string.settings__security__warn_100), - iconRes = R.drawable.ic_warning, - iconTint = Colors.Brand, + icon = { SettingsIcon(R.drawable.ic_warning) }, isChecked = enableSendAmountWarning, onClick = onSendAmountWarningClick, modifier = Modifier.testTag("SendAmountWarning"), @@ -524,24 +518,21 @@ private fun SecurityTabContent( SettingsSwitchRow( title = stringResource(R.string.settings__security__swipe_balance_to_hide), - iconRes = R.drawable.ic_warning, - iconTint = Colors.Brand, + icon = { SettingsIcon(R.drawable.ic_hand_pointing) }, isChecked = enableSwipeToHideBalance, onClick = onSwipeToHideBalanceClick, modifier = Modifier.testTag("SwipeBalanceToHide"), ) SettingsSwitchRow( title = stringResource(R.string.settings__security__hide_balance_on_open), - iconRes = R.drawable.ic_warning, - iconTint = Colors.Brand, + icon = { SettingsIcon(R.drawable.ic_eye_slash) }, isChecked = hideBalanceOnOpen, onClick = onHideBalanceOnOpenClick, modifier = Modifier.testTag("HideBalanceOnOpen"), ) SettingsSwitchRow( title = stringResource(R.string.settings__security__clipboard), - iconRes = R.drawable.ic_clipboard_text, - iconTint = Colors.Brand, + icon = { SettingsIcon(R.drawable.ic_clipboard_text) }, isChecked = enableAutoReadClipboard, onClick = onAutoReadClipboardClick, modifier = Modifier.testTag("AutoReadClipboard"), @@ -577,8 +568,7 @@ private fun AdvancedTabContent( SettingsButtonRow( title = stringResource(R.string.settings__dev_title), - iconRes = R.drawable.ic_settings_dev, - iconTint = Colors.Brand, + icon = { SettingsIcon(R.drawable.ic_settings_dev) }, onClick = onDevSettingsClick, modifier = Modifier.testTag("DevSettings"), ) @@ -589,8 +579,7 @@ private fun AdvancedTabContent( SettingsButtonRow( title = stringResource(R.string.settings__addr_type__title), - iconRes = R.drawable.ic_warning, - iconTint = Colors.Brand, + icon = { SettingsIcon(R.drawable.ic_list_dashes) }, value = if (selectedAddressTypeName.isNotEmpty()) { SettingsButtonValue.StringValue(selectedAddressTypeName) } else { @@ -601,15 +590,13 @@ private fun AdvancedTabContent( ) SettingsButtonRow( title = stringResource(R.string.settings__adv__coin_selection), - iconRes = R.drawable.ic_coins, - iconTint = Colors.Brand, + icon = { SettingsIcon(R.drawable.ic_coins) }, onClick = onCoinSelectionClick, modifier = Modifier.testTag("CoinSelectPreference"), ) SettingsButtonRow( title = stringResource(R.string.settings__adv__address_viewer), - iconRes = R.drawable.ic_eye, - iconTint = Colors.Brand, + icon = { SettingsIcon(R.drawable.ic_eye) }, onClick = onAddressViewerClick, modifier = Modifier.testTag("AddressViewer"), ) @@ -622,29 +609,25 @@ private fun AdvancedTabContent( SettingsButtonRow( title = stringResource(R.string.settings__adv__lightning_connections), - iconRes = R.drawable.ic_git_branch, - iconTint = Colors.Brand, + icon = { SettingsIcon(R.drawable.ic_lightning) }, onClick = onLightningConnectionsClick, modifier = Modifier.testTag("Channels"), ) SettingsButtonRow( title = stringResource(R.string.settings__adv__lightning_node), - iconRes = R.drawable.ic_git_branch, - iconTint = Colors.Brand, + icon = { SettingsIcon(R.drawable.ic_git_branch) }, onClick = onLightningNodeClick, modifier = Modifier.testTag("LightningNodeInfo"), ) SettingsButtonRow( title = stringResource(R.string.settings__adv__electrum_server), - iconRes = R.drawable.ic_warning, - iconTint = Colors.Brand, + icon = { SettingsIcon(R.drawable.ic_hard_drives) }, onClick = onElectrumServerClick, modifier = Modifier.testTag("ElectrumConfig"), ) SettingsButtonRow( title = stringResource(R.string.settings__adv__rgs_server), - iconRes = R.drawable.ic_broadcast, - iconTint = Colors.Brand, + icon = { SettingsIcon(R.drawable.ic_broadcast) }, onClick = onRgsServerClick, modifier = Modifier.testTag("RGSServer"), ) @@ -653,6 +636,24 @@ private fun AdvancedTabContent( } } +@Composable +private fun SettingsIcon(@DrawableRes iconRes: Int) { + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .size(32.dp) + .background(color = Colors.Black, shape = CircleShape), + ) { + Icon( + painter = painterResource(iconRes), + contentDescription = null, + tint = Colors.Brand, + modifier = Modifier.size(16.dp), + ) + } + HorizontalSpacer(8.dp) +} + @Preview(showBackground = true) @Composable private fun PreviewGeneral() { diff --git a/app/src/main/java/to/bitkit/ui/settings/support/SupportScreen.kt b/app/src/main/java/to/bitkit/ui/settings/support/SupportScreen.kt index 81d8b823f..5e43e2029 100644 --- a/app/src/main/java/to/bitkit/ui/settings/support/SupportScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/support/SupportScreen.kt @@ -149,7 +149,7 @@ private fun Content( ) SettingsButtonRow( title = stringResource(R.string.settings__support__help), - iconRes = R.drawable.ic_warning, + iconRes = R.drawable.ic_question, onClick = onClickHelpCenter, ) SettingsButtonRow( @@ -160,7 +160,7 @@ private fun Content( ) SettingsButtonRow( title = stringResource(R.string.settings__about__legal), - iconRes = R.drawable.ic_warning, + iconRes = R.drawable.ic_file_text, onClick = onClickLegal, ) SettingsButtonRow( diff --git a/app/src/main/res/drawable/ic_arrow_counter_clockwise.xml b/app/src/main/res/drawable/ic_arrow_counter_clockwise.xml new file mode 100644 index 000000000..3e6f603a8 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_counter_clockwise.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/ic_bell.xml b/app/src/main/res/drawable/ic_bell.xml index 2c95aa02a..a7b0fb77b 100644 --- a/app/src/main/res/drawable/ic_bell.xml +++ b/app/src/main/res/drawable/ic_bell.xml @@ -1,24 +1,24 @@ + android:pathData="M2,1h11.91v14.5h-11.91z"/> + android:pathData="M9.46,2H6.46C6.185,2 5.96,1.775 5.96,1.5C5.96,1.225 6.185,1 6.46,1H9.46C9.735,1 9.96,1.225 9.96,1.5C9.96,1.775 9.735,2 9.46,2Z" + android:fillColor="#FF4400"/> + android:pathData="M3.005,13.5C2.645,13.5 2.31,13.305 2.13,12.99C1.955,12.68 1.955,12.31 2.13,12.005C2.505,11.365 2.955,10.155 2.955,7.995C2.955,7.285 3.1,6.595 3.395,5.95C3.685,5.3 4.1,4.735 4.635,4.26C5.165,3.79 5.78,3.44 6.455,3.225C7.13,3.01 7.835,2.945 8.54,3.025C11.02,3.305 12.925,5.53 12.96,8.205C12.99,10.22 13.415,11.37 13.77,11.98C13.855,12.125 13.905,12.295 13.91,12.475C13.91,12.65 13.87,12.825 13.785,12.98C13.695,13.135 13.57,13.265 13.42,13.355C13.265,13.445 13.095,13.495 12.915,13.495H3.005V13.5ZM7.955,4C7.55,4 7.145,4.06 6.755,4.185C6.215,4.355 5.725,4.635 5.3,5.01C4.875,5.39 4.54,5.845 4.31,6.36C4.075,6.88 3.96,7.43 3.96,8C3.96,10.385 3.435,11.77 3,12.515L12.915,12.5C12.495,11.775 11.995,10.455 11.965,8.225C11.935,6.055 10.415,4.25 8.43,4.025C8.275,4.005 8.115,4 7.96,4H7.955Z" + android:fillColor="#FF4400"/> + android:pathData="M7.96,15.5C7.295,15.5 6.665,15.24 6.19,14.77C5.72,14.3 5.46,13.67 5.46,13C5.46,12.725 5.685,12.5 5.96,12.5C6.235,12.5 6.46,12.725 6.46,13C6.46,13.395 6.62,13.78 6.9,14.06C7.465,14.62 8.46,14.62 9.02,14.06C9.3,13.78 9.46,13.395 9.46,13C9.46,12.725 9.685,12.5 9.96,12.5C10.235,12.5 10.46,12.725 10.46,13C10.46,13.665 10.2,14.295 9.73,14.77C9.26,15.245 8.63,15.5 7.96,15.5V15.5Z" + android:fillColor="#FF4400"/> diff --git a/app/src/main/res/drawable/ic_bitcoin_modern.xml b/app/src/main/res/drawable/ic_bitcoin_modern.xml new file mode 100644 index 000000000..67986df79 --- /dev/null +++ b/app/src/main/res/drawable/ic_bitcoin_modern.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_caret_double_right.xml b/app/src/main/res/drawable/ic_caret_double_right.xml new file mode 100644 index 000000000..b1021748f --- /dev/null +++ b/app/src/main/res/drawable/ic_caret_double_right.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/ic_clipboard_text.xml b/app/src/main/res/drawable/ic_clipboard_text.xml index c3f495f06..74aaf5fed 100644 --- a/app/src/main/res/drawable/ic_clipboard_text.xml +++ b/app/src/main/res/drawable/ic_clipboard_text.xml @@ -1,27 +1,22 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="11" + android:viewportHeight="13.5"> - diff --git a/app/src/main/res/drawable/ic_database.xml b/app/src/main/res/drawable/ic_database.xml new file mode 100644 index 000000000..4ef8cc60d --- /dev/null +++ b/app/src/main/res/drawable/ic_database.xml @@ -0,0 +1,18 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_device_mobile_speaker.xml b/app/src/main/res/drawable/ic_device_mobile_speaker.xml new file mode 100644 index 000000000..a18ef193c --- /dev/null +++ b/app/src/main/res/drawable/ic_device_mobile_speaker.xml @@ -0,0 +1,19 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_eye_slash.xml b/app/src/main/res/drawable/ic_eye_slash.xml new file mode 100644 index 000000000..36e0eeeda --- /dev/null +++ b/app/src/main/res/drawable/ic_eye_slash.xml @@ -0,0 +1,26 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_file_text.xml b/app/src/main/res/drawable/ic_file_text.xml new file mode 100644 index 000000000..e63887dbc --- /dev/null +++ b/app/src/main/res/drawable/ic_file_text.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_hand_pointing.xml b/app/src/main/res/drawable/ic_hand_pointing.xml new file mode 100644 index 000000000..71bc461b8 --- /dev/null +++ b/app/src/main/res/drawable/ic_hand_pointing.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/ic_hard_drives.xml b/app/src/main/res/drawable/ic_hard_drives.xml new file mode 100644 index 000000000..7e98a27ea --- /dev/null +++ b/app/src/main/res/drawable/ic_hard_drives.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_lightning.xml b/app/src/main/res/drawable/ic_lightning.xml index ab0f9788a..2e265992f 100644 --- a/app/src/main/res/drawable/ic_lightning.xml +++ b/app/src/main/res/drawable/ic_lightning.xml @@ -1,15 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="11" + android:viewportHeight="15"> - diff --git a/app/src/main/res/drawable/ic_list_dashes.xml b/app/src/main/res/drawable/ic_list_dashes.xml new file mode 100644 index 000000000..92c0b1f78 --- /dev/null +++ b/app/src/main/res/drawable/ic_list_dashes.xml @@ -0,0 +1,30 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_lock_key.xml b/app/src/main/res/drawable/ic_lock_key.xml new file mode 100644 index 000000000..b25415a7f --- /dev/null +++ b/app/src/main/res/drawable/ic_lock_key.xml @@ -0,0 +1,22 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_question.xml b/app/src/main/res/drawable/ic_question.xml new file mode 100644 index 000000000..c1fa561ef --- /dev/null +++ b/app/src/main/res/drawable/ic_question.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_shield.xml b/app/src/main/res/drawable/ic_shield.xml new file mode 100644 index 000000000..ab8dd87af --- /dev/null +++ b/app/src/main/res/drawable/ic_shield.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/ic_smiley.xml b/app/src/main/res/drawable/ic_smiley.xml new file mode 100644 index 000000000..eb0ab1566 --- /dev/null +++ b/app/src/main/res/drawable/ic_smiley.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_stack.xml b/app/src/main/res/drawable/ic_stack.xml index 9c2d43e62..1fa1f36f0 100644 --- a/app/src/main/res/drawable/ic_stack.xml +++ b/app/src/main/res/drawable/ic_stack.xml @@ -1,23 +1,18 @@ + android:viewportWidth="13" + android:viewportHeight="14"> - diff --git a/app/src/main/res/drawable/ic_stop_circle.xml b/app/src/main/res/drawable/ic_stop_circle.xml new file mode 100644 index 000000000..4ff35298e --- /dev/null +++ b/app/src/main/res/drawable/ic_stop_circle.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/ic_translate.xml b/app/src/main/res/drawable/ic_translate.xml new file mode 100644 index 000000000..449df7825 --- /dev/null +++ b/app/src/main/res/drawable/ic_translate.xml @@ -0,0 +1,30 @@ + + + + + + + + From aa21f8ae5afc55ff792ab1072753b6393681111d Mon Sep 17 00:00:00 2001 From: jvsena42 Date: Fri, 20 Mar 2026 13:47:41 -0300 Subject: [PATCH 7/7] feat: Support screen style --- .../to/bitkit/ui/components/settings/Links.kt | 13 ++- .../ui/components/settings/SettingsIcon.kt | 33 ++++++ .../to/bitkit/ui/settings/SettingsScreen.kt | 28 +---- .../ui/settings/support/SupportScreen.kt | 105 +++++++++++++----- 4 files changed, 116 insertions(+), 63 deletions(-) create mode 100644 app/src/main/java/to/bitkit/ui/components/settings/SettingsIcon.kt diff --git a/app/src/main/java/to/bitkit/ui/components/settings/Links.kt b/app/src/main/java/to/bitkit/ui/components/settings/Links.kt index 94728fc37..13743b6b0 100644 --- a/app/src/main/java/to/bitkit/ui/components/settings/Links.kt +++ b/app/src/main/java/to/bitkit/ui/components/settings/Links.kt @@ -9,6 +9,7 @@ import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp @@ -30,7 +31,7 @@ fun Links(modifier: Modifier = Modifier) { val intent = Intent(Intent.ACTION_VIEW, Env.BITKIT_WEBSITE.toUri()) context.startActivity(intent) }, - containerColor = Colors.White16, + containerColor = Color.Transparent, modifier = Modifier.size(48.dp) ) { Icon( @@ -44,7 +45,7 @@ fun Links(modifier: Modifier = Modifier) { val intent = Intent(Intent.ACTION_VIEW, Env.SYNONYM_MEDIUM.toUri()) context.startActivity(intent) }, - containerColor = Colors.White16, + containerColor = Color.Transparent, modifier = Modifier.size(48.dp) ) { Icon( @@ -58,7 +59,7 @@ fun Links(modifier: Modifier = Modifier) { val intent = Intent(Intent.ACTION_VIEW, Env.SYNONYM_X.toUri()) context.startActivity(intent) }, - containerColor = Colors.White16, + containerColor = Color.Transparent, modifier = Modifier.size(48.dp) ) { Icon( @@ -72,7 +73,7 @@ fun Links(modifier: Modifier = Modifier) { val intent = Intent(Intent.ACTION_VIEW, Env.BITKIT_DISCORD.toUri()) context.startActivity(intent) }, - containerColor = Colors.White16, + containerColor = Color.Transparent, modifier = Modifier.size(48.dp) ) { Icon( @@ -86,7 +87,7 @@ fun Links(modifier: Modifier = Modifier) { val intent = Intent(Intent.ACTION_VIEW, Env.BITKIT_TELEGRAM.toUri()) context.startActivity(intent) }, - containerColor = Colors.White16, + containerColor = Color.Transparent, modifier = Modifier.size(48.dp) ) { Icon( @@ -100,7 +101,7 @@ fun Links(modifier: Modifier = Modifier) { val intent = Intent(Intent.ACTION_VIEW, Env.BITKIT_GITHUB.toUri()) context.startActivity(intent) }, - containerColor = Colors.White16, + containerColor = Color.Transparent, modifier = Modifier.size(48.dp) ) { Icon( diff --git a/app/src/main/java/to/bitkit/ui/components/settings/SettingsIcon.kt b/app/src/main/java/to/bitkit/ui/components/settings/SettingsIcon.kt new file mode 100644 index 000000000..ebbbfd126 --- /dev/null +++ b/app/src/main/java/to/bitkit/ui/components/settings/SettingsIcon.kt @@ -0,0 +1,33 @@ +package to.bitkit.ui.components.settings + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import to.bitkit.ui.components.HorizontalSpacer +import to.bitkit.ui.theme.Colors + +@Composable +fun SettingsIcon(@DrawableRes iconRes: Int) { + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .size(32.dp) + .background(color = Colors.Black, shape = CircleShape), + ) { + Icon( + painter = painterResource(iconRes), + contentDescription = null, + tint = Colors.Brand, + modifier = Modifier.size(16.dp), + ) + } + HorizontalSpacer(8.dp) +} diff --git a/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt b/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt index d4ed365cf..3fe68eff8 100644 --- a/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt @@ -1,27 +1,19 @@ package to.bitkit.ui.settings -import androidx.annotation.DrawableRes -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.Icon import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -37,12 +29,12 @@ import to.bitkit.ui.LocalCurrencies import to.bitkit.ui.Routes import to.bitkit.ui.appViewModel import to.bitkit.ui.components.AuthCheckAction -import to.bitkit.ui.components.HorizontalSpacer import to.bitkit.ui.components.Sheet import to.bitkit.ui.components.VerticalSpacer import to.bitkit.ui.components.settings.SectionHeader import to.bitkit.ui.components.settings.SettingsButtonRow import to.bitkit.ui.components.settings.SettingsButtonValue +import to.bitkit.ui.components.settings.SettingsIcon import to.bitkit.ui.components.settings.SettingsSwitchRow import to.bitkit.ui.navigateTo import to.bitkit.ui.navigateToAuthCheck @@ -636,24 +628,6 @@ private fun AdvancedTabContent( } } -@Composable -private fun SettingsIcon(@DrawableRes iconRes: Int) { - Box( - contentAlignment = Alignment.Center, - modifier = Modifier - .size(32.dp) - .background(color = Colors.Black, shape = CircleShape), - ) { - Icon( - painter = painterResource(iconRes), - contentDescription = null, - tint = Colors.Brand, - modifier = Modifier.size(16.dp), - ) - } - HorizontalSpacer(8.dp) -} - @Preview(showBackground = true) @Composable private fun PreviewGeneral() { diff --git a/app/src/main/java/to/bitkit/ui/settings/support/SupportScreen.kt b/app/src/main/java/to/bitkit/ui/settings/support/SupportScreen.kt index 5e43e2029..08252aada 100644 --- a/app/src/main/java/to/bitkit/ui/settings/support/SupportScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/support/SupportScreen.kt @@ -2,19 +2,27 @@ package to.bitkit.ui.settings.support import android.content.Intent import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.HorizontalDivider import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clipToBounds +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.graphics.Path import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalHapticFeedback @@ -33,21 +41,22 @@ import to.bitkit.models.Toast import to.bitkit.ui.Routes import to.bitkit.ui.appViewModel import to.bitkit.ui.components.BodyM -import to.bitkit.ui.components.BodyS import to.bitkit.ui.components.VerticalSpacer import to.bitkit.ui.components.settings.Links import to.bitkit.ui.components.settings.SettingsButtonRow -import to.bitkit.ui.components.settings.SettingsButtonValue +import to.bitkit.ui.components.settings.SettingsIcon import to.bitkit.ui.navigateTo import to.bitkit.ui.scaffold.AppTopBar import to.bitkit.ui.scaffold.DrawerNavIcon import to.bitkit.ui.scaffold.ScreenColumn import to.bitkit.ui.settingsViewModel +import to.bitkit.ui.shared.modifiers.clickableAlpha import to.bitkit.ui.shared.util.shareText import to.bitkit.ui.theme.AppThemeSurface import to.bitkit.ui.theme.Colors private const val DEV_MODE_TAP_THRESHOLD = 5 +private val BrandColor = Colors.Brand @Composable fun SupportScreen( @@ -144,60 +153,96 @@ private fun Content( SettingsButtonRow( title = stringResource(R.string.settings__support__report), - iconRes = R.drawable.ic_warning, + icon = { SettingsIcon(R.drawable.ic_warning) }, onClick = onClickReportIssue, ) SettingsButtonRow( title = stringResource(R.string.settings__support__help), - iconRes = R.drawable.ic_question, + icon = { SettingsIcon(R.drawable.ic_question) }, onClick = onClickHelpCenter, ) SettingsButtonRow( title = stringResource(R.string.settings__support__status), - iconRes = R.drawable.ic_settings_support, + icon = { SettingsIcon(R.drawable.ic_power) }, onClick = onClickAppStatus, modifier = Modifier.testTag("AppStatus"), ) SettingsButtonRow( title = stringResource(R.string.settings__about__legal), - iconRes = R.drawable.ic_file_text, + icon = { SettingsIcon(R.drawable.ic_file_text) }, onClick = onClickLegal, ) SettingsButtonRow( title = stringResource(R.string.settings__about__share), - iconRes = R.drawable.ic_share, + icon = { SettingsIcon(R.drawable.ic_share) }, onClick = onClickShare, ) - SettingsButtonRow( - title = stringResource(R.string.settings__about__version), - iconRes = R.drawable.ic_stack, - value = SettingsButtonValue.StringValue(appVersion), - onClick = onClickVersion, - modifier = Modifier.testTag("DevOptions"), - ) - Spacer(modifier = Modifier.weight(1f)) + // Version row — no chevron, value on right + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .heightIn(min = 52.dp) + .clickableAlpha { onClickVersion() } + .testTag("DevOptions"), + ) { + SettingsIcon(R.drawable.ic_stack) + BodyM( + text = stringResource(R.string.settings__about__version), + modifier = Modifier.weight(1f), + ) + BodyM(text = appVersion, color = Colors.White64) + } + HorizontalDivider() + + VerticalSpacer(32.dp) - Image( - painter = painterResource(R.drawable.bitkit_logo), - contentDescription = null, + // Part 1: Logo with diagonal orange crossing through it + Box( modifier = Modifier .fillMaxWidth() - .padding(horizontal = 32.dp, vertical = 16.dp) - .height(82.dp) - .testTag("AboutLogo"), - ) + .clipToBounds() + .drawBehind { + val path = Path().apply { + moveTo(size.width, size.height * 0.1f) + lineTo(0f, size.height * 0.65f) + lineTo(0f, size.height) + lineTo(size.width, size.height) + close() + } + drawPath(path, color = BrandColor) + }, + ) { + Image( + painter = painterResource(R.drawable.bitkit_logo), + contentDescription = null, + modifier = Modifier + .fillMaxWidth() + .height(100.dp) + .testTag("AboutLogo"), + ) + } - Links(modifier = Modifier.fillMaxWidth()) + // Part 2: Solid orange background for bottom content + Column( + modifier = Modifier + .fillMaxWidth() + .background(Colors.Brand), + ) { + VerticalSpacer(16.dp) - VerticalSpacer(16.dp) + Links(modifier = Modifier.fillMaxWidth()) - BodyS( - text = stringResource(R.string.settings__support__copyright), - color = Colors.White64, - ) + VerticalSpacer(16.dp) - VerticalSpacer(32.dp) + BodyM( + text = stringResource(R.string.settings__support__copyright), + color = Colors.White64, + ) + + VerticalSpacer(32.dp) + } } } }