android: Button to reset all settings

This commit is contained in:
Charles Lombardo 2023-05-14 13:54:13 -04:00 committed by bunnei
parent 0f06e73a7c
commit ee57aa83a4
21 changed files with 138 additions and 23 deletions

View File

@ -32,5 +32,7 @@ enum class BooleanSetting(
fun from(key: String): BooleanSetting? = fun from(key: String): BooleanSetting? =
BooleanSetting.values().firstOrNull { it.key == key } BooleanSetting.values().firstOrNull { it.key == key }
fun clear() = BooleanSetting.values().forEach { it.boolean = it.defaultValue }
} }
} }

View File

@ -30,5 +30,7 @@ enum class FloatSetting(
private val NOT_RUNTIME_EDITABLE = emptyList<FloatSetting>() private val NOT_RUNTIME_EDITABLE = emptyList<FloatSetting>()
fun from(key: String): FloatSetting? = FloatSetting.values().firstOrNull { it.key == key } fun from(key: String): FloatSetting? = FloatSetting.values().firstOrNull { it.key == key }
fun clear() = FloatSetting.values().forEach { it.float = it.defaultValue }
} }
} }

View File

@ -119,5 +119,7 @@ enum class IntSetting(
) )
fun from(key: String): IntSetting? = IntSetting.values().firstOrNull { it.key == key } fun from(key: String): IntSetting? = IntSetting.values().firstOrNull { it.key == key }
fun clear() = IntSetting.values().forEach { it.int = it.defaultValue }
} }
} }

View File

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model package org.yuzu.yuzu_emu.features.settings.model
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel

View File

@ -31,5 +31,7 @@ enum class StringSetting(
) )
fun from(key: String): StringSetting? = StringSetting.values().firstOrNull { it.key == key } fun from(key: String): StringSetting? = StringSetting.values().firstOrNull { it.key == key }
fun clear() = StringSetting.values().forEach { it.string = it.defaultValue }
} }
} }

View File

@ -8,7 +8,7 @@ import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
class HeaderSetting( class HeaderSetting(
setting: AbstractSetting?, setting: AbstractSetting?,
titleId: Int, titleId: Int,
descriptionId: Int? descriptionId: Int
) : SettingsItem(setting, titleId, descriptionId) { ) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_HEADER override val type = TYPE_HEADER
} }

View File

@ -0,0 +1,12 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model.view
class RunnableSetting(
titleId: Int,
descriptionId: Int,
val runnable: () -> Unit
) : SettingsItem(null, titleId, descriptionId) {
override val type = TYPE_RUNNABLE
}

View File

@ -16,7 +16,7 @@ import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
abstract class SettingsItem( abstract class SettingsItem(
var setting: AbstractSetting?, var setting: AbstractSetting?,
val nameId: Int, val nameId: Int,
val descriptionId: Int? val descriptionId: Int
) { ) {
abstract val type: Int abstract val type: Int
@ -34,5 +34,6 @@ abstract class SettingsItem(
const val TYPE_SUBMENU = 4 const val TYPE_SUBMENU = 4
const val TYPE_STRING_SINGLE_CHOICE = 5 const val TYPE_STRING_SINGLE_CHOICE = 5
const val TYPE_DATETIME_SETTING = 6 const val TYPE_DATETIME_SETTING = 6
const val TYPE_RUNNABLE = 7
} }
} }

View File

