mirror of
https://github.com/fankes/termux-app.git
synced 2025-09-06 10:45:23 +08:00
Merge remote-tracking branch 'remotes/origin/master'
This commit is contained in:
36
.cirrus.yml
36
.cirrus.yml
@@ -1,40 +1,20 @@
|
|||||||
container:
|
container:
|
||||||
image: cirrusci/android-sdk:28
|
image: cirrusci/android-sdk:28
|
||||||
cpu: 4
|
cpu: 2
|
||||||
memory: 8G
|
memory: 8G
|
||||||
|
|
||||||
task:
|
task:
|
||||||
name: Run tests
|
name: tests
|
||||||
script: ./gradlew test
|
script: ./gradlew test
|
||||||
|
|
||||||
task:
|
task:
|
||||||
name: Build release apk
|
name: debug-build
|
||||||
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- Run tests
|
- tests
|
||||||
|
|
||||||
environment:
|
script: |
|
||||||
KEYSTORE: ENCRYPTED[e3fa3d741db3c2929acabef0c954e995b7f86d8229f7796199ce6e15ae98cb8eae16b2e498b9daeafff35e1f3aba3f8f]
|
./gradlew assembleDebug
|
||||||
KEYSTORE_PASSWORD: ENCRYPTED[2761e799baef14b1c822dfcbe5a40ba3ae8e8c13be25563baed28ff35f66e51fa725aa9dcd29c0698023cd04a8ebd604]
|
|
||||||
|
|
||||||
build_release_apk_script: |
|
output_artifacts:
|
||||||
./gradlew assembleRelease
|
path: "./app/build/outputs/apk/debug/*.apk"
|
||||||
|
|
||||||
build_apksigner_script: |
|
|
||||||
cd ../
|
|
||||||
git clone https://github.com/fornwall/apksigner
|
|
||||||
cd apksigner
|
|
||||||
./gradlew
|
|
||||||
cp ./build/libs/apksigner-all.jar /tmp/apksigner.jar
|
|
||||||
|
|
||||||
sign_release_apk_script: |
|
|
||||||
echo "$KEYSTORE" | base64 -d > keystore.jks
|
|
||||||
java -jar /tmp/apksigner.jar -p "$KEYSTORE_PASSWORD" keystore.jks \
|
|
||||||
./app/build/outputs/apk/release/app-release-unsigned.apk \
|
|
||||||
./termux-release-g${CIRRUS_CHANGE_IN_REPO:0:8}.apk
|
|
||||||
|
|
||||||
release_artifacts:
|
|
||||||
path: "./*.apk"
|
|
||||||
|
|
||||||
unsigned_artifacts:
|
|
||||||
path: "./app/build/outputs/apk/release/*.apk"
|
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -7,6 +7,8 @@ build/
|
|||||||
*.apk
|
*.apk
|
||||||
*.so
|
*.so
|
||||||
.externalNativeBuild
|
.externalNativeBuild
|
||||||
|
.cxx
|
||||||
|
*.zip
|
||||||
|
|
||||||
# Crashlytics configuations
|
# Crashlytics configuations
|
||||||
com_crashlytics_export_strings.xml
|
com_crashlytics_export_strings.xml
|
||||||
|
18
README.md
18
README.md
@@ -5,8 +5,6 @@
|
|||||||
|
|
||||||
[Termux](https://termux.com) is an Android terminal application and Linux environment.
|
[Termux](https://termux.com) is an Android terminal application and Linux environment.
|
||||||
|
|
||||||
- [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 Reddit community](https://reddit.com/r/termux)
|
- [Termux Reddit community](https://reddit.com/r/termux)
|
||||||
- [Termux Wiki](https://wiki.termux.com/wiki/)
|
- [Termux Wiki](https://wiki.termux.com/wiki/)
|
||||||
- [Termux Twitter](http://twitter.com/termux/)
|
- [Termux Twitter](http://twitter.com/termux/)
|
||||||
@@ -15,6 +13,22 @@ Note that this repository is for the app itself (the user interface and the
|
|||||||
terminal emulation). For the packages installable inside the app, see
|
terminal emulation). For the packages installable inside the app, see
|
||||||
[termux/termux-packages](https://github.com/termux/termux-packages)
|
[termux/termux-packages](https://github.com/termux/termux-packages)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Termux:Widget 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/)
|
||||||
|
|
||||||
|
Additionally we offer development builds for those who want to try out latest
|
||||||
|
features ready to be included in future versions. Such build can be obtained
|
||||||
|
directly from [Cirrus CI artifacts](https://api.cirrus-ci.com/v1/artifact/github/termux/termux-app/debug-build/output/app/build/outputs/apk/debug/app-debug.apk).
|
||||||
|
|
||||||
|
Signature keys of all offered builds are different. Before you switch the
|
||||||
|
installation source, you will have to uninstall the Termux application and
|
||||||
|
all currently installed plugins.
|
||||||
|
|
||||||
## Terminal resources
|
## Terminal resources
|
||||||
|
|
||||||
- [XTerm control sequences](http://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
|
- [XTerm control sequences](http://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
|
||||||
|
106
app/build.gradle
106
app/build.gradle
@@ -1,4 +1,6 @@
|
|||||||
apply plugin: 'com.android.application'
|
plugins {
|
||||||
|
id "com.android.application"
|
||||||
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 28
|
compileSdkVersion 28
|
||||||
@@ -12,10 +14,30 @@ android {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.termux"
|
applicationId "com.termux"
|
||||||
minSdkVersion 21
|
minSdkVersion 24
|
||||||
targetSdkVersion 28
|
targetSdkVersion 28
|
||||||
versionCode 75
|
versionCode 84
|
||||||
versionName "0.75"
|
versionName "0.84"
|
||||||
|
|
||||||
|
externalNativeBuild {
|
||||||
|
ndkBuild {
|
||||||
|
cFlags "-std=c11", "-Wall", "-Wextra", "-Werror", "-Os", "-fno-stack-protector", "-Wl,--gc-sections"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ndk {
|
||||||
|
abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
signingConfigs {
|
||||||
|
debug {
|
||||||
|
storeFile file('dev_keystore.jks')
|
||||||
|
keyAlias 'alias'
|
||||||
|
storePassword 'xrj45yWGLbsO7W0v'
|
||||||
|
keyPassword 'xrj45yWGLbsO7W0v'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
@@ -24,12 +46,22 @@ android {
|
|||||||
shrinkResources true
|
shrinkResources true
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug {
|
||||||
|
signingConfig signingConfigs.debug
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
externalNativeBuild {
|
||||||
|
ndkBuild {
|
||||||
|
path "src/main/cpp/Android.mk"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@@ -41,3 +73,69 @@ task versionName {
|
|||||||
print android.defaultConfig.versionName
|
print android.defaultConfig.versionName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def downloadBootstrap(String arch, String expectedChecksum, int version) {
|
||||||
|
def digest = java.security.MessageDigest.getInstance("SHA-256")
|
||||||
|
|
||||||
|
def localUrl = "src/main/cpp/bootstrap-" + arch + ".zip"
|
||||||
|
def file = new File(projectDir, localUrl)
|
||||||
|
if (file.exists()) {
|
||||||
|
def buffer = new byte[8192]
|
||||||
|
def input = new FileInputStream(file)
|
||||||
|
while (true) {
|
||||||
|
def readBytes = input.read(buffer)
|
||||||
|
if (readBytes < 0) break
|
||||||
|
digest.update(buffer, 0, readBytes)
|
||||||
|
}
|
||||||
|
def checksum = new BigInteger(1, digest.digest()).toString(16)
|
||||||
|
if (checksum == expectedChecksum) {
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
logger.quiet("Deleting old local file with wrong hash: " + localUrl)
|
||||||
|
file.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def remoteUrl = "https://bintray.com/termux/bootstrap/download_file?file_path=bootstrap-" + arch + "-v" + version + ".zip"
|
||||||
|
logger.quiet("Downloading " + remoteUrl + " ...")
|
||||||
|
|
||||||
|
file.parentFile.mkdirs()
|
||||||
|
def out = new BufferedOutputStream(new FileOutputStream(file))
|
||||||
|
|
||||||
|
def connection = new URL(remoteUrl).openConnection()
|
||||||
|
connection.setInstanceFollowRedirects(true)
|
||||||
|
def digestStream = new java.security.DigestInputStream(connection.inputStream, digest)
|
||||||
|
out << digestStream
|
||||||
|
out.close()
|
||||||
|
|
||||||
|
def checksum = new BigInteger(1, digest.digest()).toString(16)
|
||||||
|
if (checksum != expectedChecksum) {
|
||||||
|
file.delete()
|
||||||
|
throw new GradleException("Wrong checksum for " + remoteUrl + ": expected: " + expectedChecksum + ", actual: " + checksum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clean {
|
||||||
|
doLast {
|
||||||
|
def tree = fileTree(new File(projectDir, 'src/main/cpp'))
|
||||||
|
tree.include 'bootstrap-*.zip'
|
||||||
|
tree.each { it.delete() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task downloadBootstraps(){
|
||||||
|
doLast {
|
||||||
|
def version = 18
|
||||||
|
downloadBootstrap("aarch64", "1a4c08a696d452b58f69102428239ec0c07521c0ca9f48b23ef70ae0e5e3d4f8", version)
|
||||||
|
downloadBootstrap("arm", "bff11f2c7e9c1055a22fc5f20bb7507b75f6034e0f5d591ec6725b3407981b85", version)
|
||||||
|
downloadBootstrap("i686", "6fb93020db2807337d82a1537e24612400cacbd10cf4bccaeb0714d51e653da1", version)
|
||||||
|
downloadBootstrap("x86_64", "a6067e5decc486dcad190c1ed9e15366c798e5e7d9b9b9ee6b4b8231290524c3", version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEvaluate {
|
||||||
|
android.applicationVariants.all { variant ->
|
||||||
|
variant.javaCompileProvider.get().dependsOn(downloadBootstraps)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BIN
app/dev_keystore.jks
Normal file
BIN
app/dev_keystore.jks
Normal file
Binary file not shown.
@@ -8,17 +8,17 @@
|
|||||||
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
|
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
|
||||||
<uses-feature android:name="android.software.leanback" android:required="false" />
|
<uses-feature android:name="android.software.leanback" android:required="false" />
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission-sdk-23 android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
|
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:extractNativeLibs="true"
|
android:extractNativeLibs="true"
|
||||||
android:allowBackup="true"
|
android:allowBackup="false"
|
||||||
android:fullBackupContent="@xml/backupscheme"
|
|
||||||
android:icon="@drawable/ic_launcher"
|
android:icon="@drawable/ic_launcher"
|
||||||
android:banner="@drawable/banner"
|
android:banner="@drawable/banner"
|
||||||
android:label="@string/application_name"
|
android:label="@string/application_name"
|
||||||
|
5
app/src/main/cpp/Android.mk
Normal file
5
app/src/main/cpp/Android.mk
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
LOCAL_PATH:= $(call my-dir)
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
LOCAL_MODULE := libtermux-bootstrap
|
||||||
|
LOCAL_SRC_FILES := termux-bootstrap-zip.S termux-bootstrap.c
|
||||||
|
include $(BUILD_SHARED_LIBRARY)
|
18
app/src/main/cpp/termux-bootstrap-zip.S
Normal file
18
app/src/main/cpp/termux-bootstrap-zip.S
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
.global blob
|
||||||
|
.global blob_size
|
||||||
|
.section .rodata
|
||||||
|
blob:
|
||||||
|
#if defined __i686__
|
||||||
|
.incbin "bootstrap-i686.zip"
|
||||||
|
#elif defined __x86_64__
|
||||||
|
.incbin "bootstrap-x86_64.zip"
|
||||||
|
#elif defined __aarch64__
|
||||||
|
.incbin "bootstrap-aarch64.zip"
|
||||||
|
#elif defined __arm__
|
||||||
|
.incbin "bootstrap-arm.zip"
|
||||||
|
#else
|
||||||
|
# error Unsupported arch
|
||||||
|
#endif
|
||||||
|
1:
|
||||||
|
blob_size:
|
||||||
|
.int 1b - blob
|
11
app/src/main/cpp/termux-bootstrap.c
Normal file
11
app/src/main/cpp/termux-bootstrap.c
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
extern jbyte blob[];
|
||||||
|
extern int blob_size;
|
||||||
|
|
||||||
|
JNIEXPORT jbyteArray JNICALL Java_com_termux_app_TermuxInstaller_getZip(JNIEnv *env, __attribute__((__unused__)) jobject This)
|
||||||
|
{
|
||||||
|
jbyteArray ret = (*env)->NewByteArray(env, blob_size);
|
||||||
|
(*env)->SetByteArrayRegion(env, ret, 0, blob_size, blob);
|
||||||
|
return ret;
|
||||||
|
}
|
@@ -139,14 +139,14 @@ public final class BackgroundJob {
|
|||||||
try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(TermuxService.PREFIX_PATH + "/etc/apt/sources.list")))) {
|
try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(TermuxService.PREFIX_PATH + "/etc/apt/sources.list")))) {
|
||||||
String line;
|
String line;
|
||||||
while ((line = in.readLine()) != null) {
|
while ((line = in.readLine()) != null) {
|
||||||
if (!line.startsWith("#") && line.contains("https://dl.bintray.com/termux/termux-packages-24")) {
|
if (!line.startsWith("#") && line.contains("//termux.net stable")) {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(LOG_TAG, "Error trying to read sources.list", e);
|
Log.e(LOG_TAG, "Error trying to read sources.list", e);
|
||||||
}
|
}
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getPid(Process p) {
|
public static int getPid(Process p) {
|
||||||
|
@@ -354,9 +354,13 @@ public final class ExtraKeysView extends GridLayout {
|
|||||||
if (Settings.System.getInt(getContext().getContentResolver(),
|
if (Settings.System.getInt(getContext().getContentResolver(),
|
||||||
Settings.System.HAPTIC_FEEDBACK_ENABLED, 0) != 0) {
|
Settings.System.HAPTIC_FEEDBACK_ENABLED, 0) != 0) {
|
||||||
|
|
||||||
// Depending on DnD settings, value can be >1 but 0 means "disabled".
|
if (Build.VERSION.SDK_INT >= 28) {
|
||||||
if (Settings.Global.getInt(getContext().getContentResolver(), "zen_mode", 0) < 1) {
|
|
||||||
finalButton.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
|
finalButton.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
|
||||||
|
} else {
|
||||||
|
// Perform haptic feedback only if no total silence mode enabled.
|
||||||
|
if (Settings.Global.getInt(getContext().getContentResolver(), "zen_mode", 0) != 2) {
|
||||||
|
finalButton.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,8 +405,14 @@ public final class ExtraKeysView extends GridLayout {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MotionEvent.ACTION_UP:
|
|
||||||
case MotionEvent.ACTION_CANCEL:
|
case MotionEvent.ACTION_CANCEL:
|
||||||
|
v.setBackgroundColor(BUTTON_COLOR);
|
||||||
|
if (scheduledExecutor != null) {
|
||||||
|
scheduledExecutor.shutdownNow();
|
||||||
|
scheduledExecutor = null;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case MotionEvent.ACTION_UP:
|
||||||
v.setBackgroundColor(BUTTON_COLOR);
|
v.setBackgroundColor(BUTTON_COLOR);
|
||||||
if (scheduledExecutor != null) {
|
if (scheduledExecutor != null) {
|
||||||
scheduledExecutor.shutdownNow();
|
scheduledExecutor.shutdownNow();
|
||||||
@@ -427,11 +437,7 @@ public final class ExtraKeysView extends GridLayout {
|
|||||||
|
|
||||||
LayoutParams param = new GridLayout.LayoutParams();
|
LayoutParams param = new GridLayout.LayoutParams();
|
||||||
param.width = 0;
|
param.width = 0;
|
||||||
if(Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) { // special handle api 21
|
param.height = 0;
|
||||||
param.height = (int)(37.5 * getResources().getDisplayMetrics().density + 0.5); // 37.5 equal to R.id.viewpager layout_height / rows in DP
|
|
||||||
} else {
|
|
||||||
param.height = 0;
|
|
||||||
}
|
|
||||||
param.setMargins(0, 0, 0, 0);
|
param.setMargins(0, 0, 0, 0);
|
||||||
param.columnSpec = GridLayout.spec(col, GridLayout.FILL, 1.f);
|
param.columnSpec = GridLayout.spec(col, GridLayout.FILL, 1.f);
|
||||||
param.rowSpec = GridLayout.spec(row, GridLayout.FILL, 1.f);
|
param.rowSpec = GridLayout.spec(row, GridLayout.FILL, 1.f);
|
||||||
|
@@ -127,6 +127,8 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
|||||||
*/
|
*/
|
||||||
boolean mIsVisible;
|
boolean mIsVisible;
|
||||||
|
|
||||||
|
boolean mIsUsingBlackUI;
|
||||||
|
|
||||||
final SoundPool mBellSoundPool = new SoundPool.Builder().setMaxStreams(1).setAudioAttributes(
|
final SoundPool mBellSoundPool = new SoundPool.Builder().setMaxStreams(1).setAudioAttributes(
|
||||||
new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
||||||
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION).build()).build();
|
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION).build()).build();
|
||||||
@@ -186,28 +188,35 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** For processes to access shared internal storage (/sdcard) we need this permission. */
|
/** For processes to access shared internal storage (/sdcard) we need this permission. */
|
||||||
@TargetApi(Build.VERSION_CODES.M)
|
|
||||||
public boolean ensureStoragePermissionGranted() {
|
public boolean ensureStoragePermissionGranted() {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
|
||||||
if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUESTCODE_PERMISSION_STORAGE);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Always granted before Android 6.0.
|
|
||||||
return true;
|
return true;
|
||||||
|
} else {
|
||||||
|
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUESTCODE_PERMISSION_STORAGE);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle bundle) {
|
public void onCreate(Bundle bundle) {
|
||||||
|
mSettings = new TermuxPreferences(this);
|
||||||
|
mIsUsingBlackUI = mSettings.isUsingBlackUI();
|
||||||
|
if (mIsUsingBlackUI) {
|
||||||
|
this.setTheme(R.style.Theme_Termux_Black);
|
||||||
|
} else {
|
||||||
|
this.setTheme(R.style.Theme_Termux);
|
||||||
|
}
|
||||||
|
|
||||||
super.onCreate(bundle);
|
super.onCreate(bundle);
|
||||||
|
|
||||||
mSettings = new TermuxPreferences(this);
|
|
||||||
|
|
||||||
setContentView(R.layout.drawer_layout);
|
setContentView(R.layout.drawer_layout);
|
||||||
|
|
||||||
|
if (mIsUsingBlackUI) {
|
||||||
|
findViewById(R.id.left_drawer).setBackgroundColor(
|
||||||
|
getResources().getColor(android.R.color.background_dark)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
mTerminalView = findViewById(R.id.terminal_view);
|
mTerminalView = findViewById(R.id.terminal_view);
|
||||||
mTerminalView.setOnKeyListener(new TermuxViewClient(this));
|
mTerminalView.setOnKeyListener(new TermuxViewClient(this));
|
||||||
|
|
||||||
@@ -434,7 +443,11 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
|||||||
boolean sessionRunning = sessionAtRow.isRunning();
|
boolean sessionRunning = sessionAtRow.isRunning();
|
||||||
|
|
||||||
TextView firstLineView = row.findViewById(R.id.row_line);
|
TextView firstLineView = row.findViewById(R.id.row_line);
|
||||||
|
if (mIsUsingBlackUI) {
|
||||||
|
firstLineView.setBackground(
|
||||||
|
getResources().getDrawable(R.drawable.selected_session_background_black)
|
||||||
|
);
|
||||||
|
}
|
||||||
String name = sessionAtRow.mSessionName;
|
String name = sessionAtRow.mSessionName;
|
||||||
String sessionTitle = sessionAtRow.getTitle();
|
String sessionTitle = sessionAtRow.getTitle();
|
||||||
|
|
||||||
@@ -454,7 +467,8 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
|||||||
} else {
|
} else {
|
||||||
firstLineView.setPaintFlags(firstLineView.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
|
firstLineView.setPaintFlags(firstLineView.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
|
||||||
}
|
}
|
||||||
int color = sessionRunning || sessionAtRow.getExitStatus() == 0 ? Color.BLACK : Color.RED;
|
int defaultColor = mIsUsingBlackUI ? Color.WHITE : Color.BLACK;
|
||||||
|
int color = sessionRunning || sessionAtRow.getExitStatus() == 0 ? defaultColor : Color.RED;
|
||||||
firstLineView.setTextColor(color);
|
firstLineView.setTextColor(color);
|
||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,6 @@ import android.app.Activity;
|
|||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
import android.system.Os;
|
import android.system.Os;
|
||||||
@@ -16,14 +15,12 @@ import com.termux.R;
|
|||||||
import com.termux.terminal.EmulatorDebug;
|
import com.termux.terminal.EmulatorDebug;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
@@ -38,7 +35,7 @@ import java.util.zip.ZipInputStream;
|
|||||||
* <p/>
|
* <p/>
|
||||||
* (3) A staging folder, $STAGING_PREFIX, is {@link #deleteFolder(File)} if left over from broken installation below.
|
* (3) A staging folder, $STAGING_PREFIX, is {@link #deleteFolder(File)} if left over from broken installation below.
|
||||||
* <p/>
|
* <p/>
|
||||||
* (4) The architecture is determined and an appropriate bootstrap zip url is determined in {@link #determineZipUrl()}.
|
* (4) The zip file is loaded from a shared library.
|
||||||
* <p/>
|
* <p/>
|
||||||
* (5) The zip, containing entries relative to the $PREFIX, is is downloaded and extracted by a zip input stream
|
* (5) The zip, containing entries relative to the $PREFIX, is is downloaded and extracted by a zip input stream
|
||||||
* continuously encountering zip file entries:
|
* continuously encountering zip file entries:
|
||||||
@@ -82,8 +79,8 @@ final class TermuxInstaller {
|
|||||||
final byte[] buffer = new byte[8096];
|
final byte[] buffer = new byte[8096];
|
||||||
final List<Pair<String, String>> symlinks = new ArrayList<>(50);
|
final List<Pair<String, String>> symlinks = new ArrayList<>(50);
|
||||||
|
|
||||||
final URL zipUrl = determineZipUrl();
|
final byte[] zipBytes = loadZipBytes();
|
||||||
try (ZipInputStream zipInput = new ZipInputStream(zipUrl.openStream())) {
|
try (ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(zipBytes))) {
|
||||||
ZipEntry zipEntry;
|
ZipEntry zipEntry;
|
||||||
while ((zipEntry = zipInput.getNextEntry()) != null) {
|
while ((zipEntry = zipInput.getNextEntry()) != null) {
|
||||||
if (zipEntry.getName().equals("SYMLINKS.txt")) {
|
if (zipEntry.getName().equals("SYMLINKS.txt")) {
|
||||||
@@ -167,34 +164,13 @@ final class TermuxInstaller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get bootstrap zip url for this systems cpu architecture. */
|
public static byte[] loadZipBytes() {
|
||||||
private static URL determineZipUrl() throws MalformedURLException {
|
// Only load the shared library when necessary to save memory usage.
|
||||||
String archName = determineTermuxArchName();
|
System.loadLibrary("termux-bootstrap");
|
||||||
String url = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
|
return getZip();
|
||||||
? "https://termux.org/bootstrap-" + archName + ".zip"
|
|
||||||
: "https://termux.net/bootstrap/bootstrap-" + archName + ".zip";
|
|
||||||
return new URL(url);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String determineTermuxArchName() {
|
public static native byte[] getZip();
|
||||||
// Note that we cannot use System.getProperty("os.arch") since that may give e.g. "aarch64"
|
|
||||||
// while a 64-bit runtime may not be installed (like on the Samsung Galaxy S5 Neo).
|
|
||||||
// Instead we search through the supported abi:s on the device, see:
|
|
||||||
// http://developer.android.com/ndk/guides/abis.html
|
|
||||||
// Note that we search for abi:s in preferred order (the ordering of the
|
|
||||||
// Build.SUPPORTED_ABIS list) to avoid e.g. installing arm on an x86 system where arm
|
|
||||||
// emulation is available.
|
|
||||||
for (String androidArch : Build.SUPPORTED_ABIS) {
|
|
||||||
switch (androidArch) {
|
|
||||||
case "arm64-v8a": return "aarch64";
|
|
||||||
case "armeabi-v7a": return "arm";
|
|
||||||
case "x86_64": return "x86_64";
|
|
||||||
case "x86": return "i686";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new RuntimeException("Unable to determine arch from Build.SUPPORTED_ABIS = " +
|
|
||||||
Arrays.toString(Build.SUPPORTED_ABIS));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Delete a folder and all its content or throw. Don't follow symlinks. */
|
/** Delete a folder and all its content or throw. Don't follow symlinks. */
|
||||||
static void deleteFolder(File fileOrDirectory) throws IOException {
|
static void deleteFolder(File fileOrDirectory) throws IOException {
|
||||||
|
@@ -58,6 +58,7 @@ final class TermuxPreferences {
|
|||||||
private static final String CURRENT_SESSION_KEY = "current_session";
|
private static final String CURRENT_SESSION_KEY = "current_session";
|
||||||
private static final String SCREEN_ALWAYS_ON_KEY = "screen_always_on";
|
private static final String SCREEN_ALWAYS_ON_KEY = "screen_always_on";
|
||||||
|
|
||||||
|
private String mUseDarkUI;
|
||||||
private boolean mScreenAlwaysOn;
|
private boolean mScreenAlwaysOn;
|
||||||
private int mFontSize;
|
private int mFontSize;
|
||||||
|
|
||||||
@@ -126,6 +127,10 @@ final class TermuxPreferences {
|
|||||||
return mScreenAlwaysOn;
|
return mScreenAlwaysOn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isUsingBlackUI() {
|
||||||
|
return mUseDarkUI.toLowerCase().equals("true");
|
||||||
|
}
|
||||||
|
|
||||||
void setScreenAlwaysOn(Context context, boolean newValue) {
|
void setScreenAlwaysOn(Context context, boolean newValue) {
|
||||||
mScreenAlwaysOn = newValue;
|
mScreenAlwaysOn = newValue;
|
||||||
PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(SCREEN_ALWAYS_ON_KEY, newValue).apply();
|
PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(SCREEN_ALWAYS_ON_KEY, newValue).apply();
|
||||||
@@ -173,6 +178,8 @@ final class TermuxPreferences {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mUseDarkUI = props.getProperty("use-black-ui", "false");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
JSONArray arr = new JSONArray(props.getProperty("extra-keys", "[['ESC', 'TAB', 'CTRL', 'ALT', '-', 'DOWN', 'UP']]"));
|
JSONArray arr = new JSONArray(props.getProperty("extra-keys", "[['ESC', 'TAB', 'CTRL', 'ALT', '-', 'DOWN', 'UP']]"));
|
||||||
|
|
||||||
|
@@ -114,19 +114,17 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
|||||||
mWifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, EmulatorDebug.LOG_TAG);
|
mWifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, EmulatorDebug.LOG_TAG);
|
||||||
mWifiLock.acquire();
|
mWifiLock.acquire();
|
||||||
|
|
||||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
String packageName = getPackageName();
|
||||||
String packageName = getPackageName();
|
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
|
||||||
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
|
Intent whitelist = new Intent();
|
||||||
Intent whitelist = new Intent();
|
whitelist.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
|
||||||
whitelist.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
|
whitelist.setData(Uri.parse("package:" + packageName));
|
||||||
whitelist.setData(Uri.parse("package:" + packageName));
|
whitelist.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
whitelist.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
startActivity(whitelist);
|
startActivity(whitelist);
|
||||||
} catch (ActivityNotFoundException e) {
|
} catch (ActivityNotFoundException e) {
|
||||||
Log.e(EmulatorDebug.LOG_TAG, "Failed to call ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS", e);
|
Log.e(EmulatorDebug.LOG_TAG, "Failed to call ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS", e);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
4
app/src/main/res/drawable/current_session_black.xml
Normal file
4
app/src/main/res/drawable/current_session_black.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||||
|
<solid android:color="#212325" />
|
||||||
|
</shape>
|
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:state_activated="true" android:drawable="@drawable/current_session_black"/>
|
||||||
|
<item android:state_activated="false" android:drawable="@drawable/session_ripple_black"/>
|
||||||
|
</selector>
|
7
app/src/main/res/drawable/session_ripple_black.xml
Normal file
7
app/src/main/res/drawable/session_ripple_black.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:color="@android:color/darker_gray" >
|
||||||
|
<item>
|
||||||
|
<color android:color="@android:color/background_dark" />
|
||||||
|
</item>
|
||||||
|
</ripple>
|
@@ -1,8 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<!-- See https://developer.android.com/training/material/theme.html for how to customize the Material theme. -->
|
|
||||||
<!-- NOTE: Cannot use "Light." since it hides the terminal scrollbar on the default black background. -->
|
|
||||||
<style name="Theme.Termux" parent="@android:style/Theme.Material.Light.NoActionBar">
|
<style name="Theme.Termux" parent="@android:style/Theme.Material.Light.NoActionBar">
|
||||||
<item name="android:statusBarColor">#000000</item>
|
<item name="android:statusBarColor">#000000</item>
|
||||||
<item name="android:colorPrimary">#FF000000</item>
|
<item name="android:colorPrimary">#FF000000</item>
|
||||||
@@ -23,9 +21,29 @@
|
|||||||
<item name="android:windowAllowEnterTransitionOverlap">true</item>
|
<item name="android:windowAllowEnterTransitionOverlap">true</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="TermuxAlertDialogStyle" parent="@android:style/Theme.Material.Light.Dialog.Alert">
|
<style name="TermuxAlertDialogStyle" parent="@android:style/Theme.Material.Light.Dialog.Alert">
|
||||||
<!-- Seen in buttons on alert dialog: -->
|
<!-- Seen in buttons on alert dialog: -->
|
||||||
<item name="android:colorAccent">#212121</item>
|
<item name="android:colorAccent">#212121</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<!-- See https://developer.android.com/training/material/theme.html for how to customize the Material theme. -->
|
||||||
|
<!-- NOTE: Cannot use "Light." since it hides the terminal scrollbar on the default black background. -->
|
||||||
|
<style name="Theme.Termux.Black" parent="@android:style/Theme.Material.NoActionBar">
|
||||||
|
<item name="android:statusBarColor">#000000</item>
|
||||||
|
<item name="android:colorPrimary">#FF000000</item>
|
||||||
|
<item name="android:windowBackground">@android:color/black</item>
|
||||||
|
|
||||||
|
<!-- Seen in buttons on left drawer: -->
|
||||||
|
<item name="android:colorAccent">#FDFDFD</item>
|
||||||
|
<!-- Avoid action mode toolbar pushing down terminal content when
|
||||||
|
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>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
|
@@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<full-backup-content>
|
|
||||||
<!-- See https://developer.android.com/training/backup/autosyncapi.html -->
|
|
||||||
<include domain="file" path="home/backup" />
|
|
||||||
</full-backup-content>
|
|
@@ -4,7 +4,7 @@ buildscript {
|
|||||||
google()
|
google()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.5.1'
|
classpath 'com.android.tools.build:gradle:3.5.2'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,5 @@
|
|||||||
#Sun Aug 25 01:57:11 CEST 2019
|
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
|
|
||||||
|
22
gradlew
vendored
22
gradlew
vendored
@@ -1,5 +1,21 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2015 the original author or authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
##
|
##
|
||||||
## Gradle start up script for UN*X
|
## Gradle start up script for UN*X
|
||||||
@@ -28,7 +44,7 @@ APP_NAME="Gradle"
|
|||||||
APP_BASE_NAME=`basename "$0"`
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m"'
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD="maximum"
|
MAX_FD="maximum"
|
||||||
@@ -109,8 +125,8 @@ if $darwin; then
|
|||||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# For Cygwin, switch paths to Windows format before running java
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
if $cygwin ; then
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
18
gradlew.bat
vendored
18
gradlew.bat
vendored
@@ -1,3 +1,19 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%" == "" @echo off
|
@if "%DEBUG%" == "" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
@@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
|
|||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
set DEFAULT_JVM_OPTS="-Xmx64m"
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
@rem Find java.exe
|
@rem Find java.exe
|
||||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
@@ -1376,10 +1376,10 @@ public final class TerminalEmulator {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'A': // "CSI${n}A" - Cursor up (CUU) ${n} rows.
|
case 'A': // "CSI${n}A" - Cursor up (CUU) ${n} rows.
|
||||||
setCursorRow(Math.max(mTopMargin, mCursorRow - getArg0(1)));
|
setCursorRow(Math.max(0, mCursorRow - getArg0(1)));
|
||||||
break;
|
break;
|
||||||
case 'B': // "CSI${n}B" - Cursor down (CUD) ${n} rows.
|
case 'B': // "CSI${n}B" - Cursor down (CUD) ${n} rows.
|
||||||
setCursorRow(Math.min(mBottomMargin - 1, mCursorRow + getArg0(1)));
|
setCursorRow(Math.min(mRows - 1, mCursorRow + getArg0(1)));
|
||||||
break;
|
break;
|
||||||
case 'C': // "CSI${n}C" - Cursor forward (CUF).
|
case 'C': // "CSI${n}C" - Cursor forward (CUF).
|
||||||
case 'a': // "CSI${n}a" - Horizontal position relative (HPR). From ISO-6428/ECMA-48.
|
case 'a': // "CSI${n}a" - Horizontal position relative (HPR). From ISO-6428/ECMA-48.
|
||||||
|
@@ -133,8 +133,6 @@ public class CursorAndScreenTest extends TerminalTestCase {
|
|||||||
withTerminalSized(3, 3).enterString("ABCDEFG\033[2AH").assertLinesAre("AHC", "DEF", "G ");
|
withTerminalSized(3, 3).enterString("ABCDEFG\033[2AH").assertLinesAre("AHC", "DEF", "G ");
|
||||||
// If an attempt is made to move the cursor above the top margin, the cursor stops at the top margin:
|
// If an attempt is made to move the cursor above the top margin, the cursor stops at the top margin:
|
||||||
withTerminalSized(3, 3).enterString("ABCDEFG\033[44AH").assertLinesAre("AHC", "DEF", "G ");
|
withTerminalSized(3, 3).enterString("ABCDEFG\033[44AH").assertLinesAre("AHC", "DEF", "G ");
|
||||||
// Set top margin and validate that cursor does not go above it:
|
|
||||||
withTerminalSized(3, 3).enterString("\033[2rABCDEFG\033[44AH").assertLinesAre("ABC", "DHF", "G ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testCursorDown() {
|
public void testCursorDown() {
|
||||||
@@ -143,8 +141,6 @@ public class CursorAndScreenTest extends TerminalTestCase {
|
|||||||
withTerminalSized(3, 3).enterString("AB\033[2BC").assertLinesAre("AB ", " ", " C");
|
withTerminalSized(3, 3).enterString("AB\033[2BC").assertLinesAre("AB ", " ", " C");
|
||||||
// If an attempt is made to move the cursor below the bottom margin, the cursor stops at the bottom margin:
|
// If an attempt is made to move the cursor below the bottom margin, the cursor stops at the bottom margin:
|
||||||
withTerminalSized(3, 3).enterString("AB\033[44BC").assertLinesAre("AB ", " ", " C");
|
withTerminalSized(3, 3).enterString("AB\033[44BC").assertLinesAre("AB ", " ", " C");
|
||||||
// Set bottom margin and validate that cursor does not go above it:
|
|
||||||
withTerminalSized(3, 3).enterString("\033[1;2rAB\033[44BC").assertLinesAre("AB ", " C", " ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testReportCursorPosition() {
|
public void testReportCursorPosition() {
|
||||||
|
@@ -107,4 +107,24 @@ public class ScrollRegionTest extends TerminalTestCase {
|
|||||||
assertLinesAre("1 ", "2 ", "3 ", "QQ", "YY");
|
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 ",
|
||||||
|
" "
|
||||||
|
);
|
||||||
|
|
||||||
|
withTerminalSized(6, 4)
|
||||||
|
.enterString("\033[1;3r\033[3;1Haaa\033[Bxxx")
|
||||||
|
.assertLinesAre(
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
"aaa ",
|
||||||
|
" xxx"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user