Explorar o código

android: Implement paired settings

Enables and disables editing on settings that rely on other boolean settings.
Charles Lombardo %!s(int64=2) %!d(string=hai) anos
pai
achega
d786d19880

+ 5 - 2
src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractSetting.kt

@@ -6,14 +6,17 @@ package org.yuzu.yuzu_emu.features.settings.model
 import org.yuzu.yuzu_emu.utils.NativeConfig
 
 interface AbstractSetting {
-    val key: String?
+    val key: String
     val category: Settings.Category
     val defaultValue: Any
     val valueAsString: String
         get() = ""
 
     val isRuntimeModifiable: Boolean
-        get() = NativeConfig.getIsRuntimeModifiable(key!!)
+        get() = NativeConfig.getIsRuntimeModifiable(key)
+
+    val pairedSettingKey: String
+        get() = NativeConfig.getPairedSettingKey(key)
 
     fun reset()
 }

+ 235 - 0
src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt

@@ -4,8 +4,15 @@
 package org.yuzu.yuzu_emu.features.settings.model.view
 
 import org.yuzu.yuzu_emu.NativeLibrary
+import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
 import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
+import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
+import org.yuzu.yuzu_emu.features.settings.model.ByteSetting
+import org.yuzu.yuzu_emu.features.settings.model.IntSetting
+import org.yuzu.yuzu_emu.features.settings.model.LongSetting
 import org.yuzu.yuzu_emu.features.settings.model.Settings
