Use MaterialAlertDialogBuilder in PreferenceDialogFragments

`PreferenceDialogFragment`s have been extended to use `MaterialAlertDialogBuilder`, which results in Material Design 3 dialogs. `DialogFragment` creation logic has been moved to `SettingsActivity` to reduce code duplication.
This commit is contained in:
lynxnb 2023-03-07 23:35:35 +01:00 committed by Niccolò Betto
parent a4a5bedeea
commit d3d22670f9
7 changed files with 243 additions and 45 deletions

View File

@ -199,7 +199,7 @@ open class IntegerListPreference @JvmOverloads constructor(
} }
} }
class IntegerListPreferenceDialogFragmentCompat : PreferenceDialogFragmentCompat() { open class IntegerListPreferenceDialogFragmentCompat : PreferenceDialogFragmentCompat() {
var clickedDialogEntryIndex = 0 var clickedDialogEntryIndex = 0
private var entries : Array<CharSequence>? = null private var entries : Array<CharSequence>? = null
private var entryValues : IntArray? = null private var entryValues : IntArray? = null
@ -259,9 +259,9 @@ open class IntegerListPreference @JvmOverloads constructor(
} }
companion object { companion object {
private const val SAVE_STATE_INDEX = "ListPreferenceDialogFragment.index" private const val SAVE_STATE_INDEX = "IntegerListPreferenceDialogFragment.index"
private const val SAVE_STATE_ENTRIES = "ListPreferenceDialogFragment.entries" private const val SAVE_STATE_ENTRIES = "IntegerListPreferenceDialogFragment.entries"
private const val SAVE_STATE_ENTRY_VALUES = "ListPreferenceDialogFragment.entryValues" private const val SAVE_STATE_ENTRY_VALUES = "IntegerListPreferenceDialogFragment.entryValues"
fun newInstance(key : String?) : IntegerListPreferenceDialogFragmentCompat { fun newInstance(key : String?) : IntegerListPreferenceDialogFragmentCompat {
val fragment = IntegerListPreferenceDialogFragmentCompat() val fragment = IntegerListPreferenceDialogFragmentCompat()

View File

@ -0,0 +1,64 @@
/*
* SPDX-License-Identifier: MPL-2.0
* Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/
@file:SuppressLint("RestrictedApi")
package emu.skyline.preference.dialog
import android.annotation.SuppressLint
import android.app.Dialog
import android.os.Build
import android.os.Bundle
import android.view.WindowInsets
import androidx.preference.EditTextPreferenceDialogFragmentCompat
import com.google.android.material.dialog.MaterialAlertDialogBuilder
/**
* An [EditTextPreferenceDialogFragmentCompat] that uses [MaterialAlertDialogBuilder]
*/
class EditTextPreferenceMaterialDialogFragmentCompat : EditTextPreferenceDialogFragmentCompat() {
override fun onCreateDialog(savedInstanceState : Bundle?) : Dialog {
val builder = MaterialAlertDialogBuilder(requireContext())
.setTitle(preference.dialogTitle)
.setIcon(preference.dialogIcon)
.setPositiveButton(preference.positiveButtonText, this)
.setNegativeButton(preference.negativeButtonText, this)
val contentView = onCreateDialogView(requireContext())
if (contentView != null) {
onBindDialogView(contentView)
builder.setView(contentView)
} else {
builder.setMessage(preference.dialogMessage)
}
onPrepareDialogBuilder(builder)
// Create the dialog
val dialog : Dialog = builder.create()
if (needInputMethod()) {
requestInputMethod(dialog)
}
return dialog
}
private fun requestInputMethod(dialog : Dialog) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
dialog.window?.decorView?.windowInsetsController?.show(WindowInsets.Type.ime())
else
scheduleShowSoftInput()
}
companion object {
fun newInstance(key : String?) : EditTextPreferenceMaterialDialogFragmentCompat {
val fragment = EditTextPreferenceMaterialDialogFragmentCompat()
val bundle = Bundle(1)
bundle.putString(ARG_KEY, key)
fragment.arguments = bundle
return fragment
}
}
}

View File

@ -0,0 +1,64 @@
/*
* SPDX-License-Identifier: MPL-2.0
* Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/
@file:SuppressLint("RestrictedApi")
package emu.skyline.preference.dialog
import android.annotation.SuppressLint
import android.app.Dialog
import android.os.Build
import android.os.Bundle
import android.view.WindowInsets
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import emu.skyline.preference.IntegerListPreference
/**
* An [IntegerListPreference.IntegerListPreferenceDialogFragmentCompat] that uses [MaterialAlertDialogBuilder]
*/
class IntegerListPreferenceMaterialDialogFragmentCompat : IntegerListPreference.IntegerListPreferenceDialogFragmentCompat() {
override fun onCreateDialog(savedInstanceState : Bundle?) : Dialog {
val builder = MaterialAlertDialogBuilder(requireContext())
.setTitle(preference.dialogTitle)
.setIcon(preference.dialogIcon)
.setPositiveButton(preference.positiveButtonText, this)
.setNegativeButton(preference.negativeButtonText, this)
val contentView = onCreateDialogView(requireContext())
if (contentView != null) {
onBindDialogView(contentView)
builder.setView(contentView)
} else {
builder.setMessage(preference.dialogMessage)
}
onPrepareDialogBuilder(builder)
// Create the dialog
val dialog : Dialog = builder.create()
if (needInputMethod()) {
requestInputMethod(dialog)
}
return dialog
}
private fun requestInputMethod(dialog : Dialog) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
dialog.window?.decorView?.windowInsetsController?.show(WindowInsets.Type.ime())
else
scheduleShowSoftInput()
}
companion object {
fun newInstance(key : String?) : IntegerListPreferenceMaterialDialogFragmentCompat {
val fragment = IntegerListPreferenceMaterialDialogFragmentCompat()
val bundle = Bundle(1)
bundle.putString(ARG_KEY, key)
fragment.arguments = bundle
return fragment
}
}
}

View File

@ -0,0 +1,64 @@
/*
* SPDX-License-Identifier: MPL-2.0
* Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/
@file:SuppressLint("RestrictedApi")
package emu.skyline.preference.dialog
import android.annotation.SuppressLint
import android.app.Dialog
import android.os.Build
import android.os.Bundle
import android.view.WindowInsets
import androidx.preference.ListPreferenceDialogFragmentCompat
import com.google.android.material.dialog.MaterialAlertDialogBuilder
/**
* A [ListPreferenceDialogFragmentCompat] that uses [MaterialAlertDialogBuilder]
*/
class ListPreferenceMaterialDialogFragmentCompat : ListPreferenceDialogFragmentCompat() {
override fun onCreateDialog(savedInstanceState : Bundle?) : Dialog {
val builder = MaterialAlertDialogBuilder(requireContext())
.setTitle(preference.dialogTitle)
.setIcon(preference.dialogIcon)
.setPositiveButton(preference.positiveButtonText, this)
.setNegativeButton(preference.negativeButtonText, this)
val contentView = onCreateDialogView(requireContext())
if (contentView != null) {
onBindDialogView(contentView)
builder.setView(contentView)
} else {
builder.setMessage(preference.dialogMessage)
}
onPrepareDialogBuilder(builder)
// Create the dialog
val dialog : Dialog = builder.create()
if (needInputMethod()) {
requestInputMethod(dialog)
}
return dialog
}
private fun requestInputMethod(dialog : Dialog) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
dialog.window?.decorView?.windowInsetsController?.show(WindowInsets.Type.ime())
else
scheduleShowSoftInput()
}
companion object {
fun newInstance(key : String?) : ListPreferenceMaterialDialogFragmentCompat {
val fragment = ListPreferenceMaterialDialogFragmentCompat()
val bundle = Bundle(1)
bundle.putString(ARG_KEY, key)
fragment.arguments = bundle
return fragment
}
}
}

View File

@ -14,7 +14,6 @@ import emu.skyline.R
import emu.skyline.data.AppItem import emu.skyline.data.AppItem
import emu.skyline.data.AppItemTag import emu.skyline.data.AppItemTag
import emu.skyline.preference.GpuDriverPreference import emu.skyline.preference.GpuDriverPreference
import emu.skyline.preference.IntegerListPreference
import emu.skyline.utils.GpuDriverHelper import emu.skyline.utils.GpuDriverHelper
import emu.skyline.utils.WindowInsetsHelper import emu.skyline.utils.WindowInsetsHelper
import emu.skyline.utils.serializable import emu.skyline.utils.serializable
@ -23,10 +22,6 @@ import emu.skyline.utils.serializable
* This fragment is used to display custom game preferences * This fragment is used to display custom game preferences
*/ */
class GameSettingsFragment : PreferenceFragmentCompat() { class GameSettingsFragment : PreferenceFragmentCompat() {
companion object {
private const val DIALOG_FRAGMENT_TAG = "androidx.preference.PreferenceFragment.DIALOG"
}
private val item by lazy { requireArguments().serializable<AppItem>(AppItemTag)!! } private val item by lazy { requireArguments().serializable<AppItem>(AppItemTag)!! }
override fun onViewCreated(view : View, savedInstanceState : Bundle?) { override fun onViewCreated(view : View, savedInstanceState : Bundle?) {
@ -87,19 +82,4 @@ class GameSettingsFragment : PreferenceFragmentCompat() {
if (BuildConfig.BUILD_TYPE == "release") if (BuildConfig.BUILD_TYPE == "release")
findPreference<PreferenceCategory>("category_debug")?.isVisible = false findPreference<PreferenceCategory>("category_debug")?.isVisible = false
} }
override fun onDisplayPreferenceDialog(preference : Preference) {
if (preference is IntegerListPreference) {
// Check if dialog is already showing
if (parentFragmentManager.findFragmentByTag(DIALOG_FRAGMENT_TAG) != null)
return
val dialogFragment = IntegerListPreference.IntegerListPreferenceDialogFragmentCompat.newInstance(preference.getKey())
@Suppress("DEPRECATION")
dialogFragment.setTargetFragment(this, 0) // androidx.preference.PreferenceDialogFragmentCompat depends on the target fragment being set correctly even though it's deprecated
dialogFragment.show(parentFragmentManager, DIALOG_FRAGMENT_TAG)
} else {
super.onDisplayPreferenceDialog(preference)
}
}
} }

View File

@ -13,7 +13,6 @@ import androidx.preference.PreferenceFragmentCompat
import androidx.preference.TwoStatePreference import androidx.preference.TwoStatePreference
import emu.skyline.BuildConfig import emu.skyline.BuildConfig
import emu.skyline.R import emu.skyline.R
import emu.skyline.preference.IntegerListPreference
import emu.skyline.utils.GpuDriverHelper import emu.skyline.utils.GpuDriverHelper
import emu.skyline.utils.WindowInsetsHelper import emu.skyline.utils.WindowInsetsHelper
@ -21,10 +20,6 @@ import emu.skyline.utils.WindowInsetsHelper
* This fragment is used to display the global preferences * This fragment is used to display the global preferences
*/ */
class GlobalSettingsFragment : PreferenceFragmentCompat() { class GlobalSettingsFragment : PreferenceFragmentCompat() {
companion object {
private const val DIALOG_FRAGMENT_TAG = "androidx.preference.PreferenceFragment.DIALOG"
}
override fun onViewCreated(view : View, savedInstanceState : Bundle?) { override fun onViewCreated(view : View, savedInstanceState : Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val recyclerView = view.findViewById<View>(R.id.recycler_view) val recyclerView = view.findViewById<View>(R.id.recycler_view)
@ -66,19 +61,4 @@ class GlobalSettingsFragment : PreferenceFragmentCompat() {
}) })
} }
} }
override fun onDisplayPreferenceDialog(preference : Preference) {
if (preference is IntegerListPreference) {
// Check if dialog is already showing
if (parentFragmentManager.findFragmentByTag(DIALOG_FRAGMENT_TAG) != null)
return
val dialogFragment = IntegerListPreference.IntegerListPreferenceDialogFragmentCompat.newInstance(preference.getKey())
@Suppress("DEPRECATION")
dialogFragment.setTargetFragment(this, 0) // androidx.preference.PreferenceDialogFragmentCompat depends on the target fragment being set correctly even though it's deprecated
dialogFragment.show(parentFragmentManager, DIALOG_FRAGMENT_TAG)
} else {
super.onDisplayPreferenceDialog(preference)
}
}
} }

