8c13ab49ceaa

feat: add adaptive Appearance scaling controls (v2.14.54)

time
2026-08-15 00:23 UTC · 2 months ago
machine
koh2
parent
9ac9634b621e
6 files VERSION+1 −1app/src/main/java/com/peachpasswords/MainActivity.kt+24 −5app/src/main/java/com/peachpasswords/ui/settings/SettingsCardBodies.kt+10 −0app/src/main/java/com/peachpasswords/ui/settings/SettingsViewModel.kt+1 −0app/src/main/java/com/peachpasswords/vault/VaultSettings.kt+2 −0app/src/test/java/com/peachpasswords/MainActivityTest.kt+23 −0
VERSION+1 −1
@@ -1 +1 @@1-2.14.531+2.14.54

view file

app/src/main/java/com/peachpasswords/MainActivity.kt+24 −5
@@ -9,6 +9,7 @@99 import androidx.compose.runtime.SideEffect1010 import androidx.compose.ui.unit.dp1111 import androidx.compose.ui.unit.Density12+import androidx.compose.ui.unit.Dp1213 import androidx.compose.ui.platform.LocalDensity1314 import androidx.compose.ui.Modifier1415 import androidx.compose.runtime.CompositionLocalProvider@@ -128,7 +129,7 @@128129                     reduceMotion = settings.reduceMotion,129130                     textSize = settings.textSize,130131                 ) {131-                    WideScreenScale {132+                    WideScreenScale(scalePreference = settings.uiScale) {132133                         PeachAppNavHost(startDestination = start, isUnlocked = vault.isUnlocked, uiGateLocked = vault.uiGateLocked)133134                     }134135                 }@@ -181,15 +182,18 @@181182  * grows every dp and sp together, so the same single-column layout simply reads182183  * as a bigger screen — no per-screen breakpoints to keep in sync.183184  *184- * Ceiling: a uniform scale, not real adaptive layout. If a screen ever wants a185- * genuinely different wide-screen arrangement it needs its own breakpoint.185+ * Auto mode keeps the existing foldable treatment but leaves tablet-like186+ * windows at phone density; Compact and Large are explicit overrides.186187  * No-op at or below [BASE_WIDTH], so phones are untouched.187188  */188189 @Composable189-private fun WideScreenScale(content: @Composable () -> Unit) {190+private fun WideScreenScale(191+    scalePreference: String = "auto",192+    content: @Composable () -> Unit,193+) {190194     BoxWithConstraints(Modifier.fillMaxSize()) {191195         val base = LocalDensity.current192-        val scale = (maxWidth / BASE_WIDTH).coerceIn(1f, MAX_SCALE)196+        val scale = wideScreenScaleFor(scalePreference, maxWidth, maxHeight)193197         if (scale == 1f) {194198             content()195199         } else {@@ -199,4 +203,19 @@199203             )200204         }201205     }206+}207+208+internal fun wideScreenScaleFor(preference: String, width: Dp, height: Dp): Float {209+    val phoneScale = (width / BASE_WIDTH).coerceIn(1f, MAX_SCALE)210+    return when (preference) {211+        "compact" -> 1f212+        "large" -> phoneScale213+        else -> if (isTabletLike(width, height)) 1f else phoneScale214+    }215+}216+217+private fun isTabletLike(width: Dp, height: Dp): Boolean {218+    val shortest = minOf(width.value, height.value)219+    val longest = maxOf(width.value, height.value)220+    return shortest >= 720f || (shortest >= 600f && longest / shortest >= 1.45f)202221 }

view file

app/src/main/java/com/peachpasswords/ui/settings/SettingsCardBodies.kt+10 −0
@@ -331,6 +331,16 @@331331             Pill("large", s.textSize == "large", fg) { vm.setTextSize("large") }332332         }333333     }334+    SubLabel("UI SCALE", fg)335+    RowLine(fg) {336+        Name("Large-screen sizing", fg)337+        Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {338+            Pill("compact", s.uiScale == "compact", fg) { vm.setUiScale("compact") }339+            Pill("auto", s.uiScale == "auto", fg) { vm.setUiScale("auto") }340+            Pill("large", s.uiScale == "large", fg) { vm.setUiScale("large") }341+        }342+    }343+    Hint("Auto keeps foldables roomy while preventing tablet controls from inflating.", fg)334344     SubLabel("ICONS", fg)335345     RowLine(fg) {336346         Name("Site icons", fg)

view file

app/src/main/java/com/peachpasswords/ui/settings/SettingsViewModel.kt+1 −0
@@ -285,6 +285,7 @@285285     fun setAccent(name: String) = mutate { it.copy(accentColor = name) }286286     fun setReduceMotion(on: Boolean) = mutate { it.copy(reduceMotion = on) }287287     fun setTextSize(size: String) = mutate { it.copy(textSize = size) }288+    fun setUiScale(scale: String) = mutate { it.copy(uiScale = scale) }288289     fun setFaviconsEnabled(on: Boolean) = mutate { it.copy(faviconsEnabled = on) }289290     fun setBreachMonitoring(on: Boolean) = mutate { it.copy(breachMonitoring = on) }290291     fun setBiometrics(on: Boolean) = mutate { it.copy(biometricsEnabled = on) }

view file

app/src/main/java/com/peachpasswords/vault/VaultSettings.kt+2 −0
@@ -10,6 +10,8 @@1010     val reduceMotion: Boolean = false,1111     /** Browser-parity text size: "small" | "medium" | "large". */1212     val textSize: String = "medium",13+    /** Large-screen layout sizing: "compact" | "auto" | "large". */14+    val uiScale: String = "auto",1315     /** U1 site icons in lists; off (default) keeps the cleaner initials-only look. */1416     val faviconsEnabled: Boolean = false,1517     val biometricsEnabled: Boolean = false,

view file

app/src/test/java/com/peachpasswords/MainActivityTest.kt+23 −0
@@ -0,0 +1,23 @@1+package com.peachpasswords2+3+import androidx.compose.ui.unit.dp4+import org.junit.Assert.assertEquals5+import org.junit.Test6+7+class MainActivityTest {8+9+    @Test fun auto_scaling_keeps_phone_and_foldable_behavior() {10+        assertEquals(1f, wideScreenScaleFor("auto", 411.dp, 800.dp), 0f)11+        assertEquals(1.35f, wideScreenScaleFor("auto", 673.dp, 841.dp), 0f)12+    }13+14+    @Test fun auto_scaling_does_not_inflate_tablet_controls() {15+        assertEquals(1f, wideScreenScaleFor("auto", 800.dp, 1280.dp), 0f)16+        assertEquals(1f, wideScreenScaleFor("auto", 600.dp, 960.dp), 0f)17+    }18+19+    @Test fun scale_presets_override_auto_layout_choice() {20+        assertEquals(1f, wideScreenScaleFor("compact", 673.dp, 841.dp), 0f)21+        assertEquals(1.35f, wideScreenScaleFor("large", 800.dp, 1280.dp), 0f)22+    }23+}

view file