Add playback updates to PlaybackService
Add the ability for PlaybackService to move to the next song when the current one ends, update the duration, and also be played/paused.
This commit is contained in:
parent
42325c456e
commit
1afaae7b0a
5 changed files with 63 additions and 7 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 ---
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in a new issue