View File

@ -14,14 +14,23 @@ import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.preference.EditTextPreference
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import com.google.android.material.internal.ToolbarUtils import com.google.android.material.internal.ToolbarUtils
import emu.skyline.R import emu.skyline.R
import emu.skyline.data.AppItemTag import emu.skyline.data.AppItemTag
import emu.skyline.databinding.SettingsActivityBinding import emu.skyline.databinding.SettingsActivityBinding
import emu.skyline.preference.IntegerListPreference
import emu.skyline.preference.dialog.EditTextPreferenceMaterialDialogFragmentCompat
import emu.skyline.preference.dialog.IntegerListPreferenceMaterialDialogFragmentCompat
import emu.skyline.preference.dialog.ListPreferenceMaterialDialogFragmentCompat
import emu.skyline.utils.WindowInsetsHelper import emu.skyline.utils.WindowInsetsHelper
class SettingsActivity : AppCompatActivity() { private const val PREFERENCE_DIALOG_FRAGMENT_TAG = "androidx.preference.PreferenceFragment.DIALOG"
class SettingsActivity : AppCompatActivity(), PreferenceFragmentCompat.OnPreferenceDisplayDialogCallback {
val binding by lazy { SettingsActivityBinding.inflate(layoutInflater) } val binding by lazy { SettingsActivityBinding.inflate(layoutInflater) }
/** /**
@ -118,4 +127,41 @@ class SettingsActivity : AppCompatActivity() {
setResult(RESULT_OK) setResult(RESULT_OK)
super.finish() super.finish()
} }
override fun onPreferenceDisplayDialog(caller : PreferenceFragmentCompat, pref : Preference) : Boolean {
when (pref) {
is IntegerListPreference -> {
// Check if dialog is already showing
if (supportFragmentManager.findFragmentByTag(PREFERENCE_DIALOG_FRAGMENT_TAG) != null)
return true
val dialogFragment = IntegerListPreferenceMaterialDialogFragmentCompat.newInstance(pref.getKey())
@Suppress("DEPRECATION")
dialogFragment.setTargetFragment(caller, 0) // androidx.preference.PreferenceDialogFragmentCompat depends on the target fragment being set correctly even though it's deprecated
dialogFragment.show(supportFragmentManager, PREFERENCE_DIALOG_FRAGMENT_TAG)
return true
}
is EditTextPreference -> {
if (supportFragmentManager.findFragmentByTag(PREFERENCE_DIALOG_FRAGMENT_TAG) != null)
return true
val dialogFragment = EditTextPreferenceMaterialDialogFragmentCompat.newInstance(pref.getKey())
@Suppress("DEPRECATION")
dialogFragment.setTargetFragment(caller, 0)
dialogFragment.show(supportFragmentManager, PREFERENCE_DIALOG_FRAGMENT_TAG)
return true
}
is ListPreference -> {
if (supportFragmentManager.findFragmentByTag(PREFERENCE_DIALOG_FRAGMENT_TAG) != null)
return true
val dialogFragment = ListPreferenceMaterialDialogFragmentCompat.newInstance(pref.getKey())
@Suppress("DEPRECATION")
dialogFragment.setTargetFragment(caller, 0)
dialogFragment.show(supportFragmentManager, PREFERENCE_DIALOG_FRAGMENT_TAG)
return true
}
else -> return false
}
}
} }