Compare commits

..

26 Commits
v0.61 ... v0.64

Author SHA1 Message Date
Fredrik Fornwall
fdae272214 Bump version to 0.64 2018-07-02 00:05:04 +02:00
Leonid Plyushch
b7864d6ac2 deleteFolder(): check if passed argument is a symlink
Prevents possible data loss when user replaced directory '~/storage' with
a symlink.
2018-07-01 18:00:54 +02:00
Peter Vágner
d1f0c76db3 TerminalView: only use accessibility features when accessibility is
enabled when starting the view
2018-06-29 12:31:15 +02:00
Peter Vágner
5652624fc2 Call setContentDescription in onScreenUpdated rather than in onDraw.
That will be much less expensive.
2018-06-29 12:31:15 +02:00
Peter Vágner
35a9101f84 terminalview: add contentDescription to the view so accessibility
services can get the text currently being shown.
2018-06-29 12:31:15 +02:00
David xu
2b6a10712b fix button background and row height bug in api 21 2018-06-29 12:30:02 +02:00
Matthew Klein
c80e126b8f Add a .gitattributes file 2018-06-28 11:57:12 +02:00
Fredrik Fornwall
89048274dd Bump version to 0.63 2018-06-25 14:08:50 +02:00
Leonid Plyushch
d3b4d35b2a deleteFolder(): don't treat symlinks as directory 2018-06-25 14:08:28 +02:00
Fredrik Fornwall
4080f41cc7 Bump version to 0.62 2018-06-24 01:56:43 +02:00
Tom Yan
b4e2c99d46 Stop exporting PS1
PS1 is not supposed to be an env var and should be shell-specific.
We will set it appropriately with the init files of the shells.
2018-06-22 01:11:51 +02:00
Quasic
c5923201a4 fix indent with vim on termux
web interface would have worked, if I had deleted the tabs, first
This vim is adding tabs instead of matching the previous line's
indentation. Looking into fixing that now...
2018-06-22 00:33:35 +02:00
Quasic
bafd21bb39 fmt indentation 2018-06-22 00:33:35 +02:00
Quasic
0f20fab02c I think this is it 2018-06-22 00:33:35 +02:00
Quasic
c5ae5bb06a oops, should have local Java 2018-06-22 00:33:35 +02:00
Quasic
bbd46a763c Fix for #572 using recursive delete
Also see #578
2018-06-22 00:33:35 +02:00
David xu
dc145d65f8 minify long press interval 2018-06-22 00:29:51 +02:00
David xu
ce2d1c0d88 restore terminalview 2018-06-22 00:29:51 +02:00
David xu
f2f7f963e6 feat for - ― 2018-06-22 00:29:51 +02:00
David xu
2e53ef038e fix popup bug 2018-06-22 00:29:51 +02:00
xqdoo00o
8c82f43dce delete unnecss code 2018-06-22 00:29:51 +02:00
david
3a16f461e7 add symbol popup 2018-06-22 00:29:51 +02:00
xqdoo00o
594a5dfe25 comment unnecss code 2018-06-22 00:29:51 +02:00
david
d5e007dbb3 feat button color 2018-06-22 00:29:51 +02:00
david
f4335d3824 add some functional keys 2018-06-22 00:29:51 +02:00
Michał Bednarski
90b6f93697 Add fast path for TerminalRow.setChar
termux/termux-app#603
2018-06-22 00:21:31 +02:00
8 changed files with 178 additions and 33 deletions

3
.gitattributes vendored Normal file
View File

@@ -0,0 +1,3 @@
* text=auto
*.bat eol=crlf
*.sh eol=lf

View File

