Skip to content

Commit

Permalink
Move Termux Properties to com.termux.app.settings.properties package
Browse files Browse the repository at this point in the history
The termux properties handling was mixed in with termux preferences. They are now moved out of into a separate sub package, the following classes are added:

- `SharedProperties` class which is an implementation similar to android's `SharedPreferences` interface for reading from ".properties" files which also maintains an in-memory cache for the key/value pairs. Two types of in-memory cache maps are maintained, one for the literal `String` values found in the file for the keys and an additional one that stores (near) primitive `Object` values for internal use by the caller. Write support is currently not implemented, but may be added if we provide users a GUI to modify the properties. We cannot just overwrite the ".properties" files, since comments also exits, so in-place editing would be required.
- `SharedPropertiesParser` interface that the caller of `SharedProperties` must implement. It is currently only used to map `String` values to internal `Object` values.
- `TermuxPropertyConstants` class that defines shared constants of the properties used by Termux app and its plugins. This class should be imported by other termux plugin apps instead of copying and defining their own constants.
- `TermuxSharedProperties` class that acts as manager for handling termux properties. It implements the `SharedPropertiesParser` interface and acts as the wrapper for the `SharedProperties` class.
  • Loading branch information
agnostic-apollo committed Mar 11, 2021
1 parent dbf8477 commit 7b4acb5
Show file tree
Hide file tree
Showing 8 changed files with 1,173 additions and 210 deletions.
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ android {
implementation "androidx.viewpager:viewpager:1.0.0"
implementation "androidx.drawerlayout:drawerlayout:1.1.1"
implementation 'androidx.core:core:1.5.0-beta02'
implementation 'com.google.guava:guava:24.1-jre'
implementation project(":terminal-view")
}

Expand Down
36 changes: 22 additions & 14 deletions app/src/main/java/com/termux/app/TermuxActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@
import com.termux.app.input.BellHandler;
import com.termux.app.input.extrakeys.ExtraKeysView;
import com.termux.app.input.FullScreenWorkAround;
import com.termux.app.settings.properties.TermuxPropertyConstants;
import com.termux.app.settings.properties.TermuxSharedProperties;
import com.termux.terminal.EmulatorDebug;
import com.termux.terminal.TerminalColors;
import com.termux.terminal.TerminalSession;
Expand Down Expand Up @@ -110,6 +112,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
ExtraKeysView mExtraKeysView;

TermuxPreferences mSettings;
TermuxSharedProperties mProperties;

