ui: rework bottom sheet state management

Try to make the bottom sheet states more coherent, especially regarding
when playback ends.
This commit is contained in:
OxygenCobalt 2022-08-02 11:13:12 -06:00
parent 35cfea78df
commit de3cc7958f
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
12 changed files with 92 additions and 67 deletions

View file

@ -52,6 +52,7 @@ class MainFragment :
private val navModel: NavigationViewModel by activityViewModels()
private var callback: DynamicBackPressedCallback? = null
private var lastInsets: WindowInsets? = null
private var keepPlaybackSheetHidden = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -117,11 +118,6 @@ class MainFragment :
override fun onPreDraw(): Boolean {
// CoordinatorLayout is insane and thus makes bottom sheet callbacks insane. Do our
// checks before every draw.
handleSheetTransitions()
return true
}
private fun handleSheetTransitions() {
val binding = requireBinding()
val playbackSheetBehavior =
binding.playbackSheet.coordinatorLayoutBehavior as PlaybackSheetBehavior
@ -162,9 +158,25 @@ class MainFragment :
isInvisible = alpha == 0f
}
playbackSheetBehavior.isDraggable =
playbackSheetBehavior.state != BottomSheetBehavior.STATE_HIDDEN &&
if (playbackModel.song.value != null) {
// Hack around the playback sheet intercepting swipe events on the queue bar
playbackSheetBehavior.isDraggable =
queueSheetBehavior.state == BottomSheetBehavior.STATE_COLLAPSED
} else {
// Sometimes lingering drags can un-hide the playback sheet even when we intend to
// hide it, make sure we keep it hidden.
tryHideAll()
}
return true
}
private fun updateSong(song: Song?) {
if (song != null) {
tryUnhideAll()
} else {
tryHideAll()
}
}
private fun handleMainNavigation(action: MainNavigationAction?) {
@ -212,11 +224,10 @@ class MainFragment :
if (playbackSheetBehavior.state != BottomSheetBehavior.STATE_HIDDEN &&
playbackSheetBehavior.state != BottomSheetBehavior.STATE_COLLAPSED) {
playbackSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
val queueSheetBehavior =
binding.queueSheet.coordinatorLayoutBehavior as QueueSheetBehavior
playbackSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
queueSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
return true
@ -225,15 +236,45 @@ class MainFragment :
return false
}
private fun updateSong(song: Song?) {
private fun tryUnhideAll(): Boolean {
val binding = requireBinding()
val playbackSheetBehavior =
binding.playbackSheet.coordinatorLayoutBehavior as PlaybackSheetBehavior
if (song != null) {
playbackSheetBehavior.unhideSafe()
} else {
playbackSheetBehavior.hideSafe()
if (playbackSheetBehavior.state == BottomSheetBehavior.STATE_HIDDEN) {
val queueSheetBehavior =
binding.queueSheet.coordinatorLayoutBehavior as QueueSheetBehavior
playbackSheetBehavior.isDraggable = true
playbackSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
queueSheetBehavior.isDraggable = true
return true
}
return false
}
private fun tryHideAll(): Boolean {
val binding = requireBinding()
val playbackSheetBehavior =
binding.playbackSheet.coordinatorLayoutBehavior as PlaybackSheetBehavior
if (playbackSheetBehavior.state != BottomSheetBehavior.STATE_HIDDEN) {
val queueSheetBehavior =
binding.queueSheet.coordinatorLayoutBehavior as QueueSheetBehavior
playbackSheetBehavior.isDraggable = false
queueSheetBehavior.isDraggable = false
playbackSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
queueSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
return true
}
return false
}
/**

View file

@ -130,7 +130,7 @@ private class AlbumDetailViewHolder private constructor(private val binding: Ite
val songCount = context.getPluralSafe(R.plurals.fmt_song_count, item.songs.size)
val duration = "<duration>"
val duration = item.durationSecs.formatDuration(true)
text =
if (item.releaseType != null) {

View file

@ -37,28 +37,9 @@ class PlaybackSheetBehavior<V : View>(context: Context, attributeSet: AttributeS
// Hack around issue where the playback sheet will try to intercept nested scrolling events
// before the queue sheet.
override fun onInterceptTouchEvent(
parent: CoordinatorLayout,
child: V,
event: MotionEvent
): Boolean = super.onInterceptTouchEvent(parent, child, event) && state != STATE_EXPANDED
override fun onInterceptTouchEvent(parent: CoordinatorLayout, child: V, event: MotionEvent) =
super.onInterceptTouchEvent(parent, child, event) && state != STATE_EXPANDED
// Note: This is an extension to Auxio's vendored BottomSheetBehavior
override fun enableHidingGestures() = false
/** Hide this sheet in a safe manner. */
fun hideSafe() {
if (state != STATE_HIDDEN) {
isDraggable = false
state = STATE_HIDDEN
}
}
/** Unhide this sheet in a safe manner. */
fun unhideSafe() {
if (state == STATE_HIDDEN) {
state = STATE_COLLAPSED
isDraggable = true
}
}
}

View file

@ -19,7 +19,6 @@ package org.oxycblt.auxio.playback.state
import kotlin.math.max
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import org.oxycblt.auxio.BuildConfig
import org.oxycblt.auxio.music.Album
@ -400,14 +399,7 @@ class PlaybackStateManager private constructor() {
suspend fun wipeState(database: PlaybackStateDatabase) {
logD("Wiping state")
withContext(Dispatchers.IO) {
delay(5000)
withContext(Dispatchers.Main) {
index = -1
notifyNewPlayback()
}
}
withContext(Dispatchers.IO) { database.write(null) }
}
/** Sanitize the state with [newLibrary]. */

View file

@ -42,8 +42,13 @@ import org.oxycblt.auxio.util.logD
/**
* The component managing the [MediaSessionCompat] instance.
*
* I really don't like how I have to do this, but until I can work with the ExoPlayer queue system
* using something like MediaSessionConnector is more or less impossible.
* Media3 is a joke. It tries so hard to be "hElpfUl" and implement so many fundamental behaviors
* into a one-size-fits-all package that it only ends up causing unending bugs and frustration. The
* queue system is horribly designed, the notification code is outdated, and the overstretched
* abstractions result in terrible performance bottlenecks and insane state bugs..
*
* Show me a way to adapt my internal queue into the new system and I will change my mind, but
* otherwise, I will stick with my normal system that works correctly.
*
* @author OxygenCobalt
*
@ -265,12 +270,12 @@ class MediaSessionComponent(
override fun onPlayFromMediaId(mediaId: String?, extras: Bundle?) {
super.onPlayFromMediaId(mediaId, extras)
// STUB: Unimplemented
// STUB: Unimplemented, no media browser
}
override fun onPlayFromUri(uri: Uri?, extras: Bundle?) {
super.onPlayFromUri(uri, extras)
// STUB: Unimplemented
// STUB: Unimplemented, no media browser
}
override fun onPlayFromSearch(query: String?, extras: Bundle?) {

View file

@ -185,14 +185,14 @@ class Settings(private val context: Context, private val callback: Callback? = n
val pauseOnRepeat: Boolean
get() = inner.getBoolean(context.getString(R.string.set_key_repeat_pause), false)
/** Whether to be actively watching for changes in the music library. */
val shouldBeObserving: Boolean
get() = inner.getBoolean(context.getString(R.string.set_key_observing), false)
/** Whether to parse metadata directly with ExoPlayer. */
val useQualityTags: Boolean
get() = inner.getBoolean(context.getString(R.string.set_key_quality_tags), false)
/** Whether to be actively watching for changes in the music library. */
val shouldBeObserving: Boolean
get() = inner.getBoolean(context.getString(R.string.set_key_observing), false)
/** Get the list of directories that music should be hidden/loaded from. */
fun getMusicDirs(storageManager: StorageManager): MusicDirs {
val dirs =

View file

@ -80,7 +80,21 @@ private val Any.autoTag: String
* taking work others did and making it objectively worse so you could arbitrage a fraction of a
* penny on every AdMob impression you get? You could do so many great things if you simply had the
* courage to come up with an idea of your own. If you still want to go on, I guess the only thing I
* can say is this: JUNE 1989 TIANAMEN SQUARE PROTESTS AND MASSACRE 六四事件
* can say is this:
*
* JUNE 1989 TIANAMEN SQUARE PROTESTS AND MASSACRE 六四事件
*
* UYGHUR GENOCIDE 新疆种族灭绝指控
*
* XINJIANG INTERMENT CAMPS 新疆再教育營
*
* KASHMIR INDEPENDENCE MOVEMENT
*
* WOMEN'S RIGHTS IN THE ISLAMIC REPUBLIC OF IRAN حقوق زنان در ایران
*
* 2022 RUSSIAN INVASION OF UKRAINE Вторжение России на Украину
*
* KURDISTAN WORKERS PARTY KÜRDISTAN İŞÇI PARTISI (PKK)
*/
private fun basedCopyleftNotice() {
if (BuildConfig.APPLICATION_ID != "org.oxycblt.auxio" &&
@ -88,6 +102,6 @@ private fun basedCopyleftNotice() {
Log.d(
"Auxio Project",
"Friendly reminder: Auxio is licensed under the " +
"GPLv3 and all modifications must be made open source!")
"GPLv3 and all derivative apps must be made open source!")
}
}

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M16,20Q14.75,20 13.875,19.125Q13,18.25 13,17Q13,15.75 13.875,14.875Q14.75,14 16,14Q16.275,14 16.525,14.037Q16.775,14.075 17,14.2V6H22V8H19V17Q19,18.25 18.125,19.125Q17.25,20 16,20ZM3,16V14H11V16ZM3,12V10H15V12ZM3,8V6H15V8Z" />
</vector>

View file

@ -53,6 +53,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:fitsSystemWindows="true"
android:layout_margin="@dimen/spacing_medium"
android:visibility="invisible">

View file

@ -55,6 +55,7 @@
android:scaleType="center"
android:paddingBottom="@dimen/spacing_small"
android:src="@drawable/ic_down_24"
android:contentDescription="@string/desc_queue_bar"
app:layout_constraintTop_toTopOf="parent" />
<TextView
@ -62,6 +63,7 @@
android:layout_height="wrap_content"
android:text="@string/lbl_queue"
android:textAppearance="@style/TextAppearance.Material3.LabelLarge"
android:textColor="?attr/colorOnSurfaceVariant"
app:layout_constraintBottom_toBottomOf="@+id/handle"
app:layout_constraintEnd_toEndOf="@+id/handle"
app:layout_constraintStart_toStartOf="parent" />

View file

@ -11,7 +11,6 @@
android:id="@+id/queue_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="ifContentScrolls"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
tools:listitem="@layout/item_queue_song" />

View file

@ -236,6 +236,7 @@
<string name="desc_clear_queue_item">Remove this queue song</string>
<string name="desc_queue_handle">Move this queue song</string>
<string name="desc_queue_bar">Open the queue</string>
<string name="desc_tab_handle">Move this tab</string>
<string name="desc_clear_search">Clear search query</string>
<string name="desc_music_dir_delete">Remove folder</string>