Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89048274dd | ||
|
|
d3b4d35b2a | ||
|
|
4080f41cc7 | ||
|
|
b4e2c99d46 | ||
|
|
c5923201a4 | ||
|
|
bafd21bb39 | ||
|
|
0f20fab02c | ||
|
|
c5ae5bb06a | ||
|
|
bbd46a763c | ||
|
|
dc145d65f8 | ||
|
|
ce2d1c0d88 | ||
|
|
f2f7f963e6 | ||
|
|
2e53ef038e | ||
|
|
8c82f43dce | ||
|
|
3a16f461e7 | ||
|
|
594a5dfe25 | ||
|
|
d5e007dbb3 | ||
|
|
f4335d3824 | ||
|
|
90b6f93697 | ||
|
|
17c4a45212 | ||
|
|
6d9ffb6922 | ||
|
|
8472fce8ba | ||
|
|
69d954a583 | ||
|
|
ec1087d56f | ||
|
|
be6a73d862 | ||
|
|
80c81b274d | ||
|
|
cd9dbac548 | ||
|
|
0c837796f0 | ||
|
|
3417e37e8d | ||
|
|
31ba36e0fa | ||
|
|
c444f1fd28 | ||
|
|
9f36ed06b8 | ||
|
|
c2ab5bcd50 | ||
|
|
48fab33b79 | ||
|
|
8d7a67645b | ||
|
|
186b49d429 | ||
|
|
40a2775f52 | ||
|
|
f802d6001d | ||
|
|
50f66a12da | ||
|
|
d8e6fd21d1 | ||
|
|
0964d83572 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -33,6 +33,8 @@ local.properties
|
|||||||
.idea/scopes/scope_settings.xml
|
.idea/scopes/scope_settings.xml
|
||||||
.idea/vcs.xml
|
.idea/vcs.xml
|
||||||
.idea/dictionaries/
|
.idea/dictionaries/
|
||||||
|
.idea/caches/
|
||||||
|
.idea/codeStyles/
|
||||||
*.iml
|
*.iml
|
||||||
|
|
||||||
# OS-specific files
|
# OS-specific files
|
||||||
|
|||||||
@@ -12,13 +12,14 @@ android:
|
|||||||
components:
|
components:
|
||||||
- platform-tools
|
- platform-tools
|
||||||
- tools
|
- tools
|
||||||
- build-tools-27.0.2
|
- build-tools-27.0.3
|
||||||
- android-27
|
- android-27
|
||||||
- extra-android-m2repository
|
- extra-android-m2repository
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- git clone https://github.com/urho3d/android-ndk.git $HOME/android-ndk
|
- git clone https://github.com/urho3d/android-ndk.git $HOME/android-ndk
|
||||||
- export ANDROID_NDK_HOME=$HOME/android-ndk
|
- export ANDROID_NDK_HOME=$HOME/android-ndk
|
||||||
|
- yes | sdkmanager "platforms;android-27"
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- ./gradlew testDebugUnitTest
|
- ./gradlew testDebugUnitTest
|
||||||
|
|||||||
@@ -2,11 +2,10 @@ apply plugin: 'com.android.application'
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 27
|
compileSdkVersion 27
|
||||||
buildToolsVersion "27.0.2"
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.android.support:support-annotations:27.0.1'
|
implementation 'com.android.support:support-annotations:27.1.1'
|
||||||
implementation "com.android.support:support-core-ui:27.0.1"
|
implementation "com.android.support:support-core-ui:27.1.1"
|
||||||
implementation project(":terminal-view")
|
implementation project(":terminal-view")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14,8 +13,8 @@ android {
|
|||||||
applicationId "com.termux"
|
applicationId "com.termux"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 27
|
targetSdkVersion 27
|
||||||
versionCode 57
|
versionCode 63
|
||||||
versionName "0.57"
|
versionName "0.63"
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
android:name="com.termux.app.TermuxActivity"
|
android:name="com.termux.app.TermuxActivity"
|
||||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
|
android:resizeableActivity="true"
|
||||||
android:windowSoftInputMode="adjustResize|stateAlwaysVisible" >
|
android:windowSoftInputMode="adjustResize|stateAlwaysVisible" >
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
@@ -49,6 +50,7 @@
|
|||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:theme="@android:style/Theme.Material.Light.DarkActionBar"
|
android:theme="@android:style/Theme.Material.Light.DarkActionBar"
|
||||||
android:parentActivityName=".app.TermuxActivity"
|
android:parentActivityName=".app.TermuxActivity"
|
||||||
|
android:resizeableActivity="true"
|
||||||
android:label="@string/application_name" />
|
android:label="@string/application_name" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
@@ -56,6 +58,7 @@
|
|||||||
android:label="@string/application_name"
|
android:label="@string/application_name"
|
||||||
android:taskAffinity="com.termux.filereceiver"
|
android:taskAffinity="com.termux.filereceiver"
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
|
android:resizeableActivity="true"
|
||||||
android:noHistory="true">
|
android:noHistory="true">
|
||||||
<!-- Accept multiple file types when sending. -->
|
<!-- Accept multiple file types when sending. -->
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
|||||||
@@ -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};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,19 @@ package com.termux.app;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
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 = 0xFF000000;
|
||||||
|
private static final int BUTTON_PRESSED_COLOR = 0xFF888888;
|
||||||
|
|
||||||
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,69 @@ 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) {
|
||||||
|
final View root = getRootView();
|
||||||
|
switch (event.getAction()) {
|
||||||
|
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 = param.height = 0;
|
||||||
|
param.setMargins(0, 0, 0, 0);
|
||||||
param.setGravity(Gravity.LEFT);
|
param.setGravity(Gravity.LEFT);
|
||||||
float weight = "▲▼◀▶".contains(buttonText) ? 0.7f : 1.f;
|
param.columnSpec = GridLayout.spec(col, GridLayout.FILL, 1.f);
|
||||||
param.columnSpec = GridLayout.spec(col, GridLayout.FILL, weight);
|
|
||||||
param.rowSpec = GridLayout.spec(row, GridLayout.FILL, 1.f);
|
param.rowSpec = GridLayout.spec(row, GridLayout.FILL, 1.f);
|
||||||
button.setLayoutParams(param);
|
button.setLayoutParams(param);
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -209,11 +210,15 @@ final class TermuxInstaller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Delete a folder and all its content or throw. */
|
/** Delete a folder and all its content or throw. */
|
||||||
static void deleteFolder(File fileOrDirectory) {
|
static void deleteFolder(File fileOrDirectory) throws IOException {
|
||||||
File[] children = fileOrDirectory.listFiles();
|
File[] children = fileOrDirectory.listFiles();
|
||||||
if (children != null) {
|
if (children != null) {
|
||||||
for (File child : children) {
|
for (File child : children) {
|
||||||
deleteFolder(child);
|
if (child.getCanonicalFile().equals(child.getAbsoluteFile())) {
|
||||||
|
deleteFolder(child);
|
||||||
|
} else {
|
||||||
|
child.delete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!fileOrDirectory.delete()) {
|
if (!fileOrDirectory.delete()) {
|
||||||
@@ -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()) {
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ public class TermuxDocumentsProvider extends DocumentsProvider {
|
|||||||
final String name = file.getName();
|
final String name = file.getName();
|
||||||
final int lastDot = name.lastIndexOf('.');
|
final int lastDot = name.lastIndexOf('.');
|
||||||
if (lastDot >= 0) {
|
if (lastDot >= 0) {
|
||||||
final String extension = name.substring(lastDot + 1);
|
final String extension = name.substring(lastDot + 1).toLowerCase();
|
||||||
final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
|
final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
|
||||||
if (mime != null) return mime;
|
if (mime != null) return mime;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
<!-- Screen border. -->
|
<!-- Screen border. -->
|
||||||
<path android:fillColor="#00000000"
|
<path android:fillColor="#00000000"
|
||||||
android:strokeColor="#000"
|
android:strokeColor="#FFF"
|
||||||
android:strokeWidth="3"
|
android:strokeWidth="3"
|
||||||
android:pathData="M7,4
|
android:pathData="M7,4
|
||||||
l34,0
|
l34,0
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Block cursor. -->
|
<!-- Block cursor. -->
|
||||||
<path android:fillColor="#000"
|
<path android:fillColor="#FFF"
|
||||||
android:pathData="M14,14
|
android:pathData="M14,14
|
||||||
l5,0
|
l5,0
|
||||||
l0,10
|
l0,10
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -4,16 +4,14 @@ buildscript {
|
|||||||
google()
|
google()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.0.1'
|
classpath 'com.android.tools.build:gradle:3.1.3'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
|
google()
|
||||||
jcenter()
|
jcenter()
|
||||||
maven {
|
|
||||||
url "https://maven.google.com"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
|||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.3.1-bin.zip
|
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStorePath=wrapper/dists
|
distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ ext {
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 27
|
compileSdkVersion 27
|
||||||
buildToolsVersion "27.0.2"
|
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
|
|||||||
@@ -301,6 +301,11 @@ public final class TerminalEmulator {
|
|||||||
* @param mouseButton one of the MOUSE_* constants of this class.
|
* @param mouseButton one of the MOUSE_* constants of this class.
|
||||||
*/
|
*/
|
||||||
public void sendMouseEvent(int mouseButton, int column, int row, boolean pressed) {
|
public void sendMouseEvent(int mouseButton, int column, int row, boolean pressed) {
|
||||||
|
if (column < 1) column = 1;
|
||||||
|
if (column > mColumns) column = mColumns;
|
||||||
|
if (row < 1) row = 1;
|
||||||
|
if (row > mRows) row = mRows;
|
||||||
|
|
||||||
if (mouseButton == MOUSE_LEFT_BUTTON_MOVED && !isDecsetInternalBitSet(DECSET_BIT_MOUSE_TRACKING_BUTTON_EVENT)) {
|
if (mouseButton == MOUSE_LEFT_BUTTON_MOVED && !isDecsetInternalBitSet(DECSET_BIT_MOUSE_TRACKING_BUTTON_EVENT)) {
|
||||||
// Do not send tracking.
|
// Do not send tracking.
|
||||||
} else if (isDecsetInternalBitSet(DECSET_BIT_MOUSE_PROTOCOL_SGR)) {
|
} else if (isDecsetInternalBitSet(DECSET_BIT_MOUSE_PROTOCOL_SGR)) {
|
||||||
@@ -308,7 +313,7 @@ public final class TerminalEmulator {
|
|||||||
} else {
|
} else {
|
||||||
mouseButton = pressed ? mouseButton : 3; // 3 for release of all buttons.
|
mouseButton = pressed ? mouseButton : 3; // 3 for release of all buttons.
|
||||||
// Clip to screen, and clip to the limits of 8-bit data.
|
// Clip to screen, and clip to the limits of 8-bit data.
|
||||||
boolean out_of_bounds = column < 1 || row < 1 || column > mColumns || row > mRows || column > 255 - 32 || row > 255 - 32;
|
boolean out_of_bounds = column > 255 - 32 || row > 255 - 32;
|
||||||
if (!out_of_bounds) {
|
if (!out_of_bounds) {
|
||||||
byte[] data = {'\033', '[', 'M', (byte) (32 + mouseButton), (byte) (32 + column), (byte) (32 + row)};
|
byte[] data = {'\033', '[', 'M', (byte) (32 + mouseButton), (byte) (32 + column), (byte) (32 + row)};
|
||||||
mSession.write(data, 0, data.length);
|
mSession.write(data, 0, data.length);
|
||||||
@@ -2333,6 +2338,9 @@ public final class TerminalEmulator {
|
|||||||
public void paste(String text) {
|
public void paste(String text) {
|
||||||
// First: Always remove escape key and C1 control characters [0x80,0x9F]:
|
// First: Always remove escape key and C1 control characters [0x80,0x9F]:
|
||||||
text = text.replaceAll("(\u001B|[\u0080-\u009F])", "");
|
text = text.replaceAll("(\u001B|[\u0080-\u009F])", "");
|
||||||
|
// Second: Replace all newlines (\n) or CRLF (\r\n) with carriage returns (\r).
|
||||||
|
text = text.replaceAll("\r?\n", "\r");
|
||||||
|
|
||||||
// Then: Implement bracketed paste mode if enabled:
|
// Then: Implement bracketed paste mode if enabled:
|
||||||
boolean bracketed = isDecsetInternalBitSet(DECSET_BIT_BRACKETED_PASTE_MODE);
|
boolean bracketed = isDecsetInternalBitSet(DECSET_BIT_BRACKETED_PASTE_MODE);
|
||||||
if (bracketed) mSession.write("\033[200~");
|
if (bracketed) mSession.write("\033[200~");
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -55,6 +55,14 @@ public class TerminalTest extends TerminalTestCase {
|
|||||||
assertEquals("\033[<0;3;4M", mOutput.getOutputAndClear());
|
assertEquals("\033[<0;3;4M", mOutput.getOutputAndClear());
|
||||||
mTerminal.sendMouseEvent(TerminalEmulator.MOUSE_LEFT_BUTTON, 3, 4, false);
|
mTerminal.sendMouseEvent(TerminalEmulator.MOUSE_LEFT_BUTTON, 3, 4, false);
|
||||||
assertEquals("\033[<0;3;4m", mOutput.getOutputAndClear());
|
assertEquals("\033[<0;3;4m", mOutput.getOutputAndClear());
|
||||||
|
|
||||||
|
// When the client says that a click is outside (which could happen when pixels are outside
|
||||||
|
// the terminal area, see https://github.com/termux/termux-app/issues/501) the terminal
|
||||||
|
// sends a click at the edge.
|
||||||
|
mTerminal.sendMouseEvent(TerminalEmulator.MOUSE_LEFT_BUTTON, 0, 0, true);
|
||||||
|
assertEquals("\033[<0;1;1M", mOutput.getOutputAndClear());
|
||||||
|
mTerminal.sendMouseEvent(TerminalEmulator.MOUSE_LEFT_BUTTON, 11, 11, false);
|
||||||
|
assertEquals("\033[<0;10;10m", mOutput.getOutputAndClear());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNormalization() throws UnsupportedEncodingException {
|
public void testNormalization() throws UnsupportedEncodingException {
|
||||||
|
|||||||
@@ -18,10 +18,9 @@ ext {
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 27
|
compileSdkVersion 27
|
||||||
buildToolsVersion "27.0.2"
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.android.support:support-annotations:27.0.0'
|
implementation 'com.android.support:support-annotations:27.1.1'
|
||||||
api project(":terminal-emulator")
|
api project(":terminal-emulator")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import com.termux.terminal.WcWidth;
|
|||||||
* <p/>
|
* <p/>
|
||||||
* Saves font metrics, so needs to be recreated each time the typeface or font size changes.
|
* Saves font metrics, so needs to be recreated each time the typeface or font size changes.
|
||||||
*/
|
*/
|
||||||
final class TerminalRenderer {
|
public final class TerminalRenderer {
|
||||||
|
|
||||||
final int mTextSize;
|
final int mTextSize;
|
||||||
final Typeface mTypeface;
|
final Typeface mTypeface;
|
||||||
|
|||||||
Reference in New Issue
Block a user