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.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.playback.system.PlaybackService
|
import org.oxycblt.auxio.playback.system.PlaybackService
|
||||||
import org.oxycblt.auxio.settings.SettingsManager
|
import org.oxycblt.auxio.settings.SettingsManager
|
||||||
import org.oxycblt.auxio.util.isEdgeOn
|
|
||||||
import org.oxycblt.auxio.util.isNight
|
import org.oxycblt.auxio.util.isNight
|
||||||
import org.oxycblt.auxio.util.logD
|
import org.oxycblt.auxio.util.logD
|
||||||
|
|
||||||
|
@ -52,7 +51,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
this, R.layout.activity_main
|
this, R.layout.activity_main
|
||||||
)
|
)
|
||||||
|
|
||||||
if (isEdgeOn()) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||||
setupEdgeToEdge(binding)
|
setupEdgeToEdge(binding)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.view.updatePadding
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.navigation.fragment.findNavController
|
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.music.Header
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.util.applyEdge
|
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
|
* 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.lifecycleOwner = viewLifecycleOwner
|
||||||
|
|
||||||
|
binding.applyEdge { bars ->
|
||||||
|
binding.queueAppbar.updatePadding(top = bars.top)
|
||||||
|
binding.queueRecycler.updatePadding(bottom = bars.bottom)
|
||||||
|
}
|
||||||
|
|
||||||
binding.queueToolbar.setNavigationOnClickListener {
|
binding.queueToolbar.setNavigationOnClickListener {
|
||||||
findNavController().navigateUp()
|
findNavController().navigateUp()
|
||||||
}
|
}
|
||||||
|
@ -75,14 +80,6 @@ class QueueFragment : Fragment() {
|
||||||
helper.attachToRecyclerView(this)
|
helper.attachToRecyclerView(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isEdgeOn()) {
|
|
||||||
binding.applyEdge()
|
|
||||||
binding.queueAppbar.applyEdge()
|
|
||||||
binding.queueRecycler.applyEdge()
|
|
||||||
} else {
|
|
||||||
binding.root.fitsSystemWindows = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- VIEWMODEL SETUP ----
|
// --- VIEWMODEL SETUP ----
|
||||||
|
|
||||||
playbackModel.userQueue.observe(viewLifecycleOwner) { userQueue ->
|
playbackModel.userQueue.observe(viewLifecycleOwner) { userQueue ->
|
||||||
|
|
|
@ -22,11 +22,11 @@ import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.view.updatePadding
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import org.oxycblt.auxio.databinding.FragmentSettingsBinding
|
import org.oxycblt.auxio.databinding.FragmentSettingsBinding
|
||||||
import org.oxycblt.auxio.util.applyEdge
|
import org.oxycblt.auxio.util.applyEdge
|
||||||
import org.oxycblt.auxio.util.isEdgeOn
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A container [Fragment] for the settings menu.
|
* A container [Fragment] for the settings menu.
|
||||||
|
@ -54,13 +54,11 @@ class SettingsFragment : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isEdgeOn()) {
|
binding.applyEdge { bars ->
|
||||||
binding.applyEdge()
|
binding.settingsAppbar.updatePadding(top = bars.top)
|
||||||
binding.settingsAppbar.applyEdge()
|
|
||||||
|
|
||||||
// We can't apply edge to the RecyclerView from here. Do that in SettingsListFragment.
|
// The padding + clipToPadding method does not seem to work with a
|
||||||
} else {
|
// FragmentContainerView. Do it directly in SettingsListFragment instead.
|
||||||
binding.root.fitsSystemWindows = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return binding.root
|
return binding.root
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.oxycblt.auxio.settings
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
|
import androidx.core.view.updatePadding
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import androidx.preference.PreferenceCategory
|
import androidx.preference.PreferenceCategory
|
||||||
|
@ -34,7 +35,6 @@ import org.oxycblt.auxio.accent.AccentDialog
|
||||||
import org.oxycblt.auxio.excluded.ExcludedDialog
|
import org.oxycblt.auxio.excluded.ExcludedDialog
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.util.applyEdge
|
import org.oxycblt.auxio.util.applyEdge
|
||||||
import org.oxycblt.auxio.util.isEdgeOn
|
|
||||||
import org.oxycblt.auxio.util.isNight
|
import org.oxycblt.auxio.util.isNight
|
||||||
import org.oxycblt.auxio.util.logD
|
import org.oxycblt.auxio.util.logD
|
||||||
import org.oxycblt.auxio.util.showToast
|
import org.oxycblt.auxio.util.showToast
|
||||||
|
@ -57,8 +57,12 @@ class SettingsListFragment : PreferenceFragmentCompat() {
|
||||||
|
|
||||||
preferenceManager.onDisplayPreferenceDialogListener = this
|
preferenceManager.onDisplayPreferenceDialogListener = this
|
||||||
|
|
||||||
if (isEdgeOn()) {
|
view.findViewById<RecyclerView>(androidx.preference.R.id.recycler_view).apply {
|
||||||
view.findViewById<RecyclerView>(androidx.preference.R.id.recycler_view).applyEdge()
|
clipToPadding = false
|
||||||
|
|
||||||
|
applyEdge { bars ->
|
||||||
|
updatePadding(bottom = bars.bottom)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logD("Fragment created.")
|
logD("Fragment created.")
|
||||||
|
@ -75,6 +79,7 @@ class SettingsListFragment : PreferenceFragmentCompat() {
|
||||||
super.onDisplayPreferenceDialog(preference)
|
super.onDisplayPreferenceDialog(preference)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively call [handlePreference] on a preference.
|
* Recursively call [handlePreference] on a preference.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -21,8 +21,10 @@ package org.oxycblt.auxio.util
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
|
import android.graphics.Rect
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
|
import android.view.View
|
||||||
import android.view.WindowInsets
|
import android.view.WindowInsets
|
||||||
import android.widget.ImageButton
|
import android.widget.ImageButton
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
@ -35,7 +37,6 @@ import androidx.core.view.updatePadding
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.viewbinding.ViewBinding
|
import androidx.viewbinding.ViewBinding
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
|
||||||
import org.oxycblt.auxio.R
|
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
|
fun ViewBinding.applyEdge(onApply: (Rect) -> Unit) {
|
||||||
|
root.applyEdge(onApply)
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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() {
|
fun View.applyEdge(onApply: (Rect) -> Unit) {
|
||||||
setOnApplyWindowInsetsListener { v, insets ->
|
when {
|
||||||
val top = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
|
||||||
insets.getInsets(WindowInsets.Type.systemBars()).top
|
setOnApplyWindowInsetsListener { v, insets ->
|
||||||
} else {
|
val bars = insets.getInsets(WindowInsets.Type.systemBars()).run {
|
||||||
@Suppress("DEPRECATION")
|
Rect(left, top, right, bottom)
|
||||||
insets.systemWindowInsetTop
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
onApply(bars)
|
||||||
* Apply edge-to-edge tweaks to a [RecyclerView].
|
insets
|
||||||
*/
|
}
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply bottom padding to make sure that the last queue item isnt incorrectly lost,
|
// Not on a version that supports edge [yet], just don't do it.
|
||||||
// but also make sure that the added padding wont clip the child views entirely.
|
else -> fitsSystemWindows = true
|
||||||
v.updatePadding(bottom = bottom)
|
|
||||||
|
|
||||||
insets
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,24 +9,24 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:animateLayoutChanges="true"
|
android:animateLayoutChanges="true"
|
||||||
android:fitsSystemWindows="true"
|
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/home_appbar"
|
android:id="@+id/home_appbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
android:background="?attr/colorSurface"
|
android:background="?attr/colorSurface"
|
||||||
android:elevation="@dimen/elevation_normal">
|
android:clickable="true"
|
||||||
|
android:elevation="@dimen/elevation_normal"
|
||||||
|
android:focusable="true">
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/home_toolbar"
|
android:id="@+id/home_toolbar"
|
||||||
style="@style/Widget.Toolbar"
|
style="@style/Widget.Toolbar"
|
||||||
app:menu="@menu/menu_home"
|
|
||||||
android:elevation="0dp"
|
android:elevation="0dp"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
app:layout_scrollFlags="scroll|enterAlways"
|
app:layout_scrollFlags="scroll|enterAlways"
|
||||||
|
app:menu="@menu/menu_home"
|
||||||
app:title="@string/info_app_name" />
|
app:title="@string/info_app_name" />
|
||||||
|
|
||||||
<com.google.android.material.tabs.TabLayout
|
<com.google.android.material.tabs.TabLayout
|
||||||
|
@ -35,13 +35,13 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
app:tabUnboundedRipple="true"
|
app:tabContentStart="@dimen/spacing_medium"
|
||||||
|
app:tabIndicatorColor="?attr/colorAccent"
|
||||||
|
app:tabMode="scrollable"
|
||||||
app:tabRippleColor="?attr/colorControlHighlight"
|
app:tabRippleColor="?attr/colorControlHighlight"
|
||||||
app:tabTextAppearance="@style/TextAppearance.TabLayout.Label"
|
app:tabTextAppearance="@style/TextAppearance.TabLayout.Label"
|
||||||
app:tabContentStart="@dimen/spacing_medium"
|
|
||||||
app:tabMode="scrollable"
|
|
||||||
app:tabTextColor="?android:attr/textColorPrimary"
|
app:tabTextColor="?android:attr/textColorPrimary"
|
||||||
app:tabIndicatorColor="?attr/colorAccent" />
|
app:tabUnboundedRipple="true" />
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
android:id="@+id/queue_recycler"
|
android:id="@+id/queue_recycler"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:clipToPadding="false"
|
||||||
android:overScrollMode="ifContentScrolls"
|
android:overScrollMode="ifContentScrolls"
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
android:name="org.oxycblt.auxio.settings.SettingsListFragment"
|
android:name="org.oxycblt.auxio.settings.SettingsListFragment"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:clipToPadding="false"
|
||||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
|
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
Loading…
Reference in a new issue