Merge pull request #11270 from t895/theme-mode

Android: Add theme mode switcher
This commit is contained in:
Mai 2022-12-04 17:56:59 +00:00 committed by GitHub
commit f21edf6d5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 170 additions and 55 deletions

View File

@ -96,7 +96,7 @@ android {
dependencies { dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' 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.exifinterface:exifinterface:1.3.3'
implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.2.1' implementation 'androidx.recyclerview:recyclerview:1.2.1'

View File

@ -34,6 +34,8 @@ public enum IntSetting implements AbstractIntSetting
MAIN_EMULATION_ORIENTATION(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, MAIN_EMULATION_ORIENTATION(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID,
"EmulationOrientation", ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE), "EmulationOrientation", ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE),
MAIN_INTERFACE_THEME(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "InterfaceTheme", 0), 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_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_MOTION_CONTROLS(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "MotionControls", 1),
MAIN_IR_MODE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "IRMode", MAIN_IR_MODE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "IRMode",

View File

@ -8,6 +8,8 @@ import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.text.TextUtils; import android.text.TextUtils;
import androidx.appcompat.app.AppCompatActivity;
import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.activities.UserDataActivity; import org.dolphinemu.dolphinemu.activities.UserDataActivity;
@ -328,33 +330,37 @@ public final class SettingsFragmentPresenter
AbstractIntSetting appTheme = new AbstractIntSetting() AbstractIntSetting appTheme = new AbstractIntSetting()
{ {
@Override public boolean isOverridden(Settings settings) @Override
public boolean isOverridden(Settings settings)
{ {
return IntSetting.MAIN_INTERFACE_THEME.isOverridden(settings); return IntSetting.MAIN_INTERFACE_THEME.isOverridden(settings);
} }
@Override public boolean isRuntimeEditable() @Override
public boolean isRuntimeEditable()
{ {
// This only affects app UI // This only affects app UI
return true; 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); 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); 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); 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, sl.add(new SingleChoiceSetting(mContext, appTheme, R.string.change_theme, 0,
R.array.themeEntries, R.array.themeValues)); 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<SettingsItem> sl) private void addAudioSettings(ArrayList<SettingsItem> sl)

View File

@ -13,6 +13,7 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
@ -87,6 +88,16 @@ public final class DirectoryInitialization
.apply(); .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) if (wiimoteIniWritten)
{ {
// This has to be done after calling NativeLibrary.Initialize(), // This has to be done after calling NativeLibrary.Initialize(),

View File

@ -10,7 +10,10 @@ import androidx.annotation.ColorInt;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsControllerCompat;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import com.google.android.material.appbar.AppBarLayout; import com.google.android.material.appbar.AppBarLayout;
@ -23,6 +26,7 @@ import org.dolphinemu.dolphinemu.ui.main.ThemeProvider;
public class ThemeHelper public class ThemeHelper
{ {
public static final String CURRENT_THEME = "current_theme"; 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 DEFAULT = 0;
public static final int MONET = 1; 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. // requested theme id is ready before the onCreate method of any given Activity.
SharedPreferences preferences = SharedPreferences preferences =
PreferenceManager.getDefaultSharedPreferences(activity.getApplicationContext()); PreferenceManager.getDefaultSharedPreferences(activity.getApplicationContext());
setThemeMode(activity);
switch (preferences.getInt(CURRENT_THEME, DEFAULT)) switch (preferences.getInt(CURRENT_THEME, DEFAULT))
{ {
case 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 = int themeMode = PreferenceManager.getDefaultSharedPreferences(activity.getApplicationContext())
PreferenceManager.getDefaultSharedPreferences(activity.getApplicationContext()); .getInt(CURRENT_THEME_MODE, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
preferences.edit().putInt(CURRENT_THEME, themeValue).apply(); 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 = windowController.setAppearanceLightStatusBars(true);
PreferenceManager.getDefaultSharedPreferences(activity.getApplicationContext()); windowController.setAppearanceLightNavigationBars(true);
preferences.edit().remove(CURRENT_THEME).apply(); }
activity.setTheme(R.style.Theme_Dolphin_Main);
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(); 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) public static void setCorrectTheme(AppCompatActivity activity)
{ {
int currentTheme = ((ThemeProvider) activity).getThemeId(); 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 gestureType = InsetsHelper.getSystemGestureType(activity.getApplicationContext());
int orientation = activity.getResources().getConfiguration().orientation; int orientation = activity.getResources().getConfiguration().orientation;

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorOnSurface"
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
</vector>

View File

@ -2,7 +2,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <selector xmlns:android="http://schemas.android.com/apk/res/android">
<item <item
android:state_selected="true" android:state_selected="true"
android:drawable="@color/dolphin_primary"/> android:drawable="@color/dolphin_blue"/>
<item <item
android:drawable="@color/tv_card_unselected"/> android:drawable="@color/tv_card_unselected"/>
</selector> </selector>

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="lightSystemBars">false</bool>
</resources>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.V27.Dolphin" parent="Theme.Dolphin">
<item name="android:windowLightNavigationBar">@bool/lightSystemBars</item>
</style>
<style name="Theme.Dolphin.Main" parent="Theme.V27.Dolphin" />
</resources>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<style name="Theme.V29.Dolphin" parent="Theme.V27.Dolphin"> <style name="Theme.V29.Dolphin" parent="Theme.Dolphin">
<item name="android:enforceStatusBarContrast">false</item> <item name="android:enforceStatusBarContrast">false</item>
<item name="android:enforceNavigationBarContrast">false</item> <item name="android:enforceNavigationBarContrast">false</item>
</style> </style>

View File

@ -507,6 +507,17 @@
<item>4</item> <item>4</item>
</integer-array> </integer-array>
<string-array name="themeModeEntries">
<item>Follow System</item>
<item>Light</item>
<item>Dark</item>
</string-array>
<integer-array name="themeModeValues">
<item>-1</item>
<item>1</item>
<item>2</item>
</integer-array>
<string-array name="synchronizeGpuThreadEntries"> <string-array name="synchronizeGpuThreadEntries">
<item>Never</item> <item>Never</item>
<item>On Idle Skipping</item> <item>On Idle Skipping</item>

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="lightSystemBars">true</bool>
</resources>

View File

@ -196,6 +196,7 @@
<string name="show_titles_in_game_list">Show Titles in Game List</string> <string name="show_titles_in_game_list">Show Titles in Game List</string>
<string name="show_titles_in_game_list_description">Show the title and creator below each game cover.</string> <string name="show_titles_in_game_list_description">Show the title and creator below each game cover.</string>
<string name="change_theme">Change App Theme</string> <string name="change_theme">Change App Theme</string>
<string name="change_theme_mode">Change Theme Mode</string>
<!-- Online Update Region Select Fragment --> <!-- Online Update Region Select Fragment -->
<string name="region_select_title">Please select a region</string> <string name="region_select_title">Please select a region</string>

View File

@ -42,7 +42,7 @@
<style name="DolphinTVDialog" parent="Theme.Material3.DayNight.Dialog.Alert"> <style name="DolphinTVDialog" parent="Theme.Material3.DayNight.Dialog.Alert">
<item name="colorSurface">@color/dolphin_inverseOnSurface</item> <item name="colorSurface">@color/dolphin_inverseOnSurface</item>
<item name="colorPrimary">@color/dolphin_primary</item> <item name="colorPrimary">@color/dolphin_blue</item>
</style> </style>
<style name="DolphinButton" parent="Widget.Material3.Button.TextButton.Dialog"> <style name="DolphinButton" parent="Widget.Material3.Button.TextButton.Dialog">

View File

@ -47,9 +47,6 @@
<item name="android:windowAllowEnterTransitionOverlap">true</item> <item name="android:windowAllowEnterTransitionOverlap">true</item>
<item name="android:windowAllowReturnTransitionOverlap">true</item> <item name="android:windowAllowReturnTransitionOverlap">true</item>
<item name="homeAsUpIndicator">@drawable/ic_back</item>
<item name="android:windowLightStatusBar" tools:targetApi="m">@bool/lightSystemBars</item>
<item name="android:windowLayoutInDisplayCutoutMode" tools:targetApi="o_mr1">default</item> <item name="android:windowLayoutInDisplayCutoutMode" tools:targetApi="o_mr1">default</item>
<item name="materialAlertDialogTheme">@style/DolphinMaterialDialog</item> <item name="materialAlertDialogTheme">@style/DolphinMaterialDialog</item>
@ -58,7 +55,7 @@
<item name="materialDividerStyle">@style/DolphinDivider</item> <item name="materialDividerStyle">@style/DolphinDivider</item>
</style> </style>
<!-- Trick for API >= 27 specific changes --> <!-- Trick for API >= 29 specific changes -->
<style name="Theme.Dolphin.Main" parent="Theme.Dolphin" /> <style name="Theme.Dolphin.Main" parent="Theme.Dolphin" />
<style name="Theme.Dolphin.Main.Material" parent="Theme.Dolphin.Main"> <style name="Theme.Dolphin.Main.Material" parent="Theme.Dolphin.Main">

View File

@ -39,10 +39,12 @@ bool IsSettingSaveable(const Config::Location& config_location)
// TODO: Kill the current Android controller mappings system // TODO: Kill the current Android controller mappings system
if (config_location.section == "Android") if (config_location.section == "Android")
{ {
static constexpr std::array<const char*, 12> android_setting_saveable = { static constexpr std::array<const char*, 13> android_setting_saveable = {
"ControlScale", "ControlOpacity", "EmulationOrientation", "JoystickRelCenter", "ControlScale", "ControlOpacity", "EmulationOrientation",
"LastPlatformTab", "MotionControls", "PhoneRumble", "ShowInputOverlay", "JoystickRelCenter", "LastPlatformTab", "MotionControls",
"IRMode", "IRAlwaysRecenter", "ShowGameTitles", "InterfaceTheme"}; "PhoneRumble", "ShowInputOverlay", "IRMode",
"IRAlwaysRecenter", "ShowGameTitles", "InterfaceTheme",
"InterfaceThemeMode"};
return std::any_of( return std::any_of(
android_setting_saveable.cbegin(), android_setting_saveable.cend(), android_setting_saveable.cbegin(), android_setting_saveable.cend(),