#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] ## [Unreleased]
### Fixed
- The app navigation has been improved to always behave the same for system, gesture, or top app bar back navigation actions.
### Added ### Added
- Proper ZEC amount abbreviation has been added across the entire app as described by the design document - 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.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.test.junit4.ComposeContentTestRule import androidx.compose.ui.test.junit4.ComposeContentTestRule
import cash.z.ecc.android.sdk.fixture.WalletBalanceFixture import cash.z.ecc.android.sdk.fixture.WalletBalanceFixture
import cash.z.ecc.android.sdk.model.MonetarySeparators import cash.z.ecc.android.sdk.model.MonetarySeparators
@ -69,11 +68,6 @@ class SendViewTestSetup(
return lastZecSend return lastZecSend
} }
fun getLastSendStage(): SendStage? {
composeTestRule.waitForIdle()
return lastSendStage
}
@Composable @Composable
@Suppress("TestFunctionName") @Suppress("TestFunctionName")
fun DefaultContent() { fun DefaultContent() {
@ -111,7 +105,6 @@ class SendViewTestSetup(
balanceState = BalanceStateFixture.new(), balanceState = BalanceStateFixture.new(),
sendStage = sendStage, sendStage = sendStage,
onCreateZecSend = setZecSend, onCreateZecSend = setZecSend,
focusManager = LocalFocusManager.current,
onBack = onBackAction, onBack = onBackAction,
onSettings = { onSettingsCount.incrementAndGet() }, onSettings = { onSettingsCount.incrementAndGet() },
onQrScannerOpen = { onQrScannerOpen = {

View File

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

View File

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

View File

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

View File

@ -5,13 +5,15 @@ import androidx.activity.ComponentActivity
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.compose.rememberNavController
import cash.z.ecc.android.sdk.ext.collectWith import cash.z.ecc.android.sdk.ext.collectWith
import co.electriccoin.zcash.spackle.EmulatorWtfUtil import co.electriccoin.zcash.spackle.EmulatorWtfUtil
import co.electriccoin.zcash.spackle.FirebaseTestLabUtil import co.electriccoin.zcash.spackle.FirebaseTestLabUtil
import co.electriccoin.zcash.ui.MainActivity
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
@Composable @Composable
fun ComponentActivity.BindCompLocalProvider(content: @Composable () -> Unit) { fun MainActivity.BindCompLocalProvider(content: @Composable () -> Unit) {
val screenSecurity = ScreenSecurity() val screenSecurity = ScreenSecurity()
observeScreenSecurityFlag(screenSecurity) observeScreenSecurityFlag(screenSecurity)
@ -20,10 +22,18 @@ fun ComponentActivity.BindCompLocalProvider(content: @Composable () -> Unit) {
val screenTimeout = ScreenTimeout() val screenTimeout = ScreenTimeout()
observeScreenTimeoutFlag(screenTimeout) observeScreenTimeoutFlag(screenTimeout)
val navController =
rememberNavController().also {
navControllerForTesting = it
}
CompositionLocalProvider( CompositionLocalProvider(
LocalScreenSecurity provides screenSecurity, LocalScreenSecurity provides screenSecurity,
LocalScreenBrightness provides screenBrightness, LocalScreenBrightness provides screenBrightness,
LocalScreenTimeout provides screenTimeout, LocalScreenTimeout provides screenTimeout,
LocalNavController provides navController,
LocalActivity provides this,
content = content 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.common.ANDROID_STATE_FLOW_TIMEOUT
import co.electriccoin.zcash.ui.preference.StandardPreferenceKeys import co.electriccoin.zcash.ui.preference.StandardPreferenceKeys
import co.electriccoin.zcash.ui.preference.StandardPreferenceSingleton 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.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.WhileSubscribed import kotlinx.coroutines.flow.WhileSubscribed
@ -20,11 +18,6 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class HomeViewModel(application: Application) : AndroidViewModel(application) { 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 * A flow of whether background sync is enabled
*/ */
@ -37,12 +30,6 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
val isKeepScreenOnWhileSyncing: StateFlow<Boolean?> = val isKeepScreenOnWhileSyncing: StateFlow<Boolean?> =
booleanStateFlow(StandardPreferenceKeys.IS_KEEP_SCREEN_ON_DURING_SYNC) 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 * 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 package co.electriccoin.zcash.ui.screen.about
import android.content.Context import android.content.Context
import androidx.activity.ComponentActivity import androidx.activity.compose.BackHandler
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -12,9 +12,8 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.configuration.AndroidConfigurationFactory import co.electriccoin.zcash.configuration.AndroidConfigurationFactory
import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.R 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.model.VersionInfo
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
import co.electriccoin.zcash.ui.screen.about.util.WebBrowserUtil import co.electriccoin.zcash.ui.screen.about.util.WebBrowserUtil
@ -24,24 +23,17 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Composable @Composable
internal fun MainActivity.WrapAbout(goBack: () -> Unit) { internal fun WrapAbout(goBack: () -> Unit) {
val walletViewModel by viewModels<WalletViewModel>() val activity = LocalActivity.current
val walletViewModel by activity.viewModels<WalletViewModel>()
val walletState = walletViewModel.walletStateInformation.collectAsStateWithLifecycle().value val walletState = walletViewModel.walletStateInformation.collectAsStateWithLifecycle().value
WrapAbout( BackHandler {
activity = this, goBack()
goBack = goBack, }
topAppBarSubTitleState = walletState
)
}
@Composable
internal fun WrapAbout(
activity: ComponentActivity,
goBack: () -> Unit,
topAppBarSubTitleState: TopAppBarSubTitleState,
) {
val configInfo = ConfigInfo.new(AndroidConfigurationFactory.getInstance(activity.applicationContext)) val configInfo = ConfigInfo.new(AndroidConfigurationFactory.getInstance(activity.applicationContext))
val versionInfo = VersionInfo.new(activity.applicationContext) val versionInfo = VersionInfo.new(activity.applicationContext)
@ -66,7 +58,7 @@ internal fun WrapAbout(
) )
}, },
snackbarHostState = snackbarHostState, snackbarHostState = snackbarHostState,
topAppBarSubTitleState = topAppBarSubTitleState, topAppBarSubTitleState = walletState,
) )
} }

View File

@ -3,19 +3,20 @@
package co.electriccoin.zcash.ui.screen.account package co.electriccoin.zcash.ui.screen.account
import android.content.Context import android.content.Context
import androidx.activity.ComponentActivity
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import cash.z.ecc.android.sdk.Synchronizer import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.internal.Twig import cash.z.ecc.android.sdk.internal.Twig
import co.electriccoin.zcash.spackle.ClipboardManagerUtil import co.electriccoin.zcash.spackle.ClipboardManagerUtil
import co.electriccoin.zcash.ui.R import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.compose.BalanceState 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.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.common.model.WalletRestoringState import co.electriccoin.zcash.ui.common.model.WalletRestoringState
import co.electriccoin.zcash.ui.common.model.WalletSnapshot import co.electriccoin.zcash.ui.common.model.WalletSnapshot
@ -33,10 +34,11 @@ import org.jetbrains.annotations.VisibleForTesting
@Composable @Composable
internal fun WrapAccount( internal fun WrapAccount(
activity: ComponentActivity,
goBalances: () -> Unit, goBalances: () -> Unit,
goSettings: () -> Unit, goSettings: () -> Unit,
) { ) {
val activity = LocalActivity.current
val walletViewModel by activity.viewModels<WalletViewModel>() val walletViewModel by activity.viewModels<WalletViewModel>()
val transactionHistoryViewModel by activity.viewModels<TransactionHistoryViewModel>() val transactionHistoryViewModel by activity.viewModels<TransactionHistoryViewModel>()
@ -59,7 +61,6 @@ internal fun WrapAccount(
WrapAccount( WrapAccount(
balanceState = balanceState, balanceState = balanceState,
context = activity.applicationContext,
goBalances = goBalances, goBalances = goBalances,
goSettings = goSettings, goSettings = goSettings,
synchronizer = synchronizer, synchronizer = synchronizer,
@ -79,7 +80,6 @@ internal fun WrapAccount(
@Suppress("LongParameterList", "LongMethod") @Suppress("LongParameterList", "LongMethod")
internal fun WrapAccount( internal fun WrapAccount(
balanceState: BalanceState, balanceState: BalanceState,
context: Context,
goBalances: () -> Unit, goBalances: () -> Unit,
goSettings: () -> Unit, goSettings: () -> Unit,
transactionsUiState: TransactionUiState, transactionsUiState: TransactionUiState,
@ -89,6 +89,8 @@ internal fun WrapAccount(
walletRestoringState: WalletRestoringState, walletRestoringState: WalletRestoringState,
walletSnapshot: WalletSnapshot? walletSnapshot: WalletSnapshot?
) { ) {
val context = LocalContext.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val snackbarHostState = remember { SnackbarHostState() } 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.UnifiedSpendingKey
import cash.z.ecc.android.sdk.model.Zatoshi import cash.z.ecc.android.sdk.model.Zatoshi
import co.electriccoin.zcash.spackle.Twig import co.electriccoin.zcash.spackle.Twig
import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.R import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.compose.BalanceState 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.TopAppBarSubTitleState
import co.electriccoin.zcash.ui.common.model.WalletRestoringState import co.electriccoin.zcash.ui.common.model.WalletRestoringState
import co.electriccoin.zcash.ui.common.model.WalletSnapshot import co.electriccoin.zcash.ui.common.model.WalletSnapshot
@ -45,10 +45,11 @@ import org.jetbrains.annotations.VisibleForTesting
@Composable @Composable
internal fun WrapBalances( internal fun WrapBalances(
activity: MainActivity,
goSettings: () -> Unit, goSettings: () -> Unit,
goMultiTrxSubmissionFailure: () -> Unit, goMultiTrxSubmissionFailure: () -> Unit,
) { ) {
val activity = LocalActivity.current
val walletViewModel by activity.viewModels<WalletViewModel>() val walletViewModel by activity.viewModels<WalletViewModel>()
val createTransactionsViewModel by activity.viewModels<CreateTransactionsViewModel>() val createTransactionsViewModel by activity.viewModels<CreateTransactionsViewModel>()
@ -89,9 +90,9 @@ internal fun WrapBalances(
const val DEFAULT_SHIELDING_THRESHOLD = 100000L const val DEFAULT_SHIELDING_THRESHOLD = 100000L
// This function should be refactored into smaller chunks
@Composable @Composable
@VisibleForTesting @VisibleForTesting
// This function should be refactored into smaller chunks
@Suppress("LongParameterList", "LongMethod", "CyclomaticComplexMethod") @Suppress("LongParameterList", "LongMethod", "CyclomaticComplexMethod")
internal fun WrapBalances( internal fun WrapBalances(
balanceState: BalanceState, balanceState: BalanceState,

View File

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

View File

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

View File

@ -16,10 +16,7 @@ import androidx.compose.material3.TabRow
import androidx.compose.material3.TabRowDefaults import androidx.compose.material3.TabRowDefaults
import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.snapshotFlow
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.res.stringResource
@ -28,7 +25,6 @@ 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.spackle.Twig
import co.electriccoin.zcash.ui.R 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
@ -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.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.ForcePage
import co.electriccoin.zcash.ui.screen.home.HomeScreenIndex
import co.electriccoin.zcash.ui.screen.home.model.TabItem import co.electriccoin.zcash.ui.screen.home.model.TabItem
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@OptIn(ExperimentalFoundationApi::class)
@Preview("Home") @Preview("Home")
@Composable @Composable
private fun ComposablePreview() { private fun ComposablePreview() {
ZcashTheme(forceDarkMode = false) { ZcashTheme(forceDarkMode = false) {
BlankSurface { BlankSurface {
Home( Home(
forcePage = null,
isKeepScreenOnWhileSyncing = false, isKeepScreenOnWhileSyncing = false,
isShowingRestoreInitDialog = false, isShowingRestoreInitDialog = false,
onPageChange = {},
setShowingRestoreInitDialog = {}, setShowingRestoreInitDialog = {},
subScreens = persistentListOf(), subScreens = persistentListOf(),
walletSnapshot = WalletSnapshotFixture.new(), walletSnapshot = WalletSnapshotFixture.new(),
@ -67,44 +59,18 @@ private fun ComposablePreview() {
@Suppress("LongParameterList") @Suppress("LongParameterList")
@Composable @Composable
fun Home( fun Home(
forcePage: ForcePage?,
isKeepScreenOnWhileSyncing: Boolean?, isKeepScreenOnWhileSyncing: Boolean?,
isShowingRestoreInitDialog: Boolean, isShowingRestoreInitDialog: Boolean,
onPageChange: (HomeScreenIndex) -> Unit,
setShowingRestoreInitDialog: () -> Unit, setShowingRestoreInitDialog: () -> Unit,
subScreens: ImmutableList<TabItem>, subScreens: ImmutableList<TabItem>,
walletSnapshot: WalletSnapshot?, walletSnapshot: WalletSnapshot?,
) { pagerState: PagerState =
val pagerState =
rememberPagerState( rememberPagerState(
initialPage = 0, initialPage = 0,
initialPageOffsetFraction = 0f, initialPageOffsetFraction = 0f,
pageCount = { subScreens.size } 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( HomeContent(
pagerState = pagerState, pagerState = pagerState,
subScreens = subScreens, subScreens = subScreens,
@ -124,7 +90,7 @@ fun Home(
@Composable @Composable
@Suppress("LongMethod") @Suppress("LongMethod")
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
fun HomeContent( private fun HomeContent(
pagerState: PagerState, pagerState: PagerState,
subScreens: ImmutableList<TabItem> subScreens: ImmutableList<TabItem>
) { ) {

View File

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

View File

@ -3,7 +3,6 @@
package co.electriccoin.zcash.ui.screen.onboarding package co.electriccoin.zcash.ui.screen.onboarding
import android.content.Context import android.content.Context
import androidx.activity.ComponentActivity
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.lifecycle.compose.collectAsStateWithLifecycle 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.android.sdk.model.ZcashNetwork
import cash.z.ecc.sdk.type.fromResources import cash.z.ecc.sdk.type.fromResources
import co.electriccoin.zcash.spackle.FirebaseTestLabUtil 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.OnboardingState
import co.electriccoin.zcash.ui.common.model.VersionInfo import co.electriccoin.zcash.ui.common.model.VersionInfo
import co.electriccoin.zcash.ui.common.model.WalletRestoringState 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.onboarding.viewmodel.OnboardingViewModel
import co.electriccoin.zcash.ui.screen.restore.WrapRestore import co.electriccoin.zcash.ui.screen.restore.WrapRestore
@Composable
internal fun MainActivity.WrapOnboarding() {
WrapOnboarding(this)
}
@Suppress("LongMethod") @Suppress("LongMethod")
@Composable @Composable
internal fun WrapOnboarding(activity: ComponentActivity) { internal fun WrapOnboarding() {
val activity = LocalActivity.current
val walletViewModel by activity.viewModels<WalletViewModel>() val walletViewModel by activity.viewModels<WalletViewModel>()
val onboardingViewModel by activity.viewModels<OnboardingViewModel>() 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.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import androidx.activity.ComponentActivity
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable 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.Twig
import co.electriccoin.zcash.spackle.getInternalCacheDirSuspend import co.electriccoin.zcash.spackle.getInternalCacheDirSuspend
import co.electriccoin.zcash.ui.R 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.compose.ScreenBrightnessState
import co.electriccoin.zcash.ui.common.model.VersionInfo import co.electriccoin.zcash.ui.common.model.VersionInfo
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
@ -31,10 +31,9 @@ import kotlinx.coroutines.withContext
import java.io.File import java.io.File
@Composable @Composable
internal fun WrapReceive( internal fun WrapReceive(onSettings: () -> Unit) {
activity: ComponentActivity, val activity = LocalActivity.current
onSettings: () -> Unit,
) {
val walletViewModel by activity.viewModels<WalletViewModel>() val walletViewModel by activity.viewModels<WalletViewModel>()
val brightnessViewModel by activity.viewModels<ScreenBrightnessViewModel>() val brightnessViewModel by activity.viewModels<ScreenBrightnessViewModel>()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,12 +1,10 @@
package co.electriccoin.zcash.ui.screen.settings package co.electriccoin.zcash.ui.screen.settings
import androidx.activity.ComponentActivity
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.ui.MainActivity 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.model.VersionInfo
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
import co.electriccoin.zcash.ui.configuration.ConfigurationEntries 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 import co.electriccoin.zcash.ui.screen.settings.viewmodel.SettingsViewModel
@Composable @Composable
internal fun MainActivity.WrapSettings( internal fun WrapSettings(
goAbout: () -> Unit, goAbout: () -> Unit,
goAdvancedSettings: () -> Unit, goAdvancedSettings: () -> Unit,
goBack: () -> Unit, goBack: () -> Unit,
goFeedback: () -> 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 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 isBackgroundSyncEnabled = settingsViewModel.isBackgroundSync.collectAsStateWithLifecycle().value
val isKeepScreenOnWhileSyncing = settingsViewModel.isKeepScreenOnWhileSyncing.collectAsStateWithLifecycle().value val isKeepScreenOnWhileSyncing = settingsViewModel.isKeepScreenOnWhileSyncing.collectAsStateWithLifecycle().value
val isAnalyticsEnabled = settingsViewModel.isAnalyticsEnabled.collectAsStateWithLifecycle().value val isAnalyticsEnabled = settingsViewModel.isAnalyticsEnabled.collectAsStateWithLifecycle().value
@ -97,7 +73,7 @@ private fun WrapSettings(
onAnalyticsSettingsChanged = { onAnalyticsSettingsChanged = {
settingsViewModel.setAnalyticsEnabled(it) settingsViewModel.setAnalyticsEnabled(it)
}, },
topAppBarSubTitleState = topAppBarSubTitleState, topAppBarSubTitleState = walletState,
) )
} }
} }

View File

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

View File

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