From ac5e6ba140fb612ed1fc87655f7eebb24167a3e7 Mon Sep 17 00:00:00 2001 From: OxygenCobalt Date: Mon, 26 Oct 2020 12:06:17 -0600 Subject: [PATCH] Fix issues with PlaybackFragment seekbar Fix bugs where PlaybackFragment's seekbar would update the player on the fly/flip out if PlaybackService updated the position while it was seeking. --- .../auxio/playback/PlaybackFragment.kt | 8 +++-- .../oxycblt/auxio/playback/PlaybackService.kt | 20 ++++++----- .../auxio/playback/PlaybackViewModel.kt | 34 ++++++++++++++++--- .../playback/state/PlaybackStateManager.kt | 5 +-- 4 files changed, 49 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt index bb42d9ad8..68ecef44f 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt @@ -134,7 +134,9 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener { } playbackModel.positionAsProgress.observe(viewLifecycleOwner) { - binding.playbackSeekBar.progress = it + if (!playbackModel.isSeeking.value!!) { + binding.playbackSeekBar.progress = it + } } Log.d(this::class.simpleName, "Fragment Created.") @@ -145,7 +147,7 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener { // Seeking callbacks override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { if (fromUser) { - playbackModel.updatePositionWithProgress(progress) + playbackModel.updatePositionDisplay(progress) } } @@ -155,5 +157,7 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener { override fun onStopTrackingTouch(seekBar: SeekBar) { playbackModel.setSeekingStatus(false) + + playbackModel.updatePosition(seekBar.progress) } } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackService.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackService.kt index 30d815f7a..bd0592657 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackService.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackService.kt @@ -22,6 +22,8 @@ import org.oxycblt.auxio.music.toURI import org.oxycblt.auxio.playback.state.PlaybackStateCallback import org.oxycblt.auxio.playback.state.PlaybackStateManager +// A Service that manages the single ExoPlayer instance and [attempts] to keep +// persistence if the app closes. class PlaybackService : Service(), Player.EventListener, PlaybackStateCallback { private val player: SimpleExoPlayer by lazy { val p = SimpleExoPlayer.Builder(applicationContext).build() @@ -54,8 +56,16 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateCallback { super.onDestroy() player.release() - playbackManager.removeCallback(this) serviceJob.cancel() + playbackManager.removeCallback(this) + } + + override fun onPlaybackStateChanged(state: Int) { + if (state == Player.STATE_ENDED) { + playbackManager.next() + } else if (state == Player.STATE_READY) { + startPollingPosition() + } } override fun onSongUpdate(song: Song?) { @@ -100,14 +110,6 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateCallback { } } - override fun onPlaybackStateChanged(state: Int) { - if (state == Player.STATE_ENDED) { - playbackManager.skipNext() - } else if (state == Player.STATE_READY) { - startPollingPosition() - } - } - inner class LocalBinder : Binder() { fun getService() = this@PlaybackService } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt index 5f5c8c493..d25e146c9 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt @@ -20,7 +20,10 @@ import org.oxycblt.auxio.playback.state.PlaybackMode import org.oxycblt.auxio.playback.state.PlaybackStateCallback import org.oxycblt.auxio.playback.state.PlaybackStateManager -// The UI frontend for PlaybackStateManager. +// A ViewModel that acts as an intermediary between the UI and PlaybackStateManager +// TODO: Implement Looping Modes +// TODO: Implement User Queue +// TODO: Implement Persistence through Bundles/Databases/Idk class PlaybackViewModel(private val context: Context) : ViewModel(), PlaybackStateCallback { // Playback private val mSong = MutableLiveData() @@ -88,14 +91,17 @@ class PlaybackViewModel(private val context: Context) : ViewModel(), PlaybackSta // --- PLAYING FUNCTIONS --- + // Play a song fun playSong(song: Song, mode: PlaybackMode) { playbackManager.playSong(song, mode) } + // Play all songs fun shuffleAll() { playbackManager.shuffleAll() } + // Play an album fun playAlbum(album: Album, shuffled: Boolean) { if (album.songs.isEmpty()) { Log.e(this::class.simpleName, "Album is empty, Not playing.") @@ -106,6 +112,7 @@ class PlaybackViewModel(private val context: Context) : ViewModel(), PlaybackSta playbackManager.playParentModel(album, shuffled) } + // Play an artist fun playArtist(artist: Artist, shuffled: Boolean) { if (artist.songs.isEmpty()) { Log.e(this::class.simpleName, "Artist is empty, Not playing.") @@ -116,6 +123,7 @@ class PlaybackViewModel(private val context: Context) : ViewModel(), PlaybackSta playbackManager.playParentModel(artist, shuffled) } + // Play a genre fun playGenre(genre: Genre, shuffled: Boolean) { if (genre.songs.isEmpty()) { Log.e(this::class.simpleName, "Genre is empty, Not playing.") @@ -128,7 +136,15 @@ class PlaybackViewModel(private val context: Context) : ViewModel(), PlaybackSta // --- POSITION FUNCTIONS --- - fun updatePositionWithProgress(progress: Int) { + // Update the position without pushing the change to playbackManager. + // This is used during seek events to give the user an idea of where they're seeking to. + fun updatePositionDisplay(progress: Int) { + mPosition.value = progress.toLong() + } + + // Update the position and push the change the playbackManager. + // This is done when the seek is confirmed to make playbackService seek to the position. + fun updatePosition(progress: Int) { playbackManager.setPosition(progress.toLong()) playbackService.doSeek(progress.toLong()) @@ -136,14 +152,17 @@ class PlaybackViewModel(private val context: Context) : ViewModel(), PlaybackSta // --- QUEUE FUNCTIONS --- + // Skip to next song. fun skipNext() { - playbackManager.skipNext() + playbackManager.next() } + // Skip to last song. fun skipPrev() { - playbackManager.skipPrev() + playbackManager.prev() } + // Remove a queue item, given a QueueAdapter index. fun removeQueueItem(adapterIndex: Int) { // Translate the adapter indices into the correct queue indices val delta = mQueue.value!!.size - nextItemsInQueue.value!!.size @@ -153,6 +172,7 @@ class PlaybackViewModel(private val context: Context) : ViewModel(), PlaybackSta playbackManager.removeQueueItem(index) } + // Move queue items, given QueueAdapter indices. fun moveQueueItems(adapterFrom: Int, adapterTo: Int) { // Translate the adapter indices into the correct queue indices val delta = mQueue.value!!.size - nextItemsInQueue.value!!.size @@ -165,12 +185,14 @@ class PlaybackViewModel(private val context: Context) : ViewModel(), PlaybackSta // --- STATUS FUNCTIONS --- + // Flip the playing status. fun invertPlayingStatus() { mCanAnimate = true playbackManager.setPlayingStatus(!playbackManager.isPlaying) } + // Flip the shuffle status. fun invertShuffleStatus() { playbackManager.setShuffleStatus(!playbackManager.isShuffling) } @@ -198,7 +220,9 @@ class PlaybackViewModel(private val context: Context) : ViewModel(), PlaybackSta } override fun onPositionUpdate(position: Long) { - mPosition.value = position + if (!mIsSeeking.value!!) { + mPosition.value = position + } } override fun onQueueUpdate(queue: MutableList) { diff --git a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt index 0da5b9dde..cff57269f 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt @@ -166,7 +166,7 @@ class PlaybackStateManager { // --- QUEUE FUNCTIONS --- - fun skipNext() { + fun next() { if (mIndex < mQueue.size) { mIndex = mIndex.inc() } @@ -176,7 +176,7 @@ class PlaybackStateManager { forceQueueUpdate() } - fun skipPrev() { + fun prev() { if (mIndex > 0) { mIndex = mIndex.dec() } @@ -215,6 +215,7 @@ class PlaybackStateManager { forceQueueUpdate() } + // Force any callbacks to update when the queue is changed. private fun forceQueueUpdate() { mQueue = mQueue }