diff --git a/app/src/main/java/com/termux/app/TermuxActivity.java b/app/src/main/java/com/termux/app/TermuxActivity.java index 2c0797ec92..bf5837ec96 100644 --- a/app/src/main/java/com/termux/app/TermuxActivity.java +++ b/app/src/main/java/com/termux/app/TermuxActivity.java @@ -49,6 +49,7 @@ import com.termux.shared.termux.interact.TextInputDialogUtils; import com.termux.shared.logger.Logger; import com.termux.shared.termux.TermuxUtils; +import com.termux.shared.theme.ThemeUtils; import com.termux.shared.view.ViewUtils; import com.termux.terminal.TerminalSession; import com.termux.terminal.TerminalSessionClient; @@ -406,7 +407,7 @@ public void onServiceDisconnected(ComponentName name) { private void setActivityTheme() { - if (mProperties.isUsingBlackUI()) { + if (ThemeUtils.shouldEnableDarkTheme(this, mProperties.getNightMode())) { this.setTheme(R.style.Theme_Termux_Black); } else { this.setTheme(R.style.Theme_Termux); @@ -414,7 +415,7 @@ private void setActivityTheme() { } private void setDrawerTheme() { - if (mProperties.isUsingBlackUI()) { + if (ThemeUtils.shouldEnableDarkTheme(this, mProperties.getNightMode())) { findViewById(R.id.left_drawer).setBackgroundColor(ContextCompat.getColor(this, android.R.color.background_dark)); ((ImageButton) findViewById(R.id.settings_button)).setColorFilter(Color.WHITE); diff --git a/app/src/main/java/com/termux/app/terminal/TermuxSessionsListViewController.java b/app/src/main/java/com/termux/app/terminal/TermuxSessionsListViewController.java index 61e07ec1e2..5e10d66dc4 100644 --- a/app/src/main/java/com/termux/app/terminal/TermuxSessionsListViewController.java +++ b/app/src/main/java/com/termux/app/terminal/TermuxSessionsListViewController.java @@ -21,6 +21,7 @@ import com.termux.R; import com.termux.app.TermuxActivity; import com.termux.shared.termux.shell.TermuxSession; +import com.termux.shared.theme.ThemeUtils; import com.termux.terminal.TerminalSession; import java.util.List; @@ -55,9 +56,9 @@ public View getView(int position, View convertView, @NonNull ViewGroup parent) { return sessionRowView; } - boolean isUsingBlackUI = mActivity.getProperties().isUsingBlackUI(); + boolean shouldEnableDarkTheme = ThemeUtils.shouldEnableDarkTheme(mActivity, mActivity.getProperties().getNightMode()); - if (isUsingBlackUI) { + if (shouldEnableDarkTheme) { sessionTitleView.setBackground( ContextCompat.getDrawable(mActivity, R.drawable.session_background_black_selected) ); @@ -84,7 +85,7 @@ public View getView(int position, View convertView, @NonNull ViewGroup parent) { } else { sessionTitleView.setPaintFlags(sessionTitleView.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); } - int defaultColor = isUsingBlackUI ? Color.WHITE : Color.BLACK; + int defaultColor = shouldEnableDarkTheme ? Color.WHITE : Color.BLACK; int color = sessionRunning || sessionAtRow.getExitStatus() == 0 ? defaultColor : Color.RED; sessionTitleView.setTextColor(color); return sessionRowView; diff --git a/termux-shared/src/main/java/com/termux/shared/models/theme/NightMode.java b/termux-shared/src/main/java/com/termux/shared/models/theme/NightMode.java new file mode 100644 index 0000000000..f37ac57fc0 --- /dev/null +++ b/termux-shared/src/main/java/com/termux/shared/models/theme/NightMode.java @@ -0,0 +1,48 @@ +package com.termux.shared.models.theme; + +import androidx.appcompat.app.AppCompatDelegate; + +/** The modes used by to decide night mode for themes. */ +public enum NightMode { + + /** Night theme should be enabled. */ + TRUE("true", AppCompatDelegate.MODE_NIGHT_YES), + + /** Dark theme should be enabled. */ + FALSE("false", AppCompatDelegate.MODE_NIGHT_NO), + + /** + * Use night or dark theme depending on system night mode. + * https://developer.android.com/guide/topics/resources/providing-resources#NightQualifier + */ + SYSTEM("system", AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); + + private final String name; + private final int mode; + + NightMode(final String name, int mode) { + this.name = name; + this.mode = mode; + } + + public String getName() { + return name; + } + + public int getMode() { + return mode; + } + + public static Integer modeOf(String name) { + if (TRUE.name.equals(name)) + return TRUE.mode; + else if (FALSE.name.equals(name)) + return FALSE.mode; + else if (SYSTEM.name.equals(name)) { + return SYSTEM.mode; + } else { + return null; + } + } + +} diff --git a/termux-shared/src/main/java/com/termux/shared/termux/settings/properties/TermuxPropertyConstants.java b/termux-shared/src/main/java/com/termux/shared/termux/settings/properties/TermuxPropertyConstants.java index 4762935e44..a657613cf6 100644 --- a/termux-shared/src/main/java/com/termux/shared/termux/settings/properties/TermuxPropertyConstants.java +++ b/termux-shared/src/main/java/com/termux/shared/termux/settings/properties/TermuxPropertyConstants.java @@ -1,6 +1,7 @@ package com.termux.shared.termux.settings.properties; import com.google.common.collect.ImmutableBiMap; +import com.termux.shared.models.theme.NightMode; import com.termux.shared.file.FileUtils; import com.termux.shared.file.filesystem.FileType; import com.termux.shared.settings.properties.SharedProperties; @@ -16,7 +17,7 @@ import java.util.Set; /* - * Version: v0.15.0 + * Version: v0.16.0 * SPDX-License-Identifier: MIT * * Changelog @@ -69,6 +70,9 @@ * * - 0.15.0 (2021-09-05) * - Add `KEY_EXTRA_KEYS_TEXT_ALL_CAPS`. + * + * - 0.16.0 (2021-10-21) + * - Add `KEY_NIGHT_MODE`. */ /** @@ -118,6 +122,7 @@ public final class TermuxPropertyConstants { /** Defines the key for whether to use black UI */ + @Deprecated public static final String KEY_USE_BLACK_UI = "use-black-ui"; // Default: "use-black-ui" @@ -295,6 +300,24 @@ public final class TermuxPropertyConstants { + /** Defines the key for {@link NightMode}. */ + public static final String KEY_NIGHT_MODE = "night-mode"; // Default: "night-mode" + + public static final String IVALUE_NIGHT_MODE_TRUE = NightMode.TRUE.getName(); + public static final String IVALUE_NIGHT_MODE_FALSE = NightMode.FALSE.getName(); + public static final String IVALUE_NIGHT_MODE_SYSTEM = NightMode.SYSTEM.getName(); + public static final String DEFAULT_IVALUE_NIGHT_MODE = IVALUE_NIGHT_MODE_SYSTEM; + + /** Defines the bidirectional map for {@link NightMode} values and their internal values */ + public static final ImmutableBiMap MAP_NIGHT_MODE = + new ImmutableBiMap.Builder() + .put(IVALUE_NIGHT_MODE_TRUE, IVALUE_NIGHT_MODE_TRUE) + .put(IVALUE_NIGHT_MODE_FALSE, IVALUE_NIGHT_MODE_FALSE) + .put(IVALUE_NIGHT_MODE_SYSTEM, IVALUE_NIGHT_MODE_SYSTEM) + .build(); + + + /** Defines the key for whether toggle soft keyboard request will show/hide or enable/disable keyboard */ public static final String KEY_SOFT_KEYBOARD_TOGGLE_BEHAVIOUR = "soft-keyboard-toggle-behaviour"; // Default: "soft-keyboard-toggle-behaviour" @@ -340,7 +363,6 @@ public final class TermuxPropertyConstants { KEY_EXTRA_KEYS_TEXT_ALL_CAPS, KEY_HIDE_SOFT_KEYBOARD_ON_STARTUP, KEY_TERMINAL_ONCLICK_URL_OPEN, - KEY_USE_BLACK_UI, KEY_USE_CTRL_SPACE_WORKAROUND, KEY_USE_FULLSCREEN, KEY_USE_FULLSCREEN_WORKAROUND, @@ -368,9 +390,10 @@ public final class TermuxPropertyConstants { KEY_DEFAULT_WORKING_DIRECTORY, KEY_EXTRA_KEYS, KEY_EXTRA_KEYS_STYLE, + KEY_NIGHT_MODE, KEY_SOFT_KEYBOARD_TOGGLE_BEHAVIOUR, KEY_VOLUME_KEYS_BEHAVIOUR - )); + )); /** Defines the set for keys loaded by termux that have default boolean behaviour with false as default. * "true" -> true diff --git a/termux-shared/src/main/java/com/termux/shared/termux/settings/properties/TermuxSharedProperties.java b/termux-shared/src/main/java/com/termux/shared/termux/settings/properties/TermuxSharedProperties.java index a5ff7f6fd2..f46de32cd0 100644 --- a/termux-shared/src/main/java/com/termux/shared/termux/settings/properties/TermuxSharedProperties.java +++ b/termux-shared/src/main/java/com/termux/shared/termux/settings/properties/TermuxSharedProperties.java @@ -183,17 +183,47 @@ public static Object getTermuxInternalPropertyValue(Context context, String key) * The class that implements the {@link SharedPropertiesParser} interface. */ public static class SharedPropertiesParserClient implements SharedPropertiesParser { + @NonNull + @Override + public Properties preProcessPropertiesOnReadFromDisk(@NonNull Context context, @NonNull Properties properties) { + return replaceUseBlackUIProperty(properties); + } + /** * Override the * {@link SharedPropertiesParser#getInternalPropertyValueFromValue(Context,String,String)} * interface function. */ @Override - public Object getInternalPropertyValueFromValue(Context context, String key, String value) { + public Object getInternalPropertyValueFromValue(@NonNull Context context, String key, String value) { return getInternalTermuxPropertyValueFromValue(context, key, value); } } + @NonNull + public static Properties replaceUseBlackUIProperty(@NonNull Properties properties) { + String useBlackUIStringValue = properties.getProperty(TermuxPropertyConstants.KEY_USE_BLACK_UI); + if (useBlackUIStringValue == null) return properties; + + Logger.logWarn(LOG_TAG, "Removing deprecated property " + TermuxPropertyConstants.KEY_USE_BLACK_UI + "=" + useBlackUIStringValue); + properties.remove(TermuxPropertyConstants.KEY_USE_BLACK_UI); + + // If KEY_NIGHT_MODE is not set + if (properties.getProperty(TermuxPropertyConstants.KEY_NIGHT_MODE) == null) { + Boolean useBlackUI = SharedProperties.getBooleanValueForStringValue(useBlackUIStringValue); + if (useBlackUI != null) { + String termuxAppTheme = useBlackUI ? TermuxPropertyConstants.IVALUE_NIGHT_MODE_TRUE : + TermuxPropertyConstants.IVALUE_NIGHT_MODE_FALSE; + Logger.logWarn(LOG_TAG, "Replacing deprecated property " + TermuxPropertyConstants.KEY_USE_BLACK_UI + "=" + useBlackUI + " with " + TermuxPropertyConstants.KEY_NIGHT_MODE + "=" + termuxAppTheme); + properties.put(TermuxPropertyConstants.KEY_NIGHT_MODE, termuxAppTheme); + } + } + + return properties; + } + + + /** * A static function that should return the internal termux {@link Object} for a key/value pair * read from properties file. @@ -213,10 +243,6 @@ public static Object getInternalTermuxPropertyValueFromValue(Context context, St - If the value is not null and does exist in MAP_*, then internal value returned by map will be used. */ switch (key) { - /* boolean */ - case TermuxPropertyConstants.KEY_USE_BLACK_UI: - return (boolean) getUseBlackUIInternalPropertyValueFromValue(context, value); - /* int */ case TermuxPropertyConstants.KEY_BELL_BEHAVIOUR: return (int) getBellBehaviourInternalPropertyValueFromValue(value); @@ -251,6 +277,8 @@ public static Object getInternalTermuxPropertyValueFromValue(Context context, St return (String) getExtraKeysInternalPropertyValueFromValue(value); case TermuxPropertyConstants.KEY_EXTRA_KEYS_STYLE: return (String) getExtraKeysStyleInternalPropertyValueFromValue(value); + case TermuxPropertyConstants.KEY_NIGHT_MODE: + return (String) getNightModeInternalPropertyValueFromValue(value); case TermuxPropertyConstants.KEY_SOFT_KEYBOARD_TOGGLE_BEHAVIOUR: return (String) getSoftKeyboardToggleBehaviourInternalPropertyValueFromValue(value); case TermuxPropertyConstants.KEY_VOLUME_KEYS_BEHAVIOUR: @@ -279,18 +307,6 @@ public static Object getInternalTermuxPropertyValueFromValue(Context context, St - /** - * Returns {@code true} or {@code false} if value is the literal string "true" or "false" respectively regardless of case. - * Otherwise returns {@code true} if the night mode is currently enabled in the system. - * - * @param value The {@link String} value to convert. - * @return Returns the internal value for value. - */ - public static boolean getUseBlackUIInternalPropertyValueFromValue(Context context, String value) { - int nightMode = context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; - return SharedProperties.getBooleanValueForStringValue(TermuxPropertyConstants.KEY_USE_BLACK_UI, value, nightMode == Configuration.UI_MODE_NIGHT_YES, true, LOG_TAG); - } - /** * Returns the internal value after mapping it based on * {@code TermuxPropertyConstants#MAP_BELL_BEHAVIOUR} if the value is not {@code null} @@ -314,11 +330,11 @@ public static int getBellBehaviourInternalPropertyValueFromValue(String value) { */ public static int getTerminalCursorBlinkRateInternalPropertyValueFromValue(String value) { return SharedProperties.getDefaultIfNotInRange(TermuxPropertyConstants.KEY_TERMINAL_CURSOR_BLINK_RATE, - DataUtils.getIntFromString(value, TermuxPropertyConstants.DEFAULT_IVALUE_TERMINAL_CURSOR_BLINK_RATE), - TermuxPropertyConstants.DEFAULT_IVALUE_TERMINAL_CURSOR_BLINK_RATE, - TermuxPropertyConstants.IVALUE_TERMINAL_CURSOR_BLINK_RATE_MIN, - TermuxPropertyConstants.IVALUE_TERMINAL_CURSOR_BLINK_RATE_MAX, - true, true, LOG_TAG); + DataUtils.getIntFromString(value, TermuxPropertyConstants.DEFAULT_IVALUE_TERMINAL_CURSOR_BLINK_RATE), + TermuxPropertyConstants.DEFAULT_IVALUE_TERMINAL_CURSOR_BLINK_RATE, + TermuxPropertyConstants.IVALUE_TERMINAL_CURSOR_BLINK_RATE_MIN, + TermuxPropertyConstants.IVALUE_TERMINAL_CURSOR_BLINK_RATE_MAX, + true, true, LOG_TAG); } /** @@ -487,6 +503,18 @@ public static String getExtraKeysStyleInternalPropertyValueFromValue(String valu return SharedProperties.getDefaultIfNullOrEmpty(value, TermuxPropertyConstants.DEFAULT_IVALUE_EXTRA_KEYS_STYLE); } + /** + * Returns the value itself if it is not {@code null}, otherwise returns {@link TermuxPropertyConstants#DEFAULT_IVALUE_NIGHT_MODE}. + * + * @param value {@link String} value to convert. + * @return Returns the internal value for value. + */ + public static String getNightModeInternalPropertyValueFromValue(String value) { + return (String) SharedProperties.getDefaultIfNotInMap(TermuxPropertyConstants.KEY_NIGHT_MODE, + TermuxPropertyConstants.MAP_NIGHT_MODE, SharedProperties.toLowerCase(value), + TermuxPropertyConstants.DEFAULT_IVALUE_NIGHT_MODE, true, LOG_TAG); + } + /** * Returns the value itself if it is not {@code null}, otherwise returns {@link TermuxPropertyConstants#DEFAULT_IVALUE_SOFT_KEYBOARD_TOGGLE_BEHAVIOUR}. * @@ -535,10 +563,6 @@ public boolean shouldOpenTerminalTranscriptURLOnClick() { return (boolean) getInternalPropertyValue(TermuxPropertyConstants.KEY_TERMINAL_ONCLICK_URL_OPEN, true); } - public boolean isUsingBlackUI() { - return (boolean) getInternalPropertyValue(TermuxPropertyConstants.KEY_USE_BLACK_UI, true); - } - public boolean isUsingCtrlSpaceWorkaround() { return (boolean) getInternalPropertyValue(TermuxPropertyConstants.KEY_USE_CTRL_SPACE_WORKAROUND, true); } @@ -587,6 +611,16 @@ public String getDefaultWorkingDirectory() { return (String) getInternalPropertyValue(TermuxPropertyConstants.KEY_DEFAULT_WORKING_DIRECTORY, true); } + public String getNightMode() { + return (String) getInternalPropertyValue(TermuxPropertyConstants.KEY_NIGHT_MODE, true); + } + + /** Get the {@link TermuxPropertyConstants#KEY_NIGHT_MODE} value from the properties file on disk. */ + public static String getNightMode(Context context) { + return (String) TermuxSharedProperties.getTermuxInternalPropertyValue(context, + TermuxPropertyConstants.KEY_NIGHT_MODE); + } + public boolean shouldEnableDisableSoftKeyboardOnToggle() { return (boolean) TermuxPropertyConstants.IVALUE_SOFT_KEYBOARD_TOGGLE_BEHAVIOUR_ENABLE_DISABLE.equals(getInternalPropertyValue(TermuxPropertyConstants.KEY_SOFT_KEYBOARD_TOGGLE_BEHAVIOUR, true)); } diff --git a/termux-shared/src/main/java/com/termux/shared/theme/ThemeUtils.java b/termux-shared/src/main/java/com/termux/shared/theme/ThemeUtils.java new file mode 100644 index 0000000000..0d73a7a90a --- /dev/null +++ b/termux-shared/src/main/java/com/termux/shared/theme/ThemeUtils.java @@ -0,0 +1,33 @@ +package com.termux.shared.theme; + +import android.content.Context; +import android.content.res.Configuration; + +import com.termux.shared.models.theme.NightMode; + +public class ThemeUtils { + + /** + * Will return true if system has enabled night mode. + * https://developer.android.com/guide/topics/resources/providing-resources#NightQualifier + */ + public static boolean isNightModeEnabled(Context context) { + if (context == null) return false; + return (context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; + + } + + /** Will return true if mode is set to {@link NightMode#TRUE}, otherwise will return true if + * mode is set to {@link NightMode#SYSTEM} and night mode is enabled by system. */ + public static boolean shouldEnableDarkTheme(Context context, String name) { + if (NightMode.TRUE.getName().equals(name)) + return true; + else if (NightMode.FALSE.getName().equals(name)) + return false; + else if (NightMode.SYSTEM.getName().equals(name)) { + return isNightModeEnabled(context); + } else { + return false; + } + } +}