Create termux-shared library package for all termux constants and shared utils

The termux plugins should use this library instead of hardcoding "com.termux" values in their source code.

The library can be included as a dependency by plugins and third party apps by including the following line in the build.gradle where x.xxx is the version number, once its published.

`implementation 'com.termux:termux-shared:x.xxx'`

The `TermuxConstants` class has been updated to `v0.17.0`, `TermuxPreferenceConstants` to `v0.9.0` and `TermuxPropertyConstants` to `v0.6.0`. Check their Changelog sections for info on changes.

Some typos and redundant code has also been fixed.
This commit is contained in:
agnostic-apollo
2021-04-07 11:31:30 +05:00
parent c9a476caf7
commit 682ce08314
73 changed files with 746 additions and 520 deletions

View File

@@ -0,0 +1,395 @@
package com.termux.shared.settings.preferences;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import com.termux.shared.logger.Logger;
import java.util.Set;
public class SharedPreferenceUtils {
private static final String LOG_TAG = "SharedPreferenceUtils";
/**
* Get {@link SharedPreferences} instance of the preferences file 'name' with the operating mode
* {@link Context#MODE_PRIVATE}. This file will be created in the app package's default
* shared preferences directory.
*
* @param context The {@link Context} to get the {@link SharedPreferences} instance.
* @param name The preferences file basename without extension.
* @return The single {@link SharedPreferences} instance that can be used to retrieve and
* modify the preference values.
*/
public static SharedPreferences getPrivateSharedPreferences(Context context, String name) {
return context.getSharedPreferences(name, Context.MODE_PRIVATE);
}
/**
* Get {@link SharedPreferences} instance of the preferences file 'name' with the operating mode
* {@link Context#MODE_PRIVATE} and {@link Context#MODE_MULTI_PROCESS}. This file will be
* created in the app package's default shared preferences directory.
*
* @param context The {@link Context} to get the {@link SharedPreferences} instance.
* @param name The preferences file basename without extension.
* @return The single {@link SharedPreferences} instance that can be used to retrieve and
* modify the preference values.
*/
public static SharedPreferences getPrivateAndMultiProcessSharedPreferences(Context context, String name) {
return context.getSharedPreferences(name, Context.MODE_PRIVATE | Context.MODE_MULTI_PROCESS);
}
/**
* Get a {@code boolean} from {@link SharedPreferences}.
*
* @param sharedPreferences The {@link SharedPreferences} to get the value from.
* @param key The key for the value.
* @param def The default value if failed to read a valid value.
* @return Returns the {@code boolean} value stored in {@link SharedPreferences}, otherwise returns
* default if failed to read a valid value, like in case of an exception.
*/
public static boolean getBoolean(SharedPreferences sharedPreferences, String key, boolean def) {
if(sharedPreferences == null) {
Logger.logError(LOG_TAG, "Error getting boolean value for the \"" + key + "\" key from null shared preferences. Returning default value \"" + def + "\".");
return def;
}
try {
return sharedPreferences.getBoolean(key, def);
}
catch (ClassCastException e) {
Logger.logStackTraceWithMessage(LOG_TAG, "Error getting boolean value for the \"" + key + "\" key from shared preferences. Returning default value \"" + def + "\".", e);
return def;
}
}
/**
* Set a {@code boolean} in {@link SharedPreferences}.
*
* @param sharedPreferences The {@link SharedPreferences} to set the value in.
* @param key The key for the value.
* @param value The value to store.
* @param commitToFile If set to {@code true}, then value will be set to shared preferences
* in-memory cache and the file synchronously. Ideally, only to be used for
* multi-process use-cases.
*/
@SuppressLint("ApplySharedPref")
public static void setBoolean(SharedPreferences sharedPreferences, String key, boolean value, boolean commitToFile) {
if(sharedPreferences == null) {
Logger.logError(LOG_TAG, "Ignoring setting boolean value \"" + value + "\" for the \"" + key + "\" key into null shared preferences.");
return;
}
if(commitToFile)
sharedPreferences.edit().putBoolean(key, value).commit();
else
sharedPreferences.edit().putBoolean(key, value).apply();
}
/**
* Get a {@code float} from {@link SharedPreferences}.
*
* @param sharedPreferences The {@link SharedPreferences} to get the value from.
* @param key The key for the value.
* @param def The default value if failed to read a valid value.
* @return Returns the {@code float} value stored in {@link SharedPreferences}, otherwise returns
* default if failed to read a valid value, like in case of an exception.
*/
public static float getFloat(SharedPreferences sharedPreferences, String key, float def) {
if(sharedPreferences == null) {
Logger.logError(LOG_TAG, "Error getting float value for the \"" + key + "\" key from null shared preferences. Returning default value \"" + def + "\".");
return def;
}
try {
return sharedPreferences.getFloat(key, def);
}
catch (ClassCastException e) {
Logger.logStackTraceWithMessage(LOG_TAG, "Error getting float value for the \"" + key + "\" key from shared preferences. Returning default value \"" + def + "\".", e);
return def;
}
}
/**
* Set a {@code float} in {@link SharedPreferences}.
*
* @param sharedPreferences The {@link SharedPreferences} to set the value in.
* @param key The key for the value.
* @param value The value to store.
* @param commitToFile If set to {@code true}, then value will be set to shared preferences
* in-memory cache and the file synchronously. Ideally, only to be used for
* multi-process use-cases.
*/
@SuppressLint("ApplySharedPref")
public static void setFloat(SharedPreferences sharedPreferences, String key, float value, boolean commitToFile) {
if(sharedPreferences == null) {
Logger.logError(LOG_TAG, "Ignoring setting float value \"" + value + "\" for the \"" + key + "\" key into null shared preferences.");
return;
}
if(commitToFile)
sharedPreferences.edit().putFloat(key, value).commit();
else
sharedPreferences.edit().putFloat(key, value).apply();
}
/**
* Get an {@code int} from {@link SharedPreferences}.
*
* @param sharedPreferences The {@link SharedPreferences} to get the value from.
* @param key The key for the value.
* @param def The default value if failed to read a valid value.
* @return Returns the {@code int} value stored in {@link SharedPreferences}, otherwise returns
* default if failed to read a valid value, like in case of an exception.
*/
public static int getInt(SharedPreferences sharedPreferences, String key, int def) {
if(sharedPreferences == null) {
Logger.logError(LOG_TAG, "Error getting int value for the \"" + key + "\" key from null shared preferences. Returning default value \"" + def + "\".");
return def;
}
try {
return sharedPreferences.getInt(key, def);
}
catch (ClassCastException e) {
Logger.logStackTraceWithMessage(LOG_TAG, "Error getting int value for the \"" + key + "\" key from shared preferences. Returning default value \"" + def + "\".", e);
return def;
}
}
/**
* Set an {@code int} in {@link SharedPreferences}.
*
* @param sharedPreferences The {@link SharedPreferences} to set the value in.
* @param key The key for the value.
* @param value The value to store.
* @param commitToFile If set to {@code true}, then value will be set to shared preferences
* in-memory cache and the file synchronously. Ideally, only to be used for
* multi-process use-cases.
*/
@SuppressLint("ApplySharedPref")
public static void setInt(SharedPreferences sharedPreferences, String key, int value, boolean commitToFile) {
if(sharedPreferences == null) {
Logger.logError(LOG_TAG, "Ignoring setting int value \"" + value + "\" for the \"" + key + "\" key into null shared preferences.");
return;
}
if(commitToFile)
sharedPreferences.edit().putInt(key, value).commit();
else
sharedPreferences.edit().putInt(key, value).apply();
}
/**
* Get a {@code long} from {@link SharedPreferences}.
*
* @param sharedPreferences The {@link SharedPreferences} to get the value from.
* @param key The key for the value.
* @param def The default value if failed to read a valid value.
* @return Returns the {@code long} value stored in {@link SharedPreferences}, otherwise returns
* default if failed to read a valid value, like in case of an exception.
*/
public static long getLong(SharedPreferences sharedPreferences, String key, long def) {
if(sharedPreferences == null) {
Logger.logError(LOG_TAG, "Error getting long value for the \"" + key + "\" key from null shared preferences. Returning default value \"" + def + "\".");
return def;
}
try {
return sharedPreferences.getLong(key, def);
}
catch (ClassCastException e) {
Logger.logStackTraceWithMessage(LOG_TAG, "Error getting long value for the \"" + key + "\" key from shared preferences. Returning default value \"" + def + "\".", e);
return def;
}
}
/**
* Set a {@code long} in {@link SharedPreferences}.
*
* @param sharedPreferences The {@link SharedPreferences} to set the value in.
* @param key The key for the value.
* @param value The value to store.
* @param commitToFile If set to {@code true}, then value will be set to shared preferences
* in-memory cache and the file synchronously. Ideally, only to be used for
* multi-process use-cases.
*/
@SuppressLint("ApplySharedPref")
public static void setLong(SharedPreferences sharedPreferences, String key, long value, boolean commitToFile) {
if(sharedPreferences == null) {
Logger.logError(LOG_TAG, "Ignoring setting long value \"" + value + "\" for the \"" + key + "\" key into null shared preferences.");
return;
}
if(commitToFile)
sharedPreferences.edit().putLong(key, value).commit();
else
sharedPreferences.edit().putLong(key, value).apply();
}
/**
* Get a {@code String} from {@link SharedPreferences}.
*
* @param sharedPreferences The {@link SharedPreferences} to get the value from.
* @param key The key for the value.
* @param def The default value if failed to read a valid value.
* @return Returns the {@code String} value stored in {@link SharedPreferences}, otherwise returns
* default if failed to read a valid value, like in case of an exception.
*/
public static String getString(SharedPreferences sharedPreferences, String key, String def) {
if(sharedPreferences == null) {
Logger.logError(LOG_TAG, "Error getting String value for the \"" + key + "\" key from null shared preferences. Returning default value \"" + def + "\".");
return def;
}
try {
return sharedPreferences.getString(key, def);
}
catch (ClassCastException e) {
Logger.logStackTraceWithMessage(LOG_TAG, "Error getting String value for the \"" + key + "\" key from shared preferences. Returning default value \"" + def + "\".", e);
return def;
}
}
/**
* Set a {@code String} in {@link SharedPreferences}.
*
* @param sharedPreferences The {@link SharedPreferences} to set the value in.
* @param key The key for the value.
* @param value The value to store.
* @param commitToFile If set to {@code true}, then value will be set to shared preferences
* in-memory cache and the file synchronously. Ideally, only to be used for
* multi-process use-cases.
*/
@SuppressLint("ApplySharedPref")
public static void setString(SharedPreferences sharedPreferences, String key, String value, boolean commitToFile) {
if(sharedPreferences == null) {
Logger.logError(LOG_TAG, "Ignoring setting String value \"" + value + "\" for the \"" + key + "\" key into null shared preferences.");
return;
}
if(commitToFile)
sharedPreferences.edit().putString(key, value).commit();
else
sharedPreferences.edit().putString(key, value).apply();
}
/**
* Get a {@code Set<String>} from {@link SharedPreferences}.
*
* @param sharedPreferences The {@link SharedPreferences} to get the value from.
* @param key The key for the value.
* @param def The default value if failed to read a valid value.
* @return Returns the {@code Set<String>} value stored in {@link SharedPreferences}, otherwise returns
* default if failed to read a valid value, like in case of an exception.
*/
public static Set<String> getStringSet(SharedPreferences sharedPreferences, String key, Set<String> def) {
if(sharedPreferences == null) {
Logger.logError(LOG_TAG, "Error getting Set<String> value for the \"" + key + "\" key from null shared preferences. Returning default value \"" + def + "\".");
return def;
}
try {
return sharedPreferences.getStringSet(key, def);
}
catch (ClassCastException e) {
Logger.logStackTraceWithMessage(LOG_TAG, "Error getting Set<String> value for the \"" + key + "\" key from shared preferences. Returning default value \"" + def + "\".", e);
return def;
}
}
/**
* Set a {@code Set<String>} in {@link SharedPreferences}.
*
* @param sharedPreferences The {@link SharedPreferences} to set the value in.
* @param key The key for the value.
* @param value The value to store.
* @param commitToFile If set to {@code true}, then value will be set to shared preferences
* in-memory cache and the file synchronously. Ideally, only to be used for
* multi-process use-cases.
*/
@SuppressLint("ApplySharedPref")
public static void setStringSet(SharedPreferences sharedPreferences, String key, Set<String> value, boolean commitToFile) {
if(sharedPreferences == null) {
Logger.logError(LOG_TAG, "Ignoring setting Set<String> value \"" + value + "\" for the \"" + key + "\" key into null shared preferences.");
return;
}
if(commitToFile)
sharedPreferences.edit().putStringSet(key, value).commit();
else
sharedPreferences.edit().putStringSet(key, value).apply();
}
/**
* Get an {@code int} from {@link SharedPreferences} that is stored as a {@link String}.
*
* @param sharedPreferences The {@link SharedPreferences} to get the value from.
* @param key The key for the value.
* @param def The default value if failed to read a valid value.
* @return Returns the {@code int} value after parsing the {@link String} value stored in
* {@link SharedPreferences}, otherwise returns default if failed to read a valid value,
* like in case of an exception.
*/
public static int getIntStoredAsString(SharedPreferences sharedPreferences, String key, int def) {
if(sharedPreferences == null) {
Logger.logError(LOG_TAG, "Error getting int value for the \"" + key + "\" key from null shared preferences. Returning default value \"" + def + "\".");
return def;
}
String stringValue;
int intValue;
try {
stringValue = sharedPreferences.getString(key, Integer.toString(def));
if(stringValue != null)
intValue = Integer.parseInt(stringValue);
else
intValue = def;
} catch (NumberFormatException | ClassCastException e) {
intValue = def;
}
return intValue;
}
/**
* Set an {@code int} into {@link SharedPreferences} that is stored as a {@link String}.
*
* @param sharedPreferences The {@link SharedPreferences} to set the value in.
* @param key The key for the value.
* @param value The value to store.
* @param commitToFile If set to {@code true}, then value will be set to shared preferences
* in-memory cache and the file synchronously. Ideally, only to be used for
* multi-process use-cases.
*/
@SuppressLint("ApplySharedPref")
public static void setIntStoredAsString(SharedPreferences sharedPreferences, String key, int value, boolean commitToFile) {
if(sharedPreferences == null) {
Logger.logError(LOG_TAG, "Ignoring setting int value \"" + value + "\" for the \"" + key + "\" key into null shared preferences.");
return;
}
if(commitToFile)
sharedPreferences.edit().putString(key, Integer.toString(value)).commit();
else
sharedPreferences.edit().putString(key, Integer.toString(value)).apply();
}
}

