diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.kt index d22431752b..dd1226d3f2 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.kt @@ -160,7 +160,7 @@ class CheatsActivity : AppCompatActivity(), PanelSlideListener { fun loadGameSpecificSettings(): Settings { val settings = Settings() - settings.loadSettings(gameId, revision, isWii) + settings.loadSettings(gameId!!, revision, isWii) return settings } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/SettingDisabledWarningFragment.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/SettingDisabledWarningFragment.kt index 737a931a85..538f4144e8 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/SettingDisabledWarningFragment.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/SettingDisabledWarningFragment.kt @@ -43,7 +43,7 @@ abstract class SettingDisabledWarningFragment( super.onResume() val activity = requireActivity() as CheatsActivity activity.loadGameSpecificSettings().use { - val cheatsEnabled = setting.getBoolean() + val cheatsEnabled = setting.boolean requireView().visibility = if (cheatsEnabled) View.GONE else View.VISIBLE } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputDeviceSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputDeviceSetting.java index 3ee7112007..f02de2e3f3 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputDeviceSetting.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputDeviceSetting.java @@ -46,8 +46,8 @@ public class InputDeviceSetting extends StringSingleChoiceSetting { String[] devices = ControllerInterface.getAllDeviceStrings(); - mChoices = devices; - mValues = devices; + setChoices(devices); + setValues(devices); } @Override diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileDialog.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileDialog.kt index 2779ee7473..acc3f605a9 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileDialog.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileDialog.kt @@ -2,7 +2,6 @@ package org.dolphinemu.dolphinemu.features.input.ui -import android.os.Build import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -14,6 +13,7 @@ import com.google.android.material.divider.MaterialDividerItemDecoration import org.dolphinemu.dolphinemu.R import org.dolphinemu.dolphinemu.databinding.DialogInputProfilesBinding import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag +import org.dolphinemu.dolphinemu.utils.SerializableHelper.serializable class ProfileDialog : BottomSheetDialogFragment() { private var presenter: ProfileDialogPresenter? = null @@ -22,11 +22,7 @@ class ProfileDialog : BottomSheetDialogFragment() { private val binding get() = _binding!! override fun onCreate(savedInstanceState: Bundle?) { - val menuTag: MenuTag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - requireArguments().getSerializable(KEY_MENU_TAG, MenuTag::class.java) as MenuTag - } else { - requireArguments().getSerializable(KEY_MENU_TAG) as MenuTag - } + val menuTag = requireArguments().serializable(KEY_MENU_TAG) presenter = ProfileDialogPresenter(this, menuTag) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractBooleanSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractBooleanSetting.java deleted file mode 100644 index 22d757d4c6..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractBooleanSetting.java +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model; - -import androidx.annotation.NonNull; - -public interface AbstractBooleanSetting extends AbstractSetting -{ - boolean getBoolean(); - - void setBoolean(@NonNull Settings settings, boolean newValue); -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractBooleanSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractBooleanSetting.kt new file mode 100644 index 0000000000..6196597dac --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractBooleanSetting.kt @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model + +interface AbstractBooleanSetting : AbstractSetting { + val boolean: Boolean + + fun setBoolean(settings: Settings, newValue: Boolean) +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractFloatSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractFloatSetting.java deleted file mode 100644 index a432c7139b..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractFloatSetting.java +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model; - -import androidx.annotation.NonNull; - -public interface AbstractFloatSetting extends AbstractSetting -{ - float getFloat(); - - void setFloat(@NonNull Settings settings, float newValue); -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractFloatSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractFloatSetting.kt new file mode 100644 index 0000000000..7dd4c82c0c --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractFloatSetting.kt @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model + +interface AbstractFloatSetting : AbstractSetting { + val float: Float + + fun setFloat(settings: Settings, newValue: Float) +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractIntSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractIntSetting.java deleted file mode 100644 index dc868d55d9..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractIntSetting.java +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model; - -import androidx.annotation.NonNull; - -public interface AbstractIntSetting extends AbstractSetting -{ - int getInt(); - - void setInt(@NonNull Settings settings, int newValue); -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractIntSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractIntSetting.kt new file mode 100644 index 0000000000..f22077dd6a --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractIntSetting.kt @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model + +interface AbstractIntSetting : AbstractSetting { + val int: Int + + fun setInt(settings: Settings, newValue: Int) +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractSetting.java deleted file mode 100644 index 9ffaf20544..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractSetting.java +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model; - -import androidx.annotation.NonNull; - -public interface AbstractSetting -{ - boolean isOverridden(); - - boolean isRuntimeEditable(); - - boolean delete(@NonNull Settings settings); -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractSetting.kt new file mode 100644 index 0000000000..6afcfab2e4 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractSetting.kt @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model + +interface AbstractSetting { + val isOverridden: Boolean + val isRuntimeEditable: Boolean + + fun delete(settings: Settings): Boolean +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractStringSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractStringSetting.java deleted file mode 100644 index 9dd9384090..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractStringSetting.java +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model; - -import androidx.annotation.NonNull; - -public interface AbstractStringSetting extends AbstractSetting -{ - @NonNull - String getString(); - - void setString(@NonNull Settings settings, @NonNull String newValue); -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractStringSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractStringSetting.kt new file mode 100644 index 0000000000..7828dbd552 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AbstractStringSetting.kt @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model + +interface AbstractStringSetting : AbstractSetting { + val string: String + + fun setString(settings: Settings, newValue: String) +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AdHocBooleanSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AdHocBooleanSetting.java deleted file mode 100644 index c12bc51ca0..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AdHocBooleanSetting.java +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model; - -import androidx.annotation.NonNull; - -public class AdHocBooleanSetting implements AbstractBooleanSetting -{ - private final String mFile; - private final String mSection; - private final String mKey; - private final boolean mDefaultValue; - - public AdHocBooleanSetting(String file, String section, String key, boolean defaultValue) - { - mFile = file; - mSection = section; - mKey = key; - mDefaultValue = defaultValue; - - if (!NativeConfig.isSettingSaveable(file, section, key)) - { - throw new IllegalArgumentException("File/section/key is unknown or legacy"); - } - } - - @Override - public boolean isOverridden() - { - return NativeConfig.isOverridden(mFile, mSection, mKey); - } - - @Override - public boolean isRuntimeEditable() - { - return true; - } - - @Override - public boolean delete(@NonNull Settings settings) - { - return NativeConfig.deleteKey(settings.getWriteLayer(), mFile, mSection, mKey); - } - - @Override - public boolean getBoolean() - { - return NativeConfig.getBoolean(NativeConfig.LAYER_ACTIVE, mFile, mSection, mKey, mDefaultValue); - } - - @Override - public void setBoolean(@NonNull Settings settings, boolean newValue) - { - NativeConfig.setBoolean(settings.getWriteLayer(), mFile, mSection, mKey, newValue); - } - - public static boolean getBooleanGlobal(String file, String section, String key, - boolean defaultValue) - { - return NativeConfig.getBoolean(NativeConfig.LAYER_ACTIVE, file, section, key, defaultValue); - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AdHocBooleanSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AdHocBooleanSetting.kt new file mode 100644 index 0000000000..f62ead3251 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AdHocBooleanSetting.kt @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model + +class AdHocBooleanSetting( + private val file: String, + private val section: String, + private val key: String, + private val defaultValue: Boolean +) : AbstractBooleanSetting { + init { + require( + NativeConfig.isSettingSaveable( + file, + section, + key + ) + ) { "File/section/key is unknown or legacy" } + } + + override val isOverridden: Boolean + get() = NativeConfig.isOverridden(file, section, key) + + override val isRuntimeEditable: Boolean = true + + override fun delete(settings: Settings): Boolean { + return NativeConfig.deleteKey(settings.writeLayer, file, section, key) + } + + override val boolean: Boolean + get() = NativeConfig.getBoolean(NativeConfig.LAYER_ACTIVE, file, section, key, defaultValue) + + override fun setBoolean(settings: Settings, newValue: Boolean) { + NativeConfig.setBoolean(settings.writeLayer, file, section, key, newValue) + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AdHocStringSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AdHocStringSetting.java deleted file mode 100644 index fdba08af71..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AdHocStringSetting.java +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model; - -import androidx.annotation.NonNull; - -public class AdHocStringSetting implements AbstractStringSetting -{ - private final String mFile; - private final String mSection; - private final String mKey; - private final String mDefaultValue; - - public AdHocStringSetting(String file, String section, String key, String defaultValue) - { - mFile = file; - mSection = section; - mKey = key; - mDefaultValue = defaultValue; - - if (!NativeConfig.isSettingSaveable(file, section, key)) - { - throw new IllegalArgumentException("File/section/key is unknown or legacy"); - } - } - - @Override - public boolean isOverridden() - { - return NativeConfig.isOverridden(mFile, mSection, mKey); - } - - @Override - public boolean isRuntimeEditable() - { - return true; - } - - @Override - public boolean delete(@NonNull Settings settings) - { - return NativeConfig.deleteKey(settings.getWriteLayer(), mFile, mSection, mKey); - } - - @NonNull @Override - public String getString() - { - return NativeConfig.getString(NativeConfig.LAYER_ACTIVE, mFile, mSection, mKey, mDefaultValue); - } - - @Override - public void setString(@NonNull Settings settings, @NonNull String newValue) - { - NativeConfig.setString(settings.getWriteLayer(), mFile, mSection, mKey, newValue); - } - - public static String getStringGlobal(String file, String section, String key, String defaultValue) - { - return NativeConfig.getString(NativeConfig.LAYER_ACTIVE, file, section, key, defaultValue); - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AdHocStringSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AdHocStringSetting.kt new file mode 100644 index 0000000000..fd416d8905 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/AdHocStringSetting.kt @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model + +class AdHocStringSetting( + private val file: String, + private val section: String, + private val key: String, + private val defaultValue: String +) : AbstractStringSetting { + init { + require( + NativeConfig.isSettingSaveable( + file, + section, + key + ) + ) { "File/section/key is unknown or legacy" } + } + + override val isOverridden: Boolean + get() = NativeConfig.isOverridden(file, section, key) + + override val isRuntimeEditable: Boolean = true + + override fun delete(settings: Settings): Boolean { + return NativeConfig.deleteKey(settings.writeLayer, file, section, key) + } + + override val string: String + get() = NativeConfig.getString(NativeConfig.LAYER_ACTIVE, file, section, key, defaultValue) + + override fun setString(settings: Settings, newValue: String) { + NativeConfig.setString(settings.writeLayer, file, section, key, newValue) + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.java deleted file mode 100644 index 581a7a9aab..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.java +++ /dev/null @@ -1,371 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model; - -import androidx.annotation.NonNull; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -public enum BooleanSetting implements AbstractBooleanSetting -{ - // These entries have the same names and order as in C++, just for consistency. - - MAIN_SKIP_IPL(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "SkipIPL", true), - MAIN_DSP_HLE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "DSPHLE", true), - MAIN_FASTMEM(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "Fastmem", true), - MAIN_CPU_THREAD(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "CPUThread", true), - MAIN_SYNC_ON_SKIP_IDLE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "SyncOnSkipIdle", true), - MAIN_ENABLE_CHEATS(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "EnableCheats", false), - MAIN_OVERRIDE_REGION_SETTINGS(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, - "OverrideRegionSettings", false), - MAIN_AUDIO_STRETCH(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "AudioStretch", false), - MAIN_BBA_XLINK_CHAT_OSD(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "BBA_XLINK_CHAT_OSD", - false), - MAIN_ADAPTER_RUMBLE_0(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "AdapterRumble0", true), - MAIN_ADAPTER_RUMBLE_1(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "AdapterRumble1", true), - MAIN_ADAPTER_RUMBLE_2(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "AdapterRumble2", true), - MAIN_ADAPTER_RUMBLE_3(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "AdapterRumble3", true), - MAIN_SIMULATE_KONGA_0(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "SimulateKonga0", false), - MAIN_SIMULATE_KONGA_1(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "SimulateKonga1", false), - MAIN_SIMULATE_KONGA_2(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "SimulateKonga2", false), - MAIN_SIMULATE_KONGA_3(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "SimulateKonga3", false), - MAIN_WII_SD_CARD(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "WiiSDCard", true), - MAIN_WII_SD_CARD_ENABLE_FOLDER_SYNC(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, - "WiiSDCardEnableFolderSync", false), - MAIN_WIIMOTE_CONTINUOUS_SCANNING(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, - "WiimoteContinuousScanning", false), - MAIN_WIIMOTE_ENABLE_SPEAKER(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, - "WiimoteEnableSpeaker", false), - MAIN_MMU(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "MMU", false), - MAIN_PAUSE_ON_PANIC(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "PauseOnPanic", false), - MAIN_ACCURATE_CPU_CACHE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "AccurateCPUCache", - false), - MAIN_SYNC_GPU(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "SyncGPU", false), - MAIN_FAST_DISC_SPEED(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "FastDiscSpeed", - false), - MAIN_OVERCLOCK_ENABLE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "OverclockEnable", false), - MAIN_RAM_OVERRIDE_ENABLE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "RAMOverrideEnable", - false), - MAIN_CUSTOM_RTC_ENABLE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "EnableCustomRTC", - false), - MAIN_AUTO_DISC_CHANGE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "AutoDiscChange", false), - MAIN_ALLOW_SD_WRITES(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "WiiSDCardAllowWrites", - true), - MAIN_ENABLE_SAVESTATES(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "EnableSaveStates", - false), - - MAIN_DSP_JIT(Settings.FILE_DOLPHIN, Settings.SECTION_INI_DSP, "EnableJIT", true), - - MAIN_EXPAND_TO_CUTOUT_AREA(Settings.FILE_DOLPHIN, Settings.SECTION_INI_INTERFACE, - "ExpandToCutoutArea", false), - MAIN_USE_PANIC_HANDLERS(Settings.FILE_DOLPHIN, Settings.SECTION_INI_INTERFACE, - "UsePanicHandlers", true), - MAIN_OSD_MESSAGES(Settings.FILE_DOLPHIN, Settings.SECTION_INI_INTERFACE, - "OnScreenDisplayMessages", true), - - MAIN_ANALYTICS_ENABLED(Settings.FILE_DOLPHIN, Settings.SECTION_ANALYTICS, "Enabled", false), - MAIN_ANALYTICS_PERMISSION_ASKED(Settings.FILE_DOLPHIN, Settings.SECTION_ANALYTICS, - "PermissionAsked", false), - - MAIN_RECURSIVE_ISO_PATHS(Settings.FILE_DOLPHIN, Settings.SECTION_INI_GENERAL, - "RecursiveISOPaths", false), - MAIN_USE_GAME_COVERS(Settings.FILE_DOLPHIN, Settings.SECTION_INI_GENERAL, - "UseGameCovers", true), - - MAIN_DEBUG_JIT_OFF(Settings.FILE_DOLPHIN, Settings.SECTION_DEBUG, "JitOff", false), - MAIN_DEBUG_JIT_LOAD_STORE_OFF(Settings.FILE_DOLPHIN, Settings.SECTION_DEBUG, "JitLoadStoreOff", - false), - MAIN_DEBUG_JIT_LOAD_STORE_FLOATING_OFF(Settings.FILE_DOLPHIN, Settings.SECTION_DEBUG, - "JitLoadStoreFloatingOff", false), - MAIN_DEBUG_JIT_LOAD_STORE_PAIRED_OFF(Settings.FILE_DOLPHIN, Settings.SECTION_DEBUG, - "JitLoadStorePairedOff", false), - MAIN_DEBUG_JIT_FLOATING_POINT_OFF(Settings.FILE_DOLPHIN, Settings.SECTION_DEBUG, - "JitFloatingPointOff", false), - MAIN_DEBUG_JIT_INTEGER_OFF(Settings.FILE_DOLPHIN, Settings.SECTION_DEBUG, "JitIntegerOff", false), - MAIN_DEBUG_JIT_PAIRED_OFF(Settings.FILE_DOLPHIN, Settings.SECTION_DEBUG, "JitPairedOff", false), - MAIN_DEBUG_JIT_SYSTEM_REGISTERS_OFF(Settings.FILE_DOLPHIN, Settings.SECTION_DEBUG, - "JitSystemRegistersOff", false), - MAIN_DEBUG_JIT_BRANCH_OFF(Settings.FILE_DOLPHIN, Settings.SECTION_DEBUG, "JitBranchOff", false), - MAIN_DEBUG_JIT_REGISTER_CACHE_OFF(Settings.FILE_DOLPHIN, Settings.SECTION_DEBUG, - "JitRegisterCacheOff", false), - - MAIN_EMULATE_SKYLANDER_PORTAL(Settings.FILE_DOLPHIN, Settings.SECTION_EMULATED_USB_DEVICES, - "EmulateSkylanderPortal", false), - - MAIN_SHOW_GAME_TITLES(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, - "ShowGameTitles", true), - MAIN_USE_BLACK_BACKGROUNDS(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, - "UseBlackBackgrounds", false), - MAIN_JOYSTICK_REL_CENTER(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, - "JoystickRelCenter", true), - MAIN_SHOW_INPUT_OVERLAY(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, - "ShowInputOverlay", true), - MAIN_IR_ALWAYS_RECENTER(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, - "IRAlwaysRecenter", false), - - MAIN_BUTTON_TOGGLE_GC_0(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleGCButtonA", true), - MAIN_BUTTON_TOGGLE_GC_1(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleGCButtonB", true), - MAIN_BUTTON_TOGGLE_GC_2(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleGCButtonX", true), - MAIN_BUTTON_TOGGLE_GC_3(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleGCButtonY", true), - MAIN_BUTTON_TOGGLE_GC_4(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleGCButtonZ", true), - MAIN_BUTTON_TOGGLE_GC_5(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleGCButtonStart", true), - MAIN_BUTTON_TOGGLE_GC_6(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleGCTriggerL", true), - MAIN_BUTTON_TOGGLE_GC_7(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleGCTriggerR", true), - MAIN_BUTTON_TOGGLE_GC_8(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleGCDPad", true), - MAIN_BUTTON_TOGGLE_GC_9(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleGCStickMain", true), - MAIN_BUTTON_TOGGLE_GC_10(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleGCStickC", true), - MAIN_BUTTON_TOGGLE_CLASSIC_0(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleClassicButtonA", true), - MAIN_BUTTON_TOGGLE_CLASSIC_1(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleClassicButtonB", true), - MAIN_BUTTON_TOGGLE_CLASSIC_2(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleClassicButtonX", true), - MAIN_BUTTON_TOGGLE_CLASSIC_3(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleClassicButtonY", true), - MAIN_BUTTON_TOGGLE_CLASSIC_4(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleClassicButtonPlus", true), - MAIN_BUTTON_TOGGLE_CLASSIC_5(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleClassicButtonMinus", true), - MAIN_BUTTON_TOGGLE_CLASSIC_6(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleClassicButtonHome", true), - MAIN_BUTTON_TOGGLE_CLASSIC_7(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleClassicTriggerL", true), - MAIN_BUTTON_TOGGLE_CLASSIC_8(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleClassicTriggerR", true), - MAIN_BUTTON_TOGGLE_CLASSIC_9(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleClassicButtonZL", true), - MAIN_BUTTON_TOGGLE_CLASSIC_10(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleClassicButtonZR", true), - MAIN_BUTTON_TOGGLE_CLASSIC_11(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleClassicDPad", true), - MAIN_BUTTON_TOGGLE_CLASSIC_12(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleClassicStickLeft", true), - MAIN_BUTTON_TOGGLE_CLASSIC_13(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleClassicStickRight", true), - MAIN_BUTTON_TOGGLE_WII_0(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleWiimoteButtonA", true), - MAIN_BUTTON_TOGGLE_WII_1(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleWiimoteButtonB", true), - MAIN_BUTTON_TOGGLE_WII_2(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleWiimoteButton1", true), - MAIN_BUTTON_TOGGLE_WII_3(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleWiimoteButton2", true), - MAIN_BUTTON_TOGGLE_WII_4(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleWiimoteButtonPlus", true), - MAIN_BUTTON_TOGGLE_WII_5(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleWiimoteButtonMinus", true), - MAIN_BUTTON_TOGGLE_WII_6(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleWiimoteButtonHome", true), - MAIN_BUTTON_TOGGLE_WII_7(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleWiimoteDPad", true), - MAIN_BUTTON_TOGGLE_WII_8(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleNunchukC", true), - MAIN_BUTTON_TOGGLE_WII_9(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleNunchukZ", true), - MAIN_BUTTON_TOGGLE_WII_10(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "ButtonToggleNunchukStick", true), - - SYSCONF_SCREENSAVER(Settings.FILE_SYSCONF, "IPL", "SSV", false), - SYSCONF_WIDESCREEN(Settings.FILE_SYSCONF, "IPL", "AR", true), - SYSCONF_PROGRESSIVE_SCAN(Settings.FILE_SYSCONF, "IPL", "PGS", true), - SYSCONF_PAL60(Settings.FILE_SYSCONF, "IPL", "E60", true), - - SYSCONF_WIIMOTE_MOTOR(Settings.FILE_SYSCONF, "BT", "MOT", true), - - GFX_WIDESCREEN_HACK(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "wideScreenHack", false), - GFX_CROP(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "Crop", false), - GFX_SHOW_FPS(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "ShowFPS", false), - GFX_SHOW_FTIMES(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "ShowFTimes", false), - GFX_SHOW_VPS(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "ShowVPS", false), - GFX_SHOW_VTIMES(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "ShowVTimes", false), - GFX_SHOW_GRAPHS(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "ShowGraphs", false), - GFX_SHOW_SPEED(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "ShowSpeed", false), - GFX_SHOW_SPEED_COLORS(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "ShowSpeedColors", true), - GFX_LOG_RENDER_TIME_TO_FILE(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, - "LogRenderTimeToFile", false), - GFX_OVERLAY_STATS(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "OverlayStats", false), - GFX_DUMP_TEXTURES(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "DumpTextures", false), - GFX_DUMP_MIP_TEXTURES(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "DumpMipTextures", false), - GFX_DUMP_BASE_TEXTURES(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "DumpBaseTextures", - false), - GFX_HIRES_TEXTURES(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "HiresTextures", false), - GFX_CACHE_HIRES_TEXTURES(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "CacheHiresTextures", - false), - GFX_DUMP_EFB_TARGET(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "DumpEFBTarget", false), - GFX_DUMP_XFB_TARGET(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "DumpXFBTarget", false), - GFX_INTERNAL_RESOLUTION_FRAME_DUMPS(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, - "InternalResolutionFrameDumps", false), - GFX_ENABLE_GPU_TEXTURE_DECODING(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, - "EnableGPUTextureDecoding", false), - GFX_ENABLE_PIXEL_LIGHTING(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, - "EnablePixelLighting", false), - GFX_FAST_DEPTH_CALC(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "FastDepthCalc", true), - GFX_TEXFMT_OVERLAY_ENABLE(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "TexFmtOverlayEnable", - false), - GFX_ENABLE_WIREFRAME(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "WireFrame", false), - GFX_DISABLE_FOG(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "DisableFog", false), - GFX_ENABLE_VALIDATION_LAYER(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, - "EnableValidationLayer", false), - GFX_BACKEND_MULTITHREADING(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, - "BackendMultithreading", true), - GFX_WAIT_FOR_SHADERS_BEFORE_STARTING(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, - "WaitForShadersBeforeStarting", false), - GFX_SAVE_TEXTURE_CACHE_TO_STATE(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, - "SaveTextureCacheToState", true), - GFX_PREFER_VS_FOR_LINE_POINT_EXPANSION(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, - "PreferVSForLinePointExpansion", false), - GFX_CPU_CULL(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "CPUCull", false), - GFX_MODS_ENABLE(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "EnableMods", false), - - GFX_ENHANCE_FORCE_TRUE_COLOR(Settings.FILE_GFX, Settings.SECTION_GFX_ENHANCEMENTS, - "ForceTrueColor", true), - GFX_ENHANCE_DISABLE_COPY_FILTER(Settings.FILE_GFX, Settings.SECTION_GFX_ENHANCEMENTS, - "DisableCopyFilter", true), - GFX_ENHANCE_ARBITRARY_MIPMAP_DETECTION(Settings.FILE_GFX, Settings.SECTION_GFX_ENHANCEMENTS, - "ArbitraryMipmapDetection", true), - - GFX_STEREO_SWAP_EYES(Settings.FILE_GFX, Settings.SECTION_STEREOSCOPY, "StereoSwapEyes", false), - - GFX_HACK_EFB_ACCESS_ENABLE(Settings.FILE_GFX, Settings.SECTION_GFX_HACKS, "EFBAccessEnable", - true), - GFX_HACK_EFB_DEFER_INVALIDATION(Settings.FILE_GFX, Settings.SECTION_GFX_HACKS, - "EFBAccessDeferInvalidation", false), - GFX_HACK_BBOX_ENABLE(Settings.FILE_GFX, Settings.SECTION_GFX_HACKS, "BBoxEnable", false), - GFX_HACK_SKIP_EFB_COPY_TO_RAM(Settings.FILE_GFX, Settings.SECTION_GFX_HACKS, - "EFBToTextureEnable", true), - GFX_HACK_SKIP_XFB_COPY_TO_RAM(Settings.FILE_GFX, Settings.SECTION_GFX_HACKS, - "XFBToTextureEnable", true), - GFX_HACK_DISABLE_COPY_TO_VRAM(Settings.FILE_GFX, Settings.SECTION_GFX_HACKS, "DisableCopyToVRAM", - false), - GFX_HACK_DEFER_EFB_COPIES(Settings.FILE_GFX, Settings.SECTION_GFX_HACKS, "DeferEFBCopies", true), - GFX_HACK_IMMEDIATE_XFB(Settings.FILE_GFX, Settings.SECTION_GFX_HACKS, "ImmediateXFBEnable", - false), - GFX_HACK_SKIP_DUPLICATE_XFBS(Settings.FILE_GFX, Settings.SECTION_GFX_HACKS, "SkipDuplicateXFBs", - true), - GFX_HACK_COPY_EFB_SCALED(Settings.FILE_GFX, Settings.SECTION_GFX_HACKS, "EFBScaledCopy", true), - GFX_HACK_EFB_EMULATE_FORMAT_CHANGES(Settings.FILE_GFX, Settings.SECTION_GFX_HACKS, - "EFBEmulateFormatChanges", false), - GFX_HACK_VERTEX_ROUNDING(Settings.FILE_GFX, Settings.SECTION_GFX_HACKS, "VertexRounding", false), - GFX_HACK_VI_SKIP(Settings.FILE_GFX, Settings.SECTION_GFX_HACKS, "VISkip", false), - GFX_HACK_FAST_TEXTURE_SAMPLING(Settings.FILE_GFX, Settings.SECTION_GFX_HACKS, - "FastTextureSampling", true), - - LOGGER_WRITE_TO_FILE(Settings.FILE_LOGGER, Settings.SECTION_LOGGER_OPTIONS, "WriteToFile", false); - - private static final BooleanSetting[] NOT_RUNTIME_EDITABLE_ARRAY = new BooleanSetting[]{ - MAIN_DSP_HLE, - MAIN_CPU_THREAD, - MAIN_ENABLE_CHEATS, - MAIN_OVERRIDE_REGION_SETTINGS, - MAIN_MMU, - MAIN_PAUSE_ON_PANIC, - MAIN_ACCURATE_CPU_CACHE, - MAIN_RAM_OVERRIDE_ENABLE, - MAIN_CUSTOM_RTC_ENABLE, - MAIN_DSP_JIT, - MAIN_EMULATE_SKYLANDER_PORTAL, - }; - - private static final Set NOT_RUNTIME_EDITABLE = - new HashSet<>(Arrays.asList(NOT_RUNTIME_EDITABLE_ARRAY)); - - private final String mFile; - private final String mSection; - private final String mKey; - private final boolean mDefaultValue; - - BooleanSetting(String file, String section, String key, boolean defaultValue) - { - mFile = file; - mSection = section; - mKey = key; - mDefaultValue = defaultValue; - } - - @Override - public boolean isOverridden() - { - return NativeConfig.isOverridden(mFile, mSection, mKey); - } - - @Override - public boolean isRuntimeEditable() - { - if (mFile.equals(Settings.FILE_SYSCONF)) - return false; - - for (BooleanSetting setting : NOT_RUNTIME_EDITABLE) - { - if (setting == this) - return false; - } - - return NativeConfig.isSettingSaveable(mFile, mSection, mKey); - } - - @Override - public boolean delete(@NonNull Settings settings) - { - if (!NativeConfig.isSettingSaveable(mFile, mSection, mKey)) - { - throw new UnsupportedOperationException( - "Unsupported setting: " + mFile + ", " + mSection + ", " + mKey); - } - - return NativeConfig.deleteKey(settings.getWriteLayer(), mFile, mSection, mKey); - } - - @Override - public boolean getBoolean() - { - return NativeConfig.getBoolean(NativeConfig.LAYER_ACTIVE, mFile, mSection, mKey, mDefaultValue); - } - - @Override - public void setBoolean(@NonNull Settings settings, boolean newValue) - { - if (!NativeConfig.isSettingSaveable(mFile, mSection, mKey)) - { - throw new UnsupportedOperationException( - "Unsupported setting: " + mFile + ", " + mSection + ", " + mKey); - } - - NativeConfig.setBoolean(settings.getWriteLayer(), mFile, mSection, mKey, newValue); - } - - public void setBoolean(int layer, boolean newValue) - { - if (!NativeConfig.isSettingSaveable(mFile, mSection, mKey)) - { - throw new UnsupportedOperationException( - "Unsupported setting: " + mFile + ", " + mSection + ", " + mKey); - } - - NativeConfig.setBoolean(layer, mFile, mSection, mKey, newValue); - } - - public static BooleanSetting getSettingForAdapterRumble(int channel) - { - return new BooleanSetting[]{MAIN_ADAPTER_RUMBLE_0, MAIN_ADAPTER_RUMBLE_1, MAIN_ADAPTER_RUMBLE_2, - MAIN_ADAPTER_RUMBLE_3}[channel]; - } - - public static BooleanSetting getSettingForSimulateKonga(int channel) - { - return new BooleanSetting[]{MAIN_SIMULATE_KONGA_0, MAIN_SIMULATE_KONGA_1, MAIN_SIMULATE_KONGA_2, - MAIN_SIMULATE_KONGA_3}[channel]; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.kt new file mode 100644 index 0000000000..e59cb8a097 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.kt @@ -0,0 +1,747 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model + +import java.util.* + +enum class BooleanSetting( + private val file: String, + private val section: String, + private val key: String, + private val defaultValue: Boolean +) : AbstractBooleanSetting { + // These entries have the same names and order as in C++, just for consistency. + MAIN_SKIP_IPL(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "SkipIPL", true), + MAIN_DSP_HLE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "DSPHLE", true), + MAIN_FASTMEM(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "Fastmem", true), + MAIN_CPU_THREAD(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "CPUThread", true), + MAIN_SYNC_ON_SKIP_IDLE( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_CORE, + "SyncOnSkipIdle", + true + ), + MAIN_ENABLE_CHEATS(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "EnableCheats", false), + MAIN_OVERRIDE_REGION_SETTINGS( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_CORE, + "OverrideRegionSettings", + false + ), + MAIN_AUDIO_STRETCH(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "AudioStretch", false), + MAIN_BBA_XLINK_CHAT_OSD( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_CORE, + "BBA_XLINK_CHAT_OSD", + false + ), + MAIN_ADAPTER_RUMBLE_0(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "AdapterRumble0", true), + MAIN_ADAPTER_RUMBLE_1(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "AdapterRumble1", true), + MAIN_ADAPTER_RUMBLE_2(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "AdapterRumble2", true), + MAIN_ADAPTER_RUMBLE_3(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "AdapterRumble3", true), + MAIN_SIMULATE_KONGA_0( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_CORE, + "SimulateKonga0", + false + ), + MAIN_SIMULATE_KONGA_1( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_CORE, + "SimulateKonga1", + false + ), + MAIN_SIMULATE_KONGA_2( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_CORE, + "SimulateKonga2", + false + ), + MAIN_SIMULATE_KONGA_3( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_CORE, + "SimulateKonga3", + false + ), + MAIN_WII_SD_CARD(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "WiiSDCard", true), + MAIN_WII_SD_CARD_ENABLE_FOLDER_SYNC( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_CORE, + "WiiSDCardEnableFolderSync", + false + ), + MAIN_WIIMOTE_CONTINUOUS_SCANNING( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_CORE, + "WiimoteContinuousScanning", + false + ), + MAIN_WIIMOTE_ENABLE_SPEAKER( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_CORE, + "WiimoteEnableSpeaker", + false + ), + MAIN_MMU(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "MMU", false), + MAIN_PAUSE_ON_PANIC(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "PauseOnPanic", false), + MAIN_ACCURATE_CPU_CACHE( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_CORE, + "AccurateCPUCache", + false + ), + MAIN_SYNC_GPU(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "SyncGPU", false), + MAIN_FAST_DISC_SPEED(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "FastDiscSpeed", false), + MAIN_OVERCLOCK_ENABLE( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_CORE, + "OverclockEnable", + false + ), + MAIN_RAM_OVERRIDE_ENABLE( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_CORE, + "RAMOverrideEnable", + false + ), + MAIN_CUSTOM_RTC_ENABLE( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_CORE, + "EnableCustomRTC", + false + ), + MAIN_AUTO_DISC_CHANGE( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_CORE, + "AutoDiscChange", + false + ), + MAIN_ALLOW_SD_WRITES( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_CORE, + "WiiSDCardAllowWrites", + true + ), + MAIN_ENABLE_SAVESTATES( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_CORE, + "EnableSaveStates", + false + ), + MAIN_DSP_JIT(Settings.FILE_DOLPHIN, Settings.SECTION_INI_DSP, "EnableJIT", true), + MAIN_EXPAND_TO_CUTOUT_AREA( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_INTERFACE, + "ExpandToCutoutArea", + false + ), + MAIN_USE_PANIC_HANDLERS( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_INTERFACE, + "UsePanicHandlers", + true + ), + MAIN_OSD_MESSAGES( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_INTERFACE, + "OnScreenDisplayMessages", + true + ), + MAIN_ANALYTICS_ENABLED(Settings.FILE_DOLPHIN, Settings.SECTION_ANALYTICS, "Enabled", false), + MAIN_ANALYTICS_PERMISSION_ASKED( + Settings.FILE_DOLPHIN, + Settings.SECTION_ANALYTICS, + "PermissionAsked", + false + ), + MAIN_RECURSIVE_ISO_PATHS( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_GENERAL, + "RecursiveISOPaths", + false + ), + MAIN_USE_GAME_COVERS( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_GENERAL, + "UseGameCovers", + true + ), + MAIN_DEBUG_JIT_OFF(Settings.FILE_DOLPHIN, Settings.SECTION_DEBUG, "JitOff", false), + MAIN_DEBUG_JIT_LOAD_STORE_OFF( + Settings.FILE_DOLPHIN, + Settings.SECTION_DEBUG, + "JitLoadStoreOff", + false + ), + MAIN_DEBUG_JIT_LOAD_STORE_FLOATING_OFF( + Settings.FILE_DOLPHIN, + Settings.SECTION_DEBUG, + "JitLoadStoreFloatingOff", + false + ), + MAIN_DEBUG_JIT_LOAD_STORE_PAIRED_OFF( + Settings.FILE_DOLPHIN, + Settings.SECTION_DEBUG, + "JitLoadStorePairedOff", + false + ), + MAIN_DEBUG_JIT_FLOATING_POINT_OFF( + Settings.FILE_DOLPHIN, + Settings.SECTION_DEBUG, + "JitFloatingPointOff", + false + ), + MAIN_DEBUG_JIT_INTEGER_OFF( + Settings.FILE_DOLPHIN, + Settings.SECTION_DEBUG, + "JitIntegerOff", + false + ), + MAIN_DEBUG_JIT_PAIRED_OFF(Settings.FILE_DOLPHIN, Settings.SECTION_DEBUG, "JitPairedOff", false), + MAIN_DEBUG_JIT_SYSTEM_REGISTERS_OFF( + Settings.FILE_DOLPHIN, + Settings.SECTION_DEBUG, + "JitSystemRegistersOff", + false + ), + MAIN_DEBUG_JIT_BRANCH_OFF(Settings.FILE_DOLPHIN, Settings.SECTION_DEBUG, "JitBranchOff", false), + MAIN_DEBUG_JIT_REGISTER_CACHE_OFF( + Settings.FILE_DOLPHIN, + Settings.SECTION_DEBUG, + "JitRegisterCacheOff", + false + ), + MAIN_EMULATE_SKYLANDER_PORTAL( + Settings.FILE_DOLPHIN, + Settings.SECTION_EMULATED_USB_DEVICES, + "EmulateSkylanderPortal", + false + ), + MAIN_SHOW_GAME_TITLES( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID, + "ShowGameTitles", + true + ), + MAIN_USE_BLACK_BACKGROUNDS( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID, + "UseBlackBackgrounds", + false + ), + MAIN_JOYSTICK_REL_CENTER( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID, + "JoystickRelCenter", + true + ), + MAIN_SHOW_INPUT_OVERLAY( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID, + "ShowInputOverlay", + true + ), + MAIN_IR_ALWAYS_RECENTER( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID, + "IRAlwaysRecenter", + false + ), + MAIN_BUTTON_TOGGLE_GC_0( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleGCButtonA", + true + ), + MAIN_BUTTON_TOGGLE_GC_1( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleGCButtonB", + true + ), + MAIN_BUTTON_TOGGLE_GC_2( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleGCButtonX", + true + ), + MAIN_BUTTON_TOGGLE_GC_3( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleGCButtonY", + true + ), + MAIN_BUTTON_TOGGLE_GC_4( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleGCButtonZ", + true + ), + MAIN_BUTTON_TOGGLE_GC_5( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleGCButtonStart", + true + ), + MAIN_BUTTON_TOGGLE_GC_6( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleGCTriggerL", + true + ), + MAIN_BUTTON_TOGGLE_GC_7( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleGCTriggerR", + true + ), + MAIN_BUTTON_TOGGLE_GC_8( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleGCDPad", + true + ), + MAIN_BUTTON_TOGGLE_GC_9( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleGCStickMain", + true + ), + MAIN_BUTTON_TOGGLE_GC_10( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleGCStickC", + true + ), + MAIN_BUTTON_TOGGLE_CLASSIC_0( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleClassicButtonA", + true + ), + MAIN_BUTTON_TOGGLE_CLASSIC_1( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleClassicButtonB", + true + ), + MAIN_BUTTON_TOGGLE_CLASSIC_2( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleClassicButtonX", + true + ), + MAIN_BUTTON_TOGGLE_CLASSIC_3( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleClassicButtonY", + true + ), + MAIN_BUTTON_TOGGLE_CLASSIC_4( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleClassicButtonPlus", + true + ), + MAIN_BUTTON_TOGGLE_CLASSIC_5( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleClassicButtonMinus", + true + ), + MAIN_BUTTON_TOGGLE_CLASSIC_6( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleClassicButtonHome", + true + ), + MAIN_BUTTON_TOGGLE_CLASSIC_7( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleClassicTriggerL", + true + ), + MAIN_BUTTON_TOGGLE_CLASSIC_8( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleClassicTriggerR", + true + ), + MAIN_BUTTON_TOGGLE_CLASSIC_9( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleClassicButtonZL", + true + ), + MAIN_BUTTON_TOGGLE_CLASSIC_10( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleClassicButtonZR", + true + ), + MAIN_BUTTON_TOGGLE_CLASSIC_11( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleClassicDPad", + true + ), + MAIN_BUTTON_TOGGLE_CLASSIC_12( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleClassicStickLeft", + true + ), + MAIN_BUTTON_TOGGLE_CLASSIC_13( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleClassicStickRight", + true + ), + MAIN_BUTTON_TOGGLE_WII_0( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleWiimoteButtonA", + true + ), + MAIN_BUTTON_TOGGLE_WII_1( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleWiimoteButtonB", + true + ), + MAIN_BUTTON_TOGGLE_WII_2( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleWiimoteButton1", + true + ), + MAIN_BUTTON_TOGGLE_WII_3( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleWiimoteButton2", + true + ), + MAIN_BUTTON_TOGGLE_WII_4( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleWiimoteButtonPlus", + true + ), + MAIN_BUTTON_TOGGLE_WII_5( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleWiimoteButtonMinus", + true + ), + MAIN_BUTTON_TOGGLE_WII_6( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleWiimoteButtonHome", + true + ), + MAIN_BUTTON_TOGGLE_WII_7( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleWiimoteDPad", + true + ), + MAIN_BUTTON_TOGGLE_WII_8( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleNunchukC", + true + ), + MAIN_BUTTON_TOGGLE_WII_9( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleNunchukZ", + true + ), + MAIN_BUTTON_TOGGLE_WII_10( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "ButtonToggleNunchukStick", + true + ), + SYSCONF_SCREENSAVER(Settings.FILE_SYSCONF, "IPL", "SSV", false), + SYSCONF_WIDESCREEN(Settings.FILE_SYSCONF, "IPL", "AR", true), + SYSCONF_PROGRESSIVE_SCAN(Settings.FILE_SYSCONF, "IPL", "PGS", true), + SYSCONF_PAL60(Settings.FILE_SYSCONF, "IPL", "E60", true), + SYSCONF_WIIMOTE_MOTOR(Settings.FILE_SYSCONF, "BT", "MOT", true), + GFX_WIDESCREEN_HACK(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "wideScreenHack", false), + GFX_CROP(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "Crop", false), + GFX_SHOW_FPS(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "ShowFPS", false), + GFX_SHOW_FTIMES(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "ShowFTimes", false), + GFX_SHOW_VPS(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "ShowVPS", false), + GFX_SHOW_VTIMES(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "ShowVTimes", false), + GFX_SHOW_GRAPHS(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "ShowGraphs", false), + GFX_SHOW_SPEED(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "ShowSpeed", false), + GFX_SHOW_SPEED_COLORS( + Settings.FILE_GFX, + Settings.SECTION_GFX_SETTINGS, + "ShowSpeedColors", + true + ), + GFX_LOG_RENDER_TIME_TO_FILE( + Settings.FILE_GFX, + Settings.SECTION_GFX_SETTINGS, + "LogRenderTimeToFile", + false + ), + GFX_OVERLAY_STATS(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "OverlayStats", false), + GFX_DUMP_TEXTURES(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "DumpTextures", false), + GFX_DUMP_MIP_TEXTURES( + Settings.FILE_GFX, + Settings.SECTION_GFX_SETTINGS, + "DumpMipTextures", + false + ), + GFX_DUMP_BASE_TEXTURES( + Settings.FILE_GFX, + Settings.SECTION_GFX_SETTINGS, + "DumpBaseTextures", + false + ), + GFX_HIRES_TEXTURES(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "HiresTextures", false), + GFX_CACHE_HIRES_TEXTURES( + Settings.FILE_GFX, + Settings.SECTION_GFX_SETTINGS, + "CacheHiresTextures", + false + ), + GFX_DUMP_EFB_TARGET(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "DumpEFBTarget", false), + GFX_DUMP_XFB_TARGET(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "DumpXFBTarget", false), + GFX_INTERNAL_RESOLUTION_FRAME_DUMPS( + Settings.FILE_GFX, + Settings.SECTION_GFX_SETTINGS, + "InternalResolutionFrameDumps", + false + ), + GFX_ENABLE_GPU_TEXTURE_DECODING( + Settings.FILE_GFX, + Settings.SECTION_GFX_SETTINGS, + "EnableGPUTextureDecoding", + false + ), + GFX_ENABLE_PIXEL_LIGHTING( + Settings.FILE_GFX, + Settings.SECTION_GFX_SETTINGS, + "EnablePixelLighting", + false + ), + GFX_FAST_DEPTH_CALC(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "FastDepthCalc", true), + GFX_TEXFMT_OVERLAY_ENABLE( + Settings.FILE_GFX, + Settings.SECTION_GFX_SETTINGS, + "TexFmtOverlayEnable", + false + ), + GFX_ENABLE_WIREFRAME(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "WireFrame", false), + GFX_DISABLE_FOG(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "DisableFog", false), + GFX_ENABLE_VALIDATION_LAYER( + Settings.FILE_GFX, + Settings.SECTION_GFX_SETTINGS, + "EnableValidationLayer", + false + ), + GFX_BACKEND_MULTITHREADING( + Settings.FILE_GFX, + Settings.SECTION_GFX_SETTINGS, + "BackendMultithreading", + true + ), + GFX_WAIT_FOR_SHADERS_BEFORE_STARTING( + Settings.FILE_GFX, + Settings.SECTION_GFX_SETTINGS, + "WaitForShadersBeforeStarting", + false + ), + GFX_SAVE_TEXTURE_CACHE_TO_STATE( + Settings.FILE_GFX, + Settings.SECTION_GFX_SETTINGS, + "SaveTextureCacheToState", + true + ), + GFX_PREFER_VS_FOR_LINE_POINT_EXPANSION( + Settings.FILE_GFX, + Settings.SECTION_GFX_SETTINGS, + "PreferVSForLinePointExpansion", + false + ), + GFX_CPU_CULL(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "CPUCull", false), + GFX_MODS_ENABLE(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "EnableMods", false), + GFX_ENHANCE_FORCE_TRUE_COLOR( + Settings.FILE_GFX, + Settings.SECTION_GFX_ENHANCEMENTS, + "ForceTrueColor", + true + ), + GFX_ENHANCE_DISABLE_COPY_FILTER( + Settings.FILE_GFX, + Settings.SECTION_GFX_ENHANCEMENTS, + "DisableCopyFilter", + true + ), + GFX_ENHANCE_ARBITRARY_MIPMAP_DETECTION( + Settings.FILE_GFX, + Settings.SECTION_GFX_ENHANCEMENTS, + "ArbitraryMipmapDetection", + true + ), + GFX_STEREO_SWAP_EYES(Settings.FILE_GFX, Settings.SECTION_STEREOSCOPY, "StereoSwapEyes", false), + GFX_HACK_EFB_ACCESS_ENABLE( + Settings.FILE_GFX, + Settings.SECTION_GFX_HACKS, + "EFBAccessEnable", + true + ), + GFX_HACK_EFB_DEFER_INVALIDATION( + Settings.FILE_GFX, + Settings.SECTION_GFX_HACKS, + "EFBAccessDeferInvalidation", + false + ), + GFX_HACK_BBOX_ENABLE(Settings.FILE_GFX, Settings.SECTION_GFX_HACKS, "BBoxEnable", false), + GFX_HACK_SKIP_EFB_COPY_TO_RAM( + Settings.FILE_GFX, + Settings.SECTION_GFX_HACKS, + "EFBToTextureEnable", + true + ), + GFX_HACK_SKIP_XFB_COPY_TO_RAM( + Settings.FILE_GFX, + Settings.SECTION_GFX_HACKS, + "XFBToTextureEnable", + true + ), + GFX_HACK_DISABLE_COPY_TO_VRAM( + Settings.FILE_GFX, + Settings.SECTION_GFX_HACKS, + "DisableCopyToVRAM", + false + ), + GFX_HACK_DEFER_EFB_COPIES( + Settings.FILE_GFX, + Settings.SECTION_GFX_HACKS, + "DeferEFBCopies", + true + ), + GFX_HACK_IMMEDIATE_XFB( + Settings.FILE_GFX, + Settings.SECTION_GFX_HACKS, + "ImmediateXFBEnable", + false + ), + GFX_HACK_SKIP_DUPLICATE_XFBS( + Settings.FILE_GFX, + Settings.SECTION_GFX_HACKS, + "SkipDuplicateXFBs", + true + ), + GFX_HACK_COPY_EFB_SCALED(Settings.FILE_GFX, Settings.SECTION_GFX_HACKS, "EFBScaledCopy", true), + GFX_HACK_EFB_EMULATE_FORMAT_CHANGES( + Settings.FILE_GFX, + Settings.SECTION_GFX_HACKS, + "EFBEmulateFormatChanges", + false + ), + GFX_HACK_VERTEX_ROUNDING( + Settings.FILE_GFX, + Settings.SECTION_GFX_HACKS, + "VertexRounding", + false + ), + GFX_HACK_VI_SKIP(Settings.FILE_GFX, Settings.SECTION_GFX_HACKS, "VISkip", false), + GFX_HACK_FAST_TEXTURE_SAMPLING( + Settings.FILE_GFX, + Settings.SECTION_GFX_HACKS, + "FastTextureSampling", + true + ), + LOGGER_WRITE_TO_FILE( + Settings.FILE_LOGGER, + Settings.SECTION_LOGGER_OPTIONS, + "WriteToFile", + false + ); + + override val isOverridden: Boolean + get() = NativeConfig.isOverridden(file, section, key) + + override val isRuntimeEditable: Boolean + get() { + if (file == Settings.FILE_SYSCONF) return false + for (setting in NOT_RUNTIME_EDITABLE) { + if (setting == this) return false + } + return NativeConfig.isSettingSaveable(file, section, key) + } + + override fun delete(settings: Settings): Boolean { + if (!NativeConfig.isSettingSaveable(file, section, key)) { + throw UnsupportedOperationException("Unsupported setting: $file, $section, $key") + } + return NativeConfig.deleteKey(settings.writeLayer, file, section, key) + } + + override val boolean: Boolean + get() = NativeConfig.getBoolean( + NativeConfig.LAYER_ACTIVE, + file, + section, + key, + defaultValue + ) + + override fun setBoolean(settings: Settings, newValue: Boolean) { + if (!NativeConfig.isSettingSaveable(file, section, key)) { + throw UnsupportedOperationException("Unsupported setting: $file, $section, $key") + } + NativeConfig.setBoolean(settings.writeLayer, file, section, key, newValue) + } + + fun setBoolean(layer: Int, newValue: Boolean) { + if (!NativeConfig.isSettingSaveable(file, section, key)) { + throw UnsupportedOperationException("Unsupported setting: $file, $section, $key") + } + NativeConfig.setBoolean(layer, file, section, key, newValue) + } + + companion object { + private val NOT_RUNTIME_EDITABLE_ARRAY = arrayOf( + MAIN_DSP_HLE, + MAIN_CPU_THREAD, + MAIN_ENABLE_CHEATS, + MAIN_OVERRIDE_REGION_SETTINGS, + MAIN_MMU, + MAIN_PAUSE_ON_PANIC, + MAIN_ACCURATE_CPU_CACHE, + MAIN_RAM_OVERRIDE_ENABLE, + MAIN_CUSTOM_RTC_ENABLE, + MAIN_DSP_JIT, + MAIN_EMULATE_SKYLANDER_PORTAL + ) + private val NOT_RUNTIME_EDITABLE: Set = + HashSet(listOf(*NOT_RUNTIME_EDITABLE_ARRAY)) + + @JvmStatic + fun getSettingForAdapterRumble(channel: Int): BooleanSetting { + return arrayOf( + MAIN_ADAPTER_RUMBLE_0, + MAIN_ADAPTER_RUMBLE_1, + MAIN_ADAPTER_RUMBLE_2, + MAIN_ADAPTER_RUMBLE_3 + )[channel] + } + + @JvmStatic + fun getSettingForSimulateKonga(channel: Int): BooleanSetting { + return arrayOf( + MAIN_SIMULATE_KONGA_0, + MAIN_SIMULATE_KONGA_1, + MAIN_SIMULATE_KONGA_2, + MAIN_SIMULATE_KONGA_3 + )[channel] + } + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/FloatSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/FloatSetting.java deleted file mode 100644 index 99d15957c4..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/FloatSetting.java +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model; - -import androidx.annotation.NonNull; - -public enum FloatSetting implements AbstractFloatSetting -{ - // These entries have the same names and order as in C++, just for consistency. - - MAIN_EMULATION_SPEED(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "EmulationSpeed", 1.0f), - MAIN_OVERCLOCK(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "Overclock", 1.0f); - - private final String mFile; - private final String mSection; - private final String mKey; - private final float mDefaultValue; - - FloatSetting(String file, String section, String key, float defaultValue) - { - mFile = file; - mSection = section; - mKey = key; - mDefaultValue = defaultValue; - } - - @Override - public boolean isOverridden() - { - return NativeConfig.isOverridden(mFile, mSection, mKey); - } - - @Override - public boolean isRuntimeEditable() - { - return NativeConfig.isSettingSaveable(mFile, mSection, mKey); - } - - @Override - public boolean delete(@NonNull Settings settings) - { - if (!NativeConfig.isSettingSaveable(mFile, mSection, mKey)) - { - throw new UnsupportedOperationException( - "Unsupported setting: " + mFile + ", " + mSection + ", " + mKey); - } - - return NativeConfig.deleteKey(settings.getWriteLayer(), mFile, mSection, mKey); - } - - @Override - public float getFloat() - { - return NativeConfig.getFloat(NativeConfig.LAYER_ACTIVE, mFile, mSection, mKey, mDefaultValue); - } - - @Override - public void setFloat(@NonNull Settings settings, float newValue) - { - if (!NativeConfig.isSettingSaveable(mFile, mSection, mKey)) - { - throw new UnsupportedOperationException( - "Unsupported setting: " + mFile + ", " + mSection + ", " + mKey); - } - - NativeConfig.setFloat(settings.getWriteLayer(), mFile, mSection, mKey, newValue); - } - - public void setFloat(int layer, float newValue) - { - NativeConfig.setFloat(layer, mFile, mSection, mKey, newValue); - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/FloatSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/FloatSetting.kt new file mode 100644 index 0000000000..99b73251e7 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/FloatSetting.kt @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model + +enum class FloatSetting( + private val file: String, + private val section: String, + private val key: String, + private val defaultValue: Float +) : AbstractFloatSetting { + // These entries have the same names and order as in C++, just for consistency. + MAIN_EMULATION_SPEED(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "EmulationSpeed", 1.0f), + MAIN_OVERCLOCK(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "Overclock", 1.0f); + + override val isOverridden: Boolean + get() = NativeConfig.isOverridden(file, section, key) + + override val isRuntimeEditable: Boolean + get() = NativeConfig.isSettingSaveable(file, section, key) + + override fun delete(settings: Settings): Boolean { + if (!NativeConfig.isSettingSaveable(file, section, key)) { + throw UnsupportedOperationException("Unsupported setting: $file, $section, $key") + } + return NativeConfig.deleteKey(settings.writeLayer, file, section, key) + } + + override val float: Float + get() = NativeConfig.getFloat(NativeConfig.LAYER_ACTIVE, file, section, key, defaultValue) + + override fun setFloat(settings: Settings, newValue: Float) { + if (!NativeConfig.isSettingSaveable(file, section, key)) { + throw UnsupportedOperationException("Unsupported setting: $file, $section, $key") + } + NativeConfig.setFloat(settings.writeLayer, file, section, key, newValue) + } + + fun setFloat(layer: Int, newValue: Float) { + NativeConfig.setFloat(layer, file, section, key, newValue) + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/IntSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/IntSetting.java deleted file mode 100644 index df562c6998..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/IntSetting.java +++ /dev/null @@ -1,196 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model; - -import android.content.pm.ActivityInfo; - -import androidx.annotation.NonNull; - -import org.dolphinemu.dolphinemu.NativeLibrary; -import org.dolphinemu.dolphinemu.overlay.InputOverlayPointer; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -public enum IntSetting implements AbstractIntSetting -{ - // These entries have the same names and order as in C++, just for consistency. - - MAIN_CPU_CORE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "CPUCore", - NativeLibrary.DefaultCPUCore()), - MAIN_GC_LANGUAGE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "SelectedLanguage", 0), - MAIN_MEM1_SIZE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "MEM1Size", 0x01800000), - MAIN_MEM2_SIZE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "MEM2Size", 0x04000000), - MAIN_SLOT_A(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "SlotA", 8), - MAIN_SLOT_B(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "SlotB", 255), - MAIN_SERIAL_PORT_1(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "SerialPort1", 255), - MAIN_FALLBACK_REGION(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "FallbackRegion", 2), - MAIN_SI_DEVICE_0(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "SIDevice0", 6), - MAIN_SI_DEVICE_1(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "SIDevice1", 0), - MAIN_SI_DEVICE_2(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "SIDevice2", 0), - MAIN_SI_DEVICE_3(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "SIDevice3", 0), - - MAIN_AUDIO_VOLUME(Settings.FILE_DOLPHIN, Settings.SECTION_INI_DSP, "Volume", 100), - - MAIN_OVERLAY_GC_CONTROLLER(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, - "OverlayGCController", 0), // Defaults to GameCube controller 1 - MAIN_OVERLAY_WII_CONTROLLER(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, - "OverlayWiiController", 4), // Defaults to Wii Remote 1 - MAIN_CONTROL_SCALE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "ControlScale", 50), - MAIN_CONTROL_OPACITY(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "ControlOpacity", 65), - MAIN_EMULATION_ORIENTATION(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, - "EmulationOrientation", ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE), - MAIN_INTERFACE_THEME(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "InterfaceTheme", 0), - MAIN_INTERFACE_THEME_MODE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, - "InterfaceThemeMode", -1), - MAIN_LAST_PLATFORM_TAB(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "LastPlatformTab", 0), - MAIN_IR_MODE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "IRMode", - InputOverlayPointer.MODE_FOLLOW), - - MAIN_DOUBLE_TAP_BUTTON(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, - "DoubleTapButton", NativeLibrary.ButtonType.WIIMOTE_BUTTON_A), - - SYSCONF_LANGUAGE(Settings.FILE_SYSCONF, "IPL", "LNG", 0x01), - SYSCONF_SOUND_MODE(Settings.FILE_SYSCONF, "IPL", "SND", 0x01), - - SYSCONF_SENSOR_BAR_POSITION(Settings.FILE_SYSCONF, "BT", "BAR", 0x01), - SYSCONF_SENSOR_BAR_SENSITIVITY(Settings.FILE_SYSCONF, "BT", "SENS", 0x03), - SYSCONF_SPEAKER_VOLUME(Settings.FILE_SYSCONF, "BT", "SPKV", 0x58), - - GFX_ASPECT_RATIO(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "AspectRatio", 0), - GFX_SAFE_TEXTURE_CACHE_COLOR_SAMPLES(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, - "SafeTextureCacheColorSamples", 128), - GFX_PNG_COMPRESSION_LEVEL(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "PNGCompressionLevel", - 6), - GFX_MSAA(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "MSAA", 1), - GFX_EFB_SCALE(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "InternalResolution", 1), - GFX_SHADER_COMPILATION_MODE(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, - "ShaderCompilationMode", 0), - - GFX_ENHANCE_FORCE_TEXTURE_FILTERING(Settings.FILE_GFX, Settings.SECTION_GFX_ENHANCEMENTS, - "ForceTextureFiltering", 0), - - GFX_ENHANCE_MAX_ANISOTROPY(Settings.FILE_GFX, Settings.SECTION_GFX_ENHANCEMENTS, "MaxAnisotropy", - 0), - - GFX_STEREO_MODE(Settings.FILE_GFX, Settings.SECTION_STEREOSCOPY, "StereoMode", 0), - GFX_STEREO_DEPTH(Settings.FILE_GFX, Settings.SECTION_STEREOSCOPY, "StereoDepth", 20), - GFX_STEREO_CONVERGENCE_PERCENTAGE(Settings.FILE_GFX, Settings.SECTION_STEREOSCOPY, - "StereoConvergencePercentage", 100), - - GFX_PERF_SAMP_WINDOW(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "PerfSampWindowMS", 1000), - - LOGGER_VERBOSITY(Settings.FILE_LOGGER, Settings.SECTION_LOGGER_OPTIONS, "Verbosity", 1), - - WIIMOTE_1_SOURCE(Settings.FILE_WIIMOTE, "Wiimote1", "Source", 1), - WIIMOTE_2_SOURCE(Settings.FILE_WIIMOTE, "Wiimote2", "Source", 0), - WIIMOTE_3_SOURCE(Settings.FILE_WIIMOTE, "Wiimote3", "Source", 0), - WIIMOTE_4_SOURCE(Settings.FILE_WIIMOTE, "Wiimote4", "Source", 0), - WIIMOTE_BB_SOURCE(Settings.FILE_WIIMOTE, "BalanceBoard", "Source", 0); - - private static final IntSetting[] NOT_RUNTIME_EDITABLE_ARRAY = new IntSetting[]{ - MAIN_CPU_CORE, - MAIN_GC_LANGUAGE, - MAIN_MEM1_SIZE, - MAIN_MEM2_SIZE, - MAIN_SLOT_A, // Can actually be changed, but specific code is required - MAIN_SLOT_B, // Can actually be changed, but specific code is required - MAIN_SERIAL_PORT_1, - MAIN_FALLBACK_REGION, - MAIN_SI_DEVICE_0, // Can actually be changed, but specific code is required - MAIN_SI_DEVICE_1, // Can actually be changed, but specific code is required - MAIN_SI_DEVICE_2, // Can actually be changed, but specific code is required - MAIN_SI_DEVICE_3, // Can actually be changed, but specific code is required - }; - - private static final Set NOT_RUNTIME_EDITABLE = - new HashSet<>(Arrays.asList(NOT_RUNTIME_EDITABLE_ARRAY)); - - private final String mFile; - private final String mSection; - private final String mKey; - private final int mDefaultValue; - - IntSetting(String file, String section, String key, int defaultValue) - { - mFile = file; - mSection = section; - mKey = key; - mDefaultValue = defaultValue; - } - - @Override - public boolean isOverridden() - { - return NativeConfig.isOverridden(mFile, mSection, mKey); - } - - @Override - public boolean isRuntimeEditable() - { - if (mFile.equals(Settings.FILE_SYSCONF)) - return false; - - for (IntSetting setting : NOT_RUNTIME_EDITABLE) - { - if (setting == this) - return false; - } - - return NativeConfig.isSettingSaveable(mFile, mSection, mKey); - } - - @Override - public boolean delete(@NonNull Settings settings) - { - if (!NativeConfig.isSettingSaveable(mFile, mSection, mKey)) - { - throw new UnsupportedOperationException( - "Unsupported setting: " + mFile + ", " + mSection + ", " + mKey); - } - - return NativeConfig.deleteKey(settings.getWriteLayer(), mFile, mSection, mKey); - } - - @Override - public int getInt() - { - return NativeConfig.getInt(NativeConfig.LAYER_ACTIVE, mFile, mSection, mKey, mDefaultValue); - } - - @Override - public void setInt(@NonNull Settings settings, int newValue) - { - if (!NativeConfig.isSettingSaveable(mFile, mSection, mKey)) - { - throw new UnsupportedOperationException( - "Unsupported setting: " + mFile + ", " + mSection + ", " + mKey); - } - - NativeConfig.setInt(settings.getWriteLayer(), mFile, mSection, mKey, newValue); - } - - public void setInt(int layer, int newValue) - { - if (!NativeConfig.isSettingSaveable(mFile, mSection, mKey)) - { - throw new UnsupportedOperationException( - "Unsupported setting: " + mFile + ", " + mSection + ", " + mKey); - } - - NativeConfig.setInt(layer, mFile, mSection, mKey, newValue); - } - - public static IntSetting getSettingForSIDevice(int channel) - { - return new IntSetting[]{MAIN_SI_DEVICE_0, MAIN_SI_DEVICE_1, MAIN_SI_DEVICE_2, MAIN_SI_DEVICE_3} - [channel]; - } - - public static IntSetting getSettingForWiimoteSource(int index) - { - return new IntSetting[]{WIIMOTE_1_SOURCE, WIIMOTE_2_SOURCE, WIIMOTE_3_SOURCE, WIIMOTE_4_SOURCE, - WIIMOTE_BB_SOURCE}[index]; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/IntSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/IntSetting.kt new file mode 100644 index 0000000000..256e68007e --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/IntSetting.kt @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model + +import android.content.pm.ActivityInfo +import org.dolphinemu.dolphinemu.NativeLibrary +import org.dolphinemu.dolphinemu.overlay.InputOverlayPointer +import java.util.* + +enum class IntSetting( + private val file: String, + private val section: String, + private val key: String, + private val defaultValue: Int +) : AbstractIntSetting { + // These entries have the same names and order as in C++, just for consistency. + MAIN_CPU_CORE( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_CORE, + "CPUCore", + NativeLibrary.DefaultCPUCore() + ), + MAIN_GC_LANGUAGE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "SelectedLanguage", 0), + MAIN_MEM1_SIZE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "MEM1Size", 0x01800000), + MAIN_MEM2_SIZE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "MEM2Size", 0x04000000), + MAIN_SLOT_A(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "SlotA", 8), + MAIN_SLOT_B(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "SlotB", 255), + MAIN_SERIAL_PORT_1(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "SerialPort1", 255), + MAIN_FALLBACK_REGION(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "FallbackRegion", 2), + MAIN_SI_DEVICE_0(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "SIDevice0", 6), + MAIN_SI_DEVICE_1(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "SIDevice1", 0), + MAIN_SI_DEVICE_2(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "SIDevice2", 0), + MAIN_SI_DEVICE_3(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "SIDevice3", 0), + MAIN_AUDIO_VOLUME(Settings.FILE_DOLPHIN, Settings.SECTION_INI_DSP, "Volume", 100), + MAIN_OVERLAY_GC_CONTROLLER( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID, + "OverlayGCController", + 0 + ), + + // Defaults to GameCube controller 1 + MAIN_OVERLAY_WII_CONTROLLER( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID, + "OverlayWiiController", + 4 + ), + + // Defaults to Wii Remote 1 + MAIN_CONTROL_SCALE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "ControlScale", 50), + MAIN_CONTROL_OPACITY(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "ControlOpacity", 65), + MAIN_EMULATION_ORIENTATION( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID, + "EmulationOrientation", + ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE + ), + MAIN_INTERFACE_THEME(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "InterfaceTheme", 0), + MAIN_INTERFACE_THEME_MODE( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID, + "InterfaceThemeMode", + -1 + ), + MAIN_LAST_PLATFORM_TAB( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID, + "LastPlatformTab", + 0 + ), + MAIN_IR_MODE( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID, + "IRMode", + InputOverlayPointer.MODE_FOLLOW + ), + MAIN_DOUBLE_TAP_BUTTON( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, + "DoubleTapButton", + NativeLibrary.ButtonType.WIIMOTE_BUTTON_A + ), + SYSCONF_LANGUAGE(Settings.FILE_SYSCONF, "IPL", "LNG", 0x01), + SYSCONF_SOUND_MODE(Settings.FILE_SYSCONF, "IPL", "SND", 0x01), + SYSCONF_SENSOR_BAR_POSITION(Settings.FILE_SYSCONF, "BT", "BAR", 0x01), + SYSCONF_SENSOR_BAR_SENSITIVITY(Settings.FILE_SYSCONF, "BT", "SENS", 0x03), + SYSCONF_SPEAKER_VOLUME(Settings.FILE_SYSCONF, "BT", "SPKV", 0x58), + GFX_ASPECT_RATIO(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "AspectRatio", 0), + GFX_SAFE_TEXTURE_CACHE_COLOR_SAMPLES( + Settings.FILE_GFX, + Settings.SECTION_GFX_SETTINGS, + "SafeTextureCacheColorSamples", + 128 + ), + GFX_PNG_COMPRESSION_LEVEL( + Settings.FILE_GFX, + Settings.SECTION_GFX_SETTINGS, + "PNGCompressionLevel", + 6 + ), + GFX_MSAA(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "MSAA", 1), + GFX_EFB_SCALE(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "InternalResolution", 1), + GFX_SHADER_COMPILATION_MODE( + Settings.FILE_GFX, + Settings.SECTION_GFX_SETTINGS, + "ShaderCompilationMode", + 0 + ), + GFX_ENHANCE_FORCE_TEXTURE_FILTERING( + Settings.FILE_GFX, + Settings.SECTION_GFX_ENHANCEMENTS, + "ForceTextureFiltering", + 0 + ), + GFX_ENHANCE_MAX_ANISOTROPY( + Settings.FILE_GFX, + Settings.SECTION_GFX_ENHANCEMENTS, + "MaxAnisotropy", + 0 + ), + GFX_STEREO_MODE(Settings.FILE_GFX, Settings.SECTION_STEREOSCOPY, "StereoMode", 0), + GFX_STEREO_DEPTH(Settings.FILE_GFX, Settings.SECTION_STEREOSCOPY, "StereoDepth", 20), + GFX_STEREO_CONVERGENCE_PERCENTAGE( + Settings.FILE_GFX, + Settings.SECTION_STEREOSCOPY, + "StereoConvergencePercentage", + 100 + ), + GFX_PERF_SAMP_WINDOW( + Settings.FILE_GFX, + Settings.SECTION_GFX_SETTINGS, + "PerfSampWindowMS", + 1000 + ), + LOGGER_VERBOSITY(Settings.FILE_LOGGER, Settings.SECTION_LOGGER_OPTIONS, "Verbosity", 1), + WIIMOTE_1_SOURCE(Settings.FILE_WIIMOTE, "Wiimote1", "Source", 1), + WIIMOTE_2_SOURCE(Settings.FILE_WIIMOTE, "Wiimote2", "Source", 0), + WIIMOTE_3_SOURCE(Settings.FILE_WIIMOTE, "Wiimote3", "Source", 0), + WIIMOTE_4_SOURCE(Settings.FILE_WIIMOTE, "Wiimote4", "Source", 0), + WIIMOTE_BB_SOURCE(Settings.FILE_WIIMOTE, "BalanceBoard", "Source", 0); + + override val isOverridden: Boolean + get() = NativeConfig.isOverridden(file, section, key) + + override val isRuntimeEditable: Boolean + get() { + if (file == Settings.FILE_SYSCONF) return false + for (setting in NOT_RUNTIME_EDITABLE) { + if (setting == this) return false + } + return NativeConfig.isSettingSaveable(file, section, key) + } + + override fun delete(settings: Settings): Boolean { + if (!NativeConfig.isSettingSaveable(file, section, key)) { + throw UnsupportedOperationException("Unsupported setting: $file, $section, $key") + } + return NativeConfig.deleteKey(settings.writeLayer, file, section, key) + } + + override val int: Int + get() = NativeConfig.getInt(NativeConfig.LAYER_ACTIVE, file, section, key, defaultValue) + + override fun setInt(settings: Settings, newValue: Int) { + if (!NativeConfig.isSettingSaveable(file, section, key)) { + throw UnsupportedOperationException("Unsupported setting: $file, $section, $key") + } + NativeConfig.setInt(settings.writeLayer, file, section, key, newValue) + } + + fun setInt(layer: Int, newValue: Int) { + if (!NativeConfig.isSettingSaveable(file, section, key)) { + throw UnsupportedOperationException("Unsupported setting: $file, $section, $key") + } + NativeConfig.setInt(layer, file, section, key, newValue) + } + + companion object { + private val NOT_RUNTIME_EDITABLE_ARRAY = arrayOf( + MAIN_CPU_CORE, + MAIN_GC_LANGUAGE, + MAIN_MEM1_SIZE, + MAIN_MEM2_SIZE, + MAIN_SLOT_A, + MAIN_SLOT_B, + MAIN_SERIAL_PORT_1, + MAIN_FALLBACK_REGION, + MAIN_SI_DEVICE_0, + MAIN_SI_DEVICE_1, + MAIN_SI_DEVICE_2, + MAIN_SI_DEVICE_3 + ) + + private val NOT_RUNTIME_EDITABLE: Set = + HashSet(listOf(*NOT_RUNTIME_EDITABLE_ARRAY)) + + @JvmStatic + fun getSettingForSIDevice(channel: Int): IntSetting { + return arrayOf( + MAIN_SI_DEVICE_0, + MAIN_SI_DEVICE_1, + MAIN_SI_DEVICE_2, + MAIN_SI_DEVICE_3 + )[channel] + } + + @JvmStatic + fun getSettingForWiimoteSource(index: Int): IntSetting { + return arrayOf( + WIIMOTE_1_SOURCE, + WIIMOTE_2_SOURCE, + WIIMOTE_3_SOURCE, + WIIMOTE_4_SOURCE, + WIIMOTE_BB_SOURCE + )[index] + } + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/NativeConfig.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/NativeConfig.java deleted file mode 100644 index 7fbe96f95e..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/NativeConfig.java +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model; - -public class NativeConfig -{ - public static final int LAYER_BASE_OR_CURRENT = 0; - public static final int LAYER_BASE = 1; - public static final int LAYER_LOCAL_GAME = 2; - public static final int LAYER_ACTIVE = 3; - public static final int LAYER_CURRENT = 4; - - public static native boolean isSettingSaveable(String file, String section, String key); - - public static native void loadGameInis(String gameId, int revision); - - public static native void unloadGameInis(); - - public static native void save(int layer); - - public static native void deleteAllKeys(int layer); - - public static native boolean isOverridden(String file, String section, String key); - - public static native boolean deleteKey(int layer, String file, String section, String key); - - public static native boolean exists(int layer, String file, String section, String key); - - public static native String getString(int layer, String file, String section, String key, - String defaultValue); - - public static native boolean getBoolean(int layer, String file, String section, String key, - boolean defaultValue); - - public static native int getInt(int layer, String file, String section, String key, - int defaultValue); - - public static native float getFloat(int layer, String file, String section, String key, - float defaultValue); - - public static native void setString(int layer, String file, String section, String key, - String value); - - public static native void setBoolean(int layer, String file, String section, String key, - boolean value); - - public static native void setInt(int layer, String file, String section, String key, int value); - - public static native void setFloat(int layer, String file, String section, String key, - float value); -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/NativeConfig.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/NativeConfig.kt new file mode 100644 index 0000000000..c49667574e --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/NativeConfig.kt @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model + +object NativeConfig { + const val LAYER_BASE_OR_CURRENT = 0 + const val LAYER_BASE = 1 + const val LAYER_LOCAL_GAME = 2 + const val LAYER_ACTIVE = 3 + const val LAYER_CURRENT = 4 + + @JvmStatic + external fun isSettingSaveable(file: String, section: String, key: String): Boolean + + @JvmStatic + external fun loadGameInis(gameId: String, revision: Int) + + @JvmStatic + external fun unloadGameInis() + + @JvmStatic + external fun save(layer: Int) + + @JvmStatic + external fun deleteAllKeys(layer: Int) + + @JvmStatic + external fun isOverridden(file: String, section: String, key: String): Boolean + + @JvmStatic + external fun deleteKey(layer: Int, file: String, section: String, key: String): Boolean + + @JvmStatic + external fun exists(layer: Int, file: String, section: String, key: String): Boolean + + @JvmStatic + external fun getString( + layer: Int, + file: String, + section: String, + key: String, + defaultValue: String + ): String + + @JvmStatic + external fun getBoolean( + layer: Int, + file: String, + section: String, + key: String, + defaultValue: Boolean + ): Boolean + + @JvmStatic + external fun getInt( + layer: Int, + file: String, + section: String, + key: String, + defaultValue: Int + ): Int + + @JvmStatic + external fun getFloat( + layer: Int, + file: String, + section: String, + key: String, + defaultValue: Float + ): Float + + @JvmStatic + external fun setString( + layer: Int, + file: String, + section: String, + key: String, + value: String? + ) + + @JvmStatic + external fun setBoolean( + layer: Int, + file: String, + section: String, + key: String, + value: Boolean + ) + + @JvmStatic + external fun setInt( + layer: Int, + file: String, + section: String, + key: String, + value: Int + ) + + @JvmStatic + external fun setFloat( + layer: Int, + file: String, + section: String, + key: String, + value: Float + ) +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/PostProcessing.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/PostProcessing.java deleted file mode 100644 index 8cd86caeb4..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/PostProcessing.java +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model; - -import androidx.annotation.NonNull; - -public class PostProcessing -{ - @NonNull - public static native String[] getShaderList(); - - @NonNull - public static native String[] getAnaglyphShaderList(); - - @NonNull - public static native String[] getPassiveShaderList(); -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/PostProcessing.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/PostProcessing.kt new file mode 100644 index 0000000000..1bb1aa04d7 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/PostProcessing.kt @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model + +object PostProcessing { + @JvmStatic + val shaderList: Array + external get + + @JvmStatic + val anaglyphShaderList: Array + external get + + @JvmStatic + val passiveShaderList: Array + external get +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/ScaledIntSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/ScaledIntSetting.kt index 91b69b8fc3..4eef9ed6cf 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/ScaledIntSetting.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/ScaledIntSetting.kt @@ -6,21 +6,18 @@ class ScaledIntSetting( private val scale: Int, private val setting: AbstractIntSetting ) : AbstractIntSetting { - override fun isOverridden(): Boolean { - return setting.isOverridden() - } + override val isOverridden: Boolean + get() = setting.isOverridden - override fun isRuntimeEditable(): Boolean { - return setting.isRuntimeEditable - } + override val isRuntimeEditable: Boolean + get() = setting.isRuntimeEditable override fun delete(settings: Settings): Boolean { return setting.delete(settings) } - override fun getInt(): Int { - return setting.getInt() / scale - } + override val int: Int + get() = setting.int / scale override fun setInt(settings: Settings, newValue: Int) { return setting.setInt(settings, newValue * scale) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.java deleted file mode 100644 index 0bb935f3df..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.java +++ /dev/null @@ -1,181 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model; - -import android.content.Context; -import android.text.TextUtils; -import android.widget.Toast; - -import org.dolphinemu.dolphinemu.NativeLibrary; -import org.dolphinemu.dolphinemu.R; -import org.dolphinemu.dolphinemu.features.input.model.MappingCommon; -import org.dolphinemu.dolphinemu.services.GameFileCacheManager; - -import java.io.Closeable; - -public class Settings implements Closeable -{ - public static final String FILE_DOLPHIN = "Dolphin"; - public static final String FILE_SYSCONF = "SYSCONF"; - public static final String FILE_GFX = "GFX"; - public static final String FILE_LOGGER = "Logger"; - public static final String FILE_WIIMOTE = "WiimoteNew"; - public static final String FILE_GAME_SETTINGS_ONLY = "GameSettingsOnly"; - - public static final String SECTION_INI_ANDROID = "Android"; - public static final String SECTION_INI_ANDROID_OVERLAY_BUTTONS = "AndroidOverlayButtons"; - public static final String SECTION_INI_GENERAL = "General"; - public static final String SECTION_INI_CORE = "Core"; - public static final String SECTION_INI_INTERFACE = "Interface"; - public static final String SECTION_INI_DSP = "DSP"; - - public static final String SECTION_LOGGER_LOGS = "Logs"; - public static final String SECTION_LOGGER_OPTIONS = "Options"; - - public static final String SECTION_GFX_SETTINGS = "Settings"; - public static final String SECTION_GFX_ENHANCEMENTS = "Enhancements"; - public static final String SECTION_GFX_HACKS = "Hacks"; - - public static final String SECTION_DEBUG = "Debug"; - public static final String SECTION_EMULATED_USB_DEVICES = "EmulatedUSBDevices"; - - public static final String SECTION_STEREOSCOPY = "Stereoscopy"; - - public static final String SECTION_BINDINGS = "Android"; - public static final String SECTION_PROFILE = "Profile"; - - public static final String SECTION_ANALYTICS = "Analytics"; - - private String mGameId; - private int mRevision; - private boolean mIsWii; - - private boolean mSettingsLoaded = false; - - private boolean mLoadedRecursiveIsoPathsValue = false; - - public boolean isGameSpecific() - { - return !TextUtils.isEmpty(mGameId); - } - - public boolean isWii() - { - return mIsWii; - } - - public int getWriteLayer() - { - return isGameSpecific() ? NativeConfig.LAYER_LOCAL_GAME : NativeConfig.LAYER_BASE_OR_CURRENT; - } - - public boolean areSettingsLoaded() - { - return mSettingsLoaded; - } - - public void loadSettings() - { - // The value of isWii doesn't matter if we don't have any SettingsActivity - loadSettings(true); - } - - public void loadSettings(boolean isWii) - { - mIsWii = isWii; - mSettingsLoaded = true; - - if (isGameSpecific()) - { - // Loading game INIs while the core is running will mess with the game INIs loaded by the core - if (NativeLibrary.IsRunning()) - throw new IllegalStateException("Attempted to load game INI while emulating"); - - NativeConfig.loadGameInis(mGameId, mRevision); - } - - mLoadedRecursiveIsoPathsValue = BooleanSetting.MAIN_RECURSIVE_ISO_PATHS.getBoolean(); - } - - public void loadSettings(String gameId, int revision, boolean isWii) - { - mGameId = gameId; - mRevision = revision; - loadSettings(isWii); - } - - public void saveSettings(Context context) - { - if (!isGameSpecific()) - { - if (context != null) - Toast.makeText(context, R.string.settings_saved, Toast.LENGTH_SHORT).show(); - - MappingCommon.save(); - - NativeConfig.save(NativeConfig.LAYER_BASE); - - NativeLibrary.ReloadLoggerConfig(); - NativeLibrary.UpdateGCAdapterScanThread(); - - if (mLoadedRecursiveIsoPathsValue != BooleanSetting.MAIN_RECURSIVE_ISO_PATHS.getBoolean()) - { - // Refresh game library - GameFileCacheManager.startRescan(); - } - } - else - { - // custom game settings - - if (context != null) - { - Toast.makeText(context, context.getString(R.string.settings_saved_game_specific, mGameId), - Toast.LENGTH_SHORT).show(); - } - - NativeConfig.save(NativeConfig.LAYER_LOCAL_GAME); - } - } - - public void clearGameSettings() - { - NativeConfig.deleteAllKeys(NativeConfig.LAYER_LOCAL_GAME); - } - - public boolean gameIniContainsJunk() - { - // Older versions of Android Dolphin would copy the entire contents of most of the global INIs - // into any game INI that got saved (with some of the sections renamed to match the game INI - // section names). The problems with this are twofold: - // - // 1. The user game INIs will contain entries that Dolphin doesn't support reading from - // game INIs. This is annoying when editing game INIs manually but shouldn't really be - // a problem for those who only use the GUI. - // - // 2. Global settings will stick around in user game INIs. For instance, if someone wants to - // change the texture cache accuracy to safe for all games, they have to edit not only the - // global settings but also every single game INI they have created, since the old value of - // the texture cache accuracy setting has been copied into every user game INI. - // - // These problems are serious enough that we should detect and delete such INI files. - // Problem 1 is easy to detect, but due to the nature of problem 2, it's unfortunately not - // possible to know which lines were added intentionally by the user and which lines were added - // unintentionally, which is why we have to delete the whole file in order to fix everything. - - if (!isGameSpecific()) - return false; - - return NativeConfig.exists(NativeConfig.LAYER_LOCAL_GAME, FILE_DOLPHIN, SECTION_INI_INTERFACE, - "ThemeName"); - } - - @Override - public void close() - { - if (isGameSpecific()) - { - NativeConfig.unloadGameInis(); - } - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.kt new file mode 100644 index 0000000000..b363104703 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.kt @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model + +import android.content.Context +import android.text.TextUtils +import android.widget.Toast +import org.dolphinemu.dolphinemu.NativeLibrary +import org.dolphinemu.dolphinemu.R +import org.dolphinemu.dolphinemu.features.input.model.MappingCommon +import org.dolphinemu.dolphinemu.services.GameFileCacheManager +import java.io.Closeable + +class Settings : Closeable { + private var gameId: String = "" + private var revision = 0 + + var isWii = false + private set + + private var settingsLoaded = false + private var loadedRecursiveIsoPathsValue = false + + private val isGameSpecific: Boolean + get() = !TextUtils.isEmpty(gameId) + + val writeLayer: Int + get() = if (isGameSpecific) NativeConfig.LAYER_LOCAL_GAME else NativeConfig.LAYER_BASE_OR_CURRENT + + fun areSettingsLoaded(): Boolean { + return settingsLoaded + } + + @JvmOverloads + fun loadSettings(isWii: Boolean = true) { + this.isWii = isWii + settingsLoaded = true + + if (isGameSpecific) { + // Loading game INIs while the core is running will mess with the game INIs loaded by the core + check(!NativeLibrary.IsRunning()) { "Attempted to load game INI while emulating" } + NativeConfig.loadGameInis(gameId, revision) + } + + loadedRecursiveIsoPathsValue = BooleanSetting.MAIN_RECURSIVE_ISO_PATHS.boolean + } + + fun loadSettings(gameId: String, revision: Int, isWii: Boolean) { + this.gameId = gameId + this.revision = revision + loadSettings(isWii) + } + + fun saveSettings(context: Context?) { + if (!isGameSpecific) { + if (context != null) Toast.makeText( + context, + R.string.settings_saved, + Toast.LENGTH_SHORT + ).show() + + MappingCommon.save() + + NativeConfig.save(NativeConfig.LAYER_BASE) + + NativeLibrary.ReloadLoggerConfig() + NativeLibrary.UpdateGCAdapterScanThread() + + if (loadedRecursiveIsoPathsValue != BooleanSetting.MAIN_RECURSIVE_ISO_PATHS.boolean) { + // Refresh game library + GameFileCacheManager.startRescan() + } + } else { + // custom game settings + if (context != null) { + Toast.makeText( + context, context.getString(R.string.settings_saved_game_specific, gameId), + Toast.LENGTH_SHORT + ).show() + } + NativeConfig.save(NativeConfig.LAYER_LOCAL_GAME) + } + } + + fun clearGameSettings() { + NativeConfig.deleteAllKeys(NativeConfig.LAYER_LOCAL_GAME) + } + + fun gameIniContainsJunk(): Boolean { + // Older versions of Android Dolphin would copy the entire contents of most of the global INIs + // into any game INI that got saved (with some of the sections renamed to match the game INI + // section names). The problems with this are twofold: + // + // 1. The user game INIs will contain entries that Dolphin doesn't support reading from + // game INIs. This is annoying when editing game INIs manually but shouldn't really be + // a problem for those who only use the GUI. + // + // 2. Global settings will stick around in user game INIs. For instance, if someone wants to + // change the texture cache accuracy to safe for all games, they have to edit not only the + // global settings but also every single game INI they have created, since the old value of + // the texture cache accuracy Setting has been copied into every user game INI. + // + // These problems are serious enough that we should detect and delete such INI files. + // Problem 1 is easy to detect, but due to the nature of problem 2, it's unfortunately not + // possible to know which lines were added intentionally by the user and which lines were added + // unintentionally, which is why we have to delete the whole file in order to fix everything. + return if (!isGameSpecific) false else NativeConfig.exists( + NativeConfig.LAYER_LOCAL_GAME, + FILE_DOLPHIN, + SECTION_INI_INTERFACE, + "ThemeName" + ) + } + + override fun close() { + if (isGameSpecific) { + NativeConfig.unloadGameInis() + } + } + + companion object { + const val FILE_DOLPHIN = "Dolphin" + const val FILE_SYSCONF = "SYSCONF" + const val FILE_GFX = "GFX" + const val FILE_LOGGER = "Logger" + const val FILE_WIIMOTE = "WiimoteNew" + const val FILE_GAME_SETTINGS_ONLY = "GameSettingsOnly" + const val SECTION_INI_ANDROID = "Android" + const val SECTION_INI_ANDROID_OVERLAY_BUTTONS = "AndroidOverlayButtons" + const val SECTION_INI_GENERAL = "General" + const val SECTION_INI_CORE = "Core" + const val SECTION_INI_INTERFACE = "Interface" + const val SECTION_INI_DSP = "DSP" + const val SECTION_LOGGER_LOGS = "Logs" + const val SECTION_LOGGER_OPTIONS = "Options" + const val SECTION_GFX_SETTINGS = "Settings" + const val SECTION_GFX_ENHANCEMENTS = "Enhancements" + const val SECTION_GFX_HACKS = "Hacks" + const val SECTION_DEBUG = "Debug" + const val SECTION_EMULATED_USB_DEVICES = "EmulatedUSBDevices" + const val SECTION_STEREOSCOPY = "Stereoscopy" + const val SECTION_ANALYTICS = "Analytics" + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/StringSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/StringSetting.java deleted file mode 100644 index c72f26aa0f..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/StringSetting.java +++ /dev/null @@ -1,113 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model; - -import androidx.annotation.NonNull; - -import org.dolphinemu.dolphinemu.NativeLibrary; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -public enum StringSetting implements AbstractStringSetting -{ - // These entries have the same names and order as in C++, just for consistency. - - MAIN_DEFAULT_ISO(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "DefaultISO", ""), - - MAIN_BBA_MAC(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "BBA_MAC", ""), - MAIN_BBA_XLINK_IP(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "BBA_XLINK_IP", ""), - - // Schthack PSO Server - https://schtserv.com/ - MAIN_BBA_BUILTIN_DNS(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "BBA_BUILTIN_DNS", - "149.56.167.128"), - - MAIN_CUSTOM_RTC_VALUE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "CustomRTCValue", - "0x386d4380"), - - MAIN_GFX_BACKEND(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "GFXBackend", - NativeLibrary.GetDefaultGraphicsBackendName()), - - MAIN_DUMP_PATH(Settings.FILE_DOLPHIN, Settings.SECTION_INI_GENERAL, "DumpPath", ""), - MAIN_LOAD_PATH(Settings.FILE_DOLPHIN, Settings.SECTION_INI_GENERAL, "LoadPath", ""), - MAIN_RESOURCEPACK_PATH(Settings.FILE_DOLPHIN, Settings.SECTION_INI_GENERAL, "ResourcePackPath", - ""), - MAIN_FS_PATH(Settings.FILE_DOLPHIN, Settings.SECTION_INI_GENERAL, "NANDRootPath", ""), - MAIN_WII_SD_CARD_IMAGE_PATH(Settings.FILE_DOLPHIN, Settings.SECTION_INI_GENERAL, "WiiSDCardPath", - ""), - MAIN_WII_SD_CARD_SYNC_FOLDER_PATH(Settings.FILE_DOLPHIN, Settings.SECTION_INI_GENERAL, - "WiiSDCardSyncFolder", ""), - MAIN_WFS_PATH(Settings.FILE_DOLPHIN, Settings.SECTION_INI_GENERAL, "WFSPath", ""), - - GFX_ENHANCE_POST_SHADER(Settings.FILE_GFX, Settings.SECTION_GFX_ENHANCEMENTS, - "PostProcessingShader", ""); - - private static final StringSetting[] NOT_RUNTIME_EDITABLE_ARRAY = new StringSetting[]{ - MAIN_CUSTOM_RTC_VALUE, - MAIN_GFX_BACKEND, - }; - - private static final Set NOT_RUNTIME_EDITABLE = - new HashSet<>(Arrays.asList(NOT_RUNTIME_EDITABLE_ARRAY)); - - private final String mFile; - private final String mSection; - private final String mKey; - private final String mDefaultValue; - - StringSetting(String file, String section, String key, String defaultValue) - { - mFile = file; - mSection = section; - mKey = key; - mDefaultValue = defaultValue; - } - - @Override - public boolean isOverridden() - { - return NativeConfig.isOverridden(mFile, mSection, mKey); - } - - @Override - public boolean isRuntimeEditable() - { - for (StringSetting setting : NOT_RUNTIME_EDITABLE) - { - if (setting == this) - return false; - } - - return NativeConfig.isSettingSaveable(mFile, mSection, mKey); - } - - @Override - public boolean delete(@NonNull Settings settings) - { - return NativeConfig.deleteKey(settings.getWriteLayer(), mFile, mSection, mKey); - } - - @NonNull @Override - public String getString() - { - if (!NativeConfig.isSettingSaveable(mFile, mSection, mKey)) - { - throw new UnsupportedOperationException( - "Unsupported setting: " + mFile + ", " + mSection + ", " + mKey); - } - - return NativeConfig.getString(NativeConfig.LAYER_ACTIVE, mFile, mSection, mKey, mDefaultValue); - } - - @Override - public void setString(@NonNull Settings settings, @NonNull String newValue) - { - NativeConfig.setString(settings.getWriteLayer(), mFile, mSection, mKey, newValue); - } - - public void setString(int layer, String newValue) - { - NativeConfig.setString(layer, mFile, mSection, mKey, newValue); - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/StringSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/StringSetting.kt new file mode 100644 index 0000000000..bb479267c9 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/StringSetting.kt @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model + +import org.dolphinemu.dolphinemu.NativeLibrary +import java.util.* + +enum class StringSetting( + private val file: String, + private val section: String, + private val key: String, + private val defaultValue: String +) : AbstractStringSetting { + // These entries have the same names and order as in C++, just for consistency. + MAIN_DEFAULT_ISO(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "DefaultISO", ""), + MAIN_BBA_MAC(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "BBA_MAC", ""), + MAIN_BBA_XLINK_IP(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "BBA_XLINK_IP", ""), + + // Schthack PSO Server - https://schtserv.com/ + MAIN_BBA_BUILTIN_DNS( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_CORE, + "BBA_BUILTIN_DNS", + "149.56.167.128" + ), + MAIN_CUSTOM_RTC_VALUE( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_CORE, + "CustomRTCValue", + "0x386d4380" + ), + MAIN_GFX_BACKEND( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_CORE, + "GFXBackend", + NativeLibrary.GetDefaultGraphicsBackendName() + ), + MAIN_DUMP_PATH(Settings.FILE_DOLPHIN, Settings.SECTION_INI_GENERAL, "DumpPath", ""), + MAIN_LOAD_PATH(Settings.FILE_DOLPHIN, Settings.SECTION_INI_GENERAL, "LoadPath", ""), + MAIN_RESOURCEPACK_PATH( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_GENERAL, + "ResourcePackPath", + "" + ), + MAIN_FS_PATH(Settings.FILE_DOLPHIN, Settings.SECTION_INI_GENERAL, "NANDRootPath", ""), + MAIN_WII_SD_CARD_IMAGE_PATH( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_GENERAL, + "WiiSDCardPath", + "" + ), + MAIN_WII_SD_CARD_SYNC_FOLDER_PATH( + Settings.FILE_DOLPHIN, + Settings.SECTION_INI_GENERAL, + "WiiSDCardSyncFolder", + "" + ), + MAIN_WFS_PATH(Settings.FILE_DOLPHIN, Settings.SECTION_INI_GENERAL, "WFSPath", ""), + GFX_ENHANCE_POST_SHADER( + Settings.FILE_GFX, + Settings.SECTION_GFX_ENHANCEMENTS, + "PostProcessingShader", + "" + ); + + override val isOverridden: Boolean + get() = NativeConfig.isOverridden(file, section, key) + + override val isRuntimeEditable: Boolean + get() { + for (setting in NOT_RUNTIME_EDITABLE) { + if (setting == this) return false + } + return NativeConfig.isSettingSaveable(file, section, key) + } + + override fun delete(settings: Settings): Boolean { + return NativeConfig.deleteKey(settings.writeLayer, file, section, key) + } + + override val string: String + get() = if (!NativeConfig.isSettingSaveable(file, section, key)) { + throw UnsupportedOperationException("Unsupported setting: $file, $section, $key") + } else NativeConfig.getString(NativeConfig.LAYER_ACTIVE, file, section, key, defaultValue) + + override fun setString(settings: Settings, newValue: String) { + NativeConfig.setString(settings.writeLayer, file, section, key, newValue) + } + + fun setString(layer: Int, newValue: String?) { + NativeConfig.setString(layer, file, section, key, newValue) + } + + companion object { + private val NOT_RUNTIME_EDITABLE_ARRAY = arrayOf( + MAIN_CUSTOM_RTC_VALUE, + MAIN_GFX_BACKEND + ) + + private val NOT_RUNTIME_EDITABLE: Set = + HashSet(listOf(*NOT_RUNTIME_EDITABLE_ARRAY)) + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/DateTimeChoiceSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/DateTimeChoiceSetting.kt index d1d411f80e..1cd15b50ee 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/DateTimeChoiceSetting.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/DateTimeChoiceSetting.kt @@ -3,29 +3,22 @@ package org.dolphinemu.dolphinemu.features.settings.model.view import android.content.Context -import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting import org.dolphinemu.dolphinemu.features.settings.model.AbstractStringSetting import org.dolphinemu.dolphinemu.features.settings.model.Settings class DateTimeChoiceSetting( context: Context, - private val setting: AbstractStringSetting, + override val setting: AbstractStringSetting, nameId: Int, descriptionId: Int ) : SettingsItem(context, nameId, descriptionId) { - override fun getType(): Int { - return TYPE_DATETIME_CHOICE - } - - override fun getSetting(): AbstractSetting { - return setting - } + override val type: Int = TYPE_DATETIME_CHOICE fun setSelectedValue(settings: Settings, selection: String) { setting.setString(settings, selection) } fun getSelectedValue(): String { - return setting.getString() + return setting.string } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FilePicker.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FilePicker.java deleted file mode 100644 index 4b0ae749a9..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FilePicker.java +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model.view; - -import android.content.Context; - -import androidx.annotation.Nullable; - -import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting; -import org.dolphinemu.dolphinemu.features.settings.model.AbstractStringSetting; -import org.dolphinemu.dolphinemu.features.settings.model.Settings; - -public final class FilePicker extends SettingsItem -{ - private AbstractStringSetting mSetting; - private int mRequestType; - private String mDefaultPathRelativeToUserDirectory; - - public FilePicker(Context context, AbstractStringSetting setting, int titleId, int descriptionId, - int requestType, @Nullable String defaultPathRelativeToUserDirectory) - { - super(context, titleId, descriptionId); - mSetting = setting; - mRequestType = requestType; - mDefaultPathRelativeToUserDirectory = defaultPathRelativeToUserDirectory; - } - - public String getSelectedValue() - { - return mSetting.getString(); - } - - public void setSelectedValue(Settings settings, String selection) - { - mSetting.setString(settings, selection); - } - - public int getRequestType() - { - return mRequestType; - } - - @Nullable - public String getDefaultPathRelativeToUserDirectory() - { - return mDefaultPathRelativeToUserDirectory; - } - - @Override - public int getType() - { - return TYPE_FILE_PICKER; - } - - @Override - public AbstractSetting getSetting() - { - return mSetting; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FilePicker.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FilePicker.kt new file mode 100644 index 0000000000..2b5bd70162 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FilePicker.kt @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model.view + +import android.content.Context +import org.dolphinemu.dolphinemu.features.settings.model.AbstractStringSetting +import org.dolphinemu.dolphinemu.features.settings.model.Settings + +class FilePicker( + context: Context, + override var setting: AbstractStringSetting, + titleId: Int, + descriptionId: Int, + val requestType: Int, + val defaultPathRelativeToUserDirectory: String? +) : SettingsItem(context, titleId, descriptionId) { + override val type: Int = TYPE_FILE_PICKER + + fun getSelectedValue() : String { + return setting.string + } + + fun setSelectedValue(settings: Settings, selection: String) { + setting.setString(settings, selection) + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FloatSliderSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FloatSliderSetting.java deleted file mode 100644 index 29432aaaa7..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FloatSliderSetting.java +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model.view; - -import android.content.Context; - -import org.dolphinemu.dolphinemu.features.settings.model.AbstractFloatSetting; -import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting; -import org.dolphinemu.dolphinemu.features.settings.model.Settings; - -public class FloatSliderSetting extends SliderSetting -{ - protected AbstractFloatSetting mSetting; - - public FloatSliderSetting(Context context, AbstractFloatSetting setting, int titleId, - int descriptionId, int min, int max, String units, int stepSize) - { - super(context, titleId, descriptionId, min, max, units, stepSize); - mSetting = setting; - } - - public FloatSliderSetting(AbstractFloatSetting setting, CharSequence name, - CharSequence description, int min, int max, String units) - { - super(name, description, min, max, units); - mSetting = setting; - } - - public int getSelectedValue() - { - return Math.round(mSetting.getFloat()); - } - - public void setSelectedValue(Settings settings, float selection) - { - mSetting.setFloat(settings, selection); - } - - @Override - public AbstractSetting getSetting() - { - return mSetting; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FloatSliderSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FloatSliderSetting.kt new file mode 100644 index 0000000000..5b2ee40fe7 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FloatSliderSetting.kt @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model.view + +import android.content.Context +import org.dolphinemu.dolphinemu.features.settings.model.AbstractFloatSetting +import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting +import org.dolphinemu.dolphinemu.features.settings.model.Settings +import kotlin.math.roundToInt + +open class FloatSliderSetting : SliderSetting { + var floatSetting: AbstractFloatSetting + + override val setting: AbstractSetting + get() = floatSetting + + constructor( + context: Context, + setting: AbstractFloatSetting, + titleId: Int, + descriptionId: Int, + min: Int, + max: Int, + units: String?, + stepSize: Int + ) : super(context, titleId, descriptionId, min, max, units, stepSize) { + floatSetting = setting + } + + constructor( + setting: AbstractFloatSetting, + name: CharSequence, + description: CharSequence?, + min: Int, + max: Int, + units: String? + ) : super(name, description, min, max, units) { + floatSetting = setting + } + + override val selectedValue: Int + get() = floatSetting.float.roundToInt() + + open fun setSelectedValue(settings: Settings?, selection: Float) { + floatSetting.setFloat(settings!!, selection) + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/HeaderSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/HeaderSetting.java deleted file mode 100644 index b65f45837b..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/HeaderSetting.java +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model.view; - -import android.content.Context; - -import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting; - -public class HeaderSetting extends SettingsItem -{ - public HeaderSetting(Context context, int titleId, int descriptionId) - { - super(context, titleId, descriptionId); - } - - public HeaderSetting(CharSequence title, CharSequence description) - { - super(title, description); - } - - @Override - public int getType() - { - return SettingsItem.TYPE_HEADER; - } - - @Override - public AbstractSetting getSetting() - { - return null; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/HeaderSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/HeaderSetting.kt new file mode 100644 index 0000000000..3a166ddddf --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/HeaderSetting.kt @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model.view + +import android.content.Context +import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting + +open class HeaderSetting : SettingsItem { + override val setting: AbstractSetting? = null + + constructor( + context: Context, + titleId: Int, + descriptionId: Int + ) : super(context, titleId, descriptionId) + + constructor(title: CharSequence, description: CharSequence?) : super(title, description) + + override val type: Int = TYPE_HEADER +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/HyperLinkHeaderSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/HyperLinkHeaderSetting.java deleted file mode 100644 index 785e6cd6f6..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/HyperLinkHeaderSetting.java +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model.view; - -import android.content.Context; - -public final class HyperLinkHeaderSetting extends HeaderSetting -{ - public HyperLinkHeaderSetting(Context context, int titleId, int descriptionId) - { - super(context, titleId, descriptionId); - } - - @Override - public int getType() - { - return SettingsItem.TYPE_HYPERLINK_HEADER; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/HyperLinkHeaderSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/HyperLinkHeaderSetting.kt new file mode 100644 index 0000000000..bc1595528b --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/HyperLinkHeaderSetting.kt @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model.view + +import android.content.Context + +class HyperLinkHeaderSetting( + context: Context, + titleId: Int, + descriptionId: Int +) : HeaderSetting(context, titleId, descriptionId) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/InputStringSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/InputStringSetting.java deleted file mode 100644 index 9cd811b82d..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/InputStringSetting.java +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model.view; - -import android.content.Context; - -import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting; -import org.dolphinemu.dolphinemu.features.settings.model.AbstractStringSetting; -import org.dolphinemu.dolphinemu.features.settings.model.Settings; -import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag; - -public class InputStringSetting extends SettingsItem -{ - private AbstractStringSetting mSetting; - - private MenuTag mMenuTag; - - public InputStringSetting(Context context, AbstractStringSetting setting, int titleId, - int descriptionId, MenuTag menuTag) - { - super(context, titleId, descriptionId); - mSetting = setting; - mMenuTag = menuTag; - } - - public InputStringSetting(Context context, AbstractStringSetting setting, int titleId, - int descriptionId) - { - this(context, setting, titleId, descriptionId, null); - } - - public InputStringSetting(Context context, AbstractStringSetting setting, int titleId, - int descriptionId, int choicesId, int valuesId, MenuTag menuTag) - { - super(context, titleId, descriptionId); - mSetting = setting; - mMenuTag = menuTag; - } - - public InputStringSetting(Context context, AbstractStringSetting setting, int titleId, - int descriptionId, int choicesId, int valuesId) - { - this(context, setting, titleId, descriptionId, choicesId, valuesId, null); - } - - public String getSelectedValue() - { - return mSetting.getString(); - } - - public MenuTag getMenuTag() - { - return mMenuTag; - } - - public void setSelectedValue(Settings settings, String selection) - { - mSetting.setString(settings, selection); - } - - @Override - public int getType() - { - return TYPE_STRING; - } - - @Override - public AbstractSetting getSetting() - { - return mSetting; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/InputStringSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/InputStringSetting.kt new file mode 100644 index 0000000000..69bb842be0 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/InputStringSetting.kt @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model.view + +import android.content.Context +import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting +import org.dolphinemu.dolphinemu.features.settings.model.AbstractStringSetting +import org.dolphinemu.dolphinemu.features.settings.model.Settings + +class InputStringSetting( + context: Context, + setting: AbstractStringSetting, + titleId: Int, + descriptionId: Int, +) : SettingsItem(context, titleId, descriptionId) { + override val type: Int = TYPE_STRING + + private var stringSetting: AbstractStringSetting = setting + + override val setting: AbstractSetting + get() = stringSetting + + val selectedValue: String + get() = stringSetting.string + + fun setSelectedValue(settings: Settings, selection: String) { + stringSetting.setString(settings, selection) + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/IntSliderSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/IntSliderSetting.java deleted file mode 100644 index e8d19f9059..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/IntSliderSetting.java +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model.view; - -import android.content.Context; - -import org.dolphinemu.dolphinemu.features.settings.model.AbstractIntSetting; -import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting; -import org.dolphinemu.dolphinemu.features.settings.model.Settings; - -public final class IntSliderSetting extends SliderSetting -{ - private AbstractIntSetting mSetting; - - public IntSliderSetting(Context context, AbstractIntSetting setting, int titleId, - int descriptionId, int min, int max, String units, int stepSize) - { - super(context, titleId, descriptionId, min, max, units, stepSize); - mSetting = setting; - } - - public int getSelectedValue() - { - return mSetting.getInt(); - } - - public void setSelectedValue(Settings settings, int selection) - { - mSetting.setInt(settings, selection); - } - - @Override - public AbstractSetting getSetting() - { - return mSetting; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/IntSliderSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/IntSliderSetting.kt new file mode 100644 index 0000000000..23bcd49317 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/IntSliderSetting.kt @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model.view + +import android.content.Context +import org.dolphinemu.dolphinemu.features.settings.model.AbstractIntSetting +import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting +import org.dolphinemu.dolphinemu.features.settings.model.Settings + +class IntSliderSetting( + context: Context, + private val intSetting: AbstractIntSetting, + titleId: Int, + descriptionId: Int, + min: Int, + max: Int, + units: String?, + stepSize: Int +) : SliderSetting(context, titleId, descriptionId, min, max, units, stepSize) { + override val setting: AbstractSetting + get() = intSetting + + override val selectedValue: Int + get() = intSetting.int + + fun setSelectedValue(settings: Settings?, selection: Int) { + intSetting.setInt(settings!!, selection) + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/InvertedSwitchSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/InvertedSwitchSetting.java deleted file mode 100644 index 621530e980..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/InvertedSwitchSetting.java +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model.view; - -import android.content.Context; - -import org.dolphinemu.dolphinemu.features.settings.model.AbstractBooleanSetting; -import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting; -import org.dolphinemu.dolphinemu.features.settings.model.Settings; - -public final class InvertedSwitchSetting extends SwitchSetting -{ - public InvertedSwitchSetting(Context context, AbstractBooleanSetting setting, int titleId, - int descriptionId) - { - super(context, setting, titleId, descriptionId); - } - - @Override - public boolean isChecked() - { - return !mSetting.getBoolean(); - } - - @Override - public void setChecked(Settings settings, boolean checked) - { - mSetting.setBoolean(settings, !checked); - } - - @Override - public AbstractSetting getSetting() - { - return mSetting; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/InvertedSwitchSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/InvertedSwitchSetting.kt new file mode 100644 index 0000000000..1a423c62f5 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/InvertedSwitchSetting.kt @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model.view + +import android.content.Context +import org.dolphinemu.dolphinemu.features.settings.model.AbstractBooleanSetting +import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting +import org.dolphinemu.dolphinemu.features.settings.model.Settings + +class InvertedSwitchSetting( + context: Context, + setting: AbstractBooleanSetting, + titleId: Int, + descriptionId: Int +) : SwitchSetting(context, setting, titleId, descriptionId) { + override val setting: AbstractSetting + get() = booleanSetting + + override val isChecked: Boolean + get() = !booleanSetting.boolean + + override fun setChecked(settings: Settings?, checked: Boolean) { + booleanSetting.setBoolean(settings!!, !checked) + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/LogSwitchSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/LogSwitchSetting.java deleted file mode 100644 index 910c8fb171..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/LogSwitchSetting.java +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model.view; - -import org.dolphinemu.dolphinemu.features.settings.model.AdHocBooleanSetting; -import org.dolphinemu.dolphinemu.features.settings.model.Settings; - -public class LogSwitchSetting extends SwitchSetting -{ - String mKey; - - public LogSwitchSetting(String key, CharSequence title, CharSequence description) - { - super(new AdHocBooleanSetting(Settings.FILE_LOGGER, Settings.SECTION_LOGGER_LOGS, key, false), - title, description); - mKey = key; - } - - public String getKey() - { - return mKey; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/LogSwitchSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/LogSwitchSetting.kt new file mode 100644 index 0000000000..1bad7bff9f --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/LogSwitchSetting.kt @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model.view + +import org.dolphinemu.dolphinemu.features.settings.model.AdHocBooleanSetting +import org.dolphinemu.dolphinemu.features.settings.model.Settings + +class LogSwitchSetting( + var key: String, + title: CharSequence?, + description: CharSequence? +) : SwitchSetting( + AdHocBooleanSetting( + Settings.FILE_LOGGER, + Settings.SECTION_LOGGER_LOGS, + key, + false + ), + title, + description +) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/PercentSliderSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/PercentSliderSetting.java deleted file mode 100644 index 834b10e077..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/PercentSliderSetting.java +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model.view; - -import android.content.Context; - -import org.dolphinemu.dolphinemu.features.settings.model.AbstractFloatSetting; -import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting; -import org.dolphinemu.dolphinemu.features.settings.model.Settings; - -public final class PercentSliderSetting extends FloatSliderSetting -{ - public PercentSliderSetting(Context context, AbstractFloatSetting setting, int titleId, - int descriptionId, int min, int max, String units, int stepSize) - { - super(context, setting, titleId, descriptionId, min, max, units, stepSize); - } - - @Override - public int getSelectedValue() - { - return Math.round(mSetting.getFloat() * 100); - } - - @Override - public void setSelectedValue(Settings settings, float selection) - { - mSetting.setFloat(settings, selection / 100); - } - - @Override - public AbstractSetting getSetting() - { - return mSetting; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/PercentSliderSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/PercentSliderSetting.kt new file mode 100644 index 0000000000..db6454b0c5 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/PercentSliderSetting.kt @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model.view + +import android.content.Context +import org.dolphinemu.dolphinemu.features.settings.model.AbstractFloatSetting +import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting +import org.dolphinemu.dolphinemu.features.settings.model.Settings +import kotlin.math.roundToInt + +class PercentSliderSetting( + context: Context, + override val setting: AbstractFloatSetting, + titleId: Int, + descriptionId: Int, + min: Int, + max: Int, + units: String?, + stepSize: Int +) : FloatSliderSetting(context, setting, titleId, descriptionId, min, max, units, stepSize) { + override val selectedValue: Int + get() = (floatSetting.float * 100).roundToInt() + + override fun setSelectedValue(settings: Settings?, selection: Float) { + floatSetting.setFloat(settings!!, selection / 100) + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/RunRunnable.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/RunRunnable.java deleted file mode 100644 index c83d4e292e..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/RunRunnable.java +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model.view; - -import android.content.Context; - -import org.dolphinemu.dolphinemu.NativeLibrary; -import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting; - -public final class RunRunnable extends SettingsItem -{ - private final int mAlertText; - private final int mToastTextAfterRun; - private final boolean mWorksDuringEmulation; - private final Runnable mRunnable; - - public RunRunnable(Context context, int titleId, int descriptionId, int alertText, - int toastTextAfterRun, boolean worksDuringEmulation, Runnable runnable) - { - super(context, titleId, descriptionId); - mAlertText = alertText; - mToastTextAfterRun = toastTextAfterRun; - mWorksDuringEmulation = worksDuringEmulation; - mRunnable = runnable; - } - - public int getAlertText() - { - return mAlertText; - } - - public int getToastTextAfterRun() - { - return mToastTextAfterRun; - } - - public Runnable getRunnable() - { - return mRunnable; - } - - @Override - public int getType() - { - return TYPE_RUN_RUNNABLE; - } - - @Override - public AbstractSetting getSetting() - { - return null; - } - - @Override - public boolean isEditable() - { - return mWorksDuringEmulation || !NativeLibrary.IsRunning(); - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/RunRunnable.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/RunRunnable.kt new file mode 100644 index 0000000000..f9d5786e22 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/RunRunnable.kt @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model.view + +import android.content.Context +import org.dolphinemu.dolphinemu.NativeLibrary +import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting + +class RunRunnable( + context: Context, + titleId: Int, + descriptionId: Int, + val alertText: Int, + val toastTextAfterRun: Int, + private val worksDuringEmulation: Boolean, + val runnable: Runnable +) : SettingsItem(context, titleId, descriptionId) { + override val type: Int = TYPE_RUN_RUNNABLE + + override val setting: AbstractSetting? = null + + override val isEditable: Boolean + get() = worksDuringEmulation || !NativeLibrary.IsRunning() +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.java deleted file mode 100644 index 7d757c3e69..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.java +++ /dev/null @@ -1,107 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model.view; - -import android.content.Context; - -import org.dolphinemu.dolphinemu.NativeLibrary; -import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting; -import org.dolphinemu.dolphinemu.features.settings.model.Settings; -import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter; - -/** - * ViewModel abstraction for an Item in the RecyclerView powering SettingsFragments. - * Most of them correspond to a single line in an INI file, but there are a few with multiple - * analogues and a few with none (Headers, for example, do not correspond to anything on disk.) - */ -public abstract class SettingsItem -{ - public static final int TYPE_HEADER = 0; - public static final int TYPE_SWITCH = 1; - public static final int TYPE_SINGLE_CHOICE = 2; - public static final int TYPE_SLIDER = 3; - public static final int TYPE_SUBMENU = 4; - public static final int TYPE_INPUT_MAPPING_CONTROL = 5; - public static final int TYPE_STRING_SINGLE_CHOICE = 6; - public static final int TYPE_SINGLE_CHOICE_DYNAMIC_DESCRIPTIONS = 8; - public static final int TYPE_FILE_PICKER = 9; - public static final int TYPE_RUN_RUNNABLE = 10; - public static final int TYPE_STRING = 11; - public static final int TYPE_HYPERLINK_HEADER = 12; - public static final int TYPE_DATETIME_CHOICE = 13; - - private final CharSequence mName; - private final CharSequence mDescription; - - /** - * Base constructor. - * - * @param name A text string to be displayed as this setting's name. - * @param description A text string to be displayed as this setting's description. - */ - public SettingsItem(CharSequence name, CharSequence description) - { - mName = name; - mDescription = description; - } - - /** - * @param nameId Resource ID for a text string to be displayed as this setting's name. - * @param descriptionId Resource ID for a text string to be displayed as this setting's description. - */ - public SettingsItem(Context context, int nameId, int descriptionId) - { - mName = nameId == 0 ? "" : context.getText(nameId); - mDescription = descriptionId == 0 ? "" : context.getText(descriptionId); - } - - public CharSequence getName() - { - return mName; - } - - public CharSequence getDescription() - { - return mDescription; - } - - /** - * Used by {@link SettingsAdapter}'s onCreateViewHolder() - * method to determine which type of ViewHolder should be created. - * - * @return An integer (ideally, one of the constants defined in this file) - */ - public abstract int getType(); - - protected abstract AbstractSetting getSetting(); - - public boolean isOverridden() - { - AbstractSetting setting = getSetting(); - return setting != null && setting.isOverridden(); - } - - public boolean isEditable() - { - if (!NativeLibrary.IsRunning()) - return true; - - AbstractSetting setting = getSetting(); - return setting != null && setting.isRuntimeEditable(); - } - - public boolean hasSetting() - { - return getSetting() != null; - } - - public boolean canClear() - { - return hasSetting(); - } - - public void clear(Settings settings) - { - getSetting().delete(settings); - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.kt new file mode 100644 index 0000000000..2180cbe029 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.kt @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model.view + +import android.content.Context +import org.dolphinemu.dolphinemu.NativeLibrary +import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting +import org.dolphinemu.dolphinemu.features.settings.model.Settings + +/** + * ViewModel abstraction for an Item in the RecyclerView powering SettingsFragments. + * Most of them correspond to a single line in an INI file, but there are a few with multiple + * analogues and a few with none (Headers, for example, do not correspond to anything on disk.) + */ +abstract class SettingsItem { + val name: CharSequence + val description: CharSequence? + + /** + * Base constructor. + * + * @param name A text string to be displayed as this Setting's name. + * @param description A text string to be displayed as this Setting's description. + */ + constructor(name: CharSequence, description: CharSequence?) { + this.name = name + this.description = description + } + + /** + * @param nameId Resource ID for a text string to be displayed as this Setting's name. + * @param descriptionId Resource ID for a text string to be displayed as this Setting's description. + */ + constructor(context: Context, nameId: Int, descriptionId: Int) { + name = if (nameId == 0) "" else context.getText(nameId) + description = if (descriptionId == 0) "" else context.getText(descriptionId) + } + + /** + * Used by [SettingsAdapter]'s onCreateViewHolder() + * method to determine which type of ViewHolder should be created. + * + * @return An integer (ideally, one of the constants defined in this file) + */ + abstract val type: Int + + abstract val setting: AbstractSetting? + + val isOverridden: Boolean + get() { + val setting = setting + return setting != null && setting.isOverridden + } + + open val isEditable: Boolean + get() { + if (!NativeLibrary.IsRunning()) return true + val setting = setting + return setting != null && setting.isRuntimeEditable + } + + private fun hasSetting(): Boolean { + return setting != null + } + + open fun canClear(): Boolean { + return hasSetting() + } + + open fun clear(settings: Settings) { + setting!!.delete(settings) + } + + companion object { + const val TYPE_HEADER = 0 + const val TYPE_SWITCH = 1 + const val TYPE_SINGLE_CHOICE = 2 + const val TYPE_SLIDER = 3 + const val TYPE_SUBMENU = 4 + const val TYPE_INPUT_MAPPING_CONTROL = 5 + const val TYPE_STRING_SINGLE_CHOICE = 6 + const val TYPE_SINGLE_CHOICE_DYNAMIC_DESCRIPTIONS = 8 + const val TYPE_FILE_PICKER = 9 + const val TYPE_RUN_RUNNABLE = 10 + const val TYPE_STRING = 11 + const val TYPE_HYPERLINK_HEADER = 12 + const val TYPE_DATETIME_CHOICE = 13 + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SingleChoiceSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SingleChoiceSetting.java deleted file mode 100644 index 1569785cd3..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SingleChoiceSetting.java +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model.view; - -import android.content.Context; - -import org.dolphinemu.dolphinemu.features.settings.model.AbstractIntSetting; -import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting; -import org.dolphinemu.dolphinemu.features.settings.model.Settings; -import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag; - -public final class SingleChoiceSetting extends SettingsItem -{ - private AbstractIntSetting mSetting; - - private int mChoicesId; - private int mValuesId; - private MenuTag menuTag; - - public SingleChoiceSetting(Context context, AbstractIntSetting setting, int titleId, - int descriptionId, int choicesId, int valuesId, MenuTag menuTag) - { - super(context, titleId, descriptionId); - mSetting = setting; - mValuesId = valuesId; - mChoicesId = choicesId; - this.menuTag = menuTag; - } - - public SingleChoiceSetting(Context context, AbstractIntSetting setting, int titleId, - int descriptionId, int choicesId, int valuesId) - { - this(context, setting, titleId, descriptionId, choicesId, valuesId, null); - } - - public int getChoicesId() - { - return mChoicesId; - } - - public int getValuesId() - { - return mValuesId; - } - - public int getSelectedValue() - { - return mSetting.getInt(); - } - - public MenuTag getMenuTag() - { - return menuTag; - } - - public void setSelectedValue(Settings settings, int selection) - { - mSetting.setInt(settings, selection); - } - - @Override - public int getType() - { - return TYPE_SINGLE_CHOICE; - } - - @Override - public AbstractSetting getSetting() - { - return mSetting; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SingleChoiceSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SingleChoiceSetting.kt new file mode 100644 index 0000000000..cd9f3ddb64 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SingleChoiceSetting.kt @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +package org.dolphinemu.dolphinemu.features.settings.model.view + +import android.content.Context +import org.dolphinemu.dolphinemu.features.settings.model.AbstractIntSetting +import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting +import org.dolphinemu.dolphinemu.features.settings.model.Settings +import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag + +class SingleChoiceSetting( + context: Context, + private val intSetting: AbstractIntSetting, + titleId: Int, + descriptionId: Int, + val choicesId: Int, + val valuesId: Int, + val menuTag: MenuTag? = null +) : SettingsItem(context, titleId, descriptionId) { + override val type: Int = TYPE_SINGLE_CHOICE + + override val setting: AbstractSetting + get() = intSetting + + val selectedValue: Int + get() = intSetting.int + + fun setSelectedValue(settings: Settings?, selection: Int) { + intSetting.setInt(settings!!, selection) + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SingleChoiceSettingDynamicDescriptions.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SingleChoiceSettingDynamicDescriptions.java deleted file mode 100644 index b59e535f87..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SingleChoiceSettingDynamicDescriptions.java +++ /dev/null @@ -1,89 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model.view; - -import android.content.Context; - -import org.dolphinemu.dolphinemu.features.settings.model.AbstractIntSetting; -import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting; -import org.dolphinemu.dolphinemu.features.settings.model.Settings; -import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag; - -public final class SingleChoiceSettingDynamicDescriptions extends SettingsItem -{ - private AbstractIntSetting mSetting; - - private int mChoicesId; - private int mValuesId; - private int mDescriptionChoicesId; - private int mDescriptionValuesId; - private MenuTag menuTag; - - public SingleChoiceSettingDynamicDescriptions(Context context, AbstractIntSetting setting, - int titleId, int descriptionId, int choicesId, int valuesId, int descriptionChoicesId, - int descriptionValuesId, MenuTag menuTag) - { - super(context, titleId, descriptionId); - mSetting = setting; - mValuesId = valuesId; - mChoicesId = choicesId; - mDescriptionChoicesId = descriptionChoicesId; - mDescriptionValuesId = descriptionValuesId; - this.menuTag = menuTag; - } - - public SingleChoiceSettingDynamicDescriptions(Context context, AbstractIntSetting setting, - int titleId, int descriptionId, int choicesId, int valuesId, int descriptionChoicesId, - int descriptionValuesId) - { - this(context, setting, titleId, descriptionId, choicesId, valuesId, descriptionChoicesId, - descriptionValuesId, null); - } - - public int getChoicesId() - { - return mChoicesId; - } - - public int getValuesId() - { - return mValuesId; - } - - public int getDescriptionChoicesId() - { - return mDescriptionChoicesId; - } - - public int getDescriptionValuesId() - { - return mDescriptionValuesId; - } - - public int getSelectedValue() - { - return mSetting.getInt(); - } - - public MenuTag getMenuTag() - { - return menuTag; - } - - public void setSelectedValue(Settings settings, int selection) - { - mSetting.setInt(settings, selection); - } - - @Override - public int getType() - { - return TYPE_SINGLE_CHOICE_DYNAMIC_DESCRIPTIONS; - } - - @Override - public AbstractSetting getSetting() - { - return mSetting; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SingleChoiceSettingDynamicDescriptions.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SingleChoiceSettingDynamicDescriptions.kt new file mode 100644 index 0000000000..e24b2f4b08 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SingleChoiceSettingDynamicDescriptions.kt @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model.view + +import android.content.Context +import org.dolphinemu.dolphinemu.features.settings.model.AbstractIntSetting +import org.dolphinemu.dolphinemu.features.settings.model.Settings + +class SingleChoiceSettingDynamicDescriptions( + context: Context, + override val setting: AbstractIntSetting, + titleId: Int, + descriptionId: Int, + val choicesId: Int, + val valuesId: Int, + val descriptionChoicesId: Int, + val descriptionValuesId: Int, +) : SettingsItem(context, titleId, descriptionId) { + override val type: Int = TYPE_SINGLE_CHOICE_DYNAMIC_DESCRIPTIONS + + val selectedValue: Int + get() = setting.int + + fun setSelectedValue(settings: Settings, selection: Int) { + setting.setInt(settings, selection) + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SliderSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SliderSetting.java deleted file mode 100644 index fccbe0a0b1..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SliderSetting.java +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model.view; - -import android.content.Context; - -import org.dolphinemu.dolphinemu.features.settings.model.Settings; - -public abstract class SliderSetting extends SettingsItem -{ - private int mMin; - private int mMax; - private String mUnits; - private int mStepSize; - - public SliderSetting(Context context, int nameId, int descriptionId, int min, int max, - String units, int stepSize) - { - super(context, nameId, descriptionId); - mMin = min; - mMax = max; - mUnits = units; - mStepSize = stepSize; - } - - public SliderSetting(CharSequence name, CharSequence description, int min, int max, String units) - { - super(name, description); - mMin = min; - mMax = max; - mUnits = units; - } - - public abstract int getSelectedValue(); - - public int getMin() - { - return mMin; - } - - public int getMax() - { - return mMax; - } - - public String getUnits() - { - return mUnits; - } - - public int getStepSize() - { - return mStepSize; - } - - @Override - public int getType() - { - return TYPE_SLIDER; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SliderSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SliderSetting.kt new file mode 100644 index 0000000000..f595c4cea3 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SliderSetting.kt @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model.view + +import android.content.Context + +abstract class SliderSetting : SettingsItem { + override val type: Int = TYPE_SLIDER + + var min: Int + private set + var max: Int + private set + var units: String? + private set + var stepSize = 0 + private set + + constructor( + context: Context, + nameId: Int, + descriptionId: Int, + min: Int, + max: Int, + units: String?, + stepSize: Int + ) : super(context, nameId, descriptionId) { + this.min = min + this.max = max + this.units = units + this.stepSize = stepSize + } + + constructor( + name: CharSequence, + description: CharSequence?, + min: Int, + max: Int, + units: String? + ) : super(name, description) { + this.min = min + this.max = max + this.units = units + } + + abstract val selectedValue: Int +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/StringSingleChoiceSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/StringSingleChoiceSetting.java deleted file mode 100644 index e83fe913cd..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/StringSingleChoiceSetting.java +++ /dev/null @@ -1,153 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model.view; - -import android.content.Context; - -import org.dolphinemu.dolphinemu.DolphinApplication; -import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting; -import org.dolphinemu.dolphinemu.features.settings.model.AbstractStringSetting; -import org.dolphinemu.dolphinemu.features.settings.model.Settings; -import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag; - -public class StringSingleChoiceSetting extends SettingsItem -{ - private final AbstractStringSetting mSetting; - - protected String[] mChoices; - protected String[] mValues; - private final MenuTag mMenuTag; - private int mNoChoicesAvailableString = 0; - - public StringSingleChoiceSetting(Context context, AbstractStringSetting setting, int titleId, - int descriptionId, String[] choices, String[] values, MenuTag menuTag) - { - super(context, titleId, descriptionId); - mSetting = setting; - mChoices = choices; - mValues = values; - mMenuTag = menuTag; - } - - public StringSingleChoiceSetting(Context context, AbstractStringSetting setting, int titleId, - int descriptionId, String[] choices, String[] values) - { - this(context, setting, titleId, descriptionId, choices, values, null); - } - - public StringSingleChoiceSetting(Context context, AbstractStringSetting setting, int titleId, - int descriptionId, String[] choices, String[] values, int noChoicesAvailableString) - { - this(context, setting, titleId, descriptionId, choices, values, null); - mNoChoicesAvailableString = noChoicesAvailableString; - } - - public StringSingleChoiceSetting(Context context, AbstractStringSetting setting, int titleId, - int descriptionId, int choicesId, int valuesId, MenuTag menuTag) - { - super(context, titleId, descriptionId); - mSetting = setting; - mChoices = DolphinApplication.getAppContext().getResources().getStringArray(choicesId); - mValues = DolphinApplication.getAppContext().getResources().getStringArray(valuesId); - mMenuTag = menuTag; - } - - public StringSingleChoiceSetting(Context context, AbstractStringSetting setting, int titleId, - int descriptionId, int choicesId, int valuesId) - { - this(context, setting, titleId, descriptionId, choicesId, valuesId, null); - } - - public String[] getChoices() - { - return mChoices; - } - - public String[] getValues() - { - return mValues; - } - - public String getChoiceAt(int index) - { - if (mChoices == null) - return null; - - if (index >= 0 && index < mChoices.length) - { - return mChoices[index]; - } - - return ""; - } - - public String getValueAt(int index) - { - if (mValues == null) - return null; - - if (index >= 0 && index < mValues.length) - { - return mValues[index]; - } - - return ""; - } - - public String getSelectedChoice() - { - return getChoiceAt(getSelectedValueIndex()); - } - - public String getSelectedValue() - { - return mSetting.getString(); - } - - public int getSelectedValueIndex() - { - String selectedValue = getSelectedValue(); - for (int i = 0; i < mValues.length; i++) - { - if (mValues[i].equals(selectedValue)) - { - return i; - } - } - - return -1; - } - - public MenuTag getMenuTag() - { - return mMenuTag; - } - - public int getNoChoicesAvailableString() - { - return mNoChoicesAvailableString; - } - - public void setSelectedValue(Settings settings, String selection) - { - mSetting.setString(settings, selection); - } - - public void refreshChoicesAndValues() - { - } - - @Override - public int getType() - { - return TYPE_STRING_SINGLE_CHOICE; - } - - @Override - public AbstractSetting getSetting() - { - return mSetting; - } -} - - diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/StringSingleChoiceSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/StringSingleChoiceSetting.kt new file mode 100644 index 0000000000..62301e23ef --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/StringSingleChoiceSetting.kt @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model.view + +import android.content.Context +import org.dolphinemu.dolphinemu.DolphinApplication +import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting +import org.dolphinemu.dolphinemu.features.settings.model.AbstractStringSetting +import org.dolphinemu.dolphinemu.features.settings.model.Settings +import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag + +open class StringSingleChoiceSetting : SettingsItem { + override val type: Int = TYPE_SINGLE_CHOICE + + private val stringSetting: AbstractStringSetting? + + override val setting: AbstractSetting? + get() = stringSetting + + var choices: Array? + protected set + var values: Array? + protected set + val menuTag: MenuTag? + var noChoicesAvailableString = 0 + private set + + open val selectedChoice: String? + get() = getChoiceAt(selectedValueIndex) + + open val selectedValue: String + get() = stringSetting!!.string + + @JvmOverloads + constructor( + context: Context, + setting: AbstractStringSetting?, + titleId: Int, + descriptionId: Int, + choices: Array?, + values: Array?, + menuTag: MenuTag? = null + ) : super(context, titleId, descriptionId) { + stringSetting = setting + this.choices = choices + this.values = values + this.menuTag = menuTag + } + + constructor( + context: Context, + setting: AbstractStringSetting, + titleId: Int, + descriptionId: Int, + choices: Array, + values: Array, + noChoicesAvailableString: Int + ) : this(context, setting, titleId, descriptionId, choices, values) { + this.noChoicesAvailableString = noChoicesAvailableString + } + + @JvmOverloads + constructor( + context: Context, + setting: AbstractStringSetting, + titleId: Int, + descriptionId: Int, + choicesId: Int, + valuesId: Int, + menuTag: MenuTag? = null + ) : super(context, titleId, descriptionId) { + stringSetting = setting + choices = DolphinApplication.getAppContext().resources.getStringArray(choicesId) + values = DolphinApplication.getAppContext().resources.getStringArray(valuesId) + this.menuTag = menuTag + } + + fun getChoiceAt(index: Int): String? { + if (choices == null) return null + + return if (index >= 0 && index < choices!!.size) { + choices!![index] + } else "" + } + + fun getValueAt(index: Int): String? { + if (values == null) return null + + return if (index >= 0 && index < values!!.size) { + values!![index] + } else "" + } + + val selectedValueIndex: Int + get() { + val selectedValue = selectedValue + for (i in values!!.indices) { + if (values!![i] == selectedValue) { + return i + } + } + return -1 + } + + open fun setSelectedValue(settings: Settings?, selection: String?) { + stringSetting!!.setString(settings!!, selection!!) + } + + open fun refreshChoicesAndValues() {} +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SubmenuSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SubmenuSetting.java deleted file mode 100644 index ddfb30bfc4..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SubmenuSetting.java +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model.view; - -import android.content.Context; - -import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting; -import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag; - -public final class SubmenuSetting extends SettingsItem -{ - private MenuTag mMenuKey; - - public SubmenuSetting(Context context, int titleId, MenuTag menuKey) - { - super(context, titleId, 0); - mMenuKey = menuKey; - } - - public MenuTag getMenuKey() - { - return mMenuKey; - } - - @Override - public int getType() - { - return TYPE_SUBMENU; - } - - @Override - public AbstractSetting getSetting() - { - return null; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SubmenuSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SubmenuSetting.kt new file mode 100644 index 0000000000..6fe573d7d0 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SubmenuSetting.kt @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model.view + +import android.content.Context +import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting +import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag + +class SubmenuSetting( + context: Context, + titleId: Int, + val menuKey: MenuTag +) : SettingsItem(context, titleId, 0) { + override val type: Int = TYPE_SUBMENU + + override val setting: AbstractSetting? = null +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SwitchSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SwitchSetting.java deleted file mode 100644 index dc7fa44714..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SwitchSetting.java +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model.view; - -import android.content.Context; - -import org.dolphinemu.dolphinemu.features.settings.model.AbstractBooleanSetting; -import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting; -import org.dolphinemu.dolphinemu.features.settings.model.Settings; - -public class SwitchSetting extends SettingsItem -{ - protected AbstractBooleanSetting mSetting; - - public SwitchSetting(Context context, AbstractBooleanSetting setting, int titleId, - int descriptionId) - { - super(context, titleId, descriptionId); - mSetting = setting; - } - - public SwitchSetting(AbstractBooleanSetting setting, CharSequence title, - CharSequence description) - { - super(title, description); - mSetting = setting; - } - - public boolean isChecked() - { - return mSetting.getBoolean(); - } - - public void setChecked(Settings settings, boolean checked) - { - mSetting.setBoolean(settings, checked); - } - - @Override - public int getType() - { - return TYPE_SWITCH; - } - - @Override - public AbstractSetting getSetting() - { - return mSetting; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SwitchSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SwitchSetting.kt new file mode 100644 index 0000000000..5f9d0cb218 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SwitchSetting.kt @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model.view + +import android.content.Context +import org.dolphinemu.dolphinemu.features.settings.model.AbstractBooleanSetting +import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting +import org.dolphinemu.dolphinemu.features.settings.model.Settings + +open class SwitchSetting : SettingsItem { + override val type: Int = TYPE_SWITCH + + protected var booleanSetting: AbstractBooleanSetting + + override val setting: AbstractSetting + get() = booleanSetting + + constructor( + context: Context, + setting: AbstractBooleanSetting, + titleId: Int, + descriptionId: Int + ) : super(context, titleId, descriptionId) { + booleanSetting = setting + } + + constructor( + setting: AbstractBooleanSetting, + title: CharSequence?, + description: CharSequence? + ) : super(title!!, description) { + booleanSetting = setting + } + + open val isChecked: Boolean + get() = booleanSetting.boolean + + open fun setChecked(settings: Settings?, checked: Boolean) { + booleanSetting.setBoolean(settings!!, checked) + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/MenuTag.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/MenuTag.java deleted file mode 100644 index a0a67dd838..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/MenuTag.java +++ /dev/null @@ -1,165 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.ui; - -import androidx.annotation.NonNull; - -import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController; - -public enum MenuTag -{ - SETTINGS("settings"), - CONFIG("config"), - CONFIG_GENERAL("config_general"), - CONFIG_INTERFACE("config_interface"), - CONFIG_AUDIO("config_audio"), - CONFIG_PATHS("config_paths"), - CONFIG_GAME_CUBE("config_gamecube"), - CONFIG_SERIALPORT1("config_serialport1"), - CONFIG_WII("config_wii"), - CONFIG_ADVANCED("config_advanced"), - CONFIG_LOG("config_log"), - DEBUG("debug"), - GRAPHICS("graphics"), - ENHANCEMENTS("enhancements"), - STEREOSCOPY("stereoscopy"), - HACKS("hacks"), - STATISTICS("statistics"), - ADVANCED_GRAPHICS("advanced_graphics"), - GCPAD_TYPE("gc_pad_type"), - WIIMOTE("wiimote"), - WIIMOTE_EXTENSION("wiimote_extension"), - GCPAD_1("gcpad", 0), - GCPAD_2("gcpad", 1), - GCPAD_3("gcpad", 2), - GCPAD_4("gcpad", 3), - WIIMOTE_1("wiimote", 0), - WIIMOTE_2("wiimote", 1), - WIIMOTE_3("wiimote", 2), - WIIMOTE_4("wiimote", 3), - WIIMOTE_EXTENSION_1("wiimote_extension", 0), - WIIMOTE_EXTENSION_2("wiimote_extension", 1), - WIIMOTE_EXTENSION_3("wiimote_extension", 2), - WIIMOTE_EXTENSION_4("wiimote_extension", 3), - WIIMOTE_GENERAL_1("wiimote_general", 0), - WIIMOTE_GENERAL_2("wiimote_general", 1), - WIIMOTE_GENERAL_3("wiimote_general", 2), - WIIMOTE_GENERAL_4("wiimote_general", 3), - WIIMOTE_MOTION_SIMULATION_1("wiimote_motion_simulation", 0), - WIIMOTE_MOTION_SIMULATION_2("wiimote_motion_simulation", 1), - WIIMOTE_MOTION_SIMULATION_3("wiimote_motion_simulation", 2), - WIIMOTE_MOTION_SIMULATION_4("wiimote_motion_simulation", 3), - WIIMOTE_MOTION_INPUT_1("wiimote_motion_input", 0), - WIIMOTE_MOTION_INPUT_2("wiimote_motion_input", 1), - WIIMOTE_MOTION_INPUT_3("wiimote_motion_input", 2), - WIIMOTE_MOTION_INPUT_4("wiimote_motion_input", 3); - - private String tag; - private int subType = -1; - - MenuTag(String tag) - { - this.tag = tag; - } - - MenuTag(String tag, int subtype) - { - this.tag = tag; - this.subType = subtype; - } - - @NonNull - @Override - public String toString() - { - if (subType != -1) - { - return tag + subType; - } - - return tag; - } - - public String getTag() - { - return tag; - } - - public int getSubType() - { - return subType; - } - - public EmulatedController getCorrespondingEmulatedController() - { - if (isGCPadMenu()) - return EmulatedController.getGcPad(getSubType()); - else if (isWiimoteMenu()) - return EmulatedController.getWiimote(getSubType()); - else - throw new UnsupportedOperationException(); - } - - public boolean isSerialPort1Menu() - { - return this == CONFIG_SERIALPORT1; - } - - public boolean isGCPadMenu() - { - return this == GCPAD_1 || this == GCPAD_2 || this == GCPAD_3 || this == GCPAD_4; - } - - public boolean isWiimoteMenu() - { - return this == WIIMOTE_1 || this == WIIMOTE_2 || this == WIIMOTE_3 || this == WIIMOTE_4; - } - - public boolean isWiimoteExtensionMenu() - { - return this == WIIMOTE_EXTENSION_1 || this == WIIMOTE_EXTENSION_2 - || this == WIIMOTE_EXTENSION_3 || this == WIIMOTE_EXTENSION_4; - } - - public static MenuTag getGCPadMenuTag(int subtype) - { - return getMenuTag("gcpad", subtype); - } - - public static MenuTag getWiimoteMenuTag(int subtype) - { - return getMenuTag("wiimote", subtype); - } - - public static MenuTag getWiimoteExtensionMenuTag(int subtype) - { - return getMenuTag("wiimote_extension", subtype); - } - - public static MenuTag getWiimoteGeneralMenuTag(int subtype) - { - return getMenuTag("wiimote_general", subtype); - } - - public static MenuTag getWiimoteMotionSimulationMenuTag(int subtype) - { - return getMenuTag("wiimote_motion_simulation", subtype); - } - - public static MenuTag getWiimoteMotionInputMenuTag(int subtype) - { - return getMenuTag("wiimote_motion_input", subtype); - } - - private static MenuTag getMenuTag(String tag, int subtype) - { - for (MenuTag menuTag : MenuTag.values()) - { - if (menuTag.tag.equals(tag) && menuTag.subType == subtype) - return menuTag; - } - - throw new IllegalArgumentException("You are asking for a menu that is not available or " + - "passing a wrong subtype"); - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/MenuTag.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/MenuTag.kt new file mode 100644 index 0000000000..38c162d6fd --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/MenuTag.kt @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.ui + +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController + +enum class MenuTag { + SETTINGS("settings"), + CONFIG("config"), + CONFIG_GENERAL("config_general"), + CONFIG_INTERFACE("config_interface"), + CONFIG_AUDIO("config_audio"), + CONFIG_PATHS("config_paths"), + CONFIG_GAME_CUBE("config_gamecube"), + CONFIG_SERIALPORT1("config_serialport1"), + CONFIG_WII("config_wii"), + CONFIG_ADVANCED("config_advanced"), + CONFIG_LOG("config_log"), + DEBUG("debug"), + GRAPHICS("graphics"), + ENHANCEMENTS("enhancements"), + STEREOSCOPY("stereoscopy"), + HACKS("hacks"), + STATISTICS("statistics"), + ADVANCED_GRAPHICS("advanced_graphics"), + GCPAD_TYPE("gc_pad_type"), + WIIMOTE("wiimote"), + WIIMOTE_EXTENSION("wiimote_extension"), + GCPAD_1("gcpad", 0), + GCPAD_2("gcpad", 1), + GCPAD_3("gcpad", 2), + GCPAD_4("gcpad", 3), + WIIMOTE_1("wiimote", 0), + WIIMOTE_2("wiimote", 1), + WIIMOTE_3("wiimote", 2), + WIIMOTE_4("wiimote", 3), + WIIMOTE_EXTENSION_1("wiimote_extension", 0), + WIIMOTE_EXTENSION_2("wiimote_extension", 1), + WIIMOTE_EXTENSION_3("wiimote_extension", 2), + WIIMOTE_EXTENSION_4("wiimote_extension", 3), + WIIMOTE_GENERAL_1("wiimote_general", 0), + WIIMOTE_GENERAL_2("wiimote_general", 1), + WIIMOTE_GENERAL_3("wiimote_general", 2), + WIIMOTE_GENERAL_4("wiimote_general", 3), + WIIMOTE_MOTION_SIMULATION_1("wiimote_motion_simulation", 0), + WIIMOTE_MOTION_SIMULATION_2("wiimote_motion_simulation", 1), + WIIMOTE_MOTION_SIMULATION_3("wiimote_motion_simulation", 2), + WIIMOTE_MOTION_SIMULATION_4("wiimote_motion_simulation", 3), + WIIMOTE_MOTION_INPUT_1("wiimote_motion_input", 0), + WIIMOTE_MOTION_INPUT_2("wiimote_motion_input", 1), + WIIMOTE_MOTION_INPUT_3("wiimote_motion_input", 2), + WIIMOTE_MOTION_INPUT_4("wiimote_motion_input", 3); + + var tag: String + private set + var subType = -1 + private set + + constructor(tag: String) { + this.tag = tag + } + + constructor(tag: String, subtype: Int) { + this.tag = tag + subType = subtype + } + + override fun toString(): String { + return if (subType != -1) { + tag + subType + } else tag + } + + val correspondingEmulatedController: EmulatedController + get() = if (isGCPadMenu) EmulatedController.getGcPad(subType) else if (isWiimoteMenu) EmulatedController.getWiimote( + subType + ) else throw UnsupportedOperationException() + + val isSerialPort1Menu: Boolean + get() = this == CONFIG_SERIALPORT1 + + val isGCPadMenu: Boolean + get() = this == GCPAD_1 || this == GCPAD_2 || this == GCPAD_3 || this == GCPAD_4 + + val isWiimoteMenu: Boolean + get() = this == WIIMOTE_1 || this == WIIMOTE_2 || this == WIIMOTE_3 || this == WIIMOTE_4 + + val isWiimoteExtensionMenu: Boolean + get() = this == WIIMOTE_EXTENSION_1 || this == WIIMOTE_EXTENSION_2 || this == WIIMOTE_EXTENSION_3 || this == WIIMOTE_EXTENSION_4 + + companion object { + @JvmStatic + fun getGCPadMenuTag(subtype: Int): MenuTag { + return getMenuTag("gcpad", subtype) + } + + @JvmStatic + fun getWiimoteMenuTag(subtype: Int): MenuTag { + return getMenuTag("wiimote", subtype) + } + + @JvmStatic + fun getWiimoteExtensionMenuTag(subtype: Int): MenuTag { + return getMenuTag("wiimote_extension", subtype) + } + + @JvmStatic + fun getWiimoteGeneralMenuTag(subtype: Int): MenuTag { + return getMenuTag("wiimote_general", subtype) + } + + @JvmStatic + fun getWiimoteMotionSimulationMenuTag(subtype: Int): MenuTag { + return getMenuTag("wiimote_motion_simulation", subtype) + } + + @JvmStatic + fun getWiimoteMotionInputMenuTag(subtype: Int): MenuTag { + return getMenuTag("wiimote_motion_input", subtype) + } + + private fun getMenuTag(tag: String, subtype: Int): MenuTag { + for (menuTag in values()) { + if (menuTag.tag == tag && menuTag.subType == subtype) return menuTag + } + throw IllegalArgumentException( + "You are asking for a menu that is not available or " + + "passing a wrong subtype" + ) + } + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.java deleted file mode 100644 index 4f1e4bdf45..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.java +++ /dev/null @@ -1,401 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.ui; - -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.provider.Settings; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.View; -import android.widget.Toast; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; -import androidx.core.graphics.Insets; -import androidx.core.view.ViewCompat; -import androidx.core.view.WindowCompat; -import androidx.core.view.WindowInsetsCompat; -import androidx.fragment.app.DialogFragment; -import androidx.fragment.app.FragmentTransaction; -import androidx.lifecycle.ViewModelProvider; - -import com.google.android.material.appbar.CollapsingToolbarLayout; -import com.google.android.material.color.MaterialColors; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; - -import org.dolphinemu.dolphinemu.NativeLibrary; -import org.dolphinemu.dolphinemu.R; -import org.dolphinemu.dolphinemu.databinding.ActivitySettingsBinding; -import org.dolphinemu.dolphinemu.ui.main.MainPresenter; -import org.dolphinemu.dolphinemu.utils.FileBrowserHelper; -import org.dolphinemu.dolphinemu.utils.InsetsHelper; -import org.dolphinemu.dolphinemu.utils.ThemeHelper; - -import java.util.Set; - -public final class SettingsActivity extends AppCompatActivity implements SettingsActivityView -{ - private static final String ARG_MENU_TAG = "menu_tag"; - private static final String ARG_GAME_ID = "game_id"; - private static final String ARG_REVISION = "revision"; - private static final String ARG_IS_WII = "is_wii"; - private static final String KEY_MAPPING_ALL_DEVICES = "all_devices"; - private static final String FRAGMENT_TAG = "settings"; - private static final String FRAGMENT_DIALOG_TAG = "settings_dialog"; - - private SettingsActivityPresenter mPresenter; - private AlertDialog dialog; - private CollapsingToolbarLayout mToolbarLayout; - - private ActivitySettingsBinding mBinding; - - private boolean mMappingAllDevices = false; - - public static void launch(Context context, MenuTag menuTag, String gameId, int revision, - boolean isWii) - { - Intent settings = new Intent(context, SettingsActivity.class); - settings.putExtra(ARG_MENU_TAG, menuTag); - settings.putExtra(ARG_GAME_ID, gameId); - settings.putExtra(ARG_REVISION, revision); - settings.putExtra(ARG_IS_WII, isWii); - context.startActivity(settings); - } - - public static void launch(Context context, MenuTag menuTag) - { - Intent settings = new Intent(context, SettingsActivity.class); - settings.putExtra(ARG_MENU_TAG, menuTag); - settings.putExtra(ARG_IS_WII, !NativeLibrary.IsRunning() || NativeLibrary.IsEmulatingWii()); - context.startActivity(settings); - } - - @Override - protected void onCreate(Bundle savedInstanceState) - { - ThemeHelper.setTheme(this); - - super.onCreate(savedInstanceState); - - // If we came here from the game list, we don't want to rescan when returning to the game list. - // But if we came here after UserDataActivity restarted the app, we do want to rescan. - if (savedInstanceState == null) - { - MainPresenter.skipRescanningLibrary(); - } - else - { - mMappingAllDevices = savedInstanceState.getBoolean(KEY_MAPPING_ALL_DEVICES); - } - - mBinding = ActivitySettingsBinding.inflate(getLayoutInflater()); - setContentView(mBinding.getRoot()); - - WindowCompat.setDecorFitsSystemWindows(getWindow(), false); - - Intent launcher = getIntent(); - String gameID = launcher.getStringExtra(ARG_GAME_ID); - if (gameID == null) - gameID = ""; - int revision = launcher.getIntExtra(ARG_REVISION, 0); - boolean isWii = launcher.getBooleanExtra(ARG_IS_WII, true); - MenuTag menuTag = (MenuTag) launcher.getSerializableExtra(ARG_MENU_TAG); - - mPresenter = new SettingsActivityPresenter(this, getSettings()); - mPresenter.onCreate(savedInstanceState, menuTag, gameID, revision, isWii, this); - - mToolbarLayout = mBinding.toolbarSettingsLayout; - setSupportActionBar(mBinding.toolbarSettings); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - - // TODO: Remove this when CollapsingToolbarLayouts are fixed by Google - // https://github.com/material-components/material-components-android/issues/1310 - ViewCompat.setOnApplyWindowInsetsListener(mToolbarLayout, null); - - setInsets(); - ThemeHelper.enableScrollTint(this, mBinding.toolbarSettings, mBinding.appbarSettings); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) - { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.menu_settings, menu); - - return true; - } - - @Override - protected void onSaveInstanceState(@NonNull Bundle outState) - { - // Critical: If super method is not called, rotations will be busted. - super.onSaveInstanceState(outState); - - mPresenter.saveState(outState); - - outState.putBoolean(KEY_MAPPING_ALL_DEVICES, mMappingAllDevices); - } - - @Override - protected void onStart() - { - super.onStart(); - mPresenter.onStart(); - } - - /** - * If this is called, the user has left the settings screen (potentially through the - * home button) and will expect their changes to be persisted. - */ - @Override - protected void onStop() - { - super.onStop(); - mPresenter.onStop(isFinishing()); - } - - @Override - protected void onDestroy() - { - super.onDestroy(); - mPresenter.onDestroy(); - } - - @Override - public void showSettingsFragment(MenuTag menuTag, Bundle extras, boolean addToStack, - String gameID) - { - if (!addToStack && getFragment() != null) - return; - - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - - if (addToStack) - { - if (areSystemAnimationsEnabled()) - { - transaction.setCustomAnimations( - R.anim.anim_settings_fragment_in, - R.anim.anim_settings_fragment_out, - 0, - R.anim.anim_pop_settings_fragment_out); - } - - transaction.addToBackStack(null); - } - transaction.replace(R.id.frame_content_settings, - SettingsFragment.newInstance(menuTag, gameID, extras), FRAGMENT_TAG); - - transaction.commit(); - } - - @Override - public void showDialogFragment(DialogFragment fragment) - { - fragment.show(getSupportFragmentManager(), FRAGMENT_DIALOG_TAG); - } - - private boolean areSystemAnimationsEnabled() - { - float duration = Settings.Global.getFloat( - getContentResolver(), - Settings.Global.ANIMATOR_DURATION_SCALE, 1); - float transition = Settings.Global.getFloat( - getContentResolver(), - Settings.Global.TRANSITION_ANIMATION_SCALE, 1); - return duration != 0 && transition != 0; - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent result) - { - super.onActivityResult(requestCode, resultCode, result); - - // If the user picked a file, as opposed to just backing out. - if (resultCode == RESULT_OK) - { - if (requestCode != MainPresenter.REQUEST_DIRECTORY) - { - Uri uri = canonicalizeIfPossible(result.getData()); - - Set validExtensions = requestCode == MainPresenter.REQUEST_GAME_FILE ? - FileBrowserHelper.GAME_EXTENSIONS : FileBrowserHelper.RAW_EXTENSION; - - int flags = Intent.FLAG_GRANT_READ_URI_PERMISSION; - if (requestCode != MainPresenter.REQUEST_GAME_FILE) - flags |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION; - int takeFlags = flags & result.getFlags(); - - FileBrowserHelper.runAfterExtensionCheck(this, uri, validExtensions, () -> - { - getContentResolver().takePersistableUriPermission(uri, takeFlags); - getFragment().getAdapter().onFilePickerConfirmation(uri.toString()); - }); - } - else - { - String path = FileBrowserHelper.getSelectedPath(result); - getFragment().getAdapter().onFilePickerConfirmation(path); - } - } - } - - @NonNull - private Uri canonicalizeIfPossible(@NonNull Uri uri) - { - Uri canonicalizedUri = getContentResolver().canonicalize(uri); - return canonicalizedUri != null ? canonicalizedUri : uri; - } - - @Override - public void showLoading() - { - if (dialog == null) - { - dialog = new MaterialAlertDialogBuilder(this) - .setTitle(getString(R.string.load_settings)) - .setView(R.layout.dialog_indeterminate_progress) - .create(); - } - dialog.show(); - } - - @Override - public void hideLoading() - { - dialog.dismiss(); - } - - @Override - public void showGameIniJunkDeletionQuestion() - { - new MaterialAlertDialogBuilder(this) - .setTitle(getString(R.string.game_ini_junk_title)) - .setMessage(getString(R.string.game_ini_junk_question)) - .setPositiveButton(R.string.yes, (dialogInterface, i) -> mPresenter.clearGameSettings()) - .setNegativeButton(R.string.no, null) - .show(); - } - - @Override - public org.dolphinemu.dolphinemu.features.settings.model.Settings getSettings() - { - return new ViewModelProvider(this).get(SettingsViewModel.class).getSettings(); - } - - @Override - public void onSettingsFileLoaded( - org.dolphinemu.dolphinemu.features.settings.model.Settings settings) - { - SettingsFragmentView fragment = getFragment(); - - if (fragment != null) - { - fragment.onSettingsFileLoaded(settings); - } - } - - @Override - public void onSettingsFileNotFound() - { - SettingsFragmentView fragment = getFragment(); - - if (fragment != null) - { - fragment.loadDefaultSettings(); - } - } - - @Override - public void showToastMessage(String message) - { - Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); - } - - @Override - public void onSettingChanged() - { - mPresenter.onSettingChanged(); - } - - @Override - public void onControllerSettingsChanged() - { - getFragment().onControllerSettingsChanged(); - } - - @Override - public void onMenuTagAction(@NonNull MenuTag menuTag, int value) - { - mPresenter.onMenuTagAction(menuTag, value); - } - - @Override - public boolean hasMenuTagActionForValue(@NonNull MenuTag menuTag, int value) - { - return mPresenter.hasMenuTagActionForValue(menuTag, value); - } - - @Override - public boolean onSupportNavigateUp() - { - onBackPressed(); - return true; - } - - private SettingsFragment getFragment() - { - return (SettingsFragment) getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG); - } - - public void setToolbarTitle(String title) - { - mBinding.toolbarSettingsLayout.setTitle(title); - } - - @Override - public void setMappingAllDevices(boolean allDevices) - { - mMappingAllDevices = allDevices; - } - - @Override - public boolean isMappingAllDevices() - { - return mMappingAllDevices; - } - - @Override - public int setOldControllerSettingsWarningVisibility(boolean visible) - { - // We use INVISIBLE instead of GONE to avoid getting a stale height for the return value - mBinding.oldControllerSettingsWarning.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); - return visible ? mBinding.oldControllerSettingsWarning.getHeight() : 0; - } - - private void setInsets() - { - ViewCompat.setOnApplyWindowInsetsListener(mBinding.appbarSettings, (v, windowInsets) -> - { - Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); - - InsetsHelper.insetAppBar(insets, mBinding.appbarSettings); - - mBinding.frameContentSettings.setPadding(insets.left, 0, insets.right, 0); - - int textPadding = getResources().getDimensionPixelSize(R.dimen.spacing_large); - mBinding.oldControllerSettingsWarning.setPadding(textPadding + insets.left, textPadding, - textPadding + insets.right, textPadding + insets.bottom); - - InsetsHelper.applyNavbarWorkaround(insets.bottom, mBinding.workaroundView); - ThemeHelper.setNavigationBarColor(this, - MaterialColors.getColor(mBinding.appbarSettings, R.attr.colorSurface)); - - return windowInsets; - }); - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.kt new file mode 100644 index 0000000000..4d950d9933 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.kt @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.ui + +import android.content.Context +import android.content.DialogInterface +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.view.Menu +import android.view.View +import android.widget.Toast +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.ViewCompat +import androidx.core.view.WindowCompat +import androidx.core.view.WindowInsetsCompat +import androidx.fragment.app.DialogFragment +import androidx.lifecycle.ViewModelProvider +import com.google.android.material.appbar.CollapsingToolbarLayout +import com.google.android.material.color.MaterialColors +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.dolphinemu.dolphinemu.NativeLibrary +import org.dolphinemu.dolphinemu.R +import org.dolphinemu.dolphinemu.databinding.ActivitySettingsBinding +import org.dolphinemu.dolphinemu.features.settings.model.Settings +import org.dolphinemu.dolphinemu.features.settings.ui.SettingsFragment.Companion.newInstance +import org.dolphinemu.dolphinemu.ui.main.MainPresenter +import org.dolphinemu.dolphinemu.utils.FileBrowserHelper +import org.dolphinemu.dolphinemu.utils.InsetsHelper +import org.dolphinemu.dolphinemu.utils.SerializableHelper.serializable +import org.dolphinemu.dolphinemu.utils.ThemeHelper.enableScrollTint +import org.dolphinemu.dolphinemu.utils.ThemeHelper.setNavigationBarColor +import org.dolphinemu.dolphinemu.utils.ThemeHelper.setTheme + +class SettingsActivity : AppCompatActivity(), SettingsActivityView { + private var presenter: SettingsActivityPresenter? = null + private var dialog: AlertDialog? = null + private var toolbarLayout: CollapsingToolbarLayout? = null + private var binding: ActivitySettingsBinding? = null + + override var isMappingAllDevices = false + + override val settings: Settings + get() = ViewModelProvider(this)[SettingsViewModel::class.java].settings + + private val fragment: SettingsFragment? + get() = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as SettingsFragment? + + override fun onCreate(savedInstanceState: Bundle?) { + setTheme(this) + + super.onCreate(savedInstanceState) + + // If we came here from the game list, we don't want to rescan when returning to the game list. + // But if we came here after UserDataActivity restarted the app, we do want to rescan. + if (savedInstanceState == null) { + MainPresenter.skipRescanningLibrary() + } else { + isMappingAllDevices = savedInstanceState.getBoolean(KEY_MAPPING_ALL_DEVICES) + } + + binding = ActivitySettingsBinding.inflate(layoutInflater) + setContentView(binding!!.root) + + WindowCompat.setDecorFitsSystemWindows(window, false) + + val launcher = intent + var gameID = launcher.getStringExtra(ARG_GAME_ID) + if (gameID == null) gameID = "" + val revision = launcher.getIntExtra(ARG_REVISION, 0) + val isWii = launcher.getBooleanExtra(ARG_IS_WII, true) + val menuTag = launcher.serializable(ARG_MENU_TAG) + + presenter = SettingsActivityPresenter(this, settings) + presenter!!.onCreate(savedInstanceState, menuTag, gameID, revision, isWii, this) + toolbarLayout = binding!!.toolbarSettingsLayout + setSupportActionBar(binding!!.toolbarSettings) + supportActionBar!!.setDisplayHomeAsUpEnabled(true) + + // TODO: Remove this when CollapsingToolbarLayouts are fixed by Google + // https://github.com/material-components/material-components-android/issues/1310 + ViewCompat.setOnApplyWindowInsetsListener(toolbarLayout!!, null) + setInsets() + enableScrollTint(this, binding!!.toolbarSettings, binding!!.appbarSettings) + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + val inflater = menuInflater + inflater.inflate(R.menu.menu_settings, menu) + return true + } + + override fun onSaveInstanceState(outState: Bundle) { + // Critical: If super method is not called, rotations will be busted. + super.onSaveInstanceState(outState) + presenter!!.saveState(outState) + outState.putBoolean(KEY_MAPPING_ALL_DEVICES, isMappingAllDevices) + } + + override fun onStart() { + super.onStart() + presenter!!.onStart() + } + + /** + * If this is called, the user has left the settings screen (potentially through the + * home button) and will expect their changes to be persisted. + */ + override fun onStop() { + super.onStop() + presenter!!.onStop(isFinishing) + } + + override fun onDestroy() { + super.onDestroy() + presenter!!.onDestroy() + } + + override fun showSettingsFragment( + menuTag: MenuTag, + extras: Bundle?, + addToStack: Boolean, + gameId: String + ) { + if (!addToStack && fragment != null) return + val transaction = supportFragmentManager.beginTransaction() + if (addToStack) { + if (areSystemAnimationsEnabled()) { + transaction.setCustomAnimations( + R.anim.anim_settings_fragment_in, + R.anim.anim_settings_fragment_out, + 0, + R.anim.anim_pop_settings_fragment_out + ) + } + transaction.addToBackStack(null) + } + transaction.replace( + R.id.frame_content_settings, + newInstance(menuTag, gameId, extras), FRAGMENT_TAG + ) + transaction.commit() + } + + override fun showDialogFragment(fragment: DialogFragment) { + fragment.show(supportFragmentManager, FRAGMENT_DIALOG_TAG) + } + + private fun areSystemAnimationsEnabled(): Boolean { + val duration = android.provider.Settings.Global.getFloat( + contentResolver, + android.provider.Settings.Global.ANIMATOR_DURATION_SCALE, + 1f + ) + val transition = android.provider.Settings.Global.getFloat( + contentResolver, + android.provider.Settings.Global.TRANSITION_ANIMATION_SCALE, + 1f + ) + return duration != 0f && transition != 0f + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, result: Intent?) { + super.onActivityResult(requestCode, resultCode, result) + + // If the user picked a file, as opposed to just backing out. + if (resultCode == RESULT_OK) { + if (requestCode != MainPresenter.REQUEST_DIRECTORY) { + val uri = canonicalizeIfPossible(result!!.data!!) + val validExtensions: Set = + if (requestCode == MainPresenter.REQUEST_GAME_FILE) FileBrowserHelper.GAME_EXTENSIONS else FileBrowserHelper.RAW_EXTENSION + var flags = Intent.FLAG_GRANT_READ_URI_PERMISSION + if (requestCode != MainPresenter.REQUEST_GAME_FILE) flags = + flags or Intent.FLAG_GRANT_WRITE_URI_PERMISSION + val takeFlags = flags and result.flags + FileBrowserHelper.runAfterExtensionCheck(this, uri, validExtensions) { + contentResolver.takePersistableUriPermission(uri, takeFlags) + fragment!!.adapter!!.onFilePickerConfirmation(uri.toString()) + } + } else { + val path = FileBrowserHelper.getSelectedPath(result) + fragment!!.adapter!!.onFilePickerConfirmation(path!!) + } + } + } + + private fun canonicalizeIfPossible(uri: Uri): Uri { + val canonicalizedUri = contentResolver.canonicalize(uri) + return canonicalizedUri ?: uri + } + + override fun showLoading() { + if (dialog == null) { + dialog = MaterialAlertDialogBuilder(this) + .setTitle(getString(R.string.load_settings)) + .setView(R.layout.dialog_indeterminate_progress) + .create() + } + dialog!!.show() + } + + override fun hideLoading() { + dialog!!.dismiss() + } + + override fun showGameIniJunkDeletionQuestion() { + MaterialAlertDialogBuilder(this) + .setTitle(getString(R.string.game_ini_junk_title)) + .setMessage(getString(R.string.game_ini_junk_question)) + .setPositiveButton(R.string.yes) { _: DialogInterface?, _: Int -> presenter!!.clearGameSettings() } + .setNegativeButton(R.string.no, null) + .show() + } + + override fun onSettingsFileLoaded(settings: Settings) { + val fragment: SettingsFragmentView? = fragment + fragment?.onSettingsFileLoaded(settings) + } + + override fun onSettingsFileNotFound() { + val fragment: SettingsFragmentView? = fragment + fragment?.loadDefaultSettings() + } + + override fun showToastMessage(message: String) { + Toast.makeText(this, message, Toast.LENGTH_SHORT).show() + } + + override fun onSettingChanged() { + presenter!!.onSettingChanged() + } + + override fun onControllerSettingsChanged() { + fragment!!.onControllerSettingsChanged() + } + + override fun onMenuTagAction(menuTag: MenuTag, value: Int) { + presenter!!.onMenuTagAction(menuTag, value) + } + + override fun hasMenuTagActionForValue(menuTag: MenuTag, value: Int): Boolean { + return presenter!!.hasMenuTagActionForValue(menuTag, value) + } + + override fun onSupportNavigateUp(): Boolean { + onBackPressed() + return true + } + + override fun setToolbarTitle(title: String) { + binding!!.toolbarSettingsLayout.title = title + } + + override fun setOldControllerSettingsWarningVisibility(visible: Boolean): Int { + // We use INVISIBLE instead of GONE to avoid getting a stale height for the return value + binding!!.oldControllerSettingsWarning.visibility = + if (visible) View.VISIBLE else View.INVISIBLE + return if (visible) binding!!.oldControllerSettingsWarning.height else 0 + } + + private fun setInsets() { + ViewCompat.setOnApplyWindowInsetsListener(binding!!.appbarSettings) { _: View?, windowInsets: WindowInsetsCompat -> + val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) + + InsetsHelper.insetAppBar(insets, binding!!.appbarSettings) + + binding!!.frameContentSettings.setPadding(insets.left, 0, insets.right, 0) + val textPadding = resources.getDimensionPixelSize(R.dimen.spacing_large) + binding!!.oldControllerSettingsWarning.setPadding( + textPadding + insets.left, + textPadding, + textPadding + insets.right, + textPadding + insets.bottom + ) + + InsetsHelper.applyNavbarWorkaround(insets.bottom, binding!!.workaroundView) + setNavigationBarColor( + this, + MaterialColors.getColor(binding!!.appbarSettings, R.attr.colorSurface) + ) + + windowInsets + } + } + + companion object { + private const val ARG_MENU_TAG = "menu_tag" + private const val ARG_GAME_ID = "game_id" + private const val ARG_REVISION = "revision" + private const val ARG_IS_WII = "is_wii" + private const val KEY_MAPPING_ALL_DEVICES = "all_devices" + private const val FRAGMENT_TAG = "settings" + private const val FRAGMENT_DIALOG_TAG = "settings_dialog" + + @JvmStatic + fun launch( + context: Context, + menuTag: MenuTag?, + gameId: String?, + revision: Int, + isWii: Boolean + ) { + val settings = Intent(context, SettingsActivity::class.java) + settings.putExtra(ARG_MENU_TAG, menuTag) + settings.putExtra(ARG_GAME_ID, gameId) + settings.putExtra(ARG_REVISION, revision) + settings.putExtra(ARG_IS_WII, isWii) + context.startActivity(settings) + } + + @JvmStatic + fun launch(context: Context, menuTag: MenuTag?) { + val settings = Intent(context, SettingsActivity::class.java) + settings.putExtra(ARG_MENU_TAG, menuTag) + settings.putExtra( + ARG_IS_WII, + !NativeLibrary.IsRunning() || NativeLibrary.IsEmulatingWii() + ) + context.startActivity(settings) + } + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityPresenter.java deleted file mode 100644 index 00561daffb..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityPresenter.java +++ /dev/null @@ -1,197 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.ui; - -import android.os.Bundle; -import android.text.TextUtils; - -import androidx.annotation.NonNull; -import androidx.core.app.ComponentActivity; - -import org.dolphinemu.dolphinemu.R; -import org.dolphinemu.dolphinemu.features.settings.model.Settings; -import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner; -import org.dolphinemu.dolphinemu.utils.DirectoryInitialization; -import org.dolphinemu.dolphinemu.utils.Log; - -public final class SettingsActivityPresenter -{ - private static final String KEY_SHOULD_SAVE = "should_save"; - - private SettingsActivityView mView; - - private Settings mSettings; - - private boolean mShouldSave; - - private MenuTag mMenuTag; - private String mGameId; - private int mRevision; - private boolean mIsWii; - private ComponentActivity mActivity; - - SettingsActivityPresenter(SettingsActivityView view, Settings settings) - { - mView = view; - mSettings = settings; - } - - public void onCreate(Bundle savedInstanceState, MenuTag menuTag, String gameId, int revision, - boolean isWii, ComponentActivity activity) - { - this.mMenuTag = menuTag; - this.mGameId = gameId; - this.mRevision = revision; - this.mIsWii = isWii; - this.mActivity = activity; - - mShouldSave = savedInstanceState != null && savedInstanceState.getBoolean(KEY_SHOULD_SAVE); - } - - public void onDestroy() - { - if (mSettings != null) - { - mSettings.close(); - mSettings = null; - } - } - - public void onStart() - { - prepareDolphinDirectoriesIfNeeded(); - } - - private void loadSettingsUI() - { - mView.hideLoading(); - - if (!mSettings.areSettingsLoaded()) - { - if (!TextUtils.isEmpty(mGameId)) - { - mSettings.loadSettings(mGameId, mRevision, mIsWii); - - if (mSettings.gameIniContainsJunk()) - { - mView.showGameIniJunkDeletionQuestion(); - } - } - else - { - mSettings.loadSettings(mIsWii); - } - } - - mView.showSettingsFragment(mMenuTag, null, false, mGameId); - mView.onSettingsFileLoaded(mSettings); - } - - private void prepareDolphinDirectoriesIfNeeded() - { - mView.showLoading(); - - new AfterDirectoryInitializationRunner().runWithLifecycle(mActivity, this::loadSettingsUI); - } - - public Settings getSettings() - { - return mSettings; - } - - public void clearGameSettings() - { - mSettings.clearGameSettings(); - onSettingChanged(); - } - - public void onStop(boolean finishing) - { - if (mSettings != null && finishing && mShouldSave) - { - Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI..."); - mSettings.saveSettings(mActivity); - } - } - - public void onSettingChanged() - { - mShouldSave = true; - } - - public void saveState(Bundle outState) - { - outState.putBoolean(KEY_SHOULD_SAVE, mShouldSave); - } - - public boolean shouldSave() - { - return mShouldSave; - } - - public void onMenuTagAction(@NonNull MenuTag menuTag, int value) - { - if (menuTag.isSerialPort1Menu()) - { - if (value != 0 && value != 255) // Not disabled or dummy - { - Bundle bundle = new Bundle(); - bundle.putInt(SettingsFragmentPresenter.ARG_SERIALPORT1_TYPE, value); - mView.showSettingsFragment(menuTag, bundle, true, mGameId); - } - } - - if (menuTag.isGCPadMenu()) - { - if (value != 0) // Not disabled - { - Bundle bundle = new Bundle(); - bundle.putInt(SettingsFragmentPresenter.ARG_CONTROLLER_TYPE, value); - mView.showSettingsFragment(menuTag, bundle, true, mGameId); - } - } - - if (menuTag.isWiimoteMenu()) - { - if (value == 1) // Emulated Wii Remote - { - mView.showSettingsFragment(menuTag, null, true, mGameId); - } - } - - if (menuTag.isWiimoteExtensionMenu()) - { - if (value != 0) // Not disabled - { - Bundle bundle = new Bundle(); - bundle.putInt(SettingsFragmentPresenter.ARG_CONTROLLER_TYPE, value); - mView.showSettingsFragment(menuTag, bundle, true, mGameId); - } - } - } - - public boolean hasMenuTagActionForValue(@NonNull MenuTag menuTag, int value) - { - if (menuTag.isSerialPort1Menu()) - { - return (value != 0 && value != 255); // Not disabled or dummy - } - - if (menuTag.isGCPadMenu()) - { - return (value != 0); // Not disabled - } - - if (menuTag.isWiimoteMenu()) - { - return (value == 1); // Emulated Wii Remote - } - - if (menuTag.isWiimoteExtensionMenu()) - { - return (value != 0); // Not disabled - } - - return false; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityPresenter.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityPresenter.kt new file mode 100644 index 0000000000..eee800463b --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityPresenter.kt @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.ui + +import android.os.Bundle +import android.text.TextUtils +import androidx.appcompat.app.AppCompatActivity +import org.dolphinemu.dolphinemu.features.settings.model.Settings +import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner +import org.dolphinemu.dolphinemu.utils.Log + +class SettingsActivityPresenter( + private val activityView: SettingsActivityView, + var settings: Settings? +) { + private var shouldSave = false + private var menuTag: MenuTag? = null + private var gameId: String? = null + private var revision = 0 + private var isWii = false + private lateinit var activity: AppCompatActivity + + fun onCreate( + savedInstanceState: Bundle?, + menuTag: MenuTag?, + gameId: String?, + revision: Int, + isWii: Boolean, + activity: AppCompatActivity + ) { + this.menuTag = menuTag + this.gameId = gameId + this.revision = revision + this.isWii = isWii + this.activity = activity + shouldSave = savedInstanceState != null && savedInstanceState.getBoolean(KEY_SHOULD_SAVE) + } + + fun onDestroy() { + if (settings != null) { + settings!!.close() + settings = null + } + } + + fun onStart() { + prepareDolphinDirectoriesIfNeeded() + } + + private fun loadSettingsUI() { + activityView.hideLoading() + if (!settings!!.areSettingsLoaded()) { + if (!TextUtils.isEmpty(gameId)) { + settings!!.loadSettings(gameId!!, revision, isWii) + if (settings!!.gameIniContainsJunk()) { + activityView.showGameIniJunkDeletionQuestion() + } + } else { + settings!!.loadSettings(isWii) + } + } + activityView.showSettingsFragment(menuTag!!, null, false, gameId!!) + activityView.onSettingsFileLoaded(settings!!) + } + + private fun prepareDolphinDirectoriesIfNeeded() { + activityView.showLoading() + AfterDirectoryInitializationRunner().runWithLifecycle(activity) { loadSettingsUI() } + } + + fun clearGameSettings() { + settings!!.clearGameSettings() + onSettingChanged() + } + + fun onStop(finishing: Boolean) { + if (settings != null && finishing && shouldSave) { + Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...") + settings!!.saveSettings(activity) + } + } + + fun onSettingChanged() { + shouldSave = true + } + + fun saveState(outState: Bundle) { + outState.putBoolean(KEY_SHOULD_SAVE, shouldSave) + } + + fun onMenuTagAction(menuTag: MenuTag, value: Int) { + if (menuTag.isSerialPort1Menu) { + // Not disabled or dummy + if (value != 0 && value != 255) { + val bundle = Bundle() + bundle.putInt(SettingsFragmentPresenter.ARG_SERIALPORT1_TYPE, value) + activityView.showSettingsFragment(menuTag, bundle, true, gameId!!) + } + } + if (menuTag.isGCPadMenu) { + // Not disabled + if (value != 0) + { + val bundle = Bundle() + bundle.putInt(SettingsFragmentPresenter.ARG_CONTROLLER_TYPE, value) + activityView.showSettingsFragment(menuTag, bundle, true, gameId!!) + } + } + if (menuTag.isWiimoteMenu) { + // Emulated Wii Remote + if (value == 1) { + activityView.showSettingsFragment(menuTag, null, true, gameId!!) + } + } + if (menuTag.isWiimoteExtensionMenu) { + // Not disabled + if (value != 0) { + val bundle = Bundle() + bundle.putInt(SettingsFragmentPresenter.ARG_CONTROLLER_TYPE, value) + activityView.showSettingsFragment(menuTag, bundle, true, gameId!!) + } + } + } + + fun hasMenuTagActionForValue(menuTag: MenuTag, value: Int): Boolean { + if (menuTag.isSerialPort1Menu) { + // Not disabled or dummy + return value != 0 && value != 255 + } + if (menuTag.isGCPadMenu) { + // Not disabled + return value != 0 + } + if (menuTag.isWiimoteMenu) { + // Emulated Wii Remote + return value == 1 + } + return if (menuTag.isWiimoteExtensionMenu) { + // Not disabled + value != 0 + } else false + } + + companion object { + private const val KEY_SHOULD_SAVE = "should_save" + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityView.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityView.java deleted file mode 100644 index 464d0ea165..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityView.java +++ /dev/null @@ -1,137 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.ui; - -import android.os.Bundle; - -import androidx.annotation.NonNull; -import androidx.fragment.app.DialogFragment; - -import org.dolphinemu.dolphinemu.features.settings.model.Settings; - -/** - * Abstraction for the Activity that manages SettingsFragments. - */ -public interface SettingsActivityView -{ - /** - * Show a new SettingsFragment. - * - * @param menuTag Identifier for the settings group that should be displayed. - * @param addToStack Whether or not this fragment should replace a previous one. - */ - void showSettingsFragment(MenuTag menuTag, Bundle extras, boolean addToStack, String gameId); - - /** - * Shows a DialogFragment. - * - * Only one can be shown at a time. - */ - void showDialogFragment(DialogFragment fragment); - - /** - * Called by a contained Fragment to get access to the Setting HashMap - * loaded from disk, so that each Fragment doesn't need to perform its own - * read operation. - * - * @return A possibly null HashMap of Settings. - */ - Settings getSettings(); - - /** - * Called when an asynchronous load operation completes. - * - * @param settings The (possibly null) result of the ini load operation. - */ - void onSettingsFileLoaded(Settings settings); - - /** - * Called when an asynchronous load operation fails. - */ - void onSettingsFileNotFound(); - - /** - * Display a popup text message on screen. - * - * @param message The contents of the onscreen message. - */ - void showToastMessage(String message); - - /** - * End the activity. - */ - void finish(); - - /** - * Called by a containing Fragment to tell the Activity that a setting was changed; - * unless this has been called, the Activity will not save to disk. - */ - void onSettingChanged(); - - /** - * Refetches the values of all controller settings. - * - * To be used when loading an input profile or performing some other action that changes all - * controller settings at once. - */ - void onControllerSettingsChanged(); - - /** - * Called by a containing Fragment to tell the containing Activity that the user wants to open the - * MenuTag associated with a setting. - * - * @param menuTag The MenuTag of the setting. - * @param value The current value of the setting. - */ - void onMenuTagAction(@NonNull MenuTag menuTag, int value); - - /** - * Returns whether anything will happen when the user wants to open the MenuTag associated with a - * setting, given the current value of the setting. - * - * @param menuTag The MenuTag of the setting. - * @param value The current value of the setting. - */ - boolean hasMenuTagActionForValue(@NonNull MenuTag menuTag, int value); - - /** - * Show loading dialog while loading the settings - */ - void showLoading(); - - /** - * Hide the loading dialog - */ - void hideLoading(); - - /** - * Tell the user that there is junk in the game INI and ask if they want to delete the whole file. - */ - void showGameIniJunkDeletionQuestion(); - - /** - * Accesses the material toolbar layout and changes the title - */ - void setToolbarTitle(String title); - - /** - * Sets whether the input mapping dialog should detect inputs from all devices, - * not just the device configured for the controller. - */ - void setMappingAllDevices(boolean allDevices); - - /** - * Returns whether the input mapping dialog should detect inputs from all devices, - * not just the device configured for the controller. - */ - boolean isMappingAllDevices(); - - /** - * Shows or hides a warning telling the user that they're using incompatible controller settings. - * The warning is hidden by default. - * - * @param visible Whether the warning should be visible. - * @return The height of the warning view, or 0 if the view is now invisible. - */ - int setOldControllerSettingsWarningVisibility(boolean visible); -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityView.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityView.kt new file mode 100644 index 0000000000..435d23fad6 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityView.kt @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.ui + +import android.os.Bundle +import androidx.fragment.app.DialogFragment +import org.dolphinemu.dolphinemu.features.settings.model.Settings + +/** + * Abstraction for the Activity that manages SettingsFragments. + */ +interface SettingsActivityView { + /** + * Show a new SettingsFragment. + * + * @param menuTag Identifier for the settings group that should be displayed. + * @param addToStack Whether or not this fragment should replace a previous one. + */ + fun showSettingsFragment( + menuTag: MenuTag, + extras: Bundle?, + addToStack: Boolean, + gameId: String + ) + + /** + * Shows a DialogFragment. + * + * Only one can be shown at a time. + */ + fun showDialogFragment(fragment: DialogFragment) + + /** + * Called by a contained Fragment to get access to the Setting HashMap + * loaded from disk, so that each Fragment doesn't need to perform its own + * read operation. + * + * @return A possibly null HashMap of Settings. + */ + val settings: Settings + + /** + * Called when an asynchronous load operation completes. + * + * @param settings The (possibly null) result of the ini load operation. + */ + fun onSettingsFileLoaded(settings: Settings) + + /** + * Called when an asynchronous load operation fails. + */ + fun onSettingsFileNotFound() + + /** + * Display a popup text message on screen. + * + * @param message The contents of the onscreen message. + */ + fun showToastMessage(message: String) + + /** + * End the activity. + */ + fun finish() + + /** + * Called by a containing Fragment to tell the Activity that a Setting was changed; + * unless this has been called, the Activity will not save to disk. + */ + fun onSettingChanged() + + /** + * Refetches the values of all controller settings. + * + * To be used when loading an input profile or performing some other action that changes all + * controller settings at once. + */ + fun onControllerSettingsChanged() + + /** + * Called by a containing Fragment to tell the containing Activity that the user wants to open the + * MenuTag associated with a Setting. + * + * @param menuTag The MenuTag of the Setting. + * @param value The current value of the Setting. + */ + fun onMenuTagAction(menuTag: MenuTag, value: Int) + + /** + * Returns whether anything will happen when the user wants to open the MenuTag associated with a + * Setting, given the current value of the Setting. + * + * @param menuTag The MenuTag of the Setting. + * @param value The current value of the Setting. + */ + fun hasMenuTagActionForValue(menuTag: MenuTag, value: Int): Boolean + + /** + * Show loading dialog while loading the settings + */ + fun showLoading() + + /** + * Hide the loading dialog + */ + fun hideLoading() + + /** + * Tell the user that there is junk in the game INI and ask if they want to delete the whole file. + */ + fun showGameIniJunkDeletionQuestion() + + /** + * Accesses the material toolbar layout and changes the title + */ + fun setToolbarTitle(title: String) + /** + * Returns whether the input mapping dialog should detect inputs from all devices, + * not just the device configured for the controller. + */ + /** + * Sets whether the input mapping dialog should detect inputs from all devices, + * not just the device configured for the controller. + */ + var isMappingAllDevices: Boolean + + /** + * Shows or hides a warning telling the user that they're using incompatible controller settings. + * The warning is hidden by default. + * + * @param visible Whether the warning should be visible. + * @return The height of the warning view, or 0 if the view is now invisible. + */ + fun setOldControllerSettingsWarningVisibility(visible: Boolean): Int +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java deleted file mode 100644 index e062dd71b2..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java +++ /dev/null @@ -1,703 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.ui; - -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.graphics.PorterDuff; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.provider.DocumentsContract; -import android.text.format.DateFormat; -import android.view.LayoutInflater; -import android.view.ViewGroup; -import android.widget.TextView; - -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; -import androidx.core.content.ContextCompat; -import androidx.recyclerview.widget.RecyclerView; - -import com.google.android.material.color.MaterialColors; -import com.google.android.material.datepicker.CalendarConstraints; -import com.google.android.material.datepicker.MaterialDatePicker; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; -import com.google.android.material.elevation.ElevationOverlayProvider; -import com.google.android.material.slider.Slider; -import com.google.android.material.textfield.TextInputEditText; -import com.google.android.material.timepicker.MaterialTimePicker; -import com.google.android.material.timepicker.TimeFormat; - -import org.dolphinemu.dolphinemu.R; -import org.dolphinemu.dolphinemu.databinding.DialogAdvancedMappingBinding; -import org.dolphinemu.dolphinemu.databinding.DialogInputStringBinding; -import org.dolphinemu.dolphinemu.databinding.DialogSliderBinding; -import org.dolphinemu.dolphinemu.databinding.ListItemHeaderBinding; -import org.dolphinemu.dolphinemu.databinding.ListItemMappingBinding; -import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding; -import org.dolphinemu.dolphinemu.databinding.ListItemSettingSwitchBinding; -import org.dolphinemu.dolphinemu.databinding.ListItemSubmenuBinding; -import org.dolphinemu.dolphinemu.features.input.ui.AdvancedMappingDialog; -import org.dolphinemu.dolphinemu.features.input.ui.MotionAlertDialog; -import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting; -import org.dolphinemu.dolphinemu.features.input.ui.viewholder.InputMappingControlSettingViewHolder; -import org.dolphinemu.dolphinemu.features.settings.model.Settings; -import org.dolphinemu.dolphinemu.features.settings.model.view.DateTimeChoiceSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.SwitchSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.FilePicker; -import org.dolphinemu.dolphinemu.features.settings.model.view.FloatSliderSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.IntSliderSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; -import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSettingDynamicDescriptions; -import org.dolphinemu.dolphinemu.features.settings.model.view.SliderSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.InputStringSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.StringSingleChoiceSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.SubmenuSetting; -import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.DateTimeSettingViewHolder; -import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.FilePickerViewHolder; -import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.HeaderHyperLinkViewHolder; -import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.HeaderViewHolder; -import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.InputStringSettingViewHolder; -import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.RunRunnableViewHolder; -import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SettingViewHolder; -import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SingleChoiceViewHolder; -import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SliderViewHolder; -import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SubmenuViewHolder; -import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SwitchSettingViewHolder; -import org.dolphinemu.dolphinemu.utils.DirectoryInitialization; -import org.dolphinemu.dolphinemu.utils.FileBrowserHelper; -import org.dolphinemu.dolphinemu.utils.Log; -import org.dolphinemu.dolphinemu.utils.PermissionsHandler; - -import java.io.File; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.TimeZone; - -public final class SettingsAdapter extends RecyclerView.Adapter - implements DialogInterface.OnClickListener, Slider.OnChangeListener -{ - private final SettingsFragmentView mView; - private final Context mContext; - private ArrayList mSettings; - - private SettingsItem mClickedItem; - private int mClickedPosition; - private int mSeekbarProgress; - - private AlertDialog mDialog; - private TextView mTextSliderValue; - - public SettingsAdapter(SettingsFragmentView view, Context context) - { - mView = view; - mContext = context; - mClickedPosition = -1; - } - - @NonNull - @Override - public SettingViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) - { - LayoutInflater inflater = LayoutInflater.from(parent.getContext()); - switch (viewType) - { - case SettingsItem.TYPE_HEADER: - return new HeaderViewHolder(ListItemHeaderBinding.inflate(inflater), this); - - case SettingsItem.TYPE_SWITCH: - return new SwitchSettingViewHolder(ListItemSettingSwitchBinding.inflate(inflater), - this); - - case SettingsItem.TYPE_STRING_SINGLE_CHOICE: - case SettingsItem.TYPE_SINGLE_CHOICE_DYNAMIC_DESCRIPTIONS: - case SettingsItem.TYPE_SINGLE_CHOICE: - return new SingleChoiceViewHolder(ListItemSettingBinding.inflate(inflater), this); - - case SettingsItem.TYPE_SLIDER: - return new SliderViewHolder(ListItemSettingBinding.inflate(inflater), this, mContext); - - case SettingsItem.TYPE_SUBMENU: - return new SubmenuViewHolder(ListItemSubmenuBinding.inflate(inflater), this); - - case SettingsItem.TYPE_INPUT_MAPPING_CONTROL: - return new InputMappingControlSettingViewHolder(ListItemMappingBinding.inflate(inflater), - this); - - case SettingsItem.TYPE_FILE_PICKER: - return new FilePickerViewHolder(ListItemSettingBinding.inflate(inflater), this); - - case SettingsItem.TYPE_RUN_RUNNABLE: - return new RunRunnableViewHolder(ListItemSettingBinding.inflate(inflater), this, mContext); - - case SettingsItem.TYPE_STRING: - return new InputStringSettingViewHolder(ListItemSettingBinding.inflate(inflater), this); - - case SettingsItem.TYPE_HYPERLINK_HEADER: - return new HeaderHyperLinkViewHolder(ListItemHeaderBinding.inflate(inflater), this); - - case SettingsItem.TYPE_DATETIME_CHOICE: - return new DateTimeSettingViewHolder(ListItemSettingBinding.inflate(inflater), this); - - default: - throw new IllegalArgumentException("Invalid view type: " + viewType); - } - } - - @Override - public void onBindViewHolder(@NonNull SettingViewHolder holder, int position) - { - holder.bind(getItem(position)); - } - - private SettingsItem getItem(int position) - { - return mSettings.get(position); - } - - @Override - public int getItemCount() - { - if (mSettings != null) - { - return mSettings.size(); - } - else - { - return 0; - } - } - - @Override - public int getItemViewType(int position) - { - return getItem(position).getType(); - } - - public Settings getSettings() - { - return mView.getSettings(); - } - - public void setSettings(ArrayList settings) - { - mSettings = settings; - notifyDataSetChanged(); - } - - public void clearSetting(SettingsItem item) - { - item.clear(getSettings()); - - mView.onSettingChanged(); - } - - public void notifyAllSettingsChanged() - { - notifyItemRangeChanged(0, getItemCount()); - - mView.onSettingChanged(); - } - - public void onBooleanClick(SwitchSetting item, boolean checked) - { - item.setChecked(getSettings(), checked); - - mView.onSettingChanged(); - } - - public void onInputStringClick(InputStringSetting item, int position) - { - LayoutInflater inflater = LayoutInflater.from(mContext); - - DialogInputStringBinding binding = DialogInputStringBinding.inflate(inflater); - TextInputEditText input = binding.input; - input.setText(item.getSelectedValue()); - - mDialog = new MaterialAlertDialogBuilder(mView.getActivity()) - .setView(binding.getRoot()) - .setMessage(item.getDescription()) - .setPositiveButton(R.string.ok, (dialogInterface, i) -> - { - String editTextInput = input.getText().toString(); - - if (!item.getSelectedValue().equals(editTextInput)) - { - notifyItemChanged(position); - mView.onSettingChanged(); - } - - item.setSelectedValue(mView.getSettings(), editTextInput); - }) - .setNegativeButton(R.string.cancel, null) - .show(); - } - - public void onSingleChoiceClick(SingleChoiceSetting item, int position) - { - mClickedItem = item; - mClickedPosition = position; - - int value = getSelectionForSingleChoiceValue(item); - - mDialog = new MaterialAlertDialogBuilder(mView.getActivity()) - .setTitle(item.getName()) - .setSingleChoiceItems(item.getChoicesId(), value, this) - .show(); - } - - public void onStringSingleChoiceClick(StringSingleChoiceSetting item, int position) - { - mClickedItem = item; - mClickedPosition = position; - - item.refreshChoicesAndValues(); - - String[] choices = item.getChoices(); - int noChoicesAvailableString = item.getNoChoicesAvailableString(); - if (noChoicesAvailableString != 0 && choices.length == 0) - { - mDialog = new MaterialAlertDialogBuilder(mView.getActivity()) - .setTitle(item.getName()) - .setMessage(noChoicesAvailableString) - .setPositiveButton(R.string.ok, null) - .show(); - } - else - { - mDialog = new MaterialAlertDialogBuilder(mView.getActivity()) - .setTitle(item.getName()) - .setSingleChoiceItems(item.getChoices(), item.getSelectedValueIndex(), - this) - .show(); - } - } - - public void onSingleChoiceDynamicDescriptionsClick(SingleChoiceSettingDynamicDescriptions item, - int position) - { - mClickedItem = item; - mClickedPosition = position; - - int value = getSelectionForSingleChoiceDynamicDescriptionsValue(item); - - mDialog = new MaterialAlertDialogBuilder(mView.getActivity()) - .setTitle(item.getName()) - .setSingleChoiceItems(item.getChoicesId(), value, this) - .show(); - } - - public void onSliderClick(SliderSetting item, int position) - { - mClickedItem = item; - mClickedPosition = position; - mSeekbarProgress = item.getSelectedValue(); - - LayoutInflater inflater = LayoutInflater.from(mView.getActivity()); - DialogSliderBinding binding = DialogSliderBinding.inflate(inflater); - - mTextSliderValue = binding.textValue; - mTextSliderValue.setText(String.valueOf(mSeekbarProgress)); - - binding.textUnits.setText(item.getUnits()); - - Slider slider = binding.slider; - slider.setValueFrom(item.getMin()); - slider.setValueTo(item.getMax()); - slider.setValue(mSeekbarProgress); - slider.setStepSize(item.getStepSize()); - - slider.addOnChangeListener(this); - - mDialog = new MaterialAlertDialogBuilder(mView.getActivity()) - .setTitle(item.getName()) - .setView(binding.getRoot()) - .setPositiveButton(R.string.ok, this) - .show(); - } - - public void onSubmenuClick(SubmenuSetting item) - { - mView.loadSubMenu(item.getMenuKey()); - } - - public void onInputMappingClick(final InputMappingControlSetting item, final int position) - { - if (item.getController().getDefaultDevice().isEmpty() && !mView.isMappingAllDevices()) - { - new MaterialAlertDialogBuilder(mView.getActivity()) - .setMessage(R.string.input_binding_no_device) - .setPositiveButton(R.string.ok, this) - .show(); - return; - } - - final MotionAlertDialog dialog = new MotionAlertDialog(mView.getActivity(), item, - mView.isMappingAllDevices()); - - Drawable background = ContextCompat.getDrawable(mContext, R.drawable.dialog_round); - @ColorInt int color = new ElevationOverlayProvider(dialog.getContext()).compositeOverlay( - MaterialColors.getColor(dialog.getWindow().getDecorView(), R.attr.colorSurface), - dialog.getWindow().getDecorView().getElevation()); - background.setColorFilter(color, PorterDuff.Mode.SRC_ATOP); - dialog.getWindow().setBackgroundDrawable(background); - - dialog.setTitle(R.string.input_binding); - dialog.setMessage(String.format(mContext.getString(R.string.input_binding_description), - item.getName())); - dialog.setButton(AlertDialog.BUTTON_NEGATIVE, mContext.getString(R.string.cancel), this); - dialog.setButton(AlertDialog.BUTTON_NEUTRAL, mContext.getString(R.string.clear), - (dialogInterface, i) -> item.clearValue()); - dialog.setOnDismissListener(dialog1 -> - { - notifyItemChanged(position); - mView.onSettingChanged(); - }); - dialog.setCanceledOnTouchOutside(false); - dialog.show(); - } - - public void onAdvancedInputMappingClick(final InputMappingControlSetting item, final int position) - { - LayoutInflater inflater = LayoutInflater.from(mContext); - - DialogAdvancedMappingBinding binding = DialogAdvancedMappingBinding.inflate(inflater); - - final AdvancedMappingDialog dialog = new AdvancedMappingDialog(mContext, binding, - item.getControlReference(), item.getController()); - - Drawable background = ContextCompat.getDrawable(mContext, R.drawable.dialog_round); - @ColorInt int color = new ElevationOverlayProvider(dialog.getContext()).compositeOverlay( - MaterialColors.getColor(dialog.getWindow().getDecorView(), R.attr.colorSurface), - dialog.getWindow().getDecorView().getElevation()); - background.setColorFilter(color, PorterDuff.Mode.SRC_ATOP); - dialog.getWindow().setBackgroundDrawable(background); - - dialog.setTitle(item.isInput() ? - R.string.input_configure_input : R.string.input_configure_output); - dialog.setView(binding.getRoot()); - dialog.setButton(AlertDialog.BUTTON_POSITIVE, mContext.getString(R.string.ok), - (dialogInterface, i) -> - { - item.setValue(dialog.getExpression()); - notifyItemChanged(position); - mView.onSettingChanged(); - }); - dialog.setButton(AlertDialog.BUTTON_NEGATIVE, mContext.getString(R.string.cancel), this); - dialog.setButton(AlertDialog.BUTTON_NEUTRAL, mContext.getString(R.string.clear), - (dialogInterface, i) -> - { - item.clearValue(); - notifyItemChanged(position); - mView.onSettingChanged(); - }); - dialog.setCanceledOnTouchOutside(false); - dialog.show(); - } - - public void onFilePickerDirectoryClick(SettingsItem item, int position) - { - mClickedItem = item; - mClickedPosition = position; - - if (!PermissionsHandler.isExternalStorageLegacy()) - { - new MaterialAlertDialogBuilder(mContext) - .setMessage(R.string.path_not_changeable_scoped_storage) - .setPositiveButton(R.string.ok, (dialog, i) -> dialog.dismiss()) - .show(); - } - else - { - FileBrowserHelper.openDirectoryPicker(mView.getActivity(), FileBrowserHelper.GAME_EXTENSIONS); - } - } - - public void onFilePickerFileClick(SettingsItem item, int position) - { - mClickedItem = item; - mClickedPosition = position; - FilePicker filePicker = (FilePicker) item; - - Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType("*/*"); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) - { - intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, filePicker.getSelectedValue()); - } - - mView.getActivity().startActivityForResult(intent, filePicker.getRequestType()); - } - - public void onDateTimeClick(DateTimeChoiceSetting item, int position) - { - mClickedItem = item; - mClickedPosition = position; - long storedTime = Long.decode(item.getSelectedValue()) * 1000; - - // Helper to extract hour and minute from epoch time - Calendar calendar = Calendar.getInstance(); - calendar.setTimeInMillis(storedTime); - calendar.setTimeZone(TimeZone.getTimeZone("UTC")); - - // Start and end epoch times available for the Wii's date picker - CalendarConstraints calendarConstraints = new CalendarConstraints.Builder() - .setStart(946684800000L) - .setEnd(2082672000000L) - .build(); - - int timeFormat = TimeFormat.CLOCK_12H; - if (DateFormat.is24HourFormat(mView.getActivity())) - { - timeFormat = TimeFormat.CLOCK_24H; - } - - MaterialDatePicker datePicker = MaterialDatePicker.Builder.datePicker() - .setSelection(storedTime) - .setTitleText(R.string.select_rtc_date) - .setCalendarConstraints(calendarConstraints) - .build(); - MaterialTimePicker timePicker = new MaterialTimePicker.Builder() - .setTimeFormat(timeFormat) - .setHour(calendar.get(Calendar.HOUR_OF_DAY)) - .setMinute(calendar.get(Calendar.MINUTE)) - .setTitleText(R.string.select_rtc_time) - .build(); - - datePicker.addOnPositiveButtonClickListener( - selection -> timePicker.show(mView.getActivity().getSupportFragmentManager(), - "TimePicker")); - timePicker.addOnPositiveButtonClickListener(selection -> - { - long epochTime = datePicker.getSelection() / 1000; - epochTime += (long) timePicker.getHour() * 60 * 60; - epochTime += (long) timePicker.getMinute() * 60; - String rtcString = "0x" + Long.toHexString(epochTime); - if (!item.getSelectedValue().equals(rtcString)) - { - notifyItemChanged(mClickedPosition); - mView.onSettingChanged(); - } - - item.setSelectedValue(mView.getSettings(), rtcString); - - mClickedItem = null; - }); - datePicker.show(mView.getActivity().getSupportFragmentManager(), "DatePicker"); - } - - public void onFilePickerConfirmation(String selectedFile) - { - FilePicker filePicker = (FilePicker) mClickedItem; - - if (!filePicker.getSelectedValue().equals(selectedFile)) - { - notifyItemChanged(mClickedPosition); - mView.onSettingChanged(); - } - - filePicker.setSelectedValue(mView.getSettings(), selectedFile); - - mClickedItem = null; - } - - public static void clearLog() - { - // Don't delete the log in case it is being monitored by another app. - File log = new File(DirectoryInitialization.getUserDirectory() + "/Logs/dolphin.log"); - - try - { - RandomAccessFile raf = new RandomAccessFile(log, "rw"); - raf.setLength(0); - } - catch (IOException e) - { - Log.error("[SettingsAdapter] Failed to clear log file: " + e.getMessage()); - } - } - - public void onMenuTagAction(@NonNull MenuTag menuTag, int value) - { - mView.onMenuTagAction(menuTag, value); - } - - public boolean hasMenuTagActionForValue(@NonNull MenuTag menuTag, int value) - { - return mView.hasMenuTagActionForValue(menuTag, value); - } - - @Override - public void onClick(DialogInterface dialog, int which) - { - if (mClickedItem instanceof SingleChoiceSetting) - { - SingleChoiceSetting scSetting = (SingleChoiceSetting) mClickedItem; - - int value = getValueForSingleChoiceSelection(scSetting, which); - if (scSetting.getSelectedValue() != value) - mView.onSettingChanged(); - - scSetting.setSelectedValue(getSettings(), value); - - closeDialog(); - } - else if (mClickedItem instanceof SingleChoiceSettingDynamicDescriptions) - { - SingleChoiceSettingDynamicDescriptions scSetting = - (SingleChoiceSettingDynamicDescriptions) mClickedItem; - - int value = getValueForSingleChoiceDynamicDescriptionsSelection(scSetting, which); - if (scSetting.getSelectedValue() != value) - mView.onSettingChanged(); - - scSetting.setSelectedValue(getSettings(), value); - - closeDialog(); - } - else if (mClickedItem instanceof StringSingleChoiceSetting) - { - StringSingleChoiceSetting scSetting = (StringSingleChoiceSetting) mClickedItem; - String value = scSetting.getValueAt(which); - if (!scSetting.getSelectedValue().equals(value)) - mView.onSettingChanged(); - - scSetting.setSelectedValue(getSettings(), value); - - closeDialog(); - } - else if (mClickedItem instanceof IntSliderSetting) - { - IntSliderSetting sliderSetting = (IntSliderSetting) mClickedItem; - if (sliderSetting.getSelectedValue() != mSeekbarProgress) - mView.onSettingChanged(); - - sliderSetting.setSelectedValue(getSettings(), mSeekbarProgress); - - closeDialog(); - } - else if (mClickedItem instanceof FloatSliderSetting) - { - FloatSliderSetting sliderSetting = (FloatSliderSetting) mClickedItem; - if (sliderSetting.getSelectedValue() != mSeekbarProgress) - mView.onSettingChanged(); - - sliderSetting.setSelectedValue(getSettings(), mSeekbarProgress); - - closeDialog(); - } - - mClickedItem = null; - mSeekbarProgress = -1; - } - - public void closeDialog() - { - if (mDialog != null) - { - if (mClickedPosition != -1) - { - notifyItemChanged(mClickedPosition); - mClickedPosition = -1; - } - mDialog.dismiss(); - mDialog = null; - } - } - - @Override - public void onValueChange(@NonNull Slider slider, float progress, boolean fromUser) - { - mSeekbarProgress = (int) progress; - mTextSliderValue.setText(String.valueOf(mSeekbarProgress)); - } - - private int getValueForSingleChoiceSelection(SingleChoiceSetting item, int which) - { - int valuesId = item.getValuesId(); - - if (valuesId > 0) - { - int[] valuesArray = mContext.getResources().getIntArray(valuesId); - return valuesArray[which]; - } - else - { - return which; - } - } - - private int getSelectionForSingleChoiceValue(SingleChoiceSetting item) - { - int value = item.getSelectedValue(); - int valuesId = item.getValuesId(); - - if (valuesId > 0) - { - int[] valuesArray = mContext.getResources().getIntArray(valuesId); - for (int index = 0; index < valuesArray.length; index++) - { - int current = valuesArray[index]; - if (current == value) - { - return index; - } - } - } - else - { - return value; - } - - return -1; - } - - private int getValueForSingleChoiceDynamicDescriptionsSelection( - SingleChoiceSettingDynamicDescriptions item, int which) - { - int valuesId = item.getValuesId(); - - if (valuesId > 0) - { - int[] valuesArray = mContext.getResources().getIntArray(valuesId); - return valuesArray[which]; - } - else - { - return which; - } - } - - private int getSelectionForSingleChoiceDynamicDescriptionsValue( - SingleChoiceSettingDynamicDescriptions item) - { - int value = item.getSelectedValue(); - int valuesId = item.getValuesId(); - - if (valuesId > 0) - { - int[] valuesArray = mContext.getResources().getIntArray(valuesId); - for (int index = 0; index < valuesArray.length; index++) - { - int current = valuesArray[index]; - if (current == value) - { - return index; - } - } - } - else - { - return value; - } - - return -1; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.kt new file mode 100644 index 0000000000..d5df7c0e09 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.kt @@ -0,0 +1,590 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.ui + +import android.content.Context +import android.content.DialogInterface +import android.content.Intent +import android.graphics.PorterDuff +import android.os.Build +import android.provider.DocumentsContract +import android.text.format.DateFormat +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.TextView +import androidx.annotation.ColorInt +import androidx.appcompat.app.AlertDialog +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.color.MaterialColors +import com.google.android.material.datepicker.CalendarConstraints +import com.google.android.material.datepicker.MaterialDatePicker +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.google.android.material.elevation.ElevationOverlayProvider +import com.google.android.material.slider.Slider +import com.google.android.material.timepicker.MaterialTimePicker +import com.google.android.material.timepicker.TimeFormat +import org.dolphinemu.dolphinemu.R +import org.dolphinemu.dolphinemu.databinding.* +import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting +import org.dolphinemu.dolphinemu.features.input.ui.AdvancedMappingDialog +import org.dolphinemu.dolphinemu.features.input.ui.MotionAlertDialog +import org.dolphinemu.dolphinemu.features.input.ui.viewholder.InputMappingControlSettingViewHolder +import org.dolphinemu.dolphinemu.features.settings.model.Settings +import org.dolphinemu.dolphinemu.features.settings.model.view.* +import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.* +import org.dolphinemu.dolphinemu.utils.DirectoryInitialization +import org.dolphinemu.dolphinemu.utils.FileBrowserHelper +import org.dolphinemu.dolphinemu.utils.Log +import org.dolphinemu.dolphinemu.utils.PermissionsHandler +import java.io.File +import java.io.IOException +import java.io.RandomAccessFile +import java.util.* + +class SettingsAdapter( + private val fragmentView: SettingsFragmentView, + private val context: Context +) : + RecyclerView.Adapter(), DialogInterface.OnClickListener, + Slider.OnChangeListener { + private var settingsList: ArrayList? = null + private var clickedItem: SettingsItem? = null + private var clickedPosition: Int = -1 + private var seekbarProgress = 0 + private var dialog: AlertDialog? = null + private var textSliderValue: TextView? = null + + val settings: Settings? + get() = fragmentView.settings + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SettingViewHolder { + val inflater = LayoutInflater.from(parent.context) + return when (viewType) { + SettingsItem.TYPE_HEADER -> HeaderViewHolder( + ListItemHeaderBinding.inflate(inflater), + this + ) + SettingsItem.TYPE_SWITCH -> SwitchSettingViewHolder( + ListItemSettingSwitchBinding.inflate(inflater), + this + ) + SettingsItem.TYPE_STRING_SINGLE_CHOICE, + SettingsItem.TYPE_SINGLE_CHOICE_DYNAMIC_DESCRIPTIONS, + SettingsItem.TYPE_SINGLE_CHOICE -> SingleChoiceViewHolder( + ListItemSettingBinding.inflate(inflater), + this + ) + SettingsItem.TYPE_SLIDER -> SliderViewHolder( + ListItemSettingBinding.inflate( + inflater + ), this, context + ) + SettingsItem.TYPE_SUBMENU -> SubmenuViewHolder( + ListItemSubmenuBinding.inflate( + inflater + ), this + ) + SettingsItem.TYPE_INPUT_MAPPING_CONTROL -> InputMappingControlSettingViewHolder( + ListItemMappingBinding.inflate(inflater), + this + ) + SettingsItem.TYPE_FILE_PICKER -> FilePickerViewHolder( + ListItemSettingBinding.inflate( + inflater + ), this + ) + SettingsItem.TYPE_RUN_RUNNABLE -> RunRunnableViewHolder( + ListItemSettingBinding.inflate( + inflater + ), this, context + ) + SettingsItem.TYPE_STRING -> InputStringSettingViewHolder( + ListItemSettingBinding.inflate( + inflater + ), this + ) + SettingsItem.TYPE_HYPERLINK_HEADER -> HeaderHyperLinkViewHolder( + ListItemHeaderBinding.inflate( + inflater + ), this + ) + SettingsItem.TYPE_DATETIME_CHOICE -> DateTimeSettingViewHolder( + ListItemSettingBinding.inflate( + inflater + ), this + ) + else -> throw IllegalArgumentException("Invalid view type: $viewType") + } + } + + override fun onBindViewHolder(holder: SettingViewHolder, position: Int) { + holder.bind(getItem(position)) + } + + private fun getItem(position: Int): SettingsItem { + return settingsList!![position] + } + + override fun getItemCount(): Int { + return if (settingsList != null) { + settingsList!!.size + } else { + 0 + } + } + + override fun getItemViewType(position: Int): Int { + return getItem(position).type + } + + fun setSettings(settings: ArrayList?) { + settingsList = settings + notifyDataSetChanged() + } + + fun clearSetting(item: SettingsItem) { + item.clear(settings!!) + fragmentView.onSettingChanged() + } + + fun notifyAllSettingsChanged() { + notifyItemRangeChanged(0, itemCount) + fragmentView.onSettingChanged() + } + + fun onBooleanClick(item: SwitchSetting, checked: Boolean) { + item.setChecked(settings, checked) + fragmentView.onSettingChanged() + } + + fun onInputStringClick(item: InputStringSetting, position: Int) { + val inflater = LayoutInflater.from(context) + val binding = DialogInputStringBinding.inflate(inflater) + val input = binding.input + input.setText(item.selectedValue) + dialog = MaterialAlertDialogBuilder(fragmentView.fragmentActivity) + .setView(binding.root) + .setMessage(item.description) + .setPositiveButton(R.string.ok) { _: DialogInterface?, _: Int -> + val editTextInput = input.text.toString() + if (item.selectedValue != editTextInput) { + notifyItemChanged(position) + fragmentView.onSettingChanged() + } + item.setSelectedValue(fragmentView.settings!!, editTextInput) + } + .setNegativeButton(R.string.cancel, null) + .show() + } + + fun onSingleChoiceClick(item: SingleChoiceSetting, position: Int) { + clickedItem = item + clickedPosition = position + val value = getSelectionForSingleChoiceValue(item) + dialog = MaterialAlertDialogBuilder(fragmentView.fragmentActivity) + .setTitle(item.name) + .setSingleChoiceItems(item.choicesId, value, this) + .show() + } + + fun onStringSingleChoiceClick(item: StringSingleChoiceSetting, position: Int) { + clickedItem = item + clickedPosition = position + item.refreshChoicesAndValues() + val choices = item.choices + val noChoicesAvailableString = item.noChoicesAvailableString + dialog = if (noChoicesAvailableString != 0 && choices!!.isEmpty()) { + MaterialAlertDialogBuilder(fragmentView.fragmentActivity) + .setTitle(item.name) + .setMessage(noChoicesAvailableString) + .setPositiveButton(R.string.ok, null) + .show() + } else { + MaterialAlertDialogBuilder(fragmentView.fragmentActivity) + .setTitle(item.name) + .setSingleChoiceItems( + item.choices, item.selectedValueIndex, + this + ) + .show() + } + } + + fun onSingleChoiceDynamicDescriptionsClick( + item: SingleChoiceSettingDynamicDescriptions, + position: Int + ) { + clickedItem = item + clickedPosition = position + + val value = getSelectionForSingleChoiceDynamicDescriptionsValue(item) + + dialog = MaterialAlertDialogBuilder(fragmentView.fragmentActivity) + .setTitle(item.name) + .setSingleChoiceItems(item.choicesId, value, this) + .show() + } + + fun onSliderClick(item: SliderSetting, position: Int) { + clickedItem = item + clickedPosition = position + seekbarProgress = item.selectedValue + + val inflater = LayoutInflater.from(fragmentView.fragmentActivity) + val binding = DialogSliderBinding.inflate(inflater) + + textSliderValue = binding.textValue + textSliderValue!!.text = seekbarProgress.toString() + + binding.textUnits.text = item.units + + val slider = binding.slider + slider.valueFrom = item.min.toFloat() + slider.valueTo = item.max.toFloat() + slider.value = seekbarProgress.toFloat() + slider.stepSize = item.stepSize.toFloat() + slider.addOnChangeListener(this) + + dialog = MaterialAlertDialogBuilder(fragmentView.fragmentActivity) + .setTitle(item.name) + .setView(binding.root) + .setPositiveButton(R.string.ok, this) + .show() + } + + fun onSubmenuClick(item: SubmenuSetting) { + fragmentView.loadSubMenu(item.menuKey) + } + + fun onInputMappingClick(item: InputMappingControlSetting, position: Int) { + if (item.controller.defaultDevice.isEmpty() && !fragmentView.isMappingAllDevices) { + MaterialAlertDialogBuilder(fragmentView.fragmentActivity) + .setMessage(R.string.input_binding_no_device) + .setPositiveButton(R.string.ok, this) + .show() + return + } + + val dialog = MotionAlertDialog( + fragmentView.fragmentActivity, item, + fragmentView.isMappingAllDevices + ) + + val background = ContextCompat.getDrawable(context, R.drawable.dialog_round) + @ColorInt val color = ElevationOverlayProvider(dialog.context).compositeOverlay( + MaterialColors.getColor(dialog.window!!.decorView, R.attr.colorSurface), + dialog.window!!.decorView.elevation + ) + background!!.setColorFilter(color, PorterDuff.Mode.SRC_ATOP) + dialog.window!!.setBackgroundDrawable(background) + + dialog.setTitle(R.string.input_binding) + dialog.setMessage( + String.format( + context.getString(R.string.input_binding_description), + item.name + ) + ) + dialog.setButton(AlertDialog.BUTTON_NEGATIVE, context.getString(R.string.cancel), this) + dialog.setButton( + AlertDialog.BUTTON_NEUTRAL, + context.getString(R.string.clear) + ) { _: DialogInterface?, _: Int -> item.clearValue() } + dialog.setOnDismissListener { + notifyItemChanged(position) + fragmentView.onSettingChanged() + } + dialog.setCanceledOnTouchOutside(false) + dialog.show() + } + + fun onAdvancedInputMappingClick(item: InputMappingControlSetting, position: Int) { + val inflater = LayoutInflater.from(context) + val binding = DialogAdvancedMappingBinding.inflate(inflater) + val dialog = AdvancedMappingDialog( + context, + binding, + item.controlReference, + item.controller + ) + + val background = ContextCompat.getDrawable(context, R.drawable.dialog_round) + @ColorInt val color = ElevationOverlayProvider(dialog.context).compositeOverlay( + MaterialColors.getColor(dialog.window!!.decorView, R.attr.colorSurface), + dialog.window!!.decorView.elevation + ) + background!!.setColorFilter(color, PorterDuff.Mode.SRC_ATOP) + dialog.window!!.setBackgroundDrawable(background) + + dialog.setTitle(if (item.isInput) R.string.input_configure_input else R.string.input_configure_output) + dialog.setView(binding.root) + dialog.setButton( + AlertDialog.BUTTON_POSITIVE, context.getString(R.string.ok) + ) { _: DialogInterface?, _: Int -> + item.value = dialog.expression + notifyItemChanged(position) + fragmentView.onSettingChanged() + } + dialog.setButton(AlertDialog.BUTTON_NEGATIVE, context.getString(R.string.cancel), this) + dialog.setButton( + AlertDialog.BUTTON_NEUTRAL, + context.getString(R.string.clear) + ) { _: DialogInterface?, _: Int -> + item.clearValue() + notifyItemChanged(position) + fragmentView.onSettingChanged() + } + dialog.setCanceledOnTouchOutside(false) + dialog.show() + } + + fun onFilePickerDirectoryClick(item: SettingsItem?, position: Int) { + clickedItem = item + clickedPosition = position + + if (!PermissionsHandler.isExternalStorageLegacy()) { + MaterialAlertDialogBuilder(context) + .setMessage(R.string.path_not_changeable_scoped_storage) + .setPositiveButton(R.string.ok) { dialog: DialogInterface, _: Int -> dialog.dismiss() } + .show() + } else { + FileBrowserHelper.openDirectoryPicker( + fragmentView.fragmentActivity, + FileBrowserHelper.GAME_EXTENSIONS + ) + } + } + + fun onFilePickerFileClick(item: SettingsItem, position: Int) { + clickedItem = item + clickedPosition = position + val filePicker = item as FilePicker + + val intent = Intent(Intent.ACTION_OPEN_DOCUMENT) + intent.addCategory(Intent.CATEGORY_OPENABLE) + intent.type = "*/*" + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, filePicker.getSelectedValue()) + } + + fragmentView.fragmentActivity.startActivityForResult(intent, filePicker.requestType) + } + + fun onDateTimeClick(item: DateTimeChoiceSetting, position: Int) { + clickedItem = item + clickedPosition = position + val storedTime = java.lang.Long.decode(item.getSelectedValue()) * 1000 + + // Helper to extract hour and minute from epoch time + val calendar = Calendar.getInstance() + calendar.timeInMillis = storedTime + calendar.timeZone = TimeZone.getTimeZone("UTC") + + // Start and end epoch times available for the Wii's date picker + val calendarConstraints = CalendarConstraints.Builder() + .setStart(946684800000L) + .setEnd(2082672000000L) + .build() + + var timeFormat = TimeFormat.CLOCK_12H + if (DateFormat.is24HourFormat(fragmentView.fragmentActivity)) { + timeFormat = TimeFormat.CLOCK_24H + } + + val datePicker = MaterialDatePicker.Builder.datePicker() + .setSelection(storedTime) + .setTitleText(R.string.select_rtc_date) + .setCalendarConstraints(calendarConstraints) + .build() + val timePicker = MaterialTimePicker.Builder() + .setTimeFormat(timeFormat) + .setHour(calendar[Calendar.HOUR_OF_DAY]) + .setMinute(calendar[Calendar.MINUTE]) + .setTitleText(R.string.select_rtc_time) + .build() + + datePicker.addOnPositiveButtonClickListener { + timePicker.show( + fragmentView.fragmentActivity.supportFragmentManager, + "TimePicker" + ) + } + timePicker.addOnPositiveButtonClickListener { + var epochTime = datePicker.selection!! / 1000 + epochTime += timePicker.hour.toLong() * 60 * 60 + epochTime += timePicker.minute.toLong() * 60 + val rtcString = "0x" + java.lang.Long.toHexString(epochTime) + if (item.getSelectedValue() != rtcString) { + notifyItemChanged(clickedPosition) + fragmentView.onSettingChanged() + } + item.setSelectedValue(fragmentView.settings!!, rtcString) + clickedItem = null + } + datePicker.show(fragmentView.fragmentActivity.supportFragmentManager, "DatePicker") + } + + fun onFilePickerConfirmation(selectedFile: String) { + val filePicker = clickedItem as FilePicker? + + if (filePicker!!.getSelectedValue() != selectedFile) { + notifyItemChanged(clickedPosition) + fragmentView.onSettingChanged() + } + + filePicker.setSelectedValue(fragmentView.settings!!, selectedFile) + + clickedItem = null + } + + fun onMenuTagAction(menuTag: MenuTag, value: Int) { + fragmentView.onMenuTagAction(menuTag, value) + } + + fun hasMenuTagActionForValue(menuTag: MenuTag, value: Int): Boolean { + return fragmentView.hasMenuTagActionForValue(menuTag, value) + } + + override fun onClick(dialog: DialogInterface, which: Int) { + when (clickedItem) { + is SingleChoiceSetting -> { + val scSetting = clickedItem as SingleChoiceSetting + + val value = getValueForSingleChoiceSelection(scSetting, which) + if (scSetting.selectedValue != value) fragmentView.onSettingChanged() + + scSetting.setSelectedValue(settings, value) + + closeDialog() + } + is SingleChoiceSettingDynamicDescriptions -> { + val scSetting = clickedItem as SingleChoiceSettingDynamicDescriptions + + val value = getValueForSingleChoiceDynamicDescriptionsSelection(scSetting, which) + if (scSetting.selectedValue != value) fragmentView.onSettingChanged() + + scSetting.setSelectedValue(settings!!, value) + + closeDialog() + } + is StringSingleChoiceSetting -> { + val scSetting = clickedItem as StringSingleChoiceSetting + + val value = scSetting.getValueAt(which) + if (scSetting.selectedValue != value) fragmentView.onSettingChanged() + + scSetting.setSelectedValue(settings, value) + + closeDialog() + } + is IntSliderSetting -> { + val sliderSetting = clickedItem as IntSliderSetting + if (sliderSetting.selectedValue != seekbarProgress) fragmentView.onSettingChanged() + sliderSetting.setSelectedValue(settings, seekbarProgress) + closeDialog() + } + is FloatSliderSetting -> { + val sliderSetting = clickedItem as FloatSliderSetting + + if (sliderSetting.selectedValue != seekbarProgress) fragmentView.onSettingChanged() + + sliderSetting.setSelectedValue(settings, seekbarProgress.toFloat()) + + closeDialog() + } + } + clickedItem = null + seekbarProgress = -1 + } + + fun closeDialog() { + if (dialog != null) { + if (clickedPosition != -1) { + notifyItemChanged(clickedPosition) + clickedPosition = -1 + } + dialog!!.dismiss() + dialog = null + } + } + + override fun onValueChange(slider: Slider, progress: Float, fromUser: Boolean) { + seekbarProgress = progress.toInt() + textSliderValue!!.text = seekbarProgress.toString() + } + + private fun getValueForSingleChoiceSelection(item: SingleChoiceSetting, which: Int): Int { + val valuesId = item.valuesId + + return if (valuesId > 0) { + val valuesArray = context.resources.getIntArray(valuesId) + valuesArray[which] + } else { + which + } + } + + private fun getSelectionForSingleChoiceValue(item: SingleChoiceSetting): Int { + val value = item.selectedValue + val valuesId = item.valuesId + + if (valuesId > 0) { + val valuesArray = context.resources.getIntArray(valuesId) + for (index in valuesArray.indices) { + val current = valuesArray[index] + if (current == value) { + return index + } + } + } else { + return value + } + return -1 + } + + private fun getValueForSingleChoiceDynamicDescriptionsSelection( + item: SingleChoiceSettingDynamicDescriptions, + which: Int + ): Int { + val valuesId = item.valuesId + return if (valuesId > 0) { + val valuesArray = context.resources.getIntArray(valuesId) + valuesArray[which] + } else { + which + } + } + + private fun getSelectionForSingleChoiceDynamicDescriptionsValue( + item: SingleChoiceSettingDynamicDescriptions + ): Int { + val value = item.selectedValue + val valuesId = item.valuesId + if (valuesId > 0) { + val valuesArray = context.resources.getIntArray(valuesId) + for (index in valuesArray.indices) { + val current = valuesArray[index] + if (current == value) { + return index + } + } + } else { + return value + } + return -1 + } + + companion object { + fun clearLog() { + // Don't delete the log in case it is being monitored by another app. + val log = File(DirectoryInitialization.getUserDirectory() + "/Logs/dolphin.log") + try { + val raf = RandomAccessFile(log, "rw") + raf.setLength(0) + } catch (e: IOException) { + Log.error("[SettingsAdapter] Failed to clear log file: " + e.message) + } + } + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.java deleted file mode 100644 index 1a7905565d..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.java +++ /dev/null @@ -1,295 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.ui; - -import android.content.Context; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.graphics.Insets; -import androidx.core.view.ViewCompat; -import androidx.core.view.WindowInsetsCompat; -import androidx.fragment.app.DialogFragment; -import androidx.fragment.app.Fragment; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import org.dolphinemu.dolphinemu.R; -import org.dolphinemu.dolphinemu.databinding.FragmentSettingsBinding; -import org.dolphinemu.dolphinemu.features.settings.model.Settings; -import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; - -public final class SettingsFragment extends Fragment implements SettingsFragmentView -{ - private static final String ARGUMENT_MENU_TAG = "menu_tag"; - private static final String ARGUMENT_GAME_ID = "game_id"; - - private SettingsFragmentPresenter mPresenter; - private SettingsActivityView mActivity; - - private SettingsAdapter mAdapter; - - private int mOldControllerSettingsWarningHeight = 0; - - private static final Map titles = new HashMap<>(); - - static - { - titles.put(MenuTag.SETTINGS, R.string.settings); - titles.put(MenuTag.CONFIG, R.string.config); - titles.put(MenuTag.CONFIG_GENERAL, R.string.general_submenu); - titles.put(MenuTag.CONFIG_INTERFACE, R.string.interface_submenu); - titles.put(MenuTag.CONFIG_AUDIO, R.string.audio_submenu); - titles.put(MenuTag.CONFIG_PATHS, R.string.paths_submenu); - titles.put(MenuTag.CONFIG_GAME_CUBE, R.string.gamecube_submenu); - titles.put(MenuTag.CONFIG_SERIALPORT1, R.string.serialport1_submenu); - titles.put(MenuTag.CONFIG_WII, R.string.wii_submenu); - titles.put(MenuTag.CONFIG_ADVANCED, R.string.advanced_submenu); - titles.put(MenuTag.DEBUG, R.string.debug_submenu); - titles.put(MenuTag.GRAPHICS, R.string.graphics_settings); - titles.put(MenuTag.ENHANCEMENTS, R.string.enhancements_submenu); - titles.put(MenuTag.STEREOSCOPY, R.string.stereoscopy_submenu); - titles.put(MenuTag.HACKS, R.string.hacks_submenu); - titles.put(MenuTag.STATISTICS, R.string.statistics_submenu); - titles.put(MenuTag.ADVANCED_GRAPHICS, R.string.advanced_graphics_submenu); - titles.put(MenuTag.CONFIG_LOG, R.string.log_submenu); - titles.put(MenuTag.GCPAD_TYPE, R.string.gcpad_settings); - titles.put(MenuTag.WIIMOTE, R.string.wiimote_settings); - titles.put(MenuTag.WIIMOTE_EXTENSION, R.string.wiimote_extensions); - titles.put(MenuTag.GCPAD_1, R.string.controller_0); - titles.put(MenuTag.GCPAD_2, R.string.controller_1); - titles.put(MenuTag.GCPAD_3, R.string.controller_2); - titles.put(MenuTag.GCPAD_4, R.string.controller_3); - titles.put(MenuTag.WIIMOTE_1, R.string.wiimote_0); - titles.put(MenuTag.WIIMOTE_2, R.string.wiimote_1); - titles.put(MenuTag.WIIMOTE_3, R.string.wiimote_2); - titles.put(MenuTag.WIIMOTE_4, R.string.wiimote_3); - titles.put(MenuTag.WIIMOTE_EXTENSION_1, R.string.wiimote_extension_0); - titles.put(MenuTag.WIIMOTE_EXTENSION_2, R.string.wiimote_extension_1); - titles.put(MenuTag.WIIMOTE_EXTENSION_3, R.string.wiimote_extension_2); - titles.put(MenuTag.WIIMOTE_EXTENSION_4, R.string.wiimote_extension_3); - titles.put(MenuTag.WIIMOTE_GENERAL_1, R.string.wiimote_general); - titles.put(MenuTag.WIIMOTE_GENERAL_2, R.string.wiimote_general); - titles.put(MenuTag.WIIMOTE_GENERAL_3, R.string.wiimote_general); - titles.put(MenuTag.WIIMOTE_GENERAL_4, R.string.wiimote_general); - titles.put(MenuTag.WIIMOTE_MOTION_SIMULATION_1, R.string.wiimote_motion_simulation); - titles.put(MenuTag.WIIMOTE_MOTION_SIMULATION_2, R.string.wiimote_motion_simulation); - titles.put(MenuTag.WIIMOTE_MOTION_SIMULATION_3, R.string.wiimote_motion_simulation); - titles.put(MenuTag.WIIMOTE_MOTION_SIMULATION_4, R.string.wiimote_motion_simulation); - titles.put(MenuTag.WIIMOTE_MOTION_INPUT_1, R.string.wiimote_motion_input); - titles.put(MenuTag.WIIMOTE_MOTION_INPUT_2, R.string.wiimote_motion_input); - titles.put(MenuTag.WIIMOTE_MOTION_INPUT_3, R.string.wiimote_motion_input); - titles.put(MenuTag.WIIMOTE_MOTION_INPUT_4, R.string.wiimote_motion_input); - } - - private FragmentSettingsBinding mBinding; - - public static Fragment newInstance(MenuTag menuTag, String gameId, Bundle extras) - { - SettingsFragment fragment = new SettingsFragment(); - - Bundle arguments = new Bundle(); - if (extras != null) - { - arguments.putAll(extras); - } - - arguments.putSerializable(ARGUMENT_MENU_TAG, menuTag); - arguments.putString(ARGUMENT_GAME_ID, gameId); - - fragment.setArguments(arguments); - return fragment; - } - - @Override - public void onAttach(@NonNull Context context) - { - super.onAttach(context); - - mActivity = (SettingsActivityView) context; - } - - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - - Bundle args = getArguments(); - MenuTag menuTag = (MenuTag) args.getSerializable(ARGUMENT_MENU_TAG); - String gameId = getArguments().getString(ARGUMENT_GAME_ID); - - mPresenter = new SettingsFragmentPresenter(this, getContext()); - mAdapter = new SettingsAdapter(this, getContext()); - - mPresenter.onCreate(menuTag, gameId, args); - } - - @NonNull - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) - { - mBinding = FragmentSettingsBinding.inflate(inflater, container, false); - return mBinding.getRoot(); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) - { - Bundle args = getArguments(); - MenuTag menuTag = (MenuTag) args.getSerializable(ARGUMENT_MENU_TAG); - - if (titles.containsKey(menuTag)) - { - mActivity.setToolbarTitle(getString(titles.get(menuTag))); - } - - LinearLayoutManager manager = new LinearLayoutManager(getActivity()); - - RecyclerView recyclerView = mBinding.listSettings; - - recyclerView.setAdapter(mAdapter); - recyclerView.setLayoutManager(manager); - - SettingsDividerItemDecoration divider = new SettingsDividerItemDecoration(requireActivity()); - recyclerView.addItemDecoration(divider); - - setInsets(); - - SettingsActivityView activity = (SettingsActivityView) getActivity(); - mPresenter.onViewCreated(menuTag, activity.getSettings()); - } - - @Override - public void onDestroyView() - { - super.onDestroyView(); - mBinding = null; - } - - @Override - public void onDetach() - { - super.onDetach(); - mActivity = null; - - if (mAdapter != null) - { - mAdapter.closeDialog(); - } - } - - @Override - public void onSettingsFileLoaded( - org.dolphinemu.dolphinemu.features.settings.model.Settings settings) - { - mPresenter.setSettings(settings); - } - - @Override - public void showSettingsList(ArrayList settingsList) - { - mAdapter.setSettings(settingsList); - } - - @Override - public void loadDefaultSettings() - { - mPresenter.loadDefaultSettings(); - } - - @Override - public SettingsAdapter getAdapter() - { - return mAdapter; - } - - @Override - public void loadSubMenu(MenuTag menuKey) - { - mActivity.showSettingsFragment(menuKey, null, true, getArguments().getString(ARGUMENT_GAME_ID)); - } - - @Override - public void showDialogFragment(DialogFragment fragment) - { - mActivity.showDialogFragment(fragment); - } - - @Override - public void showToastMessage(String message) - { - mActivity.showToastMessage(message); - } - - @Override - public Settings getSettings() - { - return mPresenter.getSettings(); - } - - @Override - public void onSettingChanged() - { - mActivity.onSettingChanged(); - } - - @Override - public void onControllerSettingsChanged() - { - mAdapter.notifyAllSettingsChanged(); - mPresenter.updateOldControllerSettingsWarningVisibility(); - } - - @Override - public void onMenuTagAction(@NonNull MenuTag menuTag, int value) - { - mActivity.onMenuTagAction(menuTag, value); - } - - public boolean hasMenuTagActionForValue(@NonNull MenuTag menuTag, int value) - { - return mActivity.hasMenuTagActionForValue(menuTag, value); - } - - @Override - public void setMappingAllDevices(boolean allDevices) - { - mActivity.setMappingAllDevices(allDevices); - } - - @Override - public boolean isMappingAllDevices() - { - return mActivity.isMappingAllDevices(); - } - - @Override - public void setOldControllerSettingsWarningVisibility(boolean visible) - { - mOldControllerSettingsWarningHeight = - mActivity.setOldControllerSettingsWarningVisibility(visible); - - // Trigger the insets listener we've registered - mBinding.listSettings.requestApplyInsets(); - } - - private void setInsets() - { - ViewCompat.setOnApplyWindowInsetsListener(mBinding.listSettings, (v, windowInsets) -> - { - Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); - int listSpacing = getResources().getDimensionPixelSize(R.dimen.spacing_list); - v.setPadding(0, 0, 0, insets.bottom + listSpacing + mOldControllerSettingsWarningHeight); - return windowInsets; - }); - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.kt new file mode 100644 index 0000000000..d9ffd9113f --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.kt @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.ui + +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updatePadding +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import androidx.recyclerview.widget.LinearLayoutManager +import org.dolphinemu.dolphinemu.R +import org.dolphinemu.dolphinemu.databinding.FragmentSettingsBinding +import org.dolphinemu.dolphinemu.features.settings.model.Settings +import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem +import org.dolphinemu.dolphinemu.utils.SerializableHelper.serializable +import java.util.* +import kotlin.collections.ArrayList + +class SettingsFragment : Fragment(), SettingsFragmentView { + private lateinit var presenter: SettingsFragmentPresenter + private var activityView: SettingsActivityView? = null + + private lateinit var menuTag: MenuTag + + override val fragmentActivity: FragmentActivity + get() = requireActivity() + + override var adapter: SettingsAdapter? = null + + private var oldControllerSettingsWarningHeight = 0 + + private var binding: FragmentSettingsBinding? = null + + override fun onAttach(context: Context) { + super.onAttach(context) + + activityView = context as SettingsActivityView + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + menuTag = requireArguments().serializable(ARGUMENT_MENU_TAG)!! + + val gameId = requireArguments().getString(ARGUMENT_GAME_ID) + presenter = SettingsFragmentPresenter(this, requireContext()) + adapter = SettingsAdapter(this, requireContext()) + + presenter.onCreate(menuTag, gameId, requireArguments()) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentSettingsBinding.inflate(inflater, container, false) + return binding!!.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + if (titles.containsKey(menuTag)) { + activityView!!.setToolbarTitle(getString(titles[menuTag]!!)) + } + + val manager = LinearLayoutManager(activity) + + val recyclerView = binding!!.listSettings + recyclerView.adapter = adapter + recyclerView.layoutManager = manager + + val divider = SettingsDividerItemDecoration(requireActivity()) + recyclerView.addItemDecoration(divider) + + setInsets() + + val activity = activity as SettingsActivityView? + presenter.onViewCreated(menuTag, activity!!.settings) + } + + override fun onDestroyView() { + super.onDestroyView() + binding = null + } + + override fun onDetach() { + super.onDetach() + activityView = null + + if (adapter != null) { + adapter!!.closeDialog() + } + } + + override fun onSettingsFileLoaded(settings: Settings) { + presenter.settings = settings + } + + override fun showSettingsList(settingsList: ArrayList) { + adapter!!.setSettings(settingsList) + } + + override fun loadDefaultSettings() { + presenter.loadDefaultSettings() + } + + override fun loadSubMenu(menuKey: MenuTag) { + activityView!!.showSettingsFragment( + menuKey, + null, + true, + requireArguments().getString(ARGUMENT_GAME_ID)!! + ) + } + + override fun showDialogFragment(fragment: DialogFragment) { + activityView!!.showDialogFragment(fragment) + } + + override fun showToastMessage(message: String) { + activityView!!.showToastMessage(message) + } + + override val settings: Settings? + get() = presenter.settings + + override fun onSettingChanged() { + activityView!!.onSettingChanged() + } + + override fun onControllerSettingsChanged() { + adapter!!.notifyAllSettingsChanged() + presenter.updateOldControllerSettingsWarningVisibility() + } + + override fun onMenuTagAction(menuTag: MenuTag, value: Int) { + activityView!!.onMenuTagAction(menuTag, value) + } + + override fun hasMenuTagActionForValue(menuTag: MenuTag, value: Int): Boolean { + return activityView!!.hasMenuTagActionForValue(menuTag, value) + } + + override var isMappingAllDevices: Boolean + get() = activityView!!.isMappingAllDevices + set(allDevices) { + activityView!!.isMappingAllDevices = allDevices + } + + override fun setOldControllerSettingsWarningVisibility(visible: Boolean) { + oldControllerSettingsWarningHeight = + activityView!!.setOldControllerSettingsWarningVisibility(visible) + + // Trigger the insets listener we've registered + binding!!.listSettings.requestApplyInsets() + } + + private fun setInsets() { + ViewCompat.setOnApplyWindowInsetsListener(binding!!.listSettings) { v: View, windowInsets: WindowInsetsCompat -> + val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) + val listSpacing = resources.getDimensionPixelSize(R.dimen.spacing_list) + v.updatePadding(bottom = insets.bottom + listSpacing + oldControllerSettingsWarningHeight) + windowInsets + } + } + + companion object { + private const val ARGUMENT_MENU_TAG = "menu_tag" + private const val ARGUMENT_GAME_ID = "game_id" + private val titles: MutableMap = EnumMap(MenuTag::class.java) + + init { + titles[MenuTag.SETTINGS] = R.string.settings + titles[MenuTag.CONFIG] = R.string.config + titles[MenuTag.CONFIG_GENERAL] = R.string.general_submenu + titles[MenuTag.CONFIG_INTERFACE] = R.string.interface_submenu + titles[MenuTag.CONFIG_AUDIO] = R.string.audio_submenu + titles[MenuTag.CONFIG_PATHS] = R.string.paths_submenu + titles[MenuTag.CONFIG_GAME_CUBE] = R.string.gamecube_submenu + titles[MenuTag.CONFIG_SERIALPORT1] = R.string.serialport1_submenu + titles[MenuTag.CONFIG_WII] = R.string.wii_submenu + titles[MenuTag.CONFIG_ADVANCED] = R.string.advanced_submenu + titles[MenuTag.DEBUG] = R.string.debug_submenu + titles[MenuTag.GRAPHICS] = R.string.graphics_settings + titles[MenuTag.ENHANCEMENTS] = R.string.enhancements_submenu + titles[MenuTag.STEREOSCOPY] = R.string.stereoscopy_submenu + titles[MenuTag.HACKS] = R.string.hacks_submenu + titles[MenuTag.STATISTICS] = R.string.statistics_submenu + titles[MenuTag.ADVANCED_GRAPHICS] = R.string.advanced_graphics_submenu + titles[MenuTag.CONFIG_LOG] = R.string.log_submenu + titles[MenuTag.GCPAD_TYPE] = R.string.gcpad_settings + titles[MenuTag.WIIMOTE] = R.string.wiimote_settings + titles[MenuTag.WIIMOTE_EXTENSION] = R.string.wiimote_extensions + titles[MenuTag.GCPAD_1] = R.string.controller_0 + titles[MenuTag.GCPAD_2] = R.string.controller_1 + titles[MenuTag.GCPAD_3] = R.string.controller_2 + titles[MenuTag.GCPAD_4] = R.string.controller_3 + titles[MenuTag.WIIMOTE_1] = R.string.wiimote_0 + titles[MenuTag.WIIMOTE_2] = R.string.wiimote_1 + titles[MenuTag.WIIMOTE_3] = R.string.wiimote_2 + titles[MenuTag.WIIMOTE_4] = R.string.wiimote_3 + titles[MenuTag.WIIMOTE_EXTENSION_1] = R.string.wiimote_extension_0 + titles[MenuTag.WIIMOTE_EXTENSION_2] = R.string.wiimote_extension_1 + titles[MenuTag.WIIMOTE_EXTENSION_3] = R.string.wiimote_extension_2 + titles[MenuTag.WIIMOTE_EXTENSION_4] = R.string.wiimote_extension_3 + titles[MenuTag.WIIMOTE_GENERAL_1] = R.string.wiimote_general + titles[MenuTag.WIIMOTE_GENERAL_2] = R.string.wiimote_general + titles[MenuTag.WIIMOTE_GENERAL_3] = R.string.wiimote_general + titles[MenuTag.WIIMOTE_GENERAL_4] = R.string.wiimote_general + titles[MenuTag.WIIMOTE_MOTION_SIMULATION_1] = R.string.wiimote_motion_simulation + titles[MenuTag.WIIMOTE_MOTION_SIMULATION_2] = R.string.wiimote_motion_simulation + titles[MenuTag.WIIMOTE_MOTION_SIMULATION_3] = R.string.wiimote_motion_simulation + titles[MenuTag.WIIMOTE_MOTION_SIMULATION_4] = R.string.wiimote_motion_simulation + titles[MenuTag.WIIMOTE_MOTION_INPUT_1] = R.string.wiimote_motion_input + titles[MenuTag.WIIMOTE_MOTION_INPUT_2] = R.string.wiimote_motion_input + titles[MenuTag.WIIMOTE_MOTION_INPUT_3] = R.string.wiimote_motion_input + titles[MenuTag.WIIMOTE_MOTION_INPUT_4] = R.string.wiimote_motion_input + } + + @JvmStatic + fun newInstance(menuTag: MenuTag?, gameId: String?, extras: Bundle?): Fragment { + val fragment = SettingsFragment() + + val arguments = Bundle() + if (extras != null) { + arguments.putAll(extras) + } + + arguments.putSerializable(ARGUMENT_MENU_TAG, menuTag) + arguments.putString(ARGUMENT_GAME_ID, gameId) + + fragment.arguments = arguments + return fragment + } + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java deleted file mode 100644 index 79b0666b5a..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java +++ /dev/null @@ -1,1402 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.ui; - -import android.content.Context; -import android.content.pm.PackageManager; -import android.os.Build; -import android.os.Bundle; -import android.text.TextUtils; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatActivity; -import androidx.collection.ArraySet; - -import org.dolphinemu.dolphinemu.NativeLibrary; -import org.dolphinemu.dolphinemu.R; -import org.dolphinemu.dolphinemu.activities.UserDataActivity; -import org.dolphinemu.dolphinemu.features.input.model.InputMappingBooleanSetting; -import org.dolphinemu.dolphinemu.features.input.model.InputMappingDoubleSetting; -import org.dolphinemu.dolphinemu.features.input.model.InputMappingIntSetting; -import org.dolphinemu.dolphinemu.features.input.model.controlleremu.ControlGroup; -import org.dolphinemu.dolphinemu.features.input.model.ControlGroupEnabledSetting; -import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController; -import org.dolphinemu.dolphinemu.features.input.model.controlleremu.NumericSetting; -import org.dolphinemu.dolphinemu.features.input.model.view.InputDeviceSetting; -import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting; -import org.dolphinemu.dolphinemu.features.input.ui.ProfileDialog; -import org.dolphinemu.dolphinemu.features.input.ui.ProfileDialogPresenter; -import org.dolphinemu.dolphinemu.features.settings.model.AbstractBooleanSetting; -import org.dolphinemu.dolphinemu.features.settings.model.AbstractIntSetting; -import org.dolphinemu.dolphinemu.features.settings.model.AdHocBooleanSetting; -import org.dolphinemu.dolphinemu.features.settings.model.AdHocStringSetting; -import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting; -import org.dolphinemu.dolphinemu.features.settings.model.FloatSetting; -import org.dolphinemu.dolphinemu.features.settings.model.IntSetting; -import org.dolphinemu.dolphinemu.features.settings.model.PostProcessing; -import org.dolphinemu.dolphinemu.features.settings.model.ScaledIntSetting; -import org.dolphinemu.dolphinemu.features.settings.model.Settings; -import org.dolphinemu.dolphinemu.features.settings.model.StringSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.DateTimeChoiceSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.SwitchSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.FilePicker; -import org.dolphinemu.dolphinemu.features.settings.model.view.FloatSliderSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.HeaderSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.HyperLinkHeaderSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.InputStringSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.IntSliderSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.InvertedSwitchSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.LogSwitchSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.PercentSliderSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.RunRunnable; -import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; -import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSettingDynamicDescriptions; -import org.dolphinemu.dolphinemu.features.settings.model.view.StringSingleChoiceSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.SubmenuSetting; -import org.dolphinemu.dolphinemu.ui.main.MainPresenter; -import org.dolphinemu.dolphinemu.utils.BooleanSupplier; -import org.dolphinemu.dolphinemu.utils.EGLHelper; -import org.dolphinemu.dolphinemu.utils.ThemeHelper; -import org.dolphinemu.dolphinemu.utils.ThreadUtil; -import org.dolphinemu.dolphinemu.utils.WiiUtils; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; - -public final class SettingsFragmentPresenter -{ - private final SettingsFragmentView mView; - private final Context mContext; - - private static final LinkedHashMap LOG_TYPE_NAMES = - NativeLibrary.GetLogTypeNames(); - - public static final String ARG_CONTROLLER_TYPE = "controller_type"; - public static final String ARG_SERIALPORT1_TYPE = "serialport1_type"; - private MenuTag mMenuTag; - private String mGameID; - - private Settings mSettings; - private ArrayList mSettingsList; - private boolean mHasOldControllerSettings = false; - - private int mSerialPort1Type; - private int mControllerNumber; - private int mControllerType; - - public SettingsFragmentPresenter(SettingsFragmentView view, Context context) - { - mView = view; - mContext = context; - } - - public void onCreate(MenuTag menuTag, String gameId, Bundle extras) - { - mGameID = gameId; - this.mMenuTag = menuTag; - - if (menuTag.isGCPadMenu() || menuTag.isWiimoteExtensionMenu()) - { - mControllerNumber = menuTag.getSubType(); - mControllerType = extras.getInt(ARG_CONTROLLER_TYPE); - } - else if (menuTag.isWiimoteMenu()) - { - mControllerNumber = menuTag.getSubType(); - } - else if (menuTag.isSerialPort1Menu()) - { - mSerialPort1Type = extras.getInt(ARG_SERIALPORT1_TYPE); - } - } - - public void onViewCreated(MenuTag menuTag, Settings settings) - { - this.mMenuTag = menuTag; - - if (!TextUtils.isEmpty(mGameID)) - { - mView.getActivity().setTitle(mContext.getString(R.string.game_settings, mGameID)); - } - - setSettings(settings); - } - - public Settings getSettings() - { - return mSettings; - } - - public void loadDefaultSettings() - { - loadSettingsList(); - } - - public void setSettings(Settings settings) - { - if (mSettingsList == null && settings != null) - { - mSettings = settings; - - loadSettingsList(); - } - else - { - mView.showSettingsList(mSettingsList); - mView.setOldControllerSettingsWarningVisibility(mHasOldControllerSettings); - } - } - - private void loadSettingsList() - { - ArrayList sl = new ArrayList<>(); - - switch (mMenuTag) - { - case SETTINGS: - addTopLevelSettings(sl); - break; - - case CONFIG: - addConfigSettings(sl); - break; - - case CONFIG_GENERAL: - addGeneralSettings(sl); - break; - - case CONFIG_INTERFACE: - addInterfaceSettings(sl); - break; - - case CONFIG_AUDIO: - addAudioSettings(sl); - break; - - case CONFIG_PATHS: - addPathsSettings(sl); - break; - - case CONFIG_GAME_CUBE: - addGameCubeSettings(sl); - break; - - case CONFIG_WII: - addWiiSettings(sl); - break; - - case CONFIG_ADVANCED: - addAdvancedSettings(sl); - break; - - case GRAPHICS: - addGraphicsSettings(sl); - break; - - case CONFIG_SERIALPORT1: - addSerialPortSubSettings(sl, mSerialPort1Type); - break; - - case GCPAD_TYPE: - addGcPadSettings(sl); - break; - - case WIIMOTE: - addWiimoteSettings(sl); - break; - - case ENHANCEMENTS: - addEnhanceSettings(sl); - break; - - case STEREOSCOPY: - addStereoSettings(sl); - break; - - case HACKS: - addHackSettings(sl); - break; - - case STATISTICS: - addStatisticsSettings(sl); - break; - - case ADVANCED_GRAPHICS: - addAdvancedGraphicsSettings(sl); - break; - - case CONFIG_LOG: - addLogConfigurationSettings(sl); - break; - - case DEBUG: - addDebugSettings(sl); - break; - - case GCPAD_1: - case GCPAD_2: - case GCPAD_3: - case GCPAD_4: - addGcPadSubSettings(sl, mControllerNumber, mControllerType); - break; - - case WIIMOTE_1: - case WIIMOTE_2: - case WIIMOTE_3: - case WIIMOTE_4: - addWiimoteSubSettings(sl, mControllerNumber); - break; - - case WIIMOTE_EXTENSION_1: - case WIIMOTE_EXTENSION_2: - case WIIMOTE_EXTENSION_3: - case WIIMOTE_EXTENSION_4: - addExtensionTypeSettings(sl, mControllerNumber, mControllerType); - break; - - case WIIMOTE_GENERAL_1: - case WIIMOTE_GENERAL_2: - case WIIMOTE_GENERAL_3: - case WIIMOTE_GENERAL_4: - addWiimoteGeneralSubSettings(sl, mControllerNumber); - break; - - case WIIMOTE_MOTION_SIMULATION_1: - case WIIMOTE_MOTION_SIMULATION_2: - case WIIMOTE_MOTION_SIMULATION_3: - case WIIMOTE_MOTION_SIMULATION_4: - addWiimoteMotionSimulationSubSettings(sl, mControllerNumber); - break; - - case WIIMOTE_MOTION_INPUT_1: - case WIIMOTE_MOTION_INPUT_2: - case WIIMOTE_MOTION_INPUT_3: - case WIIMOTE_MOTION_INPUT_4: - addWiimoteMotionInputSubSettings(sl, mControllerNumber); - break; - - default: - throw new UnsupportedOperationException("Unimplemented menu"); - } - - mSettingsList = sl; - mView.showSettingsList(mSettingsList); - } - - private void addTopLevelSettings(ArrayList sl) - { - sl.add(new SubmenuSetting(mContext, R.string.config, MenuTag.CONFIG)); - sl.add(new SubmenuSetting(mContext, R.string.graphics_settings, MenuTag.GRAPHICS)); - - sl.add(new SubmenuSetting(mContext, R.string.gcpad_settings, MenuTag.GCPAD_TYPE)); - if (mSettings.isWii()) - { - sl.add(new SubmenuSetting(mContext, R.string.wiimote_settings, MenuTag.WIIMOTE)); - } - - sl.add(new HeaderSetting(mContext, R.string.setting_clear_info, 0)); - } - - private void addConfigSettings(ArrayList sl) - { - sl.add(new SubmenuSetting(mContext, R.string.general_submenu, MenuTag.CONFIG_GENERAL)); - sl.add(new SubmenuSetting(mContext, R.string.interface_submenu, MenuTag.CONFIG_INTERFACE)); - sl.add(new SubmenuSetting(mContext, R.string.audio_submenu, MenuTag.CONFIG_AUDIO)); - sl.add(new SubmenuSetting(mContext, R.string.paths_submenu, MenuTag.CONFIG_PATHS)); - sl.add(new SubmenuSetting(mContext, R.string.gamecube_submenu, MenuTag.CONFIG_GAME_CUBE)); - sl.add(new SubmenuSetting(mContext, R.string.wii_submenu, MenuTag.CONFIG_WII)); - sl.add(new SubmenuSetting(mContext, R.string.advanced_submenu, MenuTag.CONFIG_ADVANCED)); - sl.add(new SubmenuSetting(mContext, R.string.log_submenu, MenuTag.CONFIG_LOG)); - sl.add(new SubmenuSetting(mContext, R.string.debug_submenu, MenuTag.DEBUG)); - sl.add(new RunRunnable(mContext, R.string.user_data_submenu, 0, 0, 0, false, - () -> UserDataActivity.launch(mContext))); - } - - private void addGeneralSettings(ArrayList sl) - { - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_CPU_THREAD, R.string.dual_core, - R.string.dual_core_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_ENABLE_CHEATS, R.string.enable_cheats, - 0)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_OVERRIDE_REGION_SETTINGS, - R.string.override_region_settings, 0)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_AUTO_DISC_CHANGE, - R.string.auto_disc_change, 0)); - sl.add(new PercentSliderSetting(mContext, FloatSetting.MAIN_EMULATION_SPEED, - R.string.speed_limit, 0, 0, 200, "%", 1)); - sl.add(new SingleChoiceSetting(mContext, IntSetting.MAIN_FALLBACK_REGION, - R.string.fallback_region, 0, R.array.regionEntries, R.array.regionValues)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_ANALYTICS_ENABLED, R.string.analytics, - 0)); - sl.add(new RunRunnable(mContext, R.string.analytics_new_id, 0, - R.string.analytics_new_id_confirmation, 0, true, - NativeLibrary::GenerateNewStatisticsId)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_ENABLE_SAVESTATES, - R.string.enable_save_states, R.string.enable_save_states_description)); - } - - private void addInterfaceSettings(ArrayList sl) - { - // Hide the orientation setting if the device only supports one orientation. Old devices which - // support both portrait and landscape may report support for neither, so we use ==, not &&. - PackageManager packageManager = mContext.getPackageManager(); - if (packageManager.hasSystemFeature(PackageManager.FEATURE_SCREEN_PORTRAIT) == - packageManager.hasSystemFeature(PackageManager.FEATURE_SCREEN_LANDSCAPE)) - { - sl.add(new SingleChoiceSetting(mContext, IntSetting.MAIN_EMULATION_ORIENTATION, - R.string.emulation_screen_orientation, 0, R.array.orientationEntries, - R.array.orientationValues)); - } - - // Only android 9+ supports this feature. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) - { - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_EXPAND_TO_CUTOUT_AREA, - R.string.expand_to_cutout_area, R.string.expand_to_cutout_area_description)); - } - - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_USE_PANIC_HANDLERS, - R.string.panic_handlers, R.string.panic_handlers_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_OSD_MESSAGES, R.string.osd_messages, - R.string.osd_messages_description)); - - AbstractIntSetting appTheme = new AbstractIntSetting() - { - @Override - public boolean isOverridden() - { - return IntSetting.MAIN_INTERFACE_THEME.isOverridden(); - } - - @Override - public boolean isRuntimeEditable() - { - // This only affects app UI - return true; - } - - @Override - public boolean delete(@NonNull Settings settings) - { - ThemeHelper.deleteThemeKey((AppCompatActivity) mView.getActivity()); - return IntSetting.MAIN_INTERFACE_THEME.delete(settings); - } - - @Override - public int getInt() - { - return IntSetting.MAIN_INTERFACE_THEME.getInt(); - } - - @Override - public void setInt(@NonNull Settings settings, int newValue) - { - IntSetting.MAIN_INTERFACE_THEME.setInt(settings, newValue); - ThemeHelper.saveTheme((AppCompatActivity) mView.getActivity(), newValue); - } - }; - - // If a Monet theme is run on a device below API 31, the app will crash - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) - { - sl.add(new SingleChoiceSetting(mContext, appTheme, R.string.change_theme, 0, - R.array.themeEntriesA12, R.array.themeValuesA12)); - } - else - { - sl.add(new SingleChoiceSetting(mContext, appTheme, R.string.change_theme, 0, - R.array.themeEntries, R.array.themeValues)); - } - - AbstractIntSetting themeMode = new AbstractIntSetting() - { - @Override - public boolean isOverridden() - { - return IntSetting.MAIN_INTERFACE_THEME_MODE.isOverridden(); - } - - @Override - public boolean isRuntimeEditable() - { - // This only affects app UI - return true; - } - - @Override - public boolean delete(@NonNull Settings settings) - { - ThemeHelper.deleteThemeModeKey((AppCompatActivity) mView.getActivity()); - return IntSetting.MAIN_INTERFACE_THEME_MODE.delete(settings); - } - - @Override - public int getInt() - { - return IntSetting.MAIN_INTERFACE_THEME_MODE.getInt(); - } - - @Override - public void setInt(@NonNull Settings settings, int newValue) - { - IntSetting.MAIN_INTERFACE_THEME_MODE.setInt(settings, newValue); - ThemeHelper.saveThemeMode((AppCompatActivity) mView.getActivity(), newValue); - } - }; - - sl.add(new SingleChoiceSetting(mContext, themeMode, R.string.change_theme_mode, 0, - R.array.themeModeEntries, R.array.themeModeValues)); - - AbstractBooleanSetting blackBackgrounds = new AbstractBooleanSetting() - { - @Override - public boolean isOverridden() - { - return BooleanSetting.MAIN_USE_BLACK_BACKGROUNDS.isOverridden(); - } - - @Override - public boolean isRuntimeEditable() - { - return true; - } - - @Override - public boolean delete(@NonNull Settings settings) - { - ThemeHelper.deleteBackgroundSetting((AppCompatActivity) mView.getActivity()); - return BooleanSetting.MAIN_USE_BLACK_BACKGROUNDS.delete(settings); - } - - @Override - public boolean getBoolean() - { - return BooleanSetting.MAIN_USE_BLACK_BACKGROUNDS.getBoolean(); - } - - @Override - public void setBoolean(@NonNull Settings settings, boolean newValue) - { - BooleanSetting.MAIN_USE_BLACK_BACKGROUNDS.setBoolean(settings, newValue); - ThemeHelper.saveBackgroundSetting((AppCompatActivity) mView.getActivity(), newValue); - } - }; - - sl.add(new SwitchSetting(mContext, blackBackgrounds, R.string.use_black_backgrounds, - R.string.use_black_backgrounds_description)); - } - - private void addAudioSettings(ArrayList sl) - { - final int DSP_HLE = 0; - final int DSP_LLE_RECOMPILER = 1; - final int DSP_LLE_INTERPRETER = 2; - - AbstractIntSetting dspEmulationEngine = new AbstractIntSetting() - { - @Override - public int getInt() - { - if (BooleanSetting.MAIN_DSP_HLE.getBoolean()) - { - return DSP_HLE; - } - else - { - boolean jit = BooleanSetting.MAIN_DSP_JIT.getBoolean(); - return jit ? DSP_LLE_RECOMPILER : DSP_LLE_INTERPRETER; - } - } - - @Override - public void setInt(@NonNull Settings settings, int newValue) - { - switch (newValue) - { - case DSP_HLE: - BooleanSetting.MAIN_DSP_HLE.setBoolean(settings, true); - BooleanSetting.MAIN_DSP_JIT.setBoolean(settings, true); - break; - - case DSP_LLE_RECOMPILER: - BooleanSetting.MAIN_DSP_HLE.setBoolean(settings, false); - BooleanSetting.MAIN_DSP_JIT.setBoolean(settings, true); - break; - - case DSP_LLE_INTERPRETER: - BooleanSetting.MAIN_DSP_HLE.setBoolean(settings, false); - BooleanSetting.MAIN_DSP_JIT.setBoolean(settings, false); - break; - } - } - - @Override - public boolean isOverridden() - { - return BooleanSetting.MAIN_DSP_HLE.isOverridden() || - BooleanSetting.MAIN_DSP_JIT.isOverridden(); - } - - @Override - public boolean isRuntimeEditable() - { - return BooleanSetting.MAIN_DSP_HLE.isRuntimeEditable() && - BooleanSetting.MAIN_DSP_JIT.isRuntimeEditable(); - } - - @Override - public boolean delete(@NonNull Settings settings) - { - // Not short circuiting - return BooleanSetting.MAIN_DSP_HLE.delete(settings) & - BooleanSetting.MAIN_DSP_JIT.delete(settings); - } - }; - - // TODO: Exclude values from arrays instead of having multiple arrays. - int defaultCpuCore = NativeLibrary.DefaultCPUCore(); - int dspEngineEntries; - int dspEngineValues; - if (defaultCpuCore == 1) // x86-64 - { - dspEngineEntries = R.array.dspEngineEntriesX86_64; - dspEngineValues = R.array.dspEngineValuesX86_64; - } - else // Generic - { - dspEngineEntries = R.array.dspEngineEntriesGeneric; - dspEngineValues = R.array.dspEngineValuesGeneric; - } - sl.add(new SingleChoiceSetting(mContext, dspEmulationEngine, R.string.dsp_emulation_engine, 0, - dspEngineEntries, dspEngineValues)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_AUDIO_STRETCH, R.string.audio_stretch, - R.string.audio_stretch_description)); - sl.add(new IntSliderSetting(mContext, IntSetting.MAIN_AUDIO_VOLUME, R.string.audio_volume, 0, - 0, 100, "%", 1)); - } - - private void addPathsSettings(ArrayList sl) - { - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_RECURSIVE_ISO_PATHS, - R.string.search_subfolders, 0)); - sl.add(new FilePicker(mContext, StringSetting.MAIN_DEFAULT_ISO, R.string.default_ISO, 0, - MainPresenter.REQUEST_GAME_FILE, null)); - sl.add(new FilePicker(mContext, StringSetting.MAIN_FS_PATH, R.string.wii_NAND_root, 0, - MainPresenter.REQUEST_DIRECTORY, "/Wii")); - sl.add(new FilePicker(mContext, StringSetting.MAIN_DUMP_PATH, R.string.dump_path, 0, - MainPresenter.REQUEST_DIRECTORY, "/Dump")); - sl.add(new FilePicker(mContext, StringSetting.MAIN_LOAD_PATH, R.string.load_path, 0, - MainPresenter.REQUEST_DIRECTORY, "/Load")); - sl.add(new FilePicker(mContext, StringSetting.MAIN_RESOURCEPACK_PATH, - R.string.resource_pack_path, 0, MainPresenter.REQUEST_DIRECTORY, "/ResourcePacks")); - sl.add(new FilePicker(mContext, StringSetting.MAIN_WFS_PATH, R.string.wfs_path, 0, - MainPresenter.REQUEST_DIRECTORY, "/WFS")); - } - - private void addGameCubeSettings(ArrayList sl) - { - sl.add(new HeaderSetting(mContext, R.string.ipl_settings, 0)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_SKIP_IPL, R.string.skip_main_menu, - R.string.skip_main_menu_description)); - sl.add(new SingleChoiceSetting(mContext, IntSetting.MAIN_GC_LANGUAGE, R.string.system_language, - 0, R.array.gameCubeSystemLanguageEntries, R.array.gameCubeSystemLanguageValues)); - - sl.add(new HeaderSetting(mContext, R.string.device_settings, 0)); - sl.add(new SingleChoiceSetting(mContext, IntSetting.MAIN_SLOT_A, R.string.slot_a_device, 0, - R.array.slotDeviceEntries, R.array.slotDeviceValues)); - sl.add(new SingleChoiceSetting(mContext, IntSetting.MAIN_SLOT_B, R.string.slot_b_device, 0, - R.array.slotDeviceEntries, R.array.slotDeviceValues)); - sl.add(new SingleChoiceSetting(mContext, IntSetting.MAIN_SERIAL_PORT_1, - R.string.serial_port_1_device, 0, - R.array.serialPort1DeviceEntries, R.array.serialPort1DeviceValues, - MenuTag.CONFIG_SERIALPORT1)); - } - - private void addWiiSettings(ArrayList sl) - { - sl.add(new HeaderSetting(mContext, R.string.wii_misc_settings, 0)); - sl.add(new SingleChoiceSetting(mContext, IntSetting.SYSCONF_LANGUAGE, R.string.system_language, - 0, R.array.wiiSystemLanguageEntries, R.array.wiiSystemLanguageValues)); - sl.add(new SwitchSetting(mContext, BooleanSetting.SYSCONF_WIDESCREEN, R.string.wii_widescreen, - R.string.wii_widescreen_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.SYSCONF_PAL60, R.string.wii_pal60, - R.string.wii_pal60_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.SYSCONF_SCREENSAVER, - R.string.wii_screensaver, R.string.wii_screensaver_description)); - sl.add(new SingleChoiceSetting(mContext, IntSetting.SYSCONF_SOUND_MODE, R.string.sound_mode, 0, - R.array.soundModeEntries, R.array.soundModeValues)); - - sl.add(new HeaderSetting(mContext, R.string.wii_sd_card_settings, 0)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_WII_SD_CARD, R.string.insert_sd_card, - R.string.insert_sd_card_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_ALLOW_SD_WRITES, - R.string.wii_sd_card_allow_writes, 0)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_WII_SD_CARD_ENABLE_FOLDER_SYNC, - R.string.wii_sd_card_sync, R.string.wii_sd_card_sync_description)); - // TODO: Hardcoding "Load" here is wrong, because the user may have changed the Load path. - // The code structure makes this hard to fix, and with scoped storage active the Load path - // can't be changed anyway - sl.add(new FilePicker(mContext, StringSetting.MAIN_WII_SD_CARD_IMAGE_PATH, - R.string.wii_sd_card_path, 0, MainPresenter.REQUEST_SD_FILE, "/Load/WiiSD.raw")); - sl.add(new FilePicker(mContext, StringSetting.MAIN_WII_SD_CARD_SYNC_FOLDER_PATH, - R.string.wii_sd_sync_folder, 0, MainPresenter.REQUEST_DIRECTORY, "/Load/WiiSDSync/")); - sl.add(new RunRunnable(mContext, R.string.wii_sd_card_folder_to_file, 0, - R.string.wii_sd_card_folder_to_file_confirmation, 0, false, - () -> convertOnThread(WiiUtils::syncSdFolderToSdImage))); - sl.add(new RunRunnable(mContext, R.string.wii_sd_card_file_to_folder, 0, - R.string.wii_sd_card_file_to_folder_confirmation, 0, false, - () -> convertOnThread(WiiUtils::syncSdImageToSdFolder))); - - sl.add(new HeaderSetting(mContext, R.string.wii_wiimote_settings, 0)); - sl.add(new SwitchSetting(mContext, BooleanSetting.SYSCONF_WIIMOTE_MOTOR, - R.string.wiimote_rumble, 0)); - sl.add(new IntSliderSetting(mContext, IntSetting.SYSCONF_SPEAKER_VOLUME, - R.string.wiimote_volume, 0, 0, 127, "", 1)); - sl.add(new IntSliderSetting(mContext, IntSetting.SYSCONF_SENSOR_BAR_SENSITIVITY, - R.string.sensor_bar_sensitivity, 0, 1, 5, "", 1)); - sl.add(new SingleChoiceSetting(mContext, IntSetting.SYSCONF_SENSOR_BAR_POSITION, - R.string.sensor_bar_position, 0, R.array.sensorBarPositionEntries, - R.array.sensorBarPositionValues)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_WIIMOTE_CONTINUOUS_SCANNING, - R.string.wiimote_scanning, R.string.wiimote_scanning_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_WIIMOTE_ENABLE_SPEAKER, - R.string.wiimote_speaker, R.string.wiimote_speaker_description)); - - sl.add(new HeaderSetting(mContext, R.string.emulated_usb_devices, 0)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_EMULATE_SKYLANDER_PORTAL, - R.string.emulate_skylander_portal, 0)); - } - - private void addAdvancedSettings(ArrayList sl) - { - final int SYNC_GPU_NEVER = 0; - final int SYNC_GPU_ON_IDLE_SKIP = 1; - final int SYNC_GPU_ALWAYS = 2; - - AbstractIntSetting synchronizeGpuThread = new AbstractIntSetting() - { - @Override - public int getInt() - { - if (BooleanSetting.MAIN_SYNC_GPU.getBoolean()) - { - return SYNC_GPU_ALWAYS; - } - else - { - boolean syncOnSkipIdle = BooleanSetting.MAIN_SYNC_ON_SKIP_IDLE.getBoolean(); - return syncOnSkipIdle ? SYNC_GPU_ON_IDLE_SKIP : SYNC_GPU_NEVER; - } - } - - @Override - public void setInt(@NonNull Settings settings, int newValue) - { - switch (newValue) - { - case SYNC_GPU_NEVER: - BooleanSetting.MAIN_SYNC_ON_SKIP_IDLE.setBoolean(settings, false); - BooleanSetting.MAIN_SYNC_GPU.setBoolean(settings, false); - break; - - case SYNC_GPU_ON_IDLE_SKIP: - BooleanSetting.MAIN_SYNC_ON_SKIP_IDLE.setBoolean(settings, true); - BooleanSetting.MAIN_SYNC_GPU.setBoolean(settings, false); - break; - - case SYNC_GPU_ALWAYS: - BooleanSetting.MAIN_SYNC_ON_SKIP_IDLE.setBoolean(settings, true); - BooleanSetting.MAIN_SYNC_GPU.setBoolean(settings, true); - break; - } - } - - @Override - public boolean isOverridden() - { - return BooleanSetting.MAIN_SYNC_ON_SKIP_IDLE.isOverridden() || - BooleanSetting.MAIN_SYNC_GPU.isOverridden(); - } - - @Override - public boolean isRuntimeEditable() - { - return BooleanSetting.MAIN_SYNC_ON_SKIP_IDLE.isRuntimeEditable() && - BooleanSetting.MAIN_SYNC_GPU.isRuntimeEditable(); - } - - @Override - public boolean delete(@NonNull Settings settings) - { - // Not short circuiting - return BooleanSetting.MAIN_SYNC_ON_SKIP_IDLE.delete(settings) & - BooleanSetting.MAIN_SYNC_GPU.delete(settings); - } - }; - - // TODO: Having different emuCoresEntries/emuCoresValues for each architecture is annoying. - // The proper solution would be to have one set of entries and one set of values - // and exclude the values that aren't present in PowerPC::AvailableCPUCores(). - int defaultCpuCore = NativeLibrary.DefaultCPUCore(); - int emuCoresEntries; - int emuCoresValues; - if (defaultCpuCore == 1) // x86-64 - { - emuCoresEntries = R.array.emuCoresEntriesX86_64; - emuCoresValues = R.array.emuCoresValuesX86_64; - } - else if (defaultCpuCore == 4) // AArch64 - { - emuCoresEntries = R.array.emuCoresEntriesARM64; - emuCoresValues = R.array.emuCoresValuesARM64; - } - else - { - emuCoresEntries = R.array.emuCoresEntriesGeneric; - emuCoresValues = R.array.emuCoresValuesGeneric; - } - sl.add(new HeaderSetting(mContext, R.string.cpu_options, 0)); - sl.add(new SingleChoiceSetting(mContext, IntSetting.MAIN_CPU_CORE, R.string.cpu_core, 0, - emuCoresEntries, emuCoresValues)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_MMU, R.string.mmu_enable, - R.string.mmu_enable_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_PAUSE_ON_PANIC, R.string.pause_on_panic, - R.string.pause_on_panic_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_ACCURATE_CPU_CACHE, - R.string.enable_cpu_cache, R.string.enable_cpu_cache_description)); - - sl.add(new HeaderSetting(mContext, R.string.clock_override, 0)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_OVERCLOCK_ENABLE, - R.string.overclock_enable, R.string.overclock_enable_description)); - sl.add(new PercentSliderSetting(mContext, FloatSetting.MAIN_OVERCLOCK, R.string.overclock_title, - R.string.overclock_title_description, 0, 400, "%", 1)); - - ScaledIntSetting mem1Size = new ScaledIntSetting(1024 * 1024, IntSetting.MAIN_MEM1_SIZE); - ScaledIntSetting mem2Size = new ScaledIntSetting(1024 * 1024, IntSetting.MAIN_MEM2_SIZE); - - sl.add(new HeaderSetting(mContext, R.string.memory_override, 0)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_RAM_OVERRIDE_ENABLE, - R.string.enable_memory_size_override, - R.string.enable_memory_size_override_description)); - sl.add(new IntSliderSetting(mContext, mem1Size, R.string.main_mem1_size, 0, 24, 64, "MB", 1)); - sl.add(new IntSliderSetting(mContext, mem2Size, R.string.main_mem2_size, 0, 64, 128, "MB", 1)); - - sl.add(new HeaderSetting(mContext, R.string.gpu_options, 0)); - sl.add(new SingleChoiceSetting(mContext, synchronizeGpuThread, R.string.synchronize_gpu_thread, - R.string.synchronize_gpu_thread_description, R.array.synchronizeGpuThreadEntries, - R.array.synchronizeGpuThreadValues)); - - sl.add(new HeaderSetting(mContext, R.string.custom_rtc_options, 0)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_CUSTOM_RTC_ENABLE, - R.string.custom_rtc_enable, R.string.custom_rtc_description)); - sl.add(new DateTimeChoiceSetting(mContext, StringSetting.MAIN_CUSTOM_RTC_VALUE, - R.string.set_custom_rtc, 0)); - - sl.add(new HeaderSetting(mContext, R.string.misc_settings, 0)); - sl.add(new InvertedSwitchSetting(mContext, BooleanSetting.MAIN_FAST_DISC_SPEED, - R.string.emulate_disc_speed, - R.string.emulate_disc_speed_description)); - } - - private void addSerialPortSubSettings(ArrayList sl, int serialPort1Type) - { - if (serialPort1Type == 10) // Broadband Adapter (XLink Kai) - { - sl.add(new HyperLinkHeaderSetting(mContext, R.string.xlink_kai_guide_header, 0)); - sl.add(new InputStringSetting(mContext, StringSetting.MAIN_BBA_XLINK_IP, - R.string.xlink_kai_bba_ip, R.string.xlink_kai_bba_ip_description)); - } - else if (serialPort1Type == 12) // Broadband Adapter (Built In) - { - sl.add(new InputStringSetting(mContext, StringSetting.MAIN_BBA_BUILTIN_DNS, - R.string.bba_builtin_dns, R.string.bba_builtin_dns_description)); - } - } - - private void addGcPadSettings(ArrayList sl) - { - sl.add(new SingleChoiceSetting(mContext, IntSetting.MAIN_SI_DEVICE_0, R.string.controller_0, 0, - R.array.gcpadTypeEntries, R.array.gcpadTypeValues, MenuTag.getGCPadMenuTag(0))); - sl.add(new SingleChoiceSetting(mContext, IntSetting.MAIN_SI_DEVICE_1, R.string.controller_1, 0, - R.array.gcpadTypeEntries, R.array.gcpadTypeValues, MenuTag.getGCPadMenuTag(1))); - sl.add(new SingleChoiceSetting(mContext, IntSetting.MAIN_SI_DEVICE_2, R.string.controller_2, 0, - R.array.gcpadTypeEntries, R.array.gcpadTypeValues, MenuTag.getGCPadMenuTag(2))); - sl.add(new SingleChoiceSetting(mContext, IntSetting.MAIN_SI_DEVICE_3, R.string.controller_3, 0, - R.array.gcpadTypeEntries, R.array.gcpadTypeValues, MenuTag.getGCPadMenuTag(3))); - } - - private void addWiimoteSettings(ArrayList sl) - { - sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_1_SOURCE, R.string.wiimote_0, 0, - R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(0))); - sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_2_SOURCE, R.string.wiimote_1, 0, - R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(1))); - sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_3_SOURCE, R.string.wiimote_2, 0, - R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(2))); - sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_4_SOURCE, R.string.wiimote_3, 0, - R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(3))); - } - - private void addGraphicsSettings(ArrayList sl) - { - sl.add(new HeaderSetting(mContext, R.string.graphics_general, 0)); - sl.add(new StringSingleChoiceSetting(mContext, StringSetting.MAIN_GFX_BACKEND, - R.string.video_backend, 0, R.array.videoBackendEntries, R.array.videoBackendValues)); - sl.add(new SingleChoiceSettingDynamicDescriptions(mContext, - IntSetting.GFX_SHADER_COMPILATION_MODE, R.string.shader_compilation_mode, 0, - R.array.shaderCompilationModeEntries, R.array.shaderCompilationModeValues, - R.array.shaderCompilationDescriptionEntries, - R.array.shaderCompilationDescriptionValues)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_WAIT_FOR_SHADERS_BEFORE_STARTING, - R.string.wait_for_shaders, R.string.wait_for_shaders_description)); - sl.add(new SingleChoiceSetting(mContext, IntSetting.GFX_ASPECT_RATIO, R.string.aspect_ratio, 0, - R.array.aspectRatioEntries, R.array.aspectRatioValues)); - - sl.add(new HeaderSetting(mContext, R.string.graphics_more_settings, 0)); - sl.add(new SubmenuSetting(mContext, R.string.enhancements_submenu, MenuTag.ENHANCEMENTS)); - sl.add(new SubmenuSetting(mContext, R.string.hacks_submenu, MenuTag.HACKS)); - sl.add(new SubmenuSetting(mContext, R.string.statistics_submenu, MenuTag.STATISTICS)); - sl.add(new SubmenuSetting(mContext, R.string.advanced_graphics_submenu, - MenuTag.ADVANCED_GRAPHICS)); - } - - private void addEnhanceSettings(ArrayList sl) - { - sl.add(new SingleChoiceSetting(mContext, IntSetting.GFX_EFB_SCALE, R.string.internal_resolution, - R.string.internal_resolution_description, R.array.internalResolutionEntries, - R.array.internalResolutionValues)); - sl.add(new SingleChoiceSetting(mContext, IntSetting.GFX_MSAA, R.string.FSAA, - R.string.FSAA_description, R.array.FSAAEntries, R.array.FSAAValues)); - sl.add(new SingleChoiceSetting(mContext, IntSetting.GFX_ENHANCE_MAX_ANISOTROPY, - R.string.anisotropic_filtering, R.string.anisotropic_filtering_description, - R.array.anisotropicFilteringEntries, R.array.anisotropicFilteringValues)); - sl.add(new SingleChoiceSetting(mContext, IntSetting.GFX_ENHANCE_FORCE_TEXTURE_FILTERING, - R.string.texture_filtering, R.string.texture_filtering_description, - R.array.textureFilteringEntries, R.array.textureFilteringValues)); - - int stereoModeValue = IntSetting.GFX_STEREO_MODE.getInt(); - final int anaglyphMode = 3; - String[] shaderList = stereoModeValue == anaglyphMode ? - PostProcessing.getAnaglyphShaderList() : PostProcessing.getShaderList(); - - String[] shaderListEntries = new String[shaderList.length + 1]; - shaderListEntries[0] = mContext.getString(R.string.off); - System.arraycopy(shaderList, 0, shaderListEntries, 1, shaderList.length); - - String[] shaderListValues = new String[shaderList.length + 1]; - shaderListValues[0] = ""; - System.arraycopy(shaderList, 0, shaderListValues, 1, shaderList.length); - - sl.add(new StringSingleChoiceSetting(mContext, StringSetting.GFX_ENHANCE_POST_SHADER, - R.string.post_processing_shader, 0, shaderListEntries, shaderListValues)); - - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_HACK_COPY_EFB_SCALED, - R.string.scaled_efb_copy, R.string.scaled_efb_copy_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_ENABLE_PIXEL_LIGHTING, - R.string.per_pixel_lighting, R.string.per_pixel_lighting_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_ENHANCE_FORCE_TRUE_COLOR, - R.string.force_24bit_color, R.string.force_24bit_color_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_DISABLE_FOG, R.string.disable_fog, - R.string.disable_fog_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_ENHANCE_DISABLE_COPY_FILTER, - R.string.disable_copy_filter, R.string.disable_copy_filter_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_ENHANCE_ARBITRARY_MIPMAP_DETECTION, - R.string.arbitrary_mipmap_detection, R.string.arbitrary_mipmap_detection_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_WIDESCREEN_HACK, - R.string.wide_screen_hack, R.string.wide_screen_hack_description)); - - // Check if we support stereo - // If we support desktop GL then we must support at least OpenGL 3.2 - // If we only support OpenGLES then we need both OpenGLES 3.1 and AEP - EGLHelper helper = new EGLHelper(EGLHelper.EGL_OPENGL_ES2_BIT); - - if ((helper.supportsOpenGL() && helper.GetVersion() >= 320) || - (helper.supportsGLES3() && helper.GetVersion() >= 310 && - helper.SupportsExtension("GL_ANDROID_extension_pack_es31a"))) - { - sl.add(new SubmenuSetting(mContext, R.string.stereoscopy_submenu, MenuTag.STEREOSCOPY)); - } - } - - private void addHackSettings(ArrayList sl) - { - sl.add(new HeaderSetting(mContext, R.string.embedded_frame_buffer, 0)); - sl.add(new InvertedSwitchSetting(mContext, BooleanSetting.GFX_HACK_EFB_ACCESS_ENABLE, - R.string.skip_efb_access, R.string.skip_efb_access_description)); - sl.add(new InvertedSwitchSetting(mContext, BooleanSetting.GFX_HACK_EFB_EMULATE_FORMAT_CHANGES, - R.string.ignore_format_changes, R.string.ignore_format_changes_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_HACK_SKIP_EFB_COPY_TO_RAM, - R.string.efb_copy_method, R.string.efb_copy_method_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_HACK_DEFER_EFB_COPIES, - R.string.defer_efb_copies, R.string.defer_efb_copies_description)); - - sl.add(new HeaderSetting(mContext, R.string.texture_cache, 0)); - sl.add(new SingleChoiceSetting(mContext, IntSetting.GFX_SAFE_TEXTURE_CACHE_COLOR_SAMPLES, - R.string.texture_cache_accuracy, R.string.texture_cache_accuracy_description, - R.array.textureCacheAccuracyEntries, R.array.textureCacheAccuracyValues)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_ENABLE_GPU_TEXTURE_DECODING, - R.string.gpu_texture_decoding, R.string.gpu_texture_decoding_description)); - - sl.add(new HeaderSetting(mContext, R.string.external_frame_buffer, 0)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_HACK_SKIP_XFB_COPY_TO_RAM, - R.string.xfb_copy_method, R.string.xfb_copy_method_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_HACK_IMMEDIATE_XFB, - R.string.immediate_xfb, R.string.immediate_xfb_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_HACK_SKIP_DUPLICATE_XFBS, - R.string.skip_duplicate_xfbs, R.string.skip_duplicate_xfbs_description)); - - sl.add(new HeaderSetting(mContext, R.string.other, 0)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_FAST_DEPTH_CALC, - R.string.fast_depth_calculation, R.string.fast_depth_calculation_description)); - sl.add(new InvertedSwitchSetting(mContext, BooleanSetting.GFX_HACK_BBOX_ENABLE, - R.string.disable_bbox, R.string.disable_bbox_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_HACK_VERTEX_ROUNDING, - R.string.vertex_rounding, R.string.vertex_rounding_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_HACK_VI_SKIP, R.string.vi_skip, - R.string.vi_skip_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_SAVE_TEXTURE_CACHE_TO_STATE, - R.string.texture_cache_to_state, R.string.texture_cache_to_state_description)); - } - - private void addStatisticsSettings(ArrayList sl) - { - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_SHOW_FPS, R.string.show_fps, - R.string.show_fps_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_SHOW_FTIMES, R.string.show_ftimes, - R.string.show_ftimes_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_SHOW_VPS, R.string.show_vps, - R.string.show_vps_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_SHOW_VTIMES, R.string.show_vtimes, - R.string.show_vtimes_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_SHOW_GRAPHS, R.string.show_graphs, - R.string.show_graphs_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_SHOW_SPEED, R.string.show_speed, - R.string.show_speed_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_SHOW_SPEED_COLORS, - R.string.show_speed_colors, - R.string.show_speed_colors_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_LOG_RENDER_TIME_TO_FILE, - R.string.log_render_time_to_file, - R.string.log_render_time_to_file_description)); - sl.add(new IntSliderSetting(mContext, IntSetting.GFX_PERF_SAMP_WINDOW, - R.string.performance_sample_window, R.string.performance_sample_window_description, 0, - 10000, "ms", 100)); - } - - private void addAdvancedGraphicsSettings(ArrayList sl) - { - sl.add(new HeaderSetting(mContext, R.string.gfx_mods_and_custom_textures, 0)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_MODS_ENABLE, - R.string.gfx_mods, R.string.gfx_mods_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_HIRES_TEXTURES, - R.string.load_custom_texture, R.string.load_custom_texture_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_CACHE_HIRES_TEXTURES, - R.string.cache_custom_texture, R.string.cache_custom_texture_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_DUMP_TEXTURES, - R.string.dump_texture, R.string.dump_texture_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_DUMP_BASE_TEXTURES, - R.string.dump_base_texture, R.string.dump_base_texture_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_DUMP_MIP_TEXTURES, - R.string.dump_mip_texture, R.string.dump_mip_texture_description)); - - sl.add(new HeaderSetting(mContext, R.string.misc, 0)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_CROP, R.string.crop, - R.string.crop_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.SYSCONF_PROGRESSIVE_SCAN, - R.string.progressive_scan, 0)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_BACKEND_MULTITHREADING, - R.string.backend_multithreading, R.string.backend_multithreading_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_PREFER_VS_FOR_LINE_POINT_EXPANSION, - R.string.prefer_vs_for_point_line_expansion, - R.string.prefer_vs_for_point_line_expansion_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_CPU_CULL, R.string.cpu_cull, - R.string.cpu_cull_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_HACK_EFB_DEFER_INVALIDATION, - R.string.defer_efb_invalidation, R.string.defer_efb_invalidation_description)); - sl.add(new InvertedSwitchSetting(mContext, BooleanSetting.GFX_HACK_FAST_TEXTURE_SAMPLING, - R.string.manual_texture_sampling, R.string.manual_texture_sampling_description)); - - sl.add(new HeaderSetting(mContext, R.string.frame_dumping, 0)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_INTERNAL_RESOLUTION_FRAME_DUMPS, - R.string.internal_resolution_dumps, R.string.internal_resolution_dumps_description)); - sl.add(new IntSliderSetting(mContext, IntSetting.GFX_PNG_COMPRESSION_LEVEL, - R.string.png_compression_level, 0, 0, 9, "", 1)); - - sl.add(new HeaderSetting(mContext, R.string.debugging, 0)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_ENABLE_WIREFRAME, - R.string.wireframe, R.string.leave_this_unchecked)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_OVERLAY_STATS, - R.string.show_stats, R.string.leave_this_unchecked)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_TEXFMT_OVERLAY_ENABLE, - R.string.texture_format, R.string.leave_this_unchecked)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_ENABLE_VALIDATION_LAYER, - R.string.validation_layer, R.string.leave_this_unchecked)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_DUMP_EFB_TARGET, - R.string.dump_efb, R.string.leave_this_unchecked)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_DUMP_XFB_TARGET, - R.string.dump_xfb, R.string.leave_this_unchecked)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_HACK_DISABLE_COPY_TO_VRAM, - R.string.disable_vram_copies, R.string.leave_this_unchecked)); - } - - private void addLogConfigurationSettings(ArrayList sl) - { - sl.add(new SwitchSetting(mContext, BooleanSetting.LOGGER_WRITE_TO_FILE, R.string.log_to_file, - R.string.log_to_file_description)); - sl.add(new SingleChoiceSetting(mContext, IntSetting.LOGGER_VERBOSITY, R.string.log_verbosity, 0, - getLogVerbosityEntries(), getLogVerbosityValues())); - sl.add(new RunRunnable(mContext, R.string.log_enable_all, 0, - R.string.log_enable_all_confirmation, 0, true, () -> setAllLogTypes(true))); - sl.add(new RunRunnable(mContext, R.string.log_disable_all, 0, - R.string.log_disable_all_confirmation, 0, true, () -> setAllLogTypes(false))); - sl.add(new RunRunnable(mContext, R.string.log_clear, 0, R.string.log_clear_confirmation, 0, - true, SettingsAdapter::clearLog)); - - sl.add(new HeaderSetting(mContext, R.string.log_types, 0)); - for (Map.Entry entry : LOG_TYPE_NAMES.entrySet()) - { - sl.add(new LogSwitchSetting(entry.getKey(), entry.getValue(), "")); - } - } - - private void addDebugSettings(ArrayList sl) - { - sl.add(new HeaderSetting(mContext, R.string.debug_warning, 0)); - sl.add(new InvertedSwitchSetting(mContext, BooleanSetting.MAIN_FASTMEM, - R.string.debug_fastmem, 0)); - - sl.add(new HeaderSetting(mContext, R.string.debug_jit_header, 0)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_DEBUG_JIT_OFF, R.string.debug_jitoff, - 0)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_DEBUG_JIT_LOAD_STORE_OFF, - R.string.debug_jitloadstoreoff, 0)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_DEBUG_JIT_LOAD_STORE_FLOATING_OFF, - R.string.debug_jitloadstorefloatingoff, 0)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_DEBUG_JIT_LOAD_STORE_PAIRED_OFF, - R.string.debug_jitloadstorepairedoff, 0)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_DEBUG_JIT_FLOATING_POINT_OFF, - R.string.debug_jitfloatingpointoff, 0)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_DEBUG_JIT_INTEGER_OFF, - R.string.debug_jitintegeroff, 0)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_DEBUG_JIT_PAIRED_OFF, - R.string.debug_jitpairedoff, 0)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_DEBUG_JIT_SYSTEM_REGISTERS_OFF, - R.string.debug_jitsystemregistersoff, 0)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_DEBUG_JIT_BRANCH_OFF, - R.string.debug_jitbranchoff, 0)); - sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_DEBUG_JIT_REGISTER_CACHE_OFF, - R.string.debug_jitregistercacheoff, 0)); - } - - private void addStereoSettings(ArrayList sl) - { - sl.add(new SingleChoiceSetting(mContext, IntSetting.GFX_STEREO_MODE, R.string.stereoscopy_mode, - 0, R.array.stereoscopyEntries, R.array.stereoscopyValues)); - sl.add(new IntSliderSetting(mContext, IntSetting.GFX_STEREO_DEPTH, R.string.stereoscopy_depth, - R.string.stereoscopy_depth_description, 0, 100, "%", 1)); - sl.add(new IntSliderSetting(mContext, IntSetting.GFX_STEREO_CONVERGENCE_PERCENTAGE, - R.string.stereoscopy_convergence, R.string.stereoscopy_convergence_description, 0, 200, - "%", 1)); - sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_STEREO_SWAP_EYES, - R.string.stereoscopy_swap_eyes, R.string.stereoscopy_swap_eyes_description)); - } - - private void addGcPadSubSettings(ArrayList sl, int gcPadNumber, int gcPadType) - { - if (gcPadType == 6) // Emulated - { - EmulatedController gcPad = EmulatedController.getGcPad(gcPadNumber); - - if (!TextUtils.isEmpty(mGameID)) - { - addControllerPerGameSettings(sl, "Pad", gcPadNumber); - } - else - { - addControllerMetaSettings(sl, gcPad); - addControllerMappingSettings(sl, gcPad, null); - } - } - else if (gcPadType == 12) // Adapter - { - sl.add(new SwitchSetting(mContext, BooleanSetting.getSettingForAdapterRumble(gcPadNumber), - R.string.gc_adapter_rumble, R.string.gc_adapter_rumble_description)); - sl.add(new SwitchSetting(mContext, BooleanSetting.getSettingForSimulateKonga(gcPadNumber), - R.string.gc_adapter_bongos, R.string.gc_adapter_bongos_description)); - } - } - - private void addWiimoteSubSettings(ArrayList sl, int wiimoteNumber) - { - EmulatedController wiimote = EmulatedController.getWiimote(wiimoteNumber); - - if (!TextUtils.isEmpty(mGameID)) - { - addControllerPerGameSettings(sl, "Wiimote", wiimoteNumber); - } - else - { - addControllerMetaSettings(sl, wiimote); - - sl.add(new HeaderSetting(mContext, R.string.wiimote, 0)); - sl.add(new SubmenuSetting(mContext, R.string.wiimote_general, - MenuTag.getWiimoteGeneralMenuTag(wiimoteNumber))); - sl.add(new SubmenuSetting(mContext, R.string.wiimote_motion_simulation, - MenuTag.getWiimoteMotionSimulationMenuTag(wiimoteNumber))); - sl.add(new SubmenuSetting(mContext, R.string.wiimote_motion_input, - MenuTag.getWiimoteMotionInputMenuTag(wiimoteNumber))); - - // TYPE_OTHER is included here instead of in addWiimoteGeneralSubSettings so that touchscreen - // users won't have to dig into a submenu to find the Sideways Wii Remote setting - addControllerMappingSettings(sl, wiimote, new ArraySet<>( - Arrays.asList(ControlGroup.TYPE_ATTACHMENTS, ControlGroup.TYPE_OTHER))); - } - } - - private void addExtensionTypeSettings(ArrayList sl, int wiimoteNumber, - int extensionType) - { - addControllerMappingSettings(sl, - EmulatedController.getWiimoteAttachment(wiimoteNumber, extensionType), null); - } - - private void addWiimoteGeneralSubSettings(ArrayList sl, int wiimoteNumber) - { - addControllerMappingSettings(sl, EmulatedController.getWiimote(wiimoteNumber), - Collections.singleton(ControlGroup.TYPE_BUTTONS)); - } - - private void addWiimoteMotionSimulationSubSettings(ArrayList sl, int wiimoteNumber) - { - addControllerMappingSettings(sl, EmulatedController.getWiimote(wiimoteNumber), - new ArraySet<>(Arrays.asList(ControlGroup.TYPE_FORCE, ControlGroup.TYPE_TILT, - ControlGroup.TYPE_CURSOR, ControlGroup.TYPE_SHAKE))); - } - - private void addWiimoteMotionInputSubSettings(ArrayList sl, int wiimoteNumber) - { - addControllerMappingSettings(sl, EmulatedController.getWiimote(wiimoteNumber), - new ArraySet<>(Arrays.asList(ControlGroup.TYPE_IMU_ACCELEROMETER, - ControlGroup.TYPE_IMU_GYROSCOPE, ControlGroup.TYPE_IMU_CURSOR))); - } - - /** - * Adds controller settings that can be set on a per-game basis. - * - * @param sl The list to place controller settings into. - * @param profileString The prefix used for the profile setting in game INI files. - * @param controllerNumber The index of the controller, 0-3. - */ - private void addControllerPerGameSettings(ArrayList sl, String profileString, - int controllerNumber) - { - String[] profiles = new ProfileDialogPresenter(mMenuTag).getProfileNames(false); - String profileKey = profileString + "Profile" + (controllerNumber + 1); - sl.add(new StringSingleChoiceSetting(mContext, - new AdHocStringSetting(Settings.FILE_GAME_SETTINGS_ONLY, "Controls", profileKey, ""), - R.string.input_profile, 0, profiles, profiles, R.string.input_profiles_empty)); - } - - /** - * Adds settings and actions that apply to a controller as a whole. - * For instance, the device setting and the Clear action. - * - * @param sl The list to place controller settings into. - * @param controller The controller to add settings for. - */ - private void addControllerMetaSettings(ArrayList sl, EmulatedController controller) - { - sl.add(new InputDeviceSetting(mContext, R.string.input_device, 0, controller)); - - sl.add(new SwitchSetting(mContext, new AbstractBooleanSetting() - { - @Override - public boolean isOverridden() - { - return false; - } - - @Override - public boolean isRuntimeEditable() - { - return true; - } - - @Override - public boolean delete(@NonNull Settings settings) - { - mView.setMappingAllDevices(false); - return true; - } - - @Override - public boolean getBoolean() - { - return mView.isMappingAllDevices(); - } - - @Override - public void setBoolean(@NonNull Settings settings, boolean newValue) - { - mView.setMappingAllDevices(newValue); - } - }, R.string.input_device_all_devices, R.string.input_device_all_devices_description)); - - sl.add(new RunRunnable(mContext, R.string.input_reset_to_default, - R.string.input_reset_to_default_description, R.string.input_reset_warning, 0, true, - () -> loadDefaultControllerSettings(controller))); - - sl.add(new RunRunnable(mContext, R.string.input_clear, R.string.input_clear_description, - R.string.input_reset_warning, 0, true, () -> clearControllerSettings(controller))); - - sl.add(new RunRunnable(mContext, R.string.input_profiles, 0, 0, 0, true, - () -> mView.showDialogFragment(ProfileDialog.create(mMenuTag)))); - - updateOldControllerSettingsWarningVisibility(controller); - } - - /** - * Adds mapping settings and other control-specific settings. - * - * @param sl The list to place controller settings into. - * @param controller The controller to add settings for. - * @param groupTypeFilter If this is non-null, only groups whose types match this are considered. - */ - private void addControllerMappingSettings(ArrayList sl, - EmulatedController controller, Set groupTypeFilter) - { - updateOldControllerSettingsWarningVisibility(controller); - - int groupCount = controller.getGroupCount(); - for (int i = 0; i < groupCount; i++) - { - ControlGroup group = controller.getGroup(i); - int groupType = group.getGroupType(); - if (groupTypeFilter != null && !groupTypeFilter.contains(groupType)) - continue; - - sl.add(new HeaderSetting(group.getUiName(), "")); - - if (group.getDefaultEnabledValue() != ControlGroup.DEFAULT_ENABLED_ALWAYS) - { - sl.add(new SwitchSetting(mContext, new ControlGroupEnabledSetting(group), R.string.enabled, - 0)); - } - - int controlCount = group.getControlCount(); - for (int j = 0; j < controlCount; j++) - { - sl.add(new InputMappingControlSetting(group.getControl(j), controller)); - } - - if (groupType == ControlGroup.TYPE_ATTACHMENTS) - { - NumericSetting attachmentSetting = group.getAttachmentSetting(); - sl.add(new SingleChoiceSetting(mContext, new InputMappingIntSetting(attachmentSetting), - R.string.wiimote_extensions, 0, R.array.wiimoteExtensionsEntries, - R.array.wiimoteExtensionsValues, - MenuTag.getWiimoteExtensionMenuTag(mControllerNumber))); - } - - int numericSettingCount = group.getNumericSettingCount(); - for (int j = 0; j < numericSettingCount; j++) - { - addNumericSetting(sl, group.getNumericSetting(j)); - } - } - } - - private void addNumericSetting(ArrayList sl, NumericSetting setting) - { - switch (setting.getType()) - { - case NumericSetting.TYPE_DOUBLE: - sl.add(new FloatSliderSetting(new InputMappingDoubleSetting(setting), setting.getUiName(), - "", (int) Math.ceil(setting.getDoubleMin()), - (int) Math.floor(setting.getDoubleMax()), setting.getUiSuffix())); - break; - case NumericSetting.TYPE_BOOLEAN: - sl.add(new SwitchSetting(new InputMappingBooleanSetting(setting), setting.getUiName(), - setting.getUiDescription())); - break; - } - } - - public void updateOldControllerSettingsWarningVisibility() - { - updateOldControllerSettingsWarningVisibility(mMenuTag.getCorrespondingEmulatedController()); - } - - private void updateOldControllerSettingsWarningVisibility(EmulatedController controller) - { - String defaultDevice = controller.getDefaultDevice(); - - mHasOldControllerSettings = defaultDevice.startsWith("Android/") && - defaultDevice.endsWith("/Touchscreen"); - - mView.setOldControllerSettingsWarningVisibility(mHasOldControllerSettings); - } - - private void loadDefaultControllerSettings(EmulatedController controller) - { - controller.loadDefaultSettings(); - mView.onControllerSettingsChanged(); - } - - private void clearControllerSettings(EmulatedController controller) - { - controller.clearSettings(); - mView.onControllerSettingsChanged(); - } - - private static int getLogVerbosityEntries() - { - // Value obtained from LogLevel in Common/Logging/Log.h - if (NativeLibrary.GetMaxLogLevel() == 5) - { - return R.array.logVerbosityEntriesMaxLevelDebug; - } - else - { - return R.array.logVerbosityEntriesMaxLevelInfo; - } - } - - private static int getLogVerbosityValues() - { - // Value obtained from LogLevel in Common/Logging/Log.h - if (NativeLibrary.GetMaxLogLevel() == 5) - { - return R.array.logVerbosityValuesMaxLevelDebug; - } - else - { - return R.array.logVerbosityValuesMaxLevelInfo; - } - } - - public void setAllLogTypes(boolean value) - { - Settings settings = mView.getSettings(); - - for (Map.Entry entry : LOG_TYPE_NAMES.entrySet()) - { - new AdHocBooleanSetting(Settings.FILE_LOGGER, Settings.SECTION_LOGGER_LOGS, entry.getKey(), - false).setBoolean(settings, value); - } - - mView.getAdapter().notifyAllSettingsChanged(); - } - - private void convertOnThread(BooleanSupplier f) - { - ThreadUtil.runOnThreadAndShowResult(mView.getActivity(), R.string.wii_converting, 0, () -> - mContext.getResources().getString( - f.get() ? R.string.wii_convert_success : R.string.wii_convert_failure)); - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt new file mode 100644 index 0000000000..72649a39fd --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt @@ -0,0 +1,2329 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.ui + +import android.content.Context +import android.content.pm.PackageManager +import android.os.Build +import android.os.Bundle +import android.text.TextUtils +import androidx.appcompat.app.AppCompatActivity +import androidx.collection.ArraySet +import org.dolphinemu.dolphinemu.NativeLibrary +import org.dolphinemu.dolphinemu.R +import org.dolphinemu.dolphinemu.activities.UserDataActivity +import org.dolphinemu.dolphinemu.features.input.model.ControlGroupEnabledSetting +import org.dolphinemu.dolphinemu.features.input.model.InputMappingBooleanSetting +import org.dolphinemu.dolphinemu.features.input.model.InputMappingDoubleSetting +import org.dolphinemu.dolphinemu.features.input.model.InputMappingIntSetting +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.ControlGroup +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.NumericSetting +import org.dolphinemu.dolphinemu.features.input.model.view.InputDeviceSetting +import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting +import org.dolphinemu.dolphinemu.features.input.ui.ProfileDialog +import org.dolphinemu.dolphinemu.features.input.ui.ProfileDialogPresenter +import org.dolphinemu.dolphinemu.features.settings.model.* +import org.dolphinemu.dolphinemu.features.settings.model.view.* +import org.dolphinemu.dolphinemu.ui.main.MainPresenter +import org.dolphinemu.dolphinemu.utils.* +import java.util.* +import kotlin.math.ceil +import kotlin.math.floor + +class SettingsFragmentPresenter( + private val fragmentView: SettingsFragmentView, + private val context: Context +) { + private var menuTag: MenuTag? = null + private var gameId: String? = null + + private var settingsList: ArrayList? = null + private var hasOldControllerSettings = false + + private var serialPort1Type = 0 + private var controllerNumber = 0 + private var controllerType = 0 + + fun onCreate(menuTag: MenuTag, gameId: String?, extras: Bundle) { + this.gameId = gameId + this.menuTag = menuTag + + if (menuTag.isGCPadMenu || menuTag.isWiimoteExtensionMenu) { + controllerNumber = menuTag.subType + controllerType = extras.getInt(ARG_CONTROLLER_TYPE) + } else if (menuTag.isWiimoteMenu) { + controllerNumber = menuTag.subType + } else if (menuTag.isSerialPort1Menu) { + serialPort1Type = extras.getInt(ARG_SERIALPORT1_TYPE) + } + } + + fun onViewCreated(menuTag: MenuTag?, settings: Settings?) { + this.menuTag = menuTag + + if (!TextUtils.isEmpty(gameId)) { + fragmentView.fragmentActivity.title = context.getString(R.string.game_settings, gameId) + } + + this.settings = settings + } + + var settings: Settings? = null + set(settings) { + field = settings + if (settingsList == null && settings != null) { + loadSettingsList() + } else { + fragmentView.showSettingsList(settingsList!!) + fragmentView.setOldControllerSettingsWarningVisibility(hasOldControllerSettings) + } + } + + fun loadDefaultSettings() { + loadSettingsList() + } + + private fun loadSettingsList() { + val sl = ArrayList() + when (menuTag) { + MenuTag.SETTINGS -> addTopLevelSettings(sl) + MenuTag.CONFIG -> addConfigSettings(sl) + MenuTag.CONFIG_GENERAL -> addGeneralSettings(sl) + MenuTag.CONFIG_INTERFACE -> addInterfaceSettings(sl) + MenuTag.CONFIG_AUDIO -> addAudioSettings(sl) + MenuTag.CONFIG_PATHS -> addPathsSettings(sl) + MenuTag.CONFIG_GAME_CUBE -> addGameCubeSettings(sl) + MenuTag.CONFIG_WII -> addWiiSettings(sl) + MenuTag.CONFIG_ADVANCED -> addAdvancedSettings(sl) + MenuTag.GRAPHICS -> addGraphicsSettings(sl) + MenuTag.CONFIG_SERIALPORT1 -> addSerialPortSubSettings(sl, serialPort1Type) + MenuTag.GCPAD_TYPE -> addGcPadSettings(sl) + MenuTag.WIIMOTE -> addWiimoteSettings(sl) + MenuTag.ENHANCEMENTS -> addEnhanceSettings(sl) + MenuTag.STEREOSCOPY -> addStereoSettings(sl) + MenuTag.HACKS -> addHackSettings(sl) + MenuTag.STATISTICS -> addStatisticsSettings(sl) + MenuTag.ADVANCED_GRAPHICS -> addAdvancedGraphicsSettings(sl) + MenuTag.CONFIG_LOG -> addLogConfigurationSettings(sl) + MenuTag.DEBUG -> addDebugSettings(sl) + MenuTag.GCPAD_1, + MenuTag.GCPAD_2, + MenuTag.GCPAD_3, + MenuTag.GCPAD_4 -> addGcPadSubSettings( + sl, + controllerNumber, + controllerType + ) + MenuTag.WIIMOTE_1, + MenuTag.WIIMOTE_2, + MenuTag.WIIMOTE_3, + MenuTag.WIIMOTE_4 -> addWiimoteSubSettings( + sl, + controllerNumber + ) + MenuTag.WIIMOTE_EXTENSION_1, + MenuTag.WIIMOTE_EXTENSION_2, + MenuTag.WIIMOTE_EXTENSION_3, + MenuTag.WIIMOTE_EXTENSION_4 -> addExtensionTypeSettings( + sl, + controllerNumber, + controllerType + ) + MenuTag.WIIMOTE_GENERAL_1, + MenuTag.WIIMOTE_GENERAL_2, + MenuTag.WIIMOTE_GENERAL_3, + MenuTag.WIIMOTE_GENERAL_4 -> addWiimoteGeneralSubSettings( + sl, + controllerNumber + ) + MenuTag.WIIMOTE_MOTION_SIMULATION_1, + MenuTag.WIIMOTE_MOTION_SIMULATION_2, + MenuTag.WIIMOTE_MOTION_SIMULATION_3, + MenuTag.WIIMOTE_MOTION_SIMULATION_4 -> addWiimoteMotionSimulationSubSettings( + sl, + controllerNumber + ) + MenuTag.WIIMOTE_MOTION_INPUT_1, + MenuTag.WIIMOTE_MOTION_INPUT_2, + MenuTag.WIIMOTE_MOTION_INPUT_3, + MenuTag.WIIMOTE_MOTION_INPUT_4 -> addWiimoteMotionInputSubSettings( + sl, + controllerNumber + ) + else -> throw UnsupportedOperationException("Unimplemented menu") + } + + settingsList = sl + fragmentView.showSettingsList(settingsList!!) + } + + private fun addTopLevelSettings(sl: ArrayList) { + sl.add(SubmenuSetting(context, R.string.config, MenuTag.CONFIG)) + sl.add(SubmenuSetting(context, R.string.graphics_settings, MenuTag.GRAPHICS)) + + sl.add(SubmenuSetting(context, R.string.gcpad_settings, MenuTag.GCPAD_TYPE)) + if (settings!!.isWii) { + sl.add(SubmenuSetting(context, R.string.wiimote_settings, MenuTag.WIIMOTE)) + } + + sl.add(HeaderSetting(context, R.string.setting_clear_info, 0)) + } + + private fun addConfigSettings(sl: ArrayList) { + sl.add(SubmenuSetting(context, R.string.general_submenu, MenuTag.CONFIG_GENERAL)) + sl.add(SubmenuSetting(context, R.string.interface_submenu, MenuTag.CONFIG_INTERFACE)) + sl.add(SubmenuSetting(context, R.string.audio_submenu, MenuTag.CONFIG_AUDIO)) + sl.add(SubmenuSetting(context, R.string.paths_submenu, MenuTag.CONFIG_PATHS)) + sl.add(SubmenuSetting(context, R.string.gamecube_submenu, MenuTag.CONFIG_GAME_CUBE)) + sl.add(SubmenuSetting(context, R.string.wii_submenu, MenuTag.CONFIG_WII)) + sl.add(SubmenuSetting(context, R.string.advanced_submenu, MenuTag.CONFIG_ADVANCED)) + sl.add(SubmenuSetting(context, R.string.log_submenu, MenuTag.CONFIG_LOG)) + sl.add(SubmenuSetting(context, R.string.debug_submenu, MenuTag.DEBUG)) + sl.add( + RunRunnable(context, R.string.user_data_submenu, 0, 0, 0, false) + { UserDataActivity.launch(context) } + ) + } + + private fun addGeneralSettings(sl: ArrayList) { + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_CPU_THREAD, + R.string.dual_core, + R.string.dual_core_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_ENABLE_CHEATS, + R.string.enable_cheats, + 0 + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_OVERRIDE_REGION_SETTINGS, + R.string.override_region_settings, + 0 + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_AUTO_DISC_CHANGE, + R.string.auto_disc_change, + 0 + ) + ) + sl.add( + PercentSliderSetting( + context, + FloatSetting.MAIN_EMULATION_SPEED, + R.string.speed_limit, + 0, + 0, + 200, + "%", + 1 + ) + ) + sl.add( + SingleChoiceSetting( + context, + IntSetting.MAIN_FALLBACK_REGION, + R.string.fallback_region, + 0, + R.array.regionEntries, + R.array.regionValues + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_ANALYTICS_ENABLED, + R.string.analytics, + 0 + ) + ) + sl.add( + RunRunnable( + context, + R.string.analytics_new_id, + 0, + R.string.analytics_new_id_confirmation, + 0, + true + ) { NativeLibrary.GenerateNewStatisticsId() } + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_ENABLE_SAVESTATES, + R.string.enable_save_states, + R.string.enable_save_states_description + ) + ) + } + + private fun addInterfaceSettings(sl: ArrayList) { + // Hide the orientation setting if the device only supports one orientation. Old devices which + // support both portrait and landscape may report support for neither, so we use ==, not &&. + val packageManager = context.packageManager + if (packageManager.hasSystemFeature(PackageManager.FEATURE_SCREEN_PORTRAIT) == + packageManager.hasSystemFeature(PackageManager.FEATURE_SCREEN_LANDSCAPE) + ) { + sl.add( + SingleChoiceSetting( + context, + IntSetting.MAIN_EMULATION_ORIENTATION, + R.string.emulation_screen_orientation, + 0, + R.array.orientationEntries, + R.array.orientationValues + ) + ) + } + + // Only android 9+ supports this feature. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_EXPAND_TO_CUTOUT_AREA, + R.string.expand_to_cutout_area, + R.string.expand_to_cutout_area_description + ) + ) + } + + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_USE_PANIC_HANDLERS, + R.string.panic_handlers, + R.string.panic_handlers_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_OSD_MESSAGES, + R.string.osd_messages, + R.string.osd_messages_description + ) + ) + + val appTheme: AbstractIntSetting = object : AbstractIntSetting { + override val isOverridden: Boolean + get() = IntSetting.MAIN_INTERFACE_THEME.isOverridden + + // This only affects app UI + override val isRuntimeEditable: Boolean = true + + override fun delete(settings: Settings): Boolean { + ThemeHelper.deleteThemeKey((fragmentView.fragmentActivity as AppCompatActivity)) + return IntSetting.MAIN_INTERFACE_THEME.delete(settings) + } + + override val int: Int + get() = IntSetting.MAIN_INTERFACE_THEME.int + + override fun setInt(settings: Settings, newValue: Int) { + IntSetting.MAIN_INTERFACE_THEME.setInt(settings, newValue) + ThemeHelper.saveTheme( + (fragmentView.fragmentActivity as AppCompatActivity), + newValue + ) + } + } + + // If a Monet theme is run on a device below API 31, the app will crash + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + sl.add( + SingleChoiceSetting( + context, + appTheme, + R.string.change_theme, + 0, + R.array.themeEntriesA12, + R.array.themeValuesA12 + ) + ) + } else { + sl.add( + SingleChoiceSetting( + context, + appTheme, + R.string.change_theme, + 0, + R.array.themeEntries, + R.array.themeValues + ) + ) + } + + val themeMode: AbstractIntSetting = object : AbstractIntSetting { + override val isOverridden: Boolean + get() = IntSetting.MAIN_INTERFACE_THEME_MODE.isOverridden + + // This only affects app UI + override val isRuntimeEditable: Boolean = true + + override fun delete(settings: Settings): Boolean { + ThemeHelper.deleteThemeModeKey((fragmentView.fragmentActivity as AppCompatActivity)) + return IntSetting.MAIN_INTERFACE_THEME_MODE.delete(settings) + } + + override val int: Int + get() = IntSetting.MAIN_INTERFACE_THEME_MODE.int + + override fun setInt(settings: Settings, newValue: Int) { + IntSetting.MAIN_INTERFACE_THEME_MODE.setInt(settings, newValue) + ThemeHelper.saveThemeMode( + (fragmentView.fragmentActivity as AppCompatActivity), + newValue + ) + } + } + + sl.add( + SingleChoiceSetting( + context, + themeMode, + R.string.change_theme_mode, + 0, + R.array.themeModeEntries, + R.array.themeModeValues + ) + ) + + val blackBackgrounds: AbstractBooleanSetting = object : AbstractBooleanSetting { + override val isOverridden: Boolean + get() = BooleanSetting.MAIN_USE_BLACK_BACKGROUNDS.isOverridden + + override val isRuntimeEditable: Boolean = true + + override fun delete(settings: Settings): Boolean { + ThemeHelper.deleteBackgroundSetting((fragmentView.fragmentActivity as AppCompatActivity)) + return BooleanSetting.MAIN_USE_BLACK_BACKGROUNDS.delete(settings) + } + + override val boolean: Boolean + get() = BooleanSetting.MAIN_USE_BLACK_BACKGROUNDS.boolean + + override fun setBoolean(settings: Settings, newValue: Boolean) { + BooleanSetting.MAIN_USE_BLACK_BACKGROUNDS.setBoolean(settings, newValue) + ThemeHelper.saveBackgroundSetting( + (fragmentView.fragmentActivity as AppCompatActivity), + newValue + ) + } + } + + sl.add( + SwitchSetting( + context, + blackBackgrounds, + R.string.use_black_backgrounds, + R.string.use_black_backgrounds_description + ) + ) + } + + private fun addAudioSettings(sl: ArrayList) { + val DSP_HLE = 0 + val DSP_LLE_RECOMPILER = 1 + val DSP_LLE_INTERPRETER = 2 + + val dspEmulationEngine: AbstractIntSetting = object : AbstractIntSetting { + override val int: Int + get() = if (BooleanSetting.MAIN_DSP_HLE.boolean) { + DSP_HLE + } else { + val jit = BooleanSetting.MAIN_DSP_JIT.boolean + if (jit) DSP_LLE_RECOMPILER else DSP_LLE_INTERPRETER + } + + override fun setInt(settings: Settings, newValue: Int) { + when (newValue) { + DSP_HLE -> { + BooleanSetting.MAIN_DSP_HLE.setBoolean(settings, true) + BooleanSetting.MAIN_DSP_JIT.setBoolean(settings, true) + } + DSP_LLE_RECOMPILER -> { + BooleanSetting.MAIN_DSP_HLE.setBoolean(settings, false) + BooleanSetting.MAIN_DSP_JIT.setBoolean(settings, true) + } + DSP_LLE_INTERPRETER -> { + BooleanSetting.MAIN_DSP_HLE.setBoolean(settings, false) + BooleanSetting.MAIN_DSP_JIT.setBoolean(settings, false) + } + } + } + + override val isOverridden: Boolean + get() = BooleanSetting.MAIN_DSP_HLE.isOverridden || + BooleanSetting.MAIN_DSP_JIT.isOverridden + + override val isRuntimeEditable: Boolean + get() = BooleanSetting.MAIN_DSP_HLE.isRuntimeEditable && + BooleanSetting.MAIN_DSP_JIT.isRuntimeEditable + + override fun delete(settings: Settings): Boolean { + // Not short circuiting + return BooleanSetting.MAIN_DSP_HLE.delete(settings) and + BooleanSetting.MAIN_DSP_JIT.delete(settings) + } + } + + // TODO: Exclude values from arrays instead of having multiple arrays. + val defaultCpuCore = NativeLibrary.DefaultCPUCore() + val dspEngineEntries: Int + val dspEngineValues: Int + if (defaultCpuCore == 1) { + dspEngineEntries = R.array.dspEngineEntriesX86_64 + dspEngineValues = R.array.dspEngineValuesX86_64 + } else { + dspEngineEntries = R.array.dspEngineEntriesGeneric + dspEngineValues = R.array.dspEngineValuesGeneric + } + sl.add( + SingleChoiceSetting( + context, + dspEmulationEngine, + R.string.dsp_emulation_engine, + 0, + dspEngineEntries, + dspEngineValues + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_AUDIO_STRETCH, + R.string.audio_stretch, + R.string.audio_stretch_description + ) + ) + sl.add( + IntSliderSetting( + context, + IntSetting.MAIN_AUDIO_VOLUME, + R.string.audio_volume, + 0, + 0, + 100, + "%", + 1 + ) + ) + } + + private fun addPathsSettings(sl: ArrayList) { + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_RECURSIVE_ISO_PATHS, + R.string.search_subfolders, + 0 + ) + ) + sl.add( + FilePicker( + context, + StringSetting.MAIN_DEFAULT_ISO, + R.string.default_ISO, + 0, + MainPresenter.REQUEST_GAME_FILE, + null + ) + ) + sl.add( + FilePicker( + context, + StringSetting.MAIN_FS_PATH, + R.string.wii_NAND_root, + 0, + MainPresenter.REQUEST_DIRECTORY, + "/Wii" + ) + ) + sl.add( + FilePicker( + context, + StringSetting.MAIN_DUMP_PATH, + R.string.dump_path, + 0, + MainPresenter.REQUEST_DIRECTORY, + "/Dump" + ) + ) + sl.add( + FilePicker( + context, + StringSetting.MAIN_LOAD_PATH, + R.string.load_path, + 0, + MainPresenter.REQUEST_DIRECTORY, + "/Load" + ) + ) + sl.add( + FilePicker( + context, + StringSetting.MAIN_RESOURCEPACK_PATH, + R.string.resource_pack_path, + 0, + MainPresenter.REQUEST_DIRECTORY, + "/ResourcePacks" + ) + ) + sl.add( + FilePicker( + context, + StringSetting.MAIN_WFS_PATH, + R.string.wfs_path, + 0, + MainPresenter.REQUEST_DIRECTORY, + "/WFS" + ) + ) + } + + private fun addGameCubeSettings(sl: ArrayList) { + sl.add(HeaderSetting(context, R.string.ipl_settings, 0)) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_SKIP_IPL, + R.string.skip_main_menu, + R.string.skip_main_menu_description + ) + ) + sl.add( + SingleChoiceSetting( + context, + IntSetting.MAIN_GC_LANGUAGE, + R.string.system_language, + 0, + R.array.gameCubeSystemLanguageEntries, + R.array.gameCubeSystemLanguageValues + ) + ) + + sl.add(HeaderSetting(context, R.string.device_settings, 0)) + sl.add( + SingleChoiceSetting( + context, + IntSetting.MAIN_SLOT_A, + R.string.slot_a_device, + 0, + R.array.slotDeviceEntries, + R.array.slotDeviceValues + ) + ) + sl.add( + SingleChoiceSetting( + context, + IntSetting.MAIN_SLOT_B, + R.string.slot_b_device, + 0, + R.array.slotDeviceEntries, + R.array.slotDeviceValues + ) + ) + sl.add( + SingleChoiceSetting( + context, + IntSetting.MAIN_SERIAL_PORT_1, + R.string.serial_port_1_device, + 0, + R.array.serialPort1DeviceEntries, + R.array.serialPort1DeviceValues, + MenuTag.CONFIG_SERIALPORT1 + ) + ) + } + + private fun addWiiSettings(sl: ArrayList) { + sl.add(HeaderSetting(context, R.string.wii_misc_settings, 0)) + sl.add( + SingleChoiceSetting( + context, + IntSetting.SYSCONF_LANGUAGE, + R.string.system_language, + 0, + R.array.wiiSystemLanguageEntries, + R.array.wiiSystemLanguageValues + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.SYSCONF_WIDESCREEN, + R.string.wii_widescreen, + R.string.wii_widescreen_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.SYSCONF_PAL60, + R.string.wii_pal60, + R.string.wii_pal60_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.SYSCONF_SCREENSAVER, + R.string.wii_screensaver, + R.string.wii_screensaver_description + ) + ) + sl.add( + SingleChoiceSetting( + context, + IntSetting.SYSCONF_SOUND_MODE, + R.string.sound_mode, + 0, + R.array.soundModeEntries, + R.array.soundModeValues + ) + ) + + sl.add(HeaderSetting(context, R.string.wii_sd_card_settings, 0)) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_WII_SD_CARD, + R.string.insert_sd_card, + R.string.insert_sd_card_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_ALLOW_SD_WRITES, + R.string.wii_sd_card_allow_writes, + 0 + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_WII_SD_CARD_ENABLE_FOLDER_SYNC, + R.string.wii_sd_card_sync, + R.string.wii_sd_card_sync_description + ) + ) + // TODO: Hardcoding "Load" here is wrong, because the user may have changed the Load path. + // The code structure makes this hard to fix, and with scoped storage active the Load path + // can't be changed anyway + sl.add( + FilePicker( + context, + StringSetting.MAIN_WII_SD_CARD_IMAGE_PATH, + R.string.wii_sd_card_path, + 0, + MainPresenter.REQUEST_SD_FILE, + "/Load/WiiSD.raw" + ) + ) + sl.add( + FilePicker( + context, + StringSetting.MAIN_WII_SD_CARD_SYNC_FOLDER_PATH, + R.string.wii_sd_sync_folder, + 0, + MainPresenter.REQUEST_DIRECTORY, + "/Load/WiiSDSync/" + ) + ) + sl.add( + RunRunnable( + context, + R.string.wii_sd_card_folder_to_file, + 0, + R.string.wii_sd_card_folder_to_file_confirmation, + 0, + false + ) { convertOnThread { WiiUtils.syncSdFolderToSdImage() } } + ) + sl.add( + RunRunnable( + context, + R.string.wii_sd_card_file_to_folder, + 0, + R.string.wii_sd_card_file_to_folder_confirmation, + 0, + false + ) { convertOnThread { WiiUtils.syncSdImageToSdFolder() } } + ) + + sl.add(HeaderSetting(context, R.string.wii_wiimote_settings, 0)) + sl.add( + SwitchSetting( + context, + BooleanSetting.SYSCONF_WIIMOTE_MOTOR, + R.string.wiimote_rumble, + 0 + ) + ) + sl.add( + IntSliderSetting( + context, + IntSetting.SYSCONF_SPEAKER_VOLUME, + R.string.wiimote_volume, + 0, + 0, + 127, + "", + 1 + ) + ) + sl.add( + IntSliderSetting( + context, + IntSetting.SYSCONF_SENSOR_BAR_SENSITIVITY, + R.string.sensor_bar_sensitivity, + 0, + 1, + 5, + "", + 1 + ) + ) + sl.add( + SingleChoiceSetting( + context, + IntSetting.SYSCONF_SENSOR_BAR_POSITION, + R.string.sensor_bar_position, + 0, + R.array.sensorBarPositionEntries, + R.array.sensorBarPositionValues + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_WIIMOTE_CONTINUOUS_SCANNING, + R.string.wiimote_scanning, + R.string.wiimote_scanning_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_WIIMOTE_ENABLE_SPEAKER, + R.string.wiimote_speaker, + R.string.wiimote_speaker_description + ) + ) + + sl.add(HeaderSetting(context, R.string.emulated_usb_devices, 0)) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_EMULATE_SKYLANDER_PORTAL, + R.string.emulate_skylander_portal, + 0 + ) + ) + } + + private fun addAdvancedSettings(sl: ArrayList) { + val SYNC_GPU_NEVER = 0 + val SYNC_GPU_ON_IDLE_SKIP = 1 + val SYNC_GPU_ALWAYS = 2 + + val synchronizeGpuThread: AbstractIntSetting = object : AbstractIntSetting { + override val int: Int + get() = if (BooleanSetting.MAIN_SYNC_GPU.boolean) { + SYNC_GPU_ALWAYS + } else { + val syncOnSkipIdle = BooleanSetting.MAIN_SYNC_ON_SKIP_IDLE.boolean + if (syncOnSkipIdle) SYNC_GPU_ON_IDLE_SKIP else SYNC_GPU_NEVER + } + + override fun setInt(settings: Settings, newValue: Int) { + when (newValue) { + SYNC_GPU_NEVER -> { + BooleanSetting.MAIN_SYNC_ON_SKIP_IDLE.setBoolean(settings, false) + BooleanSetting.MAIN_SYNC_GPU.setBoolean(settings, false) + } + SYNC_GPU_ON_IDLE_SKIP -> { + BooleanSetting.MAIN_SYNC_ON_SKIP_IDLE.setBoolean(settings, true) + BooleanSetting.MAIN_SYNC_GPU.setBoolean(settings, false) + } + SYNC_GPU_ALWAYS -> { + BooleanSetting.MAIN_SYNC_ON_SKIP_IDLE.setBoolean(settings, true) + BooleanSetting.MAIN_SYNC_GPU.setBoolean(settings, true) + } + } + } + + override val isOverridden: Boolean + get() = BooleanSetting.MAIN_SYNC_ON_SKIP_IDLE.isOverridden || + BooleanSetting.MAIN_SYNC_GPU.isOverridden + + override val isRuntimeEditable: Boolean + get() = BooleanSetting.MAIN_SYNC_ON_SKIP_IDLE.isRuntimeEditable && + BooleanSetting.MAIN_SYNC_GPU.isRuntimeEditable + + override fun delete(settings: Settings): Boolean { + // Not short circuiting + return BooleanSetting.MAIN_SYNC_ON_SKIP_IDLE.delete(settings) and + BooleanSetting.MAIN_SYNC_GPU.delete(settings) + } + } + + // TODO: Having different emuCoresEntries/emuCoresValues for each architecture is annoying. + // The proper solution would be to have one set of entries and one set of values + // and exclude the values that aren't present in PowerPC::AvailableCPUCores(). + val defaultCpuCore = NativeLibrary.DefaultCPUCore() + val emuCoresEntries: Int + val emuCoresValues: Int + when (defaultCpuCore) { + 1 -> { + emuCoresEntries = R.array.emuCoresEntriesX86_64 + emuCoresValues = R.array.emuCoresValuesX86_64 + } + 4 -> { + emuCoresEntries = R.array.emuCoresEntriesARM64 + emuCoresValues = R.array.emuCoresValuesARM64 + } + else -> { + emuCoresEntries = R.array.emuCoresEntriesGeneric + emuCoresValues = R.array.emuCoresValuesGeneric + } + } + + sl.add(HeaderSetting(context, R.string.cpu_options, 0)) + sl.add( + SingleChoiceSetting( + context, + IntSetting.MAIN_CPU_CORE, + R.string.cpu_core, + 0, + emuCoresEntries, + emuCoresValues + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_MMU, + R.string.mmu_enable, + R.string.mmu_enable_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_PAUSE_ON_PANIC, + R.string.pause_on_panic, + R.string.pause_on_panic_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_ACCURATE_CPU_CACHE, + R.string.enable_cpu_cache, + R.string.enable_cpu_cache_description + ) + ) + + sl.add(HeaderSetting(context, R.string.clock_override, 0)) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_OVERCLOCK_ENABLE, + R.string.overclock_enable, + R.string.overclock_enable_description + ) + ) + sl.add( + PercentSliderSetting( + context, + FloatSetting.MAIN_OVERCLOCK, + R.string.overclock_title, + R.string.overclock_title_description, + 0, + 400, + "%", + 1 + ) + ) + + val mem1Size = ScaledIntSetting(1024 * 1024, IntSetting.MAIN_MEM1_SIZE) + val mem2Size = ScaledIntSetting(1024 * 1024, IntSetting.MAIN_MEM2_SIZE) + + sl.add(HeaderSetting(context, R.string.memory_override, 0)) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_RAM_OVERRIDE_ENABLE, + R.string.enable_memory_size_override, + R.string.enable_memory_size_override_description + ) + ) + sl.add( + IntSliderSetting( + context, + mem1Size, + R.string.main_mem1_size, + 0, + 24, + 64, + "MB", + 1 + ) + ) + sl.add( + IntSliderSetting( + context, + mem2Size, + R.string.main_mem2_size, + 0, + 64, + 128, + "MB", + 1 + ) + ) + + sl.add(HeaderSetting(context, R.string.gpu_options, 0)) + sl.add( + SingleChoiceSetting( + context, + synchronizeGpuThread, + R.string.synchronize_gpu_thread, + R.string.synchronize_gpu_thread_description, + R.array.synchronizeGpuThreadEntries, + R.array.synchronizeGpuThreadValues + ) + ) + + sl.add(HeaderSetting(context, R.string.custom_rtc_options, 0)) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_CUSTOM_RTC_ENABLE, + R.string.custom_rtc_enable, + R.string.custom_rtc_description + ) + ) + sl.add( + DateTimeChoiceSetting( + context, + StringSetting.MAIN_CUSTOM_RTC_VALUE, + R.string.set_custom_rtc, + 0 + ) + ) + + sl.add(HeaderSetting(context, R.string.misc_settings, 0)) + sl.add( + InvertedSwitchSetting( + context, + BooleanSetting.MAIN_FAST_DISC_SPEED, + R.string.emulate_disc_speed, + R.string.emulate_disc_speed_description + ) + ) + } + + private fun addSerialPortSubSettings(sl: ArrayList, serialPort1Type: Int) { + if (serialPort1Type == 10) { + // Broadband Adapter (XLink Kai) + sl.add(HyperLinkHeaderSetting(context, R.string.xlink_kai_guide_header, 0)) + sl.add( + InputStringSetting( + context, + StringSetting.MAIN_BBA_XLINK_IP, + R.string.xlink_kai_bba_ip, + R.string.xlink_kai_bba_ip_description + ) + ) + } else if (serialPort1Type == 12) { + // Broadband Adapter (Built In) + sl.add( + InputStringSetting( + context, + StringSetting.MAIN_BBA_BUILTIN_DNS, + R.string.bba_builtin_dns, + R.string.bba_builtin_dns_description + ) + ) + } + } + + private fun addGcPadSettings(sl: ArrayList) { + sl.add( + SingleChoiceSetting( + context, + IntSetting.MAIN_SI_DEVICE_0, + R.string.controller_0, + 0, + R.array.gcpadTypeEntries, + R.array.gcpadTypeValues, + MenuTag.getGCPadMenuTag(0) + ) + ) + sl.add( + SingleChoiceSetting( + context, + IntSetting.MAIN_SI_DEVICE_1, + R.string.controller_1, + 0, + R.array.gcpadTypeEntries, + R.array.gcpadTypeValues, + MenuTag.getGCPadMenuTag(1) + ) + ) + sl.add( + SingleChoiceSetting( + context, + IntSetting.MAIN_SI_DEVICE_2, + R.string.controller_2, + 0, + R.array.gcpadTypeEntries, + R.array.gcpadTypeValues, + MenuTag.getGCPadMenuTag(2) + ) + ) + sl.add( + SingleChoiceSetting( + context, + IntSetting.MAIN_SI_DEVICE_3, + R.string.controller_3, + 0, + R.array.gcpadTypeEntries, + R.array.gcpadTypeValues, + MenuTag.getGCPadMenuTag(3) + ) + ) + } + + private fun addWiimoteSettings(sl: ArrayList) { + sl.add( + SingleChoiceSetting( + context, + IntSetting.WIIMOTE_1_SOURCE, + R.string.wiimote_0, + 0, + R.array.wiimoteTypeEntries, + R.array.wiimoteTypeValues, + MenuTag.getWiimoteMenuTag(0) + ) + ) + sl.add( + SingleChoiceSetting( + context, + IntSetting.WIIMOTE_2_SOURCE, + R.string.wiimote_1, + 0, + R.array.wiimoteTypeEntries, + R.array.wiimoteTypeValues, + MenuTag.getWiimoteMenuTag(1) + ) + ) + sl.add( + SingleChoiceSetting( + context, + IntSetting.WIIMOTE_3_SOURCE, + R.string.wiimote_2, + 0, + R.array.wiimoteTypeEntries, + R.array.wiimoteTypeValues, + MenuTag.getWiimoteMenuTag(2) + ) + ) + sl.add( + SingleChoiceSetting( + context, + IntSetting.WIIMOTE_4_SOURCE, + R.string.wiimote_3, + 0, + R.array.wiimoteTypeEntries, + R.array.wiimoteTypeValues, + MenuTag.getWiimoteMenuTag(3) + ) + ) + } + + private fun addGraphicsSettings(sl: ArrayList) { + sl.add(HeaderSetting(context, R.string.graphics_general, 0)) + sl.add( + StringSingleChoiceSetting( + context, + StringSetting.MAIN_GFX_BACKEND, + R.string.video_backend, + 0, + R.array.videoBackendEntries, + R.array.videoBackendValues + ) + ) + sl.add( + SingleChoiceSettingDynamicDescriptions( + context, + IntSetting.GFX_SHADER_COMPILATION_MODE, + R.string.shader_compilation_mode, + 0, + R.array.shaderCompilationModeEntries, + R.array.shaderCompilationModeValues, + R.array.shaderCompilationDescriptionEntries, + R.array.shaderCompilationDescriptionValues + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_WAIT_FOR_SHADERS_BEFORE_STARTING, + R.string.wait_for_shaders, + R.string.wait_for_shaders_description + ) + ) + sl.add( + SingleChoiceSetting( + context, + IntSetting.GFX_ASPECT_RATIO, + R.string.aspect_ratio, + 0, + R.array.aspectRatioEntries, + R.array.aspectRatioValues + ) + ) + + sl.add(HeaderSetting(context, R.string.graphics_more_settings, 0)) + sl.add( + SubmenuSetting( + context, + R.string.enhancements_submenu, + MenuTag.ENHANCEMENTS + ) + ) + sl.add( + SubmenuSetting( + context, + R.string.hacks_submenu, + MenuTag.HACKS + ) + ) + sl.add( + SubmenuSetting( + context, + R.string.statistics_submenu, + MenuTag.STATISTICS + ) + ) + sl.add( + SubmenuSetting( + context, + R.string.advanced_graphics_submenu, + MenuTag.ADVANCED_GRAPHICS + ) + ) + } + + private fun addEnhanceSettings(sl: ArrayList) { + sl.add( + SingleChoiceSetting( + context, + IntSetting.GFX_EFB_SCALE, + R.string.internal_resolution, + R.string.internal_resolution_description, + R.array.internalResolutionEntries, + R.array.internalResolutionValues + ) + ) + sl.add( + SingleChoiceSetting( + context, + IntSetting.GFX_MSAA, + R.string.FSAA, + R.string.FSAA_description, + R.array.FSAAEntries, + R.array.FSAAValues + ) + ) + sl.add( + SingleChoiceSetting( + context, + IntSetting.GFX_ENHANCE_MAX_ANISOTROPY, + R.string.anisotropic_filtering, + R.string.anisotropic_filtering_description, + R.array.anisotropicFilteringEntries, + R.array.anisotropicFilteringValues + ) + ) + sl.add( + SingleChoiceSetting( + context, + IntSetting.GFX_ENHANCE_FORCE_TEXTURE_FILTERING, + R.string.texture_filtering, + R.string.texture_filtering_description, + R.array.textureFilteringEntries, + R.array.textureFilteringValues + ) + ) + + val stereoModeValue = IntSetting.GFX_STEREO_MODE.int + val anaglyphMode = 3 + val shaderList = + if (stereoModeValue == anaglyphMode) PostProcessing.anaglyphShaderList else PostProcessing.shaderList + + val shaderListEntries = arrayOfNulls(shaderList.size + 1) + shaderListEntries[0] = context.getString(R.string.off) + System.arraycopy(shaderList, 0, shaderListEntries, 1, shaderList.size) + + val shaderListValues = arrayOfNulls(shaderList.size + 1) + shaderListValues[0] = "" + System.arraycopy(shaderList, 0, shaderListValues, 1, shaderList.size) + + sl.add( + StringSingleChoiceSetting( + context, + StringSetting.GFX_ENHANCE_POST_SHADER, + R.string.post_processing_shader, + 0, + shaderListEntries, + shaderListValues + ) + ) + + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_HACK_COPY_EFB_SCALED, + R.string.scaled_efb_copy, + R.string.scaled_efb_copy_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_ENABLE_PIXEL_LIGHTING, + R.string.per_pixel_lighting, + R.string.per_pixel_lighting_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_ENHANCE_FORCE_TRUE_COLOR, + R.string.force_24bit_color, + R.string.force_24bit_color_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_DISABLE_FOG, + R.string.disable_fog, + R.string.disable_fog_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_ENHANCE_DISABLE_COPY_FILTER, + R.string.disable_copy_filter, + R.string.disable_copy_filter_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_ENHANCE_ARBITRARY_MIPMAP_DETECTION, + R.string.arbitrary_mipmap_detection, + R.string.arbitrary_mipmap_detection_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_WIDESCREEN_HACK, + R.string.wide_screen_hack, + R.string.wide_screen_hack_description + ) + ) + + // Check if we support stereo + // If we support desktop GL then we must support at least OpenGL 3.2 + // If we only support OpenGLES then we need both OpenGLES 3.1 and AEP + val helper = EGLHelper(EGLHelper.EGL_OPENGL_ES2_BIT) + + if (helper.supportsOpenGL() && helper.GetVersion() >= 320 || helper.supportsGLES3() && helper.GetVersion() >= 310 && helper.SupportsExtension( + "GL_ANDROID_extension_pack_es31a" + ) + ) { + sl.add( + SubmenuSetting( + context, + R.string.stereoscopy_submenu, + MenuTag.STEREOSCOPY + ) + ) + } + } + + private fun addHackSettings(sl: ArrayList) { + sl.add(HeaderSetting(context, R.string.embedded_frame_buffer, 0)) + sl.add( + InvertedSwitchSetting( + context, + BooleanSetting.GFX_HACK_EFB_ACCESS_ENABLE, + R.string.skip_efb_access, + R.string.skip_efb_access_description + ) + ) + sl.add( + InvertedSwitchSetting( + context, + BooleanSetting.GFX_HACK_EFB_EMULATE_FORMAT_CHANGES, + R.string.ignore_format_changes, + R.string.ignore_format_changes_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_HACK_SKIP_EFB_COPY_TO_RAM, + R.string.efb_copy_method, + R.string.efb_copy_method_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_HACK_DEFER_EFB_COPIES, + R.string.defer_efb_copies, + R.string.defer_efb_copies_description + ) + ) + + sl.add(HeaderSetting(context, R.string.texture_cache, 0)) + sl.add( + SingleChoiceSetting( + context, + IntSetting.GFX_SAFE_TEXTURE_CACHE_COLOR_SAMPLES, + R.string.texture_cache_accuracy, + R.string.texture_cache_accuracy_description, + R.array.textureCacheAccuracyEntries, + R.array.textureCacheAccuracyValues + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_ENABLE_GPU_TEXTURE_DECODING, + R.string.gpu_texture_decoding, + R.string.gpu_texture_decoding_description + ) + ) + + sl.add(HeaderSetting(context, R.string.external_frame_buffer, 0)) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_HACK_SKIP_XFB_COPY_TO_RAM, + R.string.xfb_copy_method, + R.string.xfb_copy_method_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_HACK_IMMEDIATE_XFB, + R.string.immediate_xfb, + R.string.immediate_xfb_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_HACK_SKIP_DUPLICATE_XFBS, + R.string.skip_duplicate_xfbs, + R.string.skip_duplicate_xfbs_description + ) + ) + + sl.add(HeaderSetting(context, R.string.other, 0)) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_FAST_DEPTH_CALC, + R.string.fast_depth_calculation, + R.string.fast_depth_calculation_description + ) + ) + sl.add( + InvertedSwitchSetting( + context, + BooleanSetting.GFX_HACK_BBOX_ENABLE, + R.string.disable_bbox, + R.string.disable_bbox_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_HACK_VERTEX_ROUNDING, + R.string.vertex_rounding, + R.string.vertex_rounding_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_HACK_VI_SKIP, + R.string.vi_skip, + R.string.vi_skip_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_SAVE_TEXTURE_CACHE_TO_STATE, + R.string.texture_cache_to_state, + R.string.texture_cache_to_state_description + ) + ) + } + + private fun addStatisticsSettings(sl: ArrayList) { + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_SHOW_FPS, + R.string.show_fps, + R.string.show_fps_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_SHOW_FTIMES, + R.string.show_ftimes, + R.string.show_ftimes_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_SHOW_VPS, + R.string.show_vps, + R.string.show_vps_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_SHOW_VTIMES, + R.string.show_vtimes, + R.string.show_vtimes_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_SHOW_GRAPHS, + R.string.show_graphs, + R.string.show_graphs_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_SHOW_SPEED, + R.string.show_speed, + R.string.show_speed_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_SHOW_SPEED_COLORS, + R.string.show_speed_colors, + R.string.show_speed_colors_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_LOG_RENDER_TIME_TO_FILE, + R.string.log_render_time_to_file, + R.string.log_render_time_to_file_description + ) + ) + sl.add( + IntSliderSetting( + context, + IntSetting.GFX_PERF_SAMP_WINDOW, + R.string.performance_sample_window, + R.string.performance_sample_window_description, + 0, + 10000, + "ms", + 100 + ) + ) + } + + private fun addAdvancedGraphicsSettings(sl: ArrayList) { + sl.add(HeaderSetting(context, R.string.gfx_mods_and_custom_textures, 0)) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_MODS_ENABLE, + R.string.gfx_mods, + R.string.gfx_mods_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_HIRES_TEXTURES, + R.string.load_custom_texture, + R.string.load_custom_texture_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_CACHE_HIRES_TEXTURES, + R.string.cache_custom_texture, + R.string.cache_custom_texture_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_DUMP_TEXTURES, + R.string.dump_texture, + R.string.dump_texture_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_DUMP_BASE_TEXTURES, + R.string.dump_base_texture, + R.string.dump_base_texture_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_DUMP_MIP_TEXTURES, + R.string.dump_mip_texture, + R.string.dump_mip_texture_description + ) + ) + + sl.add(HeaderSetting(context, R.string.misc, 0)) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_CROP, + R.string.crop, + R.string.crop_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.SYSCONF_PROGRESSIVE_SCAN, + R.string.progressive_scan, + 0 + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_BACKEND_MULTITHREADING, + R.string.backend_multithreading, + R.string.backend_multithreading_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_PREFER_VS_FOR_LINE_POINT_EXPANSION, + R.string.prefer_vs_for_point_line_expansion, + R.string.prefer_vs_for_point_line_expansion_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_CPU_CULL, + R.string.cpu_cull, + R.string.cpu_cull_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_HACK_EFB_DEFER_INVALIDATION, + R.string.defer_efb_invalidation, + R.string.defer_efb_invalidation_description + ) + ) + sl.add( + InvertedSwitchSetting( + context, + BooleanSetting.GFX_HACK_FAST_TEXTURE_SAMPLING, + R.string.manual_texture_sampling, + R.string.manual_texture_sampling_description + ) + ) + + sl.add(HeaderSetting(context, R.string.frame_dumping, 0)) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_INTERNAL_RESOLUTION_FRAME_DUMPS, + R.string.internal_resolution_dumps, + R.string.internal_resolution_dumps_description + ) + ) + sl.add( + IntSliderSetting( + context, + IntSetting.GFX_PNG_COMPRESSION_LEVEL, + R.string.png_compression_level, + 0, + 0, + 9, + "", + 1 + ) + ) + + sl.add(HeaderSetting(context, R.string.debugging, 0)) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_ENABLE_WIREFRAME, + R.string.wireframe, + R.string.leave_this_unchecked + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_OVERLAY_STATS, + R.string.show_stats, + R.string.leave_this_unchecked + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_TEXFMT_OVERLAY_ENABLE, + R.string.texture_format, + R.string.leave_this_unchecked + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_ENABLE_VALIDATION_LAYER, + R.string.validation_layer, + R.string.leave_this_unchecked + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_DUMP_EFB_TARGET, + R.string.dump_efb, + R.string.leave_this_unchecked + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_DUMP_XFB_TARGET, + R.string.dump_xfb, + R.string.leave_this_unchecked + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_HACK_DISABLE_COPY_TO_VRAM, + R.string.disable_vram_copies, + R.string.leave_this_unchecked + ) + ) + } + + private fun addLogConfigurationSettings(sl: ArrayList) { + sl.add( + SwitchSetting( + context, + BooleanSetting.LOGGER_WRITE_TO_FILE, + R.string.log_to_file, + R.string.log_to_file_description + ) + ) + sl.add( + SingleChoiceSetting( + context, + IntSetting.LOGGER_VERBOSITY, + R.string.log_verbosity, + 0, + logVerbosityEntries, logVerbosityValues + ) + ) + sl.add( + RunRunnable( + context, + R.string.log_enable_all, + 0, + R.string.log_enable_all_confirmation, + 0, + true + ) { setAllLogTypes(true) }) + sl.add( + RunRunnable( + context, + R.string.log_disable_all, + 0, + R.string.log_disable_all_confirmation, + 0, + true + ) { setAllLogTypes(false) }) + sl.add( + RunRunnable( + context, + R.string.log_clear, + 0, + R.string.log_clear_confirmation, + 0, + true + ) { SettingsAdapter.clearLog() }) + + sl.add(HeaderSetting(context, R.string.log_types, 0)) + for ((key, value) in LOG_TYPE_NAMES) { + sl.add(LogSwitchSetting(key, value, "")) + } + } + + private fun addDebugSettings(sl: ArrayList) { + sl.add(HeaderSetting(context, R.string.debug_warning, 0)) + sl.add( + InvertedSwitchSetting( + context, + BooleanSetting.MAIN_FASTMEM, + R.string.debug_fastmem, + 0 + ) + ) + + sl.add(HeaderSetting(context, R.string.debug_jit_header, 0)) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_DEBUG_JIT_OFF, + R.string.debug_jitoff, + 0 + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_DEBUG_JIT_LOAD_STORE_OFF, + R.string.debug_jitloadstoreoff, + 0 + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_DEBUG_JIT_LOAD_STORE_FLOATING_OFF, + R.string.debug_jitloadstorefloatingoff, + 0 + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_DEBUG_JIT_LOAD_STORE_PAIRED_OFF, + R.string.debug_jitloadstorepairedoff, + 0 + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_DEBUG_JIT_FLOATING_POINT_OFF, + R.string.debug_jitfloatingpointoff, + 0 + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_DEBUG_JIT_INTEGER_OFF, + R.string.debug_jitintegeroff, + 0 + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_DEBUG_JIT_PAIRED_OFF, + R.string.debug_jitpairedoff, + 0 + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_DEBUG_JIT_SYSTEM_REGISTERS_OFF, + R.string.debug_jitsystemregistersoff, + 0 + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_DEBUG_JIT_BRANCH_OFF, + R.string.debug_jitbranchoff, + 0 + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.MAIN_DEBUG_JIT_REGISTER_CACHE_OFF, + R.string.debug_jitregistercacheoff, + 0 + ) + ) + } + + private fun addStereoSettings(sl: ArrayList) { + sl.add( + SingleChoiceSetting( + context, + IntSetting.GFX_STEREO_MODE, + R.string.stereoscopy_mode, + 0, + R.array.stereoscopyEntries, + R.array.stereoscopyValues + ) + ) + sl.add( + IntSliderSetting( + context, + IntSetting.GFX_STEREO_DEPTH, + R.string.stereoscopy_depth, + R.string.stereoscopy_depth_description, + 0, + 100, + "%", + 1 + ) + ) + sl.add( + IntSliderSetting( + context, + IntSetting.GFX_STEREO_CONVERGENCE_PERCENTAGE, + R.string.stereoscopy_convergence, + R.string.stereoscopy_convergence_description, + 0, + 200, + "%", + 1 + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.GFX_STEREO_SWAP_EYES, + R.string.stereoscopy_swap_eyes, + R.string.stereoscopy_swap_eyes_description + ) + ) + } + + private fun addGcPadSubSettings(sl: ArrayList, gcPadNumber: Int, gcPadType: Int) { + if (gcPadType == 6) { + // Emulated + val gcPad = EmulatedController.getGcPad(gcPadNumber) + + if (!TextUtils.isEmpty(gameId)) { + addControllerPerGameSettings(sl, "Pad", gcPadNumber) + } else { + addControllerMetaSettings(sl, gcPad) + addControllerMappingSettings(sl, gcPad, null) + } + } else if (gcPadType == 12) { + // Adapter + sl.add( + SwitchSetting( + context, + BooleanSetting.getSettingForAdapterRumble(gcPadNumber), + R.string.gc_adapter_rumble, + R.string.gc_adapter_rumble_description + ) + ) + sl.add( + SwitchSetting( + context, + BooleanSetting.getSettingForSimulateKonga(gcPadNumber), + R.string.gc_adapter_bongos, + R.string.gc_adapter_bongos_description + ) + ) + } + } + + private fun addWiimoteSubSettings(sl: ArrayList, wiimoteNumber: Int) { + val wiimote = EmulatedController.getWiimote(wiimoteNumber) + + if (!TextUtils.isEmpty(gameId)) { + addControllerPerGameSettings(sl, "Wiimote", wiimoteNumber) + } else { + addControllerMetaSettings(sl, wiimote) + + sl.add(HeaderSetting(context, R.string.wiimote, 0)) + sl.add( + SubmenuSetting( + context, + R.string.wiimote_general, + MenuTag.getWiimoteGeneralMenuTag(wiimoteNumber) + ) + ) + sl.add( + SubmenuSetting( + context, + R.string.wiimote_motion_simulation, + MenuTag.getWiimoteMotionSimulationMenuTag(wiimoteNumber) + ) + ) + sl.add( + SubmenuSetting( + context, + R.string.wiimote_motion_input, + MenuTag.getWiimoteMotionInputMenuTag(wiimoteNumber) + ) + ) + + // TYPE_OTHER is included here instead of in addWiimoteGeneralSubSettings so that touchscreen + // users won't have to dig into a submenu to find the Sideways Wii Remote setting + addControllerMappingSettings( + sl, + wiimote, + ArraySet(listOf(ControlGroup.TYPE_ATTACHMENTS, ControlGroup.TYPE_OTHER)) + ) + } + } + + private fun addExtensionTypeSettings( + sl: ArrayList, + wiimoteNumber: Int, + extensionType: Int + ) { + addControllerMappingSettings( + sl, + EmulatedController.getWiimoteAttachment(wiimoteNumber, extensionType), + null + ) + } + + private fun addWiimoteGeneralSubSettings(sl: ArrayList, wiimoteNumber: Int) { + addControllerMappingSettings( + sl, + EmulatedController.getWiimote(wiimoteNumber), + setOf(ControlGroup.TYPE_BUTTONS) + ) + } + + private fun addWiimoteMotionSimulationSubSettings( + sl: ArrayList, + wiimoteNumber: Int + ) { + addControllerMappingSettings( + sl, EmulatedController.getWiimote(wiimoteNumber), + ArraySet( + listOf( + ControlGroup.TYPE_FORCE, + ControlGroup.TYPE_TILT, + ControlGroup.TYPE_CURSOR, + ControlGroup.TYPE_SHAKE + ) + ) + ) + } + + private fun addWiimoteMotionInputSubSettings(sl: ArrayList, wiimoteNumber: Int) { + addControllerMappingSettings( + sl, EmulatedController.getWiimote(wiimoteNumber), + ArraySet( + listOf( + ControlGroup.TYPE_IMU_ACCELEROMETER, + ControlGroup.TYPE_IMU_GYROSCOPE, + ControlGroup.TYPE_IMU_CURSOR + ) + ) + ) + } + + /** + * Adds controller settings that can be set on a per-game basis. + * + * @param sl The list to place controller settings into. + * @param profileString The prefix used for the profile setting in game INI files. + * @param controllerNumber The index of the controller, 0-3. + */ + private fun addControllerPerGameSettings( + sl: ArrayList, + profileString: String, + controllerNumber: Int + ) { + val profiles = ProfileDialogPresenter(menuTag).getProfileNames(false) + val profileKey = profileString + "Profile" + (controllerNumber + 1) + sl.add( + StringSingleChoiceSetting( + context, + AdHocStringSetting(Settings.FILE_GAME_SETTINGS_ONLY, "Controls", profileKey, ""), + R.string.input_profile, + 0, + profiles, + profiles, + R.string.input_profiles_empty + ) + ) + } + + /** + * Adds settings and actions that apply to a controller as a whole. + * For instance, the device setting and the Clear action. + * + * @param sl The list to place controller settings into. + * @param controller The controller to add settings for. + */ + private fun addControllerMetaSettings( + sl: ArrayList, + controller: EmulatedController + ) { + sl.add( + InputDeviceSetting( + context, + R.string.input_device, + 0, + controller + ) + ) + + sl.add(SwitchSetting(context, object : AbstractBooleanSetting { + override val isOverridden: Boolean = false + + override val isRuntimeEditable: Boolean = true + + override fun delete(settings: Settings): Boolean { + fragmentView.isMappingAllDevices = false + return true + } + + override val boolean: Boolean + get() = fragmentView.isMappingAllDevices + + override fun setBoolean(settings: Settings, newValue: Boolean) { + fragmentView.isMappingAllDevices = newValue + } + }, R.string.input_device_all_devices, R.string.input_device_all_devices_description)) + + sl.add( + RunRunnable( + context, + R.string.input_reset_to_default, + R.string.input_reset_to_default_description, + R.string.input_reset_warning, + 0, + true + ) { loadDefaultControllerSettings(controller) }) + sl.add( + RunRunnable( + context, + R.string.input_clear, + R.string.input_clear_description, + R.string.input_reset_warning, + 0, + true + ) { clearControllerSettings(controller) }) + sl.add( + RunRunnable( + context, + R.string.input_profiles, + 0, + 0, + 0, + true + ) { fragmentView.showDialogFragment(ProfileDialog.create(menuTag!!)) }) + + updateOldControllerSettingsWarningVisibility(controller) + } + + /** + * Adds mapping settings and other control-specific settings. + * + * @param sl The list to place controller settings into. + * @param controller The controller to add settings for. + * @param groupTypeFilter If this is non-null, only groups whose types match this are considered. + */ + private fun addControllerMappingSettings( + sl: ArrayList, + controller: EmulatedController, + groupTypeFilter: Set? + ) { + updateOldControllerSettingsWarningVisibility(controller) + + val groupCount = controller.groupCount + for (i in 0 until groupCount) { + val group = controller.getGroup(i) + val groupType = group.groupType + if (groupTypeFilter != null && !groupTypeFilter.contains(groupType)) continue + + sl.add(HeaderSetting(group.uiName, "")) + + if (group.defaultEnabledValue != ControlGroup.DEFAULT_ENABLED_ALWAYS) { + sl.add( + SwitchSetting( + context, + ControlGroupEnabledSetting(group), + R.string.enabled, + 0 + ) + ) + } + + val controlCount = group.controlCount + for (j in 0 until controlCount) { + sl.add(InputMappingControlSetting(group.getControl(j), controller)) + } + + if (groupType == ControlGroup.TYPE_ATTACHMENTS) { + val attachmentSetting = group.attachmentSetting + sl.add( + SingleChoiceSetting( + context, InputMappingIntSetting(attachmentSetting), + R.string.wiimote_extensions, 0, R.array.wiimoteExtensionsEntries, + R.array.wiimoteExtensionsValues, + MenuTag.getWiimoteExtensionMenuTag(controllerNumber) + ) + ) + } + + val numericSettingCount = group.numericSettingCount + for (j in 0 until numericSettingCount) { + addNumericSetting(sl, group.getNumericSetting(j)) + } + } + } + + private fun addNumericSetting(sl: ArrayList, setting: NumericSetting) { + when (setting.type) { + NumericSetting.TYPE_DOUBLE -> sl.add( + FloatSliderSetting( + InputMappingDoubleSetting(setting), + setting.uiName, + "", + ceil(setting.doubleMin).toInt(), + floor(setting.doubleMax).toInt(), + setting.uiSuffix + ) + ) + NumericSetting.TYPE_BOOLEAN -> sl.add( + SwitchSetting( + InputMappingBooleanSetting(setting), + setting.uiName, + setting.uiDescription + ) + ) + } + } + + fun updateOldControllerSettingsWarningVisibility() { + updateOldControllerSettingsWarningVisibility(menuTag!!.correspondingEmulatedController) + } + + private fun updateOldControllerSettingsWarningVisibility(controller: EmulatedController) { + val defaultDevice = controller.defaultDevice + + hasOldControllerSettings = defaultDevice.startsWith("Android/") && + defaultDevice.endsWith("/Touchscreen") + + fragmentView.setOldControllerSettingsWarningVisibility(hasOldControllerSettings) + } + + private fun loadDefaultControllerSettings(controller: EmulatedController) { + controller.loadDefaultSettings() + fragmentView.onControllerSettingsChanged() + } + + private fun clearControllerSettings(controller: EmulatedController) { + controller.clearSettings() + fragmentView.onControllerSettingsChanged() + } + + fun setAllLogTypes(value: Boolean) { + val settings = fragmentView.settings + + for ((key) in LOG_TYPE_NAMES) { + AdHocBooleanSetting( + Settings.FILE_LOGGER, + Settings.SECTION_LOGGER_LOGS, + key, + false + ).setBoolean(settings!!, value) + } + + fragmentView.adapter!!.notifyAllSettingsChanged() + } + + private fun convertOnThread(f: BooleanSupplier) { + ThreadUtil.runOnThreadAndShowResult( + fragmentView.fragmentActivity, + R.string.wii_converting, + 0, + { context.resources.getString(if (f.get()) R.string.wii_convert_success else R.string.wii_convert_failure) } + ) + } + + companion object { + private val LOG_TYPE_NAMES = NativeLibrary.GetLogTypeNames() + const val ARG_CONTROLLER_TYPE = "controller_type" + const val ARG_SERIALPORT1_TYPE = "serialport1_type" + + // Value obtained from LogLevel in Common/Logging/Log.h + private val logVerbosityEntries: Int + get() = + if (NativeLibrary.GetMaxLogLevel() == 5) { + R.array.logVerbosityEntriesMaxLevelDebug + } else { + R.array.logVerbosityEntriesMaxLevelInfo + } + + // Value obtained from LogLevel in Common/Logging/Log.h + private val logVerbosityValues: Int + get() = + if (NativeLibrary.GetMaxLogLevel() == 5) { + R.array.logVerbosityValuesMaxLevelDebug + } else { + R.array.logVerbosityValuesMaxLevelInfo + } + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentView.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentView.java deleted file mode 100644 index 809f26f7cc..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentView.java +++ /dev/null @@ -1,123 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.ui; - -import androidx.annotation.NonNull; -import androidx.fragment.app.DialogFragment; -import androidx.fragment.app.FragmentActivity; - -import org.dolphinemu.dolphinemu.features.settings.model.Settings; -import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; - -import java.util.ArrayList; - -/** - * Abstraction for a screen showing a list of settings. Instances of - * this type of view will each display a layer of the setting hierarchy. - */ -public interface SettingsFragmentView -{ - /** - * Called by the containing Activity to notify the Fragment that an - * asynchronous load operation completed. - * - * @param settings The (possibly null) result of the ini load operation. - */ - void onSettingsFileLoaded(Settings settings); - - /** - * Pass an ArrayList of settings to the View so that it can be displayed on screen. - * - * @param settingsList The settings to display - */ - void showSettingsList(ArrayList settingsList); - - /** - * Called by the containing Activity when an asynchronous load operation fails. - * Instructs the Fragment to load the settings screen with defaults selected. - */ - void loadDefaultSettings(); - - /** - * @return The Fragment's containing activity. - */ - FragmentActivity getActivity(); - - /** - * @return The Fragment's SettingsAdapter. - */ - SettingsAdapter getAdapter(); - - /** - * Tell the Fragment to tell the containing Activity to show a new - * Fragment containing a submenu of settings. - * - * @param menuKey Identifier for the settings group that should be shown. - */ - void loadSubMenu(MenuTag menuKey); - - void showDialogFragment(DialogFragment fragment); - - /** - * Tell the Fragment to tell the containing activity to display a toast message. - * - * @param message Text to be shown in the Toast - */ - void showToastMessage(String message); - - /** - * @return The backing settings store. - */ - Settings getSettings(); - - /** - * Have the fragment tell the containing Activity that a setting was modified. - */ - void onSettingChanged(); - - /** - * Refetches the values of all controller settings. - * - * To be used when loading an input profile or performing some other action that changes all - * controller settings at once. - */ - void onControllerSettingsChanged(); - - /** - * Have the fragment tell the containing Activity that the user wants to open the MenuTag - * associated with a setting. - * - * @param menuTag The MenuTag of the setting. - * @param value The current value of the setting. - */ - void onMenuTagAction(@NonNull MenuTag menuTag, int value); - - /** - * Returns whether anything will happen when the user wants to open the MenuTag associated with a - * setting, given the current value of the setting. - * - * @param menuTag The MenuTag of the setting. - * @param value The current value of the setting. - */ - boolean hasMenuTagActionForValue(@NonNull MenuTag menuTag, int value); - - /** - * Sets whether the input mapping dialog should detect inputs from all devices, - * not just the device configured for the controller. - */ - void setMappingAllDevices(boolean allDevices); - - /** - * Returns whether the input mapping dialog should detect inputs from all devices, - * not just the device configured for the controller. - */ - boolean isMappingAllDevices(); - - /** - * Shows or hides a warning telling the user that they're using incompatible controller settings. - * The warning is hidden by default. - * - * @param visible Whether the warning should be visible. - */ - void setOldControllerSettingsWarningVisibility(boolean visible); -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentView.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentView.kt new file mode 100644 index 0000000000..fa861153dd --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentView.kt @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.ui + +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.FragmentActivity +import org.dolphinemu.dolphinemu.features.settings.model.Settings +import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem + +/** + * Abstraction for a screen showing a list of settings. Instances of + * this type of view will each display a layer of the Setting hierarchy. + */ +interface SettingsFragmentView { + /** + * Called by the containing Activity to notify the Fragment that an + * asynchronous load operation completed. + * + * @param settings The (possibly null) result of the ini load operation. + */ + fun onSettingsFileLoaded(settings: Settings) + + /** + * Pass an ArrayList of settings to the View so that it can be displayed on screen. + * + * @param settingsList The settings to display + */ + fun showSettingsList(settingsList: ArrayList) + + /** + * Called by the containing Activity when an asynchronous load operation fails. + * Instructs the Fragment to load the settings screen with defaults selected. + */ + fun loadDefaultSettings() + + /** + * @return The Fragment's containing activity. + */ + val fragmentActivity: FragmentActivity + + /** + * @return The Fragment's SettingsAdapter. + */ + val adapter: SettingsAdapter? + + /** + * Tell the Fragment to tell the containing Activity to show a new + * Fragment containing a submenu of settings. + * + * @param menuKey Identifier for the settings group that should be shown. + */ + fun loadSubMenu(menuKey: MenuTag) + fun showDialogFragment(fragment: DialogFragment) + + /** + * Tell the Fragment to tell the containing activity to display a toast message. + * + * @param message Text to be shown in the Toast + */ + fun showToastMessage(message: String) + + /** + * @return The backing settings store. + */ + val settings: Settings? + + /** + * Have the fragment tell the containing Activity that a Setting was modified. + */ + fun onSettingChanged() + + /** + * Refetches the values of all controller settings. + * + * To be used when loading an input profile or performing some other action that changes all + * controller settings at once. + */ + fun onControllerSettingsChanged() + + /** + * Have the fragment tell the containing Activity that the user wants to open the MenuTag + * associated with a Setting. + * + * @param menuTag The MenuTag of the Setting. + * @param value The current value of the Setting. + */ + fun onMenuTagAction(menuTag: MenuTag, value: Int) + + /** + * Returns whether anything will happen when the user wants to open the MenuTag associated with a + * stringSetting, given the current value of the Setting. + * + * @param menuTag The MenuTag of the Setting. + * @param value The current value of the Setting. + */ + fun hasMenuTagActionForValue(menuTag: MenuTag, value: Int): Boolean + /** + * Returns whether the input mapping dialog should detect inputs from all devices, + * not just the device configured for the controller. + */ + /** + * Sets whether the input mapping dialog should detect inputs from all devices, + * not just the device configured for the controller. + */ + var isMappingAllDevices: Boolean + + /** + * Shows or hides a warning telling the user that they're using incompatible controller settings. + * The warning is hidden by default. + * + * @param visible Whether the warning should be visible. + */ + fun setOldControllerSettingsWarningVisibility(visible: Boolean) +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsViewModel.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsViewModel.java deleted file mode 100644 index fe67445cc0..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsViewModel.java +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.ui; - -import androidx.lifecycle.ViewModel; - -import org.dolphinemu.dolphinemu.features.settings.model.Settings; - -public class SettingsViewModel extends ViewModel -{ - private final Settings mSettings = new Settings(); - - public Settings getSettings() - { - return mSettings; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsViewModel.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsViewModel.kt new file mode 100644 index 0000000000..0685c23388 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsViewModel.kt @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.ui + +import androidx.lifecycle.ViewModel +import org.dolphinemu.dolphinemu.features.settings.model.Settings + +class SettingsViewModel : ViewModel() { + val settings = Settings() +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/DateTimeSettingViewHolder.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/DateTimeSettingViewHolder.kt index 2778a489fc..07201e31d6 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/DateTimeSettingViewHolder.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/DateTimeSettingViewHolder.kt @@ -18,12 +18,15 @@ class DateTimeSettingViewHolder( private val binding: ListItemSettingBinding, adapter: SettingsAdapter ) : SettingViewHolder(binding.root, adapter) { - private var mItem: DateTimeChoiceSetting? = null + lateinit var setting: DateTimeChoiceSetting + + override val item: SettingsItem + get() = setting override fun bind(item: SettingsItem) { - mItem = item as DateTimeChoiceSetting - val inputTime = mItem!!.getSelectedValue() - binding.textSettingName.text = item.getName() + setting = item as DateTimeChoiceSetting + val inputTime = setting.getSelectedValue() + binding.textSettingName.text = item.name if (!TextUtils.isEmpty(inputTime)) { val epochTime = inputTime.substring(2).toLong(16) @@ -32,21 +35,17 @@ class DateTimeSettingViewHolder( val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM) binding.textSettingDescription.text = dateFormatter.format(zonedTime) } else { - binding.textSettingDescription.text = item.getDescription() + binding.textSettingDescription.text = item.description } - setStyle(binding.textSettingName, mItem) + setStyle(binding.textSettingName, setting) } override fun onClick(clicked: View) { - if (!mItem!!.isEditable) { + if (!setting.isEditable) { showNotRuntimeEditableError() return } - adapter.onDateTimeClick(mItem, bindingAdapterPosition) - setStyle(binding.textSettingName, mItem) - } - - override fun getItem(): SettingsItem? { - return mItem + adapter.onDateTimeClick(setting, bindingAdapterPosition) + setStyle(binding.textSettingName, setting) } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/FilePickerViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/FilePickerViewHolder.java deleted file mode 100644 index 1b75bb72b2..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/FilePickerViewHolder.java +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.ui.viewholder; - -import android.text.TextUtils; -import android.view.View; - -import androidx.annotation.Nullable; - -import org.dolphinemu.dolphinemu.R; -import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding; -import org.dolphinemu.dolphinemu.features.settings.model.view.FilePicker; -import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; -import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter; -import org.dolphinemu.dolphinemu.ui.main.MainPresenter; -import org.dolphinemu.dolphinemu.utils.DirectoryInitialization; -import org.dolphinemu.dolphinemu.utils.FileBrowserHelper; - -public final class FilePickerViewHolder extends SettingViewHolder -{ - private FilePicker mFilePicker; - private SettingsItem mItem; - - private final ListItemSettingBinding mBinding; - - public FilePickerViewHolder(ListItemSettingBinding binding, SettingsAdapter adapter) - { - super(binding.getRoot(), adapter); - mBinding = binding; - } - - @Override - public void bind(SettingsItem item) - { - mFilePicker = (FilePicker) item; - mItem = item; - - String path = mFilePicker.getSelectedValue(); - - if (FileBrowserHelper.isPathEmptyOrValid(path)) - { - itemView.setBackground(mBinding.getRoot().getBackground()); - } - else - { - itemView.setBackgroundResource(R.drawable.invalid_setting_background); - } - - mBinding.textSettingName.setText(item.getName()); - - if (!TextUtils.isEmpty(item.getDescription())) - { - mBinding.textSettingDescription.setText(item.getDescription()); - } - else - { - if (TextUtils.isEmpty(path)) - { - String defaultPathRelative = mFilePicker.getDefaultPathRelativeToUserDirectory(); - if (defaultPathRelative != null) - { - path = DirectoryInitialization.getUserDirectory() + defaultPathRelative; - } - } - - mBinding.textSettingDescription.setText(path); - } - - setStyle(mBinding.textSettingName, mItem); - } - - @Override - public void onClick(View clicked) - { - if (!mItem.isEditable()) - { - showNotRuntimeEditableError(); - return; - } - - int position = getBindingAdapterPosition(); - if (mFilePicker.getRequestType() == MainPresenter.REQUEST_DIRECTORY) - { - getAdapter().onFilePickerDirectoryClick(mItem, position); - } - else - { - getAdapter().onFilePickerFileClick(mItem, position); - } - - setStyle(mBinding.textSettingName, mItem); - } - - @Nullable @Override - protected SettingsItem getItem() - { - return mItem; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/FilePickerViewHolder.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/FilePickerViewHolder.kt new file mode 100644 index 0000000000..215615828e --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/FilePickerViewHolder.kt @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.ui.viewholder + +import android.text.TextUtils +import android.view.View +import org.dolphinemu.dolphinemu.R +import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding +import org.dolphinemu.dolphinemu.features.settings.model.view.FilePicker +import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem +import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter +import org.dolphinemu.dolphinemu.ui.main.MainPresenter +import org.dolphinemu.dolphinemu.utils.DirectoryInitialization +import org.dolphinemu.dolphinemu.utils.FileBrowserHelper + +class FilePickerViewHolder( + private val binding: ListItemSettingBinding, + adapter: SettingsAdapter? +) : SettingViewHolder(binding.getRoot(), adapter!!) { + lateinit var setting: FilePicker + + override val item: SettingsItem + get() = setting + + override fun bind(item: SettingsItem) { + setting = item as FilePicker + + var path = setting.getSelectedValue() + + if (FileBrowserHelper.isPathEmptyOrValid(path)) { + itemView.background = binding.getRoot().background + } else { + itemView.setBackgroundResource(R.drawable.invalid_setting_background) + } + + binding.textSettingName.text = setting.name + + if (!TextUtils.isEmpty(setting.description)) { + binding.textSettingDescription.text = setting.description + } else { + if (TextUtils.isEmpty(path)) { + val defaultPathRelative = setting.defaultPathRelativeToUserDirectory + if (defaultPathRelative != null) { + path = DirectoryInitialization.getUserDirectory() + defaultPathRelative + } + } + binding.textSettingDescription.text = path + } + + setStyle(binding.textSettingName, setting) + } + + override fun onClick(clicked: View) { + if (!setting.isEditable) { + showNotRuntimeEditableError() + return + } + + val position = bindingAdapterPosition + if (setting.requestType == MainPresenter.REQUEST_DIRECTORY) { + adapter.onFilePickerDirectoryClick(setting, position) + } else { + adapter.onFilePickerFileClick(setting, position) + } + + setStyle(binding.textSettingName, setting) + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/HeaderHyperLinkViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/HeaderHyperLinkViewHolder.java deleted file mode 100644 index 2d3e9ac5d0..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/HeaderHyperLinkViewHolder.java +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.ui.viewholder; - -import android.text.method.LinkMovementMethod; - -import androidx.annotation.NonNull; - -import com.google.android.material.color.MaterialColors; - -import org.dolphinemu.dolphinemu.R; -import org.dolphinemu.dolphinemu.databinding.ListItemHeaderBinding; -import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; -import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter; - -public final class HeaderHyperLinkViewHolder extends HeaderViewHolder -{ - private final ListItemHeaderBinding mBinding; - - public HeaderHyperLinkViewHolder(@NonNull ListItemHeaderBinding binding, SettingsAdapter adapter) - { - super(binding, adapter); - mBinding = binding; - itemView.setOnClickListener(null); - } - - @Override - public void bind(@NonNull SettingsItem item) - { - super.bind(item); - - mBinding.textHeaderName.setMovementMethod(LinkMovementMethod.getInstance()); - mBinding.textHeaderName.setLinkTextColor( - MaterialColors.getColor(itemView, R.attr.colorTertiary)); - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/HeaderHyperLinkViewHolder.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/HeaderHyperLinkViewHolder.kt new file mode 100644 index 0000000000..5bb58cf489 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/HeaderHyperLinkViewHolder.kt @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.ui.viewholder + +import android.text.method.LinkMovementMethod +import com.google.android.material.color.MaterialColors +import org.dolphinemu.dolphinemu.R +import org.dolphinemu.dolphinemu.databinding.ListItemHeaderBinding +import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem +import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter + +class HeaderHyperLinkViewHolder( + private val binding: ListItemHeaderBinding, + adapter: SettingsAdapter? +) : HeaderViewHolder(binding, adapter) { + init { + itemView.setOnClickListener(null) + } + + override fun bind(item: SettingsItem) { + super.bind(item) + binding.textHeaderName.movementMethod = LinkMovementMethod.getInstance() + binding.textHeaderName.setLinkTextColor( + MaterialColors.getColor(itemView, R.attr.colorTertiary) + ) + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/HeaderViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/HeaderViewHolder.java deleted file mode 100644 index 889b087396..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/HeaderViewHolder.java +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.ui.viewholder; - -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; -import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter; -import org.dolphinemu.dolphinemu.databinding.ListItemHeaderBinding; - -public class HeaderViewHolder extends SettingViewHolder -{ - private final ListItemHeaderBinding mBinding; - - public HeaderViewHolder(@NonNull ListItemHeaderBinding binding, SettingsAdapter adapter) - { - super(binding.getRoot(), adapter); - itemView.setOnClickListener(null); - mBinding = binding; - } - - @Override - public void bind(@NonNull SettingsItem item) - { - mBinding.textHeaderName.setText(item.getName()); - } - - @Override - public void onClick(View clicked) - { - // no-op - } - - @Nullable @Override - protected SettingsItem getItem() - { - return null; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/HeaderViewHolder.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/HeaderViewHolder.kt new file mode 100644 index 0000000000..0f28bf1ffe --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/HeaderViewHolder.kt @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.ui.viewholder + +import android.view.View +import org.dolphinemu.dolphinemu.databinding.ListItemHeaderBinding +import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem +import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter + +open class HeaderViewHolder( + private val binding: ListItemHeaderBinding, + adapter: SettingsAdapter? +) : SettingViewHolder(binding.root, adapter!!) { + override val item: SettingsItem? = null + + init { + itemView.setOnClickListener(null) + } + + override fun bind(item: SettingsItem) { + binding.textHeaderName.text = item.name + } + + override fun onClick(clicked: View) { + // no-op + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/InputStringSettingViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/InputStringSettingViewHolder.java deleted file mode 100644 index 6743808079..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/InputStringSettingViewHolder.java +++ /dev/null @@ -1,71 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.ui.viewholder; - -import android.text.TextUtils; -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding; -import org.dolphinemu.dolphinemu.features.settings.model.view.InputStringSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; -import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter; - -public final class InputStringSettingViewHolder extends SettingViewHolder -{ - private InputStringSetting mInputString; - - private final ListItemSettingBinding mBinding; - - public InputStringSettingViewHolder(@NonNull ListItemSettingBinding binding, - SettingsAdapter adapter) - { - super(binding.getRoot(), adapter); - mBinding = binding; - } - - @Override - public void bind(SettingsItem item) - { - mInputString = (InputStringSetting) item; - - String inputString = mInputString.getSelectedValue(); - - mBinding.textSettingName.setText(item.getName()); - - if (!TextUtils.isEmpty(inputString)) - { - mBinding.textSettingDescription.setText(inputString); - } - else - { - mBinding.textSettingDescription.setText(item.getDescription()); - } - - setStyle(mBinding.textSettingName, mInputString); - } - - @Override - public void onClick(View clicked) - { - if (!mInputString.isEditable()) - { - showNotRuntimeEditableError(); - return; - } - - int position = getBindingAdapterPosition(); - - getAdapter().onInputStringClick(mInputString, position); - - setStyle(mBinding.textSettingName, mInputString); - } - - @Nullable @Override - protected SettingsItem getItem() - { - return mInputString; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/InputStringSettingViewHolder.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/InputStringSettingViewHolder.kt new file mode 100644 index 0000000000..4af0019c30 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/InputStringSettingViewHolder.kt @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.ui.viewholder + +import android.text.TextUtils +import android.view.View +import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding +import org.dolphinemu.dolphinemu.features.settings.model.view.InputStringSetting +import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem +import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter + +class InputStringSettingViewHolder( + private val binding: ListItemSettingBinding, + adapter: SettingsAdapter +) : SettingViewHolder(binding.getRoot(), adapter) { + private lateinit var setting: InputStringSetting + + override val item: SettingsItem + get() = setting + + override fun bind(item: SettingsItem) { + setting = item as InputStringSetting + + val inputString = setting.selectedValue + + binding.textSettingName.text = setting.name + + if (!TextUtils.isEmpty(inputString)) { + binding.textSettingDescription.text = inputString + } else { + binding.textSettingDescription.text = setting.description + } + + setStyle(binding.textSettingName, setting) + } + + override fun onClick(clicked: View) { + if (!setting.isEditable) { + showNotRuntimeEditableError() + return + } + + val position = bindingAdapterPosition + + adapter.onInputStringClick(setting, position) + + setStyle(binding.textSettingName, setting) + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/RunRunnableViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/RunRunnableViewHolder.java deleted file mode 100644 index 6104a2aa2e..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/RunRunnableViewHolder.java +++ /dev/null @@ -1,93 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.ui.viewholder; - -import android.content.Context; -import android.view.View; -import android.widget.Toast; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.google.android.material.dialog.MaterialAlertDialogBuilder; - -import org.dolphinemu.dolphinemu.R; -import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding; -import org.dolphinemu.dolphinemu.features.settings.model.view.RunRunnable; -import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; -import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter; - -public final class RunRunnableViewHolder extends SettingViewHolder -{ - private RunRunnable mItem; - - private final Context mContext; - - private final ListItemSettingBinding mBinding; - - public RunRunnableViewHolder(@NonNull ListItemSettingBinding binding, SettingsAdapter adapter, - Context context) - { - super(binding.getRoot(), adapter); - mBinding = binding; - mContext = context; - } - - @Override - public void bind(SettingsItem item) - { - mItem = (RunRunnable) item; - - mBinding.textSettingName.setText(item.getName()); - mBinding.textSettingDescription.setText(item.getDescription()); - - setStyle(mBinding.textSettingName, mItem); - } - - @Override - public void onClick(View clicked) - { - if (!mItem.isEditable()) - { - showNotRuntimeEditableError(); - return; - } - - int alertTextID = mItem.getAlertText(); - - if (alertTextID > 0) - { - new MaterialAlertDialogBuilder(mContext) - .setTitle(mItem.getName()) - .setMessage(alertTextID) - .setPositiveButton(R.string.ok, (dialog, whichButton) -> - { - runRunnable(); - dialog.dismiss(); - }) - .setNegativeButton(R.string.cancel, (dialog, whichButton) -> dialog.dismiss()) - .show(); - } - else - { - runRunnable(); - } - } - - @Nullable @Override - protected SettingsItem getItem() - { - return mItem; - } - - private void runRunnable() - { - mItem.getRunnable().run(); - - if (mItem.getToastTextAfterRun() > 0) - { - Toast.makeText(mContext, mContext.getString(mItem.getToastTextAfterRun()), Toast.LENGTH_SHORT) - .show(); - } - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/RunRunnableViewHolder.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/RunRunnableViewHolder.kt new file mode 100644 index 0000000000..2ff39d51d5 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/RunRunnableViewHolder.kt @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.ui.viewholder + +import android.content.Context +import android.content.DialogInterface +import android.view.View +import android.widget.Toast +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.dolphinemu.dolphinemu.R +import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding +import org.dolphinemu.dolphinemu.features.settings.model.view.RunRunnable +import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem +import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter + +class RunRunnableViewHolder( + private val mBinding: ListItemSettingBinding, adapter: SettingsAdapter?, + private val mContext: Context +) : SettingViewHolder(mBinding.getRoot(), adapter!!) { + private lateinit var setting: RunRunnable + + override val item: SettingsItem + get() = setting + + override fun bind(item: SettingsItem) { + setting = item as RunRunnable + + mBinding.textSettingName.text = setting.name + mBinding.textSettingDescription.text = setting.description + + setStyle(mBinding.textSettingName, setting) + } + + override fun onClick(clicked: View) { + if (!setting.isEditable) { + showNotRuntimeEditableError() + return + } + + val alertTextID = setting.alertText + + if (alertTextID > 0) { + MaterialAlertDialogBuilder(mContext) + .setTitle(setting.name) + .setMessage(alertTextID) + .setPositiveButton(R.string.ok) { dialog: DialogInterface, _: Int -> + runRunnable() + dialog.dismiss() + } + .setNegativeButton(R.string.cancel) { dialog: DialogInterface, _: Int -> dialog.dismiss() } + .show() + } else { + runRunnable() + } + } + + private fun runRunnable() { + setting.runnable.run() + + if (setting.toastTextAfterRun > 0) { + Toast.makeText( + mContext, + mContext.getString(setting.toastTextAfterRun), + Toast.LENGTH_SHORT + ).show() + } + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SettingViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SettingViewHolder.java deleted file mode 100644 index 5217e4f889..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SettingViewHolder.java +++ /dev/null @@ -1,111 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.ui.viewholder; - -import android.content.Context; -import android.graphics.Paint; -import android.graphics.Typeface; -import android.view.View; -import android.widget.TextView; -import android.widget.Toast; - -import androidx.annotation.Nullable; -import androidx.recyclerview.widget.RecyclerView; - -import com.google.android.material.dialog.MaterialAlertDialogBuilder; - -import org.dolphinemu.dolphinemu.DolphinApplication; -import org.dolphinemu.dolphinemu.R; -import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; -import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter; - -public abstract class SettingViewHolder extends RecyclerView.ViewHolder - implements View.OnClickListener, View.OnLongClickListener -{ - private SettingsAdapter mAdapter; - - public SettingViewHolder(View itemView, SettingsAdapter adapter) - { - super(itemView); - - mAdapter = adapter; - - itemView.setOnClickListener(this); - itemView.setOnLongClickListener(this); - } - - protected SettingsAdapter getAdapter() - { - return mAdapter; - } - - protected void setStyle(TextView textView, SettingsItem settingsItem) - { - boolean overridden = settingsItem.isOverridden(); - textView.setTypeface(null, overridden ? Typeface.BOLD : Typeface.NORMAL); - - if (!settingsItem.isEditable()) - textView.setPaintFlags(textView.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); - } - - protected static void showNotRuntimeEditableError() - { - Toast.makeText(DolphinApplication.getAppContext(), R.string.setting_not_runtime_editable, - Toast.LENGTH_SHORT).show(); - } - - protected static void showIplNotAvailableError() - { - Toast.makeText(DolphinApplication.getAppContext(), R.string.ipl_not_found, Toast.LENGTH_SHORT) - .show(); - } - - /** - * Called by the adapter to set this ViewHolder's child views to display the list item - * it must now represent. - * - * @param item The list item that should be represented by this ViewHolder. - */ - public abstract void bind(SettingsItem item); - - /** - * Called when this ViewHolder's view is clicked on. Implementations should usually pass - * this event up to the adapter. - * - * @param clicked The view that was clicked on. - */ - public abstract void onClick(View clicked); - - @Nullable - protected abstract SettingsItem getItem(); - - public boolean onLongClick(View clicked) - { - SettingsItem item = getItem(); - - if (item == null || !item.canClear()) - return false; - - if (!item.isEditable()) - { - showNotRuntimeEditableError(); - return true; - } - - Context context = clicked.getContext(); - - new MaterialAlertDialogBuilder(context) - .setMessage(R.string.setting_clear_confirm) - .setPositiveButton(R.string.ok, (dialog, whichButton) -> - { - getAdapter().clearSetting(item); - bind(item); - Toast.makeText(context, R.string.setting_cleared, Toast.LENGTH_SHORT).show(); - dialog.dismiss(); - }) - .setNegativeButton(R.string.cancel, (dialog, whichButton) -> dialog.dismiss()) - .show(); - - return true; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SettingViewHolder.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SettingViewHolder.kt new file mode 100644 index 0000000000..0767840154 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SettingViewHolder.kt @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.ui.viewholder + +import android.content.DialogInterface +import android.graphics.Paint +import android.graphics.Typeface +import android.view.View +import android.view.View.OnLongClickListener +import android.widget.TextView +import android.widget.Toast +import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.dolphinemu.dolphinemu.DolphinApplication +import org.dolphinemu.dolphinemu.R +import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem +import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter + +abstract class SettingViewHolder(itemView: View, protected val adapter: SettingsAdapter) : + RecyclerView.ViewHolder(itemView), View.OnClickListener, OnLongClickListener { + + init { + itemView.setOnClickListener(this) + itemView.setOnLongClickListener(this) + } + + protected fun setStyle(textView: TextView, settingsItem: SettingsItem) { + val overridden = settingsItem.isOverridden + textView.setTypeface(null, if (overridden) Typeface.BOLD else Typeface.NORMAL) + + if (!settingsItem.isEditable) textView.paintFlags = + textView.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG + } + + /** + * Called by the adapter to set this ViewHolder's child views to display the list item + * it must now represent. + * + * @param item The list item that should be represented by this ViewHolder. + */ + abstract fun bind(item: SettingsItem) + + /** + * Called when this ViewHolder's view is clicked on. Implementations should usually pass + * this event up to the adapter. + * + * @param clicked The view that was clicked on. + */ + abstract override fun onClick(clicked: View) + + protected abstract val item: SettingsItem? + + override fun onLongClick(clicked: View): Boolean { + val item = item + + if (item == null || !item.canClear()) return false + + if (!item.isEditable) { + showNotRuntimeEditableError() + return true + } + + val context = clicked.context + + MaterialAlertDialogBuilder(context) + .setMessage(R.string.setting_clear_confirm) + .setPositiveButton(R.string.ok) { dialog: DialogInterface, _: Int -> + adapter.clearSetting(item) + bind(item) + Toast.makeText( + context, + R.string.setting_cleared, + Toast.LENGTH_SHORT + ).show() + dialog.dismiss() + } + .setNegativeButton(R.string.cancel) { dialog: DialogInterface, _: Int -> dialog.dismiss() } + .show() + + return true + } + + protected fun showIplNotAvailableError() { + Toast.makeText( + DolphinApplication.getAppContext(), + R.string.ipl_not_found, + Toast.LENGTH_SHORT + ).show() + } + + protected fun showNotRuntimeEditableError() { + Toast.makeText( + DolphinApplication.getAppContext(), + R.string.setting_not_runtime_editable, + Toast.LENGTH_SHORT + ).show() + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SingleChoiceViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SingleChoiceViewHolder.java deleted file mode 100644 index 15ebaf4e82..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SingleChoiceViewHolder.java +++ /dev/null @@ -1,148 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.ui.viewholder; - -import android.content.res.Resources; -import android.text.TextUtils; -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding; -import org.dolphinemu.dolphinemu.features.settings.model.Settings; -import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; -import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSettingDynamicDescriptions; -import org.dolphinemu.dolphinemu.features.settings.model.view.StringSingleChoiceSetting; -import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag; -import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter; - -import java.util.function.Supplier; - -public final class SingleChoiceViewHolder extends SettingViewHolder -{ - private SettingsItem mItem; - - private final ListItemSettingBinding mBinding; - - public SingleChoiceViewHolder(@NonNull ListItemSettingBinding binding, SettingsAdapter adapter) - { - super(binding.getRoot(), adapter); - mBinding = binding; - } - - @Override - public void bind(SettingsItem item) - { - mItem = item; - - mBinding.textSettingName.setText(item.getName()); - - if (!TextUtils.isEmpty(item.getDescription())) - { - mBinding.textSettingDescription.setText(item.getDescription()); - } - else if (item instanceof SingleChoiceSetting) - { - SingleChoiceSetting setting = (SingleChoiceSetting) item; - int selected = setting.getSelectedValue(); - Resources resMgr = mBinding.textSettingDescription.getContext().getResources(); - String[] choices = resMgr.getStringArray(setting.getChoicesId()); - int[] values = resMgr.getIntArray(setting.getValuesId()); - for (int i = 0; i < values.length; ++i) - { - if (values[i] == selected) - { - mBinding.textSettingDescription.setText(choices[i]); - } - } - } - else if (item instanceof StringSingleChoiceSetting) - { - StringSingleChoiceSetting setting = (StringSingleChoiceSetting) item; - String choice = setting.getSelectedChoice(); - mBinding.textSettingDescription.setText(choice); - } - else if (item instanceof SingleChoiceSettingDynamicDescriptions) - { - SingleChoiceSettingDynamicDescriptions setting = - (SingleChoiceSettingDynamicDescriptions) item; - int selected = setting.getSelectedValue(); - Resources resMgr = mBinding.textSettingDescription.getContext().getResources(); - String[] choices = resMgr.getStringArray(setting.getDescriptionChoicesId()); - int[] values = resMgr.getIntArray(setting.getDescriptionValuesId()); - for (int i = 0; i < values.length; ++i) - { - if (values[i] == selected) - { - mBinding.textSettingDescription.setText(choices[i]); - } - } - } - - MenuTag menuTag = null; - Supplier getSelectedValue = null; - if (item instanceof SingleChoiceSetting) - { - SingleChoiceSetting setting = (SingleChoiceSetting) item; - menuTag = setting.getMenuTag(); - getSelectedValue = setting::getSelectedValue; - } - else if (item instanceof StringSingleChoiceSetting) - { - StringSingleChoiceSetting setting = (StringSingleChoiceSetting) item; - menuTag = setting.getMenuTag(); - getSelectedValue = setting::getSelectedValueIndex; - } - - if (menuTag != null && getAdapter().hasMenuTagActionForValue(menuTag, getSelectedValue.get())) - { - mBinding.buttonMoreSettings.setVisibility(View.VISIBLE); - - final MenuTag finalMenuTag = menuTag; - final Supplier finalGetSelectedValue = getSelectedValue; - mBinding.buttonMoreSettings.setOnClickListener((view) -> - getAdapter().onMenuTagAction(finalMenuTag, finalGetSelectedValue.get())); - } - else - { - mBinding.buttonMoreSettings.setVisibility(View.GONE); - } - - setStyle(mBinding.textSettingName, mItem); - } - - @Override - public void onClick(View clicked) - { - if (!mItem.isEditable()) - { - showNotRuntimeEditableError(); - return; - } - - int position = getBindingAdapterPosition(); - if (mItem instanceof SingleChoiceSetting) - { - getAdapter().onSingleChoiceClick((SingleChoiceSetting) mItem, position); - } - else if (mItem instanceof StringSingleChoiceSetting) - { - getAdapter().onStringSingleChoiceClick((StringSingleChoiceSetting) mItem, position); - } - else if (mItem instanceof SingleChoiceSettingDynamicDescriptions) - { - getAdapter().onSingleChoiceDynamicDescriptionsClick( - (SingleChoiceSettingDynamicDescriptions) mItem, position); - } - - setStyle(mBinding.textSettingName, mItem); - } - - @Nullable @Override - protected SettingsItem getItem() - { - return mItem; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt new file mode 100644 index 0000000000..dabc51a986 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.ui.viewholder + +import android.text.TextUtils +import android.view.View +import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding +import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem +import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSetting +import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSettingDynamicDescriptions +import org.dolphinemu.dolphinemu.features.settings.model.view.StringSingleChoiceSetting +import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag +import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter + +class SingleChoiceViewHolder( + private val binding: ListItemSettingBinding, + adapter: SettingsAdapter +) : SettingViewHolder(binding.getRoot(), adapter) { + override var item: SettingsItem? = null + + override fun bind(item: SettingsItem) { + this.item = item + + binding.textSettingName.text = item.name + + if (!TextUtils.isEmpty(item.description)) { + binding.textSettingDescription.text = item.description + } else if (item is SingleChoiceSetting) { + val selected = item.selectedValue + val resources = binding.textSettingDescription.context.resources + val choices = resources.getStringArray(item.choicesId) + val values = resources.getIntArray(item.valuesId) + for (i in values.indices) { + if (values[i] == selected) { + binding.textSettingDescription.text = choices[i] + } + } + } else if (item is StringSingleChoiceSetting) { + val choice = item.selectedChoice + binding.textSettingDescription.text = choice + } else if (item is SingleChoiceSettingDynamicDescriptions) { + val selected = item.selectedValue + val resMgr = binding.textSettingDescription.context.resources + val choices = resMgr.getStringArray(item.descriptionChoicesId) + val values = resMgr.getIntArray(item.descriptionValuesId) + for (i in values.indices) { + if (values[i] == selected) { + binding.textSettingDescription.text = choices[i] + } + } + } + + var menuTag: MenuTag? = null + var selectedValue = 0 + if (item is SingleChoiceSetting) { + menuTag = item.menuTag + selectedValue = item.selectedValue + } else if (item is StringSingleChoiceSetting) { + menuTag = item.menuTag + selectedValue = item.selectedValueIndex + } + + if (menuTag != null && adapter.hasMenuTagActionForValue(menuTag, selectedValue)) { + binding.buttonMoreSettings.visibility = View.VISIBLE + + binding.buttonMoreSettings.setOnClickListener { + adapter.onMenuTagAction(menuTag, selectedValue) + } + } else { + binding.buttonMoreSettings.visibility = View.GONE + } + setStyle(binding.textSettingName, item) + } + + override fun onClick(clicked: View) { + if (!item?.isEditable!!) { + showNotRuntimeEditableError() + return + } + + val position = bindingAdapterPosition + when (item) { + is SingleChoiceSetting -> { + adapter.onSingleChoiceClick(item as SingleChoiceSetting, position) + } + is StringSingleChoiceSetting -> { + adapter.onStringSingleChoiceClick(item as StringSingleChoiceSetting, position) + } + is SingleChoiceSettingDynamicDescriptions -> { + adapter.onSingleChoiceDynamicDescriptionsClick( + item as SingleChoiceSettingDynamicDescriptions, + position + ) + } + } + setStyle(binding.textSettingName, item!!) + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SliderViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SliderViewHolder.java deleted file mode 100644 index 8d53bb1365..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SliderViewHolder.java +++ /dev/null @@ -1,74 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.ui.viewholder; - -import android.content.Context; -import android.text.TextUtils; -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.dolphinemu.dolphinemu.R; -import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding; -import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; -import org.dolphinemu.dolphinemu.features.settings.model.view.SliderSetting; -import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter; - -public final class SliderViewHolder extends SettingViewHolder -{ - private final Context mContext; - - private SliderSetting mItem; - - private final ListItemSettingBinding mBinding; - - public SliderViewHolder(@NonNull ListItemSettingBinding binding, SettingsAdapter adapter, - Context context) - { - super(binding.getRoot(), adapter); - mBinding = binding; - mContext = context; - } - - @Override - public void bind(SettingsItem item) - { - mItem = (SliderSetting) item; - - mBinding.textSettingName.setText(item.getName()); - - if (!TextUtils.isEmpty(item.getDescription())) - { - mBinding.textSettingDescription.setText(item.getDescription()); - } - else - { - mBinding.textSettingDescription.setText(mContext.getString(R.string.slider_setting_value, - mItem.getSelectedValue(), mItem.getUnits())); - } - - setStyle(mBinding.textSettingName, mItem); - } - - @Override - public void onClick(View clicked) - { - if (!mItem.isEditable()) - { - showNotRuntimeEditableError(); - return; - } - - getAdapter().onSliderClick(mItem, getBindingAdapterPosition()); - - setStyle(mBinding.textSettingName, mItem); - } - - @Nullable @Override - protected SettingsItem getItem() - { - return mItem; - } -} - diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SliderViewHolder.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SliderViewHolder.kt new file mode 100644 index 0000000000..9009be4e7d --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SliderViewHolder.kt @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.ui.viewholder + +import android.content.Context +import android.text.TextUtils +import android.view.View +import org.dolphinemu.dolphinemu.R +import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding +import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem +import org.dolphinemu.dolphinemu.features.settings.model.view.SliderSetting +import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter + +class SliderViewHolder( + private val binding: ListItemSettingBinding, adapter: SettingsAdapter?, + private val context: Context +) : SettingViewHolder(binding.getRoot(), adapter!!) { + private lateinit var setting: SliderSetting + + override val item: SettingsItem + get() = setting + + override fun bind(item: SettingsItem) { + setting = item as SliderSetting + + binding.textSettingName.text = item.name + + if (!TextUtils.isEmpty(item.description)) { + binding.textSettingDescription.text = item.description + } else { + binding.textSettingDescription.text = context.getString( + R.string.slider_setting_value, + setting.selectedValue, + setting.units + ) + } + + setStyle(binding.textSettingName, setting) + } + + override fun onClick(clicked: View) { + if (!setting.isEditable) { + showNotRuntimeEditableError() + return + } + + adapter.onSliderClick(setting, bindingAdapterPosition) + + setStyle(binding.textSettingName, setting) + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SubmenuViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SubmenuViewHolder.java deleted file mode 100644 index 828e538874..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SubmenuViewHolder.java +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.ui.viewholder; - -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.dolphinemu.dolphinemu.databinding.ListItemSubmenuBinding; -import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; -import org.dolphinemu.dolphinemu.features.settings.model.view.SubmenuSetting; -import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter; - -public final class SubmenuViewHolder extends SettingViewHolder -{ - private SubmenuSetting mItem; - - private final ListItemSubmenuBinding mBinding; - - public SubmenuViewHolder(@NonNull ListItemSubmenuBinding binding, SettingsAdapter adapter) - { - super(binding.getRoot(), adapter); - mBinding = binding; - } - - @Override - public void bind(SettingsItem item) - { - mItem = (SubmenuSetting) item; - - mBinding.textSettingName.setText(item.getName()); - } - - @Override - public void onClick(View clicked) - { - getAdapter().onSubmenuClick(mItem); - } - - @Nullable @Override - protected SettingsItem getItem() - { - return mItem; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SubmenuViewHolder.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SubmenuViewHolder.kt new file mode 100644 index 0000000000..414e85e3e3 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SubmenuViewHolder.kt @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.ui.viewholder + +import android.view.View +import org.dolphinemu.dolphinemu.databinding.ListItemSubmenuBinding +import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem +import org.dolphinemu.dolphinemu.features.settings.model.view.SubmenuSetting +import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter + +class SubmenuViewHolder( + private val mBinding: ListItemSubmenuBinding, + adapter: SettingsAdapter +) : SettingViewHolder(mBinding.root, adapter) { + private lateinit var setting: SubmenuSetting + + override val item: SettingsItem + get() = setting + + override fun bind(item: SettingsItem) { + setting = item as SubmenuSetting + mBinding.textSettingName.text = item.name + } + + override fun onClick(clicked: View) { + adapter.onSubmenuClick(setting) + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SwitchSettingViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SwitchSettingViewHolder.java deleted file mode 100644 index 398711bea2..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SwitchSettingViewHolder.java +++ /dev/null @@ -1,106 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.ui.viewholder; - -import android.view.View; - -import androidx.annotation.Nullable; - -import org.dolphinemu.dolphinemu.databinding.ListItemSettingSwitchBinding; -import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.SwitchSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; -import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter; -import org.dolphinemu.dolphinemu.utils.DirectoryInitialization; - -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; - -public final class SwitchSettingViewHolder extends SettingViewHolder -{ - private SwitchSetting mItem; - private boolean iplExists = false; - - private final ListItemSettingSwitchBinding mBinding; - - public SwitchSettingViewHolder(ListItemSettingSwitchBinding binding, SettingsAdapter adapter) - { - super(binding.getRoot(), adapter); - mBinding = binding; - } - - @Override - public void bind(SettingsItem item) - { - mItem = (SwitchSetting) item; - - mBinding.textSettingName.setText(item.getName()); - mBinding.textSettingDescription.setText(item.getDescription()); - - mBinding.settingSwitch.setChecked(mItem.isChecked()); - mBinding.settingSwitch.setEnabled(mItem.isEditable()); - - // Check for IPL to make sure user can skip. - if (mItem.getSetting() == BooleanSetting.MAIN_SKIP_IPL) - { - ArrayList iplDirs = new ArrayList<>(Arrays.asList("USA", "JAP", "EUR")); - for (String dir : iplDirs) - { - File iplFile = new File(DirectoryInitialization.getUserDirectory(), - File.separator + "GC" + File.separator + dir + File.separator + "IPL.bin"); - if (iplFile.exists()) - { - iplExists = true; - break; - } - } - - mBinding.settingSwitch.setEnabled(iplExists || !mItem.isChecked()); - } - - mBinding.settingSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> - { - // If a user has skip IPL disabled previously and deleted their IPL file, we need to allow - // them to skip it or else their game will appear broken. However, once this is enabled, we - // need to disable the option again to prevent the same issue from occurring. - if (mItem.getSetting() == BooleanSetting.MAIN_SKIP_IPL && !iplExists && isChecked) - { - mBinding.settingSwitch.setEnabled(false); - } - - getAdapter().onBooleanClick(mItem, mBinding.settingSwitch.isChecked()); - - setStyle(mBinding.textSettingName, mItem); - }); - - setStyle(mBinding.textSettingName, mItem); - } - - @Override - public void onClick(View clicked) - { - if (!mItem.isEditable()) - { - showNotRuntimeEditableError(); - return; - } - - if (mItem.getSetting() == BooleanSetting.MAIN_SKIP_IPL && !iplExists) - { - if (mItem.isChecked()) - { - showIplNotAvailableError(); - return; - } - } - - mBinding.settingSwitch.toggle(); - } - - @Nullable @Override - protected SettingsItem getItem() - { - return mItem; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt new file mode 100644 index 0000000000..fc575ead48 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.ui.viewholder + +import android.view.View +import android.widget.CompoundButton +import org.dolphinemu.dolphinemu.databinding.ListItemSettingSwitchBinding +import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting +import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem +import org.dolphinemu.dolphinemu.features.settings.model.view.SwitchSetting +import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter +import org.dolphinemu.dolphinemu.utils.DirectoryInitialization +import java.io.File +import java.util.* + +class SwitchSettingViewHolder( + private val binding: ListItemSettingSwitchBinding, + adapter: SettingsAdapter +) : SettingViewHolder(binding.root, adapter) { + private lateinit var setting: SwitchSetting + + override val item: SettingsItem + get() = setting + + private var iplExists = false + + override fun bind(item: SettingsItem) { + setting = item as SwitchSetting + + binding.textSettingName.text = item.name + binding.textSettingDescription.text = item.description + + binding.settingSwitch.isChecked = setting.isChecked + binding.settingSwitch.isEnabled = setting.isEditable + + // Check for IPL to make sure user can skip. + if (setting.setting === BooleanSetting.MAIN_SKIP_IPL) { + val iplDirs = ArrayList(listOf("USA", "JAP", "EUR")) + for (dir in iplDirs) { + val iplFile = File( + DirectoryInitialization.getUserDirectory(), + File.separator + "GC" + File.separator + dir + File.separator + "IPL.bin" + ) + if (iplFile.exists()) { + iplExists = true + break + } + } + binding.settingSwitch.isEnabled = iplExists || !setting.isChecked + } + + binding.settingSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean -> + // If a user has skip IPL disabled previously and deleted their IPL file, we need to allow + // them to skip it or else their game will appear broken. However, once this is enabled, we + // need to disable the option again to prevent the same issue from occurring. + if (setting.setting === BooleanSetting.MAIN_SKIP_IPL && !iplExists && isChecked) { + binding.settingSwitch.isEnabled = false + } + + adapter.onBooleanClick(setting, binding.settingSwitch.isChecked) + + setStyle(binding.textSettingName, setting) + } + setStyle(binding.textSettingName, setting) + } + + override fun onClick(clicked: View) { + if (!setting.isEditable) { + showNotRuntimeEditableError() + return + } + + if (setting.setting === BooleanSetting.MAIN_SKIP_IPL && !iplExists) { + if (setting.isChecked) { + showIplNotAvailableError() + return + } + } + + binding.settingSwitch.toggle() + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/utils/SettingsFile.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/utils/SettingsFile.java deleted file mode 100644 index acad8c2d19..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/utils/SettingsFile.java +++ /dev/null @@ -1,131 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.utils; - -import androidx.annotation.NonNull; - -import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivityView; -import org.dolphinemu.dolphinemu.utils.BiMap; -import org.dolphinemu.dolphinemu.utils.DirectoryInitialization; -import org.dolphinemu.dolphinemu.utils.IniFile; -import org.dolphinemu.dolphinemu.utils.Log; - -import java.io.File; - -/** - * Contains static methods for interacting with .ini files in which settings are stored. - */ -public final class SettingsFile -{ - public static final String KEY_ISO_PATH_BASE = "ISOPath"; - public static final String KEY_ISO_PATHS = "ISOPaths"; - - private static BiMap sectionsMap = new BiMap<>(); - - static - { - sectionsMap.add("Hardware", "Video_Hardware"); - sectionsMap.add("Settings", "Video_Settings"); - sectionsMap.add("Enhancements", "Video_Enhancements"); - sectionsMap.add("Stereoscopy", "Video_Stereoscopy"); - sectionsMap.add("Hacks", "Video_Hacks"); - sectionsMap.add("GameSpecific", "Video"); - } - - private SettingsFile() - { - } - - /** - * Reads a given .ini file from disk and returns it. - * If unsuccessful, outputs an error telling why it failed. - * - * @param file The ini file to load the settings from - * @param ini The object to load into - * @param view The current view. - */ - static void readFile(final File file, IniFile ini, SettingsActivityView view) - { - if (!ini.load(file, true)) - { - Log.error("[SettingsFile] Error reading from: " + file.getAbsolutePath()); - if (view != null) - view.onSettingsFileNotFound(); - } - } - - public static void readFile(final String fileName, IniFile ini, SettingsActivityView view) - { - readFile(getSettingsFile(fileName), ini, view); - } - - /** - * Reads a given .ini file from disk and returns it. - * If unsuccessful, outputs an error telling why it failed. - * - * @param gameId the id of the game to load settings for. - * @param ini The object to load into - * @param view The current view. - */ - public static void readCustomGameSettings(final String gameId, IniFile ini, - SettingsActivityView view) - { - readFile(getCustomGameSettingsFile(gameId), ini, view); - } - - /** - * Saves a given .ini file on disk. - * If unsuccessful, outputs an error telling why it failed. - * - * @param fileName The target filename without a path or extension. - * @param ini The IniFile we want to serialize. - * @param view The current view. - */ - public static void saveFile(final String fileName, IniFile ini, SettingsActivityView view) - { - if (!ini.save(getSettingsFile(fileName))) - { - Log.error("[SettingsFile] Error saving to: " + fileName + ".ini"); - if (view != null) - view.showToastMessage("Error saving " + fileName + ".ini"); - } - } - - public static void saveCustomGameSettings(final String gameId, IniFile ini) - { - ini.save(getCustomGameSettingsFile(gameId)); - } - - public static String mapSectionNameFromIni(String generalSectionName) - { - if (sectionsMap.getForward(generalSectionName) != null) - { - return sectionsMap.getForward(generalSectionName); - } - - return generalSectionName; - } - - public static String mapSectionNameToIni(String generalSectionName) - { - if (sectionsMap.getBackward(generalSectionName) != null) - { - return sectionsMap.getBackward(generalSectionName); - } - - return generalSectionName; - } - - @NonNull - public static File getSettingsFile(String fileName) - { - return new File( - DirectoryInitialization.getUserDirectory() + "/Config/" + fileName + ".ini"); - } - - public static File getCustomGameSettingsFile(String gameId) - { - return new File( - DirectoryInitialization.getUserDirectory() + "/GameSettings/" + gameId + ".ini"); - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/utils/SettingsFile.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/utils/SettingsFile.kt new file mode 100644 index 0000000000..827f04cbf3 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/utils/SettingsFile.kt @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.utils + +import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivityView +import org.dolphinemu.dolphinemu.utils.BiMap +import org.dolphinemu.dolphinemu.utils.DirectoryInitialization +import org.dolphinemu.dolphinemu.utils.IniFile +import org.dolphinemu.dolphinemu.utils.Log +import java.io.File + +/** + * Contains static methods for interacting with .ini files in which settings are stored. + */ +object SettingsFile { + const val KEY_ISO_PATH_BASE = "ISOPath" + const val KEY_ISO_PATHS = "ISOPaths" + private val sectionsMap = BiMap() + + init { + sectionsMap.apply { + add("Hardware", "Video_Hardware") + add("Settings", "Video_Settings") + add("Enhancements", "Video_Enhancements") + add("Stereoscopy", "Video_Stereoscopy") + add("Hacks", "Video_Hacks") + add("GameSpecific", "Video") + } + } + + /** + * Reads a given .ini file from disk and returns it. + * If unsuccessful, outputs an error telling why it failed. + * + * @param file The ini file to load the settings from + * @param ini The object to load into + * @param view The current view. + */ + private fun readFile(file: File, ini: IniFile, view: SettingsActivityView) { + if (!ini.load(file, true)) { + Log.error("[SettingsFile] Error reading from: " + file.absolutePath) + view.onSettingsFileNotFound() + } + } + + fun readFile(fileName: String, ini: IniFile, view: SettingsActivityView) { + readFile(getSettingsFile(fileName), ini, view) + } + + /** + * Reads a given .ini file from disk and returns it. + * If unsuccessful, outputs an error telling why it failed. + * + * @param gameId the id of the game to load settings for. + * @param ini The object to load into + * @param view The current view. + */ + fun readCustomGameSettings( + gameId: String, + ini: IniFile, + view: SettingsActivityView + ) { + readFile(getCustomGameSettingsFile(gameId), ini, view) + } + + /** + * Saves a given .ini file on disk. + * If unsuccessful, outputs an error telling why it failed. + * + * @param fileName The target filename without a path or extension. + * @param ini The IniFile we want to serialize. + * @param view The current view. + */ + fun saveFile(fileName: String, ini: IniFile, view: SettingsActivityView) { + if (!ini.save(getSettingsFile(fileName))) { + Log.error("[SettingsFile] Error saving to: $fileName.ini") + view.showToastMessage("Error saving $fileName.ini") + } + } + + fun saveCustomGameSettings(gameId: String, ini: IniFile) { + ini.save(getCustomGameSettingsFile(gameId)) + } + + fun mapSectionNameFromIni(generalSectionName: String): String? { + return if (sectionsMap.getForward(generalSectionName) != null) { + sectionsMap.getForward(generalSectionName) + } else generalSectionName + } + + fun mapSectionNameToIni(generalSectionName: String): String? { + return if (sectionsMap.getBackward(generalSectionName) != null) { + sectionsMap.getBackward(generalSectionName) + } else generalSectionName + } + + @JvmStatic + fun getSettingsFile(fileName: String): File { + return File(DirectoryInitialization.getUserDirectory() + "/Config/" + fileName + ".ini") + } + + private fun getCustomGameSettingsFile(gameId: String): File { + return File( + DirectoryInitialization.getUserDirectory() + "/GameSettings/" + gameId + ".ini" + ) + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/SerializableHelper.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/SerializableHelper.kt new file mode 100644 index 0000000000..cad3b548ce --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/SerializableHelper.kt @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.utils + +import android.content.Intent +import android.os.Build +import android.os.Bundle +import java.io.Serializable + +object SerializableHelper { + inline fun Intent.serializable(key: String): T? { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) + getSerializableExtra(key, T::class.java) + else + getSerializableExtra(key) as T? + } + + inline fun Bundle.serializable(key: String): T? { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) + getSerializable(key, T::class.java) + else + getSerializable(key) as T? + } +}