Compare commits

..

65 Commits

Author SHA1 Message Date
Leonid Pliushch
4139bf9424 version 0.107 2021-02-03 23:47:30 +02:00
Leonid Pliushch
94deecb7b1 Merge pull request #1889 from rakslice/numpad_arrows
Implement numpad arrows/home/end/pgup/pgdn/ins/del when num lock is off
2021-02-03 23:43:05 +02:00
Vincent Deville
a4381b7827 ExtraKeysView: SpecialButtons use Button instead of ToggleButton 2021-02-03 23:26:08 +02:00
Vincent Deville
866da75fa9 ExtraKeysView: Use Set instead of List for special buttons keys 2021-02-03 23:26:08 +02:00
Vincent Deville
2b6e9ade07 ExtraKeysView: Handle multiple and popup SpecialButtons 2021-02-03 23:26:08 +02:00
Leonid Pliushch
6d1b0efd3b update bootstrap archives 2021-02-03 23:15:26 +02:00
Leonid Pliushch
e03858f065 export app version in environment variable 2021-02-03 22:39:13 +02:00
Leonid Pliushch
01929397cf TermuxPreferences: get rid of unnecessary switch()'es 2021-02-03 20:27:13 +02:00
Leonid Pliushch
496da3f877 introduce a property for configuring default working directory
Issue https://github.com/termux/termux-app/issues/1192.
2021-02-03 20:22:38 +02:00
Leonid Pliushch
407e4e003a github actions: update actions/upload-artifact to v2 2021-02-03 19:05:19 +02:00
Leonid Pliushch
fec61d315f github actions: debug_build.yml is fixed now? 2021-02-03 18:59:22 +02:00
Leonid Pliushch
f3a3a89f93 github actions: use hardcoded list of debug apks 2021-02-03 18:15:03 +02:00
Leonid Pliushch
5b084f0851 github actions: include split apks to artifact archive 2021-02-03 17:53:08 +02:00
Emil Velikov
a7eb173178 app: enable split apk builds
Currently we build a single APK which handles the four supported ABIs.

Therefore each user, downloads 50-75% more than they need - adding
towards both client/server-side network as well as device storage.

Use a split APK approach - it costs nothing from build and server-side
storage POV.

Note: We're removing ndk:abiFilters - they're incompatible/superseded by
the splits:abi:include list.

Signed-off-by: Emil Velikov <emil.l.velikov@gmail.com>
2021-02-03 17:29:13 +02:00
Leonid Pliushch
fa5117a098 terminal-view: bump library version
Needs to be updated on Bintray: https://bintray.com/termux/maven/terminal-view
2021-02-03 17:02:29 +02:00
Leonid Pliushch
2ef45c85b2 terminal-emulator: bump library version
Needs to be updated on Bintray: https://bintray.com/termux/maven/terminal-emulator
2021-02-03 16:59:34 +02:00
Leonid Pliushch
58b7a26b33 add basic metadata for F-Droid.
Fixes https://github.com/termux/termux-app/issues/622.
2021-02-03 16:43:17 +02:00
Leonid Pliushch
6e2a2ed946 documents provider: fix application icon reference 2021-02-03 15:56:38 +02:00
Leonid Pliushch
7be1fe5555 icon: add regular png icon for compatibility with some launchers
See https://github.com/termux/termux-app/issues/991.

Adaptive icon will continue to be automatically used if supported.
2021-02-03 15:49:03 +02:00
Leonid Pliushch
0e94d52094 readme: remove links to Google Play and Nethunter stores
Both offer outdated builds.
2021-02-03 02:56:53 +02:00
David Kramer
096dedffb1 Remove height offset workarounds / config
* Use WindowInsetsListener to get nav bar height and use that
      instead for calculating adjusted height for extra keys view
2021-02-02 01:31:50 +02:00
David Kramer
b5d491a54c Implement full screen and add FullScreenWorkAround to fix ExtraKeysView
not displaying properly and inconsistently between various devices
2021-02-02 01:31:50 +02:00
rakslice
f6822d6c24 Merge remote-tracking branch 'origin/master' into numpad_arrows
# Conflicts:
#	terminal-view/src/main/java/com/termux/view/TerminalView.java
2021-01-17 14:57:47 -08:00
Leonid Pliushch
32c3ffd57b readme: still need to clarify that app is "not maintained"
Please note that I do not review changes made by project collaborators.
Before submitting changes to branch 'master' ensure they are working
properly. I may not find the issue and fix it in time, like in case
with v0.105 release.
2021-01-17 20:03:05 +02:00
Leonid Pliushch
92570bee06 version 0.106 2021-01-17 19:45:19 +02:00
Leonid Pliushch
05bb399893 terminal-view: fix array indexing 2021-01-17 19:40:29 +02:00
rakslice
fe584940e1 fix TerminalView possiblePropLocations IOBE 2021-01-16 19:54:11 -08:00
rakslice
78cdaef6d2 only send PF1 for num lock when in application mode 2021-01-16 19:53:22 -08:00
rakslice
7b4a69f839 when num lock is off, have numpad keys works as their numlock-off functions (arrows/home/end/pgup/pgdn/insert/delete) 2021-01-16 19:48:55 -08:00
Henrik Grimler
831aa69da8 Merge pull request #726 from dkramer95/OpenBroadcast
Sends broadcast on app open to notify addon termux receivers
2021-01-06 13:48:15 +01:00
Leonid Pliushch
a56cba6843 version 0.105 2021-01-06 14:31:56 +02:00
Leonid Pliushch
9228982632 remove restrictions from viewable file types
Fixes https://github.com/termux/termux-app/issues/1872 and
similar issues.
2021-01-06 14:28:28 +02:00
Leonid Pliushch
38114784f1 update bootstrap archives 2021-01-06 14:23:15 +02:00
Henrik Grimler
b805f1486c Merge pull request #1869 from termux/ctrlspace
Add workaround property to fix ctrl+space on devices where this does not work
2021-01-04 22:41:56 +01:00
Henrik Grimler
7d31b7f480 terminal-emulator: tests: change spaces to tabs for consistency 2021-01-02 09:12:14 +01:00
Henrik Grimler
a0298285e3 terminal-emulator: tests: avoid error about methods not being mocked
unitTests.returnDefaultValues = true fixes it.
More info: http://g.co/androidstudio/not-mocked
2021-01-02 09:12:14 +01:00
Lucy Phipps
538a1d5cdf F-Droid URL for Termux:Styling 2021-01-01 23:48:34 +02:00
Henrik Grimler
f1e973f0d2 terminal-view: add "ctrl-space-workaround" property
Makes it possible to run ctrl+space with hardware keyboards on
devices/ROMs where it otherwise is broken. On devices where it already
works this workaround breaks ctrl+space though.

Where to add this fix was investigated and found by @5bodnar.
2021-01-01 19:20:16 +01:00
Henrik Grimler
b467b68f7b terminal-view: mv code to get properties to its own function 2021-01-01 19:20:11 +01:00
Henrik Grimler
b895cbbb1e app: set importantForAutofill="no" for extra keys input field
Fixes warning "extra_keys_right.xml:2: Missing autofillHints attribute".
2021-01-01 18:13:37 +01:00
Henrik Grimler
fc30eba247 terminal-view: silence warning from use of SHOW_AS_ACTION_ALWAYS
```
Prefer "SHOW_AS_ACTION_IF_ROOM" instead of "SHOW_AS_ACTION_ALWAYS"
```
2021-01-01 18:13:36 +01:00
Henrik Grimler
b1d4c0c7fe app: disable test for "ProtectedPermissions"
Needed for tests to pass after READ_LOGS and WRITE_SECURE_SETTINGS
were added to AndroidManifest.xml.
2021-01-01 18:13:36 +01:00
Henrik Grimler
7fe5bd32c8 Update ndk to r22 2021-01-01 18:12:53 +01:00
Henrik Grimler
43bbef9a11 Update plugins and gradle to latest versions 2021-01-01 18:12:53 +01:00
Henrik Grimler
eaea0f74a5 Fix github links for terminal-{emulator,view} jcenter packages 2021-01-01 18:12:53 +01:00
Leonid Pliushch
cb13a5a531 version 0.104 2020-12-29 13:45:35 +02:00
Kevin LeBlanc
d267843e36 Don't exclude hidden files from document provider (#1220)
Credit to @johnmellor for requesting the document provider in the
first place via #79, mentioning this limitation in a comment on
that issue, and creating a commit like this one to address it.
2020-12-29 13:42:36 +02:00
Leonid Pliushch
5ca67dd885 update bootstrap archives 2020-12-29 13:40:25 +02:00
Leonid Pliushch
6b0d531758 Revert "update readme" 2020-12-17 20:27:46 +02:00
Leonid Pliushch
1b3283bd69 update bootstrap archives 2020-12-11 20:07:40 +02:00
Leonid Pliushch
2820f6a7b8 update readme 2020-12-11 17:36:14 +02:00
Leonid Pliushch
8925ae67bb update readme 2020-12-11 17:35:28 +02:00
Lucy Phipps
4066c5df42 don't add $PREFIX/bin/applets to $PATH
busybox doesn't use that folder anymore, and is deprecated anyway
2020-11-24 23:16:17 +02:00
Leonid Pliushch
20aac6aa72 copy FUNDING.yml from termux-packages repo 2020-11-18 03:04:38 +02:00
Leonid Pliushch
e1f799f9a1 version 0.103 2020-11-17 20:40:40 +02:00
Leonid Pliushch
4479de4b9b update bootstrap archives 2020-11-17 20:35:55 +02:00
Leonid Pliushch
3a8f53a54c add permission WRITE_SECURE_SETTINGS
https://github.com/termux/termux-api/issues/211
2020-11-17 20:07:33 +02:00
Leonid Pliushch
2f04a6186b add READ_LOGS permission
* https://github.com/termux/termux-app/issues/873
* https://github.com/termux/termux-app/issues/1821
2020-11-17 20:03:10 +02:00
Leonid Pliushch
ad64dd7c3d update bootstrap archives 2020-10-12 20:39:28 +03:00
Fredrik Fornwall
dfc4595ec5 version 0.102 2020-10-02 10:30:18 +02:00
Fabian Henneke
8c80efb904 Add an "Autofill password" context menu action 2020-10-02 10:21:34 +02:00
Leonid Pliushch
564079c7e9 update .gitattributes 2020-10-01 01:08:39 +03:00
Leonid Pliushch
5b6fd9b88c version 0.101 2020-09-28 03:30:21 +03:00
Leonid Pliushch
63bfe95848 update bootstrap archives again
Fixes faulty dpkg https://github.com/termux/termux-packages/issues/5858.
2020-09-28 03:28:49 +03:00
David Kramer
3bb2849a88 Sends broadcast on app open to notify addon termux receivers 2018-06-18 13:06:34 -06:00
54 changed files with 615 additions and 303 deletions

