diff --git a/app/src/main/java/com/termux/app/FullScreenHelper.java b/app/src/main/java/com/termux/app/FullScreenHelper.java
index 08164cce..4c284dd6 100644
--- a/app/src/main/java/com/termux/app/FullScreenHelper.java
+++ b/app/src/main/java/com/termux/app/FullScreenHelper.java
@@ -1,68 +1,65 @@
package com.termux.app;
-import android.app.Activity;
-import android.graphics.Rect;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
import android.view.View;
-import android.view.ViewTreeObserver;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.FrameLayout;
-import android.widget.FrameLayout.LayoutParams;
+
+import com.termux.R;
/**
- * Utility to make the touch keyboard and immersive mode work with full screen activities.
- *
+ * Utility to manage full screen immersive mode.
+ *
* See https://code.google.com/p/android/issues/detail?id=5497
*/
-final class FullScreenHelper implements ViewTreeObserver.OnGlobalLayoutListener {
+final class FullScreenHelper {
- private boolean mEnabled = false;
- private final Activity mActivity;
- private final Rect mWindowRect = new Rect();
+ private boolean mEnabled = false;
+ final TermuxActivity mActivity;
- public FullScreenHelper(Activity activity) {
- this.mActivity = activity;
- }
+ public FullScreenHelper(TermuxActivity activity) {
+ this.mActivity = activity;
+ }
- public void setImmersive(boolean enabled) {
- Window win = mActivity.getWindow();
+ public void setImmersive(boolean enabled) {
+ if (enabled == mEnabled) return;
+ mEnabled = enabled;
- if (enabled == mEnabled) {
- if (!enabled) win.setFlags(0, WindowManager.LayoutParams.FLAG_FULLSCREEN);
- return;
- }
- mEnabled = enabled;
+ View decorView = mActivity.getWindow().getDecorView();
- final View childViewOfContent = ((FrameLayout) mActivity.findViewById(android.R.id.content)).getChildAt(0);
- if (enabled) {
- win.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
- setImmersiveMode();
- childViewOfContent.getViewTreeObserver().addOnGlobalLayoutListener(this);
- } else {
- win.setFlags(0, WindowManager.LayoutParams.FLAG_FULLSCREEN);
- win.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
- childViewOfContent.getViewTreeObserver().removeOnGlobalLayoutListener(this);
- ((LayoutParams) childViewOfContent.getLayoutParams()).height = android.view.ViewGroup.LayoutParams.MATCH_PARENT;
- }
- }
+ if (enabled) {
+ decorView.setOnSystemUiVisibilityChangeListener
+ (new View.OnSystemUiVisibilityChangeListener() {
+ @Override
+ public void onSystemUiVisibilityChange(int visibility) {
+ if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
+ if (mActivity.mSettings.isShowExtraKeys()) {
+ mActivity.findViewById(R.id.viewpager).setVisibility(View.VISIBLE);
+ }
+ setImmersiveMode();
+ } else {
+ mActivity.findViewById(R.id.viewpager).setVisibility(View.GONE);
+ }
+ }
+ });
+ setImmersiveMode();
+ } else {
+ decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
+ decorView.setOnSystemUiVisibilityChangeListener(null);
+ }
+ }
- private void setImmersiveMode() {
- mActivity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
- }
+ private static boolean isColorLight(int color) {
+ double darkness = 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255;
+ return darkness < 0.5;
+ }
- @Override
- public void onGlobalLayout() {
- final View childViewOfContent = ((FrameLayout) mActivity.findViewById(android.R.id.content)).getChildAt(0);
-
- if (mEnabled) setImmersiveMode();
-
- childViewOfContent.getWindowVisibleDisplayFrame(mWindowRect);
- int usableHeightNow = Math.min(mWindowRect.height(), childViewOfContent.getRootView().getHeight());
- FrameLayout.LayoutParams layout = (LayoutParams) childViewOfContent.getLayoutParams();
- if (layout.height != usableHeightNow) {
- layout.height = usableHeightNow;
- childViewOfContent.requestLayout();
- }
- }
+ void setImmersiveMode() {
+ int flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+ | View.SYSTEM_UI_FLAG_FULLSCREEN;
+ int color = ((ColorDrawable) mActivity.getWindow().getDecorView().getBackground()).getColor();
+ if (isColorLight(color)) flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+ mActivity.getWindow().getDecorView().setSystemUiVisibility(flags);
+ }
}
diff --git a/app/src/main/java/com/termux/app/TermuxActivity.java b/app/src/main/java/com/termux/app/TermuxActivity.java
index 212d39d5..b66e2f62 100644
--- a/app/src/main/java/com/termux/app/TermuxActivity.java
+++ b/app/src/main/java/com/termux/app/TermuxActivity.java
@@ -36,6 +36,7 @@ import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.StyleSpan;
+import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Gravity;
@@ -43,10 +44,8 @@ import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
-import android.view.View.OnKeyListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.view.WindowManager;
@@ -61,14 +60,20 @@ import android.widget.TextView;
import android.widget.Toast;
import com.termux.R;
+import com.termux.terminal.EmulatorDebug;
+import com.termux.terminal.TerminalColors;
import com.termux.terminal.TerminalSession;
import com.termux.terminal.TerminalSession.SessionChangedCallback;
-import com.termux.view.TerminalKeyListener;
+import com.termux.terminal.TextStyle;
import com.termux.view.TerminalView;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
+import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -102,6 +107,8 @@ public final class TermuxActivity extends Activity implements ServiceConnection
/** The main view of the activity showing the terminal. Initialized in onCreate(). */
@SuppressWarnings("NullableProblems") @NonNull TerminalView mTerminalView;
+ ExtraKeysView mExtraKeysView;
+
final FullScreenHelper mFullScreenHelper = new FullScreenHelper(this);
TermuxPreferences mSettings;
@@ -139,12 +146,46 @@ public final class TermuxActivity extends Activity implements ServiceConnection
if (ensureStoragePermissionGranted()) TermuxInstaller.setupStorageSymlinks(TermuxActivity.this);
return;
}
- mTerminalView.checkForFontAndColors();
+ checkForFontAndColors();
mSettings.reloadFromProperties(TermuxActivity.this);
}
}
};
+ void checkForFontAndColors() {
+ try {
+ // Hard-coded paths since this file is used also in Termux:Float.
+ @SuppressLint("SdCardPath") File fontFile = new File("/data/data/com.termux/files/home/.termux/font.ttf");
+ @SuppressLint("SdCardPath") File colorsFile = new File("/data/data/com.termux/files/home/.termux/colors.properties");
+
+ final Properties props = new Properties();
+ if (colorsFile.isFile()) {
+ try (InputStream in = new FileInputStream(colorsFile)) {
+ props.load(in);
+ }
+ }
+
+ TerminalColors.COLOR_SCHEME.updateWith(props);
+ TerminalSession session = getCurrentTermSession();
+ if (session != null && session.getEmulator() != null) {
+ session.getEmulator().mColors.reset();
+ }
+ updateBackgroundColor();
+
+ final Typeface newTypeface = (fontFile.exists() && fontFile.length() > 0) ? Typeface.createFromFile(fontFile) : Typeface.MONOSPACE;
+ mTerminalView.setTypeface(newTypeface);
+ } catch (Exception e) {
+ Log.e(EmulatorDebug.LOG_TAG, "Error in checkForFontAndColors()", e);
+ }
+ }
+
+ void updateBackgroundColor() {
+ TerminalSession session = getCurrentTermSession();
+ if (session != null && session.getEmulator() != null) {
+ getWindow().getDecorView().setBackgroundColor(session.getEmulator().mColors.mCurrentColors[TextStyle.COLOR_INDEX_BACKGROUND]);
+ }
+ }
+
/** For processes to access shared internal storage (/sdcard) we need this permission. */
@TargetApi(Build.VERSION_CODES.M)
public boolean ensureStoragePermissionGranted() {
@@ -165,13 +206,11 @@ public final class TermuxActivity extends Activity implements ServiceConnection
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
- // Prevent overdraw:
- getWindow().getDecorView().setBackground(null);
-
mSettings = new TermuxPreferences(this);
setContentView(R.layout.drawer_layout);
mTerminalView = (TerminalView) findViewById(R.id.terminal_view);
+ mTerminalView.setOnKeyListener(new TermuxKeyListener(this));
mTerminalView.setTextSize(mSettings.getFontSize());
mFullScreenHelper.setImmersive(mSettings.isFullScreen());
@@ -196,10 +235,9 @@ public final class TermuxActivity extends Activity implements ServiceConnection
LayoutInflater inflater = LayoutInflater.from(TermuxActivity.this);
View layout;
if (position == 0) {
- layout = (View) inflater.inflate(R.layout.extra_keys_main, collection, false);
- mTerminalView.mModifiers = (TerminalView.KeyboardModifiers) layout;
+ layout = mExtraKeysView = (ExtraKeysView) inflater.inflate(R.layout.extra_keys_main, collection, false);
} else {
- layout = (View) inflater.inflate(R.layout.extra_keys_right, collection, false);
+ layout = inflater.inflate(R.layout.extra_keys_right, collection, false);
final EditText editText = (EditText) layout.findViewById(R.id.text_input);
editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
@@ -224,7 +262,6 @@ public final class TermuxActivity extends Activity implements ServiceConnection
viewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
- int newHeight;
if (position == 0) {
mTerminalView.requestFocus();
} else {
@@ -234,120 +271,13 @@ public final class TermuxActivity extends Activity implements ServiceConnection
}
});
- OnKeyListener keyListener = new OnKeyListener() {
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (event.getAction() != KeyEvent.ACTION_DOWN) return false;
-
- final TerminalSession currentSession = getCurrentTermSession();
- if (currentSession == null) return false;
-
- if (keyCode == KeyEvent.KEYCODE_ENTER && !currentSession.isRunning()) {
- // Return pressed with finished session - remove it.
- currentSession.finishIfRunning();
-
- int index = mTermService.removeTermSession(currentSession);
- mListViewAdapter.notifyDataSetChanged();
- if (mTermService.getSessions().isEmpty()) {
- // There are no sessions to show, so finish the activity.
- finish();
- } else {
- if (index >= mTermService.getSessions().size()) {
- index = mTermService.getSessions().size() - 1;
- }
- switchToSession(mTermService.getSessions().get(index));
- }
- return true;
- } else if (!(event.isCtrlPressed() && event.isShiftPressed())) {
- // Only hook shortcuts with Ctrl+Shift down.
- return false;
- }
-
- // Get the unmodified code point:
- int unicodeChar = event.getUnicodeChar(0);
-
- if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN || unicodeChar == 'n'/* next */) {
- int index = mTermService.getSessions().indexOf(currentSession);
- if (++index >= mTermService.getSessions().size()) index = 0;
- switchToSession(mTermService.getSessions().get(index));
- } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP || unicodeChar == 'p' /* previous */) {
- int index = mTermService.getSessions().indexOf(currentSession);
- if (--index < 0) index = mTermService.getSessions().size() - 1;
- switchToSession(mTermService.getSessions().get(index));
- } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
- getDrawer().openDrawer(Gravity.LEFT);
- } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
- getDrawer().closeDrawers();
- } else if (unicodeChar == 'f'/* full screen */) {
- toggleImmersive();
- } else if (unicodeChar == 'k'/* keyboard */) {
- InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
- } else if (unicodeChar == 'm'/* menu */) {
- mTerminalView.showContextMenu();
- } else if (unicodeChar == 'r'/* rename */) {
- renameSession(currentSession);
- } else if (unicodeChar == 'c'/* create */) {
- addNewSession(false, null);
- } else if (unicodeChar == 'u' /* urls */) {
- showUrlSelection();
- } else if (unicodeChar == 'v') {
- doPaste();
- } else if (unicodeChar == '+' || event.getUnicodeChar(KeyEvent.META_SHIFT_ON) == '+') {
- // We also check for the shifted char here since shift may be required to produce '+',
- // see https://github.com/termux/termux-api/issues/2
- changeFontSize(true);
- } else if (unicodeChar == '-') {
- changeFontSize(false);
- } else if (unicodeChar >= '1' && unicodeChar <= '9') {
- int num = unicodeChar - '1';
- if (mTermService.getSessions().size() > num) switchToSession(mTermService.getSessions().get(num));
- }
- return true;
- }
- };
- mTerminalView.setOnKeyListener(keyListener);
- findViewById(R.id.left_drawer_list).setOnKeyListener(keyListener);
-
- mTerminalView.setOnKeyListener(new TerminalKeyListener() {
- @Override
- public float onScale(float scale) {
- if (scale < 0.9f || scale > 1.1f) {
- boolean increase = scale > 1.f;
- changeFontSize(increase);
- return 1.0f;
- }
- return scale;
- }
-
- @Override
- public void onSingleTapUp(MotionEvent e) {
- InputMethodManager mgr = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- mgr.showSoftInput(mTerminalView, InputMethodManager.SHOW_IMPLICIT);
- }
-
- @Override
- public boolean shouldBackButtonBeMappedToEscape() {
- return mSettings.mBackIsEscape;
- }
-
- @Override
- public void copyModeChanged(boolean copyMode) {
- // Disable drawer while copying.
- getDrawer().setDrawerLockMode(copyMode ? DrawerLayout.LOCK_MODE_LOCKED_CLOSED : DrawerLayout.LOCK_MODE_UNLOCKED);
- }
-
- });
-
View newSessionButton = findViewById(R.id.new_session_button);
-
newSessionButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
addNewSession(false, null);
}
});
-
newSessionButton.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
@@ -380,9 +310,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
findViewById(R.id.toggle_keyboard_button).setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
- View extraKeysView = findViewById(R.id.viewpager);
- mSettings.toggleShowExtraKeys(TermuxActivity.this);
- extraKeysView.setVisibility(mSettings.isShowExtraKeys() ? View.VISIBLE : View.GONE);
+ toggleShowExtraKeys();
return true;
}
});
@@ -394,11 +322,21 @@ public final class TermuxActivity extends Activity implements ServiceConnection
startService(serviceIntent);
if (!bindService(serviceIntent, this, 0)) throw new RuntimeException("bindService() failed");
- mTerminalView.checkForFontAndColors();
+ checkForFontAndColors();
mBellSoundId = mBellSoundPool.load(this, R.raw.bell, 1);
}
+ void toggleShowExtraKeys() {
+ final ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
+ final boolean showNow = mSettings.toggleShowExtraKeys(TermuxActivity.this);
+ viewPager.setVisibility(showNow ? View.VISIBLE : View.GONE);
+ if (showNow && viewPager.getCurrentItem() == 1) {
+ // Focus the text input view if just revealed.
+ findViewById(R.id.text_input).requestFocus();
+ }
+ }
+
/**
* Part of the {@link ServiceConnection} interface. The service is bound with
* {@link #bindService(Intent, ServiceConnection, int)} in {@link #onCreate(Bundle)} which will cause a call to this
@@ -468,7 +406,12 @@ public final class TermuxActivity extends Activity implements ServiceConnection
}
}
- };
+
+ @Override
+ public void onColorsChanged(TerminalSession changedSession) {
+ if (getCurrentTermSession() == changedSession) updateBackgroundColor();
+ }
+ };
ListView listView = (ListView) findViewById(R.id.left_drawer_list);
mListViewAdapter = new ArrayAdapter(getApplicationContext(), R.layout.line_in_drawer, mTermService.getSessions()) {
@@ -563,6 +506,17 @@ public final class TermuxActivity extends Activity implements ServiceConnection
}
}
+ public void switchToSession(boolean forward) {
+ TerminalSession currentSession = getCurrentTermSession();
+ int index = mTermService.getSessions().indexOf(currentSession);
+ if (forward) {
+ if (++index >= mTermService.getSessions().size()) index = 0;
+ } else {
+ if (--index < 0) index = mTermService.getSessions().size() - 1;
+ }
+ switchToSession(mTermService.getSessions().get(index));
+ }
+
@SuppressLint("InflateParams")
void renameSession(final TerminalSession sessionToRename) {
DialogUtils.textInput(this, R.string.session_rename_title, sessionToRename.mSessionName, R.string.session_rename_positive_button, new DialogUtils.TextSetListener() {
@@ -654,7 +608,10 @@ public final class TermuxActivity extends Activity implements ServiceConnection
/** Try switching to session and note about it, but do nothing if already displaying the session. */
void switchToSession(TerminalSession session) {
- if (mTerminalView.attachSession(session)) noteSessionInfo();
+ if (mTerminalView.attachSession(session)) {
+ noteSessionInfo();
+ updateBackgroundColor();
+ }
}
String toToastTitle(TerminalSession session) {
@@ -691,7 +648,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
menu.add(Menu.NONE, CONTEXTMENU_SELECT_URL_ID, Menu.NONE, R.string.select_url);
menu.add(Menu.NONE, CONTEXTMENU_SHARE_TRANSCRIPT_ID, Menu.NONE, R.string.select_all_and_share);
menu.add(Menu.NONE, CONTEXTMENU_RESET_TERMINAL_ID, Menu.NONE, R.string.reset_terminal);
- menu.add(Menu.NONE, CONTEXTMENU_KILL_PROCESS_ID, Menu.NONE, R.string.kill_process).setEnabled(currentSession.isRunning());
+ menu.add(Menu.NONE, CONTEXTMENU_KILL_PROCESS_ID, Menu.NONE, getResources().getString(R.string.kill_process, getCurrentTermSession().getPid())).setEnabled(currentSession.isRunning());
menu.add(Menu.NONE, CONTEXTMENU_TOGGLE_FULLSCREEN_ID, Menu.NONE, R.string.toggle_fullscreen).setCheckable(true).setChecked(mSettings.isFullScreen());
menu.add(Menu.NONE, CONTEXTMENU_STYLING_ID, Menu.NONE, R.string.style_terminal);
menu.add(Menu.NONE, CONTEXTMENU_HELP_ID, Menu.NONE, R.string.help);
diff --git a/app/src/main/java/com/termux/app/TermuxKeyListener.java b/app/src/main/java/com/termux/app/TermuxKeyListener.java
new file mode 100644
index 00000000..df9d1207
--- /dev/null
+++ b/app/src/main/java/com/termux/app/TermuxKeyListener.java
@@ -0,0 +1,283 @@
+package com.termux.app;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.support.v4.widget.DrawerLayout;
+import android.view.Gravity;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.inputmethod.InputMethodManager;
+
+import com.termux.terminal.KeyHandler;
+import com.termux.terminal.TerminalEmulator;
+import com.termux.terminal.TerminalSession;
+import com.termux.view.TerminalKeyListener;
+
+import java.util.List;
+
+public final class TermuxKeyListener implements TerminalKeyListener {
+
+ final TermuxActivity mActivity;
+
+ /** Keeping track of the special keys acting as Ctrl and Fn for the soft keyboard and other hardware keys. */
+ boolean mVirtualControlKeyDown, mVirtualFnKeyDown;
+
+ public TermuxKeyListener(TermuxActivity activity) {
+ this.mActivity = activity;
+ }
+
+ @Override
+ public float onScale(float scale) {
+ if (scale < 0.9f || scale > 1.1f) {
+ boolean increase = scale > 1.f;
+ mActivity.changeFontSize(increase);
+ return 1.0f;
+ }
+ return scale;
+ }
+
+ @Override
+ public void onSingleTapUp(MotionEvent e) {
+ InputMethodManager mgr = (InputMethodManager) mActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
+ mgr.showSoftInput(mActivity.mTerminalView, InputMethodManager.SHOW_IMPLICIT);
+ }
+
+ @Override
+ public boolean shouldBackButtonBeMappedToEscape() {
+ return mActivity.mSettings.mBackIsEscape;
+ }
+
+ @Override
+ public void copyModeChanged(boolean copyMode) {
+ // Disable drawer while copying.
+ mActivity.getDrawer().setDrawerLockMode(copyMode ? DrawerLayout.LOCK_MODE_LOCKED_CLOSED : DrawerLayout.LOCK_MODE_UNLOCKED);
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent e, TerminalSession currentSession) {
+ if (handleVirtualKeys(keyCode, e, true)) return true;
+
+ TermuxService service = mActivity.mTermService;
+
+ if (keyCode == KeyEvent.KEYCODE_ENTER && !currentSession.isRunning()) {
+ // Return pressed with finished session - remove it.
+ currentSession.finishIfRunning();
+
+ int index = service.removeTermSession(currentSession);
+ mActivity.mListViewAdapter.notifyDataSetChanged();
+ if (mActivity.mTermService.getSessions().isEmpty()) {
+ // There are no sessions to show, so finish the activity.
+ mActivity.finish();
+ } else {
+ if (index >= service.getSessions().size()) {
+ index = service.getSessions().size() - 1;
+ }
+ mActivity.switchToSession(service.getSessions().get(index));
+ }
+ return true;
+ } else if (e.isCtrlPressed() && e.isShiftPressed()) {
+ // Get the unmodified code point:
+ int unicodeChar = e.getUnicodeChar(0);
+
+ if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN || unicodeChar == 'n'/* next */) {
+ mActivity.switchToSession(true);
+ } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP || unicodeChar == 'p' /* previous */) {
+ mActivity.switchToSession(false);
+ } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
+ mActivity.getDrawer().openDrawer(Gravity.LEFT);
+ } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
+ mActivity.getDrawer().closeDrawers();
+ } else if (unicodeChar == 'f'/* full screen */) {
+ mActivity.toggleImmersive();
+ } else if (unicodeChar == 'k'/* keyboard */) {
+ InputMethodManager imm = (InputMethodManager) mActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
+ } else if (unicodeChar == 'm'/* menu */) {
+ mActivity.mTerminalView.showContextMenu();
+ } else if (unicodeChar == 'r'/* rename */) {
+ mActivity.renameSession(currentSession);
+ } else if (unicodeChar == 'c'/* create */) {
+ mActivity.addNewSession(false, null);
+ } else if (unicodeChar == 'u' /* urls */) {
+ mActivity.showUrlSelection();
+ } else if (unicodeChar == 'v') {
+ mActivity.doPaste();
+ } else if (unicodeChar == '+' || e.getUnicodeChar(KeyEvent.META_SHIFT_ON) == '+') {
+ // We also check for the shifted char here since shift may be required to produce '+',
+ // see https://github.com/termux/termux-api/issues/2
+ mActivity.changeFontSize(true);
+ } else if (unicodeChar == '-') {
+ mActivity.changeFontSize(false);
+ } else if (unicodeChar >= '1' && unicodeChar <= '9') {
+ int num = unicodeChar - '1';
+ if (service.getSessions().size() > num) mActivity.switchToSession(service.getSessions().get(num));
+ }
+ return true;
+ }
+
+ return false;
+
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent e) {
+ return handleVirtualKeys(keyCode, e, false);
+ }
+
+ @Override
+ public boolean readControlKey() {
+ return (mActivity.mExtraKeysView != null && mActivity.mExtraKeysView.readControlButton()) || mVirtualControlKeyDown;
+ }
+
+ @Override
+ public boolean readAltKey() {
+ return (mActivity.mExtraKeysView != null && mActivity.mExtraKeysView.readAltButton());
+ }
+
+ @Override
+ public boolean onCodePoint(final int codePoint, boolean ctrlDown, TerminalSession session) {
+ if (mVirtualFnKeyDown) {
+ int resultingKeyCode = -1;
+ int resultingCodePoint = -1;
+ boolean altDown = false;
+ int lowerCase = Character.toLowerCase(codePoint);
+ switch (lowerCase) {
+ // Arrow keys.
+ case 'w':
+ resultingKeyCode = KeyEvent.KEYCODE_DPAD_UP;
+ break;
+ case 'a':
+ resultingKeyCode = KeyEvent.KEYCODE_DPAD_LEFT;
+ break;
+ case 's':
+ resultingKeyCode = KeyEvent.KEYCODE_DPAD_DOWN;
+ break;
+ case 'd':
+ resultingKeyCode = KeyEvent.KEYCODE_DPAD_RIGHT;
+ break;
+
+ // Page up and down.
+ case 'p':
+ resultingKeyCode = KeyEvent.KEYCODE_PAGE_UP;
+ break;
+ case 'n':
+ resultingKeyCode = KeyEvent.KEYCODE_PAGE_DOWN;
+ break;
+
+ // Some special keys:
+ case 't':
+ resultingKeyCode = KeyEvent.KEYCODE_TAB;
+ break;
+ case 'i':
+ resultingKeyCode = KeyEvent.KEYCODE_INSERT;
+ break;
+ case 'h':
+ resultingKeyCode = KeyEvent.KEYCODE_MOVE_HOME;
+ break;
+
+ // Special characters to input.
+ case 'u':
+ resultingCodePoint = '_';
+ break;
+ case 'l':
+ resultingCodePoint = '|';
+ break;
+
+ // Function keys.
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ resultingKeyCode = (codePoint - '1') + KeyEvent.KEYCODE_F1;
+ break;
+ case '0':
+ resultingKeyCode = KeyEvent.KEYCODE_F10;
+ break;
+
+ // Other special keys.
+ case 'e':
+ resultingCodePoint = /*Escape*/ 27;
+ break;
+ case '.':
+ resultingCodePoint = /*^.*/ 28;
+ break;
+
+ case 'b': // alt+b, jumping backward in readline.
+ case 'f': // alf+f, jumping forward in readline.
+ case 'x': // alt+x, common in emacs.
+ resultingCodePoint = lowerCase;
+ altDown = true;
+ break;
+
+ // Volume control.
+ case 'v':
+ resultingCodePoint = -1;
+ AudioManager audio = (AudioManager) mActivity.getSystemService(Context.AUDIO_SERVICE);
+ audio.adjustSuggestedStreamVolume(AudioManager.ADJUST_SAME, AudioManager.USE_DEFAULT_STREAM_TYPE, AudioManager.FLAG_SHOW_UI);
+ break;
+
+ // Writing mode:
+ case 'q':
+ mActivity.toggleShowExtraKeys();
+ break;
+ }
+
+ if (resultingKeyCode != -1) {
+ TerminalEmulator term = session.getEmulator();
+ session.write(KeyHandler.getCode(resultingKeyCode, 0, term.isCursorKeysApplicationMode(), term.isKeypadApplicationMode()));
+ } else if (resultingCodePoint != -1) {
+ session.writeCodePoint(altDown, resultingCodePoint);
+ }
+ return true;
+ } else if (ctrlDown) {
+ List shortcuts = mActivity.mSettings.shortcuts;
+ if (!shortcuts.isEmpty()) {
+ for (int i = shortcuts.size() - 1; i >= 0; i--) {
+ TermuxPreferences.KeyboardShortcut shortcut = shortcuts.get(i);
+ if (codePoint == shortcut.codePoint) {
+ switch (shortcut.shortcutAction) {
+ case TermuxPreferences.SHORTCUT_ACTION_CREATE_SESSION:
+ mActivity.addNewSession(false, null);
+ return true;
+ case TermuxPreferences.SHORTCUT_ACTION_PREVIOUS_SESSION:
+ mActivity.switchToSession(false);
+ return true;
+ case TermuxPreferences.SHORTCUT_ACTION_NEXT_SESSION:
+ mActivity.switchToSession(true);
+ return true;
+ case TermuxPreferences.SHORTCUT_ACTION_RENAME_SESSION:
+ mActivity.renameSession(mActivity.getCurrentTermSession());
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /** Handle dedicated volume buttons as virtual keys if applicable. */
+ private boolean handleVirtualKeys(int keyCode, KeyEvent event, boolean down) {
+ InputDevice inputDevice = event.getDevice();
+ if (inputDevice != null && inputDevice.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) {
+ // Do not steal dedicated buttons from a full external keyboard.
+ return false;
+ } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
+ mVirtualControlKeyDown = down;
+ return true;
+ } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
+ mVirtualFnKeyDown = down;
+ return true;
+ }
+ return false;
+ }
+
+
+}
diff --git a/app/src/main/java/com/termux/app/TermuxPreferences.java b/app/src/main/java/com/termux/app/TermuxPreferences.java
index 54b04d0c..a4ef7798 100644
--- a/app/src/main/java/com/termux/app/TermuxPreferences.java
+++ b/app/src/main/java/com/termux/app/TermuxPreferences.java
@@ -14,6 +14,8 @@ import java.io.File;
import java.io.FileInputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Properties;
final class TermuxPreferences {
@@ -83,9 +85,10 @@ final class TermuxPreferences {
return mShowExtraKeys;
}
- void toggleShowExtraKeys(Context context) {
+ boolean toggleShowExtraKeys(Context context) {
mShowExtraKeys = !mShowExtraKeys;
PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(SHOW_EXTRA_KEYS_KEY, mShowExtraKeys).apply();
+ return mShowExtraKeys;
}
int getFontSize() {
@@ -123,7 +126,9 @@ final class TermuxPreferences {
public void reloadFromProperties(Context context) {
try {
- File propsFile = new File(TermuxService.HOME_PATH + "/.config/termux/termux.properties");
+ File propsFile = new File(TermuxService.HOME_PATH + "/.termux/termux.properties");
+ if (!propsFile.exists()) propsFile = new File(TermuxService.HOME_PATH + "/.config/termux/termux.properties");
+
Properties props = new Properties();
if (propsFile.isFile() && propsFile.canRead()) {
try (FileInputStream in = new FileInputStream(propsFile)) {
@@ -144,10 +149,57 @@ final class TermuxPreferences {
}
mBackIsEscape = "escape".equals(props.getProperty("back-key", "back"));
+
+ 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);
} catch (Exception e) {
Toast.makeText(context, "Error loading properties: " + e.getMessage(), Toast.LENGTH_LONG).show();
Log.e("termux", "Error loading props", e);
}
}
+ public static final int SHORTCUT_ACTION_CREATE_SESSION = 1;
+ public static final int SHORTCUT_ACTION_NEXT_SESSION = 2;
+ public static final int SHORTCUT_ACTION_PREVIOUS_SESSION = 3;
+ public static final int SHORTCUT_ACTION_RENAME_SESSION = 4;
+
+ public final static class KeyboardShortcut {
+
+ public KeyboardShortcut(int codePoint, int shortcutAction) {
+ this.codePoint = codePoint;
+ this.shortcutAction = shortcutAction;
+ }
+
+ final int codePoint;
+ final int shortcutAction;
+ }
+
+ final List shortcuts = new ArrayList<>();
+
+ private void parseAction(String name, int shortcutAction, Properties props) {
+ String value = props.getProperty(name);
+ if (value == null) return;
+ String[] parts = value.trim().split("\\+");
+ String input = parts.length == 2 ? parts[1].trim() : null;
+ if (!(parts.length == 2 && parts[0].trim().equalsIgnoreCase("ctrl")) || input.isEmpty() || input.length() > 2) {
+ Log.e("termux", "Keyboard shortcut '" + name + "' is not Ctrl+");
+ 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+");
+ return;
+ } else {
+ codePoint = Character.toCodePoint(input.charAt(1), c);
+ }
+ }
+ shortcuts.add(new KeyboardShortcut(codePoint, shortcutAction));
+ }
+
}
diff --git a/app/src/main/java/com/termux/app/TermuxService.java b/app/src/main/java/com/termux/app/TermuxService.java
index 97fa02c2..05714619 100644
--- a/app/src/main/java/com/termux/app/TermuxService.java
+++ b/app/src/main/java/com/termux/app/TermuxService.java
@@ -352,4 +352,9 @@ public final class TermuxService extends Service implements SessionChangedCallba
if (mSessionChangeCallback != null) mSessionChangeCallback.onBell(session);
}
+ @Override
+ public void onColorsChanged(TerminalSession session) {
+ if (mSessionChangeCallback != null) mSessionChangeCallback.onColorsChanged(session);
+ }
+
}
diff --git a/app/src/main/java/com/termux/terminal/TerminalEmulator.java b/app/src/main/java/com/termux/terminal/TerminalEmulator.java
index 36110974..5ae7e84e 100644
--- a/app/src/main/java/com/termux/terminal/TerminalEmulator.java
+++ b/app/src/main/java/com/termux/terminal/TerminalEmulator.java
@@ -1816,6 +1816,7 @@ public final class TerminalEmulator {
return;
} else {
mColors.tryParseColor(colorIndex, textParameter.substring(parsingPairStart, i));
+ mSession.onColorsChanged();
colorIndex = -1;
parsingPairStart = -1;
}
@@ -1851,6 +1852,7 @@ public final class TerminalEmulator {
+ String.format(Locale.US, "%04x", b) + bellOrStringTerminator);
} else {
mColors.tryParseColor(specialIndex, colorSpec);
+ mSession.onColorsChanged();
}
specialIndex++;
if (endOfInput || (specialIndex > TextStyle.COLOR_INDEX_CURSOR) || ++charIndex >= textParameter.length()) break;
@@ -1877,6 +1879,7 @@ public final class TerminalEmulator {
// parameters are given, the entire table will be reset.
if (textParameter.isEmpty()) {
mColors.reset();
+ mSession.onColorsChanged();
} else {
int lastIndex = 0;
for (int charIndex = 0;; charIndex++) {
@@ -1885,6 +1888,7 @@ public final class TerminalEmulator {
try {
int colorToReset = Integer.parseInt(textParameter.substring(lastIndex, charIndex));
mColors.reset(colorToReset);
+ mSession.onColorsChanged();
if (endOfInput) break;
charIndex++;
lastIndex = charIndex;
@@ -1899,6 +1903,7 @@ public final class TerminalEmulator {
case 111: // Reset background color.
case 112: // Reset cursor color.
mColors.reset(TextStyle.COLOR_INDEX_FOREGROUND + (value - 110));
+ mSession.onColorsChanged();
break;
case 119: // Reset highlight color.
break;
@@ -2273,6 +2278,7 @@ public final class TerminalEmulator {
mUtf8Index = mUtf8ToFollow = 0;
mColors.reset();
+ mSession.onColorsChanged();
}
public String getSelectedText(int x1, int y1, int x2, int y2) {
diff --git a/app/src/main/java/com/termux/terminal/TerminalOutput.java b/app/src/main/java/com/termux/terminal/TerminalOutput.java
index e2c4c1a8..3266779f 100644
--- a/app/src/main/java/com/termux/terminal/TerminalOutput.java
+++ b/app/src/main/java/com/termux/terminal/TerminalOutput.java
@@ -23,4 +23,6 @@ public abstract class TerminalOutput {
/** Notify the terminal client that a bell character (ASCII 7, bell, BEL, \a, ^G)) has been received. */
public abstract void onBell();
+ public abstract void onColorsChanged();
+
}
diff --git a/app/src/main/java/com/termux/terminal/TerminalSession.java b/app/src/main/java/com/termux/terminal/TerminalSession.java
index c7ecec3b..4df0d3fa 100644
--- a/app/src/main/java/com/termux/terminal/TerminalSession.java
+++ b/app/src/main/java/com/termux/terminal/TerminalSession.java
@@ -41,6 +41,9 @@ public final class TerminalSession extends TerminalOutput {
void onClipboardText(TerminalSession session, String text);
void onBell(TerminalSession session);
+
+ void onColorsChanged(TerminalSession session);
+
}
private static FileDescriptor wrapFileDescriptor(int fileDescriptor) {
@@ -329,4 +332,11 @@ public final class TerminalSession extends TerminalOutput {
mChangeCallback.onBell(this);
}
+ @Override
+ public void onColorsChanged() {
+ mChangeCallback.onColorsChanged(this);
+ }
+
+ public int getPid() { return mShellPid; }
+
}
diff --git a/app/src/main/java/com/termux/view/TerminalKeyListener.java b/app/src/main/java/com/termux/view/TerminalKeyListener.java
index 15350749..a1f801da 100644
--- a/app/src/main/java/com/termux/view/TerminalKeyListener.java
+++ b/app/src/main/java/com/termux/view/TerminalKeyListener.java
@@ -1,8 +1,11 @@
package com.termux.view;
+import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
+import com.termux.terminal.TerminalSession;
+
/**
* Input and scale listener which may be set on a {@link TerminalView} through
* {@link TerminalView#setOnKeyListener(TerminalKeyListener)}.
@@ -21,4 +24,14 @@ public interface TerminalKeyListener {
void copyModeChanged(boolean copyMode);
+ boolean onKeyDown(int keyCode, KeyEvent e, TerminalSession session);
+
+ boolean onKeyUp(int keyCode, KeyEvent e);
+
+ boolean readControlKey();
+
+ boolean readAltKey();
+
+ boolean onCodePoint(int codePoint, boolean ctrlDown, TerminalSession session);
+
}
diff --git a/app/src/main/java/com/termux/view/TerminalRenderer.java b/app/src/main/java/com/termux/view/TerminalRenderer.java
index 3f8eca0a..9f56c5d5 100644
--- a/app/src/main/java/com/termux/view/TerminalRenderer.java
+++ b/app/src/main/java/com/termux/view/TerminalRenderer.java
@@ -64,8 +64,7 @@ final class TerminalRenderer {
final TerminalBuffer screen = mEmulator.getScreen();
final int[] palette = mEmulator.mColors.mCurrentColors;
- int fillColor = palette[reverseVideo ? TextStyle.COLOR_INDEX_FOREGROUND : TextStyle.COLOR_INDEX_BACKGROUND];
- canvas.drawColor(fillColor, PorterDuff.Mode.SRC);
+ if (reverseVideo) canvas.drawColor(palette[TextStyle.COLOR_INDEX_FOREGROUND], PorterDuff.Mode.SRC);
float heightOffset = mFontLineSpacingAndAscent;
for (int row = topRow; row < endRow; row++) {
diff --git a/app/src/main/java/com/termux/view/TerminalView.java b/app/src/main/java/com/termux/view/TerminalView.java
index f1fb3594..49bbd387 100644
--- a/app/src/main/java/com/termux/view/TerminalView.java
+++ b/app/src/main/java/com/termux/view/TerminalView.java
@@ -10,7 +10,6 @@ import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
-import android.media.AudioManager;
import android.os.Build;
import android.text.InputType;
import android.text.TextUtils;
@@ -34,28 +33,15 @@ import com.termux.R;
import com.termux.terminal.EmulatorDebug;
import com.termux.terminal.KeyHandler;
import com.termux.terminal.TerminalBuffer;
-import com.termux.terminal.TerminalColors;
import com.termux.terminal.TerminalEmulator;
import com.termux.terminal.TerminalSession;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.InputStream;
-import java.util.Properties;
-
/** View displaying and interacting with a {@link TerminalSession}. */
public final class TerminalView extends View {
/** Log view key and IME events. */
private static final boolean LOG_KEY_EVENTS = false;
- public interface KeyboardModifiers {
- boolean readControlButton();
- boolean readAltButton();
- }
-
- public KeyboardModifiers mModifiers;
-
/** The currently displayed terminal session, whose emulator is {@link #mEmulator}. */
TerminalSession mTermSession;
/** Our terminal emulator whose session is {@link #mTermSession}. */
@@ -68,9 +54,6 @@ public final class TerminalView extends View {
/** The top row of text to display. Ranges from -activeTranscriptRows to 0. */
int mTopRow;
- /** Keeping track of the special keys acting as Ctrl and Fn for the soft keyboard and other hardware keys. */
- boolean mVirtualControlKeyDown, mVirtualFnKeyDown;
-
boolean mIsSelectingText = false, mIsDraggingLeftSelection, mInitialTextSelection;
int mSelX1 = -1, mSelX2 = -1, mSelY1 = -1, mSelY2 = -1;
float mSelectionDownX, mSelectionDownY;
@@ -245,10 +228,13 @@ public final class TerminalView extends View {
//
// If using just "TYPE_NULL", there is a problem with the "Google Pinyin Input" being in
// word mode when used with the "En" tab available when the "Show English keyboard" option
- // is enabled - see https://github.com/termux/termux-packages/issues/25.
+ // is enabled - see https://github.com/termux/termux-packages/issues/25. It also causes
+ // the normal Google keyboard to show a row of numbers, see
+ // https://github.com/termux/termux-app/issues/87
//
- // Adding TYPE_TEXT_FLAG_NO_SUGGESTIONS fixes Pinyin Input, put causes Swype to be put in
- // word mode... Using TYPE_TEXT_VARIATION_VISIBLE_PASSWORD fixes that.
+ // Adding TYPE_TEXT_FLAG_NO_SUGGESTIONS fixes Pinyin Input and removes the row of numbers
+ // on the Google keyboard. . It also causes Swype to be put in
+ // word mode, but using TYPE_TEXT_VARIATION_VISIBLE_PASSWORD fixes that.
//
// So a bit messy. If this gets too messy it's perhaps best resolved by reverting back to just
// "TYPE_NULL" and let the Pinyin Input english keyboard be in word mode.
@@ -259,28 +245,20 @@ public final class TerminalView extends View {
return new BaseInputConnection(this, true) {
- @Override
- public boolean beginBatchEdit() {
- if (LOG_KEY_EVENTS) Log.i(EmulatorDebug.LOG_TAG, "IME: beginBatchEdit()");
- return true;
- }
-
- @Override
- public boolean endBatchEdit() {
- if (LOG_KEY_EVENTS) Log.i(EmulatorDebug.LOG_TAG, "IME: endBatchEdit()");
- return false;
- }
-
@Override
public boolean finishComposingText() {
if (LOG_KEY_EVENTS) Log.i(EmulatorDebug.LOG_TAG, "IME: finishComposingText()");
commitText(getEditable(), 0);
+
+ // Clear the editable.
+ getEditable().clear();
+
return true;
}
- @Override
- public boolean commitText(CharSequence text, int newCursorPosition) {
- if (LOG_KEY_EVENTS) Log.i(EmulatorDebug.LOG_TAG, "IME: commitText(\"" + text + "\", " + newCursorPosition + ")");
+ @Override
+ public boolean commitText(CharSequence text, int newCursorPosition) {
+ if (LOG_KEY_EVENTS) Log.i(EmulatorDebug.LOG_TAG, "IME: commitText(\"" + text + "\", " + newCursorPosition + ")");
if (mEmulator == null) return true;
final int textLengthInChars = text.length();
for (int i = 0; i < textLengthInChars; i++) {
@@ -296,9 +274,24 @@ public final class TerminalView extends View {
} else {
codePoint = firstChar;
}
- inputCodePoint(codePoint, false, false);
+
+ boolean ctrlHeld = false;
+ if (codePoint <= 31 && codePoint != 27) {
+ // E.g. penti keyboard for ctrl input.
+ ctrlHeld = true;
+ switch (codePoint) {
+ case 31: codePoint = '_'; break;
+ case 30: codePoint = '^'; break;
+ case 29: codePoint = ']'; break;
+ case 28: codePoint = '\\'; break;
+ default: codePoint += 96; break;
+ }
+ }
+
+ inputCodePoint(codePoint, ctrlHeld, false);
}
- return true;
+
+ return true;
}
@Override
@@ -313,7 +306,18 @@ public final class TerminalView extends View {
return true;
}
- };
+ @Override
+ public boolean setComposingText(CharSequence text, int newCursorPosition) {
+ if (text.length() == 0) {
+ // Avoid log spam "SpannableStringBuilder: SPAN_EXCLUSIVE_EXCLUSIVE spans cannot
+ // have a zero length" when backspacing with the Google keyboard.
+ getEditable().clear();
+ } else {
+ super.setComposingText(text, newCursorPosition);
+ }
+ return true;
+ }
+ };
}
@Override
@@ -378,6 +382,12 @@ public final class TerminalView extends View {
updateSize();
}
+ public void setTypeface(Typeface newTypeface) {
+ mRenderer = new TerminalRenderer(mRenderer.mTextSize, newTypeface);
+ updateSize();
+ invalidate();
+ }
+
@Override
public boolean onCheckIsTextEditor() {
return true;
@@ -546,19 +556,22 @@ public final class TerminalView extends View {
if (LOG_KEY_EVENTS) Log.i(EmulatorDebug.LOG_TAG, "onKeyDown(keyCode=" + keyCode + ", isSystem()=" + event.isSystem() + ", event=" + event + ")");
if (mEmulator == null) return true;
- int metaState = event.getMetaState();
- boolean controlDownFromEvent = event.isCtrlPressed();
- boolean leftAltDownFromEvent = (metaState & KeyEvent.META_ALT_LEFT_ON) != 0;
- boolean rightAltDownFromEvent = (metaState & KeyEvent.META_ALT_RIGHT_ON) != 0;
-
- if (handleVirtualKeys(keyCode, event, true)) {
+ if (mOnKeyListener.onKeyDown(keyCode, event, mTermSession)) {
invalidate();
return true;
} else if (event.isSystem() && (!mOnKeyListener.shouldBackButtonBeMappedToEscape() || keyCode != KeyEvent.KEYCODE_BACK)) {
return super.onKeyDown(keyCode, event);
- }
+ } else if (event.getAction() == KeyEvent.ACTION_MULTIPLE && keyCode == KeyEvent.KEYCODE_UNKNOWN) {
+ mTermSession.write(event.getCharacters());
+ return true;
+ }
- int keyMod = 0;
+ final int metaState = event.getMetaState();
+ final boolean controlDownFromEvent = event.isCtrlPressed();
+ final boolean leftAltDownFromEvent = (metaState & KeyEvent.META_ALT_LEFT_ON) != 0;
+ final boolean rightAltDownFromEvent = (metaState & KeyEvent.META_ALT_RIGHT_ON) != 0;
+
+ int keyMod = 0;
if (controlDownFromEvent) keyMod |= KeyHandler.KEYMOD_CTRL;
if (event.isAltPressed()) keyMod |= KeyHandler.KEYMOD_ALT;
if (event.isShiftPressed()) keyMod |= KeyHandler.KEYMOD_SHIFT;
@@ -608,15 +621,12 @@ public final class TerminalView extends View {
+ leftAltDownFromEvent + ")");
}
- boolean controlDown = controlDownFromEvent || mVirtualControlKeyDown;
- boolean altDown = leftAltDownFromEvent;
- if (mModifiers != null) {
- if (mModifiers.readControlButton()) controlDown = true;
- if (mModifiers.readAltButton()) altDown = true;
- }
+ final boolean controlDown = controlDownFromEvent || mOnKeyListener.readControlKey();
+ final boolean altDown = leftAltDownFromEvent || mOnKeyListener.readAltKey();
- int resultingKeyCode = -1; // Set if virtual key causes this to be translated to key event.
- if (controlDown) {
+ if (mOnKeyListener.onCodePoint(codePoint, controlDown, mTermSession)) return;
+
+ if (controlDown) {
if (codePoint >= 'a' && codePoint <= 'z') {
codePoint = codePoint - 'a' + 1;
} else if (codePoint >= 'A' && codePoint <= 'Z') {
@@ -635,87 +645,29 @@ public final class TerminalView extends View {
codePoint = 31;
} else if (codePoint == '8') {
codePoint = 127; // DEL
- } else if (codePoint == '9') {
- resultingKeyCode = KeyEvent.KEYCODE_F11;
- } else if (codePoint == '0') {
- resultingKeyCode = KeyEvent.KEYCODE_F12;
}
- } else if (mVirtualFnKeyDown) {
- int lowerCase = Character.toLowerCase(codePoint);
- switch (lowerCase) {
- // Arrow keys.
- case 'w': resultingKeyCode = KeyEvent.KEYCODE_DPAD_UP; break;
- case 'a': resultingKeyCode = KeyEvent.KEYCODE_DPAD_LEFT; break;
- case 's': resultingKeyCode = KeyEvent.KEYCODE_DPAD_DOWN; break;
- case 'd': resultingKeyCode = KeyEvent.KEYCODE_DPAD_RIGHT; break;
+ }
- // Page up and down.
- case 'p': resultingKeyCode = KeyEvent.KEYCODE_PAGE_UP; break;
- case 'n': resultingKeyCode = KeyEvent.KEYCODE_PAGE_DOWN; break;
-
- // Some special keys:
- case 't': resultingKeyCode = KeyEvent.KEYCODE_TAB; break;
- case 'i': resultingKeyCode = KeyEvent.KEYCODE_INSERT; break;
- case 'h': resultingKeyCode = KeyEvent.KEYCODE_MOVE_HOME; break;
-
- // Special characters to input.
- case 'u': codePoint = '_'; break;
- case 'l': codePoint = '|'; break;
-
- // Function keys.
- case '1': case '2': case '3':
- case '4': case '5': case '6':
- case '7': case '8': case '9':
- resultingKeyCode = (codePoint - '1') + KeyEvent.KEYCODE_F1;
+ if (codePoint > -1) {
+ // Work around bluetooth keyboards sending funny unicode characters instead
+ // of the more normal ones from ASCII that terminal programs expect - the
+ // desire to input the original characters should be low.
+ switch (codePoint) {
+ case 0x02DC: // SMALL TILDE.
+ codePoint = 0x007E; // TILDE (~).
break;
- case '0':
- resultingKeyCode = KeyEvent.KEYCODE_F10;
+ case 0x02CB: // MODIFIER LETTER GRAVE ACCENT.
+ codePoint = 0x0060; // GRAVE ACCENT (`).
break;
-
- // Other special keys.
- case 'e': codePoint = /*Escape*/ 27; break;
- case '.': codePoint = /*^.*/ 28; break;
-
- case 'b': // alt+b, jumping backward in readline.
- case 'f': // alf+f, jumping forward in readline.
- case 'x': // alt+x, common in emacs.
- codePoint = lowerCase;
- altDown = true;
- break;
-
- // Volume control.
- case 'v':
- codePoint = -1;
- AudioManager audio = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
- audio.adjustSuggestedStreamVolume(AudioManager.ADJUST_SAME, AudioManager.USE_DEFAULT_STREAM_TYPE, AudioManager.FLAG_SHOW_UI);
+ case 0x02C6: // MODIFIER LETTER CIRCUMFLEX ACCENT.
+ codePoint = 0x005E; // CIRCUMFLEX ACCENT (^).
break;
}
- }
- if (codePoint > -1) {
- if (resultingKeyCode > -1) {
- handleKeyCode(resultingKeyCode, 0);
- } else {
- // Work around bluetooth keyboards sending funny unicode characters instead
- // of the more normal ones from ASCII that terminal programs expect - the
- // desire to input the original characters should be low.
- switch (codePoint) {
- case 0x02DC: // SMALL TILDE.
- codePoint = 0x007E; // TILDE (~).
- break;
- case 0x02CB: // MODIFIER LETTER GRAVE ACCENT.
- codePoint = 0x0060; // GRAVE ACCENT (`).
- break;
- case 0x02C6: // MODIFIER LETTER CIRCUMFLEX ACCENT.
- codePoint = 0x005E; // CIRCUMFLEX ACCENT (^).
- break;
- }
-
- // If left alt, send escape before the code point to make e.g. Alt+B and Alt+F work in readline:
- mTermSession.writeCodePoint(altDown, codePoint);
- }
- }
- }
+ // If left alt, send escape before the code point to make e.g. Alt+B and Alt+F work in readline:
+ mTermSession.writeCodePoint(altDown, codePoint);
+ }
+ }
/** Input the specified keyCode if applicable and return if the input was consumed. */
public boolean handleKeyCode(int keyCode, int keyMod) {
@@ -740,7 +692,7 @@ public final class TerminalView extends View {
if (LOG_KEY_EVENTS) Log.i(EmulatorDebug.LOG_TAG, "onKeyUp(keyCode=" + keyCode + ", event=" + event + ")");
if (mEmulator == null) return true;
- if (handleVirtualKeys(keyCode, event, false)) {
+ if (mOnKeyListener.onKeyUp(keyCode, event)) {
invalidate();
return true;
} else if (event.isSystem()) {
@@ -751,49 +703,6 @@ public final class TerminalView extends View {
return true;
}
- /** Handle dedicated volume buttons as virtual keys if applicable. */
- private boolean handleVirtualKeys(int keyCode, KeyEvent event, boolean down) {
- InputDevice inputDevice = event.getDevice();
- if (inputDevice != null && inputDevice.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) {
- // Do not steal dedicated buttons from a full external keyboard.
- return false;
- } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
- if (LOG_KEY_EVENTS) Log.i(EmulatorDebug.LOG_TAG, "handleVirtualKeys(down=" + down + ") taking ctrl event");
- mVirtualControlKeyDown = down;
- return true;
- } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
- if (LOG_KEY_EVENTS) Log.i(EmulatorDebug.LOG_TAG, "handleVirtualKeys(down=" + down + ") taking Fn event");
- mVirtualFnKeyDown = down;
- return true;
- }
- return false;
- }
-
- public void checkForFontAndColors() {
- try {
- // Hard-coded paths since this file is used also in Termux:Float.
- @SuppressLint("SdCardPath") File fontFile = new File("/data/data/com.termux/files/home/.termux/font.ttf");
- @SuppressLint("SdCardPath") File colorsFile = new File("/data/data/com.termux/files/home/.termux/colors.properties");
-
- final Properties props = new Properties();
- if (colorsFile.isFile()) {
- try (InputStream in = new FileInputStream(colorsFile)) {
- props.load(in);
- }
- }
- TerminalColors.COLOR_SCHEME.updateWith(props);
- if (mEmulator != null) mEmulator.mColors.reset();
-
- final Typeface newTypeface = (fontFile.exists() && fontFile.length() > 0) ? Typeface.createFromFile(fontFile) : Typeface.MONOSPACE;
- mRenderer = new TerminalRenderer(mRenderer.mTextSize, newTypeface);
- updateSize();
-
- invalidate();
- } catch (Exception e) {
- Log.e(EmulatorDebug.LOG_TAG, "Error in checkForFontAndColors()", e);
- }
- }
-
/**
* This is called during layout when the size of this view has changed. If you were just added to the view
* hierarchy, you're called with the old values of 0.
diff --git a/app/src/main/res/layout/extra_keys_right.xml b/app/src/main/res/layout/extra_keys_right.xml
index cd7fc451..7a22b4df 100644
--- a/app/src/main/res/layout/extra_keys_right.xml
+++ b/app/src/main/res/layout/extra_keys_right.xml
@@ -9,7 +9,6 @@
android:inputType="text"
android:singleLine="true"
android:textColor="@android:color/white"
- android:backgroundTint="@android:color/transparent"
android:paddingTop="0dp"
android:textCursorDrawable="@null"
android:paddingBottom="0dp"