ui: refactor edge-to-edge

Merge all edge-to-edge functions into two methods for applying insets.
This generally simplifies code.
This commit is contained in:
OxygenCobalt 2021-08-29 16:18:10 -06:00
parent 1b67f6f846
commit c5b1d7d735
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
8 changed files with 75 additions and 87 deletions

View file

@ -32,7 +32,6 @@ import org.oxycblt.auxio.databinding.ActivityMainBinding
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.playback.system.PlaybackService
import org.oxycblt.auxio.settings.SettingsManager
import org.oxycblt.auxio.util.isEdgeOn
import org.oxycblt.auxio.util.isNight
import org.oxycblt.auxio.util.logD
@ -52,7 +51,7 @@ class MainActivity : AppCompatActivity() {
this, R.layout.activity_main
)
if (isEdgeOn()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setupEdgeToEdge(binding)
}

View file

@ -22,6 +22,7 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
@ -32,7 +33,6 @@ import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Header
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.util.applyEdge
import org.oxycblt.auxio.util.isEdgeOn
/**
* A [Fragment] that contains both the user queue and the next queue, with the ability to
@ -65,6 +65,11 @@ class QueueFragment : Fragment() {
binding.lifecycleOwner = viewLifecycleOwner
binding.applyEdge { bars ->
binding.queueAppbar.updatePadding(top = bars.top)
binding.queueRecycler.updatePadding(bottom = bars.bottom)
}
binding.queueToolbar.setNavigationOnClickListener {
findNavController().navigateUp()
}
@ -75,14 +80,6 @@ class QueueFragment : Fragment() {
helper.attachToRecyclerView(this)
}
if (isEdgeOn()) {
binding.applyEdge()
binding.queueAppbar.applyEdge()
binding.queueRecycler.applyEdge()
} else {
binding.root.fitsSystemWindows = true
}
// --- VIEWMODEL SETUP ----
playbackModel.userQueue.observe(viewLifecycleOwner) { userQueue ->

View file

@ -22,11 +22,11 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import org.oxycblt.auxio.databinding.FragmentSettingsBinding
import org.oxycblt.auxio.util.applyEdge
import org.oxycblt.auxio.util.isEdgeOn
/**
* A container [Fragment] for the settings menu.
@ -54,13 +54,11 @@ class SettingsFragment : Fragment() {
}
}
if (isEdgeOn()) {
binding.applyEdge()
binding.settingsAppbar.applyEdge()
binding.applyEdge { bars ->
binding.settingsAppbar.updatePadding(top = bars.top)
// We can't apply edge to the RecyclerView from here. Do that in SettingsListFragment.
} else {
binding.root.fitsSystemWindows = true
// The padding + clipToPadding method does not seem to work with a
// FragmentContainerView. Do it directly in SettingsListFragment instead.
}
return binding.root

View file

@ -21,6 +21,7 @@ package org.oxycblt.auxio.settings
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.view.updatePadding
import androidx.fragment.app.activityViewModels
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
@ -34,7 +35,6 @@ import org.oxycblt.auxio.accent.AccentDialog
import org.oxycblt.auxio.excluded.ExcludedDialog
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.util.applyEdge
import org.oxycblt.auxio.util.isEdgeOn
import org.oxycblt.auxio.util.isNight
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.showToast
@ -57,8 +57,12 @@ class SettingsListFragment : PreferenceFragmentCompat() {
preferenceManager.onDisplayPreferenceDialogListener = this
if (isEdgeOn()) {
view.findViewById<RecyclerView>(androidx.preference.R.id.recycler_view).applyEdge()
view.findViewById<RecyclerView>(androidx.preference.R.id.recycler_view).apply {
clipToPadding = false
applyEdge { bars ->
updatePadding(bottom = bars.bottom)
}
}
logD("Fragment created.")
@ -75,6 +79,7 @@ class SettingsListFragment : PreferenceFragmentCompat() {
super.onDisplayPreferenceDialog(preference)
}
}
/**
* Recursively call [handlePreference] on a preference.
*/

View file

