From 1f7c48769e93c00ccaeaf7c93256b308af4a9116 Mon Sep 17 00:00:00 2001 From: xbzk Date: Thu, 5 Mar 2026 08:59:09 -0300 Subject: [PATCH] huge set of interface fixes so background can make sense --- .../adapters/GamePropertiesAdapter.kt | 40 +++-- .../org/yuzu/yuzu_emu/dialogs/LobbyBrowser.kt | 4 +- .../features/settings/model/Settings.kt | 12 +- .../features/settings/ui/SettingsFragment.kt | 53 ++++--- .../settings/ui/SettingsFragmentPresenter.kt | 146 ++++++++++++------ .../yuzu/yuzu_emu/fragments/AboutFragment.kt | 17 +- .../yuzu/yuzu_emu/fragments/AddonsFragment.kt | 9 +- .../fragments/AppletLauncherFragment.kt | 13 +- .../fragments/DriverFetcherFragment.kt | 14 ++ .../fragments/DriverManagerFragment.kt | 13 +- .../fragments/FreedrenoSettingsFragment.kt | 40 +++-- .../yuzu_emu/fragments/GameFoldersFragment.kt | 11 ++ .../yuzu_emu/fragments/GameInfoFragment.kt | 13 +- .../fragments/GamePropertiesFragment.kt | 36 +++-- .../fragments/HomeSettingsFragment.kt | 43 ++++-- .../yuzu_emu/fragments/InstallableFragment.kt | 13 +- .../fragments/ProfileManagerFragment.kt | 7 + .../org/yuzu/yuzu_emu/ui/GamesFragment.kt | 7 + .../yuzu/yuzu_emu/utils/BackgroundHelper.kt | 64 ++++++++ .../res/drawable-nodpi/eden_background.webp | Bin 0 -> 46754 bytes .../main/res/drawable/item_release_box.xml | 4 +- .../main/res/layout-land/fragment_games.xml | 45 +++++- .../layout-w1000dp/card_installable_icon.xml | 115 ++++++++------ .../main/res/layout-w600dp/fragment_about.xml | 12 ++ .../res/layout-w600dp/fragment_game_info.xml | 10 ++ .../fragment_game_properties.xml | 14 ++ .../main/res/layout/card_driver_option.xml | 8 +- .../app/src/main/res/layout/card_folder.xml | 8 +- .../src/main/res/layout/card_game_grid.xml | 13 +- .../res/layout/card_game_grid_compact.xml | 8 +- .../src/main/res/layout/card_game_list.xml | 4 +- .../src/main/res/layout/card_home_option.xml | 8 +- .../src/main/res/layout/card_installable.xml | 12 +- .../main/res/layout/card_installable_icon.xml | 85 +++++----- .../main/res/layout/card_simple_outlined.xml | 13 +- .../main/res/layout/dialog_lobby_browser.xml | 21 ++- .../res/layout/dialog_multiplayer_connect.xml | 7 +- .../src/main/res/layout/fragment_about.xml | 35 ++++- .../src/main/res/layout/fragment_addons.xml | 16 ++ .../res/layout/fragment_applet_launcher.xml | 13 ++ .../res/layout/fragment_driver_fetcher.xml | 17 ++ .../res/layout/fragment_driver_manager.xml | 19 ++- .../src/main/res/layout/fragment_folders.xml | 19 ++- .../layout/fragment_freedreno_settings.xml | 53 ++++--- .../main/res/layout/fragment_game_info.xml | 10 ++ .../res/layout/fragment_game_properties.xml | 20 +++ .../src/main/res/layout/fragment_games.xml | 45 +++++- .../res/layout/fragment_home_settings.xml | 90 ++++++++--- .../main/res/layout/fragment_installables.xml | 13 ++ .../res/layout/fragment_profile_manager.xml | 13 ++ .../src/main/res/layout/fragment_settings.xml | 38 ++--- .../layout/item_secondary_action_button.xml | 7 + .../src/main/res/layout/list_item_addon.xml | 12 +- .../app/src/main/res/values/arrays.xml | 9 ++ .../app/src/main/res/values/strings.xml | 6 + .../app/src/main/res/values/styles.xml | 16 ++ 56 files changed, 1066 insertions(+), 327 deletions(-) create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BackgroundHelper.kt create mode 100644 src/android/app/src/main/res/drawable-nodpi/eden_background.webp create mode 100644 src/android/app/src/main/res/layout/item_secondary_action_button.xml diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt index a8ec82e560..5566423af6 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2023 yuzu Emulator Project @@ -10,6 +10,8 @@ import android.view.LayoutInflater import android.view.ViewGroup import androidx.core.content.res.ResourcesCompat import androidx.lifecycle.LifecycleOwner +import com.google.android.material.button.MaterialButton +import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.databinding.CardInstallableIconBinding import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding import org.yuzu.yuzu_emu.model.GameProperty @@ -89,29 +91,33 @@ class GamePropertiesAdapter( val hasVisibleActions = submenuProperty.secondaryActions?.any { it.isShown } == true + binding.layoutSecondaryActions.removeAllViews() + binding.dividerSecondaryActions.setVisible(false) if (hasVisibleActions) { - binding.dividerSecondaryActions.setVisible(true) binding.layoutSecondaryActions.setVisible(true) - submenuProperty.secondaryActions!!.forEach { secondaryAction -> - if (secondaryAction.isShown) { - val button = com.google.android.material.button.MaterialButton( - binding.root.context, - null, - com.google.android.material.R.attr.materialButtonOutlinedStyle - ).apply { - setIconResource(secondaryAction.iconId) - iconSize = (18 * binding.root.context.resources.displayMetrics.density).toInt() - text = binding.root.context.getString(secondaryAction.descriptionId) - contentDescription = binding.root.context.getString(secondaryAction.descriptionId) - setOnClickListener { secondaryAction.action.invoke() } - } - binding.layoutSecondaryActions.addView(button) + val visibleActions = submenuProperty.secondaryActions!!.filter { it.isShown } + val inflater = LayoutInflater.from(binding.root.context) + visibleActions.forEachIndexed { index, secondaryAction -> + val button = inflater.inflate( + R.layout.item_secondary_action_button, + binding.layoutSecondaryActions, + false + ) as MaterialButton + button.setIconResource(secondaryAction.iconId) + button.text = "" + button.contentDescription = binding.root.context + .getString(secondaryAction.descriptionId) + button.tooltipText = binding.root.context + .getString(secondaryAction.descriptionId) + if (index == visibleActions.lastIndex) { + (button.layoutParams as ViewGroup.MarginLayoutParams).marginEnd = 0 } + button.setOnClickListener { secondaryAction.action.invoke() } + binding.layoutSecondaryActions.addView(button) } } else { - binding.dividerSecondaryActions.setVisible(false) binding.layoutSecondaryActions.setVisible(false) } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/LobbyBrowser.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/LobbyBrowser.kt index 57fd551e02..95f92237cf 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/LobbyBrowser.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/LobbyBrowser.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later package org.yuzu.yuzu_emu.dialogs @@ -30,6 +30,7 @@ import org.yuzu.yuzu_emu.databinding.DialogLobbyBrowserBinding import org.yuzu.yuzu_emu.databinding.ItemLobbyRoomBinding import org.yuzu.yuzu_emu.features.settings.model.StringSetting import org.yuzu.yuzu_emu.network.NetPlayManager +import org.yuzu.yuzu_emu.utils.BackgroundHelper import java.util.Locale class LobbyBrowser(context: Context) : BottomSheetDialog(context) { @@ -46,6 +47,7 @@ class LobbyBrowser(context: Context) : BottomSheetDialog(context) { binding = DialogLobbyBrowserBinding.inflate(layoutInflater) setContentView(binding.root) + BackgroundHelper.applyBackground(binding.backgroundLogo, context) binding.emptyRefreshButton.setOnClickListener { binding.progressBar.visibility = View.VISIBLE 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 37a331b385..d15f03652c 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 @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later package org.yuzu.yuzu_emu.features.settings.model @@ -27,7 +27,7 @@ object Settings { SECTION_APP_SETTINGS(R.string.app_settings), SECTION_CUSTOM_PATHS(R.string.preferences_custom_paths), SECTION_DEBUG(R.string.preferences_debug), - SECTION_FREEDRENO(R.string.gpu_driver_settings), + SECTION_FREEDRENO(R.string.freedreno_settings_title), SECTION_APPLETS(R.string.applets_menu); } @@ -105,6 +105,14 @@ object Settings { const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds" const val PREF_STATIC_THEME_COLOR = "StaticThemeColor" + // App background preference keys + const val PREF_HOME_BACKGROUND_STYLE = "HomeBackgroundStyle" + const val HOME_BACKGROUND_STYLE_NONE = 0 + const val HOME_BACKGROUND_STYLE_EDEN = 1 + const val HOME_BACKGROUND_STYLE_DEFAULT = HOME_BACKGROUND_STYLE_NONE + const val PREF_HOME_BACKGROUND_ALPHA = "HomeBackgroundAlpha" + const val HOME_BACKGROUND_ALPHA_DEFAULT = 40 + enum class EmulationOrientation(val int: Int) { Unspecified(0), SensorLandscape(5), 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 2f527b5f62..00dc7883f8 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 @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2023 yuzu Emulator Project @@ -33,6 +33,7 @@ import org.yuzu.yuzu_emu.features.input.NativeInput import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.view.PathSetting import org.yuzu.yuzu_emu.fragments.MessageDialogFragment +import org.yuzu.yuzu_emu.utils.BackgroundHelper import org.yuzu.yuzu_emu.utils.PathUtil import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins import org.yuzu.yuzu_emu.utils.* @@ -98,23 +99,8 @@ class SettingsFragment : Fragment() { activity ) - binding.toolbarSettingsLayout.title = if (args.menuTag == Settings.MenuTag.SECTION_ROOT && - args.game != null - ) { - args.game!!.title - } else { - when (args.menuTag) { - Settings.MenuTag.SECTION_INPUT_PLAYER_ONE -> Settings.getPlayerString(1) - Settings.MenuTag.SECTION_INPUT_PLAYER_TWO -> Settings.getPlayerString(2) - Settings.MenuTag.SECTION_INPUT_PLAYER_THREE -> Settings.getPlayerString(3) - Settings.MenuTag.SECTION_INPUT_PLAYER_FOUR -> Settings.getPlayerString(4) - Settings.MenuTag.SECTION_INPUT_PLAYER_FIVE -> Settings.getPlayerString(5) - Settings.MenuTag.SECTION_INPUT_PLAYER_SIX -> Settings.getPlayerString(6) - Settings.MenuTag.SECTION_INPUT_PLAYER_SEVEN -> Settings.getPlayerString(7) - Settings.MenuTag.SECTION_INPUT_PLAYER_EIGHT -> Settings.getPlayerString(8) - else -> getString(args.menuTag.titleId) - } - } + val toolbarTitle = resolveToolbarTitle() + configureToolbar(toolbarTitle) binding.listSettings.apply { adapter = settingsAdapter @@ -193,10 +179,16 @@ class SettingsFragment : Fragment() { } presenter.onViewCreated() + applyBackgroundPreference() setInsets() } + override fun onResume() { + super.onResume() + applyBackgroundPreference() + } + private fun getPlayerIndex(): Int = when (args.menuTag) { Settings.MenuTag.SECTION_INPUT_PLAYER_ONE -> 0 @@ -210,6 +202,27 @@ class SettingsFragment : Fragment() { else -> -1 } + private fun resolveToolbarTitle(): String { + if (args.menuTag == Settings.MenuTag.SECTION_ROOT && args.game != null) { + return args.game!!.title + } + return when (args.menuTag) { + Settings.MenuTag.SECTION_INPUT_PLAYER_ONE -> Settings.getPlayerString(1) + Settings.MenuTag.SECTION_INPUT_PLAYER_TWO -> Settings.getPlayerString(2) + Settings.MenuTag.SECTION_INPUT_PLAYER_THREE -> Settings.getPlayerString(3) + Settings.MenuTag.SECTION_INPUT_PLAYER_FOUR -> Settings.getPlayerString(4) + Settings.MenuTag.SECTION_INPUT_PLAYER_FIVE -> Settings.getPlayerString(5) + Settings.MenuTag.SECTION_INPUT_PLAYER_SIX -> Settings.getPlayerString(6) + Settings.MenuTag.SECTION_INPUT_PLAYER_SEVEN -> Settings.getPlayerString(7) + Settings.MenuTag.SECTION_INPUT_PLAYER_EIGHT -> Settings.getPlayerString(8) + else -> getString(args.menuTag.titleId) + } + } + + private fun configureToolbar(title: String) { + binding.toolbarSettings.title = title + } + private fun setInsets() { ViewCompat.setOnApplyWindowInsetsListener( binding.root @@ -228,6 +241,10 @@ class SettingsFragment : Fragment() { } } + private fun applyBackgroundPreference() { + BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext()) + } + private fun hasAllFilesPermission(): Boolean { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { Environment.isExternalStorageManager() 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 77104e0614..f90e0e1cfa 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 @@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.features.settings.ui import android.annotation.SuppressLint import android.os.Build import android.widget.Toast -import androidx.preference.PreferenceManager import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication @@ -27,11 +26,10 @@ import org.yuzu.yuzu_emu.features.settings.model.Settings.MenuTag import org.yuzu.yuzu_emu.features.settings.model.ShortSetting import org.yuzu.yuzu_emu.features.settings.model.StringSetting import org.yuzu.yuzu_emu.features.settings.model.view.* -import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.InputHandler import org.yuzu.yuzu_emu.utils.NativeConfig import org.yuzu.yuzu_emu.utils.DirectoryInitialization -import androidx.core.content.edit +import org.yuzu.yuzu_emu.utils.BackgroundHelper import androidx.fragment.app.FragmentActivity import org.yuzu.yuzu_emu.fragments.MessageDialogFragment @@ -183,16 +181,6 @@ class SettingsFragmentPresenter( menuKey = MenuTag.SECTION_DEBUG ) ) - if (GpuDriverHelper.isAdrenoGpu() && !NativeConfig.isPerGameConfigLoaded()) { - add( - SubmenuSetting( - titleId = R.string.gpu_driver_settings, - descriptionId = R.string.freedreno_settings_title, - iconId = R.drawable.ic_graphics, - menuKey = MenuTag.SECTION_FREEDRENO - ) - ) - } add( SubmenuSetting( titleId = R.string.applets_menu, @@ -1084,27 +1072,6 @@ class SettingsFragmentPresenter( add(HeaderSetting(R.string.theme_and_color)) - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - add( - SingleChoiceSetting( - theme, - titleId = R.string.change_app_theme, - choicesId = R.array.themeEntriesA12, - valuesId = R.array.themeValuesA12 - ) - ) - } else { - add( - SingleChoiceSetting( - theme, - titleId = R.string.change_app_theme, - choicesId = R.array.themeEntries, - valuesId = R.array.themeValues - ) - ) - } - val themeMode: AbstractIntSetting = object : AbstractIntSetting { override fun getInt(needsGlobal: Boolean): Int = IntSetting.THEME_MODE.getInt() override fun setInt(value: Int) { @@ -1126,6 +1093,35 @@ class SettingsFragmentPresenter( } } + add( + SingleChoiceSetting( + themeMode, + titleId = R.string.change_theme_mode, + choicesId = R.array.themeModeEntries, + valuesId = R.array.themeModeValues + ) + ) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + add( + SingleChoiceSetting( + theme, + titleId = R.string.change_app_theme, + choicesId = R.array.themeEntriesA12, + valuesId = R.array.themeValuesA12 + ) + ) + } else { + add( + SingleChoiceSetting( + theme, + titleId = R.string.change_app_theme, + choicesId = R.array.themeEntries, + valuesId = R.array.themeValues + ) + ) + } + val staticThemeColor: AbstractIntSetting = object : AbstractIntSetting { override fun getInt(needsGlobal: Boolean): Int = IntSetting.STATIC_THEME_COLOR.getInt(needsGlobal) @@ -1149,15 +1145,6 @@ class SettingsFragmentPresenter( } } - add( - SingleChoiceSetting( - themeMode, - titleId = R.string.change_theme_mode, - choicesId = R.array.themeModeEntries, - valuesId = R.array.themeModeValues - ) - ) - if (IntSetting.THEME.getInt() != 1) { add( SingleChoiceSetting( @@ -1201,6 +1188,79 @@ class SettingsFragmentPresenter( ) ) + val backgroundStyleSetting: AbstractIntSetting = object : AbstractIntSetting { + override fun getInt(needsGlobal: Boolean): Int = + BackgroundHelper.getBackgroundStyle(context) + + override fun setInt(value: Int) { + BackgroundHelper.setBackgroundStyle(context, value) + settingsViewModel.setShouldRecreate(true) + } + + override val key: String = Settings.PREF_HOME_BACKGROUND_STYLE + override val isRuntimeModifiable: Boolean = true + override val pairedSettingKey: String = "" + override val isSwitchable: Boolean = false + override var global: Boolean = true + override val isSaveable: Boolean = true + override val defaultValue: Int = Settings.HOME_BACKGROUND_STYLE_DEFAULT + + override fun getValueAsString(needsGlobal: Boolean): String = + getInt(needsGlobal).toString() + + override fun reset() { + setInt(defaultValue) + } + } + + // add( + // SingleChoiceSetting( + // backgroundStyleSetting, + // titleId = R.string.home_background, + // descriptionId = R.string.home_background_description, + // choicesId = R.array.homeBackgroundEntries, + // valuesId = R.array.homeBackgroundValues + // ) + // ) + + val backgroundAlphaSetting: AbstractIntSetting = object : AbstractIntSetting { + override fun getInt(needsGlobal: Boolean): Int = + (BackgroundHelper.getBackgroundAlpha(context) * 100).toInt() + + override fun setInt(value: Int) { + BackgroundHelper.setBackgroundAlpha(context, value) + settingsViewModel.setShouldRecreate(true) + } + + override val key: String = Settings.PREF_HOME_BACKGROUND_ALPHA + override val isRuntimeModifiable: Boolean = true + override val pairedSettingKey: String = "" + override val isSwitchable: Boolean = false + override var global: Boolean = true + override val isSaveable: Boolean = true + override val defaultValue: Int = Settings.HOME_BACKGROUND_ALPHA_DEFAULT + + override fun getValueAsString(needsGlobal: Boolean): String = + getInt(needsGlobal).toString() + + override fun reset() { + setInt(defaultValue) + } + } + + if (BackgroundHelper.getBackgroundStyle(context) != Settings.HOME_BACKGROUND_STYLE_NONE) { + // add( + // SliderSetting( + // backgroundAlphaSetting, + // titleId = R.string.home_background_opacity, + // descriptionId = R.string.home_background_opacity_description, + // min = 0, + // max = 100, + // units = "%" + // ) + // ) + } + add(HeaderSetting(R.string.buttons)) add(BooleanSetting.ENABLE_FOLDER_BUTTON.key) add(BooleanSetting.ENABLE_QLAUNCH_BUTTON.key) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt index 9745970c5b..984e3ed1a0 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt @@ -1,7 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -// SPDX-FileCopyrightText: 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later package org.yuzu.yuzu_emu.fragments @@ -28,6 +25,7 @@ import org.yuzu.yuzu_emu.BuildConfig import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.databinding.FragmentAboutBinding import org.yuzu.yuzu_emu.model.HomeViewModel +import org.yuzu.yuzu_emu.utils.BackgroundHelper import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins import org.yuzu.yuzu_emu.NativeLibrary @@ -54,7 +52,9 @@ class AboutFragment : Fragment() { } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) homeViewModel.setStatusBarShadeVisibility(visible = false) + applyBackgroundPreference() binding.toolbarAbout.setNavigationOnClickListener { binding.root.findNavController().popBackStack() @@ -108,6 +108,11 @@ class AboutFragment : Fragment() { setInsets() } + override fun onResume() { + super.onResume() + applyBackgroundPreference() + } + private fun openLink(link: String) { val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link)) startActivity(intent) @@ -130,4 +135,8 @@ class AboutFragment : Fragment() { windowInsets } + + private fun applyBackgroundPreference() { + BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext()) + } } 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 573549d84b..fe67041213 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 @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later package org.yuzu.yuzu_emu.fragments @@ -26,6 +26,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentAddonsBinding import org.yuzu.yuzu_emu.model.AddonViewModel import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.utils.AddonUtil +import org.yuzu.yuzu_emu.utils.BackgroundHelper import org.yuzu.yuzu_emu.utils.FileUtil.copyFilesTo import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins import org.yuzu.yuzu_emu.utils.collect @@ -60,6 +61,7 @@ class AddonsFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) homeViewModel.setStatusBarShadeVisibility(false) + applyBackgroundPreference() binding.toolbarAddons.setNavigationOnClickListener { binding.root.findNavController().popBackStack() @@ -122,6 +124,7 @@ class AddonsFragment : Fragment() { override fun onResume() { super.onResume() + applyBackgroundPreference() addonViewModel.refreshAddons() } @@ -201,4 +204,8 @@ class AddonsFragment : Fragment() { windowInsets } + + private fun applyBackgroundPreference() { + BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext()) + } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AppletLauncherFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AppletLauncherFragment.kt index 3ab171a8d4..4b55ed1ede 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AppletLauncherFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AppletLauncherFragment.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later package org.yuzu.yuzu_emu.fragments @@ -21,6 +21,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentAppletLauncherBinding import org.yuzu.yuzu_emu.model.Applet import org.yuzu.yuzu_emu.model.AppletInfo import org.yuzu.yuzu_emu.model.HomeViewModel +import org.yuzu.yuzu_emu.utils.BackgroundHelper import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins class AppletLauncherFragment : Fragment() { @@ -48,6 +49,7 @@ class AppletLauncherFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) homeViewModel.setStatusBarShadeVisibility(visible = false) + applyBackgroundPreference() binding.toolbarApplets.setNavigationOnClickListener { binding.root.findNavController().popBackStack() @@ -91,6 +93,11 @@ class AppletLauncherFragment : Fragment() { setInsets() } + override fun onResume() { + super.onResume() + applyBackgroundPreference() + } + private fun setInsets() = ViewCompat.setOnApplyWindowInsetsListener( binding.root @@ -108,4 +115,8 @@ class AppletLauncherFragment : Fragment() { windowInsets } + + private fun applyBackgroundPreference() { + BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext()) + } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverFetcherFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverFetcherFragment.kt index 525fbd9f91..37998a03d0 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverFetcherFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverFetcherFragment.kt @@ -29,6 +29,8 @@ import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.databinding.FragmentDriverFetcherBinding import org.yuzu.yuzu_emu.features.fetcher.DriverGroupAdapter import org.yuzu.yuzu_emu.model.DriverViewModel +import org.yuzu.yuzu_emu.model.HomeViewModel +import org.yuzu.yuzu_emu.utils.BackgroundHelper import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins import java.io.IOException @@ -87,6 +89,7 @@ class DriverFetcherFragment : Fragment() { private lateinit var driverGroupAdapter: DriverGroupAdapter private val driverViewModel: DriverViewModel by activityViewModels() + private val homeViewModel: HomeViewModel by activityViewModels() private fun parseAdrenoModel(): Int { if (gpuModel == null) { @@ -138,6 +141,8 @@ class DriverFetcherFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + homeViewModel.setStatusBarShadeVisibility(visible = false) + applyBackgroundPreference() binding.toolbarDrivers.setNavigationOnClickListener { binding.root.findNavController().popBackStack() @@ -152,6 +157,11 @@ class DriverFetcherFragment : Fragment() { fetchDrivers() } + override fun onResume() { + super.onResume() + applyBackgroundPreference() + } + private fun fetchDrivers() { binding.loadingIndicator.isVisible = true @@ -240,6 +250,10 @@ class DriverFetcherFragment : Fragment() { windowInsets } + private fun applyBackgroundPreference() { + BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext()) + } + data class Artifact(val url: URL, val name: String) data class Release( diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt index 23334f05eb..1d514ba9d4 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later package org.yuzu.yuzu_emu.fragments @@ -33,6 +33,7 @@ import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.utils.FileUtil import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.NativeConfig +import org.yuzu.yuzu_emu.utils.BackgroundHelper import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins import org.yuzu.yuzu_emu.utils.collect import java.io.File @@ -66,6 +67,7 @@ class DriverManagerFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) homeViewModel.setStatusBarShadeVisibility(visible = false) + applyBackgroundPreference() driverViewModel.onOpenDriverManager(args.game) if (NativeConfig.isPerGameConfigLoaded()) { @@ -138,6 +140,11 @@ class DriverManagerFragment : Fragment() { driverViewModel.onCloseDriverManager(args.game) } + override fun onResume() { + super.onResume() + applyBackgroundPreference() + } + private fun setInsets() = ViewCompat.setOnApplyWindowInsetsListener( binding.root @@ -256,4 +263,8 @@ class DriverManagerFragment : Fragment() { .setCancelable(false) .show() } + + private fun applyBackgroundPreference() { + BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext()) + } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/FreedrenoSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/FreedrenoSettingsFragment.kt index 40111272d5..0bf7200fd9 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/FreedrenoSettingsFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/FreedrenoSettingsFragment.kt @@ -20,8 +20,10 @@ import org.yuzu.yuzu_emu.adapters.FreedrenoPresetAdapter import org.yuzu.yuzu_emu.adapters.FreedrenoVariableAdapter import org.yuzu.yuzu_emu.databinding.FragmentFreedrenoSettingsBinding import org.yuzu.yuzu_emu.model.Game +import org.yuzu.yuzu_emu.utils.BackgroundHelper import org.yuzu.yuzu_emu.utils.NativeFreedrenoConfig import org.yuzu.yuzu_emu.utils.FreedrenoPresets +import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins class FreedrenoSettingsFragment : Fragment() { @@ -67,6 +69,7 @@ class FreedrenoSettingsFragment : Fragment() { setupAdapters() loadCurrentSettings() setupButtonListeners() + applyBackgroundPreference() setupWindowInsets() } @@ -74,10 +77,15 @@ class FreedrenoSettingsFragment : Fragment() { binding.toolbarFreedreno.setNavigationOnClickListener { requireActivity().onBackPressedDispatcher.onBackPressed() } - if (isPerGameConfig) { - binding.toolbarFreedreno.title = getString(R.string.freedreno_per_game_title) - binding.toolbarFreedreno.subtitle = game!!.title - } + + binding.toolbarFreedreno.title = getString( + if (isPerGameConfig) { + R.string.freedreno_per_game_title + } else { + R.string.freedreno_settings_title + } + ) + binding.toolbarFreedreno.subtitle = null } private fun setupAdapters() { @@ -175,16 +183,28 @@ class FreedrenoSettingsFragment : Fragment() { private fun setupWindowInsets() { ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, insets -> - val systemInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars()) - binding.root.updatePadding( - left = systemInsets.left, - right = systemInsets.right, - bottom = systemInsets.bottom - ) + val barInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars()) + val cutoutInsets = insets.getInsets(WindowInsetsCompat.Type.displayCutout()) + + val leftInsets = barInsets.left + cutoutInsets.left + val rightInsets = barInsets.right + cutoutInsets.right + + binding.appbarFreedreno.updateMargins(left = leftInsets, right = rightInsets) + binding.scrollFreedreno.updateMargins(left = leftInsets, right = rightInsets) + binding.scrollFreedreno.updatePadding(bottom = barInsets.bottom) insets } } + override fun onResume() { + super.onResume() + applyBackgroundPreference() + } + + private fun applyBackgroundPreference() { + BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext()) + } + private fun showSnackbar(message: String) { Snackbar.make(binding.root, message, Snackbar.LENGTH_SHORT).show() } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt index 9c43d2c6e1..b459c82719 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt @@ -26,6 +26,7 @@ import org.yuzu.yuzu_emu.model.GameDir import org.yuzu.yuzu_emu.model.GamesViewModel import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.ui.main.MainActivity +import org.yuzu.yuzu_emu.utils.BackgroundHelper import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins import org.yuzu.yuzu_emu.utils.collect @@ -57,6 +58,7 @@ class GameFoldersFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) homeViewModel.setStatusBarShadeVisibility(visible = false) + applyBackgroundPreference() binding.toolbarFolders.setNavigationOnClickListener { binding.root.findNavController().popBackStack() @@ -100,6 +102,11 @@ class GameFoldersFragment : Fragment() { setInsets() } + override fun onResume() { + super.onResume() + applyBackgroundPreference() + } + override fun onStop() { super.onStop() gamesViewModel.onCloseGameFoldersFragment() @@ -133,4 +140,8 @@ class GameFoldersFragment : Fragment() { windowInsets } + + private fun applyBackgroundPreference() { + BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext()) + } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt index 7863e40ff5..4c1f641a20 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later package org.yuzu.yuzu_emu.fragments @@ -26,6 +26,7 @@ import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.databinding.FragmentGameInfoBinding import org.yuzu.yuzu_emu.model.GameVerificationResult import org.yuzu.yuzu_emu.model.HomeViewModel +import org.yuzu.yuzu_emu.utils.BackgroundHelper import org.yuzu.yuzu_emu.utils.GameMetadata import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins @@ -60,6 +61,7 @@ class GameInfoFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) homeViewModel.setStatusBarShadeVisibility(false) + applyBackgroundPreference() binding.apply { toolbarInfo.title = args.game.title @@ -143,6 +145,11 @@ class GameInfoFragment : Fragment() { setInsets() } + override fun onResume() { + super.onResume() + applyBackgroundPreference() + } + private fun copyToClipboard(label: String, body: String) { val clipBoard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager @@ -175,4 +182,8 @@ class GameInfoFragment : Fragment() { windowInsets } + + private fun applyBackgroundPreference() { + BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext()) + } } 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 97b0470feb..6bf40607b5 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 @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later package org.yuzu.yuzu_emu.fragments @@ -48,6 +48,7 @@ import org.yuzu.yuzu_emu.utils.FileUtil import org.yuzu.yuzu_emu.utils.GameIconUtils import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.MemoryUtil +import org.yuzu.yuzu_emu.utils.BackgroundHelper import org.yuzu.yuzu_emu.utils.ViewUtils.marquee import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins import org.yuzu.yuzu_emu.utils.collect @@ -83,6 +84,7 @@ class GamePropertiesFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) homeViewModel.setStatusBarShadeVisibility(true) + applyBackgroundPreference() binding.buttonBack.setOnClickListener { view.findNavController().popBackStack() @@ -310,6 +312,21 @@ class GamePropertiesFragment : Fragment() { ) ) + if (!args.game.isHomebrew) { + add( + SubmenuProperty( + R.string.add_ons, + R.string.add_ons_description, + R.drawable.ic_edit, + action = { + val action = GamePropertiesFragmentDirections + .actionPerGamePropertiesFragmentToAddonsFragment(args.game) + binding.root.findNavController().navigate(action) + } + ) + ) + } + if (GpuDriverHelper.supportsCustomDriverLoading()) { add( SubmenuProperty( @@ -341,18 +358,6 @@ class GamePropertiesFragment : Fragment() { } if (!args.game.isHomebrew) { - add( - SubmenuProperty( - R.string.add_ons, - R.string.add_ons_description, - R.drawable.ic_edit, - action = { - val action = GamePropertiesFragmentDirections - .actionPerGamePropertiesFragmentToAddonsFragment(args.game) - binding.root.findNavController().navigate(action) - } - ) - ) add( InstallableProperty( R.string.save_data, @@ -484,6 +489,7 @@ class GamePropertiesFragment : Fragment() { override fun onResume() { super.onResume() + applyBackgroundPreference() driverViewModel.updateDriverNameForGame(args.game) getPlayTime() reloadList() @@ -708,4 +714,8 @@ class GamePropertiesFragment : Fragment() { ).show() } } + + private fun applyBackgroundPreference() { + BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext()) + } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt index 918478bf85..f186bb9bdd 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt @@ -1,9 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - package org.yuzu.yuzu_emu.fragments import android.Manifest @@ -43,8 +40,11 @@ import org.yuzu.yuzu_emu.model.DriverViewModel import org.yuzu.yuzu_emu.model.HomeSetting import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.ui.main.MainActivity +import org.yuzu.yuzu_emu.utils.BackgroundHelper import org.yuzu.yuzu_emu.utils.FileUtil +import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.Log +import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins class HomeSettingsFragment : Fragment() { private var _binding: FragmentHomeSettingsBinding? = null @@ -71,8 +71,13 @@ class HomeSettingsFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - homeViewModel.setStatusBarShadeVisibility(visible = true) + homeViewModel.setStatusBarShadeVisibility(visible = false) mainActivity = requireActivity() as MainActivity + applyBackgroundPreference() + binding.toolbarHomeSettings.setNavigationOnClickListener { + findNavController().popBackStack() + } + binding.toolbarHomeSettings.title = getString(R.string.preferences_settings) val optionsList: MutableList = mutableListOf().apply { add( @@ -144,6 +149,18 @@ class HomeSettingsFragment : Fragment() { driverViewModel.selectedDriverTitle ) ) + if (GpuDriverHelper.isAdrenoGpu()) { + add( + HomeSetting( + R.string.freedreno_settings_title, + R.string.gpu_driver_settings, + R.drawable.ic_graphics, + { + binding.root.findNavController().navigate(R.id.freedrenoSettingsFragment) + } + ) + ) + } add( HomeSetting( R.string.multiplayer, @@ -300,6 +317,7 @@ class HomeSettingsFragment : Fragment() { override fun onResume() { super.onResume() driverViewModel.updateDriverNameForGame(null) + applyBackgroundPreference() } override fun onDestroyView() { @@ -465,21 +483,28 @@ class HomeSettingsFragment : Fragment() { } private fun setInsets() = - ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view, windowInsets -> + ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, windowInsets -> val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) + binding.appbarHomeSettings.updateMargins( + left = barInsets.left + cutoutInsets.left, + right = barInsets.right + cutoutInsets.right + ) + binding.scrollViewSettings.updatePadding( - top = barInsets.top + bottom = barInsets.bottom ) binding.homeSettingsList.updatePadding( left = barInsets.left + cutoutInsets.left, - top = cutoutInsets.top, - right = barInsets.right + cutoutInsets.right, - bottom = barInsets.bottom + right = barInsets.right + cutoutInsets.right ) windowInsets } + + private fun applyBackgroundPreference() { + BackgroundHelper.applyBackground(binding.logoImage, requireContext()) + } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt index 1b94d5f1a6..c605552f3e 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later package org.yuzu.yuzu_emu.fragments @@ -32,6 +32,7 @@ import org.yuzu.yuzu_emu.ui.main.MainActivity import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.FileUtil import org.yuzu.yuzu_emu.utils.NativeConfig +import org.yuzu.yuzu_emu.utils.BackgroundHelper import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins import org.yuzu.yuzu_emu.utils.collect import java.io.BufferedOutputStream @@ -68,6 +69,7 @@ class InstallableFragment : Fragment() { val mainActivity = requireActivity() as MainActivity homeViewModel.setStatusBarShadeVisibility(visible = false) + applyBackgroundPreference() binding.toolbarInstallables.setNavigationOnClickListener { binding.root.findNavController().popBackStack() @@ -162,6 +164,11 @@ class InstallableFragment : Fragment() { setInsets() } + override fun onResume() { + super.onResume() + applyBackgroundPreference() + } + private fun setInsets() = ViewCompat.setOnApplyWindowInsetsListener( binding.root @@ -325,4 +332,8 @@ class InstallableFragment : Fragment() { } }.show(parentFragmentManager, ProgressDialogFragment.TAG) } + + private fun applyBackgroundPreference() { + BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext()) + } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProfileManagerFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProfileManagerFragment.kt index 6ee34105e7..dcc2299169 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProfileManagerFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProfileManagerFragment.kt @@ -21,6 +21,7 @@ import org.yuzu.yuzu_emu.adapters.ProfileAdapter import org.yuzu.yuzu_emu.databinding.FragmentProfileManagerBinding import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.UserProfile +import org.yuzu.yuzu_emu.utils.BackgroundHelper import org.yuzu.yuzu_emu.utils.NativeConfig class ProfileManagerFragment : Fragment() { @@ -49,6 +50,7 @@ class ProfileManagerFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) homeViewModel.setStatusBarShadeVisibility(visible = false) + applyBackgroundPreference() binding.toolbarProfiles.setNavigationOnClickListener { findNavController().popBackStack() @@ -75,6 +77,7 @@ class ProfileManagerFragment : Fragment() { override fun onResume() { super.onResume() loadProfiles() + applyBackgroundPreference() } private fun setupRecyclerView() { @@ -187,4 +190,8 @@ class ProfileManagerFragment : Fragment() { NativeConfig.saveGlobalConfig() _binding = null } + + private fun applyBackgroundPreference() { + BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext()) + } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt index 6931a1f9d5..5eccb23038 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt @@ -40,6 +40,7 @@ import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.GamesViewModel import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.ui.main.MainActivity +import org.yuzu.yuzu_emu.utils.BackgroundHelper import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible import org.yuzu.yuzu_emu.utils.collect import info.debatty.java.stringsimilarity.Jaccard @@ -106,6 +107,7 @@ class GamesFragment : Fragment() { super.onViewCreated(view, savedInstanceState) homeViewModel.setStatusBarShadeVisibility(true) mainActivity = requireActivity() as MainActivity + applyBackgroundPreference() if (savedInstanceState != null) { binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT)) @@ -252,6 +254,7 @@ class GamesFragment : Fragment() { override fun onResume() { super.onResume() + applyBackgroundPreference() if (getCurrentViewType() == GameAdapter.VIEW_TYPE_CAROUSEL) { (binding.gridGames as? CarouselRecyclerView)?.setupCarousel(true) (binding.gridGames as? CarouselRecyclerView)?.restoreScrollState(gamesViewModel.lastScrollPosition) @@ -497,6 +500,10 @@ class GamesFragment : Fragment() { binding.addDirectory.visibility = if (showFolder) View.VISIBLE else View.GONE } + private fun applyBackgroundPreference() { + BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext()) + } + private fun setInsets() = ViewCompat.setOnApplyWindowInsetsListener( binding.root diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BackgroundHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BackgroundHelper.kt new file mode 100644 index 0000000000..b15f3ec296 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BackgroundHelper.kt @@ -0,0 +1,64 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +package org.yuzu.yuzu_emu.utils + +import android.content.Context +import android.view.View +import androidx.core.content.edit +import androidx.preference.PreferenceManager +import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.features.settings.model.Settings + +object BackgroundHelper { + fun getBackgroundStyle(context: Context): Int { + return PreferenceManager.getDefaultSharedPreferences(context).getInt( + Settings.PREF_HOME_BACKGROUND_STYLE, + Settings.HOME_BACKGROUND_STYLE_DEFAULT + ) + } + + fun setBackgroundStyle(context: Context, style: Int) { + PreferenceManager.getDefaultSharedPreferences(context).edit { + putInt(Settings.PREF_HOME_BACKGROUND_STYLE, style) + } + } + + fun getBackgroundAlpha(context: Context): Float { + val alphaPercent = PreferenceManager.getDefaultSharedPreferences(context).getInt( + Settings.PREF_HOME_BACKGROUND_ALPHA, + Settings.HOME_BACKGROUND_ALPHA_DEFAULT + ).coerceIn(0, 100) + return alphaPercent / 100f + } + + fun setBackgroundAlpha(context: Context, alphaPercent: Int) { + PreferenceManager.getDefaultSharedPreferences(context).edit { + putInt(Settings.PREF_HOME_BACKGROUND_ALPHA, alphaPercent.coerceIn(0, 100)) + } + } + + fun applyBackground(view: View, context: Context) { + val isEnabled = getBackgroundStyle(context) != Settings.HOME_BACKGROUND_STYLE_NONE + val visibilityStrength = getBackgroundAlpha(context) + + val parent = view.parent as? View + val scrim = parent?.findViewById(R.id.background_scrim) + + view.visibility = if (isEnabled) View.VISIBLE else View.GONE + + if (!isEnabled) { + scrim?.visibility = View.GONE + return + } + + if (scrim != null) { + // Keep background image opaque; control perceived intensity through a cheap flat-color scrim. + view.alpha = 1f + scrim.visibility = View.VISIBLE + scrim.alpha = (1f - visibilityStrength).coerceIn(0f, 1f) + } else { + view.alpha = visibilityStrength + } + } +} diff --git a/src/android/app/src/main/res/drawable-nodpi/eden_background.webp b/src/android/app/src/main/res/drawable-nodpi/eden_background.webp new file mode 100644 index 0000000000000000000000000000000000000000..2af59a8dc486994e42ab1ecf1ad40840f1fd874d GIT binary patch literal 46754 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 literal 0 HcmV?d00001 diff --git a/src/android/app/src/main/res/drawable/item_release_box.xml b/src/android/app/src/main/res/drawable/item_release_box.xml index 2f2ada1961..4e4ec10dcd 100644 --- a/src/android/app/src/main/res/drawable/item_release_box.xml +++ b/src/android/app/src/main/res/drawable/item_release_box.xml @@ -1,7 +1,7 @@ - + @@ -11,4 +11,4 @@ android:top="12dp" android:right="12dp" android:bottom="12dp" /> - \ No newline at end of file + diff --git a/src/android/app/src/main/res/layout-land/fragment_games.xml b/src/android/app/src/main/res/layout-land/fragment_games.xml index da778eab69..0c9ec209d1 100644 --- a/src/android/app/src/main/res/layout-land/fragment_games.xml +++ b/src/android/app/src/main/res/layout-land/fragment_games.xml @@ -8,6 +8,29 @@ android:clipChildren="false" > + + + + @@ -103,7 +131,12 @@ style="@style/EdenCard" android:layout_width="42dp" android:layout_height="42dp" + android:background="@android:color/transparent" app:cardCornerRadius="21dp" + app:cardBackgroundColor="@android:color/transparent" + app:cardElevation="0dp" + app:strokeColor="?attr/colorOutline" + app:strokeWidth="1dp" android:padding="8dp" > @@ -127,7 +160,12 @@ style="@style/EdenCard" android:layout_width="42dp" android:layout_height="42dp" + android:background="@android:color/transparent" app:cardCornerRadius="21dp" + app:cardBackgroundColor="@android:color/transparent" + app:cardElevation="0dp" + app:strokeColor="?attr/colorOutline" + app:strokeWidth="1dp" android:padding="8dp" > @@ -151,7 +189,12 @@ style="@style/EdenCard" android:layout_width="42dp" android:layout_height="42dp" + android:background="@android:color/transparent" app:cardCornerRadius="21dp" + app:cardBackgroundColor="@android:color/transparent" + app:cardElevation="0dp" + app:strokeColor="?attr/colorOutline" + app:strokeWidth="1dp" android:padding="8dp" > @@ -239,4 +282,4 @@ app:rippleColor="#99FFFFFF" /> - \ No newline at end of file + diff --git a/src/android/app/src/main/res/layout-w1000dp/card_installable_icon.xml b/src/android/app/src/main/res/layout-w1000dp/card_installable_icon.xml index 59ee1aad30..6fe4256c49 100644 --- a/src/android/app/src/main/res/layout-w1000dp/card_installable_icon.xml +++ b/src/android/app/src/main/res/layout-w1000dp/card_installable_icon.xml @@ -12,70 +12,83 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" - android:orientation="horizontal" - android:gravity="center_vertical" + android:orientation="vertical" android:paddingHorizontal="24dp" android:paddingVertical="16dp"> - - + android:gravity="center_vertical" + android:orientation="horizontal"> - + - + android:layout_weight="1" + android:orientation="vertical"> + + + + + + -