Merge 8ccedb9bc2cfaef5d1aca8b05c59248feca376ec into 42d77cd720eb42845c2afb77c6d7157e02c8c325

This commit is contained in:
Kleidis 2025-03-13 02:34:57 -03:00 committed by GitHub
commit 51a0d5ce37
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 132 additions and 43 deletions

View File

@ -12,7 +12,8 @@ class DateTimeSetting(
titleId: Int, titleId: Int,
descriptionId: Int, descriptionId: Int,
val key: String? = null, val key: String? = null,
private val defaultValue: String? = null private val defaultValue: String? = null,
override var isEnabled: Boolean = true
) : SettingsItem(setting, titleId, descriptionId) { ) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_DATETIME_SETTING override val type = TYPE_DATETIME_SETTING

View File

@ -28,6 +28,8 @@ abstract class SettingsItem(
return setting?.isRuntimeEditable ?: false return setting?.isRuntimeEditable ?: false
} }
open var isEnabled: Boolean = true
companion object { companion object {
const val TYPE_HEADER = 0 const val TYPE_HEADER = 0
const val TYPE_SWITCH = 1 const val TYPE_SWITCH = 1

View File

@ -15,7 +15,8 @@ class SingleChoiceSetting(
val choicesId: Int, val choicesId: Int,
val valuesId: Int, val valuesId: Int,
val key: String? = null, val key: String? = null,
val defaultValue: Int? = null val defaultValue: Int? = null,
override var isEnabled: Boolean = true
) : SettingsItem(setting, titleId, descriptionId) { ) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_SINGLE_CHOICE override val type = TYPE_SINGLE_CHOICE

View File

@ -20,7 +20,8 @@ class SliderSetting(
val max: Int, val max: Int,
val units: String, val units: String,
val key: String? = null, val key: String? = null,
val defaultValue: Float? = null val defaultValue: Float? = null,
override var isEnabled: Boolean = true
) : SettingsItem(setting, titleId, descriptionId) { ) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_SLIDER override val type = TYPE_SLIDER
val selectedFloat: Float val selectedFloat: Float

View File

@ -12,7 +12,8 @@ class StringInputSetting(
titleId: Int, titleId: Int,
descriptionId: Int, descriptionId: Int,
val defaultValue: String, val defaultValue: String,
val characterLimit: Int = 0 val characterLimit: Int = 0,
override var isEnabled: Boolean = true
) : SettingsItem(setting, titleId, descriptionId) { ) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_STRING_INPUT override val type = TYPE_STRING_INPUT

View File

@ -15,7 +15,8 @@ class StringSingleChoiceSetting(
val choices: Array<String>, val choices: Array<String>,
val values: Array<String>?, val values: Array<String>?,
val key: String? = null, val key: String? = null,
private val defaultValue: String? = null private val defaultValue: String? = null,
override var isEnabled: Boolean = true
) : SettingsItem(setting, titleId, descriptionId) { ) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_STRING_SINGLE_CHOICE override val type = TYPE_STRING_SINGLE_CHOICE

View File

@ -13,7 +13,8 @@ class SwitchSetting(
titleId: Int, titleId: Int,
descriptionId: Int, descriptionId: Int,
val key: String? = null, val key: String? = null,
val defaultValue: Any? = null val defaultValue: Any? = null,
override var isEnabled: Boolean = true
) : SettingsItem(setting, titleId, descriptionId) { ) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_SWITCH override val type = TYPE_SWITCH

View File

@ -7,7 +7,6 @@ package org.citra.citra_emu.features.settings.ui
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.DialogInterface import android.content.DialogInterface
import android.graphics.Color
import android.icu.util.Calendar import android.icu.util.Calendar
import android.icu.util.TimeZone import android.icu.util.TimeZone
import android.text.Editable import android.text.Editable
@ -17,11 +16,11 @@ import android.text.TextWatcher
import android.text.format.DateFormat import android.text.format.DateFormat
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.EditText
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.widget.doOnTextChanged import androidx.core.widget.doOnTextChanged
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.datepicker.MaterialDatePicker import com.google.android.material.datepicker.MaterialDatePicker
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
@ -66,7 +65,6 @@ import org.citra.citra_emu.features.settings.ui.viewholder.SwitchSettingViewHold
import org.citra.citra_emu.fragments.MessageDialogFragment import org.citra.citra_emu.fragments.MessageDialogFragment
import org.citra.citra_emu.fragments.MotionBottomSheetDialogFragment import org.citra.citra_emu.fragments.MotionBottomSheetDialogFragment
import org.citra.citra_emu.utils.SystemSaveGame import org.citra.citra_emu.utils.SystemSaveGame
import java.lang.IllegalStateException
import java.lang.NumberFormatException import java.lang.NumberFormatException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -153,15 +151,71 @@ class SettingsAdapter(
return getItem(position)?.type ?: -1 return getItem(position)?.type ?: -1
} }
fun setSettingsList(settings: ArrayList<SettingsItem>?) { fun setSettingsList(newSettings: ArrayList<SettingsItem>?) {
this.settings = settings ?: arrayListOf() if (settings == null) {
settings = newSettings ?: arrayListOf()
notifyDataSetChanged() notifyDataSetChanged()
return
}
val oldSettings = settings
val diffResult = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
override fun getOldListSize() = oldSettings?.size ?: 0
override fun getNewListSize() = newSettings?.size ?: 0
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldSettings?.get(oldItemPosition)?.setting
val newItem = newSettings?.get(newItemPosition)?.setting
return oldItem?.key == newItem?.key
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldSettings?.get(oldItemPosition)
val newItem = newSettings?.get(newItemPosition)
if (oldItem == null || newItem == null || oldItem.type != newItem.type) {
return false
}
return when (oldItem.type) {
SettingsItem.TYPE_SLIDER -> {
(oldItem as SliderSetting).isEnabled == (newItem as SliderSetting).isEnabled
}
SettingsItem.TYPE_SWITCH -> {
(oldItem as SwitchSetting).isEnabled == (newItem as SwitchSetting).isEnabled
}
SettingsItem.TYPE_SINGLE_CHOICE -> {
(oldItem as SingleChoiceSetting).isEnabled == (newItem as SingleChoiceSetting).isEnabled
}
SettingsItem.TYPE_DATETIME_SETTING -> {
(oldItem as DateTimeSetting).isEnabled == (newItem as DateTimeSetting).isEnabled
}
SettingsItem.TYPE_STRING_SINGLE_CHOICE -> {
(oldItem as StringSingleChoiceSetting).isEnabled == (newItem as StringSingleChoiceSetting).isEnabled
}
SettingsItem.TYPE_STRING_INPUT -> {
(oldItem as StringInputSetting).isEnabled == (newItem as StringInputSetting).isEnabled
}
else -> {
oldItem == newItem
}
}
}
})
settings = newSettings ?: arrayListOf()
diffResult.dispatchUpdatesTo(this)
} }
fun onBooleanClick(item: SwitchSetting, position: Int, checked: Boolean) { fun onBooleanClick(item: SwitchSetting, position: Int, checked: Boolean) {
val setting = item.setChecked(checked) val setting = item.setChecked(checked)
fragmentView.putSetting(setting) fragmentView.putSetting(setting)
fragmentView.onSettingChanged() fragmentView.onSettingChanged()
// If statement is required otherwise the app will crash on activity recreate ex. theme settings
if (fragmentView.activityView != null)
// Reload the settings list to update the UI
fragmentView.loadSettingsList()
} }
private fun onSingleChoiceClick(item: SingleChoiceSetting) { private fun onSingleChoiceClick(item: SingleChoiceSetting) {
@ -247,6 +301,7 @@ class SettingsAdapter(
notifyItemChanged(clickedPosition) notifyItemChanged(clickedPosition)
val setting = item.setSelectedValue(rtcString) val setting = item.setSelectedValue(rtcString)
fragmentView.putSetting(setting) fragmentView.putSetting(setting)
fragmentView.loadSettingsList()
clickedItem = null clickedItem = null
} }
datePicker.show( datePicker.show(
@ -402,6 +457,7 @@ class SettingsAdapter(
else -> throw IllegalStateException("Unrecognized type used for SingleChoiceSetting!") else -> throw IllegalStateException("Unrecognized type used for SingleChoiceSetting!")
} }
fragmentView?.putSetting(setting) fragmentView?.putSetting(setting)
fragmentView.loadSettingsList()
closeDialog() closeDialog()
} }
} }
@ -425,6 +481,7 @@ class SettingsAdapter(
} }
fragmentView?.putSetting(setting) fragmentView?.putSetting(setting)
fragmentView.loadSettingsList()
closeDialog() closeDialog()
} }
} }
@ -447,6 +504,7 @@ class SettingsAdapter(
fragmentView?.putSetting(setting) fragmentView?.putSetting(setting)
} }
} }
fragmentView.loadSettingsList()
closeDialog() closeDialog()
} }
} }
@ -459,6 +517,7 @@ class SettingsAdapter(
} }
val setting = it.setSelectedValue(textInputValue ?: "") val setting = it.setSelectedValue(textInputValue ?: "")
fragmentView?.putSetting(setting) fragmentView?.putSetting(setting)
fragmentView.loadSettingsList()
closeDialog() closeDialog()
} }
} }
@ -488,6 +547,7 @@ class SettingsAdapter(
} }
notifyItemChanged(position) notifyItemChanged(position)
fragmentView.onSettingChanged() fragmentView.onSettingChanged()
fragmentView.loadSettingsList()
} }
.setNegativeButton(android.R.string.cancel, null) .setNegativeButton(android.R.string.cancel, null)
.show() .show()
@ -495,10 +555,19 @@ class SettingsAdapter(
return true return true
} }
fun onClickDisabledSetting() { fun onClickDisabledSetting(isRuntimeDisabled: Boolean) {
MessageDialogFragment.newInstance( val titleId = if (isRuntimeDisabled)
R.string.setting_not_editable, R.string.setting_not_editable
else
R.string.setting_disabled
val messageId = if (isRuntimeDisabled)
R.string.setting_not_editable_description R.string.setting_not_editable_description
else
R.string.setting_disabled_description
MessageDialogFragment.newInstance(
titleId,
messageId
).show((fragmentView as SettingsFragment).childFragmentManager, MessageDialogFragment.TAG) ).show((fragmentView as SettingsFragment).childFragmentManager, MessageDialogFragment.TAG)
} }

View File

@ -47,7 +47,7 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM) val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
binding.textSettingValue.text = dateFormatter.format(zonedTime) binding.textSettingValue.text = dateFormatter.format(zonedTime)
if (setting.isEditable) { if (setting.isEditable && setting.isEnabled) {
binding.textSettingName.alpha = 1f binding.textSettingName.alpha = 1f
binding.textSettingDescription.alpha = 1f binding.textSettingDescription.alpha = 1f
binding.textSettingValue.alpha = 1f binding.textSettingValue.alpha = 1f
@ -59,18 +59,18 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
} }
override fun onClick(clicked: View) { override fun onClick(clicked: View) {
if (setting.isEditable) { if (setting.isEditable && setting.isEnabled) {
adapter.onDateTimeClick(setting, bindingAdapterPosition) adapter.onDateTimeClick(setting, bindingAdapterPosition)
} else { } else {
adapter.onClickDisabledSetting() adapter.onClickDisabledSetting(!setting.isEditable)
} }
} }
override fun onLongClick(clicked: View): Boolean { override fun onLongClick(clicked: View): Boolean {
if (setting.isEditable) { if (setting.isEditable && setting.isEnabled) {
return adapter.onLongClick(setting.setting!!, bindingAdapterPosition) return adapter.onLongClick(setting.setting!!, bindingAdapterPosition)
} else { } else {
adapter.onClickDisabledSetting() adapter.onClickDisabledSetting(!setting.isEditable)
} }
return false return false
} }

