[#1492] Restore success dialog refactor (#1507)

* [#1492] Restore success dialog refactor

Closes #1492

* [#1492] Code cleanup

Closes #1492

* Create a separate Android layer

* Changelog update

* Move restore success string resources

* Rename logic related variables

+ File a follow-up issue

* Resources rename

* Move drawable to its package

---------

Co-authored-by: Honza <rychnovsky.honza@gmail.com>
This commit is contained in:
Milan 2024-07-19 10:17:45 +02:00 committed by GitHub
parent 1a0b634ab6
commit 0bc7757aa2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 352 additions and 55 deletions

View File

@ -14,6 +14,8 @@ and this application adheres to [Semantic Versioning](https://semver.org/spec/v2
### Changed ### Changed
- The About screen has been redesigned to align with the new design guidelines - The About screen has been redesigned to align with the new design guidelines
- `StyledBalance` text styles have been refactored from `Pair` into `BalanceTextStyle` - `StyledBalance` text styles have been refactored from `Pair` into `BalanceTextStyle`
- The Restore Success dialog has been reworked into a separate screen, allowing users to opt out of the Keep screen
on while restoring option
## [1.1.3 (682)] - 2024-07-03 ## [1.1.3 (682)] - 2024-07-03

View File

@ -14,3 +14,5 @@ directly impact users rather than highlighting other key architectural updates.*
### Changed ### Changed
- The About screen has been redesigned to align with the new design guidelines - The About screen has been redesigned to align with the new design guidelines
- The Restore Success dialog has been reworked into a separate screen, allowing users to opt out of the Keep screen
on while restoring option

View File

@ -14,6 +14,8 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.util.concurrent.Executors import java.util.concurrent.Executors
@ -30,6 +32,8 @@ class AndroidPreferenceProvider(
private val sharedPreferences: SharedPreferences, private val sharedPreferences: SharedPreferences,
private val dispatcher: CoroutineDispatcher private val dispatcher: CoroutineDispatcher
) : PreferenceProvider { ) : PreferenceProvider {
private val mutex = Mutex()
/* /*
* Implementation note: EncryptedSharedPreferences are not thread-safe, so this implementation * Implementation note: EncryptedSharedPreferences are not thread-safe, so this implementation
* confines them to a single background thread. * confines them to a single background thread.
@ -45,13 +49,15 @@ class AndroidPreferenceProvider(
key: PreferenceKey, key: PreferenceKey,
value: String? value: String?
) = withContext(dispatcher) { ) = withContext(dispatcher) {
val editor = sharedPreferences.edit() mutex.withLock {
val editor = sharedPreferences.edit()
editor.putString(key.key, value) editor.putString(key.key, value)
editor.commit() editor.commit()
Unit Unit
}
} }
override suspend fun getString(key: PreferenceKey) = override suspend fun getString(key: PreferenceKey) =

View File

@ -3,7 +3,6 @@ package co.electriccoin.zcash.ui.design.component
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Checkbox import androidx.compose.material3.Checkbox
import androidx.compose.material3.CheckboxDefaults import androidx.compose.material3.CheckboxDefaults
@ -76,7 +75,6 @@ fun LabeledCheckBox(
modifier = modifier =
modifier.then( modifier.then(
Modifier Modifier
.wrapContentSize()
.clip(RoundedCornerShape(ZcashTheme.dimens.regularRippleEffectCorner)) .clip(RoundedCornerShape(ZcashTheme.dimens.regularRippleEffectCorner))
.clickable { .clickable {
setCheckedState(!checkedState) setCheckedState(!checkedState)

View File

@ -44,6 +44,7 @@ android {
"src/main/res/ui/onboarding", "src/main/res/ui/onboarding",
"src/main/res/ui/receive", "src/main/res/ui/receive",
"src/main/res/ui/restore", "src/main/res/ui/restore",
"src/main/res/ui/restore_success",
"src/main/res/ui/scan", "src/main/res/ui/scan",
"src/main/res/ui/security_warning", "src/main/res/ui/security_warning",
"src/main/res/ui/seed_recovery", "src/main/res/ui/seed_recovery",

View File

@ -33,9 +33,9 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
booleanStateFlow(StandardPreferenceKeys.IS_KEEP_SCREEN_ON_DURING_SYNC) booleanStateFlow(StandardPreferenceKeys.IS_KEEP_SCREEN_ON_DURING_SYNC)
/** /**
* A flow of whether the app presented the user with an initial restoring dialog * A flow of whether the app presented the user with restore success screem
*/ */
val isRestoringInitialWarningSeen: StateFlow<Boolean?> = val isRestoreSuccessSeen: StateFlow<Boolean?> =
booleanStateFlow(StandardPreferenceKeys.IS_RESTORING_INITIAL_WARNING_SEEN) booleanStateFlow(StandardPreferenceKeys.IS_RESTORING_INITIAL_WARNING_SEEN)
fun setRestoringInitialWarningSeen() { fun setRestoringInitialWarningSeen() {

View File

@ -55,8 +55,6 @@ internal fun WrapHome(
val isKeepScreenOnWhileSyncing = homeViewModel.isKeepScreenOnWhileSyncing.collectAsStateWithLifecycle().value val isKeepScreenOnWhileSyncing = homeViewModel.isKeepScreenOnWhileSyncing.collectAsStateWithLifecycle().value
val isRestoringInitialWarningSeen = homeViewModel.isRestoringInitialWarningSeen.collectAsStateWithLifecycle().value
val walletSnapshot = walletViewModel.walletSnapshot.collectAsStateWithLifecycle().value val walletSnapshot = walletViewModel.walletSnapshot.collectAsStateWithLifecycle().value
val walletRestoringState = walletViewModel.walletRestoringState.collectAsStateWithLifecycle().value val walletRestoringState = walletViewModel.walletRestoringState.collectAsStateWithLifecycle().value
@ -66,20 +64,24 @@ internal fun WrapHome(
walletViewModel.persistWalletRestoringState(WalletRestoringState.SYNCING) walletViewModel.persistWalletRestoringState(WalletRestoringState.SYNCING)
} }
var isShowingRestoreInitDialog by rememberSaveable { mutableStateOf(false) } // TODO [#1523]: Refactor RestoreSuccess screen navigation
val setShowingRestoreInitDialog = { // TODO [#1523]: https://github.com/Electric-Coin-Company/zashi-android/issues/1523
val isRestoreSuccessSeen = homeViewModel.isRestoreSuccessSeen.collectAsStateWithLifecycle().value
var isShowingRestoreSuccess by rememberSaveable { mutableStateOf(false) }
val setShowingRestoreSuccess = {
homeViewModel.setRestoringInitialWarningSeen() homeViewModel.setRestoringInitialWarningSeen()
isShowingRestoreInitDialog = false isShowingRestoreSuccess = false
} }
// Show initial restoring warn dialog // Show initial restore success screen
isRestoringInitialWarningSeen?.let { restoringWarningSeen -> isRestoreSuccessSeen?.let { restoreSuccessSeen ->
if (!restoringWarningSeen && walletRestoringState == WalletRestoringState.RESTORING) { if (!restoreSuccessSeen && walletRestoringState == WalletRestoringState.RESTORING) {
LaunchedEffect(key1 = isShowingRestoreInitDialog) { LaunchedEffect(key1 = isShowingRestoreSuccess) {
// Adding an extra little delay before displaying the dialog for a better UX // Adding an extra little delay before displaying for a better UX
@Suppress("MagicNumber") @Suppress("MagicNumber")
delay(1500) delay(1500)
isShowingRestoreInitDialog = true isShowingRestoreSuccess = true
} }
} }
} }
@ -90,9 +92,9 @@ internal fun WrapHome(
goSettings = goSettings, goSettings = goSettings,
goMultiTrxSubmissionFailure = goMultiTrxSubmissionFailure, goMultiTrxSubmissionFailure = goMultiTrxSubmissionFailure,
isKeepScreenOnWhileSyncing = isKeepScreenOnWhileSyncing, isKeepScreenOnWhileSyncing = isKeepScreenOnWhileSyncing,
isShowingRestoreInitDialog = isShowingRestoreInitDialog, isShowingRestoreSuccess = isShowingRestoreSuccess,
sendArguments = sendArguments, sendArguments = sendArguments,
setShowingRestoreInitDialog = setShowingRestoreInitDialog, setShowingRestoreSuccess = setShowingRestoreSuccess,
walletSnapshot = walletSnapshot walletSnapshot = walletSnapshot
) )
} }
@ -106,9 +108,9 @@ internal fun WrapHome(
goScan: () -> Unit, goScan: () -> Unit,
goSendConfirmation: (ZecSend) -> Unit, goSendConfirmation: (ZecSend) -> Unit,
isKeepScreenOnWhileSyncing: Boolean?, isKeepScreenOnWhileSyncing: Boolean?,
isShowingRestoreInitDialog: Boolean, isShowingRestoreSuccess: Boolean,
sendArguments: SendArguments, sendArguments: SendArguments,
setShowingRestoreInitDialog: () -> Unit, setShowingRestoreSuccess: () -> Unit,
walletSnapshot: WalletSnapshot?, walletSnapshot: WalletSnapshot?,
) { ) {
val focusManager = LocalFocusManager.current val focusManager = LocalFocusManager.current
@ -209,8 +211,8 @@ internal fun WrapHome(
Home( Home(
subScreens = tabs, subScreens = tabs,
isKeepScreenOnWhileSyncing = isKeepScreenOnWhileSyncing, isKeepScreenOnWhileSyncing = isKeepScreenOnWhileSyncing,
isShowingRestoreInitDialog = isShowingRestoreInitDialog, isShowingRestoreSuccess = isShowingRestoreSuccess,
setShowingRestoreInitDialog = setShowingRestoreInitDialog, setShowingRestoreSuccess = setShowingRestoreSuccess,
walletSnapshot = walletSnapshot, walletSnapshot = walletSnapshot,
pagerState = pagerState, pagerState = pagerState,
) )

View File

@ -19,21 +19,19 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension import androidx.constraintlayout.compose.Dimension
import cash.z.ecc.android.sdk.Synchronizer import cash.z.ecc.android.sdk.Synchronizer
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.compose.DisableScreenTimeout import co.electriccoin.zcash.ui.common.compose.DisableScreenTimeout
import co.electriccoin.zcash.ui.common.model.WalletSnapshot import co.electriccoin.zcash.ui.common.model.WalletSnapshot
import co.electriccoin.zcash.ui.design.component.AppAlertDialog
import co.electriccoin.zcash.ui.design.component.BlankSurface import co.electriccoin.zcash.ui.design.component.BlankSurface
import co.electriccoin.zcash.ui.design.component.NavigationTabText import co.electriccoin.zcash.ui.design.component.NavigationTabText
import co.electriccoin.zcash.ui.design.theme.ZcashTheme import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.fixture.WalletSnapshotFixture import co.electriccoin.zcash.ui.fixture.WalletSnapshotFixture
import co.electriccoin.zcash.ui.screen.home.model.TabItem import co.electriccoin.zcash.ui.screen.home.model.TabItem
import co.electriccoin.zcash.ui.screen.restoresuccess.WrapRestoreSuccess
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -46,8 +44,8 @@ private fun ComposablePreview() {
BlankSurface { BlankSurface {
Home( Home(
isKeepScreenOnWhileSyncing = false, isKeepScreenOnWhileSyncing = false,
isShowingRestoreInitDialog = false, isShowingRestoreSuccess = false,
setShowingRestoreInitDialog = {}, setShowingRestoreSuccess = {},
subScreens = persistentListOf(), subScreens = persistentListOf(),
walletSnapshot = WalletSnapshotFixture.new(), walletSnapshot = WalletSnapshotFixture.new(),
) )
@ -60,8 +58,8 @@ private fun ComposablePreview() {
@Composable @Composable
fun Home( fun Home(
isKeepScreenOnWhileSyncing: Boolean?, isKeepScreenOnWhileSyncing: Boolean?,
isShowingRestoreInitDialog: Boolean, isShowingRestoreSuccess: Boolean,
setShowingRestoreInitDialog: () -> Unit, setShowingRestoreSuccess: () -> Unit,
subScreens: ImmutableList<TabItem>, subScreens: ImmutableList<TabItem>,
walletSnapshot: WalletSnapshot?, walletSnapshot: WalletSnapshot?,
pagerState: PagerState = pagerState: PagerState =
@ -76,8 +74,8 @@ fun Home(
subScreens = subScreens, subScreens = subScreens,
) )
if (isShowingRestoreInitDialog) { if (isShowingRestoreSuccess) {
HomeRestoringInitialDialog(setShowingRestoreInitDialog) WrapRestoreSuccess(onDone = setShowingRestoreSuccess)
} }
if (isKeepScreenOnWhileSyncing == true && if (isKeepScreenOnWhileSyncing == true &&
@ -182,13 +180,3 @@ private fun HomeContent(
} }
} }
} }
@Composable
fun HomeRestoringInitialDialog(setShowingRestoreInitDialog: () -> Unit) {
AppAlertDialog(
title = stringResource(id = R.string.restoring_initial_dialog_title),
text = stringResource(id = R.string.restoring_initial_dialog_description),
confirmButtonText = stringResource(id = R.string.restoring_initial_dialog_positive_button),
onConfirmButtonClick = setShowingRestoreInitDialog
)
}

View File

@ -0,0 +1,27 @@
package co.electriccoin.zcash.ui.screen.restoresuccess
import androidx.activity.viewModels
import androidx.compose.runtime.Composable
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.ui.common.compose.LocalActivity
import co.electriccoin.zcash.ui.screen.restoresuccess.view.RestoreSuccess
import co.electriccoin.zcash.ui.screen.restoresuccess.viewmodel.RestoreSuccessViewModel
@Composable
fun WrapRestoreSuccess(onDone: () -> Unit) {
val activity = LocalActivity.current
val viewModel by activity.viewModels<RestoreSuccessViewModel>()
val state = viewModel.state.collectAsStateWithLifecycle().value
RestoreSuccess(
state =
state.copy(
onPositiveClick = {
state.onPositiveClick()
onDone()
}
)
)
}

View File

@ -0,0 +1,164 @@
package co.electriccoin.zcash.ui.screen.restoresuccess.view
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.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.component.BlankSurface
import co.electriccoin.zcash.ui.design.component.GridBgScaffold
import co.electriccoin.zcash.ui.design.component.LabeledCheckBox
import co.electriccoin.zcash.ui.design.component.PrimaryButton
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
@Composable
fun RestoreSuccess(state: RestoreSuccessViewState) {
GridBgScaffold { paddingValues ->
RestoreSuccessContent(
state = state,
modifier =
Modifier
.fillMaxSize()
.padding(
top = paddingValues.calculateTopPadding(),
bottom = paddingValues.calculateBottomPadding()
)
.verticalScroll(rememberScrollState())
)
}
}
@Composable
@Suppress("LongMethod")
private fun RestoreSuccessContent(
state: RestoreSuccessViewState,
modifier: Modifier = Modifier
) {
Column(
modifier =
modifier.then(
Modifier.padding(
start = ZcashTheme.dimens.screenHorizontalSpacingBig,
end = ZcashTheme.dimens.screenHorizontalSpacingBig
)
),
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingBig))
Text(
text = stringResource(id = R.string.restore_success_title),
style = ZcashTheme.typography.secondary.headlineMedium
)
Spacer(Modifier.height(ZcashTheme.dimens.spacingUpLarge))
Image(
painter = painterResource(id = R.drawable.img_success_dialog),
contentDescription = stringResource(id = R.string.restore_success_subtitle),
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground)
)
Spacer(Modifier.height(ZcashTheme.dimens.spacingUpLarge))
Text(
text = stringResource(id = R.string.restore_success_subtitle),
textAlign = TextAlign.Center,
style = ZcashTheme.typography.secondary.headlineSmall,
fontWeight = FontWeight.SemiBold
)
Spacer(Modifier.height(ZcashTheme.dimens.spacingUpLarge))
Text(
text = stringResource(id = R.string.restore_success_description),
style = ZcashTheme.typography.secondary.bodySmall,
)
Spacer(Modifier.height(ZcashTheme.dimens.spacingLarge))
LabeledCheckBox(
modifier = Modifier.align(Alignment.Start),
checked = state.isKeepScreenOnChecked,
onCheckedChange = { state.onCheckboxClick() },
text = stringResource(id = R.string.restoring_initial_dialog_checkbox)
)
Spacer(Modifier.height(ZcashTheme.dimens.spacingLarge))
Text(
text =
buildAnnotatedString {
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
append(stringResource(id = R.string.restore_success_note_part_1))
}
append(" ")
append(stringResource(id = R.string.restore_success_note_part_2))
},
style = ZcashTheme.extendedTypography.footnote,
)
Spacer(Modifier.height(ZcashTheme.dimens.spacingBig))
Spacer(Modifier.weight(1f))
PrimaryButton(
onClick = state.onPositiveClick,
text = stringResource(id = R.string.restore_success_button)
)
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingBig))
}
}
data class RestoreSuccessViewState(
val isKeepScreenOnChecked: Boolean,
val onCheckboxClick: () -> Unit,
val onPositiveClick: () -> Unit,
)
@Composable
private fun RestoreSuccessViewPreview() =
BlankSurface {
RestoreSuccess(
state =
RestoreSuccessViewState(
isKeepScreenOnChecked = true,
onCheckboxClick = {},
onPositiveClick = {},
)
)
}
@Preview(device = Devices.PIXEL_7_PRO)
@Composable
private fun RestoreSuccessViewPreviewLight() =
ZcashTheme(false) {
RestoreSuccessViewPreview()
}
@Preview(device = Devices.PIXEL_7_PRO)
@Composable
private fun RestoreSuccessViewPreviewDark() =
ZcashTheme(true) {
RestoreSuccessViewPreview()
}

View File

@ -0,0 +1,53 @@
package co.electriccoin.zcash.ui.screen.restoresuccess.viewmodel
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import co.electriccoin.zcash.preference.model.entry.BooleanPreferenceDefault
import co.electriccoin.zcash.ui.common.ANDROID_STATE_FLOW_TIMEOUT
import co.electriccoin.zcash.ui.preference.StandardPreferenceKeys
import co.electriccoin.zcash.ui.preference.StandardPreferenceSingleton
import co.electriccoin.zcash.ui.screen.restoresuccess.view.RestoreSuccessViewState
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.WhileSubscribed
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
class RestoreSuccessViewModel(application: Application) : AndroidViewModel(application) {
private val keepScreenOn = MutableStateFlow(DEFAULT_KEEP_SCREEN_ON)
val state =
keepScreenOn
.map { createState(keepScreenOn = it) }
.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
createState(keepScreenOn = DEFAULT_KEEP_SCREEN_ON)
)
private fun createState(keepScreenOn: Boolean) =
RestoreSuccessViewState(
isKeepScreenOnChecked = keepScreenOn,
onCheckboxClick = { this.keepScreenOn.update { !keepScreenOn } },
onPositiveClick = {
setKeepScreenOnWhileSyncing(keepScreenOn)
}
)
private fun setKeepScreenOnWhileSyncing(enabled: Boolean) {
setBooleanPreference(StandardPreferenceKeys.IS_KEEP_SCREEN_ON_DURING_SYNC, enabled)
}
private fun setBooleanPreference(
default: BooleanPreferenceDefault,
newState: Boolean
) = viewModelScope.launch {
val prefs = StandardPreferenceSingleton.getInstance(getApplication())
default.putValue(prefs, newState)
}
}
private const val DEFAULT_KEEP_SCREEN_ON = true

View File

@ -14,12 +14,8 @@ import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
class SettingsViewModel(application: Application) : AndroidViewModel(application) { class SettingsViewModel(application: Application) : AndroidViewModel(application) {
private val mutex = Mutex()
val isAnalyticsEnabled: StateFlow<Boolean?> = booleanStateFlow(StandardPreferenceKeys.IS_ANALYTICS_ENABLED) val isAnalyticsEnabled: StateFlow<Boolean?> = booleanStateFlow(StandardPreferenceKeys.IS_ANALYTICS_ENABLED)
val isBackgroundSync: StateFlow<Boolean?> = booleanStateFlow(StandardPreferenceKeys.IS_BACKGROUND_SYNC_ENABLED) val isBackgroundSync: StateFlow<Boolean?> = booleanStateFlow(StandardPreferenceKeys.IS_BACKGROUND_SYNC_ENABLED)
@ -51,9 +47,7 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
) { ) {
viewModelScope.launch { viewModelScope.launch {
val prefs = StandardPreferenceSingleton.getInstance(getApplication()) val prefs = StandardPreferenceSingleton.getInstance(getApplication())
mutex.withLock { default.putValue(prefs, newState)
default.putValue(prefs, newState)
}
} }
} }
} }

