MainActivity.kt

5.3 KB · raw

1package com.peachpasswords23import android.content.Intent4import android.content.res.Configuration5import android.graphics.Color as AndroidColor6import android.graphics.drawable.ColorDrawable7import android.os.Bundle8import android.view.WindowManager9import androidx.activity.compose.setContent10import androidx.activity.enableEdgeToEdge11import androidx.compose.foundation.isSystemInDarkTheme12import androidx.compose.runtime.SideEffect13import androidx.compose.runtime.collectAsState14import androidx.compose.runtime.getValue15import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen16import androidx.core.view.WindowCompat17import androidx.fragment.app.FragmentActivity18import dagger.hilt.android.AndroidEntryPoint19import javax.inject.Inject20import com.peachpasswords.ui.PeachAppNavHost21import com.peachpasswords.ui.components.QaFlags22import com.peachpasswords.ui.onboarding.ONBOARD_KEY23import com.peachpasswords.ui.onboarding.ONBOARD_PREFS24import com.peachpasswords.ui.onboarding.onboardingStartDestination25import com.peachpasswords.ui.theme.ACCENT_COLORS26import com.peachpasswords.ui.theme.DEFAULT_ACCENT27import com.peachpasswords.ui.theme.PeachTheme28import com.peachpasswords.vault.VaultLockTimer29import com.peachpasswords.vault.VaultRepository3031// FragmentActivity (not ComponentActivity) so androidx BiometricPrompt can attach.32@AndroidEntryPoint33class MainActivity : FragmentActivity() {34    @Inject lateinit var vault: VaultRepository35    @Inject lateinit var lockTimer: VaultLockTimer3637    // Every touch/key event resets the idle auto-lock countdown (CF-1).38    override fun onUserInteraction() {39        super.onUserInteraction()40        lockTimer.poke()41    }4243    // launchMode="singleTask" reuses this instance across `adb shell am start`44    // relaunches, so the QA capture-bypass extra (see onCreate) needs to be45    // re-read here too, not just once at process start.46    override fun onNewIntent(intent: Intent) {47        super.onNewIntent(intent)48        setIntent(intent)49        if (BuildConfig.DEBUG) {50            QaFlags.disableSecureCaptureForQa = intent.getBooleanExtra("qa_disable_secure_flag", false)51        }52    }5354    override fun onCreate(savedInstanceState: Bundle?) {55        installSplashScreen()56        super.onCreate(savedInstanceState)5758        // Match the splash/window fill to the resolved theme's --bg so launch59        // never flashes the wrong colour before Compose draws. Resolved from the60        // persisted setting synchronously (system mode reads the device night mode).61        val initialTheme = vault.settings.value.theme62        val initialDark = when (initialTheme) {63            "light" -> false64            "system" -> (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) ==65                Configuration.UI_MODE_NIGHT_YES66            else -> true67        }68        window.setBackgroundDrawable(69            ColorDrawable(AndroidColor.parseColor(if (initialDark) "#0C0A06" else "#EFEEE9")),70        )7172        enableEdgeToEdge()7374        // Block screenshots / recents thumbnails of the vault in release builds.75        if (!BuildConfig.DEBUG) {76            window.setFlags(77                WindowManager.LayoutParams.FLAG_SECURE,78                WindowManager.LayoutParams.FLAG_SECURE,79            )80        } else {81            // QA-only, debug-build-only: `adb shell am start -n <pkg>/.MainActivity82            // --ez qa_disable_secure_flag true` lets automated visual QA screenshot83            // otherwise-FLAG_SECURE screens (SecureFlagEffect). Off unless asked.84            QaFlags.disableSecureCaptureForQa = intent.getBooleanExtra("qa_disable_secure_flag", false)85        }8687        // Onboarding runs once, only on a fresh install with no vault yet.88        val onboarded = getSharedPreferences(ONBOARD_PREFS, MODE_PRIVATE)89            .getBoolean(ONBOARD_KEY, false)90        val start = onboardingStartDestination(hasVault = vault.hasVault(), onboarded = onboarded)9192        setContent {93            val settings by vault.settings.collectAsState()94            val darkTheme = when (settings.theme) {95                "light" -> false96                "system" -> isSystemInDarkTheme()97                else -> true98            }99            // enableEdgeToEdge() above only knows the system's own day/night100            // mode at launch. This app's theme can be forced independent of101            // that (settings.theme == "dark"/"light"), so on a mismatch —102            // e.g. system in light mode, app forced dark — the status bar103            // icons render in the system's assumed color (dark-on-dark) and104            // become invisible against the app's actual background, reading105            // as if the app extends behind/under the status bar. Keep the106            // system bar icon appearance in sync with the resolved theme.107            SideEffect {108                WindowCompat.getInsetsController(window, window.decorView).apply {109                    isAppearanceLightStatusBars = !darkTheme110                    isAppearanceLightNavigationBars = !darkTheme111                }112            }113            val accent = ACCENT_COLORS[settings.accentColor] ?: DEFAULT_ACCENT114            PeachTheme(115                darkTheme = darkTheme,116                accent = accent,117                reduceMotion = settings.reduceMotion,118            ) {119                PeachAppNavHost(startDestination = start, isUnlocked = vault.isUnlocked)120            }121        }122    }123}