Compare commits

..

43 Commits
v0.17 ... v0.22

Author SHA1 Message Date
Fredrik Fornwall
bce65f7db1 Setup $HOME/storage symlink for external symlink
At startup Termux now checks if there is external storage available,
and creates a private area on the external storage if one exists as
well as creating a symlink to the private are at $HOME/storage.
2015-12-12 03:02:06 +01:00
Fredrik Fornwall
e18579164f Bump version number to 0.22 2015-12-12 00:10:43 +01:00
Fredrik Fornwall
16273a1981 Fix bug with scrolling down and top scroll margin
The Termux implementation of the ${CSI}${N}T escape sequence to scroll
down N lines (SD - Pan Up) did not take the top margin into account
when figuring out where to place the scrolled rows.

Fixes #28.
2015-12-12 00:07:55 +01:00
Fredrik Fornwall
ce82979e2b Update idea codeStyleSettings.xml 2015-12-05 20:16:20 +01:00
Fredrik Fornwall
625aeab398 Reformat code 2015-11-30 00:39:24 +01:00
Fredrik Fornwall
bad6712338 Improve motion event handling after long press
When mouse reporting is enabled, do not send mouse events on up
after a long press, since that causes e.g. the cursor to move in
vim when lifting the finger after long pressing for the menu.
2015-11-29 10:56:31 +01:00
Fredrik Fornwall
b54c7909bd Fix crash with wide character in last column
Ignore wide character outputs instead of crashing when the cursor
is in the last column with autowrap disabled.
2015-11-29 10:04:50 +01:00
Fredrik Fornwall
4ccc703fcf Remove dead code 2015-11-29 10:03:38 +01:00
Fredrik Fornwall
7348820caf Add screen wraparound test 2015-11-29 09:36:06 +01:00
Fredrik Fornwall
525985b1f2 Remove some dead code 2015-11-29 08:56:56 +01:00
Fredrik Fornwall
ab3852d2e4 Add missing null guard for no current session 2015-11-29 08:55:13 +01:00
Fredrik Fornwall
9928073e48 Fix bitwise operation issue 2015-11-29 02:20:37 +01:00
Fredrik Fornwall
3091da64bc Update travis config 2015-11-29 02:00:55 +01:00
Fredrik Fornwall
74dca95101 Fix inspect code warnings 2015-11-29 01:55:41 +01:00
Fredrik Fornwall
2a6a3b76b7 Update travis config 2015-11-29 01:45:11 +01:00
Fredrik Fornwall
0e4ea95d74 Revert to gradle-bin to check travis build failure 2015-11-29 01:27:13 +01:00
Fredrik Fornwall
7389dbb56f Fix two Android Studio inspect code warnings 2015-11-29 01:12:27 +01:00
Fredrik Fornwall
d982c71efe Url-regexp: Remove redundant escapes and add test 2015-11-29 01:08:18 +01:00
Fredrik Fornwall
1b36c684d6 Add a customized auto backup
Starting with Android 6.0 the system may automatically backup app
data when a users installs an app on a new device or reinstalls an
app on one. After this commit this only affects the $HOME/backup/
folder, so that the user may choose what to backup.

