Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb13a5a531 | ||
|
|
d267843e36 | ||
|
|
5ca67dd885 | ||
|
|
6b0d531758 | ||
|
|
1b3283bd69 | ||
|
|
2820f6a7b8 | ||
|
|
8925ae67bb | ||
|
|
4066c5df42 | ||
|
|
20aac6aa72 | ||
|
|
e1f799f9a1 | ||
|
|
4479de4b9b | ||
|
|
3a8f53a54c | ||
|
|
2f04a6186b | ||
|
|
ad64dd7c3d | ||
|
|
dfc4595ec5 | ||
|
|
8c80efb904 | ||
|
|
564079c7e9 | ||
|
|
5b6fd9b88c | ||
|
|
63bfe95848 | ||
|
|
af95a99854 | ||
|
|
25523ae224 | ||
|
|
665adb6895 | ||
|
|
eb7fda28df | ||
|
|
7063a3a9da | ||
|
|
6e02b99ff6 | ||
|
|
9aae665bfc | ||
|
|
52ce6cc94b | ||
|
|
f91168eff4 | ||
|
|
8faa5b2151 | ||
|
|
216cc10f3c |
8
.gitattributes
vendored
8
.gitattributes
vendored
@@ -1,3 +1,5 @@
|
||||
* text=auto
|
||||
*.bat eol=crlf
|
||||
*.sh eol=lf
|
||||
* text=auto
|
||||
*.bat eol=crlf
|
||||
*.gradle eol=lf
|
||||
*.mk eol=lf
|
||||
*.sh eol=lf
|
||||
|
||||
2
.github/FUNDING.yml
vendored
Normal file
2
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
patreon: termux
|
||||
custom: https://paypal.me/fornwall
|
||||
@@ -17,8 +17,8 @@ android {
|
||||
applicationId "com.termux"
|
||||
minSdkVersion project.properties.minSdkVersion.toInteger()
|
||||
targetSdkVersion project.properties.targetSdkVersion.toInteger()
|
||||
versionCode 99
|
||||
versionName "0.99"
|
||||
versionCode 104
|
||||
versionName "0.104"
|
||||
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
@@ -133,11 +133,11 @@ clean {
|
||||
|
||||
task downloadBootstraps(){
|
||||
doLast {
|
||||
def version = 28
|
||||
downloadBootstrap("aarch64", "8f8c8af1d6c50bd0d6ecd5a1df8d5b0c3122a945b6febd0de0308504b4075ed0", version)
|
||||
downloadBootstrap("arm", "179465f403a73fec6534363b11cb38eed3b6b3d8e9d922d8e55dc8a06813240d", version)
|
||||
downloadBootstrap("i686", "2100ebf36befde73b08efaa7e3ccc9c218a8b5e7d69db06e16e364ab8b7dfbd5", version)
|
||||
downloadBootstrap("x86_64", "3f5192f63595426370aa69f102fac07ae00c12154878eddec24e8406e640a67f", version)
|
||||
def version = 34
|
||||
downloadBootstrap("aarch64", "7f56e8ee5573a8351925ff4010c6dea240cd56194ba0b9a686c3d8289859a05", version)
|
||||
downloadBootstrap("arm", "fa41377fb07cac2e4c15f32f3e7ea8116d60efadc1261176acb42646a71637f0", version)
|
||||
downloadBootstrap("i686", "61dcae560e706f808e524b8c3b234c47ea0e2bf6c6b0d5edebe53e6fdb956fe1", version)
|
||||
downloadBootstrap("x86_64", "1b616a4d591809874a06b06f309441320a0b9781e8ef5c148123530303b2533d", version)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
<uses-permission android:name="android.permission.READ_LOGS" />
|
||||
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
|
||||
|
||||
<application
|
||||
android:extractNativeLibs="true"
|
||||
|
||||
@@ -161,7 +161,7 @@ public final class BackgroundJob {
|
||||
environment.add("PATH= " + System.getenv("PATH"));
|
||||
} else {
|
||||
environment.add("LANG=en_US.UTF-8");
|
||||
environment.add("PATH=" + TermuxService.PREFIX_PATH + "/bin:" + TermuxService.PREFIX_PATH + "/bin/applets");
|
||||
environment.add("PATH=" + TermuxService.PREFIX_PATH + "/bin");
|
||||
environment.add("PWD=" + cwd);
|
||||
environment.add("TMPDIR=" + TermuxService.PREFIX_PATH + "/tmp");
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package com.termux.app;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
@@ -8,6 +12,8 @@ import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
import com.termux.R;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
@@ -20,9 +26,21 @@ import java.util.Properties;
|
||||
*
|
||||
* Third-party program must declare com.termux.permission.RUN_COMMAND permission and it should be
|
||||
* granted by user.
|
||||
* Full path of command or script must be given in "RUN_COMMAND_PATH" extra.
|
||||
*
|
||||
* Absolute path of command or script must be given in "RUN_COMMAND_PATH" extra.
|
||||
* The "RUN_COMMAND_ARGUMENTS", "RUN_COMMAND_WORKDIR" and "RUN_COMMAND_BACKGROUND" extras are
|
||||
* optional. The background mode defaults to false.
|
||||
* optional. The workdir defaults to termux home. The background mode defaults to "false".
|
||||
* The command path and workdir can optionally be prefixed with "$PREFIX/" or "~/" if an absolute
|
||||
* path is not to be given.
|
||||
*
|
||||
* To automatically bring to foreground and start termux commands that were started with
|
||||
* background mode "false" in android >= 10 without user having to click the notification manually,
|
||||
* requires termux to be granted draw over apps permission due to new restrictions
|
||||
* of starting activities from the background, this also applies to Termux:Tasker plugin.
|
||||
*
|
||||
* To reduce the chance of termux being killed by android even further due to violation of not
|
||||
* being able to call startForeground() within ~5s of service start in android >= 8, the user
|
||||
* may disable battery optimizations for termux.
|
||||
*
|
||||
* Sample code to run command "top" with java:
|
||||
* Intent intent = new Intent();
|
||||
@@ -50,6 +68,9 @@ public class RunCommandService extends Service {
|
||||
public static final String RUN_COMMAND_WORKDIR = "com.termux.RUN_COMMAND_WORKDIR";
|
||||
public static final String RUN_COMMAND_BACKGROUND = "com.termux.RUN_COMMAND_BACKGROUND";
|
||||
|
||||
private static final String NOTIFICATION_CHANNEL_ID = "termux_run_command_notification_channel";
|
||||
private static final int NOTIFICATION_ID = 1338;
|
||||
|
||||
class LocalBinder extends Binder {
|
||||
public final RunCommandService service = RunCommandService.this;
|
||||
}
|
||||
@@ -61,14 +82,23 @@ public class RunCommandService extends Service {
|
||||
return mBinder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
runStartForeground();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
// Run again in case service is already started and onCreate() is not called
|
||||
runStartForeground();
|
||||
|
||||
if (allowExternalApps() && RUN_COMMAND_ACTION.equals(intent.getAction())) {
|
||||
Uri programUri = new Uri.Builder().scheme("com.termux.file").path(intent.getStringExtra(RUN_COMMAND_PATH)).build();
|
||||
Uri programUri = new Uri.Builder().scheme("com.termux.file").path(parsePath(intent.getStringExtra(RUN_COMMAND_PATH))).build();
|
||||
|
||||
Intent execIntent = new Intent(TermuxService.ACTION_EXECUTE, programUri);
|
||||
execIntent.setClass(this, TermuxService.class);
|
||||
execIntent.putExtra(TermuxService.EXTRA_ARGUMENTS, intent.getStringArrayExtra(RUN_COMMAND_ARGUMENTS));
|
||||
execIntent.putExtra(TermuxService.EXTRA_CURRENT_WORKING_DIRECTORY, intent.getStringExtra(RUN_COMMAND_WORKDIR));
|
||||
execIntent.putExtra(TermuxService.EXTRA_CURRENT_WORKING_DIRECTORY, parsePath(intent.getStringExtra(RUN_COMMAND_WORKDIR)));
|
||||
execIntent.putExtra(TermuxService.EXTRA_EXECUTE_IN_BACKGROUND, intent.getBooleanExtra(RUN_COMMAND_BACKGROUND, false));
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
@@ -78,9 +108,56 @@ public class RunCommandService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
runStopForeground();
|
||||
|
||||
return Service.START_NOT_STICKY;
|
||||
}
|
||||
|
||||
private void runStartForeground() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
setupNotificationChannel();
|
||||
startForeground(NOTIFICATION_ID, buildNotification());
|
||||
}
|
||||
}
|
||||
|
||||
private void runStopForeground() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
stopForeground(true);
|
||||
}
|
||||
}
|
||||
|
||||
private Notification buildNotification() {
|
||||
Notification.Builder builder = new Notification.Builder(this);
|
||||
builder.setContentTitle(getText(R.string.application_name) + " Run Command");
|
||||
builder.setSmallIcon(R.drawable.ic_service_notification);
|
||||
|
||||
// Use a low priority:
|
||||
builder.setPriority(Notification.PRIORITY_LOW);
|
||||
|
||||
// No need to show a timestamp:
|
||||
builder.setShowWhen(false);
|
||||
|
||||
// Background color for small notification icon:
|
||||
builder.setColor(0xFF607D8B);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
builder.setChannelId(NOTIFICATION_CHANNEL_ID);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private void setupNotificationChannel() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
|
||||
|
||||
String channelName = "Termux Run Command";
|
||||
int importance = NotificationManager.IMPORTANCE_LOW;
|
||||
|
||||
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, importance);
|
||||
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
manager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
private boolean allowExternalApps() {
|
||||
File propsFile = new File(TermuxService.HOME_PATH + "/.termux/termux.properties");
|
||||
if (!propsFile.exists())
|
||||
@@ -99,4 +176,14 @@ public class RunCommandService extends Service {
|
||||
|
||||
return props.getProperty("allow-external-apps", "false").equals("true");
|
||||
}
|
||||
|
||||
/** Replace "$PREFIX/" or "~/" prefix with termux absolute paths */
|
||||
private String parsePath(String path) {
|
||||
if(path != null && !path.isEmpty()) {
|
||||
path = path.replaceAll("^\\$PREFIX\\/", TermuxService.PREFIX_PATH + "/");
|
||||
path = path.replaceAll("^~\\/", TermuxService.HOME_PATH + "/");
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.view.autofill.AutofillManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.EditText;
|
||||
@@ -92,6 +93,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
||||
private static final int CONTEXTMENU_STYLING_ID = 6;
|
||||
private static final int CONTEXTMENU_HELP_ID = 8;
|
||||
private static final int CONTEXTMENU_TOGGLE_KEEP_SCREEN_ON = 9;
|
||||
private static final int CONTEXTMENU_AUTOFILL_ID = 10;
|
||||
|
||||
private static final int MAX_SESSIONS = 8;
|
||||
|
||||
@@ -655,6 +657,12 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
||||
|
||||
menu.add(Menu.NONE, CONTEXTMENU_SELECT_URL_ID, Menu.NONE, R.string.select_url);
|
||||
menu.add(Menu.NONE, CONTEXTMENU_SHARE_TRANSCRIPT_ID, Menu.NONE, R.string.select_all_and_share);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
AutofillManager autofillManager = getSystemService(AutofillManager.class);
|
||||
if (autofillManager != null && autofillManager.isEnabled()) {
|
||||
menu.add(Menu.NONE, CONTEXTMENU_AUTOFILL_ID, Menu.NONE, R.string.autofill_password);
|
||||
}
|
||||
}
|
||||
menu.add(Menu.NONE, CONTEXTMENU_RESET_TERMINAL_ID, Menu.NONE, R.string.reset_terminal);
|
||||
menu.add(Menu.NONE, CONTEXTMENU_KILL_PROCESS_ID, Menu.NONE, getResources().getString(R.string.kill_process, getCurrentTermSession().getPid())).setEnabled(currentSession.isRunning());
|
||||
menu.add(Menu.NONE, CONTEXTMENU_STYLING_ID, Menu.NONE, R.string.style_terminal);
|
||||
@@ -870,6 +878,14 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case CONTEXTMENU_AUTOFILL_ID: {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
AutofillManager autofillManager = getSystemService(AutofillManager.class);
|
||||
if (autofillManager != null && autofillManager.isEnabled()) {
|
||||
autofillManager.requestAutofill(mTerminalView);
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
return super.onContextItemSelected(item);
|
||||
}
|
||||
|
||||
@@ -91,9 +91,7 @@ public class TermuxDocumentsProvider extends DocumentsProvider {
|
||||
final MatrixCursor result = new MatrixCursor(projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION);
|
||||
final File parent = getFileForDocId(parentDocumentId);
|
||||
for (File file : parent.listFiles()) {
|
||||
if (!file.getName().startsWith(".")) {
|
||||
includeFile(result, null, file);
|
||||
}
|
||||
includeFile(result, null, file);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -177,8 +175,7 @@ public class TermuxDocumentsProvider extends DocumentsProvider {
|
||||
} catch (IOException e) {
|
||||
isInsideHome = true;
|
||||
}
|
||||
final boolean isHidden = file.getName().startsWith(".");
|
||||
if (isInsideHome && !isHidden) {
|
||||
if (isInsideHome) {
|
||||
if (file.isDirectory()) {
|
||||
Collections.addAll(pending, file.listFiles());
|
||||
} else {
|
||||
|
||||
@@ -19,7 +19,9 @@
|
||||
android:layout_marginLeft="3dp"
|
||||
android:focusableInTouchMode="true"
|
||||
android:scrollbarThumbVertical="@drawable/terminal_scroll_shape"
|
||||
android:scrollbars="vertical" />
|
||||
android:scrollbars="vertical"
|
||||
android:importantForAutofill="no"
|
||||
android:autofillHints="password" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/left_drawer"
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<string name="share_transcript_title">Terminal transcript</string>
|
||||
<string name="help">Help</string>
|
||||
<string name="toggle_keep_screen_on">Keep screen on</string>
|
||||
<string name="autofill_password">Autofill password</string>
|
||||
|
||||
<string name="bootstrap_installer_body">Installing…</string>
|
||||
<string name="bootstrap_error_title">Unable to install</string>
|
||||
|
||||
1
gradle/wrapper/gradle-wrapper.properties
vendored
1
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
|
||||
distributionSha256Sum=7873ed5287f47ca03549ab8dcb6dc877ac7f0e3d7b1eb12685161d10080910ac
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -748,7 +748,7 @@ public final class TerminalEmulator {
|
||||
value = (mScreen == mAltBuffer) ? 1 : 2;
|
||||
} else {
|
||||
int internalBit = mapDecSetBitToInternalBit(mode);
|
||||
if (internalBit == -1) {
|
||||
if (internalBit != -1) {
|
||||
value = isDecsetInternalBitSet(internalBit) ? 1 : 2; // 1=set, 2=reset.
|
||||
} else {
|
||||
Log.e(EmulatorDebug.LOG_TAG, "Got DECRQM for unrecognized private DEC mode=" + mode);
|
||||
|
||||
@@ -31,12 +31,15 @@ import android.view.ViewParent;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.WindowManager;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.autofill.AutofillValue;
|
||||
import android.view.inputmethod.BaseInputConnection;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.widget.PopupWindow;
|
||||
import android.widget.Scroller;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import com.termux.terminal.EmulatorDebug;
|
||||
import com.termux.terminal.KeyHandler;
|
||||
import com.termux.terminal.TerminalBuffer;
|
||||
@@ -1540,4 +1543,24 @@ public final class TerminalView extends View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
@Override
|
||||
public void autofill(AutofillValue value) {
|
||||
if (value.isText()) {
|
||||
mTermSession.write(value.getTextValue().toString());
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
@Override
|
||||
public int getAutofillType() {
|
||||
return AUTOFILL_TYPE_TEXT;
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
@Override
|
||||
public AutofillValue getAutofillValue() {
|
||||
return AutofillValue.forText("");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user