From b0d7ff393d7a2dca2d1705228c906b03cbd9b4ab Mon Sep 17 00:00:00 2001 From: Antoine POPINEAU Date: Wed, 8 Jul 2020 12:46:52 +0200 Subject: [PATCH] Changed track metadata reporting method so it could work similarly across devices (notification, ambient display, lockscreen, watches, ...) (#55). --- .../otter/playback/MediaControlsManager.kt | 44 ++++++++++++------- .../apognu/otter/playback/PlayerService.kt | 39 ++++++++++++++-- 2 files changed, 64 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/com/github/apognu/otter/playback/MediaControlsManager.kt b/app/src/main/java/com/github/apognu/otter/playback/MediaControlsManager.kt index 7656f6c..38a5bcc 100644 --- a/app/src/main/java/com/github/apognu/otter/playback/MediaControlsManager.kt +++ b/app/src/main/java/com/github/apognu/otter/playback/MediaControlsManager.kt @@ -18,7 +18,9 @@ import com.github.apognu.otter.utils.* import com.squareup.picasso.Picasso import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers.Default +import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking class MediaControlsManager(val context: Service, private val scope: CoroutineScope, private val mediaSession: MediaSessionCompat) { companion object { @@ -30,6 +32,27 @@ class MediaControlsManager(val context: Service, private val scope: CoroutineSco private var notification: Notification? = null + fun buildTrackMetadata(track: Track?): MediaMetadataCompat { + track?.let { + val coverUrl = maybeNormalizeUrl(track.album.cover.original) + + return MediaMetadataCompat.Builder().apply { + putString(MediaMetadataCompat.METADATA_KEY_TITLE, track.title) + putString(MediaMetadataCompat.METADATA_KEY_ARTIST, track.artist.name) + putLong(MediaMetadata.METADATA_KEY_DURATION, (track.bestUpload()?.duration?.toLong() ?: 0L) * 1000) + + try { + runBlocking(IO) { + this@apply.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, Picasso.get().load(coverUrl).get()) + } + } catch (e: Exception) { + } + }.build() + } + + return MediaMetadataCompat.Builder().build() + } + fun updateNotification(track: Track?, playing: Boolean) { if (notification == null && !playing) return @@ -44,17 +67,6 @@ class MediaControlsManager(val context: Service, private val scope: CoroutineSco val openPendingIntent = PendingIntent.getActivity(context, 0, openIntent, 0) val coverUrl = maybeNormalizeUrl(track.album.cover.original) - val cover = coverUrl?.run { Picasso.get().load(coverUrl) } - - mediaSession.setMetadata(MediaMetadataCompat.Builder().apply { - putString(MediaMetadata.METADATA_KEY_ARTIST, track.artist.name) - putString(MediaMetadata.METADATA_KEY_TITLE, track.title) - putLong(MediaMetadata.METADATA_KEY_DURATION, (track.bestUpload()?.duration?.toLong() ?: 0L) * 1000) - - cover?.let { - try { putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, it.get()) } catch (_: Exception) {} - } - }.build()) notification = NotificationCompat.Builder( context, @@ -69,11 +81,13 @@ class MediaControlsManager(val context: Service, private val scope: CoroutineSco ) .setSmallIcon(R.drawable.ottershape) .run { - if (cover != null) { - try { setLargeIcon(cover.get()) } catch (_: Exception) {} + coverUrl?.let { + try { setLargeIcon(Picasso.get().load(coverUrl).get()) } catch (_: Exception) {} - this - } else this + return@run this + } + + this } .setContentTitle(track.title) .setContentText(track.artist.name) diff --git a/app/src/main/java/com/github/apognu/otter/playback/PlayerService.kt b/app/src/main/java/com/github/apognu/otter/playback/PlayerService.kt index dc7f841..dacfd9f 100644 --- a/app/src/main/java/com/github/apognu/otter/playback/PlayerService.kt +++ b/app/src/main/java/com/github/apognu/otter/playback/PlayerService.kt @@ -9,15 +9,15 @@ import android.media.AudioAttributes import android.media.AudioFocusRequest import android.media.AudioManager import android.os.Build +import android.os.Bundle import android.os.IBinder +import android.os.ResultReceiver import android.support.v4.media.session.MediaSessionCompat +import android.support.v4.media.session.PlaybackStateCompat import android.view.KeyEvent import com.github.apognu.otter.R import com.github.apognu.otter.utils.* -import com.google.android.exoplayer2.C -import com.google.android.exoplayer2.ExoPlaybackException -import com.google.android.exoplayer2.Player -import com.google.android.exoplayer2.SimpleExoPlayer +import com.google.android.exoplayer2.* import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector import com.google.android.exoplayer2.source.TrackGroupArray import com.google.android.exoplayer2.trackselection.TrackSelectionArray @@ -84,6 +84,13 @@ class PlayerService : Service() { mediaSession = MediaSessionCompat(this, applicationContext.packageName).apply { isActive = true + setPlaybackState(PlaybackStateCompat.Builder() + .setActions( + PlaybackStateCompat.ACTION_PLAY_PAUSE or + PlaybackStateCompat.ACTION_SEEK_TO or + PlaybackStateCompat.ACTION_SKIP_TO_NEXT or + PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS + ).build()) } mediaControlsManager = MediaControlsManager(this, scope, mediaSession) @@ -97,6 +104,30 @@ class PlayerService : Service() { MediaSessionConnector(mediaSession).also { it.setPlayer(this) + it.setMediaMetadataProvider { + mediaControlsManager.buildTrackMetadata(queue.current()) + } + + it.setQueueNavigator(object : MediaSessionConnector.QueueNavigator { + override fun onSkipToQueueItem(player: Player, controlDispatcher: ControlDispatcher, id: Long) {} + + override fun onCurrentWindowIndexChanged(player: Player) {} + + override fun onCommand(player: Player, controlDispatcher: ControlDispatcher, command: String, extras: Bundle?, cb: ResultReceiver?) = true + + override fun getSupportedQueueNavigatorActions(player: Player): Long { + return PlaybackStateCompat.ACTION_PLAY_PAUSE or PlaybackStateCompat.ACTION_SKIP_TO_NEXT or PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS + } + + override fun onSkipToNext(player: Player, controlDispatcher: ControlDispatcher) {} + + override fun getActiveQueueItemId(player: Player?) = 0L + + override fun onSkipToPrevious(player: Player, controlDispatcher: ControlDispatcher) {} + + override fun onTimelineChanged(player: Player) {} + }) + it.setMediaButtonEventHandler { player, _, mediaButtonEvent -> mediaButtonEvent.extras?.getParcelable(Intent.EXTRA_KEY_EVENT)?.let { key -> if (key.action == KeyEvent.ACTION_UP) {