Compare commits

...

25 Commits
v0.70 ... v0.75

Author SHA1 Message Date
Fredrik Fornwall
e206121bbc Bump version to 0.75 2019-09-08 21:43:52 +02:00
Fredrik Fornwall
deceffad00 Update gradle configuration 2019-09-08 21:43:07 +02:00
Leonid Plyushch
c19909cef1 improve url matching regex
Now it support complex URLs and some other schemes beyond just http/ftp.
2019-09-08 20:10:26 +02:00
Leonid Plyushch
5b7e40638c handle possible ActivityNotFoundException when requesting to disable battery optimizations
Related issue: https://github.com/termux/termux-app/issues/1224
2019-09-08 20:10:15 +02:00
Leonid Plyushch
a3673d1af5 extra keys: follow DnD mode and haptic feedback Android settings
Do not vibrate when:

 * Do not disturb mode is on.

 * Haptic feedback is disabled in Android settings.
2019-09-08 20:08:16 +02:00
Fredrik Fornwall
d5f9cf85c9 Bump version to 0.74 2019-08-18 22:11:21 +02:00
hungphd
549f09573f Change method name 'with' to 'getInstance' 2019-08-18 20:30:34 +02:00
Daniil Gentili
94e5bc86fb Ignore battery optimization settings to prevent suspension 2019-08-18 20:23:00 +02:00
Fredrik Fornwall
370ac2bd89 Avoid joining lines for shared transcript (#1166) 2019-08-04 19:42:36 +02:00
Fredrik Fornwall
7041f41981 Bump version to 0.73 2019-08-04 17:03:41 +02:00
Fredrik Fornwall
dd6a21257b Update the Android Gradle plugin 2019-08-04 16:44:58 +02:00
Fredrik Fornwall
445da0c4ea Fix problem with sharing large terminal transcript
Fixes #1166.
2019-07-14 20:26:16 +02:00
Sacha Chua
92980824b1 Add function keys F1 to F12 as keyboard options 2019-07-04 23:27:13 +02:00
Michal Bednarski
7d42b07a32 Also export ANDROID_TZDATA_ROOT
Since Android Q Beta 2 ANDROID_TZDATA_ROOT is required
in addition to ANDROID_RUNTIME_ROOT in order to run
`am` and `dalvikvm`
2019-07-04 23:26:55 +02:00
Fredrik Fornwall
e502b9169f Respect failsafe sessions in ACTION_EXECUTE 2019-06-09 20:00:17 +02:00
Leonid Plyushch
9f2d887ca0 minor fix for log message while removing tmpdir 2019-06-09 14:31:09 +03:00
Fredrik Fornwall
e6aacc5f08 Update Android Gradle plug-in 2019-05-27 14:07:50 +02:00
Fredrik Fornwall
ff935be54a Add versionName gradle task 2019-05-27 00:07:41 +02:00
Fredrik Fornwall
4e76162346 Bump version to 0.72 2019-05-27 00:03:42 +02:00
Leonid Plyushch
5fa4f2647b set BOOTCLASSPATH environment variable
Required on at least Android 5. Otherwise utilities that use dalvikvm may fail.

Issue happens on Nexus 7 with official 5.0/5.1 firmware and possibly other
AOSP ROMs.
2019-05-26 20:05:03 +02:00
Fredrik Fornwall
81b5889a26 Bump version to 0.71 2019-05-21 21:52:39 +02:00
Leonid Plyushch
514f59258a let $PREFIX/tmp be cleaned when TermuxService is going to be stopped 2019-05-21 21:52:14 +02:00
Leonid Plyushch
9f79393aa5 Revert "clean /tmp directory on cold start"
This reverts commit beb8a004e6.
2019-05-21 21:52:14 +02:00
Leonid Plyushch
e49d514236 Revert "sessions: do not clear TMPDIR if application was not started"
This reverts commit bd45837d93.
2019-05-21 21:52:14 +02:00
Leonid Plyushch
4e1462326c remove intent category "default"
It was needed only when Termux had separate icon for failsafe activity.
2019-05-21 21:52:14 +02:00
10 changed files with 179 additions and 52 deletions

View File

@@ -14,8 +14,8 @@ android {
applicationId "com.termux" applicationId "com.termux"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 28 targetSdkVersion 28
versionCode 70 versionCode 75
versionName "0.70" versionName "0.75"
} }
buildTypes { buildTypes {
@@ -35,3 +35,9 @@ android {
dependencies { dependencies {
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
} }
task versionName {
doLast {
print android.defaultConfig.versionName
}
}

View File

@@ -13,6 +13,7 @@
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission-sdk-23 android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<application <application
android:extractNativeLibs="true" android:extractNativeLibs="true"
@@ -38,12 +39,10 @@
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" /> <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter> </intent-filter>
<meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts" /> <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts" />
</activity> </activity>

View File

@@ -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) { static String[] buildEnvironment(boolean failSafe, String cwd) {
new File(TermuxService.HOME_PATH).mkdirs(); new File(TermuxService.HOME_PATH).mkdirs();
@@ -103,16 +110,15 @@ public final class BackgroundJob {
environment.add("TERM=xterm-256color"); environment.add("TERM=xterm-256color");
environment.add("HOME=" + TermuxService.HOME_PATH); environment.add("HOME=" + TermuxService.HOME_PATH);
environment.add("PREFIX=" + TermuxService.PREFIX_PATH); environment.add("PREFIX=" + TermuxService.PREFIX_PATH);
environment.add("BOOTCLASSPATH" + System.getenv("BOOTCLASSPATH"));
environment.add("ANDROID_ROOT=" + System.getenv("ANDROID_ROOT")); environment.add("ANDROID_ROOT=" + System.getenv("ANDROID_ROOT"));
environment.add("ANDROID_DATA=" + System.getenv("ANDROID_DATA")); environment.add("ANDROID_DATA=" + System.getenv("ANDROID_DATA"));
// EXTERNAL_STORAGE is needed for /system/bin/am to work on at least // EXTERNAL_STORAGE is needed for /system/bin/am to work on at least
// Samsung S7 - see https://plus.google.com/110070148244138185604/posts/gp8Lk3aCGp3. // Samsung S7 - see https://plus.google.com/110070148244138185604/posts/gp8Lk3aCGp3.
environment.add("EXTERNAL_STORAGE=" + System.getenv("EXTERNAL_STORAGE")); environment.add("EXTERNAL_STORAGE=" + System.getenv("EXTERNAL_STORAGE"));
String androidRuntimeRoot = System.getenv("ANDROID_RUNTIME_ROOT"); // ANDROID_RUNTIME_ROOT and ANDROID_TZDATA_ROOT are required for `am` to run on Android Q
// ANDROID_RUNTIME_ROOT is required for `am` to run on Android Q addToEnvIfPresent(environment, "ANDROID_RUNTIME_ROOT");
if (androidRuntimeRoot != null) { addToEnvIfPresent(environment, "ANDROID_TZDATA_ROOT");
environment.add("ANDROID_RUNTIME_ROOT=" + androidRuntimeRoot);
}
if (failSafe) { if (failSafe) {
// Keep the default path so that system binaries can be used in the failsafe session. // Keep the default path so that system binaries can be used in the failsafe session.
environment.add("PATH= " + System.getenv("PATH")); environment.add("PATH= " + System.getenv("PATH"));

View File

@@ -10,7 +10,7 @@ public class BellUtil {
private static BellUtil instance = null; private static BellUtil instance = null;
private static final Object lock = new Object(); private static final Object lock = new Object();
public static BellUtil with(Context context) { public static BellUtil getInstance(Context context) {
if (instance == null) { if (instance == null) {
synchronized (lock) { synchronized (lock) {
if (instance == null) { if (instance == null) {

View File

@@ -2,6 +2,7 @@ package com.termux.app;
import android.content.Context; import android.content.Context;
import android.os.Build; import android.os.Build;
import android.provider.Settings;
import android.util.AttributeSet; import android.util.AttributeSet;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@@ -73,6 +74,18 @@ public final class ExtraKeysView extends GridLayout {
put("RIGHT", KeyEvent.KEYCODE_DPAD_RIGHT); put("RIGHT", KeyEvent.KEYCODE_DPAD_RIGHT);
put("DOWN", KeyEvent.KEYCODE_DPAD_DOWN); put("DOWN", KeyEvent.KEYCODE_DPAD_DOWN);
put("ENTER", KeyEvent.KEYCODE_ENTER); 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) { static void sendKey(View view, String keyName) {
@@ -338,7 +351,15 @@ public final class ExtraKeysView extends GridLayout {
final Button finalButton = button; final Button finalButton = button;
button.setOnClickListener(v -> { 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(); View root = getRootView();
if(Arrays.asList("CTRL", "ALT", "FN").contains(buttonText)) { if(Arrays.asList("CTRL", "ALT", "FN").contains(buttonText)) {
ToggleButton self = (ToggleButton) finalButton; ToggleButton self = (ToggleButton) finalButton;

View File

@@ -24,7 +24,6 @@ import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import android.os.Vibrator;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.Spanned; import android.text.Spanned;
import android.text.TextUtils; import android.text.TextUtils;
@@ -402,7 +401,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
mBellSoundPool.play(mBellSoundId, 1.f, 1.f, 1, 0, 1.f); mBellSoundPool.play(mBellSoundId, 1.f, 1.f, 1, 0, 1.f);
break; break;
case TermuxPreferences.BELL_VIBRATE: case TermuxPreferences.BELL_VIBRATE:
BellUtil.with(TermuxActivity.this).doBell(); BellUtil.getInstance(TermuxActivity.this).doBell();
break; break;
case TermuxPreferences.BELL_IGNORE: case TermuxPreferences.BELL_IGNORE:
// Ignore the bell character. // Ignore the bell character.
@@ -482,7 +481,6 @@ public final class TermuxActivity extends Activity implements ServiceConnection
if (bundle != null) { if (bundle != null) {
launchFailsafe = bundle.getBoolean(TERMUX_FAILSAFE_SESSION_ACTION, false); launchFailsafe = bundle.getBoolean(TERMUX_FAILSAFE_SESSION_ACTION, false);
} }
clearTemporaryDirectory();
addNewSession(launchFailsafe, null); addNewSession(launchFailsafe, null);
} catch (WindowManager.BadTokenException e) { } catch (WindowManager.BadTokenException e) {
// Activity finished - ignore. // Activity finished - ignore.
@@ -496,7 +494,6 @@ public final class TermuxActivity extends Activity implements ServiceConnection
Intent i = getIntent(); Intent i = getIntent();
if (i != null && Intent.ACTION_RUN.equals(i.getAction())) { if (i != null && Intent.ACTION_RUN.equals(i.getAction())) {
// Android 7.1 app shortcut from res/xml/shortcuts.xml. // Android 7.1 app shortcut from res/xml/shortcuts.xml.
clearTemporaryDirectory();
boolean failSafe = i.getBooleanExtra(TERMUX_FAILSAFE_SESSION_ACTION, false); boolean failSafe = i.getBooleanExtra(TERMUX_FAILSAFE_SESSION_ACTION, false);
addNewSession(failSafe, null); addNewSession(failSafe, null);
} else { } else {
@@ -592,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) 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(); .setPositiveButton(android.R.string.ok, null).show();
} else { } else {
String executablePath = (failSafe ? "/system/bin/sh" : null); TerminalSession newSession = mTermService.createTermSession(null, null, null, failSafe);
TerminalSession newSession = mTermService.createTermSession(executablePath, null, null, failSafe);
if (sessionName != null) { if (sessionName != null) {
newSession.mSessionName = sessionName; newSession.mSessionName = sessionName;
} }
@@ -658,19 +654,86 @@ public final class TermuxActivity extends Activity implements ServiceConnection
} }
static LinkedHashSet<CharSequence> extractUrls(String text) { 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( 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); Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
LinkedHashSet<CharSequence> urlSet = new LinkedHashSet<>(); LinkedHashSet<CharSequence> urlSet = new LinkedHashSet<>();
Matcher matcher = urlPattern.matcher(text); Matcher matcher = urlPattern.matcher(text);
while (matcher.find()) { while (matcher.find()) {
int matchStart = matcher.start(1); int matchStart = matcher.start(1);
int matchEnd = matcher.end(); int matchEnd = matcher.end();
String url = text.substring(matchStart, matchEnd); String url = text.substring(matchStart, matchEnd);
urlSet.add(url); urlSet.add(url);
} }
return urlSet; return urlSet;
} }
@@ -725,7 +788,18 @@ public final class TermuxActivity extends Activity implements ServiceConnection
if (session != null) { if (session != null) {
Intent intent = new Intent(Intent.ACTION_SEND); Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain"); 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)); intent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.share_transcript_title));
startActivity(Intent.createChooser(intent, getString(R.string.share_transcript_chooser_title))); startActivity(Intent.createChooser(intent, getString(R.string.share_transcript_chooser_title)));
} }
@@ -836,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();
}
}
}
} }

View File

@@ -6,6 +6,7 @@ import android.app.NotificationChannel;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.app.Service; import android.app.Service;
import android.content.ActivityNotFoundException;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.res.Resources; import android.content.res.Resources;
@@ -16,6 +17,7 @@ import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.PowerManager; import android.os.PowerManager;
import android.provider.Settings;
import android.util.Log; import android.util.Log;
import android.widget.ArrayAdapter; 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 = wm.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, EmulatorDebug.LOG_TAG);
mWifiLock.acquire(); 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(); updateNotification();
} }
} else if (ACTION_UNLOCK_WAKE.equals(action)) { } else if (ACTION_UNLOCK_WAKE.equals(action)) {
@@ -136,7 +154,8 @@ public final class TermuxService extends Service implements SessionChangedCallba
mBackgroundTasks.add(task); mBackgroundTasks.add(task);
updateNotification(); updateNotification();
} else { } 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". // Transform executable path to session name, e.g. "/bin/do-something.sh" => "do something.sh".
if (executablePath != null) { if (executablePath != null) {
@@ -237,6 +256,18 @@ public final class TermuxService extends Service implements SessionChangedCallba
@Override @Override
public void onDestroy() { 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 (mWakeLock != null) mWakeLock.release();
if (mWifiLock != null) mWifiLock.release(); if (mWifiLock != null) mWifiLock.release();
@@ -250,14 +281,6 @@ public final class TermuxService extends Service implements SessionChangedCallba
return mTerminalSessions; return mTerminalSessions;
} }
public boolean isWakelockEnabled() {
if (mWakeLock == null) {
return false;
} else {
return mWakeLock.isHeld();
}
}
TerminalSession createTermSession(String executablePath, String[] arguments, String cwd, boolean failSafe) { TerminalSession createTermSession(String executablePath, String[] arguments, String cwd, boolean failSafe) {
new File(HOME_PATH).mkdirs(); new File(HOME_PATH).mkdirs();
@@ -267,11 +290,13 @@ public final class TermuxService extends Service implements SessionChangedCallba
boolean isLoginShell = false; boolean isLoginShell = false;
if (executablePath == null) { if (executablePath == null) {
for (String shellBinary : new String[]{"login", "bash", "zsh"}) { if (!failSafe) {
File shellFile = new File(PREFIX_PATH + "/bin/" + shellBinary); for (String shellBinary : new String[]{"login", "bash", "zsh"}) {
if (shellFile.canExecute()) { File shellFile = new File(PREFIX_PATH + "/bin/" + shellBinary);
executablePath = shellFile.getAbsolutePath(); if (shellFile.canExecute()) {
break; executablePath = shellFile.getAbsolutePath();
break;
}
} }
} }

View File

@@ -4,7 +4,7 @@ buildscript {
google() google()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.4.0' classpath 'com.android.tools.build:gradle:3.5.0'
} }
} }

View File

@@ -1,5 +1,6 @@
#Sun Aug 25 01:57:11 CEST 2019
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip

View File

@@ -41,7 +41,15 @@ public final class TerminalBuffer {
return getSelectedText(0, -getActiveTranscriptRows(), mColumns, mScreenRows).trim(); 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) { 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 StringBuilder builder = new StringBuilder();
final int columns = mColumns; final int columns = mColumns;
@@ -79,7 +87,8 @@ public final class TerminalBuffer {
} }
if (lastPrintingCharIndex != -1) if (lastPrintingCharIndex != -1)
builder.append(line, x1Index, lastPrintingCharIndex - x1Index + 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(); return builder.toString();
} }