Skip to content

Commit 36c0883

Browse files
committed
fix: album art on Wear OS
Media Session items now contain a smaller 512x512 artwork bitmap that Wear OS can read
1 parent e7fd37a commit 36c0883

4 files changed

Lines changed: 53 additions & 8 deletions

File tree

app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@
164164
android:name=".logic.GramophoneAlbumArtProvider"
165165
android:authorities="${applicationId}.albumart"
166166
android:exported="true"
167-
android:permission="com.google.android.finsky.permission.GEARHEAD_SERVICE" />
167+
android:grantUriPermissions="true" />
168168

169169
<receiver android:name=".ui.LyricWidgetProvider"
170170
android:exported="false">

app/src/main/java/org/akanework/gramophone/logic/GramophonePlaybackService.kt

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ import androidx.media3.common.Rating
5959
import androidx.media3.common.Timeline
6060
import androidx.media3.common.TrackSelectionParameters
6161
import androidx.media3.common.Tracks
62+
import coil3.size.Precision
63+
import org.akanework.gramophone.BuildConfig
6264
import androidx.media3.common.util.BitmapLoader
6365
import androidx.media3.common.util.Log
6466
import androidx.media3.common.util.Util.isBitmapFactorySupportedMimeType
@@ -160,12 +162,50 @@ class GramophonePlaybackService : MediaLibraryService(), MediaSessionService.Lis
160162
val endedWorkaroundPlayer
161163
get() = internalPlayer
162164

165+
private val pendingArtworkUrls = hashSetOf<String>()
166+
163167
private lateinit var libraryTreeLoader: LibraryTreeLoader
164168

165169
private fun convertMetadata(metadata: MediaMetadata): MediaMetadata {
166170
val artworkUri = metadata.artworkUri ?: return metadata
167171
val providerUri = ArtResolver.toProviderUri(artworkUri) ?: return metadata
168-
return metadata.buildUpon().setArtworkUri(providerUri).build()
172+
val builder = metadata.buildUpon().setArtworkUri(providerUri)
173+
if (metadata.artworkData != null) {
174+
return builder.build()
175+
}
176+
val cacheKey = artworkUri.toString()
177+
synchronized(pendingArtworkUrls) {
178+
if (pendingArtworkUrls.add(cacheKey)) {
179+
scope.launch {
180+
try {
181+
val bytes = ArtUtils.getResizedArtworkBytes(this@GramophonePlaybackService, artworkUri, 512)
182+
if (bytes != null) {
183+
withContext(Dispatchers.Main) {
184+
val player = endedWorkaroundPlayer
185+
if (player != null) {
186+
val currentIndex = player.currentMediaItemIndex
187+
val currentItem = player.getCurrentMediaItem()
188+
if (currentItem != null && currentItem.mediaMetadata.artworkUri == artworkUri) {
189+
val newMetadata = currentItem.mediaMetadata.buildUpon()
190+
.setArtworkData(bytes, MediaMetadata.PICTURE_TYPE_FRONT_COVER)
191+
.build()
192+
val newMediaItem = currentItem.buildUpon()
193+
.setMediaMetadata(newMetadata)
194+
.build()
195+
player.replaceMediaItem(currentIndex, newMediaItem)
196+
}
197+
}
198+
}
199+
}
200+
} finally {
201+
synchronized(pendingArtworkUrls) {
202+
pendingArtworkUrls.remove(cacheKey)
203+
}
204+
}
205+
}
206+
}
207+
}
208+
return builder.build()
169209
}
170210