@@ -13,8 +13,8 @@ android {
applicationId "com.termux" applicationId "com.termux"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 27 targetSdkVersion 27
versionCode 61 versionCode 64
versionName "0.61" versionName "0.64"
} }
buildTypes { buildTypes {

View File

@@ -111,14 +111,13 @@ public final class BackgroundJob {
final String pathEnv = "PATH=" + System.getenv("PATH"); final String pathEnv = "PATH=" + System.getenv("PATH");
return new String[]{termEnv, homeEnv, prefixEnv, androidRootEnv, androidDataEnv, pathEnv, externalStorageEnv}; return new String[]{termEnv, homeEnv, prefixEnv, androidRootEnv, androidDataEnv, pathEnv, externalStorageEnv};
} else { } else {
final String ps1Env = "PS1=$ ";
final String ldEnv = "LD_LIBRARY_PATH=" + TermuxService.PREFIX_PATH + "/lib"; final String ldEnv = "LD_LIBRARY_PATH=" + TermuxService.PREFIX_PATH + "/lib";
final String langEnv = "LANG=en_US.UTF-8"; final String langEnv = "LANG=en_US.UTF-8";
final String pathEnv = "PATH=" + TermuxService.PREFIX_PATH + "/bin:" + TermuxService.PREFIX_PATH + "/bin/applets"; final String pathEnv = "PATH=" + TermuxService.PREFIX_PATH + "/bin:" + TermuxService.PREFIX_PATH + "/bin/applets";
final String pwdEnv = "PWD=" + cwd; final String pwdEnv = "PWD=" + cwd;
final String tmpdirEnv = "TMPDIR=" + TermuxService.PREFIX_PATH + "/tmp"; final String tmpdirEnv = "TMPDIR=" + TermuxService.PREFIX_PATH + "/tmp";
return new String[]{termEnv, homeEnv, prefixEnv, ps1Env, ldEnv, langEnv, pathEnv, pwdEnv, androidRootEnv, androidDataEnv, externalStorageEnv, tmpdirEnv}; return new String[]{termEnv, homeEnv, prefixEnv, ldEnv, langEnv, pathEnv, pwdEnv, androidRootEnv, androidDataEnv, externalStorageEnv, tmpdirEnv};
} }
} }

View File

