diff --git a/app/src/main/kotlin/dev/patrickgold/florisboard/app/FlorisAppActivity.kt b/app/src/main/kotlin/dev/patrickgold/florisboard/app/FlorisAppActivity.kt index 9184c046..5e966598 100644 --- a/app/src/main/kotlin/dev/patrickgold/florisboard/app/FlorisAppActivity.kt +++ b/app/src/main/kotlin/dev/patrickgold/florisboard/app/FlorisAppActivity.kt @@ -21,7 +21,6 @@ import android.content.res.Configuration import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent -import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.layout.Column import androidx.compose.material.MaterialTheme import androidx.compose.material.Surface @@ -70,7 +69,6 @@ val LocalNavController = staticCompositionLocalOf { class FlorisAppActivity : ComponentActivity() { private val prefs by florisPreferenceModel() - private var isDatastoreReady by mutableStateOf(false) private var appTheme by mutableStateOf(AppTheme.AUTO) private var showAppIcon = true private var resourcesContext by mutableStateOf(this as Context) @@ -79,8 +77,10 @@ class FlorisAppActivity : ComponentActivity() { super.onCreate(savedInstanceState) installSplashScreen() - prefs.datastoreReadyStatus.observe(this) { - isDatastoreReady = it + prefs.datastoreReadyStatus.observe(this) { ready -> + if (ready) { + AppVersionUtils.updateVersionOnInstallAndLastUse(this, prefs) + } } prefs.advanced.settingsTheme.observe(this) { appTheme = it @@ -96,7 +96,6 @@ class FlorisAppActivity : ComponentActivity() { } } - AppVersionUtils.updateVersionOnInstallAndLastUse(this, prefs) WindowCompat.setDecorFitsSystemWindows(window, false) setContent { @@ -129,7 +128,6 @@ class FlorisAppActivity : ComponentActivity() { } } - @OptIn(ExperimentalAnimationApi::class) @Composable private fun AppContent() { val navController = rememberNavController() diff --git a/app/src/main/kotlin/dev/patrickgold/florisboard/app/splash/SplashScreen.kt b/app/src/main/kotlin/dev/patrickgold/florisboard/app/splash/SplashScreen.kt index 6ab40c2d..52dbdb91 100644 --- a/app/src/main/kotlin/dev/patrickgold/florisboard/app/splash/SplashScreen.kt +++ b/app/src/main/kotlin/dev/patrickgold/florisboard/app/splash/SplashScreen.kt @@ -25,6 +25,7 @@ import androidx.compose.runtime.SideEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import dev.patrickgold.florisboard.R import dev.patrickgold.florisboard.app.LocalNavController @@ -32,6 +33,7 @@ import dev.patrickgold.florisboard.app.Routes import dev.patrickgold.florisboard.app.florisPreferenceModel import dev.patrickgold.florisboard.lib.compose.FlorisCanvasIcon import dev.patrickgold.florisboard.lib.compose.LocalPreviewFieldController +import dev.patrickgold.florisboard.lib.util.InputMethodUtils import dev.patrickgold.jetpref.datastore.model.observeAsState @Composable @@ -40,6 +42,8 @@ fun SplashScreen() = Box( contentAlignment = Alignment.Center, ) { val prefs by florisPreferenceModel() + val context = LocalContext.current + val isModelLoaded by prefs.datastoreReadyStatus.observeAsState() val isImeSetUp by prefs.internal.isImeSetUp.observeAsState() val navController = LocalNavController.current @@ -51,7 +55,9 @@ fun SplashScreen() = Box( LaunchedEffect(isModelLoaded) { if (isModelLoaded) { - navController.navigate(if (isImeSetUp) Routes.Settings.Home else Routes.Setup.Screen) { + val isConfigured = isImeSetUp || + (InputMethodUtils.isFlorisboardEnabled(context) && InputMethodUtils.isFlorisboardSelected(context)) + navController.navigate(if (isConfigured) Routes.Settings.Home else Routes.Setup.Screen) { popUpTo(Routes.Splash.Screen) { inclusive = true } diff --git a/app/src/main/kotlin/dev/patrickgold/florisboard/lib/util/InputMethodUtils.kt b/app/src/main/kotlin/dev/patrickgold/florisboard/lib/util/InputMethodUtils.kt index 7f8b28dc..09e15e15 100644 --- a/app/src/main/kotlin/dev/patrickgold/florisboard/lib/util/InputMethodUtils.kt +++ b/app/src/main/kotlin/dev/patrickgold/florisboard/lib/util/InputMethodUtils.kt @@ -31,6 +31,16 @@ private const val DELIMITER = ':' private const val IME_SERVICE_CLASS_NAME = "dev.patrickgold.florisboard.FlorisImeService" object InputMethodUtils { + fun isFlorisboardEnabled(context: Context): Boolean { + val enabledImeList = AndroidSettings.Secure.getString(context, Settings.Secure.ENABLED_INPUT_METHODS) + return enabledImeList != null && parseIsFlorisboardEnabled(context, enabledImeList) + } + + fun isFlorisboardSelected(context: Context): Boolean { + val selectedIme = AndroidSettings.Secure.getString(context, Settings.Secure.DEFAULT_INPUT_METHOD) + return selectedIme != null && parseIsFlorisboardSelected(context, selectedIme) + } + @Composable fun observeIsFlorisboardEnabled( context: Context = LocalContext.current.applicationContext, diff --git a/benchmark/src/main/kotlin/dev/patrickgold/florisboard/benchmark/StartupBenchmark.kt b/benchmark/src/main/kotlin/dev/patrickgold/florisboard/benchmark/StartupBenchmark.kt index 56fc442f..c99f74b8 100644 --- a/benchmark/src/main/kotlin/dev/patrickgold/florisboard/benchmark/StartupBenchmark.kt +++ b/benchmark/src/main/kotlin/dev/patrickgold/florisboard/benchmark/StartupBenchmark.kt @@ -1,4 +1,5 @@ /* + * Copyright 2022 The Android Open Source Project * Copyright (C) 2022 Patrick Goldinger * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,27 +17,74 @@ package dev.patrickgold.florisboard.benchmark +import androidx.benchmark.macro.BaselineProfileMode +import androidx.benchmark.macro.CompilationMode import androidx.benchmark.macro.StartupMode import androidx.benchmark.macro.StartupTimingMetric import androidx.benchmark.macro.junit4.MacrobenchmarkRule -import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -@RunWith(AndroidJUnit4::class) -class StartupBenchmark { +/** + * Run this benchmark from Studio to see startup measurements, and captured system traces + * for investigating your app's performance from a cold state. + */ +@RunWith(AndroidJUnit4ClassRunner::class) +class ColdStartupBenchmark : AbstractStartupBenchmark(StartupMode.COLD) + +/** + * Run this benchmark from Studio to see startup measurements, and captured system traces + * for investigating your app's performance from a warm state. + */ +@RunWith(AndroidJUnit4ClassRunner::class) +class WarmStartupBenchmark : AbstractStartupBenchmark(StartupMode.WARM) + +/** + * Run this benchmark from Studio to see startup measurements, and captured system traces + * for investigating your app's performance from a hot state. + */ +@RunWith(AndroidJUnit4ClassRunner::class) +class HotStartupBenchmark : AbstractStartupBenchmark(StartupMode.HOT) + +/** + * Base class for benchmarks with different startup modes. + * Enables app startups from various states of baseline profile or [CompilationMode]s. + * + * Original source of this test: https://github.com/android/nowinandroid/blob/b4a2f35ed23b2cf40fe90311bdac2688d9cb69e2/benchmark/src/main/java/com/google/samples/apps/nowinandroid/startup/StartupBenchmark.kt + */ +abstract class AbstractStartupBenchmark(private val startupMode: StartupMode) { @get:Rule val benchmarkRule = MacrobenchmarkRule() @Test - fun startup() = benchmarkRule.measureRepeated( + fun startupNoCompilation() = startup(CompilationMode.None()) + + @Test + fun startupBaselineProfileDisabled() = startup( + CompilationMode.Partial(baselineProfileMode = BaselineProfileMode.Disable, warmupIterations = 1) + ) + + @Test + fun startupBaselineProfile() = startup(CompilationMode.Partial(baselineProfileMode = BaselineProfileMode.Require)) + + @Test + fun startupFullCompilation() = startup(CompilationMode.Full()) + + private fun startup(compilationMode: CompilationMode) = benchmarkRule.measureRepeated( packageName = "dev.patrickgold.florisboard", metrics = listOf(StartupTimingMetric()), + compilationMode = compilationMode, iterations = 10, - startupMode = StartupMode.COLD, + startupMode = startupMode, + setupBlock = { + pressHome() + device.executeShellCommand("ime enable dev.patrickgold.florisboard/.FlorisImeService") + device.executeShellCommand("ime set dev.patrickgold.florisboard/.FlorisImeService") + } ) { - pressHome() startActivityAndWait() + device.waitForIdle(5000) } }