summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Charles Lombardo2023-08-16 02:36:56 -0400
committerGravatar Charles Lombardo2023-08-29 19:40:17 -0400
commit6c8f2b355ace41e33e8a1ad2f95d2816893a953b (patch)
treef7e96841efb0aa2d6a9b0a16df8247c2ec9da2a1 /src
parentMerge pull request #11406 from german77/sdl2-28-2 (diff)
downloadyuzu-6c8f2b355ace41e33e8a1ad2f95d2816893a953b.tar.gz
yuzu-6c8f2b355ace41e33e8a1ad2f95d2816893a953b.tar.xz
yuzu-6c8f2b355ace41e33e8a1ad2f95d2816893a953b.zip
android: Expose interface for getting settings from native code
Completely removes code related to parsing the settings file on the java side. Now all settings are accessed via NativeConfig.kt and config.cpp has been modified to be closer to the core counterpart. Since the core currently uses QSettings, we can't remove reliance from Wini yet. This also includes simplifications to each settings interface to get closer to native code and prepare for per-game settings.
Diffstat (limited to 'src')
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractBooleanSetting.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractByteSetting.kt (renamed from src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingsViewModel.kt)6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractLongSetting.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractSetting.kt13
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractShortSetting.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt57
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ByteSetting.kt25
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt32
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt151
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/LongSetting.kt25
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingSection.kt37
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt316
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ShortSetting.kt25
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt36
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt18
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt24
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt30
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt15
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityView.kt19
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt62
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt9
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt181
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentView.kt16
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt255
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BiMap.kt25
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt31
-rw-r--r--src/android/app/src/main/jni/CMakeLists.txt2
-rw-r--r--src/android/app/src/main/jni/config.cpp31
-rw-r--r--src/android/app/src/main/jni/config.h24
-rw-r--r--src/android/app/src/main/jni/native.cpp28
-rw-r--r--src/android/app/src/main/jni/native_config.cpp224
-rw-r--r--src/android/app/src/main/jni/uisettings.cpp10
-rw-r--r--src/android/app/src/main/jni/uisettings.h29
-rw-r--r--src/android/app/src/main/res/values/arrays.xml10
-rw-r--r--src/android/app/src/main/res/values/strings.xml1
-rw-r--r--src/common/settings.cpp2
-rw-r--r--src/common/settings_common.cpp1
-rw-r--r--src/common/settings_common.h2
49 files changed, 869 insertions, 969 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
index 9c32e044c..5a7cf4ed7 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
@@ -219,10 +219,6 @@ object NativeLibrary {
219 219
220 external fun reloadSettings() 220 external fun reloadSettings()
221 221
222 external fun getUserSetting(gameID: String?, Section: String?, Key: String?): String?
223
224 external fun setUserSetting(gameID: String?, Section: String?, Key: String?, Value: String?)
225
226 external fun initGameIni(gameID: String?) 222 external fun initGameIni(gameID: String?)
227 223
228 /** 224 /**
@@ -413,14 +409,17 @@ object NativeLibrary {
413 details.ifEmpty { emulationActivity.getString(R.string.system_archive_general) } 409 details.ifEmpty { emulationActivity.getString(R.string.system_archive_general) }
414 ) 410 )
415 } 411 }
412
416 CoreError.ErrorSavestate -> { 413 CoreError.ErrorSavestate -> {
417 title = emulationActivity.getString(R.string.save_load_error) 414 title = emulationActivity.getString(R.string.save_load_error)
418 message = details 415 message = details
419 } 416 }
417
420 CoreError.ErrorUnknown -> { 418 CoreError.ErrorUnknown -> {
421 title = emulationActivity.getString(R.string.fatal_error) 419 title = emulationActivity.getString(R.string.fatal_error)
422 message = emulationActivity.getString(R.string.fatal_error_message) 420 message = emulationActivity.getString(R.string.fatal_error_message)
423 } 421 }
422
424 else -> { 423 else -> {
425 return true 424 return true
426 } 425 }
@@ -454,6 +453,7 @@ object NativeLibrary {
454 captionId = R.string.loader_error_video_core 453 captionId = R.string.loader_error_video_core
455 descriptionId = R.string.loader_error_video_core_description 454 descriptionId = R.string.loader_error_video_core_description
456 } 455 }
456
457 else -> { 457 else -> {
458 captionId = R.string.loader_error_encrypted 458 captionId = R.string.loader_error_encrypted
459 descriptionId = R.string.loader_error_encrypted_roms_description 459 descriptionId = R.string.loader_error_encrypted_roms_description
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
index 7461fb093..6f52a7a8d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
@@ -28,7 +28,6 @@ import android.view.Surface
28import android.view.View 28import android.view.View
29import android.view.inputmethod.InputMethodManager 29import android.view.inputmethod.InputMethodManager
30import android.widget.Toast 30import android.widget.Toast
31import androidx.activity.viewModels
32import androidx.appcompat.app.AppCompatActivity 31import androidx.appcompat.app.AppCompatActivity
33import androidx.core.view.WindowCompat 32import androidx.core.view.WindowCompat
34import androidx.core.view.WindowInsetsCompat 33import androidx.core.view.WindowInsetsCompat
@@ -42,7 +41,6 @@ import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding
42import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting 41import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
43import org.yuzu.yuzu_emu.features.settings.model.IntSetting 42import org.yuzu.yuzu_emu.features.settings.model.IntSetting
44import org.yuzu.yuzu_emu.features.settings.model.Settings 43import org.yuzu.yuzu_emu.features.settings.model.Settings
45import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
46import org.yuzu.yuzu_emu.model.Game 44import org.yuzu.yuzu_emu.model.Game
47import org.yuzu.yuzu_emu.utils.ControllerMappingHelper 45import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
48import org.yuzu.yuzu_emu.utils.ForegroundService 46import org.yuzu.yuzu_emu.utils.ForegroundService
@@ -72,8 +70,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
72 private val actionMute = "ACTION_EMULATOR_MUTE" 70 private val actionMute = "ACTION_EMULATOR_MUTE"
73 private val actionUnmute = "ACTION_EMULATOR_UNMUTE" 71 private val actionUnmute = "ACTION_EMULATOR_UNMUTE"
74 72
75 private val settingsViewModel: SettingsViewModel by viewModels()
76
77 override fun onDestroy() { 73 override fun onDestroy() {
78 stopForegroundService(this) 74 stopForegroundService(this)
79 super.onDestroy() 75 super.onDestroy()
@@ -82,8 +78,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
82 override fun onCreate(savedInstanceState: Bundle?) { 78 override fun onCreate(savedInstanceState: Bundle?) {
83 ThemeHelper.setTheme(this) 79 ThemeHelper.setTheme(this)
84 80
85 settingsViewModel.settings.loadSettings()
86
87 super.onCreate(savedInstanceState) 81 super.onCreate(savedInstanceState)
88 82
89 binding = ActivityEmulationBinding.inflate(layoutInflater) 83 binding = ActivityEmulationBinding.inflate(layoutInflater)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractBooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractBooleanSetting.kt
index a6e9833ee..aeda8d222 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractBooleanSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractBooleanSetting.kt
@@ -4,5 +4,7 @@
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6interface AbstractBooleanSetting : AbstractSetting { 6interface AbstractBooleanSetting : AbstractSetting {
7 var boolean: Boolean 7 val boolean: Boolean
8
9 fun setBoolean(value: Boolean)
8} 10}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingsViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractByteSetting.kt
index bd9233d62..606519ad8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingsViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractByteSetting.kt
@@ -3,8 +3,8 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6import androidx.lifecycle.ViewModel 6interface AbstractByteSetting : AbstractSetting {
7 val byte: Byte
7 8
8class SettingsViewModel : ViewModel() { 9 fun setByte(value: Byte)
9 val settings = Settings()
10} 10}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt
index 6fe4bc263..974925eed 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt
@@ -4,5 +4,7 @@
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6interface AbstractFloatSetting : AbstractSetting { 6interface AbstractFloatSetting : AbstractSetting {
7 var float: Float 7 val float: Float
8
9 fun setFloat(value: Float)
8} 10}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt
index 892b7dcfe..89b285b10 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt
@@ -4,5 +4,7 @@
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6interface AbstractIntSetting : AbstractSetting { 6interface AbstractIntSetting : AbstractSetting {
7 var int: Int 7 val int: Int
8
9 fun setInt(value: Int)
8} 10}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractLongSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractLongSetting.kt
new file mode 100644
index 000000000..4873942db
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractLongSetting.kt
@@ -0,0 +1,10 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.model
5
6interface AbstractLongSetting : AbstractSetting {
7 val long: Long
8
9 fun setLong(value: Long)
10}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractSetting.kt
index 258580209..7afed95ad 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractSetting.kt
@@ -3,10 +3,17 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6import org.yuzu.yuzu_emu.utils.NativeConfig
7
6interface AbstractSetting { 8interface AbstractSetting {
7 val key: String? 9 val key: String?
8 val section: String? 10 val category: Settings.Category
9 val isRuntimeEditable: Boolean
10 val valueAsString: String
11 val defaultValue: Any 11 val defaultValue: Any
12 val valueAsString: String
13 get() = ""
14
15 val isRuntimeModifiable: Boolean
16 get() = NativeConfig.getIsRuntimeModifiable(key!!)
17
18 fun reset() = run { }
12} 19}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractShortSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractShortSetting.kt
new file mode 100644
index 000000000..91407ccbb
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractShortSetting.kt
@@ -0,0 +1,10 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.model
5
6interface AbstractShortSetting : AbstractSetting {
7 val short: Short
8
9 fun setShort(value: Short)
10}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt
index 0d02c5997..c8935cc48 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt
@@ -4,5 +4,7 @@
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6interface AbstractStringSetting : AbstractSetting { 6interface AbstractStringSetting : AbstractSetting {
7 var string: String 7 val string: String
8
9 fun setString(value: String)
8} 10}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
index d41933766..f7528642e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
@@ -3,41 +3,34 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6import org.yuzu.yuzu_emu.utils.NativeConfig
7
6enum class BooleanSetting( 8enum class BooleanSetting(
7 override val key: String, 9 override val key: String,
8 override val section: String, 10 override val category: Settings.Category
9 override val defaultValue: Boolean
10) : AbstractBooleanSetting { 11) : AbstractBooleanSetting {
11 CPU_DEBUG_MODE("cpu_debug_mode", Settings.SECTION_CPU, false), 12 CPU_DEBUG_MODE("cpu_debug_mode", Settings.Category.Cpu),
12 FASTMEM("cpuopt_fastmem", Settings.SECTION_CPU, true), 13 FASTMEM("cpuopt_fastmem", Settings.Category.Cpu),
13 FASTMEM_EXCLUSIVES("cpuopt_fastmem_exclusives", Settings.SECTION_CPU, true), 14 FASTMEM_EXCLUSIVES("cpuopt_fastmem_exclusives", Settings.Category.Cpu),
14 PICTURE_IN_PICTURE("picture_in_picture", Settings.SECTION_GENERAL, true), 15 RENDERER_USE_SPEED_LIMIT("use_speed_limit", Settings.Category.Core),
15 USE_CUSTOM_RTC("custom_rtc_enabled", Settings.SECTION_SYSTEM, false); 16 USE_DOCKED_MODE("use_docked_mode", Settings.Category.System),
16 17 RENDERER_USE_DISK_SHADER_CACHE("use_disk_shader_cache", Settings.Category.Renderer),
17 override var boolean: Boolean = defaultValue 18 RENDERER_FORCE_MAX_CLOCK("force_max_clock", Settings.Category.Renderer),
19 RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders", Settings.Category.Renderer),
20 RENDERER_REACTIVE_FLUSHING("use_reactive_flushing", Settings.Category.Renderer),
21 RENDERER_DEBUG("debug", Settings.Category.Renderer),
22 PICTURE_IN_PICTURE("picture_in_picture", Settings.Category.Android),
23 USE_CUSTOM_RTC("custom_rtc_enabled", Settings.Category.System);
24
25 override val boolean: Boolean
26 get() = NativeConfig.getBoolean(key, false)
27
28 override fun setBoolean(value: Boolean) = NativeConfig.setBoolean(key, value)
29
30 override val defaultValue: Boolean by lazy { NativeConfig.getBoolean(key, true) }
18 31
19 override val valueAsString: String 32 override val valueAsString: String
20 get() = boolean.toString() 33 get() = if (boolean) "1" else "0"
21 34
22 override val isRuntimeEditable: Boolean 35 override fun reset() = NativeConfig.setBoolean(key, defaultValue)
23 get() {
24 for (setting in NOT_RUNTIME_EDITABLE) {
25 if (setting == this) {
26 return false
27 }
28 }
29 return true
30 }
31
32 companion object {
33 private val NOT_RUNTIME_EDITABLE = listOf(
34 PICTURE_IN_PICTURE,
35 USE_CUSTOM_RTC
36 )
37
38 fun from(key: String): BooleanSetting? =
39 BooleanSetting.values().firstOrNull { it.key == key }
40
41 fun clear() = BooleanSetting.values().forEach { it.boolean = it.defaultValue }
42 }
43} 36}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ByteSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ByteSetting.kt
new file mode 100644
index 000000000..6ec0a765e
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ByteSetting.kt
@@ -0,0 +1,25 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.model
5
6import org.yuzu.yuzu_emu.utils.NativeConfig
7
8enum class ByteSetting(
9 override val key: String,
10 override val category: Settings.Category
11) : AbstractByteSetting {
12 AUDIO_VOLUME("volume", Settings.Category.Audio);
13
14 override val byte: Byte
15 get() = NativeConfig.getByte(key, false)
16
17 override fun setByte(value: Byte) = NativeConfig.setByte(key, value)
18
19 override val defaultValue: Byte by lazy { NativeConfig.getByte(key, true) }
20
21 override val valueAsString: String
22 get() = byte.toString()
23
24 override fun reset() = NativeConfig.setByte(key, defaultValue)
25}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt
index e5545a916..0181d06f2 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt
@@ -3,34 +3,24 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6import org.yuzu.yuzu_emu.utils.NativeConfig
7
6enum class FloatSetting( 8enum class FloatSetting(
7 override val key: String, 9 override val key: String,
8 override val section: String, 10 override val category: Settings.Category
9 override val defaultValue: Float
10) : AbstractFloatSetting { 11) : AbstractFloatSetting {
11 // No float settings currently exist 12 // No float settings currently exist
12 EMPTY_SETTING("", "", 0f); 13 EMPTY_SETTING("", Settings.Category.UiGeneral);
13
14 override var float: Float = defaultValue
15 14
16 override val valueAsString: String 15 override val float: Float
17 get() = float.toString() 16 get() = NativeConfig.getFloat(key, false)
18 17
19 override val isRuntimeEditable: Boolean 18 override fun setFloat(value: Float) = NativeConfig.setFloat(key, value)
20 get() {
21 for (setting in NOT_RUNTIME_EDITABLE) {
22 if (setting == this) {
23 return false
24 }
25 }
26 return true
27 }
28 19
29 companion object { 20 override val defaultValue: Float by lazy { NativeConfig.getFloat(key, true) }
30 private val NOT_RUNTIME_EDITABLE = emptyList<FloatSetting>()
31 21
32 fun from(key: String): FloatSetting? = FloatSetting.values().firstOrNull { it.key == key } 22 override val valueAsString: String
23 get() = float.toString()
33 24
34 fun clear() = FloatSetting.values().forEach { it.float = it.defaultValue } 25 override fun reset() = NativeConfig.setFloat(key, defaultValue)
35 }
36} 26}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
index 4427a7d9d..a64757207 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
@@ -3,139 +3,34 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6import org.yuzu.yuzu_emu.utils.NativeConfig
7
6enum class IntSetting( 8enum class IntSetting(
7 override val key: String, 9 override val key: String,
8 override val section: String, 10 override val category: Settings.Category
9 override val defaultValue: Int
10) : AbstractIntSetting { 11) : AbstractIntSetting {
11 RENDERER_USE_SPEED_LIMIT( 12 CPU_ACCURACY("cpu_accuracy", Settings.Category.Cpu),
12 "use_speed_limit", 13 REGION_INDEX("region_index", Settings.Category.System),
13 Settings.SECTION_RENDERER, 14 LANGUAGE_INDEX("language_index", Settings.Category.System),
14 1 15 RENDERER_BACKEND("backend", Settings.Category.Renderer),
15 ), 16 RENDERER_ACCURACY("gpu_accuracy", Settings.Category.Renderer),
16 USE_DOCKED_MODE( 17 RENDERER_RESOLUTION("resolution_setup", Settings.Category.Renderer),
17 "use_docked_mode", 18 RENDERER_VSYNC("use_vsync", Settings.Category.Renderer),
18 Settings.SECTION_SYSTEM, 19 RENDERER_SCALING_FILTER("scaling_filter", Settings.Category.Renderer),
19 0 20 RENDERER_ANTI_ALIASING("anti_aliasing", Settings.Category.Renderer),
20 ), 21 RENDERER_SCREEN_LAYOUT("screen_layout", Settings.Category.Android),
21 RENDERER_USE_DISK_SHADER_CACHE( 22 RENDERER_ASPECT_RATIO("aspect_ratio", Settings.Category.Renderer),
22 "use_disk_shader_cache", 23 AUDIO_OUTPUT_ENGINE("output_engine", Settings.Category.Audio);
23 Settings.SECTION_RENDERER, 24
24 1 25 override val int: Int
25 ), 26 get() = NativeConfig.getInt(key, false)
26 RENDERER_FORCE_MAX_CLOCK( 27
27 "force_max_clock", 28 override fun setInt(value: Int) = NativeConfig.setInt(key, value)
28 Settings.SECTION_RENDERER, 29
29 0 30 override val defaultValue: Int by lazy { NativeConfig.getInt(key, true) }
30 ),
31 RENDERER_ASYNCHRONOUS_SHADERS(
32 "use_asynchronous_shaders",
33 Settings.SECTION_RENDERER,
34 0
35 ),
36 RENDERER_REACTIVE_FLUSHING(
37 "use_reactive_flushing",
38 Settings.SECTION_RENDERER,
39 0
40 ),
41 RENDERER_DEBUG(
42 "debug",
43 Settings.SECTION_RENDERER,
44 0
45 ),
46 RENDERER_SPEED_LIMIT(
47 "speed_limit",
48 Settings.SECTION_RENDERER,
49 100
50 ),
51 CPU_ACCURACY(
52 "cpu_accuracy",
53 Settings.SECTION_CPU,
54 0
55 ),
56 REGION_INDEX(
57 "region_index",
58 Settings.SECTION_SYSTEM,
59 -1
60 ),
61 LANGUAGE_INDEX(
62 "language_index",
63 Settings.SECTION_SYSTEM,
64 1
65 ),
66 RENDERER_BACKEND(
67 "backend",
68 Settings.SECTION_RENDERER,
69 1
70 ),
71 RENDERER_ACCURACY(
72 "gpu_accuracy",
73 Settings.SECTION_RENDERER,
74 0
75 ),
76 RENDERER_RESOLUTION(
77 "resolution_setup",
78 Settings.SECTION_RENDERER,
79 2
80 ),
81 RENDERER_VSYNC(
82 "use_vsync",
83 Settings.SECTION_RENDERER,
84 0
85 ),
86 RENDERER_SCALING_FILTER(
87 "scaling_filter",
88 Settings.SECTION_RENDERER,
89 1
90 ),
91 RENDERER_ANTI_ALIASING(
92 "anti_aliasing",
93 Settings.SECTION_RENDERER,
94 0
95 ),
96 RENDERER_SCREEN_LAYOUT(
97 "screen_layout",
98 Settings.SECTION_RENDERER,
99 Settings.LayoutOption_MobileLandscape
100 ),
101 RENDERER_ASPECT_RATIO(
102 "aspect_ratio",
103 Settings.SECTION_RENDERER,
104 0
105 ),
106 AUDIO_VOLUME(
107 "volume",
108 Settings.SECTION_AUDIO,
109 100
110 );
111
112 override var int: Int = defaultValue
113 31
114 override val valueAsString: String 32 override val valueAsString: String
115 get() = int.toString() 33 get() = int.toString()
116 34
117 override val isRuntimeEditable: Boolean 35 override fun reset() = NativeConfig.setInt(key, defaultValue)
118 get() {
119 for (setting in NOT_RUNTIME_EDITABLE) {
120 if (setting == this) {
121 return false
122 }
123 }
124 return true
125 }
126
127 companion object {
128 private val NOT_RUNTIME_EDITABLE = listOf(
129 RENDERER_USE_DISK_SHADER_CACHE,
130 RENDERER_ASYNCHRONOUS_SHADERS,
131 RENDERER_DEBUG,
132 RENDERER_BACKEND,
133 RENDERER_RESOLUTION,
134 RENDERER_VSYNC
135 )
136
137 fun from(key: String): IntSetting? = IntSetting.values().firstOrNull { it.key == key }
138
139 fun clear() = IntSetting.values().forEach { it.int = it.defaultValue }
140 }
141} 36}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/LongSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/LongSetting.kt
new file mode 100644
index 000000000..c526fc4cf
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/LongSetting.kt
@@ -0,0 +1,25 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.model
5
6import org.yuzu.yuzu_emu.utils.NativeConfig
7
8enum class LongSetting(
9 override val key: String,
10 override val category: Settings.Category
11) : AbstractLongSetting {
12 CUSTOM_RTC("custom_rtc", Settings.Category.System);
13
14 override val long: Long
15 get() = NativeConfig.getLong(key, false)
16
17 override fun setLong(value: Long) = NativeConfig.setLong(key, value)
18
19 override val defaultValue: Long by lazy { NativeConfig.getLong(key, true) }
20
21 override val valueAsString: String
22 get() = long.toString()
23
24 override fun reset() = NativeConfig.setLong(key, defaultValue)
25}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingSection.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingSection.kt
deleted file mode 100644
index 474f598a9..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingSection.kt
+++ /dev/null
@@ -1,37 +0,0 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.model
5
6/**
7 * A semantically-related group of Settings objects. These Settings are
8 * internally stored as a HashMap.
9 */
10class SettingSection(val name: String) {
11 val settings = HashMap<String, AbstractSetting>()
12
13 /**
14 * Convenience method; inserts a value directly into the backing HashMap.
15 *
16 * @param setting The Setting to be inserted.
17 */
18 fun putSetting(setting: AbstractSetting) {
19 settings[setting.key!!] = setting
20 }
21
22 /**
23 * Convenience method; gets a value directly from the backing HashMap.
24 *
25 * @param key Used to retrieve the Setting.
26 * @return A Setting object (you should probably cast this before using)
27 */
28 fun getSetting(key: String): AbstractSetting? {
29 return settings[key]
30 }
31
32 fun mergeSection(settingSection: SettingSection) {
33 for (setting in settingSection.settings.values) {
34 putSetting(setting)
35 }
36 }
37}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
index a6251bafd..0702236e8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
@@ -4,195 +4,151 @@
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6import android.text.TextUtils 6import android.text.TextUtils
7import java.util.* 7import android.widget.Toast
8import org.yuzu.yuzu_emu.R 8import org.yuzu.yuzu_emu.R
9import org.yuzu.yuzu_emu.YuzuApplication 9import org.yuzu.yuzu_emu.YuzuApplication
10import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivityView
11import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 10import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
12 11
13class Settings { 12object Settings {
14 private var gameId: String? = null 13 private val context get() = YuzuApplication.appContext
15 14
16 var isLoaded = false 15 fun saveSettings(gameId: String = "") {
17
18 /**
19 * A HashMap<String></String>, SettingSection> that constructs a new SettingSection instead of returning null
20 * when getting a key not already in the map
21 */
22 class SettingsSectionMap : HashMap<String, SettingSection?>() {
23 override operator fun get(key: String): SettingSection? {
24 if (!super.containsKey(key)) {
25 val section = SettingSection(key)
26 super.put(key, section)
27 return section
28 }
29 return super.get(key)
30 }
31 }
32
33 var sections: HashMap<String, SettingSection?> = SettingsSectionMap()
34
35 fun getSection(sectionName: String): SettingSection? {
36 return sections[sectionName]
37 }
38
39 val isEmpty: Boolean
40 get() = sections.isEmpty()
41
42 fun loadSettings(view: SettingsActivityView? = null) {
43 sections = SettingsSectionMap()
44 loadYuzuSettings(view)
45 if (!TextUtils.isEmpty(gameId)) {
46 loadCustomGameSettings(gameId!!, view)
47 }
48 isLoaded = true
49 }
50
51 private fun loadYuzuSettings(view: SettingsActivityView?) {
52 for ((fileName) in configFileSectionsMap) {
53 sections.putAll(SettingsFile.readFile(fileName, view))
54 }
55 }
56
57 private fun loadCustomGameSettings(gameId: String, view: SettingsActivityView?) {
58 // Custom game settings
59 mergeSections(SettingsFile.readCustomGameSettings(gameId, view))
60 }
61
62 private fun mergeSections(updatedSections: HashMap<String, SettingSection?>) {
63 for ((key, updatedSection) in updatedSections) {
64 if (sections.containsKey(key)) {
65 val originalSection = sections[key]
66 originalSection!!.mergeSection(updatedSection!!)
67 } else {
68 sections[key] = updatedSection
69 }
70 }
71 }
72
73 fun loadSettings(gameId: String, view: SettingsActivityView) {
74 this.gameId = gameId
75 loadSettings(view)
76 }
77
78 fun saveSettings(view: SettingsActivityView) {
79 if (TextUtils.isEmpty(gameId)) { 16 if (TextUtils.isEmpty(gameId)) {
80 view.showToastMessage( 17 Toast.makeText(
81 YuzuApplication.appContext.getString(R.string.ini_saved), 18 context,
82 false 19 context.getString(R.string.ini_saved),
83 ) 20 Toast.LENGTH_SHORT
84 21 ).show()
85 for ((fileName, sectionNames) in configFileSectionsMap) { 22 SettingsFile.saveFile(SettingsFile.FILE_NAME_CONFIG)
86 val iniSections = TreeMap<String, SettingSection>()
87 for (section in sectionNames) {
88 iniSections[section] = sections[section]!!
89 }
90
91 SettingsFile.saveFile(fileName, iniSections, view)
92 }
93 } else { 23 } else {
94 // Custom game settings 24 // TODO: Save custom game settings
95 view.showToastMessage( 25 Toast.makeText(
96 YuzuApplication.appContext.getString(R.string.gameid_saved, gameId), 26 context,
97 false 27 context.getString(R.string.gameid_saved, gameId),
98 ) 28 Toast.LENGTH_SHORT
99 29 ).show()
100 SettingsFile.saveCustomGameSettings(gameId, sections)
101 } 30 }
102 } 31 }
103 32
104 companion object { 33 enum class Category {
105 const val SECTION_GENERAL = "General" 34 Android,
106 const val SECTION_SYSTEM = "System" 35 Audio,
107 const val SECTION_RENDERER = "Renderer" 36 Core,
108 const val SECTION_AUDIO = "Audio" 37 Cpu,
109 const val SECTION_CPU = "Cpu" 38 CpuDebug,
110 const val SECTION_THEME = "Theme" 39 CpuUnsafe,
111 const val SECTION_DEBUG = "Debug" 40 Renderer,
112 41 RendererAdvanced,
113 const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown" 42 RendererDebug,
114 43 System,
115 const val PREF_OVERLAY_VERSION = "OverlayVersion" 44 SystemAudio,
116 const val PREF_LANDSCAPE_OVERLAY_VERSION = "LandscapeOverlayVersion" 45 DataStorage,
117 const val PREF_PORTRAIT_OVERLAY_VERSION = "PortraitOverlayVersion" 46 Debugging,
118 const val PREF_FOLDABLE_OVERLAY_VERSION = "FoldableOverlayVersion" 47 DebuggingGraphics,
119 val overlayLayoutPrefs = listOf( 48 Miscellaneous,
120 PREF_LANDSCAPE_OVERLAY_VERSION, 49 Network,
121 PREF_PORTRAIT_OVERLAY_VERSION, 50 WebService,
122 PREF_FOLDABLE_OVERLAY_VERSION 51 AddOns,
123 ) 52 Controls,
124 53 Ui,
125 const val PREF_CONTROL_SCALE = "controlScale" 54 UiGeneral,
126 const val PREF_CONTROL_OPACITY = "controlOpacity" 55 UiLayout,
127 const val PREF_TOUCH_ENABLED = "isTouchEnabled" 56 UiGameList,
128 const val PREF_BUTTON_A = "buttonToggle0" 57 Screenshots,
129 const val PREF_BUTTON_B = "buttonToggle1" 58 Shortcuts,
130 const val PREF_BUTTON_X = "buttonToggle2" 59 Multiplayer,
131 const val PREF_BUTTON_Y = "buttonToggle3" 60 Services,
132 const val PREF_BUTTON_L = "buttonToggle4" 61 Paths,
133 const val PREF_BUTTON_R = "buttonToggle5" 62 MaxEnum
134 const val PREF_BUTTON_ZL = "buttonToggle6"
135 const val PREF_BUTTON_ZR = "buttonToggle7"
136 const val PREF_BUTTON_PLUS = "buttonToggle8"
137 const val PREF_BUTTON_MINUS = "buttonToggle9"
138 const val PREF_BUTTON_DPAD = "buttonToggle10"
139 const val PREF_STICK_L = "buttonToggle11"
140 const val PREF_STICK_R = "buttonToggle12"
141 const val PREF_BUTTON_STICK_L = "buttonToggle13"
142 const val PREF_BUTTON_STICK_R = "buttonToggle14"
143 const val PREF_BUTTON_HOME = "buttonToggle15"
144 const val PREF_BUTTON_SCREENSHOT = "buttonToggle16"
145
146 const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter"
147 const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable"
148 const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics"
149 const val PREF_MENU_SETTINGS_SHOW_FPS = "EmulationMenuSettings_ShowFps"
150 const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay"
151
152 const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
153 const val PREF_THEME = "Theme"
154 const val PREF_THEME_MODE = "ThemeMode"
155 const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds"
156
157 private val configFileSectionsMap: MutableMap<String, List<String>> = HashMap()
158
159 val overlayPreferences = listOf(
160 PREF_OVERLAY_VERSION,
161 PREF_CONTROL_SCALE,
162 PREF_CONTROL_OPACITY,
163 PREF_TOUCH_ENABLED,
164 PREF_BUTTON_A,
165 PREF_BUTTON_B,
166 PREF_BUTTON_X,
167 PREF_BUTTON_Y,
168 PREF_BUTTON_L,
169 PREF_BUTTON_R,
170 PREF_BUTTON_ZL,
171 PREF_BUTTON_ZR,
172 PREF_BUTTON_PLUS,
173 PREF_BUTTON_MINUS,
174 PREF_BUTTON_DPAD,
175 PREF_STICK_L,
176 PREF_STICK_R,
177 PREF_BUTTON_HOME,
178 PREF_BUTTON_SCREENSHOT,
179 PREF_BUTTON_STICK_L,
180 PREF_BUTTON_STICK_R
181 )
182
183 const val LayoutOption_Unspecified = 0
184 const val LayoutOption_MobilePortrait = 4
185 const val LayoutOption_MobileLandscape = 5
186
187 init {
188 configFileSectionsMap[SettingsFile.FILE_NAME_CONFIG] =
189 listOf(
190 SECTION_GENERAL,
191 SECTION_SYSTEM,
192 SECTION_RENDERER,
193 SECTION_AUDIO,
194 SECTION_CPU
195 )
196 }
197 } 63 }
64
65 val settingsList = listOf<AbstractSetting>(
66 *BooleanSetting.values(),
67 *ByteSetting.values(),
68 *ShortSetting.values(),
69 *IntSetting.values(),
70 *FloatSetting.values(),
71 *LongSetting.values(),
72 *StringSetting.values()
73 )
74
75 const val SECTION_GENERAL = "General"
76 const val SECTION_SYSTEM = "System"
77 const val SECTION_RENDERER = "Renderer"
78 const val SECTION_AUDIO = "Audio"
79 const val SECTION_CPU = "Cpu"
80 const val SECTION_THEME = "Theme"
81 const val SECTION_DEBUG = "Debug"
82
83 const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown"
84
85 const val PREF_OVERLAY_VERSION = "OverlayVersion"
86 const val PREF_LANDSCAPE_OVERLAY_VERSION = "LandscapeOverlayVersion"
87 const val PREF_PORTRAIT_OVERLAY_VERSION = "PortraitOverlayVersion"
88 const val PREF_FOLDABLE_OVERLAY_VERSION = "FoldableOverlayVersion"
89 val overlayLayoutPrefs = listOf(
90 PREF_LANDSCAPE_OVERLAY_VERSION,
91 PREF_PORTRAIT_OVERLAY_VERSION,
92 PREF_FOLDABLE_OVERLAY_VERSION
93 )
94
95 const val PREF_CONTROL_SCALE = "controlScale"
96 const val PREF_CONTROL_OPACITY = "controlOpacity"
97 const val PREF_TOUCH_ENABLED = "isTouchEnabled"
98 const val PREF_BUTTON_A = "buttonToggle0"
99 const val PREF_BUTTON_B = "buttonToggle1"
100 const val PREF_BUTTON_X = "buttonToggle2"
101 const val PREF_BUTTON_Y = "buttonToggle3"
102 const val PREF_BUTTON_L = "buttonToggle4"
103 const val PREF_BUTTON_R = "buttonToggle5"
104 const val PREF_BUTTON_ZL = "buttonToggle6"
105 const val PREF_BUTTON_ZR = "buttonToggle7"
106 const val PREF_BUTTON_PLUS = "buttonToggle8"
107 const val PREF_BUTTON_MINUS = "buttonToggle9"
108 const val PREF_BUTTON_DPAD = "buttonToggle10"
109 const val PREF_STICK_L = "buttonToggle11"
110 const val PREF_STICK_R = "buttonToggle12"
111 const val PREF_BUTTON_STICK_L = "buttonToggle13"
112 const val PREF_BUTTON_STICK_R = "buttonToggle14"
113 const val PREF_BUTTON_HOME = "buttonToggle15"
114 const val PREF_BUTTON_SCREENSHOT = "buttonToggle16"
115
116 const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter"
117 const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable"
118 const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics"
119 const val PREF_MENU_SETTINGS_SHOW_FPS = "EmulationMenuSettings_ShowFps"
120 const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay"
121
122 const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
123 const val PREF_THEME = "Theme"
124 const val PREF_THEME_MODE = "ThemeMode"
125 const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds"
126
127 val overlayPreferences = listOf(
128 PREF_OVERLAY_VERSION,
129 PREF_CONTROL_SCALE,
130 PREF_CONTROL_OPACITY,
131 PREF_TOUCH_ENABLED,
132 PREF_BUTTON_A,
133 PREF_BUTTON_B,
134 PREF_BUTTON_X,
135 PREF_BUTTON_Y,
136 PREF_BUTTON_L,
137 PREF_BUTTON_R,
138 PREF_BUTTON_ZL,
139 PREF_BUTTON_ZR,
140 PREF_BUTTON_PLUS,
141 PREF_BUTTON_MINUS,
142 PREF_BUTTON_DPAD,
143 PREF_STICK_L,
144 PREF_STICK_R,
145 PREF_BUTTON_HOME,
146 PREF_BUTTON_SCREENSHOT,
147 PREF_BUTTON_STICK_L,
148 PREF_BUTTON_STICK_R
149 )
150
151 const val LayoutOption_Unspecified = 0
152 const val LayoutOption_MobilePortrait = 4
153 const val LayoutOption_MobileLandscape = 5
198} 154}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ShortSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ShortSetting.kt
new file mode 100644
index 000000000..c9a0c664c
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ShortSetting.kt
@@ -0,0 +1,25 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.model
5
6import org.yuzu.yuzu_emu.utils.NativeConfig
7
8enum class ShortSetting(
9 override val key: String,
10 override val category: Settings.Category
11) : AbstractShortSetting {
12 RENDERER_SPEED_LIMIT("speed_limit", Settings.Category.Core);
13
14 override val short: Short
15 get() = NativeConfig.getShort(key, false)
16
17 override fun setShort(value: Short) = NativeConfig.setShort(key, value)
18
19 override val defaultValue: Short by lazy { NativeConfig.getShort(key, true) }
20
21 override val valueAsString: String
22 get() = short.toString()
23
24 override fun reset() = NativeConfig.setShort(key, defaultValue)
25}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
index 6621289fd..9bb3e66d4 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
@@ -3,36 +3,24 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6import org.yuzu.yuzu_emu.utils.NativeConfig
7
6enum class StringSetting( 8enum class StringSetting(
7 override val key: String, 9 override val key: String,
8 override val section: String, 10 override val category: Settings.Category
9 override val defaultValue: String
10) : AbstractStringSetting { 11) : AbstractStringSetting {
11 AUDIO_OUTPUT_ENGINE("output_engine", Settings.SECTION_AUDIO, "auto"), 12 // No string settings currently exist
12 CUSTOM_RTC("custom_rtc", Settings.SECTION_SYSTEM, "0"); 13 EMPTY_SETTING("", Settings.Category.UiGeneral);
14
15 override val string: String
16 get() = NativeConfig.getString(key, false)
17
18 override fun setString(value: String) = NativeConfig.setString(key, value)
13 19
14 override var string: String = defaultValue 20 override val defaultValue: String by lazy { NativeConfig.getString(key, true) }
15 21
16 override val valueAsString: String 22 override val valueAsString: String
17 get() = string 23 get() = string
18 24
19 override val isRuntimeEditable: Boolean 25 override fun reset() = NativeConfig.setString(key, defaultValue)
20 get() {
21 for (setting in NOT_RUNTIME_EDITABLE) {
22 if (setting == this) {
23 return false
24 }
25 }
26 return true
27 }
28
29 companion object {
30 private val NOT_RUNTIME_EDITABLE = listOf(
31 CUSTOM_RTC
32 )
33
34 fun from(key: String): StringSetting? = StringSetting.values().firstOrNull { it.key == key }
35
36 fun clear() = StringSetting.values().forEach { it.string = it.defaultValue }
37 }
38} 26}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt
index bc0bf7788..7c858916e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt
@@ -3,29 +3,29 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model.view 4package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import org.yuzu.yuzu_emu.features.settings.model.AbstractLongSetting
6import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting 7import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
7import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
8 8
9class DateTimeSetting( 9class DateTimeSetting(
10 setting: AbstractSetting?, 10 setting: AbstractSetting?,
11 titleId: Int, 11 titleId: Int,
12 descriptionId: Int, 12 descriptionId: Int,
13 val key: String? = null, 13 val key: String? = null,
14 private val defaultValue: String? = null 14 private val defaultValue: Long? = null
15) : SettingsItem(setting, titleId, descriptionId) { 15) : SettingsItem(setting, titleId, descriptionId) {
16 override val type = TYPE_DATETIME_SETTING 16 override val type = TYPE_DATETIME_SETTING
17 17
18 val value: String 18 val value: Long
19 get() = if (setting != null) { 19 get() = if (setting != null) {
20 val setting = setting as AbstractStringSetting 20 val setting = setting as AbstractLongSetting
21 setting.string 21 setting.long
22 } else { 22 } else {
23 defaultValue!! 23 defaultValue!!
24 } 24 }
25 25
26 fun setSelectedValue(datetime: String): AbstractStringSetting { 26 fun setSelectedValue(datetime: Long): AbstractLongSetting {
27 val stringSetting = setting as AbstractStringSetting 27 val longSetting = setting as AbstractLongSetting
28 stringSetting.string = datetime 28 longSetting.setLong(datetime)
29 return stringSetting 29 return longSetting
30 } 30 }
31} 31}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
index 07520849e..a6cba977c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
@@ -23,7 +23,7 @@ abstract class SettingsItem(
23 val isEditable: Boolean 23 val isEditable: Boolean
24 get() { 24 get() {
25 if (!NativeLibrary.isRunning()) return true 25 if (!NativeLibrary.isRunning()) return true
26 return setting?.isRuntimeEditable ?: false 26 return setting?.isRuntimeModifiable ?: false
27 } 27 }
28 28
29 companion object { 29 companion object {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt
index 7306ec458..b6a8c4612 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt
@@ -33,7 +33,7 @@ class SingleChoiceSetting(
33 */ 33 */
34 fun setSelectedValue(selection: Int): AbstractIntSetting { 34 fun setSelectedValue(selection: Int): AbstractIntSetting {
35 val intSetting = setting as AbstractIntSetting 35 val intSetting = setting as AbstractIntSetting
36 intSetting.int = selection 36 intSetting.setInt(selection)
37 return intSetting 37 return intSetting
38 } 38 }
39} 39}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt
index 92d0167ae..e71a29e35 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt
@@ -3,10 +3,12 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model.view 4package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import org.yuzu.yuzu_emu.features.settings.model.AbstractByteSetting
6import kotlin.math.roundToInt 7import kotlin.math.roundToInt
7import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting 8import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting
8import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting 9import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
9import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting 10import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
11import org.yuzu.yuzu_emu.features.settings.model.AbstractShortSetting
10import org.yuzu.yuzu_emu.utils.Log 12import org.yuzu.yuzu_emu.utils.Log
11 13
12class SliderSetting( 14class SliderSetting(
@@ -17,14 +19,16 @@ class SliderSetting(
17 val max: Int, 19 val max: Int,
18 val units: String, 20 val units: String,
19 val key: String? = null, 21 val key: String? = null,
20 val defaultValue: Int? = null 22 val defaultValue: Any? = null
21) : SettingsItem(setting, titleId, descriptionId) { 23) : SettingsItem(setting, titleId, descriptionId) {
22 override val type = TYPE_SLIDER 24 override val type = TYPE_SLIDER
23 25
24 val selectedValue: Int 26 val selectedValue: Any
25 get() { 27 get() {
26 val setting = setting ?: return defaultValue!! 28 val setting = setting ?: return defaultValue!!
27 return when (setting) { 29 return when (setting) {
30 is AbstractByteSetting -> setting.byte.toInt()
31 is AbstractShortSetting -> setting.short.toInt()
28 is AbstractIntSetting -> setting.int 32 is AbstractIntSetting -> setting.int
29 is AbstractFloatSetting -> setting.float.roundToInt() 33 is AbstractFloatSetting -> setting.float.roundToInt()
30 else -> { 34 else -> {
@@ -43,7 +47,7 @@ class SliderSetting(
43 */ 47 */
44 fun setSelectedValue(selection: Int): AbstractIntSetting { 48 fun setSelectedValue(selection: Int): AbstractIntSetting {
45 val intSetting = setting as AbstractIntSetting 49 val intSetting = setting as AbstractIntSetting
46 intSetting.int = selection 50 intSetting.setInt(selection)
47 return intSetting 51 return intSetting
48 } 52 }
49 53
@@ -56,7 +60,19 @@ class SliderSetting(
56 */ 60 */
57 fun setSelectedValue(selection: Float): AbstractFloatSetting { 61 fun setSelectedValue(selection: Float): AbstractFloatSetting {
58 val floatSetting = setting as AbstractFloatSetting 62 val floatSetting = setting as AbstractFloatSetting
59 floatSetting.float = selection 63 floatSetting.setFloat(selection)
60 return floatSetting 64 return floatSetting
61 } 65 }
66
67 fun setSelectedValue(selection: Short): AbstractShortSetting {
68 val shortSetting = setting as AbstractShortSetting
69 shortSetting.setShort(selection)
70 return shortSetting
71 }
72
73 fun setSelectedValue(selection: Byte): AbstractByteSetting {
74 val byteSetting = setting as AbstractByteSetting
75 byteSetting.setByte(selection)
76 return byteSetting
77 }
62} 78}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt
index 3b6731dcd..2195641e3 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt
@@ -53,7 +53,7 @@ class StringSingleChoiceSetting(
53 */ 53 */
54 fun setSelectedValue(selection: String): AbstractStringSetting { 54 fun setSelectedValue(selection: String): AbstractStringSetting {
55 val stringSetting = setting as AbstractStringSetting 55 val stringSetting = setting as AbstractStringSetting
56 stringSetting.string = selection 56 stringSetting.setString(selection)
57 return stringSetting 57 return stringSetting
58 } 58 }
59} 59}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt
index 90b198718..4ed8070e5 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt
@@ -49,14 +49,14 @@ class SwitchSetting(
49 // Try integer setting 49 // Try integer setting
50 try { 50 try {
51 val setting = setting as AbstractIntSetting 51 val setting = setting as AbstractIntSetting
52 setting.int = if (checked) 1 else 0 52 setting.setInt(if (checked) 1 else 0)
53 return setting 53 return setting
54 } catch (_: ClassCastException) { 54 } catch (_: ClassCastException) {
55 } 55 }
56 56
57 // Try boolean setting 57 // Try boolean setting
58 val setting = setting as AbstractBooleanSetting 58 val setting = setting as AbstractBooleanSetting
59 setting.boolean = checked 59 setting.setBoolean(checked)
60 return setting 60 return setting
61 } 61 }
62} 62}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
index e6fffc832..733a53c8c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
@@ -21,12 +21,7 @@ import com.google.android.material.color.MaterialColors
21import java.io.IOException 21import java.io.IOException
22import org.yuzu.yuzu_emu.R 22import org.yuzu.yuzu_emu.R
23import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding 23import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
24import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
25import org.yuzu.yuzu_emu.features.settings.model.FloatSetting
26import org.yuzu.yuzu_emu.features.settings.model.IntSetting
27import org.yuzu.yuzu_emu.features.settings.model.Settings 24import org.yuzu.yuzu_emu.features.settings.model.Settings
28import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
29import org.yuzu.yuzu_emu.features.settings.model.StringSetting
30import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 25import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
31import org.yuzu.yuzu_emu.utils.* 26import org.yuzu.yuzu_emu.utils.*
32 27
@@ -35,10 +30,6 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
35 30
36 private lateinit var binding: ActivitySettingsBinding 31 private lateinit var binding: ActivitySettingsBinding
37 32
38 private val settingsViewModel: SettingsViewModel by viewModels()
39
40 override val settings: Settings get() = settingsViewModel.settings
41
42 override fun onCreate(savedInstanceState: Bundle?) { 33 override fun onCreate(savedInstanceState: Bundle?) {
43 ThemeHelper.setTheme(this) 34 ThemeHelper.setTheme(this)
44 35
@@ -171,14 +162,6 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
171 fragment?.loadSettingsList() 162 fragment?.loadSettingsList()
172 } 163 }
173 164
174 override fun showToastMessage(message: String, is_long: Boolean) {
175 Toast.makeText(
176 this,
177 message,
178 if (is_long) Toast.LENGTH_LONG else Toast.LENGTH_SHORT
179 ).show()
180 }
181
182 override fun onSettingChanged() { 165 override fun onSettingChanged() {
183 presenter.onSettingChanged() 166 presenter.onSettingChanged()
184 } 167 }
@@ -187,19 +170,18 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
187 // Prevents saving to a non-existent settings file 170 // Prevents saving to a non-existent settings file
188 presenter.onSettingsReset() 171 presenter.onSettingsReset()
189 172
190 // Reset the static memory representation of each setting
191 BooleanSetting.clear()
192 FloatSetting.clear()
193 IntSetting.clear()
194 StringSetting.clear()
195
196 // Delete settings file because the user may have changed values that do not exist in the UI 173 // Delete settings file because the user may have changed values that do not exist in the UI
197 val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG) 174 val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG)
198 if (!settingsFile.delete()) { 175 if (!settingsFile.delete()) {
199 throw IOException("Failed to delete $settingsFile") 176 throw IOException("Failed to delete $settingsFile")
200 } 177 }
178 Settings.settingsList.forEach { it.reset() }
201 179
202 showToastMessage(getString(R.string.settings_reset), true) 180 Toast.makeText(
181 applicationContext,
182 getString(R.string.settings_reset),
183 Toast.LENGTH_LONG
184 ).show()
203 finish() 185 finish()
204 } 186 }
205 187
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt
index 93e677b21..fdbad32bf 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt
@@ -5,7 +5,6 @@ package org.yuzu.yuzu_emu.features.settings.ui
5 5
6import android.content.Context 6import android.content.Context
7import android.os.Bundle 7import android.os.Bundle
8import android.text.TextUtils
9import java.io.File 8import java.io.File
10import org.yuzu.yuzu_emu.NativeLibrary 9import org.yuzu.yuzu_emu.NativeLibrary
11import org.yuzu.yuzu_emu.features.settings.model.Settings 10import org.yuzu.yuzu_emu.features.settings.model.Settings
@@ -14,8 +13,6 @@ import org.yuzu.yuzu_emu.utils.DirectoryInitialization
14import org.yuzu.yuzu_emu.utils.Log 13import org.yuzu.yuzu_emu.utils.Log
15 14
16class SettingsActivityPresenter(private val activityView: SettingsActivityView) { 15class SettingsActivityPresenter(private val activityView: SettingsActivityView) {
17 val settings: Settings get() = activityView.settings
18
19 private var shouldSave = false 16 private var shouldSave = false
20 private lateinit var menuTag: String 17 private lateinit var menuTag: String
21 private lateinit var gameId: String 18 private lateinit var gameId: String
@@ -33,13 +30,7 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView)
33 } 30 }
34 31
35 private fun loadSettingsUI() { 32 private fun loadSettingsUI() {
36 if (!settings.isLoaded) { 33 // TODO: Load custom settings contextually
37 if (!TextUtils.isEmpty(gameId)) {
38 settings.loadSettings(gameId, activityView)
39 } else {
40 settings.loadSettings(activityView)
41 }
42 }
43 activityView.showSettingsFragment(menuTag, false, gameId) 34 activityView.showSettingsFragment(menuTag, false, gameId)
44 activityView.onSettingsFileLoaded() 35 activityView.onSettingsFileLoaded()
45 } 36 }
@@ -67,9 +58,9 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView)
67 fun onStop(finishing: Boolean) { 58 fun onStop(finishing: Boolean) {
68 if (finishing && shouldSave) { 59 if (finishing && shouldSave) {
69 Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...") 60 Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...")
70 settings.saveSettings(activityView) 61 Settings.saveSettings()
62 NativeLibrary.reloadSettings()
71 } 63 }
72 NativeLibrary.reloadSettings()
73 } 64 }
74 65
75 fun onSettingChanged() { 66 fun onSettingChanged() {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityView.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityView.kt
index c186fc388..07a58b4ea 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityView.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityView.kt
@@ -3,8 +3,6 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.ui 4package org.yuzu.yuzu_emu.features.settings.ui
5 5
6import org.yuzu.yuzu_emu.features.settings.model.Settings
7
8/** 6/**
9 * Abstraction for the Activity that manages SettingsFragments. 7 * Abstraction for the Activity that manages SettingsFragments.
10 */ 8 */
@@ -18,15 +16,6 @@ interface SettingsActivityView {
18 fun showSettingsFragment(menuTag: String, addToStack: Boolean, gameId: String) 16 fun showSettingsFragment(menuTag: String, addToStack: Boolean, gameId: String)
19 17
20 /** 18 /**
21 * Called by a contained Fragment to get access to the Setting HashMap
22 * loaded from disk, so that each Fragment doesn't need to perform its own
23 * read operation.
24 *
25 * @return A HashMap of Settings.
26 */
27 val settings: Settings
28
29 /**
30 * Called when a load operation completes. 19 * Called when a load operation completes.
31 */ 20 */
32 fun onSettingsFileLoaded() 21 fun onSettingsFileLoaded()
@@ -37,14 +26,6 @@ interface SettingsActivityView {
37 fun onSettingsFileNotFound() 26 fun onSettingsFileNotFound()
38 27
39 /** 28 /**
40 * Display a popup text message on screen.
41 *
42 * @param message The contents of the onscreen message.
43 * @param is_long Whether this should be a long Toast or short one.
44 */
45 fun showToastMessage(message: String, is_long: Boolean)
46
47 /**
48 * End the activity. 29 * End the activity.
49 */ 30 */
50 fun finish() 31 fun finish()
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
index 9711e2c51..e2e8d8bec 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
@@ -24,12 +24,10 @@ import org.yuzu.yuzu_emu.databinding.DialogSliderBinding
24import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding 24import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
25import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding 25import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
26import org.yuzu.yuzu_emu.databinding.ListItemSettingsHeaderBinding 26import org.yuzu.yuzu_emu.databinding.ListItemSettingsHeaderBinding
27import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
28import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting
29import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
30import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting 27import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
31import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting 28import org.yuzu.yuzu_emu.features.settings.model.ByteSetting
32import org.yuzu.yuzu_emu.features.settings.model.FloatSetting 29import org.yuzu.yuzu_emu.features.settings.model.FloatSetting
30import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
33import org.yuzu.yuzu_emu.features.settings.model.view.* 31import org.yuzu.yuzu_emu.features.settings.model.view.*
34import org.yuzu.yuzu_emu.features.settings.ui.viewholder.* 32import org.yuzu.yuzu_emu.features.settings.ui.viewholder.*
35 33
@@ -115,8 +113,7 @@ class SettingsAdapter(
115 } 113 }
116 114
117 fun onBooleanClick(item: SwitchSetting, position: Int, checked: Boolean) { 115 fun onBooleanClick(item: SwitchSetting, position: Int, checked: Boolean) {
118 val setting = item.setChecked(checked) 116 item.setChecked(checked)
119 fragmentView.putSetting(setting)
120 fragmentView.onSettingChanged() 117 fragmentView.onSettingChanged()
121 } 118 }
122 119
@@ -150,7 +147,7 @@ class SettingsAdapter(
150 fun onDateTimeClick(item: DateTimeSetting, position: Int) { 147 fun onDateTimeClick(item: DateTimeSetting, position: Int) {
151 clickedItem = item 148 clickedItem = item
152 clickedPosition = position 149 clickedPosition = position
153 val storedTime = java.lang.Long.decode(item.value) * 1000 150 val storedTime = item.value * 1000
154 151
155 // Helper to extract hour and minute from epoch time 152 // Helper to extract hour and minute from epoch time
156 val calendar: Calendar = Calendar.getInstance() 153 val calendar: Calendar = Calendar.getInstance()
@@ -183,13 +180,11 @@ class SettingsAdapter(
183 var epochTime: Long = datePicker.selection!! / 1000 180 var epochTime: Long = datePicker.selection!! / 1000
184 epochTime += timePicker.hour.toLong() * 60 * 60 181 epochTime += timePicker.hour.toLong() * 60 * 60
185 epochTime += timePicker.minute.toLong() * 60 182 epochTime += timePicker.minute.toLong() * 60
186 val rtcString = epochTime.toString() 183 if (item.value != epochTime) {
187 if (item.value != rtcString) {
188 fragmentView.onSettingChanged() 184 fragmentView.onSettingChanged()
185 notifyItemChanged(clickedPosition)
186 item.setSelectedValue(epochTime)
189 } 187 }
190 notifyItemChanged(clickedPosition)
191 val setting = item.setSelectedValue(rtcString)
192 fragmentView.putSetting(setting)
193 clickedItem = null 188 clickedItem = null
194 } 189 }
195 datePicker.show( 190 datePicker.show(
@@ -201,7 +196,7 @@ class SettingsAdapter(
201 fun onSliderClick(item: SliderSetting, position: Int) { 196 fun onSliderClick(item: SliderSetting, position: Int) {
202 clickedItem = item 197 clickedItem = item
203 clickedPosition = position 198 clickedPosition = position
204 sliderProgress = item.selectedValue 199 sliderProgress = item.selectedValue as Int
205 200
206 val inflater = LayoutInflater.from(context) 201 val inflater = LayoutInflater.from(context)
207 val sliderBinding = DialogSliderBinding.inflate(inflater) 202 val sliderBinding = DialogSliderBinding.inflate(inflater)
@@ -249,8 +244,7 @@ class SettingsAdapter(
249 } 244 }
250 245
251 // Get the backing Setting, which may be null (if for example it was missing from the file) 246 // Get the backing Setting, which may be null (if for example it was missing from the file)
252 val setting = scSetting.setSelectedValue(value) 247 scSetting.setSelectedValue(value)
253 fragmentView.putSetting(setting)
254 closeDialog() 248 closeDialog()
255 } 249 }
256 250
@@ -258,8 +252,7 @@ class SettingsAdapter(
258 val scSetting = clickedItem as StringSingleChoiceSetting 252 val scSetting = clickedItem as StringSingleChoiceSetting
259 val value = scSetting.getValueAt(which) 253 val value = scSetting.getValueAt(which)
260 if (scSetting.selectedValue != value) fragmentView.onSettingChanged() 254 if (scSetting.selectedValue != value) fragmentView.onSettingChanged()
261 val setting = scSetting.setSelectedValue(value!!) 255 scSetting.setSelectedValue(value!!)
262 fragmentView.putSetting(setting)
263 closeDialog() 256 closeDialog()
264 } 257 }
265 258
@@ -268,13 +261,25 @@ class SettingsAdapter(
268 if (sliderSetting.selectedValue != sliderProgress) { 261 if (sliderSetting.selectedValue != sliderProgress) {
269 fragmentView.onSettingChanged() 262 fragmentView.onSettingChanged()
270 } 263 }
271 if (sliderSetting.setting is FloatSetting) { 264 when (sliderSetting.setting) {
272 val value = sliderProgress.toFloat() 265 is ByteSetting -> {
273 val setting = sliderSetting.setSelectedValue(value) 266 val value = sliderProgress.toByte()
274 fragmentView.putSetting(setting) 267 sliderSetting.setSelectedValue(value)
275 } else { 268 }
276 val setting = sliderSetting.setSelectedValue(sliderProgress) 269
277 fragmentView.putSetting(setting) 270 is ShortSetting -> {
271 val value = sliderProgress.toShort()
272 sliderSetting.setSelectedValue(value)
273 }
274
275 is FloatSetting -> {
276 val value = sliderProgress.toFloat()
277 sliderSetting.setSelectedValue(value)
278 }
279
280 else -> {
281 sliderSetting.setSelectedValue(sliderProgress)
282 }
278 } 283 }
279 closeDialog() 284 closeDialog()
280 } 285 }
@@ -286,13 +291,8 @@ class SettingsAdapter(
286 fun onLongClick(setting: AbstractSetting, position: Int): Boolean { 291 fun onLongClick(setting: AbstractSetting, position: Int): Boolean {
287 MaterialAlertDialogBuilder(context) 292 MaterialAlertDialogBuilder(context)
288 .setMessage(R.string.reset_setting_confirmation) 293 .setMessage(R.string.reset_setting_confirmation)
289 .setPositiveButton(android.R.string.ok) { dialog: DialogInterface, which: Int -> 294 .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
290 when (setting) { 295 setting.reset()
291 is AbstractBooleanSetting -> setting.boolean = setting.defaultValue as Boolean
292 is AbstractFloatSetting -> setting.float = setting.defaultValue as Float
293 is AbstractIntSetting -> setting.int = setting.defaultValue as Int
294 is AbstractStringSetting -> setting.string = setting.defaultValue as String
295 }
296 notifyItemChanged(position) 296 notifyItemChanged(position)
297 fragmentView.onSettingChanged() 297 fragmentView.onSettingChanged()
298 } 298 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
index 70a74c4dd..dc1bf6eb1 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
@@ -15,7 +15,6 @@ import androidx.fragment.app.Fragment
15import androidx.recyclerview.widget.LinearLayoutManager 15import androidx.recyclerview.widget.LinearLayoutManager
16import com.google.android.material.divider.MaterialDividerItemDecoration 16import com.google.android.material.divider.MaterialDividerItemDecoration
17import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding 17import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding
18import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
19import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem 18import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
20 19
21class SettingsFragment : Fragment(), SettingsFragmentView { 20class SettingsFragment : Fragment(), SettingsFragmentView {
@@ -89,14 +88,6 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
89 ) 88 )
90 } 89 }
91 90
92 override fun showToastMessage(message: String?, is_long: Boolean) {
93 activityView!!.showToastMessage(message!!, is_long)
94 }
95
96 override fun putSetting(setting: AbstractSetting) {
97 fragmentPresenter.putSetting(setting)
98 }
99
100 override fun onSettingChanged() { 91 override fun onSettingChanged() {
101 activityView!!.onSettingChanged() 92 activityView!!.onSettingChanged()
102 } 93 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
index 59c1d9d54..2bab9e542 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
@@ -6,16 +6,18 @@ package org.yuzu.yuzu_emu.features.settings.ui
6import android.content.SharedPreferences 6import android.content.SharedPreferences
7import android.os.Build 7import android.os.Build
8import android.text.TextUtils 8import android.text.TextUtils
9import android.widget.Toast
9import androidx.preference.PreferenceManager 10import androidx.preference.PreferenceManager
10import org.yuzu.yuzu_emu.R 11import org.yuzu.yuzu_emu.R
11import org.yuzu.yuzu_emu.YuzuApplication 12import org.yuzu.yuzu_emu.YuzuApplication
12import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting 13import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
13import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting 14import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
14import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
15import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting 15import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
16import org.yuzu.yuzu_emu.features.settings.model.ByteSetting
16import org.yuzu.yuzu_emu.features.settings.model.IntSetting 17import org.yuzu.yuzu_emu.features.settings.model.IntSetting
18import org.yuzu.yuzu_emu.features.settings.model.LongSetting
17import org.yuzu.yuzu_emu.features.settings.model.Settings 19import org.yuzu.yuzu_emu.features.settings.model.Settings
18import org.yuzu.yuzu_emu.features.settings.model.StringSetting 20import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
19import org.yuzu.yuzu_emu.features.settings.model.view.* 21import org.yuzu.yuzu_emu.features.settings.model.view.*
20import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 22import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
21import org.yuzu.yuzu_emu.fragments.ResetSettingsDialogFragment 23import org.yuzu.yuzu_emu.fragments.ResetSettingsDialogFragment
@@ -27,7 +29,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
27 private var settingsList: ArrayList<SettingsItem>? = null 29 private var settingsList: ArrayList<SettingsItem>? = null
28 30
29 private val settingsActivity get() = fragmentView.activityView as SettingsActivity 31 private val settingsActivity get() = fragmentView.activityView as SettingsActivity
30 private val settings get() = fragmentView.activityView!!.settings
31 32
32 private lateinit var preferences: SharedPreferences 33 private lateinit var preferences: SharedPreferences
33 34
@@ -41,17 +42,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
41 loadSettingsList() 42 loadSettingsList()
42 } 43 }
43 44
44 fun putSetting(setting: AbstractSetting) {
45 if (setting.section == null || setting.key == null) {
46 return
47 }
48
49 val section = settings.getSection(setting.section!!)!!
50 if (section.getSetting(setting.key!!) == null) {
51 section.putSetting(setting)
52 }
53 }
54
55 fun loadSettingsList() { 45 fun loadSettingsList() {
56 if (!TextUtils.isEmpty(gameId)) { 46 if (!TextUtils.isEmpty(gameId)) {
57 settingsActivity.setToolbarTitle("Game Settings: $gameId") 47 settingsActivity.setToolbarTitle("Game Settings: $gameId")
@@ -69,7 +59,12 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
69 Settings.SECTION_THEME -> addThemeSettings(sl) 59 Settings.SECTION_THEME -> addThemeSettings(sl)
70 Settings.SECTION_DEBUG -> addDebugSettings(sl) 60 Settings.SECTION_DEBUG -> addDebugSettings(sl)
71 else -> { 61 else -> {
72 fragmentView.showToastMessage("Unimplemented menu", false) 62 val context = YuzuApplication.appContext
63 Toast.makeText(
64 context,
65 context.getString(R.string.unimplemented_menu),
66 Toast.LENGTH_SHORT
67 ).show()
73 return 68 return
74 } 69 }
75 } 70 }
@@ -135,23 +130,23 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
135 sl.apply { 130 sl.apply {
136 add( 131 add(
137 SwitchSetting( 132 SwitchSetting(
138 IntSetting.RENDERER_USE_SPEED_LIMIT, 133 BooleanSetting.RENDERER_USE_SPEED_LIMIT,
139 R.string.frame_limit_enable, 134 R.string.frame_limit_enable,
140 R.string.frame_limit_enable_description, 135 R.string.frame_limit_enable_description,
141 IntSetting.RENDERER_USE_SPEED_LIMIT.key, 136 BooleanSetting.RENDERER_USE_SPEED_LIMIT.key,
142 IntSetting.RENDERER_USE_SPEED_LIMIT.defaultValue 137 BooleanSetting.RENDERER_USE_SPEED_LIMIT.defaultValue
143 ) 138 )
144 ) 139 )
145 add( 140 add(
146 SliderSetting( 141 SliderSetting(
147 IntSetting.RENDERER_SPEED_LIMIT, 142 ShortSetting.RENDERER_SPEED_LIMIT,
148 R.string.frame_limit_slider, 143 R.string.frame_limit_slider,
149 R.string.frame_limit_slider_description, 144 R.string.frame_limit_slider_description,
150 1, 145 1,
151 200, 146 200,
152 "%", 147 "%",
153 IntSetting.RENDERER_SPEED_LIMIT.key, 148 ShortSetting.RENDERER_SPEED_LIMIT.key,
154 IntSetting.RENDERER_SPEED_LIMIT.defaultValue 149 ShortSetting.RENDERER_SPEED_LIMIT.defaultValue
155 ) 150 )
156 ) 151 )
157 add( 152 add(
@@ -182,11 +177,11 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
182 sl.apply { 177 sl.apply {
183 add( 178 add(
184 SwitchSetting( 179 SwitchSetting(
185 IntSetting.USE_DOCKED_MODE, 180 BooleanSetting.USE_DOCKED_MODE,
186 R.string.use_docked_mode, 181 R.string.use_docked_mode,
187 R.string.use_docked_mode_description, 182 R.string.use_docked_mode_description,
188 IntSetting.USE_DOCKED_MODE.key, 183 BooleanSetting.USE_DOCKED_MODE.key,
189 IntSetting.USE_DOCKED_MODE.defaultValue 184 BooleanSetting.USE_DOCKED_MODE.defaultValue
190 ) 185 )
191 ) 186 )
192 add( 187 add(
@@ -222,11 +217,11 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
222 ) 217 )
223 add( 218 add(
224 DateTimeSetting( 219 DateTimeSetting(
225 StringSetting.CUSTOM_RTC, 220 LongSetting.CUSTOM_RTC,
226 R.string.set_custom_rtc, 221 R.string.set_custom_rtc,
227 0, 222 0,
228 StringSetting.CUSTOM_RTC.key, 223 LongSetting.CUSTOM_RTC.key,
229 StringSetting.CUSTOM_RTC.defaultValue 224 LongSetting.CUSTOM_RTC.defaultValue
230 ) 225 )
231 ) 226 )
232 } 227 }
@@ -314,38 +309,38 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
314 ) 309 )
315 add( 310 add(
316 SwitchSetting( 311 SwitchSetting(
317 IntSetting.RENDERER_USE_DISK_SHADER_CACHE, 312 BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE,
318 R.string.use_disk_shader_cache, 313 R.string.use_disk_shader_cache,
319 R.string.use_disk_shader_cache_description, 314 R.string.use_disk_shader_cache_description,
320 IntSetting.RENDERER_USE_DISK_SHADER_CACHE.key, 315 BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key,
321 IntSetting.RENDERER_USE_DISK_SHADER_CACHE.defaultValue 316 BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.defaultValue
322 ) 317 )
323 ) 318 )
324 add( 319 add(
325 SwitchSetting( 320 SwitchSetting(
326 IntSetting.RENDERER_FORCE_MAX_CLOCK, 321 BooleanSetting.RENDERER_FORCE_MAX_CLOCK,
327 R.string.renderer_force_max_clock, 322 R.string.renderer_force_max_clock,
328 R.string.renderer_force_max_clock_description, 323 R.string.renderer_force_max_clock_description,
329 IntSetting.RENDERER_FORCE_MAX_CLOCK.key, 324 BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key,
330 IntSetting.RENDERER_FORCE_MAX_CLOCK.defaultValue 325 BooleanSetting.RENDERER_FORCE_MAX_CLOCK.defaultValue
331 ) 326 )
332 ) 327 )
333 add( 328 add(
334 SwitchSetting( 329 SwitchSetting(
335 IntSetting.RENDERER_ASYNCHRONOUS_SHADERS, 330 BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS,
336 R.string.renderer_asynchronous_shaders, 331 R.string.renderer_asynchronous_shaders,
337 R.string.renderer_asynchronous_shaders_description, 332 R.string.renderer_asynchronous_shaders_description,
338 IntSetting.RENDERER_ASYNCHRONOUS_SHADERS.key, 333 BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key,
339 IntSetting.RENDERER_ASYNCHRONOUS_SHADERS.defaultValue 334 BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.defaultValue
340 ) 335 )
341 ) 336 )
342 add( 337 add(
343 SwitchSetting( 338 SwitchSetting(
344 IntSetting.RENDERER_REACTIVE_FLUSHING, 339 BooleanSetting.RENDERER_REACTIVE_FLUSHING,
345 R.string.renderer_reactive_flushing, 340 R.string.renderer_reactive_flushing,
346 R.string.renderer_reactive_flushing_description, 341 R.string.renderer_reactive_flushing_description,
347 IntSetting.RENDERER_REACTIVE_FLUSHING.key, 342 BooleanSetting.RENDERER_REACTIVE_FLUSHING.key,
348 IntSetting.RENDERER_REACTIVE_FLUSHING.defaultValue 343 BooleanSetting.RENDERER_REACTIVE_FLUSHING.defaultValue
349 ) 344 )
350 ) 345 )
351 } 346 }
@@ -355,26 +350,26 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
355 settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_audio)) 350 settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_audio))
356 sl.apply { 351 sl.apply {
357 add( 352 add(
358 StringSingleChoiceSetting( 353 SingleChoiceSetting(
359 StringSetting.AUDIO_OUTPUT_ENGINE, 354 IntSetting.AUDIO_OUTPUT_ENGINE,
360 R.string.audio_output_engine, 355 R.string.audio_output_engine,
361 0, 356 0,
362 settingsActivity.resources.getStringArray(R.array.outputEngineEntries), 357 R.array.outputEngineEntries,
363 settingsActivity.resources.getStringArray(R.array.outputEngineValues), 358 R.array.outputEngineValues,
364 StringSetting.AUDIO_OUTPUT_ENGINE.key, 359 IntSetting.AUDIO_OUTPUT_ENGINE.key,
365 StringSetting.AUDIO_OUTPUT_ENGINE.defaultValue 360 IntSetting.AUDIO_OUTPUT_ENGINE.defaultValue
366 ) 361 )
367 ) 362 )
368 add( 363 add(
369 SliderSetting( 364 SliderSetting(
370 IntSetting.AUDIO_VOLUME, 365 ByteSetting.AUDIO_VOLUME,
371 R.string.audio_volume, 366 R.string.audio_volume,
372 R.string.audio_volume_description, 367 R.string.audio_volume_description,
373 0, 368 0,
374 100, 369 100,
375 "%", 370 "%",
376 IntSetting.AUDIO_VOLUME.key, 371 ByteSetting.AUDIO_VOLUME.key,
377 IntSetting.AUDIO_VOLUME.defaultValue 372 ByteSetting.AUDIO_VOLUME.defaultValue
378 ) 373 )
379 ) 374 )
380 } 375 }
@@ -384,19 +379,19 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
384 settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_theme)) 379 settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_theme))
385 sl.apply { 380 sl.apply {
386 val theme: AbstractIntSetting = object : AbstractIntSetting { 381 val theme: AbstractIntSetting = object : AbstractIntSetting {
387 override var int: Int 382 override val int: Int
388 get() = preferences.getInt(Settings.PREF_THEME, 0) 383 get() = preferences.getInt(Settings.PREF_THEME, 0)
389 set(value) { 384
390 preferences.edit() 385 override fun setInt(value: Int) {
391 .putInt(Settings.PREF_THEME, value) 386 preferences.edit()
392 .apply() 387 .putInt(Settings.PREF_THEME, value)
393 settingsActivity.recreate() 388 .apply()
394 } 389 settingsActivity.recreate()
390 }
391
395 override val key: String? = null 392 override val key: String? = null
396 override val section: String? = null 393 override val category = Settings.Category.UiGeneral
397 override val isRuntimeEditable: Boolean = false 394 override val isRuntimeModifiable: Boolean = false
398 override val valueAsString: String
399 get() = preferences.getInt(Settings.PREF_THEME, 0).toString()
400 override val defaultValue: Any = 0 395 override val defaultValue: Any = 0
401 } 396 }
402 397
@@ -423,19 +418,19 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
423 } 418 }
424 419
425 val themeMode: AbstractIntSetting = object : AbstractIntSetting { 420 val themeMode: AbstractIntSetting = object : AbstractIntSetting {
426 override var int: Int 421 override val int: Int
427 get() = preferences.getInt(Settings.PREF_THEME_MODE, -1) 422 get() = preferences.getInt(Settings.PREF_THEME_MODE, -1)
428 set(value) { 423
429 preferences.edit() 424 override fun setInt(value: Int) {
430 .putInt(Settings.PREF_THEME_MODE, value) 425 preferences.edit()
431 .apply() 426 .putInt(Settings.PREF_THEME_MODE, value)
432 ThemeHelper.setThemeMode(settingsActivity) 427 .apply()
433 } 428 ThemeHelper.setThemeMode(settingsActivity)
429 }
430
434 override val key: String? = null 431 override val key: String? = null
435 override val section: String? = null 432 override val category = Settings.Category.UiGeneral
436 override val isRuntimeEditable: Boolean = false 433 override val isRuntimeModifiable: Boolean = false
437 override val valueAsString: String
438 get() = preferences.getInt(Settings.PREF_THEME_MODE, -1).toString()
439 override val defaultValue: Any = -1 434 override val defaultValue: Any = -1
440 } 435 }
441 436
@@ -450,20 +445,19 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
450 ) 445 )
451 446
452 val blackBackgrounds: AbstractBooleanSetting = object : AbstractBooleanSetting { 447 val blackBackgrounds: AbstractBooleanSetting = object : AbstractBooleanSetting {
453 override var boolean: Boolean 448 override val boolean: Boolean
454 get() = preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false) 449 get() = preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false)
455 set(value) { 450
456 preferences.edit() 451 override fun setBoolean(value: Boolean) {
457 .putBoolean(Settings.PREF_BLACK_BACKGROUNDS, value) 452 preferences.edit()
458 .apply() 453 .putBoolean(Settings.PREF_BLACK_BACKGROUNDS, value)
459 settingsActivity.recreate() 454 .apply()
460 } 455 settingsActivity.recreate()
456 }
457
461 override val key: String? = null 458 override val key: String? = null
462 override val section: String? = null 459 override val category = Settings.Category.UiGeneral
463 override val isRuntimeEditable: Boolean = false 460 override val isRuntimeModifiable: Boolean = false
464 override val valueAsString: String
465 get() = preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false)
466 .toString()
467 override val defaultValue: Any = false 461 override val defaultValue: Any = false
468 } 462 }
469 463
@@ -494,11 +488,11 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
494 ) 488 )
495 add( 489 add(
496 SwitchSetting( 490 SwitchSetting(
497 IntSetting.RENDERER_DEBUG, 491 BooleanSetting.RENDERER_DEBUG,
498 R.string.renderer_debug, 492 R.string.renderer_debug,
499 R.string.renderer_debug_description, 493 R.string.renderer_debug_description,
500 IntSetting.RENDERER_DEBUG.key, 494 BooleanSetting.RENDERER_DEBUG.key,
501 IntSetting.RENDERER_DEBUG.defaultValue 495 BooleanSetting.RENDERER_DEBUG.defaultValue
502 ) 496 )
503 ) 497 )
504 498
@@ -514,17 +508,18 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
514 ) 508 )
515 509
516 val fastmem = object : AbstractBooleanSetting { 510 val fastmem = object : AbstractBooleanSetting {
517 override var boolean: Boolean 511 override val boolean: Boolean
518 get() = 512 get() =
519 BooleanSetting.FASTMEM.boolean && BooleanSetting.FASTMEM_EXCLUSIVES.boolean 513 BooleanSetting.FASTMEM.boolean && BooleanSetting.FASTMEM_EXCLUSIVES.boolean
520 set(value) { 514
521 BooleanSetting.FASTMEM.boolean = value 515 override fun setBoolean(value: Boolean) {
522 BooleanSetting.FASTMEM_EXCLUSIVES.boolean = value 516 BooleanSetting.FASTMEM.setBoolean(value)
523 } 517 BooleanSetting.FASTMEM_EXCLUSIVES.setBoolean(value)
518 }
519
524 override val key: String? = null 520 override val key: String? = null
525 override val section: String = Settings.SECTION_CPU 521 override val category = Settings.Category.Cpu
526 override val isRuntimeEditable: Boolean = false 522 override val isRuntimeModifiable: Boolean = false
527 override val valueAsString: String = ""
528 override val defaultValue: Any = true 523 override val defaultValue: Any = true
529 } 524 }
530 add( 525 add(
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentView.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentView.kt
index 1ebe35eaa..a4d7a80aa 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentView.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentView.kt
@@ -3,7 +3,6 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.ui 4package org.yuzu.yuzu_emu.features.settings.ui
5 5
6import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
7import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem 6import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
8 7
9/** 8/**
@@ -37,21 +36,6 @@ interface SettingsFragmentView {
37 fun loadSubMenu(menuKey: String) 36 fun loadSubMenu(menuKey: String)
38 37
39 /** 38 /**
40 * Tell the Fragment to tell the containing activity to display a toast message.
41 *
42 * @param message Text to be shown in the Toast
43 * @param is_long Whether this should be a long Toast or short one.
44 */
45 fun showToastMessage(message: String?, is_long: Boolean)
46
47 /**
48 * Have the fragment add a setting to the HashMap.
49 *
50 * @param setting The (possibly previously missing) new setting.
51 */
52 fun putSetting(setting: AbstractSetting)
53
54 /**
55 * Have the fragment tell the containing Activity that a setting was modified. 39 * Have the fragment tell the containing Activity that a setting was modified.
56 */ 40 */
57 fun onSettingChanged() 41 fun onSettingChanged()
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
index 79572fc06..eb25ea4fb 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
@@ -29,7 +29,7 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
29 } 29 }
30 30
31 binding.textSettingValue.visibility = View.VISIBLE 31 binding.textSettingValue.visibility = View.VISIBLE
32 val epochTime = setting.value.toLong() 32 val epochTime = setting.value
33 val instant = Instant.ofEpochMilli(epochTime * 1000) 33 val instant = Instant.ofEpochMilli(epochTime * 1000)
34 val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC")) 34 val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"))
35 val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM) 35 val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
index 70a52df5d..2b04d666a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
@@ -3,18 +3,15 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.utils 4package org.yuzu.yuzu_emu.features.settings.utils
5 5
6import android.widget.Toast
6import java.io.* 7import java.io.*
7import java.util.*
8import org.ini4j.Wini 8import org.ini4j.Wini
9import org.yuzu.yuzu_emu.NativeLibrary
10import org.yuzu.yuzu_emu.R 9import org.yuzu.yuzu_emu.R
11import org.yuzu.yuzu_emu.YuzuApplication 10import org.yuzu.yuzu_emu.YuzuApplication
12import org.yuzu.yuzu_emu.features.settings.model.* 11import org.yuzu.yuzu_emu.features.settings.model.*
13import org.yuzu.yuzu_emu.features.settings.model.Settings.SettingsSectionMap
14import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivityView
15import org.yuzu.yuzu_emu.utils.BiMap
16import org.yuzu.yuzu_emu.utils.DirectoryInitialization 12import org.yuzu.yuzu_emu.utils.DirectoryInitialization
17import org.yuzu.yuzu_emu.utils.Log 13import org.yuzu.yuzu_emu.utils.Log
14import org.yuzu.yuzu_emu.utils.NativeConfig
18 15
19/** 16/**
20 * Contains static methods for interacting with .ini files in which settings are stored. 17 * Contains static methods for interacting with .ini files in which settings are stored.
@@ -22,243 +19,41 @@ import org.yuzu.yuzu_emu.utils.Log
22object SettingsFile { 19object SettingsFile {
23 const val FILE_NAME_CONFIG = "config" 20 const val FILE_NAME_CONFIG = "config"
24 21
25 private var sectionsMap = BiMap<String?, String?>()
26
27 /**
28 * Reads a given .ini file from disk and returns it as a HashMap of Settings, themselves
29 * effectively a HashMap of key/value settings. If unsuccessful, outputs an error telling why it
30 * failed.
31 *
32 * @param ini The ini file to load the settings from
33 * @param isCustomGame
34 * @param view The current view.
35 * @return An Observable that emits a HashMap of the file's contents, then completes.
36 */
37 private fun readFile(
38 ini: File?,
39 isCustomGame: Boolean,
40 view: SettingsActivityView? = null
41 ): HashMap<String, SettingSection?> {
42 val sections: HashMap<String, SettingSection?> = SettingsSectionMap()
43 var reader: BufferedReader? = null
44 try {
45 reader = BufferedReader(FileReader(ini))
46 var current: SettingSection? = null
47 var line: String?
48 while (reader.readLine().also { line = it } != null) {
49 if (line!!.startsWith("[") && line!!.endsWith("]")) {
50 current = sectionFromLine(line!!, isCustomGame)
51 sections[current.name] = current
52 } else if (current != null) {
53 val setting = settingFromLine(line!!)
54 if (setting != null) {
55 current.putSetting(setting)
56 }
57 }
58 }
59 } catch (e: FileNotFoundException) {
60 Log.error("[SettingsFile] File not found: " + e.message)
61 view?.onSettingsFileNotFound()
62 } catch (e: IOException) {
63 Log.error("[SettingsFile] Error reading from: " + e.message)
64 view?.onSettingsFileNotFound()
65 } finally {
66 if (reader != null) {
67 try {
68 reader.close()
69 } catch (e: IOException) {
70 Log.error("[SettingsFile] Error closing: " + e.message)
71 }
72 }
73 }
74 return sections
75 }
76
77 fun readFile(fileName: String, view: SettingsActivityView?): HashMap<String, SettingSection?> {
78 return readFile(getSettingsFile(fileName), false, view)
79 }
80
81 fun readFile(fileName: String): HashMap<String, SettingSection?> =
82 readFile(getSettingsFile(fileName), false)
83
84 /**
85 * Reads a given .ini file from disk and returns it as a HashMap of SettingSections, themselves
86 * effectively a HashMap of key/value settings. If unsuccessful, outputs an error telling why it
87 * failed.
88 *
89 * @param gameId the id of the game to load it's settings.
90 * @param view The current view.
91 */
92 fun readCustomGameSettings(
93 gameId: String,
94 view: SettingsActivityView?
95 ): HashMap<String, SettingSection?> {
96 return readFile(getCustomGameSettingsFile(gameId), true, view)
97 }
98
99 /** 22 /**
100 * Saves a Settings HashMap to a given .ini file on disk. If unsuccessful, outputs an error 23 * Saves a Settings HashMap to a given .ini file on disk. If unsuccessful, outputs an error
101 * telling why it failed. 24 * telling why it failed.
102 * 25 *
103 * @param fileName The target filename without a path or extension. 26 * @param fileName The target filename without a path or extension.
104 * @param sections The HashMap containing the Settings we want to serialize.
105 * @param view The current view.
106 */ 27 */
107 fun saveFile( 28 fun saveFile(fileName: String) {
108 fileName: String,
109 sections: TreeMap<String, SettingSection>,
110 view: SettingsActivityView
111 ) {
112 val ini = getSettingsFile(fileName) 29 val ini = getSettingsFile(fileName)
113 try { 30 try {
114 val writer = Wini(ini) 31 val wini = Wini(ini)
115 val keySet: Set<String> = sections.keys 32 for (specificCategory in Settings.Category.values()) {
116 for (key in keySet) { 33 val categoryHeader = NativeConfig.getConfigHeader(specificCategory.ordinal)
117 val section = sections[key] 34 for (setting in Settings.settingsList) {
118 writeSection(writer, section!!) 35 if (setting.key!!.isEmpty()) continue
36
37 val settingCategoryHeader =
38 NativeConfig.getConfigHeader(setting.category.ordinal)
39 val iniSetting: String? = wini.get(categoryHeader, setting.key)
40 if (iniSetting != null || settingCategoryHeader == categoryHeader) {
41 wini.put(settingCategoryHeader, setting.key, setting.valueAsString)
42 }
43 }
119 } 44 }
120 writer.store() 45 wini.store()
121 } catch (e: IOException) { 46 } catch (e: IOException) {
122 Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.message) 47 Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.message)
123 view.showToastMessage( 48 val context = YuzuApplication.appContext
124 YuzuApplication.appContext 49 Toast.makeText(
125 .getString(R.string.error_saving, fileName, e.message), 50 context,
126 false 51 context.getString(R.string.error_saving, fileName, e.message),
127 ) 52 Toast.LENGTH_SHORT
128 } 53 ).show()
129 }
130
131 fun saveCustomGameSettings(gameId: String?, sections: HashMap<String, SettingSection?>) {
132 val sortedSections: Set<String> = TreeSet(sections.keys)
133 for (sectionKey in sortedSections) {
134 val section = sections[sectionKey]
135 val settings = section!!.settings
136 val sortedKeySet: Set<String> = TreeSet(settings.keys)
137 for (settingKey in sortedKeySet) {
138 val setting = settings[settingKey]
139 NativeLibrary.setUserSetting(
140 gameId,
141 mapSectionNameFromIni(
142 section.name
143 ),
144 setting!!.key,
145 setting.valueAsString
146 )
147 }
148 }
149 }
150
151 private fun mapSectionNameFromIni(generalSectionName: String): String? {
152 return if (sectionsMap.getForward(generalSectionName) != null) {
153 sectionsMap.getForward(generalSectionName)
154 } else {
155 generalSectionName
156 }
157 }
158
159 private fun mapSectionNameToIni(generalSectionName: String): String {
160 return if (sectionsMap.getBackward(generalSectionName) != null) {
161 sectionsMap.getBackward(generalSectionName).toString()
162 } else {
163 generalSectionName
164 }
165 }
166
167 fun getSettingsFile(fileName: String): File {
168 return File(
169 DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini"
170 )
171 }
172
173 private fun getCustomGameSettingsFile(gameId: String): File {
174 return File(DirectoryInitialization.userDirectory + "/GameSettings/" + gameId + ".ini")
175 }
176
177 private fun sectionFromLine(line: String, isCustomGame: Boolean): SettingSection {
178 var sectionName: String = line.substring(1, line.length - 1)
179 if (isCustomGame) {
180 sectionName = mapSectionNameToIni(sectionName)
181 } 54 }
182 return SettingSection(sectionName)
183 } 55 }
184 56
185 /** 57 fun getSettingsFile(fileName: String): File =
186 * For a line of text, determines what type of data is being represented, and returns 58 File(DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini")
187 * a Setting object containing this data.
188 *
189 * @param line The line of text being parsed.
190 * @return A typed Setting containing the key/value contained in the line.
191 */
192 private fun settingFromLine(line: String): AbstractSetting? {
193 val splitLine = line.split("=".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
194 if (splitLine.size != 2) {
195 return null
196 }
197 val key = splitLine[0].trim { it <= ' ' }
198 val value = splitLine[1].trim { it <= ' ' }
199 if (value.isEmpty()) {
200 return null
201 }
202
203 val booleanSetting = BooleanSetting.from(key)
204 if (booleanSetting != null) {
205 booleanSetting.boolean = value.toBoolean()
206 return booleanSetting
207 }
208
209 val intSetting = IntSetting.from(key)
210 if (intSetting != null) {
211 intSetting.int = value.toInt()
212 return intSetting
213 }
214
215 val floatSetting = FloatSetting.from(key)
216 if (floatSetting != null) {
217 floatSetting.float = value.toFloat()
218 return floatSetting
219 }
220
221 val stringSetting = StringSetting.from(key)
222 if (stringSetting != null) {
223 stringSetting.string = value
224 return stringSetting
225 }
226
227 return null
228 }
229
230 /**
231 * Writes the contents of a Section HashMap to disk.
232 *
233 * @param parser A Wini pointed at a file on disk.
234 * @param section A section containing settings to be written to the file.
235 */
236 private fun writeSection(parser: Wini, section: SettingSection) {
237 // Write the section header.
238 val header = section.name
239
240 // Write this section's values.
241 val settings = section.settings
242 val keySet: Set<String> = settings.keys
243 for (key in keySet) {
244 val setting = settings[key]
245 parser.put(header, setting!!.key, setting.valueAsString)
246 }
247
248 BooleanSetting.values().forEach {
249 if (!keySet.contains(it.key)) {
250 parser.put(header, it.key, it.valueAsString)
251 }
252 }
253 IntSetting.values().forEach {
254 if (!keySet.contains(it.key)) {
255 parser.put(header, it.key, it.valueAsString)
256 }
257 }
258 StringSetting.values().forEach {
259 if (!keySet.contains(it.key)) {
260 parser.put(header, it.key, it.valueAsString)
261 }
262 }
263 }
264} 59}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
index aaf3a0ec1..d8dbf1f45 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
@@ -39,7 +39,6 @@ import org.yuzu.yuzu_emu.activities.EmulationActivity
39import org.yuzu.yuzu_emu.databinding.ActivityMainBinding 39import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
40import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding 40import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
41import org.yuzu.yuzu_emu.features.settings.model.Settings 41import org.yuzu.yuzu_emu.features.settings.model.Settings
42import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
43import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity 42import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
44import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 43import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
45import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment 44import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
@@ -54,7 +53,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
54 53
55 private val homeViewModel: HomeViewModel by viewModels() 54 private val homeViewModel: HomeViewModel by viewModels()
56 private val gamesViewModel: GamesViewModel by viewModels() 55 private val gamesViewModel: GamesViewModel by viewModels()
57 private val settingsViewModel: SettingsViewModel by viewModels()
58 56
59 override var themeId: Int = 0 57 override var themeId: Int = 0
60 58
@@ -62,8 +60,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
62 val splashScreen = installSplashScreen() 60 val splashScreen = installSplashScreen()
63 splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady } 61 splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady }
64 62
65 settingsViewModel.settings.loadSettings()
66
67 ThemeHelper.setTheme(this) 63 ThemeHelper.setTheme(this)
68 64
69 super.onCreate(savedInstanceState) 65 super.onCreate(savedInstanceState)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BiMap.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BiMap.kt
deleted file mode 100644
index 9cfda74ee..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BiMap.kt
+++ /dev/null
@@ -1,25 +0,0 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.utils
5
6class BiMap<K, V> {
7 private val forward: MutableMap<K, V> = HashMap()
8 private val backward: MutableMap<V, K> = HashMap()
9
10 @Synchronized
11 fun add(key: K, value: V) {
12 forward[key] = value
13 backward[value] = key
14 }
15
16 @Synchronized
17 fun getForward(key: K): V? {
18 return forward[key]
19 }
20
21 @Synchronized
22 fun getBackward(key: V): K? {
23 return backward[key]
24 }
25}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt
new file mode 100644
index 000000000..d4d981f9e
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt
@@ -0,0 +1,31 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.utils
5
6object NativeConfig {
7 external fun getBoolean(key: String, getDefault: Boolean): Boolean
8 external fun setBoolean(key: String, value: Boolean)
9
10 external fun getByte(key: String, getDefault: Boolean): Byte
11 external fun setByte(key: String, value: Byte)
12
13 external fun getShort(key: String, getDefault: Boolean): Short
14 external fun setShort(key: String, value: Short)
15
16 external fun getInt(key: String, getDefault: Boolean): Int
17 external fun setInt(key: String, value: Int)
18
19 external fun getFloat(key: String, getDefault: Boolean): Float
20 external fun setFloat(key: String, value: Float)
21
22 external fun getLong(key: String, getDefault: Boolean): Long
23 external fun setLong(key: String, value: Long)
24
25 external fun getString(key: String, getDefault: Boolean): String
26 external fun setString(key: String, value: String)
27
28 external fun getIsRuntimeModifiable(key: String): Boolean
29
30 external fun getConfigHeader(category: Int): String
31}
diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt
index e2ed08e9f..e15d1480b 100644
--- a/src/android/app/src/main/jni/CMakeLists.txt
+++ b/src/android/app/src/main/jni/CMakeLists.txt
@@ -14,6 +14,8 @@ add_library(yuzu-android SHARED
14 id_cache.cpp 14 id_cache.cpp
15 id_cache.h 15 id_cache.h
16 native.cpp 16 native.cpp
17 native_config.cpp
18 uisettings.cpp
17) 19)
18 20
19set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) 21set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp
index 9de9bd93e..34b425cb4 100644
--- a/src/android/app/src/main/jni/config.cpp
+++ b/src/android/app/src/main/jni/config.cpp
@@ -16,18 +16,20 @@
16#include "input_common/main.h" 16#include "input_common/main.h"
17#include "jni/config.h" 17#include "jni/config.h"
18#include "jni/default_ini.h" 18#include "jni/default_ini.h"
19#include "uisettings.h"
19 20
20namespace FS = Common::FS; 21namespace FS = Common::FS;
21 22
22Config::Config(std::optional<std::filesystem::path> config_path) 23Config::Config(const std::string& config_name, ConfigType config_type)
23 : config_loc{config_path.value_or(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "config.ini")}, 24 : type(config_type), global{config_type == ConfigType::GlobalConfig} {
24 config{std::make_unique<INIReader>(FS::PathToUTF8String(config_loc))} { 25 Initialize(config_name);
25 Reload();
26} 26}
27 27
28Config::~Config() = default; 28Config::~Config() = default;
29 29
30bool Config::LoadINI(const std::string& default_contents, bool retry) { 30bool Config::LoadINI(const std::string& default_contents, bool retry) {
31 void(FS::CreateParentDir(config_loc));
32 config = std::make_unique<INIReader>(FS::PathToUTF8String(config_loc));
31 const auto config_loc_str = FS::PathToUTF8String(config_loc); 33 const auto config_loc_str = FS::PathToUTF8String(config_loc);
32 if (config->ParseError() < 0) { 34 if (config->ParseError() < 0) {
33 if (retry) { 35 if (retry) {
@@ -301,9 +303,28 @@ void Config::ReadValues() {
301 303
302 // Network 304 // Network
303 ReadSetting("Network", Settings::values.network_interface); 305 ReadSetting("Network", Settings::values.network_interface);
306
307 // Android
308 ReadSetting("Android", AndroidSettings::values.picture_in_picture);
309 ReadSetting("Android", AndroidSettings::values.screen_layout);
304} 310}
305 311
306void Config::Reload() { 312void Config::Initialize(const std::string& config_name) {
313 const auto fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir);
314 const auto config_file = fmt::format("{}.ini", config_name);
315
316 switch (type) {
317 case ConfigType::GlobalConfig:
318 config_loc = FS::PathToUTF8String(fs_config_loc / config_file);
319 break;
320 case ConfigType::PerGameConfig:
321 config_loc = FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file));
322 break;
323 case ConfigType::InputProfile:
324 config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file);
325 LoadINI(DefaultINI::android_config_file);
326 return;
327 }
307 LoadINI(DefaultINI::android_config_file); 328 LoadINI(DefaultINI::android_config_file);
308 ReadValues(); 329 ReadValues();
309} 330}
diff --git a/src/android/app/src/main/jni/config.h b/src/android/app/src/main/jni/config.h
index 0d7d6e94d..e1e8f47ed 100644
--- a/src/android/app/src/main/jni/config.h
+++ b/src/android/app/src/main/jni/config.h
@@ -13,25 +13,35 @@
13class INIReader; 13class INIReader;
14 14
15class Config { 15class Config {
16 std::filesystem::path config_loc;
17 std::unique_ptr<INIReader> config;
18
19 bool LoadINI(const std::string& default_contents = "", bool retry = true); 16 bool LoadINI(const std::string& default_contents = "", bool retry = true);
20 void ReadValues();
21 17
22public: 18public:
23 explicit Config(std::optional<std::filesystem::path> config_path = std::nullopt); 19 enum class ConfigType {
20 GlobalConfig,
21 PerGameConfig,
22 InputProfile,
23 };
24
25 explicit Config(const std::string& config_name = "config",
26 ConfigType config_type = ConfigType::GlobalConfig);
24 ~Config(); 27 ~Config();
25 28
26 void Reload(); 29 void Initialize(const std::string& config_name);
27 30
28private: 31private:
29 /** 32 /**
30 * Applies a value read from the sdl2_config to a Setting. 33 * Applies a value read from the config to a Setting.
31 * 34 *
32 * @param group The name of the INI group 35 * @param group The name of the INI group
33 * @param setting The yuzu setting to modify 36 * @param setting The yuzu setting to modify
34 */ 37 */
35 template <typename Type, bool ranged> 38 template <typename Type, bool ranged>
36 void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting); 39 void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting);
40
41 void ReadValues();
42
43 const ConfigType type;
44 std::unique_ptr<INIReader> config;
45 std::string config_loc;
46 const bool global;
37}; 47};
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index 7e17833a0..b2adfdeda 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -824,34 +824,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass cl
824 Config{}; 824 Config{};
825} 825}
826 826
827jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting(JNIEnv* env, jclass clazz,
828 jstring j_game_id, jstring j_section,
829 jstring j_key) {
830 std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
831 std::string_view section = env->GetStringUTFChars(j_section, 0);
832 std::string_view key = env->GetStringUTFChars(j_key, 0);
833
834 env->ReleaseStringUTFChars(j_game_id, game_id.data());
835 env->ReleaseStringUTFChars(j_section, section.data());
836 env->ReleaseStringUTFChars(j_key, key.data());
837
838 return env->NewStringUTF("");
839}
840
841void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting(JNIEnv* env, jclass clazz,
842 jstring j_game_id, jstring j_section,
843 jstring j_key, jstring j_value) {
844 std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
845 std::string_view section = env->GetStringUTFChars(j_section, 0);
846 std::string_view key = env->GetStringUTFChars(j_key, 0);
847 std::string_view value = env->GetStringUTFChars(j_value, 0);
848
849 env->ReleaseStringUTFChars(j_game_id, game_id.data());
850 env->ReleaseStringUTFChars(j_section, section.data());
851 env->ReleaseStringUTFChars(j_key, key.data());
852 env->ReleaseStringUTFChars(j_value, value.data());
853}
854
855void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz, 827void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz,
856 jstring j_game_id) { 828 jstring j_game_id) {
857 std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); 829 std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
diff --git a/src/android/app/src/main/jni/native_config.cpp b/src/android/app/src/main/jni/native_config.cpp
new file mode 100644
index 000000000..6123b3d08
--- /dev/null
+++ b/src/android/app/src/main/jni/native_config.cpp
@@ -0,0 +1,224 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <string>
5
6#include <jni.h>
7
8#include "common/logging/log.h"
9#include "common/settings.h"
10#include "jni/android_common/android_common.h"
11#include "jni/config.h"
12#include "uisettings.h"
13
14template <typename T>
15Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
16 auto key = GetJString(env, jkey);
17 auto basicSetting = Settings::values.linkage.by_key[key];
18 auto basicAndroidSetting = AndroidSettings::values.linkage.by_key[key];
19 if (basicSetting != 0) {
20 return static_cast<Settings::Setting<T>*>(basicSetting);
21 }
22 if (basicAndroidSetting != 0) {
23 return static_cast<Settings::Setting<T>*>(basicAndroidSetting);
24 }
25 LOG_ERROR(Frontend, "[Android Native] Could not find setting - {}", key);
26 return nullptr;
27}
28
29extern "C" {
30
31jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj,
32 jstring jkey, jboolean getDefault) {
33 auto setting = getSetting<bool>(env, jkey);
34 if (setting == nullptr) {
35 return false;
36 }
37 setting->SetGlobal(true);
38
39 if (static_cast<bool>(getDefault)) {
40 return setting->GetDefault();
41 }
42
43 return setting->GetValue();
44}
45
46void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setBoolean(JNIEnv* env, jobject obj, jstring jkey,
47 jboolean value) {
48 auto setting = getSetting<bool>(env, jkey);
49 if (setting == nullptr) {
50 return;
51 }
52 setting->SetGlobal(true);
53 setting->SetValue(static_cast<bool>(value));
54}
55
56jbyte Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getByte(JNIEnv* env, jobject obj, jstring jkey,
57 jboolean getDefault) {
58 auto setting = getSetting<u8>(env, jkey);
59 if (setting == nullptr) {
60 return -1;
61 }
62 setting->SetGlobal(true);
63
64 if (static_cast<bool>(getDefault)) {
65 return setting->GetDefault();
66 }
67
68 return setting->GetValue();
69}
70
71void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setByte(JNIEnv* env, jobject obj, jstring jkey,
72 jbyte value) {
73 auto setting = getSetting<u8>(env, jkey);
74 if (setting == nullptr) {
75 return;
76 }
77 setting->SetGlobal(true);
78 setting->SetValue(value);
79}
80
81jshort Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getShort(JNIEnv* env, jobject obj, jstring jkey,
82 jboolean getDefault) {
83 auto setting = getSetting<u16>(env, jkey);
84 if (setting == nullptr) {
85 return -1;
86 }
87 setting->SetGlobal(true);
88
89 if (static_cast<bool>(getDefault)) {
90 return setting->GetDefault();
91 }
92
93 return setting->GetValue();
94}
95
96void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setShort(JNIEnv* env, jobject obj, jstring jkey,
97 jshort value) {
98 auto setting = getSetting<u16>(env, jkey);
99 if (setting == nullptr) {
100 return;
101 }
102 setting->SetGlobal(true);
103 setting->SetValue(value);
104}
105
106jint Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getInt(JNIEnv* env, jobject obj, jstring jkey,
107 jboolean getDefault) {
108 auto setting = getSetting<int>(env, jkey);
109 if (setting == nullptr) {
110 return -1;
111 }
112 setting->SetGlobal(true);
113
114 if (static_cast<bool>(getDefault)) {
115 return setting->GetDefault();
116 }
117
118 return setting->GetValue();
119}
120
121void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setInt(JNIEnv* env, jobject obj, jstring jkey,
122 jint value) {
123 auto setting = getSetting<int>(env, jkey);
124 if (setting == nullptr) {
125 return;
126 }
127 setting->SetGlobal(true);
128 setting->SetValue(value);
129}
130
131jfloat Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getFloat(JNIEnv* env, jobject obj, jstring jkey,
132 jboolean getDefault) {
133 auto setting = getSetting<float>(env, jkey);
134 if (setting == nullptr) {
135 return -1;
136 }
137 setting->SetGlobal(true);
138
139 if (static_cast<bool>(getDefault)) {
140 return setting->GetDefault();
141 }
142
143 return setting->GetValue();
144}
145
146void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setFloat(JNIEnv* env, jobject obj, jstring jkey,
147 jfloat value) {
148 auto setting = getSetting<float>(env, jkey);
149 if (setting == nullptr) {
150 return;
151 }
152 setting->SetGlobal(true);
153 setting->SetValue(value);
154}
155
156jlong Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getLong(JNIEnv* env, jobject obj, jstring jkey,
157 jboolean getDefault) {
158 auto setting = getSetting<long>(env, jkey);
159 if (setting == nullptr) {
160 return -1;
161 }
162 setting->SetGlobal(true);
163
164 if (static_cast<bool>(getDefault)) {
165 return setting->GetDefault();
166 }
167
168 return setting->GetValue();
169}
170
171void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setLong(JNIEnv* env, jobject obj, jstring jkey,
172 jlong value) {
173 auto setting = getSetting<long>(env, jkey);
174 if (setting == nullptr) {
175 return;
176 }
177 setting->SetGlobal(true);
178 setting->SetValue(value);
179}
180
181jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getString(JNIEnv* env, jobject obj, jstring jkey,
182 jboolean getDefault) {
183 auto setting = getSetting<std::string>(env, jkey);
184 if (setting == nullptr) {
185 return ToJString(env, "");
186 }
187 setting->SetGlobal(true);
188
189 if (static_cast<bool>(getDefault)) {
190 return ToJString(env, setting->GetDefault());
191 }
192
193 return ToJString(env, setting->GetValue());
194}
195
196void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setString(JNIEnv* env, jobject obj, jstring jkey,
197 jstring value) {
198 auto setting = getSetting<std::string>(env, jkey);
199 if (setting == nullptr) {
200 return;
201 }
202
203 setting->SetGlobal(true);
204 setting->SetValue(GetJString(env, value));
205}
206
207jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsRuntimeModifiable(JNIEnv* env, jobject obj,
208 jstring jkey) {
209 auto key = GetJString(env, jkey);
210 auto setting = Settings::values.linkage.by_key[key];
211 if (setting != 0) {
212 return setting->RuntimeModfiable();
213 }
214 LOG_ERROR(Frontend, "[Android Native] Could not find setting - {}", key);
215 return true;
216}
217
218jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getConfigHeader(JNIEnv* env, jobject obj,
219 jint jcategory) {
220 auto category = static_cast<Settings::Category>(jcategory);
221 return ToJString(env, Settings::TranslateCategory(category));
222}
223
224} // extern "C"
diff --git a/src/android/app/src/main/jni/uisettings.cpp b/src/android/app/src/main/jni/uisettings.cpp
new file mode 100644
index 000000000..f2f0bad50
--- /dev/null
+++ b/src/android/app/src/main/jni/uisettings.cpp
@@ -0,0 +1,10 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "uisettings.h"
5
6namespace AndroidSettings {
7
8Values values;
9
10} // namespace AndroidSettings
diff --git a/src/android/app/src/main/jni/uisettings.h b/src/android/app/src/main/jni/uisettings.h
new file mode 100644
index 000000000..494654af7
--- /dev/null
+++ b/src/android/app/src/main/jni/uisettings.h
@@ -0,0 +1,29 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <common/settings_common.h>
7#include "common/common_types.h"
8#include "common/settings_setting.h"
9
10namespace AndroidSettings {
11
12struct Values {
13 Settings::Linkage linkage;
14
15 // Android
16 Settings::Setting<bool> picture_in_picture{linkage, true, "picture_in_picture",
17 Settings::Category::Android};
18 Settings::Setting<s32> screen_layout{linkage,
19 5,
20 "screen_layout",
21 Settings::Category::Android,
22 Settings::Specialization::Default,
23 true,
24 true};
25};
26
27extern Values values;
28
29} // namespace AndroidSettings
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index 200b99185..dc10159c9 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -243,10 +243,10 @@
243 <item>@string/cubeb</item> 243 <item>@string/cubeb</item>
244 <item>@string/string_null</item> 244 <item>@string/string_null</item>
245 </string-array> 245 </string-array>
246 <string-array name="outputEngineValues"> 246 <integer-array name="outputEngineValues">
247 <item>auto</item> 247 <item>0</item>
248 <item>cubeb</item> 248 <item>1</item>
249 <item>null</item> 249 <item>3</item>
250 </string-array> 250 </integer-array>
251 251
252</resources> 252</resources>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index de1b2909b..df76563fc 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -200,6 +200,7 @@
200 <string name="ini_saved">Saved settings</string> 200 <string name="ini_saved">Saved settings</string>
201 <string name="gameid_saved">Saved settings for %1$s</string> 201 <string name="gameid_saved">Saved settings for %1$s</string>
202 <string name="error_saving">Error saving %1$s.ini: %2$s</string> 202 <string name="error_saving">Error saving %1$s.ini: %2$s</string>
203 <string name="unimplemented_menu">Unimplemented Menu</string>
203 <string name="loading">Loading…</string> 204 <string name="loading">Loading…</string>
204 <string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string> 205 <string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string>
205 <string name="reset_to_default">Reset to default</string> 206 <string name="reset_to_default">Reset to default</string>
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 524056841..4ecaf550b 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -159,6 +159,8 @@ float Volume() {
159 159
160const char* TranslateCategory(Category category) { 160const char* TranslateCategory(Category category) {
161 switch (category) { 161 switch (category) {
162 case Category::Android:
163 return "Android";
162 case Category::Audio: 164 case Category::Audio:
163 return "Audio"; 165 return "Audio";
164 case Category::Core: 166 case Category::Core:
diff --git a/src/common/settings_common.cpp b/src/common/settings_common.cpp
index 137b65d5f..5960b78aa 100644
--- a/src/common/settings_common.cpp
+++ b/src/common/settings_common.cpp
@@ -14,6 +14,7 @@ BasicSetting::BasicSetting(Linkage& linkage, const std::string& name, enum Categ
14 : label{name}, category{category_}, id{linkage.count}, save{save_}, 14 : label{name}, category{category_}, id{linkage.count}, save{save_},
15 runtime_modifiable{runtime_modifiable_}, specialization{specialization_}, 15 runtime_modifiable{runtime_modifiable_}, specialization{specialization_},
16 other_setting{other_setting_} { 16 other_setting{other_setting_} {
17 linkage.by_key.insert({name, this});
17 linkage.by_category[category].push_back(this); 18 linkage.by_category[category].push_back(this);
18 linkage.count++; 19 linkage.count++;
19} 20}
diff --git a/src/common/settings_common.h b/src/common/settings_common.h
index 3082e0ce1..5b170dfd5 100644
--- a/src/common/settings_common.h
+++ b/src/common/settings_common.h
@@ -12,6 +12,7 @@
12namespace Settings { 12namespace Settings {
13 13
14enum class Category : u32 { 14enum class Category : u32 {
15 Android,
15 Audio, 16 Audio,
16 Core, 17 Core,
17 Cpu, 18 Cpu,
@@ -68,6 +69,7 @@ public:
68 explicit Linkage(u32 initial_count = 0); 69 explicit Linkage(u32 initial_count = 0);
69 ~Linkage(); 70 ~Linkage();
70 std::map<Category, std::vector<BasicSetting*>> by_category{}; 71 std::map<Category, std::vector<BasicSetting*>> by_category{};
72 std::map<std::string, Settings::BasicSetting*> by_key{};
71 std::vector<std::function<void()>> restore_functions{}; 73 std::vector<std::function<void()>> restore_functions{};
72 u32 count; 74 u32 count;
73}; 75};