miroir de https://git.suyu.dev/suyu/suyu.git
976 lignes
39 KiB
Kotlin
976 lignes
39 KiB
Kotlin
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
package dev.suyu.suyu_emu.features.settings.ui
|
|
|
|
import android.annotation.SuppressLint
|
|
import android.os.Build
|
|
import android.widget.Toast
|
|
import dev.suyu.suyu_emu.NativeLibrary
|
|
import dev.suyu.suyu_emu.R
|
|
import dev.suyu.suyu_emu.SuyuApplication
|
|
import dev.suyu.suyu_emu.features.input.NativeInput
|
|
import dev.suyu.suyu_emu.features.input.model.AnalogDirection
|
|
import dev.suyu.suyu_emu.features.input.model.NativeAnalog
|
|
import dev.suyu.suyu_emu.features.input.model.NativeButton
|
|
import dev.suyu.suyu_emu.features.input.model.NpadStyleIndex
|
|
import dev.suyu.suyu_emu.features.settings.model.AbstractBooleanSetting
|
|
import dev.suyu.suyu_emu.features.settings.model.AbstractIntSetting
|
|
import dev.suyu.suyu_emu.features.settings.model.BooleanSetting
|
|
import dev.suyu.suyu_emu.features.settings.model.ByteSetting
|
|
import dev.suyu.suyu_emu.features.settings.model.IntSetting
|
|
import dev.suyu.suyu_emu.features.settings.model.LongSetting
|
|
import dev.suyu.suyu_emu.features.settings.model.Settings
|
|
import dev.suyu.suyu_emu.features.settings.model.Settings.MenuTag
|
|
import dev.suyu.suyu_emu.features.settings.model.ShortSetting
|
|
import dev.suyu.suyu_emu.features.settings.model.StringSetting
|
|
import dev.suyu.suyu_emu.features.settings.model.view.*
|
|
import dev.suyu.suyu_emu.utils.InputHandler
|
|
import dev.suyu.suyu_emu.utils.NativeConfig
|
|
|
|
class SettingsFragmentPresenter(
|
|
private val settingsViewModel: SettingsViewModel,
|
|
private val adapter: SettingsAdapter,
|
|
private var menuTag: MenuTag
|
|
) {
|
|
private var settingsList = ArrayList<SettingsItem>()
|
|
|
|
private val context get() = SuyuApplication.appContext
|
|
|
|
// Extension for altering settings list based on each setting's properties
|
|
fun ArrayList<SettingsItem>.add(key: String) {
|
|
val item = SettingsItem.settingsItems[key]!!
|
|
if (settingsViewModel.game != null && !item.setting.isSwitchable) {
|
|
return
|
|
}
|
|
|
|
if (!NativeConfig.isPerGameConfigLoaded() && !NativeLibrary.isRunning()) {
|
|
item.setting.global = true
|
|
}
|
|
|
|
val pairedSettingKey = item.setting.pairedSettingKey
|
|
if (pairedSettingKey.isNotEmpty()) {
|
|
val pairedSettingValue = NativeConfig.getBoolean(
|
|
pairedSettingKey,
|
|
if (NativeLibrary.isRunning() && !NativeConfig.isPerGameConfigLoaded()) {
|
|
!NativeConfig.usingGlobal(pairedSettingKey)
|
|
} else {
|
|
NativeConfig.usingGlobal(pairedSettingKey)
|
|
}
|
|
)
|
|
if (!pairedSettingValue) return
|
|
}
|
|
add(item)
|
|
}
|
|
|
|
// Allows you to show/hide abstract settings based on the paired setting key
|
|
fun ArrayList<SettingsItem>.addAbstract(item: SettingsItem) {
|
|
val pairedSettingKey = item.setting.pairedSettingKey
|
|
if (pairedSettingKey.isNotEmpty()) {
|
|
val pairedSettingsItem =
|
|
this.firstOrNull { it.setting.key == pairedSettingKey } ?: return
|
|
val pairedSetting = pairedSettingsItem.setting as AbstractBooleanSetting
|
|
if (!pairedSetting.getBoolean(!NativeConfig.isPerGameConfigLoaded())) return
|
|
}
|
|
add(item)
|
|
}
|
|
|
|
fun onViewCreated() {
|
|
loadSettingsList()
|
|
}
|
|
|
|
@SuppressLint("NotifyDataSetChanged")
|
|
fun loadSettingsList(notifyDataSetChanged: Boolean = false) {
|
|
val sl = ArrayList<SettingsItem>()
|
|
when (menuTag) {
|
|
MenuTag.SECTION_ROOT -> addConfigSettings(sl)
|
|
MenuTag.SECTION_SYSTEM -> addSystemSettings(sl)
|
|
MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl)
|
|
MenuTag.SECTION_AUDIO -> addAudioSettings(sl)
|
|
MenuTag.SECTION_INPUT -> addInputSettings(sl)
|
|
MenuTag.SECTION_INPUT_PLAYER_ONE -> addInputPlayer(sl, 0)
|
|
MenuTag.SECTION_INPUT_PLAYER_TWO -> addInputPlayer(sl, 1)
|
|
MenuTag.SECTION_INPUT_PLAYER_THREE -> addInputPlayer(sl, 2)
|
|
MenuTag.SECTION_INPUT_PLAYER_FOUR -> addInputPlayer(sl, 3)
|
|
MenuTag.SECTION_INPUT_PLAYER_FIVE -> addInputPlayer(sl, 4)
|
|
MenuTag.SECTION_INPUT_PLAYER_SIX -> addInputPlayer(sl, 5)
|
|
MenuTag.SECTION_INPUT_PLAYER_SEVEN -> addInputPlayer(sl, 6)
|
|
MenuTag.SECTION_INPUT_PLAYER_EIGHT -> addInputPlayer(sl, 7)
|
|
MenuTag.SECTION_THEME -> addThemeSettings(sl)
|
|
MenuTag.SECTION_DEBUG -> addDebugSettings(sl)
|
|
}
|
|
settingsList = sl
|
|
adapter.submitList(settingsList) {
|
|
if (notifyDataSetChanged) {
|
|
adapter.notifyDataSetChanged()
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun addConfigSettings(sl: ArrayList<SettingsItem>) {
|
|
sl.apply {
|
|
add(
|
|
SubmenuSetting(
|
|
titleId = R.string.preferences_system,
|
|
descriptionId = R.string.preferences_system_description,
|
|
iconId = R.drawable.ic_system_settings,
|
|
menuKey = MenuTag.SECTION_SYSTEM
|
|
)
|
|
)
|
|
add(
|
|
SubmenuSetting(
|
|
titleId = R.string.preferences_graphics,
|
|
descriptionId = R.string.preferences_graphics_description,
|
|
iconId = R.drawable.ic_graphics,
|
|
menuKey = MenuTag.SECTION_RENDERER
|
|
)
|
|
)
|
|
add(
|
|
SubmenuSetting(
|
|
titleId = R.string.preferences_audio,
|
|
descriptionId = R.string.preferences_audio_description,
|
|
iconId = R.drawable.ic_audio,
|
|
menuKey = MenuTag.SECTION_AUDIO
|
|
)
|
|
)
|
|
add(
|
|
SubmenuSetting(
|
|
titleId = R.string.preferences_debug,
|
|
descriptionId = R.string.preferences_debug_description,
|
|
iconId = R.drawable.ic_code,
|
|
menuKey = MenuTag.SECTION_DEBUG
|
|
)
|
|
)
|
|
add(
|
|
RunnableSetting(
|
|
titleId = R.string.reset_to_default,
|
|
descriptionId = R.string.reset_to_default_description,
|
|
isRunnable = !NativeLibrary.isRunning(),
|
|
iconId = R.drawable.ic_restore
|
|
) { settingsViewModel.setShouldShowResetSettingsDialog(true) }
|
|
)
|
|
}
|
|
}
|
|
|
|
private fun addSystemSettings(sl: ArrayList<SettingsItem>) {
|
|
sl.apply {
|
|
add(StringSetting.DEVICE_NAME.key)
|
|
add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key)
|
|
add(ShortSetting.RENDERER_SPEED_LIMIT.key)
|
|
add(BooleanSetting.USE_DOCKED_MODE.key)
|
|
add(IntSetting.REGION_INDEX.key)
|
|
add(IntSetting.LANGUAGE_INDEX.key)
|
|
add(BooleanSetting.USE_CUSTOM_RTC.key)
|
|
add(LongSetting.CUSTOM_RTC.key)
|
|
}
|
|
}
|
|
|
|
private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) {
|
|
sl.apply {
|
|
add(IntSetting.RENDERER_ACCURACY.key)
|
|
add(IntSetting.RENDERER_RESOLUTION.key)
|
|
add(IntSetting.RENDERER_VSYNC.key)
|
|
add(IntSetting.RENDERER_SCALING_FILTER.key)
|
|
add(IntSetting.FSR_SHARPENING_SLIDER.key)
|
|
add(IntSetting.RENDERER_ANTI_ALIASING.key)
|
|
add(IntSetting.MAX_ANISOTROPY.key)
|
|
add(IntSetting.RENDERER_SCREEN_LAYOUT.key)
|
|
add(IntSetting.RENDERER_ASPECT_RATIO.key)
|
|
add(IntSetting.VERTICAL_ALIGNMENT.key)
|
|
add(BooleanSetting.PICTURE_IN_PICTURE.key)
|
|
add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key)
|
|
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
|
|
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
|
|
add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key)
|
|
}
|
|
}
|
|
|
|
private fun addAudioSettings(sl: ArrayList<SettingsItem>) {
|
|
sl.apply {
|
|
add(IntSetting.AUDIO_OUTPUT_ENGINE.key)
|
|
add(ByteSetting.AUDIO_VOLUME.key)
|
|
}
|
|
}
|
|
|
|
private fun addInputSettings(sl: ArrayList<SettingsItem>) {
|
|
settingsViewModel.currentDevice = 0
|
|
|
|
if (NativeConfig.isPerGameConfigLoaded()) {
|
|
NativeInput.loadInputProfiles()
|
|
val profiles = NativeInput.getInputProfileNames().toMutableList()
|
|
profiles.add(0, "")
|
|
val prettyProfiles = profiles.toTypedArray()
|
|
prettyProfiles[0] =
|
|
context.getString(R.string.use_global_input_configuration)
|
|
sl.apply {
|
|
for (i in 0 until 8) {
|
|
add(
|
|
IntSingleChoiceSetting(
|
|
getPerGameProfileSetting(profiles, i),
|
|
titleString = getPlayerProfileString(i + 1),
|
|
choices = prettyProfiles,
|
|
values = IntArray(profiles.size) { it }.toTypedArray()
|
|
)
|
|
)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
val getConnectedIcon: (Int) -> Int = { playerIndex: Int ->
|
|
if (NativeInput.getIsConnected(playerIndex)) {
|
|
R.drawable.ic_controller
|
|
} else {
|
|
R.drawable.ic_controller_disconnected
|
|
}
|
|
}
|
|
|
|
val inputSettings = NativeConfig.getInputSettings(true)
|
|
sl.apply {
|
|
add(
|
|
SubmenuSetting(
|
|
titleString = Settings.getPlayerString(1),
|
|
descriptionString = inputSettings[0].profileName,
|
|
menuKey = MenuTag.SECTION_INPUT_PLAYER_ONE,
|
|
iconId = getConnectedIcon(0)
|
|
)
|
|
)
|
|
add(
|
|
SubmenuSetting(
|
|
titleString = Settings.getPlayerString(2),
|
|
descriptionString = inputSettings[1].profileName,
|
|
menuKey = MenuTag.SECTION_INPUT_PLAYER_TWO,
|
|
iconId = getConnectedIcon(1)
|
|
)
|
|
)
|
|
add(
|
|
SubmenuSetting(
|
|
titleString = Settings.getPlayerString(3),
|
|
descriptionString = inputSettings[2].profileName,
|
|
menuKey = MenuTag.SECTION_INPUT_PLAYER_THREE,
|
|
iconId = getConnectedIcon(2)
|
|
)
|
|
)
|
|
add(
|
|
SubmenuSetting(
|
|
titleString = Settings.getPlayerString(4),
|
|
descriptionString = inputSettings[3].profileName,
|
|
menuKey = MenuTag.SECTION_INPUT_PLAYER_FOUR,
|
|
iconId = getConnectedIcon(3)
|
|
)
|
|
)
|
|
add(
|
|
SubmenuSetting(
|
|
titleString = Settings.getPlayerString(5),
|
|
descriptionString = inputSettings[4].profileName,
|
|
menuKey = MenuTag.SECTION_INPUT_PLAYER_FIVE,
|
|
iconId = getConnectedIcon(4)
|
|
)
|
|
)
|
|
add(
|
|
SubmenuSetting(
|
|
titleString = Settings.getPlayerString(6),
|
|
descriptionString = inputSettings[5].profileName,
|
|
menuKey = MenuTag.SECTION_INPUT_PLAYER_SIX,
|
|
iconId = getConnectedIcon(5)
|
|
)
|
|
)
|
|
add(
|
|
SubmenuSetting(
|
|
titleString = Settings.getPlayerString(7),
|
|
descriptionString = inputSettings[6].profileName,
|
|
menuKey = MenuTag.SECTION_INPUT_PLAYER_SEVEN,
|
|
iconId = getConnectedIcon(6)
|
|
)
|
|
)
|
|
add(
|
|
SubmenuSetting(
|
|
titleString = Settings.getPlayerString(8),
|
|
descriptionString = inputSettings[7].profileName,
|
|
menuKey = MenuTag.SECTION_INPUT_PLAYER_EIGHT,
|
|
iconId = getConnectedIcon(7)
|
|
)
|
|
)
|
|
}
|
|
}
|
|
|
|
private fun getPlayerProfileString(player: Int): String =
|
|
context.getString(R.string.player_num_profile, player)
|
|
|
|
private fun getPerGameProfileSetting(
|
|
profiles: List<String>,
|
|
playerIndex: Int
|
|
): AbstractIntSetting {
|
|
return object : AbstractIntSetting {
|
|
private val players
|
|
get() = NativeConfig.getInputSettings(false)
|
|
|
|
override val key = ""
|
|
|
|
override fun getInt(needsGlobal: Boolean): Int {
|
|
val currentProfile = players[playerIndex].profileName
|
|
profiles.forEachIndexed { i, profile ->
|
|
if (profile == currentProfile) {
|
|
return i
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
override fun setInt(value: Int) {
|
|
NativeInput.loadPerGameConfiguration(playerIndex, value, profiles[value])
|
|
NativeInput.connectControllers(playerIndex)
|
|
NativeConfig.saveControlPlayerValues()
|
|
}
|
|
|
|
override val defaultValue = 0
|
|
|
|
override fun getValueAsString(needsGlobal: Boolean): String = getInt().toString()
|
|
|
|
override fun reset() = setInt(defaultValue)
|
|
|
|
override var global = true
|
|
|
|
override val isRuntimeModifiable = true
|
|
|
|
override val isSaveable = true
|
|
}
|
|
}
|
|
|
|
private fun addInputPlayer(sl: ArrayList<SettingsItem>, playerIndex: Int) {
|
|
sl.apply {
|
|
val connectedSetting = object : AbstractBooleanSetting {
|
|
override val key = "connected"
|
|
|
|
override fun getBoolean(needsGlobal: Boolean): Boolean =
|
|
NativeInput.getIsConnected(playerIndex)
|
|
|
|
override fun setBoolean(value: Boolean) =
|
|
NativeInput.connectControllers(playerIndex, value)
|
|
|
|
override val defaultValue = playerIndex == 0
|
|
|
|
override fun getValueAsString(needsGlobal: Boolean): String =
|
|
getBoolean(needsGlobal).toString()
|
|
|
|
override fun reset() = setBoolean(defaultValue)
|
|
}
|
|
add(SwitchSetting(connectedSetting, R.string.connected))
|
|
|
|
val styleTags = NativeInput.getSupportedStyleTags(playerIndex)
|
|
val npadType = object : AbstractIntSetting {
|
|
override val key = "npad_type"
|
|
override fun getInt(needsGlobal: Boolean): Int {
|
|
val styleIndex = NativeInput.getStyleIndex(playerIndex)
|
|
return styleTags.indexOfFirst { it == styleIndex }
|
|
}
|
|
|
|
override fun setInt(value: Int) {
|
|
NativeInput.setStyleIndex(playerIndex, styleTags[value])
|
|
settingsViewModel.setReloadListAndNotifyDataset(true)
|
|
}
|
|
|
|
override val defaultValue = NpadStyleIndex.Fullkey.int
|
|
override fun getValueAsString(needsGlobal: Boolean): String = getInt().toString()
|
|
override fun reset() = setInt(defaultValue)
|
|
override val pairedSettingKey: String = "connected"
|
|
}
|
|
addAbstract(
|
|
IntSingleChoiceSetting(
|
|
npadType,
|
|
titleId = R.string.controller_type,
|
|
choices = styleTags.map { context.getString(it.nameId) }
|
|
.toTypedArray(),
|
|
values = IntArray(styleTags.size) { it }.toTypedArray()
|
|
)
|
|
)
|
|
|
|
InputHandler.updateControllerData()
|
|
|
|
val autoMappingSetting = object : AbstractIntSetting {
|
|
override val key = "auto_mapping_device"
|
|
|
|
override fun getInt(needsGlobal: Boolean): Int = -1
|
|
|
|
override fun setInt(value: Int) {
|
|
val registeredController = InputHandler.registeredControllers[value + 1]
|
|
val displayName = registeredController.get(
|
|
"display",
|
|
context.getString(R.string.unknown)
|
|
)
|
|
NativeInput.updateMappingsWithDefault(
|
|
playerIndex,
|
|
registeredController,
|
|
displayName
|
|
)
|
|
Toast.makeText(
|
|
context,
|
|
context.getString(R.string.attempted_auto_map, displayName),
|
|
Toast.LENGTH_SHORT
|
|
).show()
|
|
settingsViewModel.setReloadListAndNotifyDataset(true)
|
|
}
|
|
|
|
override val defaultValue = -1
|
|
|
|
override fun getValueAsString(needsGlobal: Boolean) = getInt().toString()
|
|
|
|
override fun reset() = setInt(defaultValue)
|
|
|
|
override val isRuntimeModifiable: Boolean = true
|
|
}
|
|
|
|
val unknownString = context.getString(R.string.unknown)
|
|
val prettyAutoMappingControllerList = InputHandler.registeredControllers.mapNotNull {
|
|
val port = it.get("port", -1)
|
|
return@mapNotNull if (port == 100 || port == -1) {
|
|
null
|
|
} else {
|
|
it.get("display", unknownString)
|
|
}
|
|
}.toTypedArray()
|
|
add(
|
|
IntSingleChoiceSetting(
|
|
autoMappingSetting,
|
|
titleId = R.string.auto_map,
|
|
descriptionId = R.string.auto_map_description,
|
|
choices = prettyAutoMappingControllerList,
|
|
values = IntArray(prettyAutoMappingControllerList.size) { it }.toTypedArray()
|
|
)
|
|
)
|
|
|
|
val mappingFilterSetting = object : AbstractIntSetting {
|
|
override val key = "mapping_filter"
|
|
|
|
override fun getInt(needsGlobal: Boolean): Int = settingsViewModel.currentDevice
|
|
|
|
override fun setInt(value: Int) {
|
|
settingsViewModel.currentDevice = value
|
|
}
|
|
|
|
override val defaultValue = 0
|
|
|
|
override fun getValueAsString(needsGlobal: Boolean) = getInt().toString()
|
|
|
|
override fun reset() = setInt(defaultValue)
|
|
|
|
override val isRuntimeModifiable: Boolean = true
|
|
}
|
|
|
|
val prettyControllerList = InputHandler.registeredControllers.mapNotNull {
|
|
return@mapNotNull if (it.get("port", 0) == 100) {
|
|
null
|
|
} else {
|
|
it.get("display", unknownString)
|
|
}
|
|
}.toTypedArray()
|
|
add(
|
|
IntSingleChoiceSetting(
|
|
mappingFilterSetting,
|
|
titleId = R.string.input_mapping_filter,
|
|
descriptionId = R.string.input_mapping_filter_description,
|
|
choices = prettyControllerList,
|
|
values = IntArray(prettyControllerList.size) { it }.toTypedArray()
|
|
)
|
|
)
|
|
|
|
add(InputProfileSetting(playerIndex))
|
|
add(
|
|
RunnableSetting(titleId = R.string.reset_to_default, isRunnable = true) {
|
|
settingsViewModel.setShouldShowResetInputDialog(true)
|
|
}
|
|
)
|
|
|
|
val styleIndex = NativeInput.getStyleIndex(playerIndex)
|
|
|
|
// Buttons
|
|
when (styleIndex) {
|
|
NpadStyleIndex.Fullkey,
|
|
NpadStyleIndex.Handheld,
|
|
NpadStyleIndex.JoyconDual -> {
|
|
add(HeaderSetting(R.string.buttons))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.A, R.string.button_a))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.B, R.string.button_b))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.X, R.string.button_x))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.Y, R.string.button_y))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.Plus, R.string.button_plus))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.Minus, R.string.button_minus))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.Home, R.string.button_home))
|
|
add(
|
|
ButtonInputSetting(
|
|
playerIndex,
|
|
NativeButton.Capture,
|
|
R.string.button_capture
|
|
)
|
|
)
|
|
}
|
|
|
|
NpadStyleIndex.JoyconLeft -> {
|
|
add(HeaderSetting(R.string.buttons))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.Minus, R.string.button_minus))
|
|
add(
|
|
ButtonInputSetting(
|
|
playerIndex,
|
|
NativeButton.Capture,
|
|
R.string.button_capture
|
|
)
|
|
)
|
|
}
|
|
|
|
NpadStyleIndex.JoyconRight -> {
|
|
add(HeaderSetting(R.string.buttons))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.A, R.string.button_a))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.B, R.string.button_b))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.X, R.string.button_x))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.Y, R.string.button_y))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.Plus, R.string.button_plus))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.Home, R.string.button_home))
|
|
}
|
|
|
|
NpadStyleIndex.GameCube -> {
|
|
add(HeaderSetting(R.string.buttons))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.A, R.string.button_a))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.B, R.string.button_b))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.X, R.string.button_x))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.Y, R.string.button_y))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.Plus, R.string.start_pause))
|
|
}
|
|
|
|
else -> {
|
|
// No-op
|
|
}
|
|
}
|
|
|
|
when (styleIndex) {
|
|
NpadStyleIndex.Fullkey,
|
|
NpadStyleIndex.Handheld,
|
|
NpadStyleIndex.JoyconDual,
|
|
NpadStyleIndex.JoyconLeft -> {
|
|
add(HeaderSetting(R.string.dpad))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.DUp, R.string.up))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.DDown, R.string.down))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.DLeft, R.string.left))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.DRight, R.string.right))
|
|
}
|
|
|
|
else -> {
|
|
// No-op
|
|
}
|
|
}
|
|
|
|
// Left stick
|
|
when (styleIndex) {
|
|
NpadStyleIndex.Fullkey,
|
|
NpadStyleIndex.Handheld,
|
|
NpadStyleIndex.JoyconDual,
|
|
NpadStyleIndex.JoyconLeft -> {
|
|
add(HeaderSetting(R.string.left_stick))
|
|
addAll(getStickDirections(playerIndex, NativeAnalog.LStick))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.LStick, R.string.pressed))
|
|
addAll(getExtraStickSettings(playerIndex, NativeAnalog.LStick))
|
|
}
|
|
|
|
NpadStyleIndex.GameCube -> {
|
|
add(HeaderSetting(R.string.control_stick))
|
|
addAll(getStickDirections(playerIndex, NativeAnalog.LStick))
|
|
addAll(getExtraStickSettings(playerIndex, NativeAnalog.LStick))
|
|
}
|
|
|
|
else -> {
|
|
// No-op
|
|
}
|
|
}
|
|
|
|
// Right stick
|
|
when (styleIndex) {
|
|
NpadStyleIndex.Fullkey,
|
|
NpadStyleIndex.Handheld,
|
|
NpadStyleIndex.JoyconDual,
|
|
NpadStyleIndex.JoyconRight -> {
|
|
add(HeaderSetting(R.string.right_stick))
|
|
addAll(getStickDirections(playerIndex, NativeAnalog.RStick))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.RStick, R.string.pressed))
|
|
addAll(getExtraStickSettings(playerIndex, NativeAnalog.RStick))
|
|
}
|
|
|
|
NpadStyleIndex.GameCube -> {
|
|
add(HeaderSetting(R.string.c_stick))
|
|
addAll(getStickDirections(playerIndex, NativeAnalog.RStick))
|
|
addAll(getExtraStickSettings(playerIndex, NativeAnalog.RStick))
|
|
}
|
|
|
|
else -> {
|
|
// No-op
|
|
}
|
|
}
|
|
|
|
// L/R, ZL/ZR, and SL/SR
|
|
when (styleIndex) {
|
|
NpadStyleIndex.Fullkey,
|
|
NpadStyleIndex.Handheld -> {
|
|
add(HeaderSetting(R.string.triggers))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.L, R.string.button_l))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.R, R.string.button_r))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.ZL, R.string.button_zl))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.ZR, R.string.button_zr))
|
|
}
|
|
|
|
NpadStyleIndex.JoyconDual -> {
|
|
add(HeaderSetting(R.string.triggers))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.L, R.string.button_l))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.R, R.string.button_r))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.ZL, R.string.button_zl))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.ZR, R.string.button_zr))
|
|
add(
|
|
ButtonInputSetting(
|
|
playerIndex,
|
|
NativeButton.SLLeft,
|
|
R.string.button_sl_left
|
|
)
|
|
)
|
|
add(
|
|
ButtonInputSetting(
|
|
playerIndex,
|
|
NativeButton.SRLeft,
|
|
R.string.button_sr_left
|
|
)
|
|
)
|
|
add(
|
|
ButtonInputSetting(
|
|
playerIndex,
|
|
NativeButton.SLRight,
|
|
R.string.button_sl_right
|
|
)
|
|
)
|
|
add(
|
|
ButtonInputSetting(
|
|
playerIndex,
|
|
NativeButton.SRRight,
|
|
R.string.button_sr_right
|
|
)
|
|
)
|
|
}
|
|
|
|
NpadStyleIndex.JoyconLeft -> {
|
|
add(HeaderSetting(R.string.triggers))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.L, R.string.button_l))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.ZL, R.string.button_zl))
|
|
add(
|
|
ButtonInputSetting(
|
|
playerIndex,
|
|
NativeButton.SLLeft,
|
|
R.string.button_sl_left
|
|
)
|
|
)
|
|
add(
|
|
ButtonInputSetting(
|
|
playerIndex,
|
|
NativeButton.SRLeft,
|
|
R.string.button_sr_left
|
|
)
|
|
)
|
|
}
|
|
|
|
NpadStyleIndex.JoyconRight -> {
|
|
add(HeaderSetting(R.string.triggers))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.R, R.string.button_r))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.ZR, R.string.button_zr))
|
|
add(
|
|
ButtonInputSetting(
|
|
playerIndex,
|
|
NativeButton.SLRight,
|
|
R.string.button_sl_right
|
|
)
|
|
)
|
|
add(
|
|
ButtonInputSetting(
|
|
playerIndex,
|
|
NativeButton.SRRight,
|
|
R.string.button_sr_right
|
|
)
|
|
)
|
|
}
|
|
|
|
NpadStyleIndex.GameCube -> {
|
|
add(HeaderSetting(R.string.triggers))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.R, R.string.button_z))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.ZL, R.string.button_l))
|
|
add(ButtonInputSetting(playerIndex, NativeButton.ZR, R.string.button_r))
|
|
}
|
|
|
|
else -> {
|
|
// No-op
|
|
}
|
|
}
|
|
|
|
add(HeaderSetting(R.string.vibration))
|
|
val vibrationEnabledSetting = object : AbstractBooleanSetting {
|
|
override val key = "vibration"
|
|
|
|
override fun getBoolean(needsGlobal: Boolean): Boolean =
|
|
NativeConfig.getInputSettings(true)[playerIndex].vibrationEnabled
|
|
|
|
override fun setBoolean(value: Boolean) {
|
|
val settings = NativeConfig.getInputSettings(true)
|
|
settings[playerIndex].vibrationEnabled = value
|
|
NativeConfig.setInputSettings(settings, true)
|
|
}
|
|
|
|
override val defaultValue = true
|
|
|
|
override fun getValueAsString(needsGlobal: Boolean): String =
|
|
getBoolean(needsGlobal).toString()
|
|
|
|
override fun reset() = setBoolean(defaultValue)
|
|
}
|
|
add(SwitchSetting(vibrationEnabledSetting, R.string.vibration))
|
|
|
|
val useSystemVibratorSetting = object : AbstractBooleanSetting {
|
|
override val key = ""
|
|
|
|
override fun getBoolean(needsGlobal: Boolean): Boolean =
|
|
NativeConfig.getInputSettings(true)[playerIndex].useSystemVibrator
|
|
|
|
override fun setBoolean(value: Boolean) {
|
|
val settings = NativeConfig.getInputSettings(true)
|
|
settings[playerIndex].useSystemVibrator = value
|
|
NativeConfig.setInputSettings(settings, true)
|
|
}
|
|
|
|
override val defaultValue = playerIndex == 0
|
|
|
|
override fun getValueAsString(needsGlobal: Boolean): String =
|
|
getBoolean(needsGlobal).toString()
|
|
|
|
override fun reset() = setBoolean(defaultValue)
|
|
|
|
override val pairedSettingKey: String = "vibration"
|
|
}
|
|
addAbstract(SwitchSetting(useSystemVibratorSetting, R.string.use_system_vibrator))
|
|
|
|
val vibrationStrengthSetting = object : AbstractIntSetting {
|
|
override val key = ""
|
|
|
|
override fun getInt(needsGlobal: Boolean): Int =
|
|
NativeConfig.getInputSettings(true)[playerIndex].vibrationStrength
|
|
|
|
override fun setInt(value: Int) {
|
|
val settings = NativeConfig.getInputSettings(true)
|
|
settings[playerIndex].vibrationStrength = value
|
|
NativeConfig.setInputSettings(settings, true)
|
|
}
|
|
|
|
override val defaultValue = 100
|
|
|
|
override fun getValueAsString(needsGlobal: Boolean): String =
|
|
getInt(needsGlobal).toString()
|
|
|
|
override fun reset() = setInt(defaultValue)
|
|
|
|
override val pairedSettingKey: String = "vibration"
|
|
}
|
|
addAbstract(
|
|
SliderSetting(vibrationStrengthSetting, R.string.vibration_strength, units = "%")
|
|
)
|
|
}
|
|
}
|
|
|
|
// Convenience function for creating AbstractIntSettings for modifier range/stick range/stick deadzones
|
|
private fun getStickIntSettingFromParam(
|
|
playerIndex: Int,
|
|
paramName: String,
|
|
stick: NativeAnalog,
|
|
defaultValue: Float
|
|
): AbstractIntSetting =
|
|
object : AbstractIntSetting {
|
|
val params get() = NativeInput.getStickParam(playerIndex, stick)
|
|
|
|
override val key = ""
|
|
|
|
override fun getInt(needsGlobal: Boolean): Int =
|
|
(params.get(paramName, defaultValue) * 100).toInt()
|
|
|
|
override fun setInt(value: Int) {
|
|
val tempParams = params
|
|
tempParams.set(paramName, value.toFloat() / 100)
|
|
NativeInput.setStickParam(playerIndex, stick, tempParams)
|
|
}
|
|
|
|
override val defaultValue = (defaultValue * 100).toInt()
|
|
|
|
override fun getValueAsString(needsGlobal: Boolean): String =
|
|
getInt(needsGlobal).toString()
|
|
|
|
override fun reset() = setInt(this.defaultValue)
|
|
}
|
|
|
|
private fun getExtraStickSettings(
|
|
playerIndex: Int,
|
|
nativeAnalog: NativeAnalog
|
|
): List<SettingsItem> {
|
|
val stickIsController =
|
|
NativeInput.isController(NativeInput.getStickParam(playerIndex, nativeAnalog))
|
|
val modifierRangeSetting =
|
|
getStickIntSettingFromParam(playerIndex, "modifier_scale", nativeAnalog, 0.5f)
|
|
val stickRangeSetting =
|
|
getStickIntSettingFromParam(playerIndex, "range", nativeAnalog, 0.95f)
|
|
val stickDeadzoneSetting =
|
|
getStickIntSettingFromParam(playerIndex, "deadzone", nativeAnalog, 0.15f)
|
|
|
|
val out = mutableListOf<SettingsItem>().apply {
|
|
if (stickIsController) {
|
|
add(SliderSetting(stickRangeSetting, titleId = R.string.range, min = 25, max = 150))
|
|
add(SliderSetting(stickDeadzoneSetting, R.string.deadzone))
|
|
} else {
|
|
add(ModifierInputSetting(playerIndex, NativeAnalog.LStick, R.string.modifier))
|
|
add(SliderSetting(modifierRangeSetting, R.string.modifier_range))
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
private fun getStickDirections(player: Int, stick: NativeAnalog): List<AnalogInputSetting> =
|
|
listOf(
|
|
AnalogInputSetting(
|
|
player,
|
|
stick,
|
|
AnalogDirection.Up,
|
|
R.string.up
|
|
),
|
|
AnalogInputSetting(
|
|
player,
|
|
stick,
|
|
AnalogDirection.Down,
|
|
R.string.down
|
|
),
|
|
AnalogInputSetting(
|
|
player,
|
|
stick,
|
|
AnalogDirection.Left,
|
|
R.string.left
|
|
),
|
|
AnalogInputSetting(
|
|
player,
|
|
stick,
|
|
AnalogDirection.Right,
|
|
R.string.right
|
|
)
|
|
)
|
|
|
|
private fun addThemeSettings(sl: ArrayList<SettingsItem>) {
|
|
sl.apply {
|
|
val theme: AbstractIntSetting = object : AbstractIntSetting {
|
|
override fun getInt(needsGlobal: Boolean): Int = IntSetting.THEME.getInt()
|
|
override fun setInt(value: Int) {
|
|
IntSetting.THEME.setInt(value)
|
|
settingsViewModel.setShouldRecreate(true)
|
|
}
|
|
|
|
override val key: String = IntSetting.THEME.key
|
|
override val isRuntimeModifiable: Boolean = IntSetting.THEME.isRuntimeModifiable
|
|
override fun getValueAsString(needsGlobal: Boolean): String =
|
|
IntSetting.THEME.getValueAsString()
|
|
|
|
override val defaultValue: Int = IntSetting.THEME.defaultValue
|
|
override fun reset() = IntSetting.THEME.setInt(defaultValue)
|
|
}
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
add(
|
|
SingleChoiceSetting(
|
|
theme,
|
|
titleId = R.string.change_app_theme,
|
|
choicesId = R.array.themeEntriesA12,
|
|
valuesId = R.array.themeValuesA12
|
|
)
|
|
)
|
|
} else {
|
|
add(
|
|
SingleChoiceSetting(
|
|
theme,
|
|
titleId = R.string.change_app_theme,
|
|
choicesId = R.array.themeEntries,
|
|
valuesId = R.array.themeValues
|
|
)
|
|
)
|
|
}
|
|
|
|
val themeMode: AbstractIntSetting = object : AbstractIntSetting {
|
|
override fun getInt(needsGlobal: Boolean): Int = IntSetting.THEME_MODE.getInt()
|
|
override fun setInt(value: Int) {
|
|
IntSetting.THEME_MODE.setInt(value)
|
|
settingsViewModel.setShouldRecreate(true)
|
|
}
|
|
|
|
override val key: String = IntSetting.THEME_MODE.key
|
|
override val isRuntimeModifiable: Boolean =
|
|
IntSetting.THEME_MODE.isRuntimeModifiable
|
|
|
|
override fun getValueAsString(needsGlobal: Boolean): String =
|
|
IntSetting.THEME_MODE.getValueAsString()
|
|
|
|
override val defaultValue: Int = IntSetting.THEME_MODE.defaultValue
|
|
override fun reset() {
|
|
IntSetting.THEME_MODE.setInt(defaultValue)
|
|
settingsViewModel.setShouldRecreate(true)
|
|
}
|
|
}
|
|
|
|
add(
|
|
SingleChoiceSetting(
|
|
themeMode,
|
|
titleId = R.string.change_theme_mode,
|
|
choicesId = R.array.themeModeEntries,
|
|
valuesId = R.array.themeModeValues
|
|
)
|
|
)
|
|
|
|
val blackBackgrounds: AbstractBooleanSetting = object : AbstractBooleanSetting {
|
|
override fun getBoolean(needsGlobal: Boolean): Boolean =
|
|
BooleanSetting.BLACK_BACKGROUNDS.getBoolean()
|
|
|
|
override fun setBoolean(value: Boolean) {
|
|
BooleanSetting.BLACK_BACKGROUNDS.setBoolean(value)
|
|
settingsViewModel.setShouldRecreate(true)
|
|
}
|
|
|
|
override val key: String = BooleanSetting.BLACK_BACKGROUNDS.key
|
|
override val isRuntimeModifiable: Boolean =
|
|
BooleanSetting.BLACK_BACKGROUNDS.isRuntimeModifiable
|
|
|
|
override fun getValueAsString(needsGlobal: Boolean): String =
|
|
BooleanSetting.BLACK_BACKGROUNDS.getValueAsString()
|
|
|
|
override val defaultValue: Boolean = BooleanSetting.BLACK_BACKGROUNDS.defaultValue
|
|
override fun reset() {
|
|
BooleanSetting.BLACK_BACKGROUNDS
|
|
.setBoolean(BooleanSetting.BLACK_BACKGROUNDS.defaultValue)
|
|
settingsViewModel.setShouldRecreate(true)
|
|
}
|
|
}
|
|
|
|
add(
|
|
SwitchSetting(
|
|
blackBackgrounds,
|
|
titleId = R.string.use_black_backgrounds,
|
|
descriptionId = R.string.use_black_backgrounds_description
|
|
)
|
|
)
|
|
}
|
|
}
|
|
|
|
private fun addDebugSettings(sl: ArrayList<SettingsItem>) {
|
|
sl.apply {
|
|
add(HeaderSetting(R.string.gpu))
|
|
add(IntSetting.RENDERER_BACKEND.key)
|
|
add(BooleanSetting.RENDERER_DEBUG.key)
|
|
|
|
add(HeaderSetting(R.string.cpu))
|
|
add(IntSetting.CPU_BACKEND.key)
|
|
add(IntSetting.CPU_ACCURACY.key)
|
|
add(BooleanSetting.CPU_DEBUG_MODE.key)
|
|
add(SettingsItem.FASTMEM_COMBINED)
|
|
}
|
|
}
|
|
}
|