diff --git a/amethyst/src/main/AndroidManifest.xml b/amethyst/src/main/AndroidManifest.xml
index 91acb98039..467e9ac3d9 100644
--- a/amethyst/src/main/AndroidManifest.xml
+++ b/amethyst/src/main/AndroidManifest.xml
@@ -90,6 +90,11 @@
+
+
+
+
@@ -556,6 +561,25 @@
+
+
+
+
+
+
+
+
+
(null, packageName) {
+ value =
+ withContext(Dispatchers.IO) {
+ runCatching {
+ context.packageManager
+ .getApplicationIcon(packageName)
+ .toBitmap(ICON_PX, ICON_PX)
+ .asImageBitmap()
+ }.getOrNull()
+ }
+ }
+
+ val current = icon
+ if (current != null) {
+ Image(bitmap = current, contentDescription = null, modifier = Modifier.fillMaxSize())
+ } else {
+ MonogramBadge(label, size = 36, shape = RoundedCornerShape(10.dp))
+ }
+}
+
+private const val ICON_PX = 108
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/nowPlaying/AndroidNowPlayingAccess.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/nowPlaying/AndroidNowPlayingAccess.kt
new file mode 100644
index 0000000000..93bb929353
--- /dev/null
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/nowPlaying/AndroidNowPlayingAccess.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2025 Vitor Pamplona
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package com.vitorpamplona.amethyst.service.nowPlaying
+
+import android.content.Context
+import com.vitorpamplona.amethyst.commons.model.nip38UserStatuses.nowPlaying.NowPlayingAccess
+
+/** Notification access, which Android requires before it shares other apps' media sessions. */
+class AndroidNowPlayingAccess(
+ private val context: Context,
+) : NowPlayingAccess {
+ override fun hasAccess() = OtherAppsNowPlayingService.hasAccess(context)
+
+ override fun requestAccess() = OtherAppsNowPlayingService.openAccessSettings(context)
+}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/nowPlaying/InAppPlayback.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/nowPlaying/InAppPlayback.kt
new file mode 100644
index 0000000000..3515857edd
--- /dev/null
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/nowPlaying/InAppPlayback.kt
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2025 Vitor Pamplona
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package com.vitorpamplona.amethyst.service.nowPlaying
+
+import androidx.compose.runtime.Immutable
+import androidx.media3.common.C
+import androidx.media3.common.Player
+import com.vitorpamplona.amethyst.commons.model.nip38UserStatuses.nowPlaying.NowPlaying
+import com.vitorpamplona.amethyst.service.playback.composable.mediaitem.MediaItemCache
+import com.vitorpamplona.quartz.utils.TimeUtils
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.update
+import kotlin.math.abs
+
+/** What one of the app's players is audibly playing: its item's `nostr:` URI and where it ends. */
+@Immutable
+data class InAppPlayback(
+ val callbackUri: String,
+ val title: String?,
+ val artist: String?,
+ val endsAt: Long?,
+) {
+ /** Same item, ending within a few seconds: a position tick rather than a change. */
+ fun isSameMoment(other: InAppPlayback?): Boolean {
+ if (other == null || callbackUri != other.callbackUri || title != other.title || artist != other.artist) return false
+ val a = endsAt
+ val b = other.endsAt
+ return if (a == null || b == null) a == b else abs(a - b) <= NowPlaying.MOMENT_TOLERANCE_SECONDS
+ }
+}
+
+/**
+ * The players in this process that are audibly playing something with a Nostr event behind it,
+ * keyed by player. Fed by [InAppPlaybackListener]; read by the now-playing coordinator, which
+ * resolves the URI to a music track or podcast episode and ignores everything else.
+ */
+object InAppPlaybackRegistry {
+ private val playing = MutableStateFlow