View File

@@ -0,0 +1,174 @@
package com.termux.shared.settings.preferences;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.TypedValue;
import com.termux.shared.termux.TermuxConstants;
import com.termux.shared.logger.Logger;
import com.termux.shared.termux.TermuxUtils;
import com.termux.shared.data.DataUtils;
import com.termux.shared.settings.preferences.TermuxPreferenceConstants.TERMUX_APP;
import javax.annotation.Nonnull;
public class TermuxAppSharedPreferences {
private final Context mContext;
private final SharedPreferences mSharedPreferences;
private int MIN_FONTSIZE;
private int MAX_FONTSIZE;
private int DEFAULT_FONTSIZE;
private static final String LOG_TAG = "TermuxAppSharedPreferences";
public TermuxAppSharedPreferences(@Nonnull Context context) {
// We use the default context if failed to get termux package context
mContext = DataUtils.getDefaultIfNull(TermuxUtils.getTermuxPackageContext(context), context);
mSharedPreferences = getPrivateSharedPreferences(mContext);
setFontVariables(context);
}
private static SharedPreferences getPrivateSharedPreferences(Context context) {
return SharedPreferenceUtils.getPrivateSharedPreferences(context, TermuxConstants.TERMUX_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION);
}
public boolean getShowTerminalToolbar() {
return SharedPreferenceUtils.getBoolean(mSharedPreferences, TERMUX_APP.KEY_SHOW_TERMINAL_TOOLBAR, TERMUX_APP.DEFAULT_VALUE_SHOW_TERMINAL_TOOLBAR);
}
public void setShowTerminalToolbar(boolean value) {
SharedPreferenceUtils.setBoolean(mSharedPreferences, TERMUX_APP.KEY_SHOW_TERMINAL_TOOLBAR, value, false);
}
public boolean toogleShowTerminalToolbar() {
boolean currentValue = getShowTerminalToolbar();
setShowTerminalToolbar(!currentValue);
return !currentValue;
}
public boolean getSoftKeyboardEnabled() {
return SharedPreferenceUtils.getBoolean(mSharedPreferences, TERMUX_APP.KEY_SOFT_KEYBOARD_ENABLED, TERMUX_APP.DEFAULT_VALUE_KEY_SOFT_KEYBOARD_ENABLED);
}
public void setSoftKeyboardEnabled(boolean value) {
SharedPreferenceUtils.setBoolean(mSharedPreferences, TERMUX_APP.KEY_SOFT_KEYBOARD_ENABLED, value, false);
}
public boolean getKeepScreenOn() {
return SharedPreferenceUtils.getBoolean(mSharedPreferences, TERMUX_APP.KEY_KEEP_SCREEN_ON, TERMUX_APP.DEFAULT_VALUE_KEEP_SCREEN_ON);
}
public void setKeepScreenOn(boolean value) {
SharedPreferenceUtils.setBoolean(mSharedPreferences, TERMUX_APP.KEY_KEEP_SCREEN_ON, value, false);
}
private void setFontVariables(Context context) {
float dipInPixels = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, context.getResources().getDisplayMetrics());
// This is a bit arbitrary and sub-optimal. We want to give a sensible default for minimum font size
// to prevent invisible text due to zoom be mistake:
MIN_FONTSIZE = (int) (4f * dipInPixels);
// http://www.google.com/design/spec/style/typography.html#typography-line-height
int defaultFontSize = Math.round(12 * dipInPixels);
// Make it divisible by 2 since that is the minimal adjustment step:
if (defaultFontSize % 2 == 1) defaultFontSize--;
DEFAULT_FONTSIZE = defaultFontSize;
MAX_FONTSIZE = 256;
}
public int getFontSize() {
int fontSize = SharedPreferenceUtils.getIntStoredAsString(mSharedPreferences, TERMUX_APP.KEY_FONTSIZE, DEFAULT_FONTSIZE);
return DataUtils.clamp(fontSize, MIN_FONTSIZE, MAX_FONTSIZE);
}
public void setFontSize(int value) {
SharedPreferenceUtils.setIntStoredAsString(mSharedPreferences, TERMUX_APP.KEY_FONTSIZE, value, false);
}
public void changeFontSize(boolean increase) {
int fontSize = getFontSize();
fontSize += (increase ? 1 : -1) * 2;
fontSize = Math.max(MIN_FONTSIZE, Math.min(fontSize, MAX_FONTSIZE));
setFontSize(fontSize);
}
public String getCurrentSession() {
return SharedPreferenceUtils.getString(mSharedPreferences, TERMUX_APP.KEY_CURRENT_SESSION, null);
}
public void setCurrentSession(String value) {
SharedPreferenceUtils.setString(mSharedPreferences, TERMUX_APP.KEY_CURRENT_SESSION, value, false);
}
public int getLogLevel() {
return SharedPreferenceUtils.getInt(mSharedPreferences, TERMUX_APP.KEY_LOG_LEVEL, Logger.DEFAULT_LOG_LEVEL);
}
public void setLogLevel(Context context, int logLevel) {
logLevel = Logger.setLogLevel(context, logLevel);
SharedPreferenceUtils.setInt(mSharedPreferences, TERMUX_APP.KEY_LOG_LEVEL, logLevel, false);
}
public int getLastNotificationId() {
return SharedPreferenceUtils.getInt(mSharedPreferences, TERMUX_APP.KEY_LAST_NOTIFICATION_ID, TERMUX_APP.DEFAULT_VALUE_KEY_LAST_NOTIFICATION_ID);
}
public void setLastNotificationId(int notificationId) {
SharedPreferenceUtils.setInt(mSharedPreferences, TERMUX_APP.KEY_LAST_NOTIFICATION_ID, notificationId, false);
}
public boolean getTerminalViewKeyLoggingEnabled() {
return SharedPreferenceUtils.getBoolean(mSharedPreferences, TERMUX_APP.KEY_TERMINAL_VIEW_KEY_LOGGING_ENABLED, TERMUX_APP.DEFAULT_VALUE_TERMINAL_VIEW_KEY_LOGGING_ENABLED);
}
public void setTerminalViewKeyLoggingEnabled(boolean value) {
SharedPreferenceUtils.setBoolean(mSharedPreferences, TERMUX_APP.KEY_TERMINAL_VIEW_KEY_LOGGING_ENABLED, value, false);
}
public boolean getPluginErrorNotificationsEnabled() {
return SharedPreferenceUtils.getBoolean(mSharedPreferences, TERMUX_APP.KEY_PLUGIN_ERROR_NOTIFICATIONS_ENABLED, TERMUX_APP.DEFAULT_VALUE_PLUGIN_ERROR_NOTIFICATIONS_ENABLED);
}
public void setPluginErrorNotificationsEnabled(boolean value) {
SharedPreferenceUtils.setBoolean(mSharedPreferences, TERMUX_APP.KEY_PLUGIN_ERROR_NOTIFICATIONS_ENABLED, value, false);
}
public boolean getCrashReportNotificationsEnabled() {
return SharedPreferenceUtils.getBoolean(mSharedPreferences, TERMUX_APP.KEY_CRASH_REPORT_NOTIFICATIONS_ENABLED, TERMUX_APP.DEFAULT_VALUE_CRASH_REPORT_NOTIFICATIONS_ENABLED);
}
public void setCrashReportNotificationsEnabled(boolean value) {
SharedPreferenceUtils.setBoolean(mSharedPreferences, TERMUX_APP.KEY_CRASH_REPORT_NOTIFICATIONS_ENABLED, value, false);
}
}

View File

@@ -0,0 +1,138 @@
package com.termux.shared.settings.preferences;
/*
* Version: v0.9.0
*
* Changelog
*
* - 0.1.0 (2021-03-12)
* - Initial Release.
*
* - 0.2.0 (2021-03-13)
* - Added `KEY_LOG_LEVEL` and `KEY_TERMINAL_VIEW_LOGGING_ENABLED`.
*
* - 0.3.0 (2021-03-16)
* - Changed to per app scoping of variables so that the same file can store all constants of
* Termux app and its plugins. This will allow {@link com.termux.app.TermuxSettings} to
* manage preferences of plugins as well if they don't have launcher activity themselves
* and also allow plugin apps to make changes to preferences from background.
* - Added following to `TERMUX_TASKER_APP`:
* `KEY_LOG_LEVEL`.
*
* - 0.4.0 (2021-03-13)
* - Added following to `TERMUX_APP`:
* `KEY_PLUGIN_ERROR_NOTIFICATIONS_ENABLED` and `DEFAULT_VALUE_PLUGIN_ERROR_NOTIFICATIONS_ENABLED`.
*
* - 0.5.0 (2021-03-24)
* - Added following to `TERMUX_APP`:
* `KEY_LAST_NOTIFICATION_ID` and `DEFAULT_VALUE_KEY_LAST_NOTIFICATION_ID`.
*
* - 0.6.0 (2021-03-24)
* - Change `DEFAULT_VALUE_KEEP_SCREEN_ON` value to `false` in `TERMUX_APP`.
*
* - 0.7.0 (2021-03-27)
* - Added following to `TERMUX_APP`:
* `KEY_SOFT_KEYBOARD_ENABLED` and `DEFAULT_VALUE_KEY_SOFT_KEYBOARD_ENABLED`.
*
* - 0.8.0 (2021-04-06)
* - Added following to `TERMUX_APP`:
* `KEY_CRASH_REPORT_NOTIFICATIONS_ENABLED` and `DEFAULT_VALUE_CRASH_REPORT_NOTIFICATIONS_ENABLED`.
*
* - 0.9.0 (2021-04-07)
* - Updated javadocs.
*/
/**
* A class that defines shared constants of the SharedPreferences used by Termux app and its plugins.
* This class will be hosted by termux-shared lib and should be imported by other termux plugin
* apps as is instead of copying constants to random classes. The 3rd party apps can also import
* it for interacting with termux apps. If changes are made to this file, increment the version number
* and add an entry in the Changelog section above.
*/
public final class TermuxPreferenceConstants {
/**
* Termux app constants.
*/
public static final class TERMUX_APP {
/**
* Defines the key for whether to show terminal toolbar containing extra keys and text input field.
*/
public static final String KEY_SHOW_TERMINAL_TOOLBAR = "show_extra_keys";
public static final boolean DEFAULT_VALUE_SHOW_TERMINAL_TOOLBAR = true;
/**
* Defines the key for whether the soft keyboard will be enabled, for cases where users want
* to use a hardware keyboard instead.
*/
public static final String KEY_SOFT_KEYBOARD_ENABLED = "soft_keyboard_enabled";
public static final boolean DEFAULT_VALUE_KEY_SOFT_KEYBOARD_ENABLED = true;
/**
* Defines the key for whether to always keep screen on.
*/
public static final String KEY_KEEP_SCREEN_ON = "screen_always_on";
public static final boolean DEFAULT_VALUE_KEEP_SCREEN_ON = false;
/**
* Defines the key for font size of termux terminal view.
*/
public static final String KEY_FONTSIZE = "fontsize";
/**
* Defines the key for current termux terminal session.
*/
public static final String KEY_CURRENT_SESSION = "current_session";
/**
* Defines the key for current termux log level.
*/
public static final String KEY_LOG_LEVEL = "log_level";
/**
* Defines the key for last used notification id.
*/
public static final String KEY_LAST_NOTIFICATION_ID = "last_notification_id";
public static final int DEFAULT_VALUE_KEY_LAST_NOTIFICATION_ID = 0;
/**
* Defines the key for whether termux terminal view key logging is enabled or not
*/
public static final String KEY_TERMINAL_VIEW_KEY_LOGGING_ENABLED = "terminal_view_key_logging_enabled";
public static final boolean DEFAULT_VALUE_TERMINAL_VIEW_KEY_LOGGING_ENABLED = false;
/**
* Defines the key for whether flashes and notifications for plugin errors are enabled or not.
*/
public static final String KEY_PLUGIN_ERROR_NOTIFICATIONS_ENABLED = "plugin_error_notifications_enabled";
public static final boolean DEFAULT_VALUE_PLUGIN_ERROR_NOTIFICATIONS_ENABLED = true;
/**
* Defines the key for whether notifications for crash reports are enabled or not.
*/
public static final String KEY_CRASH_REPORT_NOTIFICATIONS_ENABLED = "crash_report_notifications_enabled";
public static final boolean DEFAULT_VALUE_CRASH_REPORT_NOTIFICATIONS_ENABLED = true;
}
/**
* Termux Tasker app constants.
*/
public static final class TERMUX_TASKER_APP {
/**
* Defines the key for current termux log level.
*/
public static final String KEY_LOG_LEVEL = "log_level";
}
}

View File