8
.gitattributes vendored
View File

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

2
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,2 @@
patreon: termux
custom: https://paypal.me/fornwall

View File

@@ -20,7 +20,7 @@ jobs:
run: |
./gradlew assembleDebug
- name: Store generated APK file
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v2
with:
name: termux-app
path: ./app/build/outputs/apk/debug/app-debug.apk
path: ./app/build/outputs/apk/debug

View File

@@ -14,13 +14,18 @@ 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)
***
**@termux is looking for Termux Application maintainer for implementing new features,
fixing bugs and reviewing pull requests since current one (@fornwall) is inactive.**
Issue https://github.com/termux/termux-app/issues/1072 needs extra attention.
***
## Installation
Termux application can be obtained from:
- [Google Play](https://play.google.com/store/apps/details?id=com.termux)
- [F-Droid](https://f-droid.org/en/packages/com.termux/)
- [Kali Nethunter Store](https://store.nethunter.com/en/packages/com.termux/)
Termux application can be obtained from [F-Droid](https://f-droid.org/en/packages/com.termux/).
Additionally we provide per-commit debug builds for those who want to try
out the latest features or test their pull request. This build can be obtained

View File

@@ -9,7 +9,7 @@ android {
dependencies {
implementation "androidx.annotation:annotation:1.1.0"
implementation "androidx.viewpager:viewpager:1.0.0"
implementation "androidx.drawerlayout:drawerlayout:1.1.0"
implementation "androidx.drawerlayout:drawerlayout:1.1.1"
implementation project(":terminal-view")
}
@@ -17,17 +17,22 @@ android {
applicationId "com.termux"
minSdkVersion project.properties.minSdkVersion.toInteger()
targetSdkVersion project.properties.targetSdkVersion.toInteger()
versionCode 100
versionName "0.100"
versionCode 107
versionName "0.107"
externalNativeBuild {
ndkBuild {
cFlags "-std=c11", "-Wall", "-Wextra", "-Werror", "-Os", "-fno-stack-protector", "-Wl,--gc-sections"
}
}
}
ndk {
abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
splits {
abi {
enable true
reset ()
include 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
}
}
@@ -64,6 +69,10 @@ android {
}
}
lintOptions {
disable 'ProtectedPermissions'
}
testOptions {
unitTests {
includeAndroidResources = true
@@ -72,8 +81,8 @@ android {
}
dependencies {
testImplementation 'junit:junit:4.13'
testImplementation 'org.robolectric:robolectric:4.3.1'
testImplementation 'junit:junit:4.13.1'
testImplementation 'org.robolectric:robolectric:4.4'
}
task versionName {
@@ -133,11 +142,11 @@ clean {
task downloadBootstraps(){
doLast {
def version = 29
downloadBootstrap("aarch64", "1b67ba2302efc20952c00d607a3a169e86cf8ecb66b0c6e16bee9997289119e1", version)
downloadBootstrap("arm", "dd87a5902b6b7ee4d4499a10da99a3ea605ba020ad1cc77cad16b3267ac13c2", version)
downloadBootstrap("i686", "66cf79273a2ee87730010febe258bec02abf4c4d4c6f89b1b174619baa3c5a0e", version)
downloadBootstrap("x86_64", "e9541c02e4e7df4860d76098e4f477e09952ae65323216f692d668425842bf00", version)
def version = 36
downloadBootstrap("aarch64", "9b1ee77b3e66bbb5e0ec9d766894aa9c1441f68b2bd4152e3ea54101de3c76d5", version)
downloadBootstrap("arm", "eac7c2b322a1217f28a2b540afe4f994147abee571e9fed330d5430b08d6707b", version)
downloadBootstrap("i686", "990dd1aaf5b6499bee5ed473dd5a0e18ecbc0493646b299238ac0dd0d9963396", version)
downloadBootstrap("x86_64", "a321eba39c4bb0fb2aa35f17ccff57bf8fcf0e973b0a0569f6d24bd02314e4ed", version)
}
}

View File

@@ -11,7 +11,7 @@
<permission android:name="com.termux.permission.RUN_COMMAND"
android:label="@string/run_command_permission_label"
android:description="@string/run_command_permission_description"
android:icon="@drawable/ic_launcher"
android:icon="@mipmap/ic_launcher"
android:protectionLevel="dangerous" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
@@ -22,11 +22,14 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.READ_LOGS" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<application
android:extractNativeLibs="true"
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:banner="@drawable/banner"
android:label="@string/application_name"
android:theme="@style/Theme.Termux"
@@ -81,16 +84,15 @@
<data android:mimeType="text/*" />
<data android:mimeType="video/*" />
</intent-filter>
<!-- Be more restrictive for viewing files, restricting ourselves to text files. -->
<!-- Accept multiple file types to let Termux be usable as generic file viewer. -->
<intent-filter tools:ignore="AppLinkUrlError">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/*" />
<data android:mimeType="audio/*" />
<data android:mimeType="image/*" />
<data android:mimeType="text/*" />
<data android:mimeType="application/*log*" />
<data android:mimeType="application/json" />
<data android:mimeType="application/*xml*" />
<data android:mimeType="application/*latex*" />
<data android:mimeType="application/javascript" />
<data android:mimeType="video/*" />
</intent-filter>
</activity>

View File

@@ -6,6 +6,8 @@ import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import com.termux.BuildConfig;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
@@ -138,6 +140,7 @@ public final class BackgroundJob {
List<String> environment = new ArrayList<>();
environment.add("TERMUX_VERSION=" + BuildConfig.VERSION_NAME);
environment.add("TERM=xterm-256color");
environment.add("COLORTERM=truecolor");
environment.add("HOME=" + TermuxService.HOME_PATH);
@@ -161,7 +164,7 @@ public final class BackgroundJob {
environment.add("PATH= " + System.getenv("PATH"));
} else {
environment.add("LANG=en_US.UTF-8");
environment.add("PATH=" + TermuxService.PREFIX_PATH + "/bin:" + TermuxService.PREFIX_PATH + "/bin/applets");
environment.add("PATH=" + TermuxService.PREFIX_PATH + "/bin");
environment.add("PWD=" + cwd);
environment.add("TMPDIR=" + TermuxService.PREFIX_PATH + "/tmp");
}

View File

@@ -6,6 +6,9 @@ import android.os.Build;
import android.provider.Settings;
import android.util.AttributeSet;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ScheduledExecutorService;
@@ -13,6 +16,7 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.Map;
import java.util.HashMap;
import java.util.Arrays;
import java.util.stream.Collectors;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
@@ -23,7 +27,6 @@ import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.GridLayout;
import android.widget.PopupWindow;
import android.widget.ToggleButton;
import com.termux.R;
import com.termux.view.TerminalView;
@@ -129,15 +132,27 @@ public final class ExtraKeysView extends GridLayout {
private static class SpecialButtonState {
boolean isOn = false;
ToggleButton button = null;
boolean isActive = false;
List<Button> buttons = new ArrayList<>();
void setIsActive(boolean value) {
isActive = value;
buttons.forEach(button -> button.setTextColor(value ? INTERESTING_COLOR : TEXT_COLOR));
}
}
private Map<SpecialButton, SpecialButtonState> specialButtons = new HashMap<SpecialButton, SpecialButtonState>() {{
private final Map<SpecialButton, SpecialButtonState> specialButtons = new HashMap<SpecialButton, SpecialButtonState>() {{
put(SpecialButton.CTRL, new SpecialButtonState());
put(SpecialButton.ALT, new SpecialButtonState());
put(SpecialButton.FN, new SpecialButtonState());
}};
private final Set<String> specialButtonsKeys = specialButtons.keySet().stream().map(Enum::name).collect(Collectors.toSet());
private boolean isSpecialButton(ExtraKeyButton button) {
return specialButtonsKeys.contains(button.getKey());
}
private ScheduledExecutorService scheduledExecutor;
private PopupWindow popupWindow;
private int longPressCount;
@@ -147,30 +162,36 @@ public final class ExtraKeysView extends GridLayout {
if (state == null)
throw new RuntimeException("Must be a valid special button (see source)");
if (! state.isOn)
if (!state.isOn || !state.isActive)
return false;
if (state.button == null) {
return false;
}
state.setIsActive(false);
if (state.button.isPressed())
return true;
if (! state.button.isChecked())
return false;
state.button.setChecked(false);
state.button.setTextColor(TEXT_COLOR);
return true;
}
void popup(View view, String text) {
private Button createSpecialButton(String buttonKey, boolean needUpdate) {
SpecialButtonState state = specialButtons.get(SpecialButton.valueOf(buttonKey));
state.isOn = true;
Button button = new Button(getContext(), null, android.R.attr.buttonBarButtonStyle);
button.setTextColor(state.isActive ? INTERESTING_COLOR : TEXT_COLOR);
if (needUpdate) {
state.buttons.add(button);
}
return button;
}
void popup(View view, ExtraKeyButton extraButton) {
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 button;
if(isSpecialButton(extraButton)) {
button = createSpecialButton(extraButton.getKey(), false);
} else {
button = new Button(getContext(), null, android.R.attr.buttonBarButtonStyle);
button.setTextColor(TEXT_COLOR);
}
button.setText(extraButton.getDisplay());
button.setPadding(0, 0, 0, 0);
button.setMinHeight(0);
button.setMinWidth(0);
@@ -219,7 +240,7 @@ public final class ExtraKeysView extends GridLayout {
return;
for(SpecialButtonState state : specialButtons.values())
state.button = null;
state.buttons = new ArrayList<>();
removeAllViews();
@@ -233,11 +254,8 @@ public final class ExtraKeysView extends GridLayout {
final ExtraKeyButton buttonInfo = buttons[row][col];
Button button;
if(Arrays.asList("CTRL", "ALT", "FN").contains(buttonInfo.getKey())) {
SpecialButtonState state = specialButtons.get(SpecialButton.valueOf(buttonInfo.getKey())); // for valueOf: https://stackoverflow.com/a/604426/1980630
state.isOn = true;
button = state.button = new ToggleButton(getContext(), null, android.R.attr.buttonBarButtonStyle);
button.setClickable(true);
if(isSpecialButton(buttonInfo)) {
button = createSpecialButton(buttonInfo.getKey(), true);
} else {
button = new Button(getContext(), null, android.R.attr.buttonBarButtonStyle);
}
@@ -262,10 +280,9 @@ public final class ExtraKeysView extends GridLayout {
}
View root = getRootView();
if (Arrays.asList("CTRL", "ALT", "FN").contains(buttonInfo.getKey())) {
ToggleButton self = (ToggleButton) finalButton;
self.setChecked(self.isChecked());
self.setTextColor(self.isChecked() ? INTERESTING_COLOR : TEXT_COLOR);
if (isSpecialButton(buttonInfo)) {
SpecialButtonState state = specialButtons.get(SpecialButton.valueOf(buttonInfo.getKey()));
state.setIsActive(!state.isActive);
} else {
sendKey(root, buttonInfo);
}
@@ -295,8 +312,7 @@ public final class ExtraKeysView extends GridLayout {
scheduledExecutor = null;
}
v.setBackgroundColor(BUTTON_COLOR);
String extraButtonDisplayedText = buttonInfo.getPopup().getDisplay();
popup(v, extraButtonDisplayedText);
popup(v, buttonInfo.getPopup());
}
if (popupWindow != null && event.getY() > 0) {
v.setBackgroundColor(BUTTON_PRESSED_COLOR);
@@ -325,7 +341,12 @@ public final class ExtraKeysView extends GridLayout {
popupWindow.dismiss();
popupWindow = null;
if (buttonInfo.getPopup() != null) {
sendKey(root, buttonInfo.getPopup());
if (isSpecialButton(buttonInfo.getPopup())) {
SpecialButtonState state = specialButtons.get(SpecialButton.valueOf(buttonInfo.getPopup().getKey()));
state.setIsActive(!state.isActive);
} else {
sendKey(root, buttonInfo.getPopup());
}
}
} else {
v.performClick();

View File

@@ -0,0 +1,66 @@
package com.termux.app;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewGroup;
/**
* Work around for fullscreen mode in Termux to fix ExtraKeysView not being visible.
* This class is derived from:
* https://stackoverflow.com/questions/7417123/android-how-to-adjust-layout-in-full-screen-mode-when-softkeyboard-is-visible
* and has some additional tweaks
* ---
* For more information, see https://issuetracker.google.com/issues/36911528
*/
public class FullScreenWorkAround {
private View mChildOfContent;
private int mUsableHeightPrevious;
private ViewGroup.LayoutParams mViewGroupLayoutParams;
private int mNavBarHeight;
public static void apply(TermuxActivity activity) {
new FullScreenWorkAround(activity);
}
private FullScreenWorkAround(TermuxActivity activity) {
ViewGroup content = activity.findViewById(android.R.id.content);
mChildOfContent = content.getChildAt(0);
mViewGroupLayoutParams = mChildOfContent.getLayoutParams();
mNavBarHeight = activity.getNavBarHeight();
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(this::possiblyResizeChildOfContent);
}
private void possiblyResizeChildOfContent() {
int usableHeightNow = computeUsableHeight();
if (usableHeightNow != mUsableHeightPrevious) {
int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
int heightDifference = usableHeightSansKeyboard - usableHeightNow;
if (heightDifference > (usableHeightSansKeyboard / 4)) {
// keyboard probably just became visible
// ensures that usable layout space does not extend behind the
// soft keyboard, causing the extra keys to not be visible
mViewGroupLayoutParams.height = (usableHeightSansKeyboard - heightDifference) + getNavBarHeight();
} else {
// keyboard probably just became hidden
mViewGroupLayoutParams.height = usableHeightSansKeyboard;
}
mChildOfContent.requestLayout();
mUsableHeightPrevious = usableHeightNow;
}
}
private int getNavBarHeight() {
return mNavBarHeight;
}
private int computeUsableHeight() {
Rect r = new Rect();
mChildOfContent.getWindowVisibleDisplayFrame(r);
return (r.bottom - r.top);
}
}

View File

@@ -15,6 +15,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
@@ -38,6 +39,7 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.autofill.AutofillManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter;
import android.widget.EditText;
@@ -92,6 +94,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
private static final int CONTEXTMENU_STYLING_ID = 6;
private static final int CONTEXTMENU_HELP_ID = 8;
private static final int CONTEXTMENU_TOGGLE_KEEP_SCREEN_ON = 9;
private static final int CONTEXTMENU_AUTOFILL_ID = 10;
private static final int MAX_SESSIONS = 8;
@@ -99,6 +102,8 @@ public final class TermuxActivity extends Activity implements ServiceConnection
private static final String RELOAD_STYLE_ACTION = "com.termux.app.reload_style";
private static final String BROADCAST_TERMUX_OPENED = "com.termux.app.OPENED";
/** The main view of the activity showing the terminal. Initialized in onCreate(). */
@SuppressWarnings("NullableProblems")
@NonNull
@@ -129,6 +134,8 @@ public final class TermuxActivity extends Activity implements ServiceConnection
boolean mIsUsingBlackUI;
int mNavBarHeight;
final SoundPool mBellSoundPool = new SoundPool.Builder().setMaxStreams(1).setAudioAttributes(
new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION).build()).build();
@@ -209,8 +216,19 @@ public final class TermuxActivity extends Activity implements ServiceConnection
super.onCreate(bundle);
setContentView(R.layout.drawer_layout);
View content = findViewById(android.R.id.content);
content.setOnApplyWindowInsetsListener((v, insets) -> {
mNavBarHeight = insets.getSystemWindowInsetBottom();
return insets;
});
if (mSettings.isUsingFullScreen()) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
if (mIsUsingBlackUI) {
findViewById(R.id.left_drawer).setBackgroundColor(
getResources().getColor(android.R.color.background_dark)
@@ -251,6 +269,12 @@ public final class TermuxActivity extends Activity implements ServiceConnection
if (position == 0) {
layout = mExtraKeysView = (ExtraKeysView) inflater.inflate(R.layout.extra_keys_main, collection, false);
mExtraKeysView.reload(mSettings.mExtraKeys);
// apply extra keys fix if enabled in prefs
if (mSettings.isUsingFullScreen() && mSettings.isUsingFullScreenWorkAround()) {
FullScreenWorkAround.apply(TermuxActivity.this);
}
} else {
layout = inflater.inflate(R.layout.extra_keys_right, collection, false);
final EditText editText = layout.findViewById(R.id.text_input);
@@ -322,6 +346,30 @@ public final class TermuxActivity extends Activity implements ServiceConnection
checkForFontAndColors();
mBellSoundId = mBellSoundPool.load(this, R.raw.bell, 1);
sendOpenedBroadcast();
}
public int getNavBarHeight() {
return mNavBarHeight;
}
/**
* Send a broadcast notifying Termux app has been opened
*/
void sendOpenedBroadcast() {
Intent broadcast = new Intent(BROADCAST_TERMUX_OPENED);
List<ResolveInfo> matches = getPackageManager().queryBroadcastReceivers(broadcast, 0);
// send broadcast to registered Termux receivers
// this technique is needed to work around broadcast changes that Oreo introduced
for (ResolveInfo info : matches) {
Intent explicitBroadcast = new Intent(broadcast);
ComponentName cname = new ComponentName(info.activityInfo.applicationInfo.packageName,
info.activityInfo.name);
explicitBroadcast.setComponent(cname);
sendBroadcast(explicitBroadcast);
}
}
void toggleShowExtraKeys() {
@@ -604,7 +652,14 @@ public final class TermuxActivity extends Activity implements ServiceConnection
.setPositiveButton(android.R.string.ok, null).show();
} else {
TerminalSession currentSession = getCurrentTermSession();
String workingDirectory = (currentSession == null) ? null : currentSession.getCwd();
String workingDirectory;
if (currentSession == null) {
workingDirectory = mSettings.mDefaultWorkingDir;
} else {
workingDirectory = currentSession.getCwd();
}
TerminalSession newSession = mTermService.createTermSession(null, null, workingDirectory, failSafe);
if (sessionName != null) {
newSession.mSessionName = sessionName;
@@ -655,6 +710,12 @@ public final class TermuxActivity extends Activity implements ServiceConnection
menu.add(Menu.NONE, CONTEXTMENU_SELECT_URL_ID, Menu.NONE, R.string.select_url);
menu.add(Menu.NONE, CONTEXTMENU_SHARE_TRANSCRIPT_ID, Menu.NONE, R.string.select_all_and_share);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
AutofillManager autofillManager = getSystemService(AutofillManager.class);
if (autofillManager != null && autofillManager.isEnabled()) {
menu.add(Menu.NONE, CONTEXTMENU_AUTOFILL_ID, Menu.NONE, R.string.autofill_password);
}
}
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_STYLING_ID, Menu.NONE, R.string.style_terminal);
@@ -853,7 +914,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
// The startActivity() call is not documented to throw IllegalArgumentException.
// However, crash reporting shows that it sometimes does, so catch it here.
new AlertDialog.Builder(this).setMessage(R.string.styling_not_installed)
.setPositiveButton(R.string.styling_install, (dialog, which) -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://play.google.com/store/apps/details?id=com.termux.styling")))).setNegativeButton(android.R.string.cancel, null).show();
.setPositiveButton(R.string.styling_install, (dialog, which) -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://f-droid.org/en/packages/com.termux.styling/")))).setNegativeButton(android.R.string.cancel, null).show();
}
return true;
}
@@ -870,6 +931,14 @@ public final class TermuxActivity extends Activity implements ServiceConnection
}
return true;
}
case CONTEXTMENU_AUTOFILL_ID: {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
AutofillManager autofillManager = getSystemService(AutofillManager.class);
if (autofillManager != null && autofillManager.isEnabled()) {
autofillManager.requestAutofill(mTerminalView);
}
}
}
default:
return super.onContextItemSelected(item);
}

View File

@@ -28,6 +28,8 @@ import java.util.Properties;
import androidx.annotation.IntDef;
import static com.termux.terminal.EmulatorDebug.LOG_TAG;
final class TermuxPreferences {
@IntDef({BELL_VIBRATE, BELL_BEEP, BELL_IGNORE})
@@ -67,12 +69,16 @@ final class TermuxPreferences {
private boolean mScreenAlwaysOn;
private int mFontSize;
private boolean mUseFullScreen;
private boolean mUseFullScreenWorkAround;
@AsciiBellBehaviour
int mBellBehaviour = BELL_VIBRATE;
boolean mBackIsEscape;
boolean mDisableVolumeVirtualKeys;
boolean mShowExtraKeys;
String mDefaultWorkingDir;
ExtraKeysInfos mExtraKeys;
@@ -108,7 +114,7 @@ final class TermuxPreferences {
} catch (NumberFormatException | ClassCastException e) {
mFontSize = defaultFontSize;
}
mFontSize = clamp(mFontSize, MIN_FONTSIZE, MAX_FONTSIZE);
mFontSize = clamp(mFontSize, MIN_FONTSIZE, MAX_FONTSIZE);
}
boolean toggleShowExtraKeys(Context context) {
@@ -137,6 +143,14 @@ final class TermuxPreferences {
return mUseDarkUI;
}
boolean isUsingFullScreen() {
return mUseFullScreen;
}
boolean isUsingFullScreenWorkAround() {
return mUseFullScreenWorkAround;
}
void setScreenAlwaysOn(Context context, boolean newValue) {
mScreenAlwaysOn = newValue;
PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(SCREEN_ALWAYS_ON_KEY, newValue).apply();
@@ -196,6 +210,17 @@ final class TermuxPreferences {
mUseDarkUI = nightMode == Configuration.UI_MODE_NIGHT_YES;
}
mUseFullScreen = "true".equals(props.getProperty("fullscreen", "false").toLowerCase());
mUseFullScreenWorkAround = "true".equals(props.getProperty("use-fullscreen-workaround", "false").toLowerCase());
mDefaultWorkingDir = props.getProperty("default-working-directory", TermuxService.HOME_PATH);
File workDir = new File(mDefaultWorkingDir);
if (!workDir.exists() || !workDir.isDirectory()) {
// Fallback to home directory if user configured working directory is not exist
// or is a regular file.
mDefaultWorkingDir = TermuxService.HOME_PATH;
}
String defaultExtraKeys = "[[ESC, TAB, CTRL, ALT, {key: '-', popup: '|'}, DOWN, UP]]";
try {

View File

@@ -75,7 +75,7 @@ public class TermuxDocumentsProvider extends DocumentsProvider {
row.add(Root.COLUMN_TITLE, applicationName);
row.add(Root.COLUMN_MIME_TYPES, ALL_MIME_TYPES);
row.add(Root.COLUMN_AVAILABLE_BYTES, BASE_DIR.getFreeSpace());
row.add(Root.COLUMN_ICON, R.drawable.ic_launcher);
row.add(Root.COLUMN_ICON, R.mipmap.ic_launcher);
return result;
}
@@ -91,9 +91,7 @@ public class TermuxDocumentsProvider extends DocumentsProvider {
final MatrixCursor result = new MatrixCursor(projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION);
final File parent = getFileForDocId(parentDocumentId);
for (File file : parent.listFiles()) {
if (!file.getName().startsWith(".")) {
includeFile(result, null, file);
}
includeFile(result, null, file);
}
return result;
}
@@ -177,8 +175,7 @@ public class TermuxDocumentsProvider extends DocumentsProvider {
} catch (IOException e) {
isInsideHome = true;
}
final boolean isHidden = file.getName().startsWith(".");
if (isInsideHome && !isHidden) {
if (isInsideHome) {
if (file.isDirectory()) {
Collections.addAll(pending, file.listFiles());
} else {
@@ -265,7 +262,7 @@ public class TermuxDocumentsProvider extends DocumentsProvider {
row.add(Document.COLUMN_MIME_TYPE, mimeType);
row.add(Document.COLUMN_LAST_MODIFIED, file.lastModified());
row.add(Document.COLUMN_FLAGS, flags);
row.add(Document.COLUMN_ICON, R.drawable.ic_launcher);
row.add(Document.COLUMN_ICON, R.mipmap.ic_launcher);
}
}

View File

@@ -1,34 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="108dp"
android:width="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#000000"
android:pathData="M18,54
A36,36 0 1,1 90,54
A36,36 0 1,1 18,54 Z" />
<!-- Keep in sync with adaptive ic_foreground.xml: -->
<path
android:fillColor="#FFFFFF"
android:pathData="M34,38
h6
l12,16
l-12,16
h-6
l12,-16
"
/>
<path
android:fillColor="#FFFFFF"
android:pathData="M56,66
h18
v4
h-18
"
/>
</vector>

View File

@@ -19,7 +19,9 @@
android:layout_marginLeft="3dp"
android:focusableInTouchMode="true"
android:scrollbarThumbVertical="@drawable/terminal_scroll_shape"
android:scrollbars="vertical" />
android:scrollbars="vertical"
android:importantForAutofill="no"
android:autofillHints="password" />
<LinearLayout
android:id="@+id/left_drawer"

View File

@@ -7,6 +7,7 @@
android:imeOptions="actionSend|flagNoFullscreen"
android:maxLines="1"
android:inputType="text"
android:importantForAutofill="no"
android:textColor="@android:color/white"
android:textColorHighlight="@android:color/darker_gray"
android:paddingTop="0dp"

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@android:color/black"/>
<foreground android:drawable="@drawable/ic_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@@ -12,6 +12,7 @@
<string name="share_transcript_title">Terminal transcript</string>
<string name="help">Help</string>
<string name="toggle_keep_screen_on">Keep screen on</string>
<string name="autofill_password">Autofill password</string>
<string name="bootstrap_installer_body">Installing…</string>
<string name="bootstrap_error_title">Unable to install</string>

BIN
art/ic_launcher2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

BIN
art/ic_launcher2_round.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -4,7 +4,7 @@ buildscript {
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.1'
classpath 'com.android.tools.build:gradle:4.1.1'
}
}

View File

@@ -0,0 +1,13 @@
Termux is a terminal emulator application enhanced with a large set of command line utilities ported to Android OS. The main goal is to bring a Linux command line experience to users of mobile devices with no rooting or other special setup required.
* Enjoy the Bash and Zsh shells.
* Edit files with nano and vim.
* Access servers over SSH.
* Compile C/C++ code with clang.
* Use the Python console as a pocket calculator.
* Check out projects with Git and Subversion.
* Run text-based games with frotz.
At first start a small base system is being configured. The GNU Bash, Coreutils, Findutils and other core utilities are available out-of-box. Additionally, we provide more than 1000 other packages installable by using the 'pkg' utility which currently is a frontend for the 'apt' package manager. All provided software has been patched and compiled with Android NDK to provide max compatibility with Android OS.
To learn more about application usage tips and tricks, long-press anywhere on the terminal and select the Help menu option to access Termux Wiki. This resource is also accessible directly in a web browser: https://wiki.termux.com/wiki/Main_Page.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

View File

@@ -0,0 +1 @@
Terminal emulator app with a large set of command line utilities

View File

@@ -17,5 +17,5 @@ android.useAndroidX=true
minSdkVersion=24
targetSdkVersion=28
ndkVersion=21.3.6528147
ndkVersion=22.0.7026061
compileSdkVersion=28

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -1,6 +1,6 @@
plugins {
id "com.jfrog.bintray" version "1.7.3"
id "com.github.dcendents.android-maven" version "2.0"
id "com.jfrog.bintray" version "1.8.5"
id "com.github.dcendents.android-maven" version "2.1"
}
apply plugin: 'com.android.library'
@@ -11,9 +11,9 @@ ext {
libraryName = 'TerminalEmulator'
artifact = 'terminal-emulator'
libraryDescription = 'The terminal emulator used in Termux'
siteUrl = 'https://github.com/termux/termux'
gitUrl = 'https://github.com/termux/termux.git'
libraryVersion = '0.52'
siteUrl = 'https://github.com/termux/termux-app'
gitUrl = 'https://github.com/termux/termux-app.git'
libraryVersion = '0.106'
}
android {
@@ -52,6 +52,10 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
testOptions {
unitTests.returnDefaultValues = true
}
}
tasks.withType(Test) {
@@ -61,7 +65,7 @@ tasks.withType(Test) {
}
dependencies {
testImplementation 'junit:junit:4.13'
testImplementation 'junit:junit:4.13.1'
}
apply from: '../scripts/bintray-publish.gradle'

View File

@@ -59,6 +59,7 @@ public final class KeyHandler {
public static final int KEYMOD_ALT = 0x80000000;
public static final int KEYMOD_CTRL = 0x40000000;
public static final int KEYMOD_SHIFT = 0x20000000;
public static final int KEYMOD_NUM_LOCK = 0x10000000;
private static final Map<String, Integer> TERMCAP_TO_KEYCODE = new HashMap<>();
@@ -145,10 +146,16 @@ public final class KeyHandler {
keyMod |= KEYMOD_ALT;
keyCode &= ~KEYMOD_ALT;
}
if ((keyCode & KEYMOD_NUM_LOCK) != 0) {
keyMod |= KEYMOD_NUM_LOCK;
keyCode &= ~KEYMOD_NUM_LOCK;
}
return getCode(keyCode, keyMod, cursorKeysApplication, keypadApplication);
}
public static String getCode(int keyCode, int keyMode, boolean cursorApp, boolean keypadApplication) {
boolean numLockOn = (keyMode & KEYMOD_NUM_LOCK) != 0;
keyMode &= ~KEYMOD_NUM_LOCK;
switch (keyCode) {
case KEYCODE_DPAD_CENTER:
return "\015";
@@ -228,8 +235,11 @@ public final class KeyHandler {
// Just do what xterm and gnome-terminal does:
return prefix + (((keyMode & KEYMOD_CTRL) == 0) ? "\u007F" : "\u0008");
case KEYCODE_NUM_LOCK:
return "\033OP";
if (keypadApplication) {
return "\033OP";
} else {
return null;
}
case KEYCODE_SPACE:
// If ctrl is not down, return null so that it goes through normal input processing (which may e.g. cause a
// combining accent to be written):
@@ -249,31 +259,81 @@ public final class KeyHandler {
case KEYCODE_NUMPAD_COMMA:
return ",";
case KEYCODE_NUMPAD_DOT:
return keypadApplication ? "\033On" : ".";
if (numLockOn) {
return keypadApplication ? "\033On" : ".";
} else {
// DELETE
return transformForModifiers("\033[3", keyMode, '~');
}
case KEYCODE_NUMPAD_SUBTRACT:
return keypadApplication ? transformForModifiers("\033O", keyMode, 'm') : "-";
case KEYCODE_NUMPAD_DIVIDE:
return keypadApplication ? transformForModifiers("\033O", keyMode, 'o') : "/";
case KEYCODE_NUMPAD_0:
return keypadApplication ? transformForModifiers("\033O", keyMode, 'p') : "0";
if (numLockOn) {
return keypadApplication ? transformForModifiers("\033O", keyMode, 'p') : "0";
} else {
// INSERT
return transformForModifiers("\033[2", keyMode, '~');
}
case KEYCODE_NUMPAD_1:
return keypadApplication ? transformForModifiers("\033O", keyMode, 'q') : "1";
if (numLockOn) {
return keypadApplication ? transformForModifiers("\033O", keyMode, 'q') : "1";
} else {
// END
return (keyMode == 0) ? (cursorApp ? "\033OF" : "\033[F") : transformForModifiers("\033[1", keyMode, 'F');
}
case KEYCODE_NUMPAD_2:
return keypadApplication ? transformForModifiers("\033O", keyMode, 'r') : "2";
if (numLockOn) {
return keypadApplication ? transformForModifiers("\033O", keyMode, 'r') : "2";
} else {
// DOWN
return (keyMode == 0) ? (cursorApp ? "\033OB" : "\033[B") : transformForModifiers("\033[1", keyMode, 'B');
}
case KEYCODE_NUMPAD_3:
return keypadApplication ? transformForModifiers("\033O", keyMode, 's') : "3";
if (numLockOn) {
return keypadApplication ? transformForModifiers("\033O", keyMode, 's') : "3";
} else {
// PGDN
return "\033[6~";
}
case KEYCODE_NUMPAD_4:
return keypadApplication ? transformForModifiers("\033O", keyMode, 't') : "4";
if (numLockOn) {
return keypadApplication ? transformForModifiers("\033O", keyMode, 't') : "4";
} else {
// LEFT
return (keyMode == 0) ? (cursorApp ? "\033OD" : "\033[D") : transformForModifiers("\033[1", keyMode, 'D');
}
case KEYCODE_NUMPAD_5:
return keypadApplication ? transformForModifiers("\033O", keyMode, 'u') : "5";
case KEYCODE_NUMPAD_6:
return keypadApplication ? transformForModifiers("\033O", keyMode, 'v') : "6";
if (numLockOn) {
return keypadApplication ? transformForModifiers("\033O", keyMode, 'v') : "6";
} else {
// RIGHT
return (keyMode == 0) ? (cursorApp ? "\033OC" : "\033[C") : transformForModifiers("\033[1", keyMode, 'C');
}
case KEYCODE_NUMPAD_7:
return keypadApplication ? transformForModifiers("\033O", keyMode, 'w') : "7";
if (numLockOn) {
return keypadApplication ? transformForModifiers("\033O", keyMode, 'w') : "7";
} else {
// HOME
return (keyMode == 0) ? (cursorApp ? "\033OH" : "\033[H") : transformForModifiers("\033[1", keyMode, 'H');
}
case KEYCODE_NUMPAD_8:
return keypadApplication ? transformForModifiers("\033O", keyMode, 'x') : "8";
if (numLockOn) {
return keypadApplication ? transformForModifiers("\033O", keyMode, 'x') : "8";
} else {
// UP
return (keyMode == 0) ? (cursorApp ? "\033OA" : "\033[A") : transformForModifiers("\033[1", keyMode, 'A');
}
case KEYCODE_NUMPAD_9:
return keypadApplication ? transformForModifiers("\033O", keyMode, 'y') : "9";
if (numLockOn) {
return keypadApplication ? transformForModifiers("\033O", keyMode, 'y') : "9";
} else {
// PGUP
return "\033[5~";
}
case KEYCODE_NUMPAD_EQUALS:
return keypadApplication ? transformForModifiers("\033O", keyMode, 'X') : "=";
}

View File

@@ -17,13 +17,13 @@ public class ByteQueueTest extends TestCase {
public void testCompleteWrites() throws Exception {
ByteQueue q = new ByteQueue(10);
assertTrue(q.write(new byte[]{1, 2, 3}, 0, 3));
assertTrue(q.write(new byte[]{1, 2, 3}, 0, 3));
byte[] arr = new byte[10];
assertEquals(3, q.read(arr, true));
assertArrayEquals(new byte[]{1, 2, 3}, new byte[]{arr[0], arr[1], arr[2]});
assertTrue(q.write(new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 0, 10));
assertTrue(q.write(new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 0, 10));
assertEquals(10, q.read(arr, true));
assertArrayEquals(new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, arr);
}
@@ -43,7 +43,7 @@ public class ByteQueueTest extends TestCase {
public void testWriteNotesClosing() throws Exception {
ByteQueue q = new ByteQueue(10);
q.close();
assertFalse(q.write(new byte[]{1, 2, 3}, 0, 3));
assertFalse(q.write(new byte[]{1, 2, 3}, 0, 3));
}
public void testReadNonBlocking() throws Exception {

View File

@@ -35,31 +35,31 @@ public class ControlSequenceIntroducerTest extends TerminalTestCase {
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", " ");
}
/** 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", " ");
}
/** CSI 3 J Clear scrollback (xterm, libvte; non-standard). */
public void testCsi3J() {
withTerminalSized(3, 2).enterString("a\r\nb\r\nc\r\nd");
assertEquals("a\nb\nc\nd", mTerminal.getScreen().getTranscriptText());
enterString("\033[3J");
assertEquals("c\nd", mTerminal.getScreen().getTranscriptText());
/** CSI 3 J Clear scrollback (xterm, libvte; non-standard). */
public void testCsi3J() {
withTerminalSized(3, 2).enterString("a\r\nb\r\nc\r\nd");
assertEquals("a\nb\nc\nd", mTerminal.getScreen().getTranscriptText());
enterString("\033[3J");
assertEquals("c\nd", mTerminal.getScreen().getTranscriptText());
withTerminalSized(3, 2).enterString("Lorem_ipsum");
assertEquals("Lorem_ipsum", mTerminal.getScreen().getTranscriptText());
enterString("\033[3J");
assertEquals("ipsum", mTerminal.getScreen().getTranscriptText());
withTerminalSized(3, 2).enterString("Lorem_ipsum");
assertEquals("Lorem_ipsum", mTerminal.getScreen().getTranscriptText());
enterString("\033[3J");
assertEquals("ipsum", mTerminal.getScreen().getTranscriptText());
withTerminalSized(3, 2).enterString("w\r\nx\r\ny\r\nz\033[?1049h\033[3J\033[?1049l");
assertEquals("y\nz", mTerminal.getScreen().getTranscriptText());
}
withTerminalSized(3, 2).enterString("w\r\nx\r\ny\r\nz\033[?1049h\033[3J\033[?1049l");
assertEquals("y\nz", mTerminal.getScreen().getTranscriptText());
}
}

View File

@@ -18,7 +18,7 @@ public class CursorAndScreenTest extends TerminalTestCase {
assertLinesAre("ABCDE", "FGHIJ", "KLMNO", "PQRST", "UVWXY");
for (int row = 0; row < 5; row++) {
for (int col = 0; col < 5; col++) {
long s = getStyleAt(row, col);
long s = getStyleAt(row, col);
Assert.assertEquals(col, TextStyle.decodeForeColor(s));
Assert.assertEquals(row, TextStyle.decodeBackColor(s));
}
@@ -43,7 +43,7 @@ public class CursorAndScreenTest extends TerminalTestCase {
for (int col = 0; col < 5; col++) {
int wantedForeground = (row == 1 || row == 2) ? 98 : col;
int wantedBackground = (row == 1 || row == 2) ? 99 : (row == 0 ? 2 : row);
long s = getStyleAt(row, col);
long s = getStyleAt(row, col);
Assert.assertEquals(wantedForeground, TextStyle.decodeForeColor(s));
Assert.assertEquals(wantedBackground, TextStyle.decodeBackColor(s));
}
@@ -159,11 +159,11 @@ public class CursorAndScreenTest extends TerminalTestCase {
}
}
/**
* See comments on horizontal tab handling in TerminalEmulator.java.
*
* We do not want to color already written cells when tabbing over them.
*/
/**
* See comments on horizontal tab handling in TerminalEmulator.java.
* <p/>
* We do not want to color already written cells when tabbing over them.
*/
public void DISABLED_testHorizontalTabColorsBackground() {
withTerminalSized(10, 3).enterString("\033[48;5;15m").enterString("\t");
assertCursorAt(0, 8);
@@ -214,14 +214,14 @@ public class CursorAndScreenTest extends TerminalTestCase {
" -");
}
public void testBackspaceAcrossWrappedLines() {
// Backspace should not go to previous line if not auto-wrapped:
withTerminalSized(3, 3).enterString("hi\r\n\b\byou").assertLinesAre("hi ", "you", " ");
// Backspace should go to previous line if auto-wrapped:
withTerminalSized(3, 3).enterString("hi y").assertLinesAre("hi ", "y ", " ").enterString("\b\b#").assertLinesAre("hi#", "y ", " ");
// Initial backspace should do nothing:
withTerminalSized(3, 3).enterString("\b\b\b\bhi").assertLinesAre("hi ", " ", " ");
}
public void testBackspaceAcrossWrappedLines() {
// Backspace should not go to previous line if not auto-wrapped:
withTerminalSized(3, 3).enterString("hi\r\n\b\byou").assertLinesAre("hi ", "you", " ");
// Backspace should go to previous line if auto-wrapped:
withTerminalSized(3, 3).enterString("hi y").assertLinesAre("hi ", "y ", " ").enterString("\b\b#").assertLinesAre("hi#", "y ", " ");
// Initial backspace should do nothing:
withTerminalSized(3, 3).enterString("\b\b\b\bhi").assertLinesAre("hi ", " ", " ");
}
public void testCursorSaveRestoreLocation() {
// DEC save/restore

View File

@@ -174,18 +174,30 @@ public class KeyHandlerTest extends TestCase {
assertKeysEquals("\033[23;2~", KeyHandler.getCode(KeyEvent.KEYCODE_F11, KeyHandler.KEYMOD_SHIFT, false, false));
assertKeysEquals("\033[24;2~", KeyHandler.getCode(KeyEvent.KEYCODE_F12, KeyHandler.KEYMOD_SHIFT, false, false));
assertKeysEquals("0", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_0, 0, false, false));
assertKeysEquals("1", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_1, 0, false, false));
assertKeysEquals("2", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_2, 0, false, false));
assertKeysEquals("3", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_3, 0, false, false));
assertKeysEquals("4", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_4, 0, false, false));
assertKeysEquals("0", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_0, KeyHandler.KEYMOD_NUM_LOCK, false, false));
assertKeysEquals("1", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_1, KeyHandler.KEYMOD_NUM_LOCK, false, false));
assertKeysEquals("2", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_2, KeyHandler.KEYMOD_NUM_LOCK, false, false));
assertKeysEquals("3", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_3, KeyHandler.KEYMOD_NUM_LOCK, false, false));
assertKeysEquals("4", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_4, KeyHandler.KEYMOD_NUM_LOCK, false, false));
assertKeysEquals("5", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_5, KeyHandler.KEYMOD_NUM_LOCK, false, false));
assertKeysEquals("6", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_6, KeyHandler.KEYMOD_NUM_LOCK, false, false));
assertKeysEquals("7", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_7, KeyHandler.KEYMOD_NUM_LOCK, false, false));
assertKeysEquals("8", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_8, KeyHandler.KEYMOD_NUM_LOCK, false, false));
assertKeysEquals("9", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_9, KeyHandler.KEYMOD_NUM_LOCK, false, false));
assertKeysEquals(",", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_COMMA, KeyHandler.KEYMOD_NUM_LOCK, false, false));
assertKeysEquals(".", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_DOT, KeyHandler.KEYMOD_NUM_LOCK, false, false));
assertKeysEquals("\033[2~", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_0, 0, false, false));
assertKeysEquals("\033[F", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_1, 0, false, false));
assertKeysEquals("\033[B", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_2, 0, false, false));
assertKeysEquals("\033[6~", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_3, 0, false, false));
assertKeysEquals("\033[D", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_4, 0, false, false));
assertKeysEquals("5", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_5, 0, false, false));
assertKeysEquals("6", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_6, 0, false, false));
assertKeysEquals("7", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_7, 0, false, false));
assertKeysEquals("8", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_8, 0, false, false));
assertKeysEquals("9", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_9, 0, false, false));
assertKeysEquals(",", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_COMMA, 0, false, false));
assertKeysEquals(".", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_DOT, 0, false, false));
}
assertKeysEquals("\033[C", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_6, 0, false, false));
assertKeysEquals("\033[H", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_7, 0, false, false));
assertKeysEquals("\033[A", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_8, 0, false, false));
assertKeysEquals("\033[5~", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_9, 0, false, false));
assertKeysEquals("\033[3~", KeyHandler.getCode(KeyEvent.KEYCODE_NUMPAD_DOT, 0, false, false));
}
}

