Fix buffering progress bar display

This commit is contained in:
Christophe Henry 2023-10-02 20:30:09 +02:00
parent 1a050c2d73
commit 31908b6175
5 changed files with 72 additions and 22 deletions

View File

@ -180,7 +180,9 @@ dependencies {
implementation("androidx.preference:preference-ktx:1.2.0") implementation("androidx.preference:preference-ktx:1.2.0")
implementation("androidx.recyclerview:recyclerview:1.2.1") implementation("androidx.recyclerview:recyclerview:1.2.1")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
implementation("com.google.android.material:material:1.8.0") implementation("com.google.android.material:material:1.9.0") {
exclude("androidx.constraintlayout")
}
implementation("androidx.constraintlayout:constraintlayout:2.1.4") implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("com.google.android.exoplayer:exoplayer-core:2.18.1") implementation("com.google.android.exoplayer:exoplayer-core:2.18.1")

View File

@ -1,6 +1,7 @@
package audio.funkwhale.ffa.fragments package audio.funkwhale.ffa.fragments
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.Gravity import android.view.Gravity
import android.view.View import android.view.View
import android.widget.SeekBar import android.widget.SeekBar
@ -8,8 +9,13 @@ import android.widget.SeekBar.OnSeekBarChangeListener
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.asLiveData
import androidx.lifecycle.distinctUntilChanged import androidx.lifecycle.distinctUntilChanged
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.liveData
import androidx.lifecycle.map
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import audio.funkwhale.ffa.MainNavDirections import audio.funkwhale.ffa.MainNavDirections
import audio.funkwhale.ffa.R import audio.funkwhale.ffa.R
@ -99,10 +105,6 @@ class NowPlayingFragment: Fragment(R.layout.fragment_now_playing) {
CommandBus.get().collect { onCommand(it) } CommandBus.get().collect { onCommand(it) }
} }
lifecycleScope.launch(Dispatchers.Main) {
EventBus.get().collect { onEvent(it) }
}
lifecycleScope.launch(Dispatchers.Main) { lifecycleScope.launch(Dispatchers.Main) {
ProgressBus.get().collect { onProgress(it) } ProgressBus.get().collect { onProgress(it) }
} }
@ -135,12 +137,6 @@ class NowPlayingFragment: Fragment(R.layout.fragment_now_playing) {
else -> {} else -> {}
} }
private fun onEvent(event: Event): Unit = when (event) {
is Event.Buffering -> viewModel.isBuffering.postValue(event.value)
is Event.StateChanged -> viewModel.isPlaying.postValue(event.playing)
else -> {}
}
private fun onFavorite() { private fun onFavorite() {
val currentTrack = viewModel.currentTrack.value ?: return val currentTrack = viewModel.currentTrack.value ?: return

View File

@ -12,6 +12,7 @@ import android.media.MediaMetadata
import android.os.Build import android.os.Build
import android.os.IBinder import android.os.IBinder
import android.support.v4.media.MediaMetadataCompat import android.support.v4.media.MediaMetadataCompat
import android.util.Log
import android.view.KeyEvent import android.view.KeyEvent
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import androidx.media.session.MediaButtonReceiver import androidx.media.session.MediaButtonReceiver
@ -468,8 +469,11 @@ class PlayerService : Service() {
override fun onPlaybackStateChanged(playbackState: Int) { override fun onPlaybackStateChanged(playbackState: Int) {
super.onPlaybackStateChanged(playbackState) super.onPlaybackStateChanged(playbackState)
EventBus.send(Event.Buffering(playbackState == Player.STATE_BUFFERING))
when (playbackState) { when (playbackState) {
Player.STATE_BUFFERING -> {
EventBus.send(Event.Buffering(true))
}
Player.STATE_ENDED -> { Player.STATE_ENDED -> {
setPlaybackState(false) setPlaybackState(false)
@ -488,6 +492,10 @@ class PlayerService : Service() {
mediaControlsManager.remove() mediaControlsManager.remove()
} }
} }
Player.STATE_READY -> {
EventBus.send(Event.Buffering(false))
}
} }
} }

View File

@ -2,27 +2,48 @@ package audio.funkwhale.ffa.viewmodel
import android.app.Application import android.app.Application
import android.content.Context import android.content.Context
import android.graphics.Bitmap import android.util.Log
import android.graphics.drawable.Drawable
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
import androidx.core.graphics.drawable.toDrawable
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.asLiveData
import androidx.lifecycle.distinctUntilChanged import androidx.lifecycle.distinctUntilChanged
import androidx.lifecycle.liveData
import androidx.lifecycle.map import androidx.lifecycle.map
import androidx.lifecycle.viewModelScope
import audio.funkwhale.ffa.FFA import audio.funkwhale.ffa.FFA
import audio.funkwhale.ffa.R import audio.funkwhale.ffa.R
import audio.funkwhale.ffa.model.Track import audio.funkwhale.ffa.model.Track
import audio.funkwhale.ffa.utils.CoverArt import audio.funkwhale.ffa.utils.Event
import audio.funkwhale.ffa.utils.maybeNormalizeUrl import audio.funkwhale.ffa.utils.EventBus
import com.google.android.exoplayer2.Player import com.google.android.exoplayer2.Player
import com.squareup.picasso.Picasso import kotlinx.coroutines.FlowPreview
import com.squareup.picasso.Target import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
class NowPlayingViewModel(app: Application) : AndroidViewModel(app) { class NowPlayingViewModel(app: Application) : AndroidViewModel(app) {
val isBuffering = MutableLiveData(false) val isBuffering = EventBus.get()
val isPlaying = MutableLiveData(false) .filter { it is Event.Buffering }
.map { (it as Event.Buffering).value }
.stateIn(viewModelScope, SharingStarted.Lazily, false)
.asLiveData(viewModelScope.coroutineContext)
.distinctUntilChanged()
val isPlaying = EventBus.get()
.filter { it is Event.StateChanged }
.map { (it as Event.StateChanged).playing }
.stateIn(viewModelScope, SharingStarted.Lazily, false)
.asLiveData(viewModelScope.coroutineContext)
.distinctUntilChanged()
val repeatMode = MutableLiveData(0) val repeatMode = MutableLiveData(0)
val progress = MutableLiveData(0) val progress = MutableLiveData(0)
val currentTrack = MutableLiveData<Track?>(null) val currentTrack = MutableLiveData<Track?>(null)
@ -32,6 +53,7 @@ class NowPlayingViewModel(app: Application) : AndroidViewModel(app) {
// Calling distinctUntilChanged() prevents triggering an event when the track hasn't changed // Calling distinctUntilChanged() prevents triggering an event when the track hasn't changed
val currentTrackTitle = currentTrack.distinctUntilChanged().map { it?.title ?: "" } val currentTrackTitle = currentTrack.distinctUntilChanged().map { it?.title ?: "" }
val currentTrackArtist = currentTrack.distinctUntilChanged().map { it?.artist?.name ?: "" } val currentTrackArtist = currentTrack.distinctUntilChanged().map { it?.artist?.name ?: "" }
// Not calling distinctUntilChanged() here as we need to process every event // Not calling distinctUntilChanged() here as we need to process every event
val isCurrentTrackFavorite = currentTrack.map { val isCurrentTrackFavorite = currentTrack.map {
it?.favorite ?: false it?.favorite ?: false

View File

@ -13,6 +13,14 @@
<Constraint android:id="@id/constraint_layout_placeholder"> <Constraint android:id="@id/constraint_layout_placeholder">
<PropertySet android:visibility="visible" /> <PropertySet android:visibility="visible" />
</Constraint> </Constraint>
<!--
I don't know why MotionLayout tries to control visibility for the buffer progress bar,
but it's messing with its display…
-->
<ConstraintOverride
android:id="@id/now_playing_buffering"
motion:visibilityMode="ignore"
/>
</ConstraintSet> </ConstraintSet>
<ConstraintSet android:id="@+id/end"> <ConstraintSet android:id="@+id/end">
@ -22,7 +30,14 @@
motion:layout_constraintStart_toStartOf="@id/detail_image_placeholder" motion:layout_constraintStart_toStartOf="@id/detail_image_placeholder"
motion:layout_constraintTop_toBottomOf="@id/detail_image_placeholder" motion:layout_constraintTop_toBottomOf="@id/detail_image_placeholder"
motion:layout_constraintTop_toTopOf="@id/detail_image_placeholder" motion:layout_constraintTop_toTopOf="@id/detail_image_placeholder"
motion:transitionEasing="accelerate" />
<!--
I don't know why MotionLayout tries to control visibility for the buffer progress bar,
but it's messing with its display…
-->
<ConstraintOverride
android:id="@id/now_playing_buffering"
motion:visibilityMode="ignore"
/> />
<Constraint android:id="@id/now_playing_progress"> <Constraint android:id="@id/now_playing_progress">
<PropertySet android:alpha="0" android:visibility="gone" /> <PropertySet android:alpha="0" android:visibility="gone" />
@ -53,6 +68,13 @@
motion:curveFit="spline" motion:curveFit="spline"
/> />
<KeyPosition
motion:percentX="1"
motion:framePosition="50"
motion:motionTarget="@id/now_playing_buffering"
motion:curveFit="spline"
/>
<KeyAttribute <KeyAttribute
android:alpha="0" android:alpha="0"
motion:framePosition="10" motion:framePosition="10"