Compare commits

..

73 Commits
v0.38 ... v0.45

Author SHA1 Message Date
Fredrik Fornwall
743b067cae Bump version to 0.45 2016-12-27 10:43:05 +01:00
Fredrik Fornwall
23333c074a Fix NPE regression in version 0.44 2016-12-27 10:42:41 +01:00
Fredrik Fornwall
f11644fa51 travis: Update build tools 2016-12-26 05:18:45 +01:00
Fredrik Fornwall
212be59fca gradle: Update build tools 2016-12-26 04:48:28 +01:00
Fredrik Fornwall
e3a1f8224f Add art/copy-to-other-apps.sh script 2016-12-26 04:45:00 +01:00
Fredrik Fornwall
4f40d5a26a Bump version to 0.44 2016-12-26 04:07:44 +01:00
Fredrik Fornwall
df92896eef Add Android 7.1 launcher shortcut for new session 2016-12-26 04:07:03 +01:00
Fredrik Fornwall
4c93cb42f1 Use constant for intent extra key 2016-12-26 00:57:09 +01:00
Fredrik Fornwall
34afb9de43 Give up on using vector drawables for launch icons
Instead use svg files in art/ and a script to build the png files.
2016-12-24 01:05:33 +01:00
Fredrik Fornwall
b6ea29d260 Remove duplicate updateNotification() calls 2016-12-24 00:25:24 +01:00
Fredrik Fornwall
289d58a2f0 Try it with launcher icons in drawable-anydpi 2016-12-10 22:51:11 -05:00
Fredrik Fornwall
0501ce924b gradle: Update android gradle plugin 2016-12-07 20:26:09 -05:00
Fredrik Fornwall
d12256f5e5 Add flags to imeOptions
Add IME_FLAG_NO_ENTER_ACTION and IME_ACTION_NONE. I haven't
encountered any issue without them, but specifying them is correct.
2016-12-04 17:32:40 +01:00
Fredrik Fornwall
fcbc036f92 Update gradle to 3.2.1 2016-12-04 17:26:26 +01:00
Fredrik Fornwall
70d5839334 Bump version to 0.43 for preview testing 2016-12-04 04:38:35 +01:00
Fredrik Fornwall
9c19540759 Keep track of background tasks
Also merge wake lock and wifi lock into one, and make it exposed to
termux-wake-lock and termux-wake-unlock commands.
2016-12-04 04:37:13 +01:00
Fredrik Fornwall
9fe0e49473 Render cursor color&shapes (underline and ibeam)
Allow the cursor to be colored by the theme, and support rendering
underline and ibeam cursor styles.
2016-12-04 01:06:50 +01:00
Fredrik Fornwall
357b17e972 Improve setup of symlinks to external storage
The context.getExternalFilesDirs(null) call may return several
elements, and some of them may be null.
2016-12-04 01:04:19 +01:00
Fredrik Fornwall
6334470f81 Try to handle Samsung keyboard better
The stock Samsung keyboard with 'Auto check spelling' enabled may
send multiple backspaces. Note that this auto-correction of
spelling will not work good in general with a terminal, so should
be disabled (or another keyboard used) when using Termux.
2016-11-28 00:26:37 +01:00
Fredrik Fornwall
b8cdd59c68 Use gradle build, not assembly, for coverity 2016-11-25 03:55:07 +01:00
Fredrik Fornwall
6cf36fffd7 Fix typo on coverity setup 2016-11-25 03:43:16 +01:00
Fredrik Fornwall
f10ecd4db5 Try to fix coverity scans in travis 2016-11-25 03:17:50 +01:00
Fredrik Fornwall
26457e8443 Tweak fillcolor in icons for easier svg generation 2016-11-25 02:14:54 +01:00
Fredrik Fornwall
6702846c7c Update build tools version in travis 2016-11-24 23:58:43 +01:00
Fredrik Fornwall
0c6180bbb1 Update gradle deps 2016-11-24 23:26:27 +01:00
Fredrik Fornwall
fcf07f6a19 Coverity: Specify "branch_pattern: master" 2016-11-24 20:15:31 +01:00
Fredrik Fornwall
e272e3b3b2 Update README a bit 2016-11-24 20:05:28 +01:00
Fredrik Fornwall
cdf0e72145 Enable coverity scan for all branches 2016-11-24 19:36:38 +01:00
Fredrik Fornwall
70245eb78c Use BackgroundJob.setupProcessArgs for sessions 2016-11-23 01:55:29 +01:00
Fredrik Fornwall
ee7631dfac Add a round icon for android-25 2016-11-23 01:38:17 +01:00
Fredrik Fornwall
5ecf5d12d1 Tweak the icon somewhat 2016-11-23 01:37:58 +01:00
Fredrik Fornwall
0c8cd90f4e Use $PREFIX/bin/sh for script file without shebang
Also try to handle #!(/usr)/bin/foo shebangs.
2016-11-20 16:43:27 +01:00
Fredrik Fornwall
e1ea68913f Use to a vector notification icon 2016-11-19 22:33:45 +01:00
Fredrik Fornwall
dde854eba7 Switch to a vector icon 2016-11-19 22:18:26 +01:00
Fredrik Fornwall
07a4607c04 Merge pull request #193 from friederbluemle/update-gradle-wrapper
Update Gradle wrapper to 3.2
2016-11-19 21:04:10 +01:00
Fredrik Fornwall
883be37b98 Add a shake animation on a terminal bell 2016-11-19 15:41:24 +01:00
Frieder Bluemle
d939d3d927 Update Gradle wrapper to 3.2 2016-11-18 18:01:00 +08:00
Fredrik Fornwall
a0fa51eb92 Start work on background jobs 2016-10-26 02:26:44 +02:00
Fredrik Fornwall
755513bb33 Update gradle setup 2016-10-20 01:00:28 +02:00
Fredrik Fornwall
8ad7a6669c Switch a commit() to apply() for shared prefs 2016-10-16 00:13:35 +02:00
Fredrik Fornwall
60f7aada9e Remove logging at info level 2016-10-16 00:13:04 +02:00
Fredrik Fornwall
6aa0492434 Add @NonNull annotation 2016-10-16 00:12:42 +02:00
Fredrik Fornwall
019aa44837 Update Android Gradle Plugin 2016-10-10 22:23:39 +02:00
Fredrik Fornwall
8d3d5e147f Remove outdated JNI building instructions 2016-09-29 01:13:45 +02:00
Fredrik Fornwall
44197b90e2 Update .travis.yml 2016-09-29 00:34:12 +02:00
Fredrik Fornwall
794c7ee333 Remove deprecated android:singleLine attribute 2016-09-27 00:30:05 +02:00
Fredrik Fornwall
0457ddbc69 Update build.gradle with latest versions 2016-09-27 00:29:47 +02:00
Fredrik Fornwall
283792af5e Refresh listview after changing session name 2016-09-27 00:29:08 +02:00
Fredrik Fornwall
be7cfa603a Fix gradle syntax for Android Studio 2016-09-25 01:15:49 +02:00
Fredrik Fornwall
3480bf7346 Switch from cmake to ndk-build
We switch from cmake to ndk-build to make it easier for builders
in not requiring an additional tool installed. The JNI build is
so simple so we don't really need much of a build tool anyway.
2016-09-24 20:22:10 +02:00
Fredrik Fornwall
8314a2756c Setup NDK in travis 2016-09-23 08:57:40 +02:00
Fredrik Fornwall
4de82d9fe0 Update gradle to 3.1 2016-09-21 22:34:44 +02:00
Fredrik Fornwall
d658e16801 Use the new cmake support to build JNI code 2016-09-21 22:30:20 +02:00
Fredrik Fornwall
26dcd5af88 Update gradle and Android Studio 2016-09-19 23:26:49 +02:00
Fredrik Fornwall
8056013082 Bump version to 0.42 2016-09-16 23:18:51 +02:00
Fredrik Fornwall
8e90545c4b Remove comment from build.gradle 2016-09-16 23:18:34 +02:00
Fredrik Fornwall
426ddbacbd Remove useless casts 2016-09-16 23:17:47 +02:00
Fredrik Fornwall
7e1f8a551f Change VolumeUp+H to generate ~
Using VolumeUp+H to generate a tilde (~) is more useful than
sending the home key.

Fixes #151.
2016-09-16 23:15:27 +02:00
Fredrik Fornwall
e169af0447 Change shortcuts from Ctrl+Shift to Ctrl+Alt
This works on more language layouts and devices.

Fixes #145.
2016-09-16 23:12:56 +02:00
Fredrik Fornwall
a2cb3fafee Fix numpad 0 and . key handling
Fixes #146.
2016-09-05 23:11:25 +02:00
Fredrik Fornwall
166710f14a Bump version to 0.40 2016-09-04 19:00:29 +02:00
Fredrik Fornwall
c1a9b7726f Tweak InputConnection implementation 2016-09-04 18:56:28 +02:00
Fredrik Fornwall
afb339e9d8 Format code 2016-08-30 13:47:30 +02:00
Fredrik Fornwall
64c23f498f Implement true (24-bit) color 2016-08-27 00:32:38 +02:00
Fredrik Fornwall
1dc92b2a12 Remove unused imports 2016-08-22 16:54:55 +02:00
Fredrik Fornwall
990a957383 Bump android support library version 2016-08-22 16:53:42 +02:00
Fredrik Fornwall
7bb64d724c Update android plugin for gradle 2016-08-16 10:31:10 +02:00
Fredrik Fornwall
4609dd71c6 Switch KEYCODE_HOME -> KEYCODE_MOVE_HOME in tests 2016-08-12 06:13:26 +02:00
Fredrik Fornwall
8d00f22d4c Catch KEYCODE_MOVE_HOME and not KEYCODE_HOME
The KEYCODE_HOME event is handled by the system and never delivered
to applications, it's KEYCODE_MOVE_HOME (FN+LeftArrow) we want to
handle ourselves and send as an escape sequence.

Fixes #138.
2016-08-12 04:18:09 +02:00
Fredrik Fornwall
5532421ab2 Check arches in order of preference
The documentation for Build.SUPPORTED_ABIS says:
"An ordered list of ABIs supported by this device. The most preferred
ABI is the first element in the list."

Respect that preference when checking for which arch to install
packages for.

Fixes #131.
2016-08-08 23:22:47 +02:00
Fredrik Fornwall
d2b27978e2 Bump version for v0.39 2016-08-08 23:19:49 +02:00
Fredrik Fornwall
30b05e9ab2 Merge pull request #132 from michalbednarski/extrakeysview-alignment
Make ExtraKeysView work on Android 5
2016-08-08 23:10:10 +02:00
Michał Bednarski
c350318c77 Make ExtraKeysView work on Android 5
This is done by explicitly specifying alignment as GridLayout.FILL
as I have figured out that this was fixed in Android 6 in commit
6dafd87fb4%5E%21/#F0
which set default alignment to FILL if weight is nonzero
2016-08-08 10:16:47 +02:00
67 changed files with 824 additions and 381 deletions

1
.gitignore vendored
View File

@@ -6,6 +6,7 @@
build/
*.apk
*.so
.externalNativeBuild
# Crashlytics configuations
com_crashlytics_export_strings.xml

7
.idea/gradle.xml generated
View File

@@ -12,12 +12,7 @@
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="myModules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>
</component>

View File

@@ -1,4 +1,4 @@
sudo: false
sudo: true
language: android
jdk: oraclejdk8
@@ -12,10 +12,15 @@ android:
components:
- platform-tools
- tools
- build-tools-24.0.1
- android-24
- build-tools-25.0.2
- android-25
- extra-android-m2repository
before_install:
- git clone https://github.com/urho3d/android-ndk.git $HOME/android-ndk
- export ANDROID_NDK_HOME=$HOME/android-ndk
- echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-certificates.crt
script:
- ./gradlew testDebugUnitTest
@@ -26,5 +31,5 @@ addons:
description: "Terminal emulator and Linux environment for Android"
notification_email: fredrik@fornwall.net
build_command_prepend: "./gradlew clean"
build_command: "./gradlew assemble"
branch_pattern: coverity_scan
build_command: "./gradlew build"
branch_pattern: master

