playback: split up mediasession interface and holder
This commit is contained in:
parent
f1e1152e21
commit
916c3c46df
4 changed files with 218 additions and 190 deletions
|
@ -153,6 +153,9 @@ dependencies {
|
||||||
// Tasker integration
|
// Tasker integration
|
||||||
implementation 'com.joaomgcd:taskerpluginlibrary:0.4.10'
|
implementation 'com.joaomgcd:taskerpluginlibrary:0.4.10'
|
||||||
|
|
||||||
|
// Fuzzy search
|
||||||
|
implementation 'org.apache.commons:commons-text:1.9'
|
||||||
|
|
||||||
// Testing
|
// Testing
|
||||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
|
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
|
||||||
testImplementation "junit:junit:4.13.2"
|
testImplementation "junit:junit:4.13.2"
|
||||||
|
|
|
@ -108,16 +108,6 @@ class AuxioService :
|
||||||
musicFragment.search(query, result)
|
musicFragment.search(query, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
|
||||||
override fun onSubscribe(id: String?, option: Bundle?) {
|
|
||||||
super.onSubscribe(id, option)
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
|
||||||
override fun onUnsubscribe(id: String?) {
|
|
||||||
super.onUnsubscribe(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun updateForeground(change: ForegroundListener.Change) {
|
override fun updateForeground(change: ForegroundListener.Change) {
|
||||||
val mediaNotification = playbackFragment.notification
|
val mediaNotification = playbackFragment.notification
|
||||||
if (mediaNotification != null) {
|
if (mediaNotification != null) {
|
||||||
|
|
|
@ -20,10 +20,8 @@ package org.oxycblt.auxio.playback.system
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.net.Uri
|
import android.media.session.MediaSession
|
||||||
import android.os.Bundle
|
|
||||||
import android.support.v4.media.MediaDescriptionCompat
|
import android.support.v4.media.MediaDescriptionCompat
|
||||||
import android.support.v4.media.MediaMetadataCompat
|
import android.support.v4.media.MediaMetadataCompat
|
||||||
import android.support.v4.media.session.MediaSessionCompat
|
import android.support.v4.media.session.MediaSessionCompat
|
||||||
|
@ -39,25 +37,17 @@ import org.oxycblt.auxio.IntegerTable
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.image.BitmapProvider
|
import org.oxycblt.auxio.image.BitmapProvider
|
||||||
import org.oxycblt.auxio.image.ImageSettings
|
import org.oxycblt.auxio.image.ImageSettings
|
||||||
import org.oxycblt.auxio.music.Album
|
|
||||||
import org.oxycblt.auxio.music.Artist
|
|
||||||
import org.oxycblt.auxio.music.Genre
|
|
||||||
import org.oxycblt.auxio.music.Music
|
|
||||||
import org.oxycblt.auxio.music.MusicParent
|
import org.oxycblt.auxio.music.MusicParent
|
||||||
import org.oxycblt.auxio.music.MusicRepository
|
|
||||||
import org.oxycblt.auxio.music.Playlist
|
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.music.resolveNames
|
import org.oxycblt.auxio.music.resolveNames
|
||||||
import org.oxycblt.auxio.music.service.MediaSessionUID
|
|
||||||
import org.oxycblt.auxio.playback.ActionMode
|
import org.oxycblt.auxio.playback.ActionMode
|
||||||
import org.oxycblt.auxio.playback.PlaybackSettings
|
import org.oxycblt.auxio.playback.PlaybackSettings
|
||||||
|
import org.oxycblt.auxio.playback.service.MediaSessionInterface
|
||||||
import org.oxycblt.auxio.playback.service.PlaybackActions
|
import org.oxycblt.auxio.playback.service.PlaybackActions
|
||||||
import org.oxycblt.auxio.playback.state.PlaybackCommand
|
|
||||||
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
||||||
import org.oxycblt.auxio.playback.state.Progression
|
import org.oxycblt.auxio.playback.state.Progression
|
||||||
import org.oxycblt.auxio.playback.state.QueueChange
|
import org.oxycblt.auxio.playback.state.QueueChange
|
||||||
import org.oxycblt.auxio.playback.state.RepeatMode
|
import org.oxycblt.auxio.playback.state.RepeatMode
|
||||||
import org.oxycblt.auxio.playback.state.ShuffleMode
|
|
||||||
import org.oxycblt.auxio.util.logD
|
import org.oxycblt.auxio.util.logD
|
||||||
import org.oxycblt.auxio.util.newBroadcastPendingIntent
|
import org.oxycblt.auxio.util.newBroadcastPendingIntent
|
||||||
import org.oxycblt.auxio.util.newMainPendingIntent
|
import org.oxycblt.auxio.util.newMainPendingIntent
|
||||||
|
@ -71,10 +61,9 @@ import org.oxycblt.auxio.util.newMainPendingIntent
|
||||||
class MediaSessionHolder
|
class MediaSessionHolder
|
||||||
private constructor(
|
private constructor(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
|
private val sessionInterface: MediaSessionInterface,
|
||||||
private val playbackManager: PlaybackStateManager,
|
private val playbackManager: PlaybackStateManager,
|
||||||
private val playbackSettings: PlaybackSettings,
|
private val playbackSettings: PlaybackSettings,
|
||||||
private val commandFactory: PlaybackCommand.Factory,
|
|
||||||
private val musicRepository: MusicRepository,
|
|
||||||
private val bitmapProvider: BitmapProvider,
|
private val bitmapProvider: BitmapProvider,
|
||||||
private val imageSettings: ImageSettings
|
private val imageSettings: ImageSettings
|
||||||
) :
|
) :
|
||||||
|
@ -86,29 +75,23 @@ private constructor(
|
||||||
class Factory
|
class Factory
|
||||||
@Inject
|
@Inject
|
||||||
constructor(
|
constructor(
|
||||||
|
private val sessionInterface: MediaSessionInterface,
|
||||||
private val playbackManager: PlaybackStateManager,
|
private val playbackManager: PlaybackStateManager,
|
||||||
private val playbackSettings: PlaybackSettings,
|
private val playbackSettings: PlaybackSettings,
|
||||||
private val commandFactory: PlaybackCommand.Factory,
|
|
||||||
private val musicRepository: MusicRepository,
|
|
||||||
private val bitmapProvider: BitmapProvider,
|
private val bitmapProvider: BitmapProvider,
|
||||||
private val imageSettings: ImageSettings
|
private val imageSettings: ImageSettings,
|
||||||
) {
|
) {
|
||||||
fun create(context: Context) =
|
fun create(context: Context) =
|
||||||
MediaSessionHolder(
|
MediaSessionHolder(
|
||||||
context,
|
context,
|
||||||
|
sessionInterface,
|
||||||
playbackManager,
|
playbackManager,
|
||||||
playbackSettings,
|
playbackSettings,
|
||||||
commandFactory,
|
|
||||||
musicRepository,
|
|
||||||
bitmapProvider,
|
bitmapProvider,
|
||||||
imageSettings)
|
imageSettings)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val mediaSession =
|
private val mediaSession = MediaSessionCompat(context, context.packageName)
|
||||||
MediaSessionCompat(context, context.packageName).apply {
|
|
||||||
isActive = true
|
|
||||||
setQueueTitle(context.getString(R.string.lbl_queue))
|
|
||||||
}
|
|
||||||
val token: MediaSessionCompat.Token
|
val token: MediaSessionCompat.Token
|
||||||
get() = mediaSession.sessionToken
|
get() = mediaSession.sessionToken
|
||||||
|
|
||||||
|
@ -119,6 +102,11 @@ private constructor(
|
||||||
private var foregroundListener: ForegroundListener? = null
|
private var foregroundListener: ForegroundListener? = null
|
||||||
|
|
||||||
fun attach(foregroundListener: ForegroundListener) {
|
fun attach(foregroundListener: ForegroundListener) {
|
||||||
|
mediaSession.apply {
|
||||||
|
isActive = true
|
||||||
|
setQueueTitle(context.getString(R.string.lbl_queue))
|
||||||
|
setCallback(sessionInterface)
|
||||||
|
}
|
||||||
this.foregroundListener = foregroundListener
|
this.foregroundListener = foregroundListener
|
||||||
playbackManager.addListener(this)
|
playbackManager.addListener(this)
|
||||||
playbackSettings.registerListener(this)
|
playbackSettings.registerListener(this)
|
||||||
|
@ -218,116 +206,6 @@ private constructor(
|
||||||
|
|
||||||
// --- MEDIASESSION OVERRIDES ---
|
// --- MEDIASESSION OVERRIDES ---
|
||||||
|
|
||||||
override fun onPlayFromMediaId(mediaId: String?, extras: Bundle?) {
|
|
||||||
super.onPlayFromMediaId(mediaId, extras)
|
|
||||||
val uid = MediaSessionUID.fromString(mediaId ?: return) ?: return
|
|
||||||
val command = expandIntoCommand(uid)
|
|
||||||
requireNotNull(command) { "Invalid playback configuration" }
|
|
||||||
playbackManager.play(command)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPlayFromUri(uri: Uri?, extras: Bundle?) {
|
|
||||||
super.onPlayFromUri(uri, extras)
|
|
||||||
// STUB
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPlayFromSearch(query: String?, extras: Bundle?) {
|
|
||||||
super.onPlayFromSearch(query, extras)
|
|
||||||
// STUB: Unimplemented, no search engine
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAddQueueItem(description: MediaDescriptionCompat) {
|
|
||||||
super.onAddQueueItem(description)
|
|
||||||
val deviceLibrary = musicRepository.deviceLibrary ?: return
|
|
||||||
val uid = MediaSessionUID.fromString(description.mediaId ?: return) ?: return
|
|
||||||
val song =
|
|
||||||
when (uid) {
|
|
||||||
is MediaSessionUID.SingleItem -> deviceLibrary.findSong(uid.uid)
|
|
||||||
is MediaSessionUID.ChildItem -> deviceLibrary.findSong(uid.childUid)
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
?: return
|
|
||||||
playbackManager.addToQueue(song)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onRemoveQueueItem(description: MediaDescriptionCompat) {
|
|
||||||
super.onRemoveQueueItem(description)
|
|
||||||
val deviceLibrary = musicRepository.deviceLibrary ?: return
|
|
||||||
val uid = MediaSessionUID.fromString(description.mediaId ?: return) ?: return
|
|
||||||
val song =
|
|
||||||
when (uid) {
|
|
||||||
is MediaSessionUID.SingleItem -> deviceLibrary.findSong(uid.uid)
|
|
||||||
is MediaSessionUID.ChildItem -> deviceLibrary.findSong(uid.childUid)
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
?: return
|
|
||||||
val queueIndex = playbackManager.queue.indexOf(song)
|
|
||||||
if (queueIndex > -1) {
|
|
||||||
playbackManager.removeQueueItem(queueIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPlay() {
|
|
||||||
playbackManager.playing(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPause() {
|
|
||||||
playbackManager.playing(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSkipToNext() {
|
|
||||||
playbackManager.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSkipToPrevious() {
|
|
||||||
playbackManager.prev()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSeekTo(position: Long) {
|
|
||||||
playbackManager.seekTo(position)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFastForward() {
|
|
||||||
playbackManager.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onRewind() {
|
|
||||||
playbackManager.seekTo(0)
|
|
||||||
playbackManager.playing(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSetRepeatMode(repeatMode: Int) {
|
|
||||||
playbackManager.repeatMode(
|
|
||||||
when (repeatMode) {
|
|
||||||
PlaybackStateCompat.REPEAT_MODE_ALL -> RepeatMode.ALL
|
|
||||||
PlaybackStateCompat.REPEAT_MODE_GROUP -> RepeatMode.ALL
|
|
||||||
PlaybackStateCompat.REPEAT_MODE_ONE -> RepeatMode.TRACK
|
|
||||||
else -> RepeatMode.NONE
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSetShuffleMode(shuffleMode: Int) {
|
|
||||||
playbackManager.shuffled(
|
|
||||||
shuffleMode == PlaybackStateCompat.SHUFFLE_MODE_ALL ||
|
|
||||||
shuffleMode == PlaybackStateCompat.SHUFFLE_MODE_GROUP)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSkipToQueueItem(id: Long) {
|
|
||||||
playbackManager.goto(id.toInt())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCustomAction(action: String, extras: Bundle?) {
|
|
||||||
super.onCustomAction(action, extras)
|
|
||||||
// Service already handles intents from the old notification actions, easier to
|
|
||||||
// plug into that system.
|
|
||||||
context.sendBroadcast(Intent(action))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStop() {
|
|
||||||
// Get the service to shut down with the ACTION_EXIT intent
|
|
||||||
context.sendBroadcast(Intent(PlaybackActions.ACTION_EXIT))
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- INTERNAL ---
|
// --- INTERNAL ---
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -435,40 +313,6 @@ private constructor(
|
||||||
mediaSession.setQueue(queueItems)
|
mediaSession.setQueue(queueItems)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun expandIntoCommand(uid: MediaSessionUID): PlaybackCommand? {
|
|
||||||
val music: Music
|
|
||||||
var parent: MusicParent? = null
|
|
||||||
when (uid) {
|
|
||||||
is MediaSessionUID.SingleItem -> {
|
|
||||||
music = musicRepository.find(uid.uid) ?: return null
|
|
||||||
}
|
|
||||||
is MediaSessionUID.ChildItem -> {
|
|
||||||
music = musicRepository.find(uid.childUid) ?: return null
|
|
||||||
parent = musicRepository.find(uid.parentUid) as? MusicParent ?: return null
|
|
||||||
}
|
|
||||||
else -> return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return when (music) {
|
|
||||||
is Song -> inferSongFromParent(music, parent)
|
|
||||||
is Album -> commandFactory.album(music, ShuffleMode.OFF)
|
|
||||||
is Artist -> commandFactory.artist(music, ShuffleMode.OFF)
|
|
||||||
is Genre -> commandFactory.genre(music, ShuffleMode.OFF)
|
|
||||||
is Playlist -> commandFactory.playlist(music, ShuffleMode.OFF)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun inferSongFromParent(music: Song, parent: MusicParent?) =
|
|
||||||
when (parent) {
|
|
||||||
is Album -> commandFactory.songFromAlbum(music, ShuffleMode.IMPLICIT)
|
|
||||||
is Artist -> commandFactory.songFromArtist(music, parent, ShuffleMode.IMPLICIT)
|
|
||||||
?: commandFactory.songFromArtist(music, music.artists[0], ShuffleMode.IMPLICIT)
|
|
||||||
is Genre -> commandFactory.songFromGenre(music, parent, ShuffleMode.IMPLICIT)
|
|
||||||
?: commandFactory.songFromGenre(music, music.genres[0], ShuffleMode.IMPLICIT)
|
|
||||||
is Playlist -> commandFactory.songFromPlaylist(music, parent, ShuffleMode.IMPLICIT)
|
|
||||||
null -> commandFactory.songFromAll(music, ShuffleMode.IMPLICIT)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Invalidate the current [MediaSessionCompat]'s [PlaybackStateCompat]. */
|
/** Invalidate the current [MediaSessionCompat]'s [PlaybackStateCompat]. */
|
||||||
private fun invalidateSessionState() {
|
private fun invalidateSessionState() {
|
||||||
logD("Updating media session playback state")
|
logD("Updating media session playback state")
|
||||||
|
@ -477,7 +321,7 @@ private constructor(
|
||||||
// InternalPlayer.State handles position/state information.
|
// InternalPlayer.State handles position/state information.
|
||||||
playbackManager.progression
|
playbackManager.progression
|
||||||
.intoPlaybackState(PlaybackStateCompat.Builder())
|
.intoPlaybackState(PlaybackStateCompat.Builder())
|
||||||
.setActions(ACTIONS)
|
.setActions(MediaSessionInterface.ACTIONS)
|
||||||
// Active queue ID corresponds to the indices we populated prior, use them here.
|
// Active queue ID corresponds to the indices we populated prior, use them here.
|
||||||
.setActiveQueueItemId(playbackManager.index.toLong())
|
.setActiveQueueItemId(playbackManager.index.toLong())
|
||||||
|
|
||||||
|
@ -543,17 +387,6 @@ private constructor(
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val emptyMetadata = MediaMetadataCompat.Builder().build()
|
private val emptyMetadata = MediaMetadataCompat.Builder().build()
|
||||||
private const val ACTIONS =
|
|
||||||
PlaybackStateCompat.ACTION_PLAY or
|
|
||||||
PlaybackStateCompat.ACTION_PAUSE or
|
|
||||||
PlaybackStateCompat.ACTION_PLAY_PAUSE or
|
|
||||||
PlaybackStateCompat.ACTION_SET_REPEAT_MODE or
|
|
||||||
PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE or
|
|
||||||
PlaybackStateCompat.ACTION_SKIP_TO_NEXT or
|
|
||||||
PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS or
|
|
||||||
PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM or
|
|
||||||
PlaybackStateCompat.ACTION_SEEK_TO or
|
|
||||||
PlaybackStateCompat.ACTION_STOP
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,202 @@
|
||||||
|
package org.oxycblt.auxio.playback.service
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.support.v4.media.MediaDescriptionCompat
|
||||||
|
import android.support.v4.media.session.MediaSessionCompat
|
||||||
|
import android.support.v4.media.session.PlaybackStateCompat
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import org.oxycblt.auxio.music.Album
|
||||||
|
import org.oxycblt.auxio.music.Artist
|
||||||
|
import org.oxycblt.auxio.music.Genre
|
||||||
|
import org.oxycblt.auxio.music.Music
|
||||||
|
import org.oxycblt.auxio.music.MusicParent
|
||||||
|
import org.oxycblt.auxio.music.MusicRepository
|
||||||
|
import org.oxycblt.auxio.music.Playlist
|
||||||
|
import org.oxycblt.auxio.music.Song
|
||||||
|
import org.oxycblt.auxio.music.service.MediaSessionUID
|
||||||
|
import org.oxycblt.auxio.playback.state.PlaybackCommand
|
||||||
|
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
||||||
|
import org.oxycblt.auxio.playback.state.RepeatMode
|
||||||
|
import org.oxycblt.auxio.playback.state.ShuffleMode
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class MediaSessionInterface @Inject constructor(
|
||||||
|
@ApplicationContext private val context: Context,
|
||||||
|
private val playbackManager: PlaybackStateManager,
|
||||||
|
private val commandFactory: PlaybackCommand.Factory,
|
||||||
|
private val musicRepository: MusicRepository,
|
||||||
|
) : MediaSessionCompat.Callback() {
|
||||||
|
|
||||||
|
override fun onPrepareFromMediaId(mediaId: String?, extras: Bundle?) {
|
||||||
|
super.onPrepareFromMediaId(mediaId, extras)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPlayFromMediaId(mediaId: String?, extras: Bundle?) {
|
||||||
|
super.onPlayFromMediaId(mediaId, extras)
|
||||||
|
val uid = MediaSessionUID.fromString(mediaId ?: return) ?: return
|
||||||
|
val command = expandIntoCommand(uid)
|
||||||
|
requireNotNull(command) { "Invalid playback configuration" }
|
||||||
|
playbackManager.play(command)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPrepareFromSearch(query: String?, extras: Bundle?) {
|
||||||
|
super.onPrepareFromSearch(query, extras)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPlayFromSearch(query: String?, extras: Bundle?) {
|
||||||
|
super.onPlayFromSearch(query, extras)
|
||||||
|
// STUB: Unimplemented, no search engine
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPrepareFromUri(uri: Uri?, extras: Bundle?) {
|
||||||
|
super.onPrepareFromUri(uri, extras)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPlayFromUri(uri: Uri?, extras: Bundle?) {
|
||||||
|
super.onPlayFromUri(uri, extras)
|
||||||
|
// STUB
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAddQueueItem(description: MediaDescriptionCompat) {
|
||||||
|
super.onAddQueueItem(description)
|
||||||
|
val deviceLibrary = musicRepository.deviceLibrary ?: return
|
||||||
|
val uid = MediaSessionUID.fromString(description.mediaId ?: return) ?: return
|
||||||
|
val song =
|
||||||
|
when (uid) {
|
||||||
|
is MediaSessionUID.SingleItem -> deviceLibrary.findSong(uid.uid)
|
||||||
|
is MediaSessionUID.ChildItem -> deviceLibrary.findSong(uid.childUid)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
?: return
|
||||||
|
playbackManager.addToQueue(song)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRemoveQueueItem(description: MediaDescriptionCompat) {
|
||||||
|
super.onRemoveQueueItem(description)
|
||||||
|
val deviceLibrary = musicRepository.deviceLibrary ?: return
|
||||||
|
val uid = MediaSessionUID.fromString(description.mediaId ?: return) ?: return
|
||||||
|
val song =
|
||||||
|
when (uid) {
|
||||||
|
is MediaSessionUID.SingleItem -> deviceLibrary.findSong(uid.uid)
|
||||||
|
is MediaSessionUID.ChildItem -> deviceLibrary.findSong(uid.childUid)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
?: return
|
||||||
|
val queueIndex = playbackManager.queue.indexOf(song)
|
||||||
|
if (queueIndex > -1) {
|
||||||
|
playbackManager.removeQueueItem(queueIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPlay() {
|
||||||
|
playbackManager.playing(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
playbackManager.playing(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSkipToNext() {
|
||||||
|
playbackManager.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSkipToPrevious() {
|
||||||
|
playbackManager.prev()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSeekTo(position: Long) {
|
||||||
|
playbackManager.seekTo(position)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFastForward() {
|
||||||
|
playbackManager.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRewind() {
|
||||||
|
playbackManager.seekTo(0)
|
||||||
|
playbackManager.playing(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSetRepeatMode(repeatMode: Int) {
|
||||||
|
playbackManager.repeatMode(
|
||||||
|
when (repeatMode) {
|
||||||
|
PlaybackStateCompat.REPEAT_MODE_ALL -> RepeatMode.ALL
|
||||||
|
PlaybackStateCompat.REPEAT_MODE_GROUP -> RepeatMode.ALL
|
||||||
|
PlaybackStateCompat.REPEAT_MODE_ONE -> RepeatMode.TRACK
|
||||||
|
else -> RepeatMode.NONE
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSetShuffleMode(shuffleMode: Int) {
|
||||||
|
playbackManager.shuffled(
|
||||||
|
shuffleMode == PlaybackStateCompat.SHUFFLE_MODE_ALL ||
|
||||||
|
shuffleMode == PlaybackStateCompat.SHUFFLE_MODE_GROUP)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSkipToQueueItem(id: Long) {
|
||||||
|
playbackManager.goto(id.toInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCustomAction(action: String, extras: Bundle?) {
|
||||||
|
super.onCustomAction(action, extras)
|
||||||
|
// Service already handles intents from the old notification actions, easier to
|
||||||
|
// plug into that system.
|
||||||
|
context.sendBroadcast(Intent(action))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStop() {
|
||||||
|
// Get the service to shut down with the ACTION_EXIT intent
|
||||||
|
context.sendBroadcast(Intent(PlaybackActions.ACTION_EXIT))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun expandIntoCommand(uid: MediaSessionUID): PlaybackCommand? {
|
||||||
|
val music: Music
|
||||||
|
var parent: MusicParent? = null
|
||||||
|
when (uid) {
|
||||||
|
is MediaSessionUID.SingleItem -> {
|
||||||
|
music = musicRepository.find(uid.uid) ?: return null
|
||||||
|
}
|
||||||
|
is MediaSessionUID.ChildItem -> {
|
||||||
|
music = musicRepository.find(uid.childUid) ?: return null
|
||||||
|
parent = musicRepository.find(uid.parentUid) as? MusicParent ?: return null
|
||||||
|
}
|
||||||
|
else -> return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return when (music) {
|
||||||
|
is Song -> inferSongFromParent(music, parent)
|
||||||
|
is Album -> commandFactory.album(music, ShuffleMode.OFF)
|
||||||
|
is Artist -> commandFactory.artist(music, ShuffleMode.OFF)
|
||||||
|
is Genre -> commandFactory.genre(music, ShuffleMode.OFF)
|
||||||
|
is Playlist -> commandFactory.playlist(music, ShuffleMode.OFF)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun inferSongFromParent(music: Song, parent: MusicParent?) =
|
||||||
|
when (parent) {
|
||||||
|
is Album -> commandFactory.songFromAlbum(music, ShuffleMode.IMPLICIT)
|
||||||
|
is Artist -> commandFactory.songFromArtist(music, parent, ShuffleMode.IMPLICIT)
|
||||||
|
?: commandFactory.songFromArtist(music, music.artists[0], ShuffleMode.IMPLICIT)
|
||||||
|
is Genre -> commandFactory.songFromGenre(music, parent, ShuffleMode.IMPLICIT)
|
||||||
|
?: commandFactory.songFromGenre(music, music.genres[0], ShuffleMode.IMPLICIT)
|
||||||
|
is Playlist -> commandFactory.songFromPlaylist(music, parent, ShuffleMode.IMPLICIT)
|
||||||
|
null -> commandFactory.songFromAll(music, ShuffleMode.IMPLICIT)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val ACTIONS =
|
||||||
|
PlaybackStateCompat.ACTION_PLAY or
|
||||||
|
PlaybackStateCompat.ACTION_PAUSE or
|
||||||
|
PlaybackStateCompat.ACTION_PLAY_PAUSE or
|
||||||
|
PlaybackStateCompat.ACTION_SET_REPEAT_MODE or
|
||||||
|
PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE or
|
||||||
|
PlaybackStateCompat.ACTION_SKIP_TO_NEXT or
|
||||||
|
PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS or
|
||||||
|
PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM or
|
||||||
|
PlaybackStateCompat.ACTION_SEEK_TO or
|
||||||
|
PlaybackStateCompat.ACTION_STOP
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue