diff options
| author | 2023-12-10 20:38:58 -0500 | |
|---|---|---|
| committer | 2023-12-12 17:25:36 -0500 | |
| commit | 2fce81202608cbf4b75fd46e19180fee60e47a53 (patch) | |
| tree | 6c334f5fc3abe10b35ad5d88687e900dd4c1c0ae /src/android | |
| parent | android: Add Game properties (diff) | |
| download | yuzu-2fce81202608cbf4b75fd46e19180fee60e47a53.tar.gz yuzu-2fce81202608cbf4b75fd46e19180fee60e47a53.tar.xz yuzu-2fce81202608cbf4b75fd46e19180fee60e47a53.zip | |
android: Add per-game settings
Diffstat (limited to 'src/android')
27 files changed, 303 insertions, 83 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt index 384527294..28d8dea60 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt | |||
| @@ -12,6 +12,7 @@ import org.yuzu.yuzu_emu.features.settings.model.ByteSetting | |||
| 12 | import org.yuzu.yuzu_emu.features.settings.model.IntSetting | 12 | import org.yuzu.yuzu_emu.features.settings.model.IntSetting |
| 13 | import org.yuzu.yuzu_emu.features.settings.model.LongSetting | 13 | import org.yuzu.yuzu_emu.features.settings.model.LongSetting |
| 14 | import org.yuzu.yuzu_emu.features.settings.model.ShortSetting | 14 | import org.yuzu.yuzu_emu.features.settings.model.ShortSetting |
| 15 | import org.yuzu.yuzu_emu.utils.NativeConfig | ||
| 15 | 16 | ||
| 16 | /** | 17 | /** |
| 17 | * ViewModel abstraction for an Item in the RecyclerView powering SettingsFragments. | 18 | * ViewModel abstraction for an Item in the RecyclerView powering SettingsFragments. |
| @@ -30,9 +31,19 @@ abstract class SettingsItem( | |||
| 30 | val isEditable: Boolean | 31 | val isEditable: Boolean |
| 31 | get() { | 32 | get() { |
| 32 | if (!NativeLibrary.isRunning()) return true | 33 | if (!NativeLibrary.isRunning()) return true |
| 34 | |||
| 35 | // Prevent editing settings that were modified in per-game config while editing global | ||
| 36 | // config | ||
| 37 | if (!NativeConfig.isPerGameConfigLoaded() && !setting.global) { | ||
| 38 | return false | ||
| 39 | } | ||
| 33 | return setting.isRuntimeModifiable | 40 | return setting.isRuntimeModifiable |
| 34 | } | 41 | } |
| 35 | 42 | ||
| 43 | val needsRuntimeGlobal: Boolean | ||
| 44 | get() = NativeLibrary.isRunning() && !setting.global && | ||
| 45 | !NativeConfig.isPerGameConfigLoaded() | ||
| 46 | |||
| 36 | companion object { | 47 | companion object { |
| 37 | const val TYPE_HEADER = 0 | 48 | const val TYPE_HEADER = 0 |
| 38 | const val TYPE_SWITCH = 1 | 49 | const val TYPE_SWITCH = 1 |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt index 64bfc6dd0..6f072241a 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt | |||
| @@ -19,10 +19,9 @@ import androidx.lifecycle.repeatOnLifecycle | |||
| 19 | import androidx.navigation.fragment.NavHostFragment | 19 | import androidx.navigation.fragment.NavHostFragment |
| 20 | import androidx.navigation.navArgs | 20 | import androidx.navigation.navArgs |
| 21 | import com.google.android.material.color.MaterialColors | 21 | import com.google.android.material.color.MaterialColors |
| 22 | import kotlinx.coroutines.CoroutineScope | ||
| 23 | import kotlinx.coroutines.Dispatchers | ||
| 24 | import kotlinx.coroutines.flow.collectLatest | 22 | import kotlinx.coroutines.flow.collectLatest |
| 25 | import kotlinx.coroutines.launch | 23 | import kotlinx.coroutines.launch |
| 24 | import org.yuzu.yuzu_emu.NativeLibrary | ||
| 26 | import java.io.IOException | 25 | import java.io.IOException |
| 27 | import org.yuzu.yuzu_emu.R | 26 | import org.yuzu.yuzu_emu.R |
| 28 | import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding | 27 | import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding |
| @@ -46,6 +45,9 @@ class SettingsActivity : AppCompatActivity() { | |||
| 46 | binding = ActivitySettingsBinding.inflate(layoutInflater) | 45 | binding = ActivitySettingsBinding.inflate(layoutInflater) |
| 47 | setContentView(binding.root) | 46 | setContentView(binding.root) |
| 48 | 47 | ||
| 48 | if (!NativeConfig.isPerGameConfigLoaded() && args.game != null) { | ||
| 49 | SettingsFile.loadCustomConfig(args.game!!) | ||
| 50 | } | ||
| 49 | settingsViewModel.game = args.game | 51 | settingsViewModel.game = args.game |
| 50 | 52 | ||
| 51 | val navHostFragment = | 53 | val navHostFragment = |
| @@ -126,7 +128,6 @@ class SettingsActivity : AppCompatActivity() { | |||
| 126 | 128 | ||
| 127 | override fun onStart() { | 129 | override fun onStart() { |
| 128 | super.onStart() | 130 | super.onStart() |
| 129 | // TODO: Load custom settings contextually | ||
| 130 | if (!DirectoryInitialization.areDirectoriesReady) { | 131 | if (!DirectoryInitialization.areDirectoriesReady) { |
| 131 | DirectoryInitialization.start() | 132 | DirectoryInitialization.start() |
| 132 | } | 133 | } |
| @@ -134,24 +135,35 @@ class SettingsActivity : AppCompatActivity() { | |||
| 134 | 135 | ||
| 135 | override fun onStop() { | 136 | override fun onStop() { |
| 136 | super.onStop() | 137 | super.onStop() |
| 137 | CoroutineScope(Dispatchers.IO).launch { | 138 | Log.info("[SettingsActivity] Settings activity stopping. Saving settings to INI...") |
| 138 | NativeConfig.saveSettings() | 139 | if (isFinishing) { |
| 140 | NativeLibrary.applySettings() | ||
| 141 | if (args.game == null) { | ||
| 142 | NativeConfig.saveGlobalConfig() | ||
| 143 | } else if (NativeConfig.isPerGameConfigLoaded()) { | ||
| 144 | NativeLibrary.logSettings() | ||
| 145 | NativeConfig.savePerGameConfig() | ||
| 146 | NativeConfig.unloadPerGameConfig() | ||
| 147 | } | ||
| 139 | } | 148 | } |
| 140 | } | 149 | } |
| 141 | 150 | ||
| 142 | override fun onDestroy() { | ||
| 143 | settingsViewModel.clear() | ||
| 144 | super.onDestroy() | ||
| 145 | } | ||
| 146 | |||
| 147 | fun onSettingsReset() { | 151 | fun onSettingsReset() { |
| 148 | // Delete settings file because the user may have changed values that do not exist in the UI | 152 | // Delete settings file because the user may have changed values that do not exist in the UI |
| 149 | NativeConfig.unloadConfig() | 153 | if (args.game == null) { |
| 150 | val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG) | 154 | NativeConfig.unloadGlobalConfig() |
| 151 | if (!settingsFile.delete()) { | 155 | val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG) |
| 152 | throw IOException("Failed to delete $settingsFile") | 156 | if (!settingsFile.delete()) { |
| 157 | throw IOException("Failed to delete $settingsFile") | ||
| 158 | } | ||
| 159 | NativeConfig.initializeGlobalConfig() | ||
| 160 | } else { | ||
| 161 | NativeConfig.unloadPerGameConfig() | ||
| 162 | val settingsFile = SettingsFile.getCustomSettingsFile(args.game!!) | ||
| 163 | if (!settingsFile.delete()) { | ||
| 164 | throw IOException("Failed to delete $settingsFile") | ||
| 165 | } | ||
| 153 | } | 166 | } |
| 154 | NativeConfig.initializeConfig() | ||
| 155 | 167 | ||
| 156 | Toast.makeText( | 168 | Toast.makeText( |
| 157 | applicationContext, | 169 | applicationContext, |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt index 3f23c064e..be9b3031b 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt | |||
| @@ -196,6 +196,12 @@ class SettingsAdapter( | |||
| 196 | return true | 196 | return true |
| 197 | } | 197 | } |
| 198 | 198 | ||
| 199 | fun onClearClick(item: SettingsItem, position: Int) { | ||
| 200 | item.setting.global = true | ||
| 201 | notifyItemChanged(position) | ||
| 202 | settingsViewModel.setShouldReloadSettingsList(true) | ||
| 203 | } | ||
| 204 | |||
| 199 | private class DiffCallback : DiffUtil.ItemCallback<SettingsItem>() { | 205 | private class DiffCallback : DiffUtil.ItemCallback<SettingsItem>() { |
| 200 | override fun areItemsTheSame(oldItem: SettingsItem, newItem: SettingsItem): Boolean { | 206 | override fun areItemsTheSame(oldItem: SettingsItem, newItem: SettingsItem): Boolean { |
| 201 | return oldItem.setting.key == newItem.setting.key | 207 | return oldItem.setting.key == newItem.setting.key |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt index 769baf744..d7ab0b5d9 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt | |||
| @@ -66,7 +66,13 @@ class SettingsFragment : Fragment() { | |||
| 66 | args.menuTag | 66 | args.menuTag |
| 67 | ) | 67 | ) |
| 68 | 68 | ||
| 69 | binding.toolbarSettingsLayout.title = getString(args.menuTag.titleId) | 69 | binding.toolbarSettingsLayout.title = if (args.menuTag == Settings.MenuTag.SECTION_ROOT && |
| 70 | args.game != null | ||
| 71 | ) { | ||
| 72 | args.game!!.title | ||
| 73 | } else { | ||
| 74 | getString(args.menuTag.titleId) | ||
| 75 | } | ||
| 70 | binding.listSettings.apply { | 76 | binding.listSettings.apply { |
| 71 | adapter = settingsAdapter | 77 | adapter = settingsAdapter |
| 72 | layoutManager = LinearLayoutManager(requireContext()) | 78 | layoutManager = LinearLayoutManager(requireContext()) |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt index 12a389b37..a7e965589 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt | |||
| @@ -7,6 +7,7 @@ import android.content.SharedPreferences | |||
| 7 | import android.os.Build | 7 | import android.os.Build |
| 8 | import android.widget.Toast | 8 | import android.widget.Toast |
| 9 | import androidx.preference.PreferenceManager | 9 | import androidx.preference.PreferenceManager |
| 10 | import org.yuzu.yuzu_emu.NativeLibrary | ||
| 10 | import org.yuzu.yuzu_emu.R | 11 | import org.yuzu.yuzu_emu.R |
| 11 | import org.yuzu.yuzu_emu.YuzuApplication | 12 | import org.yuzu.yuzu_emu.YuzuApplication |
| 12 | import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting | 13 | import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting |
| @@ -31,9 +32,17 @@ class SettingsFragmentPresenter( | |||
| 31 | private val preferences: SharedPreferences | 32 | private val preferences: SharedPreferences |
| 32 | get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) | 33 | get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) |
| 33 | 34 | ||
| 34 | // Extension for populating settings list based on paired settings | 35 | // Extension for altering settings list based on each setting's properties |
| 35 | fun ArrayList<SettingsItem>.add(key: String) { | 36 | fun ArrayList<SettingsItem>.add(key: String) { |
| 36 | val item = SettingsItem.settingsItems[key]!! | 37 | val item = SettingsItem.settingsItems[key]!! |
| 38 | if (settingsViewModel.game != null && !item.setting.isSwitchable) { | ||
| 39 | return | ||
| 40 | } | ||
| 41 | |||
| 42 | if (!NativeConfig.isPerGameConfigLoaded() && !NativeLibrary.isRunning()) { | ||
| 43 | item.setting.global = true | ||
| 44 | } | ||
| 45 | |||
| 37 | val pairedSettingKey = item.setting.pairedSettingKey | 46 | val pairedSettingKey = item.setting.pairedSettingKey |
| 38 | if (pairedSettingKey.isNotEmpty()) { | 47 | if (pairedSettingKey.isNotEmpty()) { |
| 39 | val pairedSettingValue = NativeConfig.getBoolean( | 48 | val pairedSettingValue = NativeConfig.getBoolean( |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt index 4e159a799..5ad0899dd 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt | |||
| @@ -13,6 +13,7 @@ import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding | |||
| 13 | import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting | 13 | import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting |
| 14 | import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem | 14 | import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem |
| 15 | import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter | 15 | import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter |
| 16 | import org.yuzu.yuzu_emu.utils.NativeConfig | ||
| 16 | 17 | ||
| 17 | class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : | 18 | class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : |
| 18 | SettingViewHolder(binding.root, adapter) { | 19 | SettingViewHolder(binding.root, adapter) { |
| @@ -35,6 +36,17 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA | |||
| 35 | val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM) | 36 | val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM) |
| 36 | binding.textSettingValue.text = dateFormatter.format(zonedTime) | 37 | binding.textSettingValue.text = dateFormatter.format(zonedTime) |
| 37 | 38 | ||
| 39 | binding.buttonClear.visibility = if (setting.setting.global || | ||
| 40 | !NativeConfig.isPerGameConfigLoaded() | ||
| 41 | ) { | ||
| 42 | View.GONE | ||
| 43 | } else { | ||
| 44 | View.VISIBLE | ||
| 45 | } | ||
| 46 | binding.buttonClear.setOnClickListener { | ||
| 47 | adapter.onClearClick(setting, bindingAdapterPosition) | ||
| 48 | } | ||
| 49 | |||
| 38 | setStyle(setting.isEditable, binding) | 50 | setStyle(setting.isEditable, binding) |
| 39 | } | 51 | } |
| 40 | 52 | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt index 036195624..507184238 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt | |||
| @@ -38,6 +38,7 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA | |||
| 38 | binding.textSettingDescription.visibility = View.GONE | 38 | binding.textSettingDescription.visibility = View.GONE |
| 39 | } | 39 | } |
| 40 | binding.textSettingValue.visibility = View.GONE | 40 | binding.textSettingValue.visibility = View.GONE |
| 41 | binding.buttonClear.visibility = View.GONE | ||
| 41 | 42 | ||
| 42 | setStyle(setting.isEditable, binding) | 43 | setStyle(setting.isEditable, binding) |
| 43 | } | 44 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SettingViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SettingViewHolder.kt index 0fd1d2eaa..d26887df8 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SettingViewHolder.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SettingViewHolder.kt | |||
| @@ -41,6 +41,7 @@ abstract class SettingViewHolder(itemView: View, protected val adapter: Settings | |||
| 41 | binding.textSettingName.alpha = opacity | 41 | binding.textSettingName.alpha = opacity |
| 42 | binding.textSettingDescription.alpha = opacity | 42 | binding.textSettingDescription.alpha = opacity |
| 43 | binding.textSettingValue.alpha = opacity | 43 | binding.textSettingValue.alpha = opacity |
| 44 | binding.buttonClear.isEnabled = isEditable | ||
| 44 | } | 45 | } |
| 45 | 46 | ||
| 46 | fun setStyle(isEditable: Boolean, binding: ListItemSettingSwitchBinding) { | 47 | fun setStyle(isEditable: Boolean, binding: ListItemSettingSwitchBinding) { |
| @@ -48,5 +49,6 @@ abstract class SettingViewHolder(itemView: View, protected val adapter: Settings | |||
| 48 | val opacity = if (isEditable) 1.0f else 0.5f | 49 | val opacity = if (isEditable) 1.0f else 0.5f |
| 49 | binding.textSettingName.alpha = opacity | 50 | binding.textSettingName.alpha = opacity |
| 50 | binding.textSettingDescription.alpha = opacity | 51 | binding.textSettingDescription.alpha = opacity |
| 52 | binding.buttonClear.isEnabled = isEditable | ||
| 51 | } | 53 | } |
| 52 | } | 54 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt index 28c4d1777..02dab3785 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt | |||
| @@ -9,6 +9,7 @@ import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem | |||
| 9 | import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting | 9 | import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting |
| 10 | import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting | 10 | import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting |
| 11 | import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter | 11 | import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter |
| 12 | import org.yuzu.yuzu_emu.utils.NativeConfig | ||
| 12 | 13 | ||
| 13 | class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : | 14 | class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : |
| 14 | SettingViewHolder(binding.root, adapter) { | 15 | SettingViewHolder(binding.root, adapter) { |
| @@ -43,6 +44,17 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti | |||
| 43 | } | 44 | } |
| 44 | } | 45 | } |
| 45 | 46 | ||
| 47 | binding.buttonClear.visibility = if (setting.setting.global || | ||
| 48 | !NativeConfig.isPerGameConfigLoaded() | ||
| 49 | ) { | ||
| 50 | View.GONE | ||
| 51 | } else { | ||
| 52 | View.VISIBLE | ||
| 53 | } | ||
| 54 | binding.buttonClear.setOnClickListener { | ||
| 55 | adapter.onClearClick(setting, bindingAdapterPosition) | ||
| 56 | } | ||
| 57 | |||
| 46 | setStyle(setting.isEditable, binding) | 58 | setStyle(setting.isEditable, binding) |
| 47 | } | 59 | } |
| 48 | 60 | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt index 67432f88e..596c18012 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt | |||
| @@ -9,6 +9,7 @@ import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding | |||
| 9 | import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem | 9 | import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem |
| 10 | import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting | 10 | import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting |
| 11 | import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter | 11 | import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter |
| 12 | import org.yuzu.yuzu_emu.utils.NativeConfig | ||
| 12 | 13 | ||
| 13 | class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : | 14 | class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : |
| 14 | SettingViewHolder(binding.root, adapter) { | 15 | SettingViewHolder(binding.root, adapter) { |
| @@ -30,6 +31,17 @@ class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAda | |||
| 30 | setting.units | 31 | setting.units |
| 31 | ) | 32 | ) |
| 32 | 33 | ||
| 34 | binding.buttonClear.visibility = if (setting.setting.global || | ||
| 35 | !NativeConfig.isPerGameConfigLoaded() | ||
| 36 | ) { | ||
| 37 | View.GONE | ||
| 38 | } else { | ||
| 39 | View.VISIBLE | ||
| 40 | } | ||
| 41 | binding.buttonClear.setOnClickListener { | ||
| 42 | adapter.onClearClick(setting, bindingAdapterPosition) | ||
| 43 | } | ||
| 44 | |||
| 33 | setStyle(setting.isEditable, binding) | 45 | setStyle(setting.isEditable, binding) |
| 34 | } | 46 | } |
| 35 | 47 | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt index 8100c65dd..20d35a17d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt | |||
| @@ -37,6 +37,7 @@ class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAd | |||
| 37 | binding.textSettingDescription.visibility = View.GONE | 37 | binding.textSettingDescription.visibility = View.GONE |
| 38 | } | 38 | } |
| 39 | binding.textSettingValue.visibility = View.GONE | 39 | binding.textSettingValue.visibility = View.GONE |
| 40 | binding.buttonClear.visibility = View.GONE | ||
| 40 | } | 41 | } |
| 41 | 42 | ||
| 42 | override fun onClick(clicked: View) { | 43 | override fun onClick(clicked: View) { |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt index 98ed888cb..d26bf9374 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt | |||
| @@ -9,6 +9,7 @@ import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding | |||
| 9 | import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem | 9 | import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem |
| 10 | import org.yuzu.yuzu_emu.features.settings.model.view.SwitchSetting | 10 | import org.yuzu.yuzu_emu.features.settings.model.view.SwitchSetting |
| 11 | import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter | 11 | import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter |
| 12 | import org.yuzu.yuzu_emu.utils.NativeConfig | ||
| 12 | 13 | ||
| 13 | class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter: SettingsAdapter) : | 14 | class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter: SettingsAdapter) : |
| 14 | SettingViewHolder(binding.root, adapter) { | 15 | SettingViewHolder(binding.root, adapter) { |
| @@ -29,7 +30,18 @@ class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter | |||
| 29 | binding.switchWidget.setOnCheckedChangeListener(null) | 30 | binding.switchWidget.setOnCheckedChangeListener(null) |
| 30 | binding.switchWidget.isChecked = setting.getIsChecked(setting.needsRuntimeGlobal) | 31 | binding.switchWidget.isChecked = setting.getIsChecked(setting.needsRuntimeGlobal) |
| 31 | binding.switchWidget.setOnCheckedChangeListener { _: CompoundButton, _: Boolean -> | 32 | binding.switchWidget.setOnCheckedChangeListener { _: CompoundButton, _: Boolean -> |
| 32 | adapter.onBooleanClick(item, binding.switchWidget.isChecked) | 33 | adapter.onBooleanClick(item, binding.switchWidget.isChecked, bindingAdapterPosition) |
| 34 | } | ||
| 35 | |||
| 36 | binding.buttonClear.visibility = if (setting.setting.global || | ||
| 37 | !NativeConfig.isPerGameConfigLoaded() | ||
| 38 | ) { | ||
| 39 | View.GONE | ||
| 40 | } else { | ||
| 41 | View.VISIBLE | ||
| 42 | } | ||
| 43 | binding.buttonClear.setOnClickListener { | ||
| 44 | adapter.onClearClick(setting, bindingAdapterPosition) | ||
| 33 | } | 45 | } |
| 34 | 46 | ||
| 35 | setStyle(setting.isEditable, binding) | 47 | setStyle(setting.isEditable, binding) |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt index 3ae5b4653..5d523be67 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt | |||
| @@ -3,15 +3,27 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.features.settings.utils | 4 | package org.yuzu.yuzu_emu.features.settings.utils |
| 5 | 5 | ||
| 6 | import android.net.Uri | ||
| 7 | import org.yuzu.yuzu_emu.model.Game | ||
| 6 | import java.io.* | 8 | import java.io.* |
| 7 | import org.yuzu.yuzu_emu.utils.DirectoryInitialization | 9 | import org.yuzu.yuzu_emu.utils.DirectoryInitialization |
| 10 | import org.yuzu.yuzu_emu.utils.FileUtil | ||
| 11 | import org.yuzu.yuzu_emu.utils.NativeConfig | ||
| 8 | 12 | ||
| 9 | /** | 13 | /** |
| 10 | * Contains static methods for interacting with .ini files in which settings are stored. | 14 | * Contains static methods for interacting with .ini files in which settings are stored. |
| 11 | */ | 15 | */ |
| 12 | object SettingsFile { | 16 | object SettingsFile { |
| 13 | const val FILE_NAME_CONFIG = "config" | 17 | const val FILE_NAME_CONFIG = "config.ini" |
| 14 | 18 | ||
| 15 | fun getSettingsFile(fileName: String): File = | 19 | fun getSettingsFile(fileName: String): File = |
| 16 | File(DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini") | 20 | File(DirectoryInitialization.userDirectory + "/config/" + fileName) |
| 21 | |||
| 22 | fun getCustomSettingsFile(game: Game): File = | ||
| 23 | File(DirectoryInitialization.userDirectory + "/config/custom/" + game.settingsName + ".ini") | ||
| 24 | |||
| 25 | fun loadCustomConfig(game: Game) { | ||
| 26 | val fileName = FileUtil.getFilename(Uri.parse(game.path)) | ||
| 27 | NativeConfig.initializePerGameConfig(game.programId, fileName) | ||
| 28 | } | ||
| 17 | } | 29 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt index b09df7db3..6466442d5 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt | |||
| @@ -52,6 +52,7 @@ import org.yuzu.yuzu_emu.databinding.DialogOverlayAdjustBinding | |||
| 52 | import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding | 52 | import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding |
| 53 | import org.yuzu.yuzu_emu.features.settings.model.IntSetting | 53 | import org.yuzu.yuzu_emu.features.settings.model.IntSetting |
| 54 | import org.yuzu.yuzu_emu.features.settings.model.Settings | 54 | import org.yuzu.yuzu_emu.features.settings.model.Settings |
| 55 | import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile | ||
| 55 | import org.yuzu.yuzu_emu.model.DriverViewModel | 56 | import org.yuzu.yuzu_emu.model.DriverViewModel |
| 56 | import org.yuzu.yuzu_emu.model.Game | 57 | import org.yuzu.yuzu_emu.model.Game |
| 57 | import org.yuzu.yuzu_emu.model.EmulationViewModel | 58 | import org.yuzu.yuzu_emu.model.EmulationViewModel |
| @@ -127,6 +128,16 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 127 | return | 128 | return |
| 128 | } | 129 | } |
| 129 | 130 | ||
| 131 | if (args.custom) { | ||
| 132 | SettingsFile.loadCustomConfig(args.game!!) | ||
| 133 | NativeConfig.unloadPerGameConfig() | ||
| 134 | } else { | ||
| 135 | NativeConfig.reloadGlobalConfig() | ||
| 136 | } | ||
| 137 | |||
| 138 | // Install the selected driver asynchronously as the game starts | ||
| 139 | driverViewModel.onLaunchGame() | ||
| 140 | |||
| 130 | // So this fragment doesn't restart on configuration changes; i.e. rotation. | 141 | // So this fragment doesn't restart on configuration changes; i.e. rotation. |
| 131 | retainInstance = true | 142 | retainInstance = true |
| 132 | preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) | 143 | preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) |
| @@ -217,6 +228,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 217 | true | 228 | true |
| 218 | } | 229 | } |
| 219 | 230 | ||
| 231 | R.id.menu_settings_per_game -> { | ||
| 232 | val action = HomeNavigationDirections.actionGlobalSettingsActivity( | ||
| 233 | args.game, | ||
| 234 | Settings.MenuTag.SECTION_ROOT | ||
| 235 | ) | ||
| 236 | binding.root.findNavController().navigate(action) | ||
| 237 | true | ||
| 238 | } | ||
| 239 | |||
| 220 | R.id.menu_overlay_controls -> { | 240 | R.id.menu_overlay_controls -> { |
| 221 | showOverlayOptions() | 241 | showOverlayOptions() |
| 222 | true | 242 | true |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFolderPropertiesDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFolderPropertiesDialogFragment.kt index b6c2e4635..1ea1e036e 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFolderPropertiesDialogFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFolderPropertiesDialogFragment.kt | |||
| @@ -13,6 +13,7 @@ import org.yuzu.yuzu_emu.R | |||
| 13 | import org.yuzu.yuzu_emu.databinding.DialogFolderPropertiesBinding | 13 | import org.yuzu.yuzu_emu.databinding.DialogFolderPropertiesBinding |
| 14 | import org.yuzu.yuzu_emu.model.GameDir | 14 | import org.yuzu.yuzu_emu.model.GameDir |
| 15 | import org.yuzu.yuzu_emu.model.GamesViewModel | 15 | import org.yuzu.yuzu_emu.model.GamesViewModel |
| 16 | import org.yuzu.yuzu_emu.utils.NativeConfig | ||
| 16 | import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable | 17 | import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable |
| 17 | 18 | ||
| 18 | class GameFolderPropertiesDialogFragment : DialogFragment() { | 19 | class GameFolderPropertiesDialogFragment : DialogFragment() { |
| @@ -49,6 +50,11 @@ class GameFolderPropertiesDialogFragment : DialogFragment() { | |||
| 49 | .show() | 50 | .show() |
| 50 | } | 51 | } |
| 51 | 52 | ||
| 53 | override fun onStop() { | ||
| 54 | super.onStop() | ||
| 55 | NativeConfig.saveGlobalConfig() | ||
| 56 | } | ||
| 57 | |||
| 52 | override fun onSaveInstanceState(outState: Bundle) { | 58 | override fun onSaveInstanceState(outState: Bundle) { |
| 53 | super.onSaveInstanceState(outState) | 59 | super.onSaveInstanceState(outState) |
| 54 | outState.putBoolean(DEEP_SCAN, deepScan) | 60 | outState.putBoolean(DEEP_SCAN, deepScan) |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt index eb5edaa10..064342cdd 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt | |||
| @@ -304,6 +304,11 @@ class SetupFragment : Fragment() { | |||
| 304 | setInsets() | 304 | setInsets() |
| 305 | } | 305 | } |
| 306 | 306 | ||
| 307 | override fun onStop() { | ||
| 308 | super.onStop() | ||
| 309 | NativeConfig.saveGlobalConfig() | ||
| 310 | } | ||
| 311 | |||
| 307 | override fun onSaveInstanceState(outState: Bundle) { | 312 | override fun onSaveInstanceState(outState: Bundle) { |
| 308 | super.onSaveInstanceState(outState) | 313 | super.onSaveInstanceState(outState) |
| 309 | if (_binding != null) { | 314 | if (_binding != null) { |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt index fd925235b..eaec09b24 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt | |||
| @@ -168,6 +168,7 @@ class GamesViewModel : ViewModel() { | |||
| 168 | fun onCloseGameFoldersFragment() = | 168 | fun onCloseGameFoldersFragment() = |
| 169 | viewModelScope.launch { | 169 | viewModelScope.launch { |
| 170 | withContext(Dispatchers.IO) { | 170 | withContext(Dispatchers.IO) { |
| 171 | NativeConfig.saveGlobalConfig() | ||
| 171 | getGameDirs(true) | 172 | getGameDirs(true) |
| 172 | } | 173 | } |
| 173 | } | 174 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt index ccc981e95..5cb6a5d57 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt | |||
| @@ -68,8 +68,4 @@ class SettingsViewModel : ViewModel() { | |||
| 68 | fun setAdapterItemChanged(value: Int) { | 68 | fun setAdapterItemChanged(value: Int) { |
| 69 | _adapterItemChanged.value = value | 69 | _adapterItemChanged.value = value |
| 70 | } | 70 | } |
| 71 | |||
| 72 | fun clear() { | ||
| 73 | game = null | ||
| 74 | } | ||
| 75 | } | 71 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt index 09ddd1bbd..b4117d761 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt | |||
| @@ -28,12 +28,9 @@ import androidx.navigation.ui.setupWithNavController | |||
| 28 | import androidx.preference.PreferenceManager | 28 | import androidx.preference.PreferenceManager |
| 29 | import com.google.android.material.color.MaterialColors | 29 | import com.google.android.material.color.MaterialColors |
| 30 | import com.google.android.material.navigation.NavigationBarView | 30 | import com.google.android.material.navigation.NavigationBarView |
| 31 | import kotlinx.coroutines.CoroutineScope | ||
| 32 | import java.io.File | 31 | import java.io.File |
| 33 | import java.io.FilenameFilter | 32 | import java.io.FilenameFilter |
| 34 | import kotlinx.coroutines.Dispatchers | ||
| 35 | import kotlinx.coroutines.launch | 33 | import kotlinx.coroutines.launch |
| 36 | import kotlinx.coroutines.withContext | ||
| 37 | import org.yuzu.yuzu_emu.HomeNavigationDirections | 34 | import org.yuzu.yuzu_emu.HomeNavigationDirections |
| 38 | import org.yuzu.yuzu_emu.NativeLibrary | 35 | import org.yuzu.yuzu_emu.NativeLibrary |
| 39 | import org.yuzu.yuzu_emu.R | 36 | import org.yuzu.yuzu_emu.R |
| @@ -258,13 +255,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 258 | super.onResume() | 255 | super.onResume() |
| 259 | } | 256 | } |
| 260 | 257 | ||
| 261 | override fun onStop() { | ||
| 262 | super.onStop() | ||
| 263 | CoroutineScope(Dispatchers.IO).launch { | ||
| 264 | NativeConfig.saveSettings() | ||
| 265 | } | ||
| 266 | } | ||
| 267 | |||
| 268 | override fun onDestroy() { | 258 | override fun onDestroy() { |
| 269 | EmulationActivity.stopForegroundService(this) | 259 | EmulationActivity.stopForegroundService(this) |
| 270 | super.onDestroy() | 260 | super.onDestroy() |
| @@ -677,7 +667,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 677 | } | 667 | } |
| 678 | 668 | ||
| 679 | // Clear existing user data | 669 | // Clear existing user data |
| 680 | NativeConfig.unloadConfig() | 670 | NativeConfig.unloadGlobalConfig() |
| 681 | File(DirectoryInitialization.userDirectory!!).deleteRecursively() | 671 | File(DirectoryInitialization.userDirectory!!).deleteRecursively() |
| 682 | 672 | ||
| 683 | // Copy archive to internal storage | 673 | // Copy archive to internal storage |
| @@ -696,7 +686,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 696 | 686 | ||
| 697 | // Reinitialize relevant data | 687 | // Reinitialize relevant data |
| 698 | NativeLibrary.initializeSystem(true) | 688 | NativeLibrary.initializeSystem(true) |
| 699 | NativeConfig.initializeConfig() | 689 | NativeConfig.initializeGlobalConfig() |
| 700 | gamesViewModel.reloadGames(false) | 690 | gamesViewModel.reloadGames(false) |
| 701 | 691 | ||
| 702 | return@newInstance getString(R.string.user_data_import_success) | 692 | return@newInstance getString(R.string.user_data_import_success) |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt index 21270fc84..0197fd712 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt | |||
| @@ -16,7 +16,7 @@ object DirectoryInitialization { | |||
| 16 | if (!areDirectoriesReady) { | 16 | if (!areDirectoriesReady) { |
| 17 | initializeInternalStorage() | 17 | initializeInternalStorage() |
| 18 | NativeLibrary.initializeSystem(false) | 18 | NativeLibrary.initializeSystem(false) |
| 19 | NativeConfig.initializeConfig() | 19 | NativeConfig.initializeGlobalConfig() |
| 20 | areDirectoriesReady = true | 20 | areDirectoriesReady = true |
| 21 | } | 21 | } |
| 22 | } | 22 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt index 7d629b7d5..2d3d8ec79 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt | |||
| @@ -7,30 +7,54 @@ import org.yuzu.yuzu_emu.model.GameDir | |||
| 7 | 7 | ||
| 8 | object NativeConfig { | 8 | object NativeConfig { |
| 9 | /** | 9 | /** |
| 10 | * Creates a Config object and opens the emulation config. | 10 | * Loads global config. |
| 11 | */ | 11 | */ |
| 12 | @Synchronized | 12 | @Synchronized |
| 13 | external fun initializeConfig() | 13 | external fun initializeGlobalConfig() |
| 14 | 14 | ||
| 15 | /** | 15 | /** |
| 16 | * Destroys the stored config object. This automatically saves the existing config. | 16 | * Destroys the stored global config object. This does not save the existing config. |
| 17 | */ | 17 | */ |
| 18 | @Synchronized | 18 | @Synchronized |
| 19 | external fun unloadConfig() | 19 | external fun unloadGlobalConfig() |
| 20 | 20 | ||
| 21 | /** | 21 | /** |
| 22 | * Reads values saved to the config file and saves them. | 22 | * Reads values in the global config file and saves them. |
| 23 | */ | 23 | */ |
| 24 | @Synchronized | 24 | @Synchronized |
| 25 | external fun reloadSettings() | 25 | external fun reloadGlobalConfig() |
| 26 | 26 | ||
| 27 | /** | 27 | /** |
| 28 | * Saves settings values in memory to disk. | 28 | * Saves global settings values in memory to disk. |
| 29 | */ | 29 | */ |
| 30 | @Synchronized | 30 | @Synchronized |
| 31 | external fun saveSettings() | 31 | external fun saveGlobalConfig() |
| 32 | 32 | ||
| 33 | external fun getBoolean(key: String, getDefault: Boolean): Boolean | 33 | /** |
| 34 | * Creates per-game config for the specified parameters. Must be unloaded once per-game config | ||
| 35 | * is closed with [unloadPerGameConfig]. All switchable values that [NativeConfig] gets/sets | ||
| 36 | * will follow the per-game config until the global config is reloaded. | ||
| 37 | * | ||
| 38 | * @param programId String representation of the u64 programId | ||
| 39 | * @param fileName Filename of the game, including its extension | ||
| 40 | */ | ||
| 41 | @Synchronized | ||
| 42 | external fun initializePerGameConfig(programId: String, fileName: String) | ||
| 43 | |||
| 44 | @Synchronized | ||
| 45 | external fun isPerGameConfigLoaded(): Boolean | ||
| 46 | |||
| 47 | /** | ||
| 48 | * Saves per-game settings values in memory to disk. | ||
| 49 | */ | ||
| 50 | @Synchronized | ||
| 51 | external fun savePerGameConfig() | ||
| 52 | |||
| 53 | /** | ||
| 54 | * Destroys the stored per-game config object. This does not save the config. | ||
| 55 | */ | ||
| 56 | @Synchronized | ||
| 57 | external fun unloadPerGameConfig() | ||
| 34 | 58 | ||
| 35 | @Synchronized | 59 | @Synchronized |
| 36 | external fun getBoolean(key: String, needsGlobal: Boolean): Boolean | 60 | external fun getBoolean(key: String, needsGlobal: Boolean): Boolean |
diff --git a/src/android/app/src/main/jni/native_config.cpp b/src/android/app/src/main/jni/native_config.cpp index 7f2485720..56989bde8 100644 --- a/src/android/app/src/main/jni/native_config.cpp +++ b/src/android/app/src/main/jni/native_config.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include <string> | 4 | #include <string> |
| 5 | 5 | ||
| 6 | #include <common/fs/fs_util.h> | ||
| 6 | #include <jni.h> | 7 | #include <jni.h> |
| 7 | 8 | ||
| 8 | #include "android_config.h" | 9 | #include "android_config.h" |
| @@ -12,17 +13,19 @@ | |||
| 12 | #include "frontend_common/config.h" | 13 | #include "frontend_common/config.h" |
| 13 | #include "jni/android_common/android_common.h" | 14 | #include "jni/android_common/android_common.h" |
| 14 | #include "jni/id_cache.h" | 15 | #include "jni/id_cache.h" |
| 16 | #include "native.h" | ||
| 15 | 17 | ||
| 16 | std::unique_ptr<AndroidConfig> config; | 18 | std::unique_ptr<AndroidConfig> global_config; |
| 19 | std::unique_ptr<AndroidConfig> per_game_config; | ||
| 17 | 20 | ||
| 18 | template <typename T> | 21 | template <typename T> |
| 19 | Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) { | 22 | Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) { |
| 20 | auto key = GetJString(env, jkey); | 23 | auto key = GetJString(env, jkey); |
| 21 | auto basicSetting = Settings::values.linkage.by_key[key]; | 24 | auto basicSetting = Settings::values.linkage.by_key[key]; |
| 22 | auto basicAndroidSetting = AndroidSettings::values.linkage.by_key[key]; | ||
| 23 | if (basicSetting != 0) { | 25 | if (basicSetting != 0) { |
| 24 | return static_cast<Settings::Setting<T>*>(basicSetting); | 26 | return static_cast<Settings::Setting<T>*>(basicSetting); |
| 25 | } | 27 | } |
| 28 | auto basicAndroidSetting = AndroidSettings::values.linkage.by_key[key]; | ||
| 26 | if (basicAndroidSetting != 0) { | 29 | if (basicAndroidSetting != 0) { |
| 27 | return static_cast<Settings::Setting<T>*>(basicAndroidSetting); | 30 | return static_cast<Settings::Setting<T>*>(basicAndroidSetting); |
| 28 | } | 31 | } |
| @@ -32,20 +35,43 @@ Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) { | |||
| 32 | 35 | ||
| 33 | extern "C" { | 36 | extern "C" { |
| 34 | 37 | ||
| 35 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializeConfig(JNIEnv* env, jobject obj) { | 38 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializeGlobalConfig(JNIEnv* env, jobject obj) { |
| 36 | config = std::make_unique<AndroidConfig>(); | 39 | global_config = std::make_unique<AndroidConfig>(); |
| 40 | } | ||
| 41 | |||
| 42 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_unloadGlobalConfig(JNIEnv* env, jobject obj) { | ||
| 43 | global_config.reset(); | ||
| 44 | } | ||
| 45 | |||
| 46 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_reloadGlobalConfig(JNIEnv* env, jobject obj) { | ||
| 47 | global_config->AndroidConfig::ReloadAllValues(); | ||
| 48 | } | ||
| 49 | |||
| 50 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_saveGlobalConfig(JNIEnv* env, jobject obj) { | ||
| 51 | global_config->AndroidConfig::SaveAllValues(); | ||
| 52 | } | ||
| 53 | |||
| 54 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializePerGameConfig(JNIEnv* env, jobject obj, | ||
| 55 | jstring jprogramId, | ||
| 56 | jstring jfileName) { | ||
| 57 | auto program_id = EmulationSession::GetProgramId(env, jprogramId); | ||
| 58 | auto file_name = GetJString(env, jfileName); | ||
| 59 | const auto config_file_name = program_id == 0 ? file_name : fmt::format("{:016X}", program_id); | ||
| 60 | per_game_config = | ||
| 61 | std::make_unique<AndroidConfig>(config_file_name, Config::ConfigType::PerGameConfig); | ||
| 37 | } | 62 | } |
| 38 | 63 | ||
| 39 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_unloadConfig(JNIEnv* env, jobject obj) { | 64 | jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_isPerGameConfigLoaded(JNIEnv* env, |
| 40 | config.reset(); | 65 | jobject obj) { |
| 66 | return per_game_config != nullptr; | ||
| 41 | } | 67 | } |
| 42 | 68 | ||
| 43 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_reloadSettings(JNIEnv* env, jobject obj) { | 69 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_savePerGameConfig(JNIEnv* env, jobject obj) { |
| 44 | config->AndroidConfig::ReloadAllValues(); | 70 | per_game_config->AndroidConfig::SaveAllValues(); |
| 45 | } | 71 | } |
| 46 | 72 | ||
| 47 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_saveSettings(JNIEnv* env, jobject obj) { | 73 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_unloadPerGameConfig(JNIEnv* env, jobject obj) { |
| 48 | config->AndroidConfig::SaveAllValues(); | 74 | per_game_config.reset(); |
| 49 | } | 75 | } |
| 50 | 76 | ||
| 51 | jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj, | 77 | jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj, |
diff --git a/src/android/app/src/main/res/layout/list_item_setting.xml b/src/android/app/src/main/res/layout/list_item_setting.xml index 544280e75..1f80682f1 100644 --- a/src/android/app/src/main/res/layout/list_item_setting.xml +++ b/src/android/app/src/main/res/layout/list_item_setting.xml | |||
| @@ -62,6 +62,16 @@ | |||
| 62 | android:textSize="13sp" | 62 | android:textSize="13sp" |
| 63 | tools:text="1x" /> | 63 | tools:text="1x" /> |
| 64 | 64 | ||
| 65 | <com.google.android.material.button.MaterialButton | ||
| 66 | android:id="@+id/button_clear" | ||
| 67 | style="@style/Widget.Material3.Button.TonalButton" | ||
| 68 | android:layout_width="wrap_content" | ||
| 69 | android:layout_height="wrap_content" | ||
| 70 | android:layout_marginTop="16dp" | ||
| 71 | android:visibility="gone" | ||
| 72 | android:text="@string/clear" | ||
| 73 | tools:visibility="visible" /> | ||
| 74 | |||
| 65 | </LinearLayout> | 75 | </LinearLayout> |
| 66 | 76 | ||
| 67 | </LinearLayout> | 77 | </LinearLayout> |
diff --git a/src/android/app/src/main/res/layout/list_item_setting_switch.xml b/src/android/app/src/main/res/layout/list_item_setting_switch.xml index a8f5aff78..5cb84182e 100644 --- a/src/android/app/src/main/res/layout/list_item_setting_switch.xml +++ b/src/android/app/src/main/res/layout/list_item_setting_switch.xml | |||
| @@ -10,41 +10,62 @@ | |||
| 10 | android:minHeight="72dp" | 10 | android:minHeight="72dp" |
| 11 | android:padding="16dp"> | 11 | android:padding="16dp"> |
| 12 | 12 | ||
| 13 | <com.google.android.material.materialswitch.MaterialSwitch | ||
| 14 | android:id="@+id/switch_widget" | ||
| 15 | android:layout_width="wrap_content" | ||
| 16 | android:layout_height="wrap_content" | ||
| 17 | android:layout_alignParentEnd="true" | ||
| 18 | android:layout_centerVertical="true" /> | ||
| 19 | |||
| 20 | <LinearLayout | 13 | <LinearLayout |
| 21 | android:layout_width="match_parent" | 14 | android:layout_width="match_parent" |
| 22 | android:layout_height="wrap_content" | 15 | android:layout_height="wrap_content" |
| 23 | android:layout_alignParentTop="true" | ||
| 24 | android:layout_centerVertical="true" | ||
| 25 | android:layout_marginEnd="24dp" | ||
| 26 | android:layout_toStartOf="@+id/switch_widget" | ||
| 27 | android:gravity="center_vertical" | ||
| 28 | android:orientation="vertical"> | 16 | android:orientation="vertical"> |
| 29 | 17 | ||
| 30 | <com.google.android.material.textview.MaterialTextView | 18 | <LinearLayout |
| 31 | android:id="@+id/text_setting_name" | 19 | android:layout_width="match_parent" |
| 32 | style="@style/TextAppearance.Material3.HeadlineMedium" | ||
| 33 | android:layout_width="wrap_content" | ||
| 34 | android:layout_height="wrap_content" | 20 | android:layout_height="wrap_content" |
| 35 | android:textAlignment="viewStart" | 21 | android:orientation="horizontal"> |
| 36 | android:textSize="17sp" | 22 | |
| 37 | app:lineHeight="28dp" | 23 | <LinearLayout |
| 38 | tools:text="@string/frame_limit_enable" /> | 24 | android:layout_width="0dp" |
| 39 | 25 | android:layout_height="wrap_content" | |
| 40 | <com.google.android.material.textview.MaterialTextView | 26 | android:layout_marginEnd="24dp" |
| 41 | android:id="@+id/text_setting_description" | 27 | android:gravity="center_vertical" |
| 42 | style="@style/TextAppearance.Material3.BodySmall" | 28 | android:orientation="vertical" |
| 29 | android:layout_weight="1"> | ||
| 30 | |||
| 31 | <com.google.android.material.textview.MaterialTextView | ||
| 32 | android:id="@+id/text_setting_name" | ||
| 33 | style="@style/TextAppearance.Material3.HeadlineMedium" | ||
| 34 | android:layout_width="wrap_content" | ||
| 35 | android:layout_height="wrap_content" | ||
| 36 | android:textAlignment="viewStart" | ||
| 37 | android:textSize="17sp" | ||
| 38 | app:lineHeight="28dp" | ||
| 39 | tools:text="@string/frame_limit_enable" /> | ||
| 40 | |||
| 41 | <com.google.android.material.textview.MaterialTextView | ||
| 42 | android:id="@+id/text_setting_description" | ||
| 43 | style="@style/TextAppearance.Material3.BodySmall" | ||
| 44 | android:layout_width="wrap_content" | ||
| 45 | android:layout_height="wrap_content" | ||
| 46 | android:layout_marginTop="@dimen/spacing_small" | ||
| 47 | android:textAlignment="viewStart" | ||
| 48 | tools:text="@string/frame_limit_enable_description" /> | ||
| 49 | |||
| 50 | </LinearLayout> | ||
| 51 | |||
| 52 | <com.google.android.material.materialswitch.MaterialSwitch | ||
| 53 | android:id="@+id/switch_widget" | ||
| 54 | android:layout_width="wrap_content" | ||
| 55 | android:layout_height="wrap_content" | ||
| 56 | android:layout_gravity="center_vertical"/> | ||
| 57 | |||
| 58 | </LinearLayout> | ||
| 59 | |||
| 60 | <com.google.android.material.button.MaterialButton | ||
| 61 | android:id="@+id/button_clear" | ||
| 62 | style="@style/Widget.Material3.Button.TonalButton" | ||
| 43 | android:layout_width="wrap_content" | 63 | android:layout_width="wrap_content" |
| 44 | android:layout_height="wrap_content" | 64 | android:layout_height="wrap_content" |
| 45 | android:layout_marginTop="@dimen/spacing_small" | 65 | android:layout_marginTop="16dp" |
| 46 | android:textAlignment="viewStart" | 66 | android:text="@string/clear" |
| 47 | tools:text="@string/frame_limit_enable_description" /> | 67 | android:visibility="gone" |
| 68 | tools:visibility="visible" /> | ||
| 48 | 69 | ||
| 49 | </LinearLayout> | 70 | </LinearLayout> |
| 50 | 71 | ||
diff --git a/src/android/app/src/main/res/menu/menu_in_game.xml b/src/android/app/src/main/res/menu/menu_in_game.xml index f98f727b6..ac6ab06ff 100644 --- a/src/android/app/src/main/res/menu/menu_in_game.xml +++ b/src/android/app/src/main/res/menu/menu_in_game.xml | |||
| @@ -12,6 +12,11 @@ | |||
| 12 | android:title="@string/preferences_settings" /> | 12 | android:title="@string/preferences_settings" /> |
| 13 | 13 | ||
| 14 | <item | 14 | <item |
| 15 | android:id="@+id/menu_settings_per_game" | ||
| 16 | android:icon="@drawable/ic_settings_outline" | ||
| 17 | android:title="@string/per_game_settings" /> | ||
| 18 | |||
| 19 | <item | ||
| 15 | android:id="@+id/menu_overlay_controls" | 20 | android:id="@+id/menu_overlay_controls" |
| 16 | android:icon="@drawable/ic_controller" | 21 | android:icon="@drawable/ic_controller" |
| 17 | android:title="@string/emulation_input_overlay" /> | 22 | android:title="@string/emulation_input_overlay" /> |
diff --git a/src/android/app/src/main/res/navigation/emulation_navigation.xml b/src/android/app/src/main/res/navigation/emulation_navigation.xml index cfc494b3f..2f8c3fa0d 100644 --- a/src/android/app/src/main/res/navigation/emulation_navigation.xml +++ b/src/android/app/src/main/res/navigation/emulation_navigation.xml | |||
| @@ -15,6 +15,10 @@ | |||
| 15 | app:argType="org.yuzu.yuzu_emu.model.Game" | 15 | app:argType="org.yuzu.yuzu_emu.model.Game" |
| 16 | app:nullable="true" | 16 | app:nullable="true" |
| 17 | android:defaultValue="@null" /> | 17 | android:defaultValue="@null" /> |
| 18 | <argument | ||
| 19 | android:name="custom" | ||
| 20 | app:argType="boolean" | ||
| 21 | android:defaultValue="false" /> | ||
| 18 | </fragment> | 22 | </fragment> |
| 19 | 23 | ||
| 20 | <activity | 24 | <activity |
diff --git a/src/android/app/src/main/res/navigation/home_navigation.xml b/src/android/app/src/main/res/navigation/home_navigation.xml index 1c69bf0db..226cf5600 100644 --- a/src/android/app/src/main/res/navigation/home_navigation.xml +++ b/src/android/app/src/main/res/navigation/home_navigation.xml | |||
| @@ -77,6 +77,10 @@ | |||
| 77 | app:argType="org.yuzu.yuzu_emu.model.Game" | 77 | app:argType="org.yuzu.yuzu_emu.model.Game" |
| 78 | app:nullable="true" | 78 | app:nullable="true" |
| 79 | android:defaultValue="@null" /> | 79 | android:defaultValue="@null" /> |
| 80 | <argument | ||
| 81 | android:name="custom" | ||
| 82 | app:argType="boolean" | ||
| 83 | android:defaultValue="false" /> | ||
| 80 | </activity> | 84 | </activity> |
| 81 | 85 | ||
| 82 | <action | 86 | <action |