recycler: remove useless header ids

Remove useless id fields from Headers, replacing them with vlaues
related to their string resource.

String resources and disc numbers are more or less garunteed to be
unique in Auxio's context.
This commit is contained in:
OxygenCobalt 2022-08-01 10:36:53 -06:00
parent 257516643f
commit 35cfea78df
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
21 changed files with 177 additions and 184 deletions

View file

@ -21,7 +21,7 @@ at the cost of longer loading times
#### What's Improved
- Migrated to better-looking motion transitions
- App now exposes an (immutable) queue.
- App now exposes an (immutable) queue to the MediaSession
#### What's Fixed
- Fixed default material theme being used before app shows up

View file

@ -142,7 +142,8 @@ class MainFragment :
isInvisible = alpha == 0f
}
binding.playbackSheet.translationZ = 3f * outPlaybackRatio
binding.playbackSheet.translationZ =
requireContext().getDimenSafe(R.dimen.elevation_normal) * outPlaybackRatio
playbackSheetBehavior.sheetBackgroundDrawable.alpha = (outPlaybackRatio * 255).toInt()
binding.playbackBarFragment.apply {

View file

@ -32,7 +32,6 @@ import com.google.android.material.transition.MaterialSharedAxis
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentDetailBinding
import org.oxycblt.auxio.detail.recycler.AlbumDetailAdapter
import org.oxycblt.auxio.detail.recycler.SortHeader
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Music

View file

@ -30,7 +30,6 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentDetailBinding
import org.oxycblt.auxio.detail.recycler.ArtistDetailAdapter
import org.oxycblt.auxio.detail.recycler.DetailAdapter
import org.oxycblt.auxio.detail.recycler.SortHeader
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Music

View file

@ -20,6 +20,7 @@ package org.oxycblt.auxio.detail
import android.app.Application
import android.media.MediaExtractor
import android.media.MediaFormat
import androidx.annotation.StringRes
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
@ -27,8 +28,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import org.oxycblt.auxio.R
import org.oxycblt.auxio.detail.recycler.DiscHeader
import org.oxycblt.auxio.detail.recycler.SortHeader
import org.oxycblt.auxio.music.*
import org.oxycblt.auxio.settings.Settings
import org.oxycblt.auxio.ui.Sort
@ -214,7 +213,7 @@ class DetailViewModel(application: Application) :
private fun refreshAlbumData(album: Album) {
logD("Refreshing album data")
val data = mutableListOf<Item>(album)
data.add(SortHeader(id = -2, R.string.lbl_songs))
data.add(SortHeader(R.string.lbl_songs))
// To create a good user experience regarding disc numbers, we intersperse
// items that show the disc number throughout the album's songs. In the case
@ -225,7 +224,7 @@ class DetailViewModel(application: Application) :
for (entry in byDisc.entries) {
val disc = entry.key
val discSongs = entry.value
data.add(DiscHeader(id = -2L - disc, disc)) // Ensure ID uniqueness
data.add(DiscHeader(disc)) // Ensure ID uniqueness
data.addAll(discSongs)
}
} else {
@ -240,33 +239,33 @@ class DetailViewModel(application: Application) :
val data = mutableListOf<Item>(artist)
val albums = Sort(Sort.Mode.ByYear, false).albums(artist.albums)
val byGroup =
val byReleaseGroup =
albums.groupBy {
if (it.releaseType == null) {
return@groupBy ArtistAlbumGrouping.ALBUMS
return@groupBy R.string.lbl_albums
}
when (it.releaseType.refinement) {
null ->
when (it.releaseType) {
is ReleaseType.Album -> ArtistAlbumGrouping.ALBUMS
is ReleaseType.EP -> ArtistAlbumGrouping.EPS
is ReleaseType.Single -> ArtistAlbumGrouping.SINGLES
is ReleaseType.Compilation -> ArtistAlbumGrouping.COMPILATIONS
is ReleaseType.Soundtrack -> ArtistAlbumGrouping.SOUNDTRACKS
is ReleaseType.Mixtape -> ArtistAlbumGrouping.MIXTAPES
is ReleaseType.Album -> R.string.lbl_albums
is ReleaseType.EP -> R.string.lbl_eps
is ReleaseType.Single -> R.string.lbl_singles
is ReleaseType.Compilation -> R.string.lbl_compilations
is ReleaseType.Soundtrack -> R.string.lbl_soundtracks
is ReleaseType.Mixtape -> R.string.lbl_mixtapes
}
ReleaseType.Refinement.LIVE -> ArtistAlbumGrouping.LIVE
ReleaseType.Refinement.REMIX -> ArtistAlbumGrouping.REMIXES
ReleaseType.Refinement.LIVE -> R.string.lbl_live_group
ReleaseType.Refinement.REMIX -> R.string.lbl_remix_group
}
}
for (entry in byGroup.entries.sortedBy { it.key }.withIndex()) {
data.add(Header(-2L - entry.index, entry.value.key.stringRes))
data.addAll(entry.value.value)
for (entry in byReleaseGroup.entries.sortedBy { it.key }) {
data.add(Header(entry.key))
data.addAll(entry.value)
}
data.add(SortHeader(-2L - byGroup.entries.size, R.string.lbl_songs))
data.add(SortHeader(R.string.lbl_songs))
data.addAll(artistSort.songs(artist.songs))
_artistData.value = data.toList()
}
@ -274,7 +273,7 @@ class DetailViewModel(application: Application) :
private fun refreshGenreData(genre: Genre) {
logD("Refreshing genre data")
val data = mutableListOf<Item>(genre)
data.add(SortHeader(-2, R.string.lbl_songs))
data.add(SortHeader(R.string.lbl_songs))
data.addAll(genreSort.songs(genre.songs))
_genreData.value = data
}
@ -326,28 +325,14 @@ class DetailViewModel(application: Application) :
override fun onCleared() {
musicStore.removeCallback(this)
}
private enum class ArtistAlbumGrouping : Comparable<ArtistAlbumGrouping> {
ALBUMS,
EPS,
SINGLES,
COMPILATIONS,
SOUNDTRACKS,
MIXTAPES,
REMIXES,
LIVE;
val stringRes: Int
get() =
when (this) {
ALBUMS -> R.string.lbl_albums
EPS -> R.string.lbl_eps
SINGLES -> R.string.lbl_singles
COMPILATIONS -> R.string.lbl_compilations
SOUNDTRACKS -> R.string.lbl_soundtracks
MIXTAPES -> R.string.lbl_mixtapes
REMIXES -> R.string.lbl_remix_group
LIVE -> R.string.lbl_live_group
}
}
}
data class SortHeader(@StringRes val string: Int) : Item() {
override val id: Long
get() = string.toLong()
}
data class DiscHeader(val disc: Int) : Item() {
override val id: Long
get() = disc.toLong()
}

View file

@ -30,7 +30,6 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentDetailBinding
import org.oxycblt.auxio.detail.recycler.DetailAdapter
import org.oxycblt.auxio.detail.recycler.GenreDetailAdapter
import org.oxycblt.auxio.detail.recycler.SortHeader
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Genre

View file

@ -25,6 +25,7 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.ItemAlbumSongBinding
import org.oxycblt.auxio.databinding.ItemDetailBinding
import org.oxycblt.auxio.databinding.ItemDiscHeaderBinding
import org.oxycblt.auxio.detail.DiscHeader
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.ui.recycler.BindingViewHolder
@ -129,7 +130,7 @@ private class AlbumDetailViewHolder private constructor(private val binding: Ite
val songCount = context.getPluralSafe(R.plurals.fmt_song_count, item.songs.size)
val duration = item.durationSecs.formatDuration(false)
val duration = "<duration>"
text =
if (item.releaseType != null) {
@ -171,10 +172,9 @@ private class AlbumDetailViewHolder private constructor(private val binding: Ite
}
}
data class DiscHeader(override val id: Long, val disc: Int) : Item()
class DiscHeaderViewHolder(private val binding: ItemDiscHeaderBinding) :
BindingViewHolder<DiscHeader, Unit>(binding.root) {
override fun bind(item: DiscHeader, listener: Unit) {
binding.discNo.textSafe = binding.context.getString(R.string.fmt_disc_no, item.disc)
}

View file

@ -19,12 +19,12 @@ package org.oxycblt.auxio.detail.recycler
import android.content.Context
import android.view.View
import androidx.annotation.StringRes
import androidx.appcompat.widget.TooltipCompat
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.IntegerTable
import org.oxycblt.auxio.databinding.ItemSortHeaderBinding
import org.oxycblt.auxio.detail.SortHeader
import org.oxycblt.auxio.ui.recycler.AsyncBackingData
import org.oxycblt.auxio.ui.recycler.BindingViewHolder
import org.oxycblt.auxio.ui.recycler.Header
@ -127,8 +127,6 @@ abstract class DetailAdapter<L : DetailAdapter.Listener>(
}
}
data class SortHeader(override val id: Long, @StringRes val string: Int) : Item()
class SortHeaderViewHolder(private val binding: ItemSortHeaderBinding) :
BindingViewHolder<SortHeader, DetailAdapter.Listener>(binding.root) {
override fun bind(item: SortHeader, listener: DetailAdapter.Listener) {

View file

@ -21,13 +21,8 @@ import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.WindowInsets
import androidx.coordinatorlayout.widget.CoordinatorLayout
import kotlin.math.max
import org.oxycblt.auxio.ui.AuxioSheetBehavior
import org.oxycblt.auxio.util.systemBarInsetsCompat
import org.oxycblt.auxio.util.systemGestureInsetsCompat
/**
* The coordinator layout behavior used for the playback sheet, hacking in the many fixes required
@ -36,8 +31,6 @@ import org.oxycblt.auxio.util.systemGestureInsetsCompat
*/
class PlaybackSheetBehavior<V : View>(context: Context, attributeSet: AttributeSet?) :
AuxioSheetBehavior<V>(context, attributeSet) {
private var lastInsets: WindowInsets? = null
init {
isHideable = true
}
@ -50,25 +43,10 @@ class PlaybackSheetBehavior<V : View>(context: Context, attributeSet: AttributeS
event: MotionEvent
): Boolean = super.onInterceptTouchEvent(parent, child, event) && state != STATE_EXPANDED
override fun onLayoutChild(parent: CoordinatorLayout, child: V, layoutDirection: Int): Boolean {
val success = super.onLayoutChild(parent, child, layoutDirection)
(child as ViewGroup).apply {
setOnApplyWindowInsetsListener { _, insets ->
lastInsets = insets
val bars = insets.systemBarInsetsCompat
val gestures = insets.systemGestureInsetsCompat
peekHeight = getChildAt(0).measuredHeight + max(gestures.bottom, bars.bottom)
insets
}
}
return success
}
// Note: This is an extension to Auxio's vendored BottomSheetBehavior
override fun enableHidingGestures() = false
/** Hide this sheet in a safe manner. */
fun hideSafe() {
if (state != STATE_HIDDEN) {
isDraggable = false
@ -76,6 +54,7 @@ class PlaybackSheetBehavior<V : View>(context: Context, attributeSet: AttributeS
}
}
/** Unhide this sheet in a safe manner. */
fun unhideSafe() {
if (state == STATE_HIDDEN) {
state = STATE_COLLAPSED

View file

@ -138,10 +138,5 @@ class QueueDragCallback(private val playbackModel: QueueViewModel) : ItemTouchHe
playbackModel.removeQueueDataItem(viewHolder.bindingAdapterPosition)
}
override fun isLongPressDragEnabled(): Boolean = false
companion object {
const val MINIMUM_INITIAL_DRAG_VELOCITY = 10
const val MAXIMUM_INITIAL_DRAG_VELOCITY = 25
}
override fun isLongPressDragEnabled() = false
}

View file

@ -34,8 +34,6 @@ import org.oxycblt.auxio.util.logD
/**
* A [Fragment] that shows the queue and enables editing as well.
*
* TODO: Improve index updates
*
* TODO: Test older versions
*
* TODO: Test restoration and song loss
@ -105,6 +103,7 @@ class QueueFragment : ViewBindingFragment<FragmentQueueBinding>(), QueueItemList
if (start != RecyclerView.NO_POSITION &&
end != RecyclerView.NO_POSITION &&
scrollTo !in start..end) {
logD("Scrolling to new position")
binding.queueRecycler.scrollToPosition(scrollTo)
}
}

View file

@ -20,13 +20,16 @@ package org.oxycblt.auxio.playback.queue
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.view.WindowInsets
import androidx.coordinatorlayout.widget.CoordinatorLayout
import kotlin.math.max
import org.oxycblt.auxio.R
import org.oxycblt.auxio.ui.AuxioSheetBehavior
import org.oxycblt.auxio.util.*
/**
* The bottom sheet behavior designed for the queue in particular.
* @author OxygenCobalt
*/
class QueueSheetBehavior<V : View>(context: Context, attributeSet: AttributeSet?) :
AuxioSheetBehavior<V>(context, attributeSet) {
private var barHeight = 0
@ -45,25 +48,18 @@ class QueueSheetBehavior<V : View>(context: Context, attributeSet: AttributeSet?
child: V,
dependency: View
): Boolean {
val ok = super.onDependentViewChanged(parent, child, dependency)
barHeight = dependency.height
return ok
return false // No change, just grabbed the height
}
override fun onLayoutChild(parent: CoordinatorLayout, child: V, layoutDirection: Int): Boolean {
val success = super.onLayoutChild(parent, child, layoutDirection)
override fun applyWindowInsets(child: View, insets: WindowInsets): WindowInsets {
super.applyWindowInsets(child, insets)
child.setOnApplyWindowInsetsListener { _, insets ->
val bars = insets.systemBarInsetsCompat
val gestures = insets.systemGestureInsetsCompat
expandedOffset = bars.top + barHeight + barSpacing
peekHeight =
(child as ViewGroup).getChildAt(0).height + max(gestures.bottom, bars.bottom)
insets.replaceSystemBarInsetsCompat(
bars.left, bars.top, bars.right, expandedOffset + bars.bottom)
}
return success
// Offset our expanded panel by the size of the playback bar, as that is shown when
// we slide up the panel.
val bars = insets.systemBarInsetsCompat
expandedOffset = bars.top + barHeight + barSpacing
return insets.replaceSystemBarInsetsCompat(
bars.left, bars.top, bars.right, expandedOffset + bars.bottom)
}
}

View file

@ -25,6 +25,10 @@ import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.state.PlaybackStateManager
/**
* Class enabling more advanced queue list functionality and queue editing.
* @author OxygenCobalt
*/
class QueueViewModel : ViewModel(), PlaybackStateManager.Callback {
private val playbackManager = PlaybackStateManager.getInstance()

View file

@ -19,6 +19,7 @@ package org.oxycblt.auxio.playback.state
import kotlin.math.max
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import org.oxycblt.auxio.BuildConfig
import org.oxycblt.auxio.music.Album
@ -399,7 +400,14 @@ class PlaybackStateManager private constructor() {
suspend fun wipeState(database: PlaybackStateDatabase) {
logD("Wiping state")
withContext(Dispatchers.IO) { database.write(null) }
withContext(Dispatchers.IO) {
delay(5000)
withContext(Dispatchers.Main) {
index = -1
notifyNewPlayback()
}
}
}
/** Sanitize the state with [newLibrary]. */

View file

@ -88,28 +88,28 @@ class SearchViewModel(application: Application) :
if (filterMode == null || filterMode == DisplayMode.SHOW_ARTISTS) {
library.artists.filterArtistsBy(query)?.let { artists ->
results.add(Header(-1, R.string.lbl_artists))
results.add(Header(R.string.lbl_artists))
results.addAll(sort.artists(artists))
}
}
if (filterMode == null || filterMode == DisplayMode.SHOW_ALBUMS) {
library.albums.filterAlbumsBy(query)?.let { albums ->
results.add(Header(-2, R.string.lbl_albums))
results.add(Header(R.string.lbl_albums))
results.addAll(sort.albums(albums))
}
}
if (filterMode == null || filterMode == DisplayMode.SHOW_GENRES) {
library.genres.filterGenresBy(query)?.let { genres ->
results.add(Header(-3, R.string.lbl_genres))
results.add(Header(R.string.lbl_genres))
results.addAll(sort.genres(genres))
}
}
if (filterMode == null || filterMode == DisplayMode.SHOW_SONGS) {
library.songs.filterSongsBy(query)?.let { songs ->
results.add(Header(-4, R.string.lbl_songs))
results.add(Header(R.string.lbl_songs))
results.addAll(sort.songs(songs))
}
}

View file

@ -23,42 +23,69 @@ import android.graphics.drawable.LayerDrawable
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.view.WindowInsets
import androidx.coordinatorlayout.widget.CoordinatorLayout
import com.google.android.material.bottomsheet.NeoBottomSheetBehavior
import com.google.android.material.shape.MaterialShapeDrawable
import org.oxycblt.auxio.R
import org.oxycblt.auxio.util.*
/**
* Implements the fundamental bottom sheet attributes used across the entire app.
* @author OxygenCobalt
*/
abstract class AuxioSheetBehavior<V : View>(context: Context, attributeSet: AttributeSet?) :
NeoBottomSheetBehavior<V>(context, attributeSet) {
private var elevationNormal = context.getDimenSafe(R.dimen.elevation_normal)
private var setup = false
val sheetBackgroundDrawable =
MaterialShapeDrawable.createWithElevationOverlay(context).apply {
fillColor = context.getAttrColorSafe(R.attr.colorSurface).stateList
elevation = elevationNormal
elevation = context.getDimenSafe(R.dimen.elevation_normal)
}
init {
// We need to disable isFitToContents for us to have our bottom sheet expand to the
// whole of the screen and not just whatever portion it takes up.
isFitToContents = false
}
/** Called when the child the bottom sheet applies to receives window insets. */
open fun applyWindowInsets(child: View, insets: WindowInsets): WindowInsets {
// All sheet behaviors derive their peek height from the size of the "bar" (i.e the
// first child) plus the gesture insets.
val gestures = insets.systemGestureInsetsCompat
peekHeight = (child as ViewGroup).getChildAt(0).height + gestures.bottom
return insets
}
// Enable experimental settings to allow us to skip the half expanded state without
// dumb hacks.
override fun shouldSkipHalfExpandedStateWhenDragging() = true
override fun shouldExpandOnUpwardDrag(dragDurationMillis: Long, yPositionPercentage: Float) =
true
override fun onLayoutChild(parent: CoordinatorLayout, child: V, layoutDirection: Int): Boolean {
val success = super.onLayoutChild(parent, child, layoutDirection)
val layout = super.onLayoutChild(parent, child, layoutDirection)
(child as ViewGroup).apply {
background =
LayerDrawable(
arrayOf(
ColorDrawable(context.getAttrColorSafe(R.attr.colorSurface)),
sheetBackgroundDrawable))
if (!setup) {
child.apply {
// Sometimes the sheet background will fade out, so guard it with another
// colorSurface drawable to prevent content overlap.
background =
LayerDrawable(
arrayOf(
ColorDrawable(context.getAttrColorSafe(R.attr.colorSurface)),
sheetBackgroundDrawable))
disableDropShadowCompat()
// Try to disable drop shadows if possible.
disableDropShadowCompat()
setOnApplyWindowInsetsListener(::applyWindowInsets)
}
setup = true
}
return success
return layout
}
}

View file

@ -28,12 +28,54 @@ import org.oxycblt.auxio.util.coordinatorLayoutBehavior
import org.oxycblt.auxio.util.replaceSystemBarInsetsCompat
import org.oxycblt.auxio.util.systemBarInsetsCompat
/**
* A behavior that automatically re-layouts and re-insets content to align with the parent layout's
* bottom sheet.
* @author OxygenCobalt
*/
class BottomSheetContentBehavior<V : View>(context: Context, attributeSet: AttributeSet?) :
CoordinatorLayout.Behavior<V>(context, attributeSet) {
private var lastInsets: WindowInsets? = null
private var dep: View? = null
private var setup: Boolean = false
private var lastInsets: WindowInsets? = null
private var lastConsumed: Int? = null
private var setup: Boolean = false
override fun layoutDependsOn(parent: CoordinatorLayout, child: V, dependency: View): Boolean {
if (dependency.coordinatorLayoutBehavior is NeoBottomSheetBehavior) {
dep = dependency
return true
}
return false
}
override fun onDependentViewChanged(
parent: CoordinatorLayout,
child: V,
dependency: View
): Boolean {
val behavior = dependency.coordinatorLayoutBehavior as NeoBottomSheetBehavior
val consumed = behavior.calculateConsumedByBar()
if (consumed < Int.MIN_VALUE) {
return false
}
if (consumed != lastConsumed) {
lastConsumed = consumed
val insets = lastInsets
if (insets != null) {
child.dispatchApplyWindowInsets(insets)
}
lastInsets?.let(child::dispatchApplyWindowInsets)
measureContent(parent, child, consumed)
layoutContent(child)
return true
}
return false
}
override fun onMeasureChild(
parent: CoordinatorLayout,
@ -107,41 +149,4 @@ class BottomSheetContentBehavior<V : View>(context: Context, attributeSet: Attri
(peekHeight * (1 - abs(offset))).toInt()
}
}
override fun layoutDependsOn(parent: CoordinatorLayout, child: V, dependency: View): Boolean {
if (dependency.coordinatorLayoutBehavior is NeoBottomSheetBehavior) {
dep = dependency
return true
}
return false
}
override fun onDependentViewChanged(
parent: CoordinatorLayout,
child: V,
dependency: View
): Boolean {
val behavior = dependency.coordinatorLayoutBehavior as NeoBottomSheetBehavior
val consumed = behavior.calculateConsumedByBar()
if (consumed < Int.MIN_VALUE) {
return false
}
if (consumed != lastConsumed) {
lastConsumed = consumed
val insets = lastInsets
if (insets != null) {
child.dispatchApplyWindowInsets(insets)
}
lastInsets?.let(child::dispatchApplyWindowInsets)
measureContent(parent, child, consumed)
layoutContent(child)
return true
}
return false
}
}

View file

@ -166,10 +166,12 @@ abstract class Item {
/** A data object used solely for the "Header" UI element. */
data class Header(
override val id: Long,
/** The string resource used for the header. */
@StringRes val string: Int
) : Item()
) : Item() {
override val id: Long
get() = string.toLong()
}
/**
* Represents data that backs a [MonoAdapter] or [MultiAdapter]. This can be implemented by any

View file

@ -23,7 +23,6 @@ import android.content.res.ColorStateList
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase
import android.graphics.Insets
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.os.Build
import android.view.View
@ -241,48 +240,46 @@ val AndroidViewModel.application: Application
fun <R> SQLiteDatabase.queryAll(tableName: String, block: (Cursor) -> R) =
query(tableName, null, null, null, null, null, null)?.use(block)
// Note: WindowInsetsCompat and it's related methods are a non-functional mess that does not
// work for Auxio's use-case. Use our own methods instead.
// Note: WindowInsetsCompat and it's related methods are an over-engineered mess that does not
// work for Auxio's use-case. Use our own compat methods instead.
/**
* Resolve system bar 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 Android 8-11.
*/
val WindowInsets.systemBarInsetsCompat: Rect
val WindowInsets.systemBarInsetsCompat: Insets
get() =
when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
getInsets(WindowInsets.Type.systemBars()).run { Rect(left, top, right, bottom) }
getInsets(WindowInsets.Type.systemBars())
}
else -> {
@Suppress("DEPRECATION")
Rect(
systemWindowInsetLeft,
systemWindowInsetTop,
systemWindowInsetRight,
systemWindowInsetBottom)
@Suppress("DEPRECATION") systemWindowInsets
}
}
/**
* Resolve gesture 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 Android 8-11.
* that properly follows all the frustrating changes that were made between Android 8-11. Note that
* if the gesture insets are not present (i.e zeroed), the bar insets will be used instead.
*/
val WindowInsets.systemGestureInsetsCompat: Rect
val WindowInsets.systemGestureInsetsCompat: Insets
get() =
// The reasoning for why we take a larger inset is because gesture insets are seemingly
// not present on some android versions. To prevent the app from appearing below the
// system bars, we fall back to the bar insets. This is guaranteed not to fire in any
// context but the gesture insets being invalid, as gesture insets are intended to
// be larger than bar insets.
when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
getInsets(WindowInsets.Type.systemGestures()).run { Rect(left, top, right, bottom) }
Insets.max(
getInsets(WindowInsets.Type.systemGestures()),
getInsets(WindowInsets.Type.systemBars()))
}
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> {
@Suppress("DEPRECATION") val gestureInsets = systemGestureInsets
Rect(
gestureInsets.left,
gestureInsets.top,
gestureInsets.right,
gestureInsets.bottom)
@Suppress("DEPRECATION") Insets.max(systemGestureInsets, systemWindowInsets)
}
else -> Rect(0, 0, 0, 0)
else -> Insets.of(0, 0, 0, 0)
}
/**

View file

@ -46,12 +46,12 @@
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/handle_wrapper"
android:layout_width="match_parent"
android:layout_height="64dp">
android:layout_height="@dimen/size_bottom_sheet_bar">
<ImageView
android:id="@+id/handle"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_height="@dimen/size_btn"
android:scaleType="center"
android:paddingBottom="@dimen/spacing_small"
android:src="@drawable/ic_down_24"

View file

@ -26,6 +26,7 @@
<dimen name="size_corners_mid_large">24dp</dimen>
<dimen name="size_btn">48dp</dimen>
<dimen name="size_bottom_sheet_bar">64dp</dimen>
<dimen name="size_play_pause_button">72dp</dimen>
<dimen name="size_icon_small">24dp</dimen>
@ -38,7 +39,6 @@
<dimen name="text_size_track_number_step">2sp</dimen>
<!-- Misc -->
<dimen name="elevation_small">2dp</dimen>
<dimen name="elevation_normal">3dp</dimen>
<dimen name="fast_scroll_popup_min_width">78dp</dimen>