mirror of
https://github.com/fankes/termux-app.git
synced 2025-09-07 11:09:49 +08:00
Work on Termux:Float input handling
This commit is contained in:
@@ -0,0 +1,214 @@
|
|||||||
|
package com.termux.window;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.media.AudioManager;
|
||||||
|
import android.view.InputDevice;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
|
||||||
|
import com.termux.terminal.KeyHandler;
|
||||||
|
import com.termux.terminal.TerminalEmulator;
|
||||||
|
import com.termux.terminal.TerminalSession;
|
||||||
|
import com.termux.view.TerminalKeyListener;
|
||||||
|
|
||||||
|
public class TermuxFloatKeyListener implements TerminalKeyListener {
|
||||||
|
|
||||||
|
private final TermuxFloatView view;
|
||||||
|
/** Keeping track of the special keys acting as Ctrl and Fn for the soft keyboard and other hardware keys. */
|
||||||
|
boolean mVirtualControlKeyDown, mVirtualFnKeyDown;
|
||||||
|
|
||||||
|
public TermuxFloatKeyListener(TermuxFloatView view) {
|
||||||
|
this.view = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float onScale(float scale) {
|
||||||
|
if (scale < 0.9f || scale > 1.1f) {
|
||||||
|
boolean increase = scale > 1.f;
|
||||||
|
((TermuxFloatService) view.getContext()).changeFontSize(increase);
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
return scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onLongPress(MotionEvent event) {
|
||||||
|
view.updateLongPressMode(true);
|
||||||
|
view.initialX = view.layoutParams.x;
|
||||||
|
view.initialY = view.layoutParams.y;
|
||||||
|
view.initialTouchX = event.getRawX();
|
||||||
|
view.initialTouchY = event.getRawY();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSingleTapUp(MotionEvent e) {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldBackButtonBeMappedToEscape() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void copyModeChanged(boolean copyMode) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onKeyDown(int keyCode, KeyEvent e, TerminalSession session) {
|
||||||
|
if (handleVirtualKeys(keyCode, e, true)) return true;
|
||||||
|
|
||||||
|
if (e.isCtrlPressed() && e.isAltPressed()) {
|
||||||
|
// Get the unmodified code point:
|
||||||
|
int unicodeChar = e.getUnicodeChar(0);
|
||||||
|
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN || unicodeChar == 'n'/* next */) {
|
||||||
|
// TODO: Toggle minimized or not.
|
||||||
|
} else if (unicodeChar == 'f'/* full screen */) {
|
||||||
|
// TODO: Toggle full screen.
|
||||||
|
} else if (unicodeChar == 'k'/* keyboard */) {
|
||||||
|
InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onKeyUp(int keyCode, KeyEvent e) {
|
||||||
|
return handleVirtualKeys(keyCode, e, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean readControlKey() {
|
||||||
|
return mVirtualControlKeyDown;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean readAltKey() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCodePoint(int codePoint, boolean ctrlDown, TerminalSession session) {
|
||||||
|
if (mVirtualFnKeyDown) {
|
||||||
|
int resultingKeyCode = -1;
|
||||||
|
int resultingCodePoint = -1;
|
||||||
|
boolean altDown = false;
|
||||||
|
int lowerCase = Character.toLowerCase(codePoint);
|
||||||
|
switch (lowerCase) {
|
||||||
|
// Arrow keys.
|
||||||
|
case 'w':
|
||||||
|
resultingKeyCode = KeyEvent.KEYCODE_DPAD_UP;
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
resultingKeyCode = KeyEvent.KEYCODE_DPAD_LEFT;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
resultingKeyCode = KeyEvent.KEYCODE_DPAD_DOWN;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
resultingKeyCode = KeyEvent.KEYCODE_DPAD_RIGHT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Page up and down.
|
||||||
|
case 'p':
|
||||||
|
resultingKeyCode = KeyEvent.KEYCODE_PAGE_UP;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
resultingKeyCode = KeyEvent.KEYCODE_PAGE_DOWN;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Some special keys:
|
||||||
|
case 't':
|
||||||
|
resultingKeyCode = KeyEvent.KEYCODE_TAB;
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
resultingKeyCode = KeyEvent.KEYCODE_INSERT;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
resultingCodePoint = '~';
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Special characters to input.
|
||||||
|
case 'u':
|
||||||
|
resultingCodePoint = '_';
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
resultingCodePoint = '|';
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Function keys.
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
resultingKeyCode = (codePoint - '1') + KeyEvent.KEYCODE_F1;
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
resultingKeyCode = KeyEvent.KEYCODE_F10;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Other special keys.
|
||||||
|
case 'e':
|
||||||
|
resultingCodePoint = /*Escape*/ 27;
|
||||||
|
break;
|
||||||
|
case '.':
|
||||||
|
resultingCodePoint = /*^.*/ 28;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'b': // alt+b, jumping backward in readline.
|
||||||
|
case 'f': // alf+f, jumping forward in readline.
|
||||||
|
case 'x': // alt+x, common in emacs.
|
||||||
|
resultingCodePoint = lowerCase;
|
||||||
|
altDown = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Volume control.
|
||||||
|
case 'v':
|
||||||
|
resultingCodePoint = -1;
|
||||||
|
AudioManager audio = (AudioManager) view.getContext().getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
audio.adjustSuggestedStreamVolume(AudioManager.ADJUST_SAME, AudioManager.USE_DEFAULT_STREAM_TYPE, AudioManager.FLAG_SHOW_UI);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultingKeyCode != -1) {
|
||||||
|
TerminalEmulator term = session.getEmulator();
|
||||||
|
session.write(KeyHandler.getCode(resultingKeyCode, 0, term.isCursorKeysApplicationMode(), term.isKeypadApplicationMode()));
|
||||||
|
} else if (resultingCodePoint != -1) {
|
||||||
|
session.writeCodePoint(altDown, resultingCodePoint);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Handle dedicated volume buttons as virtual keys if applicable. */
|
||||||
|
private boolean handleVirtualKeys(int keyCode, KeyEvent event, boolean down) {
|
||||||
|
InputDevice inputDevice = event.getDevice();
|
||||||
|
if (inputDevice != null && inputDevice.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) {
|
||||||
|
// Do not steal dedicated buttons from a full external keyboard.
|
||||||
|
return false;
|
||||||
|
} else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
|
||||||
|
mVirtualControlKeyDown = down;
|
||||||
|
return true;
|
||||||
|
} else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
|
||||||
|
mVirtualFnKeyDown = down;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,16 +1,11 @@
|
|||||||
package com.termux.window;
|
package com.termux.window;
|
||||||
|
|
||||||
import com.termux.terminal.TerminalSession;
|
|
||||||
import com.termux.view.TerminalKeyListener;
|
|
||||||
import com.termux.view.TerminalView;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.PixelFormat;
|
import android.graphics.PixelFormat;
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.KeyEvent;
|
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.ScaleGestureDetector;
|
import android.view.ScaleGestureDetector;
|
||||||
import android.view.ScaleGestureDetector.OnScaleGestureListener;
|
import android.view.ScaleGestureDetector.OnScaleGestureListener;
|
||||||
@@ -19,6 +14,8 @@ import android.view.inputmethod.InputMethodManager;
|
|||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.termux.view.TerminalView;
|
||||||
|
|
||||||
public class TermuxFloatView extends LinearLayout {
|
public class TermuxFloatView extends LinearLayout {
|
||||||
|
|
||||||
public static final float ALPHA_FOCUS = 0.9f;
|
public static final float ALPHA_FOCUS = 0.9f;
|
||||||
@@ -37,13 +34,15 @@ public class TermuxFloatView extends LinearLayout {
|
|||||||
Toast mLastToast;
|
Toast mLastToast;
|
||||||
|
|
||||||
private boolean withFocus = true;
|
private boolean withFocus = true;
|
||||||
private int initialX;
|
int initialX;
|
||||||
private int initialY;
|
int initialY;
|
||||||
private float initialTouchX;
|
float initialTouchX;
|
||||||
private float initialTouchY;
|
float initialTouchY;
|
||||||
|
|
||||||
boolean isInLongPressState;
|
boolean isInLongPressState;
|
||||||
|
|
||||||
|
final int[] location = new int[2];
|
||||||
|
|
||||||
final ScaleGestureDetector mScaleDetector = new ScaleGestureDetector(getContext(), new OnScaleGestureListener() {
|
final ScaleGestureDetector mScaleDetector = new ScaleGestureDetector(getContext(), new OnScaleGestureListener() {
|
||||||
private static final int MIN_SIZE = 50;
|
private static final int MIN_SIZE = 50;
|
||||||
|
|
||||||
@@ -86,69 +85,7 @@ public class TermuxFloatView extends LinearLayout {
|
|||||||
|
|
||||||
public void initializeFloatingWindow() {
|
public void initializeFloatingWindow() {
|
||||||
mTerminalView = (TerminalView) findViewById(R.id.terminal_view);
|
mTerminalView = (TerminalView) findViewById(R.id.terminal_view);
|
||||||
|
mTerminalView.setOnKeyListener(new TermuxFloatKeyListener(this));
|
||||||
mTerminalView.setOnKeyListener(new TerminalKeyListener() {
|
|
||||||
@Override
|
|
||||||
public float onScale(float scale) {
|
|
||||||
if (scale < 0.9f || scale > 1.1f) {
|
|
||||||
boolean increase = scale > 1.f;
|
|
||||||
((TermuxFloatService) getContext()).changeFontSize(increase);
|
|
||||||
return 1.0f;
|
|
||||||
}
|
|
||||||
return scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onLongPress(MotionEvent event) {
|
|
||||||
updateLongPressMode(true);
|
|
||||||
initialX = layoutParams.x;
|
|
||||||
initialY = layoutParams.y;
|
|
||||||
initialTouchX = event.getRawX();
|
|
||||||
initialTouchY = event.getRawY();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSingleTapUp(MotionEvent e) {
|
|
||||||
// Do nothing.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldBackButtonBeMappedToEscape() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void copyModeChanged(boolean copyMode) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onKeyDown(int keyCode, KeyEvent e, TerminalSession session) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onKeyUp(int keyCode, KeyEvent e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean readControlKey() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean readAltKey() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCodePoint(int codePoint, boolean ctrlDown, TerminalSession session) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -185,32 +122,29 @@ public class TermuxFloatView extends LinearLayout {
|
|||||||
@Override
|
@Override
|
||||||
public boolean onInterceptTouchEvent(MotionEvent event) {
|
public boolean onInterceptTouchEvent(MotionEvent event) {
|
||||||
if (isInLongPressState) return true;
|
if (isInLongPressState) return true;
|
||||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
|
||||||
if ((event.getMetaState() & (KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON)) != 0) {
|
getLocationOnScreen(location);
|
||||||
updateLongPressMode(true);
|
int x = layoutParams.x; // location[0];
|
||||||
initialX = layoutParams.x;
|
int y = layoutParams.y; // location[1];
|
||||||
initialY = layoutParams.y;
|
float touchX = event.getRawX();
|
||||||
initialTouchX = event.getRawX();
|
float touchY = event.getRawY();
|
||||||
initialTouchY = event.getRawY();
|
boolean clickedInside = (touchX >= x) && (touchX <= (x + layoutParams.width)) && (touchY >= y) && (touchY <= (y + layoutParams.height));
|
||||||
return true;
|
|
||||||
}
|
switch (event.getAction()) {
|
||||||
// FIXME: params.x and params.y are outdated when snapping to end of screen, where the movement stops but x
|
case MotionEvent.ACTION_DOWN:
|
||||||
// and y are wrong.
|
if (!clickedInside) changeFocus(false);
|
||||||
float touchX = event.getRawX();
|
break;
|
||||||
float touchY = event.getRawY();
|
case MotionEvent.ACTION_UP:
|
||||||
boolean clickedInside = (touchX >= layoutParams.x) && (touchX <= (layoutParams.x + layoutParams.width)) && (touchY >= layoutParams.y)
|
if (clickedInside) {
|
||||||
&& (touchY <= (layoutParams.y + layoutParams.height));
|
changeFocus(true);
|
||||||
if (withFocus != clickedInside) {
|
showTouchKeyboard();
|
||||||
changeFocus(clickedInside);
|
}
|
||||||
} else if (clickedInside) {
|
break;
|
||||||
// When clicking inside, show keyboard if the user has hidden it:
|
}
|
||||||
showTouchKeyboard();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showTouchKeyboard() {
|
void showTouchKeyboard() {
|
||||||
mTerminalView.post(new Runnable() {
|
mTerminalView.post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -219,10 +153,10 @@ public class TermuxFloatView extends LinearLayout {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateLongPressMode(boolean newValue) {
|
void updateLongPressMode(boolean newValue) {
|
||||||
isInLongPressState = newValue;
|
isInLongPressState = newValue;
|
||||||
setBackgroundResource(newValue ? R.drawable.floating_window_background_resize : R.drawable.floating_window_background);
|
setBackgroundResource(newValue ? R.drawable.floating_window_background_resize : R.drawable.floating_window_background);
|
||||||
setAlpha(newValue ? ALPHA_MOVING : ALPHA_FOCUS);
|
setAlpha(newValue ? ALPHA_MOVING : (withFocus ? ALPHA_FOCUS : ALPHA_NOT_FOCUS));
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
Toast toast = Toast.makeText(getContext(), R.string.after_long_press, Toast.LENGTH_SHORT);
|
Toast toast = Toast.makeText(getContext(), R.string.after_long_press, Toast.LENGTH_SHORT);
|
||||||
toast.setGravity(Gravity.CENTER, 0, 0);
|
toast.setGravity(Gravity.CENTER, 0, 0);
|
||||||
@@ -254,12 +188,15 @@ public class TermuxFloatView extends LinearLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Visually indicate focus and show the soft input as needed. */
|
/** Visually indicate focus and show the soft input as needed. */
|
||||||
private void changeFocus(boolean newFocus) {
|
void changeFocus(boolean newFocus) {
|
||||||
withFocus = newFocus;
|
if (newFocus == withFocus) {
|
||||||
|
if (newFocus) showTouchKeyboard();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
withFocus = newFocus;
|
||||||
layoutParams.flags = computeLayoutFlags(withFocus);
|
layoutParams.flags = computeLayoutFlags(withFocus);
|
||||||
mWindowManager.updateViewLayout(this, layoutParams);
|
mWindowManager.updateViewLayout(this, layoutParams);
|
||||||
setAlpha(newFocus ? ALPHA_FOCUS : ALPHA_NOT_FOCUS);
|
setAlpha(newFocus ? ALPHA_FOCUS : ALPHA_NOT_FOCUS);
|
||||||
if (newFocus) showTouchKeyboard();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Show a toast and dismiss the last one if still visible. */
|
/** Show a toast and dismiss the last one if still visible. */
|
||||||
|
Reference in New Issue
Block a user