From f0ba58f5aa3524a2f97b268dfd7578ba3ef433bc Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Thu, 20 Apr 2023 17:44:19 -0400 Subject: [PATCH] android: Add theme picker --- .../features/settings/model/Settings.kt | 2 + .../settings/ui/SettingsFragmentPresenter.kt | 59 ++++++++++++++++++- .../org/yuzu/yuzu_emu/ui/main/MainActivity.kt | 14 ++++- .../yuzu/yuzu_emu/ui/main/ThemeProvider.kt | 11 ++++ .../org/yuzu/yuzu_emu/utils/ThemeHelper.kt | 22 ++++++- .../app/src/main/res/values/arrays.xml | 16 +++++ .../app/src/main/res/values/strings.xml | 6 ++ 7 files changed, 127 insertions(+), 3 deletions(-) create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/ThemeProvider.kt 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 610f281b2..b00110e91 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 @@ -107,6 +107,7 @@ class Settings { const val SECTION_RENDERER = "Renderer" const val SECTION_AUDIO = "Audio" const val SECTION_CPU = "Cpu" + const val SECTION_THEME = "Theme" const val PREF_OVERLAY_INIT = "OverlayInit" const val PREF_CONTROL_SCALE = "controlScale" @@ -134,6 +135,7 @@ class Settings { const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay" const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch" + const val PREF_THEME = "Theme" private val configFileSectionsMap: MutableMap> = HashMap() 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 29faed72d..b49a8bbec 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 @@ -3,11 +3,15 @@ package org.yuzu.yuzu_emu.features.settings.ui +import android.content.SharedPreferences +import android.os.Build import android.text.TextUtils import androidx.appcompat.app.AppCompatActivity +import androidx.preference.PreferenceManager import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.YuzuApplication +import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting -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.view.* @@ -21,12 +25,15 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) private val settingsActivity get() = fragmentView.activityView as AppCompatActivity private val settings get() = fragmentView.activityView!!.settings + private lateinit var preferences: SharedPreferences + fun onCreate(menuTag: String, gameId: String) { this.gameId = gameId this.menuTag = menuTag } fun onViewCreated() { + preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) loadSettingsList() } @@ -55,6 +62,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) Settings.SECTION_SYSTEM -> addSystemSettings(sl) Settings.SECTION_RENDERER -> addGraphicsSettings(sl) Settings.SECTION_AUDIO -> addAudioSettings(sl) + Settings.SECTION_THEME -> addThemeSettings(sl) else -> { fragmentView.showToastMessage("Unimplemented menu", false) return @@ -99,6 +107,14 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) Settings.SECTION_AUDIO ) ) + add( + SubmenuSetting( + null, + R.string.preferences_theme, + 0, + Settings.SECTION_THEME + ) + ) } } @@ -300,4 +316,45 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) ) ) } + + private fun addThemeSettings(sl: ArrayList) { + settingsActivity.setTitle(R.string.preferences_theme) + sl.apply { + val theme: AbstractIntSetting = object : AbstractIntSetting { + override var int: Int + get() = preferences.getInt(Settings.PREF_THEME, 0) + set(value) { + preferences.edit().putInt(Settings.PREF_THEME, value).apply() + settingsActivity.recreate() + } + override val key: String? = null + override val section: String? = null + override val isRuntimeEditable: Boolean = true + override val valueAsString: String + get() = preferences.getInt(Settings.PREF_THEME, 0).toString() + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + add( + SingleChoiceSetting( + theme, + R.string.change_app_theme, + 0, + R.array.themeEntriesA12, + R.array.themeValuesA12 + ) + ) + } else { + add( + SingleChoiceSetting( + theme, + R.string.change_app_theme, + 0, + R.array.themeEntries, + R.array.themeValues + ) + ) + } + } + } } 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 c03c28aa1..a33469644 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 @@ -39,12 +39,14 @@ import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.utils.* import java.io.IOException -class MainActivity : AppCompatActivity() { +class MainActivity : AppCompatActivity(), ThemeProvider { private lateinit var binding: ActivityMainBinding private val homeViewModel: HomeViewModel by viewModels() private val gamesViewModel: GamesViewModel by viewModels() + override var themeId: Int = 0 + override fun onCreate(savedInstanceState: Bundle?) { val splashScreen = installSplashScreen() splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady } @@ -166,6 +168,11 @@ class MainActivity : AppCompatActivity() { }.start() } + override fun onResume() { + ThemeHelper.setCorrectTheme(this) + super.onResume() + } + override fun onDestroy() { EmulationActivity.tryDismissRunningNotification(this) super.onDestroy() @@ -180,6 +187,11 @@ class MainActivity : AppCompatActivity() { windowInsets } + override fun setTheme(resId: Int) { + super.setTheme(resId) + themeId = resId + } + private fun hasExtension(path: String, extension: String): Boolean { return path.substring(path.lastIndexOf(".") + 1).contains(extension) } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/ThemeProvider.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/ThemeProvider.kt new file mode 100644 index 000000000..511a6e4fa --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/ThemeProvider.kt @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.ui.main + +interface ThemeProvider { + /** + * Provides theme ID by overriding an activity's 'setTheme' method and returning that result + */ + var themeId: Int +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt index 481498f7b..72e2fb75e 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt @@ -10,15 +10,27 @@ import androidx.annotation.ColorInt import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import androidx.core.view.WindowCompat -import com.google.android.material.color.MaterialColors +import androidx.preference.PreferenceManager import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.YuzuApplication +import org.yuzu.yuzu_emu.features.settings.model.Settings +import org.yuzu.yuzu_emu.ui.main.ThemeProvider import kotlin.math.roundToInt object ThemeHelper { const val SYSTEM_BAR_ALPHA = 0.9f + private const val DEFAULT = 0 + private const val MATERIAL_YOU = 1 + @JvmStatic fun setTheme(activity: AppCompatActivity) { + val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) + when (preferences.getInt(Settings.PREF_THEME, 0)) { + DEFAULT -> activity.setTheme(R.style.Theme_Yuzu_Main) + MATERIAL_YOU -> activity.setTheme(R.style.Theme_Yuzu_Main_MaterialYou) + } + val windowController = WindowCompat.getInsetsController( activity.window, activity.window.decorView @@ -60,4 +72,12 @@ object ThemeHelper { Color.green(color), Color.blue(color) ) } + + fun setCorrectTheme(activity: AppCompatActivity) { + val currentTheme = (activity as ThemeProvider).themeId + setTheme(activity) + if (currentTheme != (activity as ThemeProvider).themeId) { + activity.recreate() + } + } } diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 628d43197..a98ccd985 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -183,4 +183,20 @@ @string/gamepad_screenshot + + @string/theme_default + + + 0 + + + + @string/theme_default + @string/theme_material_you + + + 0 + 1 + + diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 92fe67bf0..90c94d67f 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -103,6 +103,7 @@ System Graphics Audio + Theme and Color Your ROM is encrypted @@ -229,4 +230,9 @@ Preparing shaders Building shaders + + Change App Theme + Default + Material You +