View File

@ -45,7 +45,7 @@ class InputBindingSettingViewHolder(val binding: ListItemSettingBinding, adapter
if (setting.isEditable) { if (setting.isEditable) {
adapter.onInputBindingClick(setting, bindingAdapterPosition) adapter.onInputBindingClick(setting, bindingAdapterPosition)
} else { } else {
adapter.onClickDisabledSetting() adapter.onClickDisabledSetting(!setting.isEditable)
} }
} }
@ -53,7 +53,7 @@ class InputBindingSettingViewHolder(val binding: ListItemSettingBinding, adapter
if (setting.isEditable) { if (setting.isEditable) {
adapter.onLongClick(setting.setting!!, bindingAdapterPosition) adapter.onLongClick(setting.setting!!, bindingAdapterPosition)
} else { } else {
adapter.onClickDisabledSetting() adapter.onClickDisabledSetting(!setting.isEditable)
} }
return false return false
} }

View File

@ -60,7 +60,7 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
override fun onClick(clicked: View) { override fun onClick(clicked: View) {
if (!setting.isRuntimeRunnable && EmulationActivity.isRunning()) { if (!setting.isRuntimeRunnable && EmulationActivity.isRunning()) {
adapter.onClickDisabledSetting() adapter.onClickDisabledSetting(true)
} else { } else {
setting.runnable.invoke() setting.runnable.invoke()
} }

View File

@ -27,7 +27,7 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
binding.textSettingValue.visibility = View.VISIBLE binding.textSettingValue.visibility = View.VISIBLE
binding.textSettingValue.text = getTextSetting() binding.textSettingValue.text = getTextSetting()
if (setting.isEditable) { if (setting.isEditable && setting.isEnabled) {
binding.textSettingName.alpha = 1f binding.textSettingName.alpha = 1f
binding.textSettingDescription.alpha = 1f binding.textSettingDescription.alpha = 1f
binding.textSettingValue.alpha = 1f binding.textSettingValue.alpha = 1f
@ -65,8 +65,8 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
} }
override fun onClick(clicked: View) { override fun onClick(clicked: View) {
if (!setting.isEditable) { if (!setting.isEditable || !setting.isEnabled) {
adapter.onClickDisabledSetting() adapter.onClickDisabledSetting(!setting.isEditable)
return return
} }
@ -84,10 +84,10 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
} }
override fun onLongClick(clicked: View): Boolean { override fun onLongClick(clicked: View): Boolean {
if (setting.isEditable) { if (setting.isEditable && setting.isEnabled) {
return adapter.onLongClick(setting.setting!!, bindingAdapterPosition) return adapter.onLongClick(setting.setting!!, bindingAdapterPosition)
} else { } else {
adapter.onClickDisabledSetting() adapter.onClickDisabledSetting(!setting.isEditable)
} }
return false return false
} }

View File

@ -35,7 +35,7 @@ class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAda
else -> "${(setting.setting as AbstractIntSetting).int}${setting.units}" else -> "${(setting.setting as AbstractIntSetting).int}${setting.units}"
} }
if (setting.isEditable) { if (setting.isEditable && setting.isEnabled) {
binding.textSettingName.alpha = 1f binding.textSettingName.alpha = 1f
binding.textSettingDescription.alpha = 1f binding.textSettingDescription.alpha = 1f
binding.textSettingValue.alpha = 1f binding.textSettingValue.alpha = 1f
@ -47,18 +47,18 @@ class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAda
} }
override fun onClick(clicked: View) { override fun onClick(clicked: View) {
if (setting.isEditable) { if (setting.isEditable && setting.isEnabled) {
adapter.onSliderClick(setting, bindingAdapterPosition) adapter.onSliderClick(setting, bindingAdapterPosition)
} else { } else {
adapter.onClickDisabledSetting() adapter.onClickDisabledSetting(!setting.isEditable)
} }
} }
override fun onLongClick(clicked: View): Boolean { override fun onLongClick(clicked: View): Boolean {
if (setting.isEditable) { if (setting.isEditable && setting.isEnabled) {
return adapter.onLongClick(setting.setting!!, bindingAdapterPosition) return adapter.onLongClick(setting.setting!!, bindingAdapterPosition)
} else { } else {
adapter.onClickDisabledSetting() adapter.onClickDisabledSetting(!setting.isEditable)
} }
return false return false
} }

