Explorar o código

android: Add theme picker

Charles Lombardo %!s(int64=3) %!d(string=hai) anos
pai
achega
f0ba58f5aa

+ 2 - 0
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<String, List<String>> = HashMap()
 

+ 58 - 1
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<SettingsItem>) {
+        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
+                    )
+                )
+            }
+        }
+    }
 }

+ 13 - 1
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)
     }

+ 11 - 0
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
+}

+ 21 - 1
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()
+        }
+    }
 }

+ 16 - 0
src/android/app/src/main/res/values/arrays.xml

@@ -183,4 +183,20 @@
         <item>@string/gamepad_screenshot</item>
     </string-array>
 
+    <string-array name="themeEntries">
+        <item>@string/theme_default</item>
+    </string-array>
+    <integer-array name="themeValues">
+        <item>0</item>
+    </integer-array>
+
+    <string-array name="themeEntriesA12">
+        <item>@string/theme_default</item>
+        <item>@string/theme_material_you</item>
+    </string-array>
+    <integer-array name="themeValuesA12">
+        <item>0</item>
+        <item>1</item>
+    </integer-array>
+
 </resources>

+ 6 - 0
src/android/app/src/main/res/values/strings.xml

@@ -103,6 +103,7 @@
     <string name="preferences_system">System</string>
     <string name="preferences_graphics">Graphics</string>
     <string name="preferences_audio">Audio</string>
+    <string name="preferences_theme">Theme and Color</string>
 
     <!-- ROM loading errors -->
     <string name="loader_error_encrypted">Your ROM is encrypted</string>
@@ -229,4 +230,9 @@
     <string name="preparing_shaders">Preparing shaders</string>
     <string name="building_shaders">Building shaders</string>
 
+    <!-- Theme options -->
+    <string name="change_app_theme">Change App Theme</string>
+    <string name="theme_default">Default</string>
+    <string name="theme_material_you">Material You</string>
+
 </resources>