See https://developer.android.com/training/backup/autosyncapi.html
2015-11-29 00:55:05 +01:00
Fredrik Fornwall
a6a83b1fcd Tweak file picker layout 2015-11-29 00:42:47 +01:00
Fredrik Fornwall
d6f01bfe9a Update gradle config and add Support Annotations 2015-11-29 00:25:51 +01:00
Fredrik Fornwall
271dd7dcee Specify Android Studio "Inspect Code" profile 2015-11-29 00:23:18 +01:00
Fredrik Fornwall
95fbb810e2 Code simplifications 2015-11-29 00:20:45 +01:00
Fredrik Fornwall
1aa439311b Remove some warnings 2015-11-29 00:17:50 +01:00
Fredrik Fornwall
12ddaccaf7 Extract hardcoded string to resource 2015-11-29 00:09:25 +01:00
Fredrik Fornwall
09fe7e5941 Use the gradle source distribution for the wrapper 2015-11-29 00:08:07 +01:00
Fredrik Fornwall
36cc010a87 Show bold text in bright colors
Fixes 17. Could be made an option in the future if necessary.
2015-11-24 17:31:48 +01:00
Fredrik Fornwall
b1aaf5abe5 Add *.apk to .gitignore 2015-11-24 17:31:32 +01:00
Fredrik Fornwall
9b3dc57447 Remove junk from app/build.gradle 2015-11-20 23:29:44 +01:00
Fredrik Fornwall
f7ce206212 Bump version to 0.20 2015-11-20 00:40:52 +01:00
Fredrik Fornwall
0deacd8fc6 Show "-press Enter to close" at session exit
This makes it more clear how to close the session after finishing.
Fixes #15.
2015-11-20 00:38:58 +01:00
Fredrik Fornwall
65cfcffa6f Update to gradle 2.9 2015-11-17 21:53:40 +01:00
Fredrik Fornwall
463b927813 Bump version to 0.19 2015-11-13 00:22:18 +01:00
Fredrik Fornwall
09ecd14764 Fix crash when using some unicode characters
We should never try to look at characters in a row after those
actually used.
2015-11-13 00:14:35 +01:00
Fredrik Fornwall
89912be500 Fix crash on installation if activity destroyed
If the activity had been destroyed waiting for the installation to
finish the code would crash trying to display welcome dialogs (on
success) or error dialogs (on error).
2015-11-13 00:10:21 +01:00
Fredrik Fornwall
3b4e3b0e42 Update to Android Gradle Plugin version 1.5.0 2015-11-12 22:16:09 +01:00
Fredrik Fornwall
7a726c035c Clarify Apache License version (fixes #10) 2015-11-07 23:09:52 +01:00
Fredrik Fornwall
430a98e9ad Merge pull request #11 from RyDroid/minor
Minor changes in README and adding an EditorConfig file
2015-11-07 23:06:50 +01:00
Nicola Spanti (RyDroid)
01de6b4d18 Minor changes in README and adding an EditorConfig file 2015-11-07 19:39:13 +01:00
Fredrik Fornwall
1652c1dcf3 Update gradle wrapper to 2.8 2015-11-06 00:05:32 +01:00
Fredrik Fornwall
7db3200c13 Add a file picker to pick files from $HOME
This allows e.g. files from the $HOME folder to be attached to emails
or uploaded when in other apps.

Also bump version to 0.18.
2015-11-05 23:56:51 +01:00
Fredrik Fornwall
9e7ca8b689 Make build-jnilibs.sh always clear directories 2015-10-25 22:35:42 +01:00
Fredrik Fornwall
cb42c19d76 Remove <uses-sdk/> element in favour of gradle 2015-10-25 21:58:09 +01:00
37 changed files with 735 additions and 162 deletions

14
.editorconfig Normal file
View 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

1
.gitignore vendored
View File

@@ -4,6 +4,7 @@
# Built application files
build/
*.apk
# Crashlytics configuations
com_crashlytics_export_strings.xml

229
.idea/codeStyleSettings.xml generated Normal file
View File

@@ -0,0 +1,229 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectCodeStyleSettingsManager">
<option name="PER_PROJECT_SETTINGS">
<value>
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
<value />
</option>
<option name="IMPORT_LAYOUT_TABLE">
<value>
<package name="android" withSubpackages="true" static="false" />
<emptyLine />
<package name="com" withSubpackages="true" static="false" />
<emptyLine />
<package name="junit" withSubpackages="true" static="false" />
<emptyLine />
<package name="net" withSubpackages="true" static="false" />
<emptyLine />
<package name="org" withSubpackages="true" static="false" />
<emptyLine />
<package name="java" withSubpackages="true" static="false" />
<emptyLine />
<package name="javax" withSubpackages="true" static="false" />
<emptyLine />
<package name="" withSubpackages="true" static="false" />
<emptyLine />
<package name="" withSubpackages="true" static="true" />
<emptyLine />
</value>
</option>
<option name="RIGHT_MARGIN" value="100" />
<AndroidXmlCodeStyleSettings>
<option name="USE_CUSTOM_SETTINGS" value="true" />
</AndroidXmlCodeStyleSettings>
<Objective-C-extensions>
<option name="GENERATE_INSTANCE_VARIABLES_FOR_PROPERTIES" value="ASK" />
<option name="RELEASE_STYLE" value="IVAR" />
<option name="TYPE_QUALIFIERS_PLACEMENT" value="BEFORE" />
<file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
</file>
<class>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
</class>
<extensions>
<pair source="cpp" header="h" />
<pair source="c" header="h" />
</extensions>
</Objective-C-extensions>
<XML>
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_NAMESPACE>Namespace:</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_NAMESPACE>Namespace:</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_width</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_height</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_.*</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:width</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:height</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
</value>
</option>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="TermuxCodeStyle" />
</component>
</project>

View File

@@ -0,0 +1,15 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="AndroidLintGoogleAppIndexingWarning" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="EmptyStatementBody" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_reportEmptyBlocks" value="true" />
<option name="commentsAreContent" value="true" />
</inspection_tool>
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
</profile>
</component>

View File

@@ -0,0 +1,7 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="PROJECT_PROFILE" value="Project Default" />
<option name="USE_PROJECT_PROFILE" value="true" />
<version value="1.0" />
</settings>
</component>

View File

@@ -11,9 +11,9 @@ android:
components:
- platform-tools
- tools
- build-tools-23.0.1
- build-tools-23.0.2
- android-23
- sys-img-x86-android-23
- extra-android-m2repository
script:
- ./gradlew testDebugUnitTest

View File

@@ -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).

View File