View File

@ -25,21 +25,31 @@ class StringInputViewHolder(val binding: ListItemSettingBinding, adapter: Settin
} }
binding.textSettingValue.visibility = View.VISIBLE binding.textSettingValue.visibility = View.VISIBLE
binding.textSettingValue.text = setting.setting?.valueAsString binding.textSettingValue.text = setting.setting?.valueAsString
if (setting.isEditable && setting.isEnabled) {
binding.textSettingName.alpha = 1f
binding.textSettingDescription.alpha = 1f
binding.textSettingValue.alpha = 1f
} else {
binding.textSettingName.alpha = 0.5f
binding.textSettingDescription.alpha = 0.5f
binding.textSettingValue.alpha = 0.5f
}
} }
override fun onClick(clicked: View) { override fun onClick(clicked: View) {
if (!setting.isEditable) { if (!setting.isEditable || !setting.isEnabled) {
adapter.onClickDisabledSetting() adapter.onClickDisabledSetting(!setting.isEditable)
return return
} }
adapter.onStringInputClick((setting as StringInputSetting), bindingAdapterPosition) adapter.onStringInputClick((setting as StringInputSetting), bindingAdapterPosition)
} }
override fun onLongClick(clicked: View): Boolean { override fun onLongClick(clicked: View): Boolean {
if (setting.isEditable) { if (setting.isEditable && setting.isEnabled) {
return adapter.onLongClick(setting.setting!!, bindingAdapterPosition) return adapter.onLongClick(setting.setting!!, bindingAdapterPosition)
} else { } else {
adapter.onClickDisabledSetting() adapter.onClickDisabledSetting(!setting.isEditable)
} }
return false return false
} }

