mirror of
https://github.com/Michatec/Radio.git
synced 2026-03-31 23:46:28 +02:00
feat(ui): implement theme selection dialog with TV support
This commit is contained in:
@@ -84,6 +84,7 @@ object Keys {
|
|||||||
const val DIALOG_REMOVE_STATION: Int = 2
|
const val DIALOG_REMOVE_STATION: Int = 2
|
||||||
const val DIALOG_UPDATE_STATION_IMAGES: Int = 4
|
const val DIALOG_UPDATE_STATION_IMAGES: Int = 4
|
||||||
const val DIALOG_RESTORE_COLLECTION: Int = 5
|
const val DIALOG_RESTORE_COLLECTION: Int = 5
|
||||||
|
const val DIALOG_THEME_SELECTION: Int = 6
|
||||||
|
|
||||||
// dialog results
|
// dialog results
|
||||||
const val DIALOG_EMPTY_PAYLOAD_STRING: String = ""
|
const val DIALOG_EMPTY_PAYLOAD_STRING: String = ""
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import android.content.ClipData
|
|||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@@ -16,8 +17,10 @@ import androidx.appcompat.app.AppCompatActivity
|
|||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.preference.*
|
import androidx.preference.*
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.michatec.radio.dialogs.ErrorDialog
|
import com.michatec.radio.dialogs.ErrorDialog
|
||||||
|
import com.michatec.radio.dialogs.ThemeSelectionDialog
|
||||||
import com.michatec.radio.dialogs.YesNoDialog
|
import com.michatec.radio.dialogs.YesNoDialog
|
||||||
import com.michatec.radio.helpers.*
|
import com.michatec.radio.helpers.*
|
||||||
import com.michatec.radio.helpers.AppThemeHelper.getColor
|
import com.michatec.radio.helpers.AppThemeHelper.getColor
|
||||||
@@ -31,7 +34,7 @@ import java.util.*
|
|||||||
/*
|
/*
|
||||||
* SettingsFragment class
|
* SettingsFragment class
|
||||||
*/
|
*/
|
||||||
class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogListener {
|
class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogListener, ThemeSelectionDialog.ThemeSelectionDialogListener {
|
||||||
|
|
||||||
|
|
||||||
/* Define log tag */
|
/* Define log tag */
|
||||||
@@ -55,35 +58,24 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
|||||||
val screen = preferenceManager.createPreferenceScreen(context)
|
val screen = preferenceManager.createPreferenceScreen(context)
|
||||||
|
|
||||||
// set up "App Theme" preference
|
// set up "App Theme" preference
|
||||||
val preferenceThemeSelection = ListPreference(activity as Context)
|
val preferenceThemeSelection = Preference(activity as Context)
|
||||||
preferenceThemeSelection.title = getString(R.string.pref_theme_selection_title)
|
preferenceThemeSelection.title = getString(R.string.pref_theme_selection_title)
|
||||||
preferenceThemeSelection.setIcon(R.drawable.ic_brush_24dp)
|
preferenceThemeSelection.setIcon(R.drawable.ic_brush_24dp)
|
||||||
preferenceThemeSelection.key = Keys.PREF_THEME_SELECTION
|
preferenceThemeSelection.key = Keys.PREF_THEME_SELECTION
|
||||||
preferenceThemeSelection.summary = "${getString(R.string.pref_theme_selection_summary)} ${
|
preferenceThemeSelection.summary = "${getString(R.string.pref_theme_selection_summary)} ${
|
||||||
AppThemeHelper.getCurrentTheme(activity as Context)
|
AppThemeHelper.getCurrentTheme(activity as Context)
|
||||||
}"
|
}"
|
||||||
preferenceThemeSelection.entries = arrayOf(
|
preferenceThemeSelection.setOnPreferenceClickListener {
|
||||||
getString(R.string.pref_theme_selection_mode_device_default),
|
// check if device is a TV
|
||||||
getString(R.string.pref_theme_selection_mode_light),
|
val isTv = requireContext().packageManager.hasSystemFeature(android.content.pm.PackageManager.FEATURE_LEANBACK)
|
||||||
getString(R.string.pref_theme_selection_mode_dark)
|
if (isTv) {
|
||||||
)
|
// show TV-specific theme selection dialog
|
||||||
preferenceThemeSelection.entryValues = arrayOf(
|
ThemeSelectionDialog(this).show(activity as Context)
|
||||||
Keys.STATE_THEME_FOLLOW_SYSTEM,
|
|
||||||
Keys.STATE_THEME_LIGHT_MODE,
|
|
||||||
Keys.STATE_THEME_DARK_MODE
|
|
||||||
)
|
|
||||||
preferenceThemeSelection.setDefaultValue(Keys.STATE_THEME_FOLLOW_SYSTEM)
|
|
||||||
preferenceThemeSelection.setOnPreferenceChangeListener { preference, newValue ->
|
|
||||||
if (preference is ListPreference) {
|
|
||||||
val index: Int = preference.entryValues.indexOf(newValue)
|
|
||||||
preferenceThemeSelection.summary =
|
|
||||||
"${getString(R.string.pref_theme_selection_summary)} ${preference.entries[index]}"
|
|
||||||
|
|
||||||
AppThemeHelper.setTheme(newValue as String)
|
|
||||||
return@setOnPreferenceChangeListener true
|
|
||||||
} else {
|
} else {
|
||||||
return@setOnPreferenceChangeListener false
|
// show standard theme selection dialog for non-TV devices
|
||||||
|
showThemeSelectionDialog()
|
||||||
}
|
}
|
||||||
|
return@setOnPreferenceClickListener true
|
||||||
}
|
}
|
||||||
|
|
||||||
// set up "Update Station Images" preference
|
// set up "Update Station Images" preference
|
||||||
@@ -306,6 +298,57 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Shows theme selection dialog for non-TV devices */
|
||||||
|
private fun showThemeSelectionDialog() {
|
||||||
|
val themes = arrayOf(
|
||||||
|
getString(R.string.pref_theme_selection_mode_device_default),
|
||||||
|
getString(R.string.pref_theme_selection_mode_light),
|
||||||
|
getString(R.string.pref_theme_selection_mode_dark)
|
||||||
|
)
|
||||||
|
val themeValues = arrayOf(
|
||||||
|
Keys.STATE_THEME_FOLLOW_SYSTEM,
|
||||||
|
Keys.STATE_THEME_LIGHT_MODE,
|
||||||
|
Keys.STATE_THEME_DARK_MODE
|
||||||
|
)
|
||||||
|
val currentTheme = AppThemeHelper.getCurrentTheme(activity as Context)
|
||||||
|
val currentIndex = themes.indexOf(currentTheme)
|
||||||
|
|
||||||
|
val builder = MaterialAlertDialogBuilder(activity as Context)
|
||||||
|
builder.setTitle(getString(R.string.pref_theme_selection_title))
|
||||||
|
builder.setSingleChoiceItems(themes, currentIndex) { dialog, which ->
|
||||||
|
val selectedTheme = themeValues[which]
|
||||||
|
AppThemeHelper.setTheme(selectedTheme)
|
||||||
|
// update summary
|
||||||
|
val preferenceThemeSelection = findPreference<Preference>(Keys.PREF_THEME_SELECTION)
|
||||||
|
preferenceThemeSelection?.summary = "${getString(R.string.pref_theme_selection_summary)} ${themes[which]}"
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
builder.setNegativeButton(R.string.dialog_generic_button_cancel, null)
|
||||||
|
builder.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Overrides onThemeSelectionDialog from ThemeSelectionDialogListener */
|
||||||
|
override fun onThemeSelectionDialog(dialogResult: Boolean, selectedTheme: String) {
|
||||||
|
if (dialogResult) {
|
||||||
|
// update summary
|
||||||
|
val themes = arrayOf(
|
||||||
|
getString(R.string.pref_theme_selection_mode_device_default),
|
||||||
|
getString(R.string.pref_theme_selection_mode_light),
|
||||||
|
getString(R.string.pref_theme_selection_mode_dark)
|
||||||
|
)
|
||||||
|
val themeValues = arrayOf(
|
||||||
|
Keys.STATE_THEME_FOLLOW_SYSTEM,
|
||||||
|
Keys.STATE_THEME_LIGHT_MODE,
|
||||||
|
Keys.STATE_THEME_DARK_MODE
|
||||||
|
)
|
||||||
|
val index = themeValues.indexOf(selectedTheme)
|
||||||
|
val preferenceThemeSelection = findPreference<Preference>(Keys.PREF_THEME_SELECTION)
|
||||||
|
preferenceThemeSelection?.summary = "${getString(R.string.pref_theme_selection_summary)} ${themes[index]}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Overrides onYesNoDialog from YesNoDialogListener */
|
/* Overrides onYesNoDialog from YesNoDialogListener */
|
||||||
override fun onYesNoDialog(
|
override fun onYesNoDialog(
|
||||||
type: Int,
|
type: Int,
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
package com.michatec.radio.dialogs
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.widget.RadioButton
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import com.michatec.radio.Keys
|
||||||
|
import com.michatec.radio.R
|
||||||
|
import com.michatec.radio.helpers.AppThemeHelper
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ThemeSelectionDialog class
|
||||||
|
*/
|
||||||
|
class ThemeSelectionDialog(private var themeSelectionDialogListener: ThemeSelectionDialogListener) {
|
||||||
|
|
||||||
|
/* Interface used to communicate back to activity */
|
||||||
|
interface ThemeSelectionDialogListener {
|
||||||
|
fun onThemeSelectionDialog(dialogResult: Boolean, selectedTheme: String) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Main class variables */
|
||||||
|
private lateinit var dialog: AlertDialog
|
||||||
|
|
||||||
|
|
||||||
|
/* Construct and show dialog */
|
||||||
|
fun show(context: Context) {
|
||||||
|
// prepare dialog builder
|
||||||
|
val builder = MaterialAlertDialogBuilder(context)
|
||||||
|
|
||||||
|
// inflate custom layout
|
||||||
|
val inflater = LayoutInflater.from(context)
|
||||||
|
val view = inflater.inflate(R.layout.dialog_theme_selection, null)
|
||||||
|
|
||||||
|
// find radio buttons
|
||||||
|
val radioGroup = view.findViewById<android.widget.RadioGroup>(R.id.theme_radio_group)
|
||||||
|
val radioFollowSystem = view.findViewById<RadioButton>(R.id.radio_theme_follow_system)
|
||||||
|
val radioLight = view.findViewById<RadioButton>(R.id.radio_theme_light)
|
||||||
|
val radioDark = view.findViewById<RadioButton>(R.id.radio_theme_dark)
|
||||||
|
|
||||||
|
// set current selection
|
||||||
|
val currentTheme = AppThemeHelper.getCurrentTheme(context)
|
||||||
|
when (currentTheme) {
|
||||||
|
context.getString(R.string.pref_theme_selection_mode_device_default) -> {
|
||||||
|
radioFollowSystem.isChecked = true
|
||||||
|
}
|
||||||
|
context.getString(R.string.pref_theme_selection_mode_light) -> {
|
||||||
|
radioLight.isChecked = true
|
||||||
|
}
|
||||||
|
context.getString(R.string.pref_theme_selection_mode_dark) -> {
|
||||||
|
radioDark.isChecked = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set up radio group listener
|
||||||
|
radioGroup.setOnCheckedChangeListener { _, checkedId ->
|
||||||
|
val selectedTheme = when (checkedId) {
|
||||||
|
R.id.radio_theme_follow_system -> Keys.STATE_THEME_FOLLOW_SYSTEM
|
||||||
|
R.id.radio_theme_light -> Keys.STATE_THEME_LIGHT_MODE
|
||||||
|
R.id.radio_theme_dark -> Keys.STATE_THEME_DARK_MODE
|
||||||
|
else -> Keys.STATE_THEME_FOLLOW_SYSTEM
|
||||||
|
}
|
||||||
|
// apply theme immediately
|
||||||
|
AppThemeHelper.setTheme(selectedTheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
// set custom view
|
||||||
|
builder.setView(view)
|
||||||
|
|
||||||
|
// add OK button
|
||||||
|
builder.setPositiveButton(R.string.dialog_generic_button_ok) { _, _ ->
|
||||||
|
// get selected theme
|
||||||
|
val selectedTheme = when (radioGroup.checkedRadioButtonId) {
|
||||||
|
R.id.radio_theme_follow_system -> Keys.STATE_THEME_FOLLOW_SYSTEM
|
||||||
|
R.id.radio_theme_light -> Keys.STATE_THEME_LIGHT_MODE
|
||||||
|
R.id.radio_theme_dark -> Keys.STATE_THEME_DARK_MODE
|
||||||
|
else -> Keys.STATE_THEME_FOLLOW_SYSTEM
|
||||||
|
}
|
||||||
|
// notify listener
|
||||||
|
themeSelectionDialogListener.onThemeSelectionDialog(true, selectedTheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add cancel button
|
||||||
|
builder.setNegativeButton(R.string.dialog_generic_button_cancel) { _, _ ->
|
||||||
|
// notify listener
|
||||||
|
themeSelectionDialogListener.onThemeSelectionDialog(false, Keys.STATE_THEME_FOLLOW_SYSTEM)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle outside-click as cancel
|
||||||
|
builder.setOnCancelListener {
|
||||||
|
themeSelectionDialogListener.onThemeSelectionDialog(false, Keys.STATE_THEME_FOLLOW_SYSTEM)
|
||||||
|
}
|
||||||
|
|
||||||
|
// display dialog
|
||||||
|
dialog = builder.create()
|
||||||
|
dialog.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/dialog_title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/pref_theme_selection_title"
|
||||||
|
android:textSize="24sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:paddingBottom="16dp" />
|
||||||
|
|
||||||
|
<RadioGroup
|
||||||
|
android:id="@+id/theme_radio_group"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/radio_theme_follow_system"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/pref_theme_selection_mode_device_default"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:button="@null"
|
||||||
|
android:drawableStart="?android:attr/listChoiceIndicatorSingle"
|
||||||
|
android:drawablePadding="16dp"
|
||||||
|
android:focusable="true"
|
||||||
|
android:clickable="true" />
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/radio_theme_light"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/pref_theme_selection_mode_light"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:button="@null"
|
||||||
|
android:drawableStart="?android:attr/listChoiceIndicatorSingle"
|
||||||
|
android:drawablePadding="16dp"
|
||||||
|
android:focusable="true"
|
||||||
|
android:clickable="true" />
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/radio_theme_dark"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/pref_theme_selection_mode_dark"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:button="@null"
|
||||||
|
android:drawableStart="?android:attr/listChoiceIndicatorSingle"
|
||||||
|
android:drawablePadding="16dp"
|
||||||
|
android:focusable="true"
|
||||||
|
android:clickable="true" />
|
||||||
|
|
||||||
|
</RadioGroup>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
Reference in New Issue
Block a user