@@ -2,7 +2,11 @@ apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
buildToolsVersion "23.0.2"
dependencies {
compile 'com.android.support:support-annotations:23.1.1'
}
sourceSets {
main {
@@ -13,32 +17,15 @@ android {
defaultConfig {
applicationId "com.termux"
minSdkVersion 21
targetSdkVersion 22
versionCode 17
versionName "0.17"
}
signingConfigs {
release {
if (System.getenv("TRAVIS")) {
storeFile rootProject.file('travis.keystore')
storePassword 'abcdef'
keyAlias 'travis'
keyPassword 'abcdef'
} else {
storeFile new File(TERMUX_KEYSTORE_FILE)
storePassword TERMUX_KEYSTORE_PASSWORD
keyAlias TERMUX_KEYSTORE_ALIAS
keyPassword TERMUX_KEYSTORE_PASSWORD
}
}
targetSdkVersion 23
versionCode 22
versionName "0.22"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
}

View File

@@ -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" />
@@ -16,11 +14,13 @@
<application
android:allowBackup="true"
android:fullBackupContent="@xml/backupscheme"
android:icon="@drawable/ic_launcher"
android:banner="@drawable/banner"
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 +35,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>

View File

@@ -35,6 +35,8 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Vibrator;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
@@ -52,6 +54,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;
@@ -86,7 +89,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
private static final String RELOAD_STYLE_ACTION = "com.termux.app.reload_style";
/** The main view of the activity showing the terminal. */
TerminalView mTerminalView;
@NonNull TerminalView mTerminalView;
final FullScreenHelper mFullScreenHelper = new FullScreenHelper(this);
@@ -142,6 +145,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
if (event.getAction() != KeyEvent.ACTION_DOWN) return false;
final TerminalSession currentSession = getCurrentTermSession();
if (currentSession == null) return false;
if (keyCode == KeyEvent.KEYCODE_ENTER && !currentSession.isRunning()) {
// Return pressed with finished session - remove it.
@@ -289,6 +293,8 @@ public final class TermuxActivity extends Activity implements ServiceConnection
mTerminalView.checkForTypeface();
mTerminalView.checkForColors();
TermuxInstaller.setupStorageSymlink(this);
}
/**
@@ -414,18 +420,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 {
@@ -456,7 +466,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
}
}
TerminalSession getCurrentTermSession() {
@Nullable TerminalSession getCurrentTermSession() {
return mTerminalView.getCurrentSession();
}
@@ -575,12 +585,11 @@ public final class TermuxActivity extends Activity implements ServiceConnection
return false;
}
void showUrlSelection() {
String text = getCurrentTermSession().getEmulator().getScreen().getTranscriptText();
static LinkedHashSet<CharSequence> extractUrls(String text) {
// Pattern for recognizing a URL, based off RFC 3986
// http://stackoverflow.com/questions/5713558/detect-and-extract-url-from-a-string
final Pattern urlPattern = Pattern.compile(
"(?:^|[\\W])((ht|f)tp(s?):\\/\\/|www\\.)" + "(([\\w\\-]+\\.){1,}?([\\w\\-.~]+\\/?)*" + "[\\p{Alnum}.,%_=?&#\\-+()\\[\\]\\*$~@!:/{};']*)",
"(?:^|[\\W])((ht|f)tp(s?)://|www\\.)" + "(([\\w\\-]+\\.)+?([\\w\\-.~]+/?)*" + "[\\p{Alnum}.,%_=?&#\\-+()\\[\\]\\*$~@!:/{};']*)",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
LinkedHashSet<CharSequence> urlSet = new LinkedHashSet<>();
Matcher matcher = urlPattern.matcher(text);
@@ -590,7 +599,12 @@ public final class TermuxActivity extends Activity implements ServiceConnection
String url = text.substring(matchStart, matchEnd);
urlSet.add(url);
}
return urlSet;
}
void showUrlSelection() {
String text = getCurrentTermSession().getEmulator().getScreen().getTranscriptText();
LinkedHashSet<CharSequence> urlSet = extractUrls(text);
if (urlSet.isEmpty()) {
new AlertDialog.Builder(this).setMessage(R.string.select_url_no_found).show();
return;

View File

@@ -1,16 +1,5 @@
package com.termux.app;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
@@ -21,10 +10,22 @@ 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;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* Install the Termux bootstrap packages if necessary by following the below steps:
*
@@ -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.
}
}
});
}
@@ -191,4 +200,36 @@ final class TermuxInstaller {
}
}
public static void setupStorageSymlink(final Context context) {
final File[] dirs = context.getExternalFilesDirs(null);
if (dirs == null || dirs.length < 2) return;
new Thread() {
public void run() {
try {
final File externalDir = dirs[1];
File homeDir = new File(TermuxService.HOME_PATH);
homeDir.mkdirs();
File externalLink = new File(homeDir, "storage");
if (externalLink.exists()) {
if (externalLink.getCanonicalPath().equals(externalDir.getPath())) {
// Keeping existing link.
return;
} else {
// Removing old link to give place to new.
if (!externalLink.delete()) {
Log.e("termux", "Unable to remove old $HOME/storage to give place for new");
return;
}
}
}
Os.symlink(externalDir.getAbsolutePath(), externalLink.getAbsolutePath());
} catch (Exception e) {
Log.e("termux", "Error setting up link", e);
}
}
}.start();
}
}

View File

@@ -768,16 +768,10 @@ public class DrawerLayout extends ViewGroup {
// or pick a magic number from thin air otherwise.
// TODO Better communication with tools of this bogus state.
// It will crash on a real device.
if (widthMode == MeasureSpec.AT_MOST) {
widthMode = MeasureSpec.EXACTLY;
} else if (widthMode == MeasureSpec.UNSPECIFIED) {
widthMode = MeasureSpec.EXACTLY;
if (widthMode == MeasureSpec.UNSPECIFIED) {
widthSize = 300;
}
if (heightMode == MeasureSpec.AT_MOST) {
heightMode = MeasureSpec.EXACTLY;
} else if (heightMode == MeasureSpec.UNSPECIFIED) {
heightMode = MeasureSpec.EXACTLY;
if (heightMode == MeasureSpec.UNSPECIFIED) {
heightSize = 300;
}
} else {
@@ -934,10 +928,7 @@ public class DrawerLayout extends ViewGroup {
private static boolean hasOpaqueBackground(View v) {
final Drawable bg = v.getBackground();
if (bg != null) {
return bg.getOpacity() == PixelFormat.OPAQUE;
}
return false;
return bg != null && bg.getOpacity() == PixelFormat.OPAQUE;
}
/**
@@ -1114,7 +1105,6 @@ public class DrawerLayout extends ViewGroup {
mRightDragger.processTouchEvent(ev);
final int action = ev.getAction();
boolean wantTouchEvents = true;
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
@@ -1154,7 +1144,7 @@ public class DrawerLayout extends ViewGroup {
}
}
return wantTouchEvents;
return true;
}
@Override
@@ -1317,10 +1307,7 @@ public class DrawerLayout extends ViewGroup {
*/
public boolean isDrawerOpen(int drawerGravity) {
final View drawerView = findDrawerWithGravity(drawerGravity);
if (drawerView != null) {
return isDrawerOpen(drawerView);
}
return false;
return drawerView != null && isDrawerOpen(drawerView);
}
/**
@@ -1350,10 +1337,7 @@ public class DrawerLayout extends ViewGroup {
*/
public boolean isDrawerVisible(int drawerGravity) {
final View drawerView = findDrawerWithGravity(drawerGravity);
if (drawerView != null) {
return isDrawerVisible(drawerView);
}
return false;
return drawerView != null && isDrawerVisible(drawerView);
}
private boolean hasPeekingDrawer() {
@@ -1776,10 +1760,7 @@ public class DrawerLayout extends ViewGroup {
@Override
public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child, AccessibilityEvent event) {
if (CAN_HIDE_DESCENDANTS || includeChildForAccessibility(child)) {
return super.onRequestSendAccessibilityEvent(host, child, event);
}
return false;
return (CAN_HIDE_DESCENDANTS || includeChildForAccessibility(child)) && super.onRequestSendAccessibilityEvent(host, child, event);
}
}
@@ -1796,4 +1777,4 @@ public class DrawerLayout extends ViewGroup {
}
}
}
}
}

