From 3d97ea73e38d6157ab64ee7c1a67432d0ce5cd72 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 15 Oct 2024 16:26:27 +0200 Subject: [PATCH 01/20] chapters --- app/build.gradle.kts | 2 +- .../lissen/playback/MediaRepository.kt | 24 ++++++-------- .../composable/PlayingQueueComposable.kt | 11 +++++-- .../composable/TrackControlComposable.kt | 22 ++++++++----- .../composable/TrackDetailsComposable.kt | 13 +++++++- .../lissen/viewmodel/PlayerViewModel.kt | 32 ++++++++++++++++++- 6 files changed, 77 insertions(+), 27 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index acc35578..1563ab65 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -34,7 +34,7 @@ android { debug { matchingFallbacks.add("release") isMinifyEnabled = false - isDebuggable = false + isDebuggable = true } } diff --git a/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt b/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt index f0b225de..75da456b 100644 --- a/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt +++ b/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt @@ -11,7 +11,6 @@ import androidx.core.content.ContextCompat import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.localbroadcastmanager.content.LocalBroadcastManager -import androidx.media3.common.MediaItem import androidx.media3.common.Player import androidx.media3.session.MediaController import androidx.media3.session.SessionToken @@ -45,9 +44,6 @@ class MediaRepository @Inject constructor(@ApplicationContext private val contex private val _currentPosition = MutableLiveData() val currentPosition: LiveData = _currentPosition - private val _currentMediaItemIndex = MutableLiveData() - val currentMediaItemIndex: LiveData = _currentMediaItemIndex - private val _playingBook = MutableLiveData() val playingBook: LiveData = _playingBook @@ -76,17 +72,10 @@ class MediaRepository @Inject constructor(@ApplicationContext private val contex .registerReceiver(bookDetailsReadyReceiver, IntentFilter(PLAYBACK_READY)) mediaController.addListener(object : Player.Listener { - override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) { - _currentMediaItemIndex.value = mediaController.currentMediaItemIndex - } - override fun onIsPlayingChanged(isPlaying: Boolean) { _isPlaying.value = isPlaying - _currentMediaItemIndex.value = mediaController.currentMediaItemIndex } }) - - startUpdatingProgress() } override fun onFailure(t: Throwable) { @@ -106,11 +95,11 @@ class MediaRepository @Inject constructor(@ApplicationContext private val contex preparePlay(book) } _playingBook.postValue(book) + startUpdatingProgress(book) } private fun preparePlay(book: DetailedBook) { _currentPosition.postValue(0) - _currentMediaItemIndex.postValue(0) _isPlaying.postValue(false) val intent = Intent(context, AudioPlayerService::class.java).apply { @@ -168,10 +157,17 @@ class MediaRepository @Inject constructor(@ApplicationContext private val contex context.startService(intent) } - private fun startUpdatingProgress() { + private fun startUpdatingProgress(detailedBook: DetailedBook) { handler.postDelayed(object : Runnable { override fun run() { - _currentPosition.value = mediaController.currentPosition / 1000 + val currentIndex = mediaController.currentMediaItemIndex + val previousFilesDuration = + detailedBook.files.take(currentIndex).sumOf { it.duration } + val currentFilePosition = + mediaController.currentPosition / 1000.0 + + _currentPosition.value = (previousFilesDuration + currentFilePosition).toLong() + handler.postDelayed(this, 500) } }, 500) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlayingQueueComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlayingQueueComposable.kt index a38604c2..a8800bdd 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlayingQueueComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlayingQueueComposable.kt @@ -15,6 +15,9 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect 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.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.nestedscroll.NestedScrollConnection @@ -34,10 +37,11 @@ fun PlayingQueueComposable( modifier: Modifier = Modifier, ) { val isPlaybackReady by viewModel.isPlaybackReady.observeAsState(false) - val currentTrackIndex by viewModel.currentTrackIndex.observeAsState(0) val book by viewModel.book.observeAsState() val chapters = book?.files ?: emptyList() + var currentTrackIndex by remember { mutableStateOf(0) } + val currentPosition by viewModel.currentPosition.observeAsState(0) val playingQueueExpanded by viewModel.playingQueueExpanded.observeAsState(false) val listState = rememberLazyListState() @@ -57,11 +61,14 @@ fun PlayingQueueComposable( } } + LaunchedEffect(currentPosition) { + currentTrackIndex = viewModel.calculateTrackIndex(currentPosition) + } + LaunchedEffect(currentTrackIndex) { when { currentTrackIndex > 0 -> listState.animateScrollToItem(currentTrackIndex - 1) else -> listState.animateScrollToItem(currentTrackIndex) - } } diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt index 2ff7d1e5..22702eb0 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt @@ -27,6 +27,7 @@ import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue @@ -42,8 +43,10 @@ fun TrackControlComposable( modifier: Modifier = Modifier ) { val isPlaying by viewModel.isPlaying.observeAsState(false) - val currentPosition by viewModel.currentPosition.observeAsState(0L) - val currentTrackIndex by viewModel.currentTrackIndex.observeAsState(0) + val totalPosition by viewModel.currentPosition.observeAsState(0L) + + var currentTrackIndex by remember { mutableIntStateOf(0) } + var currentTrackPosition by remember { mutableStateOf(0f) } val book by viewModel.book.observeAsState() val chapters = book?.files ?: emptyList() @@ -57,11 +60,14 @@ fun TrackControlComposable( } } - LaunchedEffect(currentPosition) { + LaunchedEffect(totalPosition) { + currentTrackIndex = viewModel.calculateTrackIndex(totalPosition) + currentTrackPosition = viewModel.calculateTrackPosition(totalPosition).toFloat() + when (isDragging) { true -> {} false -> { - sliderPosition = currentPosition.toFloat() + sliderPosition = currentTrackPosition } } } @@ -95,12 +101,12 @@ fun TrackControlComposable( horizontalArrangement = Arrangement.SpaceBetween ) { Text( - text = currentPosition.toInt().formatFully(), + text = currentTrackPosition.toInt().formatFully(), style = MaterialTheme.typography.bodySmall, color = colorScheme.onBackground.copy(alpha = 0.6f) ) Text( - text = "-${maxOf(0f, duration - currentPosition).toInt().formatFully()}", + text = "-${maxOf(0f, duration - currentTrackPosition).toInt().formatFully()}", style = MaterialTheme.typography.bodySmall, color = colorScheme.onBackground.copy(alpha = 0.6f) ) @@ -131,7 +137,7 @@ fun TrackControlComposable( IconButton( onClick = { - viewModel.seekTo(maxOf(0f, currentPosition - 10f)) + viewModel.seekTo(maxOf(0f, totalPosition - 10f)) }, modifier = Modifier.weight(1f) ) { @@ -159,7 +165,7 @@ fun TrackControlComposable( IconButton( onClick = { - viewModel.seekTo(minOf(duration, currentPosition + 30f)) + viewModel.seekTo(minOf(duration, totalPosition + 30f)) }, modifier = Modifier.weight(1f) ) { diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackDetailsComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackDetailsComposable.kt index 1e51e8b3..0d706f53 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackDetailsComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackDetailsComposable.kt @@ -10,9 +10,13 @@ import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.MaterialTheme.typography 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.draw.clip @@ -36,9 +40,16 @@ fun TrackDetailsComposable( viewModel: PlayerViewModel, modifier: Modifier = Modifier ) { - val currentTrackIndex by viewModel.currentTrackIndex.observeAsState(0) + + val totalPosition by viewModel.currentPosition.observeAsState(0L) + + var currentTrackIndex by remember { mutableStateOf(0) } val book by viewModel.book.observeAsState() + LaunchedEffect(totalPosition) { + currentTrackIndex = viewModel.calculateTrackIndex(totalPosition) + } + val context = LocalContext.current val imageRequest = remember(book?.id) { ImageRequest.Builder(context) diff --git a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt index eabb481f..d9b7ca31 100644 --- a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt +++ b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt @@ -27,12 +27,42 @@ class PlayerViewModel @Inject constructor( val isPlaying: LiveData = mediaRepository.isPlaying val currentPosition: LiveData = mediaRepository.currentPosition - val currentTrackIndex: LiveData = mediaRepository.currentMediaItemIndex fun togglePlayingQueue() { _playingQueueExpanded.value = !(_playingQueueExpanded.value ?: false) } + fun calculateTrackIndex(position: Long): Int { + val currentBook = book.value ?: return 0 + + return currentBook + .files + .foldIndexed(0) { index, accumulatedDuration, file -> + val newAccumulatedDuration = accumulatedDuration + file.duration + + if (position < newAccumulatedDuration) { + return index + } + + newAccumulatedDuration.toInt() + } + } + + fun calculateTrackPosition(overallPosition: Long): Long { + val currentBook = book.value ?: return 0L + + var accumulatedDuration = 0.0 + currentBook.files.forEach { file -> + val fileDuration = file.duration + if (overallPosition < accumulatedDuration + fileDuration) { + return (overallPosition - accumulatedDuration).toLong() + } + accumulatedDuration += fileDuration + } + + return (overallPosition - accumulatedDuration).toLong() + } + fun preparePlayback(bookId: String) { viewModelScope.launch { val result = withContext(Dispatchers.IO) { From fd68827edf344f3d80620fdcb496aecfc582fc16 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 15 Oct 2024 16:43:56 +0200 Subject: [PATCH 02/20] chapters --- .../composable/PlayingQueueComposable.kt | 11 ++----- .../composable/TrackControlComposable.kt | 30 +++++++---------- .../composable/TrackDetailsComposable.kt | 12 +------ .../lissen/viewmodel/PlayerViewModel.kt | 33 ++++++++++++++++++- 4 files changed, 46 insertions(+), 40 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlayingQueueComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlayingQueueComposable.kt index a8800bdd..71bf5dde 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlayingQueueComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlayingQueueComposable.kt @@ -15,9 +15,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect 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.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.nestedscroll.NestedScrollConnection @@ -37,11 +34,11 @@ fun PlayingQueueComposable( modifier: Modifier = Modifier, ) { val isPlaybackReady by viewModel.isPlaybackReady.observeAsState(false) + val book by viewModel.book.observeAsState() val chapters = book?.files ?: emptyList() - var currentTrackIndex by remember { mutableStateOf(0) } - val currentPosition by viewModel.currentPosition.observeAsState(0) + val currentTrackIndex by viewModel.currentTrackIndex.observeAsState(0) val playingQueueExpanded by viewModel.playingQueueExpanded.observeAsState(false) val listState = rememberLazyListState() @@ -61,10 +58,6 @@ fun PlayingQueueComposable( } } - LaunchedEffect(currentPosition) { - currentTrackIndex = viewModel.calculateTrackIndex(currentPosition) - } - LaunchedEffect(currentTrackIndex) { when { currentTrackIndex > 0 -> listState.animateScrollToItem(currentTrackIndex - 1) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt index 22702eb0..05749962 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt @@ -23,11 +23,9 @@ import androidx.compose.material3.MaterialTheme.colorScheme 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.mutableFloatStateOf -import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue @@ -43,10 +41,10 @@ fun TrackControlComposable( modifier: Modifier = Modifier ) { val isPlaying by viewModel.isPlaying.observeAsState(false) - val totalPosition by viewModel.currentPosition.observeAsState(0L) - var currentTrackIndex by remember { mutableIntStateOf(0) } - var currentTrackPosition by remember { mutableStateOf(0f) } + val currentTrackIndex by viewModel.currentTrackIndex.observeAsState(0) + val currentTrackPosition by viewModel.currentTrackPosition.observeAsState(0L) + val currentTrackDuration by viewModel.currentTrackDuration.observeAsState(0f) val book by viewModel.book.observeAsState() val chapters = book?.files ?: emptyList() @@ -54,20 +52,12 @@ fun TrackControlComposable( var sliderPosition by remember { mutableFloatStateOf(0f) } var isDragging by remember { mutableStateOf(false) } - val duration by remember { - derivedStateOf { - book?.files?.getOrNull(currentTrackIndex)?.duration?.toFloat() ?: 0f - } - } - - LaunchedEffect(totalPosition) { - currentTrackIndex = viewModel.calculateTrackIndex(totalPosition) - currentTrackPosition = viewModel.calculateTrackPosition(totalPosition).toFloat() + LaunchedEffect(currentTrackPosition) { when (isDragging) { true -> {} false -> { - sliderPosition = currentTrackPosition + sliderPosition = currentTrackPosition.toFloat() } } } @@ -86,7 +76,7 @@ fun TrackControlComposable( isDragging = false viewModel.seekTo(sliderPosition) }, - valueRange = 0f..(duration), + valueRange = 0f..(currentTrackDuration), colors = SliderDefaults .colors( thumbColor = colorScheme.primary, @@ -106,7 +96,9 @@ fun TrackControlComposable( color = colorScheme.onBackground.copy(alpha = 0.6f) ) Text( - text = "-${maxOf(0f, duration - currentTrackPosition).toInt().formatFully()}", + text = "-${ + maxOf(0f, currentTrackDuration - currentTrackPosition).toInt().formatFully() + }", style = MaterialTheme.typography.bodySmall, color = colorScheme.onBackground.copy(alpha = 0.6f) ) @@ -137,7 +129,7 @@ fun TrackControlComposable( IconButton( onClick = { - viewModel.seekTo(maxOf(0f, totalPosition - 10f)) + //viewModel.seekTo(maxOf(0f, totalPosition - 10f)) // change me! }, modifier = Modifier.weight(1f) ) { @@ -165,7 +157,7 @@ fun TrackControlComposable( IconButton( onClick = { - viewModel.seekTo(minOf(duration, totalPosition + 30f)) + //viewModel.seekTo(minOf(duration, totalPosition + 30f)) change me! }, modifier = Modifier.weight(1f) ) { diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackDetailsComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackDetailsComposable.kt index 0d706f53..7acba427 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackDetailsComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackDetailsComposable.kt @@ -10,13 +10,9 @@ import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.MaterialTheme.typography 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.draw.clip @@ -41,15 +37,9 @@ fun TrackDetailsComposable( modifier: Modifier = Modifier ) { - val totalPosition by viewModel.currentPosition.observeAsState(0L) - - var currentTrackIndex by remember { mutableStateOf(0) } + val currentTrackIndex by viewModel.currentTrackIndex.observeAsState(0) val book by viewModel.book.observeAsState() - LaunchedEffect(totalPosition) { - currentTrackIndex = viewModel.calculateTrackIndex(totalPosition) - } - val context = LocalContext.current val imageRequest = remember(book?.id) { ImageRequest.Builder(context) diff --git a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt index d9b7ca31..27e033bb 100644 --- a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt +++ b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt @@ -1,6 +1,7 @@ package org.grakovne.lissen.viewmodel import androidx.lifecycle.LiveData +import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -21,17 +22,47 @@ class PlayerViewModel @Inject constructor( val book: LiveData = mediaRepository.playingBook + private val mediaItemPosition: LiveData = mediaRepository.currentPosition private val _playingQueueExpanded = MutableLiveData(false) + val playingQueueExpanded: LiveData = _playingQueueExpanded val isPlaybackReady: LiveData = mediaRepository.isPlaybackReady val isPlaying: LiveData = mediaRepository.isPlaying - val currentPosition: LiveData = mediaRepository.currentPosition + + private val _currentTrackIndex = MediatorLiveData().apply { + addSource(mediaItemPosition) { updateCurrentTrackData() } + addSource(book) { updateCurrentTrackData() } + } + val currentTrackIndex: LiveData = _currentTrackIndex + + private val _currentTrackPosition = MediatorLiveData().apply { + addSource(mediaItemPosition) { updateCurrentTrackData() } + addSource(book) { updateCurrentTrackData() } + } + val currentTrackPosition: LiveData = _currentTrackPosition + + private val _currentTrackDuration = MediatorLiveData().apply { + addSource(mediaItemPosition) { updateCurrentTrackData() } + addSource(book) { updateCurrentTrackData() } + } + + val currentTrackDuration: LiveData = _currentTrackDuration fun togglePlayingQueue() { _playingQueueExpanded.value = !(_playingQueueExpanded.value ?: false) } + private fun updateCurrentTrackData() { + val book = book.value ?: return + val position = mediaItemPosition.value ?: return + + val trackIndex = calculateTrackIndex(position) + _currentTrackIndex.value = trackIndex + _currentTrackPosition.value = calculateTrackPosition(position) + _currentTrackDuration.value = book.files.getOrNull(trackIndex)?.duration?.toFloat() ?: 0f + } + fun calculateTrackIndex(position: Long): Int { val currentBook = book.value ?: return 0 From e081fe629b691f1145ae5352f36b9347956b4d5f Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 15 Oct 2024 16:46:48 +0200 Subject: [PATCH 03/20] chapters --- .../ui/screens/player/composable/PlayingQueueComposable.kt | 1 - .../ui/screens/player/composable/TrackControlComposable.kt | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlayingQueueComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlayingQueueComposable.kt index 71bf5dde..91a2ec41 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlayingQueueComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlayingQueueComposable.kt @@ -37,7 +37,6 @@ fun PlayingQueueComposable( val book by viewModel.book.observeAsState() val chapters = book?.files ?: emptyList() - val currentTrackIndex by viewModel.currentTrackIndex.observeAsState(0) val playingQueueExpanded by viewModel.playingQueueExpanded.observeAsState(false) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt index 05749962..ee7c08f6 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt @@ -129,7 +129,7 @@ fun TrackControlComposable( IconButton( onClick = { - //viewModel.seekTo(maxOf(0f, totalPosition - 10f)) // change me! + viewModel.seekTo(maxOf(0f, currentTrackPosition - 10f)) }, modifier = Modifier.weight(1f) ) { @@ -157,7 +157,7 @@ fun TrackControlComposable( IconButton( onClick = { - //viewModel.seekTo(minOf(duration, totalPosition + 30f)) change me! + viewModel.seekTo(minOf(currentTrackDuration, currentTrackPosition + 30f)) }, modifier = Modifier.weight(1f) ) { From 05fe00c217f519cfacf20610fbb218362c8d2f11 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 15 Oct 2024 17:02:19 +0200 Subject: [PATCH 04/20] chapters --- .../lissen/playback/MediaRepository.kt | 12 ++++----- .../composable/PlayingQueueComposable.kt | 2 +- .../composable/PlaylistItemComposable.kt | 7 ++--- .../composable/TrackControlComposable.kt | 2 +- .../composable/TrackDetailsComposable.kt | 2 +- .../lissen/viewmodel/PlayerViewModel.kt | 26 ++++++++++++------- 6 files changed, 29 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt b/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt index 75da456b..b70eef00 100644 --- a/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt +++ b/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt @@ -41,8 +41,8 @@ class MediaRepository @Inject constructor(@ApplicationContext private val contex private val _isPlaybackReady = MutableLiveData(false) val isPlaybackReady: LiveData = _isPlaybackReady - private val _currentPosition = MutableLiveData() - val currentPosition: LiveData = _currentPosition + private val _mediaItemPosition = MutableLiveData() + val mediaItemPosition: LiveData = _mediaItemPosition private val _playingBook = MutableLiveData() val playingBook: LiveData = _playingBook @@ -99,7 +99,7 @@ class MediaRepository @Inject constructor(@ApplicationContext private val contex } private fun preparePlay(book: DetailedBook) { - _currentPosition.postValue(0) + _mediaItemPosition.postValue(0) _isPlaying.postValue(false) val intent = Intent(context, AudioPlayerService::class.java).apply { @@ -128,7 +128,7 @@ class MediaRepository @Inject constructor(@ApplicationContext private val contex mediaController.run { if (currentMediaItemIndex + 1 < currentTimeline.windowCount) { seekTo(currentMediaItemIndex + 1, 0) - _currentPosition.value = 0 + _mediaItemPosition.value = 0 } } } @@ -137,7 +137,7 @@ class MediaRepository @Inject constructor(@ApplicationContext private val contex mediaController.run { if (currentMediaItemIndex > 0) { seekTo(currentMediaItemIndex - 1, 0) - _currentPosition.value = 0 + _mediaItemPosition.value = 0 } } } @@ -166,7 +166,7 @@ class MediaRepository @Inject constructor(@ApplicationContext private val contex val currentFilePosition = mediaController.currentPosition / 1000.0 - _currentPosition.value = (previousFilesDuration + currentFilePosition).toLong() + _mediaItemPosition.value = (previousFilesDuration + currentFilePosition).toLong() handler.postDelayed(this, 500) } diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlayingQueueComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlayingQueueComposable.kt index 91a2ec41..2c6d17e0 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlayingQueueComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlayingQueueComposable.kt @@ -36,7 +36,7 @@ fun PlayingQueueComposable( val isPlaybackReady by viewModel.isPlaybackReady.observeAsState(false) val book by viewModel.book.observeAsState() - val chapters = book?.files ?: emptyList() + val chapters = book?.chapters ?: emptyList() val currentTrackIndex by viewModel.currentTrackIndex.observeAsState(0) val playingQueueExpanded by viewModel.playingQueueExpanded.observeAsState(false) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlaylistItemComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlaylistItemComposable.kt index 2dadf8e5..6028b12c 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlaylistItemComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlaylistItemComposable.kt @@ -23,12 +23,13 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import org.grakovne.lissen.R +import org.grakovne.lissen.domain.BookChapter import org.grakovne.lissen.domain.BookFile import org.grakovne.lissen.ui.extensions.formatLeadingMinutes @Composable fun PlaylistItemComposable( - track: BookFile, + track: BookChapter, isSelected: Boolean, onClick: () -> Unit ) { @@ -56,7 +57,7 @@ fun PlaylistItemComposable( Spacer(modifier = Modifier.width(8.dp)) Text( - text = track.name, + text = track.title, style = MaterialTheme.typography.titleMedium, color = colorScheme.onBackground, overflow = TextOverflow.Ellipsis, @@ -65,7 +66,7 @@ fun PlaylistItemComposable( modifier = Modifier.weight(1f) ) Text( - text = track.duration.toInt().formatLeadingMinutes(), + text = (track.end - track.start).toInt().formatLeadingMinutes(), style = MaterialTheme.typography.bodySmall, modifier = Modifier.padding(start = 8.dp), fontWeight = if (isSelected) FontWeight.Bold else FontWeight.Normal, diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt index ee7c08f6..06d43fa2 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt @@ -47,7 +47,7 @@ fun TrackControlComposable( val currentTrackDuration by viewModel.currentTrackDuration.observeAsState(0f) val book by viewModel.book.observeAsState() - val chapters = book?.files ?: emptyList() + val chapters = book?.chapters ?: emptyList() var sliderPosition by remember { mutableFloatStateOf(0f) } var isDragging by remember { mutableStateOf(false) } diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackDetailsComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackDetailsComposable.kt index 7acba427..c77700fb 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackDetailsComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackDetailsComposable.kt @@ -90,7 +90,7 @@ fun TrackDetailsComposable( text = stringResource( R.string.player_screen_now_playing_title_chapter_of, currentTrackIndex + 1, - book?.files?.size ?: "?" + book?.chapters?.size ?: "?" ), style = typography.bodyMedium, color = colorScheme.onBackground.copy(alpha = 0.6f) diff --git a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt index 27e033bb..d75489cf 100644 --- a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt +++ b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt @@ -22,7 +22,7 @@ class PlayerViewModel @Inject constructor( val book: LiveData = mediaRepository.playingBook - private val mediaItemPosition: LiveData = mediaRepository.currentPosition + private val mediaItemPosition: LiveData = mediaRepository.mediaItemPosition private val _playingQueueExpanded = MutableLiveData(false) val playingQueueExpanded: LiveData = _playingQueueExpanded @@ -60,16 +60,20 @@ class PlayerViewModel @Inject constructor( val trackIndex = calculateTrackIndex(position) _currentTrackIndex.value = trackIndex _currentTrackPosition.value = calculateTrackPosition(position) - _currentTrackDuration.value = book.files.getOrNull(trackIndex)?.duration?.toFloat() ?: 0f + _currentTrackDuration.value = book + .chapters + .getOrNull(trackIndex) + ?.let { it.end - it.start } + ?.toFloat() ?: 0f } fun calculateTrackIndex(position: Long): Int { val currentBook = book.value ?: return 0 return currentBook - .files + .chapters .foldIndexed(0) { index, accumulatedDuration, file -> - val newAccumulatedDuration = accumulatedDuration + file.duration + val newAccumulatedDuration = accumulatedDuration + (file.end - file.start) if (position < newAccumulatedDuration) { return index @@ -83,13 +87,15 @@ class PlayerViewModel @Inject constructor( val currentBook = book.value ?: return 0L var accumulatedDuration = 0.0 - currentBook.files.forEach { file -> - val fileDuration = file.duration - if (overallPosition < accumulatedDuration + fileDuration) { - return (overallPosition - accumulatedDuration).toLong() + currentBook + .chapters + .forEach { file -> + val fileDuration = (file.end - file.start) + if (overallPosition < accumulatedDuration + fileDuration) { + return (overallPosition - accumulatedDuration).toLong() + } + accumulatedDuration += fileDuration } - accumulatedDuration += fileDuration - } return (overallPosition - accumulatedDuration).toLong() } From 7aded55720188073ddfbb05270b40cea4bbff75a Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 15 Oct 2024 17:34:25 +0200 Subject: [PATCH 05/20] chapters --- .../lissen/playback/MediaRepository.kt | 2 + .../playback/service/PlaybackService.kt | 18 ++- .../composable/PlayingQueueComposable.kt | 2 +- .../composable/TrackControlComposable.kt | 6 +- .../composable/TrackDetailsComposable.kt | 2 +- .../lissen/viewmodel/PlayerViewModel.kt | 124 +++++++++++------- 6 files changed, 93 insertions(+), 61 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt b/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt index b70eef00..e2022d50 100644 --- a/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt +++ b/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt @@ -151,6 +151,8 @@ class MediaRepository @Inject constructor(@ApplicationContext private val contex fun seekTo(position: Float) { val intent = Intent(context, AudioPlayerService::class.java).apply { action = ACTION_SEEK_TO + + putExtra(BOOK_EXTRA, playingBook.value) putExtra(POSITION, position) } diff --git a/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt b/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt index 0db4a438..48662414 100644 --- a/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt +++ b/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt @@ -82,8 +82,9 @@ class AudioPlayerService : MediaSessionService() { } ACTION_SEEK_TO -> { + val book = intent.getSerializableExtra(BOOK_EXTRA) as? DetailedBook val position = intent.getFloatExtra(POSITION, 0f) - exoPlayer.seekTo((position * 1000).toLong()) + book?.let { seek(it.files, position) } return START_NOT_STICKY } @@ -151,22 +152,27 @@ class AudioPlayerService : MediaSessionService() { } - private fun setPlaybackProgress( + private fun seek( chapters: List, - progress: MediaProgress? + position: Float? ) { - when (progress) { + when (position) { null -> exoPlayer.seekTo(0, 0) else -> { val duration = chapters.runningFold(0.0) { acc, chapter -> acc + chapter.duration } - val targetChapter = duration.indexOfFirst { it > progress.currentTime } - val chapterProgress = progress.currentTime - duration[targetChapter - 1] + val targetChapter = duration.indexOfFirst { it > position } + val chapterProgress = position - duration[targetChapter - 1] exoPlayer.seekTo(targetChapter - 1, (chapterProgress * 1000).toLong()) } } } + private fun setPlaybackProgress( + chapters: List, + progress: MediaProgress? + ) = seek(chapters, progress?.currentTime?.toFloat()) + companion object { const val ACTION_PLAY = "org.grakovne.lissen.player.service.PLAY" const val ACTION_PAUSE = "org.grakovne.lissen.player.service.PAUSE" diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlayingQueueComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlayingQueueComposable.kt index 2c6d17e0..8a9d03cb 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlayingQueueComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlayingQueueComposable.kt @@ -37,7 +37,7 @@ fun PlayingQueueComposable( val book by viewModel.book.observeAsState() val chapters = book?.chapters ?: emptyList() - val currentTrackIndex by viewModel.currentTrackIndex.observeAsState(0) + val currentTrackIndex by viewModel.currentChapterIndex.observeAsState(0) val playingQueueExpanded by viewModel.playingQueueExpanded.observeAsState(false) val listState = rememberLazyListState() diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt index 06d43fa2..338c8234 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt @@ -42,9 +42,9 @@ fun TrackControlComposable( ) { val isPlaying by viewModel.isPlaying.observeAsState(false) - val currentTrackIndex by viewModel.currentTrackIndex.observeAsState(0) - val currentTrackPosition by viewModel.currentTrackPosition.observeAsState(0L) - val currentTrackDuration by viewModel.currentTrackDuration.observeAsState(0f) + val currentTrackIndex by viewModel.currentChapterIndex.observeAsState(0) + val currentTrackPosition by viewModel.currentChapterPosition.observeAsState(0L) + val currentTrackDuration by viewModel.currentChapterDuration.observeAsState(0f) val book by viewModel.book.observeAsState() val chapters = book?.chapters ?: emptyList() diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackDetailsComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackDetailsComposable.kt index c77700fb..f88a6c63 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackDetailsComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackDetailsComposable.kt @@ -37,7 +37,7 @@ fun TrackDetailsComposable( modifier: Modifier = Modifier ) { - val currentTrackIndex by viewModel.currentTrackIndex.observeAsState(0) + val currentTrackIndex by viewModel.currentChapterIndex.observeAsState(0) val book by viewModel.book.observeAsState() val context = LocalContext.current diff --git a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt index d75489cf..207a8d78 100644 --- a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt +++ b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt @@ -30,24 +30,23 @@ class PlayerViewModel @Inject constructor( val isPlaying: LiveData = mediaRepository.isPlaying - private val _currentTrackIndex = MediatorLiveData().apply { + private val _currentChapterIndex = MediatorLiveData().apply { addSource(mediaItemPosition) { updateCurrentTrackData() } addSource(book) { updateCurrentTrackData() } } - val currentTrackIndex: LiveData = _currentTrackIndex + val currentChapterIndex: LiveData = _currentChapterIndex - private val _currentTrackPosition = MediatorLiveData().apply { + private val _currentChapterPosition = MediatorLiveData().apply { addSource(mediaItemPosition) { updateCurrentTrackData() } addSource(book) { updateCurrentTrackData() } } - val currentTrackPosition: LiveData = _currentTrackPosition + val currentChapterPosition: LiveData = _currentChapterPosition - private val _currentTrackDuration = MediatorLiveData().apply { + private val _currentChapterDuration = MediatorLiveData().apply { addSource(mediaItemPosition) { updateCurrentTrackData() } addSource(book) { updateCurrentTrackData() } } - - val currentTrackDuration: LiveData = _currentTrackDuration + val currentChapterDuration: LiveData = _currentChapterDuration fun togglePlayingQueue() { _playingQueueExpanded.value = !(_playingQueueExpanded.value ?: false) @@ -57,47 +56,17 @@ class PlayerViewModel @Inject constructor( val book = book.value ?: return val position = mediaItemPosition.value ?: return - val trackIndex = calculateTrackIndex(position) - _currentTrackIndex.value = trackIndex - _currentTrackPosition.value = calculateTrackPosition(position) - _currentTrackDuration.value = book + val trackIndex = calculateChapterIndex(position) + val trackPosition = calculateChapterPosition(position) + + _currentChapterIndex.value = trackIndex + _currentChapterPosition.value = trackPosition + _currentChapterDuration.value = book .chapters .getOrNull(trackIndex) ?.let { it.end - it.start } - ?.toFloat() ?: 0f - } - - fun calculateTrackIndex(position: Long): Int { - val currentBook = book.value ?: return 0 - - return currentBook - .chapters - .foldIndexed(0) { index, accumulatedDuration, file -> - val newAccumulatedDuration = accumulatedDuration + (file.end - file.start) - - if (position < newAccumulatedDuration) { - return index - } - - newAccumulatedDuration.toInt() - } - } - - fun calculateTrackPosition(overallPosition: Long): Long { - val currentBook = book.value ?: return 0L - - var accumulatedDuration = 0.0 - currentBook - .chapters - .forEach { file -> - val fileDuration = (file.end - file.start) - if (overallPosition < accumulatedDuration + fileDuration) { - return (overallPosition - accumulatedDuration).toLong() - } - accumulatedDuration += fileDuration - } - - return (overallPosition - accumulatedDuration).toLong() + ?.toFloat() + ?: 0f } fun preparePlayback(bookId: String) { @@ -119,20 +88,41 @@ class PlayerViewModel @Inject constructor( } } - fun seekTo(position: Float) { - mediaRepository.seekTo(position) + fun seekTo(chapterPosition: Float) { + val absolutePosition = currentChapterIndex.value + ?.let { chapterIndex -> + book + .value + ?.chapters + ?.get(chapterIndex) + ?.start + ?.toFloat() + } + ?.let { it + chapterPosition } ?: return + + mediaRepository.seekTo(absolutePosition) } fun setChapter(index: Int) { - mediaRepository.setTrack(index) + val chapterStartsAt = book + .value + ?.chapters + ?.get(index) + ?.start + ?.toFloat() + ?: 0f + + mediaRepository.seekTo(chapterStartsAt) } fun nextTrack() { - mediaRepository.nextTrack() + val nextChapterIndex = currentChapterIndex.value?.let { it + 1 } ?: return + setChapter(nextChapterIndex) } fun previousTrack() { - mediaRepository.previousTrack() + val previousChapterIndex = currentChapterIndex.value?.let { it - 1 } ?: return + setChapter(previousChapterIndex) } fun togglePlayPause() { @@ -150,4 +140,38 @@ class PlayerViewModel @Inject constructor( private fun pause() { mediaRepository.pauseAudio() } + + + private fun calculateChapterIndex(position: Long): Int { + val currentBook = book.value ?: return 0 + + return currentBook + .chapters + .foldIndexed(0) { index, accumulatedDuration, file -> + val newAccumulatedDuration = accumulatedDuration + (file.end - file.start) + + if (position < newAccumulatedDuration) { + return index + } + + newAccumulatedDuration.toInt() + } + } + + private fun calculateChapterPosition(overallPosition: Long): Long { + val currentBook = book.value ?: return 0L + + var accumulatedDuration = 0.0 + currentBook + .chapters + .forEach { file -> + val fileDuration = (file.end - file.start) + if (overallPosition < accumulatedDuration + fileDuration) { + return (overallPosition - accumulatedDuration).toLong() + } + accumulatedDuration += fileDuration + } + + return (overallPosition - accumulatedDuration).toLong() + } } \ No newline at end of file From eb80d07991a63b19a25509dac416e60cb1b0b02f Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 15 Oct 2024 17:37:03 +0200 Subject: [PATCH 06/20] chapters --- .../lissen/viewmodel/PlayerViewModel.kt | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt index 207a8d78..8c2a88c3 100644 --- a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt +++ b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt @@ -147,31 +147,32 @@ class PlayerViewModel @Inject constructor( return currentBook .chapters - .foldIndexed(0) { index, accumulatedDuration, file -> - val newAccumulatedDuration = accumulatedDuration + (file.end - file.start) + .foldIndexed(0) { index, acc, file -> + val newAccumulated = acc + (file.end - file.start) - if (position < newAccumulatedDuration) { + if (position < newAccumulated) { return index } - newAccumulatedDuration.toInt() + newAccumulated.toInt() } } private fun calculateChapterPosition(overallPosition: Long): Long { val currentBook = book.value ?: return 0L - var accumulatedDuration = 0.0 - currentBook - .chapters - .forEach { file -> - val fileDuration = (file.end - file.start) - if (overallPosition < accumulatedDuration + fileDuration) { - return (overallPosition - accumulatedDuration).toLong() + val accumulatedDuration = currentBook.chapters + .fold(0L) { acc, chapter -> + val chapterDuration = chapter.end - chapter.start + val newAccumulated = acc + chapterDuration.toLong() + + if (overallPosition < newAccumulated) { + return overallPosition - acc } - accumulatedDuration += fileDuration + + newAccumulated } - return (overallPosition - accumulatedDuration).toLong() + return overallPosition - accumulatedDuration } } \ No newline at end of file From ad8d512909de9ffbe3c5f220704d26333932331b Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 15 Oct 2024 17:47:31 +0200 Subject: [PATCH 07/20] chapters --- .../main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt index 8c2a88c3..757117ad 100644 --- a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt +++ b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt @@ -32,19 +32,16 @@ class PlayerViewModel @Inject constructor( private val _currentChapterIndex = MediatorLiveData().apply { addSource(mediaItemPosition) { updateCurrentTrackData() } - addSource(book) { updateCurrentTrackData() } } val currentChapterIndex: LiveData = _currentChapterIndex private val _currentChapterPosition = MediatorLiveData().apply { addSource(mediaItemPosition) { updateCurrentTrackData() } - addSource(book) { updateCurrentTrackData() } } val currentChapterPosition: LiveData = _currentChapterPosition private val _currentChapterDuration = MediatorLiveData().apply { addSource(mediaItemPosition) { updateCurrentTrackData() } - addSource(book) { updateCurrentTrackData() } } val currentChapterDuration: LiveData = _currentChapterDuration From 8a41ec17229c919ace83e733b935b102557e158d Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 15 Oct 2024 17:47:54 +0200 Subject: [PATCH 08/20] chapters --- .../java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt index 757117ad..064be9a8 100644 --- a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt +++ b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt @@ -31,17 +31,17 @@ class PlayerViewModel @Inject constructor( val isPlaying: LiveData = mediaRepository.isPlaying private val _currentChapterIndex = MediatorLiveData().apply { - addSource(mediaItemPosition) { updateCurrentTrackData() } + addSource(mediaItemPosition) { updateChapterProgress() } } val currentChapterIndex: LiveData = _currentChapterIndex private val _currentChapterPosition = MediatorLiveData().apply { - addSource(mediaItemPosition) { updateCurrentTrackData() } + addSource(mediaItemPosition) { updateChapterProgress() } } val currentChapterPosition: LiveData = _currentChapterPosition private val _currentChapterDuration = MediatorLiveData().apply { - addSource(mediaItemPosition) { updateCurrentTrackData() } + addSource(mediaItemPosition) { updateChapterProgress() } } val currentChapterDuration: LiveData = _currentChapterDuration @@ -49,7 +49,7 @@ class PlayerViewModel @Inject constructor( _playingQueueExpanded.value = !(_playingQueueExpanded.value ?: false) } - private fun updateCurrentTrackData() { + private fun updateChapterProgress() { val book = book.value ?: return val position = mediaItemPosition.value ?: return From 39bc425abcbf0067891e58b93a029db0c63323de Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 15 Oct 2024 17:52:30 +0200 Subject: [PATCH 09/20] chapters --- .../lissen/playback/service/PlaybackService.kt | 2 -- .../org/grakovne/lissen/viewmodel/PlayerViewModel.kt | 11 +++++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt b/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt index 48662414..f4922573 100644 --- a/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt +++ b/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt @@ -111,8 +111,6 @@ class AudioPlayerService : MediaSessionService() { exoPlayer.playWhenReady = false withContext(Dispatchers.IO) { - //playbackSynchronizationService.stopPlaybackSynchronization() - val prepareQueue = async { val playingQueue = book .files diff --git a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt index 064be9a8..8c2a88c3 100644 --- a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt +++ b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt @@ -31,17 +31,20 @@ class PlayerViewModel @Inject constructor( val isPlaying: LiveData = mediaRepository.isPlaying private val _currentChapterIndex = MediatorLiveData().apply { - addSource(mediaItemPosition) { updateChapterProgress() } + addSource(mediaItemPosition) { updateCurrentTrackData() } + addSource(book) { updateCurrentTrackData() } } val currentChapterIndex: LiveData = _currentChapterIndex private val _currentChapterPosition = MediatorLiveData().apply { - addSource(mediaItemPosition) { updateChapterProgress() } + addSource(mediaItemPosition) { updateCurrentTrackData() } + addSource(book) { updateCurrentTrackData() } } val currentChapterPosition: LiveData = _currentChapterPosition private val _currentChapterDuration = MediatorLiveData().apply { - addSource(mediaItemPosition) { updateChapterProgress() } + addSource(mediaItemPosition) { updateCurrentTrackData() } + addSource(book) { updateCurrentTrackData() } } val currentChapterDuration: LiveData = _currentChapterDuration @@ -49,7 +52,7 @@ class PlayerViewModel @Inject constructor( _playingQueueExpanded.value = !(_playingQueueExpanded.value ?: false) } - private fun updateChapterProgress() { + private fun updateCurrentTrackData() { val book = book.value ?: return val position = mediaItemPosition.value ?: return From 2c0b4b5a8d8613fcd6e2d784e5b50ffb3e8316ad Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 15 Oct 2024 17:58:00 +0200 Subject: [PATCH 10/20] chapters --- .../org/grakovne/lissen/playback/MediaRepository.kt | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt b/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt index e2022d50..71942a4e 100644 --- a/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt +++ b/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt @@ -163,14 +163,11 @@ class MediaRepository @Inject constructor(@ApplicationContext private val contex handler.postDelayed(object : Runnable { override fun run() { val currentIndex = mediaController.currentMediaItemIndex - val previousFilesDuration = - detailedBook.files.take(currentIndex).sumOf { it.duration } - val currentFilePosition = - mediaController.currentPosition / 1000.0 + val accumulated = detailedBook.files.take(currentIndex).sumOf { it.duration } + val currentFilePosition = mediaController.currentPosition / 1000.0 - _mediaItemPosition.value = (previousFilesDuration + currentFilePosition).toLong() - - handler.postDelayed(this, 500) + _mediaItemPosition.value = (accumulated + currentFilePosition).toLong() + handler.postDelayed(this, 1000) } }, 500) } From d76e9e5b291472585176b476fd0ae25cc6e4a530 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 15 Oct 2024 17:59:04 +0200 Subject: [PATCH 11/20] chapters --- .../lissen/playback/MediaRepository.kt | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt b/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt index 71942a4e..a7fd39f6 100644 --- a/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt +++ b/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt @@ -124,30 +124,6 @@ class MediaRepository @Inject constructor(@ApplicationContext private val contex context.startService(intent) } - fun nextTrack() { - mediaController.run { - if (currentMediaItemIndex + 1 < currentTimeline.windowCount) { - seekTo(currentMediaItemIndex + 1, 0) - _mediaItemPosition.value = 0 - } - } - } - - fun previousTrack() { - mediaController.run { - if (currentMediaItemIndex > 0) { - seekTo(currentMediaItemIndex - 1, 0) - _mediaItemPosition.value = 0 - } - } - } - - fun setTrack(index: Int) { - if (index in 0 until mediaController.currentTimeline.windowCount) { - mediaController.seekTo(index, 0) - } - } - fun seekTo(position: Float) { val intent = Intent(context, AudioPlayerService::class.java).apply { action = ACTION_SEEK_TO From 52300aff30856948ec38e85508deb6edd2cd9be5 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 15 Oct 2024 18:15:06 +0200 Subject: [PATCH 12/20] chapters --- .../composable/TrackControlComposable.kt | 21 +++++++++---------- .../lissen/viewmodel/PlayerViewModel.kt | 10 ++++----- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt index 338c8234..6603c49d 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt @@ -44,20 +44,19 @@ fun TrackControlComposable( val currentTrackIndex by viewModel.currentChapterIndex.observeAsState(0) val currentTrackPosition by viewModel.currentChapterPosition.observeAsState(0L) - val currentTrackDuration by viewModel.currentChapterDuration.observeAsState(0f) + val currentTrackDuration by viewModel.currentChapterDuration.observeAsState(0L) val book by viewModel.book.observeAsState() val chapters = book?.chapters ?: emptyList() - var sliderPosition by remember { mutableFloatStateOf(0f) } + var sliderPosition by remember { mutableStateOf(0L) } var isDragging by remember { mutableStateOf(false) } - - LaunchedEffect(currentTrackPosition) { + LaunchedEffect(currentTrackPosition, currentTrackIndex, currentTrackDuration) { when (isDragging) { true -> {} false -> { - sliderPosition = currentTrackPosition.toFloat() + sliderPosition = currentTrackPosition } } } @@ -67,16 +66,16 @@ fun TrackControlComposable( ) { Slider( - value = sliderPosition, + value = sliderPosition.toFloat(), onValueChange = { newPosition -> isDragging = true - sliderPosition = newPosition + sliderPosition = newPosition.toLong() }, onValueChangeFinished = { isDragging = false viewModel.seekTo(sliderPosition) }, - valueRange = 0f..(currentTrackDuration), + valueRange = 0f..currentTrackDuration.toFloat(), colors = SliderDefaults .colors( thumbColor = colorScheme.primary, @@ -97,7 +96,7 @@ fun TrackControlComposable( ) Text( text = "-${ - maxOf(0f, currentTrackDuration - currentTrackPosition).toInt().formatFully() + maxOf(0L, currentTrackDuration - currentTrackPosition).toInt().formatFully() }", style = MaterialTheme.typography.bodySmall, color = colorScheme.onBackground.copy(alpha = 0.6f) @@ -129,7 +128,7 @@ fun TrackControlComposable( IconButton( onClick = { - viewModel.seekTo(maxOf(0f, currentTrackPosition - 10f)) + viewModel.seekTo(maxOf(0L, currentTrackPosition - 10L)) }, modifier = Modifier.weight(1f) ) { @@ -157,7 +156,7 @@ fun TrackControlComposable( IconButton( onClick = { - viewModel.seekTo(minOf(currentTrackDuration, currentTrackPosition + 30f)) + viewModel.seekTo(minOf(currentTrackDuration, currentTrackPosition + 30L)) }, modifier = Modifier.weight(1f) ) { diff --git a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt index 8c2a88c3..1e384e94 100644 --- a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt +++ b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt @@ -42,11 +42,11 @@ class PlayerViewModel @Inject constructor( } val currentChapterPosition: LiveData = _currentChapterPosition - private val _currentChapterDuration = MediatorLiveData().apply { + private val _currentChapterDuration = MediatorLiveData().apply { addSource(mediaItemPosition) { updateCurrentTrackData() } addSource(book) { updateCurrentTrackData() } } - val currentChapterDuration: LiveData = _currentChapterDuration + val currentChapterDuration: LiveData = _currentChapterDuration fun togglePlayingQueue() { _playingQueueExpanded.value = !(_playingQueueExpanded.value ?: false) @@ -65,8 +65,8 @@ class PlayerViewModel @Inject constructor( .chapters .getOrNull(trackIndex) ?.let { it.end - it.start } - ?.toFloat() - ?: 0f + ?.toLong() + ?: 0L } fun preparePlayback(bookId: String) { @@ -88,7 +88,7 @@ class PlayerViewModel @Inject constructor( } } - fun seekTo(chapterPosition: Float) { + fun seekTo(chapterPosition: Long) { val absolutePosition = currentChapterIndex.value ?.let { chapterIndex -> book From 1d76ff126aa41513fd86f5794acda3e4a1f3a8c3 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 15 Oct 2024 18:17:01 +0200 Subject: [PATCH 13/20] chapters --- .../main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt index 1e384e94..55e2a5dd 100644 --- a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt +++ b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt @@ -150,7 +150,7 @@ class PlayerViewModel @Inject constructor( .foldIndexed(0) { index, acc, file -> val newAccumulated = acc + (file.end - file.start) - if (position < newAccumulated) { + if (position < newAccumulated.toLong()) { return index } From 1ba55c09e28f63c7c274fe0a67eaa11cc4c2cc90 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 15 Oct 2024 18:25:09 +0200 Subject: [PATCH 14/20] chapters --- .../LibraryItemIdResponseConverter.kt | 3 +- .../grakovne/lissen/domain/DetailedBook.kt | 1 + .../lissen/playback/MediaRepository.kt | 2 +- .../playback/service/PlaybackService.kt | 6 +-- .../composable/PlaylistItemComposable.kt | 2 +- .../lissen/viewmodel/PlayerViewModel.kt | 45 +++++++++---------- 6 files changed, 28 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/converter/LibraryItemIdResponseConverter.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/converter/LibraryItemIdResponseConverter.kt index 0d00278c..dce708fa 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/converter/LibraryItemIdResponseConverter.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/converter/LibraryItemIdResponseConverter.kt @@ -42,7 +42,8 @@ class LibraryItemIdResponseConverter @Inject constructor() { start = it.start, end = it.end, title = it.title, - id = it.id + id = it.id, + duration = it.end - it.start ) }, progress = progressResponse diff --git a/app/src/main/java/org/grakovne/lissen/domain/DetailedBook.kt b/app/src/main/java/org/grakovne/lissen/domain/DetailedBook.kt index 23270e31..09a7a81f 100644 --- a/app/src/main/java/org/grakovne/lissen/domain/DetailedBook.kt +++ b/app/src/main/java/org/grakovne/lissen/domain/DetailedBook.kt @@ -25,6 +25,7 @@ data class MediaProgress( ) : Serializable data class BookChapter( + val duration: Double, val start: Double, val end: Double, val title: String, diff --git a/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt b/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt index a7fd39f6..c938b958 100644 --- a/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt +++ b/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt @@ -124,7 +124,7 @@ class MediaRepository @Inject constructor(@ApplicationContext private val contex context.startService(intent) } - fun seekTo(position: Float) { + fun seekTo(position: Long) { val intent = Intent(context, AudioPlayerService::class.java).apply { action = ACTION_SEEK_TO diff --git a/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt b/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt index f4922573..3183e317 100644 --- a/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt +++ b/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt @@ -83,7 +83,7 @@ class AudioPlayerService : MediaSessionService() { ACTION_SEEK_TO -> { val book = intent.getSerializableExtra(BOOK_EXTRA) as? DetailedBook - val position = intent.getFloatExtra(POSITION, 0f) + val position = intent.getLongExtra(POSITION, 0L) book?.let { seek(it.files, position) } return START_NOT_STICKY } @@ -152,7 +152,7 @@ class AudioPlayerService : MediaSessionService() { private fun seek( chapters: List, - position: Float? + position: Long? ) { when (position) { null -> exoPlayer.seekTo(0, 0) @@ -169,7 +169,7 @@ class AudioPlayerService : MediaSessionService() { private fun setPlaybackProgress( chapters: List, progress: MediaProgress? - ) = seek(chapters, progress?.currentTime?.toFloat()) + ) = seek(chapters, progress?.currentTime?.toLong()) companion object { const val ACTION_PLAY = "org.grakovne.lissen.player.service.PLAY" diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlaylistItemComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlaylistItemComposable.kt index 6028b12c..b976bf87 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlaylistItemComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlaylistItemComposable.kt @@ -66,7 +66,7 @@ fun PlaylistItemComposable( modifier = Modifier.weight(1f) ) Text( - text = (track.end - track.start).toInt().formatLeadingMinutes(), + text = track.duration.toInt().formatLeadingMinutes(), style = MaterialTheme.typography.bodySmall, modifier = Modifier.padding(start = 8.dp), fontWeight = if (isSelected) FontWeight.Bold else FontWeight.Normal, diff --git a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt index 55e2a5dd..91a55c26 100644 --- a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt +++ b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt @@ -64,7 +64,7 @@ class PlayerViewModel @Inject constructor( _currentChapterDuration.value = book .chapters .getOrNull(trackIndex) - ?.let { it.end - it.start } + ?.duration ?.toLong() ?: 0L } @@ -96,7 +96,7 @@ class PlayerViewModel @Inject constructor( ?.chapters ?.get(chapterIndex) ?.start - ?.toFloat() + ?.toLong() } ?.let { it + chapterPosition } ?: return @@ -109,8 +109,8 @@ class PlayerViewModel @Inject constructor( ?.chapters ?.get(index) ?.start - ?.toFloat() - ?: 0f + ?.toLong() + ?: 0L mediaRepository.seekTo(chapterStartsAt) } @@ -144,35 +144,30 @@ class PlayerViewModel @Inject constructor( private fun calculateChapterIndex(position: Long): Int { val currentBook = book.value ?: return 0 + var accumulatedDuration = 0L - return currentBook - .chapters - .foldIndexed(0) { index, acc, file -> - val newAccumulated = acc + (file.end - file.start) - - if (position < newAccumulated.toLong()) { - return index - } - - newAccumulated.toInt() + for ((index, chapter) in currentBook.chapters.withIndex()) { + accumulatedDuration += chapter.duration.toLong() + if (position < accumulatedDuration) { + return index } + } + + return currentBook.chapters.size - 1 } private fun calculateChapterPosition(overallPosition: Long): Long { val currentBook = book.value ?: return 0L + var accumulatedDuration = 0L - val accumulatedDuration = currentBook.chapters - .fold(0L) { acc, chapter -> - val chapterDuration = chapter.end - chapter.start - val newAccumulated = acc + chapterDuration.toLong() - - if (overallPosition < newAccumulated) { - return overallPosition - acc - } - - newAccumulated + for (chapter in currentBook.chapters) { + val chapterEnd = accumulatedDuration + chapter.duration.toLong() + if (overallPosition < chapterEnd) { + return overallPosition - accumulatedDuration } + accumulatedDuration = chapterEnd + } - return overallPosition - accumulatedDuration + return 0L } } \ No newline at end of file From 26b029c6db0cd8c8c9bf3d4f3c2be61da95b41b8 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 15 Oct 2024 18:28:52 +0200 Subject: [PATCH 15/20] chapters --- .../org/grakovne/lissen/viewmodel/PlayerViewModel.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt index 91a55c26..5db6193e 100644 --- a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt +++ b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt @@ -144,10 +144,10 @@ class PlayerViewModel @Inject constructor( private fun calculateChapterIndex(position: Long): Int { val currentBook = book.value ?: return 0 - var accumulatedDuration = 0L + var accumulatedDuration = 0.0 for ((index, chapter) in currentBook.chapters.withIndex()) { - accumulatedDuration += chapter.duration.toLong() + accumulatedDuration += chapter.duration if (position < accumulatedDuration) { return index } @@ -158,12 +158,12 @@ class PlayerViewModel @Inject constructor( private fun calculateChapterPosition(overallPosition: Long): Long { val currentBook = book.value ?: return 0L - var accumulatedDuration = 0L + var accumulatedDuration = 0.0 for (chapter in currentBook.chapters) { - val chapterEnd = accumulatedDuration + chapter.duration.toLong() + val chapterEnd = accumulatedDuration + chapter.duration if (overallPosition < chapterEnd) { - return overallPosition - accumulatedDuration + return (overallPosition - accumulatedDuration).toLong() } accumulatedDuration = chapterEnd } From 3230381e18a552a75de8900c0afc3fa6a843cd40 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 15 Oct 2024 18:37:56 +0200 Subject: [PATCH 16/20] chapters --- .../java/org/grakovne/lissen/playback/MediaRepository.kt | 2 +- .../grakovne/lissen/playback/service/PlaybackService.kt | 8 ++++---- .../java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt | 4 +--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt b/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt index c938b958..ad139fd4 100644 --- a/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt +++ b/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt @@ -124,7 +124,7 @@ class MediaRepository @Inject constructor(@ApplicationContext private val contex context.startService(intent) } - fun seekTo(position: Long) { + fun seekTo(position: Double) { val intent = Intent(context, AudioPlayerService::class.java).apply { action = ACTION_SEEK_TO diff --git a/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt b/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt index 3183e317..8b829b5f 100644 --- a/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt +++ b/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt @@ -83,7 +83,7 @@ class AudioPlayerService : MediaSessionService() { ACTION_SEEK_TO -> { val book = intent.getSerializableExtra(BOOK_EXTRA) as? DetailedBook - val position = intent.getLongExtra(POSITION, 0L) + val position = intent.getDoubleExtra(POSITION, 0.0) book?.let { seek(it.files, position) } return START_NOT_STICKY } @@ -152,12 +152,12 @@ class AudioPlayerService : MediaSessionService() { private fun seek( chapters: List, - position: Long? + position: Double? ) { when (position) { null -> exoPlayer.seekTo(0, 0) else -> { - val duration = chapters.runningFold(0.0) { acc, chapter -> acc + chapter.duration } + val duration = chapters.runningFold(0.0) { acc, chapter -> acc + chapter.duration.toLong() } val targetChapter = duration.indexOfFirst { it > position } val chapterProgress = position - duration[targetChapter - 1] @@ -169,7 +169,7 @@ class AudioPlayerService : MediaSessionService() { private fun setPlaybackProgress( chapters: List, progress: MediaProgress? - ) = seek(chapters, progress?.currentTime?.toLong()) + ) = seek(chapters, progress?.currentTime) companion object { const val ACTION_PLAY = "org.grakovne.lissen.player.service.PLAY" diff --git a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt index 5db6193e..3dca233d 100644 --- a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt +++ b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt @@ -96,7 +96,6 @@ class PlayerViewModel @Inject constructor( ?.chapters ?.get(chapterIndex) ?.start - ?.toLong() } ?.let { it + chapterPosition } ?: return @@ -109,8 +108,7 @@ class PlayerViewModel @Inject constructor( ?.chapters ?.get(index) ?.start - ?.toLong() - ?: 0L + ?: 0.0 mediaRepository.seekTo(chapterStartsAt) } From 848479b7276c55fbf72ce40d2679dc7bbe6c4d39 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 15 Oct 2024 18:41:35 +0200 Subject: [PATCH 17/20] chapters --- .../lissen/playback/MediaRepository.kt | 8 +++--- .../composable/TrackControlComposable.kt | 14 +++++------ .../lissen/viewmodel/PlayerViewModel.kt | 25 +++++++++---------- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt b/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt index ad139fd4..f3633f3a 100644 --- a/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt +++ b/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt @@ -41,8 +41,8 @@ class MediaRepository @Inject constructor(@ApplicationContext private val contex private val _isPlaybackReady = MutableLiveData(false) val isPlaybackReady: LiveData = _isPlaybackReady - private val _mediaItemPosition = MutableLiveData() - val mediaItemPosition: LiveData = _mediaItemPosition + private val _mediaItemPosition = MutableLiveData() + val mediaItemPosition: LiveData = _mediaItemPosition private val _playingBook = MutableLiveData() val playingBook: LiveData = _playingBook @@ -99,7 +99,7 @@ class MediaRepository @Inject constructor(@ApplicationContext private val contex } private fun preparePlay(book: DetailedBook) { - _mediaItemPosition.postValue(0) + _mediaItemPosition.postValue(0.0) _isPlaying.postValue(false) val intent = Intent(context, AudioPlayerService::class.java).apply { @@ -142,7 +142,7 @@ class MediaRepository @Inject constructor(@ApplicationContext private val contex val accumulated = detailedBook.files.take(currentIndex).sumOf { it.duration } val currentFilePosition = mediaController.currentPosition / 1000.0 - _mediaItemPosition.value = (accumulated + currentFilePosition).toLong() + _mediaItemPosition.value = (accumulated + currentFilePosition) handler.postDelayed(this, 1000) } }, 500) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt index 6603c49d..a5e658c3 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt @@ -43,13 +43,13 @@ fun TrackControlComposable( val isPlaying by viewModel.isPlaying.observeAsState(false) val currentTrackIndex by viewModel.currentChapterIndex.observeAsState(0) - val currentTrackPosition by viewModel.currentChapterPosition.observeAsState(0L) - val currentTrackDuration by viewModel.currentChapterDuration.observeAsState(0L) + val currentTrackPosition by viewModel.currentChapterPosition.observeAsState(0.0) + val currentTrackDuration by viewModel.currentChapterDuration.observeAsState(0.0) val book by viewModel.book.observeAsState() val chapters = book?.chapters ?: emptyList() - var sliderPosition by remember { mutableStateOf(0L) } + var sliderPosition by remember { mutableStateOf(0.0) } var isDragging by remember { mutableStateOf(false) } LaunchedEffect(currentTrackPosition, currentTrackIndex, currentTrackDuration) { @@ -69,7 +69,7 @@ fun TrackControlComposable( value = sliderPosition.toFloat(), onValueChange = { newPosition -> isDragging = true - sliderPosition = newPosition.toLong() + sliderPosition = newPosition.toDouble() }, onValueChangeFinished = { isDragging = false @@ -96,7 +96,7 @@ fun TrackControlComposable( ) Text( text = "-${ - maxOf(0L, currentTrackDuration - currentTrackPosition).toInt().formatFully() + maxOf(0.0, currentTrackDuration - currentTrackPosition).toInt().formatFully() }", style = MaterialTheme.typography.bodySmall, color = colorScheme.onBackground.copy(alpha = 0.6f) @@ -128,7 +128,7 @@ fun TrackControlComposable( IconButton( onClick = { - viewModel.seekTo(maxOf(0L, currentTrackPosition - 10L)) + viewModel.seekTo(maxOf(0.0, currentTrackPosition - 10L)) }, modifier = Modifier.weight(1f) ) { @@ -156,7 +156,7 @@ fun TrackControlComposable( IconButton( onClick = { - viewModel.seekTo(minOf(currentTrackDuration, currentTrackPosition + 30L)) + viewModel.seekTo(minOf(currentTrackDuration, currentTrackPosition + 30.0)) }, modifier = Modifier.weight(1f) ) { diff --git a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt index 3dca233d..2ca327bc 100644 --- a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt +++ b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt @@ -22,7 +22,7 @@ class PlayerViewModel @Inject constructor( val book: LiveData = mediaRepository.playingBook - private val mediaItemPosition: LiveData = mediaRepository.mediaItemPosition + private val mediaItemPosition: LiveData = mediaRepository.mediaItemPosition private val _playingQueueExpanded = MutableLiveData(false) val playingQueueExpanded: LiveData = _playingQueueExpanded @@ -36,17 +36,17 @@ class PlayerViewModel @Inject constructor( } val currentChapterIndex: LiveData = _currentChapterIndex - private val _currentChapterPosition = MediatorLiveData().apply { + private val _currentChapterPosition = MediatorLiveData().apply { addSource(mediaItemPosition) { updateCurrentTrackData() } addSource(book) { updateCurrentTrackData() } } - val currentChapterPosition: LiveData = _currentChapterPosition + val currentChapterPosition: LiveData = _currentChapterPosition - private val _currentChapterDuration = MediatorLiveData().apply { + private val _currentChapterDuration = MediatorLiveData().apply { addSource(mediaItemPosition) { updateCurrentTrackData() } addSource(book) { updateCurrentTrackData() } } - val currentChapterDuration: LiveData = _currentChapterDuration + val currentChapterDuration: LiveData = _currentChapterDuration fun togglePlayingQueue() { _playingQueueExpanded.value = !(_playingQueueExpanded.value ?: false) @@ -65,8 +65,7 @@ class PlayerViewModel @Inject constructor( .chapters .getOrNull(trackIndex) ?.duration - ?.toLong() - ?: 0L + ?: 0.0 } fun preparePlayback(bookId: String) { @@ -88,7 +87,7 @@ class PlayerViewModel @Inject constructor( } } - fun seekTo(chapterPosition: Long) { + fun seekTo(chapterPosition: Double) { val absolutePosition = currentChapterIndex.value ?.let { chapterIndex -> book @@ -140,7 +139,7 @@ class PlayerViewModel @Inject constructor( } - private fun calculateChapterIndex(position: Long): Int { + private fun calculateChapterIndex(position: Double): Int { val currentBook = book.value ?: return 0 var accumulatedDuration = 0.0 @@ -154,18 +153,18 @@ class PlayerViewModel @Inject constructor( return currentBook.chapters.size - 1 } - private fun calculateChapterPosition(overallPosition: Long): Long { - val currentBook = book.value ?: return 0L + private fun calculateChapterPosition(overallPosition: Double): Double { + val currentBook = book.value ?: return 0.0 var accumulatedDuration = 0.0 for (chapter in currentBook.chapters) { val chapterEnd = accumulatedDuration + chapter.duration if (overallPosition < chapterEnd) { - return (overallPosition - accumulatedDuration).toLong() + return (overallPosition - accumulatedDuration) } accumulatedDuration = chapterEnd } - return 0L + return 0.0 } } \ No newline at end of file From 6a9bb60480d686d7f50be47d6729c7a350eb3859 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 15 Oct 2024 18:43:48 +0200 Subject: [PATCH 18/20] chapters --- .../org/grakovne/lissen/playback/service/PlaybackService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt b/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt index 8b829b5f..6975e05e 100644 --- a/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt +++ b/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt @@ -157,7 +157,7 @@ class AudioPlayerService : MediaSessionService() { when (position) { null -> exoPlayer.seekTo(0, 0) else -> { - val duration = chapters.runningFold(0.0) { acc, chapter -> acc + chapter.duration.toLong() } + val duration = chapters.runningFold(0.0) { acc, chapter -> acc + chapter.duration } val targetChapter = duration.indexOfFirst { it > position } val chapterProgress = position - duration[targetChapter - 1] From 6ee1c9933d448d4c2d08bf6da0ef17f9c2c9168f Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 15 Oct 2024 18:50:44 +0200 Subject: [PATCH 19/20] chapters --- .../ui/screens/player/composable/TrackControlComposable.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt index a5e658c3..97a5ba4b 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/TrackControlComposable.kt @@ -25,6 +25,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.runtime.mutableDoubleStateOf import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -49,7 +50,7 @@ fun TrackControlComposable( val book by viewModel.book.observeAsState() val chapters = book?.chapters ?: emptyList() - var sliderPosition by remember { mutableStateOf(0.0) } + var sliderPosition by remember { mutableDoubleStateOf(0.0) } var isDragging by remember { mutableStateOf(false) } LaunchedEffect(currentTrackPosition, currentTrackIndex, currentTrackDuration) { From 31ccabf3fa7cfc8732c6ecb4e18b7499b9f0f807 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 15 Oct 2024 18:55:16 +0200 Subject: [PATCH 20/20] chapters --- .../main/java/org/grakovne/lissen/playback/MediaRepository.kt | 2 ++ .../main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt b/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt index f3633f3a..c7dc03b0 100644 --- a/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt +++ b/app/src/main/java/org/grakovne/lissen/playback/MediaRepository.kt @@ -136,6 +136,8 @@ class MediaRepository @Inject constructor(@ApplicationContext private val contex } private fun startUpdatingProgress(detailedBook: DetailedBook) { + handler.removeCallbacksAndMessages(null) + handler.postDelayed(object : Runnable { override fun run() { val currentIndex = mediaController.currentMediaItemIndex diff --git a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt index 2ca327bc..42e3cd59 100644 --- a/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt +++ b/app/src/main/java/org/grakovne/lissen/viewmodel/PlayerViewModel.kt @@ -54,7 +54,7 @@ class PlayerViewModel @Inject constructor( private fun updateCurrentTrackData() { val book = book.value ?: return - val position = mediaItemPosition.value ?: return + val position = mediaRepository.mediaItemPosition.value ?: return val trackIndex = calculateChapterIndex(position) val trackPosition = calculateChapterPosition(position)