+import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
 
 /**
  * ViewModel abstraction for an Item in the RecyclerView powering SettingsFragments.
@@ -37,11 +44,239 @@ abstract class SettingsItem(
         const val TYPE_DATETIME_SETTING = 6
         const val TYPE_RUNNABLE = 7
 
+        const val FASTMEM_COMBINED = "fastmem_combined"
+
         val emptySetting = object : AbstractSetting {
             override val key: String = ""
             override val category: Settings.Category = Settings.Category.Ui
             override val defaultValue: Any = false
             override fun reset() {}
         }
+
+        // Extension for putting SettingsItems into a hashmap without repeating yourself
+        fun HashMap<String, SettingsItem>.put(item: SettingsItem) {
+            put(item.setting.key, item)
+        }
+
+        // List of all general
+        val settingsItems = HashMap<String, SettingsItem>().apply {
+            put(
+                SwitchSetting(
+                    BooleanSetting.RENDERER_USE_SPEED_LIMIT,
+                    R.string.frame_limit_enable,
+                    R.string.frame_limit_enable_description
+                )
+            )
+            put(
+                SliderSetting(
+                    ShortSetting.RENDERER_SPEED_LIMIT,
+                    R.string.frame_limit_slider,
+                    R.string.frame_limit_slider_description,
+                    1,
+                    200,
+                    "%"
+                )
+            )
+            put(
+                SingleChoiceSetting(
+                    IntSetting.CPU_ACCURACY,
+                    R.string.cpu_accuracy,
+                    0,
+                    R.array.cpuAccuracyNames,
+                    R.array.cpuAccuracyValues
+                )
+            )
+            put(
+                SwitchSetting(
+                    BooleanSetting.PICTURE_IN_PICTURE,
+                    R.string.picture_in_picture,
+                    R.string.picture_in_picture_description
+                )
+            )
+            put(
+                SwitchSetting(
+                    BooleanSetting.USE_DOCKED_MODE,
+                    R.string.use_docked_mode,
+                    R.string.use_docked_mode_description
+                )
+            )
+            put(
+                SingleChoiceSetting(
+                    IntSetting.REGION_INDEX,
+                    R.string.emulated_region,
+                    0,
+                    R.array.regionNames,
+                    R.array.regionValues
+                )
+            )
+            put(
+                SingleChoiceSetting(
+                    IntSetting.LANGUAGE_INDEX,
+                    R.string.emulated_language,
+                    0,
+                    R.array.languageNames,
+                    R.array.languageValues
+                )
+            )
+            put(
+                SwitchSetting(
+                    BooleanSetting.USE_CUSTOM_RTC,
+                    R.string.use_custom_rtc,
+                    R.string.use_custom_rtc_description
+                )
+            )
+            put(DateTimeSetting(LongSetting.CUSTOM_RTC, R.string.set_custom_rtc, 0))
+            put(
+                SingleChoiceSetting(
+                    IntSetting.RENDERER_ACCURACY,
+                    R.string.renderer_accuracy,
+                    0,
+                    R.array.rendererAccuracyNames,
+                    R.array.rendererAccuracyValues
+                )
+            )
+            put(
+                SingleChoiceSetting(
+                    IntSetting.RENDERER_RESOLUTION,
+                    R.string.renderer_resolution,
+                    0,
+                    R.array.rendererResolutionNames,
+                    R.array.rendererResolutionValues
+                )
+            )
+            put(
+                SingleChoiceSetting(
+                    IntSetting.RENDERER_VSYNC,
+                    R.string.renderer_vsync,
+                    0,
+                    R.array.rendererVSyncNames,
+                    R.array.rendererVSyncValues
+                )
+            )
+            put(
+                SingleChoiceSetting(
+                    IntSetting.RENDERER_SCALING_FILTER,
+                    R.string.renderer_scaling_filter,
+                    0,
+                    R.array.rendererScalingFilterNames,
+                    R.array.rendererScalingFilterValues
+                )
+            )
+            put(
+                SingleChoiceSetting(
+                    IntSetting.RENDERER_ANTI_ALIASING,
+                    R.string.renderer_anti_aliasing,
+                    0,
+                    R.array.rendererAntiAliasingNames,
+                    R.array.rendererAntiAliasingValues
+                )
+            )
+            put(
+                SingleChoiceSetting(
+                    IntSetting.RENDERER_SCREEN_LAYOUT,
+                    R.string.renderer_screen_layout,
+                    0,
+                    R.array.rendererScreenLayoutNames,
+                    R.array.rendererScreenLayoutValues
+                )
+            )
+            put(
+                SingleChoiceSetting(
+                    IntSetting.RENDERER_ASPECT_RATIO,
+                    R.string.renderer_aspect_ratio,
+                    0,
+                    R.array.rendererAspectRatioNames,
+                    R.array.rendererAspectRatioValues
+                )
+            )
+            put(
+                SwitchSetting(
+                    BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE,
+                    R.string.use_disk_shader_cache,
+                    R.string.use_disk_shader_cache_description
+                )
+            )
+            put(
+                SwitchSetting(
+                    BooleanSetting.RENDERER_FORCE_MAX_CLOCK,
+                    R.string.renderer_force_max_clock,
+                    R.string.renderer_force_max_clock_description
+                )
+            )
+            put(
+                SwitchSetting(
+                    BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS,
+                    R.string.renderer_asynchronous_shaders,
+                    R.string.renderer_asynchronous_shaders_description
+                )
+            )
+            put(
+                SwitchSetting(
+                    BooleanSetting.RENDERER_REACTIVE_FLUSHING,
+                    R.string.renderer_reactive_flushing,
+                    R.string.renderer_reactive_flushing_description
+                )
+            )
+            put(
+                SingleChoiceSetting(
+                    IntSetting.AUDIO_OUTPUT_ENGINE,
+                    R.string.audio_output_engine,
+                    0,
+                    R.array.outputEngineEntries,
+                    R.array.outputEngineValues
+                )
+            )
+            put(
+                SliderSetting(
+                    ByteSetting.AUDIO_VOLUME,
+                    R.string.audio_volume,
+                    R.string.audio_volume_description,
+                    0,
+                    100,
+                    "%"
+                )
+            )
+            put(
+                SingleChoiceSetting(
+                    IntSetting.RENDERER_BACKEND,
+                    R.string.renderer_api,
+                    0,
+                    R.array.rendererApiNames,
+                    R.array.rendererApiValues
+                )
+            )
+            put(
+                SwitchSetting(
+                    BooleanSetting.RENDERER_DEBUG,
+                    R.string.renderer_debug,
+                    R.string.renderer_debug_description
+                )
+            )
+            put(
+                SwitchSetting(
+                    BooleanSetting.CPU_DEBUG_MODE,
+                    R.string.cpu_debug_mode,
+                    R.string.cpu_debug_mode_description
+                )
+            )
+
+            val fastmem = object : AbstractBooleanSetting {
+                override val boolean: Boolean
+                    get() =
+                        BooleanSetting.FASTMEM.boolean && BooleanSetting.FASTMEM_EXCLUSIVES.boolean
+
+                override fun setBoolean(value: Boolean) {
+                    BooleanSetting.FASTMEM.setBoolean(value)
+                    BooleanSetting.FASTMEM_EXCLUSIVES.setBoolean(value)
+                }
+
+                override val key: String = FASTMEM_COMBINED
+                override val category = Settings.Category.Cpu
+                override val isRuntimeModifiable: Boolean = false
+                override val defaultValue: Boolean = true
+                override fun reset() = setBoolean(defaultValue)
+            }
+            put(SwitchSetting(fastmem, R.string.fastmem, 0))
+        }
     }
 }

+ 20 - 14
src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt

@@ -14,7 +14,9 @@ import android.widget.TextView
 import androidx.appcompat.app.AlertDialog
 import androidx.lifecycle.ViewModelProvider
 import androidx.navigation.findNavController
-import androidx.recyclerview.widget.RecyclerView
+import androidx.recyclerview.widget.AsyncDifferConfig
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
 import com.google.android.material.datepicker.MaterialDatePicker
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import com.google.android.material.slider.Slider
@@ -37,8 +39,8 @@ import org.yuzu.yuzu_emu.model.SettingsViewModel
 class SettingsAdapter(
     private val fragment: SettingsFragment,
     private val context: Context
-) : RecyclerView.Adapter<SettingViewHolder?>(), DialogInterface.OnClickListener {
-    private var settings = ArrayList<SettingsItem>()
+) : ListAdapter<SettingsItem, SettingViewHolder>(AsyncDifferConfig.Builder(DiffCallback()).build()),
+    DialogInterface.OnClickListener {
     private var clickedItem: SettingsItem? = null
     private var clickedPosition: Int
     private var dialog: AlertDialog? = null
@@ -94,24 +96,18 @@ class SettingsAdapter(
     }
 
     override fun onBindViewHolder(holder: SettingViewHolder, position: Int) {
-        holder.bind(getItem(position))
+        holder.bind(currentList[position])
     }
 
-    private fun getItem(position: Int): SettingsItem = settings[position]
-
-    override fun getItemCount(): Int = settings.size
+    override fun getItemCount(): Int = currentList.size
 
     override fun getItemViewType(position: Int): Int {
-        return getItem(position).type
-    }
-
-    fun setSettingsList(settings: ArrayList<SettingsItem>) {
-        this.settings = settings
-        notifyDataSetChanged()
+        return currentList[position].type
     }
 
-    fun onBooleanClick(item: SwitchSetting, position: Int, checked: Boolean) {
+    fun onBooleanClick(item: SwitchSetting, checked: Boolean) {
         item.checked = checked
+        settingsViewModel.setShouldReloadSettingsList(true)
         settingsViewModel.shouldSave = true
     }
 
@@ -338,4 +334,14 @@ class SettingsAdapter(
         }
         return -1
     }
+
+    private class DiffCallback : DiffUtil.ItemCallback<SettingsItem>() {
+        override fun areItemsTheSame(oldItem: SettingsItem, newItem: SettingsItem): Boolean {
+            return oldItem.setting.key == newItem.setting.key
+        }
+
+        override fun areContentsTheSame(oldItem: SettingsItem, newItem: SettingsItem): Boolean {
+            return oldItem.setting.key == newItem.setting.key
+        }
+    }
 }

+ 7 - 0
src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt

@@ -77,6 +77,13 @@ class SettingsFragment : Fragment() {
             if (it.isNotEmpty()) binding.toolbarSettingsLayout.title = it
         }
 
+        settingsViewModel.shouldReloadSettingsList.observe(viewLifecycleOwner) {
+            if (it) {
+                settingsViewModel.setShouldReloadSettingsList(false)
+                presenter.loadSettingsList()
+            }
+        }
+
         presenter.onViewCreated()
 
         setInsets()

+ 43 - 222
src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt

@@ -22,6 +22,7 @@ import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
 import org.yuzu.yuzu_emu.features.settings.model.view.*
 import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
 import org.yuzu.yuzu_emu.model.SettingsViewModel
+import org.yuzu.yuzu_emu.utils.NativeConfig
 
 class SettingsFragmentPresenter(
     private val settingsViewModel: SettingsViewModel,
@@ -36,11 +37,22 @@ class SettingsFragmentPresenter(
 
     private val context: Context get() = YuzuApplication.appContext
 
+    // Extension for populating settings list based on paired settings
+    fun ArrayList<SettingsItem>.add(key: String) {
+        val item = SettingsItem.settingsItems[key]!!
+        val pairedSettingKey = item.setting.pairedSettingKey
+        if (pairedSettingKey.isNotEmpty()) {
+            val pairedSettingValue = NativeConfig.getBoolean(pairedSettingKey, false)
+            if (!pairedSettingValue) return
+        }
+        add(item)
+    }
+
     fun onViewCreated() {
         loadSettingsList()
     }
 
-    private fun loadSettingsList() {
+    fun loadSettingsList() {
         if (!TextUtils.isEmpty(gameId)) {
             settingsViewModel.setToolbarTitle(
                 context.getString(
@@ -70,7 +82,7 @@ class SettingsFragmentPresenter(
             }
         }
         settingsList = sl
-        adapter.setSettingsList(settingsList)
+        adapter.submitList(settingsList)
     }
 
     private fun addConfigSettings(sl: ArrayList<SettingsItem>) {
@@ -92,200 +104,46 @@ class SettingsFragmentPresenter(
     private fun addGeneralSettings(sl: ArrayList<SettingsItem>) {
         settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_general))
         sl.apply {
-            add(
-                SwitchSetting(
-                    BooleanSetting.RENDERER_USE_SPEED_LIMIT,
-                    R.string.frame_limit_enable,
-                    R.string.frame_limit_enable_description
-                )
-            )
-            add(
-                SliderSetting(
-                    ShortSetting.RENDERER_SPEED_LIMIT,
-                    R.string.frame_limit_slider,
-                    R.string.frame_limit_slider_description,
-                    1,
-                    200,
-                    "%"
-                )
-            )
-            add(
-                SingleChoiceSetting(
-                    IntSetting.CPU_ACCURACY,
-                    R.string.cpu_accuracy,
-                    0,
-                    R.array.cpuAccuracyNames,
-                    R.array.cpuAccuracyValues
-                )
-            )
-            add(
-                SwitchSetting(
-                    BooleanSetting.PICTURE_IN_PICTURE,
-                    R.string.picture_in_picture,
-                    R.string.picture_in_picture_description
-                )
-            )
+            add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key)
+            add(ShortSetting.RENDERER_SPEED_LIMIT.key)
+            add(IntSetting.CPU_ACCURACY.key)
+            add(BooleanSetting.PICTURE_IN_PICTURE.key)
         }
     }
 
     private fun addSystemSettings(sl: ArrayList<SettingsItem>) {
         settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_system))
         sl.apply {
-            add(
-                SwitchSetting(
-                    BooleanSetting.USE_DOCKED_MODE,
-                    R.string.use_docked_mode,
-                    R.string.use_docked_mode_description
-                )
-            )
-            add(
-                SingleChoiceSetting(
-                    IntSetting.REGION_INDEX,
-                    R.string.emulated_region,
-                    0,
-                    R.array.regionNames,
-                    R.array.regionValues
-                )
-            )
-            add(
-                SingleChoiceSetting(
-                    IntSetting.LANGUAGE_INDEX,
-                    R.string.emulated_language,
-                    0,
-                    R.array.languageNames,
-                    R.array.languageValues
-                )
-            )
-            add(
-                SwitchSetting(
-                    BooleanSetting.USE_CUSTOM_RTC,
-                    R.string.use_custom_rtc,
-                    R.string.use_custom_rtc_description
-                )
-            )
-            add(DateTimeSetting(LongSetting.CUSTOM_RTC, R.string.set_custom_rtc, 0))
+            add(BooleanSetting.USE_DOCKED_MODE.key)
+            add(IntSetting.REGION_INDEX.key)
+            add(IntSetting.LANGUAGE_INDEX.key)
+            add(BooleanSetting.USE_CUSTOM_RTC.key)
+            add(LongSetting.CUSTOM_RTC.key)
         }
     }
 
     private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) {
         settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_graphics))
         sl.apply {
-            add(
-                SingleChoiceSetting(
-                    IntSetting.RENDERER_ACCURACY,
-                    R.string.renderer_accuracy,
-                    0,
-                    R.array.rendererAccuracyNames,
-                    R.array.rendererAccuracyValues
-                )
-            )
-            add(
-                SingleChoiceSetting(
-                    IntSetting.RENDERER_RESOLUTION,
-                    R.string.renderer_resolution,
-                    0,
-                    R.array.rendererResolutionNames,
-                    R.array.rendererResolutionValues
-                )
-            )
-            add(
-                SingleChoiceSetting(
-                    IntSetting.RENDERER_VSYNC,
-                    R.string.renderer_vsync,
-                    0,
-                    R.array.rendererVSyncNames,
-                    R.array.rendererVSyncValues
-                )
-            )
-            add(
-                SingleChoiceSetting(
-                    IntSetting.RENDERER_SCALING_FILTER,
-                    R.string.renderer_scaling_filter,
-                    0,
-                    R.array.rendererScalingFilterNames,
-                    R.array.rendererScalingFilterValues
-                )
-            )
-            add(
-                SingleChoiceSetting(
-                    IntSetting.RENDERER_ANTI_ALIASING,
-                    R.string.renderer_anti_aliasing,
-                    0,
-                    R.array.rendererAntiAliasingNames,
-                    R.array.rendererAntiAliasingValues
-                )
-            )
-            add(
-                SingleChoiceSetting(
-                    IntSetting.RENDERER_SCREEN_LAYOUT,
-                    R.string.renderer_screen_layout,
-                    0,
-                    R.array.rendererScreenLayoutNames,
-                    R.array.rendererScreenLayoutValues
-                )
-            )
-            add(
-                SingleChoiceSetting(
-                    IntSetting.RENDERER_ASPECT_RATIO,
-                    R.string.renderer_aspect_ratio,
-                    0,
-                    R.array.rendererAspectRatioNames,
-                    R.array.rendererAspectRatioValues
-                )
-            )
-            add(
-                SwitchSetting(
-                    BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE,
-                    R.string.use_disk_shader_cache,
-                    R.string.use_disk_shader_cache_description
-                )
-            )
-            add(
-                SwitchSetting(
-                    BooleanSetting.RENDERER_FORCE_MAX_CLOCK,
-                    R.string.renderer_force_max_clock,
-                    R.string.renderer_force_max_clock_description
-                )
-            )
-            add(
-                SwitchSetting(
-                    BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS,
-                    R.string.renderer_asynchronous_shaders,
-                    R.string.renderer_asynchronous_shaders_description
-                )
-            )
-            add(
-                SwitchSetting(
-                    BooleanSetting.RENDERER_REACTIVE_FLUSHING,
-                    R.string.renderer_reactive_flushing,
-                    R.string.renderer_reactive_flushing_description
-                )
-            )
+            add(IntSetting.RENDERER_ACCURACY.key)
+            add(IntSetting.RENDERER_RESOLUTION.key)
+            add(IntSetting.RENDERER_VSYNC.key)
+            add(IntSetting.RENDERER_SCALING_FILTER.key)
+            add(IntSetting.RENDERER_ANTI_ALIASING.key)
+            add(IntSetting.RENDERER_SCREEN_LAYOUT.key)
+            add(IntSetting.RENDERER_ASPECT_RATIO.key)
+            add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key)
+            add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
+            add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
+            add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key)
         }
     }
 
     private fun addAudioSettings(sl: ArrayList<SettingsItem>) {
         settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_audio))
         sl.apply {
-            add(
-                SingleChoiceSetting(
-                    IntSetting.AUDIO_OUTPUT_ENGINE,
-                    R.string.audio_output_engine,
-                    0,
-                    R.array.outputEngineEntries,
-                    R.array.outputEngineValues
-                )
-            )
-            add(
-                SliderSetting(
-                    ByteSetting.AUDIO_VOLUME,
-                    R.string.audio_volume,
-                    R.string.audio_volume_description,
-                    0,
-                    100,
-                    "%"
-                )
-            )
+            add(IntSetting.AUDIO_OUTPUT_ENGINE.key)
+            add(ByteSetting.AUDIO_VOLUME.key)
         }
     }
 
@@ -303,7 +161,7 @@ class SettingsFragmentPresenter(
                     settingsViewModel.setShouldRecreate(true)
                 }
 
-                override val key: String? = null
+                override val key: String = Settings.PREF_THEME
                 override val category = Settings.Category.UiGeneral
                 override val isRuntimeModifiable: Boolean = false
                 override val defaultValue: Int = 0
@@ -347,7 +205,7 @@ class SettingsFragmentPresenter(
                     settingsViewModel.setShouldRecreate(true)
                 }
 
-                override val key: String? = null
+                override val key: String = Settings.PREF_THEME_MODE
                 override val category = Settings.Category.UiGeneral
                 override val isRuntimeModifiable: Boolean = false
                 override val defaultValue: Int = -1
@@ -380,7 +238,7 @@ class SettingsFragmentPresenter(
                     settingsViewModel.setShouldRecreate(true)
                 }
 
-                override val key: String? = null
+                override val key: String = Settings.PREF_BLACK_BACKGROUNDS
                 override val category = Settings.Category.UiGeneral
                 override val isRuntimeModifiable: Boolean = false
                 override val defaultValue: Boolean = false
@@ -406,49 +264,12 @@ class SettingsFragmentPresenter(
         settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_debug))
         sl.apply {
             add(HeaderSetting(R.string.gpu))
-            add(
-                SingleChoiceSetting(
-                    IntSetting.RENDERER_BACKEND,
-                    R.string.renderer_api,
-                    0,
-                    R.array.rendererApiNames,
-                    R.array.rendererApiValues
-                )
-            )
-            add(
-                SwitchSetting(
-                    BooleanSetting.RENDERER_DEBUG,
-                    R.string.renderer_debug,
-                    R.string.renderer_debug_description
-                )
-            )
+            add(IntSetting.RENDERER_BACKEND.key)
+            add(BooleanSetting.RENDERER_DEBUG.key)
 
             add(HeaderSetting(R.string.cpu))
-            add(
-                SwitchSetting(
-                    BooleanSetting.CPU_DEBUG_MODE,
-                    R.string.cpu_debug_mode,
-                    R.string.cpu_debug_mode_description
-                )
-            )
-
-            val fastmem = object : AbstractBooleanSetting {
-                override val boolean: Boolean
-                    get() =
-                        BooleanSetting.FASTMEM.boolean && BooleanSetting.FASTMEM_EXCLUSIVES.boolean
-
-                override fun setBoolean(value: Boolean) {
-                    BooleanSetting.FASTMEM.setBoolean(value)
-                    BooleanSetting.FASTMEM_EXCLUSIVES.setBoolean(value)
-                }
-
-                override val key: String? = null
-                override val category = Settings.Category.Cpu
-                override val isRuntimeModifiable: Boolean = false
-                override val defaultValue: Boolean = true
-                override fun reset() = setBoolean(defaultValue)
-            }
-            add(SwitchSetting(fastmem, R.string.fastmem, 0))
+            add(BooleanSetting.CPU_DEBUG_MODE.key)
+            add(SettingsItem.FASTMEM_COMBINED)
         }
     }
 }

+ 2 - 2
src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt

@@ -29,7 +29,7 @@ class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter
         binding.switchWidget.setOnCheckedChangeListener(null)
         binding.switchWidget.isChecked = setting.checked
         binding.switchWidget.setOnCheckedChangeListener { _: CompoundButton, _: Boolean ->
-            adapter.onBooleanClick(item, bindingAdapterPosition, binding.switchWidget.isChecked)
+            adapter.onBooleanClick(item, binding.switchWidget.isChecked)
         }
 
         setStyle(setting.isEditable, binding)
@@ -43,7 +43,7 @@ class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter
 
     override fun onLongClick(clicked: View): Boolean {
         if (setting.isEditable) {
-            return adapter.onLongClick(setting.setting!!, bindingAdapterPosition)
+            return adapter.onLongClick(setting.setting, bindingAdapterPosition)
         }
         return false
     }

+ 7 - 0
src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt

@@ -24,6 +24,9 @@ class SettingsViewModel : ViewModel() {
     private val _shouldShowResetSettingsDialog = MutableLiveData(false)
     val shouldShowResetSettingsDialog: LiveData<Boolean> get() = _shouldShowResetSettingsDialog
 
+    private val _shouldReloadSettingsList = MutableLiveData(false)
+    val shouldReloadSettingsList: LiveData<Boolean> get() = _shouldReloadSettingsList
+
     fun setToolbarTitle(value: String) {
         _toolbarTitle.value = value
     }
@@ -40,6 +43,10 @@ class SettingsViewModel : ViewModel() {
         _shouldShowResetSettingsDialog.value = value
     }
 
+    fun setShouldReloadSettingsList(value: Boolean) {
+        _shouldReloadSettingsList.value = value
+    }
+
     fun clear() {
         game = null
         shouldSave = false

+ 2 - 0
src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt

@@ -28,4 +28,6 @@ object NativeConfig {
     external fun getIsRuntimeModifiable(key: String): Boolean
 
     external fun getConfigHeader(category: Int): String
+
+    external fun getPairedSettingKey(key: String): String
 }

+ 14 - 1
src/android/app/src/main/jni/native_config.cpp

@@ -216,9 +216,22 @@ jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsRuntimeModifiable(JNIEn
 }
 
 jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getConfigHeader(JNIEnv* env, jobject obj,
-                                                                         jint jcategory) {
+                                                                   jint jcategory) {
     auto category = static_cast<Settings::Category>(jcategory);
     return ToJString(env, Settings::TranslateCategory(category));
 }
 
+jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getPairedSettingKey(JNIEnv* env, jobject obj,
+                                                                       jstring jkey) {
+    auto setting = getSetting<std::string>(env, jkey);
+    if (setting == nullptr) {
+        return ToJString(env, "");
+    }
+    if (setting->PairedSetting() == nullptr) {
+        return ToJString(env, "");
+    }
+
+    return ToJString(env, setting->PairedSetting()->GetLabel());
+}
+
 } // extern "C"