View File

@ -33,26 +33,26 @@ class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter
adapter.onBooleanClick(item, bindingAdapterPosition, binding.switchWidget.isChecked) adapter.onBooleanClick(item, bindingAdapterPosition, binding.switchWidget.isChecked)
} }
binding.switchWidget.isEnabled = setting.isEditable binding.switchWidget.isEnabled = setting.isEditable && setting.isEnabled
val textAlpha = if (setting.isEditable) 1f else 0.5f val textAlpha = if (setting.isEditable && setting.isEnabled) 1f else 0.5f
binding.textSettingName.alpha = textAlpha binding.textSettingName.alpha = textAlpha
binding.textSettingDescription.alpha = textAlpha binding.textSettingDescription.alpha = textAlpha
} }
override fun onClick(clicked: View) { override fun onClick(clicked: View) {
if (setting.isEditable) { if (setting.isEditable && setting.isEnabled) {
binding.switchWidget.toggle() binding.switchWidget.toggle()
} else { } else {
adapter.onClickDisabledSetting() adapter.onClickDisabledSetting(!setting.isEditable)
} }
} }
override fun onLongClick(clicked: View): Boolean { override fun onLongClick(clicked: View): Boolean {
if (setting.isEditable) { if (setting.isEditable && setting.isEnabled) {
return adapter.onLongClick(setting.setting!!, bindingAdapterPosition) return adapter.onLongClick(setting.setting!!, bindingAdapterPosition)
} else { } else {
adapter.onClickDisabledSetting() adapter.onClickDisabledSetting(!setting.isEditable)
} }
return false return false
} }

View File

@ -315,6 +315,8 @@
<string name="select_rtc_time">Select RTC time</string> <string name="select_rtc_time">Select RTC time</string>
<string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string> <string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string>
<string name="setting_not_editable">You can\'t edit this now</string> <string name="setting_not_editable">You can\'t edit this now</string>
<string name="setting_disabled">Setting disabled</string>
<string name="setting_disabled_description">This setting is currently disabled due to another setting not being the appropriate value.</string>
<string name="setting_not_editable_description">This option can\'t be changed while a game is running.</string> <string name="setting_not_editable_description">This option can\'t be changed while a game is running.</string>
<string name="auto_select">Auto-Select</string> <string name="auto_select">Auto-Select</string>