settings: improve UI
Make settings follow both edge-to-edge and the liftOnScroll idioms. This has some minor issues with state, but these should be fixed when I'm able to make a smooth transition for theme changes.
This commit is contained in:
parent
34ac629659
commit
1b67f6f846
5 changed files with 118 additions and 89 deletions
|
@ -18,23 +18,20 @@
|
|||
|
||||
package org.oxycblt.auxio.playback.queue
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowInsets
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentQueueBinding
|
||||
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
|
||||
|
||||
/**
|
||||
|
@ -78,7 +75,13 @@ class QueueFragment : Fragment() {
|
|||
helper.attachToRecyclerView(this)
|
||||
}
|
||||
|
||||
setupEdgeForQueue(binding)
|
||||
if (isEdgeOn()) {
|
||||
binding.applyEdge()
|
||||
binding.queueAppbar.applyEdge()
|
||||
binding.queueRecycler.applyEdge()
|
||||
} else {
|
||||
binding.root.fitsSystemWindows = true
|
||||
}
|
||||
|
||||
// --- VIEWMODEL SETUP ----
|
||||
|
||||
|
@ -111,64 +114,6 @@ class QueueFragment : Fragment() {
|
|||
return binding.root
|
||||
}
|
||||
|
||||
private fun setupEdgeForQueue(binding: FragmentQueueBinding) {
|
||||
if (isEdgeOn()) {
|
||||
binding.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
|
||||
}
|
||||
|
||||
binding.queueAppbar.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
|
||||
}
|
||||
|
||||
v.updatePadding(top = top)
|
||||
|
||||
insets
|
||||
}
|
||||
|
||||
binding.queueRecycler.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,
|
||||
// but also make sure that the added padding wont clip the child views entirely.
|
||||
(v as RecyclerView).apply {
|
||||
clipToPadding = false
|
||||
updatePadding(bottom = bottom)
|
||||
overScrollMode = RecyclerView.OVER_SCROLL_IF_CONTENT_SCROLLS
|
||||
}
|
||||
|
||||
insets
|
||||
}
|
||||
} else {
|
||||
binding.root.fitsSystemWindows = true
|
||||
}
|
||||
}
|
||||
|
||||
// --- QUEUE DATA ---
|
||||
|
||||
/**
|
||||
|
|
|
@ -25,6 +25,8 @@ import android.view.ViewGroup
|
|||
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.
|
||||
|
@ -52,6 +54,15 @@ class SettingsFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
if (isEdgeOn()) {
|
||||
binding.applyEdge()
|
||||
binding.settingsAppbar.applyEdge()
|
||||
|
||||
// We can't apply edge to the RecyclerView from here. Do that in SettingsListFragment.
|
||||
} else {
|
||||
binding.root.fitsSystemWindows = true
|
||||
}
|
||||
|
||||
return binding.root
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,12 +26,15 @@ import androidx.preference.Preference
|
|||
import androidx.preference.PreferenceCategory
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.children
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import coil.Coil
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.accent.Accent
|
||||
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
|
||||
|
@ -54,6 +57,10 @@ class SettingsListFragment : PreferenceFragmentCompat() {
|
|||
|
||||
preferenceManager.onDisplayPreferenceDialogListener = this
|
||||
|
||||
if (isEdgeOn()) {
|
||||
view.findViewById<RecyclerView>(androidx.preference.R.id.recycler_view).applyEdge()
|
||||
}
|
||||
|
||||
logD("Fragment created.")
|
||||
}
|
||||
|
||||
|
@ -117,29 +124,13 @@ class SettingsListFragment : PreferenceFragmentCompat() {
|
|||
summary = Accent.get().getDetailedSummary(context)
|
||||
}
|
||||
|
||||
SettingsManager.KEY_SHOW_COVERS -> {
|
||||
SettingsManager.KEY_SHOW_COVERS, SettingsManager.KEY_QUALITY_COVERS -> {
|
||||
onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, _ ->
|
||||
Coil.imageLoader(requireContext()).apply {
|
||||
bitmapPool.clear()
|
||||
memoryCache.clear()
|
||||
}
|
||||
|
||||
requireActivity().recreate()
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
SettingsManager.KEY_QUALITY_COVERS -> {
|
||||
onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, _ ->
|
||||
// Clear out any cached images, before recreating the activity
|
||||
Coil.imageLoader(requireContext()).apply {
|
||||
bitmapPool.clear()
|
||||
memoryCache.clear()
|
||||
}
|
||||
|
||||
requireActivity().recreate()
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import android.content.res.ColorStateList
|
|||
import android.content.res.Resources
|
||||
import android.os.Build
|
||||
import android.util.TypedValue
|
||||
import android.view.WindowInsets
|
||||
import android.widget.ImageButton
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.AttrRes
|
||||
|
@ -30,8 +31,11 @@ import androidx.annotation.ColorInt
|
|||
import androidx.annotation.ColorRes
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.core.content.ContextCompat
|
||||
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
|
||||
|
||||
/**
|
||||
|
@ -139,3 +143,69 @@ fun @receiver:AttrRes Int.resolveAttr(context: Context): Int {
|
|||
* Check if edge-to-edge is on. Really a glorified version check.
|
||||
*/
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply edge-to-edge tweaks to an [AppBarLayout].
|
||||
*/
|
||||
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
|
||||
}
|
||||
|
||||
v.updatePadding(top = top)
|
||||
|
||||
insets
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,24 +4,36 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context=".settings.SettingsFragment">
|
||||
|
||||
<LinearLayout
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/settings_coordinator"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
android:background="?attr/colorSurface"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/settings_toolbar"
|
||||
style="@style/Widget.Toolbar.Icon.Down"
|
||||
app:menu="@menu/menu_settings"
|
||||
app:title="@string/set_title" />
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/settings_appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/colorSurface"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
app:liftOnScroll="true">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/settings_toolbar"
|
||||
style="@style/Widget.Toolbar.Icon.Down"
|
||||
app:menu="@menu/menu_settings"
|
||||
app:title="@string/set_title" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/settings_list_fragment"
|
||||
android:name="org.oxycblt.auxio.settings.SettingsListFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
</layout>
|
Loading…
Reference in a new issue