View File

@@ -93,7 +93,7 @@ public class ResizeTest extends TerminalTestCase {
enterString("\033[2J");
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
long style = getStyleAt(r, c);
long style = getStyleAt(r, c);
assertEquals(119, TextStyle.decodeForeColor(style));
assertEquals(129, TextStyle.decodeBackColor(style));
}
@@ -105,7 +105,7 @@ public class ResizeTest extends TerminalTestCase {
// After resize, screen should still be same color:
for (int r = 0; r < rows - 2; r++) {
for (int c = 0; c < cols; c++) {
long style = getStyleAt(r, c);
long style = getStyleAt(r, c);
assertEquals(119, TextStyle.decodeForeColor(style));
assertEquals(129, TextStyle.decodeBackColor(style));
}

View File

@@ -107,24 +107,24 @@ public class ScrollRegionTest extends TerminalTestCase {
assertLinesAre("1 ", "2 ", "3 ", "QQ", "YY");
}
/** See https://github.com/termux/termux-app/issues/1340 */
public void testScrollRegionDoesNotLimitCursorMovement() {
withTerminalSized(6, 4)
.enterString("\033[4;7r\033[3;1Haaa\033[Axxx")
.assertLinesAre(
" ",
" xxx",
"aaa ",
" "
);
/** See https://github.com/termux/termux-app/issues/1340 */
public void testScrollRegionDoesNotLimitCursorMovement() {
withTerminalSized(6, 4)
.enterString("\033[4;7r\033[3;1Haaa\033[Axxx")
.assertLinesAre(
" ",
" xxx",
"aaa ",
" "
);
withTerminalSized(6, 4)
.enterString("\033[1;3r\033[3;1Haaa\033[Bxxx")
.assertLinesAre(
" ",
" ",
"aaa ",
" xxx"
);
}
withTerminalSized(6, 4)
.enterString("\033[1;3r\033[3;1Haaa\033[Bxxx")
.assertLinesAre(
" ",
" ",
"aaa ",
" xxx"
);
}
}

View File

@@ -159,7 +159,7 @@ public class TerminalTest extends TerminalTestCase {
assertEquals(119, mTerminal.mForeColor);
assertEquals(129, mTerminal.mBackColor);
// Invalid parameter:
// Invalid parameter:
enterString("\033[48;8;129m");
assertEquals(119, mTerminal.mForeColor);
assertEquals(129, mTerminal.mBackColor);
@@ -169,30 +169,30 @@ public class TerminalTest extends TerminalTestCase {
assertEquals(178, mTerminal.mForeColor);
assertEquals(179, mTerminal.mBackColor);
// 24 bit colors:
enterString(("\033[0m")); // Reset fg and bg colors.
enterString("\033[38;2;255;127;2m");
int expectedForeground = 0xff000000 | (255 << 16) | (127 << 8) | 2;
assertEquals(expectedForeground, mTerminal.mForeColor);
assertEquals(TextStyle.COLOR_INDEX_BACKGROUND, mTerminal.mBackColor);
enterString("\033[48;2;1;2;254m");
int expectedBackground = 0xff000000 | (1 << 16) | (2 << 8) | 254;
assertEquals(expectedForeground, mTerminal.mForeColor);
assertEquals(expectedBackground, mTerminal.mBackColor);
// 24 bit colors:
enterString(("\033[0m")); // Reset fg and bg colors.
enterString("\033[38;2;255;127;2m");
int expectedForeground = 0xff000000 | (255 << 16) | (127 << 8) | 2;
assertEquals(expectedForeground, mTerminal.mForeColor);
assertEquals(TextStyle.COLOR_INDEX_BACKGROUND, mTerminal.mBackColor);
enterString("\033[48;2;1;2;254m");
int expectedBackground = 0xff000000 | (1 << 16) | (2 << 8) | 254;
assertEquals(expectedForeground, mTerminal.mForeColor);
assertEquals(expectedBackground, mTerminal.mBackColor);
// 24 bit colors, set fg and bg at once:
enterString(("\033[0m")); // Reset fg and bg colors.
assertEquals(TextStyle.COLOR_INDEX_FOREGROUND, mTerminal.mForeColor);
assertEquals(TextStyle.COLOR_INDEX_BACKGROUND, mTerminal.mBackColor);
enterString("\033[38;2;255;127;2;48;2;1;2;254m");
assertEquals(expectedForeground, mTerminal.mForeColor);
assertEquals(expectedBackground, mTerminal.mBackColor);
// 24 bit colors, set fg and bg at once:
enterString(("\033[0m")); // Reset fg and bg colors.
assertEquals(TextStyle.COLOR_INDEX_FOREGROUND, mTerminal.mForeColor);
assertEquals(TextStyle.COLOR_INDEX_BACKGROUND, mTerminal.mBackColor);
enterString("\033[38;2;255;127;2;48;2;1;2;254m");
assertEquals(expectedForeground, mTerminal.mForeColor);
assertEquals(expectedBackground, mTerminal.mBackColor);
// 24 bit colors, invalid input:
enterString("\033[38;2;300;127;2;48;2;1;300;254m");
assertEquals(expectedForeground, mTerminal.mForeColor);
assertEquals(expectedBackground, mTerminal.mBackColor);
}
// 24 bit colors, invalid input:
enterString("\033[38;2;300;127;2;48;2;1;300;254m");
assertEquals(expectedForeground, mTerminal.mForeColor);
assertEquals(expectedBackground, mTerminal.mBackColor);
}
public void testBackgroundColorErase() {
final int rows = 3;
@@ -200,7 +200,7 @@ public class TerminalTest extends TerminalTestCase {
withTerminalSized(cols, rows);
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
long style = getStyleAt(r, c);
long style = getStyleAt(r, c);
assertEquals(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.decodeForeColor(style));
assertEquals(TextStyle.COLOR_INDEX_BACKGROUND, TextStyle.decodeBackColor(style));
}
@@ -213,7 +213,7 @@ public class TerminalTest extends TerminalTestCase {
enterString("\033[2J");
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
long style = getStyleAt(r, c);
long style = getStyleAt(r, c);
assertEquals(119, TextStyle.decodeForeColor(style));
assertEquals(129, TextStyle.decodeBackColor(style));
}
@@ -224,7 +224,7 @@ public class TerminalTest extends TerminalTestCase {
enterString("\033[2L");
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
long style = getStyleAt(r, c);
long style = getStyleAt(r, c);
assertEquals((r == 0 || r == 1) ? 139 : 129, TextStyle.decodeBackColor(style));
}
}
@@ -289,9 +289,9 @@ public class TerminalTest extends TerminalTestCase {
withTerminalSized(3, 3).enterString("abc\r ").assertLinesAre(" bc", " ", " ").assertCursorAt(0, 1);
}
public void testTab() {
withTerminalSized(11, 2).enterString("01234567890\r\tXX").assertLinesAre("01234567XX0", " ");
withTerminalSized(11, 2).enterString("01234567890\033[44m\r\tXX").assertLinesAre("01234567XX0", " ");
}
public void testTab() {
withTerminalSized(11, 2).enterString("01234567890\r\tXX").assertLinesAre("01234567XX0", " ");
withTerminalSized(11, 2).enterString("01234567890\033[44m\r\tXX").assertLinesAre("01234567XX0", " ");
}
}

