Changed track metadata reporting method so it could work similarly across devices (notification, ambient display, lockscreen, watches, ...) (#55).
This commit is contained in:
parent
a3f74af076
commit
b0d7ff393d
|
@ -18,7 +18,9 @@ import com.github.apognu.otter.utils.*
|
||||||
import com.squareup.picasso.Picasso
|
import com.squareup.picasso.Picasso
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers.Default
|
import kotlinx.coroutines.Dispatchers.Default
|
||||||
|
import kotlinx.coroutines.Dispatchers.IO
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
class MediaControlsManager(val context: Service, private val scope: CoroutineScope, private val mediaSession: MediaSessionCompat) {
|
class MediaControlsManager(val context: Service, private val scope: CoroutineScope, private val mediaSession: MediaSessionCompat) {
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -30,6 +32,27 @@ class MediaControlsManager(val context: Service, private val scope: CoroutineSco
|
||||||
|
|
||||||
private var notification: Notification? = null
|
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) {
|
fun updateNotification(track: Track?, playing: Boolean) {
|
||||||
if (notification == null && !playing) return
|
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 openPendingIntent = PendingIntent.getActivity(context, 0, openIntent, 0)
|
||||||
|
|
||||||
val coverUrl = maybeNormalizeUrl(track.album.cover.original)
|
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(
|
notification = NotificationCompat.Builder(
|
||||||
context,
|
context,
|
||||||
|
@ -69,11 +81,13 @@ class MediaControlsManager(val context: Service, private val scope: CoroutineSco
|
||||||
)
|
)
|
||||||
.setSmallIcon(R.drawable.ottershape)
|
.setSmallIcon(R.drawable.ottershape)
|
||||||
.run {
|
.run {
|
||||||
if (cover != null) {
|
coverUrl?.let {
|
||||||
try { setLargeIcon(cover.get()) } catch (_: Exception) {}
|
try { setLargeIcon(Picasso.get().load(coverUrl).get()) } catch (_: Exception) {}
|
||||||
|
|
||||||
this
|
return@run this
|
||||||
} else this
|
}
|
||||||
|
|
||||||
|
this
|
||||||
}
|
}
|
||||||
.setContentTitle(track.title)
|
.setContentTitle(track.title)
|
||||||
.setContentText(track.artist.name)
|
.setContentText(track.artist.name)
|
||||||
|
|
|
@ -9,15 +9,15 @@ import android.media.AudioAttributes
|
||||||
import android.media.AudioFocusRequest
|
import android.media.AudioFocusRequest
|
||||||
import android.media.AudioManager
|
import android.media.AudioManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
|
import android.os.ResultReceiver
|
||||||
import android.support.v4.media.session.MediaSessionCompat
|
import android.support.v4.media.session.MediaSessionCompat
|
||||||
|
import android.support.v4.media.session.PlaybackStateCompat
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import com.github.apognu.otter.R
|
import com.github.apognu.otter.R
|
||||||
import com.github.apognu.otter.utils.*
|
import com.github.apognu.otter.utils.*
|
||||||
import com.google.android.exoplayer2.C
|
import com.google.android.exoplayer2.*
|
||||||
import com.google.android.exoplayer2.ExoPlaybackException
|
|
||||||
import com.google.android.exoplayer2.Player
|
|
||||||
import com.google.android.exoplayer2.SimpleExoPlayer
|
|
||||||
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector
|
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector
|
||||||
import com.google.android.exoplayer2.source.TrackGroupArray
|
import com.google.android.exoplayer2.source.TrackGroupArray
|
||||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray
|
import com.google.android.exoplayer2.trackselection.TrackSelectionArray
|
||||||
|
@ -84,6 +84,13 @@ class PlayerService : Service() {
|
||||||
|
|
||||||
mediaSession = MediaSessionCompat(this, applicationContext.packageName).apply {
|
mediaSession = MediaSessionCompat(this, applicationContext.packageName).apply {
|
||||||
isActive = true
|
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)
|
mediaControlsManager = MediaControlsManager(this, scope, mediaSession)
|
||||||
|
@ -97,6 +104,30 @@ class PlayerService : Service() {
|
||||||
|
|
||||||
MediaSessionConnector(mediaSession).also {
|
MediaSessionConnector(mediaSession).also {
|
||||||
it.setPlayer(this)
|
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 ->
|
it.setMediaButtonEventHandler { player, _, mediaButtonEvent ->
|
||||||
mediaButtonEvent.extras?.getParcelable<KeyEvent>(Intent.EXTRA_KEY_EVENT)?.let { key ->
|
mediaButtonEvent.extras?.getParcelable<KeyEvent>(Intent.EXTRA_KEY_EVENT)?.let { key ->
|
||||||
if (key.action == KeyEvent.ACTION_UP) {
|
if (key.action == KeyEvent.ACTION_UP) {
|
||||||
|
|
Loading…
Reference in New Issue