4969c80c9f49
feat: allow-screenshots toggle, search bar border removal, deck gap reduction (v2.9.31)
- time
- 2026-08-02 01:00 UTC · 2 months ago
- machine
- koh2
- parent
- e064cc9e34e5
↓ peach-android-4969c80c.tar.gzatlas at this save
files changed10 files
VERSION+1 −1app/src/main/java/com/peachpasswords/MainActivity.kt+9 −5app/src/main/java/com/peachpasswords/autofill/AutofillEntryPickerActivity.kt+1 −1app/src/main/java/com/peachpasswords/passkeys/PasskeyCredentialSelectionActivity.kt+1 −1app/src/main/java/com/peachpasswords/ui/components/SecureWindowPolicy.kt+17 −15app/src/main/java/com/peachpasswords/ui/settings/SettingsCardBodies.kt+4 −0app/src/main/java/com/peachpasswords/ui/settings/SettingsScreen.kt+1 −1app/src/main/java/com/peachpasswords/ui/settings/SettingsViewModel.kt+1 −0app/src/main/java/com/peachpasswords/ui/vault/VaultListScreen.kt+0 −1app/src/main/java/com/peachpasswords/vault/VaultSettings.kt+1 −0app/src/main/java/com/peachpasswords/MainActivity.kt+9 −5
@@ -80,13 +80,12 @@8080 8181 enableEdgeToEdge()8282 83- // Block screenshots / recents thumbnails of the vault in release builds.83+ // Default to FLAG_SECURE immediately (before Compose draws) so there is84+ // never an unprotected flash. The user's "allow screenshots" preference85+ // is applied reactively inside setContent once settings are available.8486 if (!BuildConfig.DEBUG) {85- SecureWindowPolicy.installRelease(window)87+ SecureWindowPolicy.setUserSecure(window, true)8688 } else {87- // QA-only, debug-build-only: `adb shell am start -n <pkg>/.MainActivity88- // --ez qa_disable_secure_flag true` lets automated visual QA screenshot89- // otherwise-FLAG_SECURE screens (SecureFlagEffect). Off unless asked.9089 QaFlags.disableSecureCaptureForQa = intent.getBooleanExtra(9190 QaFlags.EXTRA_DISABLE_SECURE_CAPTURE,9291 false,@@ -111,6 +110,11 @@111110 }112111 }113112 val accent = ACCENT_COLORS[settings.accentColor] ?: DEFAULT_ACCENT113+ SideEffect {114+ if (!BuildConfig.DEBUG) {115+ SecureWindowPolicy.setUserSecure(window, !settings.allowScreenshots)116+ }117+ }114118 PeachTheme(115119 accent = accent,116120 reduceMotion = settings.reduceMotion,app/src/main/java/com/peachpasswords/autofill/AutofillEntryPickerActivity.kt+1 −1
@@ -101,7 +101,7 @@101101 // release-wide FLAG_SECURE set there. Show vault metadata and accept the102102 // master password — protect against screenshots / recents thumbnails.103103 if (!BuildConfig.DEBUG) {104- SecureWindowPolicy.installRelease(window)104+ SecureWindowPolicy.setUserSecure(window, true)105105 } else if (!qaSecureCaptureBypass()) {106106 // Debug builds are secure by default too. QA must opt out explicitly107107 // on this activity's own launch intent; MainActivity's window policyapp/src/main/java/com/peachpasswords/passkeys/PasskeyCredentialSelectionActivity.kt+1 −1
@@ -43,7 +43,7 @@4343 // operate on passkey material. Block screenshots / recents in every build4444 // unless an explicit debug QA bypass was placed on this activity's intent.4545 if (!BuildConfig.DEBUG) {46- SecureWindowPolicy.installRelease(window)46+ SecureWindowPolicy.setUserSecure(window, true)4747 } else if (!qaSecureCaptureBypass()) {4848 SecureWindowPolicy.acquire(window)4949 debugSecureLease = trueapp/src/main/java/com/peachpasswords/ui/components/SecureWindowPolicy.kt+17 −15
@@ -16,44 +16,46 @@1616 internal object SecureWindowPolicy {1717 private data class State(1818 var leases: Int = 0,19- var releaseOwned: Boolean = false,19+ var userSecure: Boolean = true,2020 )2121 2222 private val states = WeakHashMap<Window, State>()2323 24- /** Install a release-wide owner that is never cleared for this window. */25- fun installRelease(window: Window) {26- synchronized(states) {27- val state = states.getOrPut(window) { State() }28- state.releaseOwned = true24+ private fun evaluate(window: Window, state: State) {25+ if (state.userSecure || state.leases > 0) {2926 window.setFlags(3027 WindowManager.LayoutParams.FLAG_SECURE,3128 WindowManager.LayoutParams.FLAG_SECURE,3229 )30+ } else {31+ window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)3332 }3433 }3534 35+ /** Set the user's screenshot preference for this window. */36+ fun setUserSecure(window: Window, secure: Boolean) {37+ synchronized(states) {38+ val state = states.getOrPut(window) { State() }39+ state.userSecure = secure40+ evaluate(window, state)41+ }42+ }43+3644 /** Acquire one debug/composable lease for a window. */3745 fun acquire(window: Window) {3846 synchronized(states) {3947 val state = states.getOrPut(window) { State() }4048 state.leases++41- window.setFlags(42- WindowManager.LayoutParams.FLAG_SECURE,43- WindowManager.LayoutParams.FLAG_SECURE,44- )49+ evaluate(window, state)4550 }4651 }4752 48- /** Release one lease; release-owned windows remain protected forever. */53+ /** Release one lease; re-evaluates the flag. */4954 fun release(window: Window) {5055 synchronized(states) {5156 val state = states[window] ?: return5257 if (state.leases > 0) state.leases--53- if (state.leases == 0 && !state.releaseOwned) {54- window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)55- states.remove(window)56- }58+ evaluate(window, state)5759 }5860 }5961 }app/src/main/java/com/peachpasswords/ui/settings/SettingsCardBodies.kt+4 −0
@@ -292,6 +292,10 @@292292 293293 SubLabel("LOCKING", fg, top = 0.dp)294294 RowLine(fg) {295+ Name("Allow screenshots", fg)296+ Toggle(s.allowScreenshots, fg) { vm.setAllowScreenshots(it) }297+ }298+ RowLine(fg) {295299 Name("Auto-lock after", fg)296300 val idx = lockOptions.indexOfFirst { it.first == current }.coerceAtLeast(0)297301 SubtleButton(lockOptions[idx].second, fg) {app/src/main/java/com/peachpasswords/ui/settings/SettingsScreen.kt+1 −1
@@ -104,7 +104,7 @@104104 private val VERSION_LABEL_FOOTPRINT = PeachSpacing.xs + 12.dp105105 106106 /** Clean gap left between the lowest fanned card and the build label. */107-private val DECK_TO_VERSION_GAP = 6.dp107+private val DECK_TO_VERSION_GAP = 2.dp108108 109109 /** Drag distance past which a stack card flips between focused and deck states. */110110 private val DRAG_THRESHOLD = 60.dpapp/src/main/java/com/peachpasswords/ui/settings/SettingsViewModel.kt+1 −0
@@ -281,6 +281,7 @@281281 fun setBreachMonitoring(on: Boolean) = mutate { it.copy(breachMonitoring = on) }282282 fun setBiometrics(on: Boolean) = mutate { it.copy(biometricsEnabled = on) }283283 fun setPasskeyProviderEnabled(on: Boolean) = mutate { it.copy(passkeyProviderEnabled = on) }284+ fun setAllowScreenshots(on: Boolean) = mutate { it.copy(allowScreenshots = on) }284285 285286 /**286287 * Enable biometric unlock: prompt to wrap the live vault key under a hardwareapp/src/main/java/com/peachpasswords/ui/vault/VaultListScreen.kt+0 −1
@@ -399,7 +399,6 @@399399 .padding(horizontal = PeachSpacing.md, vertical = PeachSpacing.sm)400400 .clip(RoundedCornerShape(12.dp))401401 .background(peachColors.S2)402- .border(1.dp, peachColors.T3, RoundedCornerShape(12.dp))403402 .padding(horizontal = PeachSpacing.md, vertical = PeachSpacing.sm)404403 // C1: inverse of EntryDetailSearchStrip's slide-up-to-open —405404 // slide the list's own search bar down to collapse back toapp/src/main/java/com/peachpasswords/vault/VaultSettings.kt+1 −0
@@ -14,6 +14,7 @@1414 val passkeyProviderEnabled: Boolean = false,1515 val duressConfigured: Boolean = false,1616 val shortcuts: Map<String, ShortcutBinding> = emptyMap(),17+ val allowScreenshots: Boolean = false,1718 )1819 1920 @Serializable