[#185] Refactor navigation
This moves navigation to its own file, simplifies the MainActivity, and moves Android integration composables to their own files. Navigation still does not have tests, so a separate change will need to be implemented to add testing to navigation.
This commit is contained in:
parent
1bd5e7518e
commit
334dc3c41f
|
@ -32,6 +32,7 @@ import cash.z.ecc.sdk.fixture.WalletAddressFixture
|
||||||
import co.electriccoin.zcash.spackle.FirebaseTestLabUtil
|
import co.electriccoin.zcash.spackle.FirebaseTestLabUtil
|
||||||
import co.electriccoin.zcash.test.UiTestPrerequisites
|
import co.electriccoin.zcash.test.UiTestPrerequisites
|
||||||
import co.electriccoin.zcash.ui.MainActivity
|
import co.electriccoin.zcash.ui.MainActivity
|
||||||
|
import co.electriccoin.zcash.ui.NavigationTargets
|
||||||
import co.electriccoin.zcash.ui.R
|
import co.electriccoin.zcash.ui.R
|
||||||
import co.electriccoin.zcash.ui.design.component.ConfigurationOverride
|
import co.electriccoin.zcash.ui.design.component.ConfigurationOverride
|
||||||
import co.electriccoin.zcash.ui.design.component.UiMode
|
import co.electriccoin.zcash.ui.design.component.UiMode
|
||||||
|
@ -301,7 +302,7 @@ class ScreenshotTest : UiTestPrerequisites() {
|
||||||
composeTestRule.waitUntil { composeTestRule.activity.walletViewModel.walletSnapshot.value != null }
|
composeTestRule.waitUntil { composeTestRule.activity.walletViewModel.walletSnapshot.value != null }
|
||||||
requestZecScreenshots(resContext, tag, composeTestRule)
|
requestZecScreenshots(resContext, tag, composeTestRule)
|
||||||
|
|
||||||
navigateTo(MainActivity.NAV_HOME)
|
navigateTo(NavigationTargets.HOME)
|
||||||
composeTestRule.waitUntil { composeTestRule.activity.walletViewModel.secretState.value is SecretState.Ready }
|
composeTestRule.waitUntil { composeTestRule.activity.walletViewModel.secretState.value is SecretState.Ready }
|
||||||
|
|
||||||
composeTestRule.onNode(hasText(resContext.getString(R.string.home_button_send))).also {
|
composeTestRule.onNode(hasText(resContext.getString(R.string.home_button_send))).also {
|
||||||
|
@ -313,7 +314,7 @@ class ScreenshotTest : UiTestPrerequisites() {
|
||||||
composeTestRule.waitUntil { composeTestRule.activity.walletViewModel.walletSnapshot.value != null }
|
composeTestRule.waitUntil { composeTestRule.activity.walletViewModel.walletSnapshot.value != null }
|
||||||
sendZecScreenshots(resContext, tag, composeTestRule)
|
sendZecScreenshots(resContext, tag, composeTestRule)
|
||||||
|
|
||||||
navigateTo(MainActivity.NAV_HOME)
|
navigateTo(NavigationTargets.HOME)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
package co.electriccoin.zcash.ui
|
package co.electriccoin.zcash.ui
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
|
@ -11,67 +8,31 @@ import androidx.activity.viewModels
|
||||||
import androidx.annotation.VisibleForTesting
|
import androidx.annotation.VisibleForTesting
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.NavHostController
|
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.sdk.model.ZecRequest
|
|
||||||
import cash.z.ecc.sdk.send
|
|
||||||
import co.electriccoin.zcash.ui.design.compat.FontCompat
|
import co.electriccoin.zcash.ui.design.compat.FontCompat
|
||||||
import co.electriccoin.zcash.ui.design.component.ConfigurationOverride
|
import co.electriccoin.zcash.ui.design.component.ConfigurationOverride
|
||||||
import co.electriccoin.zcash.ui.design.component.GradientSurface
|
import co.electriccoin.zcash.ui.design.component.GradientSurface
|
||||||
import co.electriccoin.zcash.ui.design.component.Override
|
import co.electriccoin.zcash.ui.design.component.Override
|
||||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||||
import co.electriccoin.zcash.ui.screen.about.WrapAbout
|
|
||||||
import co.electriccoin.zcash.ui.screen.address.view.WalletAddresses
|
|
||||||
import co.electriccoin.zcash.ui.screen.backup.WrapBackup
|
import co.electriccoin.zcash.ui.screen.backup.WrapBackup
|
||||||
import co.electriccoin.zcash.ui.screen.backup.copyToClipboard
|
|
||||||
import co.electriccoin.zcash.ui.screen.home.WrapHome
|
|
||||||
import co.electriccoin.zcash.ui.screen.home.model.spendableBalance
|
|
||||||
import co.electriccoin.zcash.ui.screen.home.viewmodel.CheckUpdateViewModel
|
|
||||||
import co.electriccoin.zcash.ui.screen.home.viewmodel.SecretState
|
import co.electriccoin.zcash.ui.screen.home.viewmodel.SecretState
|
||||||
import co.electriccoin.zcash.ui.screen.home.viewmodel.WalletViewModel
|
import co.electriccoin.zcash.ui.screen.home.viewmodel.WalletViewModel
|
||||||
import co.electriccoin.zcash.ui.screen.onboarding.WrapOnboarding
|
import co.electriccoin.zcash.ui.screen.onboarding.WrapOnboarding
|
||||||
import co.electriccoin.zcash.ui.screen.profile.WrapProfile
|
|
||||||
import co.electriccoin.zcash.ui.screen.request.view.Request
|
|
||||||
import co.electriccoin.zcash.ui.screen.scan.WrapScan
|
|
||||||
import co.electriccoin.zcash.ui.screen.seed.view.Seed
|
|
||||||
import co.electriccoin.zcash.ui.screen.send.view.Send
|
|
||||||
import co.electriccoin.zcash.ui.screen.settings.WrapSettings
|
|
||||||
import co.electriccoin.zcash.ui.screen.support.WrapSupport
|
|
||||||
import co.electriccoin.zcash.ui.screen.update.AppUpdateCheckerImp
|
|
||||||
import co.electriccoin.zcash.ui.screen.update.WrapUpdate
|
|
||||||
import co.electriccoin.zcash.ui.screen.update.model.UpdateState
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
@Suppress("TooManyFunctions")
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||||
val walletViewModel by viewModels<WalletViewModel>()
|
val walletViewModel by viewModels<WalletViewModel>()
|
||||||
|
|
||||||
// TODO [#382]: https://github.com/zcash/secant-android-wallet/issues/382
|
|
||||||
// TODO [#403]: https://github.com/zcash/secant-android-wallet/issues/403
|
|
||||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
|
||||||
val checkUpdateViewModel by viewModels<CheckUpdateViewModel> {
|
|
||||||
CheckUpdateViewModel.CheckUpdateViewModelFactory(
|
|
||||||
application,
|
|
||||||
AppUpdateCheckerImp.new()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
|
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
|
||||||
lateinit var navControllerForTesting: NavHostController
|
lateinit var navControllerForTesting: NavHostController
|
||||||
|
|
||||||
|
@ -128,11 +89,15 @@ class MainActivity : ComponentActivity() {
|
||||||
SecretState.None -> {
|
SecretState.None -> {
|
||||||
WrapOnboarding()
|
WrapOnboarding()
|
||||||
}
|
}
|
||||||
is SecretState.NeedsBackup -> WrapBackup(
|
is SecretState.NeedsBackup -> {
|
||||||
|
WrapBackup(
|
||||||
secretState.persistableWallet,
|
secretState.persistableWallet,
|
||||||
onBackupComplete = { walletViewModel.persistBackupComplete() }
|
onBackupComplete = { walletViewModel.persistBackupComplete() }
|
||||||
)
|
)
|
||||||
is SecretState.Ready -> Navigation()
|
}
|
||||||
|
is SecretState.Ready -> {
|
||||||
|
Navigation()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -148,282 +113,8 @@ class MainActivity : ComponentActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("LongMethod")
|
|
||||||
@Composable
|
|
||||||
@SuppressWarnings("LongMethod")
|
|
||||||
private fun Navigation() {
|
|
||||||
val navController = rememberNavController().also {
|
|
||||||
// This suppress is necessary, as this is how we set up the nav controller for tests.
|
|
||||||
@SuppressLint("RestrictedApi")
|
|
||||||
navControllerForTesting = it
|
|
||||||
}
|
|
||||||
|
|
||||||
NavHost(navController = navController, startDestination = NAV_HOME) {
|
|
||||||
composable(NAV_HOME) {
|
|
||||||
WrapHome(
|
|
||||||
goScan = { navController.navigateJustOnce(NAV_SCAN) },
|
|
||||||
goProfile = { navController.navigateJustOnce(NAV_PROFILE) },
|
|
||||||
goSend = { navController.navigateJustOnce(NAV_SEND) },
|
|
||||||
goRequest = { navController.navigateJustOnce(NAV_REQUEST) }
|
|
||||||
)
|
|
||||||
|
|
||||||
WrapCheckForUpdate()
|
|
||||||
}
|
|
||||||
composable(NAV_PROFILE) {
|
|
||||||
WrapProfile(
|
|
||||||
onBack = { navController.popBackStackJustOnce(NAV_PROFILE) },
|
|
||||||
onAddressDetails = { navController.navigateJustOnce(NAV_WALLET_ADDRESS_DETAILS) },
|
|
||||||
onAddressBook = { },
|
|
||||||
onSettings = { navController.navigateJustOnce(NAV_SETTINGS) },
|
|
||||||
onCoinholderVote = { },
|
|
||||||
onSupport = { navController.navigateJustOnce(NAV_SUPPORT) },
|
|
||||||
onAbout = { navController.navigateJustOnce(NAV_ABOUT) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
composable(NAV_WALLET_ADDRESS_DETAILS) {
|
|
||||||
WrapWalletAddresses(
|
|
||||||
goBack = {
|
|
||||||
navController.popBackStackJustOnce(NAV_WALLET_ADDRESS_DETAILS)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
composable(NAV_SETTINGS) {
|
|
||||||
WrapSettings(
|
|
||||||
goBack = {
|
|
||||||
navController.popBackStackJustOnce(NAV_SETTINGS)
|
|
||||||
},
|
|
||||||
goWalletBackup = {
|
|
||||||
navController.navigateJustOnce(NAV_SEED)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
composable(NAV_SEED) {
|
|
||||||
WrapSeed(
|
|
||||||
goBack = {
|
|
||||||
navController.popBackStackJustOnce(NAV_SEED)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
composable(NAV_REQUEST) {
|
|
||||||
WrapRequest(goBack = { navController.popBackStackJustOnce(NAV_REQUEST) })
|
|
||||||
}
|
|
||||||
composable(NAV_SEND) {
|
|
||||||
WrapSend(goBack = { navController.popBackStackJustOnce(NAV_SEND) })
|
|
||||||
}
|
|
||||||
composable(NAV_SUPPORT) {
|
|
||||||
// Pop back stack won't be right if we deep link into support
|
|
||||||
WrapSupport(goBack = { navController.popBackStackJustOnce(NAV_SUPPORT) })
|
|
||||||
}
|
|
||||||
composable(NAV_ABOUT) {
|
|
||||||
WrapAbout(goBack = { navController.popBackStackJustOnce(NAV_ABOUT) })
|
|
||||||
}
|
|
||||||
composable(NAV_SCAN) {
|
|
||||||
WrapScanValidator(
|
|
||||||
onScanValid = {
|
|
||||||
// TODO [#449] https://github.com/zcash/secant-android-wallet/issues/449
|
|
||||||
navController.navigateJustOnce(NAV_SEND) {
|
|
||||||
popUpTo(NAV_HOME) { inclusive = false }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
goBack = { navController.popBackStackJustOnce(NAV_SCAN) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun WrapScanValidator(
|
|
||||||
onScanValid: (address: String) -> Unit,
|
|
||||||
goBack: () -> Unit
|
|
||||||
) {
|
|
||||||
val synchronizer = walletViewModel.synchronizer.collectAsState().value
|
|
||||||
if (synchronizer == null) {
|
|
||||||
// Display loading indicator
|
|
||||||
} else {
|
|
||||||
WrapScan(
|
|
||||||
onScanDone = { result ->
|
|
||||||
lifecycleScope.launch {
|
|
||||||
val isAddressValid = !synchronizer.validateAddress(result).isNotValid
|
|
||||||
if (isAddressValid) {
|
|
||||||
onScanValid(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
goBack = goBack
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun WrapCheckForUpdate() {
|
|
||||||
val updateInfo = checkUpdateViewModel.updateInfo.collectAsState().value
|
|
||||||
|
|
||||||
updateInfo?.let {
|
|
||||||
if (it.appUpdateInfo != null && it.state == UpdateState.Prepared) {
|
|
||||||
WrapUpdate(updateInfo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for an app update asynchronously. We create an effect that matches the activity
|
|
||||||
// lifecycle. If the wrapping compose recomposes, the check shouldn't run again.
|
|
||||||
LaunchedEffect(true) {
|
|
||||||
checkUpdateViewModel.checkForAppUpdate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun WrapWalletAddresses(
|
|
||||||
goBack: () -> Unit
|
|
||||||
) {
|
|
||||||
val walletAddresses = walletViewModel.addresses.collectAsState().value
|
|
||||||
if (null == walletAddresses) {
|
|
||||||
// Display loading indicator
|
|
||||||
} else {
|
|
||||||
WalletAddresses(
|
|
||||||
walletAddresses,
|
|
||||||
goBack
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun WrapSeed(
|
|
||||||
goBack: () -> Unit
|
|
||||||
) {
|
|
||||||
val persistableWallet = run {
|
|
||||||
val secretState = walletViewModel.secretState.collectAsState().value
|
|
||||||
if (secretState is SecretState.Ready) {
|
|
||||||
secretState.persistableWallet
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val synchronizer = walletViewModel.synchronizer.collectAsState().value
|
|
||||||
if (null == synchronizer || null == persistableWallet) {
|
|
||||||
// Display loading indicator
|
|
||||||
} else {
|
|
||||||
Seed(
|
|
||||||
persistableWallet = persistableWallet,
|
|
||||||
onBack = goBack,
|
|
||||||
onCopyToClipboard = {
|
|
||||||
copyToClipboard(applicationContext, persistableWallet)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun WrapRequest(
|
|
||||||
goBack: () -> Unit
|
|
||||||
) {
|
|
||||||
val walletAddresses = walletViewModel.addresses.collectAsState().value
|
|
||||||
if (null == walletAddresses) {
|
|
||||||
// Display loading indicator
|
|
||||||
} else {
|
|
||||||
Request(
|
|
||||||
walletAddresses.unified,
|
|
||||||
goBack = goBack,
|
|
||||||
onCreateAndSend = {
|
|
||||||
val chooserIntent = Intent.createChooser(it.newShareIntent(applicationContext), null)
|
|
||||||
|
|
||||||
startActivity(chooserIntent)
|
|
||||||
|
|
||||||
goBack()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun WrapSend(
|
|
||||||
goBack: () -> Unit
|
|
||||||
) {
|
|
||||||
val synchronizer = walletViewModel.synchronizer.collectAsState().value
|
|
||||||
val spendableBalance = walletViewModel.walletSnapshot.collectAsState().value?.spendableBalance()
|
|
||||||
val spendingKey = walletViewModel.spendingKey.collectAsState().value
|
|
||||||
if (null == synchronizer || null == spendableBalance || null == spendingKey) {
|
|
||||||
// Display loading indicator
|
|
||||||
} else {
|
|
||||||
Send(
|
|
||||||
mySpendableBalance = spendableBalance,
|
|
||||||
goBack = goBack,
|
|
||||||
onCreateAndSend = {
|
|
||||||
synchronizer.send(spendingKey, it)
|
|
||||||
|
|
||||||
goBack()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
internal val SPLASH_SCREEN_DELAY = 0.seconds
|
internal val SPLASH_SCREEN_DELAY = 0.seconds
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
const val NAV_HOME = "home"
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
const val NAV_PROFILE = "profile"
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
const val NAV_WALLET_ADDRESS_DETAILS = "wallet_address_details"
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
const val NAV_SETTINGS = "settings"
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
const val NAV_SEED = "seed"
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
const val NAV_REQUEST = "request"
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
const val NAV_SEND = "send"
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
const val NAV_SUPPORT = "support"
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
const val NAV_ABOUT = "about"
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
const val NAV_SCAN = "scan"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ZecRequest.newShareIntent(context: Context) = runBlocking {
|
|
||||||
Intent().apply {
|
|
||||||
action = Intent.ACTION_SEND
|
|
||||||
putExtra(Intent.EXTRA_TEXT, context.getString(R.string.request_template_format, toUri()))
|
|
||||||
type = "text/plain"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun NavHostController.navigateJustOnce(
|
|
||||||
route: String,
|
|
||||||
navOptionsBuilder: (NavOptionsBuilder.() -> Unit)? = null
|
|
||||||
) {
|
|
||||||
if (currentDestination?.route == route) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (navOptionsBuilder != null) {
|
|
||||||
navigate(route, navOptionsBuilder)
|
|
||||||
} else {
|
|
||||||
navigate(route)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pops up the current screen from the back stack. Parameter currentRouteToBePopped is meant to be
|
|
||||||
* set only to the current screen so we can easily debounce multiple screen popping from the back stack.
|
|
||||||
*
|
|
||||||
* @param currentRouteToBePopped current screen which should be popped up.
|
|
||||||
*/
|
|
||||||
private fun NavHostController.popBackStackJustOnce(currentRouteToBePopped: String) {
|
|
||||||
if (currentDestination?.route != currentRouteToBePopped) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
popBackStack()
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,173 @@
|
||||||
|
package co.electriccoin.zcash.ui
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
|
import androidx.navigation.NavOptionsBuilder
|
||||||
|
import androidx.navigation.compose.NavHost
|
||||||
|
import androidx.navigation.compose.composable
|
||||||
|
import androidx.navigation.compose.rememberNavController
|
||||||
|
import co.electriccoin.zcash.ui.NavigationTargets.ABOUT
|
||||||
|
import co.electriccoin.zcash.ui.NavigationTargets.HOME
|
||||||
|
import co.electriccoin.zcash.ui.NavigationTargets.PROFILE
|
||||||
|
import co.electriccoin.zcash.ui.NavigationTargets.REQUEST
|
||||||
|
import co.electriccoin.zcash.ui.NavigationTargets.SCAN
|
||||||
|
import co.electriccoin.zcash.ui.NavigationTargets.SEED
|
||||||
|
import co.electriccoin.zcash.ui.NavigationTargets.SEND
|
||||||
|
import co.electriccoin.zcash.ui.NavigationTargets.SETTINGS
|
||||||
|
import co.electriccoin.zcash.ui.NavigationTargets.SUPPORT
|
||||||
|
import co.electriccoin.zcash.ui.NavigationTargets.WALLET_ADDRESS_DETAILS
|
||||||
|
import co.electriccoin.zcash.ui.screen.about.WrapAbout
|
||||||
|
import co.electriccoin.zcash.ui.screen.address.WrapWalletAddresses
|
||||||
|
import co.electriccoin.zcash.ui.screen.home.WrapHome
|
||||||
|
import co.electriccoin.zcash.ui.screen.profile.WrapProfile
|
||||||
|
import co.electriccoin.zcash.ui.screen.request.WrapRequest
|
||||||
|
import co.electriccoin.zcash.ui.screen.scan.WrapScanValidator
|
||||||
|
import co.electriccoin.zcash.ui.screen.seed.WrapSeed
|
||||||
|
import co.electriccoin.zcash.ui.screen.send.WrapSend
|
||||||
|
import co.electriccoin.zcash.ui.screen.settings.WrapSettings
|
||||||
|
import co.electriccoin.zcash.ui.screen.support.WrapSupport
|
||||||
|
import co.electriccoin.zcash.ui.screen.update.WrapCheckForUpdate
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@Suppress("LongMethod")
|
||||||
|
internal fun MainActivity.Navigation() {
|
||||||
|
val navController = rememberNavController().also {
|
||||||
|
// This suppress is necessary, as this is how we set up the nav controller for tests.
|
||||||
|
@SuppressLint("RestrictedApi")
|
||||||
|
navControllerForTesting = it
|
||||||
|
}
|
||||||
|
|
||||||
|
NavHost(navController = navController, startDestination = HOME) {
|
||||||
|
composable(HOME) {
|
||||||
|
WrapHome(
|
||||||
|
goScan = { navController.navigateJustOnce(SCAN) },
|
||||||
|
goProfile = { navController.navigateJustOnce(PROFILE) },
|
||||||
|
goSend = { navController.navigateJustOnce(SEND) },
|
||||||
|
goRequest = { navController.navigateJustOnce(REQUEST) }
|
||||||
|
)
|
||||||
|
|
||||||
|
WrapCheckForUpdate()
|
||||||
|
}
|
||||||
|
composable(PROFILE) {
|
||||||
|
WrapProfile(
|
||||||
|
onBack = { navController.popBackStackJustOnce(PROFILE) },
|
||||||
|
onAddressDetails = { navController.navigateJustOnce(WALLET_ADDRESS_DETAILS) },
|
||||||
|
onAddressBook = { },
|
||||||
|
onSettings = { navController.navigateJustOnce(SETTINGS) },
|
||||||
|
onCoinholderVote = { },
|
||||||
|
onSupport = { navController.navigateJustOnce(SUPPORT) },
|
||||||
|
onAbout = { navController.navigateJustOnce(ABOUT) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composable(WALLET_ADDRESS_DETAILS) {
|
||||||
|
WrapWalletAddresses(
|
||||||
|
goBack = {
|
||||||
|
navController.popBackStackJustOnce(WALLET_ADDRESS_DETAILS)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composable(SETTINGS) {
|
||||||
|
WrapSettings(
|
||||||
|
goBack = {
|
||||||
|
navController.popBackStackJustOnce(SETTINGS)
|
||||||
|
},
|
||||||
|
goWalletBackup = {
|
||||||
|
navController.navigateJustOnce(SEED)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composable(SEED) {
|
||||||
|
WrapSeed(
|
||||||
|
goBack = {
|
||||||
|
navController.popBackStackJustOnce(SEED)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composable(REQUEST) {
|
||||||
|
WrapRequest(goBack = { navController.popBackStackJustOnce(REQUEST) })
|
||||||
|
}
|
||||||
|
composable(SEND) {
|
||||||
|
WrapSend(goBack = { navController.popBackStackJustOnce(SEND) })
|
||||||
|
}
|
||||||
|
composable(SUPPORT) {
|
||||||
|
// Pop back stack won't be right if we deep link into support
|
||||||
|
WrapSupport(goBack = { navController.popBackStackJustOnce(SUPPORT) })
|
||||||
|
}
|
||||||
|
composable(ABOUT) {
|
||||||
|
WrapAbout(goBack = { navController.popBackStackJustOnce(ABOUT) })
|
||||||
|
}
|
||||||
|
composable(SCAN) {
|
||||||
|
WrapScanValidator(
|
||||||
|
onScanValid = {
|
||||||
|
// TODO [#449] https://github.com/zcash/secant-android-wallet/issues/449
|
||||||
|
navController.navigateJustOnce(SEND) {
|
||||||
|
popUpTo(HOME) { inclusive = false }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
goBack = { navController.popBackStackJustOnce(SCAN) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun NavHostController.navigateJustOnce(
|
||||||
|
route: String,
|
||||||
|
navOptionsBuilder: (NavOptionsBuilder.() -> Unit)? = null
|
||||||
|
) {
|
||||||
|
if (currentDestination?.route == route) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (navOptionsBuilder != null) {
|
||||||
|
navigate(route, navOptionsBuilder)
|
||||||
|
} else {
|
||||||
|
navigate(route)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pops up the current screen from the back stack. Parameter currentRouteToBePopped is meant to be
|
||||||
|
* set only to the current screen so we can easily debounce multiple screen popping from the back stack.
|
||||||
|
*
|
||||||
|
* @param currentRouteToBePopped current screen which should be popped up.
|
||||||
|
*/
|
||||||
|
private fun NavHostController.popBackStackJustOnce(currentRouteToBePopped: String) {
|
||||||
|
if (currentDestination?.route != currentRouteToBePopped) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
popBackStack()
|
||||||
|
}
|
||||||
|
|
||||||
|
object NavigationTargets {
|
||||||
|
@VisibleForTesting
|
||||||
|
const val HOME = "home"
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
const val PROFILE = "profile"
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
const val WALLET_ADDRESS_DETAILS = "wallet_address_details"
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
const val SETTINGS = "settings"
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
const val SEED = "seed"
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
const val REQUEST = "request"
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
const val SEND = "send"
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
const val SUPPORT = "support"
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
const val ABOUT = "about"
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
const val SCAN = "scan"
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
@file:Suppress("ktlint:filename")
|
||||||
|
|
||||||
|
package co.electriccoin.zcash.ui.screen.address
|
||||||
|
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.viewModels
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import co.electriccoin.zcash.ui.MainActivity
|
||||||
|
import co.electriccoin.zcash.ui.screen.address.view.WalletAddresses
|
||||||
|
import co.electriccoin.zcash.ui.screen.home.viewmodel.WalletViewModel
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun MainActivity.WrapWalletAddresses(
|
||||||
|
goBack: () -> Unit
|
||||||
|
) {
|
||||||
|
WrapWalletAddresses(this, goBack)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun WrapWalletAddresses(
|
||||||
|
activity: ComponentActivity,
|
||||||
|
goBack: () -> Unit
|
||||||
|
) {
|
||||||
|
val walletViewModel by activity.viewModels<WalletViewModel>()
|
||||||
|
|
||||||
|
val walletAddresses = walletViewModel.addresses.collectAsState().value
|
||||||
|
if (null == walletAddresses) {
|
||||||
|
// Display loading indicator
|
||||||
|
} else {
|
||||||
|
WalletAddresses(
|
||||||
|
walletAddresses,
|
||||||
|
goBack
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
@file:Suppress("ktlint:filename")
|
||||||
|
|
||||||
|
package co.electriccoin.zcash.ui.screen.request
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.viewModels
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import cash.z.ecc.sdk.model.ZecRequest
|
||||||
|
import co.electriccoin.zcash.ui.MainActivity
|
||||||
|
import co.electriccoin.zcash.ui.R
|
||||||
|
import co.electriccoin.zcash.ui.screen.home.viewmodel.WalletViewModel
|
||||||
|
import co.electriccoin.zcash.ui.screen.request.view.Request
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun MainActivity.WrapRequest(
|
||||||
|
goBack: () -> Unit
|
||||||
|
) {
|
||||||
|
WrapRequest(this, goBack)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun WrapRequest(
|
||||||
|
activity: ComponentActivity,
|
||||||
|
goBack: () -> Unit
|
||||||
|
) {
|
||||||
|
val walletViewModel by activity.viewModels<WalletViewModel>()
|
||||||
|
val walletAddresses = walletViewModel.addresses.collectAsState().value
|
||||||
|
|
||||||
|
if (null == walletAddresses) {
|
||||||
|
// Display loading indicator
|
||||||
|
} else {
|
||||||
|
Request(
|
||||||
|
walletAddresses.unified,
|
||||||
|
goBack = goBack,
|
||||||
|
onCreateAndSend = {
|
||||||
|
val chooserIntent = Intent.createChooser(
|
||||||
|
it.newShareIntent(activity.applicationContext),
|
||||||
|
null
|
||||||
|
)
|
||||||
|
|
||||||
|
activity.startActivity(chooserIntent)
|
||||||
|
|
||||||
|
goBack()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ZecRequest.newShareIntent(context: Context) = runBlocking {
|
||||||
|
Intent().apply {
|
||||||
|
action = Intent.ACTION_SEND
|
||||||
|
putExtra(Intent.EXTRA_TEXT, context.getString(R.string.request_template_format, toUri()))
|
||||||
|
type = "text/plain"
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,22 +3,57 @@
|
||||||
package co.electriccoin.zcash.ui.screen.scan
|
package co.electriccoin.zcash.ui.screen.scan
|
||||||
|
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
|
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.collectAsState
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
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.screen.home.viewmodel.WalletViewModel
|
||||||
import co.electriccoin.zcash.ui.screen.scan.util.SettingsUtil
|
import co.electriccoin.zcash.ui.screen.scan.util.SettingsUtil
|
||||||
import co.electriccoin.zcash.ui.screen.scan.view.Scan
|
import co.electriccoin.zcash.ui.screen.scan.view.Scan
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun MainActivity.WrapScan(
|
internal fun MainActivity.WrapScanValidator(
|
||||||
goBack: () -> Unit,
|
onScanValid: (address: String) -> Unit,
|
||||||
onScanDone: (result: String) -> Unit
|
goBack: () -> Unit
|
||||||
) {
|
) {
|
||||||
WrapScan(this, onScanDone, goBack)
|
WrapScanValidator(
|
||||||
|
this,
|
||||||
|
onScanValid = onScanValid,
|
||||||
|
goBack = goBack
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun WrapScanValidator(
|
||||||
|
activity: ComponentActivity,
|
||||||
|
onScanValid: (address: String) -> Unit,
|
||||||
|
goBack: () -> Unit
|
||||||
|
) {
|
||||||
|
val walletViewModel by activity.viewModels<WalletViewModel>()
|
||||||
|
|
||||||
|
val synchronizer = walletViewModel.synchronizer.collectAsState().value
|
||||||
|
if (synchronizer == null) {
|
||||||
|
// Display loading indicator
|
||||||
|
} else {
|
||||||
|
WrapScan(
|
||||||
|
activity,
|
||||||
|
onScanned = { result ->
|
||||||
|
activity.lifecycleScope.launch {
|
||||||
|
val isAddressValid = !synchronizer.validateAddress(result).isNotValid
|
||||||
|
if (isAddressValid) {
|
||||||
|
onScanValid(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
goBack = goBack
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
@file:Suppress("ktlint:filename")
|
||||||
|
|
||||||
|
package co.electriccoin.zcash.ui.screen.seed
|
||||||
|
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.viewModels
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import co.electriccoin.zcash.ui.MainActivity
|
||||||
|
import co.electriccoin.zcash.ui.screen.backup.copyToClipboard
|
||||||
|
import co.electriccoin.zcash.ui.screen.home.viewmodel.SecretState
|
||||||
|
import co.electriccoin.zcash.ui.screen.home.viewmodel.WalletViewModel
|
||||||
|
import co.electriccoin.zcash.ui.screen.seed.view.Seed
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun MainActivity.WrapSeed(
|
||||||
|
goBack: () -> Unit
|
||||||
|
) {
|
||||||
|
WrapSeed(this, goBack)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun WrapSeed(
|
||||||
|
activity: ComponentActivity,
|
||||||
|
goBack: () -> Unit
|
||||||
|
) {
|
||||||
|
val walletViewModel by activity.viewModels<WalletViewModel>()
|
||||||
|
|
||||||
|
val persistableWallet = run {
|
||||||
|
val secretState = walletViewModel.secretState.collectAsState().value
|
||||||
|
if (secretState is SecretState.Ready) {
|
||||||
|
secretState.persistableWallet
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val synchronizer = walletViewModel.synchronizer.collectAsState().value
|
||||||
|
if (null == synchronizer || null == persistableWallet) {
|
||||||
|
// Display loading indicator
|
||||||
|
} else {
|
||||||
|
Seed(
|
||||||
|
persistableWallet = persistableWallet,
|
||||||
|
onBack = goBack,
|
||||||
|
onCopyToClipboard = {
|
||||||
|
copyToClipboard(activity.applicationContext, persistableWallet)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
@file:Suppress("ktlint:filename")
|
||||||
|
|
||||||
|
package co.electriccoin.zcash.ui.screen.send
|
||||||
|
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.viewModels
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import cash.z.ecc.sdk.send
|
||||||
|
import co.electriccoin.zcash.ui.MainActivity
|
||||||
|
import co.electriccoin.zcash.ui.screen.home.model.spendableBalance
|
||||||
|
import co.electriccoin.zcash.ui.screen.home.viewmodel.WalletViewModel
|
||||||
|
import co.electriccoin.zcash.ui.screen.send.view.Send
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun MainActivity.WrapSend(
|
||||||
|
goBack: () -> Unit
|
||||||
|
) {
|
||||||
|
WrapSend(this, goBack)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun WrapSend(
|
||||||
|
activity: ComponentActivity,
|
||||||
|
goBack: () -> Unit
|
||||||
|
) {
|
||||||
|
val walletViewModel by activity.viewModels<WalletViewModel>()
|
||||||
|
|
||||||
|
val synchronizer = walletViewModel.synchronizer.collectAsState().value
|
||||||
|
val spendableBalance = walletViewModel.walletSnapshot.collectAsState().value?.spendableBalance()
|
||||||
|
val spendingKey = walletViewModel.spendingKey.collectAsState().value
|
||||||
|
if (null == synchronizer || null == spendableBalance || null == spendingKey) {
|
||||||
|
// Display loading indicator
|
||||||
|
} else {
|
||||||
|
Send(
|
||||||
|
mySpendableBalance = spendableBalance,
|
||||||
|
goBack = goBack,
|
||||||
|
onCreateAndSend = {
|
||||||
|
synchronizer.send(spendingKey, it)
|
||||||
|
|
||||||
|
goBack()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,13 +3,16 @@ package co.electriccoin.zcash.ui.screen.update
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
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.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
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.screen.home.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
|
||||||
import co.electriccoin.zcash.ui.screen.update.util.PlayStoreUtil
|
import co.electriccoin.zcash.ui.screen.update.util.PlayStoreUtil
|
||||||
|
@ -19,17 +22,39 @@ import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun MainActivity.WrapUpdate(
|
internal fun MainActivity.WrapCheckForUpdate() {
|
||||||
updateInfo: UpdateInfo
|
WrapCheckForUpdate(this)
|
||||||
) {
|
|
||||||
WrapUpdate(
|
|
||||||
activity = this,
|
|
||||||
inputUpdateInfo = updateInfo
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun WrapUpdate(
|
private fun WrapCheckForUpdate(activity: ComponentActivity) {
|
||||||
|
// TODO [#382]: https://github.com/zcash/secant-android-wallet/issues/382
|
||||||
|
// TODO [#403]: https://github.com/zcash/secant-android-wallet/issues/403
|
||||||
|
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||||
|
val checkUpdateViewModel by activity.viewModels<CheckUpdateViewModel> {
|
||||||
|
CheckUpdateViewModel.CheckUpdateViewModelFactory(
|
||||||
|
activity.application,
|
||||||
|
AppUpdateCheckerImp.new()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val updateInfo = checkUpdateViewModel.updateInfo.collectAsState().value
|
||||||
|
|
||||||
|
updateInfo?.let {
|
||||||
|
if (it.appUpdateInfo != null && it.state == UpdateState.Prepared) {
|
||||||
|
WrapUpdate(activity, updateInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for an app update asynchronously. We create an effect that matches the activity
|
||||||
|
// lifecycle. If the wrapping compose recomposes, the check shouldn't run again.
|
||||||
|
LaunchedEffect(true) {
|
||||||
|
checkUpdateViewModel.checkForAppUpdate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun WrapUpdate(
|
||||||
activity: ComponentActivity,
|
activity: ComponentActivity,
|
||||||
inputUpdateInfo: UpdateInfo
|
inputUpdateInfo: UpdateInfo
|
||||||
) {
|
) {
|
||||||
|
|
Loading…
Reference in New Issue