Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed1874db05 | ||
|
|
cb60803a80 | ||
|
|
fc92a27cb2 | ||
|
|
29e62e608f | ||
|
|
8a7f93d722 | ||
|
|
420683fe65 | ||
|
|
a3256ed551 | ||
|
|
57add98e3c | ||
|
|
6e5c04e04f | ||
|
|
528a05ef61 | ||
|
|
cb2f892dc5 | ||
|
|
8b6e8d7fdd | ||
|
|
d0eeaa9fc3 | ||
|
|
b793913481 | ||
|
|
d48b438c40 | ||
|
|
11afe895e1 | ||
|
|
bc96f71a2d | ||
|
|
87dfded5e6 | ||
|
|
7c0ae4cb54 | ||
|
|
b917acbbfa | ||
|
|
7d9d6fb797 | ||
|
|
74040dd37f | ||
|
|
e94f06d0f7 | ||
|
|
af21b6dc3e | ||
|
|
e0e8257f1c |
@@ -1,4 +1,4 @@
|
|||||||
sudo: true
|
sudo: false
|
||||||
language: android
|
language: android
|
||||||
jdk: oraclejdk8
|
jdk: oraclejdk8
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@ env:
|
|||||||
global:
|
global:
|
||||||
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
|
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
|
||||||
# via the "travis encrypt" command using the project repo's public key
|
# via the "travis encrypt" command using the project repo's public key
|
||||||
- secure: "ACnFJxw0VusS2lnGXL+epP/CNJmftWS39YcPdgN2EurWw5ZfXSo7vi+zpMB+11IBS3LQyLFFUambi2N9L4lbReZkHVkoVcZFGZlwbXNTAeqT8CABPTcuOyEOZU4bJwqeYU87ztYipENMLNECaZrgWx5odbWLKnSJQw7Zkb4ArCstfXfYk9u8q49ThRxQyGwHW2xKp1an5aa+3Y6IY+ywsSHw6AvXbyFH078Kolxy86caagczcfmKcMi15QYzwAvFggUphvsO3M5PHJMQXuaNlQxDcQRGUEXsK8aZE0dPH5PB97SFjDALZqI7NEpjZAk5htWjX48ssW064LDbjcBg/ZLgDd8R8uhA159NVZgvcnP2czCn6pmggx1sW5MBmcj7i+bJS2ejaMO+KoovWlVvsch742H5QR6rQaNkjDZRsGVLYvJaR1gBLs898UoT1hcHWoqLVR22r2VFo7OWWCRfNRvZuZDR2HIrYRdFvn8P3nWVMkvXwgsOlxWG5sN+yQqW+6lZS7hivsFhtYs4CkRdoZIan3Qvi/CkY8Lg+ESkZ3IJ0NnId8qOWH+8Xl1sqZ7xlsWTd1sYYHlpvkdvqw1HNLP22EpwwKW5Kb5zBEd/qs3o1OO0Tqa0MR6JpgGdHHRk1iZ25+qTfRVP06vO2RXsgAx4SZfO7DyB0QZn8tGNMMI="
|
- secure: "LdajbHNfRlpnqzhX5KY2Vr7KtzU9vXDs1TCNn93J6Dt522f2AaiyUDJvISvz+uslk0WJiS5bB5vGwQmXginxz6Qi6uMgMbjWXulv1vfs6ZviKpUX348DOp1qKPa8WfVNB66F84SwGIfc8cRMAgCFw79l/DFgLErubF8vKo1wZ8Hmvrz//+RJ0BGMa3YRc4VyJhAL0P+0Wc1Q2Im7R9EovAxC5pZXBIMSgr6g5GzLWPisbNLXpMPGsDeYhcenO6XCtCCy+aNxUYM8vcrLDzlVXR5Hy7KEs/MGRTS0Yk13TWUEYa5wBpKelFTszdWYLVn5ANreh/aXRVfHpnW3epotMYguLx1kSvOhWEnc4F+qqv3nle2LpDg9Y9bcLyTTcYnPl9smqEVVjEDu0FoIr1V58xkG4Oc6BPIvLRjlMVU96PXh2HxMLuGsJ/xM+uAFU9oVMbC07xn42Eu5O4NHOHJNOwMWac4/lSKRK8W/7/vWuXj5vhkD9ZsGVpN70UtY5HAfNUGADnTeDblvjgFTNZ2mUN/u0o7Z8ZFURYllZ9YU+Vr2nPf9CAhVBjuwFWx8uRQpAg1aDmc1dVMJijRBeBeU/uWhYqsGp34wkNEl8VGzob4R4QTyI8+T7CndGqKVmbTK/SjqKhjjPpbXIAfOH+JtxvAnNmb8XeQSJ32uK2nexFo="
|
||||||
|
|
||||||
android:
|
android:
|
||||||
components:
|
components:
|
||||||
@@ -19,7 +19,6 @@ android:
|
|||||||
before_install:
|
before_install:
|
||||||
- git clone https://github.com/urho3d/android-ndk.git $HOME/android-ndk
|
- git clone https://github.com/urho3d/android-ndk.git $HOME/android-ndk
|
||||||
- export ANDROID_NDK_HOME=$HOME/android-ndk
|
- export ANDROID_NDK_HOME=$HOME/android-ndk
|
||||||
- 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:
|
script:
|
||||||
- ./gradlew testDebugUnitTest
|
- ./gradlew testDebugUnitTest
|
||||||
|
|||||||
@@ -5,16 +5,16 @@ android {
|
|||||||
buildToolsVersion "25.0.2"
|
buildToolsVersion "25.0.2"
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile 'com.android.support:support-annotations:25.0.1'
|
compile 'com.android.support:support-annotations:25.1.0'
|
||||||
compile "com.android.support:support-v4:25.0.1"
|
compile "com.android.support:support-v4:25.1.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.termux"
|
applicationId "com.termux"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 25
|
targetSdkVersion 25
|
||||||
versionCode 45
|
versionCode 47
|
||||||
versionName "0.45"
|
versionName "0.47"
|
||||||
|
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
ndkBuild {
|
ndkBuild {
|
||||||
|
|||||||
@@ -90,6 +90,14 @@
|
|||||||
android:name="com.termux.app.TermuxService"
|
android:name="com.termux.app.TermuxService"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
|
<receiver android:name=".app.TermuxOpenReceiver" />
|
||||||
|
|
||||||
|
<provider android:authorities="com.termux.files"
|
||||||
|
android:readPermission="android.permission.permRead"
|
||||||
|
android:exported="true"
|
||||||
|
android:grantUriPermissions="true"
|
||||||
|
android:name="com.termux.app.TermuxOpenReceiver$ContentProvider" />
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import java.lang.reflect.Field;
|
|||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -105,7 +106,6 @@ public final class BackgroundJob {
|
|||||||
// 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.
|
||||||
final String externalStorageEnv = "EXTERNAL_STORAGE=" + System.getenv("EXTERNAL_STORAGE");
|
final String externalStorageEnv = "EXTERNAL_STORAGE=" + System.getenv("EXTERNAL_STORAGE");
|
||||||
String[] env;
|
|
||||||
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.
|
||||||
final String pathEnv = "PATH=" + System.getenv("PATH");
|
final String pathEnv = "PATH=" + System.getenv("PATH");
|
||||||
@@ -186,9 +186,7 @@ public final class BackgroundJob {
|
|||||||
List<String> result = new ArrayList<>();
|
List<String> result = new ArrayList<>();
|
||||||
if (interpreter != null) result.add(interpreter);
|
if (interpreter != null) result.add(interpreter);
|
||||||
result.add(fileToExecute);
|
result.add(fileToExecute);
|
||||||
if (args != null) {
|
if (args != null) Collections.addAll(result, args);
|
||||||
for (String arg : args) result.add(arg);
|
|
||||||
}
|
|
||||||
return result.toArray(new String[result.size()]);
|
return result.toArray(new String[result.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -141,8 +141,6 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
|||||||
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION).build()).build();
|
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION).build()).build();
|
||||||
int mBellSoundId;
|
int mBellSoundId;
|
||||||
|
|
||||||
Animation mOnBellAnimation;
|
|
||||||
|
|
||||||
private final BroadcastReceiver mBroadcastReceiever = new BroadcastReceiver() {
|
private final BroadcastReceiver mBroadcastReceiever = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
@@ -213,8 +211,6 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
|||||||
public void onCreate(Bundle bundle) {
|
public void onCreate(Bundle bundle) {
|
||||||
super.onCreate(bundle);
|
super.onCreate(bundle);
|
||||||
|
|
||||||
mOnBellAnimation = AnimationUtils.loadAnimation(this, R.anim.on_bell);
|
|
||||||
|
|
||||||
mSettings = new TermuxPreferences(this);
|
mSettings = new TermuxPreferences(this);
|
||||||
|
|
||||||
setContentView(R.layout.drawer_layout);
|
setContentView(R.layout.drawer_layout);
|
||||||
@@ -254,7 +250,9 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
|||||||
TerminalSession session = getCurrentTermSession();
|
TerminalSession session = getCurrentTermSession();
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
if (session.isRunning()) {
|
if (session.isRunning()) {
|
||||||
session.write(editText.getText().toString() + "\n");
|
String textToSend = editText.getText().toString();
|
||||||
|
if (textToSend.length() == 0) textToSend = "\n";
|
||||||
|
session.write(textToSend);
|
||||||
} else {
|
} else {
|
||||||
removeFinishedSession(session);
|
removeFinishedSession(session);
|
||||||
}
|
}
|
||||||
@@ -410,8 +408,6 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
|||||||
public void onBell(TerminalSession session) {
|
public void onBell(TerminalSession session) {
|
||||||
if (!mIsVisible) return;
|
if (!mIsVisible) return;
|
||||||
|
|
||||||
mTerminalView.startAnimation(mOnBellAnimation);
|
|
||||||
|
|
||||||
switch (mSettings.mBellBehaviour) {
|
switch (mSettings.mBellBehaviour) {
|
||||||
case TermuxPreferences.BELL_BEEP:
|
case TermuxPreferences.BELL_BEEP:
|
||||||
mBellSoundPool.play(mBellSoundId, 1.f, 1.f, 1, 0, 1.f);
|
mBellSoundPool.play(mBellSoundId, 1.f, 1.f, 1, 0, 1.f);
|
||||||
@@ -500,17 +496,6 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
|||||||
public void run() {
|
public void run() {
|
||||||
if (mTermService == null) return; // Activity might have been destroyed.
|
if (mTermService == null) return; // Activity might have been destroyed.
|
||||||
try {
|
try {
|
||||||
if (TermuxPreferences.isShowWelcomeDialog(TermuxActivity.this)) {
|
|
||||||
new AlertDialog.Builder(TermuxActivity.this).setTitle(R.string.welcome_dialog_title).setMessage(R.string.welcome_dialog_body)
|
|
||||||
.setCancelable(false).setPositiveButton(android.R.string.ok, null)
|
|
||||||
.setNegativeButton(R.string.welcome_dialog_dont_show_again_button, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
TermuxPreferences.disableWelcomeDialog(TermuxActivity.this);
|
|
||||||
dialog.dismiss();
|
|
||||||
}
|
|
||||||
}).show();
|
|
||||||
}
|
|
||||||
addNewSession(false, null);
|
addNewSession(false, null);
|
||||||
} catch (WindowManager.BadTokenException e) {
|
} catch (WindowManager.BadTokenException e) {
|
||||||
// Activity finished - ignore.
|
// Activity finished - ignore.
|
||||||
|
|||||||
171
app/src/main/java/com/termux/app/TermuxOpenReceiver.java
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
package com.termux.app;
|
||||||
|
|
||||||
|
import android.content.ActivityNotFoundException;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.MatrixCursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
|
import com.termux.terminal.EmulatorDebug;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
|
||||||
|
public class TermuxOpenReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
final Uri data = intent.getData();
|
||||||
|
if (data == null) {
|
||||||
|
Log.e(EmulatorDebug.LOG_TAG, "termux-open: Called without intent data");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final boolean isExternalUrl = data.getScheme() != null && !data.getScheme().equals("file");
|
||||||
|
if (isExternalUrl) {
|
||||||
|
Intent viewIntent = new Intent(Intent.ACTION_VIEW, data);
|
||||||
|
try {
|
||||||
|
context.startActivity(viewIntent);
|
||||||
|
} catch (ActivityNotFoundException e) {
|
||||||
|
Log.e(EmulatorDebug.LOG_TAG, "termux-open: No app handles the url " + data);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String filePath = data.getPath();
|
||||||
|
final String contentTypeExtra = intent.getStringExtra("content-type");
|
||||||
|
final boolean useChooser = intent.getBooleanExtra("chooser", false);
|
||||||
|
final String intentAction = intent.getAction() == null ? Intent.ACTION_VIEW : intent.getAction();
|
||||||
|
|
||||||
|
switch (intentAction) {
|
||||||
|
case Intent.ACTION_SEND:
|
||||||
|
case Intent.ACTION_VIEW:
|
||||||
|
// Ok.
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.e(EmulatorDebug.LOG_TAG, "Invalid action '" + intentAction + "', using 'view'");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
final File fileToShare = new File(filePath);
|
||||||
|
if (!(fileToShare.isFile() && fileToShare.canRead())) {
|
||||||
|
Log.e(EmulatorDebug.LOG_TAG, "termux-open: Not a readable file: '" + fileToShare.getAbsolutePath() + "'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent sendIntent = new Intent();
|
||||||
|
sendIntent.setAction(intentAction);
|
||||||
|
sendIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
|
||||||
|
String contentTypeToUse;
|
||||||
|
if (contentTypeExtra == null) {
|
||||||
|
String fileName = fileToShare.getName();
|
||||||
|
int lastDotIndex = fileName.lastIndexOf('.');
|
||||||
|
String fileExtension = fileName.substring(lastDotIndex + 1, fileName.length());
|
||||||
|
MimeTypeMap mimeTypes = MimeTypeMap.getSingleton();
|
||||||
|
// Lower casing makes it work with e.g. "JPG":
|
||||||
|
contentTypeToUse = mimeTypes.getMimeTypeFromExtension(fileExtension.toLowerCase());
|
||||||
|
if (contentTypeToUse == null) contentTypeToUse = "application/octet-stream";
|
||||||
|
} else {
|
||||||
|
contentTypeToUse = contentTypeExtra;
|
||||||
|
}
|
||||||
|
|
||||||
|
Uri uriToShare = Uri.withAppendedPath(Uri.parse("content://com.termux.files/"), filePath);
|
||||||
|
|
||||||
|
if (Intent.ACTION_SEND.equals(intentAction)) {
|
||||||
|
sendIntent.putExtra(Intent.EXTRA_STREAM, uriToShare);
|
||||||
|
sendIntent.setType(contentTypeToUse);
|
||||||
|
} else {
|
||||||
|
sendIntent.setDataAndType(uriToShare, contentTypeToUse);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useChooser) {
|
||||||
|
sendIntent = Intent.createChooser(sendIntent, null).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
context.startActivity(sendIntent);
|
||||||
|
} catch (ActivityNotFoundException e) {
|
||||||
|
Log.e(EmulatorDebug.LOG_TAG, "termux-open: No app handles the url " + data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ContentProvider extends android.content.ContentProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreate() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
||||||
|
File file = new File(uri.getPath());
|
||||||
|
|
||||||
|
if (projection == null) {
|
||||||
|
projection = new String[]{
|
||||||
|
MediaStore.MediaColumns.DISPLAY_NAME,
|
||||||
|
MediaStore.MediaColumns.SIZE,
|
||||||
|
MediaStore.MediaColumns._ID
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Object[] row = new Object[projection.length];
|
||||||
|
for (int i = 0; i < projection.length; i++) {
|
||||||
|
String column = projection[i];
|
||||||
|
Object value;
|
||||||
|
switch (column) {
|
||||||
|
case MediaStore.MediaColumns.DISPLAY_NAME:
|
||||||
|
value = file.getName();
|
||||||
|
break;
|
||||||
|
case MediaStore.MediaColumns.SIZE:
|
||||||
|
value = (int) file.length();
|
||||||
|
break;
|
||||||
|
case MediaStore.MediaColumns._ID:
|
||||||
|
value = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
|
row[i] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
MatrixCursor cursor = new MatrixCursor(projection);
|
||||||
|
cursor.addRow(row);
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType(Uri uri) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Uri insert(Uri uri, ContentValues values) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
|
||||||
|
File file = new File(uri.getPath());
|
||||||
|
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -36,7 +36,6 @@ final class TermuxPreferences {
|
|||||||
private static final String SHOW_EXTRA_KEYS_KEY = "show_extra_keys";
|
private static final String SHOW_EXTRA_KEYS_KEY = "show_extra_keys";
|
||||||
private static final String FONTSIZE_KEY = "fontsize";
|
private static final String FONTSIZE_KEY = "fontsize";
|
||||||
private static final String CURRENT_SESSION_KEY = "current_session";
|
private static final String CURRENT_SESSION_KEY = "current_session";
|
||||||
private static final String SHOW_WELCOME_DIALOG_KEY = "intro_dialog";
|
|
||||||
|
|
||||||
private boolean mFullScreen;
|
private boolean mFullScreen;
|
||||||
private int mFontSize;
|
private int mFontSize;
|
||||||
@@ -117,14 +116,6 @@ final class TermuxPreferences {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isShowWelcomeDialog(Context context) {
|
|
||||||
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(SHOW_WELCOME_DIALOG_KEY, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void disableWelcomeDialog(Context context) {
|
|
||||||
PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(SHOW_WELCOME_DIALOG_KEY, false).apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reloadFromProperties(Context context) {
|
public void reloadFromProperties(Context context) {
|
||||||
try {
|
try {
|
||||||
File propsFile = new File(TermuxService.HOME_PATH + "/.termux/termux.properties");
|
File propsFile = new File(TermuxService.HOME_PATH + "/.termux/termux.properties");
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import android.os.Binder;
|
|||||||
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.support.v4.content.WakefulBroadcastReceiver;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
|
|
||||||
@@ -23,7 +24,6 @@ import com.termux.terminal.TerminalSession;
|
|||||||
import com.termux.terminal.TerminalSession.SessionChangedCallback;
|
import com.termux.terminal.TerminalSession.SessionChangedCallback;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -152,6 +152,11 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
|||||||
Log.e(EmulatorDebug.LOG_TAG, "Unknown TermuxService action: '" + action + "'");
|
Log.e(EmulatorDebug.LOG_TAG, "Unknown TermuxService action: '" + action + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((flags & START_FLAG_REDELIVERY) == 0) {
|
||||||
|
// Service is started by WBR, not restarted by system, so release the WakeLock from WBR.
|
||||||
|
WakefulBroadcastReceiver.completeWakefulIntent(intent);
|
||||||
|
}
|
||||||
|
|
||||||
// If this service really do get killed, there is no point restarting it automatically - let the user do on next
|
// If this service really do get killed, there is no point restarting it automatically - let the user do on next
|
||||||
// start of {@link Term):
|
// start of {@link Term):
|
||||||
return Service.START_NOT_STICKY;
|
return Service.START_NOT_STICKY;
|
||||||
@@ -251,28 +256,11 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
|||||||
boolean isLoginShell = false;
|
boolean isLoginShell = false;
|
||||||
|
|
||||||
if (executablePath == null) {
|
if (executablePath == null) {
|
||||||
File shell = new File(HOME_PATH, ".termux/shell");
|
for (String shellBinary : new String[]{"login", "bash", "zsh"}) {
|
||||||
if (shell.exists()) {
|
File shellFile = new File(PREFIX_PATH + "/bin/" + shellBinary);
|
||||||
try {
|
if (shellFile.canExecute()) {
|
||||||
File canonicalFile = shell.getCanonicalFile();
|
executablePath = shellFile.getAbsolutePath();
|
||||||
if (canonicalFile.isFile() && canonicalFile.canExecute()) {
|
break;
|
||||||
executablePath = canonicalFile.getName().equals("busybox") ? (PREFIX_PATH + "/bin/ash") : canonicalFile.getAbsolutePath();
|
|
||||||
} else {
|
|
||||||
Log.w(EmulatorDebug.LOG_TAG, "$HOME/.termux/shell points to non-executable shell: " + canonicalFile.getAbsolutePath());
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(EmulatorDebug.LOG_TAG, "Error checking $HOME/.termux/shell", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (executablePath == null) {
|
|
||||||
// Try bash, zsh and ash in that order:
|
|
||||||
for (String shellBinary : new String[]{"bash", "zsh", "ash"}) {
|
|
||||||
File shellFile = new File(PREFIX_PATH + "/bin/" + shellBinary);
|
|
||||||
if (shellFile.canExecute()) {
|
|
||||||
executablePath = shellFile.getAbsolutePath();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -123,12 +123,12 @@ public final class TerminalSession extends TerminalOutput {
|
|||||||
String exitDescription = "\r\n[Process completed";
|
String exitDescription = "\r\n[Process completed";
|
||||||
if (exitCode > 0) {
|
if (exitCode > 0) {
|
||||||
// Non-zero process exit.
|
// Non-zero process exit.
|
||||||
exitDescription += " with code " + exitCode;
|
exitDescription += " (code " + exitCode + ")";
|
||||||
} else if (exitCode < 0) {
|
} else if (exitCode < 0) {
|
||||||
// Negated signal.
|
// Negated signal.
|
||||||
exitDescription += " with signal " + (-exitCode);
|
exitDescription += " (signal " + (-exitCode) + ")";
|
||||||
}
|
}
|
||||||
exitDescription += " - press Enter to close]";
|
exitDescription += " - press Enter]";
|
||||||
|
|
||||||
byte[] bytesToWrite = exitDescription.getBytes(StandardCharsets.UTF_8);
|
byte[] bytesToWrite = exitDescription.getBytes(StandardCharsets.UTF_8);
|
||||||
mEmulator.append(bytesToWrite, bytesToWrite.length);
|
mEmulator.append(bytesToWrite, bytesToWrite.length);
|
||||||
|
|||||||
@@ -238,9 +238,9 @@ public final class TerminalView extends View {
|
|||||||
// https://github.com/termux/termux-app/issues/137 (japanese chars and TYPE_NULL).
|
// https://github.com/termux/termux-app/issues/137 (japanese chars and TYPE_NULL).
|
||||||
outAttrs.inputType = InputType.TYPE_NULL;
|
outAttrs.inputType = InputType.TYPE_NULL;
|
||||||
|
|
||||||
outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_FULLSCREEN |
|
// Note that IME_ACTION_NONE cannot be used as that makes it impossible to input newlines using the on-screen
|
||||||
EditorInfo.IME_FLAG_NO_ENTER_ACTION |
|
// keyboard on Android TV (see https://github.com/termux/termux-app/issues/221).
|
||||||
EditorInfo.IME_ACTION_NONE;
|
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
|
||||||
|
|
||||||
return new BaseInputConnection(this, true) {
|
return new BaseInputConnection(this, true) {
|
||||||
|
|
||||||
@@ -298,6 +298,14 @@ public final class TerminalView extends View {
|
|||||||
|
|
||||||
boolean ctrlHeld = false;
|
boolean ctrlHeld = false;
|
||||||
if (codePoint <= 31 && codePoint != 27) {
|
if (codePoint <= 31 && codePoint != 27) {
|
||||||
|
if (codePoint == '\n') {
|
||||||
|
// The AOSP keyboard and descendants seems to send \n as text when the enter key is pressed,
|
||||||
|
// instead of a key event like most other keyboard apps. A terminal expects \r for the enter
|
||||||
|
// key (although when icrnl is enabled this doesn't make a difference - run 'stty -icrnl' to
|
||||||
|
// check the behaviour).
|
||||||
|
codePoint = '\r';
|
||||||
|
}
|
||||||
|
|
||||||
// E.g. penti keyboard for ctrl input.
|
// E.g. penti keyboard for ctrl input.
|
||||||
ctrlHeld = true;
|
ctrlHeld = true;
|
||||||
switch (codePoint) {
|
switch (codePoint) {
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
<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>
|
|
||||||
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 3.3 KiB |
@@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
<!-- Block cursor. -->
|
<!-- Block cursor. -->
|
||||||
<path android:fillColor="#000"
|
<path android:fillColor="#000"
|
||||||
android:pathData="M12,12
|
android:pathData="M14,14
|
||||||
l5,0
|
l5,0
|
||||||
l0,10
|
l0,10
|
||||||
l-5,0"
|
l-5,0"
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 343 B After Width: | Height: | Size: 338 B |
|
Before Width: | Height: | Size: 204 B After Width: | Height: | Size: 203 B |
|
Before Width: | Height: | Size: 324 B After Width: | Height: | Size: 331 B |
|
Before Width: | Height: | Size: 510 B After Width: | Height: | Size: 505 B |
|
Before Width: | Height: | Size: 685 B After Width: | Height: | Size: 691 B |
@@ -11,10 +11,6 @@
|
|||||||
<string name="share_transcript_title">Terminal transcript</string>
|
<string name="share_transcript_title">Terminal transcript</string>
|
||||||
<string name="help">Help</string>
|
<string name="help">Help</string>
|
||||||
|
|
||||||
<string name="welcome_dialog_title">Welcome to Termux</string>
|
|
||||||
<string name="welcome_dialog_body">Long press and select <i>More…</i> to show a menu where <i>Help</i> is available.\n\nExecute <b>apt update</b> to update the packages list before installing packages.</string>
|
|
||||||
<string name="welcome_dialog_dont_show_again_button">Do not show again</string>
|
|
||||||
|
|
||||||
<string name="bootstrap_installer_body">Installing…</string>
|
<string name="bootstrap_installer_body">Installing…</string>
|
||||||
<string name="bootstrap_error_title">Unable to install</string>
|
<string name="bootstrap_error_title">Unable to install</string>
|
||||||
<string name="bootstrap_error_body">Termux was unable to install the bootstrap packages.\n\nCheck your network connection and try again.</string>
|
<string name="bootstrap_error_body">Termux was unable to install the bootstrap packages.\n\nCheck your network connection and try again.</string>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<!-- NOTE: Cannot use "Light." since it hides the terminal scrollbar on the default black background. -->
|
<!-- NOTE: Cannot use "Light." since it hides the terminal scrollbar on the default black background. -->
|
||||||
<style name="Theme.Termux" parent="@android:style/Theme.Material.Light.NoActionBar">
|
<style name="Theme.Termux" parent="@android:style/Theme.Material.Light.NoActionBar">
|
||||||
<item name="android:statusBarColor">#000000</item>
|
<item name="android:statusBarColor">#000000</item>
|
||||||
|
<item name="android:colorPrimary">#FF000000</item>
|
||||||
<item name="android:windowBackground">@android:color/black</item>
|
<item name="android:windowBackground">@android:color/black</item>
|
||||||
|
|
||||||
<!-- Seen in buttons on left drawer: -->
|
<!-- Seen in buttons on left drawer: -->
|
||||||
@@ -13,6 +14,10 @@
|
|||||||
<!-- Avoid action mode toolbar pushing down terminal content when
|
<!-- Avoid action mode toolbar pushing down terminal content when
|
||||||
selecting text on pre-6.0 (non-floating toolbar). -->
|
selecting text on pre-6.0 (non-floating toolbar). -->
|
||||||
<item name="android:windowActionModeOverlay">true</item>
|
<item name="android:windowActionModeOverlay">true</item>
|
||||||
|
|
||||||
|
<!-- https://developer.android.com/training/tv/start/start.html#transition-color -->
|
||||||
|
<item name="android:windowAllowReturnTransitionOverlap">true</item>
|
||||||
|
<item name="android:windowAllowEnterTransitionOverlap">true</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="TermuxAlertDialogStyle" parent="@android:style/Theme.Material.Light.Dialog.Alert">
|
<style name="TermuxAlertDialogStyle" parent="@android:style/Theme.Material.Light.Dialog.Alert">
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
|
<shortcuts xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<shortcut
|
<shortcut
|
||||||
android:shortcutId="new_session"
|
android:shortcutId="new_session"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:icon="@drawable/ic_new_session"
|
android:icon="@drawable/ic_new_session"
|
||||||
android:shortcutShortLabel="@string/new_session">
|
android:shortcutShortLabel="@string/new_session"
|
||||||
|
tools:targetApi="n_mr1">
|
||||||
<intent
|
<intent
|
||||||
android:action="android.intent.action.RUN"
|
android:action="android.intent.action.RUN"
|
||||||
android:targetPackage="com.termux"
|
android:targetPackage="com.termux"
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ public class WcWidthTest extends TestCase {
|
|||||||
assertWidthIs(1, 'å');
|
assertWidthIs(1, 'å');
|
||||||
assertWidthIs(1, 'ä');
|
assertWidthIs(1, 'ä');
|
||||||
assertWidthIs(1, 'ö');
|
assertWidthIs(1, 'ö');
|
||||||
|
assertWidthIs(1, 0x23F2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSomeWide() {
|
public void testSomeWide() {
|
||||||
@@ -45,6 +46,7 @@ public class WcWidthTest extends TestCase {
|
|||||||
public void testCombining() {
|
public void testCombining() {
|
||||||
assertWidthIs(0, 0x0302);
|
assertWidthIs(0, 0x0302);
|
||||||
assertWidthIs(0, 0x0308);
|
assertWidthIs(0, 0x0308);
|
||||||
|
assertWidthIs(0, 0xFE0F);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testWordJoiner() {
|
public void testWordJoiner() {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ for DENSITY in mdpi hdpi xhdpi xxhdpi xxxhdpi; do
|
|||||||
PNG=$FOLDER/$FILE.png
|
PNG=$FOLDER/$FILE.png
|
||||||
|
|
||||||
# Update other apps:
|
# Update other apps:
|
||||||
for APP in tasker widget api; do
|
for APP in api boot styling tasker widget; do
|
||||||
APPDIR=../../termux-$APP
|
APPDIR=../../termux-$APP
|
||||||
if [ -d $APPDIR ]; then
|
if [ -d $APPDIR ]; then
|
||||||
APP_FOLDER=$APPDIR/app/src/main/res/mipmap-$DENSITY
|
APP_FOLDER=$APPDIR/app/src/main/res/mipmap-$DENSITY
|
||||||
|
|||||||
31
art/feature-graphic.svg
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||||
|
<!--
|
||||||
|
This is a feature graphic:
|
||||||
|
https://support.google.com/googleplay/android-developer/answer/1078870
|
||||||
|
- 1024px by 500px, no alpha
|
||||||
|
- Don't include any copy or important visual information near the borders of the asset,
|
||||||
|
specifically near the bottom third of the frame.
|
||||||
|
- Try to center align any logo/copy information in the vertical and horizontal center of the frame.
|
||||||
|
- If adding text, use large font sizes.
|
||||||
|
- Your graphic may be displayed alone without the app icon.
|
||||||
|
-->
|
||||||
|
<svg xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
version="1.1"
|
||||||
|
viewBox="0 0 1024 500">
|
||||||
|
|
||||||
|
<rect fill="#0" width="100%" height="100%" />
|
||||||
|
|
||||||
|
<text id="shell_prompt"
|
||||||
|
x="130"
|
||||||
|
y="330"
|
||||||
|
style="fill: #ffffff; font-size: 124px; font-family: Menlo;">
|
||||||
|
<!--
|
||||||
|
<tspan>$</tspan>
|
||||||
|
<tspan x="290">Termux</tspan>
|
||||||
|
<tspan x="734">█</tspan>
|
||||||
|
-->
|
||||||
|
<tspan>$ Termux █</tspan>
|
||||||
|
</text>
|
||||||
|
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1006 B |
5
art/generate-feature-graphic.sh
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "Generating feature graphics to ~/termux-icons/termux-feature-graphic.png..."
|
||||||
|
mkdir -p ~/termux-icons/
|
||||||
|
rsvg-convert feature-graphic.svg > ~/termux-icons/feature-graphic.png
|
||||||
9
art/generate-tv-banner.sh
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "Generating feature graphics to ~/termux-icons/termux-feature-graphic.png..."
|
||||||
|
mkdir -p ~/termux-icons/
|
||||||
|
|
||||||
|
# The Android TV banner on google play (1280x720) has same aspect ratio
|
||||||
|
# as the banner in the app (320x180).
|
||||||
|
rsvg-convert -w 1280 -h 720 tv-banner.svg > ~/termux-icons/tv-banner.png
|
||||||
|
rsvg-convert -w 320 -h 180 tv-banner.svg > ../app/src/main/res/drawable/banner.png
|
||||||
@@ -4,20 +4,20 @@
|
|||||||
<path fill="#000"
|
<path fill="#000"
|
||||||
stroke="#BFCBCD"
|
stroke="#BFCBCD"
|
||||||
stroke-width="2"
|
stroke-width="2"
|
||||||
d="M7,4
|
d="M9,6
|
||||||
l34,0
|
l30,0
|
||||||
q3 0,3 3
|
q3 0,3 3
|
||||||
l0,34
|
l0,30
|
||||||
q0 3, -3 3
|
q0 3, -3 3
|
||||||
l-34,0
|
l-30,0
|
||||||
q-3 0, -3-3
|
q-3 0, -3-3
|
||||||
l0 -34
|
l0 -30
|
||||||
q0 -3, 3 -3"
|
q0 -3, 3 -3"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Block cursor. -->
|
<!-- Block cursor. -->
|
||||||
<path fill="#FFF"
|
<path fill="#FFF"
|
||||||
d="M12,12
|
d="M14,14
|
||||||
l5,0
|
l5,0
|
||||||
l0,10
|
l0,10
|
||||||
l-5,0"
|
l-5,0"
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 512 B After Width: | Height: | Size: 512 B |
24
art/tv-banner.svg
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||||
|
<!--
|
||||||
|
This is a tv banner graphic:
|
||||||
|
https://developer.android.com/design/tv/patterns.html#banner
|
||||||
|
- Size: 320 x 180 px, xhdpi resource in app
|
||||||
|
- Size: 1280 x 720 in google play.
|
||||||
|
- Text must be included in the image. If your app is available in more
|
||||||
|
than one language, you must provide versions of the banner image for each supported language.
|
||||||
|
-->
|
||||||
|
<svg xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
version="1.1"
|
||||||
|
viewBox="0 0 1280 720">
|
||||||
|
|
||||||
|
<rect fill="#0" width="100%" height="100%" />
|
||||||
|
|
||||||
|
<text id="shell_prompt"
|
||||||
|
x="200"
|
||||||
|
y="410"
|
||||||
|
style="fill: #ffffff; font-size: 210px; font-family: Menlo, Monospace;">
|
||||||
|
<tspan>Termux ▌</tspan>
|
||||||
|
</text>
|
||||||
|
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 780 B |