diff --git a/app/build.gradle b/app/build.gradle
index 3d71ee0..1782dd0 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -68,6 +68,9 @@ android {
dependencies {
implementation 'androidx.core:core-ktx:1.9.0'
+ // Pinned rather than taken transitively from appcompat: enableEdgeToEdge() (used by both
+ // activities) landed in 1.8.0 and the edge-to-edge backport was refined for SDK 35 after it.
+ implementation 'androidx.activity:activity:1.9.3'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.10.0'
implementation 'androidx.recyclerview:recyclerview:1.3.2'
diff --git a/app/src/main/java/dev/konraditurbe/osmosis/ui/MainActivity.kt b/app/src/main/java/dev/konraditurbe/osmosis/ui/MainActivity.kt
index 1a1f674..99d5fd1 100644
--- a/app/src/main/java/dev/konraditurbe/osmosis/ui/MainActivity.kt
+++ b/app/src/main/java/dev/konraditurbe/osmosis/ui/MainActivity.kt
@@ -23,6 +23,7 @@ import androidx.appcompat.app.AppCompatActivity
import android.net.LinkProperties
import android.net.Network
import androidx.activity.OnBackPressedCallback
+import androidx.activity.enableEdgeToEdge
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
@@ -271,6 +272,12 @@ class MainActivity : AppCompatActivity(), OsmoScanner.Listener, GattClient.Liste
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ // Opt in explicitly instead of inheriting the targetSdk-35 default, so every supported release
+ // behaves the same way. Without it, API 29-34 keeps opaque system bars while 35+ goes
+ // edge-to-edge, which is two layouts to reason about and only one of them gets tested on the
+ // device in front of you. The bar icon polarity auto()-picks off the system dark mode, matching
+ // what @bool/osmo_light_system_bars does for the pre-35 theme.
+ enableEdgeToEdge()
setContentView(R.layout.activity_main)
// targetSdk 35+ forces edge-to-edge: android:statusBarColor/navigationBarColor in the theme are
// ignored and the window draws under the bars. Pad the root by the bar + cutout insets so the
diff --git a/app/src/main/java/dev/konraditurbe/osmosis/ui/MediaPreviewActivity.kt b/app/src/main/java/dev/konraditurbe/osmosis/ui/MediaPreviewActivity.kt
index 8c36eb2..cac504c 100644
--- a/app/src/main/java/dev/konraditurbe/osmosis/ui/MediaPreviewActivity.kt
+++ b/app/src/main/java/dev/konraditurbe/osmosis/ui/MediaPreviewActivity.kt
@@ -5,7 +5,8 @@ import android.content.Intent
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
-import android.os.Build
+import androidx.activity.SystemBarStyle
+import androidx.activity.enableEdgeToEdge
import android.os.Bundle
import android.os.Handler
import android.os.Looper
@@ -99,28 +100,22 @@ class MediaPreviewActivity : AppCompatActivity() {
private lateinit var scrubTime: TextView
private val scrubFrames by lazy { ScrubFrames { Log.i("Osmosis", it) } }
+ /** Newest status-bar/cutout inset, kept so the floating scrub bubble can clamp against it. */
+ private var topInsetPx = 0
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ // Full-screen media viewer: black bars with light icons on every supported release (the app's
+ // cream theme sets the opposite, which is unreadable over a dark preview). SystemBarStyle.dark
+ // is what makes this one call cover both platform routes — it paints the scrim on the API 29-34
+ // devices, where the bars are opaque and used to need window.statusBarColor, and goes fully
+ // transparent over the layout's black root from 35 on, where those setters are ignored.
+ enableEdgeToEdge(
+ statusBarStyle = SystemBarStyle.dark(android.graphics.Color.BLACK),
+ navigationBarStyle = SystemBarStyle.dark(android.graphics.Color.BLACK),
+ )
setContentView(R.layout.activity_preview)
- // Full-screen media viewer: light bar icons (the app's cream theme sets the opposite), so they
- // stay readable over the dark preview.
- //
- // The bars go black by two different routes depending on the platform, and BOTH are needed
- // because minSdk is 29. From targetSdk 35 the window is edge-to-edge, statusBarColor and
- // navigationBarColor are ignored, and the bars are transparent over the layout's black root.
- // On API 29-34 there is no edge-to-edge: the bars are opaque and painted from these setters,
- // so dropping them leaves the theme's cream showing behind white icons.
- if (Build.VERSION.SDK_INT < 35) {
- @Suppress("DEPRECATION")
- window.statusBarColor = android.graphics.Color.BLACK
- @Suppress("DEPRECATION")
- window.navigationBarColor = android.graphics.Color.BLACK
- }
- androidx.core.view.WindowCompat.getInsetsController(window, window.decorView).apply {
- isAppearanceLightStatusBars = false
- isAppearanceLightNavigationBars = false
- }
// The media itself is meant to run full-bleed under the bars; only the overlays get inset. The
// insets are ADDED to each overlay's own layout padding, and the base padding is captured once
// so re-dispatches (rotation, IME, bar show/hide) don't accumulate.
@@ -134,6 +129,7 @@ class MediaPreviewActivity : AppCompatActivity() {
androidx.core.view.WindowInsetsCompat.Type.systemBars() or
androidx.core.view.WindowInsetsCompat.Type.displayCutout()
)
+ topInsetPx = bars.top
for ((v, base) in basePadding) {
val top = if (v in topOverlays) bars.top else 0
val bottom = if (v in bottomOverlays) bars.bottom else 0
@@ -648,7 +644,10 @@ class MediaPreviewActivity : AppCompatActivity() {
val margin = 8 * resources.displayMetrics.density
val maxX = (previewRoot.width - w - margin).coerceAtLeast(margin)
scrubPreview.translationX = (thumbX - w / 2f).coerceIn(margin, maxX)
- scrubPreview.translationY = (bar[1] - root[1] - h - margin).coerceAtLeast(0f)
+ // Floor at the status-bar inset, not at 0: the root runs edge-to-edge, so a bubble pushed to
+ // the top of the window (short screen, tall bubble) would otherwise land under the clock.
+ scrubPreview.translationY =
+ (bar[1] - root[1] - h - margin).coerceAtLeast(topInsetPx.toFloat())
}
/** Once the thumb stops moving, pull the keyframe actually under it (debounced from the drag). */
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 66d3855..ac9675b 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -17,10 +17,13 @@
- @color/osmo_muted
- @color/osmo_track
-
+
- ?android:attr/colorBackground
- ?android:attr/colorBackground
- @bool/osmo_light_system_bars