@@ -0,0 +1,52 @@
package com.termux.shared.settings.preferences;
import android.content.Context;
import android.content.SharedPreferences;
import com.termux.shared.termux.TermuxConstants;
import com.termux.shared.settings.preferences.TermuxPreferenceConstants.TERMUX_TASKER_APP;
import com.termux.shared.data.DataUtils;
import com.termux.shared.logger.Logger;
import com.termux.shared.termux.TermuxUtils;
import javax.annotation.Nonnull;
public class TermuxTaskerAppSharedPreferences {
private final Context mContext;
private final SharedPreferences mSharedPreferences;
private final SharedPreferences mMultiProcessSharedPreferences;
private static final String LOG_TAG = "TermuxTaskerAppSharedPreferences";
public TermuxTaskerAppSharedPreferences(@Nonnull Context context) {
// We use the default context if failed to get termux-tasker package context
mContext = DataUtils.getDefaultIfNull(TermuxUtils.getTermuxTaskerPackageContext(context), context);
mSharedPreferences = getPrivateSharedPreferences(mContext);
mMultiProcessSharedPreferences = getPrivateAndMultiProcessSharedPreferences(mContext);
}
private static SharedPreferences getPrivateSharedPreferences(Context context) {
return SharedPreferenceUtils.getPrivateSharedPreferences(context, TermuxConstants.TERMUX_TASKER_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION);
}
private static SharedPreferences getPrivateAndMultiProcessSharedPreferences(Context context) {
return SharedPreferenceUtils.getPrivateAndMultiProcessSharedPreferences(context, TermuxConstants.TERMUX_TASKER_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION);
}
public int getLogLevel(boolean readFromFfile) {
if(readFromFfile)
return SharedPreferenceUtils.getInt(mMultiProcessSharedPreferences, TERMUX_TASKER_APP.KEY_LOG_LEVEL, Logger.DEFAULT_LOG_LEVEL);
else
return SharedPreferenceUtils.getInt(mSharedPreferences, TERMUX_TASKER_APP.KEY_LOG_LEVEL, Logger.DEFAULT_LOG_LEVEL);
}
public void setLogLevel(Context context, int logLevel, boolean commitToFile) {
logLevel = Logger.setLogLevel(context, logLevel);
SharedPreferenceUtils.setInt(mSharedPreferences, TERMUX_TASKER_APP.KEY_LOG_LEVEL, logLevel, commitToFile);
}
}

View File

@@ -0,0 +1,461 @@
package com.termux.shared.settings.properties;
import android.content.Context;
import android.widget.Toast;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.primitives.Primitives;
import com.termux.shared.logger.Logger;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* An implementation similar to android's {@link android.content.SharedPreferences} interface for
* reading and writing to and from ".properties" files which also maintains an in-memory cache for
* the key/value pairs when an instance object is used. Operations are done under
* synchronization locks and should be thread safe.
*
* If {@link SharedProperties} instance object is used, then two types of in-memory cache maps are
* maintained, one for the literal {@link String} values found in the file for the keys and an
* additional one that stores (near) primitive {@link Object} values for internal use by the caller.
*
* The {@link SharedProperties} also provides static functions that can be used to read properties
* from files or individual key values or even their internal values. An automatic mapping to a
* boolean as internal value can also be done. An in-memory cache is not maintained, nor are locks used.
*
* This currently only has read support, write support can/will be added later if needed. Check android's
* SharedPreferencesImpl class for reference implementation.
*
* https://cs.android.com/android/platform/superproject/+/android-11.0.0_r3:frameworks/base/core/java/android/app/SharedPreferencesImpl.java
*/
public class SharedProperties {
/**
* The {@link Properties} object that maintains an in-memory cache of values loaded from the
* {@link #mPropertiesFile} file. The key/value pairs are of any keys that are found in the file
* against their literal values in the file.
*/
private Properties mProperties;
/**
* The {@link HashMap<>} object that maintains an in-memory cache of internal values for the values
* loaded from the {@link #mPropertiesFile} file. The key/value pairs are of any keys defined by
* {@link #mPropertiesList} that are found in the file against their internal {@link Object} values
* returned by the call to
* {@link SharedPropertiesParser#getInternalPropertyValueFromValue(Context, String, String)} interface.
*/
private Map<String, Object> mMap;
private final Context mContext;
private final File mPropertiesFile;
private final Set<String> mPropertiesList;
private final SharedPropertiesParser mSharedPropertiesParser;
private final Object mLock = new Object();
/** Defines the bidirectional map for boolean values and their internal values */
public static final ImmutableBiMap<String, Boolean> MAP_GENERIC_BOOLEAN =
new ImmutableBiMap.Builder<String, Boolean>()
.put("true", true)
.put("false", false)
.build();
/** Defines the bidirectional map for inverted boolean values and their internal values */
public static final ImmutableBiMap<String, Boolean> MAP_GENERIC_INVERTED_BOOLEAN =
new ImmutableBiMap.Builder<String, Boolean>()
.put("true", false)
.put("false", true)
.build();
private static final String LOG_TAG = "SharedProperties";
/**
* Constructor for the SharedProperties class.
*
* @param context The Context for operations.
* @param propertiesFile The {@link File} object to load properties from.
* @param propertiesList The {@link Set<String>} object that defined which properties to load.
* If this is set to {@code null}, then all properties that exist in
* {@code propertiesFile} will be read by {@link #loadPropertiesFromDisk()}
* @param sharedPropertiesParser The implementation of the {@link SharedPropertiesParser} interface.
*/
public SharedProperties(@Nonnull Context context, @Nullable File propertiesFile, Set<String> propertiesList, @Nonnull SharedPropertiesParser sharedPropertiesParser) {
mContext = context;
mPropertiesFile = propertiesFile;
mPropertiesList = propertiesList;
mSharedPropertiesParser = sharedPropertiesParser;
mProperties = new Properties();
mMap = new HashMap<>();
}
/**
* Load the properties defined by {@link #mPropertiesList} or all properties if its {@code null}
* from the {@link #mPropertiesFile} file to update the {@link #mProperties} and {@link #mMap}
* in-memory cache.
* Properties are not loading automatically when constructor is called and must be manually called.
*/
public void loadPropertiesFromDisk() {
synchronized (mLock) {
// Get properties from mPropertiesFile
Properties properties = getProperties(false);
// We still need to load default values into mMap, so we assume no properties defined if
// reading from mPropertiesFile failed
if (properties == null)
properties = new Properties();
HashMap<String, Object> map = new HashMap<>();
Properties newProperties = new Properties();
Set<String> propertiesList = mPropertiesList;
if (propertiesList == null)
propertiesList = properties.stringPropertyNames();
String value;
Object internalValue;
for (String key : propertiesList) {
value = properties.getProperty(key); // value will be null if key does not exist in propertiesFile
Logger.logDebug(LOG_TAG, key + " : " + value);
// Call the {@link SharedPropertiesParser#getInternalPropertyValueFromValue(Context,String,String)}
// interface method to get the internal value to store in the {@link #mMap}.
internalValue = mSharedPropertiesParser.getInternalPropertyValueFromValue(mContext, key, value);
// If the internal value was successfully added to map, then also add value to newProperties
// We only store values in-memory defined by propertiesList
if (putToMap(map, key, internalValue)) { // null internalValue will be put into map
putToProperties(newProperties, key, value); // null value will **not** be into properties
}
}
mMap = map;
mProperties = newProperties;
}
}
/**
* Get the {@link Properties} object for the {@link #mPropertiesFile}. The {@link Properties}
* object will also contain properties not defined by the {@link #mPropertiesList} if cache
* value is {@code false}.
*
* @param cached If {@code true}, then the {@link #mProperties} in-memory cache is returned. Otherwise
* the {@link Properties} object is directly read from the {@link #mPropertiesFile}.
* @return Returns the {@link Properties} object if read from file, otherwise a copy of {@link #mProperties}.
*/
public Properties getProperties(boolean cached) {
synchronized (mLock) {
if (cached) {
if (mProperties == null) mProperties = new Properties();
return getPropertiesCopy(mProperties);
} else {
return getPropertiesFromFile(mContext, mPropertiesFile);
}
}
}
/**
* Get the {@link String} value for the key passed from the {@link #mPropertiesFile}.
*
* @param key The key to read from the {@link Properties} object.
* @param cached If {@code true}, then the value is returned from the {@link #mProperties} in-memory cache.
* Otherwise the {@link Properties} object is read directly from the {@link #mPropertiesFile}
* and value is returned from it against the key.
* @return Returns the {@link String} object. This will be {@code null} if key is not found.
*/
public String getProperty(String key, boolean cached) {
synchronized (mLock) {
return (String) getProperties(cached).get(key);
}
}
/**
* Get the {@link #mMap} object for the {@link #mPropertiesFile}. A call to
* {@link #loadPropertiesFromDisk()} must be made before this.
*
* @return Returns a copy of {@link #mMap} object.
*/
public Map<String, Object> getInternalProperties() {
synchronized (mLock) {
if (mMap == null) mMap = new HashMap<>();
return getMapCopy(mMap);
}
}
/**
* Get the internal {@link Object} value for the key passed from the {@link #mPropertiesFile}.
* The value is returned from the {@link #mMap} in-memory cache, so a call to
* {@link #loadPropertiesFromDisk()} must be made before this.
*
* @param key The key to read from the {@link #mMap} object.
* @return Returns the {@link Object} object. This will be {@code null} if key is not found or
* if object was {@code null}. Use {@link HashMap#containsKey(Object)} to detect the later.
* situation.
*/
public Object getInternalProperty(String key) {
synchronized (mLock) {
// null keys are not allowed to be stored in mMap
if (key != null)
return getInternalProperties().get(key);
else
return null;
}
}
/**
* A static function to get the {@link Properties} object for the propertiesFile. A lock is not
* taken when this function is called.
*
* @param context The {@link Context} to use to show a flash if an exception is raised while
* reading the file. If context is {@code null}, then flash will not be shown.
* @param propertiesFile The {@link File} to read the {@link Properties} from.
* @return Returns the {@link Properties} object. It will be {@code null} if an exception is
* raised while reading the file.
*/
public static Properties getPropertiesFromFile(Context context, File propertiesFile) {
Properties properties = new Properties();
if (propertiesFile == null) {
Logger.logWarn(LOG_TAG, "Not loading properties since file is null");
return properties;
}
try {
try (FileInputStream in = new FileInputStream(propertiesFile)) {
Logger.logVerbose(LOG_TAG, "Loading properties from \"" + propertiesFile.getAbsolutePath() + "\" file");
properties.load(new InputStreamReader(in, StandardCharsets.UTF_8));
}
} catch (Exception e) {
if (context != null)
Toast.makeText(context, "Could not open properties file \"" + propertiesFile.getAbsolutePath() + "\": " + e.getMessage(), Toast.LENGTH_LONG).show();
Logger.logStackTraceWithMessage(LOG_TAG, "Error loading properties file \"" + propertiesFile.getAbsolutePath() + "\"", e);
return null;
}
return properties;
}
/**
* A static function to get the {@link String} value for the {@link Properties} key read from
* the propertiesFile file.
*
* @param context The {@link Context} for the {@link #getPropertiesFromFile(Context,File)} call.
* @param propertiesFile The {@link File} to read the {@link Properties} from.
* @param key The key to read.
* @param def The default value.
* @return Returns the {@link String} object. This will be {@code null} if key is not found.
*/
public static String getProperty(Context context, File propertiesFile, String key, String def) {
return (String) getDefaultIfNull(getDefaultIfNull(getPropertiesFromFile(context, propertiesFile), new Properties()).get(key), def);
}
/**
* A static function to get the internal {@link Object} value for the {@link String} value for
* the {@link Properties} key read from the propertiesFile file.
*
* @param context The {@link Context} for the {@link #getPropertiesFromFile(Context,File)} call.
* @param propertiesFile The {@link File} to read the {@link Properties} from.
* @param key The key to read.
* @param sharedPropertiesParser The implementation of the {@link SharedPropertiesParser} interface.
* @return Returns the {@link String} Object returned by the call to
* {@link SharedPropertiesParser#getInternalPropertyValueFromValue(Context,String,String)}.
*/
public static Object getInternalProperty(Context context, File propertiesFile, String key, @Nonnull SharedPropertiesParser sharedPropertiesParser) {
String value = (String) getDefaultIfNull(getPropertiesFromFile(context, propertiesFile), new Properties()).get(key);
// Call the {@link SharedPropertiesParser#getInternalPropertyValueFromValue(Context,String,String)}
// interface method to get the internal value to return.
return sharedPropertiesParser.getInternalPropertyValueFromValue(context, key, value);
}
/**
* A static function to check if the value is {@code true} for {@link Properties} key read from
* the propertiesFile file.
*
* @param context The {@link Context} for the {@link #getPropertiesFromFile(Context,File)}call.
* @param propertiesFile The {@link File} to read the {@link Properties} from.
* @param key The key to read.
* @return Returns the {@code true} if the {@link Properties} key {@link String} value equals "true",
* regardless of case. If the key does not exist in the file or does not equal "true", then
* {@code false} will be returned.
*/
public static boolean isPropertyValueTrue(Context context, File propertiesFile, String key) {
return (boolean) getBooleanValueForStringValue((String) getProperty(context, propertiesFile, key, null), false);
}
/**
* A static function to check if the value is {@code false} for {@link Properties} key read from
* the propertiesFile file.
*
* @param context The {@link Context} for the {@link #getPropertiesFromFile(Context,File)} call.
* @param propertiesFile The {@link File} to read the {@link Properties} from.
* @param key The key to read.
* @return Returns the {@code true} if the {@link Properties} key {@link String} value equals "false",
* regardless of case. If the key does not exist in the file or does not equal "false", then
* {@code true} will be returned.
*/
public static boolean isPropertyValueFalse(Context context, File propertiesFile, String key) {
return (boolean) getInvertedBooleanValueForStringValue((String) getProperty(context, propertiesFile, key, null), true);
}
/**
* Put a value in a {@link #mMap}.
* The key cannot be {@code null}.
* Only {@code null}, primitive or their wrapper classes or String class objects are allowed to be added to
* the map, although this limitation may be changed.
*
* @param map The {@link Map} object to add value to.
* @param key The key for which to add the value to the map.
* @param value The {@link Object} to add to the map.
* @return Returns {@code true} if value was successfully added, otherwise {@code false}.
*/
public static boolean putToMap(HashMap<String, Object> map, String key, Object value) {
if (map == null) {
Logger.logError(LOG_TAG, "Map passed to SharedProperties.putToProperties() is null");
return false;
}
// null keys are not allowed to be stored in mMap
if (key == null) {
Logger.logError(LOG_TAG, "Cannot put a null key into properties map");
return false;
}
boolean put = false;
if (value != null) {
Class<?> clazz = value.getClass();
if (clazz.isPrimitive() || Primitives.isWrapperType(clazz) || value instanceof String) {
put = true;
}
} else {
put = true;
}
if (put) {
map.put(key, value);
return true;
} else {
Logger.logError(LOG_TAG, "Cannot put a non-primitive value for the key \"" + key + "\" into properties map");
return false;
}
}
/**
* Put a value in a {@link Map}.
* The key cannot be {@code null}.
* Passing {@code null} as the value argument is equivalent to removing the key from the
* properties.
*
* @param properties The {@link Properties} object to add value to.
* @param key The key for which to add the value to the properties.
* @param value The {@link String} to add to the properties.
* @return Returns {@code true} if value was successfully added, otherwise {@code false}.
*/
public static boolean putToProperties(Properties properties, String key, String value) {
if (properties == null) {
Logger.logError(LOG_TAG, "Properties passed to SharedProperties.putToProperties() is null");
return false;
}
// null keys are not allowed to be stored in mMap
if (key == null) {
Logger.logError(LOG_TAG, "Cannot put a null key into properties");
return false;
}
if (value != null) {
properties.put(key, value);
return true;
} else {
properties.remove(key);
}
return true;
}
public static Properties getPropertiesCopy(Properties inputProperties) {
if (inputProperties == null) return null;
Properties outputProperties = new Properties();
for (String key : inputProperties.stringPropertyNames()) {
outputProperties.put(key, inputProperties.get(key));
}
return outputProperties;
}
public static Map<String, Object> getMapCopy(Map<String, Object> map) {
if (map == null) return null;
return new HashMap<>(map);
}
/**
* Get the boolean value for the {@link String} value.
*
* @param value The {@link String} value to convert.
* @param def The default {@link boolean} value to return.
* @return Returns {@code true} or {@code false} if value is the literal string "true" or "false" respectively,
* regardless of case. Otherwise returns default value.
*/
public static boolean getBooleanValueForStringValue(String value, boolean def) {
return (boolean) getDefaultIfNull(MAP_GENERIC_BOOLEAN.get(toLowerCase(value)), def);
}
/**
* Get the inverted boolean value for the {@link String} value.
*
* @param value The {@link String} value to convert.
* @param def The default {@link boolean} value to return.
* @return Returns {@code true} or {@code false} if value is the literal string "false" or "true" respectively,
* regardless of case. Otherwise returns default value.
*/
public static boolean getInvertedBooleanValueForStringValue(String value, boolean def) {
return (boolean) getDefaultIfNull(MAP_GENERIC_INVERTED_BOOLEAN.get(toLowerCase(value)), def);
}
/**
* Get the object itself if it is not {@code null}, otherwise default.
*
* @param object The {@link Object} to check.
* @param def The default {@link Object}.
* @return Returns {@code object} if it is not {@code null}, otherwise returns {@code def}.
*/
public static <T> T getDefaultIfNull(@androidx.annotation.Nullable T object, @androidx.annotation.Nullable T def) {
return (object == null) ? def : object;
}
/**
* Covert the {@link String} value to lowercase.
*
* @param value The {@link String} value to convert.
* @return Returns the lowercased value.
*/
public static String toLowerCase(String value) {
if (value == null) return null; else return value.toLowerCase();
}
}

