Update accent dialog
Completely redo the accent dialog to remove its dependence on MaterialDialogs and also to make it more ergonomic.
This commit is contained in:
parent
194bcfcd05
commit
ac1e686704
13 changed files with 267 additions and 102 deletions
|
@ -1,4 +1,4 @@
|
|||
package org.oxycblt.auxio.settings.ui
|
||||
package org.oxycblt.auxio.settings
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
|
@ -6,7 +6,6 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import org.oxycblt.auxio.databinding.FragmentSettingsBinding
|
||||
import org.oxycblt.auxio.settings.ui.AboutDialog
|
||||
|
||||
/**
|
||||
* A container [Fragment] for the settings menu.
|
||||
|
|
|
@ -13,8 +13,8 @@ import org.oxycblt.auxio.R
|
|||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.recycler.DisplayMode
|
||||
import org.oxycblt.auxio.settings.accent.AccentDialog
|
||||
import org.oxycblt.auxio.settings.blacklist.BlacklistDialog
|
||||
import org.oxycblt.auxio.settings.ui.AccentDialog
|
||||
import org.oxycblt.auxio.ui.Accent
|
||||
import org.oxycblt.auxio.ui.createToast
|
||||
|
||||
|
@ -35,6 +35,8 @@ class SettingsListFragment : PreferenceFragmentCompat() {
|
|||
recursivelyHandleChildren(it)
|
||||
}
|
||||
|
||||
preferenceManager.onDisplayPreferenceDialogListener = this
|
||||
|
||||
logD("Fragment created.")
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package org.oxycblt.auxio.settings.ui
|
||||
package org.oxycblt.auxio.settings.accent
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.TooltipCompat
|
||||
|
@ -13,11 +13,15 @@ import org.oxycblt.auxio.ui.toStateList
|
|||
/**
|
||||
* An adapter that displays the list of all possible accents, and highlights the current one.
|
||||
* @author OxygenCobalt
|
||||
* @param onConfirm What to do when an accent is confirmed.
|
||||
* @param onSelect What to do when an accent is selected.
|
||||
*/
|
||||
|
||||
class AccentAdapter(
|
||||
private val onConfirm: (accent: Accent) -> Unit
|
||||
private var curAccent: Accent,
|
||||
private val onSelect: (accent: Accent) -> Unit
|
||||
) : RecyclerView.Adapter<AccentAdapter.ViewHolder>() {
|
||||
private var selectedViewHolder: ViewHolder? = null
|
||||
|
||||
override fun getItemCount(): Int = ACCENTS.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
|
@ -28,31 +32,43 @@ class AccentAdapter(
|
|||
holder.bind(ACCENTS[position])
|
||||
}
|
||||
|
||||
private fun setAccent(accent: Accent) {
|
||||
curAccent = accent
|
||||
onSelect(accent)
|
||||
}
|
||||
|
||||
inner class ViewHolder(
|
||||
private val binding: ItemAccentBinding
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(accent: Accent) {
|
||||
setSelected(accent == curAccent)
|
||||
|
||||
binding.accent.apply {
|
||||
contentDescription = context.getString(accent.name)
|
||||
|
||||
imageTintList = if (accent == Accent.get()) {
|
||||
isEnabled = false
|
||||
|
||||
R.color.background.toStateList(context)
|
||||
} else {
|
||||
isEnabled = true
|
||||
|
||||
android.R.color.transparent.toStateList(context)
|
||||
}
|
||||
|
||||
backgroundTintList = accent.getStateList(context)
|
||||
|
||||
TooltipCompat.setTooltipText(this, contentDescription)
|
||||
}
|
||||
|
||||
setOnClickListener {
|
||||
onConfirm(accent)
|
||||
}
|
||||
binding.accent.setOnClickListener {
|
||||
setAccent(accent)
|
||||
setSelected(true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setSelected(isSelected: Boolean) {
|
||||
val context = binding.accent.context
|
||||
|
||||
binding.accent.isEnabled = !isSelected
|
||||
|
||||
binding.accent.imageTintList = if (isSelected) {
|
||||
// Switch out the currently selected viewholder with this one.
|
||||
selectedViewHolder?.setSelected(false)
|
||||
selectedViewHolder = this
|
||||
|
||||
R.color.background.toStateList(context)
|
||||
} else {
|
||||
android.R.color.transparent.toStateList(context)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
package org.oxycblt.auxio.settings.accent
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatDialogFragment
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.oxycblt.auxio.databinding.DialogAccentBinding
|
||||
import org.oxycblt.auxio.settings.SettingsManager
|
||||
import org.oxycblt.auxio.ui.ACCENTS
|
||||
import org.oxycblt.auxio.ui.Accent
|
||||
import org.oxycblt.auxio.ui.inflater
|
||||
import org.oxycblt.auxio.ui.toColor
|
||||
|
||||
/**
|
||||
* Dialog responsible for showing the list of accents to select.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class AccentDialog : AppCompatDialogFragment() {
|
||||
private val settingsManager = SettingsManager.getInstance()
|
||||
private var pendingAccent = Accent.get()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
val binding = DialogAccentBinding.inflate(inflater)
|
||||
|
||||
savedInstanceState?.getInt(KEY_PENDING_ACCENT)?.let { index ->
|
||||
pendingAccent = ACCENTS.getOrElse(index) { pendingAccent }
|
||||
}
|
||||
|
||||
// --- UI SETUP ---
|
||||
|
||||
binding.accentRecycler.apply {
|
||||
adapter = AccentAdapter(pendingAccent) { accent ->
|
||||
pendingAccent = accent
|
||||
|
||||
updateAccent(binding)
|
||||
}
|
||||
}
|
||||
|
||||
binding.accentConfirm.setOnClickListener {
|
||||
if (pendingAccent != Accent.get()) {
|
||||
settingsManager.accent = pendingAccent
|
||||
|
||||
requireActivity().recreate()
|
||||
}
|
||||
|
||||
dismiss()
|
||||
}
|
||||
|
||||
binding.accentCancel.setOnClickListener {
|
||||
dismiss()
|
||||
}
|
||||
|
||||
updateAccent(binding)
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
|
||||
outState.putInt(KEY_PENDING_ACCENT, ACCENTS.indexOf(pendingAccent))
|
||||
}
|
||||
|
||||
private fun updateAccent(binding: DialogAccentBinding) {
|
||||
val accentColor = pendingAccent.color.toColor(requireContext())
|
||||
|
||||
binding.accentCancel.setTextColor(accentColor)
|
||||
binding.accentConfirm.setTextColor(accentColor)
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return MaterialAlertDialogBuilder(requireContext(), theme)
|
||||
.setView(onCreateView(requireActivity().inflater, null, savedInstanceState))
|
||||
.create()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val KEY_PENDING_ACCENT = "AXKEY_PEND_ACCENT"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package org.oxycblt.auxio.settings.accent
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.util.TypedValue
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlin.math.max
|
||||
|
||||
/**
|
||||
* A sub-class of [GridLayoutManager] that automatically sets the spans so that they fit the width
|
||||
* of this dialog.
|
||||
*/
|
||||
@Suppress("UNUSED")
|
||||
class AutoGridLayoutManager(
|
||||
context: Context,
|
||||
attrs: AttributeSet,
|
||||
defStyleAttr: Int,
|
||||
defStyleRes: Int
|
||||
) : GridLayoutManager(context, attrs, defStyleAttr, defStyleRes) {
|
||||
private var columnWidth: Int = 0
|
||||
private var lastWidth = -1
|
||||
private var lastHeight = -1
|
||||
|
||||
init {
|
||||
// We use 72dp here since that's the rough size of the accent item, give or take.
|
||||
// This will need to be modified if this is used beyond the accent dialog.
|
||||
columnWidth = TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP, 72F,
|
||||
context.resources.displayMetrics
|
||||
).toInt()
|
||||
}
|
||||
|
||||
override fun onLayoutChildren(recycler: RecyclerView.Recycler?, state: RecyclerView.State?) {
|
||||
if (width > 0 && height > 0 && (lastWidth != width || lastHeight != height)) {
|
||||
val totalSpace = width - paddingRight - paddingLeft
|
||||
val spanCount = max(1, totalSpace / columnWidth)
|
||||
|
||||
setSpanCount(spanCount)
|
||||
}
|
||||
|
||||
lastWidth = width
|
||||
lastHeight = height
|
||||
|
||||
super.onLayoutChildren(recycler, state)
|
||||
}
|
||||
}
|
|
@ -24,7 +24,9 @@ import org.oxycblt.auxio.R
|
|||
import org.oxycblt.auxio.databinding.DialogBlacklistBinding
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.ui.Accent
|
||||
import org.oxycblt.auxio.ui.createToast
|
||||
import org.oxycblt.auxio.ui.toColor
|
||||
import java.io.File
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
|
@ -48,29 +50,48 @@ class BlacklistDialog : BottomSheetDialogFragment() {
|
|||
): View {
|
||||
val binding = DialogBlacklistBinding.inflate(inflater)
|
||||
|
||||
val accent = Accent.get().color.toColor(requireContext())
|
||||
|
||||
val adapter = BlacklistEntryAdapter { path ->
|
||||
blacklistModel.removePath(path)
|
||||
}
|
||||
|
||||
requireContext().setTheme(Accent.get().theme)
|
||||
|
||||
// --- UI SETUP ---
|
||||
|
||||
binding.blacklistRecycler.adapter = adapter
|
||||
binding.blacklistAdd.setOnClickListener {
|
||||
showFileDialog()
|
||||
|
||||
// Dialogs don't know how to into theming, so I have to manually set the accent color
|
||||
// to each of the buttons since the overall fragment theme is Neutral.
|
||||
binding.blacklistAdd.apply {
|
||||
setTextColor(accent)
|
||||
|
||||
setOnClickListener {
|
||||
showFileDialog()
|
||||
}
|
||||
}
|
||||
|
||||
binding.blacklistCancel.setOnClickListener {
|
||||
dismiss()
|
||||
}
|
||||
binding.blacklistCancel.apply {
|
||||
setTextColor(accent)
|
||||
|
||||
binding.blacklistConfirm.setOnClickListener {
|
||||
if (blacklistModel.isModified()) {
|
||||
saveAndRestart()
|
||||
} else {
|
||||
setOnClickListener {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
binding.blacklistConfirm.apply {
|
||||
setTextColor(accent)
|
||||
|
||||
setOnClickListener {
|
||||
if (blacklistModel.isModified()) {
|
||||
saveAndRestart()
|
||||
} else {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- VIEWMODEL SETUP ---
|
||||
|
||||
blacklistModel.paths.observe(viewLifecycleOwner) { paths ->
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
package org.oxycblt.auxio.settings.ui
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.afollestad.materialdialogs.customview.customView
|
||||
import com.afollestad.materialdialogs.utils.invalidateDividers
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.settings.SettingsManager
|
||||
import org.oxycblt.auxio.ui.ACCENTS
|
||||
import org.oxycblt.auxio.ui.Accent
|
||||
|
||||
/**
|
||||
* Dialog responsible for showing the list of accents to select.
|
||||
* TODO: Move this to a Bottom Sheet and eliminate the MaterialDialogs dependency
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class AccentDialog : DialogFragment() {
|
||||
private val settingsManager = SettingsManager.getInstance()
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
// Roll my own RecyclerView since [To no surprise whatsoever] Material Dialogs
|
||||
// has a bug where ugly dividers will show with the RecyclerView even if you disable them.
|
||||
// This is why I hate using third party libraries.
|
||||
val recycler = RecyclerView(requireContext()).apply {
|
||||
adapter = AccentAdapter { accent ->
|
||||
if (accent != Accent.get()) {
|
||||
settingsManager.accent = accent
|
||||
|
||||
requireActivity().recreate()
|
||||
}
|
||||
|
||||
dismiss()
|
||||
}
|
||||
|
||||
layoutManager = LinearLayoutManager(
|
||||
requireContext(), LinearLayoutManager.HORIZONTAL, false
|
||||
)
|
||||
|
||||
post {
|
||||
// Combine the width of the recyclerview with the width of an item in order
|
||||
// to center the currently selected accent.
|
||||
val childWidth = getChildAt(0).width / 2
|
||||
|
||||
(layoutManager as LinearLayoutManager)
|
||||
.scrollToPositionWithOffset(
|
||||
ACCENTS.indexOf(Accent.get()),
|
||||
(width / 2) - childWidth
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return MaterialDialog(requireActivity())
|
||||
.title(R.string.setting_accent)
|
||||
.negativeButton(android.R.string.cancel)
|
||||
.customView(view = recycler)
|
||||
.noDividers()
|
||||
}
|
||||
|
||||
private fun MaterialDialog.noDividers(): MaterialDialog {
|
||||
invalidateDividers(showTop = false, showBottom = false)
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context=".settings.ui.AboutDialog">
|
||||
tools:context=".settings.AboutDialog">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
|
|
57
app/src/main/res/layout/dialog_accent.xml
Normal file
57
app/src/main/res/layout/dialog_accent.xml
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/background"
|
||||
android:paddingBottom="@dimen/margin_medium"
|
||||
android:theme="@style/Theme.Neutral">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/accent_header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/inter_exbold"
|
||||
android:padding="@dimen/padding_medium"
|
||||
android:text="@string/setting_accent"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="?attr/colorPrimary"
|
||||
android:textSize="@dimen/text_size_toolbar_header"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/accent_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:foregroundGravity="center"
|
||||
android:overScrollMode="never"
|
||||
app:layoutManager="org.oxycblt.auxio.settings.accent.AutoGridLayoutManager"
|
||||
app:layout_constraintTop_toBottomOf="@+id/accent_header"
|
||||
tools:spanCount="6"
|
||||
tools:itemCount="18"
|
||||
tools:listitem="@layout/item_accent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/accent_cancel"
|
||||
style="@style/Widget.Button.Dialog"
|
||||
android:layout_marginTop="@dimen/margin_medium"
|
||||
android:layout_marginEnd="@dimen/padding_small"
|
||||
android:text="@android:string/cancel"
|
||||
app:layout_constraintEnd_toStartOf="@+id/accent_confirm"
|
||||
app:layout_constraintTop_toBottomOf="@+id/accent_recycler" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/accent_confirm"
|
||||
style="@style/Widget.Button.Dialog"
|
||||
android:layout_marginEnd="@dimen/margin_medium"
|
||||
android:text="@android:string/ok"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/accent_cancel"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/accent_cancel" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
|
@ -1,17 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context=".settings.ui.AccentAdapter.ViewHolder">
|
||||
tools:context=".settings.pref.AccentAdapter.ViewHolder">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/margin_mid_small">
|
||||
android:padding="@dimen/margin_small">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/accent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/ui_circle_ripple"
|
||||
android:padding="@dimen/margin_medium"
|
||||
android:scaleType="fitCenter"
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
<item name="colorControlHighlight">@color/selection_color</item>
|
||||
<item name="colorControlActivated">?attr/colorPrimary</item>
|
||||
<item name="cornerRadius">0dp</item>
|
||||
<item name="colorSurface">@color/background</item>
|
||||
|
||||
<item name="md_background_color">@color/background</item>
|
||||
<item name="md_corner_radius">0dp</item>
|
||||
|
@ -231,5 +232,6 @@
|
|||
<item name="android:paddingEnd">@dimen/padding_mid_small</item>
|
||||
<item name="android:minWidth">@dimen/width_dialog_button_min</item>
|
||||
<item name="android:background">@drawable/ui_ripple</item>
|
||||
<item name="android:fontFamily">@font/inter_semibold</item>
|
||||
</style>
|
||||
</resources>
|
|
@ -143,5 +143,6 @@
|
|||
app:iconSpaceReserved="false"
|
||||
app:key="KEY_BLACKLIST"
|
||||
app:summary="@string/setting_content_blacklist_desc" />
|
||||
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
Loading…
Reference in a new issue