secant-android-wallet/ui-lib/src/main/java/co/electriccoin/zcash/ui/MainActivity.kt

177 lines
7.1 KiB
Kotlin
Raw Normal View History

package co.electriccoin.zcash.ui
import android.os.Bundle
2021-12-03 05:19:15 -08:00
import android.os.SystemClock
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
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
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
2021-12-09 12:18:18 -08:00
import androidx.compose.ui.Modifier
2021-12-03 05:19:15 -08:00
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.NavHostController
import co.electriccoin.zcash.ui.common.BindCompLocalProvider
import co.electriccoin.zcash.ui.configuration.RemoteConfig
import co.electriccoin.zcash.ui.design.component.ConfigurationOverride
import co.electriccoin.zcash.ui.design.component.GradientSurface
import co.electriccoin.zcash.ui.design.component.Override
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.screen.backup.WrapNewWallet
import co.electriccoin.zcash.ui.screen.home.viewmodel.HomeViewModel
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.WrapOnboarding
import co.electriccoin.zcash.ui.screen.warning.WrapNotEnoughSpace
import co.electriccoin.zcash.ui.screen.warning.viewmodel.StorageCheckViewModel
import co.electriccoin.zcash.work.WorkIds
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
2021-12-03 05:19:15 -08:00
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
class MainActivity : ComponentActivity() {
val homeViewModel by viewModels<HomeViewModel>()
val walletViewModel by viewModels<WalletViewModel>()
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
val storageCheckViewModel by viewModels<StorageCheckViewModel>()
lateinit var navControllerForTesting: NavHostController
val configurationOverrideFlow = MutableStateFlow<ConfigurationOverride?>(null)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
2021-12-03 05:19:15 -08:00
setupSplashScreen()
setupUiContent()
monitorForBackgroundSync()
}
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
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) {
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
// Note this condition needs to be kept in sync with the condition in MainContent()
homeViewModel.configurationFlow.value == null || 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 {
Override(configurationOverrideFlow) {
ZcashTheme {
GradientSurface(
Modifier
.fillMaxWidth()
.fillMaxHeight()
) {
BindCompLocalProvider {
val isEnoughSpace by storageCheckViewModel.isEnoughSpace.collectAsStateWithLifecycle()
if (isEnoughSpace == false) {
WrapNotEnoughSpace()
} else {
MainContent()
}
2021-12-09 12:18:18 -08:00
}
2021-12-06 12:31:39 -08:00
}
}
}
// Force collection to improve performance; sync can start happening while
// the user is going through the backup flow.
walletViewModel.synchronizer.collectAsStateWithLifecycle()
}
}
@Composable
private fun MainContent() {
val configuration = homeViewModel.configurationFlow.collectAsStateWithLifecycle().value
val secretState = walletViewModel.secretState.collectAsStateWithLifecycle().value
// Note this condition needs to be kept in sync with the condition in setupSplashScreen()
if (null == configuration || secretState == SecretState.Loading) {
// For now, keep displaying splash screen using condition above.
// In the future, we might consider displaying something different here.
} else {
// Note that the deeply nested child views will probably receive arguments derived from
// the configuration. The CompositionLocalProvider is helpful for passing the configuration
// to the "platform" layer, which is where the arguments will be derived from.
CompositionLocalProvider(RemoteConfig provides configuration) {
when (secretState) {
SecretState.None -> {
WrapOnboarding()
}
is SecretState.NeedsBackup -> {
WrapNewWallet(
secretState.persistableWallet,
onBackupComplete = { walletViewModel.persistBackupComplete() }
)
}
is SecretState.Ready -> {
Navigation()
}
else -> {
error("Unhandled secret state: $secretState")
}
}
}
}
}
private fun monitorForBackgroundSync() {
val isEnableBackgroundSyncFlow = run {
val homeViewModel by viewModels<HomeViewModel>()
val isSecretReadyFlow = walletViewModel.secretState.map { it is SecretState.Ready }
val isBackgroundSyncEnabledFlow = homeViewModel.isBackgroundSyncEnabled.filterNotNull()
isSecretReadyFlow.combine(isBackgroundSyncEnabledFlow) { isSecretReady, isBackgroundSyncEnabled ->
isSecretReady && isBackgroundSyncEnabled
}
}
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
isEnableBackgroundSyncFlow.collect { isEnableBackgroundSync ->
if (isEnableBackgroundSync) {
WorkIds.enableBackgroundSynchronization(application)
} else {
WorkIds.disableBackgroundSynchronization(application)
}
}
}
}
}
2021-12-03 05:19:15 -08:00
companion object {
@VisibleForTesting
internal val SPLASH_SCREEN_DELAY = 0.seconds
}
}