View File

@@ -0,0 +1,23 @@
package com.termux.shared.settings.properties;
import android.content.Context;
import java.util.HashMap;
/**
* An interface that must be defined by the caller of the {@link SharedProperties} class.
*/
public interface SharedPropertiesParser {
/**
* A function that should return the internal {@link Object} to be stored for a key/value pair
* read from properties file in the {@link HashMap <>} in-memory cache.
*
* @param context The context for operations.
* @param key The key for which the internal object is required.
* @param value The literal value for the property found is the properties file.
* @return Returns the {@link Object} object to store in the {@link HashMap <>} in-memory cache.
*/
Object getInternalPropertyValueFromValue(Context context, String key, String value);
}

View File

@@ -0,0 +1,258 @@
package com.termux.shared.settings.properties;
import com.google.common.collect.ImmutableBiMap;
import com.termux.shared.termux.TermuxConstants;
import com.termux.shared.logger.Logger;
import java.io.File;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/*
* Version: v0.6.0
*
* Changelog
*
* - 0.1.0 (2021-03-11)
* - Initial Release.
*
* - 0.2.0 (2021-03-11)
* - Renamed `HOME_PATH` to `TERMUX_HOME_DIR_PATH`.
* - Renamed `TERMUX_PROPERTIES_PRIMARY_PATH` to `TERMUX_PROPERTIES_PRIMARY_FILE_PATH`.
* - Renamed `TERMUX_PROPERTIES_SECONDARY_FILE_PATH` to `TERMUX_PROPERTIES_SECONDARY_FILE_PATH`.
*
* - 0.3.0 (2021-03-16)
* - Add `*TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR*`.
*
* - 0.4.0 (2021-03-16)
* - Removed `MAP_GENERIC_BOOLEAN` and `MAP_GENERIC_INVERTED_BOOLEAN`.
*
* - 0.5.0 (2021-03-25)
* - Add `KEY_HIDE_SOFT_KEYBOARD_ON_STARTUP`.
*
* - 0.6.0 (2021-04-07)
* - Updated javadocs.
*/
/**
* A class that defines shared constants of the SharedProperties used by Termux app and its plugins.
* This class will be hosted by termux-shared lib and should be imported by other termux plugin
* apps as is instead of copying constants to random classes. The 3rd party apps can also import
* it for interacting with termux apps. If changes are made to this file, increment the version number
* and add an entry in the Changelog section above.
*
* The properties are loaded from the first file found at
* {@link TermuxConstants#TERMUX_PROPERTIES_PRIMARY_FILE_PATH} or
* {@link TermuxConstants#TERMUX_PROPERTIES_SECONDARY_FILE_PATH}
*/
public final class TermuxPropertyConstants {
/** Defines the key for whether to use back key as the escape key */
public static final String KEY_USE_BACK_KEY_AS_ESCAPE_KEY = "back-key"; // Default: "back-key"
public static final String VALUE_BACK_KEY_BEHAVIOUR_BACK = "back";
public static final String VALUE_BACK_KEY_BEHAVIOUR_ESCAPE = "escape";
/** Defines the key for whether to enforce character based input to fix the issue where for some devices like Samsung, the letters might not appear until enter is pressed */
public static final String KEY_ENFORCE_CHAR_BASED_INPUT = "enforce-char-based-input"; // Default: "enforce-char-based-input"
/** Defines the key for whether to hide soft keyboard when termux app is started */
public static final String KEY_HIDE_SOFT_KEYBOARD_ON_STARTUP = "hide-soft-keyboard-on-startup"; // Default: "hide-soft-keyboard-on-startup"
/** Defines the key for whether to use black UI */
public static final String KEY_USE_BLACK_UI = "use-black-ui"; // Default: "use-black-ui"
/** Defines the key for whether to use ctrl space workaround to fix the issue where ctrl+space does not work on some ROMs */
public static final String KEY_USE_CTRL_SPACE_WORKAROUND = "ctrl-space-workaround"; // Default: "ctrl-space-workaround"
/** Defines the key for whether to use fullscreen */
public static final String KEY_USE_FULLSCREEN = "fullscreen"; // Default: "fullscreen"
/** Defines the key for whether to use fullscreen workaround */
public static final String KEY_USE_FULLSCREEN_WORKAROUND = "use-fullscreen-workaround"; // Default: "use-fullscreen-workaround"
/** Defines the key for whether virtual volume keys are disabled */
public static final String KEY_VIRTUAL_VOLUME_KEYS_DISABLED = "volume-keys"; // Default: "volume-keys"
public static final String VALUE_VOLUME_KEY_BEHAVIOUR_VOLUME = "volume";
public static final String VALUE_VOLUME_KEY_BEHAVIOUR_VIRTUAL = "virtual";
/** Defines the key for the bell behaviour */
public static final String KEY_BELL_BEHAVIOUR = "bell-character"; // Default: "bell-character"
public static final String VALUE_BELL_BEHAVIOUR_VIBRATE = "vibrate";
public static final String VALUE_BELL_BEHAVIOUR_BEEP = "beep";
public static final String VALUE_BELL_BEHAVIOUR_IGNORE = "ignore";
public static final String DEFAULT_VALUE_BELL_BEHAVIOUR = VALUE_BELL_BEHAVIOUR_VIBRATE;
public static final int IVALUE_BELL_BEHAVIOUR_VIBRATE = 1;
public static final int IVALUE_BELL_BEHAVIOUR_BEEP = 2;
public static final int IVALUE_BELL_BEHAVIOUR_IGNORE = 3;
public static final int DEFAULT_IVALUE_BELL_BEHAVIOUR = IVALUE_BELL_BEHAVIOUR_VIBRATE;
/** Defines the bidirectional map for bell behaviour values and their internal values */
public static final ImmutableBiMap<String, Integer> MAP_BELL_BEHAVIOUR =
new ImmutableBiMap.Builder<String, Integer>()
.put(VALUE_BELL_BEHAVIOUR_VIBRATE, IVALUE_BELL_BEHAVIOUR_VIBRATE)
.put(VALUE_BELL_BEHAVIOUR_BEEP, IVALUE_BELL_BEHAVIOUR_BEEP)
.put(VALUE_BELL_BEHAVIOUR_IGNORE, IVALUE_BELL_BEHAVIOUR_IGNORE)
.build();
/** Defines the key for the bell behaviour */
public static final String KEY_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR = "terminal-toolbar-height"; // Default: "terminal-toolbar-height"
public static final float IVALUE_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR_MIN = 0.4f;
public static final float IVALUE_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR_MAX = 3;
public static final float DEFAULT_IVALUE_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR = 1;
/** Defines the key for create session shortcut */
public static final String KEY_SHORTCUT_CREATE_SESSION = "shortcut.create-session"; // Default: "shortcut.create-session"
/** Defines the key for next session shortcut */
public static final String KEY_SHORTCUT_NEXT_SESSION = "shortcut.next-session"; // Default: "shortcut.next-session"
/** Defines the key for previous session shortcut */
public static final String KEY_SHORTCUT_PREVIOUS_SESSION = "shortcut.previous-session"; // Default: "shortcut.previous-session"
/** Defines the key for rename session shortcut */
public static final String KEY_SHORTCUT_RENAME_SESSION = "shortcut.rename-session"; // Default: "shortcut.rename-session"
public static final int ACTION_SHORTCUT_CREATE_SESSION = 1;
public static final int ACTION_SHORTCUT_NEXT_SESSION = 2;
public static final int ACTION_SHORTCUT_PREVIOUS_SESSION = 3;
public static final int ACTION_SHORTCUT_RENAME_SESSION = 4;
/** Defines the bidirectional map for session shortcut values and their internal actions */
public static final ImmutableBiMap<String, Integer> MAP_SESSION_SHORTCUTS =
new ImmutableBiMap.Builder<String, Integer>()
.put(KEY_SHORTCUT_CREATE_SESSION, ACTION_SHORTCUT_CREATE_SESSION)
.put(KEY_SHORTCUT_NEXT_SESSION, ACTION_SHORTCUT_NEXT_SESSION)
.put(KEY_SHORTCUT_PREVIOUS_SESSION, ACTION_SHORTCUT_PREVIOUS_SESSION)
.put(KEY_SHORTCUT_RENAME_SESSION, ACTION_SHORTCUT_RENAME_SESSION)
.build();
/** Defines the key for the default working directory */
public static final String KEY_DEFAULT_WORKING_DIRECTORY = "default-working-directory"; // Default: "default-working-directory"
/** Defines the default working directory */
public static final String DEFAULT_IVALUE_DEFAULT_WORKING_DIRECTORY = TermuxConstants.TERMUX_HOME_DIR_PATH;
/** Defines the key for extra keys */
public static final String KEY_EXTRA_KEYS = "extra-keys"; // Default: "extra-keys"
/** Defines the key for extra keys style */
public static final String KEY_EXTRA_KEYS_STYLE = "extra-keys-style"; // Default: "extra-keys-style"
public static final String DEFAULT_IVALUE_EXTRA_KEYS = "[[ESC, TAB, CTRL, ALT, {key: '-', popup: '|'}, DOWN, UP]]";
public static final String DEFAULT_IVALUE_EXTRA_KEYS_STYLE = "default";
/** Defines the set for keys loaded by termux
* Setting this to {@code null} will make {@link SharedProperties} throw an exception.
* */
public static final Set<String> TERMUX_PROPERTIES_LIST = new HashSet<>(Arrays.asList(
// boolean
KEY_ENFORCE_CHAR_BASED_INPUT,
KEY_HIDE_SOFT_KEYBOARD_ON_STARTUP,
KEY_USE_BACK_KEY_AS_ESCAPE_KEY,
KEY_USE_BLACK_UI,
KEY_USE_CTRL_SPACE_WORKAROUND,
KEY_USE_FULLSCREEN,
KEY_USE_FULLSCREEN_WORKAROUND,
KEY_VIRTUAL_VOLUME_KEYS_DISABLED,
TermuxConstants.PROP_ALLOW_EXTERNAL_APPS,
// int
KEY_BELL_BEHAVIOUR,
// float
KEY_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR,
// Integer
KEY_SHORTCUT_CREATE_SESSION,
KEY_SHORTCUT_NEXT_SESSION,
KEY_SHORTCUT_PREVIOUS_SESSION,
KEY_SHORTCUT_RENAME_SESSION,
// String
KEY_DEFAULT_WORKING_DIRECTORY,
KEY_EXTRA_KEYS,
KEY_EXTRA_KEYS_STYLE
));
/** Defines the set for keys loaded by termux that have default boolean behaviour
* "true" -> true
* "false" -> false
* default: false
* */
public static final Set<String> TERMUX_DEFAULT_BOOLEAN_BEHAVIOUR_PROPERTIES_LIST = new HashSet<>(Arrays.asList(
KEY_ENFORCE_CHAR_BASED_INPUT,
KEY_HIDE_SOFT_KEYBOARD_ON_STARTUP,
KEY_USE_CTRL_SPACE_WORKAROUND,
KEY_USE_FULLSCREEN,
KEY_USE_FULLSCREEN_WORKAROUND,
TermuxConstants.PROP_ALLOW_EXTERNAL_APPS
));
/** Defines the set for keys loaded by termux that have default inverted boolean behaviour
* "false" -> true
* "true" -> false
* default: true
* */
public static final Set<String> TERMUX_DEFAULT_INVERETED_BOOLEAN_BEHAVIOUR_PROPERTIES_LIST = new HashSet<>(Arrays.asList(
));
/** Returns the first {@link File} found at
* {@link TermuxConstants#TERMUX_PROPERTIES_PRIMARY_FILE_PATH} or
* {@link TermuxConstants#TERMUX_PROPERTIES_SECONDARY_FILE_PATH}
* from which termux properties can be loaded.
* If the {@link File} found is not a regular file or is not readable then null is returned.
*
* @return Returns the {@link File} object for termux properties.
*/
public static File getTermuxPropertiesFile() {
String[] possiblePropertiesFileLocations = {
TermuxConstants.TERMUX_PROPERTIES_PRIMARY_FILE_PATH,
TermuxConstants.TERMUX_PROPERTIES_SECONDARY_FILE_PATH
};
File propertiesFile = new File(possiblePropertiesFileLocations[0]);
int i = 0;
while (!propertiesFile.exists() && i < possiblePropertiesFileLocations.length) {
propertiesFile = new File(possiblePropertiesFileLocations[i]);
i += 1;
}
if (propertiesFile.isFile() && propertiesFile.canRead()) {
return propertiesFile;
} else {
Logger.logDebug("No readable termux.properties file found");
return null;
}
}
}

