Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e206121bbc | ||
|
|
deceffad00 | ||
|
|
c19909cef1 | ||
|
|
5b7e40638c | ||
|
|
a3673d1af5 | ||
|
|
d5f9cf85c9 | ||
|
|
549f09573f | ||
|
|
94e5bc86fb | ||
|
|
370ac2bd89 | ||
|
|
7041f41981 | ||
|
|
dd6a21257b | ||
|
|
445da0c4ea | ||
|
|
92980824b1 | ||
|
|
7d42b07a32 | ||
|
|
e502b9169f | ||
|
|
9f2d887ca0 | ||
|
|
e6aacc5f08 | ||
|
|
ff935be54a | ||
|
|
4e76162346 | ||
|
|
5fa4f2647b | ||
|
|
81b5889a26 | ||
|
|
514f59258a | ||
|
|
9f79393aa5 | ||
|
|
e49d514236 | ||
|
|
4e1462326c | ||
|
|
b0f1773a92 | ||
|
|
af9f28c010 | ||
|
|
fef0c66868 | ||
|
|
7a5da83ce2 | ||
|
|
97f01387b9 | ||
|
|
012ff83a06 | ||
|
|
a082c63849 | ||
|
|
8c27084086 | ||
|
|
f4fb45cbd6 | ||
|
|
0605fc4843 | ||
|
|
3bbd61f9d7 |
@@ -14,8 +14,8 @@ android {
|
||||
applicationId "com.termux"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 28
|
||||
versionCode 67
|
||||
versionName "0.67"
|
||||
versionCode 75
|
||||
versionName "0.75"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
@@ -35,3 +35,9 @@ android {
|
||||
dependencies {
|
||||
testImplementation 'junit:junit:4.12'
|
||||
}
|
||||
|
||||
task versionName {
|
||||
doLast {
|
||||
print android.defaultConfig.versionName
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission-sdk-23 android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
|
||||
|
||||
<application
|
||||
android:extractNativeLibs="true"
|
||||
@@ -30,6 +31,7 @@
|
||||
|
||||
<activity
|
||||
android:name="com.termux.app.TermuxActivity"
|
||||
android:label="@string/application_name"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||
android:launchMode="singleTask"
|
||||
android:resizeableActivity="true"
|
||||
@@ -37,25 +39,14 @@
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
<meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="com.termux.app.TermuxFailsafeActivity"
|
||||
android:label="@string/app_failsafe_mode" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="com.termux.app.TermuxHelpActivity"
|
||||
android:exported="false"
|
||||
|
||||
@@ -93,6 +93,13 @@ public final class BackgroundJob {
|
||||
};
|
||||
}
|
||||
|
||||
private static void addToEnvIfPresent(List<String> environment, String name) {
|
||||
String value = System.getenv(name);
|
||||
if (value != null) {
|
||||
environment.add(name + "=" + value);
|
||||
}
|
||||
}
|
||||
|
||||
static String[] buildEnvironment(boolean failSafe, String cwd) {
|
||||
new File(TermuxService.HOME_PATH).mkdirs();
|
||||
|
||||
@@ -103,11 +110,15 @@ public final class BackgroundJob {
|
||||
environment.add("TERM=xterm-256color");
|
||||
environment.add("HOME=" + TermuxService.HOME_PATH);
|
||||
environment.add("PREFIX=" + TermuxService.PREFIX_PATH);
|
||||
environment.add("BOOTCLASSPATH" + System.getenv("BOOTCLASSPATH"));
|
||||
environment.add("ANDROID_ROOT=" + System.getenv("ANDROID_ROOT"));
|
||||
environment.add("ANDROID_DATA=" + System.getenv("ANDROID_DATA"));
|
||||
// EXTERNAL_STORAGE is needed for /system/bin/am to work on at least
|
||||
// Samsung S7 - see https://plus.google.com/110070148244138185604/posts/gp8Lk3aCGp3.
|
||||
environment.add("EXTERNAL_STORAGE=" + System.getenv("EXTERNAL_STORAGE"));
|
||||
// ANDROID_RUNTIME_ROOT and ANDROID_TZDATA_ROOT are required for `am` to run on Android Q
|
||||
addToEnvIfPresent(environment, "ANDROID_RUNTIME_ROOT");
|
||||
addToEnvIfPresent(environment, "ANDROID_TZDATA_ROOT");
|
||||
if (failSafe) {
|
||||
// Keep the default path so that system binaries can be used in the failsafe session.
|
||||
environment.add("PATH= " + System.getenv("PATH"));
|
||||
|
||||
63
app/src/main/java/com/termux/app/BellUtil.java
Normal file
63
app/src/main/java/com/termux/app/BellUtil.java
Normal file
@@ -0,0 +1,63 @@
|
||||
package com.termux.app;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.SystemClock;
|
||||
import android.os.Vibrator;
|
||||
|
||||
public class BellUtil {
|
||||
private static BellUtil instance = null;
|
||||
private static final Object lock = new Object();
|
||||
|
||||
public static BellUtil getInstance(Context context) {
|
||||
if (instance == null) {
|
||||
synchronized (lock) {
|
||||
if (instance == null) {
|
||||
instance = new BellUtil((Vibrator) context.getApplicationContext().getSystemService(Context.VIBRATOR_SERVICE));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
private static final long DURATION = 50;
|
||||
private static final long MIN_PAUSE = 3 * DURATION;
|
||||
|
||||
private final Handler handler = new Handler(Looper.getMainLooper());
|
||||
private long lastBell = 0;
|
||||
private final Runnable bellRunnable;
|
||||
|
||||
private BellUtil(final Vibrator vibrator) {
|
||||
bellRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (vibrator != null) {
|
||||
vibrator.vibrate(DURATION);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public synchronized void doBell() {
|
||||
long now = now();
|
||||
long timeSinceLastBell = now - lastBell;
|
||||
|
||||
if (timeSinceLastBell < 0) {
|
||||
// there is a next bell pending; don't schedule another one
|
||||
} else if (timeSinceLastBell < MIN_PAUSE) {
|
||||
// there was a bell recently, scheudle the next one
|
||||
handler.postDelayed(bellRunnable, MIN_PAUSE - timeSinceLastBell);
|
||||
lastBell = lastBell + MIN_PAUSE;
|
||||
} else {
|
||||
// the last bell was long ago, do it now
|
||||
bellRunnable.run();
|
||||
lastBell = now;
|
||||
}
|
||||
}
|
||||
|
||||
private long now() {
|
||||
return SystemClock.uptimeMillis();
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.termux.app;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
@@ -73,6 +74,18 @@ public final class ExtraKeysView extends GridLayout {
|
||||
put("RIGHT", KeyEvent.KEYCODE_DPAD_RIGHT);
|
||||
put("DOWN", KeyEvent.KEYCODE_DPAD_DOWN);
|
||||
put("ENTER", KeyEvent.KEYCODE_ENTER);
|
||||
put("F1", KeyEvent.KEYCODE_F1);
|
||||
put("F2", KeyEvent.KEYCODE_F2);
|
||||
put("F3", KeyEvent.KEYCODE_F3);
|
||||
put("F4", KeyEvent.KEYCODE_F4);
|
||||
put("F5", KeyEvent.KEYCODE_F5);
|
||||
put("F6", KeyEvent.KEYCODE_F6);
|
||||
put("F7", KeyEvent.KEYCODE_F7);
|
||||
put("F8", KeyEvent.KEYCODE_F8);
|
||||
put("F9", KeyEvent.KEYCODE_F9);
|
||||
put("F10", KeyEvent.KEYCODE_F10);
|
||||
put("F11", KeyEvent.KEYCODE_F11);
|
||||
put("F12", KeyEvent.KEYCODE_F12);
|
||||
}};
|
||||
|
||||
static void sendKey(View view, String keyName) {
|
||||
@@ -115,10 +128,14 @@ public final class ExtraKeysView extends GridLayout {
|
||||
|
||||
if (! state.isOn)
|
||||
return false;
|
||||
|
||||
|
||||
if (state.button == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state.button.isPressed())
|
||||
return true;
|
||||
|
||||
|
||||
if (! state.button.isChecked())
|
||||
return false;
|
||||
|
||||
@@ -334,7 +351,15 @@ public final class ExtraKeysView extends GridLayout {
|
||||
|
||||
final Button finalButton = button;
|
||||
button.setOnClickListener(v -> {
|
||||
finalButton.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
|
||||
if (Settings.System.getInt(getContext().getContentResolver(),
|
||||
Settings.System.HAPTIC_FEEDBACK_ENABLED, 0) != 0) {
|
||||
|
||||
// Depending on DnD settings, value can be >1 but 0 means "disabled".
|
||||
if (Settings.Global.getInt(getContext().getContentResolver(), "zen_mode", 0) < 1) {
|
||||
finalButton.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
|
||||
}
|
||||
}
|
||||
|
||||
View root = getRootView();
|
||||
if(Arrays.asList("CTRL", "ALT", "FN").contains(buttonText)) {
|
||||
ToggleButton self = (ToggleButton) finalButton;
|
||||
|
||||
@@ -24,7 +24,6 @@ import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.Vibrator;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
@@ -83,6 +82,8 @@ import androidx.viewpager.widget.ViewPager;
|
||||
*/
|
||||
public final class TermuxActivity extends Activity implements ServiceConnection {
|
||||
|
||||
public static final String TERMUX_FAILSAFE_SESSION_ACTION = "com.termux.app.failsafe_session";
|
||||
|
||||
private static final int CONTEXTMENU_SELECT_URL_ID = 0;
|
||||
private static final int CONTEXTMENU_SHARE_TRANSCRIPT_ID = 1;
|
||||
private static final int CONTEXTMENU_PASTE_ID = 3;
|
||||
@@ -400,7 +401,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
||||
mBellSoundPool.play(mBellSoundId, 1.f, 1.f, 1, 0, 1.f);
|
||||
break;
|
||||
case TermuxPreferences.BELL_VIBRATE:
|
||||
((Vibrator) getSystemService(VIBRATOR_SERVICE)).vibrate(50);
|
||||
BellUtil.getInstance(TermuxActivity.this).doBell();
|
||||
break;
|
||||
case TermuxPreferences.BELL_IGNORE:
|
||||
// Ignore the bell character.
|
||||
@@ -478,9 +479,8 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
||||
Bundle bundle = getIntent().getExtras();
|
||||
boolean launchFailsafe = false;
|
||||
if (bundle != null) {
|
||||
launchFailsafe = bundle.getBoolean(TermuxFailsafeActivity.TERMUX_FAILSAFE_SESSION_ACTION, false);
|
||||
launchFailsafe = bundle.getBoolean(TERMUX_FAILSAFE_SESSION_ACTION, false);
|
||||
}
|
||||
clearTemporaryDirectory();
|
||||
addNewSession(launchFailsafe, null);
|
||||
} catch (WindowManager.BadTokenException e) {
|
||||
// Activity finished - ignore.
|
||||
@@ -494,8 +494,8 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
||||
Intent i = getIntent();
|
||||
if (i != null && Intent.ACTION_RUN.equals(i.getAction())) {
|
||||
// Android 7.1 app shortcut from res/xml/shortcuts.xml.
|
||||
clearTemporaryDirectory();
|
||||
addNewSession(false, null);
|
||||
boolean failSafe = i.getBooleanExtra(TERMUX_FAILSAFE_SESSION_ACTION, false);
|
||||
addNewSession(failSafe, null);
|
||||
} else {
|
||||
switchToSession(getStoredCurrentSessionOrLast());
|
||||
}
|
||||
@@ -589,8 +589,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
||||
new AlertDialog.Builder(this).setTitle(R.string.max_terminals_reached_title).setMessage(R.string.max_terminals_reached_message)
|
||||
.setPositiveButton(android.R.string.ok, null).show();
|
||||
} else {
|
||||
String executablePath = (failSafe ? "/system/bin/sh" : null);
|
||||
TerminalSession newSession = mTermService.createTermSession(executablePath, null, null, failSafe);
|
||||
TerminalSession newSession = mTermService.createTermSession(null, null, null, failSafe);
|
||||
if (sessionName != null) {
|
||||
newSession.mSessionName = sessionName;
|
||||
}
|
||||
@@ -655,19 +654,86 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
||||
}
|
||||
|
||||
static LinkedHashSet<CharSequence> extractUrls(String text) {
|
||||
// Pattern for recognizing a URL, based off RFC 3986
|
||||
// http://stackoverflow.com/questions/5713558/detect-and-extract-url-from-a-string
|
||||
|
||||
StringBuilder regex_sb = new StringBuilder();
|
||||
|
||||
regex_sb.append("("); // Begin first matching group.
|
||||
regex_sb.append("(?:"); // Begin scheme group.
|
||||
regex_sb.append("dav|"); // The DAV proto.
|
||||
regex_sb.append("dict|"); // The DICT proto.
|
||||
regex_sb.append("dns|"); // The DNS proto.
|
||||
regex_sb.append("file|"); // File path.
|
||||
regex_sb.append("finger|"); // The Finger proto.
|
||||
regex_sb.append("ftp(?:s?)|"); // The FTP proto.
|
||||
regex_sb.append("git|"); // The Git proto.
|
||||
regex_sb.append("gopher|"); // The Gopher proto.
|
||||
regex_sb.append("http(?:s?)|"); // The HTTP proto.
|
||||
regex_sb.append("imap(?:s?)|"); // The IMAP proto.
|
||||
regex_sb.append("irc(?:[6s]?)|"); // The IRC proto.
|
||||
regex_sb.append("ip[fn]s|"); // The IPFS proto.
|
||||
regex_sb.append("ldap(?:s?)|"); // The LDAP proto.
|
||||
regex_sb.append("pop3(?:s?)|"); // The POP3 proto.
|
||||
regex_sb.append("redis(?:s?)|"); // The Redis proto.
|
||||
regex_sb.append("rsync|"); // The Rsync proto.
|
||||
regex_sb.append("rtsp(?:[su]?)|"); // The RTSP proto.
|
||||
regex_sb.append("sftp|"); // The SFTP proto.
|
||||
regex_sb.append("smb(?:s?)|"); // The SAMBA proto.
|
||||
regex_sb.append("smtp(?:s?)|"); // The SMTP proto.
|
||||
regex_sb.append("svn(?:(?:\\+ssh)?)|"); // The Subversion proto.
|
||||
regex_sb.append("tcp|"); // The TCP proto.
|
||||
regex_sb.append("telnet|"); // The Telnet proto.
|
||||
regex_sb.append("tftp|"); // The TFTP proto.
|
||||
regex_sb.append("udp|"); // The UDP proto.
|
||||
regex_sb.append("vnc|"); // The VNC proto.
|
||||
regex_sb.append("ws(?:s?)"); // The Websocket proto.
|
||||
regex_sb.append(")://"); // End scheme group.
|
||||
regex_sb.append(")"); // End first matching group.
|
||||
|
||||
|
||||
// Begin second matching group.
|
||||
regex_sb.append("(");
|
||||
|
||||
// User name and/or password in format 'user:pass@'.
|
||||
regex_sb.append("(?:\\S+(?::\\S*)?@)?");
|
||||
|
||||
// Begin host group.
|
||||
regex_sb.append("(?:");
|
||||
|
||||
// IP address (from http://www.regular-expressions.info/examples.html).
|
||||
regex_sb.append("(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|");
|
||||
|
||||
// Host name or domain.
|
||||
regex_sb.append("(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)(?:(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))?|");
|
||||
|
||||
// Just path. Used in case of 'file://' scheme.
|
||||
regex_sb.append("/(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)");
|
||||
|
||||
// End host group.
|
||||
regex_sb.append(")");
|
||||
|
||||
// Port number.
|
||||
regex_sb.append("(?::\\d{1,5})?");
|
||||
|
||||
// Resource path with optional query string.
|
||||
regex_sb.append("(?:/[a-zA-Z0-9:@%\\-._~!$&()*+,;=?/]*)?");
|
||||
|
||||
// End second matching group.
|
||||
regex_sb.append(")");
|
||||
|
||||
final Pattern urlPattern = Pattern.compile(
|
||||
"(?:^|[\\W])((ht|f)tp(s?)://|www\\.)" + "(([\\w\\-]+\\.)+?([\\w\\-.~]+/?)*" + "[\\p{Alnum}.,%_=?&#\\-+()\\[\\]*$~@!:/{};']*)",
|
||||
regex_sb.toString(),
|
||||
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
|
||||
|
||||
LinkedHashSet<CharSequence> urlSet = new LinkedHashSet<>();
|
||||
Matcher matcher = urlPattern.matcher(text);
|
||||
|
||||
while (matcher.find()) {
|
||||
int matchStart = matcher.start(1);
|
||||
int matchEnd = matcher.end();
|
||||
String url = text.substring(matchStart, matchEnd);
|
||||
urlSet.add(url);
|
||||
}
|
||||
|
||||
return urlSet;
|
||||
}
|
||||
|
||||
@@ -722,7 +788,18 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
||||
if (session != null) {
|
||||
Intent intent = new Intent(Intent.ACTION_SEND);
|
||||
intent.setType("text/plain");
|
||||
intent.putExtra(Intent.EXTRA_TEXT, session.getEmulator().getScreen().getTranscriptText().trim());
|
||||
String transcriptText = session.getEmulator().getScreen().getTranscriptTextWithoutJoinedLines().trim();
|
||||
// See https://github.com/termux/termux-app/issues/1166.
|
||||
final int MAX_LENGTH = 100_000;
|
||||
if (transcriptText.length() > MAX_LENGTH) {
|
||||
int cutOffIndex = transcriptText.length() - MAX_LENGTH;
|
||||
int nextNewlineIndex = transcriptText.indexOf('\n', cutOffIndex);
|
||||
if (nextNewlineIndex != -1 && nextNewlineIndex != transcriptText.length() - 1) {
|
||||
cutOffIndex = nextNewlineIndex + 1;
|
||||
}
|
||||
transcriptText = transcriptText.substring(cutOffIndex).trim();
|
||||
}
|
||||
intent.putExtra(Intent.EXTRA_TEXT, transcriptText);
|
||||
intent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.share_transcript_title));
|
||||
startActivity(Intent.createChooser(intent, getString(R.string.share_transcript_chooser_title)));
|
||||
}
|
||||
@@ -833,18 +910,4 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
||||
}
|
||||
}
|
||||
|
||||
private void clearTemporaryDirectory() {
|
||||
if (mTermService.getSessions().size() == 0 && !mTermService.isWakelockEnabled()) {
|
||||
File termuxTmpDir = new File(TermuxService.PREFIX_PATH + "/tmp");
|
||||
if (termuxTmpDir.exists()) {
|
||||
try {
|
||||
TermuxInstaller.deleteFolder(termuxTmpDir);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
termuxTmpDir.mkdirs();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
package com.termux.app;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
public final class TermuxFailsafeActivity extends Activity {
|
||||
|
||||
public static final String TERMUX_FAILSAFE_SESSION_ACTION = "com.termux.app.failsafe_session";
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
Intent intent = new Intent(TermuxFailsafeActivity.this, TermuxActivity.class);
|
||||
intent.putExtra(TERMUX_FAILSAFE_SESSION_ACTION, true);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
@@ -16,6 +17,7 @@ import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.PowerManager;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import android.widget.ArrayAdapter;
|
||||
|
||||
@@ -112,6 +114,22 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
||||
mWifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, EmulatorDebug.LOG_TAG);
|
||||
mWifiLock.acquire();
|
||||
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
String packageName = getPackageName();
|
||||
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
|
||||
Intent whitelist = new Intent();
|
||||
whitelist.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
|
||||
whitelist.setData(Uri.parse("package:" + packageName));
|
||||
whitelist.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
try {
|
||||
startActivity(whitelist);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Log.e(EmulatorDebug.LOG_TAG, "Failed to call ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateNotification();
|
||||
}
|
||||
} else if (ACTION_UNLOCK_WAKE.equals(action)) {
|
||||
@@ -136,7 +154,8 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
||||
mBackgroundTasks.add(task);
|
||||
updateNotification();
|
||||
} else {
|
||||
TerminalSession newSession = createTermSession(executablePath, arguments, cwd, false);
|
||||
boolean failsafe = intent.getBooleanExtra(TermuxActivity.TERMUX_FAILSAFE_SESSION_ACTION, false);
|
||||
TerminalSession newSession = createTermSession(executablePath, arguments, cwd, failsafe);
|
||||
|
||||
// Transform executable path to session name, e.g. "/bin/do-something.sh" => "do something.sh".
|
||||
if (executablePath != null) {
|
||||
@@ -214,7 +233,7 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
||||
builder.setShowWhen(false);
|
||||
|
||||
// Background color for small notification icon:
|
||||
builder.setColor(0xFF000000);
|
||||
builder.setColor(0xFF607D8B);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
builder.setChannelId(NOTIFICATION_CHANNEL_ID);
|
||||
@@ -237,6 +256,18 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
File termuxTmpDir = new File(TermuxService.PREFIX_PATH + "/tmp");
|
||||
|
||||
if (termuxTmpDir.exists()) {
|
||||
try {
|
||||
TermuxInstaller.deleteFolder(termuxTmpDir.getCanonicalFile());
|
||||
} catch (Exception e) {
|
||||
Log.e(EmulatorDebug.LOG_TAG, "Error while removing file at " + termuxTmpDir.getAbsolutePath(), e);
|
||||
}
|
||||
|
||||
termuxTmpDir.mkdirs();
|
||||
}
|
||||
|
||||
if (mWakeLock != null) mWakeLock.release();
|
||||
if (mWifiLock != null) mWifiLock.release();
|
||||
|
||||
@@ -250,14 +281,6 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
||||
return mTerminalSessions;
|
||||
}
|
||||
|
||||
public boolean isWakelockEnabled() {
|
||||
if (mWakeLock == null) {
|
||||
return false;
|
||||
} else {
|
||||
return mWakeLock.isHeld();
|
||||
}
|
||||
}
|
||||
|
||||
TerminalSession createTermSession(String executablePath, String[] arguments, String cwd, boolean failSafe) {
|
||||
new File(HOME_PATH).mkdirs();
|
||||
|
||||
@@ -267,11 +290,13 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
||||
boolean isLoginShell = false;
|
||||
|
||||
if (executablePath == null) {
|
||||
for (String shellBinary : new String[]{"login", "bash", "zsh"}) {
|
||||
File shellFile = new File(PREFIX_PATH + "/bin/" + shellBinary);
|
||||
if (shellFile.canExecute()) {
|
||||
executablePath = shellFile.getAbsolutePath();
|
||||
break;
|
||||
if (!failSafe) {
|
||||
for (String shellBinary : new String[]{"login", "bash", "zsh"}) {
|
||||
File shellFile = new File(PREFIX_PATH + "/bin/" + shellBinary);
|
||||
if (shellFile.canExecute()) {
|
||||
executablePath = shellFile.getAbsolutePath();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,53 +1,51 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="application_name">Termux</string>
|
||||
<string name="shared_user_label">Termux user</string>
|
||||
<string name="app_failsafe_mode">Termux (failsafe)</string>
|
||||
<string name="new_session">New session</string>
|
||||
<string name="new_session_failsafe">Failsafe</string>
|
||||
<string name="toggle_soft_keyboard">Keyboard</string>
|
||||
<string name="reset_terminal">Reset</string>
|
||||
<string name="style_terminal">Style</string>
|
||||
<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="application_name">Termux</string>
|
||||
<string name="shared_user_label">Termux user</string>
|
||||
<string name="new_session">New session</string>
|
||||
<string name="new_session_failsafe">Failsafe</string>
|
||||
<string name="toggle_soft_keyboard">Keyboard</string>
|
||||
<string name="reset_terminal">Reset</string>
|
||||
<string name="style_terminal">Style</string>
|
||||
<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="bootstrap_installer_body">Installing…</string>
|
||||
<string name="bootstrap_error_title">Unable to install</string>
|
||||
<string name="bootstrap_error_body">Termux was unable to install the bootstrap packages.\n\nCheck your network connection and try again.</string>
|
||||
<string name="bootstrap_error_abort">Abort</string>
|
||||
<string name="bootstrap_error_try_again">Try again</string>
|
||||
<string name="bootstrap_error_not_primary_user_message">Termux can only be installed on the primary user account.</string>
|
||||
<string name="bootstrap_installer_body">Installing…</string>
|
||||
<string name="bootstrap_error_title">Unable to install</string>
|
||||
<string name="bootstrap_error_body">Termux was unable to install the bootstrap packages.\n\nCheck your network connection and try again.</string>
|
||||
<string name="bootstrap_error_abort">Abort</string>
|
||||
<string name="bootstrap_error_try_again">Try again</string>
|
||||
<string name="bootstrap_error_not_primary_user_message">Termux can only be installed on the primary user account.</string>
|
||||
|
||||
<string name="max_terminals_reached_title">Max terminals reached</string>
|
||||
<string name="max_terminals_reached_message">Close down existing ones before creating new.</string>
|
||||
<string name="max_terminals_reached_title">Max terminals reached</string>
|
||||
<string name="max_terminals_reached_message">Close down existing ones before creating new.</string>
|
||||
|
||||
<string name="reset_toast_notification">Terminal reset.</string>
|
||||
<string name="reset_toast_notification">Terminal reset.</string>
|
||||
|
||||
<string name="select_url">Select URL</string>
|
||||
<string name="select_url_dialog_title">Click URL to copy or long press to open</string>
|
||||
<string name="select_all_and_share">Share transcript</string>
|
||||
<string name="select_url_no_found">No URL found in the terminal.</string>
|
||||
<string name="select_url_copied_to_clipboard">URL copied to clipboard</string>
|
||||
<string name="share_transcript_chooser_title">Send text to:</string>
|
||||
<string name="select_url">Select URL</string>
|
||||
<string name="select_url_dialog_title">Click URL to copy or long press to open</string>
|
||||
<string name="select_all_and_share">Share transcript</string>
|
||||
<string name="select_url_no_found">No URL found in the terminal.</string>
|
||||
<string name="select_url_copied_to_clipboard">URL copied to clipboard</string>
|
||||
<string name="share_transcript_chooser_title">Send text to:</string>
|
||||
|
||||
<string name="kill_process">Kill process (%d)</string>
|
||||
<string name="confirm_kill_process">Really kill this session?</string>
|
||||
<string name="kill_process">Kill process (%d)</string>
|
||||
<string name="confirm_kill_process">Really kill this session?</string>
|
||||
|
||||
<string name="session_rename_title">Set session name</string>
|
||||
<string name="session_rename_positive_button">Set</string>
|
||||
<string name="session_new_named_title">New named session</string>
|
||||
<string name="session_new_named_positive_button">Create</string>
|
||||
<string name="session_rename_title">Set session name</string>
|
||||
<string name="session_rename_positive_button">Set</string>
|
||||
<string name="session_new_named_title">New named session</string>
|
||||
<string name="session_new_named_positive_button">Create</string>
|
||||
|
||||
<string name="styling_not_installed">The Termux:Style add-on is not installed.</string>
|
||||
<string name="styling_install">Install</string>
|
||||
<string name="styling_not_installed">The Termux:Style add-on is not installed.</string>
|
||||
<string name="styling_install">Install</string>
|
||||
|
||||
<string name="notification_action_exit">Exit</string>
|
||||
<string name="notification_action_wake_lock">Acquire wakelock</string>
|
||||
<string name="notification_action_wake_unlock">Release wakelock</string>
|
||||
|
||||
<string name="file_received_title">Save file in ~/downloads/</string>
|
||||
<string name="file_received_edit_button">Edit</string>
|
||||
<string name="file_received_open_folder_button">Open folder</string>
|
||||
<string name="notification_action_exit">Exit</string>
|
||||
<string name="notification_action_wake_lock">Acquire wakelock</string>
|
||||
<string name="notification_action_wake_unlock">Release wakelock</string>
|
||||
|
||||
<string name="file_received_title">Save file in ~/downloads/</string>
|
||||
<string name="file_received_edit_button">Edit</string>
|
||||
<string name="file_received_open_folder_button">Open folder</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<shortcuts xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<shortcut
|
||||
android:shortcutId="new_session"
|
||||
android:enabled="true"
|
||||
@@ -11,4 +12,19 @@
|
||||
android:targetPackage="com.termux"
|
||||
android:targetClass="com.termux.app.TermuxActivity"/>
|
||||
</shortcut>
|
||||
|
||||
<shortcut
|
||||
android:shortcutId="new_failsafe_session"
|
||||
android:enabled="true"
|
||||
android:icon="@drawable/ic_new_session"
|
||||
android:shortcutShortLabel="@string/new_session_failsafe"
|
||||
tools:targetApi="n_mr1">
|
||||
<intent
|
||||
android:action="android.intent.action.RUN"
|
||||
android:targetPackage="com.termux"
|
||||
android:targetClass="com.termux.app.TermuxActivity">
|
||||
<extra android:name="com.termux.app.failsafe_session" android:value="true" />
|
||||
</intent>
|
||||
</shortcut>
|
||||
|
||||
</shortcuts>
|
||||
|
||||
@@ -4,7 +4,7 @@ buildscript {
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.3.2'
|
||||
classpath 'com.android.tools.build:gradle:3.5.0'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,6 @@
|
||||
#Sun Aug 25 01:57:11 CEST 2019
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
|
||||
|
||||
@@ -41,7 +41,15 @@ public final class TerminalBuffer {
|
||||
return getSelectedText(0, -getActiveTranscriptRows(), mColumns, mScreenRows).trim();
|
||||
}
|
||||
|
||||
public String getTranscriptTextWithoutJoinedLines() {
|
||||
return getSelectedText(0, -getActiveTranscriptRows(), mColumns, mScreenRows, false).trim();
|
||||
}
|
||||
|
||||
public String getSelectedText(int selX1, int selY1, int selX2, int selY2) {
|
||||
return getSelectedText(selX1, selY1, selX2, selY2, true);
|
||||
}
|
||||
|
||||
public String getSelectedText(int selX1, int selY1, int selX2, int selY2, boolean joinBackLines) {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
final int columns = mColumns;
|
||||
|
||||
@@ -79,7 +87,8 @@ public final class TerminalBuffer {
|
||||
}
|
||||
if (lastPrintingCharIndex != -1)
|
||||
builder.append(line, x1Index, lastPrintingCharIndex - x1Index + 1);
|
||||
if (!rowLineWrap && row < selY2 && row < mScreenRows - 1) builder.append('\n');
|
||||
if ((!joinBackLines || !rowLineWrap)
|
||||
&& row < selY2 && row < mScreenRows - 1) builder.append('\n');
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user