/**
* The connection to the {@link TermuxService}. Requested in {@link #onCreate(Bundle)} with a call to
Expand Down Expand Up @@ -144,16 +147,19 @@ public final class TermuxActivity extends Activity implements ServiceConnection
public void onReceive(Context context, Intent intent) {
if (mIsVisible) {
String whatToReload = intent.getStringExtra(TERMUX_ACTIVITY.EXTRA_RELOAD_STYLE);
Log.d("termux", "Reloading termux style for: " + whatToReload);
if ("storage".equals(whatToReload)) {
if (ensureStoragePermissionGranted())
TermuxInstaller.setupStorageSymlinks(TermuxActivity.this);
return;
}

checkForFontAndColors();
mSettings.reloadFromProperties(TermuxActivity.this);

mProperties.loadTermuxPropertiesFromDisk();

if (mExtraKeysView != null) {
mExtraKeysView.reload(mSettings.mExtraKeys);
mExtraKeysView.reload(mProperties.getExtraKeysInfo());
}
}
}
Expand Down Expand Up @@ -205,7 +211,9 @@ public boolean ensureStoragePermissionGranted() {
@Override
public void onCreate(Bundle bundle) {
mSettings = new TermuxPreferences(this);
mIsUsingBlackUI = mSettings.isUsingBlackUI();
mProperties = new TermuxSharedProperties(this);

mIsUsingBlackUI = mProperties.isUsingBlackUI();
if (mIsUsingBlackUI) {
this.setTheme(R.style.Theme_Termux_Black);
} else {
Expand All @@ -223,7 +231,7 @@ public void onCreate(Bundle bundle) {
return insets;
});

if (mSettings.isUsingFullScreen()) {
if (mProperties.isUsingFullScreen()) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}

Expand All @@ -245,7 +253,7 @@ public void onCreate(Bundle bundle) {


ViewGroup.LayoutParams layoutParams = viewPager.getLayoutParams();
layoutParams.height = layoutParams.height * (mSettings.mExtraKeys == null ? 0 : mSettings.mExtraKeys.getMatrix().length);
layoutParams.height = layoutParams.height * (mProperties.getExtraKeysInfo() == null ? 0 : mProperties.getExtraKeysInfo().getMatrix().length);
viewPager.setLayoutParams(layoutParams);

viewPager.setAdapter(new PagerAdapter() {
Expand All @@ -266,10 +274,10 @@ public Object instantiateItem(@NonNull ViewGroup collection, int position) {
View layout;
if (position == 0) {
layout = mExtraKeysView = (ExtraKeysView) inflater.inflate(R.layout.extra_keys_main, collection, false);
mExtraKeysView.reload(mSettings.mExtraKeys);
mExtraKeysView.reload(mProperties.getExtraKeysInfo());

// apply extra keys fix if enabled in prefs
if (mSettings.isUsingFullScreen() && mSettings.isUsingFullScreenWorkAround()) {
if (mProperties.isUsingFullScreen() && mProperties.isUsingFullScreenWorkAround()) {
FullScreenWorkAround.apply(TermuxActivity.this);
}

Expand Down Expand Up @@ -451,14 +459,14 @@ public void onClipboardText(TerminalSession session, String text) {
public void onBell(TerminalSession session) {
if (!mIsVisible) return;

switch (mSettings.mBellBehaviour) {
case TermuxPreferences.BELL_BEEP:
mBellSoundPool.play(mBellSoundId, 1.f, 1.f, 1, 0, 1.f);
break;
case TermuxPreferences.BELL_VIBRATE:
switch (mProperties.getBellBehaviour()) {
case TermuxPropertyConstants.IVALUE_BELL_BEHAVIOUR_VIBRATE:
BellHandler.getInstance(TermuxActivity.this).doBell();
break;
case TermuxPreferences.BELL_IGNORE:
case TermuxPropertyConstants.IVALUE_BELL_BEHAVIOUR_BEEP:
mBellSoundPool.play(mBellSoundId, 1.f, 1.f, 1, 0, 1.f);
break;
case TermuxPropertyConstants.IVALUE_BELL_BEHAVIOUR_IGNORE:
// Ignore the bell character.
break;
}
Expand Down Expand Up @@ -665,7 +673,7 @@ void addNewSession(boolean failSafe, String sessionName) {

String workingDirectory;
if (currentSession == null) {
workingDirectory = mSettings.mDefaultWorkingDir;
workingDirectory = mProperties.getDefaultWorkingDirectory();
} else {
workingDirectory = currentSession.getCwd();
}
Expand Down
192 changes: 6 additions & 186 deletions app/src/main/java/com/termux/app/TermuxPreferences.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,61 +2,13 @@

import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.preference.PreferenceManager;
import android.util.Log;
import android.util.TypedValue;
import android.widget.Toast;
import com.termux.terminal.TerminalSession;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import androidx.annotation.IntDef;

import static com.termux.terminal.EmulatorDebug.LOG_TAG;
import com.termux.terminal.TerminalSession;

final class TermuxPreferences {

@IntDef({BELL_VIBRATE, BELL_BEEP, BELL_IGNORE})
@Retention(RetentionPolicy.SOURCE)
@interface AsciiBellBehaviour {
}

final static class KeyboardShortcut {

KeyboardShortcut(int codePoint, int shortcutAction) {
this.codePoint = codePoint;
this.shortcutAction = shortcutAction;
}

final int codePoint;
final int shortcutAction;
}

static final int SHORTCUT_ACTION_CREATE_SESSION = 1;
static final int SHORTCUT_ACTION_NEXT_SESSION = 2;
static final int SHORTCUT_ACTION_PREVIOUS_SESSION = 3;
static final int SHORTCUT_ACTION_RENAME_SESSION = 4;

static final int BELL_VIBRATE = 1;
static final int BELL_BEEP = 2;
static final int BELL_IGNORE = 3;

private final int MIN_FONTSIZE;
private static final int MAX_FONTSIZE = 256;

Expand All @@ -65,34 +17,11 @@ final static class KeyboardShortcut {
private static final String CURRENT_SESSION_KEY = "current_session";
private static final String SCREEN_ALWAYS_ON_KEY = "screen_always_on";

private boolean mUseDarkUI;
private boolean mScreenAlwaysOn;
private int mFontSize;

private boolean mUseFullScreen;
private boolean mUseFullScreenWorkAround;

@AsciiBellBehaviour
int mBellBehaviour = BELL_VIBRATE;

boolean mBackIsEscape;
boolean mDisableVolumeVirtualKeys;
boolean mShowExtraKeys;
String mDefaultWorkingDir;

ExtraKeysInfos mExtraKeys;

final List<KeyboardShortcut> shortcuts = new ArrayList<>();

/**
* If value is not in the range [min, max], set it to either min or max.
*/
static int clamp(int value, int min, int max) {
return Math.min(Math.max(value, min), max);
}

TermuxPreferences(Context context) {
reloadFromProperties(context);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);

float dipInPixels = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, context.getResources().getDisplayMetrics());
Expand Down Expand Up @@ -139,18 +68,6 @@ boolean isScreenAlwaysOn() {
return mScreenAlwaysOn;
}

boolean isUsingBlackUI() {
return mUseDarkUI;
}

