playback: use gesture insets in playback layout

When using gesture navigation, swipe up events might conflict with the
slide up behavior of the playback layout. To fix this, inset the
playback bar based on the gesture insets instead of the system bar
insets.
This commit is contained in:
OxygenCobalt 2022-01-18 19:51:06 -07:00
parent e4f2906767
commit e5901fa9e2
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
11 changed files with 62 additions and 53 deletions

View file

@ -37,7 +37,7 @@ import org.oxycblt.auxio.settings.SettingsManager
import org.oxycblt.auxio.util.isNight
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.replaceInsetsCompat
import org.oxycblt.auxio.util.systemBarsCompat
import org.oxycblt.auxio.util.systemBarInsetsCompat
/**
* The single [AppCompatActivity] for Auxio.
@ -113,18 +113,25 @@ class MainActivity : AppCompatActivity() {
private fun applyEdgeToEdgeWindow(binding: ViewBinding) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
// Do modern edge to edge, which happens to be around twice the size of the
// old way of doing things. Thanks android, very cool!
logD("Doing R+ edge-to-edge.")
window?.setDecorFitsSystemWindows(false)
// "Should we automatically acquire the insets we need and return them
// whenever the user wants them?"
// "Nah, let's make the user define what insets they want instead through
// a barely-documented API that is not brought up in a single tutorial!"
// "Great idea!"
binding.root.setOnApplyWindowInsetsListener { _, insets ->
WindowInsets.Builder()
.setInsets(
WindowInsets.Type.systemBars(),
insets.getInsetsIgnoringVisibility(WindowInsets.Type.systemBars())
)
.setInsets(
WindowInsets.Type.systemGestures(),
insets.getInsetsIgnoringVisibility(WindowInsets.Type.systemGestures())
)
.build()
.applyLeftRightInsets(binding)
}
@ -145,7 +152,7 @@ class MainActivity : AppCompatActivity() {
}
private fun WindowInsets.applyLeftRightInsets(binding: ViewBinding): WindowInsets {
val bars = systemBarsCompat
val bars = systemBarInsetsCompat
binding.root.updatePadding(
left = bars.left,

View file

@ -23,7 +23,7 @@ import android.util.AttributeSet
import android.view.WindowInsets
import android.widget.FrameLayout
import androidx.core.view.updatePadding
import org.oxycblt.auxio.util.systemBarsCompat
import org.oxycblt.auxio.util.systemBarInsetsCompat
/**
* A container for a FloatingActionButton that enables edge-to-edge support.
@ -43,7 +43,7 @@ class FloatingActionButtonContainer @JvmOverloads constructor(
}
override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets {
updatePadding(bottom = insets.systemBarsCompat.bottom)
updatePadding(bottom = insets.systemBarInsetsCompat.bottom)
return insets
}

View file

@ -44,7 +44,7 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.util.canScroll
import org.oxycblt.auxio.util.resolveAttr
import org.oxycblt.auxio.util.resolveDrawable
import org.oxycblt.auxio.util.systemBarsCompat
import org.oxycblt.auxio.util.systemBarInsetsCompat
import kotlin.math.abs
/**
@ -315,7 +315,7 @@ class FastScrollRecyclerView @JvmOverloads constructor(
}
override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets {
val bars = insets.systemBarsCompat
val bars = insets.systemBarInsetsCompat
updatePadding(
initialPadding.left, initialPadding.top, initialPadding.right,

View file

@ -19,6 +19,7 @@
package org.oxycblt.auxio.playback
import android.content.Context
import android.os.Build
import android.util.AttributeSet
import android.view.WindowInsets
import androidx.constraintlayout.widget.ConstraintLayout
@ -31,7 +32,7 @@ import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.util.inflater
import org.oxycblt.auxio.util.resolveAttr
import org.oxycblt.auxio.util.systemBarsCompat
import org.oxycblt.auxio.util.systemBarInsetsCompat
/**
* A view displaying the playback state in a compact manner. This is only meant to be used
@ -48,17 +49,39 @@ class PlaybackBarView @JvmOverloads constructor(
id = R.id.playback_bar
// Deliberately override the progress bar color [in a Lollipop-friendly way] so that
// we use colorSecondary instead of colorSurfaceVariant. This is for two reasons:
// 1. colorSurfaceVariant is used with the assumption that the view that is using it
// is not elevated and is therefore not colored. This view is elevated.
// 2. The way a solid color plays along with a ripple just doesnt look that good.
// we use colorSecondary instead of colorSurfaceVariant. This is because
// colorSurfaceVariant is used with the assumption that the view that is using it is
// not elevated and is therefore not colored. This view is elevated.
binding.playbackProgressBar.trackColor = MaterialColors.compositeARGBWithAlpha(
R.attr.colorSecondary.resolveAttr(context), (255 * 0.2).toInt()
)
}
override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets {
updatePadding(bottom = insets.systemBarsCompat.bottom)
// Since we swipe up this view, we need to make sure it does not collide with
// any gesture events. So, apply the system gesture insets if present and then
// only default to the system bar insets when there are no other options.
val gesturePadding = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
insets.getInsets(WindowInsets.Type.systemGestures()).bottom
}
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> {
@Suppress("DEPRECATION")
insets.systemGestureInsets.bottom
}
else -> 0
}
updatePadding(
bottom =
if (gesturePadding != 0)
gesturePadding
else
insets.systemBarInsetsCompat.bottom
)
return insets
}

View file

@ -34,7 +34,7 @@ import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.playback.state.LoopMode
import org.oxycblt.auxio.ui.memberBinding
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.systemBarsCompat
import org.oxycblt.auxio.util.systemBarInsetsCompat
/**
* A [Fragment] that displays more information about the song, along with more media controls.
@ -62,7 +62,7 @@ class PlaybackFragment : Fragment() {
binding.detailModel = detailModel
binding.root.setOnApplyWindowInsetsListener { _, insets ->
val bars = insets.systemBarsCompat
val bars = insets.systemBarInsetsCompat
binding.root.updatePadding(
top = bars.top,

View file

@ -3,10 +3,8 @@ package org.oxycblt.auxio.playback
import android.content.Context
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
import android.util.AttributeSet
@ -26,9 +24,10 @@ import org.oxycblt.auxio.BuildConfig
import org.oxycblt.auxio.R
import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.util.replaceInsetsCompat
import org.oxycblt.auxio.util.resolveAttr
import org.oxycblt.auxio.util.resolveDrawable
import org.oxycblt.auxio.util.systemBarsCompat
import org.oxycblt.auxio.util.systemBarInsetsCompat
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
@ -169,7 +168,7 @@ class PlaybackLayout @JvmOverloads constructor(
} catch (e: Exception) {
// Band-aid to stop the app crashing if we have to swap out the content view
// without warning (which we have to do sometimes because android is the worst
// thing ever
// thing ever)
}
}
}
@ -379,29 +378,11 @@ class PlaybackLayout @JvmOverloads constructor(
// We kind to do a reverse-measure to figure out how we should inset this view.
// Find how much space is lost by the panel and then combine that with the
// bottom inset to find how much space we should apply.
val bars = insets.systemBarsCompat
val bars = insets.systemBarInsetsCompat
val consumedByPanel = computePanelTopPosition(panelOffset) - measuredHeight
val adjustedBottomInset = (consumedByPanel + bars.bottom).coerceAtLeast(0)
return when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
WindowInsets.Builder(insets)
.setInsets(
WindowInsets.Type.systemBars(),
Insets.of(bars.left, bars.top, bars.right, adjustedBottomInset)
)
.build()
}
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 -> {
@Suppress("DEPRECATION")
insets.replaceSystemWindowInsets(
bars.left, bars.top, bars.right, adjustedBottomInset
)
}
else -> insets
}
return insets.replaceInsetsCompat(bars.left, bars.top, bars.right, adjustedBottomInset)
}
override fun onSaveInstanceState(): Parcelable = Bundle().apply {
@ -536,8 +517,7 @@ class PlaybackLayout @JvmOverloads constructor(
/**
* Do the nice view animations that occur whenever we slide up the playback panel.
* The way I transition is largely inspired by Android 12's notification panel, with the
* compact view fading out completely before the panel view fades in. We don't fade out the
* content though so we have cohesion between the other sliding transitions.
* compact view fading out completely before the panel view fades in.
*/
private fun updatePanelTransition() {
val ratio = max(panelOffset, 0f)
@ -546,7 +526,6 @@ class PlaybackLayout @JvmOverloads constructor(
val halfOutRatio = min(ratio / 0.5f, 1f)
val halfInRatio = max(ratio - 0.5f, 0f) / 0.5f
// Optimize out drawing for this view completely
contentView.apply {
alpha = outRatio
isInvisible = alpha == 0f
@ -569,7 +548,7 @@ class PlaybackLayout @JvmOverloads constructor(
// [reminder that this view also applies the bottom window inset] and we can't
// apply padding to the whole container layout since that would adjust the size
// of the playback view. This seems to be the least obtrusive way to do this.
lastInsets?.systemBarsCompat?.let { bars ->
lastInsets?.systemBarInsetsCompat?.let { bars ->
val params = layoutParams as FrameLayout.LayoutParams
val oldTopMargin = params.topMargin

View file

@ -38,7 +38,7 @@ import org.oxycblt.auxio.databinding.FragmentAboutBinding
import org.oxycblt.auxio.home.HomeViewModel
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.showToast
import org.oxycblt.auxio.util.systemBarsCompat
import org.oxycblt.auxio.util.systemBarInsetsCompat
/**
* A [BottomSheetDialogFragment] that shows Auxio's about screen.
@ -55,7 +55,7 @@ class AboutFragment : Fragment() {
val binding = FragmentAboutBinding.inflate(layoutInflater)
binding.aboutContents.setOnApplyWindowInsetsListener { _, insets ->
binding.aboutContents.updatePadding(bottom = insets.systemBarsCompat.bottom)
binding.aboutContents.updatePadding(bottom = insets.systemBarInsetsCompat.bottom)
insets
}

View file

@ -40,7 +40,7 @@ import org.oxycblt.auxio.settings.pref.IntListPreference
import org.oxycblt.auxio.util.isNight
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.showToast
import org.oxycblt.auxio.util.systemBarsCompat
import org.oxycblt.auxio.util.systemBarInsetsCompat
/**
* The actual fragment containing the settings menu. Inherits [PreferenceFragmentCompat].
@ -64,7 +64,7 @@ class SettingsListFragment : PreferenceFragmentCompat() {
clipToPadding = false
setOnApplyWindowInsetsListener { _, insets ->
updatePadding(bottom = insets.systemBarsCompat.bottom)
updatePadding(bottom = insets.systemBarInsetsCompat.bottom)
insets
}

View file

@ -30,7 +30,7 @@ import androidx.core.content.res.ResourcesCompat
import androidx.core.view.updatePadding
import com.google.android.material.appbar.AppBarLayout
import org.oxycblt.auxio.util.logE
import org.oxycblt.auxio.util.systemBarsCompat
import org.oxycblt.auxio.util.systemBarInsetsCompat
/**
* An [AppBarLayout] that fixes a bug with the default implementation where the lifted state
@ -67,7 +67,7 @@ open class EdgeAppBarLayout @JvmOverloads constructor(
override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets {
super.onApplyWindowInsets(insets)
updatePadding(top = insets.systemBarsCompat.top)
updatePadding(top = insets.systemBarInsetsCompat.top)
return insets
}

View file

@ -23,7 +23,7 @@ import android.util.AttributeSet
import android.view.WindowInsets
import androidx.core.view.updatePadding
import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.util.systemBarsCompat
import org.oxycblt.auxio.util.systemBarInsetsCompat
/**
* A [RecyclerView] that automatically applies insets to itself.
@ -34,7 +34,7 @@ class EdgeRecyclerView @JvmOverloads constructor(
defStyleAttr: Int = -1
) : RecyclerView(context, attrs, defStyleAttr) {
override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets {
updatePadding(bottom = insets.systemBarsCompat.bottom)
updatePadding(bottom = insets.systemBarInsetsCompat.bottom)
return insets
}
}

View file

@ -122,7 +122,7 @@ fun @receiver:AttrRes Int.resolveAttr(context: Context): Int {
* Resolve window insets in a version-aware manner. This can be used to apply padding to
* a view that properly follows all the frustrating changes that were made between 8-11.
*/
val WindowInsets.systemBarsCompat: Rect get() {
val WindowInsets.systemBarInsetsCompat: Rect get() {
return when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
getInsets(WindowInsets.Type.systemBars()).run {