feat: add RadioPlayerAdapter — thin Player facade for Media3
Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
package xyz.cottongin.radio247.service
|
||||
|
||||
import android.os.Looper
|
||||
import androidx.media3.common.BasePlayer
|
||||
import androidx.media3.common.C
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.common.MediaMetadata
|
||||
import androidx.media3.common.PlaybackParameters
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.common.Timeline
|
||||
import xyz.cottongin.radio247.audio.IcyMetadata
|
||||
import xyz.cottongin.radio247.data.model.Station
|
||||
|
||||
class RadioPlayerAdapter(
|
||||
private val onPlay: () -> Unit,
|
||||
private val onStop: () -> Unit,
|
||||
) : BasePlayer() {
|
||||
|
||||
private val listeners = mutableListOf<Player.Listener>()
|
||||
private var _playbackState: Int = Player.STATE_IDLE
|
||||
private var _playWhenReady: Boolean = false
|
||||
private var _mediaMetadata: MediaMetadata = MediaMetadata.EMPTY
|
||||
private var _currentMediaItem: MediaItem? = null
|
||||
|
||||
override fun getApplicationLooper(): Looper = Looper.getMainLooper()
|
||||
|
||||
override fun addListener(listener: Player.Listener) {
|
||||
listeners.add(listener)
|
||||
}
|
||||
|
||||
override fun removeListener(listener: Player.Listener) {
|
||||
listeners.remove(listener)
|
||||
}
|
||||
|
||||
override fun getAvailableCommands(): Player.Commands =
|
||||
Player.Commands.Builder()
|
||||
.add(Player.COMMAND_PLAY_PAUSE)
|
||||
.add(Player.COMMAND_STOP)
|
||||
.add(Player.COMMAND_GET_CURRENT_MEDIA_ITEM)
|
||||
.add(Player.COMMAND_GET_MEDIA_ITEMS_METADATA)
|
||||
.build()
|
||||
|
||||
override fun getPlaybackState(): Int = _playbackState
|
||||
override fun getPlayWhenReady(): Boolean = _playWhenReady
|
||||
override fun isPlaying(): Boolean = _playbackState == Player.STATE_READY && _playWhenReady
|
||||
override fun getMediaMetadata(): MediaMetadata = _mediaMetadata
|
||||
override fun getCurrentMediaItem(): MediaItem? = _currentMediaItem
|
||||
|
||||
override fun play() {
|
||||
_playWhenReady = true
|
||||
onPlay()
|
||||
}
|
||||
|
||||
override fun pause() {
|
||||
_playWhenReady = false
|
||||
onStop()
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
_playWhenReady = false
|
||||
onStop()
|
||||
}
|
||||
|
||||
override fun prepare() {}
|
||||
|
||||
override fun setPlayWhenReady(playWhenReady: Boolean) {
|
||||
if (playWhenReady) play() else pause()
|
||||
}
|
||||
|
||||
override fun release() {
|
||||
listeners.clear()
|
||||
}
|
||||
|
||||
override fun getPlaybackParameters(): PlaybackParameters = PlaybackParameters.DEFAULT
|
||||
override fun setPlaybackParameters(playbackParameters: PlaybackParameters) {}
|
||||
|
||||
override fun getRepeatMode(): Int = Player.REPEAT_MODE_OFF
|
||||
override fun setRepeatMode(repeatMode: Int) {}
|
||||
|
||||
override fun getShuffleModeEnabled(): Boolean = false
|
||||
override fun setShuffleModeEnabled(shuffleModeEnabled: Boolean) {}
|
||||
|
||||
override fun seekToDefaultPosition() {}
|
||||
override fun seekToDefaultPosition(mediaItemIndex: Int) {}
|
||||
override fun seekTo(positionMs: Long) {}
|
||||
override fun seekTo(mediaItemIndex: Int, positionMs: Long) {}
|
||||
|
||||
override fun getCurrentTimeline(): Timeline = Timeline.EMPTY
|
||||
override fun getCurrentMediaItemIndex(): Int = 0
|
||||
override fun getContentPosition(): Long = 0
|
||||
override fun getContentDuration(): Long = C.TIME_UNSET
|
||||
override fun getCurrentPosition(): Long = 0
|
||||
override fun getDuration(): Long = C.TIME_UNSET
|
||||
override fun getBufferedPosition(): Long = 0
|
||||
override fun getTotalBufferedDuration(): Long = 0
|
||||
override fun isCurrentMediaItemLive(): Boolean = true
|
||||
|
||||
fun updatePlaybackState(state: PlaybackState) {
|
||||
val (newPlayerState, newPlayWhenReady) = when (state) {
|
||||
is PlaybackState.Idle -> Player.STATE_IDLE to false
|
||||
is PlaybackState.Connecting -> Player.STATE_BUFFERING to true
|
||||
is PlaybackState.Playing -> Player.STATE_READY to true
|
||||
is PlaybackState.Paused -> Player.STATE_READY to false
|
||||
is PlaybackState.Reconnecting -> Player.STATE_BUFFERING to true
|
||||
}
|
||||
|
||||
val stateChanged = newPlayerState != _playbackState
|
||||
val playWhenReadyChanged = newPlayWhenReady != _playWhenReady
|
||||
_playbackState = newPlayerState
|
||||
_playWhenReady = newPlayWhenReady
|
||||
|
||||
if (stateChanged || playWhenReadyChanged) {
|
||||
listeners.forEach { listener ->
|
||||
if (playWhenReadyChanged) {
|
||||
listener.onPlayWhenReadyChanged(
|
||||
newPlayWhenReady,
|
||||
Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST
|
||||
)
|
||||
}
|
||||
if (stateChanged) {
|
||||
listener.onPlaybackStateChanged(newPlayerState)
|
||||
}
|
||||
listener.onIsPlayingChanged(isPlaying)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateStation(station: Station) {
|
||||
_currentMediaItem = MediaItem.Builder()
|
||||
.setMediaId("station:${station.id}")
|
||||
.setMediaMetadata(
|
||||
MediaMetadata.Builder()
|
||||
.setTitle(station.name)
|
||||
.setIsPlayable(true)
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
listeners.forEach {
|
||||
it.onMediaItemTransition(
|
||||
_currentMediaItem,
|
||||
Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateMetadata(
|
||||
station: Station,
|
||||
metadata: IcyMetadata?,
|
||||
artworkUri: android.net.Uri? = null
|
||||
) {
|
||||
_mediaMetadata = MediaMetadata.Builder()
|
||||
.setStation(station.name)
|
||||
.setTitle(metadata?.title ?: station.name)
|
||||
.setArtist(metadata?.artist)
|
||||
.setArtworkUri(artworkUri)
|
||||
.setIsPlayable(true)
|
||||
.build()
|
||||
listeners.forEach { it.onMediaMetadataChanged(_mediaMetadata) }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user