171211
private fun convertItem(item: MediaItem): MediaItem {
@@ -458,6 +498,7 @@ class GramophonePlaybackService : MediaLibraryService(), MediaSessionService.Lis
458498
.data(data)
459499
.memoryCacheKey(data.hashCode().toString())
460500
.size(limit, limit)
501+
.precision(Precision.INEXACT)
461502
.allowHardware(false)
462503
.target(
463504
onStart = { _ ->
@@ -495,6 +536,7 @@ class GramophonePlaybackService : MediaLibraryService(), MediaSessionService.Lis
495536
ImageRequest.Builder(this@GramophonePlaybackService)
496537
.data(uri)
497538
.size(limit, limit)
539+
.precision(Precision.INEXACT)
498540
.allowHardware(false)
499541
.target(
500542
onStart = { _ ->

app/src/main/java/org/akanework/gramophone/logic/utils/ArtResolver.kt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ object ArtResolver {
9292
val filePath = path
9393
val parentPath = File(filePath).parent
9494
val list = mutableListOf<ArtResource>()
95-
if (size <= 320) {
95+
if (size <= 512) {
9696
list.add(ArtResource.SongMediaStore(songId))
9797
if (parentPath != null) {
9898
list.add(ArtResource.AlbumFolder(parentPath))
@@ -110,7 +110,7 @@ object ArtResolver {
110110
scheme == "gramophoneAlbumCover" -> {
111111
val albumId = authority
112112
val folderPath = path
113-
if (size <= 320) {
113+
if (size <= 512) {
114114
listOf(
115115
ArtResource.AlbumMediaStore(albumId),
116116
ArtResource.AlbumFolder(folderPath)
@@ -135,7 +135,7 @@ object ArtResolver {
135135
if (type == "song") {
136136
val parentPath = File(realPath).parent
137137
val list = mutableListOf<ArtResource>()
138-
if (size <= 320) {
138+
if (size <= 512) {
139139
list.add(ArtResource.SongMediaStore(id))
140140
if (parentPath != null) {
141141
list.add(ArtResource.AlbumFolder(parentPath))
@@ -150,7 +150,7 @@ object ArtResolver {
150150
}
151151
list
152152
} else if (type == "album") {
153-
if (size <= 320) {
153+
if (size <= 512) {
154154
listOf(
155155
ArtResource.AlbumMediaStore(id),
156156
ArtResource.AlbumFolder(realPath)
@@ -316,6 +316,9 @@ object ArtResolver {
316316
* Returns `null` if the URI is not one of the custom schemes.
317317
*/
318318
fun toProviderUri(uri: Uri): Uri? {
319+
if (uri.scheme == ContentResolver.SCHEME_CONTENT && uri.authority == PROVIDER_AUTHORITY) {
320+
return uri
321+
}
319322
return when (uri.scheme) {
320323
"gramophoneSongCover" -> buildProviderUri(
321324
"song",

app/src/main/java/org/akanework/gramophone/logic/utils/CoilArtPipeline.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ object CoilArtPipeline {
2929
fun Options.getArtworkBucketSize(): Int {
3030
val requestWidth = size.width.pxOrElse { 0 }
3131
val requestHeight = size.height.pxOrElse { 0 }
32-
return if (requestWidth > 320 || requestHeight > 320) 1024 else 320
32+
return if (requestWidth > 512 || requestHeight > 512) 1024 else 512
3333
}
3434

3535
class ResolutionInterceptor : Interceptor {
@@ -52,7 +52,7 @@ object CoilArtPipeline {
5252
val size = chain.size
5353
val androidUri = coilUri.toAndroidUri()
5454
// Use a stable bucket size for candidates to improve cache hits
55-
val bucketSize = if (size.width.pxOrElse { 0 } > 320 || size.height.pxOrElse { 0 } > 320) 1024 else 320
55+
val bucketSize = if (size.width.pxOrElse { 0 } > 512 || size.height.pxOrElse { 0 } > 512) 1024 else 512
5656
val candidates = ArtResolver.getResolutionList(androidUri, bucketSize)
5757

5858
Log.d(TAG, "Intercepted artwork URI: $coilUri, bucketSize: $bucketSize, candidates: ${candidates.size}")

0 commit comments

Comments
 (0)