View File

@@ -3,25 +3,19 @@ Termux app
[![Travis build status](https://travis-ci.org/termux/termux-app.svg?branch=master)](https://travis-ci.org/termux/termux-app)
[![Join the chat at https://gitter.im/termux/termux](https://badges.gitter.im/termux/termux.svg)](https://gitter.im/termux/termux)
Termux is an Android terminal app and Linux environment.
[Termux](https://termux.com) is an Android terminal app and Linux environment.
* [Termux on Google Play Store](https://play.google.com/store/apps/details?id=com.termux)
* [Termux on F-Droid](https://f-droid.org/repository/browse/?fdid=com.termux)
* [termux.com](http://termux.com)
* [Termux Help](http://termux.com/help/)
* [Termux app on GitHub](https://github.com/termux/termux-app)
* [Termux packages on GitHub](https://github.com/termux/termux-packages)
* [Termux Google+ community](http://termux.com/community/)
Note that this repository is for the app itself (the user interface and the terminal emulation). For the packages installable inside the app, see [termux/termux-packages](https://github.com/termux/termux-packages)
License
=======
Released under [the GPLv3 license](https://www.gnu.org/licenses/gpl.html). Contains code from `Terminal Emulator for Android` which is released under [the Apache License 2.0](https://www.apache.org/licenses/).
Building JNI libraries
======================
Execute the `build-jnilibs.sh` script to build the required JNI libraries.
Terminal resources
==================
* [XTerm control sequences](http://invisible-island.net/xterm/ctlseqs/ctlseqs.html)

View File

@@ -1,25 +1,29 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 24
buildToolsVersion "24.0.1"
compileSdkVersion 25
buildToolsVersion "25.0.2"
dependencies {
compile 'com.android.support:support-annotations:24.1.1'
compile "com.android.support:support-v4:24.1.1"
compile 'com.android.support:support-annotations:25.0.1'
compile "com.android.support:support-v4:25.0.1"
}
defaultConfig {
applicationId "com.termux"
minSdkVersion 21
targetSdkVersion 24
versionCode 38
versionName "0.38"
targetSdkVersion 25
versionCode 45
versionName "0.45"
externalNativeBuild {
ndkBuild {
cFlags "-std=c11", "-Wall", "-Wextra", "-Werror", "-Os", "-fno-stack-protector", "-Wl,--gc-sections"
}
}
ndk {
moduleName "libtermux"
abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
cFlags "-std=c11 -Wall -Wextra -Os -fno-stack-protector -nostdlib -Wl,--gc-sections"
abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
}
}
@@ -30,6 +34,12 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
ndkBuild {
path "src/main/jni/Android.mk"
}
}
}
dependencies {

View File

@@ -15,7 +15,8 @@
<application
android:allowBackup="true"
android:fullBackupContent="@xml/backupscheme"
android:icon="@drawable/ic_launcher"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:banner="@drawable/banner"
android:label="@string/application_name"
android:theme="@style/Theme.Termux"
@@ -34,6 +35,7 @@
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
<meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts" />
</activity>
<activity

View File

@@ -4,58 +4,70 @@ import android.util.Log;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* A background job launched by Termux.
*/
public final class BackgroundJob {
private static final String LOG_TAG = "termux-background";
private static final String LOG_TAG = "termux-task";
final Process mProcess;
public BackgroundJob(File cwd, File fileToExecute, String[] args) throws IOException {
String[] env = buildEnvironment(false, cwd.getAbsolutePath());
public BackgroundJob(String cwd, String fileToExecute, final String[] args, final TermuxService service) {
String[] env = buildEnvironment(false, cwd);
if (cwd == null) cwd = TermuxService.HOME_PATH;
String[] progArray = new String[args.length + 1];
final String[] progArray = setupProcessArgs(fileToExecute, args);
final String processDescription = Arrays.toString(progArray);
mProcess = Runtime.getRuntime().exec(progArray, env, cwd);
new Thread() {
@Override
public void run() {
while (true) {
try {
int exitCode = mProcess.waitFor();
if (exitCode == 0) {
Log.i(LOG_TAG, "exited normally");
return;
} else {
Log.i(LOG_TAG, "exited with exit code: " + exitCode);
}
} catch (InterruptedException e) {
// Ignore.
}
}
}
}.start();
Process process;
try {
process = Runtime.getRuntime().exec(progArray, env, new File(cwd));
} catch (IOException e) {
mProcess = null;
// TODO: Visible error message?
Log.e(LOG_TAG, "Failed running background job: " + processDescription, e);
return;
}
mProcess = process;
final int pid = getPid(mProcess);
new Thread() {
@Override
public void run() {
Log.i(LOG_TAG, "[" + pid + "] starting: " + processDescription);
InputStream stdout = mProcess.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(stdout, StandardCharsets.UTF_8));
String line;
try {
// FIXME: Long lines.
while ((line = reader.readLine()) != null) {
Log.i(LOG_TAG, line);
Log.i(LOG_TAG, "[" + pid + "] stdout: " + line);
}
} catch (IOException e) {
Log.e(LOG_TAG, "Error reading output", e);
}
try {
int exitCode = mProcess.waitFor();
service.onBackgroundJobExited(BackgroundJob.this);
if (exitCode == 0) {
Log.i(LOG_TAG, "[" + pid + "] exited normally");
} else {
Log.w(LOG_TAG, "[" + pid + "] exited with code: " + exitCode);
}
} catch (InterruptedException e) {
// Ignore.
}
}
@@ -71,7 +83,7 @@ public final class BackgroundJob {
try {
// FIXME: Long lines.
while ((line = reader.readLine()) != null) {
Log.e(LOG_TAG, line);
Log.i(LOG_TAG, "[" + pid + "] stderr: " + line);
}
} catch (IOException e) {
// Ignore.
@@ -80,7 +92,7 @@ public final class BackgroundJob {
};
}
public String[] buildEnvironment(boolean failSafe, String cwd) {
public static String[] buildEnvironment(boolean failSafe, String cwd) {
new File(TermuxService.HOME_PATH).mkdirs();
if (cwd == null) cwd = TermuxService.HOME_PATH;
@@ -109,4 +121,75 @@ public final class BackgroundJob {
}
}
public static int getPid(Process p) {
try {
Field f = p.getClass().getDeclaredField("pid");
f.setAccessible(true);
try {
return f.getInt(p);
} finally {
f.setAccessible(false);
}
} catch (Throwable e) {
return -1;
}
}
static String[] setupProcessArgs(String fileToExecute, String[] args) {
// The file to execute may either be:
// - An elf file, in which we execute it directly.
// - A script file without shebang, which we execute with our standard shell $PREFIX/bin/sh instead of the
// system /system/bin/sh. The system shell may vary and may not work at all due to LD_LIBRARY_PATH.
// - A file with shebang, which we try to handle with e.g. /bin/foo -> $PREFIX/bin/foo.
String interpreter = null;
try {
File file = new File(fileToExecute);
try (FileInputStream in = new FileInputStream(file)) {
byte[] buffer = new byte[256];
int bytesRead = in.read(buffer);
if (bytesRead > 4) {
if (buffer[0] == 0x7F && buffer[1] == 'E' && buffer[2] == 'L' && buffer[3] == 'F') {
// Elf file, do nothing.
} else if (buffer[0] == '#' && buffer[1] == '!') {
// Try to parse shebang.
StringBuilder builder = new StringBuilder();
for (int i = 2; i < bytesRead; i++) {
char c = (char) buffer[i];
if (c == ' ' || c == '\n') {
if (builder.length() == 0) {
// Skip whitespace after shebang.
continue;
} else {
// End of shebang.
String executable = builder.toString();
if (executable.startsWith("/usr") || executable.startsWith("/bin")) {
String[] parts = executable.split("/");
String binary = parts[parts.length - 1];
interpreter = TermuxService.PREFIX_PATH + "/bin/" + binary;
}
break;
}
} else {
builder.append(c);
}
}
} else {
// No shebang and no ELF, use standard shell.
interpreter = TermuxService.PREFIX_PATH + "/bin/sh";
}
}
}
} catch (IOException e) {
// Ignore.
}
List<String> result = new ArrayList<>();
if (interpreter != null) result.add(interpreter);
result.add(fileToExecute);
if (args != null) {
for (String arg : args) result.add(arg);
}
return result.toArray(new String[result.size()]);
}
}

View File

@@ -165,8 +165,8 @@ public final class ExtraKeysView extends GridLayout {
param.rightMargin = param.topMargin = 0;
param.setGravity(Gravity.LEFT);
float weight = "▲▼◀▶".contains(buttonText) ? 0.7f : 1.f;
param.columnSpec = GridLayout.spec(col, weight);
param.rowSpec = GridLayout.spec(row, 1.f);
param.columnSpec = GridLayout.spec(col, GridLayout.FILL, weight);
param.rowSpec = GridLayout.spec(row, GridLayout.FILL, 1.f);
button.setLayoutParams(param);
addView(button);

View File

@@ -49,6 +49,8 @@ import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
@@ -139,6 +141,8 @@ public final class TermuxActivity extends Activity implements ServiceConnection
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION).build()).build();
int mBellSoundId;
Animation mOnBellAnimation;
private final BroadcastReceiver mBroadcastReceiever = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -209,6 +213,8 @@ public final class TermuxActivity extends Activity implements ServiceConnection
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
mOnBellAnimation = AnimationUtils.loadAnimation(this, R.anim.on_bell);
mSettings = new TermuxPreferences(this);
setContentView(R.layout.drawer_layout);
@@ -402,20 +408,22 @@ public final class TermuxActivity extends Activity implements ServiceConnection
@Override
public void onBell(TerminalSession session) {
if (mIsVisible) {
switch (mSettings.mBellBehaviour) {
case TermuxPreferences.BELL_BEEP:
mBellSoundPool.play(mBellSoundId, 1.f, 1.f, 1, 0, 1.f);
break;
case TermuxPreferences.BELL_VIBRATE:
((Vibrator) getSystemService(VIBRATOR_SERVICE)).vibrate(50);
break;
case TermuxPreferences.BELL_IGNORE:
// Ignore the bell character.
break;
}
if (!mIsVisible) return;
mTerminalView.startAnimation(mOnBellAnimation);
switch (mSettings.mBellBehaviour) {
case TermuxPreferences.BELL_BEEP:
mBellSoundPool.play(mBellSoundId, 1.f, 1.f, 1, 0, 1.f);
break;
case TermuxPreferences.BELL_VIBRATE:
((Vibrator) getSystemService(VIBRATOR_SERVICE)).vibrate(50);
break;
case TermuxPreferences.BELL_IGNORE:
// Ignore the bell character.
break;
}
}
@Override
@@ -429,8 +437,9 @@ public final class TermuxActivity extends Activity implements ServiceConnection
final StyleSpan boldSpan = new StyleSpan(Typeface.BOLD);
final StyleSpan italicSpan = new StyleSpan(Typeface.ITALIC);
@NonNull
@Override
public View getView(int position, View convertView, ViewGroup parent) {
public View getView(int position, View convertView, @NonNull ViewGroup parent) {
View row = convertView;
if (row == null) {
LayoutInflater inflater = getLayoutInflater();
@@ -513,7 +522,13 @@ public final class TermuxActivity extends Activity implements ServiceConnection
finish();
}
} else {
switchToSession(getStoredCurrentSessionOrLast());
Intent i = getIntent();
if (i != null && Intent.ACTION_RUN.equals(i.getAction())) {
// Android 7.1 app shortcut from res/xml/shortcuts.xml.
addNewSession(false, null);
} else {
switchToSession(getStoredCurrentSessionOrLast());
}
}
}
@@ -534,6 +549,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
@Override
public void onTextSet(String text) {
sessionToRename.mSessionName = text;
mListViewAdapter.notifyDataSetChanged();
}
}, -1, null, -1, null, null);
}

View File

@@ -184,25 +184,28 @@ final class TermuxInstaller {
/** Get bootstrap zip url for this systems cpu architecture. */
static URL determineZipUrl() throws MalformedURLException {
String termuxArch = null;
String archName = determineTermuxArchName();
return new URL("https://termux.net/bootstrap/bootstrap-" + archName + ".zip");
}
private static String determineTermuxArchName() {
// Note that we cannot use System.getProperty("os.arch") since that may give e.g. "aarch64"
// while a 64-bit runtime may not be installed (like on the Samsung Galaxy S5 Neo).
// Instead we search through the supported abi:s on the device, see:
// http://developer.android.com/ndk/guides/abis.html
// Note that we search for abi:s in preferred order, and want to avoid installing arm on
// an x86 system where arm emulation is available.
final String[] androidArchNames = {"arm64-v8a", "x86_64", "x86", "armeabi-v7a"};
final String[] termuxArchNames = {"aarch64", "x86_64", "i686", "arm"};
final List<String> supportedArches = Arrays.asList(Build.SUPPORTED_ABIS);
for (int i = 0; i < termuxArchNames.length; i++) {
if (supportedArches.contains(androidArchNames[i])) {
termuxArch = termuxArchNames[i];
break;
// Note that we search for abi:s in preferred order (the ordering of the
// Build.SUPPORTED_ABIS list) to avoid e.g. installing arm on an x86 system where arm
// emulation is available.
for (String androidArch : Build.SUPPORTED_ABIS) {
switch (androidArch) {
case "arm64-v8a": return "aarch64";
case "armeabi-v7a": return "arm";
case "x86_64": return "x86_64";
case "x86": return "i686";
}
}
return new URL("https://termux.net/bootstrap/bootstrap-" + termuxArch + ".zip");
throw new RuntimeException("Unable to determine arch from Build.SUPPORTED_ABIS = " +
Arrays.toString(Build.SUPPORTED_ABIS));
}
/** Delete a folder and all its content or throw. */
@@ -254,9 +257,13 @@ final class TermuxInstaller {
Os.symlink(moviesDir.getAbsolutePath(), new File(storageDir, "movies").getAbsolutePath());
final File[] dirs = context.getExternalFilesDirs(null);
if (dirs != null && dirs.length >= 2) {
final File externalDir = dirs[1];
Os.symlink(externalDir.getAbsolutePath(), new File(storageDir, "external").getAbsolutePath());
if (dirs != null && dirs.length > 1) {
for (int i = 1; i < dirs.length; i++) {
File dir = dirs[i];
if (dir == null) continue;
String symlinkName = "external-" + i;
Os.symlink(dir.getAbsolutePath(), new File(storageDir, symlinkName).getAbsolutePath());
}
}
} catch (Exception e) {
Log.e(LOG_TAG, "Error setting up link", e);

View File

@@ -3,7 +3,6 @@ package com.termux.app;
import android.content.Context;
import android.media.AudioManager;
import android.support.v4.widget.DrawerLayout;
import android.util.Log;
import android.view.Gravity;
import android.view.InputDevice;
import android.view.KeyEvent;
@@ -62,7 +61,7 @@ public final class TermuxKeyListener implements TerminalKeyListener {
if (keyCode == KeyEvent.KEYCODE_ENTER && !currentSession.isRunning()) {
mActivity.removeFinishedSession(currentSession);
return true;
} else if (e.isCtrlPressed() && e.isShiftPressed()) {
} else if (e.isCtrlPressed() && e.isAltPressed()) {
// Get the unmodified code point:
int unicodeChar = e.getUnicodeChar(0);
@@ -161,7 +160,7 @@ public final class TermuxKeyListener implements TerminalKeyListener {
resultingKeyCode = KeyEvent.KEYCODE_INSERT;
break;
case 'h':
resultingKeyCode = KeyEvent.KEYCODE_MOVE_HOME;
resultingCodePoint = '~';
break;
// Special characters to input.

View File

@@ -105,7 +105,7 @@ final class TermuxPreferences {
}
static void storeCurrentSession(Context context, TerminalSession session) {
PreferenceManager.getDefaultSharedPreferences(context).edit().putString(TermuxPreferences.CURRENT_SESSION_KEY, session.mHandle).commit();
PreferenceManager.getDefaultSharedPreferences(context).edit().putString(TermuxPreferences.CURRENT_SESSION_KEY, session.mHandle).apply();
}
static TerminalSession getCurrentSession(TermuxActivity context) {

View File

@@ -11,6 +11,7 @@ import android.content.res.Resources;
import android.net.Uri;
import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManager;
import android.util.Log;
@@ -48,18 +49,16 @@ public final class TermuxService extends Service implements SessionChangedCallba
private static final int NOTIFICATION_ID = 1337;
/** Intent action to stop the service. */
private static final String ACTION_STOP_SERVICE = "com.termux.service_stop";
/** Intent action to toggle the wake lock, {@link #mWakeLock}, which this service may hold. */
private static final String ACTION_LOCK_WAKE = "com.termux.service_toggle_wake_lock";
/** Intent action to toggle the wifi lock, {@link #mWifiLock}, which this service may hold. */
private static final String ACTION_LOCK_WIFI = "com.termux.service_toggle_wifi_lock";
private static final String ACTION_LOCK_WAKE = "com.termux.service_wake_lock";
private static final String ACTION_UNLOCK_WAKE = "com.termux.service_wake_unlock";
/** Intent action to launch a new terminal session. Executed from TermuxWidgetProvider. */
public static final String ACTION_EXECUTE = "com.termux.service_execute";
public static final String EXTRA_ARGUMENTS = "com.termux.execute.arguments";
public static final String EXTRA_CURRENT_WORKING_DIRECTORY = "com.termux.execute.cwd";
private static final String EXTRA_EXECUTE_IN_BACKGROUND = "com.termux.execute.background";
/** This service is only bound from inside the same process and never uses IPC. */
class LocalBinder extends Binder {
@@ -68,6 +67,8 @@ public final class TermuxService extends Service implements SessionChangedCallba
private final IBinder mBinder = new LocalBinder();
private final Handler mHandler = new Handler();
/**
* The terminal sessions which this service manages.
* <p/>
@@ -76,9 +77,12 @@ public final class TermuxService extends Service implements SessionChangedCallba
*/
final List<TerminalSession> mTerminalSessions = new ArrayList<>();
final List<BackgroundJob> mBackgroundTasks = new ArrayList<>();
/** Note that the service may often outlive the activity, so need to clear this reference. */
SessionChangedCallback mSessionChangeCallback;
/** The wake lock and wifi lock are always acquired and released together. */
private PowerManager.WakeLock mWakeLock;
private WifiManager.WifiLock mWifiLock;
@@ -99,41 +103,51 @@ public final class TermuxService extends Service implements SessionChangedCallba
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, EmulatorDebug.LOG_TAG);
mWakeLock.acquire();
} else {
mWakeLock.release();
mWakeLock = null;
}
updateNotification();
} else if (ACTION_LOCK_WIFI.equals(action)) {
if (mWifiLock == null) {
WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
mWifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, EmulatorDebug.LOG_TAG);
mWifiLock.acquire();
} else {
updateNotification();
}
} else if (ACTION_UNLOCK_WAKE.equals(action)) {
if (mWakeLock != null) {
mWakeLock.release();
mWakeLock = null;
mWifiLock.release();
mWifiLock = null;
updateNotification();
}
updateNotification();
} else if (ACTION_EXECUTE.equals(action)) {
Uri executableUri = intent.getData();
String executablePath = (executableUri == null ? null : executableUri.getPath());
String[] arguments = (executableUri == null ? null : intent.getStringArrayExtra(EXTRA_ARGUMENTS));
String cwd = intent.getStringExtra(EXTRA_CURRENT_WORKING_DIRECTORY);
TerminalSession newSession = createTermSession(executablePath, arguments, cwd, false);
// Transform executable path to session name, e.g. "/bin/do-something.sh" => "do something.sh".
if (executablePath != null) {
int lastSlash = executablePath.lastIndexOf('/');
String name = (lastSlash == -1) ? executablePath : executablePath.substring(lastSlash + 1);
name = name.replace('-', ' ');
newSession.mSessionName = name;
if (intent.getBooleanExtra(EXTRA_EXECUTE_IN_BACKGROUND, false)) {
BackgroundJob task = new BackgroundJob(cwd, executablePath, arguments, this);
mBackgroundTasks.add(task);
updateNotification();
} else {
TerminalSession newSession = createTermSession(executablePath, arguments, cwd, false);
// Transform executable path to session name, e.g. "/bin/do-something.sh" => "do something.sh".
if (executablePath != null) {
int lastSlash = executablePath.lastIndexOf('/');
String name = (lastSlash == -1) ? executablePath : executablePath.substring(lastSlash + 1);
name = name.replace('-', ' ');
newSession.mSessionName = name;
}
// Make the newly created session the current one to be displayed:
TermuxPreferences.storeCurrentSession(this, newSession);
// Launch the main Termux app, which will now show the current session:
startActivity(new Intent(this, TermuxActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
// Make the newly created session the current one to be displayed:
TermuxPreferences.storeCurrentSession(this, newSession);
// Launch the main Termux app, which will now show to current session:
startActivity(new Intent(this, TermuxActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
} else if (action != null) {
Log.e(EmulatorDebug.LOG_TAG, "Unknown TermuxService action: '" + action + "'");
}
@@ -155,8 +169,8 @@ public final class TermuxService extends Service implements SessionChangedCallba
/** Update the shown foreground service notification after making any changes that affect it. */
private void updateNotification() {
if (mWakeLock == null && mWifiLock == null && getSessions().isEmpty()) {
// Exit if we are updating after the user disabled all locks with no sessions.
if (mWakeLock == null && mTerminalSessions.isEmpty() && mBackgroundTasks.isEmpty()) {
// Exit if we are updating after the user disabled all locks with no sessions or tasks running.
stopSelf();
} else {
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).notify(NOTIFICATION_ID, buildNotification());
@@ -171,18 +185,15 @@ public final class TermuxService extends Service implements SessionChangedCallba
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notifyIntent, 0);
int sessionCount = mTerminalSessions.size();
String contentText = sessionCount + " terminal session" + (sessionCount == 1 ? "" : "s");
boolean wakeLockHeld = mWakeLock != null;
boolean wifiLockHeld = mWifiLock != null;
if (wakeLockHeld && wifiLockHeld) {
contentText += " (wake&wifi lock held)";
} else if (wakeLockHeld) {
contentText += " (wake lock held)";
} else if (wifiLockHeld) {
contentText += " (wifi lock held)";
int taskCount = mBackgroundTasks.size();
String contentText = sessionCount + " session" + (sessionCount == 1 ? "" : "s");
if (taskCount > 0) {
contentText += ", " + taskCount + " task" + (taskCount == 1 ? "" : "s");
}
final boolean wakeLockHeld = mWakeLock != null;
if (wakeLockHeld) contentText += " (wake lock held)";
Notification.Builder builder = new Notification.Builder(this);
builder.setContentTitle(getText(R.string.application_name));
builder.setContentText(contentText);
@@ -192,7 +203,7 @@ public final class TermuxService extends Service implements SessionChangedCallba
// If holding a wake or wifi lock consider the notification of high priority since it's using power,
// otherwise use a minimal priority since this is just a background service notification:
builder.setPriority((wakeLockHeld || wifiLockHeld) ? Notification.PRIORITY_HIGH : Notification.PRIORITY_MIN);
builder.setPriority((wakeLockHeld) ? Notification.PRIORITY_HIGH : Notification.PRIORITY_MIN);
// No need to show a timestamp:
builder.setShowWhen(false);
@@ -204,13 +215,13 @@ public final class TermuxService extends Service implements SessionChangedCallba
Intent exitIntent = new Intent(this, TermuxService.class).setAction(ACTION_STOP_SERVICE);
builder.addAction(android.R.drawable.ic_delete, res.getString(R.string.notification_action_exit), PendingIntent.getService(this, 0, exitIntent, 0));
Intent toggleWakeLockIntent = new Intent(this, TermuxService.class).setAction(ACTION_LOCK_WAKE);
builder.addAction(android.R.drawable.ic_lock_lock, res.getString(R.string.notification_action_wakelock),
PendingIntent.getService(this, 0, toggleWakeLockIntent, 0));
Intent toggleWifiLockIntent = new Intent(this, TermuxService.class).setAction(ACTION_LOCK_WIFI);
builder.addAction(android.R.drawable.ic_lock_lock, res.getString(R.string.notification_action_wifilock),
PendingIntent.getService(this, 0, toggleWifiLockIntent, 0));
String newWakeAction = wakeLockHeld ? ACTION_UNLOCK_WAKE : ACTION_LOCK_WAKE;
Intent toggleWakeLockIntent = new Intent(this, TermuxService.class).setAction(newWakeAction);
String actionTitle = res.getString(wakeLockHeld ?
R.string.notification_action_wake_unlock :
R.string.notification_action_wake_lock);
int actionIcon = wakeLockHeld ? android.R.drawable.ic_lock_idle_lock : android.R.drawable.ic_lock_lock;
builder.addAction(actionIcon, actionTitle, PendingIntent.getService(this, 0, toggleWakeLockIntent, 0));
return builder.build();
}
@@ -236,30 +247,9 @@ public final class TermuxService extends Service implements SessionChangedCallba
if (cwd == null) cwd = HOME_PATH;
final String termEnv = "TERM=xterm-256color";
final String homeEnv = "HOME=" + HOME_PATH;
final String prefixEnv = "PREFIX=" + PREFIX_PATH;
final String androidRootEnv = "ANDROID_ROOT=" + System.getenv("ANDROID_ROOT");
final String androidDataEnv = "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.
final String externalStorageEnv = "EXTERNAL_STORAGE=" + System.getenv("EXTERNAL_STORAGE");
String[] env;
if (failSafe) {
// Keep the default path so that system binaries can be used in the failsafe session.
final String pathEnv = "PATH=" + System.getenv("PATH");
env = new String[]{termEnv, homeEnv, prefixEnv, androidRootEnv, androidDataEnv, pathEnv, externalStorageEnv};
} else {
final String ps1Env = "PS1=$ ";
final String ldEnv = "LD_LIBRARY_PATH=" + PREFIX_PATH + "/lib";
final String langEnv = "LANG=en_US.UTF-8";
final String pathEnv = "PATH=" + PREFIX_PATH + "/bin:" + PREFIX_PATH + "/bin/applets";
final String pwdEnv = "PWD=" + cwd;
String[] env = BackgroundJob.buildEnvironment(failSafe, cwd);
boolean isLoginShell = false;
env = new String[]{termEnv, homeEnv, prefixEnv, ps1Env, ldEnv, langEnv, pathEnv, pwdEnv, androidRootEnv, androidDataEnv, externalStorageEnv};
}
String shellName;
if (executablePath == null) {
File shell = new File(HOME_PATH, ".termux/shell");
if (shell.exists()) {
@@ -290,23 +280,18 @@ public final class TermuxService extends Service implements SessionChangedCallba
// Fall back to system shell as last resort:
executablePath = "/system/bin/sh";
}
String[] parts = executablePath.split("/");
shellName = "-" + parts[parts.length - 1];
} else {
int lastSlashIndex = executablePath.lastIndexOf('/');
shellName = lastSlashIndex == -1 ? executablePath : executablePath.substring(lastSlashIndex + 1);
isLoginShell = true;
}
String[] args;
if (arguments == null) {
args = new String[]{shellName};
} else {
args = new String[arguments.length + 1];
args[0] = shellName;
String[] processArgs = BackgroundJob.setupProcessArgs(executablePath, arguments);
executablePath = processArgs[0];
int lastSlashIndex = executablePath.lastIndexOf('/');
String processName = (isLoginShell ? "-" : "") +
(lastSlashIndex == -1 ? executablePath : executablePath.substring(lastSlashIndex + 1));
System.arraycopy(arguments, 0, args, 1, arguments.length);
}
String[] args = new String[processArgs.length];
args[0] = processName;
if (processArgs.length > 1) System.arraycopy(processArgs, 1, args, 1, processArgs.length - 1);
TerminalSession session = new TerminalSession(executablePath, cwd, args, env, this);
mTerminalSessions.add(session);
@@ -358,4 +343,13 @@ public final class TermuxService extends Service implements SessionChangedCallba
if (mSessionChangeCallback != null) mSessionChangeCallback.onColorsChanged(session);
}
public void onBackgroundJobExited(final BackgroundJob task) {
mHandler.post(new Runnable() {
@Override
public void run() {
mBackgroundTasks.remove(task);
updateNotification();
}
});
}
}

View File

@@ -75,7 +75,7 @@ public class TermuxDocumentsProvider extends DocumentsProvider {
row.add(Root.COLUMN_TITLE, applicationName);
row.add(Root.COLUMN_MIME_TYPES, ALL_MIME_TYPES);
row.add(Root.COLUMN_AVAILABLE_BYTES, BASE_DIR.getFreeSpace());
row.add(Root.COLUMN_ICON, R.drawable.ic_launcher);
row.add(Root.COLUMN_ICON, R.mipmap.ic_launcher);
return result;
}
@@ -236,7 +236,7 @@ public class TermuxDocumentsProvider extends DocumentsProvider {
row.add(Document.COLUMN_MIME_TYPE, mimeType);
row.add(Document.COLUMN_LAST_MODIFIED, file.lastModified());
row.add(Document.COLUMN_FLAGS, flags);
row.add(Document.COLUMN_ICON, R.drawable.ic_launcher);
row.add(Document.COLUMN_ICON, R.mipmap.ic_launcher);
}
}

View File

@@ -26,9 +26,9 @@ import static android.view.KeyEvent.KEYCODE_F7;
import static android.view.KeyEvent.KEYCODE_F8;
import static android.view.KeyEvent.KEYCODE_F9;
import static android.view.KeyEvent.KEYCODE_FORWARD_DEL;
import static android.view.KeyEvent.KEYCODE_HOME;
import static android.view.KeyEvent.KEYCODE_INSERT;
import static android.view.KeyEvent.KEYCODE_MOVE_END;
import static android.view.KeyEvent.KEYCODE_MOVE_HOME;
import static android.view.KeyEvent.KEYCODE_NUMPAD_0;
import static android.view.KeyEvent.KEYCODE_NUMPAD_1;
import static android.view.KeyEvent.KEYCODE_NUMPAD_2;
@@ -66,7 +66,7 @@ public final class KeyHandler {
// terminfo: http://pubs.opengroup.org/onlinepubs/7990989799/xcurses/terminfo.html
// termcap: http://man7.org/linux/man-pages/man5/termcap.5.html
TERMCAP_TO_KEYCODE.put("%i", KEYMOD_SHIFT | KEYCODE_DPAD_RIGHT);
TERMCAP_TO_KEYCODE.put("#2", KEYMOD_SHIFT | KEYCODE_HOME); // Shifted home
TERMCAP_TO_KEYCODE.put("#2", KEYMOD_SHIFT | KEYCODE_MOVE_HOME); // Shifted home
TERMCAP_TO_KEYCODE.put("#4", KEYMOD_SHIFT | KEYCODE_DPAD_LEFT);
TERMCAP_TO_KEYCODE.put("*7", KEYMOD_SHIFT | KEYCODE_MOVE_END); // Shifted end key
@@ -98,7 +98,7 @@ public final class KeyHandler {
TERMCAP_TO_KEYCODE.put("kb", KEYCODE_DEL); // backspace key
TERMCAP_TO_KEYCODE.put("kd", KEYCODE_DPAD_DOWN); // terminfo=kcud1, down-arrow key
TERMCAP_TO_KEYCODE.put("kh", KEYCODE_HOME);
TERMCAP_TO_KEYCODE.put("kh", KEYCODE_MOVE_HOME);
TERMCAP_TO_KEYCODE.put("kl", KEYCODE_DPAD_LEFT);
TERMCAP_TO_KEYCODE.put("kr", KEYCODE_DPAD_RIGHT);
@@ -107,7 +107,7 @@ public final class KeyHandler {
// t_K3 <kPageUp> keypad page-up key
// t_K4 <kEnd> keypad end key
// t_K5 <kPageDown> keypad page-down key
TERMCAP_TO_KEYCODE.put("K1", KEYCODE_HOME);
TERMCAP_TO_KEYCODE.put("K1", KEYCODE_MOVE_HOME);
TERMCAP_TO_KEYCODE.put("K3", KEYCODE_PAGE_UP);
TERMCAP_TO_KEYCODE.put("K4", KEYCODE_MOVE_END);
TERMCAP_TO_KEYCODE.put("K5", KEYCODE_PAGE_DOWN);
@@ -162,7 +162,9 @@ public final class KeyHandler {
case KEYCODE_DPAD_LEFT:
return (keyMode == 0) ? (cursorApp ? "\033OD" : "\033[D") : transformForModifiers("\033[1", keyMode, 'D');
case KEYCODE_HOME:
case KEYCODE_MOVE_HOME:
// Note that KEYCODE_HOME is handled by the system and never delivered to applications.
// On a Logitech k810 keyboard KEYCODE_MOVE_HOME is sent by FN+LeftArrow.
return (keyMode == 0) ? (cursorApp ? "\033OH" : "\033[H") : transformForModifiers("\033[1", keyMode, 'H');
case KEYCODE_MOVE_END:
return (keyMode == 0) ? (cursorApp ? "\033OF" : "\033[F") : transformForModifiers("\033[1", keyMode, 'F');
@@ -217,9 +219,6 @@ public final class KeyHandler {
case KEYCODE_FORWARD_DEL:
return transformForModifiers("\033[3", keyMode, '~');
case KEYCODE_NUMPAD_DOT:
return keypadApplication ? "\033On" : "\033[3~";
case KEYCODE_PAGE_UP:
return "\033[5~";
case KEYCODE_PAGE_DOWN:
@@ -249,12 +248,14 @@ public final class KeyHandler {
return keypadApplication ? transformForModifiers("\033O", keyMode, 'k') : "+";
case KEYCODE_NUMPAD_COMMA:
return ",";
case KEYCODE_NUMPAD_DOT:
return keypadApplication ? "\033On" : ".";
case KEYCODE_NUMPAD_SUBTRACT:
return keypadApplication ? transformForModifiers("\033O", keyMode, 'm') : "-";
case KEYCODE_NUMPAD_DIVIDE:
return keypadApplication ? transformForModifiers("\033O", keyMode, 'o') : "/";
case KEYCODE_NUMPAD_0:
return keypadApplication ? transformForModifiers("\033O", keyMode, 'p') : "1";
return keypadApplication ? transformForModifiers("\033O", keyMode, 'p') : "0";
case KEYCODE_NUMPAD_1:
return keypadApplication ? transformForModifiers("\033O", keyMode, 'q') : "1";
case KEYCODE_NUMPAD_2:

View File

@@ -140,7 +140,7 @@ public final class TerminalBuffer {
* @param newRows The number of rows the screen should have.
* @param cursor An int[2] containing the (column, row) cursor location.
*/
public void resize(int newColumns, int newRows, int newTotalRows, int[] cursor, int currentStyle, boolean altScreen) {
public void resize(int newColumns, int newRows, int newTotalRows, int[] cursor, long currentStyle, boolean altScreen) {
// newRows > mTotalRows should not normally happen since mTotalRows is TRANSCRIPT_ROWS (10000):
if (newColumns == mColumns && newRows <= mTotalRows) {
// Fast resize where just the rows changed.
@@ -237,7 +237,7 @@ public final class TerminalBuffer {
}
int currentOldCol = 0;
int styleAtCol = 0;
long styleAtCol = 0;
for (int i = 0; i < lastNonSpaceIndex; i++) {
// Note that looping over java character, not cells.
char c = oldLine.mText[i];
@@ -321,7 +321,7 @@ public final class TerminalBuffer {
* @param bottomMargin One line after the last line that is scrolled.
* @param style the style for the newly exposed line.
*/
public void scrollDownOneLine(int topMargin, int bottomMargin, int style) {
public void scrollDownOneLine(int topMargin, int bottomMargin, long style) {
if (topMargin > bottomMargin - 1 || topMargin < 0 || bottomMargin > mScreenRows)
throw new IllegalArgumentException("topMargin=" + topMargin + ", bottomMargin=" + bottomMargin + ", mScreenRows=" + mScreenRows);
@@ -374,7 +374,7 @@ public final class TerminalBuffer {
* InvalidParemeterException will be thrown. Typically this is called with a "val" argument of 32 to clear a block
* of characters.
*/
public void blockSet(int sx, int sy, int w, int h, int val, int style) {
public void blockSet(int sx, int sy, int w, int h, int val, long style) {
if (sx < 0 || sx + w > mColumns || sy < 0 || sy + h > mScreenRows) {
throw new IllegalArgumentException(
"Illegal arguments! blockSet(" + sx + ", " + sy + ", " + w + ", " + h + ", " + val + ", " + mColumns + ", " + mScreenRows + ")");
@@ -388,14 +388,14 @@ public final class TerminalBuffer {
return (mLines[row] == null) ? (mLines[row] = new TerminalRow(mColumns, 0)) : mLines[row];
}
public void setChar(int column, int row, int codePoint, int style) {
public void setChar(int column, int row, int codePoint, long style) {
if (row >= mScreenRows || column >= mColumns)
throw new IllegalArgumentException("row=" + row + ", column=" + column + ", mScreenRows=" + mScreenRows + ", mColumns=" + mColumns);
row = externalToInternalRow(row);
allocateFullLineIfNecessary(row).setChar(column, codePoint, style);
}
public int getStyleAt(int externalRow, int column) {
public long getStyleAt(int externalRow, int column) {
return allocateFullLineIfNecessary(externalToInternalRow(externalRow)).getStyle(column);
}
@@ -407,7 +407,7 @@ public final class TerminalBuffer {
int startOfLine = (rectangular || y == top) ? left : leftMargin;
int endOfLine = (rectangular || y + 1 == bottom) ? right : rightMargin;
for (int x = startOfLine; x < endOfLine; x++) {
int currentStyle = line.getStyle(x);
long currentStyle = line.getStyle(x);
int foreColor = TextStyle.decodeForeColor(currentStyle);
int backColor = TextStyle.decodeBackColor(currentStyle);
int effect = TextStyle.decodeEffect(currentStyle);

View File

@@ -57,7 +57,7 @@ public final class TerminalColorScheme {
0xff808080, 0xff8a8a8a, 0xff949494, 0xff9e9e9e, 0xffa8a8a8, 0xffb2b2b2, 0xffbcbcbc, 0xffc6c6c6, 0xffd0d0d0, 0xffdadada, 0xffe4e4e4, 0xffeeeeee,
// COLOR_INDEX_DEFAULT_FOREGROUND, COLOR_INDEX_DEFAULT_BACKGROUND and COLOR_INDEX_DEFAULT_CURSOR:
0xffffffff, 0xff000000, 0xffffffff};
0xffffffff, 0xff000000, 0xffA9AAA9};
public final int[] mDefaultColors = new int[TextStyle.NUM_INDEXED_COLORS];

View File

@@ -12,7 +12,7 @@ import java.util.Stack;
/**
* Renders text into a screen. Contains all the terminal-specific knowledge and state. Emulates a subset of the X Window
* System xterm terminal, which in turn is an emulator for a subset of the Digital Equipment Corporation vt100 terminal.
* <p/>
* <p>
* References:
* <ul>
* <li>http://invisible-island.net/xterm/ctlseqs/ctlseqs.html</li>
@@ -145,7 +145,7 @@ public final class TerminalEmulator {
/**
* The alternate screen buffer, exactly as large as the display and contains no additional saved lines (so that when
* the alternate screen buffer is active, you cannot scroll back to view saved lines).
* <p/>
* <p>
* See http://www.xfree86.org/current/ctlseqs.html#The%20Alternate%20Screen%20Buffer
*/
final TerminalBuffer mAltBuffer;
@@ -206,10 +206,15 @@ public final class TerminalEmulator {
*/
private boolean mAboutToAutoWrap;
/** Foreground and background color indices, 0..255. */
/**
* Current foreground and background colors. Can either be a color index in [0,259] or a truecolor (24-bit) value.
* For a 24-bit value the top byte (0xff000000) is set.
*
* @see TextStyle
*/
int mForeColor, mBackColor;
/** Current TextStyle effect */
/** Current {@link TextStyle} effect. */
private int mEffect;
/**
@@ -611,7 +616,7 @@ public final class TerminalEmulator {
int left = Math.min(getArg(argIndex++, 1, true) + effectiveLeftMargin, effectiveRightMargin + 1);
int bottom = Math.min(getArg(argIndex++, mRows, true) + effectiveTopMargin, effectiveBottomMargin);
int right = Math.min(getArg(argIndex, mColumns, true) + effectiveLeftMargin, effectiveRightMargin);
int style = getStyle();
long style = getStyle();
for (int row = top - 1; row < bottom; row++)
for (int col = left - 1; col < right; col++)
if (!selective || (TextStyle.decodeEffect(mScreen.getStyleAt(row, col)) & TextStyle.CHARACTER_ATTRIBUTE_PROTECTED) == 0)
@@ -623,7 +628,7 @@ public final class TerminalEmulator {
case 't': // "${CSI}${TOP}${LEFT}${BOTTOM}${RIGHT}${ATTRIBUTES}$t"
// Reverse attributes in rectangular area (DECRARA - http://www.vt100.net/docs/vt510-rm/DECRARA).
boolean reverse = b == 't';
// FIXME: "coordinates of the rectangular area are affected by the setting of origin mode (DECOM)".s
// FIXME: "coordinates of the rectangular area are affected by the setting of origin mode (DECOM)".
int top = Math.min(getArg(0, 1, true) - 1, effectiveBottomMargin) + effectiveTopMargin;
int left = Math.min(getArg(1, 1, true) - 1, effectiveRightMargin) + effectiveLeftMargin;
int bottom = Math.min(getArg(2, mRows, true) + 1, effectiveBottomMargin - 1) + effectiveTopMargin;
@@ -717,7 +722,6 @@ public final class TerminalEmulator {
}
break;
case ESC_PERCENT:
Log.i(EmulatorDebug.LOG_TAG, "Ignoring character set sequence 'ESC % " + (char) b + "'");
break;
case ESC_OSC:
doOsc(b);
@@ -953,7 +957,7 @@ public final class TerminalEmulator {
unknownSequence(b);
break;
}
int style = getStyle();
long style = getStyle();
for (int row = startRow; row < endRow; row++) {
for (int col = startCol; col < endCol; col++) {
if ((TextStyle.decodeEffect(mScreen.getStyleAt(row, col)) & TextStyle.CHARACTER_ATTRIBUTE_PROTECTED) == 0)
@@ -1687,43 +1691,42 @@ public final class TerminalEmulator {
} else if (code >= 30 && code <= 37) {
mForeColor = code - 30;
} else if (code == 38 || code == 48) {
// ISO-8613-3 controls to set foreground (38) or background (48) colors.
// P_s = (38|48) ; 2 ; P_r ; P_g ; P_b => Set to RGB value in range (0-255).
// P_s = (38|48) ; 5 ; P_s => Set to indexed color.
if (i + 2 <= mArgIndex) {
int color = -1;
int firstArg = mArgs[i + 1];
if (firstArg == 2) {
if (i + 4 > mArgIndex) {
Log.w(EmulatorDebug.LOG_TAG, "Too few CSI" + code + ";2 RGB arguments");
} else {
int red = mArgs[i + 2], green = mArgs[i + 3], blue = mArgs[i + 4];
if (red < 0 || green < 0 || blue < 0 || red > 255 || green > 255 || blue > 255) {
finishSequenceAndLogError("Invalid RGB: " + red + "," + green + "," + blue);
} else {
// TODO: Implement 24 bit color.
finishSequenceAndLogError("Unimplemented RGB: " + red + "," + green + "," + blue);
}
i += 4; // "2;P_r;P_g;P_r"
}
} else if (firstArg == 5) {
color = mArgs[i + 2];
i += 2; // "5;P_s"
// Extended set foreground(38)/background (48) color.
// This is followed by either "2;$R;$G;$B" to set a 24-bit color or
// "5;$INDEX" to set an indexed color.
if (i + 2 > mArgIndex) continue;
int firstArg = mArgs[i + 1];
if (firstArg == 2) {
if (i + 4 > mArgIndex) {
Log.w(EmulatorDebug.LOG_TAG, "Too few CSI" + code + ";2 RGB arguments");
} else {
finishSequenceAndLogError("Invalid ISO-8613-3 SGR first argument: " + firstArg);
}
if (i != -1) {
if (color >= 0 && color < TextStyle.NUM_INDEXED_COLORS) {
if (code == 38) {
mForeColor = color;
} else {
mBackColor = color;
}
int red = mArgs[i + 2], green = mArgs[i + 3], blue = mArgs[i + 4];
if (red < 0 || green < 0 || blue < 0 || red > 255 || green > 255 || blue > 255) {
finishSequenceAndLogError("Invalid RGB: " + red + "," + green + "," + blue);
} else {
if (LOG_ESCAPE_SEQUENCES)
Log.w(EmulatorDebug.LOG_TAG, "Invalid color index: " + color);
int argbColor = 0xff000000 | (red << 16) | (green << 8) | blue;
if (code == 38) {
mForeColor = argbColor;
} else {
mBackColor = argbColor;
}
}
i += 4; // "2;P_r;P_g;P_r"
}
} else if (firstArg == 5) {
int color = mArgs[i + 2];
i += 2; // "5;P_s"
if (color >= 0 && color < TextStyle.NUM_INDEXED_COLORS) {
if (code == 38) {
mForeColor = color;
} else {
mBackColor = color;
}
} else {
if (LOG_ESCAPE_SEQUENCES) Log.w(EmulatorDebug.LOG_TAG, "Invalid color index: " + color);
}
} else {
finishSequenceAndLogError("Invalid ISO-8613-3 SGR first argument: " + firstArg);
}
} else if (code == 39) { // Set default foreground color.
mForeColor = TextStyle.COLOR_INDEX_FOREGROUND;
@@ -1924,7 +1927,7 @@ public final class TerminalEmulator {
mScreen.blockSet(sx, sy, w, h, ' ', getStyle());
}
private int getStyle() {
private long getStyle() {
return TextStyle.encode(mForeColor, mBackColor, mEffect);
}

View File

@@ -20,13 +20,13 @@ public final class TerminalRow {
/** If this row has been line wrapped due to text output at the end of line. */
boolean mLineWrap;
/** The style bits of each cell in the row. See {@link TextStyle}. */
final int[] mStyle;
final long[] mStyle;
/** Construct a blank row (containing only whitespace, ' ') with a specified style. */
public TerminalRow(int columns, int style) {
public TerminalRow(int columns, long style) {
mColumns = columns;
mText = new char[(int) (SPARE_CAPACITY_FACTOR * columns)];
mStyle = new int[columns];
mStyle = new long[columns];
clear(style);
}
@@ -112,14 +112,14 @@ public final class TerminalRow {
return false;
}
public void clear(int style) {
public void clear(long style) {
Arrays.fill(mText, ' ');
Arrays.fill(mStyle, style);
mSpaceUsed = (short) mColumns;
}
// https://github.com/steven676/Android-Terminal-Emulator/commit/9a47042620bec87617f0b4f5d50568535668fe26
public void setChar(int columnToSet, int codePoint, int style) {
public void setChar(int columnToSet, int codePoint, long style) {
mStyle[columnToSet] = style;
final int newCodePointDisplayWidth = WcWidth.width(codePoint);
@@ -225,7 +225,7 @@ public final class TerminalRow {
return true;
}
public final int getStyle(int column) {
public final long getStyle(int column) {
return mStyle[column];
}

View File

@@ -173,7 +173,7 @@ public final class TerminalSession extends TerminalOutput {
* @param rows The number of rows in the terminal window.
*/
public void initializeEmulator(int columns, int rows) {
mEmulator = new TerminalEmulator(this, columns, rows, /* transcript= */5000);
mEmulator = new TerminalEmulator(this, columns, rows, /* transcript= */2000);
int[] processId = new int[1];
mTerminalFileDescriptor = JNI.createSubprocess(mShellPath, mCwd, mArgs, mEnv, processId, rows, columns);

View File

@@ -1,11 +1,13 @@
package com.termux.terminal;
/**
* Encodes effects, foreground and background colors into a 32 bit integer, which are stored for each cell in a terminal
* Encodes effects, foreground and background colors into a 64 bit long, which are stored for each cell in a terminal
* row in {@link TerminalRow#mStyle}.
* <p/>
* The foreground and background colors take 9 bits each, leaving (32-9-9)=14 bits for effect flags. Using 9 for now
* (the different CHARACTER_ATTRIBUTE_* bits).
* The bit layout is:
* - 16 flags (11 currently used).
* - 24 for foreground color (only 9 first bits if a color index).
* - 24 for background color (only 9 first bits if a color index).
*/
public final class TextStyle {
@@ -25,6 +27,10 @@ public final class TextStyle {
public final static int CHARACTER_ATTRIBUTE_PROTECTED = 1 << 7;
/** Dim colors. Also known as faint or half intensity. */
public final static int CHARACTER_ATTRIBUTE_DIM = 1 << 8;
/** If true (24-bit) color is used for the cell for foreground. */
private final static int CHARACTER_ATTRIBUTE_TRUECOLOR_FOREGROUND = 1 << 9;
/** If true (24-bit) color is used for the cell for foreground. */
private final static int CHARACTER_ATTRIBUTE_TRUECOLOR_BACKGROUND= 1 << 10;
public final static int COLOR_INDEX_FOREGROUND = 256;
public final static int COLOR_INDEX_BACKGROUND = 257;
@@ -34,22 +40,47 @@ public final class TextStyle {
public final static int NUM_INDEXED_COLORS = 259;
/** Normal foreground and background colors and no effects. */
final static int NORMAL = encode(COLOR_INDEX_FOREGROUND, COLOR_INDEX_BACKGROUND, 0);
final static long NORMAL = encode(COLOR_INDEX_FOREGROUND, COLOR_INDEX_BACKGROUND, 0);
static int encode(int foreColor, int backColor, int effect) {
return ((effect & 0b111111111) << 18) | ((foreColor & 0b111111111) << 9) | (backColor & 0b111111111);
static long encode(int foreColor, int backColor, int effect) {
long result = effect & 0b111111111;
if ((0xff000000 & foreColor) == 0xff000000) {
// 24-bit color.
result |= CHARACTER_ATTRIBUTE_TRUECOLOR_FOREGROUND | ((foreColor & 0x00ffffffL) << 40L);
} else {
// Indexed color.
result |= (foreColor & 0b111111111L) << 40;
}
if ((0xff000000 & backColor) == 0xff000000) {
// 24-bit color.
result |= CHARACTER_ATTRIBUTE_TRUECOLOR_BACKGROUND | ((backColor & 0x00ffffffL) << 16L);
} else {
// Indexed color.
result |= (backColor & 0b111111111L) << 16L;
}
return result;
}
public static int decodeForeColor(int encodedColor) {
return (encodedColor >> 9) & 0b111111111;
public static int decodeForeColor(long style) {
if ((style & CHARACTER_ATTRIBUTE_TRUECOLOR_FOREGROUND) == 0) {
return (int) ((style >>> 40) & 0b111111111L);
} else {
return 0xff000000 | (int) ((style >>> 40) & 0x00ffffffL);
}
}
public static int decodeBackColor(int encodedColor) {
return encodedColor & 0b111111111;
public static int decodeBackColor(long style) {
if ((style & CHARACTER_ATTRIBUTE_TRUECOLOR_BACKGROUND) == 0) {
return (int) ((style >>> 16) & 0b111111111L);
} else {
return 0xff000000 | (int) ((style >>> 16) & 0x00ffffffL);
}
}
public static int decodeEffect(int encodedColor) {
return (encodedColor >> 18) & 0b111111111;
public static int decodeEffect(long style) {
return (int) (style & 0b11111111111);
}
}

View File

@@ -54,7 +54,8 @@ final class TerminalRenderer {
}
/** Render the terminal to a canvas with at a specified row scroll, and an optional rectangular selection. */
public final void render(TerminalEmulator mEmulator, Canvas canvas, int topRow, int selectionY1, int selectionY2, int selectionX1, int selectionX2) {
public final void render(TerminalEmulator mEmulator, Canvas canvas, int topRow,
int selectionY1, int selectionY2, int selectionX1, int selectionX2) {
final boolean reverseVideo = mEmulator.isReverseVideo();
final int endRow = topRow + mEmulator.mRows;
final int columns = mEmulator.mColumns;
@@ -63,6 +64,7 @@ final class TerminalRenderer {
final boolean cursorVisible = mEmulator.isShowingCursor();
final TerminalBuffer screen = mEmulator.getScreen();
final int[] palette = mEmulator.mColors.mCurrentColors;
final int cursorShape = mEmulator.getCursorStyle();
if (reverseVideo)
canvas.drawColor(palette[TextStyle.COLOR_INDEX_FOREGROUND], PorterDuff.Mode.SRC);
@@ -82,7 +84,7 @@ final class TerminalRenderer {
final char[] line = lineObject.mText;
final int charsUsedInLine = lineObject.getSpaceUsed();
int lastRunStyle = 0;
long lastRunStyle = 0;
boolean lastRunInsideCursor = false;
int lastRunStartColumn = -1;
int lastRunStartIndex = 0;
@@ -97,7 +99,7 @@ final class TerminalRenderer {
final int codePoint = charIsHighsurrogate ? Character.toCodePoint(charAtIndex, line[currentCharIndex + 1]) : charAtIndex;
final int codePointWcWidth = WcWidth.width(codePoint);
final boolean insideCursor = (column >= selx1 && column <= selx2) || (cursorX == column || (codePointWcWidth == 2 && cursorX == column + 1));
final int style = lineObject.getStyle(column);
final long style = lineObject.getStyle(column);
// Check if the measured text width for this code point is not the same as that expected by wcwidth().
// This could happen for some fonts which are not truly monospace, or for more exotic characters such as
@@ -113,8 +115,10 @@ final class TerminalRenderer {
} else {
final int columnWidthSinceLastRun = column - lastRunStartColumn;
final int charsSinceLastRun = currentCharIndex - lastRunStartIndex;
drawTextRun(canvas, line, palette, heightOffset, lastRunStartColumn, columnWidthSinceLastRun, lastRunStartIndex, charsSinceLastRun,
measuredWidthForRun, lastRunInsideCursor, lastRunStyle, reverseVideo);
int cursorColor = lastRunInsideCursor ? mEmulator.mColors.mCurrentColors[TextStyle.COLOR_INDEX_CURSOR] : 0;
drawTextRun(canvas, line, palette, heightOffset, lastRunStartColumn, columnWidthSinceLastRun,
lastRunStartIndex, charsSinceLastRun, measuredWidthForRun,
cursorColor, cursorShape, lastRunStyle, reverseVideo);
}
measuredWidthForRun = 0.f;
lastRunStyle = style;
@@ -135,29 +139,42 @@ final class TerminalRenderer {
final int columnWidthSinceLastRun = columns - lastRunStartColumn;
final int charsSinceLastRun = currentCharIndex - lastRunStartIndex;
int cursorColor = lastRunInsideCursor ? mEmulator.mColors.mCurrentColors[TextStyle.COLOR_INDEX_CURSOR] : 0;
drawTextRun(canvas, line, palette, heightOffset, lastRunStartColumn, columnWidthSinceLastRun, lastRunStartIndex, charsSinceLastRun,
measuredWidthForRun, lastRunInsideCursor, lastRunStyle, reverseVideo);
measuredWidthForRun, cursorColor, cursorShape, lastRunStyle, reverseVideo);
}
}
/**
* @param canvas the canvas to render on
* @param palette the color palette to look up colors from textStyle
* @param y height offset into the canvas where to render the line: line * {@link #mFontLineSpacing}
* @param startColumn the run offset in columns
* @param runWidthColumns the run width in columns - this is computed from wcwidth() and may not be what the font measures to
* @param text the java char array to render text from
* @param startCharIndex index into the text array where to start
* @param runWidthChars number of java characters from the text array to render
* @param cursor true if rendering a cursor or selection
* @param textStyle the background, foreground and effect encoded using {@link TextStyle}
* @param reverseVideo if the screen is rendered with the global reverse video flag set
*/
private void drawTextRun(Canvas canvas, char[] text, int[] palette, float y, int startColumn, int runWidthColumns, int startCharIndex, int runWidthChars,
float mes, boolean cursor, int textStyle, boolean reverseVideo) {
private void drawTextRun(Canvas canvas, char[] text, int[] palette, float y, int startColumn, int runWidthColumns,
int startCharIndex, int runWidthChars, float mes, int cursor, int cursorStyle,
long textStyle, boolean reverseVideo) {
int foreColor = TextStyle.decodeForeColor(textStyle);
int backColor = TextStyle.decodeBackColor(textStyle);
final int effect = TextStyle.decodeEffect(textStyle);
int backColor = TextStyle.decodeBackColor(textStyle);
final boolean bold = (effect & (TextStyle.CHARACTER_ATTRIBUTE_BOLD | TextStyle.CHARACTER_ATTRIBUTE_BLINK)) != 0;
final boolean underline = (effect & TextStyle.CHARACTER_ATTRIBUTE_UNDERLINE) != 0;
final boolean italic = (effect & TextStyle.CHARACTER_ATTRIBUTE_ITALIC) != 0;
final boolean strikeThrough = (effect & TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH) != 0;
final boolean dim = (effect & TextStyle.CHARACTER_ATTRIBUTE_DIM) != 0;
if ((foreColor & 0xff000000) != 0xff000000) {
// Let bold have bright colors if applicable (one of the first 8):
if (bold && foreColor >= 0 && foreColor < 8) foreColor += 8;
foreColor = palette[foreColor];
}
if ((backColor & 0xff000000) != 0xff000000) {
backColor = palette[backColor];
}
// Reverse video here if _one and only one_ of the reverse flags are set:
final boolean reverseVideoHere = reverseVideo ^ (effect & (TextStyle.CHARACTER_ATTRIBUTE_INVERSE)) != 0;
if (reverseVideoHere) {
int tmp = foreColor;
foreColor = backColor;
backColor = tmp;
}
float left = startColumn * mFontWidth;
float right = left + runWidthColumns * mFontWidth;
@@ -171,50 +188,38 @@ final class TerminalRenderer {
savedMatrix = true;
}
// Reverse video here if _one and only one_ of the reverse flags are set:
boolean reverseVideoHere = reverseVideo ^ (effect & (TextStyle.CHARACTER_ATTRIBUTE_INVERSE)) != 0;
// Switch if _one and only one_ of reverse video and cursor is set:
if (reverseVideoHere ^ cursor) {
int tmp = foreColor;
foreColor = backColor;
backColor = tmp;
}
if (backColor != TextStyle.COLOR_INDEX_BACKGROUND) {
if (backColor != palette[TextStyle.COLOR_INDEX_BACKGROUND]) {
// Only draw non-default background.
mTextPaint.setColor(palette[backColor]);
mTextPaint.setColor(backColor);
canvas.drawRect(left, y - mFontLineSpacingAndAscent + mFontAscent, right, y, mTextPaint);
}
if (cursor != 0) {
mTextPaint.setColor(cursor);
float cursorHeight = mFontLineSpacingAndAscent - mFontAscent;
if (cursorStyle == TerminalEmulator.CURSOR_STYLE_UNDERLINE) cursorHeight /= 4.;
else if (cursorStyle == TerminalEmulator.CURSOR_STYLE_BAR) right -= ((right - left) * 3) / 4.;
canvas.drawRect(left, y - cursorHeight, right, y, mTextPaint);
}
if ((effect & TextStyle.CHARACTER_ATTRIBUTE_INVISIBLE) == 0) {
// Treat blink as bold:
final boolean bold = (effect & (TextStyle.CHARACTER_ATTRIBUTE_BOLD | TextStyle.CHARACTER_ATTRIBUTE_BLINK)) != 0;
final boolean underline = (effect & TextStyle.CHARACTER_ATTRIBUTE_UNDERLINE) != 0;
final boolean italic = (effect & TextStyle.CHARACTER_ATTRIBUTE_ITALIC) != 0;
final boolean strikeThrough = (effect & TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH) != 0;
final boolean dim = (effect & TextStyle.CHARACTER_ATTRIBUTE_DIM) != 0;
// Let bold have bright colors if applicable (one of the first 8):
final int actualForeColor = foreColor + (bold && foreColor < 8 ? 8 : 0);
int foreColorARGB = palette[actualForeColor];
if (dim) {
int red = (0xFF & (foreColorARGB >> 16));
int green = (0xFF & (foreColorARGB >> 8));
int blue = (0xFF & foreColorARGB);
int red = (0xFF & (foreColor >> 16));
int green = (0xFF & (foreColor >> 8));
int blue = (0xFF & foreColor);
// Dim color handling used by libvte which in turn took it from xterm
// (https://bug735245.bugzilla-attachments.gnome.org/attachment.cgi?id=284267):
red = red * 2 / 3;
green = green * 2 / 3;
blue = blue * 2 / 3;
foreColorARGB = 0xFF000000 + (red << 16) + (green << 8) + blue;
foreColor = 0xFF000000 + (red << 16) + (green << 8) + blue;
}
mTextPaint.setFakeBoldText(bold);
mTextPaint.setUnderlineText(underline);
mTextPaint.setTextSkewX(italic ? -0.35f : 0.f);
mTextPaint.setStrikeThruText(strikeThrough);
mTextPaint.setColor(foreColorARGB);
mTextPaint.setColor(foreColor);
// The text alignment is the default Paint.Align.LEFT.
canvas.drawText(text, startCharIndex, runWidthChars, left, y - mFontLineSpacingAndAscent, mTextPaint);

View File

@@ -10,6 +10,7 @@ import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.os.Build;
import android.text.Editable;
import android.text.InputType;
import android.text.TextUtils;
import android.util.AttributeSet;
@@ -233,30 +234,53 @@ public final class TerminalView extends View {
// Previous keyboard issues:
// https://github.com/termux/termux-packages/issues/25
// https://github.com/termux/termux-app/issues/87.
// https://github.com/termux/termux-app/issues/126 for breakage from that.
// https://github.com/termux/termux-app/issues/126.
// https://github.com/termux/termux-app/issues/137 (japanese chars and TYPE_NULL).
outAttrs.inputType = InputType.TYPE_NULL;
// Let part of the application show behind when in landscape:
outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_FULLSCREEN;
outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_FULLSCREEN |
EditorInfo.IME_FLAG_NO_ENTER_ACTION |
EditorInfo.IME_ACTION_NONE;
return new BaseInputConnection(this, true) {
@Override
public boolean finishComposingText() {
if (LOG_KEY_EVENTS) Log.i(EmulatorDebug.LOG_TAG, "IME: finishComposingText()");
commitText(getEditable(), 0);
super.finishComposingText();
// Clear the editable.
sendTextToTerminal(getEditable());
getEditable().clear();
return true;
}
@Override
public boolean commitText(CharSequence text, int newCursorPosition) {
if (LOG_KEY_EVENTS)
if (LOG_KEY_EVENTS) {
Log.i(EmulatorDebug.LOG_TAG, "IME: commitText(\"" + text + "\", " + newCursorPosition + ")");
}
super.commitText(text, newCursorPosition);
if (mEmulator == null) return true;
Editable content = getEditable();
sendTextToTerminal(content);
content.clear();
return true;
}
@Override
public boolean deleteSurroundingText(int leftLength, int rightLength) {
if (LOG_KEY_EVENTS) {
Log.i(EmulatorDebug.LOG_TAG, "IME: deleteSurroundingText(" + leftLength + ", " + rightLength + ")");
}
// The stock Samsung keyboard with 'Auto check spelling' enabled sends leftLength > 1.
KeyEvent deleteKey = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL);
for (int i = 0; i < leftLength; i++) sendKeyEvent(deleteKey);
return super.deleteSurroundingText(leftLength, rightLength);
}
void sendTextToTerminal(CharSequence text) {
final int textLengthInChars = text.length();
for (int i = 0; i < textLengthInChars; i++) {
char firstChar = text.charAt(i);
@@ -297,35 +321,8 @@ public final class TerminalView extends View {
inputCodePoint(codePoint, ctrlHeld, false);
}
return true;
}
@Override
public boolean deleteSurroundingText(int leftLength, int rightLength) {
if (LOG_KEY_EVENTS)
Log.i(EmulatorDebug.LOG_TAG, "IME: deleteSurroundingText(" + leftLength + ", " + rightLength + ")");
// If leftLength=2 it may be due to a UTF-16 surrogate pair. So we cannot send
// multiple key events for that. Let's just hope that keyboards don't use
// leftLength > 1 for other purposes (such as holding down backspace for repeat).
sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
return true;
}
@Override
public boolean setComposingText(CharSequence text, int newCursorPosition) {
if (LOG_KEY_EVENTS)
Log.i(EmulatorDebug.LOG_TAG, "IME: setComposingText(\"" + text + "\", " + newCursorPosition + ")");
if (text.length() == 0) {
// Avoid log spam "SpannableStringBuilder: SPAN_EXCLUSIVE_EXCLUSIVE spans cannot
// have a zero length" when backspacing with the Google keyboard.
getEditable().clear();
} else {
super.setComposingText(text, newCursorPosition);
}
return true;
}
};
}

View File

@@ -0,0 +1,5 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE:= libtermux
LOCAL_SRC_FILES:= termux.c
include $(BUILD_SHARED_LIBRARY)

View File

@@ -0,0 +1,7 @@
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:duration="30"
android:fromXDelta="-1%"
android:repeatCount="1"
android:repeatMode="reverse"
android:toXDelta="1%"/>
</set>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 695 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 786 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 597 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 779 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 983 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,17 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFF"
android:pathData="M 12, 12
m -10.5, 0
a 10.5,10.5 0 1,0 21,0
a 10.5,10.5 0 1,0 -21,0"/>
<path
android:fillColor="#FF000000"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM17,13h-4v4h-2v-4L7,13v-2h4L11,7h2v4h4v2z"/>
</vector>

View File

@@ -0,0 +1,33 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="48dp"
android:width="48dp"
android:viewportWidth="48"
android:viewportHeight="48">
<!--
https://material.google.com/style/icons.html
-->
<!-- Screen border. -->
<path android:fillColor="#00000000"
android:strokeColor="#000"
android:strokeWidth="3"
android:pathData="M7,4
l34,0
q3 0,3 3
l0,34
q0 3, -3 3
l-34,0
q-3 0, -3-3
l0 -34
q0 -3, 3 -3"
/>
<!-- Block cursor. -->
<path android:fillColor="#000"
android:pathData="M12,12
l5,0
l0,10
l-5,0"
/>
</vector>

View File

@@ -7,7 +7,6 @@
android:imeOptions="actionSend|flagNoFullscreen"
android:maxLines="1"
android:inputType="text"
android:singleLine="true"
android:textColor="@android:color/white"
android:paddingTop="0dp"
android:textCursorDrawable="@null"

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 685 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@@ -50,8 +50,8 @@
<string name="styling_install">Install</string>
<string name="notification_action_exit">Exit</string>
<string name="notification_action_wakelock">Wake</string>
<string name="notification_action_wifilock">Wifi</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>

View File

@@ -0,0 +1,12 @@
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<shortcut
android:shortcutId="new_session"
android:enabled="true"
android:icon="@drawable/ic_new_session"
android:shortcutShortLabel="@string/new_session">
<intent
android:action="android.intent.action.RUN"
android:targetPackage="com.termux"
android:targetClass="com.termux.app.TermuxActivity"/>
</shortcut>
</shortcuts>

View File

@@ -18,7 +18,7 @@ public class CursorAndScreenTest extends TerminalTestCase {
assertLinesAre("ABCDE", "FGHIJ", "KLMNO", "PQRST", "UVWXY");
for (int row = 0; row < 5; row++) {
for (int col = 0; col < 5; col++) {
int s = getStyleAt(row, col);
long s = getStyleAt(row, col);
Assert.assertEquals(col, TextStyle.decodeForeColor(s));
Assert.assertEquals(row, TextStyle.decodeBackColor(s));
}
@@ -28,7 +28,7 @@ public class CursorAndScreenTest extends TerminalTestCase {
assertLinesAre("KLMNO", "PQRST", "UVWXY", " ", " ");
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 5; col++) {
int s = getStyleAt(row, col);
long s = getStyleAt(row, col);
Assert.assertEquals(col, TextStyle.decodeForeColor(s));
Assert.assertEquals(row + 2, TextStyle.decodeBackColor(s));
}
@@ -43,7 +43,7 @@ public class CursorAndScreenTest extends TerminalTestCase {
for (int col = 0; col < 5; col++) {
int wantedForeground = (row == 1 || row == 2) ? 98 : col;
int wantedBackground = (row == 1 || row == 2) ? 99 : (row == 0 ? 2 : row);
int s = getStyleAt(row, col);
long s = getStyleAt(row, col);
Assert.assertEquals(wantedForeground, TextStyle.decodeForeColor(s));
Assert.assertEquals(wantedBackground, TextStyle.decodeBackColor(s));
}

View File

@@ -141,10 +141,10 @@ public class KeyHandlerTest extends TestCase {
assertKeysEquals("\033[1;6D", KeyHandler.getCode(KeyEvent.KEYCODE_DPAD_LEFT, mod, false, false));
// Home/end keys:
assertKeysEquals("\033[H", KeyHandler.getCode(KeyEvent.KEYCODE_HOME, 0, false, false));
assertKeysEquals("\033[H", KeyHandler.getCode(KeyEvent.KEYCODE_MOVE_HOME, 0, false, false));
assertKeysEquals("\033[F", KeyHandler.getCode(KeyEvent.KEYCODE_MOVE_END, 0, false, false));
// ... shifted:
assertKeysEquals("\033[1;2H", KeyHandler.getCode(KeyEvent.KEYCODE_HOME, KeyHandler.KEYMOD_SHIFT, false, false));
assertKeysEquals("\033[1;2H", KeyHandler.getCode(KeyEvent.KEYCODE_MOVE_HOME, KeyHandler.KEYMOD_SHIFT, false, false));
assertKeysEquals("\033[1;2F", KeyHandler.getCode(KeyEvent.KEYCODE_MOVE_END, KeyHandler.KEYMOD_SHIFT, false, false));
// Function keys F1-F12:
@@ -173,5 +173,19 @@ public class KeyHandlerTest extends TestCase {
assertKeysEquals("\033[21;2~", KeyHandler.getCode(KeyEvent.KEYCODE_F10, KeyHandler.KEYMOD_SHIFT, false, false));
assertKeysEquals("\033[23;2~", KeyHandler.getCode(KeyEvent.KEYCODE_F11, KeyHandler.KEYMOD_SHIFT, false, false));
assertKeysEquals("\033[24;2~", KeyHandler.getCode(KeyEvent.KEYCODE_F12, KeyHandler.KEYMOD_SHIFT, false, false));
}
assertKeysEquals("0", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_0, 0, false, false));
assertKeysEquals("1", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_1, 0, false, false));
assertKeysEquals("2", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_2, 0, false, false));
assertKeysEquals("3", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_3, 0, false, false));
assertKeysEquals("4", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_4, 0, false, false));
assertKeysEquals("5", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_5, 0, false, false));
assertKeysEquals("6", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_6, 0, false, false));
assertKeysEquals("7", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_7, 0, false, false));
assertKeysEquals("8", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_8, 0, false, false));
assertKeysEquals("9", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_9, 0, false, false));
assertKeysEquals(",", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_COMMA, 0, false, false));
assertKeysEquals(".", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_DOT, 0, false, false));
}
}

View File

@@ -93,7 +93,7 @@ public class ResizeTest extends TerminalTestCase {
enterString("\033[2J");
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
int style = getStyleAt(r, c);
long style = getStyleAt(r, c);
assertEquals(119, TextStyle.decodeForeColor(style));
assertEquals(129, TextStyle.decodeBackColor(style));
}
@@ -105,7 +105,7 @@ public class ResizeTest extends TerminalTestCase {
// After resize, screen should still be same color:
for (int r = 0; r < rows - 2; r++) {
for (int c = 0; c < cols; c++) {
int style = getStyleAt(r, c);
long style = getStyleAt(r, c);
assertEquals(119, TextStyle.decodeForeColor(style));
assertEquals(129, TextStyle.decodeBackColor(style));
}
@@ -116,7 +116,7 @@ public class ResizeTest extends TerminalTestCase {
resize(cols, rows);
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
int style = getStyleAt(r, c);
long style = getStyleAt(r, c);
assertEquals(119, TextStyle.decodeForeColor(style));
assertEquals("wrong at row=" + r, r >= 3 ? 200 : 129, TextStyle.decodeBackColor(style));
}

View File

@@ -1,6 +1,6 @@
package com.termux.terminal;
public class ScreenBufferTest extends TerminalTest {
public class ScreenBufferTest extends TerminalTestCase {
public void testBasics() {
TerminalBuffer screen = new TerminalBuffer(5, 3, 3);

View File

@@ -147,12 +147,11 @@ public class TerminalTest extends TerminalTestCase {
enterString("\033[38;5;119m");
assertEquals(119, mTerminal.mForeColor);
assertEquals(TextStyle.COLOR_INDEX_BACKGROUND, mTerminal.mBackColor);
enterString("\033[48;5;129m");
assertEquals(119, mTerminal.mForeColor);
assertEquals(129, mTerminal.mBackColor);
// Invalid parameter:
// Invalid parameter:
enterString("\033[48;8;129m");
assertEquals(119, mTerminal.mForeColor);
assertEquals(129, mTerminal.mBackColor);
@@ -161,7 +160,31 @@ public class TerminalTest extends TerminalTestCase {
enterString("\033[38;5;178;48;5;179;m");
assertEquals(178, mTerminal.mForeColor);
assertEquals(179, mTerminal.mBackColor);
}
// 24 bit colors:
enterString(("\033[0m")); // Reset fg and bg colors.
enterString("\033[38;2;255;127;2m");
int expectedForeground = 0xff000000 | (255 << 16) | (127 << 8) | 2;
assertEquals(expectedForeground, mTerminal.mForeColor);
assertEquals(TextStyle.COLOR_INDEX_BACKGROUND, mTerminal.mBackColor);
enterString("\033[48;2;1;2;254m");
int expectedBackground = 0xff000000 | (1 << 16) | (2 << 8) | 254;
assertEquals(expectedForeground, mTerminal.mForeColor);
assertEquals(expectedBackground, mTerminal.mBackColor);
// 24 bit colors, set fg and bg at once:
enterString(("\033[0m")); // Reset fg and bg colors.
assertEquals(TextStyle.COLOR_INDEX_FOREGROUND, mTerminal.mForeColor);
assertEquals(TextStyle.COLOR_INDEX_BACKGROUND, mTerminal.mBackColor);
enterString("\033[38;2;255;127;2;48;2;1;2;254m");
assertEquals(expectedForeground, mTerminal.mForeColor);
assertEquals(expectedBackground, mTerminal.mBackColor);
// 24 bit colors, invalid input:
enterString("\033[38;2;300;127;2;48;2;1;300;254m");
assertEquals(expectedForeground, mTerminal.mForeColor);
assertEquals(expectedBackground, mTerminal.mBackColor);
}
public void testBackgroundColorErase() {
final int rows = 3;
@@ -169,7 +192,7 @@ public class TerminalTest extends TerminalTestCase {
withTerminalSized(cols, rows);
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
int style = getStyleAt(r, c);
long style = getStyleAt(r, c);
assertEquals(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.decodeForeColor(style));
assertEquals(TextStyle.COLOR_INDEX_BACKGROUND, TextStyle.decodeBackColor(style));
}
@@ -182,7 +205,7 @@ public class TerminalTest extends TerminalTestCase {
enterString("\033[2J");
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
int style = getStyleAt(r, c);
long style = getStyleAt(r, c);
assertEquals(119, TextStyle.decodeForeColor(style));
assertEquals(129, TextStyle.decodeBackColor(style));
}
@@ -193,7 +216,7 @@ public class TerminalTest extends TerminalTestCase {
enterString("\033[2L");
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
int style = getStyleAt(r, c);
long style = getStyleAt(r, c);
assertEquals((r == 0 || r == 1) ? 139 : 129, TextStyle.decodeBackColor(style));
}
}

View File

@@ -240,7 +240,7 @@ public abstract class TerminalTestCase extends TestCase {
}
/** For testing only. Encoded style according to {@link TextStyle}. */
public int getStyleAt(int externalRow, int column) {
public long getStyleAt(int externalRow, int column) {
return mTerminal.getScreen().getStyleAt(externalRow, column);
}
@@ -296,7 +296,7 @@ public abstract class TerminalTestCase extends TestCase {
}
public void assertForegroundColorAt(int externalRow, int column, int color) {
int style = mTerminal.getScreen().mLines[mTerminal.getScreen().externalToInternalRow(externalRow)].getStyle(column);
long style = mTerminal.getScreen().mLines[mTerminal.getScreen().externalToInternalRow(externalRow)].getStyle(column);
assertEquals(color, TextStyle.decodeForeColor(style));
}

View File

@@ -13,7 +13,7 @@ public class TextStyleTest extends TestCase {
for (int fx : ALL_EFFECTS) {
for (int fg = 0; fg < TextStyle.NUM_INDEXED_COLORS; fg++) {
for (int bg = 0; bg < TextStyle.NUM_INDEXED_COLORS; bg++) {
int encoded = TextStyle.encode(fg, bg, fx);
long encoded = TextStyle.encode(fg, bg, fx);
assertEquals(fg, TextStyle.decodeForeColor(encoded));
assertEquals(bg, TextStyle.decodeBackColor(encoded));
assertEquals(fx, TextStyle.decodeEffect(encoded));
@@ -22,7 +22,23 @@ public class TextStyleTest extends TestCase {
}
}
public void testEncodingCombinations() {
public void testEncoding24Bit() {
int[] values = {255, 240, 127, 1, 0};
for (int red : values) {
for (int green : values) {
for (int blue : values) {
int argb = 0xFF000000 | (red << 16) | (green << 8) | blue;
long encoded = TextStyle.encode(argb, 0, 0);
assertEquals(argb, TextStyle.decodeForeColor(encoded));
encoded = TextStyle.encode(0, argb, 0);
assertEquals(argb, TextStyle.decodeBackColor(encoded));
}
}
}
}
public void testEncodingCombinations() {
for (int f1 : ALL_EFFECTS) {
for (int f2 : ALL_EFFECTS) {
int combined = f1 | f2;
@@ -32,13 +48,13 @@ public class TextStyleTest extends TestCase {
}
public void testEncodingStrikeThrough() {
int encoded = TextStyle.encode(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.COLOR_INDEX_BACKGROUND,
long encoded = TextStyle.encode(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.COLOR_INDEX_BACKGROUND,
TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH);
assertTrue((TextStyle.decodeEffect(encoded) & TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH) != 0);
}
public void testEncodingProtected() {
int encoded = TextStyle.encode(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.COLOR_INDEX_BACKGROUND,
long encoded = TextStyle.encode(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.COLOR_INDEX_BACKGROUND,
TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH);
assertTrue((TextStyle.decodeEffect(encoded) & TextStyle.CHARACTER_ATTRIBUTE_PROTECTED) == 0);
encoded = TextStyle.encode(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.COLOR_INDEX_BACKGROUND,

20
art/copy-to-other-apps.sh Executable file
View File

@@ -0,0 +1,20 @@
#!/bin/sh
for DENSITY in mdpi hdpi xhdpi xxhdpi xxxhdpi; do
FOLDER=../app/src/main/res/mipmap-$DENSITY
for FILE in ic_launcher ic_launcher_round; do
PNG=$FOLDER/$FILE.png
# Update other apps:
for APP in tasker widget api; do
APPDIR=../../termux-$APP
if [ -d $APPDIR ]; then
APP_FOLDER=$APPDIR/app/src/main/res/mipmap-$DENSITY
mkdir -p $APP_FOLDER
cp $PNG $APP_FOLDER/$FILE.png
fi
done
done
done

20
art/generate-pngs.sh Executable file
View File

@@ -0,0 +1,20 @@
#!/bin/sh
for DENSITY in mdpi hdpi xhdpi xxhdpi xxxhdpi; do
case $DENSITY in
mdpi) SIZE=48;;
hdpi) SIZE=72;;
xhdpi) SIZE=96;;
xxhdpi) SIZE=144;;
xxxhdpi) SIZE=192;;
esac
FOLDER=../app/src/main/res/mipmap-$DENSITY
mkdir -p $FOLDER
for FILE in ic_launcher ic_launcher_round; do
PNG=$FOLDER/$FILE.png
rsvg-convert -w $SIZE -h $SIZE $FILE.svg > $PNG
zopflipng -y $PNG $PNG
done
done

26
art/ic_launcher.svg Normal file
View File

@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<!-- Screen and border. -->
<path fill="#000"
stroke="#BFCBCD"
stroke-width="2"
d="M7,4
l34,0
q3 0,3 3
l0,34
q0 3, -3 3
l-34,0
q-3 0, -3-3
l0 -34
q0 -3, 3 -3"
/>
<!-- Block cursor. -->
<path fill="#FFF"
d="M12,12
l5,0
l0,10
l-5,0"
/>
</svg>

After

Width:  |  Height:  |  Size: 512 B

21
art/ic_launcher_round.svg Normal file
View File

@@ -0,0 +1,21 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<!-- Screen and border. -->
<path fill="#000"
stroke="#BFCBCD"
stroke-width="2"
d="M 24, 24
m -21, 0
a 21,21 0 1,0 42,0
a 21,21 0 1,0 -42,0"
/>
<!-- Block cursor. -->
<path fill="#FFF"
d="M15,15
l5,0
l0,10
l-5,0"
/>
</svg>

After

Width:  |  Height:  |  Size: 429 B

View File

@@ -5,10 +5,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath 'com.android.tools.build:gradle:2.2.3'
}
}

View File

@@ -12,6 +12,4 @@
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
#Fri May 13 01:11:09 CEST 2016
org.gradle.jvmargs=-Xmx2048M
android.useDeprecatedNdk=true

Binary file not shown.

View File

@@ -1,6 +1,6 @@
#Sat Jul 23 17:08:29 CEST 2016
#Sun Dec 04 17:26:05 CET 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-3.2.1-bin.zip

22
gradlew vendored
View File

@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/usr/bin/env sh
##############################################################################
##
@@ -154,11 +154,19 @@ if $cygwin ; then
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
# Escape application args
save ( ) {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
APP_ARGS=$(save "$@")
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
gradlew.bat vendored Normal file
View File

@@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega