mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-28 15:31:50 +01:00
Add per-game settings configuration functionality
This commit is contained in:
parent
0467614dc0
commit
1a11aaa651
@ -23,6 +23,7 @@ import com.google.android.material.snackbar.Snackbar
|
||||
import emu.skyline.data.AppItem
|
||||
import emu.skyline.databinding.AppDialogBinding
|
||||
import emu.skyline.loader.LoaderResult
|
||||
import emu.skyline.settings.SettingsActivity
|
||||
|
||||
/**
|
||||
* This dialog is used to show extra game metadata and provide extra options such as pinning the game to the home screen
|
||||
@ -75,6 +76,13 @@ class AppDialog : BottomSheetDialogFragment() {
|
||||
startActivity(Intent(activity, EmulationActivity::class.java).apply { data = item.uri })
|
||||
}
|
||||
|
||||
binding.gameSettings.isEnabled = item.loaderResult == LoaderResult.Success
|
||||
binding.gameSettings.setOnClickListener {
|
||||
startActivity(Intent(activity, SettingsActivity::class.java).apply {
|
||||
putExtras(requireArguments())
|
||||
})
|
||||
}
|
||||
|
||||
val shortcutManager = requireActivity().getSystemService(ShortcutManager::class.java)
|
||||
binding.gamePin.isEnabled = shortcutManager.isRequestPinShortcutSupported
|
||||
|
||||
|
@ -333,8 +333,7 @@ class MainActivity : AppCompatActivity() {
|
||||
super.onResume()
|
||||
|
||||
// Try to return to normal GPU clocks upon resuming back to main activity, to avoid GPU being stuck at max clocks after a crash
|
||||
if (EmulationSettings.global.forceMaxGpuClocks)
|
||||
GpuDriverHelper.forceMaxGpuClocks(false)
|
||||
GpuDriverHelper.forceMaxGpuClocks(false)
|
||||
|
||||
var layoutTypeChanged = false
|
||||
for (appViewItem in adapter.allItems.filterIsInstance(AppViewItem::class.java)) {
|
||||
|
@ -22,6 +22,7 @@ import emu.skyline.adapter.GenericListItem
|
||||
import emu.skyline.adapter.GpuDriverViewItem
|
||||
import emu.skyline.adapter.SelectableGenericAdapter
|
||||
import emu.skyline.adapter.SpacingItemDecoration
|
||||
import emu.skyline.data.AppItem
|
||||
import emu.skyline.databinding.GpuDriverActivityBinding
|
||||
import emu.skyline.settings.EmulationSettings
|
||||
import emu.skyline.utils.GpuDriverHelper
|
||||
@ -38,6 +39,8 @@ import kotlinx.coroutines.launch
|
||||
class GpuDriverActivity : AppCompatActivity() {
|
||||
private val binding by lazy { GpuDriverActivityBinding.inflate(layoutInflater) }
|
||||
|
||||
private val item by lazy { intent.extras?.getSerializable("item") as AppItem? }
|
||||
|
||||
private val adapter = SelectableGenericAdapter(0)
|
||||
|
||||
lateinit var emulationSettings : EmulationSettings
|
||||
@ -80,7 +83,8 @@ class GpuDriverActivity : AppCompatActivity() {
|
||||
|
||||
GpuDriverHelper.getInstalledDrivers(this).onEachIndexed { index, (file, metadata) ->
|
||||
items.add(GpuDriverViewItem(metadata).apply {
|
||||
onDelete = { position, wasChecked ->
|
||||
// Enable the delete button when configuring global settings only
|
||||
onDelete = if (emulationSettings.isGlobal) { position, wasChecked ->
|
||||
// If the driver was selected, select the system driver as the active one
|
||||
if (wasChecked)
|
||||
emulationSettings.gpuDriver = EmulationSettings.SYSTEM_GPU_DRIVER
|
||||
@ -103,7 +107,7 @@ class GpuDriverActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
}).show()
|
||||
}
|
||||
} else null
|
||||
|
||||
onClick = {
|
||||
emulationSettings.gpuDriver = metadata.label
|
||||
@ -127,8 +131,18 @@ class GpuDriverActivity : AppCompatActivity() {
|
||||
WindowInsetsHelper.addMargin(binding.addDriverButton, bottom = true)
|
||||
|
||||
setSupportActionBar(binding.titlebar.toolbar)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
supportActionBar?.title = getString(R.string.gpu_driver_config)
|
||||
supportActionBar?.apply {
|
||||
setDisplayHomeAsUpEnabled(true)
|
||||
title = getString(R.string.gpu_driver_config)
|
||||
subtitle = item?.title
|
||||
}
|
||||
|
||||
emulationSettings = if (item == null) {
|
||||
EmulationSettings.global
|
||||
} else {
|
||||
val appItem = item as AppItem
|
||||
EmulationSettings.forTitleId(appItem.titleId ?: appItem.key())
|
||||
}
|
||||
|
||||
val layoutManager = LinearLayoutManager(this)
|
||||
binding.driverList.layoutManager = layoutManager
|
||||
@ -173,7 +187,6 @@ class GpuDriverActivity : AppCompatActivity() {
|
||||
installCallback.launch(intent)
|
||||
}
|
||||
|
||||
emulationSettings = EmulationSettings.global
|
||||
populateAdapter()
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.Preference.SummaryProvider
|
||||
import androidx.preference.R
|
||||
import emu.skyline.data.AppItem
|
||||
import emu.skyline.settings.EmulationSettings
|
||||
import emu.skyline.utils.GpuDriverHelper
|
||||
import emu.skyline.R as SkylineR
|
||||
@ -25,6 +26,12 @@ class GpuDriverPreference @JvmOverloads constructor(context : Context, attrs : A
|
||||
notifyChanged()
|
||||
}
|
||||
|
||||
/**
|
||||
* The app item being configured, used to load the correct settings in [GpuDriverActivity]
|
||||
* This is populated by [emu.skyline.settings.GameSettingsFragment]
|
||||
*/
|
||||
var item : AppItem? = null
|
||||
|
||||
init {
|
||||
val supportsCustomDriverLoading = GpuDriverHelper.supportsCustomDriverLoading()
|
||||
if (supportsCustomDriverLoading) {
|
||||
@ -48,5 +55,7 @@ class GpuDriverPreference @JvmOverloads constructor(context : Context, attrs : A
|
||||
/**
|
||||
* This launches [GpuDriverActivity] on click to manage driver packages
|
||||
*/
|
||||
override fun onClick() = driverCallback.launch(Intent(context, GpuDriverActivity::class.java))
|
||||
override fun onClick() = driverCallback.launch(Intent(context, GpuDriverActivity::class.java).apply {
|
||||
putExtra("item", item)
|
||||
})
|
||||
}
|
||||
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
* Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
*/
|
||||
|
||||
package emu.skyline.preference
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import androidx.preference.Preference
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import emu.skyline.R
|
||||
|
||||
/**
|
||||
* Clears all values in the current shared preferences, showing a dialog to confirm the action
|
||||
* This preference recreates the activity to update the UI after modifying shared preferences
|
||||
*/
|
||||
class ResetSettingsPreference @JvmOverloads constructor(context : Context, attrs : AttributeSet? = null, defStyleAttr : Int = androidx.preference.R.attr.preferenceStyle) : Preference(context, attrs, defStyleAttr) {
|
||||
init {
|
||||
setOnPreferenceClickListener {
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setTitle(title)
|
||||
.setMessage(R.string.reset_settings_warning)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
// Clear all shared preferences
|
||||
sharedPreferences?.apply { edit().clear().apply() }
|
||||
// Recreate the activity to update the UI after modifying shared preferences
|
||||
(context as? Activity)?.recreate()
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
* Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
*/
|
||||
|
||||
package emu.skyline.settings
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.preference.*
|
||||
import emu.skyline.BuildConfig
|
||||
import emu.skyline.R
|
||||
import emu.skyline.data.AppItem
|
||||
import emu.skyline.preference.GpuDriverPreference
|
||||
import emu.skyline.preference.IntegerListPreference
|
||||
import emu.skyline.utils.GpuDriverHelper
|
||||
import emu.skyline.utils.WindowInsetsHelper
|
||||
|
||||
/**
|
||||
* This fragment is used to display custom game preferences
|
||||
*/
|
||||
class GameSettingsFragment : PreferenceFragmentCompat() {
|
||||
companion object {
|
||||
private const val DIALOG_FRAGMENT_TAG = "androidx.preference.PreferenceFragment.DIALOG"
|
||||
}
|
||||
|
||||
private val item by lazy { requireArguments().getSerializable("item")!! as AppItem }
|
||||
|
||||
override fun onViewCreated(view : View, savedInstanceState : Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val recyclerView = view.findViewById<View>(R.id.recycler_view)
|
||||
WindowInsetsHelper.setPadding(recyclerView, bottom = true)
|
||||
|
||||
(activity as AppCompatActivity).supportActionBar?.subtitle = item.title
|
||||
}
|
||||
|
||||
/**
|
||||
* This constructs the preferences from XML preference resources
|
||||
*/
|
||||
override fun onCreatePreferences(savedInstanceState : Bundle?, rootKey : String?) {
|
||||
preferenceManager.sharedPreferencesName = EmulationSettings.prefNameForTitle(item.titleId ?: item.key())
|
||||
addPreferencesFromResource(R.xml.custom_game_preferences)
|
||||
addPreferencesFromResource(R.xml.emulation_preferences)
|
||||
|
||||
// Toggle emulation settings enabled state based on use_custom_settings state
|
||||
listOf<Preference?>(
|
||||
findPreference("category_system"),
|
||||
findPreference("category_presentation"),
|
||||
findPreference("category_gpu"),
|
||||
findPreference("category_hacks"),
|
||||
findPreference("category_audio"),
|
||||
findPreference("category_debug")
|
||||
).forEach { it?.dependency = "use_custom_settings" }
|
||||
|
||||
// Uncheck `disable_frame_throttling` if `force_triple_buffering` gets disabled
|
||||
val disableFrameThrottlingPref = findPreference<CheckBoxPreference>("disable_frame_throttling")!!
|
||||
findPreference<CheckBoxPreference>("force_triple_buffering")?.setOnPreferenceChangeListener { _, newValue ->
|
||||
if (newValue == false)
|
||||
disableFrameThrottlingPref.isChecked = false
|
||||
true
|
||||
}
|
||||
|
||||
// Only show debug settings in debug builds
|
||||
@Suppress("SENSELESS_COMPARISON")
|
||||
if (BuildConfig.BUILD_TYPE != "release")
|
||||
findPreference<Preference>("category_debug")?.isVisible = true
|
||||
|
||||
if (!GpuDriverHelper.supportsForceMaxGpuClocks()) {
|
||||
val forceMaxGpuClocksPref = findPreference<CheckBoxPreference>("force_max_gpu_clocks")!!
|
||||
forceMaxGpuClocksPref.isSelectable = false
|
||||
forceMaxGpuClocksPref.isChecked = false
|
||||
forceMaxGpuClocksPref.summary = context!!.getString(R.string.force_max_gpu_clocks_desc_unsupported)
|
||||
}
|
||||
|
||||
findPreference<GpuDriverPreference>("gpu_driver")?.item = item
|
||||
}
|
||||
|
||||
override fun onDisplayPreferenceDialog(preference : Preference) {
|
||||
if (preference is IntegerListPreference) {
|
||||
// Check if dialog is already showing
|
||||
if (parentFragmentManager.findFragmentByTag(DIALOG_FRAGMENT_TAG) != null)
|
||||
return
|
||||
|
||||
val dialogFragment = IntegerListPreference.IntegerListPreferenceDialogFragmentCompat.newInstance(preference.getKey())
|
||||
@Suppress("DEPRECATION")
|
||||
dialogFragment.setTargetFragment(this, 0) // androidx.preference.PreferenceDialogFragmentCompat depends on the target fragment being set correctly even though it's deprecated
|
||||
dialogFragment.show(parentFragmentManager, DIALOG_FRAGMENT_TAG)
|
||||
} else {
|
||||
super.onDisplayPreferenceDialog(preference)
|
||||
}
|
||||
}
|
||||
}
|
@ -25,8 +25,12 @@ class SettingsActivity : AppCompatActivity() {
|
||||
|
||||
/**
|
||||
* The instance of [PreferenceFragmentCompat] that is shown inside [R.id.settings]
|
||||
* Retrieves extras from the intent if any and instantiates the appropriate fragment
|
||||
*/
|
||||
private val preferenceFragment by lazy {
|
||||
if (intent.hasExtra("item"))
|
||||
GameSettingsFragment().apply { arguments = intent.extras }
|
||||
else
|
||||
GlobalSettingsFragment()
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:nextFocusRight="@id/game_play"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingBottom="16dp">
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/game_icon"
|
||||
@ -36,9 +36,8 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
app:layout_constraintHeight_min="140dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHeight_min="140dp"
|
||||
app:layout_constraintStart_toEndOf="@id/game_icon"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
@ -86,43 +85,59 @@
|
||||
app:layout_constraintTop_toBottomOf="@id/game_version"
|
||||
tools:text="Nintendo" />
|
||||
|
||||
<com.google.android.flexbox.FlexboxLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:flexWrap="nowrap"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/game_author"
|
||||
app:layout_constraintVertical_bias="1">
|
||||
|
||||
<Button
|
||||
android:id="@+id/game_play"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton.Icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/play"
|
||||
android:focusedByDefault="true"
|
||||
android:text="@string/play"
|
||||
android:textColor="?attr/colorAccent"
|
||||
app:icon="@drawable/ic_play"
|
||||
app:iconTint="?attr/colorAccent"
|
||||
app:layout_flexGrow="1"
|
||||
app:layout_maxWidth="180dp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/game_pin"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton.Icon"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:contentDescription="@string/pin"
|
||||
android:textColor="?attr/colorAccent"
|
||||
app:icon="@drawable/ic_add_home"
|
||||
app:iconGravity="textStart"
|
||||
android:padding="0dp"
|
||||
app:iconPadding="0dp" />
|
||||
</com.google.android.flexbox.FlexboxLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<com.google.android.flexbox.FlexboxLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
app:flexWrap="nowrap"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/game_icon"
|
||||
app:layout_constraintVertical_bias="1">
|
||||
|
||||
<Button
|
||||
android:id="@+id/game_play"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton.Icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/play"
|
||||
android:focusedByDefault="true"
|
||||
android:text="@string/play"
|
||||
android:textColor="?attr/colorAccent"
|
||||
app:icon="@drawable/ic_play"
|
||||
app:iconTint="?attr/colorAccent"
|
||||
app:layout_flexGrow="1"
|
||||
app:layout_maxWidth="140dp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/game_settings"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton.Icon"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:contentDescription="@string/settings"
|
||||
android:padding="0dp"
|
||||
android:textColor="?attr/colorAccent"
|
||||
app:icon="@drawable/ic_settings"
|
||||
app:iconGravity="textStart"
|
||||
app:iconPadding="0dp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/game_pin"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton.Icon"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:contentDescription="@string/pin"
|
||||
android:padding="0dp"
|
||||
android:textColor="?attr/colorAccent"
|
||||
app:icon="@drawable/ic_add_home"
|
||||
app:iconGravity="textStart"
|
||||
app:iconPadding="0dp" />
|
||||
|
||||
</com.google.android.flexbox.FlexboxLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</LinearLayout>
|
||||
|
@ -43,6 +43,14 @@
|
||||
<string name="select_action">Always Show Game Information</string>
|
||||
<string name="select_action_desc_on">Game information will be shown on clicking a game</string>
|
||||
<string name="select_action_desc_off">Game information will only be shown on long-clicking a game</string>
|
||||
<!-- Settings - Game -->
|
||||
<string name="game">Game</string>
|
||||
<string name="use_custom_settings">Enable Custom Settings</string>
|
||||
<string name="use_custom_settings_desc_on">Custom settings are enabled for this game</string>
|
||||
<string name="use_custom_settings_desc_off">Custom settings are disabled for this game</string>
|
||||
<string name="reset_custom_settings">Reset Custom Settings</string>
|
||||
<string name="reset_custom_settings_desc">Reset custom settings to the default values</string>
|
||||
<string name="reset_settings_warning">Are you sure you want to reset all settings to the default values? <b>Current settings will be lost</b></string>
|
||||
<!-- Settings - System -->
|
||||
<string name="system">System</string>
|
||||
<string name="use_docked">Use Docked Mode</string>
|
||||
|
17
app/src/main/res/xml/custom_game_preferences.xml
Normal file
17
app/src/main/res/xml/custom_game_preferences.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<PreferenceCategory
|
||||
android:key="category_game"
|
||||
android:title="@string/game">
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="false"
|
||||
android:summaryOff="@string/use_custom_settings_desc_off"
|
||||
android:summaryOn="@string/use_custom_settings_desc_on"
|
||||
app:key="use_custom_settings"
|
||||
app:title="@string/use_custom_settings" />
|
||||
<emu.skyline.preference.ResetSettingsPreference
|
||||
android:summary="@string/reset_custom_settings_desc"
|
||||
app:key="reset_custom_settings"
|
||||
app:title="@string/reset_custom_settings" />
|
||||
</PreferenceCategory>
|
||||
</androidx.preference.PreferenceScreen>
|
Loading…
Reference in New Issue
Block a user