View File

@@ -18,7 +18,7 @@ public abstract class TerminalTestCase extends TestCase {
public final List<ChangedTitle> titleChanges = new ArrayList<>();
public final List<String> clipboardPuts = new ArrayList<>();
public int bellsRung = 0;
public int colorsChanged = 0;
public int colorsChanged = 0;
@Override
public void write(byte[] data, int offset, int count) {
@@ -26,10 +26,10 @@ public abstract class TerminalTestCase extends TestCase {
}
public String getOutputAndClear() {
String result = new String(baos.toByteArray(), StandardCharsets.UTF_8);
baos.reset();
return result;
}
String result = new String(baos.toByteArray(), StandardCharsets.UTF_8);
baos.reset();
return result;
}
@Override
public void titleChanged(String oldTitle, String newTitle) {
@@ -46,11 +46,11 @@ public abstract class TerminalTestCase extends TestCase {
bellsRung++;
}
@Override
public void onColorsChanged() {
colorsChanged++;
}
}
@Override
public void onColorsChanged() {
colorsChanged++;
}
}
public TerminalEmulator mTerminal;
public MockTerminalOutput mOutput;

View File

@@ -22,23 +22,23 @@ public class TextStyleTest extends TestCase {
}
}
public void testEncoding24Bit() {
int[] values = {255, 240, 127, 1, 0};
for (int red : values) {
for (int green : values) {
for (int blue : values) {
int argb = 0xFF000000 | (red << 16) | (green << 8) | blue;
long encoded = TextStyle.encode(argb, 0, 0);
assertEquals(argb, TextStyle.decodeForeColor(encoded));
encoded = TextStyle.encode(0, argb, 0);
assertEquals(argb, TextStyle.decodeBackColor(encoded));
}
}
}
}
public void testEncoding24Bit() {
int[] values = {255, 240, 127, 1, 0};
for (int red : values) {
for (int green : values) {
for (int blue : values) {
int argb = 0xFF000000 | (red << 16) | (green << 8) | blue;
long encoded = TextStyle.encode(argb, 0, 0);
assertEquals(argb, TextStyle.decodeForeColor(encoded));
encoded = TextStyle.encode(0, argb, 0);
assertEquals(argb, TextStyle.decodeBackColor(encoded));
}
}
}
}
public void testEncodingCombinations() {
public void testEncodingCombinations() {
for (int f1 : ALL_EFFECTS) {
for (int f2 : ALL_EFFECTS) {
int combined = f1 | f2;
@@ -54,9 +54,9 @@ public class TextStyleTest extends TestCase {
}
public void testEncodingProtected() {
long encoded = TextStyle.encode(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.COLOR_INDEX_BACKGROUND,
long encoded = TextStyle.encode(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.COLOR_INDEX_BACKGROUND,
TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH);
assertEquals(0, (TextStyle.decodeEffect(encoded) & TextStyle.CHARACTER_ATTRIBUTE_PROTECTED));
assertEquals(0, (TextStyle.decodeEffect(encoded) & TextStyle.CHARACTER_ATTRIBUTE_PROTECTED));
encoded = TextStyle.encode(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.COLOR_INDEX_BACKGROUND,
TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH | TextStyle.CHARACTER_ATTRIBUTE_PROTECTED);
assertTrue((TextStyle.decodeEffect(encoded) & TextStyle.CHARACTER_ATTRIBUTE_PROTECTED) != 0);

View File

@@ -15,12 +15,12 @@ public class WcWidthTest extends TestCase {
}
}
public void testSomeWidthOne() {
assertWidthIs(1, 'å');
assertWidthIs(1, 'ä');
assertWidthIs(1, 'ö');
assertWidthIs(1, 0x23F2);
}
public void testSomeWidthOne() {
assertWidthIs(1, 'å');
assertWidthIs(1, 'ä');
assertWidthIs(1, 'ö');
assertWidthIs(1, 0x23F2);
}
public void testSomeWide() {
assertWidthIs(2, '');
@@ -44,18 +44,18 @@ public class WcWidthTest extends TestCase {
}
public void testCombining() {
assertWidthIs(0, 0x0302);
assertWidthIs(0, 0x0308);
assertWidthIs(0, 0xFE0F);
}
assertWidthIs(0, 0x0302);
assertWidthIs(0, 0x0308);
assertWidthIs(0, 0xFE0F);
}
public void testWordJoiner() {
// https://en.wikipedia.org/wiki/Word_joiner
// The word joiner (WJ) is a code point in Unicode used to separate words when using scripts
// that do not use explicit spacing. It is encoded since Unicode version 3.2
// (released in 2002) as U+2060 WORD JOINER (HTML &#8288;).
// The word joiner does not produce any space, and prohibits a line break at its position.
assertWidthIs(0, 0x2060);
public void testWordJoiner() {
// https://en.wikipedia.org/wiki/Word_joiner
// The word joiner (WJ) is a code point in Unicode used to separate words when using scripts
// that do not use explicit spacing. It is encoded since Unicode version 3.2
// (released in 2002) as U+2060 WORD JOINER (HTML &#8288;).
// The word joiner does not produce any space, and prohibits a line break at its position.
assertWidthIs(0, 0x2060);
}
public void testSofthyphen() {
@@ -72,10 +72,10 @@ public class WcWidthTest extends TestCase {
assertWidthIs(1, 0x11A3);
}
public void testEmojis() {
assertWidthIs(2, 0x1F428); // KOALA.
assertWidthIs(2, 0x231a); // WATCH.
assertWidthIs(2, 0x1F643); // UPSIDE-DOWN FACE (Unicode 8).
}
public void testEmojis() {
assertWidthIs(2, 0x1F428); // KOALA.
assertWidthIs(2, 0x231a); // WATCH.
assertWidthIs(2, 0x1F643); // UPSIDE-DOWN FACE (Unicode 8).
}
}

View File

@@ -1,6 +1,6 @@
plugins {
id "com.jfrog.bintray" version "1.7.3"
id "com.github.dcendents.android-maven" version "2.0"
id "com.jfrog.bintray" version "1.8.5"
id "com.github.dcendents.android-maven" version "2.1"
}
apply plugin: 'com.android.library'
@@ -11,9 +11,9 @@ ext {
libraryName = 'TerminalView'
artifact = 'terminal-view'
libraryDescription = 'The terminal view used in Termux'
siteUrl = 'https://github.com/termux/termux'
gitUrl = 'https://github.com/termux/termux.git'
libraryVersion = '0.50'
siteUrl = 'https://github.com/termux/termux-app'
gitUrl = 'https://github.com/termux/termux-app.git'
libraryVersion = '0.106'
}
android {
@@ -44,7 +44,7 @@ android {
}
dependencies {
testImplementation 'junit:junit:4.13'
testImplementation 'junit:junit:4.13.1'
}
apply from: '../scripts/bintray-publish.gradle'

View File

@@ -31,12 +31,15 @@ import android.view.ViewParent;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import android.view.autofill.AutofillValue;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.widget.PopupWindow;
import android.widget.Scroller;
import androidx.annotation.RequiresApi;
import com.termux.terminal.EmulatorDebug;
import com.termux.terminal.KeyHandler;
import com.termux.terminal.TerminalBuffer;
@@ -252,20 +255,7 @@ public final class TerminalView extends View {
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
File propsFile = new File(getContext().getFilesDir() + "/home/.termux/termux.properties");
if (!propsFile.exists())
propsFile = new File(getContext().getFilesDir() + "/home/.config/termux/termux.properties");
Properties props = new Properties();
try {
if (propsFile.isFile() && propsFile.canRead()) {
try (FileInputStream in = new FileInputStream(propsFile)) {
props.load(new InputStreamReader(in, StandardCharsets.UTF_8));
}
}
} catch (Exception e) {
Log.e("termux", "Error loading props", e);
}
Properties props = getProperties();
if (props.getProperty("enforce-char-based-input", "false").equals("true")) {
// Some keyboards seems do not reset the internal state on TYPE_NULL.
@@ -549,6 +539,8 @@ public final class TerminalView extends View {
@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
Properties props = getProperties();
if (LOG_KEY_EVENTS)
Log.i(EmulatorDebug.LOG_TAG, "onKeyPreIme(keyCode=" + keyCode + ", event=" + event + ")");
if (keyCode == KeyEvent.KEYCODE_BACK) {
@@ -564,6 +556,11 @@ public final class TerminalView extends View {
return onKeyUp(keyCode, event);
}
}
} else if (props.getProperty("ctrl-space-workaround", "false").equals("true") &&
keyCode == KeyEvent.KEYCODE_SPACE && event.isCtrlPressed()) {
/* ctrl + space does not work on some ROMs without this workaround.
However, this breaks it on devices where it works out of the box. */
return onKeyDown(keyCode, event);
}
return super.onKeyPreIme(keyCode, event);
}
@@ -594,6 +591,7 @@ public final class TerminalView extends View {
if (controlDown) keyMod |= KeyHandler.KEYMOD_CTRL;
if (event.isAltPressed() || leftAltDown) keyMod |= KeyHandler.KEYMOD_ALT;
if (event.isShiftPressed()) keyMod |= KeyHandler.KEYMOD_SHIFT;
if (event.isNumLockOn()) keyMod |= KeyHandler.KEYMOD_NUM_LOCK;
if (!event.isFunctionPressed() && handleKeyCode(keyCode, keyMod)) {
if (LOG_KEY_EVENTS) Log.i(EmulatorDebug.LOG_TAG, "handleKeyCode() took key event");
return true;
@@ -1194,7 +1192,7 @@ public final class TerminalView extends View {
final ActionMode.Callback callback = new ActionMode.Callback() {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
int show = MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT;
int show = MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT;
ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
menu.add(Menu.NONE, 1, Menu.NONE, R.string.copy_text).setShowAsAction(show);
@@ -1540,4 +1538,52 @@ public final class TerminalView extends View {
}
}
}
private Properties getProperties() {
File propsFile;
Properties props = new Properties();
String possiblePropLocations[] = {
getContext().getFilesDir() + "/home/.termux/termux.properties",
getContext().getFilesDir() + "/home/.config/termux/termux.properties"
};
propsFile = new File(possiblePropLocations[0]);
int i = 0;
while (!propsFile.exists() && i < possiblePropLocations.length) {
propsFile = new File(possiblePropLocations[i]);
i += 1;
}
try {
if (propsFile.isFile() && propsFile.canRead()) {
try (FileInputStream in = new FileInputStream(propsFile)) {
props.load(new InputStreamReader(in, StandardCharsets.UTF_8));
}
}
} catch (Exception e) {
Log.e("termux", "Error loading props", e);
}
return props;
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void autofill(AutofillValue value) {
if (value.isText()) {
mTermSession.write(value.getTextValue().toString());
}
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public int getAutofillType() {
return AUTOFILL_TYPE_TEXT;
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public AutofillValue getAutofillValue() {
return AutofillValue.forText("");
}
}