Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6f0809558 | ||
|
|
11ed7e45d8 | ||
|
|
ed1874db05 | ||
|
|
cb60803a80 | ||
|
|
fc92a27cb2 | ||
|
|
29e62e608f | ||
|
|
8a7f93d722 | ||
|
|
420683fe65 | ||
|
|
a3256ed551 | ||
|
|
57add98e3c | ||
|
|
6e5c04e04f | ||
|
|
528a05ef61 |
@@ -1,4 +1,4 @@
|
||||
sudo: true
|
||||
sudo: false
|
||||
language: android
|
||||
jdk: oraclejdk8
|
||||
|
||||
@@ -6,7 +6,7 @@ env:
|
||||
global:
|
||||
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
|
||||
# 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:
|
||||
components:
|
||||
@@ -19,7 +19,6 @@ android:
|
||||
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
|
||||
|
||||
@@ -13,8 +13,8 @@ android {
|
||||
applicationId "com.termux"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 25
|
||||
versionCode 46
|
||||
versionName "0.46"
|
||||
versionCode 48
|
||||
versionName "0.48"
|
||||
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
|
||||
@@ -90,6 +90,14 @@
|
||||
android:name="com.termux.app.TermuxService"
|
||||
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>
|
||||
|
||||
</manifest>
|
||||
|
||||
@@ -250,7 +250,9 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
||||
TerminalSession session = getCurrentTermSession();
|
||||
if (session != null) {
|
||||
if (session.isRunning()) {
|
||||
session.write(editText.getText().toString() + "\n");
|
||||
String textToSend = editText.getText().toString();
|
||||
if (textToSend.length() == 0) textToSend = "\n";
|
||||
session.write(textToSend);
|
||||
} else {
|
||||
removeFinishedSession(session);
|
||||
}
|
||||
@@ -494,17 +496,6 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
||||
public void run() {
|
||||
if (mTermService == null) return; // Activity might have been destroyed.
|
||||
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);
|
||||
} catch (WindowManager.BadTokenException e) {
|
||||
// Activity finished - ignore.
|
||||
|
||||
172
app/src/main/java/com/termux/app/TermuxOpenReceiver.java
Normal file
@@ -0,0 +1,172 @@
|
||||
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);
|
||||
viewIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
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 FONTSIZE_KEY = "fontsize";
|
||||
private static final String CURRENT_SESSION_KEY = "current_session";
|
||||
private static final String SHOW_WELCOME_DIALOG_KEY = "intro_dialog";
|
||||
|
||||
private boolean mFullScreen;
|
||||
private int mFontSize;
|
||||
@@ -117,14 +116,6 @@ final class TermuxPreferences {
|
||||
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) {
|
||||
try {
|
||||
File propsFile = new File(TermuxService.HOME_PATH + "/.termux/termux.properties");
|
||||
|
||||
@@ -24,7 +24,6 @@ import com.termux.terminal.TerminalSession;
|
||||
import com.termux.terminal.TerminalSession.SessionChangedCallback;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -257,28 +256,11 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
||||
boolean isLoginShell = false;
|
||||
|
||||
if (executablePath == null) {
|
||||
File shell = new File(HOME_PATH, ".termux/shell");
|
||||
if (shell.exists()) {
|
||||
try {
|
||||
File canonicalFile = shell.getCanonicalFile();
|
||||
if (canonicalFile.isFile() && canonicalFile.canExecute()) {
|
||||
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;
|
||||
}
|
||||
for (String shellBinary : new String[]{"login", "bash", "zsh"}) {
|
||||
File shellFile = new File(PREFIX_PATH + "/bin/" + shellBinary);
|
||||
if (shellFile.canExecute()) {
|
||||
executablePath = shellFile.getAbsolutePath();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
<!-- Block cursor. -->
|
||||
<path android:fillColor="#000"
|
||||
android:pathData="M12,12
|
||||
android:pathData="M14,14
|
||||
l5,0
|
||||
l0,10
|
||||
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="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_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>
|
||||
|
||||
@@ -4,20 +4,20 @@
|
||||
<path fill="#000"
|
||||
stroke="#BFCBCD"
|
||||
stroke-width="2"
|
||||
d="M7,4
|
||||
l34,0
|
||||
d="M9,6
|
||||
l30,0
|
||||
q3 0,3 3
|
||||
l0,34
|
||||
l0,30
|
||||
q0 3, -3 3
|
||||
l-34,0
|
||||
l-30,0
|
||||
q-3 0, -3-3
|
||||
l0 -34
|
||||
l0 -30
|
||||
q0 -3, 3 -3"
|
||||
/>
|
||||
|
||||
<!-- Block cursor. -->
|
||||
<path fill="#FFF"
|
||||
d="M12,12
|
||||
d="M14,14
|
||||
l5,0
|
||||
l0,10
|
||||
l-5,0"
|
||||
|
||||
|
Before Width: | Height: | Size: 512 B After Width: | Height: | Size: 512 B |