2026-03-18 05:58:09 -04:00
|
|
|
package xyz.cottongin.radio247.service
|
|
|
|
|
|
|
|
|
|
import android.os.Looper
|
|
|
|
|
import androidx.media3.common.MediaItem
|
|
|
|
|
import androidx.media3.common.MediaMetadata
|
|
|
|
|
import androidx.media3.common.Player
|
2026-03-18 06:10:17 -04:00
|
|
|
import androidx.media3.common.SimpleBasePlayer
|
|
|
|
|
import com.google.common.util.concurrent.Futures
|
|
|
|
|
import com.google.common.util.concurrent.ListenableFuture
|
2026-03-18 05:58:09 -04:00
|
|
|
import xyz.cottongin.radio247.audio.IcyMetadata
|
|
|
|
|
import xyz.cottongin.radio247.data.model.Station
|
|
|
|
|
|
|
|
|
|
class RadioPlayerAdapter(
|
|
|
|
|
private val onPlay: () -> Unit,
|
|
|
|
|
private val onStop: () -> Unit,
|
2026-03-18 06:10:17 -04:00
|
|
|
) : SimpleBasePlayer(Looper.getMainLooper()) {
|
2026-03-18 05:58:09 -04:00
|
|
|
|
2026-03-18 06:10:17 -04:00
|
|
|
@Volatile
|
2026-03-18 05:58:09 -04:00
|
|
|
private var _playbackState: Int = Player.STATE_IDLE
|
2026-03-18 06:10:17 -04:00
|
|
|
|
|
|
|
|
@Volatile
|
2026-03-18 05:58:09 -04:00
|
|
|
private var _playWhenReady: Boolean = false
|
|
|
|
|
|
2026-03-18 06:10:17 -04:00
|
|
|
@Volatile
|
|
|
|
|
private var _mediaMetadata: MediaMetadata = MediaMetadata.EMPTY
|
2026-03-18 05:58:09 -04:00
|
|
|
|
2026-03-18 06:10:17 -04:00
|
|
|
@Volatile
|
|
|
|
|
private var _currentMediaItem: MediaItem? = null
|
2026-03-18 05:58:09 -04:00
|
|
|
|
2026-03-18 06:10:17 -04:00
|
|
|
private val 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 getState(): SimpleBasePlayer.State {
|
|
|
|
|
val mediaItem = _currentMediaItem
|
|
|
|
|
val playlist = if (mediaItem != null) {
|
|
|
|
|
listOf(getPlaceholderMediaItemData(mediaItem))
|
|
|
|
|
} else {
|
|
|
|
|
emptyList()
|
|
|
|
|
}
|
2026-03-18 05:58:09 -04:00
|
|
|
|
2026-03-18 06:10:17 -04:00
|
|
|
return SimpleBasePlayer.State.Builder()
|
|
|
|
|
.setAvailableCommands(commands)
|
|
|
|
|
.setPlaybackState(_playbackState)
|
|
|
|
|
.setPlayWhenReady(_playWhenReady, Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST)
|
|
|
|
|
.setCurrentMediaItemIndex(if (mediaItem != null) 0 else 0)
|
|
|
|
|
.setContentPositionMs(0L)
|
|
|
|
|
.setIsLoading(false)
|
|
|
|
|
.setPlaylist(playlist)
|
2026-03-18 05:58:09 -04:00
|
|
|
.build()
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-18 06:10:17 -04:00
|
|
|
override fun handleSetPlayWhenReady(playWhenReady: Boolean): ListenableFuture<*> {
|
|
|
|
|
_playWhenReady = playWhenReady
|
|
|
|
|
if (playWhenReady) {
|
|
|
|
|
onPlay()
|
|
|
|
|
} else {
|
|
|
|
|
onStop()
|
|
|
|
|
}
|
|
|
|
|
return Futures.immediateVoidFuture()
|
2026-03-18 05:58:09 -04:00
|
|
|
}
|
|
|
|
|
|
2026-03-18 06:10:17 -04:00
|
|
|
override fun handleStop(): ListenableFuture<*> {
|
2026-03-18 05:58:09 -04:00
|
|
|
_playWhenReady = false
|
|
|
|
|
onStop()
|
2026-03-18 06:10:17 -04:00
|
|
|
return Futures.immediateVoidFuture()
|
2026-03-18 05:58:09 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
_playbackState = newPlayerState
|
|
|
|
|
_playWhenReady = newPlayWhenReady
|
2026-03-18 06:10:17 -04:00
|
|
|
invalidateState()
|
2026-03-18 05:58:09 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun updateStation(station: Station) {
|
|
|
|
|
_currentMediaItem = MediaItem.Builder()
|
|
|
|
|
.setMediaId("station:${station.id}")
|
|
|
|
|
.setMediaMetadata(
|
|
|
|
|
MediaMetadata.Builder()
|
|
|
|
|
.setTitle(station.name)
|
|
|
|
|
.setIsPlayable(true)
|
|
|
|
|
.build()
|
|
|
|
|
)
|
|
|
|
|
.build()
|
2026-03-18 06:10:17 -04:00
|
|
|
invalidateState()
|
2026-03-18 05:58:09 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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()
|
2026-03-18 06:10:17 -04:00
|
|
|
invalidateState()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun clearState() {
|
|
|
|
|
_currentMediaItem = null
|
|
|
|
|
_mediaMetadata = MediaMetadata.EMPTY
|
|
|
|
|
_playbackState = Player.STATE_IDLE
|
|
|
|
|
_playWhenReady = false
|
2026-03-18 05:58:09 -04:00
|
|
|
}
|
|
|
|
|
}
|