View File

@ -3,8 +3,4 @@
<string name="home_tab_send">Send</string> <string name="home_tab_send">Send</string>
<string name="home_tab_receive">Receive</string> <string name="home_tab_receive">Receive</string>
<string name="home_tab_balances">Balances</string> <string name="home_tab_balances">Balances</string>
<string name="restoring_initial_dialog_title">Success</string>
<string name="restoring_initial_dialog_description">Your wallet has been successfully restored! During the initial sync, your funds cannot be spent or sent. Depending on the age of your wallet, it may take a few hours to fully sync.</string>
<string name="restoring_initial_dialog_positive_button">OK</string>
</resources> </resources>

View File

@ -0,0 +1,54 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="78dp"
android:height="175dp"
android:viewportWidth="78"
android:viewportHeight="175">
<group>
<clip-path
android:pathData="M40.14,128.56l-6.2,2.5l4,9.72l6.2,-2.5z"/>
<path
android:pathData="M42.35,133.92L39.75,134.96C39.66,135 39.58,135.08 39.55,135.17C39.51,135.26 39.52,135.36 39.57,135.45L41.84,139.22L35.84,135.68L38.53,134.59C38.62,134.55 38.69,134.48 38.73,134.39C38.76,134.3 38.76,134.2 38.71,134.11L36.44,130.06L42.35,133.92Z"
android:fillColor="#ffffff"/>
</group>
<path
android:pathData="M44.49,69.32L35.54,65.32L42.3,59.57L30.59,44.77L48.54,54.48L49.7,58.58L41.31,62.95L44.49,69.33V69.32ZM36.78,65.29L43.34,68.22L40.83,63.18L36.78,65.29ZM48.17,55.14L38.01,64.04L49.06,58.29L48.17,55.14ZM32.63,46.47L42.68,59.18L47.83,54.7L32.63,46.47Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M12.93,21.03L19.85,13.15C19.96,13.03 19.95,12.83 19.83,12.73C19.7,12.62 19.5,12.63 19.39,12.76L17.67,14.72C17.15,14.51 16.62,14.4 16.07,14.4C13.55,14.4 11.79,16.74 11.72,16.83C11.63,16.94 11.64,17.07 11.72,17.19C12.4,18.03 13.11,18.65 13.85,19.04L12.46,20.63C12.35,20.75 12.36,20.95 12.48,21.05C12.55,21.1 12.61,21.12 12.68,21.12C12.77,21.12 12.85,21.08 12.92,21.01L12.93,21.03ZM12.36,17C12.81,16.48 14.24,15 16.09,15C16.48,15 16.88,15.06 17.25,15.19L16.78,15.74C16.56,15.6 16.3,15.54 16.02,15.54C15.21,15.54 14.54,16.21 14.54,17.01C14.54,17.36 14.66,17.68 14.86,17.93L14.28,18.6C13.62,18.26 12.99,17.73 12.37,17H12.36ZM20.32,16.81C20.41,16.93 20.41,17.07 20.32,17.19C20.25,17.29 18.49,19.61 15.96,19.61C15.67,19.61 15.36,19.57 15.07,19.5L15.54,18.97C15.69,18.99 15.84,18.99 15.98,18.99C17.8,18.99 19.23,17.52 19.68,17C19.24,16.49 18.79,16.08 18.34,15.76L18.74,15.31C19.27,15.69 19.8,16.18 20.31,16.8L20.32,16.81Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M65.52,1.35H12.47C6.28,1.35 1.25,6.37 1.25,12.57V134.04C1.25,140.23 6.28,145.25 12.47,145.25H65.52C71.71,145.25 76.73,140.23 76.73,134.04V12.57C76.73,6.37 71.71,1.35 65.52,1.35Z"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#ffffff"/>
<path
android:pathData="M39.74,78.89C38.1,78.89 36.45,78.7 34.84,78.34C31.2,77.51 27.81,75.77 25.03,73.27V73.14L24.32,72.45C21.88,70.06 20.02,67.12 18.92,63.89L23.81,66.45L22.28,69.67L23.26,70.83C27.37,75.68 33.38,78.46 39.77,78.46C44.87,78.46 49.83,76.66 53.71,73.37C57.24,70.37 59.77,66.25 60.81,61.73C60.83,61.62 60.92,61.54 61.03,61.54C61.13,61.54 61.21,61.63 61.21,61.73C58.95,71.68 49.91,78.89 39.74,78.89ZM18.24,51.92C18.14,51.91 18.06,51.81 18.06,51.71C20.35,42.28 28.79,35.3 38.56,34.81C38.92,34.79 39.27,34.78 39.62,34.78C45.06,34.78 50.26,36.76 54.31,40.37V40.5L55.01,41.19C57.47,43.59 59.32,46.54 60.43,49.78L55.42,47.19L56.92,43.97L55.94,42.82C52.97,39.32 48.86,36.82 44.38,35.77C42.77,35.4 41.12,35.2 39.48,35.2C29.41,35.2 20.77,42.04 18.45,51.83C18.45,51.86 18.35,51.91 18.27,51.91H18.23L18.24,51.92Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M43.13,145.25H43.63C43.91,145.25 44.13,145.47 44.13,145.75V156.75C44.13,157.58 43.46,158.25 42.63,158.25H42.13V167.03C42.13,167.31 41.91,167.53 41.63,167.53C41.35,167.53 41.13,167.31 41.13,167.03V157.75C41.13,157.47 41.35,157.25 41.63,157.25H42.63C42.91,157.25 43.13,157.03 43.13,156.75V146.25H36.13V156.75C36.13,157.03 36.35,157.25 36.63,157.25H37.63C37.91,157.25 38.13,157.47 38.13,157.75V171.03C38.13,171.31 37.91,171.53 37.63,171.53C37.35,171.53 37.13,171.31 37.13,171.03V158.25H36.63C35.8,158.25 35.13,157.58 35.13,156.75V145.75C35.13,145.47 35.35,145.25 35.63,145.25H36.13"
android:fillColor="#ffffff"/>
<path
android:strokeWidth="1"
android:pathData="M34.78,174.03L45.76,163.05"
android:fillColor="#00000000"
android:strokeColor="#ffffff"/>
<path
android:pathData="M27.9,152.55C28.11,152.45 28.19,152.18 28.07,151.94C27.96,151.7 27.7,151.58 27.5,151.68L22.77,153.91C22.56,154.01 22.49,154.28 22.6,154.52C22.71,154.76 22.97,154.88 23.18,154.78L27.9,152.55Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M51.36,152.55C51.15,152.45 51.07,152.18 51.19,151.94C51.3,151.7 51.56,151.58 51.76,151.68L56.49,153.91C56.7,154.01 56.78,154.28 56.66,154.52C56.55,154.76 56.29,154.88 56.08,154.78L51.36,152.55Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M26.63,148.85C26.87,148.84 27.03,148.62 27,148.35C26.99,148.3 26.98,148.24 26.95,148.2C26.87,148.02 26.7,147.89 26.51,147.91L23.11,148.05C22.87,148.06 22.71,148.28 22.74,148.54C22.78,148.8 23,149.01 23.23,149L26.63,148.85Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M52.63,148.85C52.39,148.84 52.23,148.62 52.27,148.35C52.28,148.3 52.28,148.24 52.31,148.2C52.39,148.02 52.56,147.89 52.75,147.91L56.16,148.05C56.39,148.06 56.55,148.28 56.52,148.54C56.48,148.8 56.26,149.01 56.03,149L52.63,148.85Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M27.9,158.43L30.33,155.71C30.45,155.58 30.47,155.36 30.39,155.19C30.36,155.13 30.34,155.09 30.29,155.05C30.12,154.85 29.84,154.84 29.69,155.01L27.26,157.73C27.1,157.91 27.11,158.21 27.29,158.4C27.47,158.59 27.74,158.6 27.9,158.43Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M51.37,158.43L48.93,155.71C48.81,155.58 48.79,155.36 48.87,155.19C48.9,155.13 48.92,155.09 48.97,155.05C49.14,154.85 49.42,154.84 49.57,155.01L52.01,157.73C52.17,157.91 52.15,158.21 51.97,158.4C51.8,158.59 51.52,158.6 51.37,158.43Z"
android:fillColor="#ffffff"/>
</vector>

View File

@ -0,0 +1,10 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="restore_success_title">Keep Zashi open!</string>
<string name="restore_success_subtitle">Your wallet has been successfully restored and is now syncing</string>
<string name="restore_success_description">To prevent interruption, keep your screen awake, plug your device into a power source, and keep it in a secure place.</string>
<string name="restoring_initial_dialog_checkbox">Keep screen on while restoring.</string>
<string name="restore_success_note_part_1">Note:</string>
<string name="restore_success_note_part_2">During the initial sync your funds cannot be sent or
spent. Depending on the age of your wallet, it may take a few hours to fully sync.</string>
<string name="restore_success_button">GOT IT!</string>
</resources>