From b9fffa2e66025e7f4b3f8a4ff4e6c71a171452f8 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Fri, 11 Nov 2022 20:44:30 -0500 Subject: [PATCH] Android: Add theme mode switcher Similar to app themes, theme modes have to be loaded before directory initialization is ready. So we save the proper key the same way. --- Source/Android/app/build.gradle | 2 +- .../features/settings/model/IntSetting.java | 2 + .../ui/SettingsFragmentPresenter.java | 61 +++++++++++-- .../utils/DirectoryInitialization.java | 11 +++ .../dolphinemu/utils/ThemeHelper.java | 91 +++++++++++++++++-- .../app/src/main/res/drawable/ic_back.xml | 9 -- .../main/res/drawable/tv_card_background.xml | 2 +- .../app/src/main/res/values-night/bools.xml | 4 - .../app/src/main/res/values-v27/themes.xml | 8 -- .../app/src/main/res/values-v29/themes.xml | 2 +- .../app/src/main/res/values/arrays.xml | 11 +++ .../Android/app/src/main/res/values/bools.xml | 4 - .../app/src/main/res/values/strings.xml | 1 + .../app/src/main/res/values/styles.xml | 2 +- .../app/src/main/res/values/themes.xml | 5 +- .../Core/ConfigLoaders/IsSettingSaveable.cpp | 10 +- 16 files changed, 170 insertions(+), 55 deletions(-) delete mode 100644 Source/Android/app/src/main/res/drawable/ic_back.xml delete mode 100644 Source/Android/app/src/main/res/values-night/bools.xml delete mode 100644 Source/Android/app/src/main/res/values-v27/themes.xml delete mode 100644 Source/Android/app/src/main/res/values/bools.xml diff --git a/Source/Android/app/build.gradle b/Source/Android/app/build.gradle index f13c7f81b4..23f9e6e3d6 100644 --- a/Source/Android/app/build.gradle +++ b/Source/Android/app/build.gradle @@ -96,7 +96,7 @@ android { dependencies { coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' - implementation 'androidx.appcompat:appcompat:1.5.0' + implementation 'androidx.appcompat:appcompat:1.5.1' implementation 'androidx.exifinterface:exifinterface:1.3.3' implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.recyclerview:recyclerview:1.2.1' diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/IntSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/IntSetting.java index 11fc2a0046..c7f2b0c54c 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/IntSetting.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/IntSetting.java @@ -34,6 +34,8 @@ public enum IntSetting implements AbstractIntSetting MAIN_EMULATION_ORIENTATION(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "EmulationOrientation", ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE), MAIN_INTERFACE_THEME(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "InterfaceTheme", 0), + MAIN_INTERFACE_THEME_MODE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, + "InterfaceThemeMode", -1), MAIN_LAST_PLATFORM_TAB(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "LastPlatformTab", 0), MAIN_MOTION_CONTROLS(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "MotionControls", 1), MAIN_IR_MODE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "IRMode", diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java index 1f073ace20..3c0838029b 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java @@ -8,6 +8,8 @@ import android.os.Build; import android.os.Bundle; import android.text.TextUtils; +import androidx.appcompat.app.AppCompatActivity; + import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.activities.UserDataActivity; @@ -328,33 +330,37 @@ public final class SettingsFragmentPresenter AbstractIntSetting appTheme = new AbstractIntSetting() { - @Override public boolean isOverridden(Settings settings) + @Override + public boolean isOverridden(Settings settings) { return IntSetting.MAIN_INTERFACE_THEME.isOverridden(settings); } - @Override public boolean isRuntimeEditable() + @Override + public boolean isRuntimeEditable() { // This only affects app UI return true; } - @Override public boolean delete(Settings settings) + @Override + public boolean delete(Settings settings) { - ThemeHelper.deleteThemeKey(mView.getActivity()); + ThemeHelper.deleteThemeKey((AppCompatActivity) mView.getActivity()); return IntSetting.MAIN_INTERFACE_THEME.delete(settings); } - @Override public int getInt(Settings settings) + @Override + public int getInt(Settings settings) { return IntSetting.MAIN_INTERFACE_THEME.getInt(settings); } - @Override public void setInt(Settings settings, int newValue) + @Override + public void setInt(Settings settings, int newValue) { - ThemeHelper.saveTheme(mView.getActivity(), newValue); IntSetting.MAIN_INTERFACE_THEME.setInt(settings, newValue); - mView.getActivity().recreate(); + ThemeHelper.saveTheme((AppCompatActivity) mView.getActivity(), newValue); } }; @@ -369,6 +375,45 @@ public final class SettingsFragmentPresenter sl.add(new SingleChoiceSetting(mContext, appTheme, R.string.change_theme, 0, R.array.themeEntries, R.array.themeValues)); } + + AbstractIntSetting themeMode = new AbstractIntSetting() + { + @Override + public boolean isOverridden(Settings settings) + { + return IntSetting.MAIN_INTERFACE_THEME_MODE.isOverridden(settings); + } + + @Override + public boolean isRuntimeEditable() + { + // This only affects app UI + return true; + } + + @Override + public boolean delete(Settings settings) + { + ThemeHelper.deleteThemeModeKey((AppCompatActivity) mView.getActivity()); + return IntSetting.MAIN_INTERFACE_THEME_MODE.delete(settings); + } + + @Override + public int getInt(Settings settings) + { + return IntSetting.MAIN_INTERFACE_THEME_MODE.getInt(settings); + } + + @Override + public void setInt(Settings settings, int newValue) + { + IntSetting.MAIN_INTERFACE_THEME_MODE.setInt(settings, newValue); + ThemeHelper.saveThemeMode((AppCompatActivity) mView.getActivity(), newValue); + } + }; + + sl.add(new SingleChoiceSetting(mContext, themeMode, R.string.change_theme_mode, 0, + R.array.themeModeEntries, R.array.themeModeValues)); } private void addAudioSettings(ArrayList sl) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryInitialization.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryInitialization.java index f872802a9f..15cf635615 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryInitialization.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryInitialization.java @@ -13,6 +13,7 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatDelegate; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.preference.PreferenceManager; @@ -87,6 +88,16 @@ public final class DirectoryInitialization .apply(); } + if (IntSetting.MAIN_INTERFACE_THEME_MODE.getIntGlobal() != + preferences.getInt(ThemeHelper.CURRENT_THEME_MODE, + AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)) + { + preferences.edit() + .putInt(ThemeHelper.CURRENT_THEME_MODE, + IntSetting.MAIN_INTERFACE_THEME_MODE.getIntGlobal()) + .apply(); + } + if (wiimoteIniWritten) { // This has to be done after calling NativeLibrary.Initialize(), diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ThemeHelper.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ThemeHelper.java index ff96e033c0..040e20990a 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ThemeHelper.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ThemeHelper.java @@ -10,7 +10,10 @@ import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.app.AppCompatDelegate; import androidx.core.content.ContextCompat; +import androidx.core.view.WindowCompat; +import androidx.core.view.WindowInsetsControllerCompat; import androidx.preference.PreferenceManager; import com.google.android.material.appbar.AppBarLayout; @@ -23,6 +26,7 @@ import org.dolphinemu.dolphinemu.ui.main.ThemeProvider; public class ThemeHelper { public static final String CURRENT_THEME = "current_theme"; + public static final String CURRENT_THEME_MODE = "current_theme_mode"; public static final int DEFAULT = 0; public static final int MONET = 1; @@ -38,6 +42,7 @@ public class ThemeHelper // requested theme id is ready before the onCreate method of any given Activity. SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity.getApplicationContext()); + setThemeMode(activity); switch (preferences.getInt(CURRENT_THEME, DEFAULT)) { case DEFAULT: @@ -70,22 +75,88 @@ public class ThemeHelper } } - public static void saveTheme(@NonNull Activity activity, int themeValue) + private static void setThemeMode(@NonNull AppCompatActivity activity) { - SharedPreferences preferences = - PreferenceManager.getDefaultSharedPreferences(activity.getApplicationContext()); - preferences.edit().putInt(CURRENT_THEME, themeValue).apply(); + int themeMode = PreferenceManager.getDefaultSharedPreferences(activity.getApplicationContext()) + .getInt(CURRENT_THEME_MODE, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); + activity.getDelegate().setLocalNightMode(themeMode); + + WindowInsetsControllerCompat windowController = + WindowCompat.getInsetsController(activity.getWindow(), + activity.getWindow().getDecorView()); + int systemReportedThemeMode = + activity.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; + + switch (themeMode) + { + case AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM: + switch (systemReportedThemeMode) + { + case Configuration.UI_MODE_NIGHT_NO: + setLightModeSystemBars(windowController); + break; + case Configuration.UI_MODE_NIGHT_YES: + setDarkModeSystemBars(windowController); + break; + } + break; + case AppCompatDelegate.MODE_NIGHT_NO: + setLightModeSystemBars(windowController); + break; + case AppCompatDelegate.MODE_NIGHT_YES: + setDarkModeSystemBars(windowController); + break; + } } - public static void deleteThemeKey(@NonNull Activity activity) + private static void setLightModeSystemBars(@NonNull WindowInsetsControllerCompat windowController) { - SharedPreferences preferences = - PreferenceManager.getDefaultSharedPreferences(activity.getApplicationContext()); - preferences.edit().remove(CURRENT_THEME).apply(); - activity.setTheme(R.style.Theme_Dolphin_Main); + windowController.setAppearanceLightStatusBars(true); + windowController.setAppearanceLightNavigationBars(true); + } + + private static void setDarkModeSystemBars(@NonNull WindowInsetsControllerCompat windowController) + { + windowController.setAppearanceLightStatusBars(false); + windowController.setAppearanceLightNavigationBars(false); + } + + public static void saveTheme(@NonNull AppCompatActivity activity, int themeValue) + { + PreferenceManager.getDefaultSharedPreferences(activity.getApplicationContext()) + .edit() + .putInt(CURRENT_THEME, themeValue) + .apply(); activity.recreate(); } + public static void deleteThemeKey(@NonNull AppCompatActivity activity) + { + PreferenceManager.getDefaultSharedPreferences(activity.getApplicationContext()) + .edit() + .remove(CURRENT_THEME) + .apply(); + activity.recreate(); + } + + public static void saveThemeMode(@NonNull AppCompatActivity activity, int themeModeValue) + { + PreferenceManager.getDefaultSharedPreferences(activity.getApplicationContext()) + .edit() + .putInt(CURRENT_THEME_MODE, themeModeValue) + .apply(); + setThemeMode(activity); + } + + public static void deleteThemeModeKey(@NonNull AppCompatActivity activity) + { + PreferenceManager.getDefaultSharedPreferences(activity.getApplicationContext()) + .edit() + .remove(CURRENT_THEME_MODE) + .apply(); + setThemeMode(activity); + } + public static void setCorrectTheme(AppCompatActivity activity) { int currentTheme = ((ThemeProvider) activity).getThemeId(); @@ -110,7 +181,7 @@ public class ThemeHelper } } - public static void setNavigationBarColor(Activity activity, @ColorInt int color) + public static void setNavigationBarColor(@NonNull Activity activity, @ColorInt int color) { int gestureType = InsetsHelper.getSystemGestureType(activity.getApplicationContext()); int orientation = activity.getResources().getConfiguration().orientation; diff --git a/Source/Android/app/src/main/res/drawable/ic_back.xml b/Source/Android/app/src/main/res/drawable/ic_back.xml deleted file mode 100644 index 344d5e6974..0000000000 --- a/Source/Android/app/src/main/res/drawable/ic_back.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/Source/Android/app/src/main/res/drawable/tv_card_background.xml b/Source/Android/app/src/main/res/drawable/tv_card_background.xml index ee4320d5b3..58ae5f09a8 100644 --- a/Source/Android/app/src/main/res/drawable/tv_card_background.xml +++ b/Source/Android/app/src/main/res/drawable/tv_card_background.xml @@ -2,7 +2,7 @@ + android:drawable="@color/dolphin_blue"/> diff --git a/Source/Android/app/src/main/res/values-night/bools.xml b/Source/Android/app/src/main/res/values-night/bools.xml deleted file mode 100644 index 85800a2f2c..0000000000 --- a/Source/Android/app/src/main/res/values-night/bools.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - false - diff --git a/Source/Android/app/src/main/res/values-v27/themes.xml b/Source/Android/app/src/main/res/values-v27/themes.xml deleted file mode 100644 index ec8f103c8d..0000000000 --- a/Source/Android/app/src/main/res/values-v27/themes.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - diff --git a/Source/Android/app/src/main/res/values/arrays.xml b/Source/Android/app/src/main/res/values/arrays.xml index 0089d6eef6..4b789407b3 100644 --- a/Source/Android/app/src/main/res/values/arrays.xml +++ b/Source/Android/app/src/main/res/values/arrays.xml @@ -507,6 +507,17 @@ 4 + + Follow System + Light + Dark + + + -1 + 1 + 2 + + Never On Idle Skipping diff --git a/Source/Android/app/src/main/res/values/bools.xml b/Source/Android/app/src/main/res/values/bools.xml deleted file mode 100644 index fbc2def833..0000000000 --- a/Source/Android/app/src/main/res/values/bools.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - true - diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml index f35b5adb60..e262eb65c5 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -196,6 +196,7 @@ Show Titles in Game List Show the title and creator below each game cover. Change App Theme + Change Theme Mode Please select a region diff --git a/Source/Android/app/src/main/res/values/styles.xml b/Source/Android/app/src/main/res/values/styles.xml index 9c71db19ee..c5d03d9e5c 100644 --- a/Source/Android/app/src/main/res/values/styles.xml +++ b/Source/Android/app/src/main/res/values/styles.xml @@ -42,7 +42,7 @@ - +