@@ -1,13 +1,20 @@
package com.termux.app; package com.termux.app;
import android.content.Context; import android.content.Context;
import android.os.Build;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.Gravity;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ScheduledExecutorService;
import android.view.HapticFeedbackConstants; import android.view.HapticFeedbackConstants;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.widget.Button; import android.widget.Button;
import android.widget.GridLayout; import android.widget.GridLayout;
import android.widget.PopupWindow;
import android.widget.ToggleButton; import android.widget.ToggleButton;
import com.termux.R; import com.termux.R;
@@ -21,6 +28,8 @@ import com.termux.view.TerminalView;
public final class ExtraKeysView extends GridLayout { public final class ExtraKeysView extends GridLayout {
private static final int TEXT_COLOR = 0xFFFFFFFF; private static final int TEXT_COLOR = 0xFFFFFFFF;
private static final int BUTTON_COLOR = 0x00000000;
private static final int BUTTON_PRESSED_COLOR = 0x7FFFFFFF;
public ExtraKeysView(Context context, AttributeSet attrs) { public ExtraKeysView(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
@@ -38,16 +47,28 @@ public final class ExtraKeysView extends GridLayout {
case "TAB": case "TAB":
keyCode = KeyEvent.KEYCODE_TAB; keyCode = KeyEvent.KEYCODE_TAB;
break; break;
case "": case "HOME":
keyCode = KeyEvent.KEYCODE_MOVE_HOME;
break;
case "END":
keyCode = KeyEvent.KEYCODE_MOVE_END;
break;
case "PGUP":
keyCode = KeyEvent.KEYCODE_PAGE_UP;
break;
case "PGDN":
keyCode = KeyEvent.KEYCODE_PAGE_DOWN;
break;
case "":
keyCode = KeyEvent.KEYCODE_DPAD_UP; keyCode = KeyEvent.KEYCODE_DPAD_UP;
break; break;
case "": case "":
keyCode = KeyEvent.KEYCODE_DPAD_LEFT; keyCode = KeyEvent.KEYCODE_DPAD_LEFT;
break; break;
case "": case "":
keyCode = KeyEvent.KEYCODE_DPAD_RIGHT; keyCode = KeyEvent.KEYCODE_DPAD_RIGHT;
break; break;
case "": case "":
keyCode = KeyEvent.KEYCODE_DPAD_DOWN; keyCode = KeyEvent.KEYCODE_DPAD_DOWN;
break; break;
case "": case "":
@@ -57,11 +78,11 @@ public final class ExtraKeysView extends GridLayout {
chars = keyName; chars = keyName;
} }
TerminalView terminalView = view.findViewById(R.id.terminal_view);
if (keyCode > 0) { if (keyCode > 0) {
view.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode)); terminalView.onKeyDown(keyCode, new KeyEvent(KeyEvent.ACTION_UP, keyCode));
view.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyCode)); // view.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyCode));
} else { } else {
TerminalView terminalView = view.findViewById(R.id.terminal_view);
TerminalSession session = terminalView.getCurrentSession(); TerminalSession session = terminalView.getCurrentSession();
if (session != null) session.write(chars); if (session != null) session.write(chars);
} }
@@ -70,6 +91,9 @@ public final class ExtraKeysView extends GridLayout {
private ToggleButton controlButton; private ToggleButton controlButton;
private ToggleButton altButton; private ToggleButton altButton;
private ToggleButton fnButton; private ToggleButton fnButton;
private ScheduledExecutorService scheduledExecutor;
private PopupWindow popupWindow;
private int longPressCount;
public boolean readControlButton() { public boolean readControlButton() {
if (controlButton.isPressed()) return true; if (controlButton.isPressed()) return true;
@@ -101,24 +125,47 @@ public final class ExtraKeysView extends GridLayout {
return result; return result;
} }
void popup(View view, String text) {
int width = view.getMeasuredWidth();
int height = view.getMeasuredHeight();
Button button = new Button(getContext(), null, android.R.attr.buttonBarButtonStyle);
button.setText(text);
button.setTextColor(TEXT_COLOR);
button.setPadding(0, 0, 0, 0);
button.setMinHeight(0);
button.setMinWidth(0);
button.setMinimumWidth(0);
button.setMinimumHeight(0);
button.setWidth(width);
button.setHeight(height);
button.setBackgroundColor(BUTTON_PRESSED_COLOR);
popupWindow = new PopupWindow(this);
popupWindow.setWidth(LayoutParams.WRAP_CONTENT);
popupWindow.setHeight(LayoutParams.WRAP_CONTENT);
popupWindow.setContentView(button);
popupWindow.setOutsideTouchable(true);
popupWindow.setFocusable(false);
popupWindow.showAsDropDown(view, 0, -2 * height);
}
void reload() { void reload() {
altButton = controlButton = null; altButton = controlButton = null;
removeAllViews(); removeAllViews();
String[][] buttons = { String[][] buttons = {
{"ESC", "CTRL", "ALT", "TAB", "", "/", "|"} {"ESC", "/", "", "HOME", "", "END", "PGUP"},
{"TAB", "CTRL", "ALT", "", "", "", "PGDN"}
}; };
final int rows = buttons.length; final int rows = buttons.length;
final int cols = buttons[0].length; final int[] cols = {buttons[0].length, buttons[1].length};
setRowCount(rows); setRowCount(rows);
setColumnCount(cols); setColumnCount(cols[0]);
for (int row = 0; row < rows; row++) { for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) { for (int col = 0; col < cols[row]; col++) {
final String buttonText = buttons[row][col]; final String buttonText = buttons[row][col];
Button button; Button button;
switch (buttonText) { switch (buttonText) {
case "CTRL": case "CTRL":
@@ -140,6 +187,7 @@ public final class ExtraKeysView extends GridLayout {
button.setText(buttonText); button.setText(buttonText);
button.setTextColor(TEXT_COLOR); button.setTextColor(TEXT_COLOR);
button.setPadding(0, 0, 0, 0);
final Button finalButton = button; final Button finalButton = button;
button.setOnClickListener(new OnClickListener() { button.setOnClickListener(new OnClickListener() {
@@ -162,12 +210,73 @@ public final class ExtraKeysView extends GridLayout {
} }
}); });
GridLayout.LayoutParams param = new GridLayout.LayoutParams(); button.setOnTouchListener(new OnTouchListener() {
param.height = param.width = 0; @Override
param.rightMargin = param.topMargin = 0; public boolean onTouch(View v, MotionEvent event) {
param.setGravity(Gravity.LEFT); final View root = getRootView();
float weight = "▲▼◀▶".contains(buttonText) ? 0.7f : 1.f; switch (event.getAction()) {
param.columnSpec = GridLayout.spec(col, GridLayout.FILL, weight); case MotionEvent.ACTION_DOWN:
longPressCount = 0;
v.setBackgroundColor(BUTTON_PRESSED_COLOR);
if ("↑↓←→".contains(buttonText)) {
scheduledExecutor = Executors.newSingleThreadScheduledExecutor();
scheduledExecutor.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
longPressCount++;
sendKey(root, buttonText);
}
}, 400, 80, TimeUnit.MILLISECONDS);
}
return true;
case MotionEvent.ACTION_MOVE:
if ("―/".contains(buttonText)) {
if (popupWindow == null && event.getY() < 0) {
v.setBackgroundColor(BUTTON_COLOR);
String text = "".equals(buttonText) ? "|" : "\\";
popup(v, text);
}
if (popupWindow != null && event.getY() > 0) {
v.setBackgroundColor(BUTTON_PRESSED_COLOR);
popupWindow.dismiss();
popupWindow = null;
}
}
return true;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
v.setBackgroundColor(BUTTON_COLOR);
if (scheduledExecutor != null) {
scheduledExecutor.shutdownNow();
scheduledExecutor = null;
}
if (longPressCount == 0) {
if (popupWindow != null && "―/".contains(buttonText)) {
popupWindow.setContentView(null);
popupWindow.dismiss();
popupWindow = null;
sendKey(root, "".equals(buttonText) ? "|" : "\\");
} else {
v.performClick();
}
}
return true;
default:
return true;
}
}
});
LayoutParams param = new GridLayout.LayoutParams();
param.width = 0;
if(Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP){ //special handle api 21
param.height = (int)(37.5 * getResources().getDisplayMetrics().density + 0.5); // 37.5 equal to R.id.viewpager layout_heihgt / rows in DP
}else{
param.height = 0;
}
param.setMargins(0, 0, 0, 0);
param.columnSpec = GridLayout.spec(col, GridLayout.FILL, 1.f);
param.rowSpec = GridLayout.spec(row, GridLayout.FILL, 1.f); param.rowSpec = GridLayout.spec(row, GridLayout.FILL, 1.f);
button.setLayoutParams(param); button.setLayoutParams(param);

View File

@@ -21,6 +21,7 @@ import com.termux.terminal.EmulatorDebug;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
@@ -208,14 +209,18 @@ final class TermuxInstaller {
Arrays.toString(Build.SUPPORTED_ABIS)); Arrays.toString(Build.SUPPORTED_ABIS));
} }
/** Delete a folder and all its content or throw. */ /** Delete a folder and all its content or throw. Don't follow symlinks. */
static void deleteFolder(File fileOrDirectory) { static void deleteFolder(File fileOrDirectory) throws IOException {
File[] children = fileOrDirectory.listFiles(); if (fileOrDirectory.getCanonicalPath().equals(fileOrDirectory.getAbsolutePath()) && fileOrDirectory.isDirectory()) {
if (children != null) { File[] children = fileOrDirectory.listFiles();
for (File child : children) {
deleteFolder(child); if (children != null) {
for (File child : children) {
deleteFolder(child);
}
} }
} }
if (!fileOrDirectory.delete()) { if (!fileOrDirectory.delete()) {
throw new RuntimeException("Unable to delete " + (fileOrDirectory.isDirectory() ? "directory " : "file ") + fileOrDirectory.getAbsolutePath()); throw new RuntimeException("Unable to delete " + (fileOrDirectory.isDirectory() ? "directory " : "file ") + fileOrDirectory.getAbsolutePath());
} }
@@ -228,9 +233,13 @@ final class TermuxInstaller {
try { try {
File storageDir = new File(TermuxService.HOME_PATH, "storage"); File storageDir = new File(TermuxService.HOME_PATH, "storage");
if (storageDir.exists() && !storageDir.delete()) { if (storageDir.exists()) {
Log.e(LOG_TAG, "Could not delete old $HOME/storage"); try {
return; deleteFolder(storageDir);
} catch (IOException e) {
Log.e(LOG_TAG, "Could not delete old $HOME/storage, " + e.getMessage());
return;
}
} }
if (!storageDir.mkdirs()) { if (!storageDir.mkdirs()) {

View File

@@ -70,7 +70,7 @@
android:id="@+id/viewpager" android:id="@+id/viewpager"
android:visibility="gone" android:visibility="gone"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="40dp" android:layout_height="75dp"
android:background="@android:drawable/screen_background_dark_transparent" android:background="@android:drawable/screen_background_dark_transparent"
android:layout_alignParentBottom="true" /> android:layout_alignParentBottom="true" />
</RelativeLayout> </RelativeLayout>

View File

@@ -21,6 +21,8 @@ public final class TerminalRow {
boolean mLineWrap; boolean mLineWrap;
/** The style bits of each cell in the row. See {@link TextStyle}. */ /** The style bits of each cell in the row. See {@link TextStyle}. */
final long[] mStyle; final long[] mStyle;
/** If this row might contain chars with width != 1, used for deactivating fast path */
boolean mHasNonOneWidthOrSurrogateChars;
/** Construct a blank row (containing only whitespace, ' ') with a specified style. */ /** Construct a blank row (containing only whitespace, ' ') with a specified style. */
public TerminalRow(int columns, long style) { public TerminalRow(int columns, long style) {
@@ -32,6 +34,7 @@ public final class TerminalRow {
/** NOTE: The sourceX2 is exclusive. */ /** NOTE: The sourceX2 is exclusive. */
public void copyInterval(TerminalRow line, int sourceX1, int sourceX2, int destinationX) { public void copyInterval(TerminalRow line, int sourceX1, int sourceX2, int destinationX) {
mHasNonOneWidthOrSurrogateChars |= line.mHasNonOneWidthOrSurrogateChars;
final int x1 = line.findStartOfColumn(sourceX1); final int x1 = line.findStartOfColumn(sourceX1);
final int x2 = line.findStartOfColumn(sourceX2); final int x2 = line.findStartOfColumn(sourceX2);
boolean startingFromSecondHalfOfWideChar = (sourceX1 > 0 && line.wideDisplayCharacterStartingAt(sourceX1 - 1)); boolean startingFromSecondHalfOfWideChar = (sourceX1 > 0 && line.wideDisplayCharacterStartingAt(sourceX1 - 1));
@@ -116,6 +119,7 @@ public final class TerminalRow {
Arrays.fill(mText, ' '); Arrays.fill(mText, ' ');
Arrays.fill(mStyle, style); Arrays.fill(mStyle, style);
mSpaceUsed = (short) mColumns; mSpaceUsed = (short) mColumns;
mHasNonOneWidthOrSurrogateChars = false;
} }
// https://github.com/steven676/Android-Terminal-Emulator/commit/9a47042620bec87617f0b4f5d50568535668fe26 // https://github.com/steven676/Android-Terminal-Emulator/commit/9a47042620bec87617f0b4f5d50568535668fe26
@@ -123,6 +127,17 @@ public final class TerminalRow {
mStyle[columnToSet] = style; mStyle[columnToSet] = style;
final int newCodePointDisplayWidth = WcWidth.width(codePoint); final int newCodePointDisplayWidth = WcWidth.width(codePoint);
// Fast path when we don't have any chars with width != 1
if (!mHasNonOneWidthOrSurrogateChars) {
if (codePoint >= Character.MIN_SUPPLEMENTARY_CODE_POINT || newCodePointDisplayWidth != 1) {
mHasNonOneWidthOrSurrogateChars = true;
} else {
mText[columnToSet] = (char) codePoint;
return;
}
}
final boolean newIsCombining = newCodePointDisplayWidth <= 0; final boolean newIsCombining = newCodePointDisplayWidth <= 0;
boolean wasExtraColForWideChar = (columnToSet > 0) && wideDisplayCharacterStartingAt(columnToSet - 1); boolean wasExtraColForWideChar = (columnToSet > 0) && wideDisplayCharacterStartingAt(columnToSet - 1);

View File

@@ -15,6 +15,7 @@ import android.text.InputType;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log; import android.util.Log;
import android.view.accessibility.AccessibilityManager;
import android.view.ActionMode; import android.view.ActionMode;
import android.view.HapticFeedbackConstants; import android.view.HapticFeedbackConstants;
import android.view.InputDevice; import android.view.InputDevice;
@@ -75,6 +76,8 @@ public final class TerminalView extends View {
/** 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; int mCombiningAccent;
private boolean mAccessibilityEnabled;
public TerminalView(Context context, AttributeSet attributes) { // NO_UCD (unused code) public TerminalView(Context context, AttributeSet attributes) { // NO_UCD (unused code)
super(context, attributes); super(context, attributes);
mGestureRecognizer = new GestureAndScaleRecognizer(context, new GestureAndScaleRecognizer.Listener() { mGestureRecognizer = new GestureAndScaleRecognizer(context, new GestureAndScaleRecognizer.Listener() {
@@ -197,6 +200,8 @@ public final class TerminalView extends View {
} }
}); });
mScroller = new Scroller(context); mScroller = new Scroller(context);
AccessibilityManager am = (AccessibilityManager) context.getSystemService(context.ACCESSIBILITY_SERVICE);
mAccessibilityEnabled = am.isEnabled();
} }
/** /**
@@ -384,6 +389,7 @@ public final class TerminalView extends View {
mEmulator.clearScrollCounter(); mEmulator.clearScrollCounter();
invalidate(); invalidate();
if (mAccessibilityEnabled) setContentDescription(getText());
} }
/** /**
@@ -915,4 +921,8 @@ public final class TerminalView extends View {
return mTermSession; return mTermSession;
} }
private CharSequence getText() {
return mEmulator.getScreen().getSelectedText(0, mTopRow, mEmulator.mColumns, mTopRow +mEmulator.mRows);
}
} }