From 5b702953307e26d52524b1b7b6edd688475d6ad3 Mon Sep 17 00:00:00 2001 From: OxygenCobalt Date: Mon, 27 Jun 2022 10:28:17 -0600 Subject: [PATCH] ui: fix bottom sheet issues Fix several issues with the bottom sheet implementation, mostly relating to what occurs when the bar is hidden. Previously, if the bar was hidden and then re-shown, it would not be visible, and you could override a hidden event with another tap. These are now both resolved by removing a stray visibility assignment and replacing the old drag checks with a new isDraggable attribute. In the future, I hope to abuse BottomSheetBehavior into not being a pile of garbage and replace this system once and for all. --- .../java/org/oxycblt/auxio/MainFragment.kt | 2 + .../auxio/playback/PlaybackViewModel.kt | 4 +- .../org/oxycblt/auxio/ui/BottomSheetLayout.kt | 62 ++++++++++--------- 3 files changed, 36 insertions(+), 32 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt index 7faae8e04..b75b13881 100644 --- a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt @@ -114,8 +114,10 @@ class MainFragment : ViewBindingFragment() { private fun updateSong(song: Song?) { val binding = requireBinding() if (song != null) { + binding.bottomSheetLayout.isDraggable = true binding.bottomSheetLayout.show() } else { + binding.bottomSheetLayout.isDraggable = false binding.bottomSheetLayout.hide() } } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt index 3649566fa..1a05a938c 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt @@ -22,6 +22,7 @@ import android.content.Context import android.net.Uri import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch @@ -276,8 +277,7 @@ class PlaybackViewModel(application: Application) : // --- SAVE/RESTORE FUNCTIONS --- /** - * Force save the current [PlaybackStateManager] state to the database. Called by - * SettingsListFragment. + * Force save the current [PlaybackStateManager] state to the database. */ fun savePlaybackState(context: Context, onDone: () -> Unit) { viewModelScope.launch { diff --git a/app/src/main/java/org/oxycblt/auxio/ui/BottomSheetLayout.kt b/app/src/main/java/org/oxycblt/auxio/ui/BottomSheetLayout.kt index 26e70dc45..baa81d9be 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/BottomSheetLayout.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/BottomSheetLayout.kt @@ -58,6 +58,7 @@ import org.oxycblt.auxio.util.stateList * BottomSheetBehavior has a multitude of shortcomings based that make it a non-starter for Auxio, * such as: * - No edge-to-edge support + * - Does not resize other content * - Extreme jank * - Terrible APIs that you have to use just to make the UX tolerable * - Reliance on CoordinatorLayout, which is just a terrible component in general and everyone @@ -76,9 +77,8 @@ import org.oxycblt.auxio.util.stateList * * @author OxygenCobalt (With help from Umano and Hai Zhang) * - * TODO: Implement rounded corners on the bar (when rounded covers is enabled) - * - * TODO: Fix several issues with a full-collapse event (blocks automatic rescanning) + * TODO: Phase out this for BottomSheetBehavior once I can abuse that into functioning correctly. It + * is generally superior to this and would allow me to implement extra things like a rounded bar. */ class BottomSheetLayout @JvmOverloads @@ -157,6 +157,8 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : private var initMotionY = 0f private val tRect = Rect() + var isDraggable = false + init { setWillNotDraw(false) } @@ -220,12 +222,7 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : // Dragging events are really complex and we don't want to mess up the state // while we are in one. - if (state == panelState || panelState == PanelState.DRAGGING) { - return - } - - // Disallow events that aren't showing the bar when disabled - if (state != PanelState.HIDDEN && state != PanelState.COLLAPSED && !isEnabled) { + if (state == panelState) { return } @@ -425,7 +422,7 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : override fun onTouchEvent(ev: MotionEvent): Boolean { performClick() - return if (!canSlide) { + return if (!isDraggable) { super.onTouchEvent(ev) } else try { @@ -438,7 +435,7 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : } override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { - if (!canSlide) { + if (!isDraggable) { return super.onInterceptTouchEvent(ev) } @@ -588,26 +585,37 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : } } - private val canSlide: Boolean - get() = panelState != PanelState.HIDDEN && isEnabled - private inner class DragHelperCallback : ViewDragHelper.Callback() { // Only capture on a fully expanded panel view override fun tryCaptureView(child: View, pointerId: Int) = child === containerView && panelOffset >= 0 override fun onViewDragStateChanged(state: Int) { - if (state == ViewDragHelper.STATE_IDLE) { - panelOffset = computePanelOffset(containerView.top) - - when { - panelOffset == 1f -> setPanelStateInternal(PanelState.EXPANDED) - panelOffset == 0f -> setPanelStateInternal(PanelState.COLLAPSED) - panelOffset < 0f -> { - setPanelStateInternal(PanelState.HIDDEN) - containerView.visibility = INVISIBLE + when (state) { + ViewDragHelper.STATE_DRAGGING -> { + if (!isDraggable) { + return } - else -> setPanelStateInternal(PanelState.EXPANDED) + + // We're dragging, so we need to update our state accordingly + if (panelState != PanelState.DRAGGING) { + lastIdlePanelState = panelState + } + + setPanelStateInternal(PanelState.DRAGGING) + } + ViewDragHelper.STATE_IDLE -> { + panelOffset = computePanelOffset(containerView.top) + + val newState = + when { + panelOffset == 1f -> PanelState.EXPANDED + panelOffset == 0f -> PanelState.COLLAPSED + panelOffset < 0f -> PanelState.HIDDEN + else -> PanelState.EXPANDED + } + + setPanelStateInternal(newState) } } } @@ -621,12 +629,6 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : dx: Int, dy: Int ) { - // We're dragging, so we need to update our state accordingly - if (panelState != PanelState.DRAGGING) { - lastIdlePanelState = panelState - } - - setPanelStateInternal(PanelState.DRAGGING) // Update our panel offset using the new top value panelOffset = computePanelOffset(top)