termux-app/terminal-view/src/main/java/com/termux/view/TerminalView.java

964 lines
37 KiB
Java
Raw Normal View History

2015-10-25 14:27:32 +00:00
package com.termux.view;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.ClipData;
import android.content.ClipboardManager;
2015-10-25 14:27:32 +00:00
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Typeface;
import android.os.Build;
2016-09-04 16:56:28 +00:00
import android.text.Editable;
2015-10-25 14:27:32 +00:00
import android.text.InputType;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.ActionMode;
2015-10-25 14:27:32 +00:00
import android.view.HapticFeedbackConstants;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
2019-10-05 10:05:42 +00:00
import android.view.ViewConfiguration;
import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityManager;
import android.view.autofill.AutofillValue;
2015-10-25 14:27:32 +00:00
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.widget.Scroller;
import androidx.annotation.RequiresApi;
import com.termux.terminal.KeyHandler;
import com.termux.terminal.TerminalEmulator;
import com.termux.terminal.TerminalSession;
import com.termux.view.textselection.TextSelectionCursorController;
2015-10-25 14:27:32 +00:00
/** View displaying and interacting with a {@link TerminalSession}. */
public final class TerminalView extends View {
Implement GUI based Termux settings manager and a centralized logging framework The settings activity can be accessed by long pressing on terminal view and selecting "Settings" from the popup shown. It uses the Android's Preference framework. Currently only debugging preferences to set log level and enabling terminal view key logging are provided. The Preference framework by default uses the keys set in `app:key` attribute in the respective preferences XML file to store the values in the default `SharedPreferences` file of the app. However, since we rely on `TermuxPreferenceConstants` and `TermuxPropertyConstants` classes to define key names so that they can be easily shared between termux and its plugin apps, we provide our own `PreferenceDataStore` for storing key/value pairs. The key name in the XML file can optionally be the same. Check `DebuggingPreferencesFragment` class for a sample. Each new preference category fragment should be added to `app/settings/` with its data store. This commit may allow support to be added for modifying `termux.properties` file directly from the UI but that requires more work, since writing to property files with comments require in-place modification. The `Logger` class provides various static functions for logging that should be used from now on instead of directly calling android `Log.*` functions. The log level is automatically loaded from shared preferences at application startup via `TermuxApplication` and set in the static `Logger.CURRENT_LOG_LEVEL` variable. Changing the log level through the settings activity also changes the log level immediately. The 4 supported log levels are: - LOG_LEVEL_OFF which will log nothing. - LOG_LEVEL_NORMAL which will start logging error, warn and info messages and stacktraces. - LOG_LEVEL_DEBUG which will start logging debug messages. - LOG_LEVEL_VERBOSE which will start logging verbose messages. The default log level is `LOG_LEVEL_NORMAL` which will not log debug or verbose messages. Contributors can add useful log entries at those levels where ever they feel is appropriate so that it allows users and devs to more easily help solve issues or find bugs, specially without having to recompile termux after having to manually add general log entries to the source. DO NOT log data that may have private info of users like command arguments at log levels below debug, like `BackgroundJob` was doing previously. Logging to file support may be added later, will require log file rotation support and storage permissions.
2021-03-13 11:49:29 +00:00
/** Log terminal view key and IME events. */
private static boolean TERMINAL_VIEW_KEY_LOGGING_ENABLED = false;
/** The currently displayed terminal session, whose emulator is {@link #mEmulator}. */
public TerminalSession mTermSession;
/** Our terminal emulator whose session is {@link #mTermSession}. */
public TerminalEmulator mEmulator;
public TerminalRenderer mRenderer;
public TerminalViewClient mClient;
private TextSelectionCursorController mTextSelectionCursorController;
/** The top row of text to display. Ranges from -activeTranscriptRows to 0. */
int mTopRow;
int[] mDefaultSelectors = new int[]{-1,-1,-1,-1};
float mScaleFactor = 1.f;
final GestureAndScaleRecognizer mGestureRecognizer;
/** Keep track of where mouse touch event started which we report as mouse scroll. */
private int mMouseScrollStartX = -1, mMouseScrollStartY = -1;
/** Keep track of the time when a touch event leading to sending mouse scroll events started. */
private long mMouseStartDownTime = -1;
final Scroller mScroller;
/** What was left in from scrolling movement. */
float mScrollRemainder;
/** If non-zero, this is the last unicode code point received if that was a combining character. */
int mCombiningAccent;
private final boolean mAccessibilityEnabled;
Implement GUI based Termux settings manager and a centralized logging framework The settings activity can be accessed by long pressing on terminal view and selecting "Settings" from the popup shown. It uses the Android's Preference framework. Currently only debugging preferences to set log level and enabling terminal view key logging are provided. The Preference framework by default uses the keys set in `app:key` attribute in the respective preferences XML file to store the values in the default `SharedPreferences` file of the app. However, since we rely on `TermuxPreferenceConstants` and `TermuxPropertyConstants` classes to define key names so that they can be easily shared between termux and its plugin apps, we provide our own `PreferenceDataStore` for storing key/value pairs. The key name in the XML file can optionally be the same. Check `DebuggingPreferencesFragment` class for a sample. Each new preference category fragment should be added to `app/settings/` with its data store. This commit may allow support to be added for modifying `termux.properties` file directly from the UI but that requires more work, since writing to property files with comments require in-place modification. The `Logger` class provides various static functions for logging that should be used from now on instead of directly calling android `Log.*` functions. The log level is automatically loaded from shared preferences at application startup via `TermuxApplication` and set in the static `Logger.CURRENT_LOG_LEVEL` variable. Changing the log level through the settings activity also changes the log level immediately. The 4 supported log levels are: - LOG_LEVEL_OFF which will log nothing. - LOG_LEVEL_NORMAL which will start logging error, warn and info messages and stacktraces. - LOG_LEVEL_DEBUG which will start logging debug messages. - LOG_LEVEL_VERBOSE which will start logging verbose messages. The default log level is `LOG_LEVEL_NORMAL` which will not log debug or verbose messages. Contributors can add useful log entries at those levels where ever they feel is appropriate so that it allows users and devs to more easily help solve issues or find bugs, specially without having to recompile termux after having to manually add general log entries to the source. DO NOT log data that may have private info of users like command arguments at log levels below debug, like `BackgroundJob` was doing previously. Logging to file support may be added later, will require log file rotation support and storage permissions.
2021-03-13 11:49:29 +00:00
private static final String LOG_TAG = "TerminalView";
public TerminalView(Context context, AttributeSet attributes) { // NO_UCD (unused code)
super(context, attributes);
mGestureRecognizer = new GestureAndScaleRecognizer(context, new GestureAndScaleRecognizer.Listener() {
boolean scrolledWithFinger;
@Override
public boolean onUp(MotionEvent event) {
mScrollRemainder = 0.0f;
if (mEmulator != null && mEmulator.isMouseTrackingActive() && !isSelectingText() && !scrolledWithFinger) {
// Quick event processing when mouse tracking is active - do not wait for check of double tapping
// for zooming.
sendMouseEventCode(event, TerminalEmulator.MOUSE_LEFT_BUTTON, true);
sendMouseEventCode(event, TerminalEmulator.MOUSE_LEFT_BUTTON, false);
return true;
}
scrolledWithFinger = false;
return false;
}
@Override
public boolean onSingleTapUp(MotionEvent event) {
if (mEmulator == null) return true;
if (isSelectingText()) {
2019-10-05 10:05:42 +00:00
stopTextSelectionMode();
return true;
}
requestFocus();
if (!mEmulator.isMouseTrackingActive()) {
if (!event.isFromSource(InputDevice.SOURCE_MOUSE)) {
mClient.onSingleTapUp(event);
return true;
}
}
return false;
}
@Override
public boolean onScroll(MotionEvent e, float distanceX, float distanceY) {
2019-10-05 10:05:42 +00:00
if (mEmulator == null) return true;
if (mEmulator.isMouseTrackingActive() && e.isFromSource(InputDevice.SOURCE_MOUSE)) {
// If moving with mouse pointer while pressing button, report that instead of scroll.
// This means that we never report moving with button press-events for touch input,
// since we cannot just start sending these events without a starting press event,
// which we do not do for touch input, only mouse in onTouchEvent().
sendMouseEventCode(e, TerminalEmulator.MOUSE_LEFT_BUTTON_MOVED, true);
} else {
scrolledWithFinger = true;
distanceY += mScrollRemainder;
int deltaRows = (int) (distanceY / mRenderer.mFontLineSpacing);
mScrollRemainder = distanceY - deltaRows * mRenderer.mFontLineSpacing;
doScroll(e, deltaRows);
}
return true;
}
@Override
public boolean onScale(float focusX, float focusY, float scale) {
if (mEmulator == null || isSelectingText()) return true;
mScaleFactor *= scale;
mScaleFactor = mClient.onScale(mScaleFactor);
return true;
}
@Override
public boolean onFling(final MotionEvent e2, float velocityX, float velocityY) {
2019-10-05 10:30:54 +00:00
if (mEmulator == null) return true;
// Do not start scrolling until last fling has been taken care of:
if (!mScroller.isFinished()) return true;
final boolean mouseTrackingAtStartOfFling = mEmulator.isMouseTrackingActive();
float SCALE = 0.25f;
if (mouseTrackingAtStartOfFling) {
mScroller.fling(0, 0, 0, -(int) (velocityY * SCALE), 0, 0, -mEmulator.mRows / 2, mEmulator.mRows / 2);
} else {
mScroller.fling(0, mTopRow, 0, -(int) (velocityY * SCALE), 0, 0, -mEmulator.getScreen().getActiveTranscriptRows(), 0);
}
post(new Runnable() {
private int mLastY = 0;
@Override
public void run() {
if (mouseTrackingAtStartOfFling != mEmulator.isMouseTrackingActive()) {
mScroller.abortAnimation();
return;
}
if (mScroller.isFinished()) return;
boolean more = mScroller.computeScrollOffset();
int newY = mScroller.getCurrY();
int diff = mouseTrackingAtStartOfFling ? (newY - mLastY) : (newY - mTopRow);
doScroll(e2, diff);
mLastY = newY;
if (more) post(this);
}
});
return true;
}
@Override
public boolean onDown(float x, float y) {
// Why is true not returned here?
// https://developer.android.com/training/gestures/detector.html#detect-a-subset-of-supported-gestures
// Although setting this to true still does not solve the following errors when long pressing in terminal view text area
// ViewDragHelper: Ignoring pointerId=0 because ACTION_DOWN was not received for this pointer before ACTION_MOVE
// Commenting out the call to mGestureDetector.onTouchEvent(event) in GestureAndScaleRecognizer#onTouchEvent() removes
// the error logging, so issue is related to GestureDetector
return false;
}
@Override
public boolean onDoubleTap(MotionEvent event) {
// Do not treat is as a single confirmed tap - it may be followed by zoom.
return false;
}
@Override
public void onLongPress(MotionEvent event) {
if (mGestureRecognizer.isInProgress()) return;
if (mClient.onLongPress(event)) return;
if (!isSelectingText()) {
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
startTextSelectionMode(event);
}
}
});
mScroller = new Scroller(context);
2019-01-11 20:19:06 +00:00
AccessibilityManager am = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
mAccessibilityEnabled = am.isEnabled();
}
/**
* @param client The {@link TerminalViewClient} interface implementation to allow
* for communication between {@link TerminalView} and its client.
Implement GUI based Termux settings manager and a centralized logging framework The settings activity can be accessed by long pressing on terminal view and selecting "Settings" from the popup shown. It uses the Android's Preference framework. Currently only debugging preferences to set log level and enabling terminal view key logging are provided. The Preference framework by default uses the keys set in `app:key` attribute in the respective preferences XML file to store the values in the default `SharedPreferences` file of the app. However, since we rely on `TermuxPreferenceConstants` and `TermuxPropertyConstants` classes to define key names so that they can be easily shared between termux and its plugin apps, we provide our own `PreferenceDataStore` for storing key/value pairs. The key name in the XML file can optionally be the same. Check `DebuggingPreferencesFragment` class for a sample. Each new preference category fragment should be added to `app/settings/` with its data store. This commit may allow support to be added for modifying `termux.properties` file directly from the UI but that requires more work, since writing to property files with comments require in-place modification. The `Logger` class provides various static functions for logging that should be used from now on instead of directly calling android `Log.*` functions. The log level is automatically loaded from shared preferences at application startup via `TermuxApplication` and set in the static `Logger.CURRENT_LOG_LEVEL` variable. Changing the log level through the settings activity also changes the log level immediately. The 4 supported log levels are: - LOG_LEVEL_OFF which will log nothing. - LOG_LEVEL_NORMAL which will start logging error, warn and info messages and stacktraces. - LOG_LEVEL_DEBUG which will start logging debug messages. - LOG_LEVEL_VERBOSE which will start logging verbose messages. The default log level is `LOG_LEVEL_NORMAL` which will not log debug or verbose messages. Contributors can add useful log entries at those levels where ever they feel is appropriate so that it allows users and devs to more easily help solve issues or find bugs, specially without having to recompile termux after having to manually add general log entries to the source. DO NOT log data that may have private info of users like command arguments at log levels below debug, like `BackgroundJob` was doing previously. Logging to file support may be added later, will require log file rotation support and storage permissions.
2021-03-13 11:49:29 +00:00
*/
public void setTerminalViewClient(TerminalViewClient client) {
this.mClient = client;
Implement GUI based Termux settings manager and a centralized logging framework The settings activity can be accessed by long pressing on terminal view and selecting "Settings" from the popup shown. It uses the Android's Preference framework. Currently only debugging preferences to set log level and enabling terminal view key logging are provided. The Preference framework by default uses the keys set in `app:key` attribute in the respective preferences XML file to store the values in the default `SharedPreferences` file of the app. However, since we rely on `TermuxPreferenceConstants` and `TermuxPropertyConstants` classes to define key names so that they can be easily shared between termux and its plugin apps, we provide our own `PreferenceDataStore` for storing key/value pairs. The key name in the XML file can optionally be the same. Check `DebuggingPreferencesFragment` class for a sample. Each new preference category fragment should be added to `app/settings/` with its data store. This commit may allow support to be added for modifying `termux.properties` file directly from the UI but that requires more work, since writing to property files with comments require in-place modification. The `Logger` class provides various static functions for logging that should be used from now on instead of directly calling android `Log.*` functions. The log level is automatically loaded from shared preferences at application startup via `TermuxApplication` and set in the static `Logger.CURRENT_LOG_LEVEL` variable. Changing the log level through the settings activity also changes the log level immediately. The 4 supported log levels are: - LOG_LEVEL_OFF which will log nothing. - LOG_LEVEL_NORMAL which will start logging error, warn and info messages and stacktraces. - LOG_LEVEL_DEBUG which will start logging debug messages. - LOG_LEVEL_VERBOSE which will start logging verbose messages. The default log level is `LOG_LEVEL_NORMAL` which will not log debug or verbose messages. Contributors can add useful log entries at those levels where ever they feel is appropriate so that it allows users and devs to more easily help solve issues or find bugs, specially without having to recompile termux after having to manually add general log entries to the source. DO NOT log data that may have private info of users like command arguments at log levels below debug, like `BackgroundJob` was doing previously. Logging to file support may be added later, will require log file rotation support and storage permissions.
2021-03-13 11:49:29 +00:00
}
/**
* Sets terminal view key logging is enabled or not.
*
* @param value The boolean value that defines the state.
*/
Implement GUI based Termux settings manager and a centralized logging framework The settings activity can be accessed by long pressing on terminal view and selecting "Settings" from the popup shown. It uses the Android's Preference framework. Currently only debugging preferences to set log level and enabling terminal view key logging are provided. The Preference framework by default uses the keys set in `app:key` attribute in the respective preferences XML file to store the values in the default `SharedPreferences` file of the app. However, since we rely on `TermuxPreferenceConstants` and `TermuxPropertyConstants` classes to define key names so that they can be easily shared between termux and its plugin apps, we provide our own `PreferenceDataStore` for storing key/value pairs. The key name in the XML file can optionally be the same. Check `DebuggingPreferencesFragment` class for a sample. Each new preference category fragment should be added to `app/settings/` with its data store. This commit may allow support to be added for modifying `termux.properties` file directly from the UI but that requires more work, since writing to property files with comments require in-place modification. The `Logger` class provides various static functions for logging that should be used from now on instead of directly calling android `Log.*` functions. The log level is automatically loaded from shared preferences at application startup via `TermuxApplication` and set in the static `Logger.CURRENT_LOG_LEVEL` variable. Changing the log level through the settings activity also changes the log level immediately. The 4 supported log levels are: - LOG_LEVEL_OFF which will log nothing. - LOG_LEVEL_NORMAL which will start logging error, warn and info messages and stacktraces. - LOG_LEVEL_DEBUG which will start logging debug messages. - LOG_LEVEL_VERBOSE which will start logging verbose messages. The default log level is `LOG_LEVEL_NORMAL` which will not log debug or verbose messages. Contributors can add useful log entries at those levels where ever they feel is appropriate so that it allows users and devs to more easily help solve issues or find bugs, specially without having to recompile termux after having to manually add general log entries to the source. DO NOT log data that may have private info of users like command arguments at log levels below debug, like `BackgroundJob` was doing previously. Logging to file support may be added later, will require log file rotation support and storage permissions.
2021-03-13 11:49:29 +00:00
public void setIsTerminalViewKeyLoggingEnabled(boolean value) {
TERMINAL_VIEW_KEY_LOGGING_ENABLED = value;
}
/**
* Attach a {@link TerminalSession} to this view.
*
* @param session The {@link TerminalSession} this view will be displaying.
*/
public boolean attachSession(TerminalSession session) {
if (session == mTermSession) return false;
mTopRow = 0;
mTermSession = session;
mEmulator = null;
mCombiningAccent = 0;
updateSize();
// Wait with enabling the scrollbar until we have a terminal to get scroll position from.
setVerticalScrollBarEnabled(true);
return true;
}
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
if (mClient.shouldEnforceCharBasedInput()) {
// Some keyboards seems do not reset the internal state on TYPE_NULL.
// Affects mostly Samsung stock keyboards.
// https://github.com/termux/termux-app/issues/686
outAttrs.inputType = InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
} else {
// Using InputType.NULL is the most correct input type and avoids issues with other hacks.
//
// Previous keyboard issues:
// https://github.com/termux/termux-packages/issues/25
// https://github.com/termux/termux-app/issues/87.
// https://github.com/termux/termux-app/issues/126.
// https://github.com/termux/termux-app/issues/137 (japanese chars and TYPE_NULL).
outAttrs.inputType = InputType.TYPE_NULL;
}
2015-10-25 14:27:32 +00:00
// Note that IME_ACTION_NONE cannot be used as that makes it impossible to input newlines using the on-screen
// keyboard on Android TV (see https://github.com/termux/termux-app/issues/221).
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
2015-10-25 14:27:32 +00:00
return new BaseInputConnection(this, true) {
2015-10-25 14:27:32 +00:00
@Override
public boolean finishComposingText() {
Implement GUI based Termux settings manager and a centralized logging framework The settings activity can be accessed by long pressing on terminal view and selecting "Settings" from the popup shown. It uses the Android's Preference framework. Currently only debugging preferences to set log level and enabling terminal view key logging are provided. The Preference framework by default uses the keys set in `app:key` attribute in the respective preferences XML file to store the values in the default `SharedPreferences` file of the app. However, since we rely on `TermuxPreferenceConstants` and `TermuxPropertyConstants` classes to define key names so that they can be easily shared between termux and its plugin apps, we provide our own `PreferenceDataStore` for storing key/value pairs. The key name in the XML file can optionally be the same. Check `DebuggingPreferencesFragment` class for a sample. Each new preference category fragment should be added to `app/settings/` with its data store. This commit may allow support to be added for modifying `termux.properties` file directly from the UI but that requires more work, since writing to property files with comments require in-place modification. The `Logger` class provides various static functions for logging that should be used from now on instead of directly calling android `Log.*` functions. The log level is automatically loaded from shared preferences at application startup via `TermuxApplication` and set in the static `Logger.CURRENT_LOG_LEVEL` variable. Changing the log level through the settings activity also changes the log level immediately. The 4 supported log levels are: - LOG_LEVEL_OFF which will log nothing. - LOG_LEVEL_NORMAL which will start logging error, warn and info messages and stacktraces. - LOG_LEVEL_DEBUG which will start logging debug messages. - LOG_LEVEL_VERBOSE which will start logging verbose messages. The default log level is `LOG_LEVEL_NORMAL` which will not log debug or verbose messages. Contributors can add useful log entries at those levels where ever they feel is appropriate so that it allows users and devs to more easily help solve issues or find bugs, specially without having to recompile termux after having to manually add general log entries to the source. DO NOT log data that may have private info of users like command arguments at log levels below debug, like `BackgroundJob` was doing previously. Logging to file support may be added later, will require log file rotation support and storage permissions.
2021-03-13 11:49:29 +00:00
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED) mClient.logInfo(LOG_TAG, "IME: finishComposingText()");
2016-09-04 16:56:28 +00:00
super.finishComposingText();
2016-06-27 22:56:30 +00:00
2016-09-04 16:56:28 +00:00
sendTextToTerminal(getEditable());
2016-06-27 22:56:30 +00:00
getEditable().clear();
return true;
}
2015-10-25 14:27:32 +00:00
2016-06-27 22:56:30 +00:00
@Override
public boolean commitText(CharSequence text, int newCursorPosition) {
Implement GUI based Termux settings manager and a centralized logging framework The settings activity can be accessed by long pressing on terminal view and selecting "Settings" from the popup shown. It uses the Android's Preference framework. Currently only debugging preferences to set log level and enabling terminal view key logging are provided. The Preference framework by default uses the keys set in `app:key` attribute in the respective preferences XML file to store the values in the default `SharedPreferences` file of the app. However, since we rely on `TermuxPreferenceConstants` and `TermuxPropertyConstants` classes to define key names so that they can be easily shared between termux and its plugin apps, we provide our own `PreferenceDataStore` for storing key/value pairs. The key name in the XML file can optionally be the same. Check `DebuggingPreferencesFragment` class for a sample. Each new preference category fragment should be added to `app/settings/` with its data store. This commit may allow support to be added for modifying `termux.properties` file directly from the UI but that requires more work, since writing to property files with comments require in-place modification. The `Logger` class provides various static functions for logging that should be used from now on instead of directly calling android `Log.*` functions. The log level is automatically loaded from shared preferences at application startup via `TermuxApplication` and set in the static `Logger.CURRENT_LOG_LEVEL` variable. Changing the log level through the settings activity also changes the log level immediately. The 4 supported log levels are: - LOG_LEVEL_OFF which will log nothing. - LOG_LEVEL_NORMAL which will start logging error, warn and info messages and stacktraces. - LOG_LEVEL_DEBUG which will start logging debug messages. - LOG_LEVEL_VERBOSE which will start logging verbose messages. The default log level is `LOG_LEVEL_NORMAL` which will not log debug or verbose messages. Contributors can add useful log entries at those levels where ever they feel is appropriate so that it allows users and devs to more easily help solve issues or find bugs, specially without having to recompile termux after having to manually add general log entries to the source. DO NOT log data that may have private info of users like command arguments at log levels below debug, like `BackgroundJob` was doing previously. Logging to file support may be added later, will require log file rotation support and storage permissions.
2021-03-13 11:49:29 +00:00
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED) {
mClient.logInfo(LOG_TAG, "IME: commitText(\"" + text + "\", " + newCursorPosition + ")");
2016-09-04 16:56:28 +00:00
}
super.commitText(text, newCursorPosition);
if (mEmulator == null) return true;
2016-09-04 16:56:28 +00:00
Editable content = getEditable();
sendTextToTerminal(content);
content.clear();
return true;
}
@Override
public boolean deleteSurroundingText(int leftLength, int rightLength) {
Implement GUI based Termux settings manager and a centralized logging framework The settings activity can be accessed by long pressing on terminal view and selecting "Settings" from the popup shown. It uses the Android's Preference framework. Currently only debugging preferences to set log level and enabling terminal view key logging are provided. The Preference framework by default uses the keys set in `app:key` attribute in the respective preferences XML file to store the values in the default `SharedPreferences` file of the app. However, since we rely on `TermuxPreferenceConstants` and `TermuxPropertyConstants` classes to define key names so that they can be easily shared between termux and its plugin apps, we provide our own `PreferenceDataStore` for storing key/value pairs. The key name in the XML file can optionally be the same. Check `DebuggingPreferencesFragment` class for a sample. Each new preference category fragment should be added to `app/settings/` with its data store. This commit may allow support to be added for modifying `termux.properties` file directly from the UI but that requires more work, since writing to property files with comments require in-place modification. The `Logger` class provides various static functions for logging that should be used from now on instead of directly calling android `Log.*` functions. The log level is automatically loaded from shared preferences at application startup via `TermuxApplication` and set in the static `Logger.CURRENT_LOG_LEVEL` variable. Changing the log level through the settings activity also changes the log level immediately. The 4 supported log levels are: - LOG_LEVEL_OFF which will log nothing. - LOG_LEVEL_NORMAL which will start logging error, warn and info messages and stacktraces. - LOG_LEVEL_DEBUG which will start logging debug messages. - LOG_LEVEL_VERBOSE which will start logging verbose messages. The default log level is `LOG_LEVEL_NORMAL` which will not log debug or verbose messages. Contributors can add useful log entries at those levels where ever they feel is appropriate so that it allows users and devs to more easily help solve issues or find bugs, specially without having to recompile termux after having to manually add general log entries to the source. DO NOT log data that may have private info of users like command arguments at log levels below debug, like `BackgroundJob` was doing previously. Logging to file support may be added later, will require log file rotation support and storage permissions.
2021-03-13 11:49:29 +00:00
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED) {
mClient.logInfo(LOG_TAG, "IME: deleteSurroundingText(" + leftLength + ", " + rightLength + ")");
}
// The stock Samsung keyboard with 'Auto check spelling' enabled sends leftLength > 1.
KeyEvent deleteKey = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL);
for (int i = 0; i < leftLength; i++) sendKeyEvent(deleteKey);
2016-09-04 16:56:28 +00:00
return super.deleteSurroundingText(leftLength, rightLength);
}
void sendTextToTerminal(CharSequence text) {
2019-10-05 10:54:15 +00:00
stopTextSelectionMode();
final int textLengthInChars = text.length();
for (int i = 0; i < textLengthInChars; i++) {
char firstChar = text.charAt(i);
int codePoint;
if (Character.isHighSurrogate(firstChar)) {
if (++i < textLengthInChars) {
codePoint = Character.toCodePoint(firstChar, text.charAt(i));
} else {
// At end of string, with no low surrogate following the high:
codePoint = TerminalEmulator.UNICODE_REPLACEMENT_CHAR;
}
} else {
codePoint = firstChar;
}
2016-06-27 22:56:30 +00:00
boolean ctrlHeld = false;
if (codePoint <= 31 && codePoint != 27) {
if (codePoint == '\n') {
// The AOSP keyboard and descendants seems to send \n as text when the enter key is pressed,
// instead of a key event like most other keyboard apps. A terminal expects \r for the enter
// key (although when icrnl is enabled this doesn't make a difference - run 'stty -icrnl' to
// check the behaviour).
codePoint = '\r';
}
2016-06-27 22:56:30 +00:00
// 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;
2016-06-27 22:56:30 +00:00
}
}
inputCodePoint(codePoint, ctrlHeld, false);
}
}
2015-10-25 14:27:32 +00:00
2016-06-27 22:56:30 +00:00
};
}
@Override
protected int computeVerticalScrollRange() {
return mEmulator == null ? 1 : mEmulator.getScreen().getActiveRows();
}
@Override
protected int computeVerticalScrollExtent() {
return mEmulator == null ? 1 : mEmulator.mRows;
}
@Override
protected int computeVerticalScrollOffset() {
return mEmulator == null ? 1 : mEmulator.getScreen().getActiveRows() + mTopRow - mEmulator.mRows;
}
public void onScreenUpdated() {
if (mEmulator == null) return;
2019-01-25 03:31:35 +00:00
int rowsInHistory = mEmulator.getScreen().getActiveTranscriptRows();
if (mTopRow < -rowsInHistory) mTopRow = -rowsInHistory;
boolean skipScrolling = false;
if (isSelectingText()) {
// Do not scroll when selecting text.
int rowShift = mEmulator.getScrollCounter();
if (-mTopRow + rowShift > rowsInHistory) {
// .. unless we're hitting the end of history transcript, in which
// case we abort text selection and scroll to end.
2019-10-05 10:05:42 +00:00
stopTextSelectionMode();
} else {
skipScrolling = true;
mTopRow -= rowShift;
decrementYTextSelectionCursors(rowShift);
}
}
if (!skipScrolling && mTopRow != 0) {
// Scroll down if not already there.
if (mTopRow < -3) {
// Awaken scroll bars only if scrolling a noticeable amount
// - we do not want visible scroll bars during normal typing
// of one row at a time.
awakenScrollBars();
}
mTopRow = 0;
}
mEmulator.clearScrollCounter();
invalidate();
if (mAccessibilityEnabled) setContentDescription(getText());
}
/**
* Sets the text size, which in turn sets the number of rows and columns.
*
* @param textSize the new font size, in density-independent pixels.
*/
public void setTextSize(int textSize) {
mRenderer = new TerminalRenderer(textSize, mRenderer == null ? Typeface.MONOSPACE : mRenderer.mTypeface);
updateSize();
}
2015-10-25 14:27:32 +00:00
2016-06-27 22:56:30 +00:00
public void setTypeface(Typeface newTypeface) {
mRenderer = new TerminalRenderer(mRenderer.mTextSize, newTypeface);
updateSize();
invalidate();
}
@Override
public boolean onCheckIsTextEditor() {
return true;
}
@Override
public boolean isOpaque() {
return true;
}
/** Send a single mouse event code to the terminal. */
void sendMouseEventCode(MotionEvent e, int button, boolean pressed) {
int x = (int) (e.getX() / mRenderer.mFontWidth) + 1;
int y = (int) ((e.getY() - mRenderer.mFontLineSpacingAndAscent) / mRenderer.mFontLineSpacing) + 1;
if (pressed && (button == TerminalEmulator.MOUSE_WHEELDOWN_BUTTON || button == TerminalEmulator.MOUSE_WHEELUP_BUTTON)) {
if (mMouseStartDownTime == e.getDownTime()) {
x = mMouseScrollStartX;
y = mMouseScrollStartY;
} else {
mMouseStartDownTime = e.getDownTime();
mMouseScrollStartX = x;
mMouseScrollStartY = y;
}
}
mEmulator.sendMouseEvent(button, x, y, pressed);
}
/** Perform a scroll, either from dragging the screen or by scrolling a mouse wheel. */
void doScroll(MotionEvent event, int rowsDown) {
boolean up = rowsDown < 0;
int amount = Math.abs(rowsDown);
for (int i = 0; i < amount; i++) {
if (mEmulator.isMouseTrackingActive()) {
sendMouseEventCode(event, up ? TerminalEmulator.MOUSE_WHEELUP_BUTTON : TerminalEmulator.MOUSE_WHEELDOWN_BUTTON, true);
} else if (mEmulator.isAlternateBufferActive()) {
// Send up and down key events for scrolling, which is what some terminals do to make scroll work in
// e.g. less, which shifts to the alt screen without mouse handling.
handleKeyCode(up ? KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN, 0);
} else {
mTopRow = Math.min(0, Math.max(-(mEmulator.getScreen().getActiveTranscriptRows()), mTopRow + (up ? -1 : 1)));
if (!awakenScrollBars()) invalidate();
}
}
}
/** Overriding {@link View#onGenericMotionEvent(MotionEvent)}. */
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
if (mEmulator != null && event.isFromSource(InputDevice.SOURCE_MOUSE) && event.getAction() == MotionEvent.ACTION_SCROLL) {
// Handle mouse wheel scrolling.
boolean up = event.getAxisValue(MotionEvent.AXIS_VSCROLL) > 0.0f;
doScroll(event, up ? -3 : 3);
return true;
}
return false;
}
@SuppressLint("ClickableViewAccessibility")
@Override
@TargetApi(23)
public boolean onTouchEvent(MotionEvent event) {
if (mEmulator == null) return true;
final int action = event.getAction();
if (isSelectingText()) {
updateFloatingToolbarVisibility(event);
mGestureRecognizer.onTouchEvent(event);
return true;
} else if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
if (event.isButtonPressed(MotionEvent.BUTTON_SECONDARY)) {
if (action == MotionEvent.ACTION_DOWN) showContextMenu();
return true;
} else if (event.isButtonPressed(MotionEvent.BUTTON_TERTIARY)) {
ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clipData = clipboard.getPrimaryClip();
if (clipData != null) {
CharSequence paste = clipData.getItemAt(0).coerceToText(getContext());
if (!TextUtils.isEmpty(paste)) mEmulator.paste(paste.toString());
}
} else if (mEmulator.isMouseTrackingActive()) { // BUTTON_PRIMARY.
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_UP:
sendMouseEventCode(event, TerminalEmulator.MOUSE_LEFT_BUTTON, event.getAction() == MotionEvent.ACTION_DOWN);
break;
case MotionEvent.ACTION_MOVE:
sendMouseEventCode(event, TerminalEmulator.MOUSE_LEFT_BUTTON_MOVED, true);
break;
}
return true;
}
}
mGestureRecognizer.onTouchEvent(event);
return true;
}
@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
Implement GUI based Termux settings manager and a centralized logging framework The settings activity can be accessed by long pressing on terminal view and selecting "Settings" from the popup shown. It uses the Android's Preference framework. Currently only debugging preferences to set log level and enabling terminal view key logging are provided. The Preference framework by default uses the keys set in `app:key` attribute in the respective preferences XML file to store the values in the default `SharedPreferences` file of the app. However, since we rely on `TermuxPreferenceConstants` and `TermuxPropertyConstants` classes to define key names so that they can be easily shared between termux and its plugin apps, we provide our own `PreferenceDataStore` for storing key/value pairs. The key name in the XML file can optionally be the same. Check `DebuggingPreferencesFragment` class for a sample. Each new preference category fragment should be added to `app/settings/` with its data store. This commit may allow support to be added for modifying `termux.properties` file directly from the UI but that requires more work, since writing to property files with comments require in-place modification. The `Logger` class provides various static functions for logging that should be used from now on instead of directly calling android `Log.*` functions. The log level is automatically loaded from shared preferences at application startup via `TermuxApplication` and set in the static `Logger.CURRENT_LOG_LEVEL` variable. Changing the log level through the settings activity also changes the log level immediately. The 4 supported log levels are: - LOG_LEVEL_OFF which will log nothing. - LOG_LEVEL_NORMAL which will start logging error, warn and info messages and stacktraces. - LOG_LEVEL_DEBUG which will start logging debug messages. - LOG_LEVEL_VERBOSE which will start logging verbose messages. The default log level is `LOG_LEVEL_NORMAL` which will not log debug or verbose messages. Contributors can add useful log entries at those levels where ever they feel is appropriate so that it allows users and devs to more easily help solve issues or find bugs, specially without having to recompile termux after having to manually add general log entries to the source. DO NOT log data that may have private info of users like command arguments at log levels below debug, like `BackgroundJob` was doing previously. Logging to file support may be added later, will require log file rotation support and storage permissions.
2021-03-13 11:49:29 +00:00
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED)
mClient.logInfo(LOG_TAG, "onKeyPreIme(keyCode=" + keyCode + ", event=" + event + ")");
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (isSelectingText()) {
2019-10-05 10:05:42 +00:00
stopTextSelectionMode();
return true;
} else if (mClient.shouldBackButtonBeMappedToEscape()) {
// Intercept back button to treat it as escape:
switch (event.getAction()) {
case KeyEvent.ACTION_DOWN:
return onKeyDown(keyCode, event);
case KeyEvent.ACTION_UP:
return onKeyUp(keyCode, event);
}
}
} else if (mClient.shouldUseCtrlSpaceWorkaround() &&
keyCode == KeyEvent.KEYCODE_SPACE && event.isCtrlPressed()) {
/* ctrl+space does not work on some ROMs without this workaround.
However, this breaks it on devices where it works out of the box. */
return onKeyDown(keyCode, event);
}
return super.onKeyPreIme(keyCode, event);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
Implement GUI based Termux settings manager and a centralized logging framework The settings activity can be accessed by long pressing on terminal view and selecting "Settings" from the popup shown. It uses the Android's Preference framework. Currently only debugging preferences to set log level and enabling terminal view key logging are provided. The Preference framework by default uses the keys set in `app:key` attribute in the respective preferences XML file to store the values in the default `SharedPreferences` file of the app. However, since we rely on `TermuxPreferenceConstants` and `TermuxPropertyConstants` classes to define key names so that they can be easily shared between termux and its plugin apps, we provide our own `PreferenceDataStore` for storing key/value pairs. The key name in the XML file can optionally be the same. Check `DebuggingPreferencesFragment` class for a sample. Each new preference category fragment should be added to `app/settings/` with its data store. This commit may allow support to be added for modifying `termux.properties` file directly from the UI but that requires more work, since writing to property files with comments require in-place modification. The `Logger` class provides various static functions for logging that should be used from now on instead of directly calling android `Log.*` functions. The log level is automatically loaded from shared preferences at application startup via `TermuxApplication` and set in the static `Logger.CURRENT_LOG_LEVEL` variable. Changing the log level through the settings activity also changes the log level immediately. The 4 supported log levels are: - LOG_LEVEL_OFF which will log nothing. - LOG_LEVEL_NORMAL which will start logging error, warn and info messages and stacktraces. - LOG_LEVEL_DEBUG which will start logging debug messages. - LOG_LEVEL_VERBOSE which will start logging verbose messages. The default log level is `LOG_LEVEL_NORMAL` which will not log debug or verbose messages. Contributors can add useful log entries at those levels where ever they feel is appropriate so that it allows users and devs to more easily help solve issues or find bugs, specially without having to recompile termux after having to manually add general log entries to the source. DO NOT log data that may have private info of users like command arguments at log levels below debug, like `BackgroundJob` was doing previously. Logging to file support may be added later, will require log file rotation support and storage permissions.
2021-03-13 11:49:29 +00:00
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED)
mClient.logInfo(LOG_TAG, "onKeyDown(keyCode=" + keyCode + ", isSystem()=" + event.isSystem() + ", event=" + event + ")");
if (mEmulator == null) return true;
if (isSelectingText()) {
stopTextSelectionMode();
}
if (mClient.onKeyDown(keyCode, event, mTermSession)) {
invalidate();
return true;
} else if (event.isSystem() && (!mClient.shouldBackButtonBeMappedToEscape() || keyCode != KeyEvent.KEYCODE_BACK)) {
return super.onKeyDown(keyCode, event);
} else if (event.getAction() == KeyEvent.ACTION_MULTIPLE && keyCode == KeyEvent.KEYCODE_UNKNOWN) {
2016-06-27 22:56:30 +00:00
mTermSession.write(event.getCharacters());
return true;
}
2015-10-25 14:27:32 +00:00
2016-06-27 22:56:30 +00:00
final int metaState = event.getMetaState();
Improvements to extra keys (#1479) * Make popup keys for extra keys row configurable This makes the keys you get when swiping up on a key configurable. You can configure such a key by using an array of strings instead of a single string in the row. The first entry will be the normal key and the second will be the extra key. This is a slightly breaking change, as people that have configured custom extra keys with "-" or "/" will have to change the config to keep the popup keys. The default config will remain the same in terms of functionality, i.e. it includes the same popup key for "-". * Make popup keys interact well with long press keys This stops the repeat action when the popup is shown, and makes sure the popup is closed when you release even if there has been some repeat actions. * Support configuring the style of the extra keys This adds a setting for choosing between the different ways to render key names that were already present in ExtraKeysView. The available setting values are "arrows-only", "arrows-all", "all", "none" and "default". Other values will fallback to "default". Can be used as a workaround for #1410 * Support using modifier keys with letter keys in extra keys This allows you to use the modifier keys on the extra keys rows, e.g. ctrl, together with another button on the extra keys rows, as long as that button is a normal letter and not a special key. Support for special keys will come in the next commit. * Support using modifier keys with special keys in extra keys This allows you to use the modifier keys on the extra keys rows together with a special key on the extra keys rows, e.g. CTRL+LEFT. Fixes #745, fixes most of #895 and possibly #154 * Support mapping extra keys to other actions This adds a setting called extra-keys-map which allows you to map a key on the extra keys rows to another action. The value is a json object where the key is the button text as configured in extra-keys and the value is the action. Multiple actions can be used, but if they are special characters (like ESC or LEFT) they have to be separated from the other characters with a space on each side. If you want an actual space character, use SPACE. For example if you want to add a key to go to the next active channel in weechat, you can use this: extra-keys-map = {"weechat next": "ESC a"} And then add "weechat next" to extra-keys. The name can of course be whatever you want. Or if you want the button for the UP arrow to show ⇧ instead of ↑, you can use this: extra-keys-map = {"⇧": "UP"} And put "⇧" in extra-keys instead of "UP". Modifier keys (ctrl, alt and shift) can't be used in this map yet. Support for ctrl and alt will come in the next commit. I think this fixes #1186 * Support CTRL and ALT in extra keys map This allows you to use CTRL and ALT in extra-keys-map. For example if you want a button to exit the terminal, you can use this: extra-keys-map = {"exit": "CTRL d"} And add "exit" to extra-keys. * Support a KEYBOARD button in extra keys This toggles showing the keyboard input method. * Support specifying macro keys in the extra-keys option Instead of specifying macros in the separate extra-keys-map option by matching the key name in the two options, you can now use "macro" instead of "key" in extra-keys, and it will be a macro, i.e. a sequence of multiple keys separated by space. * Remove option extra-keys-map Now that you can specify macro in extra-keys, there is no point in having this separate option. Instead of specifying the value to display as key, and the macro to perform in extra-keys-map, you would now specify the value to display in the display property and the macro to perform in the macro property. * Lookup display text when creating ExtraKeyButton This will make it easier to support key aliases for macros in the next commit. * Add support for a key to open the drawer Fixes (I think) #1325
2020-06-09 09:17:07 +00:00
final boolean controlDown = event.isCtrlPressed() || mClient.readControlKey();
final boolean leftAltDown = (metaState & KeyEvent.META_ALT_LEFT_ON) != 0 || mClient.readAltKey();
2016-06-27 22:56:30 +00:00
final boolean rightAltDownFromEvent = (metaState & KeyEvent.META_ALT_RIGHT_ON) != 0;
int keyMod = 0;
Improvements to extra keys (#1479) * Make popup keys for extra keys row configurable This makes the keys you get when swiping up on a key configurable. You can configure such a key by using an array of strings instead of a single string in the row. The first entry will be the normal key and the second will be the extra key. This is a slightly breaking change, as people that have configured custom extra keys with "-" or "/" will have to change the config to keep the popup keys. The default config will remain the same in terms of functionality, i.e. it includes the same popup key for "-". * Make popup keys interact well with long press keys This stops the repeat action when the popup is shown, and makes sure the popup is closed when you release even if there has been some repeat actions. * Support configuring the style of the extra keys This adds a setting for choosing between the different ways to render key names that were already present in ExtraKeysView. The available setting values are "arrows-only", "arrows-all", "all", "none" and "default". Other values will fallback to "default". Can be used as a workaround for #1410 * Support using modifier keys with letter keys in extra keys This allows you to use the modifier keys on the extra keys rows, e.g. ctrl, together with another button on the extra keys rows, as long as that button is a normal letter and not a special key. Support for special keys will come in the next commit. * Support using modifier keys with special keys in extra keys This allows you to use the modifier keys on the extra keys rows together with a special key on the extra keys rows, e.g. CTRL+LEFT. Fixes #745, fixes most of #895 and possibly #154 * Support mapping extra keys to other actions This adds a setting called extra-keys-map which allows you to map a key on the extra keys rows to another action. The value is a json object where the key is the button text as configured in extra-keys and the value is the action. Multiple actions can be used, but if they are special characters (like ESC or LEFT) they have to be separated from the other characters with a space on each side. If you want an actual space character, use SPACE. For example if you want to add a key to go to the next active channel in weechat, you can use this: extra-keys-map = {"weechat next": "ESC a"} And then add "weechat next" to extra-keys. The name can of course be whatever you want. Or if you want the button for the UP arrow to show ⇧ instead of ↑, you can use this: extra-keys-map = {"⇧": "UP"} And put "⇧" in extra-keys instead of "UP". Modifier keys (ctrl, alt and shift) can't be used in this map yet. Support for ctrl and alt will come in the next commit. I think this fixes #1186 * Support CTRL and ALT in extra keys map This allows you to use CTRL and ALT in extra-keys-map. For example if you want a button to exit the terminal, you can use this: extra-keys-map = {"exit": "CTRL d"} And add "exit" to extra-keys. * Support a KEYBOARD button in extra keys This toggles showing the keyboard input method. * Support specifying macro keys in the extra-keys option Instead of specifying macros in the separate extra-keys-map option by matching the key name in the two options, you can now use "macro" instead of "key" in extra-keys, and it will be a macro, i.e. a sequence of multiple keys separated by space. * Remove option extra-keys-map Now that you can specify macro in extra-keys, there is no point in having this separate option. Instead of specifying the value to display as key, and the macro to perform in extra-keys-map, you would now specify the value to display in the display property and the macro to perform in the macro property. * Lookup display text when creating ExtraKeyButton This will make it easier to support key aliases for macros in the next commit. * Add support for a key to open the drawer Fixes (I think) #1325
2020-06-09 09:17:07 +00:00
if (controlDown) keyMod |= KeyHandler.KEYMOD_CTRL;
if (event.isAltPressed() || leftAltDown) keyMod |= KeyHandler.KEYMOD_ALT;
if (event.isShiftPressed()) keyMod |= KeyHandler.KEYMOD_SHIFT;
if (event.isNumLockOn()) keyMod |= KeyHandler.KEYMOD_NUM_LOCK;
if (!event.isFunctionPressed() && handleKeyCode(keyCode, keyMod)) {
Implement GUI based Termux settings manager and a centralized logging framework The settings activity can be accessed by long pressing on terminal view and selecting "Settings" from the popup shown. It uses the Android's Preference framework. Currently only debugging preferences to set log level and enabling terminal view key logging are provided. The Preference framework by default uses the keys set in `app:key` attribute in the respective preferences XML file to store the values in the default `SharedPreferences` file of the app. However, since we rely on `TermuxPreferenceConstants` and `TermuxPropertyConstants` classes to define key names so that they can be easily shared between termux and its plugin apps, we provide our own `PreferenceDataStore` for storing key/value pairs. The key name in the XML file can optionally be the same. Check `DebuggingPreferencesFragment` class for a sample. Each new preference category fragment should be added to `app/settings/` with its data store. This commit may allow support to be added for modifying `termux.properties` file directly from the UI but that requires more work, since writing to property files with comments require in-place modification. The `Logger` class provides various static functions for logging that should be used from now on instead of directly calling android `Log.*` functions. The log level is automatically loaded from shared preferences at application startup via `TermuxApplication` and set in the static `Logger.CURRENT_LOG_LEVEL` variable. Changing the log level through the settings activity also changes the log level immediately. The 4 supported log levels are: - LOG_LEVEL_OFF which will log nothing. - LOG_LEVEL_NORMAL which will start logging error, warn and info messages and stacktraces. - LOG_LEVEL_DEBUG which will start logging debug messages. - LOG_LEVEL_VERBOSE which will start logging verbose messages. The default log level is `LOG_LEVEL_NORMAL` which will not log debug or verbose messages. Contributors can add useful log entries at those levels where ever they feel is appropriate so that it allows users and devs to more easily help solve issues or find bugs, specially without having to recompile termux after having to manually add general log entries to the source. DO NOT log data that may have private info of users like command arguments at log levels below debug, like `BackgroundJob` was doing previously. Logging to file support may be added later, will require log file rotation support and storage permissions.
2021-03-13 11:49:29 +00:00
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED) mClient.logInfo(LOG_TAG, "handleKeyCode() took key event");
return true;
}
// Clear Ctrl since we handle that ourselves:
int bitsToClear = KeyEvent.META_CTRL_MASK;
if (rightAltDownFromEvent) {
// Let right Alt/Alt Gr be used to compose characters.
} else {
// Use left alt to send to terminal (e.g. Left Alt+B to jump back a word), so remove:
bitsToClear |= KeyEvent.META_ALT_ON | KeyEvent.META_ALT_LEFT_ON;
}
int effectiveMetaState = event.getMetaState() & ~bitsToClear;
int result = event.getUnicodeChar(effectiveMetaState);
Implement GUI based Termux settings manager and a centralized logging framework The settings activity can be accessed by long pressing on terminal view and selecting "Settings" from the popup shown. It uses the Android's Preference framework. Currently only debugging preferences to set log level and enabling terminal view key logging are provided. The Preference framework by default uses the keys set in `app:key` attribute in the respective preferences XML file to store the values in the default `SharedPreferences` file of the app. However, since we rely on `TermuxPreferenceConstants` and `TermuxPropertyConstants` classes to define key names so that they can be easily shared between termux and its plugin apps, we provide our own `PreferenceDataStore` for storing key/value pairs. The key name in the XML file can optionally be the same. Check `DebuggingPreferencesFragment` class for a sample. Each new preference category fragment should be added to `app/settings/` with its data store. This commit may allow support to be added for modifying `termux.properties` file directly from the UI but that requires more work, since writing to property files with comments require in-place modification. The `Logger` class provides various static functions for logging that should be used from now on instead of directly calling android `Log.*` functions. The log level is automatically loaded from shared preferences at application startup via `TermuxApplication` and set in the static `Logger.CURRENT_LOG_LEVEL` variable. Changing the log level through the settings activity also changes the log level immediately. The 4 supported log levels are: - LOG_LEVEL_OFF which will log nothing. - LOG_LEVEL_NORMAL which will start logging error, warn and info messages and stacktraces. - LOG_LEVEL_DEBUG which will start logging debug messages. - LOG_LEVEL_VERBOSE which will start logging verbose messages. The default log level is `LOG_LEVEL_NORMAL` which will not log debug or verbose messages. Contributors can add useful log entries at those levels where ever they feel is appropriate so that it allows users and devs to more easily help solve issues or find bugs, specially without having to recompile termux after having to manually add general log entries to the source. DO NOT log data that may have private info of users like command arguments at log levels below debug, like `BackgroundJob` was doing previously. Logging to file support may be added later, will require log file rotation support and storage permissions.
2021-03-13 11:49:29 +00:00
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED)
mClient.logInfo(LOG_TAG, "KeyEvent#getUnicodeChar(" + effectiveMetaState + ") returned: " + result);
if (result == 0) {
return false;
}
int oldCombiningAccent = mCombiningAccent;
if ((result & KeyCharacterMap.COMBINING_ACCENT) != 0) {
// If entered combining accent previously, write it out:
if (mCombiningAccent != 0)
Improvements to extra keys (#1479) * Make popup keys for extra keys row configurable This makes the keys you get when swiping up on a key configurable. You can configure such a key by using an array of strings instead of a single string in the row. The first entry will be the normal key and the second will be the extra key. This is a slightly breaking change, as people that have configured custom extra keys with "-" or "/" will have to change the config to keep the popup keys. The default config will remain the same in terms of functionality, i.e. it includes the same popup key for "-". * Make popup keys interact well with long press keys This stops the repeat action when the popup is shown, and makes sure the popup is closed when you release even if there has been some repeat actions. * Support configuring the style of the extra keys This adds a setting for choosing between the different ways to render key names that were already present in ExtraKeysView. The available setting values are "arrows-only", "arrows-all", "all", "none" and "default". Other values will fallback to "default". Can be used as a workaround for #1410 * Support using modifier keys with letter keys in extra keys This allows you to use the modifier keys on the extra keys rows, e.g. ctrl, together with another button on the extra keys rows, as long as that button is a normal letter and not a special key. Support for special keys will come in the next commit. * Support using modifier keys with special keys in extra keys This allows you to use the modifier keys on the extra keys rows together with a special key on the extra keys rows, e.g. CTRL+LEFT. Fixes #745, fixes most of #895 and possibly #154 * Support mapping extra keys to other actions This adds a setting called extra-keys-map which allows you to map a key on the extra keys rows to another action. The value is a json object where the key is the button text as configured in extra-keys and the value is the action. Multiple actions can be used, but if they are special characters (like ESC or LEFT) they have to be separated from the other characters with a space on each side. If you want an actual space character, use SPACE. For example if you want to add a key to go to the next active channel in weechat, you can use this: extra-keys-map = {"weechat next": "ESC a"} And then add "weechat next" to extra-keys. The name can of course be whatever you want. Or if you want the button for the UP arrow to show ⇧ instead of ↑, you can use this: extra-keys-map = {"⇧": "UP"} And put "⇧" in extra-keys instead of "UP". Modifier keys (ctrl, alt and shift) can't be used in this map yet. Support for ctrl and alt will come in the next commit. I think this fixes #1186 * Support CTRL and ALT in extra keys map This allows you to use CTRL and ALT in extra-keys-map. For example if you want a button to exit the terminal, you can use this: extra-keys-map = {"exit": "CTRL d"} And add "exit" to extra-keys. * Support a KEYBOARD button in extra keys This toggles showing the keyboard input method. * Support specifying macro keys in the extra-keys option Instead of specifying macros in the separate extra-keys-map option by matching the key name in the two options, you can now use "macro" instead of "key" in extra-keys, and it will be a macro, i.e. a sequence of multiple keys separated by space. * Remove option extra-keys-map Now that you can specify macro in extra-keys, there is no point in having this separate option. Instead of specifying the value to display as key, and the macro to perform in extra-keys-map, you would now specify the value to display in the display property and the macro to perform in the macro property. * Lookup display text when creating ExtraKeyButton This will make it easier to support key aliases for macros in the next commit. * Add support for a key to open the drawer Fixes (I think) #1325
2020-06-09 09:17:07 +00:00
inputCodePoint(mCombiningAccent, controlDown, leftAltDown);
mCombiningAccent = result & KeyCharacterMap.COMBINING_ACCENT_MASK;
} else {
if (mCombiningAccent != 0) {
int combinedChar = KeyCharacterMap.getDeadChar(mCombiningAccent, result);
if (combinedChar > 0) result = combinedChar;
mCombiningAccent = 0;
}
Improvements to extra keys (#1479) * Make popup keys for extra keys row configurable This makes the keys you get when swiping up on a key configurable. You can configure such a key by using an array of strings instead of a single string in the row. The first entry will be the normal key and the second will be the extra key. This is a slightly breaking change, as people that have configured custom extra keys with "-" or "/" will have to change the config to keep the popup keys. The default config will remain the same in terms of functionality, i.e. it includes the same popup key for "-". * Make popup keys interact well with long press keys This stops the repeat action when the popup is shown, and makes sure the popup is closed when you release even if there has been some repeat actions. * Support configuring the style of the extra keys This adds a setting for choosing between the different ways to render key names that were already present in ExtraKeysView. The available setting values are "arrows-only", "arrows-all", "all", "none" and "default". Other values will fallback to "default". Can be used as a workaround for #1410 * Support using modifier keys with letter keys in extra keys This allows you to use the modifier keys on the extra keys rows, e.g. ctrl, together with another button on the extra keys rows, as long as that button is a normal letter and not a special key. Support for special keys will come in the next commit. * Support using modifier keys with special keys in extra keys This allows you to use the modifier keys on the extra keys rows together with a special key on the extra keys rows, e.g. CTRL+LEFT. Fixes #745, fixes most of #895 and possibly #154 * Support mapping extra keys to other actions This adds a setting called extra-keys-map which allows you to map a key on the extra keys rows to another action. The value is a json object where the key is the button text as configured in extra-keys and the value is the action. Multiple actions can be used, but if they are special characters (like ESC or LEFT) they have to be separated from the other characters with a space on each side. If you want an actual space character, use SPACE. For example if you want to add a key to go to the next active channel in weechat, you can use this: extra-keys-map = {"weechat next": "ESC a"} And then add "weechat next" to extra-keys. The name can of course be whatever you want. Or if you want the button for the UP arrow to show ⇧ instead of ↑, you can use this: extra-keys-map = {"⇧": "UP"} And put "⇧" in extra-keys instead of "UP". Modifier keys (ctrl, alt and shift) can't be used in this map yet. Support for ctrl and alt will come in the next commit. I think this fixes #1186 * Support CTRL and ALT in extra keys map This allows you to use CTRL and ALT in extra-keys-map. For example if you want a button to exit the terminal, you can use this: extra-keys-map = {"exit": "CTRL d"} And add "exit" to extra-keys. * Support a KEYBOARD button in extra keys This toggles showing the keyboard input method. * Support specifying macro keys in the extra-keys option Instead of specifying macros in the separate extra-keys-map option by matching the key name in the two options, you can now use "macro" instead of "key" in extra-keys, and it will be a macro, i.e. a sequence of multiple keys separated by space. * Remove option extra-keys-map Now that you can specify macro in extra-keys, there is no point in having this separate option. Instead of specifying the value to display as key, and the macro to perform in extra-keys-map, you would now specify the value to display in the display property and the macro to perform in the macro property. * Lookup display text when creating ExtraKeyButton This will make it easier to support key aliases for macros in the next commit. * Add support for a key to open the drawer Fixes (I think) #1325
2020-06-09 09:17:07 +00:00
inputCodePoint(result, controlDown, leftAltDown);
}
if (mCombiningAccent != oldCombiningAccent) invalidate();
return true;
}
Improvements to extra keys (#1479) * Make popup keys for extra keys row configurable This makes the keys you get when swiping up on a key configurable. You can configure such a key by using an array of strings instead of a single string in the row. The first entry will be the normal key and the second will be the extra key. This is a slightly breaking change, as people that have configured custom extra keys with "-" or "/" will have to change the config to keep the popup keys. The default config will remain the same in terms of functionality, i.e. it includes the same popup key for "-". * Make popup keys interact well with long press keys This stops the repeat action when the popup is shown, and makes sure the popup is closed when you release even if there has been some repeat actions. * Support configuring the style of the extra keys This adds a setting for choosing between the different ways to render key names that were already present in ExtraKeysView. The available setting values are "arrows-only", "arrows-all", "all", "none" and "default". Other values will fallback to "default". Can be used as a workaround for #1410 * Support using modifier keys with letter keys in extra keys This allows you to use the modifier keys on the extra keys rows, e.g. ctrl, together with another button on the extra keys rows, as long as that button is a normal letter and not a special key. Support for special keys will come in the next commit. * Support using modifier keys with special keys in extra keys This allows you to use the modifier keys on the extra keys rows together with a special key on the extra keys rows, e.g. CTRL+LEFT. Fixes #745, fixes most of #895 and possibly #154 * Support mapping extra keys to other actions This adds a setting called extra-keys-map which allows you to map a key on the extra keys rows to another action. The value is a json object where the key is the button text as configured in extra-keys and the value is the action. Multiple actions can be used, but if they are special characters (like ESC or LEFT) they have to be separated from the other characters with a space on each side. If you want an actual space character, use SPACE. For example if you want to add a key to go to the next active channel in weechat, you can use this: extra-keys-map = {"weechat next": "ESC a"} And then add "weechat next" to extra-keys. The name can of course be whatever you want. Or if you want the button for the UP arrow to show ⇧ instead of ↑, you can use this: extra-keys-map = {"⇧": "UP"} And put "⇧" in extra-keys instead of "UP". Modifier keys (ctrl, alt and shift) can't be used in this map yet. Support for ctrl and alt will come in the next commit. I think this fixes #1186 * Support CTRL and ALT in extra keys map This allows you to use CTRL and ALT in extra-keys-map. For example if you want a button to exit the terminal, you can use this: extra-keys-map = {"exit": "CTRL d"} And add "exit" to extra-keys. * Support a KEYBOARD button in extra keys This toggles showing the keyboard input method. * Support specifying macro keys in the extra-keys option Instead of specifying macros in the separate extra-keys-map option by matching the key name in the two options, you can now use "macro" instead of "key" in extra-keys, and it will be a macro, i.e. a sequence of multiple keys separated by space. * Remove option extra-keys-map Now that you can specify macro in extra-keys, there is no point in having this separate option. Instead of specifying the value to display as key, and the macro to perform in extra-keys-map, you would now specify the value to display in the display property and the macro to perform in the macro property. * Lookup display text when creating ExtraKeyButton This will make it easier to support key aliases for macros in the next commit. * Add support for a key to open the drawer Fixes (I think) #1325
2020-06-09 09:17:07 +00:00
public void inputCodePoint(int codePoint, boolean controlDownFromEvent, boolean leftAltDownFromEvent) {
Implement GUI based Termux settings manager and a centralized logging framework The settings activity can be accessed by long pressing on terminal view and selecting "Settings" from the popup shown. It uses the Android's Preference framework. Currently only debugging preferences to set log level and enabling terminal view key logging are provided. The Preference framework by default uses the keys set in `app:key` attribute in the respective preferences XML file to store the values in the default `SharedPreferences` file of the app. However, since we rely on `TermuxPreferenceConstants` and `TermuxPropertyConstants` classes to define key names so that they can be easily shared between termux and its plugin apps, we provide our own `PreferenceDataStore` for storing key/value pairs. The key name in the XML file can optionally be the same. Check `DebuggingPreferencesFragment` class for a sample. Each new preference category fragment should be added to `app/settings/` with its data store. This commit may allow support to be added for modifying `termux.properties` file directly from the UI but that requires more work, since writing to property files with comments require in-place modification. The `Logger` class provides various static functions for logging that should be used from now on instead of directly calling android `Log.*` functions. The log level is automatically loaded from shared preferences at application startup via `TermuxApplication` and set in the static `Logger.CURRENT_LOG_LEVEL` variable. Changing the log level through the settings activity also changes the log level immediately. The 4 supported log levels are: - LOG_LEVEL_OFF which will log nothing. - LOG_LEVEL_NORMAL which will start logging error, warn and info messages and stacktraces. - LOG_LEVEL_DEBUG which will start logging debug messages. - LOG_LEVEL_VERBOSE which will start logging verbose messages. The default log level is `LOG_LEVEL_NORMAL` which will not log debug or verbose messages. Contributors can add useful log entries at those levels where ever they feel is appropriate so that it allows users and devs to more easily help solve issues or find bugs, specially without having to recompile termux after having to manually add general log entries to the source. DO NOT log data that may have private info of users like command arguments at log levels below debug, like `BackgroundJob` was doing previously. Logging to file support may be added later, will require log file rotation support and storage permissions.
2021-03-13 11:49:29 +00:00
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED) {
mClient.logInfo(LOG_TAG, "inputCodePoint(codePoint=" + codePoint + ", controlDownFromEvent=" + controlDownFromEvent + ", leftAltDownFromEvent="
+ leftAltDownFromEvent + ")");
}
2015-10-25 14:27:32 +00:00
if (mTermSession == null) return;
final boolean controlDown = controlDownFromEvent || mClient.readControlKey();
final boolean altDown = leftAltDownFromEvent || mClient.readAltKey();
if (mClient.onCodePoint(codePoint, controlDown, mTermSession)) return;
2016-06-27 22:56:30 +00:00
if (controlDown) {
if (codePoint >= 'a' && codePoint <= 'z') {
codePoint = codePoint - 'a' + 1;
} else if (codePoint >= 'A' && codePoint <= 'Z') {
codePoint = codePoint - 'A' + 1;
} else if (codePoint == ' ' || codePoint == '2') {
codePoint = 0;
} else if (codePoint == '[' || codePoint == '3') {
codePoint = 27; // ^[ (Esc)
} else if (codePoint == '\\' || codePoint == '4') {
codePoint = 28;
} else if (codePoint == ']' || codePoint == '5') {
codePoint = 29;
} else if (codePoint == '^' || codePoint == '6') {
codePoint = 30; // control-^
2016-07-26 22:26:50 +00:00
} else if (codePoint == '_' || codePoint == '7' || codePoint == '/') {
// "Ctrl-/ sends 0x1f which is equivalent of Ctrl-_ since the days of VT102"
// - http://apple.stackexchange.com/questions/24261/how-do-i-send-c-that-is-control-slash-to-the-terminal
codePoint = 31;
} else if (codePoint == '8') {
codePoint = 127; // DEL
}
}
2016-04-22 00:20:10 +00:00
2016-06-27 22:56:30 +00:00
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 (~).
2016-04-22 00:20:10 +00:00
break;
2016-06-27 22:56:30 +00:00
case 0x02CB: // MODIFIER LETTER GRAVE ACCENT.
codePoint = 0x0060; // GRAVE ACCENT (`).
break;
case 0x02C6: // MODIFIER LETTER CIRCUMFLEX ACCENT.
codePoint = 0x005E; // CIRCUMFLEX ACCENT (^).
2016-04-22 00:20:10 +00:00
break;
}
2015-10-25 14:27:32 +00:00
2016-06-27 22:56:30 +00:00
// 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);
}
}
2015-10-25 14:27:32 +00:00
/** Input the specified keyCode if applicable and return if the input was consumed. */
public boolean handleKeyCode(int keyCode, int keyMod) {
TerminalEmulator term = mTermSession.getEmulator();
String code = KeyHandler.getCode(keyCode, keyMod, term.isCursorKeysApplicationMode(), term.isKeypadApplicationMode());
if (code == null) return false;
mTermSession.write(code);
return true;
}
/**
* Called when a key is released in the view.
*
* @param keyCode The keycode of the key which was released.
* @param event A {@link KeyEvent} describing the event.
* @return Whether the event was handled.
*/
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
Implement GUI based Termux settings manager and a centralized logging framework The settings activity can be accessed by long pressing on terminal view and selecting "Settings" from the popup shown. It uses the Android's Preference framework. Currently only debugging preferences to set log level and enabling terminal view key logging are provided. The Preference framework by default uses the keys set in `app:key` attribute in the respective preferences XML file to store the values in the default `SharedPreferences` file of the app. However, since we rely on `TermuxPreferenceConstants` and `TermuxPropertyConstants` classes to define key names so that they can be easily shared between termux and its plugin apps, we provide our own `PreferenceDataStore` for storing key/value pairs. The key name in the XML file can optionally be the same. Check `DebuggingPreferencesFragment` class for a sample. Each new preference category fragment should be added to `app/settings/` with its data store. This commit may allow support to be added for modifying `termux.properties` file directly from the UI but that requires more work, since writing to property files with comments require in-place modification. The `Logger` class provides various static functions for logging that should be used from now on instead of directly calling android `Log.*` functions. The log level is automatically loaded from shared preferences at application startup via `TermuxApplication` and set in the static `Logger.CURRENT_LOG_LEVEL` variable. Changing the log level through the settings activity also changes the log level immediately. The 4 supported log levels are: - LOG_LEVEL_OFF which will log nothing. - LOG_LEVEL_NORMAL which will start logging error, warn and info messages and stacktraces. - LOG_LEVEL_DEBUG which will start logging debug messages. - LOG_LEVEL_VERBOSE which will start logging verbose messages. The default log level is `LOG_LEVEL_NORMAL` which will not log debug or verbose messages. Contributors can add useful log entries at those levels where ever they feel is appropriate so that it allows users and devs to more easily help solve issues or find bugs, specially without having to recompile termux after having to manually add general log entries to the source. DO NOT log data that may have private info of users like command arguments at log levels below debug, like `BackgroundJob` was doing previously. Logging to file support may be added later, will require log file rotation support and storage permissions.
2021-03-13 11:49:29 +00:00
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED)
mClient.logInfo(LOG_TAG, "onKeyUp(keyCode=" + keyCode + ", event=" + event + ")");
if (mEmulator == null) return true;
if (mClient.onKeyUp(keyCode, event)) {
invalidate();
return true;
} else if (event.isSystem()) {
// Let system key events through.
return super.onKeyUp(keyCode, event);
}
return true;
}
/**
* 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.
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
updateSize();
}
/** Check if the terminal size in rows and columns should be updated. */
public void updateSize() {
int viewWidth = getWidth();
int viewHeight = getHeight();
if (viewWidth == 0 || viewHeight == 0 || mTermSession == null) return;
// Set to 80 and 24 if you want to enable vttest.
int newColumns = Math.max(4, (int) (viewWidth / mRenderer.mFontWidth));
int newRows = Math.max(4, (viewHeight - mRenderer.mFontLineSpacingAndAscent) / mRenderer.mFontLineSpacing);
if (mEmulator == null || (newColumns != mEmulator.mColumns || newRows != mEmulator.mRows)) {
mTermSession.updateSize(newColumns, newRows);
mEmulator = mTermSession.getEmulator();
mTopRow = 0;
scrollTo(0, 0);
invalidate();
}
}
@Override
protected void onDraw(Canvas canvas) {
if (mEmulator == null) {
canvas.drawColor(0XFF000000);
} else {
// render the terminal view and highlight any selected text
int[] sel = mDefaultSelectors;
if (mTextSelectionCursorController != null) {
mTextSelectionCursorController.getSelectors(sel);
}
mRenderer.render(mEmulator, canvas, mTopRow, sel[0], sel[1], sel[2], sel[3]);
// render the text selection handles
renderTextSelection();
2019-10-05 10:05:42 +00:00
}
}
2019-10-05 10:05:42 +00:00
public TerminalSession getCurrentSession() {
return mTermSession;
}
private CharSequence getText() {
return mEmulator.getScreen().getSelectedText(0, mTopRow, mEmulator.mColumns, mTopRow + mEmulator.mRows);
}
public int getCursorX(float x) {
2019-10-05 10:05:42 +00:00
return (int) (x / mRenderer.mFontWidth);
}
public int getCursorY(float y) {
2019-10-05 10:05:42 +00:00
return (int) (((y - 40) / mRenderer.mFontLineSpacing) + mTopRow);
}
public int getPointX(int cx) {
2019-10-05 10:05:42 +00:00
if (cx > mEmulator.mColumns) {
cx = mEmulator.mColumns;
}
return Math.round(cx * mRenderer.mFontWidth);
}
public int getPointY(int cy) {
2019-10-05 10:05:42 +00:00
return Math.round((cy - mTopRow) * mRenderer.mFontLineSpacing);
}
public int getTopRow() {
return mTopRow;
2019-10-05 10:05:42 +00:00
}
public void setTopRow(int mTopRow) {
this.mTopRow = mTopRow;
2019-10-05 10:05:42 +00:00
}
2019-12-18 16:34:14 +00:00
/**
* Define functions required for AutoFill API
*/
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void autofill(AutofillValue value) {
if (value.isText()) {
mTermSession.write(value.getTextValue().toString());
2019-10-05 10:05:42 +00:00
}
}
2019-10-05 10:05:42 +00:00
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public int getAutofillType() {
return AUTOFILL_TYPE_TEXT;
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public AutofillValue getAutofillValue() {
return AutofillValue.forText("");
}
2019-10-05 10:05:42 +00:00
2019-12-18 15:50:23 +00:00
/**
* Define functions required for text selection and its handles.
*/
TextSelectionCursorController getTextSelectionCursorController() {
if (mTextSelectionCursorController == null) {
mTextSelectionCursorController = new TextSelectionCursorController(this);
2019-12-18 15:50:23 +00:00
final ViewTreeObserver observer = getViewTreeObserver();
if (observer != null) {
observer.addOnTouchModeChangeListener(mTextSelectionCursorController);
2019-12-18 15:50:23 +00:00
}
}
return mTextSelectionCursorController;
}
2019-10-05 10:05:42 +00:00
private void showTextSelectionCursors(MotionEvent event) {
getTextSelectionCursorController().show(event);
}
2019-10-05 10:05:42 +00:00
private boolean hideTextSelectionCursors() {
return getTextSelectionCursorController().hide();
}
2019-10-05 10:05:42 +00:00
private void renderTextSelection() {
if (mTextSelectionCursorController != null)
mTextSelectionCursorController.render();
}
2019-10-05 10:05:42 +00:00
public boolean isSelectingText() {
if (mTextSelectionCursorController != null) {
return mTextSelectionCursorController.isActive();
} else {
2019-10-05 10:05:42 +00:00
return false;
}
}
2019-10-05 10:05:42 +00:00
private ActionMode getTextSelectionActionMode() {
if (mTextSelectionCursorController != null) {
return mTextSelectionCursorController.getActionMode();
} else {
return null;
2019-10-05 10:05:42 +00:00
}
}
2019-10-05 10:05:42 +00:00
public void startTextSelectionMode(MotionEvent event) {
if (!requestFocus()) {
return;
2019-10-05 10:05:42 +00:00
}
showTextSelectionCursors(event);
mClient.copyModeChanged(isSelectingText());
2019-10-05 10:05:42 +00:00
invalidate();
2019-10-05 10:05:42 +00:00
}
public void stopTextSelectionMode() {
if (hideTextSelectionCursors()) {
mClient.copyModeChanged(isSelectingText());
invalidate();
2019-10-05 10:05:42 +00:00
}
}
private void decrementYTextSelectionCursors(int decrement) {
if (mTextSelectionCursorController != null) {
mTextSelectionCursorController.decrementYTextSelectionCursors(decrement);
2019-10-05 10:05:42 +00:00
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
2019-10-05 10:05:42 +00:00
if (mTextSelectionCursorController != null) {
getViewTreeObserver().addOnTouchModeChangeListener(mTextSelectionCursorController);
2019-10-05 10:05:42 +00:00
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
2019-10-05 10:05:42 +00:00
if (mTextSelectionCursorController != null) {
// Might solve the following exception
// android.view.WindowLeaked: Activity com.termux.app.TermuxActivity has leaked window android.widget.PopupWindow
stopTextSelectionMode();
2019-10-05 10:05:42 +00:00
getViewTreeObserver().removeOnTouchModeChangeListener(mTextSelectionCursorController);
mTextSelectionCursorController.onDetached();
}
}
2019-10-05 10:05:42 +00:00
/**
* Define functions required for long hold toolbar.
*/
2019-10-05 10:05:42 +00:00
private final Runnable mShowFloatingToolbar = new Runnable() {
@Override
public void run() {
if (getTextSelectionActionMode() != null) {
getTextSelectionActionMode().hide(0); // hide off.
2019-10-05 10:05:42 +00:00
}
}
};
private void showFloatingToolbar() {
if (getTextSelectionActionMode() != null) {
2019-10-05 10:05:42 +00:00
int delay = ViewConfiguration.getDoubleTapTimeout();
postDelayed(mShowFloatingToolbar, delay);
}
}
void hideFloatingToolbar() {
if (getTextSelectionActionMode() != null) {
removeCallbacks(mShowFloatingToolbar);
getTextSelectionActionMode().hide(-1);
}
}
public void updateFloatingToolbarVisibility(MotionEvent event) {
if (getTextSelectionActionMode() != null) {
2019-10-05 10:05:42 +00:00
switch (event.getActionMasked()) {
case MotionEvent.ACTION_MOVE:
hideFloatingToolbar();
2019-10-05 10:05:42 +00:00
break;
case MotionEvent.ACTION_UP: // fall through
case MotionEvent.ACTION_CANCEL:
showFloatingToolbar();
}
}
}
2015-10-25 14:27:32 +00:00
}