1package com.peachpasswords23import android.content.Intent4import android.graphics.Color as AndroidColor5import android.graphics.drawable.ColorDrawable6import android.os.Bundle7import androidx.activity.compose.setContent8import androidx.activity.enableEdgeToEdge9import androidx.compose.runtime.SideEffect10import androidx.compose.ui.unit.dp11import androidx.compose.ui.unit.Density12import androidx.compose.ui.platform.LocalDensity13import androidx.compose.ui.Modifier14import androidx.compose.runtime.CompositionLocalProvider15import androidx.compose.runtime.Composable16import androidx.compose.foundation.layout.fillMaxSize17import androidx.compose.foundation.layout.BoxWithConstraints18import androidx.compose.runtime.collectAsState19import androidx.compose.runtime.getValue20import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen21import androidx.core.view.WindowCompat22import androidx.fragment.app.FragmentActivity23import dagger.hilt.android.AndroidEntryPoint24import javax.inject.Inject25import com.peachpasswords.autofill.AutofillIntents26import com.peachpasswords.autofill.AutofillNavRepository27import com.peachpasswords.ui.PeachAppNavHost28import com.peachpasswords.ui.components.QaFlags29import com.peachpasswords.ui.components.SecureWindowPolicy30import com.peachpasswords.ui.onboarding.ONBOARD_KEY31import com.peachpasswords.ui.onboarding.ONBOARD_PREFS32import com.peachpasswords.ui.onboarding.onboardingStartDestination33import com.peachpasswords.ui.theme.ACCENT_COLORS34import com.peachpasswords.ui.theme.DEFAULT_ACCENT35import com.peachpasswords.ui.theme.PeachTheme36import com.peachpasswords.updates.EXTRA_OPEN_UPDATE37import com.peachpasswords.updates.UpdateRepository38import com.peachpasswords.vault.VaultLockTimer39import com.peachpasswords.vault.VaultRepository404142@AndroidEntryPoint43class MainActivity : FragmentActivity() {44 @Inject lateinit var vault: VaultRepository45 @Inject lateinit var lockTimer: VaultLockTimer46 @Inject lateinit var updates: UpdateRepository47 @Inject lateinit var autofillNav: AutofillNavRepository4849 50 override fun onUserInteraction() {51 super.onUserInteraction()52 lockTimer.poke()53 }5455 56 57 58 override fun onNewIntent(intent: Intent) {59 super.onNewIntent(intent)60 setIntent(intent)61 routeIntent(intent)62 if (BuildConfig.DEBUG) {63 QaFlags.disableSecureCaptureForQa = intent.getBooleanExtra(64 QaFlags.EXTRA_DISABLE_SECURE_CAPTURE,65 false,66 )67 }68 }6970 override fun onCreate(savedInstanceState: Bundle?) {71 installSplashScreen()72 super.onCreate(savedInstanceState)7374 75 routeIntent(intent)7677 78 79 window.setBackgroundDrawable(ColorDrawable(AndroidColor.parseColor("#0C0A06")))8081 enableEdgeToEdge()8283 84 85 86 if (!BuildConfig.DEBUG) {87 SecureWindowPolicy.setUserSecure(window, true)88 } else {89 QaFlags.disableSecureCaptureForQa = intent.getBooleanExtra(90 QaFlags.EXTRA_DISABLE_SECURE_CAPTURE,91 false,92 )93 }9495 96 val onboarded = getSharedPreferences(ONBOARD_PREFS, MODE_PRIVATE)97 .getBoolean(ONBOARD_KEY, false)98 val start = onboardingStartDestination(hasVault = vault.hasVault(), onboarded = onboarded)99100 setContent {101 val settings by vault.settings.collectAsState()102 103 104 105 106 SideEffect {107 WindowCompat.getInsetsController(window, window.decorView).apply {108 isAppearanceLightStatusBars = false109 isAppearanceLightNavigationBars = false110 }111 }112 val accent = ACCENT_COLORS[settings.accentColor] ?: DEFAULT_ACCENT113 SideEffect {114 if (!BuildConfig.DEBUG) {115 SecureWindowPolicy.setUserSecure(window, !settings.allowScreenshots)116 }117 }118 PeachTheme(119 accent = accent,120 reduceMotion = settings.reduceMotion,121 textSize = settings.textSize,122 ) {123 WideScreenScale {124 PeachAppNavHost(startDestination = start, isUnlocked = vault.isUnlocked, uiGateLocked = vault.uiGateLocked)125 }126 }127 }128 }129}130131132private val BASE_WIDTH = 411.dp133134135136137138139140141142143private fun MainActivity.routeIntent(intent: Intent) {144 if (intent.getBooleanExtra(EXTRA_OPEN_UPDATE, false)) updates.requestOpen()145 forwardNewEntryRequest(intent)146 routeAction(intent.action)?.let(PendingActionQueue::request)147}148149150151152153154private fun MainActivity.forwardNewEntryRequest(intent: Intent) {155 if (!intent.getBooleanExtra(AutofillIntents.EXTRA_OPEN_NEW_ENTRY, false)) return156 autofillNav.requestNewEntry(157 url = intent.getStringExtra(AutofillIntents.EXTRA_NEW_ENTRY_URL),158 username = intent.getStringExtra(AutofillIntents.EXTRA_NEW_ENTRY_USERNAME),159 email = intent.getStringExtra(AutofillIntents.EXTRA_NEW_ENTRY_EMAIL),160 )161}162163164private const val MAX_SCALE = 1.35f165166167168169170171172173174175176@Composable177private fun WideScreenScale(content: @Composable () -> Unit) {178 BoxWithConstraints(Modifier.fillMaxSize()) {179 val base = LocalDensity.current180 val scale = (maxWidth / BASE_WIDTH).coerceIn(1f, MAX_SCALE)181 if (scale == 1f) {182 content()183 } else {184 CompositionLocalProvider(185 LocalDensity provides Density(base.density * scale, base.fontScale),186 content = content,187 )188 }189 }190}