View File

@@ -1485,10 +1485,7 @@ public class ViewDragHelper {
* @return true if the supplied view is under the given point, false otherwise
*/
public boolean isViewUnder(View view, int x, int y) {
if (view == null) {
return false;
}
return x >= view.getLeft() && x < view.getRight() && y >= view.getTop() && y < view.getBottom();
return view != null && x >= view.getLeft() && x < view.getRight() && y >= view.getTop() && y < view.getBottom();
}
/**
@@ -1522,4 +1519,4 @@ public class ViewDragHelper {
return result;
}
}
}

View File

@@ -0,0 +1,106 @@
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<>(this, android.R.layout.simple_list_item_1, mFileNames);
enterDirectory(new File(TERMUX_HOME));
setListAdapter(mAdapter);
}
@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();
}
}

View File

@@ -0,0 +1,51 @@
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 android.support.annotation.NonNull;
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(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
return null;
}
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Override
public Uri insert(@NonNull Uri uri, ContentValues values) {
return null;
}
@Override
public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
return 0;
}
@Override
public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
@Override
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
File file = new File(uri.getPath());
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
}
}

View File

@@ -1444,7 +1444,7 @@ public final class TerminalEmulator {
final int linesToScrollArg = getArg0(1);
final int linesBetweenTopAndBottomMargins = mBottomMargin - mTopMargin;
final int linesToScroll = Math.min(linesBetweenTopAndBottomMargins, linesToScrollArg);
mScreen.blockCopy(0, mTopMargin, mColumns, linesBetweenTopAndBottomMargins - linesToScroll, 0, linesToScroll);
mScreen.blockCopy(0, mTopMargin, mColumns, linesBetweenTopAndBottomMargins - linesToScroll, 0, mTopMargin + linesToScroll);
blockClear(0, mTopMargin, mColumns, linesToScroll);
} else {
// "${CSI}${func};${startx};${starty};${firstrow};${lastrow}T" - initiate highlight mouse tracking.
@@ -2156,15 +2156,22 @@ public final class TerminalEmulator {
final boolean autoWrap = isDecsetInternalBitSet(DECSET_BIT_AUTOWRAP);
final int displayWidth = WcWidth.width(codePoint);
final boolean cursorInLastColumn = mCursorCol == mRightMargin - 1;
if (autoWrap && (mCursorCol == mRightMargin - 1 && ((mAboutToAutoWrap && displayWidth == 1) || displayWidth == 2))) {
mScreen.setLineWrap(mCursorRow);
mCursorCol = mLeftMargin;
if (mCursorRow + 1 < mBottomMargin) {
mCursorRow++;
} else {
scrollDownOneLine();
if (autoWrap) {
if (cursorInLastColumn && ((mAboutToAutoWrap && displayWidth == 1) || displayWidth == 2)) {
mScreen.setLineWrap(mCursorRow);
mCursorCol = mLeftMargin;
if (mCursorRow + 1 < mBottomMargin) {
mCursorRow++;
} else {
scrollDownOneLine();
}
}
} else if (cursorInLastColumn && displayWidth == 2) {
// The behaviour when a wide character is output with cursor in the last column when
// autowrap is disabled is not obvious - it's ignored here.
return;
}
if (mInsertMode && displayWidth > 0) {

View File

@@ -119,7 +119,7 @@ public final class TerminalSession extends TerminalOutput {
// Negated signal.
exitDescription += " with signal " + (-exitCode);
}
exitDescription += "]";
exitDescription += " - press Enter to close]";
byte[] bytesToWrite = exitDescription.getBytes(StandardCharsets.UTF_8);
mEmulator.append(bytesToWrite, bytesToWrite.length);

View File

@@ -6,7 +6,7 @@ import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
/** A combination of {@link GestureDetector} and {@link ScaleGestureDetector}. */
public class GestureAndScaleRecognizer {
public final class GestureAndScaleRecognizer {
public interface Listener {
boolean onSingleTapUp(MotionEvent e);
@@ -29,6 +29,7 @@ public class GestureAndScaleRecognizer {
private final GestureDetector mGestureDetector;
private final ScaleGestureDetector mScaleDetector;
final Listener mListener;
boolean isAfterLongPress;
public GestureAndScaleRecognizer(Context context, Listener listener) {
mListener = listener;
@@ -52,6 +53,7 @@ public class GestureAndScaleRecognizer {
@Override
public void onLongPress(MotionEvent e) {
mListener.onLongPress(e);
isAfterLongPress = true;
}
}, null, true /* ignoreMultitouch */);
@@ -88,8 +90,17 @@ public class GestureAndScaleRecognizer {
public void onTouchEvent(MotionEvent event) {
mGestureDetector.onTouchEvent(event);
mScaleDetector.onTouchEvent(event);
if (event.getAction() == MotionEvent.ACTION_UP) {
mListener.onUp(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isAfterLongPress = false;
break;
case MotionEvent.ACTION_UP:
if (!isAfterLongPress) {
// This behaviour is desired when in e.g. vim with mouse events, where we do not
// want to move the cursor when lifting finger after a long press.
mListener.onUp(event);
}
break;
}
}
@@ -97,4 +108,4 @@ public class GestureAndScaleRecognizer {
return mScaleDetector.isInProgress();
}
}
}

View File

@@ -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;
@@ -204,7 +205,10 @@ final class TerminalRenderer {
final boolean strikeThrough = (effect & TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH) != 0;
final boolean dim = (effect & TextStyle.CHARACTER_ATTRIBUTE_DIM) != 0;
int foreColorARGB = palette[foreColor];
// 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));

View File

@@ -0,0 +1,21 @@
<?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="0dp"
android:layout_weight="1"
android:drawSelectorOnTop="false"/>
<TextView android:id="@android:id/empty"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/empty_folder"/>
</LinearLayout>

View File

@@ -56,4 +56,6 @@
<string name="notification_action_wakelock">Wake</string>
<string name="notification_action_wifilock">Wifi</string>
<string name="empty_folder">Empty folder.</string>
</resources>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<full-backup-content>
<!-- See https://developer.android.com/training/backup/autosyncapi.html -->
<include domain="file" path="home/backup" />
</full-backup-content>

View File

@@ -0,0 +1,25 @@
package com.termux.app;
import junit.framework.TestCase;
import java.util.Collections;
import java.util.LinkedHashSet;
public class TermuxActivityTest extends TestCase {
private void assertUrlsAre(String text, String... urls) {
LinkedHashSet<String> expected = new LinkedHashSet<>();
Collections.addAll(expected, urls);
assertEquals(expected, TermuxActivity.extractUrls(text));
}
public void testExtractUrls() {
assertUrlsAre("hello http://example.com world", "http://example.com");
assertUrlsAre("http://example.com\nhttp://another.com", "http://example.com", "http://another.com");
assertUrlsAre("hello http://example.com world and http://more.example.com with secure https://more.example.com",
"http://example.com", "http://more.example.com", "https://more.example.com");
}
}

View File

@@ -17,21 +17,21 @@ public class ByteQueueTest extends TestCase {
public void testCompleteWrites() throws Exception {
ByteQueue q = new ByteQueue(10);
assertEquals(true, q.write(new byte[] { 1, 2, 3 }, 0, 3));
assertEquals(true, q.write(new byte[]{1, 2, 3}, 0, 3));
byte[] arr = new byte[10];
assertEquals(3, q.read(arr, true));
assertArrayEquals(new byte[] { 1, 2, 3 }, new byte[] { arr[0], arr[1], arr[2] });
assertArrayEquals(new byte[]{1, 2, 3}, new byte[]{arr[0], arr[1], arr[2]});
assertEquals(true, q.write(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, 0, 10));
assertEquals(true, q.write(new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 0, 10));
assertEquals(10, q.read(arr, true));
assertArrayEquals(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, arr);
assertArrayEquals(new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, arr);
}
public void testQueueWraparound() throws Exception {
ByteQueue q = new ByteQueue(10);
byte[] origArray = new byte[] { 1, 2, 3, 4, 5, 6 };
byte[] origArray = new byte[]{1, 2, 3, 4, 5, 6};
byte[] readArray = new byte[origArray.length];
for (int i = 0; i < 20; i++) {
q.write(origArray, 0, origArray.length);
@@ -43,7 +43,7 @@ public class ByteQueueTest extends TestCase {
public void testWriteNotesClosing() throws Exception {
ByteQueue q = new ByteQueue(10);
q.close();
assertEquals(false, q.write(new byte[] { 1, 2, 3 }, 0, 3));
assertEquals(false, q.write(new byte[]{1, 2, 3}, 0, 3));
}
public void testReadNonBlocking() throws Exception {

View File

@@ -4,13 +4,13 @@ package com.termux.terminal;
* <pre>
* "CSI ? Pm h", DEC Private Mode Set (DECSET)
* </pre>
*
* <p/>
* and
*
* <p/>
* <pre>
* "CSI ? Pm l", DEC Private Mode Reset (DECRST)
* </pre>
*
* <p/>
* controls various aspects of the terminal
*/
public class DecSetTest extends TerminalTestCase {
@@ -59,4 +59,15 @@ public class DecSetTest extends TerminalTestCase {
assertEquals("Terminal reset() should disable bracketed paste mode", "a", mOutput.getOutputAndClear());
}
/** DECSET 7, DECAWM, controls wraparound mode. */
public void testWrapAroundMode() {
// Default with wraparound:
withTerminalSized(3, 3).enterString("abcd").assertLinesAre("abc", "d ", " ");
// With wraparound disabled:
withTerminalSized(3, 3).enterString("\033[?7labcd").assertLinesAre("abd", " ", " ");
enterString("efg").assertLinesAre("abg", " ", " ");
// Re-enabling wraparound:
enterString("\033[?7hhij").assertLinesAre("abh", "ij ", " ");
}
}

View File

@@ -1,6 +1,7 @@
package com.termux.terminal;
import android.view.KeyEvent;
import junit.framework.TestCase;
public class KeyHandlerTest extends TestCase {

View File

@@ -1,11 +1,11 @@
package com.termux.terminal;
import android.util.Base64;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import android.util.Base64;
/** "ESC ]" is the Operating System Command. */
public class OperatingSystemControlTest extends TerminalTestCase {

View File

@@ -2,7 +2,7 @@ package com.termux.terminal;
/**
* ${CSI}${top};${bottom}r" - set Scrolling Region [top;bottom] (default = full size of window) (DECSTBM).
*
* <p/>
* "DECSTBM moves the cursor to column 1, line 1 of the page" (http://www.vt100.net/docs/vt510-rm/DECSTBM).
*/
public class ScrollRegionTest extends TerminalTestCase {
@@ -94,4 +94,8 @@ public class ScrollRegionTest extends TerminalTestCase {
withTerminalSized(3, 3).enterString("\033[?69h\033[0;2sABCD\0339").assertLinesAre("B ", "D ", " ");
}
public void testScrollDownWithScrollRegion() {
withTerminalSized(2, 5).enterString("1\r\n2\r\n3\r\n4\r\n5").assertLinesAre("1 ", "2 ", "3 ", "4 ", "5 ");
enterString("\033[3r").enterString("\033[2T").assertLinesAre("1 ", "2 ", " ", " ", "3 ");
}
}

View File

@@ -1,10 +1,10 @@
package com.termux.terminal;
import junit.framework.TestCase;
import java.util.Arrays;
import java.util.Random;
import junit.framework.TestCase;
public class TerminalRowTest extends TestCase {
/** The properties of these code points are validated in {@link #testStaticConstants()}. */
@@ -96,7 +96,7 @@ public class TerminalRowTest extends TestCase {
assertEquals(80, row.getSpaceUsed());
assertColumnCharIndicesStartsWith(0, 1, 2, 3);
char[] someChars = new char[] { 'a', 'c', 'e', '4', '5', '6', '7', '8' };
char[] someChars = new char[]{'a', 'c', 'e', '4', '5', '6', '7', '8'};
char[] rawLine = new char[80];
Arrays.fill(rawLine, ' ');
@@ -373,13 +373,13 @@ public class TerminalRowTest extends TestCase {
assertEquals(0, WcWidth.width(0x009F));
assertEquals(1, Character.charCount(0x009F));
int[] points = new int[] { 0xC2541, 'a', '8', 0x73EE, 0x009F, 0x881F, 0x8324, 0xD4C9, 0xFFFD, 'B', 0x009B, 0x61C9, 'Z' };
int[] points = new int[]{0xC2541, 'a', '8', 0x73EE, 0x009F, 0x881F, 0x8324, 0xD4C9, 0xFFFD, 'B', 0x009B, 0x61C9, 'Z'};
// int[] expected = new int[] { TerminalEmulator.UNICODE_REPLACEMENT_CHAR, 'a', '8', 0x73EE, 0x009F, 0x881F, 0x8324, 0xD4C9, 0xFFFD,
// 'B', 0x009B, 0x61C9, 'Z' };
int currentColumn = 0;
for (int i = 0; i < points.length; i++) {
row.setChar(currentColumn, points[i], 0);
currentColumn += WcWidth.width(points[i]);
for (int point : points) {
row.setChar(currentColumn, point, 0);
currentColumn += WcWidth.width(point);
}
// assertLineStartsWith(points);
// assertEquals(Character.highSurrogate(0xC2541), line.mText[0]);

View File

@@ -1,5 +1,8 @@
package com.termux.terminal;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
@@ -9,16 +12,10 @@ import java.util.List;
import java.util.Objects;
import java.util.Set;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import com.termux.terminal.TerminalEmulator;
import com.termux.terminal.TerminalOutput;
public abstract class TerminalTestCase extends TestCase {
public static class MockTerminalOutput extends TerminalOutput {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
public final List<ChangedTitle> titleChanges = new ArrayList<>();
public final List<String> clipboardPuts = new ArrayList<>();
public int bellsRung = 0;
@@ -57,7 +54,7 @@ public abstract class TerminalTestCase extends TestCase {
public TerminalEmulator mTerminal;
public MockTerminalOutput mOutput;
public static class ChangedTitle {
public static final class ChangedTitle {
final String oldTitle;
final String newTitle;
@@ -68,6 +65,7 @@ public abstract class TerminalTestCase extends TestCase {
@Override
public boolean equals(Object o) {
if (!(o instanceof ChangedTitle)) return false;
ChangedTitle other = (ChangedTitle) o;
return Objects.equals(oldTitle, other.oldTitle) && Objects.equals(newTitle, other.newTitle);
}
@@ -115,8 +113,8 @@ public abstract class TerminalTestCase extends TestCase {
}
}
private static class LineWrapper {
TerminalRow mLine;
private static final class LineWrapper {
final TerminalRow mLine;
public LineWrapper(TerminalRow line) {
mLine = line;
@@ -129,7 +127,7 @@ public abstract class TerminalTestCase extends TestCase {
@Override
public boolean equals(Object o) {
return ((LineWrapper) o).mLine == mLine;
return o instanceof LineWrapper && ((LineWrapper) o).mLine == mLine;
}
}

View File

@@ -4,10 +4,10 @@ import junit.framework.TestCase;
public class TextStyleTest extends TestCase {
private static final int[] ALL_EFFECTS = new int[] { 0, TextStyle.CHARACTER_ATTRIBUTE_BOLD, TextStyle.CHARACTER_ATTRIBUTE_ITALIC,
private static final int[] ALL_EFFECTS = new int[]{0, TextStyle.CHARACTER_ATTRIBUTE_BOLD, TextStyle.CHARACTER_ATTRIBUTE_ITALIC,
TextStyle.CHARACTER_ATTRIBUTE_UNDERLINE, TextStyle.CHARACTER_ATTRIBUTE_BLINK, TextStyle.CHARACTER_ATTRIBUTE_INVERSE,
TextStyle.CHARACTER_ATTRIBUTE_INVISIBLE, TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH, TextStyle.CHARACTER_ATTRIBUTE_PROTECTED,
TextStyle.CHARACTER_ATTRIBUTE_DIM };
TextStyle.CHARACTER_ATTRIBUTE_DIM};
public void testEncodingSingle() {
for (int fx : ALL_EFFECTS) {
@@ -34,7 +34,7 @@ public class TextStyleTest extends TestCase {
public void testEncodingStrikeThrough() {
int encoded = TextStyle.encode(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.COLOR_INDEX_BACKGROUND,
TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH);
assertTrue((TextStyle.decodeEffect(encoded) | TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH) != 0);
assertTrue((TextStyle.decodeEffect(encoded) & TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH) != 0);
}
public void testEncodingProtected() {

View File

@@ -10,14 +10,14 @@ public class UnicodeInputTest extends TerminalTestCase {
// continue with valid successor bytes (see Table 3-7), it must not consume the successor bytes as part of the ill-formed
// subsequence whenever those successor bytes themselves constitute part of a well-formed UTF-8 code unit subsequence."
withTerminalSized(5, 5);
mTerminal.append(new byte[] { (byte) 0b11101111, (byte) 'a' }, 2);
mTerminal.append(new byte[]{(byte) 0b11101111, (byte) 'a'}, 2);
assertLineIs(0, ((char) TerminalEmulator.UNICODE_REPLACEMENT_CHAR) + "a ");
}
public void testUnassignedCodePoint() throws UnsupportedEncodingException {
withTerminalSized(3, 3);
// UTF-8 for U+C2541, an unassigned code point:
byte[] b = new byte[] { (byte) 0xf3, (byte) 0x82, (byte) 0x95, (byte) 0x81 };
byte[] b = new byte[]{(byte) 0xf3, (byte) 0x82, (byte) 0x95, (byte) 0x81};
mTerminal.append(b, b.length);
enterString("Y");
assertEquals(1, Character.charCount(TerminalEmulator.UNICODE_REPLACEMENT_CHAR));
@@ -26,10 +26,10 @@ public class UnicodeInputTest extends TerminalTestCase {
public void testStuff() {
withTerminalSized(80, 24);
byte[] b = new byte[] { (byte) 0xf3, (byte) 0x82, (byte) 0x95, (byte) 0x81, (byte) 0x61, (byte) 0x38, (byte) 0xe7, (byte) 0x8f,
byte[] b = new byte[]{(byte) 0xf3, (byte) 0x82, (byte) 0x95, (byte) 0x81, (byte) 0x61, (byte) 0x38, (byte) 0xe7, (byte) 0x8f,
(byte) 0xae, (byte) 0xc2, (byte) 0x9f, (byte) 0xe8, (byte) 0xa0, (byte) 0x9f, (byte) 0xe8, (byte) 0x8c, (byte) 0xa4,
(byte) 0xed, (byte) 0x93, (byte) 0x89, (byte) 0xef, (byte) 0xbf, (byte) 0xbd, (byte) 0x42, (byte) 0xc2, (byte) 0x9b,
(byte) 0xe6, (byte) 0x87, (byte) 0x89, (byte) 0x5a };
(byte) 0xe6, (byte) 0x87, (byte) 0x89, (byte) 0x5a};
mTerminal.append(b, b.length);
}
@@ -80,8 +80,16 @@ public class UnicodeInputTest extends TerminalTestCase {
public void testOverlongUtf8Encoding() throws Exception {
// U+0020 should be encoded as 0x20, 0xc0 0xa0 is an overlong encoding
// so should be replaced with the replacement char U+FFFD.
withTerminalSized(5, 5).mTerminal.append(new byte[] { (byte) 0xc0, (byte) 0xa0, 'Y' }, 3);
withTerminalSized(5, 5).mTerminal.append(new byte[]{(byte) 0xc0, (byte) 0xa0, 'Y'}, 3);
assertLineIs(0, "\uFFFDY ");
}
public void testWideCharacterWithoutWrapping() throws Exception {
// With wraparound disabled. The behaviour when a wide character is output with cursor in
// the last column when autowrap is disabled is not obvious, but we expect the wide
// character to be ignored here.
withTerminalSized(3, 3).enterString("\033[?7l").enterString("枝枝枝").assertLinesAre("", " ", " ");
enterString("a枝").assertLinesAre("枝a", " ", " ");
}
}

View File

@@ -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

View File

@@ -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

Binary file not shown.

View File

@@ -1,6 +1,6 @@
#Thu Oct 22 22:50:58 CEST 2015
#Sat Nov 28 23:59:10 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.9-all.zip

4
gradlew vendored
View File

@@ -56,9 +56,9 @@ while [ -h "$PRG" ] ; do
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar