Compare commits

...

116 Commits
v0.49 ... v0.64

Author SHA1 Message Date
Fredrik Fornwall
fdae272214 Bump version to 0.64 2018-07-02 00:05:04 +02:00
Leonid Plyushch
b7864d6ac2 deleteFolder(): check if passed argument is a symlink
Prevents possible data loss when user replaced directory '~/storage' with
a symlink.
2018-07-01 18:00:54 +02:00
Peter Vágner
d1f0c76db3 TerminalView: only use accessibility features when accessibility is
enabled when starting the view
2018-06-29 12:31:15 +02:00
Peter Vágner
5652624fc2 Call setContentDescription in onScreenUpdated rather than in onDraw.
That will be much less expensive.
2018-06-29 12:31:15 +02:00
Peter Vágner
35a9101f84 terminalview: add contentDescription to the view so accessibility
services can get the text currently being shown.
2018-06-29 12:31:15 +02:00
David xu
2b6a10712b fix button background and row height bug in api 21 2018-06-29 12:30:02 +02:00
Matthew Klein
c80e126b8f Add a .gitattributes file 2018-06-28 11:57:12 +02:00
Fredrik Fornwall
89048274dd Bump version to 0.63 2018-06-25 14:08:50 +02:00
Leonid Plyushch
d3b4d35b2a deleteFolder(): don't treat symlinks as directory 2018-06-25 14:08:28 +02:00
Fredrik Fornwall
4080f41cc7 Bump version to 0.62 2018-06-24 01:56:43 +02:00
Tom Yan
b4e2c99d46 Stop exporting PS1
PS1 is not supposed to be an env var and should be shell-specific.
We will set it appropriately with the init files of the shells.
2018-06-22 01:11:51 +02:00
Quasic
c5923201a4 fix indent with vim on termux
web interface would have worked, if I had deleted the tabs, first
This vim is adding tabs instead of matching the previous line's
indentation. Looking into fixing that now...
2018-06-22 00:33:35 +02:00
Quasic
bafd21bb39 fmt indentation 2018-06-22 00:33:35 +02:00
Quasic
0f20fab02c I think this is it 2018-06-22 00:33:35 +02:00
Quasic
c5ae5bb06a oops, should have local Java 2018-06-22 00:33:35 +02:00
Quasic
bbd46a763c Fix for #572 using recursive delete
Also see #578
2018-06-22 00:33:35 +02:00
David xu
dc145d65f8 minify long press interval 2018-06-22 00:29:51 +02:00
David xu
ce2d1c0d88 restore terminalview 2018-06-22 00:29:51 +02:00
David xu
f2f7f963e6 feat for - ― 2018-06-22 00:29:51 +02:00
David xu
2e53ef038e fix popup bug 2018-06-22 00:29:51 +02:00
xqdoo00o
8c82f43dce delete unnecss code 2018-06-22 00:29:51 +02:00
david
3a16f461e7 add symbol popup 2018-06-22 00:29:51 +02:00
xqdoo00o
594a5dfe25 comment unnecss code 2018-06-22 00:29:51 +02:00
david
d5e007dbb3 feat button color 2018-06-22 00:29:51 +02:00
david
f4335d3824 add some functional keys 2018-06-22 00:29:51 +02:00
Michał Bednarski
90b6f93697 Add fast path for TerminalRow.setChar
termux/termux-app#603
2018-06-22 00:21:31 +02:00
Fredrik Fornwall
17c4a45212 Bump gradle dependencies 2018-06-15 00:22:52 +02:00
Fredrik Fornwall
6d9ffb6922 Bump version to 0.61 2018-06-15 00:21:27 +02:00
Fredrik Fornwall
8472fce8ba Use lower when looking up mime type from extension
Fixes #721.
2018-06-15 00:19:47 +02:00
Fredrik Fornwall
69d954a583 Update gradle configuration 2018-06-14 23:58:41 +02:00
easyaspi314 (Devin)
ec1087d56f Replace CRLF with CR as well.
This should replace both \r\n and \n with \r now.

"\r?\n" matches 0 or 1 \r and one \n, which should capture both
escape sequences.
2018-06-13 22:45:37 +02:00
easyaspi314 (Devin)
be6a73d862 Replace \n with \r when pasting (Fixes #678)
Termux will now properly send \r to the terminal instead of \n when pasting
multiline strings.

This fixes cat not repeating back lines and nano accidentally justifying
text (because \n maps to ^J), as well as other potential issues.

This matches the behavior of other terminals, such as iTerm2 which explicitly
does it here:
https://github.com/gnachman/iTerm2/blob/f8a5930/sources/iTermPasteHelper.m#L113

Signed-off-by: easyaspi314 (Devin) <easyaspi314@users.noreply.github.com>
2018-06-13 22:45:37 +02:00
l-jonas
80c81b274d Update build.gradle 2018-06-11 12:26:35 +02:00
Jonas L
cd9dbac548 Add Google Repository
The Google-Repository is required for compilation because some dependencies were removed from jcenter()
2018-06-11 12:26:35 +02:00
Fredrik Fornwall
0c837796f0 Update Android plugin for Gradle 2018-06-11 12:23:52 +02:00
Fredrik Fornwall
3417e37e8d Update gitignore for Android Studio 3.1 2018-04-02 01:38:47 +02:00
Fredrik Fornwall
31ba36e0fa Remove explicit buildToolsVersion
https://developer.android.com/studio/releases/gradle-plugin.html#behavior_changes
2018-04-02 00:58:02 +02:00
Fredrik Fornwall
c444f1fd28 Update gradle configuration 2018-04-02 00:40:16 +02:00
RangerMauve
9f36ed06b8 Make TerminalRenderer public
I want to use the renderer with a custom canvas without having to render to an Android View which requires a context and all sorts of stuff.

Can't currently do that because the renderer is package-private
2018-03-10 22:55:10 +01:00
Fredrik Fornwall
c2ab5bcd50 Bump some dependency versions 2018-02-06 23:30:28 +01:00
Fredrik Fornwall
48fab33b79 Bump version to 0.60 2018-01-26 01:00:37 +01:00
Marcel Dopita
8d7a67645b Fix notification icon color
Icons used in notifications/status bar are monochrome and should use only white color and transparency. It is displayed fine on Android emulators but it's shown in black on Samsung Galaxy S7 running Android 7.0.

After the change, status bar icon is white and the small icon in notification still uses the black color set via setColor(). Termux doesn't support devices running Android older than 5.0 but using white color in vector drawables would be also necessary to generate proper (white) bitmap icons.
2018-01-01 23:17:22 +01:00
Fredrik Fornwall
186b49d429 Work around travis API 27 SDK checksum mismatch
See https://github.com/travis-ci/travis-ci/issues/8874.
2018-01-01 23:16:43 +01:00
Fredrik Fornwall
40a2775f52 Bump version to 0.59 2017-12-09 00:59:19 +01:00
Fredrik Fornwall
f802d6001d Do not send mouse clicks out of range (fixes #501) 2017-12-09 00:58:31 +01:00
Fredrik Fornwall
50f66a12da Bump version to 0.58 2017-12-08 23:32:44 +01:00
Fredrik Fornwall
d8e6fd21d1 Set android:resizeableActivity="true"
Setting android:resizeableActivity="true" should AFAIK not be needed
as https://developer.android.com/guide/topics/ui/multi-window.html
states that:

  If your app targets API level 24, but you do not specify a value
  for this attribute, the attribute's value defaults to true.

However, on a Galaxy S8 running Android 7.0 this attribute is needed
in order to have resizeable windows when in Samsung Dex mode.

Fixes #309.
2017-12-08 23:30:45 +01:00
Fredrik Fornwall
0964d83572 Bump com.android.support dependencies 2017-12-07 10:25:02 +01:00
Fredrik Fornwall
cee0466dd7 Bump version to 0.57 2017-12-07 01:48:51 +01:00
Fredrik Fornwall
de4f334e24 Remove the fullscreen setting
Trying to use fullscreen doesn't work well in a multi-windowed world
and makes using translucent navigation and status bars more complicated.
2017-12-07 01:46:16 +01:00
Fredrik Fornwall
ab205ae05b Update build tools in travis 2017-12-07 01:45:30 +01:00
Fredrik Fornwall
b29b24f507 Add android.max_aspect=10.0 metadata
This is needed to mark the app with

	"This app is optimized to run in full screen"

when run on a Samsung Galaxy S8.
2017-12-07 01:39:43 +01:00
Fredrik Fornwall
b3472e9e62 Disable the "quick scale" gesture
Disable the default android behaviour where a double tap followed by a
swipe is interpreted as a zoom gesture. Most people seem not to know
about it and hit it by mistake, see #495 for an example.
2017-12-07 01:36:29 +01:00
Fredrik Fornwall
ab59e08959 Update build tools 2017-12-07 01:12:39 +01:00
Fredrik Fornwall
8b566485e8 Do not clear the sessions on ACTION_STOP_SERVICE
There is no point doing so and it may cause problems with the list
adapter.
2017-11-26 00:56:13 +01:00
Fredrik Fornwall
231c02a0c7 Check for null mTermSession in inputCodePoint() 2017-11-26 00:50:13 +01:00
Fredrik Fornwall
65f30e77ba Add tools:ignore="AppLinkUrlError" 2017-11-25 22:59:03 +01:00
Fredrik Fornwall
686677ae45 Update android support dependencies 2017-11-25 22:58:46 +01:00
Fredrik Fornwall
85037a75a6 Remove redundant cast 2017-11-25 22:58:23 +01:00
Fredrik Fornwall
6025afc2c0 Update the Android Plugin for Gradle to 3.0.1 2017-11-24 23:11:55 +01:00
Fredrik Fornwall
a4e4f76775 Remove some Android Studio warnings 2017-11-22 01:27:14 +01:00
Fredrik Fornwall
02af113dda Bump app version to 0.56 2017-11-19 20:54:16 +01:00
Fredrik Fornwall
fc4ef838bf Configure proguard to keep line numbers 2017-11-19 20:53:56 +01:00
Fredrik Fornwall
86bd3ca21b Bump app version to 0.54 2017-11-19 15:27:17 +01:00
Fredrik Fornwall
367398bafb Update gradle configuration 2017-11-19 15:27:00 +01:00
Lauri Tirkkonen
dd502e55f8 set default notification priority to low
since we're a foreground service, in oreo we already get a higher
priority; see
https://developer.android.com/reference/android/app/NotificationManager.html#IMPORTANCE_MIN
2017-11-12 23:44:14 +01:00
Fredrik Fornwall
798125ef7a Remove outdated comment 2017-11-12 23:06:28 +01:00
Fredrik Fornwall
5126f06e06 Update .travis.yml to latest setup 2017-11-12 22:30:09 +01:00
Fredrik Fornwall
0bdbf314ef Update gradle configuration 2017-11-07 04:25:48 +01:00
Fredrik Fornwall
2deb9899bd Update buildToolsVersion to 27.0.1 2017-11-07 04:13:26 +01:00
Fredrik Fornwall
694ccc38c4 Remove WakefulBroadcastReceiver usage 2017-11-04 23:38:49 +01:00
Fredrik Fornwall
c949940374 Update support deps 2017-11-02 01:56:42 +01:00
Fredrik Fornwall
d09f70de1e Use a notification channel on Android O 2017-11-01 21:15:41 +01:00
Fredrik Fornwall
ac338ce2c5 Update gradle to 4.3 2017-10-31 19:07:00 +01:00
Fredrik Fornwall
092a83a688 Validate file path in TermuxOpenReceiver 2017-10-28 16:19:58 +02:00
Fredrik Fornwall
3533b13de8 Get rid of two android studio warnings 2017-10-28 11:05:43 +02:00
Fredrik Fornwall
d68a0f05be Update gradle configuration 2017-10-28 11:05:43 +02:00
Fredrik Fornwall
9e5293a08e Do not scroll when below bottom margin 2017-10-28 11:05:43 +02:00
Fredrik Fornwall
3f04a0a0d5 Bump build tools version in .travis.yml 2017-10-28 11:05:43 +02:00
Fredrik Fornwall
e558f824c5 Update gradle configuration 2017-10-28 11:05:43 +02:00
Auxilus
8ff72ddd8b replace help url with wiki url
HELP page wiki changed to https://wiki.termux.com/wiki/Main_Page as it now has more info than http://termux.com/help.html
2017-09-26 18:14:19 +02:00
Fredrik Fornwall
3e05356a69 Merge pull request #421 from sdrausty/master
Added two important links to README.md
2017-09-20 22:37:49 +02:00
Fredrik Fornwall
82673d3f82 Update gradle configuration 2017-09-17 11:47:12 +02:00
Fredrik Fornwall
f2757fdb76 Merge pull request #424 from EdwardBetts/patch-1
correct spelling mistake
2017-09-15 00:21:47 +02:00
Edward Betts
81fb669d0e correct spelling mistake 2017-09-13 16:16:08 +01:00
S D Rausty
4a45b1b617 Commit on 20170911 at 13:23:22 and at 1505150602 in Unix time. 2017-09-11 13:23:22 -04:00
S D Rausty
62b08b3cf1 Merge pull request #1 from termux/master
Hello
2017-09-10 13:24:32 -04:00
Fredrik Fornwall
1e83ea3151 Merge pull request #412 from Auxilus/patch-2
Added wiki link
2017-09-06 16:26:54 +02:00
Auxilus
9c42fdb3d6 Added wiki link 2017-09-05 10:43:30 +05:30
Fredrik Fornwall
42e3163e92 Update gradle to 4.1 2017-08-24 20:49:43 +02:00
Fredrik Fornwall
68912139f6 Additional REP test 2017-08-23 23:29:59 +02:00
Fredrik Fornwall
edc92bbcbb Start supporting the REP escape sequence 2017-08-13 16:14:31 +02:00
Fredrik Fornwall
d7520642de Update gradle 2017-07-29 03:05:48 +02:00
Fredrik Fornwall
adae111d5c Update gradle config 2017-07-09 21:07:18 +02:00
Fredrik Fornwall
dc9272790b Merge pull request #301 from kzlin129/android-things-auto-launch
[Android-things] Launch TermuxActivity automatically on boot
2017-06-26 02:04:03 +02:00
Fredrik Fornwall
05ea2ea238 Bump version to 0.53 2017-06-26 01:30:08 +02:00
Fredrik Fornwall
ad293562bc Fix possible crash in the copy/paste/more dialog 2017-06-26 01:29:05 +02:00
Fredrik Fornwall
f9a565d1e0 Ignore C1 control codes
C1 control codes are not used nowadays and just risks messing up
the terminal when they are used by accident.
2017-06-26 01:28:18 +02:00
Will Lin
5b3909cb73 Update AndroidManifest.xml with expanded comment 2017-06-17 15:39:23 +08:00
Fredrik Fornwall
7913e765d5 Bump version to 0.52 2017-06-13 16:44:33 +02:00
Fredrik Fornwall
ed3a3269d8 Merge pull request #339 from bumper314/master
Add Multi Window support for Samsung devices
2017-06-13 10:13:26 +02:00
Steven Audette
dc3994d2cf Add Multi Window support for Samsung devices
A single meta-data entry is enough to make Termux "Multi Window" aware on supported Samsung devices.
Tested and confirmed working on Samsung Note Pro 12.2 running Android 5.1.1.

This addition should not interfer with Android 6+'s split screen feature.
Tested and confirmed split screen functions as normal on a Nexus 6 running Android 7.0.

This addition should go unnoticed on non-Smasung Android 5 devices.
Tested and confirmed no functionality visible on Nexus 5 running Android 5.1.
2017-06-12 01:47:27 -06:00
Fredrik Fornwall
c972377bd1 Update gradle config 2017-06-11 22:47:59 +02:00
Fredrik Fornwall
d4be782c03 Bump version to 0.51 2017-06-05 22:49:11 +02:00
Fredrik Fornwall
c29909726c Bump terminal-emulator libraryVersion to 0.50 2017-06-05 22:45:44 +02:00
Fredrik Fornwall
45bac89298 Remove unused xmlns:android declaration 2017-06-05 22:45:15 +02:00
Fredrik Fornwall
c06770c353 Merge pull request #330 from whydoubt/cursor_preserve_color
Make cursor save/restore affect color attributes
2017-06-05 22:35:39 +02:00
Fredrik Fornwall
52a627efc8 Merge pull request #331 from whydoubt/fix_parameter_fc
Fix FC from too many control code parameters
2017-06-05 22:35:32 +02:00
Jeff Smith
2e9383720c Make cursor save/restore affect color attributes
SGR attributes are stored in three variables: mEffect, mForeColor, and
mBackColor.  Saving/restoring the cursor only preserves mEffect.

Change the cursor save/restore methods to additionally preserve
mForeColor and mBackColor.  This affects both 'explit' saving/restoring
the cursor and switching to/from the alternate screen buffer.
2017-06-03 17:45:36 -05:00
Jeff Smith
58f9f1be71 Add tests for cursor save/restore 2017-06-03 17:45:36 -05:00
Jeff Smith
058441dda6 Fix FC from too many control code parameters
When the number of parameters in a CSI control code exceeds the size of
the mArgs array, the code may attempt to read past the end of the array,
resulting in a force close of the app.

Stop the index from moving past the last element of the mArgs array.
2017-06-03 00:15:09 -05:00
Jeff Smith
888802a519 Add test for CSI followed by many parameters 2017-06-03 00:14:20 -05:00
Fredrik Fornwall
1a09b6d2a6 Change android:extractNativeLibs to false
We're trying this to see if that fixes an F-Droid build issue.

See #319.
2017-05-18 13:25:41 +02:00
Fredrik Fornwall
9d3f7734a1 Update android build tools 2017-05-16 11:11:57 +02:00
Fredrik Fornwall
61f766b59f Bump build tools in .travis.yml 2017-05-16 00:21:15 +02:00
Will Lin
1be5139253 [Android-things] Launch TermuxActivity automatically on boot 2017-04-18 20:13:47 +08:00
42 changed files with 437 additions and 228 deletions

3
.gitattributes vendored Normal file
View File

@@ -0,0 +1,3 @@
* text=auto
*.bat eol=crlf
*.sh eol=lf

2
.gitignore vendored
View File

@@ -33,6 +33,8 @@ local.properties
.idea/scopes/scope_settings.xml
.idea/vcs.xml
.idea/dictionaries/
.idea/caches/
.idea/codeStyles/
*.iml
# OS-specific files

View File

@@ -12,13 +12,14 @@ android:
components:
- platform-tools
- tools
- build-tools-25.0.2
- android-25
- build-tools-27.0.3
- android-27
- extra-android-m2repository
before_install:
- git clone https://github.com/urho3d/android-ndk.git $HOME/android-ndk
- export ANDROID_NDK_HOME=$HOME/android-ndk
- yes | sdkmanager "platforms;android-27"
script:
- ./gradlew testDebugUnitTest

View File

@@ -7,8 +7,11 @@ Termux app
* [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 Help](http://termux.com/help/)
* [Termux Facebook](https://facebook.com/termux/)
* [Termux Google+ community](http://termux.com/community/)
* [Termux Help](http://termux.com/help/)
* [Termux Twitter](http://twitter.com/termux/)
* [Termux Wiki](https://wiki.termux.com/wiki/)
Note that this repository is for the app itself (the user interface and the terminal emulation). For the packages installable inside the app, see [termux/termux-packages](https://github.com/termux/termux-packages)

View File

@@ -1,21 +1,20 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "25.0.3"
compileSdkVersion 27
dependencies {
compile 'com.android.support:support-annotations:25.3.1'
compile "com.android.support:support-v4:25.3.1"
compile project(":terminal-view")
implementation 'com.android.support:support-annotations:27.1.1'
implementation "com.android.support:support-core-ui:27.1.1"
implementation project(":terminal-view")
}
defaultConfig {
applicationId "com.termux"
minSdkVersion 21
targetSdkVersion 25
versionCode 49
versionName "0.49"
targetSdkVersion 27
versionCode 64
versionName "0.64"
}
buildTypes {
@@ -28,5 +27,5 @@ android {
}
dependencies {
testCompile 'junit:junit:4.12'
testImplementation 'junit:junit:4.12'
}

View File

@@ -7,11 +7,5 @@
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
-renamesourcefileattribute SourceFile
-keepattributes SourceFile,LineNumberTable

View File

@@ -1,4 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.termux"
android:installLocation="internalOnly"
android:sharedUserId="com.termux"
@@ -13,7 +14,7 @@
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:extractNativeLibs="false"
android:extractNativeLibs="true"
android:allowBackup="true"
android:fullBackupContent="@xml/backupscheme"
android:icon="@mipmap/ic_launcher"
@@ -23,10 +24,15 @@
android:theme="@style/Theme.Termux"
android:supportsRtl="false" >
<!-- This (or rather, value 2.1 or higher) is needed to make the Samsung Galaxy S8
mark the app with "This app is optimized to run in full screen." -->
<meta-data android:name="android.max_aspect" android:value="10.0" />
<activity
android:name="com.termux.app.TermuxActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:launchMode="singleTask"
android:resizeableActivity="true"
android:windowSoftInputMode="adjustResize|stateAlwaysVisible" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -44,6 +50,7 @@
android:exported="false"
android:theme="@android:style/Theme.Material.Light.DarkActionBar"
android:parentActivityName=".app.TermuxActivity"
android:resizeableActivity="true"
android:label="@string/application_name" />
<activity
@@ -51,6 +58,7 @@
android:label="@string/application_name"
android:taskAffinity="com.termux.filereceiver"
android:excludeFromRecents="true"
android:resizeableActivity="true"
android:noHistory="true">
<!-- Accept multiple file types when sending. -->
<intent-filter>
@@ -65,7 +73,7 @@
<data android:mimeType="video/*" />
</intent-filter>
<!-- Be more restrictive for viewing files, restricting ourselves to text files. -->
<intent-filter>
<intent-filter tools:ignore="AppLinkUrlError">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/*" />
@@ -76,6 +84,18 @@
</intent-filter>
</activity>
<activity-alias
android:name=".HomeActivity"
android:targetActivity="com.termux.app.TermuxActivity">
<!-- Launch activity automatically on boot on Android Things devices -->
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.IOT_LAUNCHER"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity-alias>
<provider
android:name=".filepicker.TermuxDocumentsProvider"
android:authorities="com.termux.documents"
@@ -99,6 +119,7 @@
android:grantUriPermissions="true"
android:name="com.termux.app.TermuxOpenReceiver$ContentProvider" />
<meta-data android:name="com.sec.android.support.multiwindow" android:value="true" />
</application>
</manifest>

View File

@@ -111,14 +111,13 @@ public final class BackgroundJob {
final String pathEnv = "PATH=" + System.getenv("PATH");
return new String[]{termEnv, homeEnv, prefixEnv, androidRootEnv, androidDataEnv, pathEnv, externalStorageEnv};
} else {
final String ps1Env = "PS1=$ ";
final String ldEnv = "LD_LIBRARY_PATH=" + TermuxService.PREFIX_PATH + "/lib";
final String langEnv = "LANG=en_US.UTF-8";
final String pathEnv = "PATH=" + TermuxService.PREFIX_PATH + "/bin:" + TermuxService.PREFIX_PATH + "/bin/applets";
final String pwdEnv = "PWD=" + cwd;
final String tmpdirEnv = "TMPDIR=" + TermuxService.PREFIX_PATH + "/tmp";
return new String[]{termEnv, homeEnv, prefixEnv, ps1Env, ldEnv, langEnv, pathEnv, pwdEnv, androidRootEnv, androidDataEnv, externalStorageEnv, tmpdirEnv};
return new String[]{termEnv, homeEnv, prefixEnv, ldEnv, langEnv, pathEnv, pwdEnv, androidRootEnv, androidDataEnv, externalStorageEnv, tmpdirEnv};
}
}

View File

@@ -1,13 +1,20 @@
package com.termux.app;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.Gravity;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ScheduledExecutorService;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.GridLayout;
import android.widget.PopupWindow;
import android.widget.ToggleButton;
import com.termux.R;
@@ -21,6 +28,8 @@ import com.termux.view.TerminalView;
public final class ExtraKeysView extends GridLayout {
private static final int TEXT_COLOR = 0xFFFFFFFF;
private static final int BUTTON_COLOR = 0x00000000;
private static final int BUTTON_PRESSED_COLOR = 0x7FFFFFFF;
public ExtraKeysView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -38,16 +47,28 @@ public final class ExtraKeysView extends GridLayout {
case "TAB":
keyCode = KeyEvent.KEYCODE_TAB;
break;
case "":
case "HOME":
keyCode = KeyEvent.KEYCODE_MOVE_HOME;
break;
case "END":
keyCode = KeyEvent.KEYCODE_MOVE_END;
break;
case "PGUP":
keyCode = KeyEvent.KEYCODE_PAGE_UP;
break;
case "PGDN":
keyCode = KeyEvent.KEYCODE_PAGE_DOWN;
break;
case "":
keyCode = KeyEvent.KEYCODE_DPAD_UP;
break;
case "":
case "":
keyCode = KeyEvent.KEYCODE_DPAD_LEFT;
break;
case "":
case "":
keyCode = KeyEvent.KEYCODE_DPAD_RIGHT;
break;
case "":
case "":
keyCode = KeyEvent.KEYCODE_DPAD_DOWN;
break;
case "":
@@ -57,11 +78,11 @@ public final class ExtraKeysView extends GridLayout {
chars = keyName;
}
TerminalView terminalView = view.findViewById(R.id.terminal_view);
if (keyCode > 0) {
view.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
view.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyCode));
terminalView.onKeyDown(keyCode, new KeyEvent(KeyEvent.ACTION_UP, keyCode));
// view.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyCode));
} else {
TerminalView terminalView = (TerminalView) view.findViewById(R.id.terminal_view);
TerminalSession session = terminalView.getCurrentSession();
if (session != null) session.write(chars);
}
@@ -70,6 +91,9 @@ public final class ExtraKeysView extends GridLayout {
private ToggleButton controlButton;
private ToggleButton altButton;
private ToggleButton fnButton;
private ScheduledExecutorService scheduledExecutor;
private PopupWindow popupWindow;
private int longPressCount;
public boolean readControlButton() {
if (controlButton.isPressed()) return true;
@@ -101,24 +125,47 @@ public final class ExtraKeysView extends GridLayout {
return result;
}
void popup(View view, String text) {
int width = view.getMeasuredWidth();
int height = view.getMeasuredHeight();
Button button = new Button(getContext(), null, android.R.attr.buttonBarButtonStyle);
button.setText(text);
button.setTextColor(TEXT_COLOR);
button.setPadding(0, 0, 0, 0);
button.setMinHeight(0);
button.setMinWidth(0);
button.setMinimumWidth(0);
button.setMinimumHeight(0);
button.setWidth(width);
button.setHeight(height);
button.setBackgroundColor(BUTTON_PRESSED_COLOR);
popupWindow = new PopupWindow(this);
popupWindow.setWidth(LayoutParams.WRAP_CONTENT);
popupWindow.setHeight(LayoutParams.WRAP_CONTENT);
popupWindow.setContentView(button);
popupWindow.setOutsideTouchable(true);
popupWindow.setFocusable(false);
popupWindow.showAsDropDown(view, 0, -2 * height);
}
void reload() {
altButton = controlButton = null;
removeAllViews();
String[][] buttons = {
{"ESC", "CTRL", "ALT", "TAB", "", "/", "|"}
{"ESC", "/", "", "HOME", "", "END", "PGUP"},
{"TAB", "CTRL", "ALT", "", "", "", "PGDN"}
};
final int rows = buttons.length;
final int cols = buttons[0].length;
final int[] cols = {buttons[0].length, buttons[1].length};
setRowCount(rows);
setColumnCount(cols);
setColumnCount(cols[0]);
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
for (int col = 0; col < cols[row]; col++) {
final String buttonText = buttons[row][col];
Button button;
switch (buttonText) {
case "CTRL":
@@ -140,6 +187,7 @@ public final class ExtraKeysView extends GridLayout {
button.setText(buttonText);
button.setTextColor(TEXT_COLOR);
button.setPadding(0, 0, 0, 0);
final Button finalButton = button;
button.setOnClickListener(new OnClickListener() {
@@ -162,12 +210,73 @@ public final class ExtraKeysView extends GridLayout {
}
});
GridLayout.LayoutParams param = new GridLayout.LayoutParams();
param.height = param.width = 0;
param.rightMargin = param.topMargin = 0;
param.setGravity(Gravity.LEFT);
float weight = "▲▼◀▶".contains(buttonText) ? 0.7f : 1.f;
param.columnSpec = GridLayout.spec(col, GridLayout.FILL, weight);
button.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
final View root = getRootView();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
longPressCount = 0;
v.setBackgroundColor(BUTTON_PRESSED_COLOR);
if ("↑↓←→".contains(buttonText)) {
scheduledExecutor = Executors.newSingleThreadScheduledExecutor();
scheduledExecutor.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
longPressCount++;
sendKey(root, buttonText);
}
}, 400, 80, TimeUnit.MILLISECONDS);
}
return true;
case MotionEvent.ACTION_MOVE:
if ("―/".contains(buttonText)) {
if (popupWindow == null && event.getY() < 0) {
v.setBackgroundColor(BUTTON_COLOR);
String text = "".equals(buttonText) ? "|" : "\\";
popup(v, text);
}
if (popupWindow != null && event.getY() > 0) {
v.setBackgroundColor(BUTTON_PRESSED_COLOR);
popupWindow.dismiss();
popupWindow = null;
}
}
return true;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
v.setBackgroundColor(BUTTON_COLOR);
if (scheduledExecutor != null) {
scheduledExecutor.shutdownNow();
scheduledExecutor = null;
}
if (longPressCount == 0) {
if (popupWindow != null && "―/".contains(buttonText)) {
popupWindow.setContentView(null);
popupWindow.dismiss();
popupWindow = null;
sendKey(root, "".equals(buttonText) ? "|" : "\\");
} else {
v.performClick();
}
}
return true;
default:
return true;
}
}
});
LayoutParams param = new GridLayout.LayoutParams();
param.width = 0;
if(Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP){ //special handle api 21
param.height = (int)(37.5 * getResources().getDisplayMetrics().density + 0.5); // 37.5 equal to R.id.viewpager layout_heihgt / rows in DP
}else{
param.height = 0;
}
param.setMargins(0, 0, 0, 0);
param.columnSpec = GridLayout.spec(col, GridLayout.FILL, 1.f);
param.rowSpec = GridLayout.spec(row, GridLayout.FILL, 1.f);
button.setLayoutParams(param);

View File

@@ -1,67 +0,0 @@
package com.termux.app;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.view.View;
import com.termux.R;
/**
* Utility to manage full screen immersive mode.
* <p/>
* See https://code.google.com/p/android/issues/detail?id=5497
*/
final class FullScreenHelper {
private boolean mEnabled = false;
final TermuxActivity mActivity;
public FullScreenHelper(TermuxActivity activity) {
this.mActivity = activity;
}
public void setImmersive(boolean enabled) {
if (enabled == mEnabled) return;
mEnabled = enabled;
View decorView = mActivity.getWindow().getDecorView();
if (enabled) {
decorView.setOnSystemUiVisibilityChangeListener
(new View.OnSystemUiVisibilityChangeListener() {
@Override
public void onSystemUiVisibilityChange(int visibility) {
if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
if (mActivity.mSettings.isShowExtraKeys()) {
mActivity.findViewById(R.id.viewpager).setVisibility(View.VISIBLE);
}
setImmersiveMode();
} else {
mActivity.findViewById(R.id.viewpager).setVisibility(View.GONE);
}
}
});
setImmersiveMode();
} else {
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
decorView.setOnSystemUiVisibilityChangeListener(null);
}
}
private static boolean isColorLight(int color) {
double darkness = 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255;
return darkness < 0.5;
}
void setImmersiveMode() {
int flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
| View.SYSTEM_UI_FLAG_FULLSCREEN;
int color = ((ColorDrawable) mActivity.getWindow().getDecorView().getBackground()).getColor();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && isColorLight(color))
flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
mActivity.getWindow().getDecorView().setSystemUiVisibility(flags);
}
}

View File

@@ -73,6 +73,7 @@ import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -95,7 +96,6 @@ public final class TermuxActivity extends Activity implements ServiceConnection
private static final int CONTEXTMENU_KILL_PROCESS_ID = 4;
private static final int CONTEXTMENU_RESET_TERMINAL_ID = 5;
private static final int CONTEXTMENU_STYLING_ID = 6;
private static final int CONTEXTMENU_TOGGLE_FULLSCREEN_ID = 7;
private static final int CONTEXTMENU_HELP_ID = 8;
private static final int MAX_SESSIONS = 8;
@@ -111,8 +111,6 @@ public final class TermuxActivity extends Activity implements ServiceConnection
ExtraKeysView mExtraKeysView;
final FullScreenHelper mFullScreenHelper = new FullScreenHelper(this);
TermuxPreferences mSettings;
/**
@@ -157,7 +155,6 @@ public final class TermuxActivity extends Activity implements ServiceConnection
void checkForFontAndColors() {
try {
// Hard-coded paths since this file is used also in Termux:Float.
@SuppressLint("SdCardPath") File fontFile = new File("/data/data/com.termux/files/home/.termux/font.ttf");
@SuppressLint("SdCardPath") File colorsFile = new File("/data/data/com.termux/files/home/.termux/colors.properties");
@@ -212,14 +209,13 @@ public final class TermuxActivity extends Activity implements ServiceConnection
mSettings = new TermuxPreferences(this);
setContentView(R.layout.drawer_layout);
mTerminalView = (TerminalView) findViewById(R.id.terminal_view);
mTerminalView = findViewById(R.id.terminal_view);
mTerminalView.setOnKeyListener(new TermuxViewClient(this));
mTerminalView.setTextSize(mSettings.getFontSize());
mFullScreenHelper.setImmersive(mSettings.isFullScreen());
mTerminalView.requestFocus();
final ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
final ViewPager viewPager = findViewById(R.id.viewpager);
if (mSettings.isShowExtraKeys()) viewPager.setVisibility(View.VISIBLE);
viewPager.setAdapter(new PagerAdapter() {
@@ -229,19 +225,20 @@ public final class TermuxActivity extends Activity implements ServiceConnection
}
@Override
public boolean isViewFromObject(View view, Object object) {
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view == object;
}
@NonNull
@Override
public Object instantiateItem(ViewGroup collection, int position) {
public Object instantiateItem(@NonNull ViewGroup collection, int position) {
LayoutInflater inflater = LayoutInflater.from(TermuxActivity.this);
View layout;
if (position == 0) {
layout = mExtraKeysView = (ExtraKeysView) inflater.inflate(R.layout.extra_keys_main, collection, false);
} else {
layout = inflater.inflate(R.layout.extra_keys_right, collection, false);
final EditText editText = (EditText) layout.findViewById(R.id.text_input);
final EditText editText = layout.findViewById(R.id.text_input);
editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
@@ -265,7 +262,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
}
@Override
public void destroyItem(ViewGroup collection, int position, Object view) {
public void destroyItem(@NonNull ViewGroup collection, int position, @NonNull Object view) {
collection.removeView((View) view);
}
});
@@ -276,7 +273,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
if (position == 0) {
mTerminalView.requestFocus();
} else {
final EditText editText = (EditText) viewPager.findViewById(R.id.text_input);
final EditText editText = viewPager.findViewById(R.id.text_input);
if (editText != null) editText.requestFocus();
}
}
@@ -340,7 +337,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
}
void toggleShowExtraKeys() {
final ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
final ViewPager viewPager = findViewById(R.id.viewpager);
final boolean showNow = mSettings.toggleShowExtraKeys(TermuxActivity.this);
viewPager.setVisibility(showNow ? View.VISIBLE : View.GONE);
if (showNow && viewPager.getCurrentItem() == 1) {
@@ -425,7 +422,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
}
};
ListView listView = (ListView) findViewById(R.id.left_drawer_list);
ListView listView = findViewById(R.id.left_drawer_list);
mListViewAdapter = new ArrayAdapter<TerminalSession>(getApplicationContext(), R.layout.line_in_drawer, mTermService.getSessions()) {
final StyleSpan boldSpan = new StyleSpan(Typeface.BOLD);
final StyleSpan italicSpan = new StyleSpan(Typeface.ITALIC);
@@ -442,7 +439,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
TerminalSession sessionAtRow = getItem(position);
boolean sessionRunning = sessionAtRow.isRunning();
TextView firstLineView = (TextView) row.findViewById(R.id.row_line);
TextView firstLineView = row.findViewById(R.id.row_line);
String name = sessionAtRow.mSessionName;
String sessionTitle = sessionAtRow.getTitle();
@@ -538,10 +535,8 @@ public final class TermuxActivity extends Activity implements ServiceConnection
@Override
public void onServiceDisconnected(ComponentName name) {
if (mTermService != null) {
// Respect being stopped from the TermuxService notification action.
finish();
}
// Respect being stopped from the TermuxService notification action.
finish();
}
@Nullable
@@ -645,7 +640,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
final int indexOfSession = mTermService.getSessions().indexOf(session);
showToast(toToastTitle(session), false);
mListViewAdapter.notifyDataSetChanged();
final ListView lv = ((ListView) findViewById(R.id.left_drawer_list));
final ListView lv = findViewById(R.id.left_drawer_list);
lv.setItemChecked(indexOfSession, true);
lv.smoothScrollToPosition(indexOfSession);
}
@@ -659,7 +654,6 @@ public final class TermuxActivity extends Activity implements ServiceConnection
menu.add(Menu.NONE, CONTEXTMENU_SHARE_TRANSCRIPT_ID, Menu.NONE, R.string.select_all_and_share);
menu.add(Menu.NONE, CONTEXTMENU_RESET_TERMINAL_ID, Menu.NONE, R.string.reset_terminal);
menu.add(Menu.NONE, CONTEXTMENU_KILL_PROCESS_ID, Menu.NONE, getResources().getString(R.string.kill_process, getCurrentTermSession().getPid())).setEnabled(currentSession.isRunning());
menu.add(Menu.NONE, CONTEXTMENU_TOGGLE_FULLSCREEN_ID, Menu.NONE, R.string.toggle_fullscreen).setCheckable(true).setChecked(mSettings.isFullScreen());
menu.add(Menu.NONE, CONTEXTMENU_STYLING_ID, Menu.NONE, R.string.style_terminal);
menu.add(Menu.NONE, CONTEXTMENU_HELP_ID, Menu.NONE, R.string.help);
}
@@ -793,11 +787,8 @@ public final class TermuxActivity extends Activity implements ServiceConnection
}
}).setNegativeButton(android.R.string.cancel, null).show();
}
}
return true;
case CONTEXTMENU_TOGGLE_FULLSCREEN_ID:
toggleImmersive();
return true;
}
case CONTEXTMENU_HELP_ID:
startActivity(new Intent(this, TermuxHelpActivity.class));
return true;
@@ -813,12 +804,6 @@ public final class TermuxActivity extends Activity implements ServiceConnection
}
}
void toggleImmersive() {
boolean newValue = !mSettings.isFullScreen();
mSettings.setFullScreen(this, newValue);
mFullScreenHelper.setImmersive(newValue);
}
void changeFontSize(boolean increase) {
mSettings.changeFontSize(this, increase);
mTerminalView.setTextSize(mSettings.getFontSize());
@@ -837,9 +822,8 @@ public final class TermuxActivity extends Activity implements ServiceConnection
public TerminalSession getStoredCurrentSessionOrLast() {
TerminalSession stored = TermuxPreferences.getCurrentSession(this);
if (stored != null) return stored;
int numberOfSessions = mTermService.getSessions().size();
if (numberOfSessions == 0) return null;
return mTermService.getSessions().get(numberOfSessions - 1);
List<TerminalSession> sessions = mTermService.getSessions();
return sessions.isEmpty() ? null : sessions.get(sessions.size() - 1);
}
/** Show a toast and dismiss the last one if still visible. */

View File

@@ -39,7 +39,7 @@ public final class TermuxHelpActivity extends Activity {
mWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("https://termux.com")) {
if (url.startsWith("https://wiki.termux.com")) {
// Inline help.
setContentView(progressLayout);
return false;
@@ -60,7 +60,7 @@ public final class TermuxHelpActivity extends Activity {
setContentView(mWebView);
}
});
mWebView.loadUrl("https://termux.com/help.html");
mWebView.loadUrl("https://wiki.termux.com/wiki/Main_Page");
}
@Override

View File

@@ -21,6 +21,7 @@ import com.termux.terminal.EmulatorDebug;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
@@ -43,7 +44,7 @@ import java.util.zip.ZipInputStream;
* (4) The architecture is determined and an appropriate bootstrap zip url is determined in {@link #determineZipUrl()}.
* <p/>
* (5) The zip, containing entries relative to the $PREFIX, is is downloaded and extracted by a zip input stream
* continously encountering zip file entries:
* continuously encountering zip file entries:
* <p/>
* (5.1) If the zip entry encountered is SYMLINKS.txt, go through it and remember all symlinks to setup.
* <p/>
@@ -208,14 +209,18 @@ final class TermuxInstaller {
Arrays.toString(Build.SUPPORTED_ABIS));
}
/** Delete a folder and all its content or throw. */
static void deleteFolder(File fileOrDirectory) {
File[] children = fileOrDirectory.listFiles();
if (children != null) {
for (File child : children) {
deleteFolder(child);
/** Delete a folder and all its content or throw. Don't follow symlinks. */
static void deleteFolder(File fileOrDirectory) throws IOException {
if (fileOrDirectory.getCanonicalPath().equals(fileOrDirectory.getAbsolutePath()) && fileOrDirectory.isDirectory()) {
File[] children = fileOrDirectory.listFiles();
if (children != null) {
for (File child : children) {
deleteFolder(child);
}
}
}
if (!fileOrDirectory.delete()) {
throw new RuntimeException("Unable to delete " + (fileOrDirectory.isDirectory() ? "directory " : "file ") + fileOrDirectory.getAbsolutePath());
}
@@ -228,9 +233,13 @@ final class TermuxInstaller {
try {
File storageDir = new File(TermuxService.HOME_PATH, "storage");
if (storageDir.exists() && !storageDir.delete()) {
Log.e(LOG_TAG, "Could not delete old $HOME/storage");
return;
if (storageDir.exists()) {
try {
deleteFolder(storageDir);
} catch (IOException e) {
Log.e(LOG_TAG, "Could not delete old $HOME/storage, " + e.getMessage());
return;
}
}
if (!storageDir.mkdirs()) {

View File

@@ -8,6 +8,7 @@ import android.content.Intent;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
@@ -18,6 +19,7 @@ import com.termux.terminal.EmulatorDebug;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
public class TermuxOpenReceiver extends BroadcastReceiver {
@@ -171,6 +173,16 @@ public class TermuxOpenReceiver extends BroadcastReceiver {
@Override
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
File file = new File(uri.getPath());
try {
String path = file.getCanonicalPath();
String storagePath = Environment.getExternalStorageDirectory().getCanonicalPath();
// See https://support.google.com/faqs/answer/7496913:
if (!(path.startsWith(TermuxService.FILES_PATH) || path.startsWith(storagePath))) {
throw new IllegalArgumentException("Invalid path: " + path);
}
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
}
}

View File

@@ -32,12 +32,10 @@ final class TermuxPreferences {
private final int MIN_FONTSIZE;
private static final int MAX_FONTSIZE = 256;
private static final String FULLSCREEN_KEY = "fullscreen";
private static final String SHOW_EXTRA_KEYS_KEY = "show_extra_keys";
private static final String FONTSIZE_KEY = "fontsize";
private static final String CURRENT_SESSION_KEY = "current_session";
private boolean mFullScreen;
private int mFontSize;
@AsciiBellBehaviour
@@ -56,7 +54,6 @@ final class TermuxPreferences {
// to prevent invisible text due to zoom be mistake:
MIN_FONTSIZE = (int) (4f * dipInPixels);
mFullScreen = prefs.getBoolean(FULLSCREEN_KEY, false);
mShowExtraKeys = prefs.getBoolean(SHOW_EXTRA_KEYS_KEY, false);
// http://www.google.com/design/spec/style/typography.html#typography-line-height
@@ -72,15 +69,6 @@ final class TermuxPreferences {
mFontSize = Math.max(MIN_FONTSIZE, Math.min(mFontSize, MAX_FONTSIZE));
}
boolean isFullScreen() {
return mFullScreen;
}
void setFullScreen(Context context, boolean newValue) {
mFullScreen = newValue;
PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(FULLSCREEN_KEY, newValue).apply();
}
boolean isShowExtraKeys() {
return mShowExtraKeys;
}

View File

@@ -2,6 +2,7 @@ package com.termux.app;
import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
@@ -11,10 +12,10 @@ import android.content.res.Resources;
import android.net.Uri;
import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManager;
import android.support.v4.content.WakefulBroadcastReceiver;
import android.util.Log;
import android.widget.ArrayAdapter;
@@ -41,6 +42,8 @@ import java.util.List;
*/
public final class TermuxService extends Service implements SessionChangedCallback {
private static final String NOTIFICATION_CHANNEL_ID = "termux_notification_channel";
/** Note that this is a symlink on the Android M preview. */
@SuppressLint("SdCardPath")
public static final String FILES_PATH = "/data/data/com.termux/files";
@@ -153,11 +156,6 @@ public final class TermuxService extends Service implements SessionChangedCallba
Log.e(EmulatorDebug.LOG_TAG, "Unknown TermuxService action: '" + action + "'");
}
if ((flags & START_FLAG_REDELIVERY) == 0) {
// Service is started by WBR, not restarted by system, so release the WakeLock from WBR.
WakefulBroadcastReceiver.completeWakefulIntent(intent);
}
// If this service really do get killed, there is no point restarting it automatically - let the user do on next
// start of {@link Term):
return Service.START_NOT_STICKY;
@@ -170,6 +168,7 @@ public final class TermuxService extends Service implements SessionChangedCallba
@Override
public void onCreate() {
setupNotificationChannel();
startForeground(NOTIFICATION_ID, buildNotification());
}
@@ -208,8 +207,8 @@ public final class TermuxService extends Service implements SessionChangedCallba
builder.setOngoing(true);
// If holding a wake or wifi lock consider the notification of high priority since it's using power,
// otherwise use a minimal priority since this is just a background service notification:
builder.setPriority((wakeLockHeld) ? Notification.PRIORITY_HIGH : Notification.PRIORITY_MIN);
// otherwise use a low priority
builder.setPriority((wakeLockHeld) ? Notification.PRIORITY_HIGH : Notification.PRIORITY_LOW);
// No need to show a timestamp:
builder.setShowWhen(false);
@@ -217,6 +216,10 @@ public final class TermuxService extends Service implements SessionChangedCallba
// Background color for small notification icon:
builder.setColor(0xFF000000);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder.setChannelId(NOTIFICATION_CHANNEL_ID);
}
Resources res = getResources();
Intent exitIntent = new Intent(this, TermuxService.class).setAction(ACTION_STOP_SERVICE);
builder.addAction(android.R.drawable.ic_delete, res.getString(R.string.notification_action_exit), PendingIntent.getService(this, 0, exitIntent, 0));
@@ -241,7 +244,6 @@ public final class TermuxService extends Service implements SessionChangedCallba
for (int i = 0; i < mTerminalSessions.size(); i++)
mTerminalSessions.get(i).finishIfRunning();
mTerminalSessions.clear();
}
public List<TerminalSession> getSessions() {
@@ -341,4 +343,17 @@ public final class TermuxService extends Service implements SessionChangedCallba
}
});
}
private void setupNotificationChannel() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
String channelName = "Termux";
String channelDescription = "Notifications from Termux";
int importance = NotificationManager.IMPORTANCE_LOW;
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName,importance);
channel.setDescription(channelDescription);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.createNotificationChannel(channel);
}
}

View File

@@ -73,8 +73,6 @@ public final class TermuxViewClient implements TerminalViewClient {
mActivity.getDrawer().openDrawer(Gravity.LEFT);
} else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
mActivity.getDrawer().closeDrawers();
} else if (unicodeChar == 'f'/* full screen */) {
mActivity.toggleImmersive();
} else if (unicodeChar == 'k'/* keyboard */) {
InputMethodManager imm = (InputMethodManager) mActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);

View File

@@ -195,7 +195,7 @@ public class TermuxDocumentsProvider extends DocumentsProvider {
final String name = file.getName();
final int lastDot = name.lastIndexOf('.');
if (lastDot >= 0) {
final String extension = name.substring(lastDot + 1);
final String extension = name.substring(lastDot + 1).toLowerCase();
final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
if (mime != null) return mime;
}

View File

@@ -9,7 +9,7 @@
<!-- Screen border. -->
<path android:fillColor="#00000000"
android:strokeColor="#000"
android:strokeColor="#FFF"
android:strokeWidth="3"
android:pathData="M7,4
l34,0
@@ -23,7 +23,7 @@
/>
<!-- Block cursor. -->
<path android:fillColor="#000"
<path android:fillColor="#FFF"
android:pathData="M14,14
l5,0
l0,10

View File

@@ -1,7 +1,8 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
android:orientation="vertical"
android:fitsSystemWindows="true">
<android.support.v4.widget.DrawerLayout
android:id="@+id/drawer_layout"
@@ -69,7 +70,7 @@
android:id="@+id/viewpager"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_height="75dp"
android:background="@android:drawable/screen_background_dark_transparent"
android:layout_alignParentBottom="true" />
</RelativeLayout>

View File

@@ -7,7 +7,6 @@
<string name="toggle_soft_keyboard">Keyboard</string>
<string name="reset_terminal">Reset</string>
<string name="style_terminal">Style</string>
<string name="toggle_fullscreen">Fullscreen</string>
<string name="share_transcript_title">Terminal transcript</string>
<string name="help">Help</string>

View File

@@ -15,6 +15,9 @@
selecting text on pre-6.0 (non-floating toolbar). -->
<item name="android:windowActionModeOverlay">true</item>
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
<!-- https://developer.android.com/training/tv/start/start.html#transition-color -->
<item name="android:windowAllowReturnTransitionOverlap">true</item>
<item name="android:windowAllowEnterTransitionOverlap">true</item>

View File

@@ -1,16 +1,16 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.2'
classpath 'com.android.tools.build:gradle:3.1.3'
}
}
allprojects {
repositories {
google()
jcenter()
}
}

Binary file not shown.

View File

@@ -1,6 +1,5 @@
#Mon Mar 06 01:34:12 CET 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-bin.zip

6
gradlew vendored
View File

@@ -33,11 +33,11 @@ DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
warn () {
echo "$*"
}
die ( ) {
die () {
echo
echo "$*"
echo
@@ -155,7 +155,7 @@ if $cygwin ; then
fi
# Escape application args
save ( ) {
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}

View File

@@ -1,5 +1,4 @@
// Start https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle
apply plugin: 'com.github.dcendents.android-maven'
group = publishedGroupId // Maven Group ID for the artifact
install {
repositories.mavenInstaller {

View File

@@ -1,6 +1,6 @@
plugins {
id "com.jfrog.bintray" version "1.7"
id "com.github.dcendents.android-maven" version "1.5"
id "com.jfrog.bintray" version "1.7.3"
id "com.github.dcendents.android-maven" version "2.0"
}
apply plugin: 'com.android.library'
@@ -13,16 +13,15 @@ ext {
libraryDescription = 'The terminal emulator used in Termux'
siteUrl = 'https://github.com/termux/termux'
gitUrl = 'https://github.com/termux/termux.git'
libraryVersion = '0.49'
libraryVersion = '0.52'
}
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
compileSdkVersion 27
defaultConfig {
minSdkVersion 21
targetSdkVersion 25
targetSdkVersion 27
externalNativeBuild {
ndkBuild {
@@ -56,7 +55,7 @@ tasks.withType(Test) {
}
dependencies {
testCompile 'junit:junit:4.12'
testImplementation 'junit:junit:4.12'
}
apply from: '../scripts/bintray-publish.gradle'

View File

@@ -1,3 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.termux.terminal">
<manifest package="com.termux.terminal">
</manifest>

View File

@@ -223,6 +223,7 @@ public final class TerminalEmulator {
private byte mUtf8ToFollow, mUtf8Index;
private final byte[] mUtf8InputBuffer = new byte[4];
private int mLastEmittedCodePoint = -1;
public final TerminalColors mColors = new TerminalColors();
@@ -300,6 +301,11 @@ public final class TerminalEmulator {
* @param mouseButton one of the MOUSE_* constants of this class.
*/
public void sendMouseEvent(int mouseButton, int column, int row, boolean pressed) {
if (column < 1) column = 1;
if (column > mColumns) column = mColumns;
if (row < 1) row = 1;
if (row > mRows) row = mRows;
if (mouseButton == MOUSE_LEFT_BUTTON_MOVED && !isDecsetInternalBitSet(DECSET_BIT_MOUSE_TRACKING_BUTTON_EVENT)) {
// Do not send tracking.
} else if (isDecsetInternalBitSet(DECSET_BIT_MOUSE_PROTOCOL_SGR)) {
@@ -307,7 +313,7 @@ public final class TerminalEmulator {
} else {
mouseButton = pressed ? mouseButton : 3; // 3 for release of all buttons.
// Clip to screen, and clip to the limits of 8-bit data.
boolean out_of_bounds = column < 1 || row < 1 || column > mColumns || row > mRows || column > 255 - 32 || row > 255 - 32;
boolean out_of_bounds = column > 255 - 32 || row > 255 - 32;
if (!out_of_bounds) {
byte[] data = {'\033', '[', 'M', (byte) (32 + mouseButton), (byte) (32 + column), (byte) (32 + row)};
mSession.write(data, 0, data.length);
@@ -419,10 +425,11 @@ public final class TerminalEmulator {
mUtf8Index = mUtf8ToFollow = 0;
if (codePoint >= 0x80 && codePoint <= 0x9F) {
// Sequence decoded to a C1 control character which is the same as escape followed by
// ((code & 0x7F) + 0x40).
processCodePoint(/* escape (hexadecimal=0x1B, octal=033): */27);
processCodePoint((codePoint & 0x7F) + 0x40);
// Sequence decoded to a C1 control character which we ignore. They are
// not used nowadays and increases the risk of messing up the terminal state
// on binary input. XTerm does not allow them in utf-8:
// "It is not possible to use a C1 control obtained from decoding the
// UTF-8 text" - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
} else {
switch (Character.getType(codePoint)) {
case Character.UNASSIGNED:
@@ -632,6 +639,7 @@ public final class TerminalEmulator {
int bottom = Math.min(getArg(2, mRows, true) + 1, effectiveBottomMargin - 1) + effectiveTopMargin;
int right = Math.min(getArg(3, mColumns, true) + 1, effectiveRightMargin - 1) + effectiveLeftMargin;
if (mArgIndex >= 4) {
if (mArgIndex >= mArgs.length) mArgIndex = mArgs.length - 1;
for (int i = 4; i <= mArgIndex; i++) {
int bits = 0;
boolean setOrClear = true; // True if setting, false if clearing.
@@ -965,6 +973,7 @@ public final class TerminalEmulator {
break;
case 'h':
case 'l':
if (mArgIndex >= mArgs.length) mArgIndex = mArgs.length - 1;
for (int i = 0; i <= mArgIndex; i++)
doDecSetOrReset(b == 'h', mArgs[i]);
break;
@@ -981,6 +990,7 @@ public final class TerminalEmulator {
break;
case 'r':
case 's':
if (mArgIndex >= mArgs.length) mArgIndex = mArgs.length - 1;
for (int i = 0; i <= mArgIndex; i++) {
int externalBit = mArgs[i];
int internalBit = mapDecSetBitToInternalBit(externalBit);
@@ -1187,12 +1197,20 @@ public final class TerminalEmulator {
}
private void doLinefeed() {
boolean belowScrollingRegion = mCursorRow >= mBottomMargin;
int newCursorRow = mCursorRow + 1;
if (newCursorRow >= mBottomMargin) {
scrollDownOneLine();
newCursorRow = mBottomMargin - 1;
if (belowScrollingRegion) {
// Move down (but not scroll) as long as we are above the last row.
if (mCursorRow != mRows - 1) {
setCursorRow(newCursorRow);
}
} else {
if (newCursorRow == mBottomMargin) {
scrollDownOneLine();
newCursorRow = mBottomMargin - 1;
}
setCursorRow(newCursorRow);
}
setCursorRow(newCursorRow);
}
private void continueSequence(int state) {
@@ -1307,6 +1325,8 @@ public final class TerminalEmulator {
state.mSavedCursorRow = mCursorRow;
state.mSavedCursorCol = mCursorCol;
state.mSavedEffect = mEffect;
state.mSavedForeColor = mForeColor;
state.mSavedBackColor = mBackColor;
state.mSavedDecFlags = mCurrentDecSetFlags;
state.mUseLineDrawingG0 = mUseLineDrawingG0;
state.mUseLineDrawingG1 = mUseLineDrawingG1;
@@ -1318,6 +1338,8 @@ public final class TerminalEmulator {
SavedScreenState state = (mScreen == mMainBuffer) ? mSavedStateMain : mSavedStateAlt;
setCursorRowCol(state.mSavedCursorRow, state.mSavedCursorCol);
mEffect = state.mSavedEffect;
mForeColor = state.mSavedForeColor;
mBackColor = state.mSavedBackColor;
int mask = (DECSET_BIT_AUTOWRAP | DECSET_BIT_ORIGIN_MODE);
mCurrentDecSetFlags = (mCurrentDecSetFlags & ~mask) | (state.mSavedDecFlags & mask);
mUseLineDrawingG0 = state.mUseLineDrawingG0;
@@ -1501,6 +1523,11 @@ public final class TerminalEmulator {
case '`': // Horizontal position absolute (HPA - http://www.vt100.net/docs/vt510-rm/HPA).
setCursorColRespectingOriginMode(getArg0(1) - 1);
break;
case 'b': // Repeat the preceding graphic character Ps times (REP).
if (mLastEmittedCodePoint == -1) break;
final int numRepeat = getArg0(1);
for (int i = 0; i < numRepeat; i++) emitCodePoint(mLastEmittedCodePoint);
break;
case 'c': // Primary Device Attributes (http://www.vt100.net/docs/vt510-rm/DA1) if argument is missing or zero.
// The important part that may still be used by some (tmux stores this value but does not currently use it)
// is the first response parameter identifying the terminal service class, where we send 64 for "vt420".
@@ -1557,7 +1584,7 @@ public final class TerminalEmulator {
break;
case 'r': // "CSI${top};${bottom}r" - set top and bottom Margins (DECSTBM).
{
// http://www.vt100.net/docs/vt510-rm/DECSTBM
// https://vt100.net/docs/vt510-rm/DECSTBM.html
// The top margin defaults to 1, the bottom margin defaults to mRows.
// The escape sequence numbers top 1..23, but we number top 0..22.
// The escape sequence numbers bottom 2..24, and so do we (because we use a zero based numbering
@@ -1566,6 +1593,7 @@ public final class TerminalEmulator {
// Also require that top + 2 <= bottom.
mTopMargin = Math.max(0, Math.min(getArg0(1) - 1, mRows - 2));
mBottomMargin = Math.max(mTopMargin + 2, Math.min(getArg1(mRows), mRows));
// DECSTBM moves the cursor to column 1, line 1 of the page respecting origin mode.
setCursorPosition(0, 0);
}
@@ -1639,6 +1667,7 @@ public final class TerminalEmulator {
/** Select Graphic Rendition (SGR) - see http://en.wikipedia.org/wiki/ANSI_escape_code#graphics. */
private void selectGraphicRendition() {
if (mArgIndex >= mArgs.length) mArgIndex = mArgs.length - 1;
for (int i = 0; i <= mArgIndex; i++) {
int code = mArgs[i];
if (code < 0) {
@@ -2049,6 +2078,7 @@ public final class TerminalEmulator {
buf.append(", escapeState=");
buf.append(mEscapeState);
boolean firstArg = true;
if (mArgIndex >= mArgs.length) mArgIndex = mArgs.length - 1;
for (int i = 0; i <= mArgIndex; i++) {
int value = mArgs[i];
if (value >= 0) {
@@ -2081,6 +2111,7 @@ public final class TerminalEmulator {
* @param codePoint The code point of the character to display
*/
private void emitCodePoint(int codePoint) {
mLastEmittedCodePoint = codePoint;
if (mUseLineDrawingUsesG0 ? mUseLineDrawingG0 : mUseLineDrawingG1) {
// http://www.vt100.net/docs/vt102-ug/table5-15.html.
switch (codePoint) {
@@ -2263,8 +2294,8 @@ public final class TerminalEmulator {
mBottomMargin = mRows;
mRightMargin = mColumns;
mAboutToAutoWrap = false;
mForeColor = TextStyle.COLOR_INDEX_FOREGROUND;
mBackColor = TextStyle.COLOR_INDEX_BACKGROUND;
mForeColor = mSavedStateMain.mSavedForeColor = mSavedStateAlt.mSavedForeColor = TextStyle.COLOR_INDEX_FOREGROUND;
mBackColor = mSavedStateMain.mSavedBackColor = mSavedStateAlt.mSavedBackColor = TextStyle.COLOR_INDEX_BACKGROUND;
setDefaultTabStops();
mUseLineDrawingG0 = mUseLineDrawingG1 = false;
@@ -2307,6 +2338,9 @@ public final class TerminalEmulator {
public void paste(String text) {
// First: Always remove escape key and C1 control characters [0x80,0x9F]:
text = text.replaceAll("(\u001B|[\u0080-\u009F])", "");
// Second: Replace all newlines (\n) or CRLF (\r\n) with carriage returns (\r).
text = text.replaceAll("\r?\n", "\r");
// Then: Implement bracketed paste mode if enabled:
boolean bracketed = isDecsetInternalBitSet(DECSET_BIT_BRACKETED_PASTE_MODE);
if (bracketed) mSession.write("\033[200~");
@@ -2318,7 +2352,7 @@ public final class TerminalEmulator {
static final class SavedScreenState {
/** Saved state of the cursor position, Used to implement the save/restore cursor position escape sequences. */
int mSavedCursorRow, mSavedCursorCol;
int mSavedEffect;
int mSavedEffect, mSavedForeColor, mSavedBackColor;
int mSavedDecFlags;
boolean mUseLineDrawingG0, mUseLineDrawingG1, mUseLineDrawingUsesG0 = true;
}

View File

@@ -21,6 +21,8 @@ public final class TerminalRow {
boolean mLineWrap;
/** The style bits of each cell in the row. See {@link TextStyle}. */
final long[] mStyle;
/** If this row might contain chars with width != 1, used for deactivating fast path */
boolean mHasNonOneWidthOrSurrogateChars;
/** Construct a blank row (containing only whitespace, ' ') with a specified style. */
public TerminalRow(int columns, long style) {
@@ -32,6 +34,7 @@ public final class TerminalRow {
/** NOTE: The sourceX2 is exclusive. */
public void copyInterval(TerminalRow line, int sourceX1, int sourceX2, int destinationX) {
mHasNonOneWidthOrSurrogateChars |= line.mHasNonOneWidthOrSurrogateChars;
final int x1 = line.findStartOfColumn(sourceX1);
final int x2 = line.findStartOfColumn(sourceX2);
boolean startingFromSecondHalfOfWideChar = (sourceX1 > 0 && line.wideDisplayCharacterStartingAt(sourceX1 - 1));
@@ -116,6 +119,7 @@ public final class TerminalRow {
Arrays.fill(mText, ' ');
Arrays.fill(mStyle, style);
mSpaceUsed = (short) mColumns;
mHasNonOneWidthOrSurrogateChars = false;
}
// https://github.com/steven676/Android-Terminal-Emulator/commit/9a47042620bec87617f0b4f5d50568535668fe26
@@ -123,6 +127,17 @@ public final class TerminalRow {
mStyle[columnToSet] = style;
final int newCodePointDisplayWidth = WcWidth.width(codePoint);
// Fast path when we don't have any chars with width != 1
if (!mHasNonOneWidthOrSurrogateChars) {
if (codePoint >= Character.MIN_SUPPLEMENTARY_CODE_POINT || newCodePointDisplayWidth != 1) {
mHasNonOneWidthOrSurrogateChars = true;
} else {
mText[columnToSet] = (char) codePoint;
return;
}
}
final boolean newIsCombining = newCodePointDisplayWidth <= 0;
boolean wasExtraColForWideChar = (columnToSet > 0) && wideDisplayCharacterStartingAt(columnToSet - 1);

View File

@@ -29,4 +29,21 @@ public class ControlSequenceIntroducerTest extends TerminalTestCase {
withTerminalSized(13, 2).enterString("abcdefghijkl\b\b\b\b\b\033[20X").assertLinesAre("abcdefg ", " ");
}
/** CSI Pm m Set SGR parameter(s) from semicolon-separated list Pm. */
public void testCsiSGRParameters() {
// Set more parameters (19) than supported (16). Additional parameters should be silently consumed.
withTerminalSized(3, 2).enterString("\033[0;38;2;255;255;255;48;2;0;0;0;1;2;3;4;5;7;8;9mabc").assertLinesAre("abc", " ");
}
/** CSI Ps b Repeat the preceding graphic character Ps times (REP). */
public void testRepeat() {
withTerminalSized(3, 2).enterString("a\033[b").assertLinesAre("aa ", " ");
withTerminalSized(3, 2).enterString("a\033[2b").assertLinesAre("aaa", " ");
// When no char has been output we ignore REP:
withTerminalSized(3, 2).enterString("\033[b").assertLinesAre(" ", " ");
// This shows that REP outputs the last emitted code point and not the one relative to the
// current cursor position:
withTerminalSized(5, 2).enterString("abcde\033[2G\033[2b\n").assertLinesAre("aeede", " ");
}
}

View File

@@ -227,4 +227,44 @@ public class CursorAndScreenTest extends TerminalTestCase {
withTerminalSized(3, 3).enterString("\b\b\b\bhi").assertLinesAre("hi ", " ", " ");
}
public void testCursorSaveRestoreLocation() {
// DEC save/restore
withTerminalSized(4, 2).enterString("t\0337est\r\nme\0338ry ").assertLinesAre("try ", "me ");
// ANSI.SYS save/restore
withTerminalSized(4, 2).enterString("t\033[sest\r\nme\033[ury ").assertLinesAre("try ", "me ");
// Alternate screen enter/exit
withTerminalSized(4, 2).enterString("t\033[?1049h\033[Hest\r\nme").assertLinesAre("est ", "me ").enterString("\033[?1049lry").assertLinesAre("try ", " ");
}
public void testCursorSaveRestoreTextStyle() {
long s;
// DEC save/restore
withTerminalSized(4, 2).enterString("\033[31;42;4m..\0337\033[36;47;24m\0338..");
s = getStyleAt(0, 3);
Assert.assertEquals(1, TextStyle.decodeForeColor(s));
Assert.assertEquals(2, TextStyle.decodeBackColor(s));
Assert.assertEquals(TextStyle.CHARACTER_ATTRIBUTE_UNDERLINE, TextStyle.decodeEffect(s));
// ANSI.SYS save/restore
withTerminalSized(4, 2).enterString("\033[31;42;4m..\033[s\033[36;47;24m\033[u..");
s = getStyleAt(0, 3);
Assert.assertEquals(1, TextStyle.decodeForeColor(s));
Assert.assertEquals(2, TextStyle.decodeBackColor(s));
Assert.assertEquals(TextStyle.CHARACTER_ATTRIBUTE_UNDERLINE, TextStyle.decodeEffect(s));
// Alternate screen enter/exit
withTerminalSized(4, 2);
enterString("\033[31;42;4m..\033[?1049h\033[H\033[36;47;24m.");
s = getStyleAt(0, 0);
Assert.assertEquals(6, TextStyle.decodeForeColor(s));
Assert.assertEquals(7, TextStyle.decodeBackColor(s));
Assert.assertEquals(0, TextStyle.decodeEffect(s));
enterString("\033[?1049l..");
s = getStyleAt(0, 3);
Assert.assertEquals(1, TextStyle.decodeForeColor(s));
Assert.assertEquals(2, TextStyle.decodeBackColor(s));
Assert.assertEquals(TextStyle.CHARACTER_ATTRIBUTE_UNDERLINE, TextStyle.decodeEffect(s));
}
}

View File

@@ -98,4 +98,13 @@ public class ScrollRegionTest extends TerminalTestCase {
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 ");
}
public void testScrollDownBelowScrollRegion() {
withTerminalSized(2, 5).enterString("1\r\n2\r\n3\r\n4\r\n5").assertLinesAre("1 ", "2 ", "3 ", "4 ", "5 ");
enterString("\033[1;3r"); // DECSTBM margins.
enterString("\033[4;1H"); // Place cursor just below bottom margin.
enterString("QQ\r\nRR\r\n\r\n\r\nYY");
assertLinesAre("1 ", "2 ", "3 ", "QQ", "YY");
}
}

View File

@@ -27,6 +27,7 @@ public class TerminalRowTest extends TestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
row = new TerminalRow(COLUMNS, TextStyle.NORMAL);
}

View File

@@ -55,6 +55,14 @@ public class TerminalTest extends TerminalTestCase {
assertEquals("\033[<0;3;4M", mOutput.getOutputAndClear());
mTerminal.sendMouseEvent(TerminalEmulator.MOUSE_LEFT_BUTTON, 3, 4, false);
assertEquals("\033[<0;3;4m", mOutput.getOutputAndClear());
// When the client says that a click is outside (which could happen when pixels are outside
// the terminal area, see https://github.com/termux/termux-app/issues/501) the terminal
// sends a click at the edge.
mTerminal.sendMouseEvent(TerminalEmulator.MOUSE_LEFT_BUTTON, 0, 0, true);
assertEquals("\033[<0;1;1M", mOutput.getOutputAndClear());
mTerminal.sendMouseEvent(TerminalEmulator.MOUSE_LEFT_BUTTON, 11, 11, false);
assertEquals("\033[<0;10;10m", mOutput.getOutputAndClear());
}
public void testNormalization() throws UnsupportedEncodingException {

View File

@@ -103,6 +103,7 @@ public abstract class TerminalTestCase extends TestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
mOutput = new MockTerminalOutput();
}

View File

@@ -1,6 +1,6 @@
plugins {
id "com.jfrog.bintray" version "1.7"
id "com.github.dcendents.android-maven" version "1.5"
id "com.jfrog.bintray" version "1.7.3"
id "com.github.dcendents.android-maven" version "2.0"
}
apply plugin: 'com.android.library'
@@ -13,21 +13,20 @@ ext {
libraryDescription = 'The terminal view used in Termux'
siteUrl = 'https://github.com/termux/termux'
gitUrl = 'https://github.com/termux/termux.git'
libraryVersion = '0.49'
libraryVersion = '0.50'
}
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
compileSdkVersion 27
dependencies {
compile 'com.android.support:support-annotations:25.3.1'
compile project(":terminal-emulator")
implementation 'com.android.support:support-annotations:27.1.1'
api project(":terminal-emulator")
}
defaultConfig {
minSdkVersion 21
targetSdkVersion 25
targetSdkVersion 27
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
@@ -40,7 +39,7 @@ android {
}
dependencies {
testCompile 'junit:junit:4.12'
testImplementation 'junit:junit:4.12'
}
apply from: '../scripts/bintray-publish.gradle'

View File

@@ -1,3 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.termux.view">
<manifest package="com.termux.view">
</manifest>

View File

@@ -85,6 +85,7 @@ final class GestureAndScaleRecognizer {
return mListener.onScale(detector.getFocusX(), detector.getFocusY(), detector.getScaleFactor());
}
});
mScaleDetector.setQuickScaleEnabled(false);
}
public void onTouchEvent(MotionEvent event) {

View File

@@ -16,7 +16,7 @@ import com.termux.terminal.WcWidth;
* <p/>
* Saves font metrics, so needs to be recreated each time the typeface or font size changes.
*/
final class TerminalRenderer {
public final class TerminalRenderer {
final int mTextSize;
final Typeface mTypeface;

View File

@@ -15,6 +15,7 @@ import android.text.InputType;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.accessibility.AccessibilityManager;
import android.view.ActionMode;
import android.view.HapticFeedbackConstants;
import android.view.InputDevice;
@@ -75,6 +76,8 @@ public final class TerminalView extends View {
/** If non-zero, this is the last unicode code point received if that was a combining character. */
int mCombiningAccent;
private boolean mAccessibilityEnabled;
public TerminalView(Context context, AttributeSet attributes) { // NO_UCD (unused code)
super(context, attributes);
mGestureRecognizer = new GestureAndScaleRecognizer(context, new GestureAndScaleRecognizer.Listener() {
@@ -197,6 +200,8 @@ public final class TerminalView extends View {
}
});
mScroller = new Scroller(context);
AccessibilityManager am = (AccessibilityManager) context.getSystemService(context.ACCESSIBILITY_SERVICE);
mAccessibilityEnabled = am.isEnabled();
}
/**
@@ -384,6 +389,7 @@ public final class TerminalView extends View {
mEmulator.clearScrollCounter();
invalidate();
if (mAccessibilityEnabled) setContentDescription(getText());
}
/**
@@ -642,6 +648,8 @@ public final class TerminalView extends View {
+ leftAltDownFromEvent + ")");
}
if (mTermSession == null) return;
final boolean controlDown = controlDownFromEvent || mClient.readControlKey();
final boolean altDown = leftAltDownFromEvent || mClient.readAltKey();
@@ -834,6 +842,10 @@ public final class TerminalView extends View {
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
if (!mIsSelectingText) {
// Fix issue where the dialog is pressed while being dismissed.
return true;
}
switch (item.getItemId()) {
case 1:
String selectedText = mEmulator.getSelectedText(mSelX1, mSelY1, mSelX2, mSelY2).trim();
@@ -909,4 +921,8 @@ public final class TerminalView extends View {
return mTermSession;
}
private CharSequence getText() {
return mEmulator.getScreen().getSelectedText(0, mTopRow, mEmulator.mColumns, mTopRow +mEmulator.mRows);
}
}