View File

@@ -0,0 +1,458 @@
package com.termux.shared.settings.properties;
import android.content.Context;
import android.content.res.Configuration;
import com.termux.shared.logger.Logger;
import com.termux.shared.data.DataUtils;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.annotation.Nonnull;
public class TermuxSharedProperties implements SharedPropertiesParser {
protected final Context mContext;
protected final SharedProperties mSharedProperties;
protected final File mPropertiesFile;
private static final String LOG_TAG = "TermuxSharedProperties";
public TermuxSharedProperties(@Nonnull Context context) {
mContext = context;
mPropertiesFile = TermuxPropertyConstants.getTermuxPropertiesFile();
mSharedProperties = new SharedProperties(context, mPropertiesFile, TermuxPropertyConstants.TERMUX_PROPERTIES_LIST, this);
loadTermuxPropertiesFromDisk();
}
/**
* Reload the termux properties from disk into an in-memory cache.
*/
public void loadTermuxPropertiesFromDisk() {
mSharedProperties.loadPropertiesFromDisk();
dumpPropertiesToLog();
dumpInternalPropertiesToLog();
}
/**
* Get the {@link Properties} from the {@link #mPropertiesFile} file.
*
* @param cached If {@code true}, then the {@link Properties} in-memory cache is returned.
* Otherwise the {@link Properties} object is read directly from the
* {@link #mPropertiesFile} file.
* @return Returns the {@link Properties} object. It will be {@code null} if an exception is
* raised while reading the file.
*/
public Properties getProperties(boolean cached) {
return mSharedProperties.getProperties(cached);
}
/**
* Get the {@link String} value for the key passed from the {@link #mPropertiesFile} file.
*
* @param key The key to read.
* @param def The default value.
* @param cached If {@code true}, then the value is returned from the the {@link Properties} in-memory cache.
* Otherwise the {@link Properties} object is read directly from the file
* and value is returned from it against the key.
* @return Returns the {@link String} object. This will be {@code null} if key is not found.
*/
public String getPropertyValue(String key, String def, boolean cached) {
return SharedProperties.getDefaultIfNull(mSharedProperties.getProperty(key, cached), def);
}
/**
* A function to check if the value is {@code true} for {@link Properties} key read from
* the {@link #mPropertiesFile} file.
*
* @param key The key to read.
* @param cached If {@code true}, then the value is checked from the the {@link Properties} in-memory cache.
* Otherwise the {@link Properties} object is read directly from the file
* and value is checked from it.
* @return Returns the {@code true} if the {@link Properties} key {@link String} value equals "true",
* regardless of case. If the key does not exist in the file or does not equal "true", then
* {@code false} will be returned.
*/
public boolean isPropertyValueTrue(String key, boolean cached) {
return (boolean) SharedProperties.getBooleanValueForStringValue((String) getPropertyValue(key, null, cached), false);
}
/**
* A function to check if the value is {@code false} for {@link Properties} key read from
* the {@link #mPropertiesFile} file.
*
* @param key The key to read.
* @param cached If {@code true}, then the value is checked from the the {@link Properties} in-memory cache.
* Otherwise the {@link Properties} object is read directly from the file
* and value is checked from it.
* @return Returns {@code true} if the {@link Properties} key {@link String} value equals "false",
* regardless of case. If the key does not exist in the file or does not equal "false", then
* {@code true} will be returned.
*/
public boolean isPropertyValueFalse(String key, boolean cached) {
return (boolean) SharedProperties.getInvertedBooleanValueForStringValue((String) getPropertyValue(key, null, cached), true);
}
/**
* Get the internal value {@link Object} {@link HashMap <>} in-memory cache for the
* {@link #mPropertiesFile} file. A call to {@link #loadTermuxPropertiesFromDisk()} must be made
* before this.
*
* @return Returns a copy of {@link Map} object.
*/
public Map<String, Object> getInternalProperties() {
return mSharedProperties.getInternalProperties();
}
/**
* Get the internal {@link Object} value for the key passed from the {@link #mPropertiesFile} file.
* If cache is {@code true}, then value is returned from the {@link HashMap <>} in-memory cache,
* so a call to {@link #loadTermuxPropertiesFromDisk()} must be made before this.
*
* @param key The key to read from the {@link HashMap<>} in-memory cache.
* @param cached If {@code true}, then the value is returned from the the {@link HashMap <>} in-memory cache,
* but if the value is null, then an attempt is made to return the default value.
* If {@code false}, then the {@link Properties} object is read directly from the file
* and internal value is returned for the property value against the key.
* @return Returns the {@link Object} object. This will be {@code null} if key is not found or
* the object stored against the key is {@code null}.
*/
public Object getInternalPropertyValue(String key, boolean cached) {
Object value;
if (cached) {
value = mSharedProperties.getInternalProperty(key);
// If the value is not null since key was found or if the value was null since the
// object stored for the key was itself null, we detect the later by checking if the key
// exists in the map.
if (value != null || mSharedProperties.getInternalProperties().containsKey(key)) {
return value;
} else {
// This should not happen normally unless mMap was modified after the
// {@link #loadTermuxPropertiesFromDisk()} call
// A null value can still be returned by
// {@link #getInternalPropertyValueFromValue(Context,String,String)} for some keys
value = getInternalPropertyValueFromValue(mContext, key, null);
Logger.logWarn(LOG_TAG, "The value for \"" + key + "\" not found in SharedProperties cahce, force returning default value: `" + value + "`");
return value;
}
} else {
// We get the property value directly from file and return its internal value
return getInternalPropertyValueFromValue(mContext, key, mSharedProperties.getProperty(key, false));
}
}
/**
* Override the
* {@link SharedPropertiesParser#getInternalPropertyValueFromValue(Context,String,String)}
* interface function.
*/
@Override
public Object getInternalPropertyValueFromValue(Context context, String key, String value) {
return getInternalTermuxPropertyValueFromValue(context, key, value);
}
/**
* A static function that should return the internal termux {@link Object} for a key/value pair
* read from properties file.
*
* @param context The context for operations.
* @param key The key for which the internal object is required.
* @param value The literal value for the property found is the properties file.
* @return Returns the internal termux {@link Object} object.
*/
public static Object getInternalTermuxPropertyValueFromValue(Context context, String key, String value) {
if (key == null) return null;
/*
For keys where a MAP_* is checked by respective functions. Note that value to this function
would actually be the key for the MAP_*:
- If the value is currently null, then searching MAP_* should also return null and internal default value will be used.
- If the value is not null and does not exist in MAP_*, then internal default value will be used.
- 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_BACK_KEY_AS_ESCAPE_KEY:
return (boolean) getUseBackKeyAsEscapeKeyInternalPropertyValueFromValue(value);
case TermuxPropertyConstants.KEY_USE_BLACK_UI:
return (boolean) getUseBlackUIInternalPropertyValueFromValue(context, value);
case TermuxPropertyConstants.KEY_VIRTUAL_VOLUME_KEYS_DISABLED:
return (boolean) getVolumeKeysDisabledInternalPropertyValueFromValue(value);
// int
case TermuxPropertyConstants.KEY_BELL_BEHAVIOUR:
return (int) getBellBehaviourInternalPropertyValueFromValue(value);
// float
case TermuxPropertyConstants.KEY_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR:
return (float) getTerminalToolbarHeightScaleFactorInternalPropertyValueFromValue(value);
// Integer (may be null)
case TermuxPropertyConstants.KEY_SHORTCUT_CREATE_SESSION:
case TermuxPropertyConstants.KEY_SHORTCUT_NEXT_SESSION:
case TermuxPropertyConstants.KEY_SHORTCUT_PREVIOUS_SESSION:
case TermuxPropertyConstants.KEY_SHORTCUT_RENAME_SESSION:
return (Integer) getCodePointForSessionShortcuts(key, value);
// String (may be null)
case TermuxPropertyConstants.KEY_DEFAULT_WORKING_DIRECTORY:
return (String) getDefaultWorkingDirectoryInternalPropertyValueFromValue(value);
case TermuxPropertyConstants.KEY_EXTRA_KEYS:
return (String) getExtraKeysInternalPropertyValueFromValue(value);
case TermuxPropertyConstants.KEY_EXTRA_KEYS_STYLE:
return (String) getExtraKeysStyleInternalPropertyValueFromValue(value);
default:
// default boolean behaviour
if (TermuxPropertyConstants.TERMUX_DEFAULT_BOOLEAN_BEHAVIOUR_PROPERTIES_LIST.contains(key))
return (boolean) SharedProperties.getBooleanValueForStringValue(value, false);
// default inverted boolean behaviour
else if (TermuxPropertyConstants.TERMUX_DEFAULT_INVERETED_BOOLEAN_BEHAVIOUR_PROPERTIES_LIST.contains(key))
return (boolean) SharedProperties.getInvertedBooleanValueForStringValue(value, true);
// just use String object as is (may be null)
else
return value;
}
}
/**
* Returns {@code true} if value is not {@code null} and equals {@link TermuxPropertyConstants#VALUE_BACK_KEY_BEHAVIOUR_ESCAPE}, otherwise false.
*
* @param value The {@link String} value to convert.
* @return Returns the internal value for value.
*/
public static boolean getUseBackKeyAsEscapeKeyInternalPropertyValueFromValue(String value) {
return SharedProperties.getDefaultIfNull(value, TermuxPropertyConstants.VALUE_BACK_KEY_BEHAVIOUR_BACK).equals(TermuxPropertyConstants.VALUE_BACK_KEY_BEHAVIOUR_ESCAPE);
}
/**
* 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(value, nightMode == Configuration.UI_MODE_NIGHT_YES);
}
/**
* Returns {@code true} if value is not {@code null} and equals
* {@link TermuxPropertyConstants#VALUE_VOLUME_KEY_BEHAVIOUR_VOLUME}, otherwise {@code false}.
*
* @param value The {@link String} value to convert.
* @return Returns the internal value for value.
*/
public static boolean getVolumeKeysDisabledInternalPropertyValueFromValue(String value) {
return SharedProperties.getDefaultIfNull(value, TermuxPropertyConstants.VALUE_VOLUME_KEY_BEHAVIOUR_VIRTUAL).equals(TermuxPropertyConstants.VALUE_VOLUME_KEY_BEHAVIOUR_VOLUME);
}
/**
* Returns the internal value after mapping it based on
* {@code TermuxPropertyConstants#MAP_BELL_BEHAVIOUR} if the value is not {@code null}
* and is valid, otherwise returns {@code TermuxPropertyConstants#DEFAULT_IVALUE_BELL_BEHAVIOUR}.
*
* @param value The {@link String} value to convert.
* @return Returns the internal value for value.
*/
public static int getBellBehaviourInternalPropertyValueFromValue(String value) {
return SharedProperties.getDefaultIfNull(TermuxPropertyConstants.MAP_BELL_BEHAVIOUR.get(SharedProperties.toLowerCase(value)), TermuxPropertyConstants.DEFAULT_IVALUE_BELL_BEHAVIOUR);
}
/**
* Returns the int for the value if its not null and is between
* {@code TermuxPropertyConstants#IVALUE_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR_MIN} and
* {@code TermuxPropertyConstants#IVALUE_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR_MAX},
* otherwise returns {@code TermuxPropertyConstants#DEFAULT_IVALUE_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR}.
*
* @param value The {@link String} value to convert.
* @return Returns the internal value for value.
*/
public static float getTerminalToolbarHeightScaleFactorInternalPropertyValueFromValue(String value) {
return rangeTerminalToolbarHeightScaleFactorValue(DataUtils.getFloatFromString(value, TermuxPropertyConstants.DEFAULT_IVALUE_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR));
}
/**
* Returns the value itself if it is between
* {@code TermuxPropertyConstants#IVALUE_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR_MIN} and
* {@code TermuxPropertyConstants#IVALUE_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR_MAX},
* otherwise returns {@code TermuxPropertyConstants#DEFAULT_IVALUE_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR}.
*
* @param value The value to clamp.
* @return Returns the clamped value.
*/
public static float rangeTerminalToolbarHeightScaleFactorValue(float value) {
return DataUtils.rangedOrDefault(value,
TermuxPropertyConstants.DEFAULT_IVALUE_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR,
TermuxPropertyConstants.IVALUE_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR_MIN,
TermuxPropertyConstants.IVALUE_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR_MAX);
}
/**
* Returns the code point for the value if key is not {@code null} and value is not {@code null} and is valid,
* otherwise returns {@code null}.
*
* @param key The key for session shortcut.
* @param value The {@link String} value to convert.
* @return Returns the internal value for value.
*/
public static Integer getCodePointForSessionShortcuts(String key, String value) {
if (key == null) return null;
if (value == null) return null;
String[] parts = value.toLowerCase().trim().split("\\+");
String input = parts.length == 2 ? parts[1].trim() : null;
if (!(parts.length == 2 && parts[0].trim().equals("ctrl")) || input.isEmpty() || input.length() > 2) {
Logger.logError(LOG_TAG, "Keyboard shortcut '" + key + "' is not Ctrl+<something>");
return null;
}
char c = input.charAt(0);
int codePoint = c;
if (Character.isLowSurrogate(c)) {
if (input.length() != 2 || Character.isHighSurrogate(input.charAt(1))) {
Logger.logError(LOG_TAG, "Keyboard shortcut '" + key + "' is not Ctrl+<something>");
return null;
} else {
codePoint = Character.toCodePoint(input.charAt(1), c);
}
}
return codePoint;
}
/**
* Returns the path itself if a directory exists at it and is readable, otherwise returns
* {@link TermuxPropertyConstants#DEFAULT_IVALUE_DEFAULT_WORKING_DIRECTORY}.
*
* @param path The {@link String} path to check.
* @return Returns the internal value for value.
*/
public static String getDefaultWorkingDirectoryInternalPropertyValueFromValue(String path) {
if (path == null || path.isEmpty()) return TermuxPropertyConstants.DEFAULT_IVALUE_DEFAULT_WORKING_DIRECTORY;
File workDir = new File(path);
if (!workDir.exists() || !workDir.isDirectory() || !workDir.canRead()) {
// Fallback to default directory if user configured working directory does not exist
// or is not a directory or is not readable.
return TermuxPropertyConstants.DEFAULT_IVALUE_DEFAULT_WORKING_DIRECTORY;
} else {
return path;
}
}
/**
* Returns the value itself if it is not {@code null}, otherwise returns {@link TermuxPropertyConstants#DEFAULT_IVALUE_EXTRA_KEYS}.
*
* @param value The {@link String} value to convert.
* @return Returns the internal value for value.
*/
public static String getExtraKeysInternalPropertyValueFromValue(String value) {
return SharedProperties.getDefaultIfNull(value, TermuxPropertyConstants.DEFAULT_IVALUE_EXTRA_KEYS);
}
/**
* Returns the value itself if it is not {@code null}, otherwise returns {@link TermuxPropertyConstants#DEFAULT_IVALUE_EXTRA_KEYS_STYLE}.
*
* @param value {@link String} value to convert.
* @return Returns the internal value for value.
*/
public static String getExtraKeysStyleInternalPropertyValueFromValue(String value) {
return SharedProperties.getDefaultIfNull(value, TermuxPropertyConstants.DEFAULT_IVALUE_EXTRA_KEYS_STYLE);
}
public boolean isEnforcingCharBasedInput() {
return (boolean) getInternalPropertyValue(TermuxPropertyConstants.KEY_ENFORCE_CHAR_BASED_INPUT, true);
}
public boolean shouldSoftKeyboardBeHiddenOnStartup() {
return (boolean) getInternalPropertyValue(TermuxPropertyConstants.KEY_HIDE_SOFT_KEYBOARD_ON_STARTUP, true);
}
public boolean isBackKeyTheEscapeKey() {
return (boolean) getInternalPropertyValue(TermuxPropertyConstants.KEY_USE_BACK_KEY_AS_ESCAPE_KEY, 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);
}
public boolean isUsingFullScreen() {
return (boolean) getInternalPropertyValue(TermuxPropertyConstants.KEY_USE_FULLSCREEN, true);
}
public boolean isUsingFullScreenWorkAround() {
return (boolean) getInternalPropertyValue(TermuxPropertyConstants.KEY_USE_FULLSCREEN_WORKAROUND, true);
}
public boolean areVirtualVolumeKeysDisabled() {
return (boolean) getInternalPropertyValue(TermuxPropertyConstants.KEY_VIRTUAL_VOLUME_KEYS_DISABLED, true);
}
public int getBellBehaviour() {
return (int) getInternalPropertyValue(TermuxPropertyConstants.KEY_BELL_BEHAVIOUR, true);
}
public float getTerminalToolbarHeightScaleFactor() {
return rangeTerminalToolbarHeightScaleFactorValue((float) getInternalPropertyValue(TermuxPropertyConstants.KEY_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR, true));
}
public String getDefaultWorkingDirectory() {
return (String) getInternalPropertyValue(TermuxPropertyConstants.KEY_DEFAULT_WORKING_DIRECTORY, true);
}
public void dumpPropertiesToLog() {
Properties properties = getProperties(true);
StringBuilder propertiesDump = new StringBuilder();
propertiesDump.append("Termux Properties:");
if (properties != null) {
for (String key : properties.stringPropertyNames()) {
propertiesDump.append("\n").append(key).append(": `").append(properties.get(key)).append("`");
}
} else {
propertiesDump.append(" null");
}
Logger.logVerbose(LOG_TAG, propertiesDump.toString());
}
public void dumpInternalPropertiesToLog() {
HashMap<String, Object> internalProperties = (HashMap<String, Object>) getInternalProperties();
StringBuilder internalPropertiesDump = new StringBuilder();
internalPropertiesDump.append("Termux Internal Properties:");
if (internalProperties != null) {
for (String key : internalProperties.keySet()) {
internalPropertiesDump.append("\n").append(key).append(": `").append(internalProperties.get(key)).append("`");
}
}
Logger.logVerbose(LOG_TAG, internalPropertiesDump.toString());
}
}