Android: Convert ThemeHelper to Kotlin

This commit is contained in:
Charles Lombardo 2022-12-18 16:54:28 -05:00
parent 9f4d9994bc
commit 2f5808e7a4
2 changed files with 240 additions and 293 deletions

View File

@ -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));
}
}

View File

@ -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)
)
}
}