From 2f5808e7a413b3762a88442d60a04e26c275ef10 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Sun, 18 Dec 2022 16:54:28 -0500 Subject: [PATCH] Android: Convert ThemeHelper to Kotlin --- .../dolphinemu/utils/ThemeHelper.java | 293 ------------------ .../dolphinemu/utils/ThemeHelper.kt | 240 ++++++++++++++ 2 files changed, 240 insertions(+), 293 deletions(-) delete mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ThemeHelper.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ThemeHelper.kt 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 deleted file mode 100644 index c7aead7aa9..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ThemeHelper.java +++ /dev/null @@ -1,293 +0,0 @@ -package org.dolphinemu.dolphinemu.utils; - -import android.app.Activity; -import android.content.SharedPreferences; -import android.content.res.Configuration; -import android.graphics.Color; -import android.os.Build; - -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; -import com.google.android.material.appbar.MaterialToolbar; -import com.google.android.material.color.MaterialColors; -import com.google.android.material.elevation.ElevationOverlayProvider; - -import org.dolphinemu.dolphinemu.R; -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 String USE_BLACK_BACKGROUNDS = "use_black_backgrounds"; - - public static final int DEFAULT = 0; - public static final int MONET = 1; - public static final int MATERIAL_DEFAULT = 2; - public static final int GREEN = 3; - public static final int PINK = 4; - - public static final float NAV_BAR_ALPHA = 0.9f; - - public static void setTheme(@NonNull AppCompatActivity activity) - { - // We have to use shared preferences in addition to Dolphin's settings to guarantee that the - // 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: - activity.setTheme(R.style.Theme_Dolphin_Main); - break; - - case MONET: - activity.setTheme(R.style.Theme_Dolphin_Main_MaterialYou); - break; - - case MATERIAL_DEFAULT: - activity.setTheme(R.style.Theme_Dolphin_Main_Material); - break; - - case GREEN: - activity.setTheme(R.style.Theme_Dolphin_Main_Green); - break; - - case PINK: - activity.setTheme(R.style.Theme_Dolphin_Main_Pink); - break; - } - - if (preferences.getBoolean(USE_BLACK_BACKGROUNDS, false)) - { - activity.setTheme(R.style.ThemeOverlay_Dolphin_Dark); - } - - // Since the top app bar matches the color of the status bar, devices below API 23 have to get a - // black status bar since their icons do not adapt based on background color - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) - { - activity.getWindow().setStatusBarColor( - ContextCompat.getColor(activity.getApplicationContext(), android.R.color.black)); - } - } - - private static void setThemeMode(@NonNull AppCompatActivity activity) - { - 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; - } - } - - private static void setLightModeSystemBars(@NonNull WindowInsetsControllerCompat windowController) - { - 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 saveBackgroundSetting(AppCompatActivity activity, boolean backgroundValue) - { - PreferenceManager.getDefaultSharedPreferences(activity.getApplicationContext()) - .edit() - .putBoolean(USE_BLACK_BACKGROUNDS, backgroundValue) - .apply(); - activity.recreate(); - } - - public static void deleteBackgroundSetting(AppCompatActivity activity) - { - PreferenceManager.getDefaultSharedPreferences(activity.getApplicationContext()) - .edit() - .remove(USE_BLACK_BACKGROUNDS) - .apply(); - activity.recreate(); - } - - public static void setCorrectTheme(AppCompatActivity activity) - { - int currentTheme = ((ThemeProvider) activity).getThemeId(); - setTheme(activity); - - if (currentTheme != ((ThemeProvider) activity).getThemeId()) - { - activity.recreate(); - } - } - - public static void setStatusBarColor(AppCompatActivity activity, @ColorInt int color) - { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) - { - activity.getWindow().setStatusBarColor( - ContextCompat.getColor(activity.getApplicationContext(), android.R.color.black)); - } - else - { - activity.getWindow().setStatusBarColor(color); - } - } - - public static void setNavigationBarColor(@NonNull Activity activity, @ColorInt int color) - { - int gestureType = InsetsHelper.getSystemGestureType(activity.getApplicationContext()); - int orientation = activity.getResources().getConfiguration().orientation; - - // Use black if the Android version is too low to support changing button colors - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O_MR1) - { - activity.getWindow().setNavigationBarColor( - ContextCompat.getColor(activity.getApplicationContext(), android.R.color.black)); - } - // Use a solid color when the navigation bar is on the left/right edge of the screen - else if ((gestureType == InsetsHelper.THREE_BUTTON_NAVIGATION || - gestureType == InsetsHelper.TWO_BUTTON_NAVIGATION) && - orientation == Configuration.ORIENTATION_LANDSCAPE) - { - activity.getWindow().setNavigationBarColor(color); - } - // Use semi-transparent color when in portrait mode with three/two button navigation to - // partially see list items behind the navigation bar - else if (gestureType == InsetsHelper.THREE_BUTTON_NAVIGATION || - gestureType == InsetsHelper.TWO_BUTTON_NAVIGATION) - { - activity.getWindow().setNavigationBarColor(getColorWithOpacity(color, NAV_BAR_ALPHA)); - } - // Use transparent color when using gesture navigation - else - { - activity.getWindow().setNavigationBarColor( - ContextCompat.getColor(activity.getApplicationContext(), - android.R.color.transparent)); - } - } - - public static void enableScrollTint(AppCompatActivity activity, MaterialToolbar toolbar, - @NonNull AppBarLayout appBarLayout) - { - appBarLayout.addOnOffsetChangedListener((layout, verticalOffset) -> - { - if (-verticalOffset >= (layout.getTotalScrollRange() / 2)) - { - @ColorInt int color = - new ElevationOverlayProvider(appBarLayout.getContext()).compositeOverlay( - MaterialColors.getColor(appBarLayout, R.attr.colorSurface), - activity.getResources().getDimensionPixelSize(R.dimen.elevated_app_bar)); - toolbar.setBackgroundColor(color); - setStatusBarColor(activity, color); - } - else - { - @ColorInt int statusBarColor = ContextCompat.getColor(activity.getApplicationContext(), - android.R.color.transparent); - @ColorInt int appBarColor = MaterialColors.getColor(toolbar, R.attr.colorSurface); - toolbar.setBackgroundColor(appBarColor); - setStatusBarColor(activity, statusBarColor); - } - }); - } - - public static void enableStatusBarScrollTint(AppCompatActivity activity, - @NonNull AppBarLayout appBarLayout) - { - appBarLayout.addOnOffsetChangedListener((layout, verticalOffset) -> - { - if (-verticalOffset > 0) - { - @ColorInt int color = MaterialColors.getColor(appBarLayout, R.attr.colorSurface); - setStatusBarColor(activity, color); - } - else - { - @ColorInt int statusBarColor = ContextCompat.getColor(activity.getApplicationContext(), - android.R.color.transparent); - setStatusBarColor(activity, statusBarColor); - } - }); - } - - @RequiresApi(api = Build.VERSION_CODES.O_MR1) @ColorInt - private static int getColorWithOpacity(@ColorInt int color, float alphaFactor) - { - return Color.argb(Math.round(alphaFactor * Color.alpha(color)), Color.red(color), - Color.green(color), Color.blue(color)); - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ThemeHelper.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ThemeHelper.kt new file mode 100644 index 0000000000..7328126297 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ThemeHelper.kt @@ -0,0 +1,240 @@ +package org.dolphinemu.dolphinemu.utils + +import android.app.Activity +import androidx.appcompat.app.AppCompatActivity +import org.dolphinemu.dolphinemu.R +import android.os.Build +import androidx.core.content.ContextCompat +import androidx.appcompat.app.AppCompatDelegate +import androidx.core.view.WindowInsetsControllerCompat +import androidx.core.view.WindowCompat +import org.dolphinemu.dolphinemu.ui.main.ThemeProvider +import android.content.res.Configuration +import com.google.android.material.appbar.MaterialToolbar +import com.google.android.material.appbar.AppBarLayout +import com.google.android.material.elevation.ElevationOverlayProvider +import com.google.android.material.color.MaterialColors +import android.graphics.Color +import androidx.annotation.ColorInt +import androidx.annotation.RequiresApi +import androidx.preference.PreferenceManager +import kotlin.math.roundToInt + +object ThemeHelper { + + const val CURRENT_THEME = "current_theme" + const val CURRENT_THEME_MODE = "current_theme_mode" + const val USE_BLACK_BACKGROUNDS = "use_black_backgrounds" + + const val DEFAULT = 0 + const val MONET = 1 + const val MATERIAL_DEFAULT = 2 + const val GREEN = 3 + const val PINK = 4 + + const val NAV_BAR_ALPHA = 0.9f + + @JvmStatic + fun setTheme(activity: AppCompatActivity) { + // We have to use shared preferences in addition to Dolphin's settings to guarantee that the + // requested theme id is ready before the onCreate method of any given Activity. + val preferences = PreferenceManager.getDefaultSharedPreferences(activity.applicationContext) + setThemeMode(activity) + when (preferences.getInt(CURRENT_THEME, DEFAULT)) { + DEFAULT -> activity.setTheme(R.style.Theme_Dolphin_Main) + MONET -> activity.setTheme(R.style.Theme_Dolphin_Main_MaterialYou) + MATERIAL_DEFAULT -> activity.setTheme(R.style.Theme_Dolphin_Main_Material) + GREEN -> activity.setTheme(R.style.Theme_Dolphin_Main_Green) + PINK -> activity.setTheme(R.style.Theme_Dolphin_Main_Pink) + } + if (preferences.getBoolean(USE_BLACK_BACKGROUNDS, false)) { + activity.setTheme(R.style.ThemeOverlay_Dolphin_Dark) + } + + // Since the top app bar matches the color of the status bar, devices below API 23 have to get a + // black status bar since their icons do not adapt based on background color + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + activity.window.statusBarColor = + ContextCompat.getColor(activity.applicationContext, android.R.color.black) + } + } + + private fun setThemeMode(activity: AppCompatActivity) { + val themeMode = PreferenceManager.getDefaultSharedPreferences(activity.applicationContext) + .getInt(CURRENT_THEME_MODE, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) + activity.delegate.localNightMode = themeMode + val windowController = WindowCompat.getInsetsController( + activity.window, + activity.window.decorView + ) + val systemReportedThemeMode = + activity.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK + when (themeMode) { + AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM -> when (systemReportedThemeMode) { + Configuration.UI_MODE_NIGHT_NO -> setLightModeSystemBars(windowController) + Configuration.UI_MODE_NIGHT_YES -> setDarkModeSystemBars(windowController) + } + AppCompatDelegate.MODE_NIGHT_NO -> setLightModeSystemBars(windowController) + AppCompatDelegate.MODE_NIGHT_YES -> setDarkModeSystemBars(windowController) + } + } + + private fun setLightModeSystemBars(windowController: WindowInsetsControllerCompat) { + windowController.isAppearanceLightStatusBars = true + windowController.isAppearanceLightNavigationBars = true + } + + private fun setDarkModeSystemBars(windowController: WindowInsetsControllerCompat) { + windowController.isAppearanceLightStatusBars = false + windowController.isAppearanceLightNavigationBars = false + } + + @JvmStatic + fun saveTheme(activity: AppCompatActivity, themeValue: Int) { + PreferenceManager.getDefaultSharedPreferences(activity.applicationContext) + .edit() + .putInt(CURRENT_THEME, themeValue) + .apply() + activity.recreate() + } + + @JvmStatic + fun deleteThemeKey(activity: AppCompatActivity) { + PreferenceManager.getDefaultSharedPreferences(activity.applicationContext) + .edit() + .remove(CURRENT_THEME) + .apply() + activity.recreate() + } + + @JvmStatic + fun saveThemeMode(activity: AppCompatActivity, themeModeValue: Int) { + PreferenceManager.getDefaultSharedPreferences(activity.applicationContext) + .edit() + .putInt(CURRENT_THEME_MODE, themeModeValue) + .apply() + setThemeMode(activity) + } + + @JvmStatic + fun deleteThemeModeKey(activity: AppCompatActivity) { + PreferenceManager.getDefaultSharedPreferences(activity.applicationContext) + .edit() + .remove(CURRENT_THEME_MODE) + .apply() + setThemeMode(activity) + } + + @JvmStatic + fun saveBackgroundSetting(activity: AppCompatActivity, backgroundValue: Boolean) { + PreferenceManager.getDefaultSharedPreferences(activity.applicationContext) + .edit() + .putBoolean(USE_BLACK_BACKGROUNDS, backgroundValue) + .apply() + activity.recreate() + } + + @JvmStatic + fun deleteBackgroundSetting(activity: AppCompatActivity) { + PreferenceManager.getDefaultSharedPreferences(activity.applicationContext) + .edit() + .remove(USE_BLACK_BACKGROUNDS) + .apply() + activity.recreate() + } + + @JvmStatic + fun setCorrectTheme(activity: AppCompatActivity) { + val currentTheme = (activity as ThemeProvider).themeId + setTheme(activity) + if (currentTheme != (activity as ThemeProvider).themeId) { + activity.recreate() + } + } + + @JvmStatic + fun setStatusBarColor(activity: AppCompatActivity, @ColorInt color: Int) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + activity.window.statusBarColor = + ContextCompat.getColor(activity.applicationContext, android.R.color.black) + } else { + activity.window.statusBarColor = color + } + } + + @JvmStatic + fun setNavigationBarColor(activity: Activity, @ColorInt color: Int) { + val gestureType = InsetsHelper.getSystemGestureType(activity.applicationContext) + val orientation = activity.resources.configuration.orientation + + // Use black if the Android version is too low to support changing button colors + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O_MR1) { + activity.window.navigationBarColor = + ContextCompat.getColor(activity.applicationContext, android.R.color.black) + } else if ((gestureType == InsetsHelper.THREE_BUTTON_NAVIGATION || + gestureType == InsetsHelper.TWO_BUTTON_NAVIGATION) && + orientation == Configuration.ORIENTATION_LANDSCAPE + ) { + activity.window.navigationBarColor = color + } else if (gestureType == InsetsHelper.THREE_BUTTON_NAVIGATION || + gestureType == InsetsHelper.TWO_BUTTON_NAVIGATION + ) { + activity.window.navigationBarColor = getColorWithOpacity(color, NAV_BAR_ALPHA) + } else { + activity.window.navigationBarColor = ContextCompat.getColor( + activity.applicationContext, + android.R.color.transparent + ) + } + } + + @JvmStatic + fun enableScrollTint( + activity: AppCompatActivity, toolbar: MaterialToolbar, appBarLayout: AppBarLayout + ) { + appBarLayout.addOnOffsetChangedListener { layout: AppBarLayout, verticalOffset: Int -> + if (-verticalOffset >= layout.totalScrollRange / 2) { + @ColorInt val color = + ElevationOverlayProvider(appBarLayout.context).compositeOverlay( + MaterialColors.getColor(appBarLayout, R.attr.colorSurface), + activity.resources.getDimensionPixelSize(R.dimen.elevated_app_bar).toFloat() + ) + toolbar.setBackgroundColor(color) + setStatusBarColor(activity, color) + } else { + @ColorInt val statusBarColor = ContextCompat.getColor( + activity.applicationContext, + android.R.color.transparent + ) + @ColorInt val appBarColor = MaterialColors.getColor(toolbar, R.attr.colorSurface) + toolbar.setBackgroundColor(appBarColor) + setStatusBarColor(activity, statusBarColor) + } + } + } + + @JvmStatic + fun enableStatusBarScrollTint(activity: AppCompatActivity, appBarLayout: AppBarLayout) { + appBarLayout.addOnOffsetChangedListener { _: AppBarLayout, verticalOffset: Int -> + if (-verticalOffset > 0) { + @ColorInt val color = MaterialColors.getColor(appBarLayout, R.attr.colorSurface) + setStatusBarColor(activity, color) + } else { + @ColorInt val statusBarColor = ContextCompat.getColor( + activity.applicationContext, + android.R.color.transparent + ) + setStatusBarColor(activity, statusBarColor) + } + } + } + + @RequiresApi(api = Build.VERSION_CODES.O_MR1) + @ColorInt + private fun getColorWithOpacity(@ColorInt color: Int, alphaFactor: Float): Int { + return Color.argb( + (alphaFactor * Color.alpha(color)).roundToInt(), Color.red(color), + Color.green(color), Color.blue(color) + ) + } +}