playback: use layer background in playback panel

Use a layered background in the playback panel, with a colorSurface layer
behind the elevated MaterialShapeDrawable. This is for safety, as there
might be cases where a fully transparent MaterialShapeDrawable results in
unexpected behavior.
This commit is contained in:
OxygenCobalt 2021-11-25 10:10:07 -07:00
parent 2ae22500d3
commit 56ded96b10
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
2 changed files with 41 additions and 13 deletions

View file

@ -5,6 +5,7 @@ import android.content.res.ColorStateList
import android.graphics.Canvas
import android.graphics.Insets
import android.graphics.Rect
import android.graphics.drawable.LayerDrawable
import android.os.Build
import android.os.Bundle
import android.os.Parcelable
@ -25,6 +26,7 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.resolveAttr
import org.oxycblt.auxio.util.resolveDrawable
import org.oxycblt.auxio.util.systemBarsCompat
import kotlin.math.abs
import kotlin.math.max
@ -34,12 +36,14 @@ import kotlin.math.min
* This layout handles pretty much every aspect of the playback UI flow, notably the playback
* bar and it's ability to slide up into the playback view. It's a blend of Hai Zhang's
* PersistentBarLayout and Umano's SlidingUpPanelLayout, albeit heavily minified to remove
* extraneous use cases and updated to support the latest SDK level.
* extraneous use cases and updated to support the latest SDK level and androidx tools.
*
* **Note:** If you want to adapt this layout into your own app. Good luck. This layout has been
* heavily minified to Auxio's use case in particular and is really hard to understand since it
* has a ton of state and view magic. I tried my best to document it, but it's probably not the
* most friendly or extendable. You have been warned.
* reduced to Auxio's use case in particular and is really hard to understand since it has a ton
* of state and view magic. I tried my best to document it, but it's probably not the most friendly
* or extendable. You have been warned.
*
* @author OxygenCobalt (With help from Umano and Hai Zhang)
*/
class PlaybackLayout @JvmOverloads constructor(
context: Context,
@ -61,6 +65,8 @@ class PlaybackLayout @JvmOverloads constructor(
private val playbackContainerView: FrameLayout
private val playbackBarView: PlaybackBarView
private val playbackPanelView: FrameLayout
private val playbackContainerBg: MaterialShapeDrawable
private val playbackFragment = PlaybackFragment()
/**
@ -94,10 +100,16 @@ class PlaybackLayout @JvmOverloads constructor(
*/
private var panelOffset = 0f
// Miscellaneous view things
private var initMotionX = 0f
private var initMotionY = 0f
private val tRect = Rect()
/** See [isDragging] */
private val dragStateField = ViewDragHelper::class.java.getDeclaredField("mDragState").apply {
isAccessible = true
}
init {
setWillNotDraw(false)
@ -110,10 +122,18 @@ class PlaybackLayout @JvmOverloads constructor(
isFocusable = false
isFocusableInTouchMode = false
background = MaterialShapeDrawable.createWithElevationOverlay(context).apply {
playbackContainerBg = MaterialShapeDrawable.createWithElevationOverlay(context).apply {
fillColor = ColorStateList.valueOf(R.attr.colorSurface.resolveAttr(context))
elevation = resources.getDimensionPixelSize(R.dimen.elevation_normal).toFloat()
}
// The way we fade out the elevation overlay is not by actually reducing the elevation
// but by fading out the background drawable itself. To be safe, we apply this
// background drawable to a layer list with another colorSurface shape drawable, just
// in case weird things happen if background drawable is completely transparent.
background = (R.drawable.ui_panel_bg.resolveDrawable(context) as LayerDrawable).apply {
setDrawableByLayerId(R.id.panel_overlay, playbackContainerBg)
}
}
playbackBarView = PlaybackBarView(context).apply {
@ -205,7 +225,6 @@ class PlaybackLayout @JvmOverloads constructor(
logD(panelState)
if (panelState == PanelState.EXPANDED) {
applyState(PanelState.COLLAPSED)
logD("I AM EXPANDED WILL COLLAPSE")
return true
}
@ -512,10 +531,7 @@ class PlaybackLayout @JvmOverloads constructor(
// We can't grab the drag state outside of a callback, but that's stupid and I don't
// want to vendor ViewDragHelper so I just do reflection instead.
val state = try {
this::class.java.getDeclaredField("mDragState").run {
isAccessible = true
get(dragHelper) as Int
}
dragStateField.get(this)
} catch (e: Exception) {
ViewDragHelper.STATE_IDLE
}
@ -549,9 +565,9 @@ class PlaybackLayout @JvmOverloads constructor(
// Optimize out drawing for this view completely
contentView.isInvisible = outRatio == 0f
// Slowly reduce the elevation as we slide up, eventually resulting in a neutral color
// instead of an elevated one when fully expanded.
(playbackContainerView.background as MaterialShapeDrawable).alpha = (outRatio * 255).toInt()
// Slowly reduce the elevation of the container as we slide up, eventually resulting in a
// neutral color instead of an elevated one when fully expanded.
playbackContainerBg.alpha = (outRatio * 255).toInt()
// Fade out our bar view as we slide up
playbackBarView.apply {

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape android:shape="rectangle">
<solid android:color="?attr/colorSurface" />
</shape>
</item>
<item android:id="@+id/panel_overlay">
<shape android:shape="rectangle" />
</item>
</layer-list>