mirror of https://github.com/termux/termux-app
WIP
This commit is contained in:
parent
7979b975de
commit
49955f2c50
|
@ -12,14 +12,14 @@
|
|||
android:required="false" />
|
||||
|
||||
<permission
|
||||
android:name="${TERMUX_PACKAGE_NAME}.permission.RUN_COMMAND"
|
||||
android:name="com.termux.permission.RUN_COMMAND"
|
||||
android:description="@string/permission_run_command_description"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/permission_run_command_label"
|
||||
android:protectionLevel="dangerous" />
|
||||
|
||||
<permission
|
||||
android:name="com.termux.permission.internal"
|
||||
android:name="com.termux.permission.TERMUX_INTERNAL"
|
||||
android:description="@string/permission_run_command_description"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/permission_run_command_label"
|
||||
|
@ -70,14 +70,15 @@
|
|||
</activity>
|
||||
<activity-alias
|
||||
android:name=".app.TermuxActivityInternal"
|
||||
android:targetActivity=".app.TermuxActivity"/>
|
||||
android:targetActivity=".app.TermuxActivity"
|
||||
android:exported="true"
|
||||
android:permission="com.termux.permission.TERMUX_INTERNAL" />
|
||||
|
||||
<activity
|
||||
android:name=".app.TermuxHelpActivity"
|
||||
android:exported="false"
|
||||
android:parentActivityName=".app.TermuxActivity"
|
||||
android:resizeableActivity="true"
|
||||
tools:targetApi="n" />
|
||||
android:resizeableActivity="true" />
|
||||
|
||||
<activity
|
||||
android:name=".app.TermuxFileReceiverActivity"
|
||||
|
|
|
@ -44,6 +44,7 @@ import androidx.viewpager.widget.ViewPager;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
@ -80,8 +81,6 @@ public final class TermuxActivity extends AppCompatActivity implements ServiceCo
|
|||
private static final String ARG_TERMINAL_TOOLBAR_TEXT_INPUT = "terminal_toolbar_text_input";
|
||||
private static final String ARG_ACTIVITY_RECREATED = "activity_recreated";
|
||||
|
||||
private static final String LOG_TAG = "TermuxActivity";
|
||||
|
||||
/**
|
||||
* The connection to the {@link TermuxService}. Requested in {@link #onCreate(Bundle)} with a call to
|
||||
* {@link #bindService(Intent, ServiceConnection, int)}, and obtained and stored in
|
||||
|
@ -104,7 +103,7 @@ public final class TermuxActivity extends AppCompatActivity implements ServiceCo
|
|||
* The {@link TerminalSessionClient} interface implementation to allow for communication between
|
||||
* {@link TerminalSession} and {@link TermuxActivity}.
|
||||
*/
|
||||
TermuxTerminalSessionActivityClient mTermuxTerminalSessionActivityClient;
|
||||
final TermuxTerminalSessionActivityClient mTermuxTerminalSessionActivityClient = new TermuxTerminalSessionActivityClient(this);
|
||||
|
||||
/**
|
||||
* The terminal extra keys view.
|
||||
|
@ -132,7 +131,7 @@ public final class TermuxActivity extends AppCompatActivity implements ServiceCo
|
|||
Toast mLastToast;
|
||||
|
||||
/**
|
||||
* If between onResume() and onStop(). Note that only one session is in the foreground of the terminal view at the
|
||||
* If between onStart() and onStop(). Note that only one session is in the foreground of the terminal view at the
|
||||
* time, so if the session causing a change is not in the foreground it should probably be treated as background.
|
||||
*/
|
||||
private boolean mIsVisible;
|
||||
|
@ -156,7 +155,6 @@ public final class TermuxActivity extends AppCompatActivity implements ServiceCo
|
|||
|
||||
setContentView(R.layout.activity_termux);
|
||||
|
||||
mTermuxTerminalSessionActivityClient = new TermuxTerminalSessionActivityClient(this);
|
||||
mTermuxTerminalViewClient = new TermuxTerminalViewClient(this, mTermuxTerminalSessionActivityClient);
|
||||
|
||||
mTerminalView = findViewById(R.id.terminal_view);
|
||||
|
@ -208,38 +206,26 @@ public final class TermuxActivity extends AppCompatActivity implements ServiceCo
|
|||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
|
||||
mIsVisible = true;
|
||||
mTermuxTerminalSessionActivityClient.onStart();
|
||||
|
||||
if (mTermuxTerminalViewClient != null) {
|
||||
mTermuxTerminalViewClient.onStart();
|
||||
}
|
||||
|
||||
registerTermuxActivityBroadcastReceiver();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (mTermuxTerminalSessionActivityClient != null) {
|
||||
mTermuxTerminalSessionActivityClient.onResume();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
|
||||
mIsVisible = false;
|
||||
|
||||
if (mTermuxTerminalSessionActivityClient != null) {
|
||||
mTermuxTerminalSessionActivityClient.onStop();
|
||||
}
|
||||
|
||||
unregisterReceiver(mTermuxActivityBroadcastReceiver);
|
||||
|
||||
getDrawer().closeDrawers();
|
||||
}
|
||||
|
||||
|
@ -267,6 +253,42 @@ public final class TermuxActivity extends AppCompatActivity implements ServiceCo
|
|||
savedInstanceState.putBoolean(ARG_ACTIVITY_RECREATED, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
Log.e("termux", "NEW INTENT: " + intent.getAction());
|
||||
try {
|
||||
if ("com.termux.app.NEW_STYLE".equals(intent.getAction())) {
|
||||
byte[] newFont = intent.getByteArrayExtra("com.termux.app.extra.NEW_FONT");
|
||||
if (newFont != null) {
|
||||
File fontFile = new File(TermuxConstants.FONT_PATH);
|
||||
if (newFont.length == 0) {
|
||||
fontFile.delete();
|
||||
} else {
|
||||
try (FileOutputStream fos = new FileOutputStream(fontFile)) {
|
||||
fos.write(newFont);
|
||||
}
|
||||
}
|
||||
}
|
||||
byte[] newColors = intent.getByteArrayExtra("com.termux.app.extra.NEW_COLORS");
|
||||
if (newColors != null) {
|
||||
File colorsFile = new File(TermuxConstants.COLORS_PATH);
|
||||
if (newColors.length == 0) {
|
||||
colorsFile.delete();
|
||||
} else {
|
||||
try (FileOutputStream fos = new FileOutputStream(colorsFile)) {
|
||||
fos.write(newColors);
|
||||
}
|
||||
}
|
||||
}
|
||||
mTermuxTerminalSessionActivityClient.onReloadActivityStyling();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TermuxConstants.LOG_TAG, "Error handling new intent", e);
|
||||
TermuxMessageDialogUtils.showToast(this, "Error handling new intent: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -516,6 +538,7 @@ public final class TermuxActivity extends AppCompatActivity implements ServiceCo
|
|||
try {
|
||||
startActivity(stylingIntent);
|
||||
} catch (ActivityNotFoundException | IllegalArgumentException e) {
|
||||
Log.e(TermuxConstants.LOG_TAG, "Error starting Termux:Style", e);
|
||||
// The startActivity() call is not documented to throw IllegalArgumentException.
|
||||
// However, crash reporting shows that it sometimes does, so catch it here.
|
||||
String installationUrl = isInstalledFromGooglePlay()
|
||||
|
@ -612,7 +635,6 @@ public final class TermuxActivity extends AppCompatActivity implements ServiceCo
|
|||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(ACTION_RELOAD_STYLE);
|
||||
intentFilter.addAction(ACTION_REQUEST_PERMISSIONS);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 33) {
|
||||
registerReceiver(mTermuxActivityBroadcastReceiver, intentFilter, Context.RECEIVER_NOT_EXPORTED);
|
||||
} else {
|
||||
|
@ -623,6 +645,7 @@ public final class TermuxActivity extends AppCompatActivity implements ServiceCo
|
|||
class TermuxActivityBroadcastReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Log.e("termux", "onReceive. visible=" + mIsVisible + ", action=" + intent.getAction());
|
||||
if (mIsVisible) {
|
||||
switch (intent.getAction()) {
|
||||
case ACTION_RELOAD_STYLE:
|
||||
|
@ -644,7 +667,6 @@ public final class TermuxActivity extends AppCompatActivity implements ServiceCo
|
|||
|
||||
setTerminalToolbarHeight();
|
||||
|
||||
if (mTermuxTerminalSessionActivityClient != null)
|
||||
mTermuxTerminalSessionActivityClient.onReloadActivityStyling();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.termux.app;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class TermuxConstants {
|
||||
|
||||
public static final String LOG_TAG = "termux";
|
||||
|
@ -9,6 +11,9 @@ public class TermuxConstants {
|
|||
public static final String BIN_PATH = PREFIX_PATH + "/bin";
|
||||
public static final String HOME_PATH = FILES_PATH + "/home";
|
||||
|
||||
public static final String FONT_PATH = TermuxConstants.HOME_PATH + "/.termux/font.ttf";
|
||||
public static final String COLORS_PATH = TermuxConstants.HOME_PATH + "/.termux/colors.properties";
|
||||
|
||||
public static final int TERMUX_APP_NOTIFICATION_ID = 1337;
|
||||
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ public class TermuxPreferences {
|
|||
}
|
||||
|
||||
public String getCurrentSession() {
|
||||
return prefs.getString(PREF_KEEP_SCREEN_ON, null);
|
||||
return prefs.getString(PREF_CURRENT_SESSION, null);
|
||||
}
|
||||
|
||||
public void setShowTerminalToolbar(boolean newValue) {
|
||||
|
|
|
@ -33,7 +33,8 @@ public class TermuxProperties {
|
|||
}
|
||||
|
||||
boolean isBackKeyTheEscapeKey() {
|
||||
return properties.getProperty("back-key", "escape").equalsIgnoreCase("escape");
|
||||
Log.e("termux", "isBackThe='" + properties.getProperty("back-key", "back"));
|
||||
return properties.getProperty("back-key", "back").equalsIgnoreCase("escape");
|
||||
}
|
||||
|
||||
boolean isEnforcingCharBasedInput() {
|
||||
|
@ -41,7 +42,7 @@ public class TermuxProperties {
|
|||
}
|
||||
|
||||
boolean areVirtualVolumeKeysDisabled() {
|
||||
return false; // TODO
|
||||
return properties.getProperty("volume-keys", "normal").equalsIgnoreCase("volume");
|
||||
}
|
||||
|
||||
public String getExtraKeys() {
|
||||
|
|
|
@ -113,8 +113,6 @@ public final class TermuxTerminalSessionActivityClient implements TerminalSessio
|
|||
|
||||
private int mBellSoundId;
|
||||
|
||||
private static final String LOG_TAG = "TermuxTerminalSessionActivityClient";
|
||||
|
||||
public TermuxTerminalSessionActivityClient(TermuxActivity activity) {
|
||||
this.mActivity = activity;
|
||||
}
|
||||
|
@ -124,7 +122,7 @@ public final class TermuxTerminalSessionActivityClient implements TerminalSessio
|
|||
*/
|
||||
public void onCreate() {
|
||||
// Set terminal fonts and colors
|
||||
checkForFontAndColors();
|
||||
onReloadActivityStyling();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -160,7 +158,8 @@ public final class TermuxTerminalSessionActivityClient implements TerminalSessio
|
|||
public void onStop() {
|
||||
// Store current session in shared preferences so that it can be restored later in
|
||||
// {@link #onStart} if needed.
|
||||
setCurrentStoredSession();
|
||||
TerminalSession currentSession = mActivity.getCurrentSession();
|
||||
mActivity.mPreferences.setCurrentSession(currentSession == null ? null : currentSession.mHandle);
|
||||
|
||||
// Release mBellSoundPool resources, specially to prevent exceptions like the following to be thrown
|
||||
// java.util.concurrent.TimeoutException: android.media.SoundPool.finalize() timed out after 10 seconds
|
||||
|
@ -173,8 +172,29 @@ public final class TermuxTerminalSessionActivityClient implements TerminalSessio
|
|||
* Should be called when mActivity.reloadActivityStyling() is called
|
||||
*/
|
||||
public void onReloadActivityStyling() {
|
||||
// Set terminal fonts and colors
|
||||
checkForFontAndColors();
|
||||
try {
|
||||
File fontFile = new File(TermuxConstants.FONT_PATH);
|
||||
File colorsFile = new File(TermuxConstants.COLORS_PATH);
|
||||
|
||||
final Properties props = new Properties();
|
||||
if (colorsFile.isFile()) {
|
||||
try (InputStream in = new FileInputStream(colorsFile)) {
|
||||
props.load(in);
|
||||
}
|
||||
}
|
||||
|
||||
TerminalColors.COLOR_SCHEME.updateWith(props);
|
||||
TerminalSession session = mActivity.getCurrentSession();
|
||||
if (session != null && session.getEmulator() != null) {
|
||||
session.getEmulator().mColors.reset();
|
||||
}
|
||||
updateBackgroundColor();
|
||||
|
||||
final Typeface newTypeface = (fontFile.exists() && fontFile.length() > 0) ? Typeface.createFromFile(fontFile) : Typeface.MONOSPACE;
|
||||
mActivity.getTerminalView().setTypeface(newTypeface);
|
||||
} catch (Exception e) {
|
||||
Log.e(TermuxConstants.LOG_TAG, "Error in onReloadActivityStyling()", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -287,7 +307,7 @@ public final class TermuxTerminalSessionActivityClient implements TerminalSessio
|
|||
mBellSoundId = mBellSoundPool.load(mActivity, com.termux.R.raw.bell, 1);
|
||||
} catch (Exception e) {
|
||||
// Catch java.lang.RuntimeException: Unable to resume activity {com.termux/com.termux.app.TermuxActivity}: android.content.res.Resources$NotFoundException: File res/raw/bell.ogg from drawable resource ID
|
||||
Log.e(LOG_TAG, "Failed to load bell sound pool", e);
|
||||
Log.e(TermuxConstants.LOG_TAG, "Failed to load bell sound pool", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -392,25 +412,15 @@ public final class TermuxTerminalSessionActivityClient implements TerminalSessio
|
|||
}
|
||||
}
|
||||
|
||||
public void setCurrentStoredSession() {
|
||||
TerminalSession currentSession = mActivity.getCurrentSession();
|
||||
mActivity.mPreferences.setCurrentSession(currentSession == null ? null : currentSession.mHandle);
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* The current session as stored or the last one if that does not exist.
|
||||
*/
|
||||
public TerminalSession getCurrentStoredSessionOrLast() {
|
||||
String currentSessionHandle = mActivity.mPreferences.getCurrentSession();
|
||||
if (currentSessionHandle == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if the session handle found matches one of the currently running sessions
|
||||
TermuxService service = mActivity.getTermuxService();
|
||||
if (service == null) return null;
|
||||
|
||||
String currentSessionHandle = mActivity.mPreferences.getCurrentSession();
|
||||
TerminalSession currentSession = service.getTerminalSessionForHandle(currentSessionHandle);
|
||||
|
||||
if (currentSession == null) {
|
||||
|
@ -482,33 +492,6 @@ public final class TermuxTerminalSessionActivityClient implements TerminalSessio
|
|||
return toastTitle.toString();
|
||||
}
|
||||
|
||||
|
||||
public void checkForFontAndColors() {
|
||||
try {
|
||||
File fontFile = new File(TermuxConstants.HOME_PATH + "/.termux/font.ttf");
|
||||
File colorsFile = new File(TermuxConstants.HOME_PATH + "/.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 = mActivity.getCurrentSession();
|
||||
if (session != null && session.getEmulator() != null) {
|
||||
session.getEmulator().mColors.reset();
|
||||
}
|
||||
updateBackgroundColor();
|
||||
|
||||
final Typeface newTypeface = (fontFile.exists() && fontFile.length() > 0) ? Typeface.createFromFile(fontFile) : Typeface.MONOSPACE;
|
||||
mActivity.getTerminalView().setTypeface(newTypeface);
|
||||
} catch (Exception e) {
|
||||
Log.e(LOG_TAG, "Error in checkForFontAndColors()", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateBackgroundColor() {
|
||||
if (!mActivity.isVisible()) return;
|
||||
TerminalSession session = mActivity.getCurrentSession();
|
||||
|
|
|
@ -44,16 +44,24 @@ import com.termux.terminal.TerminalEmulator;
|
|||
import com.termux.terminal.TerminalSession;
|
||||
import com.termux.view.textselection.TextSelectionCursorController;
|
||||
|
||||
/** View displaying and interacting with a {@link TerminalSession}. */
|
||||
/**
|
||||
* View displaying and interacting with a {@link TerminalSession}.
|
||||
*/
|
||||
public final class TerminalView extends View {
|
||||
|
||||
/** Log terminal view key and IME events. */
|
||||
/**
|
||||
* Log terminal view key and IME events.
|
||||
*/
|
||||
private static final boolean TERMINAL_VIEW_KEY_LOGGING_ENABLED = false;
|
||||
|
||||
/** The currently displayed terminal session, whose emulator is {@link #mEmulator}. */
|
||||
/**
|
||||
* The currently displayed terminal session, whose emulator is {@link #mEmulator}.
|
||||
*/
|
||||
public TerminalSession mTermSession;
|
||||
|
||||
/** Our terminal emulator whose session is {@link #mTermSession}. */
|
||||
/**
|
||||
* Our terminal emulator whose session is {@link #mTermSession}.
|
||||
*/
|
||||
public TerminalEmulator mEmulator;
|
||||
|
||||
public TerminalRenderer mRenderer;
|
||||
|
@ -66,35 +74,47 @@ public final class TerminalView extends View {
|
|||
|
||||
private TextSelectionCursorController mTextSelectionCursorController;
|
||||
|
||||
/** The top row of text to display. Ranges from -activeTranscriptRows to 0. */
|
||||
/**
|
||||
* 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. */
|
||||
/**
|
||||
* 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. */
|
||||
/**
|
||||
* 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. */
|
||||
/**
|
||||
* 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. */
|
||||
/**
|
||||
* If non-zero, this is the last unicode code point received if that was a combining character.
|
||||
*/
|
||||
int mCombiningAccent;
|
||||
|
||||
private final boolean mAccessibilityEnabled;
|
||||
|
||||
/** The {@link KeyEvent} is generated from a virtual keyboard, like manually with the {@link KeyEvent#KeyEvent(int, int)} constructor. */
|
||||
/**
|
||||
* The {@link KeyEvent} is generated from a virtual keyboard, like manually with the {@link KeyEvent#KeyEvent(int, int)} constructor.
|
||||
*/
|
||||
public final static int KEY_EVENT_SOURCE_VIRTUAL_KEYBOARD = KeyCharacterMap.VIRTUAL_KEYBOARD; // -1
|
||||
|
||||
/** The {@link KeyEvent} is generated from a non-physical device, like if 0 value is returned by {@link KeyEvent#getDeviceId()}. */
|
||||
/**
|
||||
* The {@link KeyEvent} is generated from a non-physical device, like if 0 value is returned by {@link KeyEvent#getDeviceId()}.
|
||||
*/
|
||||
public final static int KEY_EVENT_SOURCE_SOFT_KEYBOARD = 0;
|
||||
|
||||
private static final String LOG_TAG = "TerminalView";
|
||||
|
@ -451,9 +471,11 @@ public final class TerminalView extends View {
|
|||
if (mAccessibilityEnabled) setContentDescription(getText());
|
||||
}
|
||||
|
||||
/** This must be called by the hosting activity in {@link Activity#onContextMenuClosed(Menu)}
|
||||
/**
|
||||
* This must be called by the hosting activity in {@link Activity#onContextMenuClosed(Menu)}
|
||||
* when context menu for the {@link TerminalView} is started by
|
||||
* {@link TextSelectionCursorController#ACTION_MORE} is closed. */
|
||||
* {@link TextSelectionCursorController#ACTION_MORE} is closed.
|
||||
*/
|
||||
public void onContextMenuClosed(Menu menu) {
|
||||
// Unset the stored text since it shouldn't be used anymore and should be cleared from memory
|
||||
unsetStoredSelectedText();
|
||||
|
@ -502,10 +524,12 @@ public final class TerminalView extends View {
|
|||
if (relativeToScroll) {
|
||||
row += mTopRow;
|
||||
}
|
||||
return new int[] { column, row };
|
||||
return new int[]{column, row};
|
||||
}
|
||||
|
||||
/** Send a single mouse event code to the terminal. */
|
||||
/**
|
||||
* Send a single mouse event code to the terminal.
|
||||
*/
|
||||
void sendMouseEventCode(MotionEvent e, int button, boolean pressed) {
|
||||
int[] columnAndRow = getColumnAndRow(e, false);
|
||||
int x = columnAndRow[0] + 1;
|
||||
|
@ -523,7 +547,9 @@ public final class TerminalView extends View {
|
|||
mEmulator.sendMouseEvent(button, x, y, pressed);
|
||||
}
|
||||
|
||||
/** Perform a scroll, either from dragging the screen or by scrolling a mouse wheel. */
|
||||
/**
|
||||
* 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);
|
||||
|
@ -541,7 +567,9 @@ public final class TerminalView extends View {
|
|||
}
|
||||
}
|
||||
|
||||
/** Overriding {@link View#onGenericMotionEvent(MotionEvent)}. */
|
||||
/**
|
||||
* Overriding {@link View#onGenericMotionEvent(MotionEvent)}.
|
||||
*/
|
||||
@Override
|
||||
public boolean onGenericMotionEvent(MotionEvent event) {
|
||||
if (mEmulator != null && event.isFromSource(InputDevice.SOURCE_MOUSE) && event.getAction() == MotionEvent.ACTION_SCROLL) {
|
||||
|
@ -596,8 +624,9 @@ public final class TerminalView extends View {
|
|||
|
||||
@Override
|
||||
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
|
||||
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED)
|
||||
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED) {
|
||||
Log.i(LOG_TAG, "onKeyPreIme(keyCode=" + keyCode + ", event=" + event + ")");
|
||||
}
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
if (isSelectingText()) {
|
||||
stopTextSelectionMode();
|
||||
|
@ -621,7 +650,7 @@ public final class TerminalView extends View {
|
|||
* Gboard calls this when shouldEnforceCharBasedInput() is disabled (InputType.TYPE_NULL) instead
|
||||
* of calling commitText(), with deviceId=-1. However, Hacker's Keyboard, OpenBoard, LG Keyboard
|
||||
* call commitText().
|
||||
*
|
||||
* <p>
|
||||
* This function may also be called directly without android calling it, like by
|
||||
* `TerminalExtraKeys` which generates a KeyEvent manually which uses {@link KeyCharacterMap#VIRTUAL_KEYBOARD}
|
||||
* as the device (deviceId=-1), as does Gboard. That would normally use mappings defined in
|
||||
|
@ -629,7 +658,7 @@ public final class TerminalView extends View {
|
|||
* used by virtual keyboard or hardware keyboard. Note that virtual keyboard device is not the
|
||||
* same as software keyboard, like Gboard, etc. Its a fake device used for generating events and
|
||||
* for testing.
|
||||
*
|
||||
* <p>
|
||||
* We handle shift key in `commitText()` to convert codepoint to uppercase case there with a
|
||||
* call to {@link Character#toUpperCase(int)}, but here we instead rely on getUnicodeChar() for
|
||||
* conversion of keyCode, for both hardware keyboard shift key (via effectiveMetaState) and
|
||||
|
@ -639,7 +668,7 @@ public final class TerminalView extends View {
|
|||
* languages since `Virtual.kcm` in english only by default or at least in AOSP. For both hardware
|
||||
* shift key (via effectiveMetaState) and `mClient.readShiftKey()`, `getUnicodeChar()` is used
|
||||
* for shift specific behaviour which usually is to uppercase.
|
||||
*
|
||||
* <p>
|
||||
* For fn key on hardware keyboard, android checks kcm files for hardware keyboards, which is
|
||||
* `Generic.kcm` by default, unless a vendor specific one is defined. The event passed will have
|
||||
* {@link KeyEvent#META_FUNCTION_ON} set. If the kcm file only defines a single character or unicode
|
||||
|
@ -648,7 +677,7 @@ public final class TerminalView extends View {
|
|||
* android will first pass an event with original key `DPAD_UP` and {@link KeyEvent#META_FUNCTION_ON}
|
||||
* set. But this function will not consume it and android will pass another event with `PAGE_UP`
|
||||
* and {@link KeyEvent#META_FUNCTION_ON} not set, which will be consumed.
|
||||
*
|
||||
* <p>
|
||||
* Now there are some other issues as well, firstly ctrl and alt flags are not passed to
|
||||
* `getUnicodeChar()`, so modified key values in kcm are not used. Secondly, if the kcm file
|
||||
* for other modifiers like shift or fn define a non-alphabet, like { fn: '\u0015' } to act as
|
||||
|
@ -661,7 +690,7 @@ public final class TerminalView extends View {
|
|||
* Hacker's Keyboard calls `commitText()` so don't test fn/shift with it for this function.
|
||||
* https://github.com/termux/termux-app/pull/2237
|
||||
* https://github.com/agnostic-apollo/termux-app/blob/terminal-code-point-custom-mapping/terminal-view/src/main/java/com/termux/view/TerminalView.java
|
||||
*
|
||||
* <p>
|
||||
* Key Character Map (kcm) and Key Layout (kl) files info:
|
||||
* https://source.android.com/devices/input/key-character-map-files
|
||||
* https://source.android.com/devices/input/key-layout-files
|
||||
|
@ -669,14 +698,14 @@ public final class TerminalView extends View {
|
|||
* AOSP kcm and kl files:
|
||||
* https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/base/data/keyboards
|
||||
* https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/base/packages/InputDevices/res/raw
|
||||
*
|
||||
* <p>
|
||||
* KeyCodes:
|
||||
* https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/base/core/java/android/view/KeyEvent.java
|
||||
* https://cs.android.com/android/platform/superproject/+/master:frameworks/native/include/android/keycodes.h
|
||||
*
|
||||
* <p>
|
||||
* `dumpsys input`:
|
||||
* https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/native/services/inputflinger/reader/EventHub.cpp;l=1917
|
||||
*
|
||||
* <p>
|
||||
* Loading of keymap:
|
||||
* https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/native/services/inputflinger/reader/EventHub.cpp;l=1644
|
||||
* https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/native/libs/input/Keyboard.cpp;l=41
|
||||
|
@ -684,18 +713,18 @@ public final class TerminalView extends View {
|
|||
* OVERLAY keymaps for hardware keyboards may be combined as well:
|
||||
* https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/native/libs/input/KeyCharacterMap.cpp;l=165
|
||||
* https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/native/libs/input/KeyCharacterMap.cpp;l=831
|
||||
*
|
||||
* <p>
|
||||
* Parse kcm file:
|
||||
* https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/native/libs/input/KeyCharacterMap.cpp;l=727
|
||||
* Parse key value:
|
||||
* https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/native/libs/input/KeyCharacterMap.cpp;l=981
|
||||
*
|
||||
* <p>
|
||||
* `KeyEvent.getUnicodeChar()`
|
||||
* https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/base/core/java/android/view/KeyEvent.java;l=2716
|
||||
* https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/KeyCharacterMap.java;l=368
|
||||
* https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/base/core/jni/android_view_KeyCharacterMap.cpp;l=117
|
||||
* https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/native/libs/input/KeyCharacterMap.cpp;l=231
|
||||
*
|
||||
* <p>
|
||||
* Keyboard layouts advertised by applications, like for hardware keyboards via #ACTION_QUERY_KEYBOARD_LAYOUTS
|
||||
* Config is stored in `/data/system/input-manager-state.xml`
|
||||
* https://github.com/ris58h/custom-keyboard-layout
|
||||
|
@ -852,7 +881,9 @@ public final class TerminalView extends View {
|
|||
}
|
||||
}
|
||||
|
||||
/** Input the specified keyCode if applicable and return if the input was consumed. */
|
||||
/**
|
||||
* Input the specified keyCode if applicable and return if the input was consumed.
|
||||
*/
|
||||
public boolean handleKeyCode(int keyCode, int keyMod) {
|
||||
// Ensure cursor is shown when a key is pressed down like long hold on (arrow) keys
|
||||
if (mEmulator != null)
|
||||
|
@ -924,7 +955,9 @@ public final class TerminalView extends View {
|
|||
updateSize();
|
||||
}
|
||||
|
||||
/** Check if the terminal size in rows and columns should be updated. */
|
||||
/**
|
||||
* Check if the terminal size in rows and columns should be updated.
|
||||
*/
|
||||
public void updateSize() {
|
||||
int viewWidth = getWidth();
|
||||
int viewHeight = getHeight();
|
||||
|
@ -978,17 +1011,32 @@ public final class TerminalView extends View {
|
|||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
mRenderer.render(mEmulator, canvas, mTopRow, sel[0], sel[1], sel[2], sel[3]);
|
||||
int selectionY1 = -1;
|
||||
int selectionY2 = -1;
|
||||
int selectionX1 = -1;
|
||||
int selectionX2 = -1;
|
||||
if (mTextSelectionCursorController != null) {
|
||||
selectionY1 = mTextSelectionCursorController.mSelY1;
|
||||
selectionY2 = mTextSelectionCursorController.mSelY2;
|
||||
selectionX1 = mTextSelectionCursorController.mSelX1;
|
||||
selectionX2 = mTextSelectionCursorController.mSelX2;
|
||||
}
|
||||
|
||||
// render the text selection handles
|
||||
renderTextSelection();
|
||||
mRenderer.render(
|
||||
mEmulator,
|
||||
canvas,
|
||||
mTopRow,
|
||||
selectionY1,
|
||||
selectionY2,
|
||||
selectionX1,
|
||||
selectionX2
|
||||
);
|
||||
|
||||
if (mTextSelectionCursorController != null) {
|
||||
mTextSelectionCursorController.render();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1028,7 +1076,6 @@ public final class TerminalView extends View {
|
|||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Define functions required for AutoFill API
|
||||
*/
|
||||
|
@ -1076,11 +1123,6 @@ public final class TerminalView extends View {
|
|||
return getTextSelectionCursorController().hide();
|
||||
}
|
||||
|
||||
private void renderTextSelection() {
|
||||
if (mTextSelectionCursorController != null)
|
||||
mTextSelectionCursorController.render();
|
||||
}
|
||||
|
||||
public boolean isSelectingText() {
|
||||
if (mTextSelectionCursorController != null) {
|
||||
return mTextSelectionCursorController.isActive();
|
||||
|
@ -1089,15 +1131,20 @@ public final class TerminalView extends View {
|
|||
}
|
||||
}
|
||||
|
||||
/** Get the selected text stored before "MORE" button was pressed on the context menu. */
|
||||
/**
|
||||
* Get the selected text stored before "MORE" button was pressed on the context menu.
|
||||
*/
|
||||
@Nullable
|
||||
public String getStoredSelectedText() {
|
||||
return mTextSelectionCursorController != null ? mTextSelectionCursorController.getStoredSelectedText() : null;
|
||||
}
|
||||
|
||||
/** Unset the selected text stored before "MORE" button was pressed on the context menu. */
|
||||
/**
|
||||
* Unset the selected text stored before "MORE" button was pressed on the context menu.
|
||||
*/
|
||||
public void unsetStoredSelectedText() {
|
||||
if (mTextSelectionCursorController != null) mTextSelectionCursorController.unsetStoredSelectedText();
|
||||
if (mTextSelectionCursorController != null)
|
||||
mTextSelectionCursorController.unsetStoredSelectedText();
|
||||
}
|
||||
|
||||
private ActionMode getTextSelectionActionMode() {
|
||||
|
@ -1156,7 +1203,6 @@ public final class TerminalView extends View {
|
|||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Define functions required for long hold toolbar.
|
||||
*/
|
||||
|
|
|
@ -27,7 +27,7 @@ public class TextSelectionCursorController implements CursorController {
|
|||
private long mShowStartTime = System.currentTimeMillis();
|
||||
|
||||
private final int mHandleHeight;
|
||||
private int mSelX1 = -1, mSelX2 = -1, mSelY1 = -1, mSelY2 = -1;
|
||||
public int mSelX1 = -1, mSelX2 = -1, mSelY1 = -1, mSelY2 = -1;
|
||||
|
||||
private ActionMode mActionMode;
|
||||
public final int ACTION_COPY = 1;
|
||||
|
@ -353,16 +353,6 @@ public class TextSelectionCursorController implements CursorController {
|
|||
return mIsSelectingText;
|
||||
}
|
||||
|
||||
public void getSelectors(int[] sel) {
|
||||
if (sel == null || sel.length != 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
sel[0] = mSelY1;
|
||||
sel[1] = mSelY2;
|
||||
sel[2] = mSelX1;
|
||||
sel[3] = mSelX2;
|
||||
}
|
||||
|
||||
/** Get the currently selected text. */
|
||||
public String getSelectedText() {
|
||||
|
|
Loading…
Reference in New Issue