diff --git a/app/src/main/java/org/oxycblt/auxio/settings/ui/AboutDialog.kt b/app/src/main/java/org/oxycblt/auxio/settings/AboutDialog.kt similarity index 99% rename from app/src/main/java/org/oxycblt/auxio/settings/ui/AboutDialog.kt rename to app/src/main/java/org/oxycblt/auxio/settings/AboutDialog.kt index fcfe00988..2978a0c1a 100644 --- a/app/src/main/java/org/oxycblt/auxio/settings/ui/AboutDialog.kt +++ b/app/src/main/java/org/oxycblt/auxio/settings/AboutDialog.kt @@ -1,4 +1,4 @@ -package org.oxycblt.auxio.settings.ui +package org.oxycblt.auxio.settings import android.content.Intent import android.os.Bundle diff --git a/app/src/main/java/org/oxycblt/auxio/settings/SettingsFragment.kt b/app/src/main/java/org/oxycblt/auxio/settings/SettingsFragment.kt index 902282d33..1fe2ea5b9 100644 --- a/app/src/main/java/org/oxycblt/auxio/settings/SettingsFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/settings/SettingsFragment.kt @@ -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. diff --git a/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt b/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt index fba992e00..745590e5a 100644 --- a/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt @@ -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.") } diff --git a/app/src/main/java/org/oxycblt/auxio/settings/ui/AccentAdapter.kt b/app/src/main/java/org/oxycblt/auxio/settings/accent/AccentAdapter.kt similarity index 55% rename from app/src/main/java/org/oxycblt/auxio/settings/ui/AccentAdapter.kt rename to app/src/main/java/org/oxycblt/auxio/settings/accent/AccentAdapter.kt index 77a101de7..ad4930baf 100644 --- a/app/src/main/java/org/oxycblt/auxio/settings/ui/AccentAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/settings/accent/AccentAdapter.kt @@ -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() { + 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) } } } diff --git a/app/src/main/java/org/oxycblt/auxio/settings/accent/AccentDialog.kt b/app/src/main/java/org/oxycblt/auxio/settings/accent/AccentDialog.kt new file mode 100644 index 000000000..c78b1c2c7 --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/settings/accent/AccentDialog.kt @@ -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" + } +} diff --git a/app/src/main/java/org/oxycblt/auxio/settings/accent/AutoGridLayoutManager.kt b/app/src/main/java/org/oxycblt/auxio/settings/accent/AutoGridLayoutManager.kt new file mode 100644 index 000000000..b98bbe57d --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/settings/accent/AutoGridLayoutManager.kt @@ -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) + } +} diff --git a/app/src/main/java/org/oxycblt/auxio/settings/blacklist/BlacklistDialog.kt b/app/src/main/java/org/oxycblt/auxio/settings/blacklist/BlacklistDialog.kt index 34dccf9de..42e286a9e 100644 --- a/app/src/main/java/org/oxycblt/auxio/settings/blacklist/BlacklistDialog.kt +++ b/app/src/main/java/org/oxycblt/auxio/settings/blacklist/BlacklistDialog.kt @@ -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 -> diff --git a/app/src/main/java/org/oxycblt/auxio/settings/ui/AccentDialog.kt b/app/src/main/java/org/oxycblt/auxio/settings/ui/AccentDialog.kt deleted file mode 100644 index 440b02321..000000000 --- a/app/src/main/java/org/oxycblt/auxio/settings/ui/AccentDialog.kt +++ /dev/null @@ -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 - } -} diff --git a/app/src/main/res/layout/dialog_about.xml b/app/src/main/res/layout/dialog_about.xml index 517f0d6a4..d2d25b4cb 100644 --- a/app/src/main/res/layout/dialog_about.xml +++ b/app/src/main/res/layout/dialog_about.xml @@ -2,7 +2,7 @@ + tools:context=".settings.AboutDialog"> + + + + + + + + +