Twelve: Add loading progress bar to now playing fragment
Change-Id: I8a228dab1f39ed9cf3340198b0b5b5cee1a9f40f
diff --git a/app/src/main/java/org/lineageos/twelve/ext/Player.kt b/app/src/main/java/org/lineageos/twelve/ext/Player.kt
index 9ba4891..b7e525b 100644
--- a/app/src/main/java/org/lineageos/twelve/ext/Player.kt
+++ b/app/src/main/java/org/lineageos/twelve/ext/Player.kt
@@ -13,6 +13,7 @@
import androidx.media3.common.Tracks
import androidx.media3.common.util.UnstableApi
import kotlinx.coroutines.channels.awaitClose
+import org.lineageos.twelve.models.PlaybackState
import org.lineageos.twelve.models.RepeatMode
@OptIn(UnstableApi::class)
@@ -46,6 +47,21 @@
}
}
+fun Player.playbackStateFlow() = conflatedCallbackFlow {
+ val listener = object : Player.Listener {
+ override fun onPlaybackStateChanged(playbackState: Int) {
+ trySend(typedPlaybackState)
+ }
+ }
+
+ addListener(listener)
+ trySend(typedPlaybackState)
+
+ awaitClose {
+ removeListener(listener)
+ }
+}
+
fun Player.isPlayingFlow() = conflatedCallbackFlow {
val listener = object : Player.Listener {
override fun onIsPlayingChanged(isPlaying: Boolean) {
@@ -150,3 +166,12 @@
RepeatMode.ALL -> Player.REPEAT_MODE_ALL
}
}
+
+val Player.typedPlaybackState: PlaybackState
+ get() = when (playbackState) {
+ Player.STATE_IDLE -> PlaybackState.IDLE
+ Player.STATE_BUFFERING -> PlaybackState.BUFFERING
+ Player.STATE_READY -> PlaybackState.READY
+ Player.STATE_ENDED -> PlaybackState.ENDED
+ else -> throw Exception("Unknown playback state")
+ }
diff --git a/app/src/main/java/org/lineageos/twelve/fragments/NowPlayingFragment.kt b/app/src/main/java/org/lineageos/twelve/fragments/NowPlayingFragment.kt
index 58962a1..28c3ed4 100644
--- a/app/src/main/java/org/lineageos/twelve/fragments/NowPlayingFragment.kt
+++ b/app/src/main/java/org/lineageos/twelve/fragments/NowPlayingFragment.kt
@@ -35,12 +35,14 @@
import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView
+import com.google.android.material.progressindicator.LinearProgressIndicator
import com.google.android.material.slider.Slider
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import org.lineageos.twelve.R
import org.lineageos.twelve.TwelveApplication
import org.lineageos.twelve.ext.getViewProperty
+import org.lineageos.twelve.models.PlaybackState
import org.lineageos.twelve.models.RepeatMode
import org.lineageos.twelve.utils.TimestampFormatter
import org.lineageos.twelve.viewmodels.NowPlayingViewModel
@@ -66,6 +68,7 @@
private val equalizerMaterialButton by getViewProperty<MaterialButton>(R.id.equalizerMaterialButton)
private val fileTypeMaterialCardView by getViewProperty<MaterialCardView>(R.id.fileTypeMaterialCardView)
private val fileTypeTextView by getViewProperty<TextView>(R.id.fileTypeTextView)
+ private val linearProgressIndicator by getViewProperty<LinearProgressIndicator>(R.id.linearProgressIndicator)
private val moreMaterialButton by getViewProperty<MaterialButton>(R.id.moreMaterialButton)
private val nestedScrollView by getViewProperty<NestedScrollView>(R.id.nestedScrollView)
private val nextTrackMaterialButton by getViewProperty<MaterialButton>(R.id.nextTrackMaterialButton)
@@ -193,6 +196,14 @@
}
launch {
+ viewModel.playbackState.collectLatest { playbackState ->
+ playbackState?.let {
+ linearProgressIndicator.isVisible = it == PlaybackState.BUFFERING
+ }
+ }
+ }
+
+ launch {
viewModel.mediaItem.collectLatest { mediaItem ->
addOrRemoveFromPlaylistsMaterialButton.setOnClickListener {
mediaItem?.localConfiguration?.uri?.let { uri ->
diff --git a/app/src/main/java/org/lineageos/twelve/models/PlaybackState.kt b/app/src/main/java/org/lineageos/twelve/models/PlaybackState.kt
new file mode 100644
index 0000000..998b71c
--- /dev/null
+++ b/app/src/main/java/org/lineageos/twelve/models/PlaybackState.kt
@@ -0,0 +1,16 @@
+/*
+ * SPDX-FileCopyrightText: 2024 The LineageOS Project
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.lineageos.twelve.models
+
+/**
+ * Player playback status.
+ */
+enum class PlaybackState {
+ IDLE,
+ BUFFERING,
+ READY,
+ ENDED,
+}
diff --git a/app/src/main/java/org/lineageos/twelve/viewmodels/NowPlayingViewModel.kt b/app/src/main/java/org/lineageos/twelve/viewmodels/NowPlayingViewModel.kt
index 8ca33ff..fd3ae41 100644
--- a/app/src/main/java/org/lineageos/twelve/viewmodels/NowPlayingViewModel.kt
+++ b/app/src/main/java/org/lineageos/twelve/viewmodels/NowPlayingViewModel.kt
@@ -28,6 +28,7 @@
import org.lineageos.twelve.ext.mediaMetadataFlow
import org.lineageos.twelve.ext.next
import org.lineageos.twelve.ext.playbackParametersFlow
+import org.lineageos.twelve.ext.playbackStateFlow
import org.lineageos.twelve.ext.repeatModeFlow
import org.lineageos.twelve.ext.shuffleModeFlow
import org.lineageos.twelve.ext.tracksFlow
@@ -71,6 +72,17 @@
)
@OptIn(ExperimentalCoroutinesApi::class)
+ val playbackState = mediaController
+ .filterNotNull()
+ .flatMapLatest { it.playbackStateFlow() }
+ .flowOn(Dispatchers.Main)
+ .stateIn(
+ viewModelScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = null
+ )
+
+ @OptIn(ExperimentalCoroutinesApi::class)
val isPlaying = mediaController
.filterNotNull()
.flatMapLatest { it.isPlayingFlow() }
diff --git a/app/src/main/res/layout/fragment_now_playing.xml b/app/src/main/res/layout/fragment_now_playing.xml
index 0f81781..09488ab 100644
--- a/app/src/main/res/layout/fragment_now_playing.xml
+++ b/app/src/main/res/layout/fragment_now_playing.xml
@@ -67,6 +67,16 @@
android:layout_height="wrap_content"
android:orientation="vertical">
+ <com.google.android.material.progressindicator.LinearProgressIndicator
+ android:id="@+id/linearProgressIndicator"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:indeterminate="true"
+ android:visibility="gone"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
<com.google.android.material.card.MaterialCardView
android:id="@+id/albumArtMaterialCardView"
style="@style/Widget.Material3.CardView.Filled"