@ -21,8 +21,10 @@ package org.oxycblt.auxio.util
import android.content.Context
import android.content.res.ColorStateList
import android.content.res.Resources
import android.graphics.Rect
import android.os.Build
import android.util.TypedValue
import android.view.View
import android.view.WindowInsets
import android.widget.ImageButton
import android.widget.TextView
@ -35,7 +37,6 @@ import androidx.core.view.updatePadding
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
import com.google.android.material.appbar.AppBarLayout
import org.oxycblt.auxio.R
/**
@ -140,72 +141,58 @@ fun @receiver:AttrRes Int.resolveAttr(context: Context): Int {
}
/**
* Check if edge-to-edge is on. Really a glorified version check.
* Apply edge-to-edge tweaks to the root of a [ViewBinding].
* @param onApply What to do when the system bar insets are provided
*/
fun isEdgeOn(): Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1
/**
* Apply edge-to-edge tweaks to the root view of a layout. This is largely for handling
* edge-to-edge on phone landscape modes.
*/
fun ViewBinding.applyEdge() {
root.setOnApplyWindowInsetsListener { v, insets ->
// Account for the side navigation bar if required.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val bars = insets.getInsets(WindowInsets.Type.systemBars())
v.updatePadding(
left = bars.left,
right = bars.right
)
} else {
@Suppress("DEPRECATION")
v.updatePadding(
left = insets.systemWindowInsetLeft,
right = insets.systemWindowInsetRight
)
}
insets
}
fun ViewBinding.applyEdge(onApply: (Rect) -> Unit) {
root.applyEdge(onApply)
}
/**
* Apply edge-to-edge tweaks to an [AppBarLayout].
* Apply edge-to-edge tweaks to a [View].
* @param onApply What to do when the system bar insets are provided
*/
fun AppBarLayout.applyEdge() {
setOnApplyWindowInsetsListener { v, insets ->
val top = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
insets.getInsets(WindowInsets.Type.systemBars()).top
} else {
@Suppress("DEPRECATION")
insets.systemWindowInsetTop
fun View.applyEdge(onApply: (Rect) -> Unit) {
when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
setOnApplyWindowInsetsListener { v, insets ->
val bars = insets.getInsets(WindowInsets.Type.systemBars()).run {
Rect(left, top, right, bottom)
}
updatePadding(
left = bars.left,
right = bars.right
)
onApply(bars)
insets
}
}
v.updatePadding(top = top)
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 -> {
setOnApplyWindowInsetsListener { v, insets ->
val bars = insets.run {
Rect(
systemWindowInsetLeft,
systemWindowInsetTop,
systemWindowInsetRight,
systemWindowInsetBottom
)
}
insets
}
}
updatePadding(
left = bars.left,
right = bars.right
)
/**
* Apply edge-to-edge tweaks to a [RecyclerView].
*/
fun RecyclerView.applyEdge() {
clipToPadding = false
setOnApplyWindowInsetsListener { v, insets ->
val bottom = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
insets.getInsets(WindowInsets.Type.systemBars()).bottom
} else {
@Suppress("DEPRECATION")
insets.systemWindowInsetBottom
onApply(bars)
insets
}
}
// Apply bottom padding to make sure that the last queue item isnt incorrectly lost,
// but also make sure that the added padding wont clip the child views entirely.
v.updatePadding(bottom = bottom)
insets
// Not on a version that supports edge [yet], just don't do it.
else -> fitsSystemWindows = true
}
}

View file

@ -9,24 +9,24 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:fitsSystemWindows="true"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/home_appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:background="?attr/colorSurface"
android:elevation="@dimen/elevation_normal">
android:clickable="true"
android:elevation="@dimen/elevation_normal"
android:focusable="true">
<androidx.appcompat.widget.Toolbar
android:id="@+id/home_toolbar"
style="@style/Widget.Toolbar"
app:menu="@menu/menu_home"
android:elevation="0dp"
android:fitsSystemWindows="true"
app:layout_scrollFlags="scroll|enterAlways"
app:menu="@menu/menu_home"
app:title="@string/info_app_name" />
<com.google.android.material.tabs.TabLayout
@ -35,13 +35,13 @@
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
app:tabUnboundedRipple="true"
app:tabContentStart="@dimen/spacing_medium"
app:tabIndicatorColor="?attr/colorAccent"
app:tabMode="scrollable"
app:tabRippleColor="?attr/colorControlHighlight"
app:tabTextAppearance="@style/TextAppearance.TabLayout.Label"
app:tabContentStart="@dimen/spacing_medium"
app:tabMode="scrollable"
app:tabTextColor="?android:attr/textColorPrimary"
app:tabIndicatorColor="?attr/colorAccent" />
app:tabUnboundedRipple="true" />
</com.google.android.material.appbar.AppBarLayout>

View file

@ -32,6 +32,7 @@
android:id="@+id/queue_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:overScrollMode="ifContentScrolls"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"

View file

@ -33,6 +33,7 @@
android:name="org.oxycblt.auxio.settings.SettingsListFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>