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:
parent
1b67f6f846
commit
c5b1d7d735
8 changed files with 75 additions and 87 deletions
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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 ->
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue