ui: switch to inset-based bottom sheet content

Do not do a measure + inset method with BottomSheetContentBehavior,
instead, try to re-apply window insets to adapt with the bar instead.

This fixes a lot of view clipping issues that made motion transitions
non-ideal and prevented a rounded playback bar. Only remaining issue is
RecyclerView instances, which need to be further reworked to resolve
scroll issues and edge effect problems.
This commit is contained in:
OxygenCobalt 2022-08-06 09:21:20 -06:00
parent 913db88fde
commit 0474940ee3
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
7 changed files with 35 additions and 47 deletions

View file

@ -2,6 +2,12 @@
## dev ## dev
#### What's Improved
- Improved bottom sheet code to prevent clipping issues
#### What's Fixed
- Fixed incorrect font being used in the queue title
## 2.6.0 ## 2.6.0
#### What's New #### What's New

View file

@ -22,7 +22,6 @@ import android.util.AttributeSet
import android.view.WindowInsets import android.view.WindowInsets
import android.widget.FrameLayout import android.widget.FrameLayout
import androidx.annotation.AttrRes import androidx.annotation.AttrRes
import androidx.core.view.updatePadding
import org.oxycblt.auxio.util.systemBarInsetsCompat import org.oxycblt.auxio.util.systemBarInsetsCompat
/** /**
@ -37,12 +36,9 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
clipToPadding = false clipToPadding = false
} }
override fun dispatchApplyWindowInsets(insets: WindowInsets): WindowInsets {
return onApplyWindowInsets(insets)
}
override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets { override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets {
updatePadding(bottom = insets.systemBarInsetsCompat.bottom) // Save a layout by simply moving the view bounds upwards
translationY = -insets.systemBarInsetsCompat.bottom.toFloat()
return insets return insets
} }
} }

View file

@ -22,7 +22,10 @@ import android.util.AttributeSet
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View import android.view.View
import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.coordinatorlayout.widget.CoordinatorLayout
import org.oxycblt.auxio.R
import org.oxycblt.auxio.settings.Settings
import org.oxycblt.auxio.ui.AuxioSheetBehavior import org.oxycblt.auxio.ui.AuxioSheetBehavior
import org.oxycblt.auxio.util.getDimen
/** /**
* The coordinator layout behavior used for the playback sheet, hacking in the many fixes required * The coordinator layout behavior used for the playback sheet, hacking in the many fixes required
@ -33,6 +36,9 @@ class PlaybackSheetBehavior<V : View>(context: Context, attributeSet: AttributeS
AuxioSheetBehavior<V>(context, attributeSet) { AuxioSheetBehavior<V>(context, attributeSet) {
init { init {
isHideable = true isHideable = true
if (Settings(context).roundMode) {
sheetBackgroundDrawable.setCornerSize(context.getDimen(R.dimen.size_corners_medium))
}
} }
// Hack around issue where the playback sheet will try to intercept nested scrolling events // Hack around issue where the playback sheet will try to intercept nested scrolling events

View file

@ -130,7 +130,7 @@ class QueueDragCallback(private val playbackModel: QueueViewModel) : ItemTouchHe
recyclerView: RecyclerView, recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder, viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder target: RecyclerView.ViewHolder
): Boolean = ) =
playbackModel.moveQueueDataItems( playbackModel.moveQueueDataItems(
viewHolder.bindingAdapterPosition, target.bindingAdapterPosition) viewHolder.bindingAdapterPosition, target.bindingAdapterPosition)

View file

@ -18,7 +18,6 @@
package org.oxycblt.auxio.ui package org.oxycblt.auxio.ui
import android.content.Context import android.content.Context
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.LayerDrawable import android.graphics.drawable.LayerDrawable
import android.os.Build import android.os.Build
import android.util.AttributeSet import android.util.AttributeSet
@ -73,12 +72,12 @@ abstract class AuxioSheetBehavior<V : View>(context: Context, attributeSet: Attr
child.apply { child.apply {
// Sometimes the sheet background will fade out, so guard it with another // Sometimes the sheet background will fade out, so guard it with another
// colorSurface drawable to prevent content overlap. // colorSurface drawable to prevent content overlap.
background = val guardDrawable =
LayerDrawable( MaterialShapeDrawable(sheetBackgroundDrawable.shapeAppearanceModel).apply {
arrayOf( fillColor = context.getAttrColorCompat(R.attr.colorSurface)
ColorDrawable( }
context.getAttrColorCompat(R.attr.colorSurface).defaultColor),
sheetBackgroundDrawable)) background = LayerDrawable(arrayOf(guardDrawable, sheetBackgroundDrawable))
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
// Shadows aren't disabled by default, do that. // Shadows aren't disabled by default, do that.

View file

@ -24,6 +24,7 @@ import android.view.WindowInsets
import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.coordinatorlayout.widget.CoordinatorLayout
import com.google.android.material.bottomsheet.NeoBottomSheetBehavior import com.google.android.material.bottomsheet.NeoBottomSheetBehavior
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.max
import org.oxycblt.auxio.util.coordinatorLayoutBehavior import org.oxycblt.auxio.util.coordinatorLayoutBehavior
import org.oxycblt.auxio.util.replaceSystemBarInsetsCompat import org.oxycblt.auxio.util.replaceSystemBarInsetsCompat
import org.oxycblt.auxio.util.systemBarInsetsCompat import org.oxycblt.auxio.util.systemBarInsetsCompat
@ -62,15 +63,7 @@ class BottomSheetContentBehavior<V : View>(context: Context, attributeSet: Attri
if (consumed != lastConsumed) { if (consumed != lastConsumed) {
lastConsumed = consumed lastConsumed = consumed
val insets = lastInsets
if (insets != null) {
child.dispatchApplyWindowInsets(insets)
}
lastInsets?.let(child::dispatchApplyWindowInsets) lastInsets?.let(child::dispatchApplyWindowInsets)
measureContent(parent, child, consumed)
layoutContent(child)
return true return true
} }
@ -85,21 +78,19 @@ class BottomSheetContentBehavior<V : View>(context: Context, attributeSet: Attri
parentHeightMeasureSpec: Int, parentHeightMeasureSpec: Int,
heightUsed: Int heightUsed: Int
): Boolean { ): Boolean {
val dep = dep ?: return false val contentWidthSpec =
val behavior = dep.coordinatorLayoutBehavior as NeoBottomSheetBehavior View.MeasureSpec.makeMeasureSpec(parent.measuredWidth, View.MeasureSpec.EXACTLY)
val consumed = behavior.calculateConsumedByBar() val contentHeightSpec =
if (consumed == Int.MIN_VALUE) { View.MeasureSpec.makeMeasureSpec(parent.measuredHeight, View.MeasureSpec.EXACTLY)
return false
}
measureContent(parent, child, consumed) child.measure(contentWidthSpec, contentHeightSpec)
return true return true
} }
override fun onLayoutChild(parent: CoordinatorLayout, child: V, layoutDirection: Int): Boolean { override fun onLayoutChild(parent: CoordinatorLayout, child: V, layoutDirection: Int): Boolean {
super.onLayoutChild(parent, child, layoutDirection) super.onLayoutChild(parent, child, layoutDirection)
layoutContent(child) child.layout(0, 0, child.measuredWidth, child.measuredHeight)
if (!setup) { if (!setup) {
child.setOnApplyWindowInsetsListener { v, insets -> child.setOnApplyWindowInsetsListener { v, insets ->
@ -112,9 +103,9 @@ class BottomSheetContentBehavior<V : View>(context: Context, attributeSet: Attri
} }
val bars = insets.systemBarInsetsCompat val bars = insets.systemBarInsetsCompat
val newBottom = max(bars.bottom, consumed)
insets.replaceSystemBarInsetsCompat( insets.replaceSystemBarInsetsCompat(bars.left, bars.top, bars.right, newBottom)
bars.left, bars.top, bars.right, (bars.bottom - consumed).coerceAtLeast(0))
} }
setup = true setup = true
@ -123,20 +114,6 @@ class BottomSheetContentBehavior<V : View>(context: Context, attributeSet: Attri
return true return true
} }
private fun measureContent(parent: View, child: View, consumed: Int) {
val contentWidthSpec =
View.MeasureSpec.makeMeasureSpec(parent.measuredWidth, View.MeasureSpec.EXACTLY)
val contentHeightSpec =
View.MeasureSpec.makeMeasureSpec(
parent.measuredHeight - consumed, View.MeasureSpec.EXACTLY)
child.measure(contentWidthSpec, contentHeightSpec)
}
private fun layoutContent(child: View) {
child.layout(0, 0, child.measuredWidth, child.measuredHeight)
}
private fun NeoBottomSheetBehavior<*>.calculateConsumedByBar(): Int { private fun NeoBottomSheetBehavior<*>.calculateConsumedByBar(): Int {
val offset = calculateSlideOffset() val offset = calculateSlideOffset()
if (offset == Float.MIN_VALUE || peekHeight < 0) { if (offset == Float.MIN_VALUE || peekHeight < 0) {

View file

@ -27,7 +27,11 @@ import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.util.systemBarInsetsCompat import org.oxycblt.auxio.util.systemBarInsetsCompat
/** A [RecyclerView] that automatically applies insets to itself. */ /**
* A [RecyclerView] that automatically applies insets to itself.
*
* TODO: Correctly handle edge-to-edge regarding scroll effects and saved scroll positions.
*/
open class AuxioRecyclerView open class AuxioRecyclerView
@JvmOverloads @JvmOverloads
constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr: Int = 0) : constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr: Int = 0) :