boolean isUsingFullScreen() {
return mUseFullScreen;
}

boolean isUsingFullScreenWorkAround() {
return mUseFullScreenWorkAround;
}

void setScreenAlwaysOn(Context context, boolean newValue) {
mScreenAlwaysOn = newValue;
PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(SCREEN_ALWAYS_ON_KEY, newValue).apply();
Expand All @@ -169,108 +86,11 @@ static TerminalSession getCurrentSession(TermuxActivity context) {
return null;
}

void reloadFromProperties(Context context) {
File propsFile = new File(TermuxConstants.TERMUX_PROPERTIES_PRIMARY_PATH);
if (!propsFile.exists())
propsFile = new File(TermuxConstants.TERMUX_PROPERTIES_SECONDARY_PATH);

Properties props = new Properties();
try {
if (propsFile.isFile() && propsFile.canRead()) {
try (FileInputStream in = new FileInputStream(propsFile)) {
props.load(new InputStreamReader(in, StandardCharsets.UTF_8));
}
}
} catch (Exception e) {
Toast.makeText(context, "Could not open properties file termux.properties: " + e.getMessage(), Toast.LENGTH_LONG).show();
Log.e("termux", "Error loading props", e);
}

switch (props.getProperty("bell-character", "vibrate")) {
case "beep":
mBellBehaviour = BELL_BEEP;
break;
case "ignore":
mBellBehaviour = BELL_IGNORE;
break;
default: // "vibrate".
mBellBehaviour = BELL_VIBRATE;
break;
}

switch (props.getProperty("use-black-ui", "").toLowerCase()) {
case "true":
mUseDarkUI = true;
break;
case "false":
mUseDarkUI = false;
break;
default:
int nightMode = context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
mUseDarkUI = nightMode == Configuration.UI_MODE_NIGHT_YES;
}

mUseFullScreen = "true".equals(props.getProperty("fullscreen", "false").toLowerCase());
mUseFullScreenWorkAround = "true".equals(props.getProperty("use-fullscreen-workaround", "false").toLowerCase());

mDefaultWorkingDir = props.getProperty("default-working-directory", TermuxConstants.HOME_PATH);
File workDir = new File(mDefaultWorkingDir);
if (!workDir.exists() || !workDir.isDirectory()) {
// Fallback to home directory if user configured working directory is not exist
// or is a regular file.
mDefaultWorkingDir = TermuxConstants.HOME_PATH;
}

String defaultExtraKeys = "[[ESC, TAB, CTRL, ALT, {key: '-', popup: '|'}, DOWN, UP]]";

try {
String extrakeyProp = props.getProperty("extra-keys", defaultExtraKeys);
String extraKeysStyle = props.getProperty("extra-keys-style", "default");
mExtraKeys = new ExtraKeysInfos(extrakeyProp, extraKeysStyle);
} catch (JSONException e) {
Toast.makeText(context, "Could not load the extra-keys property from the config: " + e.toString(), Toast.LENGTH_LONG).show();
Log.e("termux", "Error loading props", e);

try {
mExtraKeys = new ExtraKeysInfos(defaultExtraKeys, "default");
} catch (JSONException e2) {
e2.printStackTrace();
Toast.makeText(context, "Can't create default extra keys", Toast.LENGTH_LONG).show();
mExtraKeys = null;
}
}

mBackIsEscape = "escape".equals(props.getProperty("back-key", "back"));
mDisableVolumeVirtualKeys = "volume".equals(props.getProperty("volume-keys", "virtual"));

shortcuts.clear();
parseAction("shortcut.create-session", SHORTCUT_ACTION_CREATE_SESSION, props);
parseAction("shortcut.next-session", SHORTCUT_ACTION_NEXT_SESSION, props);
parseAction("shortcut.previous-session", SHORTCUT_ACTION_PREVIOUS_SESSION, props);
parseAction("shortcut.rename-session", SHORTCUT_ACTION_RENAME_SESSION, props);
}

private void parseAction(String name, int shortcutAction, Properties props) {
String value = props.getProperty(name);
if (value == null) return;
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) {
Log.e("termux", "Keyboard shortcut '" + name + "' is not Ctrl+<something>");
return;
}

char c = input.charAt(0);
int codePoint = c;
if (Character.isLowSurrogate(c)) {
if (input.length() != 2 || Character.isHighSurrogate(input.charAt(1))) {
Log.e("termux", "Keyboard shortcut '" + name + "' is not Ctrl+<something>");
return;
} else {
codePoint = Character.toCodePoint(input.charAt(1), c);
}
}
shortcuts.add(new KeyboardShortcut(codePoint, shortcutAction));
/**
* If value is not in the range [min, max], set it to either min or max.
*/
static int clamp(int value, int min, int max) {
return Math.min(Math.max(value, min), max);
}

}
Loading

0 comments on commit 7b4acb5

Please sign in to comment.