Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
463b927813 | ||
|
|
09ecd14764 | ||
|
|
89912be500 | ||
|
|
3b4e3b0e42 | ||
|
|
7a726c035c | ||
|
|
430a98e9ad | ||
|
|
01de6b4d18 | ||
|
|
1652c1dcf3 | ||
|
|
7db3200c13 | ||
|
|
9e7ca8b689 | ||
|
|
cb42c19d76 |
14
.editorconfig
Normal file
14
.editorconfig
Normal file
@@ -0,0 +1,14 @@
|
||||
# Copying and distribution of this file, with or without modification,
|
||||
# are permitted in any medium without royalty provided this notice is
|
||||
# preserved. This file is offered as-is, without any warranty.
|
||||
|
||||
# EditorConfig
|
||||
# http://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
@@ -5,7 +5,8 @@ Termux app
|
||||
|
||||
Termux is an Android terminal app and Linux environment.
|
||||
|
||||
* [Termux on Google Play](http://play.google.com/store/apps/details?id=com.termux)
|
||||
* [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)
|
||||
@@ -14,7 +15,7 @@ Termux is an Android terminal app and Linux environment.
|
||||
|
||||
License
|
||||
=======
|
||||
Released under the GPLv3 license. Contains code from `Terminal Emulator for Android` which is released under the Apache 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
|
||||
======================
|
||||
@@ -29,9 +30,9 @@ Terminal resources
|
||||
Terminal emulators
|
||||
==================
|
||||
* VTE (libvte): Terminal emulator widget for GTK+, mainly used in gnome-terminal. [Source](https://github.com/GNOME/vte), [Open Issues](https://bugzilla.gnome.org/buglist.cgi?quicksearch=product%3A%22vte%22+), and [All (including closed) issues](https://bugzilla.gnome.org/buglist.cgi?bug_status=RESOLVED&bug_status=VERIFIED&chfield=resolution&chfieldfrom=-2000d&chfieldvalue=FIXED&product=vte&resolution=FIXED).
|
||||
* iTerm 2: Mac terminal application. [Source](https://github.com/gnachman/iTerm2), [Issues](https://gitlab.com/gnachman/iterm2/issues) and [Documentation](http://www.iterm2.com/documentation.html) (which includes [iTerm2 proprietary escape codes](http://www.iterm2.com/documentation-escape-codes.html)).
|
||||
* iTerm 2: OS X terminal application. [Source](https://github.com/gnachman/iTerm2), [Issues](https://gitlab.com/gnachman/iterm2/issues) and [Documentation](http://www.iterm2.com/documentation.html) (which includes [iTerm2 proprietary escape codes](http://www.iterm2.com/documentation-escape-codes.html)).
|
||||
* Konsole: KDE terminal application. [Source](https://projects.kde.org/projects/kde/applications/konsole/repository), in particular [tests](https://projects.kde.org/projects/kde/applications/konsole/repository/revisions/master/show/tests), [Bugs](https://bugs.kde.org/buglist.cgi?bug_severity=critical&bug_severity=grave&bug_severity=major&bug_severity=crash&bug_severity=normal&bug_severity=minor&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&product=konsole) and [Wishes](https://bugs.kde.org/buglist.cgi?bug_severity=wishlist&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&product=konsole).
|
||||
* hterm: Javascript terminal implementation from chromium. [Source](https://github.com/chromium/hterm), including [tests](https://github.com/chromium/hterm/blob/master/js/hterm_vt_tests.js), and [google group](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-hterm).
|
||||
* hterm: JavaScript terminal implementation from Chromium. [Source](https://github.com/chromium/hterm), including [tests](https://github.com/chromium/hterm/blob/master/js/hterm_vt_tests.js), and [Google group](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-hterm).
|
||||
* xterm: The grandfather of terminal emulators. [Source](http://invisible-island.net/datafiles/release/xterm.tar.gz).
|
||||
* Connectbot: Android SSH client. [Source](https://github.com/connectbot/connectbot)
|
||||
* Android Terminal Emulator: Android terminal app which Termux terminal handling is based on. Inactive. [Source](https://github.com/jackpal/Android-Terminal-Emulator).
|
||||
|
||||
@@ -14,8 +14,8 @@ android {
|
||||
applicationId "com.termux"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 22
|
||||
versionCode 17
|
||||
versionName "0.17"
|
||||
versionCode 19
|
||||
versionName "0.19"
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
|
||||
<uses-feature android:name="android.software.leanback" android:required="false" />
|
||||
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="22" />
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
@@ -21,6 +19,7 @@
|
||||
android:label="@string/application_name"
|
||||
android:theme="@style/Theme.Termux"
|
||||
android:supportsRtl="false" >
|
||||
|
||||
<activity
|
||||
android:name="com.termux.app.TermuxActivity"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||
@@ -35,13 +34,41 @@
|
||||
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="com.termux.app.TermuxHelpActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/application_help" />
|
||||
|
||||
<activity
|
||||
android:name="com.termux.filepicker.TermuxFilePickerActivity"
|
||||
android:label="@string/application_name"
|
||||
android:theme="@android:style/Theme.Material"
|
||||
android:noHistory="true">
|
||||
<intent-filter>
|
||||
<!--
|
||||
http://stackoverflow.com/questions/6486716/using-intent-action-pick-for-specific-path
|
||||
"That said, you should consider ACTION_PICK deprecated. The modern action is ACTION_GET_CONTENT
|
||||
which is much better supported; you will find support of ACTION_PICK spotty and inconsistent.
|
||||
Unfortunately ACTION_GET_CONTENT also does not let you specify a directory."
|
||||
-->
|
||||
<action android:name="android.intent.action.GET_CONTENT" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.OPENABLE" />
|
||||
<data android:mimeType="*/*" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<provider android:authorities="com.termux.filepicker.provider"
|
||||
android:readPermission="com.termux.filepickder.READ"
|
||||
android:exported="true"
|
||||
android:grantUriPermissions="true"
|
||||
android:name="com.termux.filepicker.TermuxFilePickerProvider" />
|
||||
|
||||
<service
|
||||
android:name="com.termux.app.TermuxService"
|
||||
android:exported="false" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
@@ -52,6 +52,7 @@ import android.view.View.OnClickListener;
|
||||
import android.view.View.OnKeyListener;
|
||||
import android.view.View.OnLongClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
@@ -414,18 +415,22 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
||||
TermuxInstaller.setupIfNeeded(TermuxActivity.this, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
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();
|
||||
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.
|
||||
}
|
||||
addNewSession(false, null);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.content.DialogInterface.OnDismissListener;
|
||||
import android.system.Os;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.termux.R;
|
||||
import com.termux.terminal.EmulatorDebug;
|
||||
@@ -138,27 +139,35 @@ final class TermuxInstaller {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
new AlertDialog.Builder(activity).setTitle(R.string.bootstrap_error_title).setMessage(R.string.bootstrap_error_body)
|
||||
.setNegativeButton(R.string.bootstrap_error_abort, new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
activity.finish();
|
||||
}
|
||||
}).setPositiveButton(R.string.bootstrap_error_try_again, new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
TermuxInstaller.setupIfNeeded(activity, whenDone);
|
||||
}
|
||||
}).show();
|
||||
try {
|
||||
new AlertDialog.Builder(activity).setTitle(R.string.bootstrap_error_title).setMessage(R.string.bootstrap_error_body)
|
||||
.setNegativeButton(R.string.bootstrap_error_abort, new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
activity.finish();
|
||||
}
|
||||
}).setPositiveButton(R.string.bootstrap_error_try_again, new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
TermuxInstaller.setupIfNeeded(activity, whenDone);
|
||||
}
|
||||
}).show();
|
||||
} catch (WindowManager.BadTokenException e) {
|
||||
// Activity already dismissed - ignore.
|
||||
}
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
progress.dismiss();
|
||||
try {
|
||||
progress.dismiss();
|
||||
} catch (RuntimeException e) {
|
||||
// Activity already dismissed - ignore.
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
package com.termux.filepicker;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.ListActivity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
|
||||
import com.termux.R;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/** Activity allowing picking files from the $HOME folder. */
|
||||
public class TermuxFilePickerActivity extends ListActivity {
|
||||
|
||||
@SuppressLint("SdCardPath")
|
||||
final String TERMUX_HOME = "/data/data/com.termux/files/home";
|
||||
|
||||
private File mCurrentDirectory;
|
||||
private final List<File> mFiles = new ArrayList<>();
|
||||
private final List<String> mFileNames = new ArrayList<>();
|
||||
private ArrayAdapter mAdapter;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.file_picker);
|
||||
|
||||
mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mFileNames);
|
||||
|
||||
enterDirectory(new File(TERMUX_HOME));
|
||||
setListAdapter(mAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
if (id == android.R.id.home) {
|
||||
enterDirectory(mCurrentDirectory.getParentFile());
|
||||
return true;
|
||||
} else {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onListItemClick(ListView l, View v, int position, long id) {
|
||||
super.onListItemClick(l, v, position, id);
|
||||
File requestFile = mFiles.get(position);
|
||||
if (requestFile.isDirectory()) {
|
||||
enterDirectory(requestFile);
|
||||
} else {
|
||||
Uri returnUri = Uri.withAppendedPath(Uri.parse("content://com.termux.filepicker.provider/"), requestFile.getAbsolutePath());
|
||||
Intent returnIntent = new Intent().setData(returnUri);
|
||||
returnIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
setResult(Activity.RESULT_OK, returnIntent);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
void enterDirectory(File directory) {
|
||||
getActionBar().setDisplayHomeAsUpEnabled(!directory.getAbsolutePath().equals(TERMUX_HOME));
|
||||
|
||||
String title = directory.getAbsolutePath() + "/";
|
||||
if (title.startsWith(TERMUX_HOME)) {
|
||||
title = "~" + title.substring(TERMUX_HOME.length(), title.length());
|
||||
}
|
||||
setTitle(title);
|
||||
|
||||
mCurrentDirectory = directory;
|
||||
mFiles.clear();
|
||||
mFileNames.clear();
|
||||
mFiles.addAll(Arrays.asList(mCurrentDirectory.listFiles()));
|
||||
|
||||
Collections.sort(mFiles, new Comparator<File>() {
|
||||
@Override
|
||||
public int compare(File f1, File f2) {
|
||||
final String n1 = f1.getName();
|
||||
final String n2 = f2.getName();
|
||||
// Display dot folders last:
|
||||
if (n1.startsWith(".") && !n2.startsWith(".")) {
|
||||
return 1;
|
||||
} else if (n2.startsWith(".") && !n1.startsWith(".")) {
|
||||
return -1;
|
||||
}
|
||||
return n1.compareToIgnoreCase(n2);
|
||||
}
|
||||
});
|
||||
|
||||
for (File file : mFiles) {
|
||||
mFileNames.add(file.getName() + (file.isDirectory() ? "/" : ""));
|
||||
}
|
||||
mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.termux.filepicker;
|
||||
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
/** Provider of files content uris picked from {@link com.termux.filepicker.TermuxFilePickerActivity}. */
|
||||
public class TermuxFilePickerProvider extends ContentProvider {
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
@@ -80,6 +80,7 @@ final class TerminalRenderer {
|
||||
|
||||
TerminalRow lineObject = screen.allocateFullLineIfNecessary(screen.externalToInternalRow(row));
|
||||
final char[] line = lineObject.mText;
|
||||
final int charsUsedInLine = lineObject.getSpaceUsed();
|
||||
|
||||
int lastRunStyle = 0;
|
||||
boolean lastRunInsideCursor = false;
|
||||
@@ -125,7 +126,7 @@ final class TerminalRenderer {
|
||||
measuredWidthForRun += measuredCodePointWidth;
|
||||
column += codePointWcWidth;
|
||||
currentCharIndex += charsForCodePoint;
|
||||
while (WcWidth.width(line, currentCharIndex) <= 0) {
|
||||
while (currentCharIndex < charsUsedInLine && WcWidth.width(line, currentCharIndex) <= 0) {
|
||||
// Eat combining chars so that they are treated as part of the last non-combining code point,
|
||||
// instead of e.g. being considered inside the cursor in the next run.
|
||||
currentCharIndex += Character.isHighSurrogate(line[currentCharIndex]) ? 2 : 1;
|
||||
|
||||
20
app/src/main/res/layout/file_picker.xml
Normal file
20
app/src/main/res/layout/file_picker.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingRight="8dp">
|
||||
|
||||
<ListView android:id="@android:id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:drawSelectorOnTop="false"/>
|
||||
|
||||
<TextView android:id="@android:id/empty"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:text="$HOME is empty!"/>
|
||||
</LinearLayout>
|
||||
@@ -4,6 +4,10 @@
|
||||
|
||||
set -e -u
|
||||
|
||||
SRC_JNILIBS=app/src/main/jniLibs/
|
||||
rm -Rf $SRC_JNILIBS
|
||||
mkdir -p $SRC_JNILIBS
|
||||
|
||||
PROJECTDIR=`mktemp -d`
|
||||
JNIDIR=$PROJECTDIR/jni
|
||||
LIBSDIR=$PROJECTDIR/libs
|
||||
@@ -12,6 +16,6 @@ mkdir $JNIDIR
|
||||
cp app/src/main/jni/* $JNIDIR/
|
||||
|
||||
ndk-build NDK_PROJECT_PATH=$PROJECTDIR
|
||||
cp -Rf $LIBSDIR/* app/src/main/jniLibs/
|
||||
cp -Rf $LIBSDIR/* $SRC_JNILIBS
|
||||
|
||||
rm -Rf $PROJECTDIR
|
||||
|
||||
@@ -5,7 +5,7 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:1.3.1'
|
||||
classpath 'com.android.tools.build:gradle:1.5.0'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
#Thu Oct 22 22:50:58 CEST 2015
|
||||
#Fri Nov 06 00:04:44 CET 2015
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.7-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-bin.zip
|
||||
|
||||
Reference in New Issue
Block a user