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.
This commit is contained in:
OxygenCobalt 2022-06-27 10:28:17 -06:00
parent c3721266b5
commit 5b70295330
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
3 changed files with 36 additions and 32 deletions

View file

@ -114,8 +114,10 @@ class MainFragment : ViewBindingFragment<FragmentMainBinding>() {
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()
}
}

View file

@ -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 {

View file

@ -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)