From ab28fb6323ad9f5bae6e592d9ef6bffbc693cc39 Mon Sep 17 00:00:00 2001 From: OxygenCobalt Date: Tue, 1 Jun 2021 10:16:30 -0600 Subject: [PATCH] playback: add event handling to mediasession Add event handling to the MediaSession. This completes the new PlaybackSessionConnector class and possibly addresses the issue raised in #20. --- .../auxio/playback/system/PlaybackService.kt | 72 ----------------- .../system/PlaybackSessionConnector.kt | 81 +++++++++++++++++-- app/src/main/res/values/styles.xml | 8 +- 3 files changed, 80 insertions(+), 81 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt index 4029812ce..8df6ca438 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt @@ -12,9 +12,7 @@ import android.media.AudioManager import android.os.Build import android.os.IBinder import android.os.PowerManager -import android.support.v4.media.MediaMetadataCompat import android.support.v4.media.session.MediaSessionCompat -import android.view.KeyEvent import com.google.android.exoplayer2.C import com.google.android.exoplayer2.ExoPlaybackException import com.google.android.exoplayer2.MediaItem @@ -35,7 +33,6 @@ import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.takeWhile import kotlinx.coroutines.launch -import org.oxycblt.auxio.coil.loadBitmap import org.oxycblt.auxio.logD import org.oxycblt.auxio.music.Parent import org.oxycblt.auxio.music.Song @@ -220,8 +217,6 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callbac player.setMediaItem(MediaItem.fromUri(song.id.toURI())) player.prepare() - pushMetadataToSession(song) - notification.setMetadata( this, song, settingsManager.colorizeNotif, ::startForegroundOrNotify ) @@ -354,27 +349,6 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callbac onSeek(playbackManager.position) } - /** - * Upload the song metadata to the [MediaSessionCompat], so that things such as album art - * show up on the lock screen. - */ - private fun pushMetadataToSession(song: Song) { - val builder = MediaMetadataCompat.Builder() - .putString(MediaMetadataCompat.METADATA_KEY_TITLE, song.name) - .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, song.name) - .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, song.album.artist.name) - .putString(MediaMetadataCompat.METADATA_KEY_AUTHOR, song.album.artist.name) - .putString(MediaMetadataCompat.METADATA_KEY_COMPOSER, song.album.artist.name) - .putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST, song.album.artist.name) - .putString(MediaMetadataCompat.METADATA_KEY_ALBUM, song.album.name) - .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, song.duration) - - loadBitmap(this, song) { bitmap -> - builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, bitmap) - mediaSession.setMetadata(builder.build()) - } - } - /** * Start polling the position on a coroutine. */ @@ -449,52 +423,6 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callbac } } - /** - * Handle a media button intent. - */ - private fun handleMediaButtonEvent(event: Intent): Boolean { - val item = event.getParcelableExtra(Intent.EXTRA_KEY_EVENT) - - if (item != null && item.action == KeyEvent.ACTION_DOWN) { - return when (item.keyCode) { - // Play/Pause if any of the keys are play/pause - KeyEvent.KEYCODE_MEDIA_PAUSE, KeyEvent.KEYCODE_MEDIA_PLAY, - KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, KeyEvent.KEYCODE_HEADSETHOOK -> { - playbackManager.setPlaying(!playbackManager.isPlaying) - true - } - - // Go to the next song is the key is next - KeyEvent.KEYCODE_MEDIA_NEXT -> { - playbackManager.next() - true - } - - // Go to the previous song if the key is back - KeyEvent.KEYCODE_MEDIA_PREVIOUS -> { - playbackManager.prev() - true - } - - // Rewind if the key is rewind - KeyEvent.KEYCODE_MEDIA_REWIND -> { - playbackManager.rewind() - true - } - - // Stop the service entirely if the key was stop/close - KeyEvent.KEYCODE_MEDIA_STOP, KeyEvent.KEYCODE_MEDIA_CLOSE -> { - stopSelf() - true - } - - else -> false - } - } - - return false - } - /** * A [BroadcastReceiver] for receiving system events from the media notification or the headset. */ diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackSessionConnector.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackSessionConnector.kt index 3be251e6c..d4fc89896 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackSessionConnector.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackSessionConnector.kt @@ -1,18 +1,20 @@ package org.oxycblt.auxio.playback.system import android.content.Context +import android.content.Intent import android.os.SystemClock import android.support.v4.media.MediaMetadataCompat import android.support.v4.media.session.MediaSessionCompat import android.support.v4.media.session.PlaybackStateCompat import org.oxycblt.auxio.coil.loadBitmap import org.oxycblt.auxio.music.Song +import org.oxycblt.auxio.playback.state.LoopMode import org.oxycblt.auxio.playback.state.PlaybackStateManager class PlaybackSessionConnector( private val context: Context, private val mediaSession: MediaSessionCompat -) : PlaybackStateManager.Callback { +) : PlaybackStateManager.Callback, MediaSessionCompat.Callback() { private val playbackManager = PlaybackStateManager.getInstance() private val emptyMetadata = MediaMetadataCompat.Builder().build() @@ -20,9 +22,9 @@ class PlaybackSessionConnector( .setActions(ACTIONS) private var playerState = PlaybackStateCompat.STATE_NONE - private var playerPosition = playbackManager.position init { + mediaSession.setCallback(this) playbackManager.addCallback(this) onSongUpdate(playbackManager.song) @@ -34,6 +36,62 @@ class PlaybackSessionConnector( playbackManager.removeCallback(this) } + // --- MEDIASESSION CALLBACKS --- + + override fun onPlay() { + playbackManager.setPlaying(true) + } + + override fun onPause() { + playbackManager.setPlaying(false) + } + + override fun onSkipToNext() { + playbackManager.next() + } + + override fun onSkipToPrevious() { + playbackManager.prev() + } + + override fun onSeekTo(position: Long) { + // Set the state to buffering to prevent weird delays on the duration counter when seeking. + // And yes, STATE_PAUSED is the only state that works with this code. Because of course it is. + setPlayerState(PlaybackStateCompat.STATE_PAUSED) + playbackManager.seekTo(position) + setPlayerState(getPlayerState()) + } + + override fun onRewind() { + playbackManager.rewind() + } + + override fun onSetRepeatMode(repeatMode: Int) { + val mode = when (repeatMode) { + PlaybackStateCompat.REPEAT_MODE_ALL -> LoopMode.ALL + PlaybackStateCompat.REPEAT_MODE_GROUP -> LoopMode.ALL + PlaybackStateCompat.REPEAT_MODE_ONE -> LoopMode.TRACK + else -> LoopMode.NONE + } + + playbackManager.setLoopMode(mode) + } + + override fun onSetShuffleMode(shuffleMode: Int) { + playbackManager.setShuffling( + shuffleMode == PlaybackStateCompat.SHUFFLE_MODE_ALL || + shuffleMode == PlaybackStateCompat.SHUFFLE_MODE_GROUP, + true + ) + } + + override fun onStop() { + // Get the service to shut down with the ACTION_EXIT intent + context.sendBroadcast(Intent(PlaybackNotification.ACTION_EXIT)) + } + + // --- PLAYBACKSTATEMANAGER CALLBACKS --- + override fun onSongUpdate(song: Song?) { if (song == null) { mediaSession.setMetadata(emptyMetadata) @@ -67,18 +125,31 @@ class PlaybackSessionConnector( ) } - override fun onPositionUpdate(position: Long) { - playerPosition = position + override fun onSeek(position: Long) { updateState() } + // --- MISC --- + private fun setPlayerState(state: Int) { playerState = state updateState() } + private fun getPlayerState(): Int { + if (playbackManager.song == null) { + return PlaybackStateCompat.STATE_STOPPED + } + + return if (playbackManager.isPlaying) { + PlaybackStateCompat.STATE_PLAYING + } else { + PlaybackStateCompat.STATE_PAUSED + } + } + private fun updateState() { - state.setState(playerState, playerPosition, 1.0f, SystemClock.elapsedRealtime()) + state.setState(playerState, playbackManager.position, 1.0f, SystemClock.elapsedRealtime()) mediaSession.setPlaybackState(state.build()) } diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index aa647d81e..2946b8565 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -28,13 +28,13 @@ - - com.google.android.material.theme.MaterialComponentsViewInflater - + + com.google.android.material.theme.MaterialComponentsViewInflater @style/ThemeOverlay.MaterialComponents.Dialog.Alert @style/Theme.CustomDialog +