diff --git a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt index a0d3aa9a5..35e4a377a 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt @@ -13,8 +13,8 @@ import org.oxycblt.auxio.R import org.oxycblt.auxio.databinding.FragmentAlbumDetailBinding import org.oxycblt.auxio.detail.adapters.DetailSongAdapter import org.oxycblt.auxio.music.MusicStore -import org.oxycblt.auxio.playback.state.PlaybackMode import org.oxycblt.auxio.playback.PlaybackViewModel +import org.oxycblt.auxio.playback.state.PlaybackMode import org.oxycblt.auxio.theme.applyDivider import org.oxycblt.auxio.theme.disable diff --git a/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt b/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt index a843020b0..a2dad5034 100644 --- a/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt @@ -25,8 +25,8 @@ import org.oxycblt.auxio.music.BaseModel import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.music.Song -import org.oxycblt.auxio.playback.state.PlaybackMode import org.oxycblt.auxio.playback.PlaybackViewModel +import org.oxycblt.auxio.playback.state.PlaybackMode import org.oxycblt.auxio.theme.applyColor import org.oxycblt.auxio.theme.applyDivider import org.oxycblt.auxio.theme.resolveAttr 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 eef5d726d..30d815f7a 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackService.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackService.kt @@ -8,6 +8,15 @@ import android.os.IBinder import com.google.android.exoplayer2.MediaItem import com.google.android.exoplayer2.Player import com.google.android.exoplayer2.SimpleExoPlayer +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.conflate +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.takeWhile +import kotlinx.coroutines.launch import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.toURI import org.oxycblt.auxio.playback.state.PlaybackStateCallback @@ -26,6 +35,11 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateCallback { private val mBinder = LocalBinder() private val playbackManager = PlaybackStateManager.getInstance() + private val serviceJob = Job() + private val serviceScope = CoroutineScope( + serviceJob + Dispatchers.Main + ) + override fun onBind(intent: Intent): IBinder { return mBinder } @@ -41,17 +55,59 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateCallback { player.release() playbackManager.removeCallback(this) + serviceJob.cancel() } override fun onSongUpdate(song: Song?) { - song?.let { - val item = MediaItem.fromUri(it.id.toURI()) + song?.let { song -> + val item = MediaItem.fromUri(song.id.toURI()) player.setMediaItem(item) player.prepare() player.play() } } + override fun onPlayingUpdate(isPlaying: Boolean) { + if (isPlaying) { + player.play() + + startPollingPosition() + } else { + player.pause() + } + } + + fun doSeek(position: Long) { + player.seekTo(position * 1000) + } + + // Awful Hack to get position polling to work, as exoplayer does not provide any + // onPositionChanged callback for some inane reason. + // FIXME: Consider using exoplayer UI elements here, don't be surprised if this causes problems. + + private fun pollCurrentPosition() = flow { + while (player.currentPosition <= player.duration) { + emit(player.currentPosition) + delay(500) + } + }.conflate() + + private fun startPollingPosition() { + serviceScope.launch { + pollCurrentPosition().takeWhile { true }.collect { + playbackManager.setPosition(it / 1000) + } + } + } + + 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 0d7b472b8..5f5c8c493 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt @@ -82,9 +82,7 @@ class PlaybackViewModel(private val context: Context) : ViewModel(), PlaybackSta playbackIntent = Intent(context, PlaybackService::class.java).also { context.bindService(it, connection, Context.BIND_AUTO_CREATE) } - } - init { playbackManager.addCallback(this) } @@ -132,6 +130,8 @@ class PlaybackViewModel(private val context: Context) : ViewModel(), PlaybackSta fun updatePositionWithProgress(progress: Int) { playbackManager.setPosition(progress.toLong()) + + playbackService.doSeek(progress.toLong()) } // --- QUEUE FUNCTIONS --- diff --git a/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt b/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt index 608b593de..4decf70f2 100644 --- a/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt @@ -10,8 +10,8 @@ import androidx.fragment.app.activityViewModels import org.oxycblt.auxio.R import org.oxycblt.auxio.databinding.FragmentSongsBinding import org.oxycblt.auxio.music.MusicStore -import org.oxycblt.auxio.playback.state.PlaybackMode import org.oxycblt.auxio.playback.PlaybackViewModel +import org.oxycblt.auxio.playback.state.PlaybackMode import org.oxycblt.auxio.theme.applyDivider class SongsFragment : Fragment() {