diff options
Diffstat (limited to 'src/android/app')
12 files changed, 218 insertions, 27 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt index 71be2d0b2..0165cb2d1 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt @@ -24,7 +24,9 @@ enum class IntSetting(override val key: String) : AbstractIntSetting { THEME_MODE("theme_mode"), OVERLAY_SCALE("control_scale"), OVERLAY_OPACITY("control_opacity"), - LOCK_DRAWER("lock_drawer"); + LOCK_DRAWER("lock_drawer"), + VERTICAL_ALIGNMENT("vertical_alignment"), + FSR_SHARPENING_SLIDER("fsr_sharpening_slider"); override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt index fee80bb21..862c6c483 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt @@ -93,4 +93,15 @@ object Settings { entries.firstOrNull { it.int == int } ?: Unspecified } } + + enum class EmulationVerticalAlignment(val int: Int) { + Top(1), + Center(0), + Bottom(2); + + companion object { + fun from(int: Int): EmulationVerticalAlignment = + entries.firstOrNull { it.int == int } ?: Center + } + } } 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 12f7aa1ab..21ca97bc1 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 @@ -189,6 +189,16 @@ abstract class SettingsItem( ) ) put( + SliderSetting( + IntSetting.FSR_SHARPENING_SLIDER, + R.string.fsr_sharpness, + R.string.fsr_sharpness_description, + 0, + 100, + "%" + ) + ) + put( SingleChoiceSetting( IntSetting.RENDERER_ANTI_ALIASING, R.string.renderer_anti_aliasing, @@ -216,6 +226,15 @@ abstract class SettingsItem( ) ) put( + SingleChoiceSetting( + IntSetting.VERTICAL_ALIGNMENT, + R.string.vertical_alignment, + 0, + R.array.verticalAlignmentEntries, + R.array.verticalAlignmentValues + ) + ) + put( SwitchSetting( BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE, R.string.use_disk_shader_cache, 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 2ad2f4966..db1a58147 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 @@ -143,10 +143,12 @@ class SettingsFragmentPresenter( add(IntSetting.RENDERER_RESOLUTION.key) add(IntSetting.RENDERER_VSYNC.key) add(IntSetting.RENDERER_SCALING_FILTER.key) + add(IntSetting.FSR_SHARPENING_SLIDER.key) add(IntSetting.RENDERER_ANTI_ALIASING.key) add(IntSetting.MAX_ANISOTROPY.key) add(IntSetting.RENDERER_SCREEN_LAYOUT.key) add(IntSetting.RENDERER_ASPECT_RATIO.key) + add(IntSetting.VERTICAL_ALIGNMENT.key) add(BooleanSetting.PICTURE_IN_PICTURE.key) add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key) add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt index f5647fa95..872553ac4 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt @@ -104,7 +104,10 @@ class AddonsFragment : Fragment() { requireActivity(), titleId = R.string.addon_notice, descriptionId = R.string.addon_notice_description, - positiveAction = { addonViewModel.showModInstallPicker(true) } + dismissible = false, + positiveAction = { addonViewModel.showModInstallPicker(true) }, + negativeAction = {}, + negativeButtonTitleId = R.string.close ).show(parentFragmentManager, MessageDialogFragment.TAG) addonViewModel.showModNoticeDialog(false) } @@ -119,7 +122,8 @@ class AddonsFragment : Fragment() { requireActivity(), titleId = R.string.confirm_uninstall, descriptionId = R.string.confirm_uninstall_description, - positiveAction = { addonViewModel.onDeleteAddon(it) } + positiveAction = { addonViewModel.onDeleteAddon(it) }, + negativeAction = {} ).show(parentFragmentManager, MessageDialogFragment.TAG) addonViewModel.setAddonToDelete(null) } 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 44af896da..6b25cc525 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 @@ -15,7 +15,9 @@ import android.os.Handler import android.os.Looper import android.os.PowerManager import android.os.SystemClock +import android.util.Rational import android.view.* +import android.widget.FrameLayout import android.widget.TextView import android.widget.Toast import androidx.activity.OnBackPressedCallback @@ -24,6 +26,7 @@ import androidx.core.content.res.ResourcesCompat import androidx.core.graphics.Insets import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding import androidx.drawerlayout.widget.DrawerLayout import androidx.drawerlayout.widget.DrawerLayout.DrawerListener @@ -52,6 +55,7 @@ import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting import org.yuzu.yuzu_emu.features.settings.model.IntSetting import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.Settings.EmulationOrientation +import org.yuzu.yuzu_emu.features.settings.model.Settings.EmulationVerticalAlignment import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile import org.yuzu.yuzu_emu.model.DriverViewModel import org.yuzu.yuzu_emu.model.Game @@ -617,7 +621,46 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } private fun updateScreenLayout() { - binding.surfaceEmulation.setAspectRatio(null) + val verticalAlignment = + EmulationVerticalAlignment.from(IntSetting.VERTICAL_ALIGNMENT.getInt()) + val aspectRatio = when (IntSetting.RENDERER_ASPECT_RATIO.getInt()) { + 0 -> Rational(16, 9) + 1 -> Rational(4, 3) + 2 -> Rational(21, 9) + 3 -> Rational(16, 10) + else -> null // Best fit + } + when (verticalAlignment) { + EmulationVerticalAlignment.Top -> { + binding.surfaceEmulation.setAspectRatio(aspectRatio) + val params = FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + params.gravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL + binding.surfaceEmulation.layoutParams = params + } + + EmulationVerticalAlignment.Center -> { + binding.surfaceEmulation.setAspectRatio(null) + binding.surfaceEmulation.updateLayoutParams { + width = ViewGroup.LayoutParams.MATCH_PARENT + height = ViewGroup.LayoutParams.MATCH_PARENT + } + } + + EmulationVerticalAlignment.Bottom -> { + binding.surfaceEmulation.setAspectRatio(aspectRatio) + val params = + FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + params.gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL + binding.surfaceEmulation.layoutParams = params + } + } + emulationState.updateSurface() emulationActivity?.buildPictureInPictureParams() updateOrientation() } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt index d14b2c634..3ea5e16ca 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt @@ -243,7 +243,9 @@ class GamePropertiesFragment : Fragment() { requireActivity(), titleId = R.string.delete_save_data, descriptionId = R.string.delete_save_data_warning_description, - positiveAction = { + positiveButtonTitleId = android.R.string.cancel, + negativeButtonTitleId = android.R.string.ok, + negativeAction = { File(args.game.saveDir).deleteRecursively() Toast.makeText( YuzuApplication.appContext, diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt index 22b084b9a..c370964e1 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt @@ -4,7 +4,6 @@ package org.yuzu.yuzu_emu.fragments import android.app.Dialog -import android.content.DialogInterface import android.content.Intent import android.net.Uri import android.os.Bundle @@ -16,18 +15,52 @@ import androidx.lifecycle.ViewModelProvider import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.model.MessageDialogViewModel +import org.yuzu.yuzu_emu.utils.Log class MessageDialogFragment : DialogFragment() { private val messageDialogViewModel: MessageDialogViewModel by activityViewModels() override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val titleId = requireArguments().getInt(TITLE_ID) - val titleString = requireArguments().getString(TITLE_STRING)!! + val title = if (titleId != 0) { + getString(titleId) + } else { + requireArguments().getString(TITLE_STRING)!! + } + val descriptionId = requireArguments().getInt(DESCRIPTION_ID) - val descriptionString = requireArguments().getString(DESCRIPTION_STRING)!! + val description = if (descriptionId != 0) { + getString(descriptionId) + } else { + requireArguments().getString(DESCRIPTION_STRING)!! + } + + val positiveButtonId = requireArguments().getInt(POSITIVE_BUTTON_TITLE_ID) + val positiveButtonString = requireArguments().getString(POSITIVE_BUTTON_TITLE_STRING)!! + val positiveButton = if (positiveButtonId != 0) { + getString(positiveButtonId) + } else if (positiveButtonString.isNotEmpty()) { + positiveButtonString + } else if (messageDialogViewModel.positiveAction != null) { + getString(android.R.string.ok) + } else { + getString(R.string.close) + } + + val negativeButtonId = requireArguments().getInt(NEGATIVE_BUTTON_TITLE_ID) + val negativeButtonString = requireArguments().getString(NEGATIVE_BUTTON_TITLE_STRING)!! + val negativeButton = if (negativeButtonId != 0) { + getString(negativeButtonId) + } else if (negativeButtonString.isNotEmpty()) { + negativeButtonString + } else { + getString(android.R.string.cancel) + } + val helpLinkId = requireArguments().getInt(HELP_LINK) val dismissible = requireArguments().getBoolean(DISMISSIBLE) - val clearPositiveAction = requireArguments().getBoolean(CLEAR_POSITIVE_ACTION) + val clearPositiveAction = requireArguments().getBoolean(CLEAR_ACTIONS) + val showNegativeButton = requireArguments().getBoolean(SHOW_NEGATIVE_BUTTON) val builder = MaterialAlertDialogBuilder(requireContext()) @@ -35,21 +68,19 @@ class MessageDialogFragment : DialogFragment() { messageDialogViewModel.positiveAction = null } - if (messageDialogViewModel.positiveAction == null) { - builder.setPositiveButton(R.string.close, null) - } else { - builder.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int -> - messageDialogViewModel.positiveAction?.invoke() - }.setNegativeButton(android.R.string.cancel, null) + builder.setPositiveButton(positiveButton) { _, _ -> + messageDialogViewModel.positiveAction?.invoke() + } + if (messageDialogViewModel.negativeAction != null || showNegativeButton) { + builder.setNegativeButton(negativeButton) { _, _ -> + messageDialogViewModel.negativeAction?.invoke() + } } - if (titleId != 0) builder.setTitle(titleId) - if (titleString.isNotEmpty()) builder.setTitle(titleString) - - if (descriptionId != 0) { - builder.setMessage(Html.fromHtml(getString(descriptionId), Html.FROM_HTML_MODE_LEGACY)) + if (title.isNotEmpty()) builder.setTitle(title) + if (description.isNotEmpty()) { + builder.setMessage(Html.fromHtml(description, Html.FROM_HTML_MODE_LEGACY)) } - if (descriptionString.isNotEmpty()) builder.setMessage(descriptionString) if (helpLinkId != 0) { builder.setNeutralButton(R.string.learn_more) { _, _ -> @@ -76,8 +107,41 @@ class MessageDialogFragment : DialogFragment() { private const val DESCRIPTION_STRING = "DescriptionString" private const val HELP_LINK = "Link" private const val DISMISSIBLE = "Dismissible" - private const val CLEAR_POSITIVE_ACTION = "ClearPositiveAction" - + private const val CLEAR_ACTIONS = "ClearActions" + private const val POSITIVE_BUTTON_TITLE_ID = "PositiveButtonTitleId" + private const val POSITIVE_BUTTON_TITLE_STRING = "PositiveButtonTitleString" + private const val SHOW_NEGATIVE_BUTTON = "ShowNegativeButton" + private const val NEGATIVE_BUTTON_TITLE_ID = "NegativeButtonTitleId" + private const val NEGATIVE_BUTTON_TITLE_STRING = "NegativeButtonTitleString" + + /** + * Creates a new [MessageDialogFragment] instance. + * @param activity Activity that will hold a [MessageDialogViewModel] instance if using + * [positiveAction] or [negativeAction]. + * @param titleId String resource ID that will be used for the title. [titleString] used if 0. + * @param titleString String that will be used for the title. No title is set if empty. + * @param descriptionId String resource ID that will be used for the description. + * [descriptionString] used if 0. + * @param descriptionString String that will be used for the description. + * No description is set if empty. + * @param helpLinkId String resource ID that contains a help link. Will be added as a neutral + * button with the title R.string.help. + * @param dismissible Whether the dialog is dismissible or not. Typically used to ensure that + * the user clicks on one of the dialog buttons before closing. + * @param positiveButtonTitleId String resource ID that will be used for the positive button. + * [positiveButtonTitleString] used if 0. + * @param positiveButtonTitleString String that will be used for the positive button. + * android.R.string.close used if empty. android.R.string.ok will be used if [positiveAction] + * is not null. + * @param positiveAction Lambda to run when the positive button is clicked. + * @param showNegativeButton Normally the negative button isn't shown if there is no + * [negativeAction] set. This can override that behavior to always show a button. + * @param negativeButtonTitleId String resource ID that will be used for the negative button. + * [negativeButtonTitleString] used if 0. + * @param negativeButtonTitleString String that will be used for the negative button. + * android.R.string.cancel used if empty. + * @param negativeAction Lambda to run when the negative button is clicked + */ fun newInstance( activity: FragmentActivity? = null, titleId: Int = 0, @@ -86,16 +150,27 @@ class MessageDialogFragment : DialogFragment() { descriptionString: String = "", helpLinkId: Int = 0, dismissible: Boolean = true, - positiveAction: (() -> Unit)? = null + positiveButtonTitleId: Int = 0, + positiveButtonTitleString: String = "", + positiveAction: (() -> Unit)? = null, + showNegativeButton: Boolean = false, + negativeButtonTitleId: Int = 0, + negativeButtonTitleString: String = "", + negativeAction: (() -> Unit)? = null ): MessageDialogFragment { - var clearPositiveAction = false + var clearActions = false if (activity != null) { ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply { clear() this.positiveAction = positiveAction + this.negativeAction = negativeAction } } else { - clearPositiveAction = true + clearActions = true + } + + if (activity == null && (positiveAction == null || negativeAction == null)) { + Log.warning("[$TAG] Tried to set action with no activity!") } val dialog = MessageDialogFragment() @@ -106,7 +181,12 @@ class MessageDialogFragment : DialogFragment() { putString(DESCRIPTION_STRING, descriptionString) putInt(HELP_LINK, helpLinkId) putBoolean(DISMISSIBLE, dismissible) - putBoolean(CLEAR_POSITIVE_ACTION, clearPositiveAction) + putBoolean(CLEAR_ACTIONS, clearActions) + putInt(POSITIVE_BUTTON_TITLE_ID, positiveButtonTitleId) + putString(POSITIVE_BUTTON_TITLE_STRING, positiveButtonTitleString) + putBoolean(SHOW_NEGATIVE_BUTTON, showNegativeButton) + putInt(NEGATIVE_BUTTON_TITLE_ID, negativeButtonTitleId) + putString(NEGATIVE_BUTTON_TITLE_STRING, negativeButtonTitleString) } dialog.arguments = bundle return dialog diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/MessageDialogViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/MessageDialogViewModel.kt index 641c5cb17..2db005e49 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/MessageDialogViewModel.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/MessageDialogViewModel.kt @@ -7,8 +7,10 @@ import androidx.lifecycle.ViewModel class MessageDialogViewModel : ViewModel() { var positiveAction: (() -> Unit)? = null + var negativeAction: (() -> Unit)? = null fun clear() { positiveAction = null + negativeAction = null } } diff --git a/src/android/app/src/main/jni/android_settings.h b/src/android/app/src/main/jni/android_settings.h index 4a3bc8e53..00baf86a9 100644 --- a/src/android/app/src/main/jni/android_settings.h +++ b/src/android/app/src/main/jni/android_settings.h @@ -38,6 +38,13 @@ struct Values { Settings::Specialization::Default, true, true}; + Settings::Setting<s32> vertical_alignment{linkage, + 0, + "vertical_alignment", + Settings::Category::Android, + Settings::Specialization::Default, + true, + true}; Settings::SwitchableSetting<std::string, false> driver_path{linkage, "", "driver_path", Settings::Category::GpuDriver}; diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 4701913eb..1bd6455b4 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -292,4 +292,15 @@ <item>5</item> </integer-array> + <string-array name="verticalAlignmentEntries"> + <item>@string/top</item> + <item>@string/center</item> + <item>@string/bottom</item> + </string-array> + <integer-array name="verticalAlignmentValues"> + <item>1</item> + <item>0</item> + <item>2</item> + </integer-array> + </resources> diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 489e00107..78a4c958a 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -226,6 +226,8 @@ <string name="renderer_screen_layout">Orientation</string> <string name="renderer_aspect_ratio">Aspect ratio</string> <string name="renderer_scaling_filter">Window adapting filter</string> + <string name="fsr_sharpness">FSR sharpness</string> + <string name="fsr_sharpness_description">Determines how sharpened the image will look while using FSR\'s dynamic contrast</string> <string name="renderer_anti_aliasing">Anti-aliasing method</string> <string name="renderer_force_max_clock">Force maximum clocks (Adreno only)</string> <string name="renderer_force_max_clock_description">Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied).</string> @@ -558,6 +560,12 @@ <string name="mute">Mute</string> <string name="unmute">Unmute</string> + <!-- Emulation vertical alignment --> + <string name="vertical_alignment">Vertical alignment</string> + <string name="top">Top</string> + <string name="center">Center</string> + <string name="bottom">Bottom</string> + <!-- Licenses screen strings --> <string name="licenses">Licenses</string> <string name="license_fidelityfx_fsr" translatable="false">FidelityFX-FSR</string> |