#814 Compose back handler (#1494)

* #814 Compose back handler

Closes #814

* #814 Test hotfixes

Closes #814

* Back handling for onboarding

Closes #814

* [#814] Changelog update

Closes #814

---------

Co-authored-by: Milan Cerovsky <milan.cerovsky@leeaf.life>
This commit is contained in:
Milan 2024-07-01 12:44:51 +02:00 committed by GitHub
parent 508552f0fa
commit 42deed3391
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 219 additions and 304 deletions

View File

@ -9,6 +9,9 @@ directly impact users rather than highlighting other key architectural updates.*
## [Unreleased]
### Fixed
- The app navigation has been improved to always behave the same for system, gesture, or top app bar back navigation actions.
### Added
- Proper ZEC amount abbreviation has been added across the entire app as described by the design document

View File

@ -5,7 +5,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import cash.z.ecc.android.sdk.fixture.WalletBalanceFixture
import cash.z.ecc.android.sdk.model.MonetarySeparators
@ -69,11 +68,6 @@ class SendViewTestSetup(
return lastZecSend
}
fun getLastSendStage(): SendStage? {
composeTestRule.waitForIdle()
return lastSendStage
}
@Composable
@Suppress("TestFunctionName")
fun DefaultContent() {
@ -111,7 +105,6 @@ class SendViewTestSetup(
balanceState = BalanceStateFixture.new(),
sendStage = sendStage,
onCreateZecSend = setZecSend,
focusManager = LocalFocusManager.current,
onBack = onBackAction,
onSettings = { onSettingsCount.incrementAndGet() },
onQrScannerOpen = {

View File

@ -1,6 +1,5 @@
package co.electriccoin.zcash.ui.screen.send.integration
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.test.junit4.StateRestorationTester
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
@ -71,7 +70,6 @@ class SendViewIntegrationTest {
synchronizer = synchronizer,
walletSnapshot = walletSnapshot,
spendingKey = spendingKey,
focusManager = LocalFocusManager.current,
goToQrScanner = {},
goBack = {},
goBalances = {},

View File

@ -1,7 +1,9 @@
package co.electriccoin.zcash.ui.screen.update.view
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import co.electriccoin.zcash.ui.common.compose.LocalActivity
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.screen.update.WrapUpdate
import co.electriccoin.zcash.ui.screen.update.model.UpdateInfo
@ -13,10 +15,11 @@ class UpdateViewAndroidTestSetup(
@Composable
@Suppress("TestFunctionName")
fun DefaultContent() {
WrapUpdate(
composeTestRule.activity,
updateInfo
)
CompositionLocalProvider(LocalActivity provides composeTestRule.activity) {
WrapUpdate(
updateInfo
)
}
}
fun setDefaultContent() {

View File

@ -13,7 +13,6 @@ import androidx.navigation.NavHostController
import androidx.navigation.NavOptionsBuilder
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.model.ZecSend
import co.electriccoin.zcash.spackle.Twig
@ -36,6 +35,7 @@ import co.electriccoin.zcash.ui.NavigationTargets.SEED_RECOVERY
import co.electriccoin.zcash.ui.NavigationTargets.SEND_CONFIRMATION
import co.electriccoin.zcash.ui.NavigationTargets.SETTINGS
import co.electriccoin.zcash.ui.NavigationTargets.SUPPORT
import co.electriccoin.zcash.ui.common.compose.LocalNavController
import co.electriccoin.zcash.ui.common.model.SerializableAddress
import co.electriccoin.zcash.ui.configuration.ConfigurationEntries
import co.electriccoin.zcash.ui.configuration.RemoteConfig
@ -74,10 +74,7 @@ import kotlinx.serialization.json.Json
@Composable
@Suppress("LongMethod")
internal fun MainActivity.Navigation() {
val navController =
rememberNavController().also {
navControllerForTesting = it
}
val navController = LocalNavController.current
// Helper properties for triggering the system security UI from callbacks
val (exportPrivateDataAuthentication, setExportPrivateDataAuthentication) =
@ -268,7 +265,6 @@ private fun MainActivity.NavigationHome(
backStack: NavBackStackEntry
) {
WrapHome(
goBack = { finish() },
goScan = { navController.navigateJustOnce(SCAN) },
goSendConfirmation = { zecSend ->
navController.currentBackStackEntry?.savedStateHandle?.let { handle ->

View File

@ -5,13 +5,15 @@ import androidx.activity.ComponentActivity
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.lifecycle.lifecycleScope
import androidx.navigation.compose.rememberNavController
import cash.z.ecc.android.sdk.ext.collectWith
import co.electriccoin.zcash.spackle.EmulatorWtfUtil
import co.electriccoin.zcash.spackle.FirebaseTestLabUtil
import co.electriccoin.zcash.ui.MainActivity
import kotlinx.coroutines.flow.map
@Composable
fun ComponentActivity.BindCompLocalProvider(content: @Composable () -> Unit) {
fun MainActivity.BindCompLocalProvider(content: @Composable () -> Unit) {
val screenSecurity = ScreenSecurity()
observeScreenSecurityFlag(screenSecurity)
@ -20,10 +22,18 @@ fun ComponentActivity.BindCompLocalProvider(content: @Composable () -> Unit) {
val screenTimeout = ScreenTimeout()
observeScreenTimeoutFlag(screenTimeout)
val navController =
rememberNavController().also {
navControllerForTesting = it
}
CompositionLocalProvider(
LocalScreenSecurity provides screenSecurity,
LocalScreenBrightness provides screenBrightness,
LocalScreenTimeout provides screenTimeout,
LocalNavController provides navController,
LocalActivity provides this,
content = content
)
}

View File

@ -0,0 +1,10 @@
package co.electriccoin.zcash.ui.common.compose
import androidx.activity.ComponentActivity
import androidx.compose.runtime.compositionLocalOf
@Suppress("CompositionLocalAllowlist")
val LocalActivity =
compositionLocalOf<ComponentActivity> {
error("Activity not provided")
}

View File

@ -0,0 +1,10 @@
package co.electriccoin.zcash.ui.common.compose
import androidx.compose.runtime.compositionLocalOf
import androidx.navigation.NavHostController
@Suppress("CompositionLocalAllowlist")
val LocalNavController =
compositionLocalOf<NavHostController> {
error("NavController not provided")
}

View File

@ -9,8 +9,6 @@ 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.home.HomeScreenIndex
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.WhileSubscribed
@ -20,11 +18,6 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
class HomeViewModel(application: Application) : AndroidViewModel(application) {
/**
* Current Home sub-screen index in flow.
*/
val screenIndex: MutableStateFlow<HomeScreenIndex> = MutableStateFlow(HomeScreenIndex.ACCOUNT)
/**
* A flow of whether background sync is enabled
*/
@ -37,12 +30,6 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
val isKeepScreenOnWhileSyncing: StateFlow<Boolean?> =
booleanStateFlow(StandardPreferenceKeys.IS_KEEP_SCREEN_ON_DURING_SYNC)
/**
* A flow of whether the app uses simple or detailed block synchronization status information for the UI
*/
val isDetailedSyncStatus: StateFlow<Boolean?> =
booleanStateFlow(StandardPreferenceKeys.IS_DETAILED_SYNC_STATUS)
/**
* A flow of whether the app presented the user with an initial restoring dialog
*/

View File

@ -3,7 +3,7 @@
package co.electriccoin.zcash.ui.screen.about
import android.content.Context
import androidx.activity.ComponentActivity
import androidx.activity.compose.BackHandler
import androidx.activity.viewModels
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
@ -12,9 +12,8 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.configuration.AndroidConfigurationFactory
import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.common.compose.LocalActivity
import co.electriccoin.zcash.ui.common.model.VersionInfo
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
import co.electriccoin.zcash.ui.screen.about.util.WebBrowserUtil
@ -24,24 +23,17 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@Composable
internal fun MainActivity.WrapAbout(goBack: () -> Unit) {
val walletViewModel by viewModels<WalletViewModel>()
internal fun WrapAbout(goBack: () -> Unit) {
val activity = LocalActivity.current
val walletViewModel by activity.viewModels<WalletViewModel>()
val walletState = walletViewModel.walletStateInformation.collectAsStateWithLifecycle().value
WrapAbout(
activity = this,
goBack = goBack,
topAppBarSubTitleState = walletState
)
}
BackHandler {
goBack()
}
@Composable
internal fun WrapAbout(
activity: ComponentActivity,
goBack: () -> Unit,
topAppBarSubTitleState: TopAppBarSubTitleState,
) {
val configInfo = ConfigInfo.new(AndroidConfigurationFactory.getInstance(activity.applicationContext))
val versionInfo = VersionInfo.new(activity.applicationContext)
@ -66,7 +58,7 @@ internal fun WrapAbout(
)
},
snackbarHostState = snackbarHostState,
topAppBarSubTitleState = topAppBarSubTitleState,
topAppBarSubTitleState = walletState,
)
}

View File

@ -3,19 +3,20 @@
package co.electriccoin.zcash.ui.screen.account
import android.content.Context
import androidx.activity.ComponentActivity
import androidx.activity.viewModels
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.internal.Twig
import co.electriccoin.zcash.spackle.ClipboardManagerUtil
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.compose.BalanceState
import co.electriccoin.zcash.ui.common.compose.LocalActivity
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.common.model.WalletRestoringState
import co.electriccoin.zcash.ui.common.model.WalletSnapshot
@ -33,10 +34,11 @@ import org.jetbrains.annotations.VisibleForTesting
@Composable
internal fun WrapAccount(
activity: ComponentActivity,
goBalances: () -> Unit,
goSettings: () -> Unit,
) {
val activity = LocalActivity.current
val walletViewModel by activity.viewModels<WalletViewModel>()
val transactionHistoryViewModel by activity.viewModels<TransactionHistoryViewModel>()
@ -59,7 +61,6 @@ internal fun WrapAccount(
WrapAccount(
balanceState = balanceState,
context = activity.applicationContext,
goBalances = goBalances,
goSettings = goSettings,
synchronizer = synchronizer,
@ -79,7 +80,6 @@ internal fun WrapAccount(
@Suppress("LongParameterList", "LongMethod")
internal fun WrapAccount(
balanceState: BalanceState,
context: Context,
goBalances: () -> Unit,
goSettings: () -> Unit,
transactionsUiState: TransactionUiState,
@ -89,6 +89,8 @@ internal fun WrapAccount(
walletRestoringState: WalletRestoringState,
walletSnapshot: WalletSnapshot?
) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val snackbarHostState = remember { SnackbarHostState() }

View File

@ -19,9 +19,9 @@ import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.model.UnifiedSpendingKey
import cash.z.ecc.android.sdk.model.Zatoshi
import co.electriccoin.zcash.spackle.Twig
import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.compose.BalanceState
import co.electriccoin.zcash.ui.common.compose.LocalActivity
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.common.model.WalletRestoringState
import co.electriccoin.zcash.ui.common.model.WalletSnapshot
@ -45,10 +45,11 @@ import org.jetbrains.annotations.VisibleForTesting
@Composable
internal fun WrapBalances(
activity: MainActivity,
goSettings: () -> Unit,
goMultiTrxSubmissionFailure: () -> Unit,
) {
val activity = LocalActivity.current
val walletViewModel by activity.viewModels<WalletViewModel>()
val createTransactionsViewModel by activity.viewModels<CreateTransactionsViewModel>()
@ -89,9 +90,9 @@ internal fun WrapBalances(
const val DEFAULT_SHIELDING_THRESHOLD = 100000L
// This function should be refactored into smaller chunks
@Composable
@VisibleForTesting
// This function should be refactored into smaller chunks
@Suppress("LongParameterList", "LongMethod", "CyclomaticComplexMethod")
internal fun WrapBalances(
balanceState: BalanceState,

View File

@ -1,7 +1,6 @@
package co.electriccoin.zcash.ui.screen.exportdata
import android.content.Context
import androidx.activity.ComponentActivity
import androidx.activity.compose.BackHandler
import androidx.activity.viewModels
import androidx.compose.material3.SnackbarHostState
@ -15,6 +14,7 @@ import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.sdk.type.fromResources
import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.compose.LocalActivity
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.common.model.VersionInfo
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
@ -38,7 +38,6 @@ internal fun MainActivity.WrapExportPrivateData(
val walletState = walletViewModel.walletStateInformation.collectAsStateWithLifecycle().value
WrapExportPrivateData(
this,
goBack = goBack,
onShare = onConfirm,
synchronizer = synchronizer,
@ -48,12 +47,13 @@ internal fun MainActivity.WrapExportPrivateData(
@Composable
internal fun WrapExportPrivateData(
activity: ComponentActivity,
goBack: () -> Unit,
onShare: () -> Unit,
synchronizer: Synchronizer?,
topAppBarSubTitleState: TopAppBarSubTitleState,
) {
val activity = LocalActivity.current
BackHandler {
goBack()
}

View File

@ -4,18 +4,23 @@ package co.electriccoin.zcash.ui.screen.home
import androidx.activity.compose.BackHandler
import androidx.activity.viewModels
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import cash.z.ecc.android.sdk.model.ZecSend
import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.compose.LocalActivity
import co.electriccoin.zcash.ui.common.compose.RestoreScreenBrightness
import co.electriccoin.zcash.ui.common.model.WalletRestoringState
import co.electriccoin.zcash.ui.common.model.WalletSnapshot
@ -30,25 +35,23 @@ import co.electriccoin.zcash.ui.screen.receive.WrapReceive
import co.electriccoin.zcash.ui.screen.send.WrapSend
import co.electriccoin.zcash.ui.screen.send.model.SendArguments
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
@Composable
@Suppress("LongParameterList")
internal fun MainActivity.WrapHome(
goBack: () -> Unit,
internal fun WrapHome(
goSettings: () -> Unit,
goMultiTrxSubmissionFailure: () -> Unit,
goScan: () -> Unit,
goSendConfirmation: (ZecSend) -> Unit,
sendArguments: SendArguments
) {
val homeViewModel by viewModels<HomeViewModel>()
val activity = LocalActivity.current
val walletViewModel by viewModels<WalletViewModel>()
val homeViewModel by activity.viewModels<HomeViewModel>()
val homeScreenIndex = homeViewModel.screenIndex.collectAsStateWithLifecycle().value
val walletViewModel by activity.viewModels<WalletViewModel>()
val isKeepScreenOnWhileSyncing = homeViewModel.isKeepScreenOnWhileSyncing.collectAsStateWithLifecycle().value
@ -82,56 +85,58 @@ internal fun MainActivity.WrapHome(
}
WrapHome(
this,
goBack = goBack,
goScan = goScan,
goSendConfirmation = goSendConfirmation,
goSettings = goSettings,
goMultiTrxSubmissionFailure = goMultiTrxSubmissionFailure,
homeScreenIndex = homeScreenIndex,
isKeepScreenOnWhileSyncing = isKeepScreenOnWhileSyncing,
isShowingRestoreInitDialog = isShowingRestoreInitDialog,
onPageChange = {
homeViewModel.screenIndex.value = it
},
sendArguments = sendArguments,
setShowingRestoreInitDialog = setShowingRestoreInitDialog,
walletSnapshot = walletSnapshot
)
}
@OptIn(ExperimentalFoundationApi::class)
@Suppress("LongParameterList", "LongMethod")
@Composable
internal fun WrapHome(
activity: MainActivity,
goBack: () -> Unit,
goSettings: () -> Unit,
goMultiTrxSubmissionFailure: () -> Unit,
goScan: () -> Unit,
goSendConfirmation: (ZecSend) -> Unit,
homeScreenIndex: HomeScreenIndex,
isKeepScreenOnWhileSyncing: Boolean?,
isShowingRestoreInitDialog: Boolean,
onPageChange: (HomeScreenIndex) -> Unit,
sendArguments: SendArguments,
setShowingRestoreInitDialog: () -> Unit,
walletSnapshot: WalletSnapshot?,
) {
// Flow for propagating the new page index to the pager in the view layer
val forceHomePageIndexFlow: MutableSharedFlow<ForcePage?> =
MutableSharedFlow(
Int.MAX_VALUE,
Int.MAX_VALUE,
BufferOverflow.SUSPEND
)
val forceIndex = forceHomePageIndexFlow.collectAsState(initial = null).value
val focusManager = LocalFocusManager.current
val homeGoBack: () -> Unit = {
when (homeScreenIndex) {
HomeScreenIndex.ACCOUNT -> goBack()
HomeScreenIndex.SEND,
HomeScreenIndex.RECEIVE,
HomeScreenIndex.BALANCES -> forceHomePageIndexFlow.tryEmit(ForcePage(HomeScreenIndex.ACCOUNT))
val activity = LocalActivity.current
val scope = rememberCoroutineScope()
val pagerState =
rememberPagerState(
initialPage = 0,
initialPageOffsetFraction = 0f,
pageCount = { 4 }
)
val homeGoBack: () -> Unit by remember(pagerState.currentPage, scope) {
derivedStateOf {
{
when (pagerState.currentPage) {
HomeScreenIndex.ACCOUNT.pageIndex -> activity.finish()
HomeScreenIndex.SEND.pageIndex,
HomeScreenIndex.RECEIVE.pageIndex,
HomeScreenIndex.BALANCES.pageIndex ->
scope.launch {
pagerState.animateScrollToPage(HomeScreenIndex.ACCOUNT.pageIndex)
}
}
}
}
}
@ -140,7 +145,7 @@ internal fun WrapHome(
}
// Reset the screen brightness for all pages except Receive which maintain the screen brightness by itself
if (homeScreenIndex != HomeScreenIndex.RECEIVE) {
if (pagerState.currentPage != HomeScreenIndex.RECEIVE.pageIndex) {
RestoreScreenBrightness()
}
@ -152,8 +157,11 @@ internal fun WrapHome(
testTag = HomeTag.TAB_ACCOUNT,
screenContent = {
WrapAccount(
activity = activity,
goBalances = { forceHomePageIndexFlow.tryEmit(ForcePage(HomeScreenIndex.BALANCES)) },
goBalances = {
scope.launch {
pagerState.animateScrollToPage(HomeScreenIndex.BALANCES.pageIndex)
}
},
goSettings = goSettings
)
}
@ -164,10 +172,13 @@ internal fun WrapHome(
testTag = HomeTag.TAB_SEND,
screenContent = {
WrapSend(
activity = activity,
goToQrScanner = goScan,
goBack = homeGoBack,
goBalances = { forceHomePageIndexFlow.tryEmit(ForcePage(HomeScreenIndex.BALANCES)) },
goBalances = {
scope.launch {
pagerState.animateScrollToPage(HomeScreenIndex.BALANCES.pageIndex)
}
},
goSendConfirmation = goSendConfirmation,
goSettings = goSettings,
sendArguments = sendArguments
@ -179,10 +190,7 @@ internal fun WrapHome(
title = stringResource(id = R.string.home_tab_receive),
testTag = HomeTag.TAB_RECEIVE,
screenContent = {
WrapReceive(
activity = activity,
onSettings = goSettings
)
WrapReceive(onSettings = goSettings)
}
),
TabItem(
@ -191,7 +199,6 @@ internal fun WrapHome(
testTag = HomeTag.TAB_BALANCES,
screenContent = {
WrapBalances(
activity = activity,
goSettings = goSettings,
goMultiTrxSubmissionFailure = goMultiTrxSubmissionFailure
)
@ -201,33 +208,27 @@ internal fun WrapHome(
Home(
subScreens = tabs,
forcePage = forceIndex,
isKeepScreenOnWhileSyncing = isKeepScreenOnWhileSyncing,
isShowingRestoreInitDialog = isShowingRestoreInitDialog,
onPageChange = onPageChange,
setShowingRestoreInitDialog = setShowingRestoreInitDialog,
walletSnapshot = walletSnapshot
walletSnapshot = walletSnapshot,
pagerState = pagerState,
)
}
/**
* Wrapper class used to pass forced pages index into the view layer
*/
class ForcePage(
val currentPage: HomeScreenIndex,
)
LaunchedEffect(pagerState.currentPage) {
if (pagerState.currentPage != HomeScreenIndex.SEND.pageIndex) {
focusManager.clearFocus(true)
}
}
}
/**
* Enum of the Home screen sub-screens
*/
enum class HomeScreenIndex {
// WARN: Be careful when re-ordering these, as the ordinal number states for their order
ACCOUNT,
SEND,
RECEIVE,
BALANCES, ;
companion object {
fun fromIndex(index: Int) = entries[index]
}
@Suppress("MagicNumber")
enum class HomeScreenIndex(val pageIndex: Int) {
ACCOUNT(0),
SEND(1),
RECEIVE(2),
BALANCES(3)
}

View File

@ -16,10 +16,7 @@ import androidx.compose.material3.TabRow
import androidx.compose.material3.TabRowDefaults
import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
@ -28,7 +25,6 @@ import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension
import cash.z.ecc.android.sdk.Synchronizer
import co.electriccoin.zcash.spackle.Twig
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.compose.DisableScreenTimeout
import co.electriccoin.zcash.ui.common.model.WalletSnapshot
@ -37,24 +33,20 @@ import co.electriccoin.zcash.ui.design.component.BlankSurface
import co.electriccoin.zcash.ui.design.component.NavigationTabText
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.fixture.WalletSnapshotFixture
import co.electriccoin.zcash.ui.screen.home.ForcePage
import co.electriccoin.zcash.ui.screen.home.HomeScreenIndex
import co.electriccoin.zcash.ui.screen.home.model.TabItem
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch
@OptIn(ExperimentalFoundationApi::class)
@Preview("Home")
@Composable
private fun ComposablePreview() {
ZcashTheme(forceDarkMode = false) {
BlankSurface {
Home(
forcePage = null,
isKeepScreenOnWhileSyncing = false,
isShowingRestoreInitDialog = false,
onPageChange = {},
setShowingRestoreInitDialog = {},
subScreens = persistentListOf(),
walletSnapshot = WalletSnapshotFixture.new(),
@ -67,44 +59,18 @@ private fun ComposablePreview() {
@Suppress("LongParameterList")
@Composable
fun Home(
forcePage: ForcePage?,
isKeepScreenOnWhileSyncing: Boolean?,
isShowingRestoreInitDialog: Boolean,
onPageChange: (HomeScreenIndex) -> Unit,
setShowingRestoreInitDialog: () -> Unit,
subScreens: ImmutableList<TabItem>,
walletSnapshot: WalletSnapshot?,
) {
val pagerState =
pagerState: PagerState =
rememberPagerState(
initialPage = 0,
initialPageOffsetFraction = 0f,
pageCount = { subScreens.size }
)
// Using [rememberUpdatedState] to ensure that always the latest lambda is captured
// And to avoid Detekt warning: Lambda parameters in a @Composable that are referenced directly inside of
// restarting effects can cause issues or unpredictable behavior.
val currentOnPageChange = rememberUpdatedState(newValue = onPageChange)
// Listening for the current page change
LaunchedEffect(pagerState, currentOnPageChange) {
snapshotFlow {
pagerState.currentPage
}.distinctUntilChanged()
.collect { page ->
Twig.info { "Current pager page: $page" }
currentOnPageChange.value(HomeScreenIndex.fromIndex(page))
}
}
// Force page change e.g. when system back navigation event detected
forcePage?.let {
LaunchedEffect(forcePage) {
pagerState.animateScrollToPage(forcePage.currentPage.ordinal)
}
}
) {
HomeContent(
pagerState = pagerState,
subScreens = subScreens,
@ -124,7 +90,7 @@ fun Home(
@Composable
@Suppress("LongMethod")
@OptIn(ExperimentalFoundationApi::class)
fun HomeContent(
private fun HomeContent(
pagerState: PagerState,
subScreens: ImmutableList<TabItem>
) {

View File

@ -1,28 +1,20 @@
package co.electriccoin.zcash.ui.screen.newwalletrecovery
import androidx.activity.ComponentActivity
import androidx.compose.runtime.Composable
import cash.z.ecc.android.sdk.model.PersistableWallet
import co.electriccoin.zcash.spackle.ClipboardManagerUtil
import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.compose.LocalActivity
import co.electriccoin.zcash.ui.common.model.VersionInfo
import co.electriccoin.zcash.ui.screen.newwalletrecovery.view.NewWalletRecovery
@Composable
fun MainActivity.WrapNewWalletRecovery(
fun WrapNewWalletRecovery(
persistableWallet: PersistableWallet,
onBackupComplete: () -> Unit
) {
WrapNewWalletRecovery(this, persistableWallet, onBackupComplete)
}
val activity = LocalActivity.current
@Composable
private fun WrapNewWalletRecovery(
activity: ComponentActivity,
persistableWallet: PersistableWallet,
onBackupComplete: () -> Unit
) {
val versionInfo = VersionInfo.new(activity.applicationContext)
NewWalletRecovery(

View File

@ -3,7 +3,6 @@
package co.electriccoin.zcash.ui.screen.onboarding
import android.content.Context
import androidx.activity.ComponentActivity
import androidx.activity.viewModels
import androidx.compose.runtime.Composable
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@ -15,7 +14,7 @@ import cash.z.ecc.android.sdk.model.SeedPhrase
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.sdk.type.fromResources
import co.electriccoin.zcash.spackle.FirebaseTestLabUtil
import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.common.compose.LocalActivity
import co.electriccoin.zcash.ui.common.model.OnboardingState
import co.electriccoin.zcash.ui.common.model.VersionInfo
import co.electriccoin.zcash.ui.common.model.WalletRestoringState
@ -25,14 +24,10 @@ import co.electriccoin.zcash.ui.screen.onboarding.view.Onboarding
import co.electriccoin.zcash.ui.screen.onboarding.viewmodel.OnboardingViewModel
import co.electriccoin.zcash.ui.screen.restore.WrapRestore
@Composable
internal fun MainActivity.WrapOnboarding() {
WrapOnboarding(this)
}
@Suppress("LongMethod")
@Composable
internal fun WrapOnboarding(activity: ComponentActivity) {
internal fun WrapOnboarding() {
val activity = LocalActivity.current
val walletViewModel by activity.viewModels<WalletViewModel>()
val onboardingViewModel by activity.viewModels<OnboardingViewModel>()

View File

@ -4,7 +4,6 @@ package co.electriccoin.zcash.ui.screen.receive
import android.content.Context
import android.graphics.Bitmap
import androidx.activity.ComponentActivity
import androidx.activity.viewModels
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
@ -16,6 +15,7 @@ import co.electriccoin.zcash.spackle.ClipboardManagerUtil
import co.electriccoin.zcash.spackle.Twig
import co.electriccoin.zcash.spackle.getInternalCacheDirSuspend
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.compose.LocalActivity
import co.electriccoin.zcash.ui.common.compose.ScreenBrightnessState
import co.electriccoin.zcash.ui.common.model.VersionInfo
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
@ -31,10 +31,9 @@ import kotlinx.coroutines.withContext
import java.io.File
@Composable
internal fun WrapReceive(
activity: ComponentActivity,
onSettings: () -> Unit,
) {
internal fun WrapReceive(onSettings: () -> Unit) {
val activity = LocalActivity.current
val walletViewModel by activity.viewModels<WalletViewModel>()
val brightnessViewModel by activity.viewModels<ScreenBrightnessViewModel>()

View File

@ -2,6 +2,7 @@
package co.electriccoin.zcash.ui.screen.restore.view
import androidx.activity.compose.BackHandler
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.ExperimentalFoundationApi
@ -229,6 +230,11 @@ fun RestoreWallet(
val currentStage = restoreState.current.collectAsStateWithLifecycle().value
var isSeedValid by rememberSaveable { mutableStateOf(false) }
BackHandler {
onBack()
}
// To avoid unnecessary recompositions that this flow produces
SideEffect {
scope.launch {

View File

@ -1,6 +1,6 @@
package co.electriccoin.zcash.ui.screen.scan
import android.content.Context
import androidx.activity.compose.BackHandler
import androidx.activity.viewModels
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
@ -9,6 +9,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.type.AddressType
@ -33,8 +34,11 @@ internal fun MainActivity.WrapScanValidator(
val walletState = walletViewModel.walletStateInformation.collectAsStateWithLifecycle().value
BackHandler {
goBack()
}
WrapScan(
context = this,
onScanValid = onScanValid,
goBack = goBack,
synchronizer = synchronizer,
@ -44,12 +48,13 @@ internal fun MainActivity.WrapScanValidator(
@Composable
fun WrapScan(
context: Context,
goBack: () -> Unit,
onScanValid: (address: SerializableAddress) -> Unit,
synchronizer: Synchronizer?,
topAppBarSubTitleState: TopAppBarSubTitleState,
) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val snackbarHostState = remember { SnackbarHostState() }

View File

@ -1,7 +1,6 @@
package co.electriccoin.zcash.ui.screen.scan.view
import android.Manifest
import android.content.Context
import android.view.ViewGroup
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
@ -696,7 +695,6 @@ fun ScanCameraView(
)
imageAnalysis.qrCodeFlow(
context = context,
framePosition = framePosition,
).collectAsState(initial = null).value?.let {
onScanned(it)
@ -707,11 +705,10 @@ fun ScanCameraView(
// Using callbackFlow because QrCodeAnalyzer has a non-suspending callback which makes
// a basic flow builder not work here.
@Composable
fun ImageAnalysis.qrCodeFlow(
context: Context,
framePosition: FramePosition,
): Flow<String> =
remember {
fun ImageAnalysis.qrCodeFlow(framePosition: FramePosition): Flow<String> {
val context = LocalContext.current
return remember {
callbackFlow {
setAnalyzer(
ContextCompat.getMainExecutor(context),
@ -732,3 +729,4 @@ fun ImageAnalysis.qrCodeFlow(
}
}
}
}

View File

@ -1,31 +1,24 @@
package co.electriccoin.zcash.ui.screen.securitywarning
import androidx.activity.ComponentActivity
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import co.electriccoin.zcash.configuration.AndroidConfigurationFactory
import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.common.compose.LocalActivity
import co.electriccoin.zcash.ui.common.model.VersionInfo
import co.electriccoin.zcash.ui.screen.securitywarning.view.SecurityWarning
@Composable
internal fun MainActivity.WrapSecurityWarning(
onBack: () -> Unit,
onConfirm: () -> Unit
) {
WrapSecurityWarning(
this,
onBack = onBack,
onConfirm = onConfirm
)
}
@Composable
internal fun WrapSecurityWarning(
activity: ComponentActivity,
onBack: () -> Unit,
onConfirm: () -> Unit
) {
val activity = LocalActivity.current
BackHandler {
onBack()
}
SecurityWarning(
versionInfo = VersionInfo.new(activity.applicationContext),
onBack = onBack,

View File

@ -1,14 +1,13 @@
package co.electriccoin.zcash.ui.screen.seedrecovery
import androidx.activity.ComponentActivity
import androidx.activity.compose.BackHandler
import androidx.activity.viewModels
import androidx.compose.runtime.Composable
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import cash.z.ecc.android.sdk.Synchronizer
import co.electriccoin.zcash.spackle.ClipboardManagerUtil
import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.compose.LocalActivity
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.common.model.VersionInfo
import co.electriccoin.zcash.ui.common.viewmodel.SecretState
@ -17,11 +16,13 @@ import co.electriccoin.zcash.ui.design.component.CircularScreenProgressIndicator
import co.electriccoin.zcash.ui.screen.seedrecovery.view.SeedRecovery
@Composable
internal fun MainActivity.WrapSeedRecovery(
internal fun WrapSeedRecovery(
goBack: () -> Unit,
onDone: () -> Unit,
) {
val walletViewModel by viewModels<WalletViewModel>()
val activity = LocalActivity.current
val walletViewModel by activity.viewModels<WalletViewModel>()
val synchronizer = walletViewModel.synchronizer.collectAsStateWithLifecycle().value
@ -30,7 +31,6 @@ internal fun MainActivity.WrapSeedRecovery(
val walletState = walletViewModel.walletStateInformation.collectAsStateWithLifecycle().value
WrapSeedRecovery(
activity = this,
goBack = goBack,
onDone = onDone,
secretState = secretState,
@ -42,13 +42,14 @@ internal fun MainActivity.WrapSeedRecovery(
@Composable
@Suppress("LongParameterList")
private fun WrapSeedRecovery(
activity: ComponentActivity,
goBack: () -> Unit,
onDone: () -> Unit,
topAppBarSubTitleState: TopAppBarSubTitleState,
synchronizer: Synchronizer?,
secretState: SecretState,
) {
val activity = LocalActivity.current
BackHandler {
goBack()
}

View File

@ -3,8 +3,6 @@
package co.electriccoin.zcash.ui.screen.send
import android.content.pm.PackageManager
import androidx.activity.ComponentActivity
import androidx.activity.compose.BackHandler
import androidx.activity.viewModels
import androidx.annotation.VisibleForTesting
import androidx.compose.runtime.Composable
@ -12,9 +10,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.focus.FocusManager
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.model.MonetarySeparators
@ -25,12 +21,11 @@ import cash.z.ecc.android.sdk.model.toZecString
import cash.z.ecc.android.sdk.type.AddressType
import co.electriccoin.zcash.spackle.Twig
import co.electriccoin.zcash.ui.common.compose.BalanceState
import co.electriccoin.zcash.ui.common.compose.LocalActivity
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.common.model.WalletSnapshot
import co.electriccoin.zcash.ui.common.viewmodel.HomeViewModel
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
import co.electriccoin.zcash.ui.design.component.CircularScreenProgressIndicator
import co.electriccoin.zcash.ui.screen.home.HomeScreenIndex
import co.electriccoin.zcash.ui.screen.send.ext.Saver
import co.electriccoin.zcash.ui.screen.send.model.AmountState
import co.electriccoin.zcash.ui.screen.send.model.MemoState
@ -44,7 +39,6 @@ import java.util.Locale
@Composable
@Suppress("LongParameterList")
internal fun WrapSend(
activity: ComponentActivity,
sendArguments: SendArguments?,
goToQrScanner: () -> Unit,
goBack: () -> Unit,
@ -52,6 +46,8 @@ internal fun WrapSend(
goSendConfirmation: (ZecSend) -> Unit,
goSettings: () -> Unit,
) {
val activity = LocalActivity.current
val walletViewModel by activity.viewModels<WalletViewModel>()
val hasCameraFeature = activity.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)
@ -62,15 +58,6 @@ internal fun WrapSend(
val spendingKey = walletViewModel.spendingKey.collectAsStateWithLifecycle().value
val homeViewModel by activity.viewModels<HomeViewModel>()
val focusManager = LocalFocusManager.current
if (homeViewModel.screenIndex.collectAsStateWithLifecycle().value != HomeScreenIndex.SEND) {
// Clear focus on Send Form text fields
focusManager.clearFocus(true)
}
// TODO [#1171]: Remove default MonetarySeparators locale
// TODO [#1171]: https://github.com/Electric-Coin-Company/zashi-android/issues/1171
val monetarySeparators = MonetarySeparators.current(Locale.US)
@ -85,7 +72,6 @@ internal fun WrapSend(
synchronizer = synchronizer,
walletSnapshot = walletSnapshot,
spendingKey = spendingKey,
focusManager = focusManager,
goToQrScanner = goToQrScanner,
goBack = goBack,
goBalances = goBalances,
@ -102,7 +88,6 @@ internal fun WrapSend(
@Composable
internal fun WrapSend(
balanceState: BalanceState,
focusManager: FocusManager,
goToQrScanner: () -> Unit,
goBack: () -> Unit,
goBalances: () -> Unit,
@ -188,10 +173,6 @@ internal fun WrapSend(
}
}
BackHandler {
onBackAction()
}
if (null == synchronizer || null == walletSnapshot || null == spendingKey) {
// TODO [#1146]: Consider moving CircularScreenProgressIndicator from Android layer to View layer
// TODO [#1146]: Improve this by allowing screen composition and updating it after the data is available
@ -217,7 +198,6 @@ internal fun WrapSend(
}
}
},
focusManager = focusManager,
onBack = onBackAction,
onSettings = goSettings,
recipientAddressState = recipientAddressState,

View File

@ -31,7 +31,6 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.focus.FocusManager
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInRoot
@ -93,7 +92,6 @@ private fun PreviewSendForm() {
Send(
sendStage = SendStage.Form,
onCreateZecSend = {},
focusManager = LocalFocusManager.current,
onBack = {},
onSettings = {},
onQrScannerOpen = {},
@ -119,7 +117,6 @@ private fun SendFormTransparentAddressPreview() {
Send(
sendStage = SendStage.Form,
onCreateZecSend = {},
focusManager = LocalFocusManager.current,
onBack = {},
onSettings = {},
onQrScannerOpen = {},
@ -151,7 +148,6 @@ fun Send(
balanceState: BalanceState,
sendStage: SendStage,
onCreateZecSend: (ZecSend) -> Unit,
focusManager: FocusManager,
onBack: () -> Unit,
onSettings: () -> Unit,
onQrScannerOpen: () -> Unit,
@ -176,7 +172,6 @@ fun Send(
balanceState = balanceState,
walletSnapshot = walletSnapshot,
onBack = onBack,
focusManager = focusManager,
sendStage = sendStage,
onCreateZecSend = onCreateZecSend,
recipientAddressState = recipientAddressState,
@ -232,7 +227,6 @@ private fun SendTopAppBar(
private fun SendMainContent(
balanceState: BalanceState,
walletSnapshot: WalletSnapshot,
focusManager: FocusManager,
onBack: () -> Unit,
goBalances: () -> Unit,
onCreateZecSend: (ZecSend) -> Unit,
@ -260,7 +254,6 @@ private fun SendMainContent(
memoState = memoState,
setMemoState = setMemoState,
onCreateZecSend = onCreateZecSend,
focusManager = focusManager,
onQrScannerOpen = onQrScannerOpen,
goBalances = goBalances,
hasCameraFeature = hasCameraFeature,
@ -286,7 +279,6 @@ private fun SendMainContent(
private fun SendForm(
balanceState: BalanceState,
walletSnapshot: WalletSnapshot,
focusManager: FocusManager,
recipientAddressState: RecipientAddressState,
onRecipientAddressChange: (String) -> Unit,
amountState: AmountState,
@ -329,7 +321,6 @@ private fun SendForm(
// TODO [#1256]: https://github.com/Electric-Coin-Company/zashi-android/issues/1256
SendFormAddressTextField(
focusManager = focusManager,
hasCameraFeature = hasCameraFeature,
onQrScannerOpen = onQrScannerOpen,
recipientAddressState = recipientAddressState,
@ -340,7 +331,6 @@ private fun SendForm(
SendFormAmountTextField(
amountSate = amountState,
focusManager = focusManager,
imeAction =
if (recipientAddressState.type == AddressType.Transparent) {
ImeAction.Done
@ -358,7 +348,6 @@ private fun SendForm(
SendFormMemoTextField(
memoState = memoState,
setMemoState = setMemoState,
focusManager = focusManager,
isMemoFieldAvailable = (
recipientAddressState.address.isEmpty() ||
recipientAddressState.type is AddressType.Invalid ||
@ -475,12 +464,13 @@ fun SendButton(
@Suppress("LongMethod")
@Composable
fun SendFormAddressTextField(
focusManager: FocusManager,
hasCameraFeature: Boolean,
onQrScannerOpen: () -> Unit,
recipientAddressState: RecipientAddressState,
setRecipientAddress: (String) -> Unit,
) {
val focusManager = LocalFocusManager.current
val bringIntoViewRequester = remember { BringIntoViewRequester() }
Column(
@ -560,13 +550,14 @@ fun SendFormAddressTextField(
@Composable
fun SendFormAmountTextField(
amountSate: AmountState,
focusManager: FocusManager,
imeAction: ImeAction,
isTransparentRecipient: Boolean,
monetarySeparators: MonetarySeparators,
setAmountState: (AmountState) -> Unit,
walletSnapshot: WalletSnapshot,
) {
val focusManager = LocalFocusManager.current
val context = LocalContext.current
val zcashCurrency = ZcashCurrency.getLocalizedName(context)
@ -580,6 +571,7 @@ fun SendFormAmountTextField(
stringResource(id = R.string.send_amount_invalid)
}
}
is AmountState.Valid -> {
if (walletSnapshot.spendableBalance() < amountSate.zatoshi) {
stringResource(id = R.string.send_amount_insufficient_balance)
@ -651,13 +643,14 @@ fun SendFormAmountTextField(
@Suppress("LongMethod", "LongParameterList")
@Composable
fun SendFormMemoTextField(
focusManager: FocusManager,
isMemoFieldAvailable: Boolean,
memoState: MemoState,
setMemoState: (MemoState) -> Unit,
scrollState: ScrollState,
scrollTo: Int
) {
val focusManager = LocalFocusManager.current
val scope = rememberCoroutineScope()
val bringIntoViewRequester = remember { BringIntoViewRequester() }

View File

@ -1,12 +1,10 @@
package co.electriccoin.zcash.ui.screen.settings
import androidx.activity.ComponentActivity
import androidx.activity.compose.BackHandler
import androidx.activity.viewModels
import androidx.compose.runtime.Composable
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.common.compose.LocalActivity
import co.electriccoin.zcash.ui.common.model.VersionInfo
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
import co.electriccoin.zcash.ui.configuration.ConfigurationEntries
@ -17,42 +15,20 @@ import co.electriccoin.zcash.ui.screen.settings.view.Settings
import co.electriccoin.zcash.ui.screen.settings.viewmodel.SettingsViewModel
@Composable
internal fun MainActivity.WrapSettings(
internal fun WrapSettings(
goAbout: () -> Unit,
goAdvancedSettings: () -> Unit,
goBack: () -> Unit,
goFeedback: () -> Unit,
) {
val walletViewModel by viewModels<WalletViewModel>()
val activity = LocalActivity.current
val settingsViewModel by viewModels<SettingsViewModel>()
val walletViewModel by activity.viewModels<WalletViewModel>()
val settingsViewModel by activity.viewModels<SettingsViewModel>()
val walletState = walletViewModel.walletStateInformation.collectAsStateWithLifecycle().value
WrapSettings(
activity = this,
goAbout = goAbout,
goAdvancedSettings = goAdvancedSettings,
goBack = goBack,
goFeedback = goFeedback,
settingsViewModel = settingsViewModel,
topAppBarSubTitleState = walletState,
walletViewModel = walletViewModel,
)
}
@Composable
@Suppress("LongParameterList")
private fun WrapSettings(
activity: ComponentActivity,
goAbout: () -> Unit,
goAdvancedSettings: () -> Unit,
goBack: () -> Unit,
goFeedback: () -> Unit,
settingsViewModel: SettingsViewModel,
topAppBarSubTitleState: TopAppBarSubTitleState,
walletViewModel: WalletViewModel,
) {
val isBackgroundSyncEnabled = settingsViewModel.isBackgroundSync.collectAsStateWithLifecycle().value
val isKeepScreenOnWhileSyncing = settingsViewModel.isKeepScreenOnWhileSyncing.collectAsStateWithLifecycle().value
val isAnalyticsEnabled = settingsViewModel.isAnalyticsEnabled.collectAsStateWithLifecycle().value
@ -97,7 +73,7 @@ private fun WrapSettings(
onAnalyticsSettingsChanged = {
settingsViewModel.setAnalyticsEnabled(it)
},
topAppBarSubTitleState = topAppBarSubTitleState,
topAppBarSubTitleState = walletState,
)
}
}

View File

@ -3,8 +3,9 @@
package co.electriccoin.zcash.ui.screen.support
import android.content.Intent
import androidx.activity.ComponentActivity
import androidx.activity.compose.BackHandler
import androidx.activity.viewModels
import androidx.annotation.VisibleForTesting
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
@ -12,8 +13,8 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.compose.LocalActivity
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
import co.electriccoin.zcash.ui.screen.support.model.SupportInfo
@ -24,30 +25,37 @@ import co.electriccoin.zcash.ui.util.EmailUtil
import kotlinx.coroutines.launch
@Composable
internal fun MainActivity.WrapSupport(goBack: () -> Unit) {
val supportViewModel by viewModels<SupportViewModel>()
internal fun WrapSupport(goBack: () -> Unit) {
val activity = LocalActivity.current
val walletViewModel by viewModels<WalletViewModel>()
val supportViewModel by activity.viewModels<SupportViewModel>()
val walletViewModel by activity.viewModels<WalletViewModel>()
val supportInfo = supportViewModel.supportInfo.collectAsStateWithLifecycle().value
val walletState = walletViewModel.walletStateInformation.collectAsStateWithLifecycle().value
BackHandler {
goBack()
}
WrapSupport(
activity = this,
goBack = goBack,
supportInfo = supportInfo,
topAppBarSubTitleState = walletState
)
}
@VisibleForTesting
@Composable
internal fun WrapSupport(
activity: ComponentActivity,
goBack: () -> Unit,
supportInfo: SupportInfo?,
topAppBarSubTitleState: TopAppBarSubTitleState,
) {
val activity = LocalActivity.current
val snackbarHostState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope()

View File

@ -1,7 +1,6 @@
package co.electriccoin.zcash.ui.screen.update
import android.content.Context
import androidx.activity.ComponentActivity
import androidx.activity.compose.BackHandler
import androidx.activity.viewModels
import androidx.annotation.VisibleForTesting
@ -11,8 +10,8 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.compose.LocalActivity
import co.electriccoin.zcash.ui.common.viewmodel.CheckUpdateViewModel
import co.electriccoin.zcash.ui.screen.update.model.UpdateInfo
import co.electriccoin.zcash.ui.screen.update.model.UpdateState
@ -23,12 +22,9 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@Composable
internal fun MainActivity.WrapCheckForUpdate() {
WrapCheckForUpdate(this)
}
internal fun WrapCheckForUpdate() {
val activity = LocalActivity.current
@Composable
private fun WrapCheckForUpdate(activity: ComponentActivity) {
// TODO [#403]: Manual testing of already implemented in-app update mechanisms
// TODO [#403]: https://github.com/Electric-Coin-Company/zashi-android/issues/403
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
@ -43,7 +39,7 @@ private fun WrapCheckForUpdate(activity: ComponentActivity) {
updateInfo?.let {
if (it.appUpdateInfo != null && it.state == UpdateState.Prepared) {
WrapUpdate(activity, updateInfo)
WrapUpdate(updateInfo)
}
}
@ -56,10 +52,9 @@ private fun WrapCheckForUpdate(activity: ComponentActivity) {
@VisibleForTesting
@Composable
internal fun WrapUpdate(
activity: ComponentActivity,
inputUpdateInfo: UpdateInfo
) {
internal fun WrapUpdate(inputUpdateInfo: UpdateInfo) {
val activity = LocalActivity.current
val viewModel by activity.viewModels<UpdateViewModel> {
UpdateViewModel.UpdateViewModelFactory(
activity.application,
@ -78,10 +73,12 @@ internal fun WrapUpdate(
// just return as we are already in Home compose
return
}
UpdateState.Failed -> {
// we need to refresh AppUpdateInfo object, as it can be used only once
viewModel.checkForAppUpdate()
}
UpdateState.Prepared, UpdateState.Running -> {
// valid stages
}