@ -6,9 +6,6 @@ package org.yuzu.yuzu_emu.features.settings.model.view
import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting 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.IntSetting
import org.yuzu.yuzu_emu.utils.Log
class SwitchSetting( class SwitchSetting(
setting: AbstractSetting, setting: AbstractSetting,

View File

@ -8,7 +8,6 @@ import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.View import android.view.View
import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
@ -21,9 +20,15 @@ import com.google.android.material.color.MaterialColors
import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.FloatSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.utils.* import org.yuzu.yuzu_emu.utils.*
import java.io.IOException
class SettingsActivity : AppCompatActivity(), SettingsActivityView { class SettingsActivity : AppCompatActivity(), SettingsActivityView {
private val presenter = SettingsActivityPresenter(this) private val presenter = SettingsActivityPresenter(this)
@ -165,6 +170,26 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
presenter.onSettingChanged() presenter.onSettingChanged()
} }
fun onSettingsReset() {
// Prevents saving to a non-existent settings file
presenter.onSettingsReset()
// Reset the static memory representation of each setting
BooleanSetting.clear()
FloatSetting.clear()
IntSetting.clear()
StringSetting.clear()
// Delete settings file because the user may have changed values that do not exist in the UI
val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG)
if (!settingsFile.delete()) {
throw IOException("Failed to delete $settingsFile")
}
showToastMessage(getString(R.string.settings_reset), true)
finish()
}
private val settingsFragment: SettingsFragment? private val settingsFragment: SettingsFragment?
get() = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as SettingsFragment? get() = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as SettingsFragment?

View File

@ -70,6 +70,10 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView)
shouldSave = true shouldSave = true
} }
fun onSettingsReset() {
shouldSave = false
}
fun saveState(outState: Bundle) { fun saveState(outState: Bundle) {
outState.putBoolean(KEY_SHOULD_SAVE, shouldSave) outState.putBoolean(KEY_SHOULD_SAVE, shouldSave)
} }

View File

@ -79,6 +79,10 @@ class SettingsAdapter(
DateTimeViewHolder(ListItemSettingBinding.inflate(inflater), this) DateTimeViewHolder(ListItemSettingBinding.inflate(inflater), this)
} }
SettingsItem.TYPE_RUNNABLE -> {
RunnableViewHolder(ListItemSettingBinding.inflate(inflater), this)
}
else -> { else -> {
// TODO: Create an error view since we can't return null now // TODO: Create an error view since we can't return null now
HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this) HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this)

View File

@ -6,8 +6,8 @@ package org.yuzu.yuzu_emu.features.settings.ui
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.Build import android.os.Build
import android.text.TextUtils import android.text.TextUtils
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
@ -26,7 +26,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
private lateinit var gameId: String private lateinit var gameId: String
private var settingsList: ArrayList<SettingsItem>? = null private var settingsList: ArrayList<SettingsItem>? = null
private val settingsActivity get() = fragmentView.activityView as AppCompatActivity private val settingsActivity get() = fragmentView.activityView as SettingsActivity
private val settings get() = fragmentView.activityView!!.settings private val settings get() = fragmentView.activityView!!.settings
private lateinit var preferences: SharedPreferences private lateinit var preferences: SharedPreferences
@ -111,6 +111,12 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
Settings.SECTION_AUDIO Settings.SECTION_AUDIO
) )
) )
add(
RunnableSetting(
R.string.reset_to_default,
0
) { resetSettings() }
)
} }
} }
@ -338,7 +344,9 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
override var int: Int override var int: Int
get() = preferences.getInt(Settings.PREF_THEME, 0) get() = preferences.getInt(Settings.PREF_THEME, 0)
set(value) { set(value) {
preferences.edit().putInt(Settings.PREF_THEME, value).apply() preferences.edit()
.putInt(Settings.PREF_THEME, value)
.apply()
settingsActivity.recreate() settingsActivity.recreate()
} }
override val key: String? = null override val key: String? = null
@ -375,7 +383,9 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
override var int: Int override var int: Int
get() = preferences.getInt(Settings.PREF_THEME_MODE, -1) get() = preferences.getInt(Settings.PREF_THEME_MODE, -1)
set(value) { set(value) {
preferences.edit().putInt(Settings.PREF_THEME_MODE, value).apply() preferences.edit()
.putInt(Settings.PREF_THEME_MODE, value)
.apply()
ThemeHelper.setThemeMode(settingsActivity) ThemeHelper.setThemeMode(settingsActivity)
} }
override val key: String? = null override val key: String? = null
@ -400,14 +410,17 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
override var boolean: Boolean override var boolean: Boolean
get() = preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false) get() = preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false)
set(value) { set(value) {
preferences.edit().putBoolean(Settings.PREF_BLACK_BACKGROUNDS, value).apply() preferences.edit()
.putBoolean(Settings.PREF_BLACK_BACKGROUNDS, value)
.apply()
settingsActivity.recreate() settingsActivity.recreate()
} }
override val key: String? = null override val key: String? = null
override val section: String? = null override val section: String? = null
override val isRuntimeEditable: Boolean = false override val isRuntimeEditable: Boolean = false
override val valueAsString: String override val valueAsString: String
get() = preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false).toString() get() = preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false)
.toString()
override val defaultValue: Any = false override val defaultValue: Any = false
} }
@ -420,4 +433,15 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
) )
} }
} }
private fun resetSettings() {
MaterialAlertDialogBuilder(settingsActivity)
.setTitle(R.string.reset_all_settings)
.setMessage(R.string.reset_all_settings_description)
.setPositiveButton(android.R.string.ok) { _, _ ->
settingsActivity.onSettingsReset()
}
.setNegativeButton(android.R.string.cancel, null)
.show()
}
} }

