player screen

This commit is contained in:
grakovne
2024-10-08 00:22:57 +02:00
parent 89a7e25ab8
commit e2269417d7
3 changed files with 84 additions and 21 deletions

View File

@@ -8,8 +8,6 @@ import android.os.Looper
import androidx.annotation.OptIn
import androidx.core.content.ContextCompat
import androidx.lifecycle.MutableLiveData
import androidx.media3.common.MediaItem
import androidx.media3.common.MediaMetadata
import androidx.media3.common.Player
import androidx.media3.common.Timeline
import androidx.media3.common.util.UnstableApi
@@ -21,23 +19,26 @@ import com.google.common.util.concurrent.MoreExecutors
import dagger.hilt.android.qualifiers.ApplicationContext
import org.grakovne.lissen.domain.DetailedBook
import org.grakovne.lissen.player.service.AudioPlayerService
import org.grakovne.lissen.repository.ServerMediaRepository
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
@OptIn(UnstableApi::class)
class MediaRepository
@Inject constructor(
@ApplicationContext private val context: Context,
private val serverMediaRepository: ServerMediaRepository
@ApplicationContext private val context: Context
) {
private val sessionToken =
SessionToken(context, ComponentName(context, AudioPlayerService::class.java))
private lateinit var mediaController: MediaController
private val token = SessionToken(
context,
ComponentName(context, AudioPlayerService::class.java)
)
val _isPlaying = MutableLiveData(false)
val _currentPosition = MutableLiveData<Long>()
val _currentMediaItemIndex = MutableLiveData<Int>()
private val handler = Handler(Looper.getMainLooper())
@@ -50,7 +51,7 @@ class MediaRepository
}
init {
val controllerBuilder = MediaController.Builder(context, sessionToken)
val controllerBuilder = MediaController.Builder(context, token)
val futureController = controllerBuilder.buildAsync()
Futures.addCallback(
@@ -62,6 +63,7 @@ class MediaRepository
controller.addListener(object : Player.Listener {
override fun onPlaybackStateChanged(playbackState: Int) {
_isPlaying.postValue(playbackState == Player.STATE_READY && controller.isPlaying)
_currentMediaItemIndex.postValue(mediaController.currentMediaItemIndex)
when (controller.isPlaying) {
true -> startUpdatingProgress()
@@ -82,13 +84,19 @@ class MediaRepository
oldPosition: Player.PositionInfo,
newPosition: Player.PositionInfo,
reason: Int
) = _currentPosition.postValue(mediaController.currentPosition)
) {
_currentMediaItemIndex.postValue(mediaController.currentMediaItemIndex)
_currentPosition.postValue(mediaController.currentPosition)
}
override fun onTimelineChanged(
timeline: Timeline,
reason: Int
) = _currentPosition.postValue(mediaController.currentPosition)
) {
_currentMediaItemIndex.postValue(mediaController.currentMediaItemIndex)
_currentPosition.postValue(mediaController.currentPosition)
}
})
}
@@ -101,7 +109,6 @@ class MediaRepository
}
@OptIn(UnstableApi::class)
fun playAudio(book: DetailedBook) {
val intent = Intent(context, AudioPlayerService::class.java).apply {
action = AudioPlayerService.ACTION_START_FOREGROUND
@@ -121,11 +128,48 @@ class MediaRepository
ContextCompat.startForegroundService(context, intent)
}
fun startUpdatingProgress() {
fun nextTrack() {
val nextIndex = mediaController.currentMediaItemIndex + 1
val timeline = mediaController.currentTimeline
if (nextIndex < timeline.windowCount) {
mediaController.seekTo(nextIndex, 0)
}
}
fun setTrack(index: Int) {
val timeline = mediaController.currentTimeline
if (index < timeline.windowCount && index >= 0) {
mediaController.seekTo(index, 0)
}
}
fun seekTo(position: Float) {
stopUpdatingProgress()
val duration = mediaController.duration
if (duration > 0) {
val newPosition = (1000 * position).toLong()
mediaController.seekTo(newPosition)
}
startUpdatingProgress()
}
fun previousTrack() {
val previousIndex = mediaController.currentMediaItemIndex - 1
if (previousIndex > 0) {
mediaController.seekTo(previousIndex, 0)
}
}
private fun startUpdatingProgress() {
handler.post(updateProgressAction)
}
fun stopUpdatingProgress() {
private fun stopUpdatingProgress() {
handler.removeCallbacks(updateProgressAction)
}
}

View File

@@ -23,8 +23,13 @@ import androidx.compose.material3.Slider
import androidx.compose.material3.SliderDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@@ -43,8 +48,16 @@ fun TrackControlComposable(
val book by viewModel.book.observeAsState()
val chapters = book?.chapters ?: emptyList()
var sliderPosition by remember { mutableStateOf(0f) }
var isDragging by remember { mutableStateOf(false) }
val duration = book?.chapters?.get(currentTrackIndex)?.duration?.toFloat() ?: 0f
val draggableTrackProgress by remember {
derivedStateOf {
if (!isDragging) currentPosition.toFloat() else sliderPosition
}
}
Column(
modifier = modifier
.fillMaxWidth()
@@ -52,8 +65,15 @@ fun TrackControlComposable(
) {
Slider(
value = currentPosition.toFloat(),
onValueChange = { newPosition -> viewModel.seekTo(newPosition) },
value = draggableTrackProgress,
onValueChange = { newPosition ->
sliderPosition = newPosition
isDragging = true
},
onValueChangeFinished = {
viewModel.seekTo(sliderPosition)
isDragging = false
},
valueRange = 0f..(duration),
modifier = Modifier
.height(36.dp)

View File

@@ -26,10 +26,9 @@ class PlayerViewModel @Inject constructor(
val playingQueueExpanded = _playingQueueExpanded
val isPlaying = mediaRepository._isPlaying
val currentPosition = mediaRepository._currentPosition
private val _currentTrackIndex = MutableLiveData(0)
private val _currentTrackIndex = mediaRepository._currentMediaItemIndex
val currentTrackIndex: LiveData<Int> = _currentTrackIndex
fun togglePlayingQueue() {
@@ -62,19 +61,19 @@ class PlayerViewModel @Inject constructor(
}
fun seekTo(position: Float) {
//_currentPosition.value = position
mediaRepository.seekTo(position)
}
fun setChapter(index: Int) {
_currentTrackIndex.value = index
mediaRepository.setTrack(index)
}
fun nextTrack() {
_currentTrackIndex.value = _currentTrackIndex.value?.plus(1)
mediaRepository.nextTrack()
}
fun previousTrack() {
_currentTrackIndex.value = _currentTrackIndex.value?.minus(1)
mediaRepository.previousTrack()
}
fun togglePlayPause() {