2022-03-08 11:05:03 -08:00
|
|
|
package co.electriccoin.zcash.ui
|
2021-09-14 14:02:06 -07:00
|
|
|
|
2022-05-11 13:24:52 -07:00
|
|
|
import android.annotation.SuppressLint
|
2021-12-02 12:33:55 -08:00
|
|
|
import android.content.ClipboardManager
|
2021-12-06 12:31:39 -08:00
|
|
|
import android.content.Context
|
2022-02-17 05:08:06 -08:00
|
|
|
import android.content.Intent
|
2021-09-14 14:02:06 -07:00
|
|
|
import android.os.Bundle
|
2021-12-03 05:19:15 -08:00
|
|
|
import android.os.SystemClock
|
2021-09-14 14:02:06 -07:00
|
|
|
import androidx.activity.ComponentActivity
|
|
|
|
import androidx.activity.compose.setContent
|
2021-10-09 07:37:03 -07:00
|
|
|
import androidx.activity.viewModels
|
2021-12-03 05:19:15 -08:00
|
|
|
import androidx.annotation.VisibleForTesting
|
2021-12-09 12:18:18 -08:00
|
|
|
import androidx.compose.foundation.layout.fillMaxHeight
|
|
|
|
import androidx.compose.foundation.layout.fillMaxWidth
|
2021-11-17 12:19:49 -08:00
|
|
|
import androidx.compose.runtime.Composable
|
2022-06-08 01:39:33 -07:00
|
|
|
import androidx.compose.runtime.LaunchedEffect
|
2021-11-12 04:09:30 -08:00
|
|
|
import androidx.compose.runtime.collectAsState
|
2021-12-09 12:18:18 -08:00
|
|
|
import androidx.compose.ui.Modifier
|
2022-05-31 09:38:02 -07:00
|
|
|
import androidx.compose.ui.platform.LocalContext
|
2021-12-03 05:19:15 -08:00
|
|
|
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
2021-12-06 12:31:39 -08:00
|
|
|
import androidx.lifecycle.lifecycleScope
|
2022-01-31 13:42:22 -08:00
|
|
|
import androidx.navigation.NavHostController
|
2021-12-29 11:38:14 -08:00
|
|
|
import androidx.navigation.compose.NavHost
|
|
|
|
import androidx.navigation.compose.composable
|
|
|
|
import androidx.navigation.compose.rememberNavController
|
2021-12-09 12:21:30 -08:00
|
|
|
import cash.z.ecc.android.sdk.type.ZcashNetwork
|
2022-05-08 05:47:25 -07:00
|
|
|
import cash.z.ecc.sdk.fixture.SeedPhraseFixture
|
2021-11-17 12:19:49 -08:00
|
|
|
import cash.z.ecc.sdk.model.PersistableWallet
|
2021-12-09 12:21:30 -08:00
|
|
|
import cash.z.ecc.sdk.model.SeedPhrase
|
2022-02-17 05:08:06 -08:00
|
|
|
import cash.z.ecc.sdk.model.ZecRequest
|
2022-03-01 05:11:23 -08:00
|
|
|
import cash.z.ecc.sdk.send
|
2021-12-09 12:21:30 -08:00
|
|
|
import cash.z.ecc.sdk.type.fromResources
|
2022-05-31 09:38:02 -07:00
|
|
|
import co.electriccoin.zcash.spackle.EmulatorWtfUtil
|
2022-05-08 05:47:25 -07:00
|
|
|
import co.electriccoin.zcash.spackle.FirebaseTestLabUtil
|
2022-03-08 11:05:03 -08:00
|
|
|
import co.electriccoin.zcash.ui.design.compat.FontCompat
|
|
|
|
import co.electriccoin.zcash.ui.design.component.GradientSurface
|
|
|
|
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
2022-04-25 05:35:30 -07:00
|
|
|
import co.electriccoin.zcash.ui.screen.about.WrapAbout
|
2022-04-19 05:04:56 -07:00
|
|
|
import co.electriccoin.zcash.ui.screen.backup.WrapBackup
|
|
|
|
import co.electriccoin.zcash.ui.screen.backup.copyToClipboard
|
2022-03-08 11:05:03 -08:00
|
|
|
import co.electriccoin.zcash.ui.screen.home.model.spendableBalance
|
|
|
|
import co.electriccoin.zcash.ui.screen.home.view.Home
|
2022-05-16 04:40:50 -07:00
|
|
|
import co.electriccoin.zcash.ui.screen.home.viewmodel.CheckUpdateViewModel
|
2022-03-08 11:05:03 -08:00
|
|
|
import co.electriccoin.zcash.ui.screen.home.viewmodel.SecretState
|
|
|
|
import co.electriccoin.zcash.ui.screen.home.viewmodel.WalletViewModel
|
|
|
|
import co.electriccoin.zcash.ui.screen.onboarding.view.Onboarding
|
|
|
|
import co.electriccoin.zcash.ui.screen.onboarding.viewmodel.OnboardingViewModel
|
2022-04-25 05:35:30 -07:00
|
|
|
import co.electriccoin.zcash.ui.screen.profile.WrapProfile
|
2022-03-08 11:05:03 -08:00
|
|
|
import co.electriccoin.zcash.ui.screen.request.view.Request
|
|
|
|
import co.electriccoin.zcash.ui.screen.restore.view.RestoreWallet
|
|
|
|
import co.electriccoin.zcash.ui.screen.restore.viewmodel.CompleteWordSetState
|
|
|
|
import co.electriccoin.zcash.ui.screen.restore.viewmodel.RestoreViewModel
|
2022-05-19 05:41:58 -07:00
|
|
|
import co.electriccoin.zcash.ui.screen.scan.WrapScan
|
2022-03-08 11:05:03 -08:00
|
|
|
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.view.Settings
|
2022-04-19 18:28:49 -07:00
|
|
|
import co.electriccoin.zcash.ui.screen.support.WrapSupport
|
2022-05-16 04:40:50 -07:00
|
|
|
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
|
2022-03-08 11:05:03 -08:00
|
|
|
import co.electriccoin.zcash.ui.screen.wallet_address.view.WalletAddresses
|
2021-12-06 12:31:39 -08:00
|
|
|
import kotlinx.coroutines.launch
|
2022-03-30 05:16:57 -07:00
|
|
|
import kotlinx.coroutines.runBlocking
|
2021-12-03 05:19:15 -08:00
|
|
|
import kotlin.time.Duration
|
|
|
|
import kotlin.time.Duration.Companion.milliseconds
|
|
|
|
import kotlin.time.Duration.Companion.seconds
|
2021-09-14 14:02:06 -07:00
|
|
|
|
2022-01-26 13:33:02 -08:00
|
|
|
@Suppress("TooManyFunctions")
|
2021-09-14 14:02:06 -07:00
|
|
|
class MainActivity : ComponentActivity() {
|
2021-10-09 07:37:03 -07:00
|
|
|
|
2022-01-31 13:42:22 -08:00
|
|
|
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
|
|
|
val walletViewModel by viewModels<WalletViewModel>()
|
|
|
|
|
2022-05-16 04:40:50 -07:00
|
|
|
// 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()
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-01-31 13:42:22 -08:00
|
|
|
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
|
|
|
|
lateinit var navControllerForTesting: NavHostController
|
2021-11-12 04:09:30 -08:00
|
|
|
|
2021-09-14 14:02:06 -07:00
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
|
|
super.onCreate(savedInstanceState)
|
2021-12-03 05:19:15 -08:00
|
|
|
|
|
|
|
setupSplashScreen()
|
|
|
|
|
2022-02-21 06:50:09 -08:00
|
|
|
if (FontCompat.isFontPrefetchNeeded()) {
|
2021-12-06 12:31:39 -08:00
|
|
|
lifecycleScope.launch {
|
2022-02-21 06:50:09 -08:00
|
|
|
FontCompat.prefetchFontsLegacy(applicationContext)
|
2021-12-03 05:19:15 -08:00
|
|
|
|
2021-12-06 12:31:39 -08:00
|
|
|
setupUiContent()
|
2021-09-14 14:02:06 -07:00
|
|
|
}
|
2022-02-21 06:50:09 -08:00
|
|
|
} else {
|
|
|
|
setupUiContent()
|
2021-09-14 14:02:06 -07:00
|
|
|
}
|
|
|
|
}
|
2021-11-17 12:19:49 -08:00
|
|
|
|
2021-12-03 05:19:15 -08:00
|
|
|
private fun setupSplashScreen() {
|
2021-12-09 12:18:18 -08:00
|
|
|
val splashScreen = installSplashScreen()
|
|
|
|
val start = SystemClock.elapsedRealtime().milliseconds
|
|
|
|
|
2022-04-04 05:48:47 -07:00
|
|
|
splashScreen.setKeepOnScreenCondition {
|
2021-12-09 12:18:18 -08:00
|
|
|
if (SPLASH_SCREEN_DELAY > Duration.ZERO) {
|
|
|
|
val now = SystemClock.elapsedRealtime().milliseconds
|
2021-12-03 05:19:15 -08:00
|
|
|
|
2021-12-09 12:18:18 -08:00
|
|
|
// This delay is for debug purposes only; do not enable for production usage.
|
|
|
|
if (now - start < SPLASH_SCREEN_DELAY) {
|
2022-04-04 05:48:47 -07:00
|
|
|
return@setKeepOnScreenCondition true
|
2021-12-09 12:18:18 -08:00
|
|
|
}
|
2021-12-03 05:19:15 -08:00
|
|
|
}
|
2021-12-09 12:18:18 -08:00
|
|
|
|
2021-12-29 11:38:14 -08:00
|
|
|
SecretState.Loading == walletViewModel.secretState.value
|
2021-12-03 05:19:15 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-06 12:31:39 -08:00
|
|
|
private fun setupUiContent() {
|
|
|
|
setContent {
|
|
|
|
ZcashTheme {
|
2021-12-09 12:18:18 -08:00
|
|
|
GradientSurface(
|
|
|
|
Modifier
|
|
|
|
.fillMaxWidth()
|
|
|
|
.fillMaxHeight()
|
|
|
|
) {
|
2022-01-31 13:42:22 -08:00
|
|
|
when (val secretState = walletViewModel.secretState.collectAsState().value) {
|
2021-12-29 11:38:14 -08:00
|
|
|
SecretState.Loading -> {
|
2021-12-09 12:18:18 -08:00
|
|
|
// For now, keep displaying splash screen using condition above.
|
|
|
|
// In the future, we might consider displaying something different here.
|
|
|
|
}
|
2021-12-29 11:38:14 -08:00
|
|
|
SecretState.None -> {
|
2021-12-09 12:18:18 -08:00
|
|
|
WrapOnboarding()
|
|
|
|
}
|
2022-04-19 05:04:56 -07:00
|
|
|
is SecretState.NeedsBackup -> WrapBackup(
|
|
|
|
secretState.persistableWallet,
|
|
|
|
onBackupComplete = { walletViewModel.persistBackupComplete() }
|
|
|
|
)
|
2021-12-29 11:38:14 -08:00
|
|
|
is SecretState.Ready -> Navigation()
|
2021-12-06 12:31:39 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-01-31 13:42:22 -08:00
|
|
|
|
|
|
|
// Force collection to improve performance; sync can start happening while
|
|
|
|
// the user is going through the backup flow. Don't use eager collection in the view model,
|
|
|
|
// so that the collection is still tied to UI lifecycle.
|
|
|
|
lifecycleScope.launch {
|
2022-02-21 06:50:09 -08:00
|
|
|
walletViewModel.synchronizer.collect {
|
|
|
|
}
|
2022-01-31 13:42:22 -08:00
|
|
|
}
|
2021-12-06 12:31:39 -08:00
|
|
|
}
|
|
|
|
|
2021-12-09 12:21:30 -08:00
|
|
|
@Composable
|
|
|
|
private fun WrapOnboarding() {
|
|
|
|
val onboardingViewModel by viewModels<OnboardingViewModel>()
|
|
|
|
|
2022-04-14 09:34:12 -07:00
|
|
|
// TODO [#383]: https://github.com/zcash/secant-android-wallet/issues/383
|
2021-12-09 12:21:30 -08:00
|
|
|
if (!onboardingViewModel.isImporting.collectAsState().value) {
|
|
|
|
Onboarding(
|
|
|
|
onboardingState = onboardingViewModel.onboardingState,
|
2022-05-08 05:47:25 -07:00
|
|
|
onImportWallet = {
|
|
|
|
// In the case of the app currently being messed with by the robo test runner on
|
|
|
|
// Firebase Test Lab or Google Play pre-launch report, we want to skip creating
|
|
|
|
// a new or restoring an existing wallet screens by persisting an existing wallet
|
|
|
|
// with a mock seed.
|
|
|
|
if (FirebaseTestLabUtil.isFirebaseTestLab(applicationContext)) {
|
|
|
|
persistExistingWalletWithSeedPhrase(SeedPhraseFixture.new())
|
|
|
|
return@Onboarding
|
|
|
|
}
|
|
|
|
|
|
|
|
onboardingViewModel.isImporting.value = true
|
|
|
|
},
|
2021-12-09 12:21:30 -08:00
|
|
|
onCreateWallet = {
|
2022-05-08 05:47:25 -07:00
|
|
|
if (FirebaseTestLabUtil.isFirebaseTestLab(applicationContext)) {
|
|
|
|
persistExistingWalletWithSeedPhrase(SeedPhraseFixture.new())
|
|
|
|
return@Onboarding
|
|
|
|
}
|
|
|
|
|
2021-12-09 12:21:30 -08:00
|
|
|
walletViewModel.persistNewWallet()
|
|
|
|
}
|
|
|
|
)
|
2022-01-31 13:42:22 -08:00
|
|
|
|
|
|
|
reportFullyDrawn()
|
2021-12-09 12:21:30 -08:00
|
|
|
} else {
|
|
|
|
WrapRestore()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-17 12:19:49 -08:00
|
|
|
@Composable
|
2021-12-09 12:21:30 -08:00
|
|
|
private fun WrapRestore() {
|
|
|
|
val onboardingViewModel by viewModels<OnboardingViewModel>()
|
|
|
|
val restoreViewModel by viewModels<RestoreViewModel>()
|
|
|
|
|
|
|
|
when (val completeWordList = restoreViewModel.completeWordList.collectAsState().value) {
|
|
|
|
CompleteWordSetState.Loading -> {
|
|
|
|
// Although it might perform IO, it should be relatively fast.
|
|
|
|
// Consider whether to display indeterminate progress here.
|
|
|
|
// Another option would be to go straight to the restore screen with autocomplete
|
|
|
|
// disabled for a few milliseconds. Users would probably never notice due to the
|
|
|
|
// time it takes to re-orient on the new screen, unless users were doing this
|
|
|
|
// on a daily basis and become very proficient at our UI. The Therac-25 has
|
|
|
|
// historical precedent on how that could cause problems.
|
2021-11-17 12:19:49 -08:00
|
|
|
}
|
2021-12-09 12:21:30 -08:00
|
|
|
is CompleteWordSetState.Loaded -> {
|
|
|
|
RestoreWallet(
|
|
|
|
completeWordList.list,
|
|
|
|
restoreViewModel.userWordList,
|
|
|
|
onBack = { onboardingViewModel.isImporting.value = false },
|
|
|
|
paste = {
|
|
|
|
val clipboardManager = getSystemService(ClipboardManager::class.java)
|
|
|
|
return@RestoreWallet clipboardManager?.primaryClip?.toString()
|
|
|
|
},
|
|
|
|
onFinished = {
|
2022-05-08 05:47:25 -07:00
|
|
|
persistExistingWalletWithSeedPhrase(
|
2021-12-09 12:21:30 -08:00
|
|
|
SeedPhrase(restoreViewModel.userWordList.current.value)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2021-11-17 12:19:49 -08:00
|
|
|
}
|
2021-12-03 05:19:15 -08:00
|
|
|
|
2022-05-08 05:47:25 -07:00
|
|
|
/**
|
|
|
|
* Persists existing wallet together with the backup complete flag to disk. Be aware of that, it
|
|
|
|
* triggers navigation changes, as we observe the WalletViewModel.secretState.
|
|
|
|
*
|
|
|
|
* Write the backup complete flag first, then the seed phrase. That avoids the UI flickering to
|
|
|
|
* the backup screen. Assume if a user is restoring from a backup, then the user has a valid backup.
|
|
|
|
*
|
|
|
|
* @param seedPhrase to be persisted along with the wallet object
|
|
|
|
*/
|
|
|
|
private fun persistExistingWalletWithSeedPhrase(seedPhrase: SeedPhrase) {
|
|
|
|
walletViewModel.persistBackupComplete()
|
|
|
|
|
|
|
|
val network = ZcashNetwork.fromResources(application)
|
|
|
|
val restoredWallet = PersistableWallet(
|
|
|
|
network,
|
|
|
|
null,
|
|
|
|
seedPhrase
|
|
|
|
)
|
|
|
|
walletViewModel.persistExistingWallet(restoredWallet)
|
|
|
|
}
|
|
|
|
|
2022-04-25 05:35:30 -07:00
|
|
|
@Suppress("LongMethod")
|
2021-12-03 05:19:15 -08:00
|
|
|
@Composable
|
2022-05-16 04:40:50 -07:00
|
|
|
@SuppressWarnings("LongMethod")
|
2021-12-29 11:38:14 -08:00
|
|
|
private fun Navigation() {
|
2022-01-31 13:42:22 -08:00
|
|
|
val navController = rememberNavController().also {
|
2022-05-11 13:24:52 -07:00
|
|
|
// This suppress is necessary, as this is how we set up the nav controller for tests.
|
|
|
|
@SuppressLint("RestrictedApi")
|
2022-01-31 13:42:22 -08:00
|
|
|
navControllerForTesting = it
|
|
|
|
}
|
2022-01-26 13:33:02 -08:00
|
|
|
|
2022-01-31 13:42:22 -08:00
|
|
|
NavHost(navController = navController, startDestination = NAV_HOME) {
|
|
|
|
composable(NAV_HOME) {
|
2021-12-31 05:28:16 -08:00
|
|
|
WrapHome(
|
2022-05-19 05:41:58 -07:00
|
|
|
goScan = { navController.navigate(NAV_SCAN) },
|
2022-01-31 13:42:22 -08:00
|
|
|
goProfile = { navController.navigate(NAV_PROFILE) },
|
2022-03-01 05:11:23 -08:00
|
|
|
goSend = { navController.navigate(NAV_SEND) },
|
2022-02-17 05:08:06 -08:00
|
|
|
goRequest = { navController.navigate(NAV_REQUEST) }
|
2021-12-31 05:28:16 -08:00
|
|
|
)
|
|
|
|
}
|
2022-01-31 13:42:22 -08:00
|
|
|
composable(NAV_PROFILE) {
|
2022-01-13 09:49:08 -08:00
|
|
|
WrapProfile(
|
|
|
|
onBack = { navController.popBackStack() },
|
2022-01-31 13:42:22 -08:00
|
|
|
onAddressDetails = { navController.navigate(NAV_WALLET_ADDRESS_DETAILS) },
|
2022-01-13 09:49:08 -08:00
|
|
|
onAddressBook = { },
|
2022-01-31 13:42:22 -08:00
|
|
|
onSettings = { navController.navigate(NAV_SETTINGS) },
|
2022-01-26 13:33:02 -08:00
|
|
|
onCoinholderVote = { },
|
2022-04-25 05:35:30 -07:00
|
|
|
onSupport = { navController.navigate(NAV_SUPPORT) },
|
|
|
|
onAbout = { navController.navigate(NAV_ABOUT) }
|
2022-01-26 13:33:02 -08:00
|
|
|
)
|
2022-01-13 09:49:08 -08:00
|
|
|
}
|
2022-01-31 13:42:22 -08:00
|
|
|
composable(NAV_WALLET_ADDRESS_DETAILS) {
|
2021-12-31 05:28:16 -08:00
|
|
|
WrapWalletAddresses(
|
|
|
|
goBack = {
|
|
|
|
navController.popBackStack()
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
2022-01-31 13:42:22 -08:00
|
|
|
composable(NAV_SETTINGS) {
|
2022-01-26 13:33:02 -08:00
|
|
|
WrapSettings(
|
|
|
|
goBack = {
|
|
|
|
navController.popBackStack()
|
|
|
|
},
|
|
|
|
goWalletBackup = {
|
2022-01-31 13:42:22 -08:00
|
|
|
navController.navigate(NAV_SEED)
|
2022-01-26 13:33:02 -08:00
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
2022-01-31 13:42:22 -08:00
|
|
|
composable(NAV_SEED) {
|
2022-01-26 13:33:02 -08:00
|
|
|
WrapSeed(
|
|
|
|
goBack = {
|
|
|
|
navController.popBackStack()
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
2022-02-17 05:08:06 -08:00
|
|
|
composable(NAV_REQUEST) {
|
|
|
|
WrapRequest(goBack = { navController.popBackStack() })
|
|
|
|
}
|
2022-03-01 05:11:23 -08:00
|
|
|
composable(NAV_SEND) {
|
|
|
|
WrapSend(goBack = { navController.popBackStack() })
|
|
|
|
}
|
2022-04-19 18:28:49 -07:00
|
|
|
composable(NAV_SUPPORT) {
|
|
|
|
// Pop back stack won't be right if we deep link into support
|
|
|
|
WrapSupport(goBack = { navController.popBackStack() })
|
|
|
|
}
|
2022-04-25 05:35:30 -07:00
|
|
|
composable(NAV_ABOUT) {
|
|
|
|
WrapAbout(goBack = { navController.popBackStack() })
|
|
|
|
}
|
2022-05-19 05:41:58 -07:00
|
|
|
composable(NAV_SCAN) {
|
2022-06-02 04:35:51 -07:00
|
|
|
WrapScanValidator(
|
|
|
|
onScanValid = {
|
|
|
|
// TODO [#449] https://github.com/zcash/secant-android-wallet/issues/449
|
|
|
|
if (navController.currentDestination?.route == NAV_SCAN) {
|
|
|
|
navController.navigate(NAV_SEND) {
|
|
|
|
popUpTo(NAV_HOME) { inclusive = false }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
goBack = { navController.popBackStack() }
|
|
|
|
)
|
2022-05-19 05:41:58 -07:00
|
|
|
}
|
2021-12-29 11:38:14 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-02 04:35:51 -07:00
|
|
|
@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
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-29 11:38:14 -08:00
|
|
|
@Composable
|
|
|
|
private fun WrapHome(
|
|
|
|
goScan: () -> Unit,
|
|
|
|
goProfile: () -> Unit,
|
|
|
|
goSend: () -> Unit,
|
|
|
|
goRequest: () -> Unit
|
|
|
|
) {
|
|
|
|
val walletSnapshot = walletViewModel.walletSnapshot.collectAsState().value
|
|
|
|
if (null == walletSnapshot) {
|
|
|
|
// Display loading indicator
|
|
|
|
} else {
|
2022-05-31 09:38:02 -07:00
|
|
|
val context = LocalContext.current
|
|
|
|
|
|
|
|
// We might eventually want to check the debuggable property of the manifest instead
|
|
|
|
// of relying on BuildConfig.
|
|
|
|
val isDebugMenuEnabled = BuildConfig.DEBUG &&
|
|
|
|
!FirebaseTestLabUtil.isFirebaseTestLab(context) &&
|
|
|
|
!EmulatorWtfUtil.isEmulatorWtf(context)
|
|
|
|
|
2021-12-29 11:38:14 -08:00
|
|
|
Home(
|
|
|
|
walletSnapshot,
|
|
|
|
walletViewModel.transactionSnapshot.collectAsState().value,
|
|
|
|
goScan = goScan,
|
|
|
|
goRequest = goRequest,
|
|
|
|
goSend = goSend,
|
2022-05-31 09:38:02 -07:00
|
|
|
goProfile = goProfile,
|
|
|
|
isDebugMenuEnabled = isDebugMenuEnabled
|
2021-12-29 11:38:14 -08:00
|
|
|
)
|
2022-01-31 13:42:22 -08:00
|
|
|
|
|
|
|
reportFullyDrawn()
|
2022-05-16 04:40:50 -07:00
|
|
|
|
|
|
|
WrapCheckForUpdate()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Composable
|
|
|
|
private fun WrapCheckForUpdate() {
|
|
|
|
val updateInfo = checkUpdateViewModel.updateInfo.collectAsState().value
|
|
|
|
|
|
|
|
updateInfo?.let {
|
|
|
|
if (it.appUpdateInfo != null && it.state == UpdateState.Prepared) {
|
|
|
|
WrapUpdate(updateInfo)
|
|
|
|
}
|
2021-12-29 11:38:14 -08:00
|
|
|
}
|
2022-06-08 01:39:33 -07:00
|
|
|
|
|
|
|
// 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()
|
|
|
|
}
|
2021-12-03 05:19:15 -08:00
|
|
|
}
|
|
|
|
|
2021-12-31 05:28:16 -08:00
|
|
|
@Composable
|
|
|
|
private fun WrapWalletAddresses(
|
|
|
|
goBack: () -> Unit,
|
|
|
|
) {
|
|
|
|
val walletAddresses = walletViewModel.addresses.collectAsState().value
|
|
|
|
if (null == walletAddresses) {
|
|
|
|
// Display loading indicator
|
|
|
|
} else {
|
|
|
|
WalletAddresses(
|
|
|
|
walletAddresses,
|
|
|
|
goBack
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-26 13:33:02 -08:00
|
|
|
@Composable
|
|
|
|
private fun WrapSettings(
|
|
|
|
goBack: () -> Unit,
|
|
|
|
goWalletBackup: () -> Unit
|
|
|
|
) {
|
|
|
|
val synchronizer = walletViewModel.synchronizer.collectAsState().value
|
|
|
|
if (null == synchronizer) {
|
|
|
|
// Display loading indicator
|
|
|
|
} else {
|
|
|
|
Settings(
|
|
|
|
onBack = goBack,
|
|
|
|
onBackupWallet = goWalletBackup,
|
|
|
|
onRescanWallet = {
|
|
|
|
walletViewModel.rescanBlockchain()
|
|
|
|
}, onWipeWallet = {
|
2022-02-19 13:01:37 -08:00
|
|
|
walletViewModel.wipeWallet()
|
2022-01-26 13:33:02 -08:00
|
|
|
|
2022-02-19 13:01:37 -08:00
|
|
|
// If wipe ever becomes an operation to also delete the seed, then we'll also need
|
|
|
|
// to do the following to clear any retained state from onboarding (only happens if
|
|
|
|
// occurring during same session as onboarding)
|
|
|
|
// onboardingViewModel.onboardingState.goToBeginning()
|
|
|
|
// onboardingViewModel.isImporting.value = false
|
|
|
|
}
|
2022-01-26 13:33:02 -08:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@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)
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-17 05:08:06 -08:00
|
|
|
@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()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-01 05:11:23 -08:00
|
|
|
@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()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-03 05:19:15 -08:00
|
|
|
companion object {
|
|
|
|
@VisibleForTesting
|
|
|
|
internal val SPLASH_SCREEN_DELAY = 0.seconds
|
2022-01-31 13:42:22 -08:00
|
|
|
|
|
|
|
@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"
|
2022-02-17 05:08:06 -08:00
|
|
|
|
|
|
|
@VisibleForTesting
|
|
|
|
const val NAV_REQUEST = "request"
|
2022-03-01 05:11:23 -08:00
|
|
|
|
|
|
|
@VisibleForTesting
|
|
|
|
const val NAV_SEND = "send"
|
2022-04-19 18:28:49 -07:00
|
|
|
|
|
|
|
@VisibleForTesting
|
|
|
|
const val NAV_SUPPORT = "support"
|
2022-04-25 05:35:30 -07:00
|
|
|
|
|
|
|
@VisibleForTesting
|
|
|
|
const val NAV_ABOUT = "about"
|
2022-05-19 05:41:58 -07:00
|
|
|
|
|
|
|
@VisibleForTesting
|
|
|
|
const val NAV_SCAN = "scan"
|
2021-12-03 05:19:15 -08:00
|
|
|
}
|
2021-09-14 14:02:06 -07:00
|
|
|
}
|
2021-12-06 12:31:39 -08:00
|
|
|
|
2022-03-30 05:16:57 -07:00
|
|
|
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"
|
|
|
|
}
|
2022-02-17 05:08:06 -08:00
|
|
|
}
|