View File

@ -21,7 +21,7 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
override fun bind(item: SettingsItem) { override fun bind(item: SettingsItem) {
setting = item as DateTimeSetting setting = item as DateTimeSetting
binding.textSettingName.setText(item.nameId) binding.textSettingName.setText(item.nameId)
if (item.descriptionId!! > 0) { if (item.descriptionId != 0) {
binding.textSettingDescription.setText(item.descriptionId) binding.textSettingDescription.setText(item.descriptionId)
binding.textSettingDescription.visibility = View.VISIBLE binding.textSettingDescription.visibility = View.VISIBLE
} else { } else {

View File

@ -0,0 +1,35 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.features.settings.model.view.RunnableSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
private lateinit var setting: RunnableSetting
override fun bind(item: SettingsItem) {
setting = item as RunnableSetting
binding.textSettingName.setText(item.nameId)
if (item.descriptionId != 0) {
binding.textSettingDescription.setText(item.descriptionId)
binding.textSettingDescription.visibility = View.VISIBLE
} else {
binding.textSettingDescription.visibility = View.GONE
}
}
override fun onClick(clicked: View) {
setting.runnable.invoke()
}
override fun onLongClick(clicked: View): Boolean {
// no-op
return true
}
}

View File

@ -18,7 +18,7 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
setting = item setting = item
binding.textSettingName.setText(item.nameId) binding.textSettingName.setText(item.nameId)
binding.textSettingDescription.visibility = View.VISIBLE binding.textSettingDescription.visibility = View.VISIBLE
if (item.descriptionId!! > 0) { if (item.descriptionId != 0) {
binding.textSettingDescription.setText(item.descriptionId) binding.textSettingDescription.setText(item.descriptionId)
} else if (item is SingleChoiceSetting) { } else if (item is SingleChoiceSetting) {
val resMgr = binding.textSettingDescription.context.resources val resMgr = binding.textSettingDescription.context.resources

View File

@ -16,7 +16,7 @@ class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAda
override fun bind(item: SettingsItem) { override fun bind(item: SettingsItem) {
setting = item as SliderSetting setting = item as SliderSetting
binding.textSettingName.setText(item.nameId) binding.textSettingName.setText(item.nameId)
if (item.descriptionId!! > 0) { if (item.descriptionId != 0) {
binding.textSettingDescription.setText(item.descriptionId) binding.textSettingDescription.setText(item.descriptionId)
binding.textSettingDescription.visibility = View.VISIBLE binding.textSettingDescription.visibility = View.VISIBLE
} else { } else {

View File

@ -16,7 +16,7 @@ class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAd
override fun bind(item: SettingsItem) { override fun bind(item: SettingsItem) {
this.item = item as SubmenuSetting this.item = item as SubmenuSetting
binding.textSettingName.setText(item.nameId) binding.textSettingName.setText(item.nameId)
if (item.descriptionId!! > 0) { if (item.descriptionId != 0) {
binding.textSettingDescription.setText(item.descriptionId) binding.textSettingDescription.setText(item.descriptionId)
binding.textSettingDescription.visibility = View.VISIBLE binding.textSettingDescription.visibility = View.VISIBLE
} else { } else {

View File

@ -18,7 +18,7 @@ class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter
override fun bind(item: SettingsItem) { override fun bind(item: SettingsItem) {
setting = item as SwitchSetting setting = item as SwitchSetting
binding.textSettingName.setText(item.nameId) binding.textSettingName.setText(item.nameId)
if (item.descriptionId!! > 0) { if (item.descriptionId != 0) {
binding.textSettingDescription.setText(item.descriptionId) binding.textSettingDescription.setText(item.descriptionId)
binding.textSettingDescription.visibility = View.VISIBLE binding.textSettingDescription.visibility = View.VISIBLE
} else { } else {

View File

@ -22,7 +22,7 @@ import java.util.*
object SettingsFile { object SettingsFile {
const val FILE_NAME_CONFIG = "config" const val FILE_NAME_CONFIG = "config"
private val sectionsMap = BiMap<String?, String?>() private var sectionsMap = BiMap<String?, String?>()
/** /**
* Reads a given .ini file from disk and returns it as a HashMap of Settings, themselves * Reads a given .ini file from disk and returns it as a HashMap of Settings, themselves
@ -154,7 +154,7 @@ object SettingsFile {
} else generalSectionName } else generalSectionName
} }
private fun getSettingsFile(fileName: String): File { fun getSettingsFile(fileName: String): File {
return File( return File(
DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini" DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini"
) )
@ -182,13 +182,11 @@ object SettingsFile {
private fun settingFromLine(line: String): AbstractSetting? { private fun settingFromLine(line: String): AbstractSetting? {
val splitLine = line.split("=".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() val splitLine = line.split("=".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
if (splitLine.size != 2) { if (splitLine.size != 2) {
Log.warning("Skipping invalid config line \"$line\"")
return null return null
} }
val key = splitLine[0].trim { it <= ' ' } val key = splitLine[0].trim { it <= ' ' }
val value = splitLine[1].trim { it <= ' ' } val value = splitLine[1].trim { it <= ' ' }
if (value.isEmpty()) { if (value.isEmpty()) {
Log.warning("Skipping null value in config line \"$line\"")
return null return null
} }
@ -216,7 +214,7 @@ object SettingsFile {
return stringSetting return stringSetting
} }
return StringSetting.from(key) return null
} }
/** /**

View File

@ -143,8 +143,12 @@
<string name="ini_saved">Saved settings</string> <string name="ini_saved">Saved settings</string>
<string name="gameid_saved">Saved settings for %1$s</string> <string name="gameid_saved">Saved settings for %1$s</string>
<string name="error_saving">Error saving %1$s.ini: %2$s</string> <string name="error_saving">Error saving %1$s.ini: %2$s</string>
<string name="loading">Loading...</string> <string name="loading">Loading</string>
<string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string> <string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string>
<string name="reset_to_default">Reset to default</string>
<string name="reset_all_settings">Reset all settings?</string>
<string name="reset_all_settings_description">All Advanced Settings will be reset to their default configuration. This can not be undone.</string>
<string name="settings_reset">Settings reset</string>
<!-- GPU driver installation --> <!-- GPU driver installation -->
<string name="select_gpu_driver">Select GPU driver</string> <string name="select_gpu_driver">Select GPU driver</string>