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 # Built application files
build/ build/
*.apk
# Crashlytics configuations # Crashlytics configuations
com_crashlytics_export_strings.xml 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: components:
- platform-tools - platform-tools
- tools - tools
- build-tools-23.0.1 - build-tools-23.0.2
- android-23 - android-23
- sys-img-x86-android-23 - extra-android-m2repository
script: script:
- ./gradlew testDebugUnitTest - ./gradlew testDebugUnitTest

View File

@@ -5,7 +5,8 @@ Termux app
Termux is an Android terminal app and Linux environment. 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.com](http://termux.com)
* [Termux Help](http://termux.com/help/) * [Termux Help](http://termux.com/help/)
* [Termux app on GitHub](https://github.com/termux/termux-app) * [Termux app on GitHub](https://github.com/termux/termux-app)
@@ -14,7 +15,7 @@ Termux is an Android terminal app and Linux environment.
License 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 Building JNI libraries
====================== ======================
@@ -29,9 +30,9 @@ Terminal resources
Terminal emulators 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). * 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). * 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). * 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) * 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). * 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 { android {
compileSdkVersion 23 compileSdkVersion 23
buildToolsVersion "23.0.1" buildToolsVersion "23.0.2"
dependencies {
compile 'com.android.support:support-annotations:23.1.1'
}
sourceSets { sourceSets {
main { main {
@@ -13,32 +17,15 @@ android {
defaultConfig { defaultConfig {
applicationId "com.termux" applicationId "com.termux"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 22 targetSdkVersion 23
versionCode 17 versionCode 22
versionName "0.17" versionName "0.22"
}
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
}
}
} }
buildTypes { buildTypes {
release { release {
minifyEnabled false minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 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.hardware.touchscreen" android:required="false" />
<uses-feature android:name="android.software.leanback" 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.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
@@ -16,11 +14,13 @@
<application <application
android:allowBackup="true" android:allowBackup="true"
android:fullBackupContent="@xml/backupscheme"
android:icon="@drawable/ic_launcher" android:icon="@drawable/ic_launcher"
android:banner="@drawable/banner" android:banner="@drawable/banner"
android:label="@string/application_name" android:label="@string/application_name"
android:theme="@style/Theme.Termux" android:theme="@style/Theme.Termux"
android:supportsRtl="false" > android:supportsRtl="false" >
<activity <activity
android:name="com.termux.app.TermuxActivity" android:name="com.termux.app.TermuxActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize" android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
@@ -35,13 +35,41 @@
<category android:name="android.intent.category.LEANBACK_LAUNCHER" /> <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name="com.termux.app.TermuxHelpActivity" android:name="com.termux.app.TermuxHelpActivity"
android:exported="false" android:exported="false"
android:label="@string/application_help" /> 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 <service
android:name="com.termux.app.TermuxService" android:name="com.termux.app.TermuxService"
android:exported="false" /> android:exported="false" />
</application> </application>
</manifest> </manifest>

View File

@@ -35,6 +35,8 @@ import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import android.os.Vibrator; import android.os.Vibrator;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.Spanned; import android.text.Spanned;
import android.text.TextUtils; import android.text.TextUtils;
@@ -52,6 +54,7 @@ import android.view.View.OnClickListener;
import android.view.View.OnKeyListener; import android.view.View.OnKeyListener;
import android.view.View.OnLongClickListener; import android.view.View.OnLongClickListener;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener; 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"; private static final String RELOAD_STYLE_ACTION = "com.termux.app.reload_style";
/** The main view of the activity showing the terminal. */ /** The main view of the activity showing the terminal. */
TerminalView mTerminalView; @NonNull TerminalView mTerminalView;
final FullScreenHelper mFullScreenHelper = new FullScreenHelper(this); 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; if (event.getAction() != KeyEvent.ACTION_DOWN) return false;
final TerminalSession currentSession = getCurrentTermSession(); final TerminalSession currentSession = getCurrentTermSession();
if (currentSession == null) return false;
if (keyCode == KeyEvent.KEYCODE_ENTER && !currentSession.isRunning()) { if (keyCode == KeyEvent.KEYCODE_ENTER && !currentSession.isRunning()) {
// Return pressed with finished session - remove it. // Return pressed with finished session - remove it.
@@ -289,6 +293,8 @@ public final class TermuxActivity extends Activity implements ServiceConnection
mTerminalView.checkForTypeface(); mTerminalView.checkForTypeface();
mTerminalView.checkForColors(); mTerminalView.checkForColors();
TermuxInstaller.setupStorageSymlink(this);
} }
/** /**
@@ -414,18 +420,22 @@ public final class TermuxActivity extends Activity implements ServiceConnection
TermuxInstaller.setupIfNeeded(TermuxActivity.this, new Runnable() { TermuxInstaller.setupIfNeeded(TermuxActivity.this, new Runnable() {
@Override @Override
public void run() { public void run() {
if (TermuxPreferences.isShowWelcomeDialog(TermuxActivity.this)) { try {
new AlertDialog.Builder(TermuxActivity.this).setTitle(R.string.welcome_dialog_title).setMessage(R.string.welcome_dialog_body) if (TermuxPreferences.isShowWelcomeDialog(TermuxActivity.this)) {
.setCancelable(false).setPositiveButton(android.R.string.ok, null) new AlertDialog.Builder(TermuxActivity.this).setTitle(R.string.welcome_dialog_title).setMessage(R.string.welcome_dialog_body)
.setNegativeButton(R.string.welcome_dialog_dont_show_again_button, new DialogInterface.OnClickListener() { .setCancelable(false).setPositiveButton(android.R.string.ok, null)
@Override .setNegativeButton(R.string.welcome_dialog_dont_show_again_button, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) { @Override
TermuxPreferences.disableWelcomeDialog(TermuxActivity.this); public void onClick(DialogInterface dialog, int which) {
dialog.dismiss(); TermuxPreferences.disableWelcomeDialog(TermuxActivity.this);
} dialog.dismiss();
}).show(); }
}).show();
}
addNewSession(false, null);
} catch (WindowManager.BadTokenException e) {
// Activity finished - ignore.
} }
addNewSession(false, null);
} }
}); });
} else { } else {
@@ -456,7 +466,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
} }
} }
TerminalSession getCurrentTermSession() { @Nullable TerminalSession getCurrentTermSession() {
return mTerminalView.getCurrentSession(); return mTerminalView.getCurrentSession();
} }
@@ -575,12 +585,11 @@ public final class TermuxActivity extends Activity implements ServiceConnection
return false; return false;
} }
void showUrlSelection() { static LinkedHashSet<CharSequence> extractUrls(String text) {
String text = getCurrentTermSession().getEmulator().getScreen().getTranscriptText();
// Pattern for recognizing a URL, based off RFC 3986 // Pattern for recognizing a URL, based off RFC 3986
// http://stackoverflow.com/questions/5713558/detect-and-extract-url-from-a-string // http://stackoverflow.com/questions/5713558/detect-and-extract-url-from-a-string
final Pattern urlPattern = Pattern.compile( 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); Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
LinkedHashSet<CharSequence> urlSet = new LinkedHashSet<>(); LinkedHashSet<CharSequence> urlSet = new LinkedHashSet<>();
Matcher matcher = urlPattern.matcher(text); Matcher matcher = urlPattern.matcher(text);
@@ -590,7 +599,12 @@ public final class TermuxActivity extends Activity implements ServiceConnection
String url = text.substring(matchStart, matchEnd); String url = text.substring(matchStart, matchEnd);
urlSet.add(url); urlSet.add(url);
} }
return urlSet;
}
void showUrlSelection() {
String text = getCurrentTermSession().getEmulator().getScreen().getTranscriptText();
LinkedHashSet<CharSequence> urlSet = extractUrls(text);
if (urlSet.isEmpty()) { if (urlSet.isEmpty()) {
new AlertDialog.Builder(this).setMessage(R.string.select_url_no_found).show(); new AlertDialog.Builder(this).setMessage(R.string.select_url_no_found).show();
return; return;

View File

@@ -1,16 +1,5 @@
package com.termux.app; 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.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.ProgressDialog; import android.app.ProgressDialog;
@@ -21,10 +10,22 @@ import android.content.DialogInterface.OnDismissListener;
import android.system.Os; import android.system.Os;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
import android.view.WindowManager;
import com.termux.R; import com.termux.R;
import com.termux.terminal.EmulatorDebug; 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: * Install the Termux bootstrap packages if necessary by following the below steps:
* *
@@ -138,27 +139,35 @@ final class TermuxInstaller {
activity.runOnUiThread(new Runnable() { activity.runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
new AlertDialog.Builder(activity).setTitle(R.string.bootstrap_error_title).setMessage(R.string.bootstrap_error_body) try {
.setNegativeButton(R.string.bootstrap_error_abort, new OnClickListener() { new AlertDialog.Builder(activity).setTitle(R.string.bootstrap_error_title).setMessage(R.string.bootstrap_error_body)
@Override .setNegativeButton(R.string.bootstrap_error_abort, new OnClickListener() {
public void onClick(DialogInterface dialog, int which) { @Override
dialog.dismiss(); public void onClick(DialogInterface dialog, int which) {
activity.finish(); dialog.dismiss();
} activity.finish();
}).setPositiveButton(R.string.bootstrap_error_try_again, new OnClickListener() { }
@Override }).setPositiveButton(R.string.bootstrap_error_try_again, new OnClickListener() {
public void onClick(DialogInterface dialog, int which) { @Override
dialog.dismiss(); public void onClick(DialogInterface dialog, int which) {
TermuxInstaller.setupIfNeeded(activity, whenDone); dialog.dismiss();
} TermuxInstaller.setupIfNeeded(activity, whenDone);
}).show(); }
}).show();
} catch (WindowManager.BadTokenException e) {
// Activity already dismissed - ignore.
}
} }
}); });
} finally { } finally {
activity.runOnUiThread(new Runnable() { activity.runOnUiThread(new Runnable() {
@Override @Override
public void run() { 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. // or pick a magic number from thin air otherwise.
// TODO Better communication with tools of this bogus state. // TODO Better communication with tools of this bogus state.
// It will crash on a real device. // It will crash on a real device.
if (widthMode == MeasureSpec.AT_MOST) { if (widthMode == MeasureSpec.UNSPECIFIED) {
widthMode = MeasureSpec.EXACTLY;
} else if (widthMode == MeasureSpec.UNSPECIFIED) {
widthMode = MeasureSpec.EXACTLY;
widthSize = 300; widthSize = 300;
} }
if (heightMode == MeasureSpec.AT_MOST) { if (heightMode == MeasureSpec.UNSPECIFIED) {
heightMode = MeasureSpec.EXACTLY;
} else if (heightMode == MeasureSpec.UNSPECIFIED) {
heightMode = MeasureSpec.EXACTLY;
heightSize = 300; heightSize = 300;
} }
} else { } else {
@@ -934,10 +928,7 @@ public class DrawerLayout extends ViewGroup {
private static boolean hasOpaqueBackground(View v) { private static boolean hasOpaqueBackground(View v) {
final Drawable bg = v.getBackground(); final Drawable bg = v.getBackground();
if (bg != null) { return bg != null && bg.getOpacity() == PixelFormat.OPAQUE;
return bg.getOpacity() == PixelFormat.OPAQUE;
}
return false;
} }
/** /**
@@ -1114,7 +1105,6 @@ public class DrawerLayout extends ViewGroup {
mRightDragger.processTouchEvent(ev); mRightDragger.processTouchEvent(ev);
final int action = ev.getAction(); final int action = ev.getAction();
boolean wantTouchEvents = true;
switch (action & MotionEvent.ACTION_MASK) { switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: { case MotionEvent.ACTION_DOWN: {
@@ -1154,7 +1144,7 @@ public class DrawerLayout extends ViewGroup {
} }
} }
return wantTouchEvents; return true;
} }
@Override @Override
@@ -1317,10 +1307,7 @@ public class DrawerLayout extends ViewGroup {
*/ */
public boolean isDrawerOpen(int drawerGravity) { public boolean isDrawerOpen(int drawerGravity) {
final View drawerView = findDrawerWithGravity(drawerGravity); final View drawerView = findDrawerWithGravity(drawerGravity);
if (drawerView != null) { return drawerView != null && isDrawerOpen(drawerView);
return isDrawerOpen(drawerView);
}
return false;
} }
/** /**
@@ -1350,10 +1337,7 @@ public class DrawerLayout extends ViewGroup {
*/ */
public boolean isDrawerVisible(int drawerGravity) { public boolean isDrawerVisible(int drawerGravity) {
final View drawerView = findDrawerWithGravity(drawerGravity); final View drawerView = findDrawerWithGravity(drawerGravity);
if (drawerView != null) { return drawerView != null && isDrawerVisible(drawerView);
return isDrawerVisible(drawerView);
}
return false;
} }
private boolean hasPeekingDrawer() { private boolean hasPeekingDrawer() {
@@ -1776,10 +1760,7 @@ public class DrawerLayout extends ViewGroup {
@Override @Override
public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child, AccessibilityEvent event) { public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child, AccessibilityEvent event) {
if (CAN_HIDE_DESCENDANTS || includeChildForAccessibility(child)) { return (CAN_HIDE_DESCENDANTS || includeChildForAccessibility(child)) && super.onRequestSendAccessibilityEvent(host, child, event);
return super.onRequestSendAccessibilityEvent(host, child, event);
}
return false;
} }
} }
@@ -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 * @return true if the supplied view is under the given point, false otherwise
*/ */
public boolean isViewUnder(View view, int x, int y) { public boolean isViewUnder(View view, int x, int y) {
if (view == null) { return view != null && x >= view.getLeft() && x < view.getRight() && y >= view.getTop() && y < view.getBottom();
return false;
}
return x >= view.getLeft() && x < view.getRight() && y >= view.getTop() && y < view.getBottom();
} }
/** /**
@@ -1522,4 +1519,4 @@ public class ViewDragHelper {
return result; 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 linesToScrollArg = getArg0(1);
final int linesBetweenTopAndBottomMargins = mBottomMargin - mTopMargin; final int linesBetweenTopAndBottomMargins = mBottomMargin - mTopMargin;
final int linesToScroll = Math.min(linesBetweenTopAndBottomMargins, linesToScrollArg); 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); blockClear(0, mTopMargin, mColumns, linesToScroll);
} else { } else {
// "${CSI}${func};${startx};${starty};${firstrow};${lastrow}T" - initiate highlight mouse tracking. // "${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 boolean autoWrap = isDecsetInternalBitSet(DECSET_BIT_AUTOWRAP);
final int displayWidth = WcWidth.width(codePoint); final int displayWidth = WcWidth.width(codePoint);
final boolean cursorInLastColumn = mCursorCol == mRightMargin - 1;
if (autoWrap && (mCursorCol == mRightMargin - 1 && ((mAboutToAutoWrap && displayWidth == 1) || displayWidth == 2))) { if (autoWrap) {
mScreen.setLineWrap(mCursorRow); if (cursorInLastColumn && ((mAboutToAutoWrap && displayWidth == 1) || displayWidth == 2)) {
mCursorCol = mLeftMargin; mScreen.setLineWrap(mCursorRow);
if (mCursorRow + 1 < mBottomMargin) { mCursorCol = mLeftMargin;
mCursorRow++; if (mCursorRow + 1 < mBottomMargin) {
} else { mCursorRow++;
scrollDownOneLine(); } 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) { if (mInsertMode && displayWidth > 0) {

View File

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

View File

@@ -6,7 +6,7 @@ import android.view.MotionEvent;
import android.view.ScaleGestureDetector; import android.view.ScaleGestureDetector;
/** A combination of {@link GestureDetector} and {@link ScaleGestureDetector}. */ /** A combination of {@link GestureDetector} and {@link ScaleGestureDetector}. */
public class GestureAndScaleRecognizer { public final class GestureAndScaleRecognizer {
public interface Listener { public interface Listener {
boolean onSingleTapUp(MotionEvent e); boolean onSingleTapUp(MotionEvent e);
@@ -29,6 +29,7 @@ public class GestureAndScaleRecognizer {
private final GestureDetector mGestureDetector; private final GestureDetector mGestureDetector;
private final ScaleGestureDetector mScaleDetector; private final ScaleGestureDetector mScaleDetector;
final Listener mListener; final Listener mListener;
boolean isAfterLongPress;
public GestureAndScaleRecognizer(Context context, Listener listener) { public GestureAndScaleRecognizer(Context context, Listener listener) {
mListener = listener; mListener = listener;
@@ -52,6 +53,7 @@ public class GestureAndScaleRecognizer {
@Override @Override
public void onLongPress(MotionEvent e) { public void onLongPress(MotionEvent e) {
mListener.onLongPress(e); mListener.onLongPress(e);
isAfterLongPress = true;
} }
}, null, true /* ignoreMultitouch */); }, null, true /* ignoreMultitouch */);
@@ -88,8 +90,17 @@ public class GestureAndScaleRecognizer {
public void onTouchEvent(MotionEvent event) { public void onTouchEvent(MotionEvent event) {
mGestureDetector.onTouchEvent(event); mGestureDetector.onTouchEvent(event);
mScaleDetector.onTouchEvent(event); mScaleDetector.onTouchEvent(event);
if (event.getAction() == MotionEvent.ACTION_UP) { switch (event.getAction()) {
mListener.onUp(event); 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(); return mScaleDetector.isInProgress();
} }
} }

View File

@@ -80,6 +80,7 @@ final class TerminalRenderer {
TerminalRow lineObject = screen.allocateFullLineIfNecessary(screen.externalToInternalRow(row)); TerminalRow lineObject = screen.allocateFullLineIfNecessary(screen.externalToInternalRow(row));
final char[] line = lineObject.mText; final char[] line = lineObject.mText;
final int charsUsedInLine = lineObject.getSpaceUsed();
int lastRunStyle = 0; int lastRunStyle = 0;
boolean lastRunInsideCursor = false; boolean lastRunInsideCursor = false;
@@ -125,7 +126,7 @@ final class TerminalRenderer {
measuredWidthForRun += measuredCodePointWidth; measuredWidthForRun += measuredCodePointWidth;
column += codePointWcWidth; column += codePointWcWidth;
currentCharIndex += charsForCodePoint; 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, // 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. // instead of e.g. being considered inside the cursor in the next run.
currentCharIndex += Character.isHighSurrogate(line[currentCharIndex]) ? 2 : 1; 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 strikeThrough = (effect & TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH) != 0;
final boolean dim = (effect & TextStyle.CHARACTER_ATTRIBUTE_DIM) != 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) { if (dim) {
int red = (0xFF & (foreColorARGB >> 16)); int red = (0xFF & (foreColorARGB >> 16));
int green = (0xFF & (foreColorARGB >> 8)); 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_wakelock">Wake</string>
<string name="notification_action_wifilock">Wifi</string> <string name="notification_action_wifilock">Wifi</string>
<string name="empty_folder">Empty folder.</string>
</resources> </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 { public void testCompleteWrites() throws Exception {
ByteQueue q = new ByteQueue(10); 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]; byte[] arr = new byte[10];
assertEquals(3, q.read(arr, true)); 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)); 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 { public void testQueueWraparound() throws Exception {
ByteQueue q = new ByteQueue(10); 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]; byte[] readArray = new byte[origArray.length];
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
q.write(origArray, 0, origArray.length); q.write(origArray, 0, origArray.length);
@@ -43,7 +43,7 @@ public class ByteQueueTest extends TestCase {
public void testWriteNotesClosing() throws Exception { public void testWriteNotesClosing() throws Exception {
ByteQueue q = new ByteQueue(10); ByteQueue q = new ByteQueue(10);
q.close(); 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 { public void testReadNonBlocking() throws Exception {

View File

@@ -4,13 +4,13 @@ package com.termux.terminal;
* <pre> * <pre>
* "CSI ? Pm h", DEC Private Mode Set (DECSET) * "CSI ? Pm h", DEC Private Mode Set (DECSET)
* </pre> * </pre>
* * <p/>
* and * and
* * <p/>
* <pre> * <pre>
* "CSI ? Pm l", DEC Private Mode Reset (DECRST) * "CSI ? Pm l", DEC Private Mode Reset (DECRST)
* </pre> * </pre>
* * <p/>
* controls various aspects of the terminal * controls various aspects of the terminal
*/ */
public class DecSetTest extends TerminalTestCase { 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()); 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; package com.termux.terminal;
import android.view.KeyEvent; import android.view.KeyEvent;
import junit.framework.TestCase; import junit.framework.TestCase;
public class KeyHandlerTest extends TestCase { public class KeyHandlerTest extends TestCase {

View File

@@ -1,11 +1,11 @@
package com.termux.terminal; package com.termux.terminal;
import android.util.Base64;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import android.util.Base64;
/** "ESC ]" is the Operating System Command. */ /** "ESC ]" is the Operating System Command. */
public class OperatingSystemControlTest extends TerminalTestCase { 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). * ${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). * "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 { 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 ", " "); 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; package com.termux.terminal;
import junit.framework.TestCase;
import java.util.Arrays; import java.util.Arrays;
import java.util.Random; import java.util.Random;
import junit.framework.TestCase;
public class TerminalRowTest extends TestCase { public class TerminalRowTest extends TestCase {
/** The properties of these code points are validated in {@link #testStaticConstants()}. */ /** The properties of these code points are validated in {@link #testStaticConstants()}. */
@@ -96,7 +96,7 @@ public class TerminalRowTest extends TestCase {
assertEquals(80, row.getSpaceUsed()); assertEquals(80, row.getSpaceUsed());
assertColumnCharIndicesStartsWith(0, 1, 2, 3); 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]; char[] rawLine = new char[80];
Arrays.fill(rawLine, ' '); Arrays.fill(rawLine, ' ');
@@ -373,13 +373,13 @@ public class TerminalRowTest extends TestCase {
assertEquals(0, WcWidth.width(0x009F)); assertEquals(0, WcWidth.width(0x009F));
assertEquals(1, Character.charCount(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, // int[] expected = new int[] { TerminalEmulator.UNICODE_REPLACEMENT_CHAR, 'a', '8', 0x73EE, 0x009F, 0x881F, 0x8324, 0xD4C9, 0xFFFD,
// 'B', 0x009B, 0x61C9, 'Z' }; // 'B', 0x009B, 0x61C9, 'Z' };
int currentColumn = 0; int currentColumn = 0;
for (int i = 0; i < points.length; i++) { for (int point : points) {
row.setChar(currentColumn, points[i], 0); row.setChar(currentColumn, point, 0);
currentColumn += WcWidth.width(points[i]); currentColumn += WcWidth.width(point);
} }
// assertLineStartsWith(points); // assertLineStartsWith(points);
// assertEquals(Character.highSurrogate(0xC2541), line.mText[0]); // assertEquals(Character.highSurrogate(0xC2541), line.mText[0]);

View File

@@ -1,5 +1,8 @@
package com.termux.terminal; package com.termux.terminal;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@@ -9,16 +12,10 @@ import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set; 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 abstract class TerminalTestCase extends TestCase {
public static class MockTerminalOutput extends TerminalOutput { 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<ChangedTitle> titleChanges = new ArrayList<>();
public final List<String> clipboardPuts = new ArrayList<>(); public final List<String> clipboardPuts = new ArrayList<>();
public int bellsRung = 0; public int bellsRung = 0;
@@ -57,7 +54,7 @@ public abstract class TerminalTestCase extends TestCase {
public TerminalEmulator mTerminal; public TerminalEmulator mTerminal;
public MockTerminalOutput mOutput; public MockTerminalOutput mOutput;
public static class ChangedTitle { public static final class ChangedTitle {
final String oldTitle; final String oldTitle;
final String newTitle; final String newTitle;
@@ -68,6 +65,7 @@ public abstract class TerminalTestCase extends TestCase {
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (!(o instanceof ChangedTitle)) return false;
ChangedTitle other = (ChangedTitle) o; ChangedTitle other = (ChangedTitle) o;
return Objects.equals(oldTitle, other.oldTitle) && Objects.equals(newTitle, other.newTitle); 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 { private static final class LineWrapper {
TerminalRow mLine; final TerminalRow mLine;
public LineWrapper(TerminalRow line) { public LineWrapper(TerminalRow line) {
mLine = line; mLine = line;
@@ -129,7 +127,7 @@ public abstract class TerminalTestCase extends TestCase {
@Override @Override
public boolean equals(Object o) { 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 { 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_UNDERLINE, TextStyle.CHARACTER_ATTRIBUTE_BLINK, TextStyle.CHARACTER_ATTRIBUTE_INVERSE,
TextStyle.CHARACTER_ATTRIBUTE_INVISIBLE, TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH, TextStyle.CHARACTER_ATTRIBUTE_PROTECTED, TextStyle.CHARACTER_ATTRIBUTE_INVISIBLE, TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH, TextStyle.CHARACTER_ATTRIBUTE_PROTECTED,
TextStyle.CHARACTER_ATTRIBUTE_DIM }; TextStyle.CHARACTER_ATTRIBUTE_DIM};
public void testEncodingSingle() { public void testEncodingSingle() {
for (int fx : ALL_EFFECTS) { for (int fx : ALL_EFFECTS) {
@@ -34,7 +34,7 @@ public class TextStyleTest extends TestCase {
public void testEncodingStrikeThrough() { public void testEncodingStrikeThrough() {
int encoded = TextStyle.encode(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.COLOR_INDEX_BACKGROUND, int encoded = TextStyle.encode(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.COLOR_INDEX_BACKGROUND,
TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH); 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() { 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 // 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." // subsequence whenever those successor bytes themselves constitute part of a well-formed UTF-8 code unit subsequence."
withTerminalSized(5, 5); 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 "); assertLineIs(0, ((char) TerminalEmulator.UNICODE_REPLACEMENT_CHAR) + "a ");
} }
public void testUnassignedCodePoint() throws UnsupportedEncodingException { public void testUnassignedCodePoint() throws UnsupportedEncodingException {
withTerminalSized(3, 3); withTerminalSized(3, 3);
// UTF-8 for U+C2541, an unassigned code point: // 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); mTerminal.append(b, b.length);
enterString("Y"); enterString("Y");
assertEquals(1, Character.charCount(TerminalEmulator.UNICODE_REPLACEMENT_CHAR)); assertEquals(1, Character.charCount(TerminalEmulator.UNICODE_REPLACEMENT_CHAR));
@@ -26,10 +26,10 @@ public class UnicodeInputTest extends TerminalTestCase {
public void testStuff() { public void testStuff() {
withTerminalSized(80, 24); 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) 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) 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); mTerminal.append(b, b.length);
} }
@@ -80,8 +80,16 @@ public class UnicodeInputTest extends TerminalTestCase {
public void testOverlongUtf8Encoding() throws Exception { public void testOverlongUtf8Encoding() throws Exception {
// U+0020 should be encoded as 0x20, 0xc0 0xa0 is an overlong encoding // U+0020 should be encoded as 0x20, 0xc0 0xa0 is an overlong encoding
// so should be replaced with the replacement char U+FFFD. // 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 "); 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 set -e -u
SRC_JNILIBS=app/src/main/jniLibs/
rm -Rf $SRC_JNILIBS
mkdir -p $SRC_JNILIBS
PROJECTDIR=`mktemp -d` PROJECTDIR=`mktemp -d`
JNIDIR=$PROJECTDIR/jni JNIDIR=$PROJECTDIR/jni
LIBSDIR=$PROJECTDIR/libs LIBSDIR=$PROJECTDIR/libs
@@ -12,6 +16,6 @@ mkdir $JNIDIR
cp app/src/main/jni/* $JNIDIR/ cp app/src/main/jni/* $JNIDIR/
ndk-build NDK_PROJECT_PATH=$PROJECTDIR ndk-build NDK_PROJECT_PATH=$PROJECTDIR
cp -Rf $LIBSDIR/* app/src/main/jniLibs/ cp -Rf $LIBSDIR/* $SRC_JNILIBS
rm -Rf $PROJECTDIR rm -Rf $PROJECTDIR

View File

@@ -5,7 +5,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { 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 // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // 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 distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists 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 fi
done done
SAVED="`pwd`" SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&- cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`" APP_HOME="`pwd -P`"
cd "$SAVED" >&- cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar