2022-06-22 02:48:19 -07:00
|
|
|
@file:Suppress("ktlint:filename")
|
|
|
|
|
2022-06-09 10:17:58 -07:00
|
|
|
package co.electriccoin.zcash.ui.screen.onboarding
|
|
|
|
|
|
|
|
import android.content.ClipboardManager
|
|
|
|
import android.content.Context
|
|
|
|
import androidx.activity.ComponentActivity
|
|
|
|
import androidx.activity.viewModels
|
|
|
|
import androidx.compose.runtime.Composable
|
|
|
|
import androidx.compose.ui.platform.LocalContext
|
2022-12-22 00:38:02 -08:00
|
|
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
2023-02-17 03:05:23 -08:00
|
|
|
import cash.z.ecc.android.sdk.model.PersistableWallet
|
|
|
|
import cash.z.ecc.android.sdk.model.SeedPhrase
|
2022-08-04 08:56:50 -07:00
|
|
|
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
2022-06-09 10:17:58 -07:00
|
|
|
import cash.z.ecc.sdk.fixture.SeedPhraseFixture
|
|
|
|
import cash.z.ecc.sdk.type.fromResources
|
|
|
|
import co.electriccoin.zcash.spackle.EmulatorWtfUtil
|
|
|
|
import co.electriccoin.zcash.spackle.FirebaseTestLabUtil
|
|
|
|
import co.electriccoin.zcash.ui.BuildConfig
|
|
|
|
import co.electriccoin.zcash.ui.MainActivity
|
2023-02-21 10:28:03 -08:00
|
|
|
import co.electriccoin.zcash.ui.configuration.ConfigurationEntries
|
|
|
|
import co.electriccoin.zcash.ui.configuration.RemoteConfig
|
2022-06-09 10:17:58 -07:00
|
|
|
import co.electriccoin.zcash.ui.screen.home.viewmodel.WalletViewModel
|
2023-02-21 10:28:03 -08:00
|
|
|
import co.electriccoin.zcash.ui.screen.onboarding.model.OnboardingStage
|
|
|
|
import co.electriccoin.zcash.ui.screen.onboarding.state.OnboardingState
|
2022-06-09 10:17:58 -07:00
|
|
|
import co.electriccoin.zcash.ui.screen.onboarding.view.Onboarding
|
|
|
|
import co.electriccoin.zcash.ui.screen.onboarding.viewmodel.OnboardingViewModel
|
|
|
|
import co.electriccoin.zcash.ui.screen.restore.view.RestoreWallet
|
|
|
|
import co.electriccoin.zcash.ui.screen.restore.viewmodel.CompleteWordSetState
|
|
|
|
import co.electriccoin.zcash.ui.screen.restore.viewmodel.RestoreViewModel
|
|
|
|
|
|
|
|
@Composable
|
|
|
|
internal fun MainActivity.WrapOnboarding() {
|
|
|
|
WrapOnboarding(this)
|
|
|
|
}
|
|
|
|
|
|
|
|
@Composable
|
|
|
|
internal fun WrapOnboarding(
|
|
|
|
activity: ComponentActivity
|
|
|
|
) {
|
|
|
|
val walletViewModel by activity.viewModels<WalletViewModel>()
|
|
|
|
val onboardingViewModel by activity.viewModels<OnboardingViewModel>()
|
|
|
|
|
|
|
|
val applicationContext = LocalContext.current.applicationContext
|
|
|
|
|
|
|
|
// We might eventually want to check the debuggable property of the manifest instead
|
|
|
|
// of relying on BuildConfig.
|
|
|
|
val isDebugMenuEnabled = BuildConfig.DEBUG &&
|
|
|
|
!FirebaseTestLabUtil.isFirebaseTestLab(applicationContext) &&
|
|
|
|
!EmulatorWtfUtil.isEmulatorWtf(applicationContext)
|
|
|
|
|
|
|
|
// TODO [#383]: https://github.com/zcash/secant-android-wallet/issues/383
|
2022-12-22 00:38:02 -08:00
|
|
|
if (!onboardingViewModel.isImporting.collectAsStateWithLifecycle().value) {
|
2023-02-21 10:28:03 -08:00
|
|
|
val isFullOnboardingEnabled = ConfigurationEntries.IS_FULL_ONBOARDING_ENABLED.getValue(RemoteConfig.current)
|
|
|
|
val onboardingState = if (isFullOnboardingEnabled) {
|
|
|
|
onboardingViewModel.onboardingState
|
|
|
|
} else {
|
|
|
|
// Force to the last screen, which is the "create wallet" screen.
|
|
|
|
// This simplifies the implementation inside the Onboarding composable.
|
|
|
|
OnboardingState(OnboardingStage.values().last())
|
|
|
|
}
|
|
|
|
|
2022-06-09 10:17:58 -07:00
|
|
|
Onboarding(
|
2023-02-21 10:28:03 -08:00
|
|
|
isFullOnboardingEnabled = isFullOnboardingEnabled,
|
|
|
|
onboardingState = onboardingState,
|
2022-06-09 10:17:58 -07:00
|
|
|
isDebugMenuEnabled = isDebugMenuEnabled,
|
|
|
|
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(
|
|
|
|
applicationContext,
|
|
|
|
walletViewModel,
|
|
|
|
SeedPhraseFixture.new()
|
|
|
|
)
|
|
|
|
return@Onboarding
|
|
|
|
}
|
|
|
|
|
2023-02-21 10:28:03 -08:00
|
|
|
onboardingViewModel.setIsImporting(true)
|
2022-06-09 10:17:58 -07:00
|
|
|
},
|
|
|
|
onCreateWallet = {
|
|
|
|
if (FirebaseTestLabUtil.isFirebaseTestLab(applicationContext)) {
|
|
|
|
persistExistingWalletWithSeedPhrase(
|
|
|
|
applicationContext,
|
|
|
|
walletViewModel,
|
|
|
|
SeedPhraseFixture.new()
|
|
|
|
)
|
|
|
|
return@Onboarding
|
|
|
|
}
|
|
|
|
|
|
|
|
walletViewModel.persistNewWallet()
|
|
|
|
},
|
|
|
|
onFixtureWallet = {
|
|
|
|
persistExistingWalletWithSeedPhrase(
|
|
|
|
applicationContext,
|
|
|
|
walletViewModel,
|
|
|
|
SeedPhraseFixture.new()
|
|
|
|
)
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
activity.reportFullyDrawn()
|
|
|
|
} else {
|
|
|
|
WrapRestore(activity)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Composable
|
|
|
|
private fun WrapRestore(activity: ComponentActivity) {
|
|
|
|
val walletViewModel by activity.viewModels<WalletViewModel>()
|
|
|
|
val onboardingViewModel by activity.viewModels<OnboardingViewModel>()
|
|
|
|
val restoreViewModel by activity.viewModels<RestoreViewModel>()
|
|
|
|
|
|
|
|
val applicationContext = LocalContext.current.applicationContext
|
|
|
|
|
2022-12-22 00:38:02 -08:00
|
|
|
when (val completeWordList = restoreViewModel.completeWordList.collectAsStateWithLifecycle().value) {
|
2022-06-09 10:17:58 -07:00
|
|
|
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.
|
|
|
|
}
|
|
|
|
is CompleteWordSetState.Loaded -> {
|
|
|
|
RestoreWallet(
|
|
|
|
completeWordList.list,
|
|
|
|
restoreViewModel.userWordList,
|
2023-02-21 10:28:03 -08:00
|
|
|
onBack = { onboardingViewModel.setIsImporting(false) },
|
2022-06-09 10:17:58 -07:00
|
|
|
paste = {
|
|
|
|
val clipboardManager = applicationContext.getSystemService(ClipboardManager::class.java)
|
|
|
|
return@RestoreWallet clipboardManager?.primaryClip?.toString()
|
|
|
|
},
|
|
|
|
onFinished = {
|
|
|
|
persistExistingWalletWithSeedPhrase(
|
|
|
|
applicationContext,
|
|
|
|
walletViewModel,
|
|
|
|
SeedPhrase(restoreViewModel.userWordList.current.value)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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(
|
|
|
|
context: Context,
|
|
|
|
walletViewModel: WalletViewModel,
|
|
|
|
seedPhrase: SeedPhrase
|
|
|
|
) {
|
|
|
|
walletViewModel.persistBackupComplete()
|
|
|
|
|
|
|
|
val network = ZcashNetwork.fromResources(context)
|
|
|
|
val restoredWallet = PersistableWallet(
|
|
|
|
network,
|
|
|
|
null,
|
|
|
|
seedPhrase
|
|
|
|
)
|
|
|
|
walletViewModel.persistExistingWallet(restoredWallet)
|
|
|
|
}
|