[#101] Improve app startup pre API 26

This commit is contained in:
Carter Jernigan 2021-12-06 15:31:39 -05:00
parent 4a07b2e0b9
commit 694d5d73c9
2 changed files with 86 additions and 18 deletions

View File

@ -2,6 +2,7 @@ package cash.z.ecc.ui
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.os.Bundle
import android.os.SystemClock
import androidx.activity.ComponentActivity
@ -10,7 +11,9 @@ import androidx.activity.viewModels
import androidx.annotation.VisibleForTesting
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.core.content.res.ResourcesCompat
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.lifecycleScope
import cash.z.ecc.sdk.model.PersistableWallet
import cash.z.ecc.ui.screen.backup.view.BackupWallet
import cash.z.ecc.ui.screen.backup.viewmodel.BackupViewModel
@ -20,6 +23,10 @@ import cash.z.ecc.ui.screen.home.viewmodel.WalletViewModel
import cash.z.ecc.ui.screen.onboarding.view.Onboarding
import cash.z.ecc.ui.screen.onboarding.viewmodel.OnboardingViewModel
import cash.z.ecc.ui.theme.ZcashTheme
import cash.z.ecc.ui.util.AndroidApiVersion
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
@ -36,6 +43,37 @@ class MainActivity : ComponentActivity() {
setupSplashScreen()
if (AndroidApiVersion.isAtLeastO) {
setupUiContent()
} else {
lifecycleScope.launch {
prefetchFontLegacy(applicationContext, R.font.rubik_medium)
prefetchFontLegacy(applicationContext, R.font.rubik_regular)
setupUiContent()
}
}
}
private fun setupSplashScreen() {
installSplashScreen().also {
val start = SystemClock.elapsedRealtime().milliseconds
it.setKeepVisibleCondition {
if (SPLASH_SCREEN_DELAY > Duration.ZERO) {
val now = SystemClock.elapsedRealtime().milliseconds
// This delay is for debug purposes only; do not enable for production usage.
if (now - start < SPLASH_SCREEN_DELAY) {
return@setKeepVisibleCondition true
}
}
WalletState.Loading == walletViewModel.state.value
}
}
}
private fun setupUiContent() {
setContent {
ZcashTheme {
val walletState = walletViewModel.state.collectAsState().value
@ -59,24 +97,6 @@ class MainActivity : ComponentActivity() {
}
}
private fun setupSplashScreen() {
installSplashScreen().also {
val start = SystemClock.elapsedRealtime().milliseconds
it.setKeepVisibleCondition {
if (SPLASH_SCREEN_DELAY > Duration.ZERO) {
val now = SystemClock.elapsedRealtime().milliseconds
// This delay is for debug purposes only; do not enable for production usage.
if (now - start < SPLASH_SCREEN_DELAY) {
return@setKeepVisibleCondition true
}
}
WalletState.Loading == walletViewModel.state.value
}
}
}
@Composable
private fun WrapBackup(persistableWallet: PersistableWallet) {
BackupWallet(
@ -115,3 +135,17 @@ class MainActivity : ComponentActivity() {
internal val SPLASH_SCREEN_DELAY = 0.seconds
}
}
/**
* Pre-fetches fonts on Android N (API 25) and below.
*/
/*
* ResourcesCompat is used implicitly by Compose on older Android versions.
* The backwards compatibility library performs disk IO and then
* caches the results. This moves that IO off the main thread, to prevent ANRs and
* jank during app startup.
*/
private suspend fun prefetchFontLegacy(context: Context, @androidx.annotation.FontRes fontRes: Int) =
withContext(Dispatchers.IO) {
ResourcesCompat.getFont(context, fontRes)
}

View File

@ -0,0 +1,34 @@
package cash.z.ecc.ui.util
import android.os.Build
import androidx.annotation.ChecksSdkIntAtLeast
import androidx.annotation.IntRange
object AndroidApiVersion {
/**
* @param sdk SDK version number to test against the current environment.
* @return `true` if [android.os.Build.VERSION.SDK_INT] is greater than or equal to
* [sdk].
*/
@ChecksSdkIntAtLeast(parameter = 0)
fun isAtLeast(@IntRange(from = Build.VERSION_CODES.BASE.toLong()) sdk: Int): Boolean {
return Build.VERSION.SDK_INT >= sdk
}
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.P)
val isAtLeastP = isAtLeast(Build.VERSION_CODES.P)
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.O)
val isAtLeastO = isAtLeast(Build.VERSION_CODES.O)
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.Q)
val isAtLeastQ = isAtLeast(Build.VERSION_CODES.Q)
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.R)
val isAtLeastR = isAtLeast(Build.VERSION_CODES.R)
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.S)
val isAtLeastS = isAtLeast(Build.VERSION_CODES.S)
val isPreview = 0 != Build.VERSION.PREVIEW_SDK_INT
}