diff --git a/.cirrus.yml b/.cirrus.yml index 72d9b530..8e6a5449 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,40 +1,20 @@ container: image: cirrusci/android-sdk:28 - cpu: 4 + cpu: 2 memory: 8G task: - name: Run tests + name: tests script: ./gradlew test task: - name: Build release apk + name: debug-build depends_on: - - Run tests + - tests - environment: - KEYSTORE: ENCRYPTED[e3fa3d741db3c2929acabef0c954e995b7f86d8229f7796199ce6e15ae98cb8eae16b2e498b9daeafff35e1f3aba3f8f] - KEYSTORE_PASSWORD: ENCRYPTED[2761e799baef14b1c822dfcbe5a40ba3ae8e8c13be25563baed28ff35f66e51fa725aa9dcd29c0698023cd04a8ebd604] + script: | + ./gradlew assembleDebug - build_release_apk_script: | - ./gradlew assembleRelease - - 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" + output_artifacts: + path: "./app/build/outputs/apk/debug/*.apk" diff --git a/.gitignore b/.gitignore index a7327fba..5f7ee3c3 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ build/ *.apk *.so .externalNativeBuild +.cxx +*.zip # Crashlytics configuations com_crashlytics_export_strings.xml diff --git a/README.md b/README.md index 3c865508..734fc6a4 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,6 @@ [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 Wiki](https://wiki.termux.com/wiki/) - [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 [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 - [XTerm control sequences](http://invisible-island.net/xterm/ctlseqs/ctlseqs.html) diff --git a/app/build.gradle b/app/build.gradle index b3b1d12e..d56089c0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'com.android.application' +plugins { + id "com.android.application" +} android { compileSdkVersion 28 @@ -12,10 +14,30 @@ android { defaultConfig { applicationId "com.termux" - minSdkVersion 21 + minSdkVersion 24 targetSdkVersion 28 - versionCode 75 - versionName "0.75" + versionCode 84 + 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 { @@ -24,12 +46,22 @@ android { shrinkResources true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } + + debug { + signingConfig signingConfigs.debug + } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + externalNativeBuild { + ndkBuild { + path "src/main/cpp/Android.mk" + } + } } dependencies { @@ -41,3 +73,69 @@ task 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) + } +} + diff --git a/app/dev_keystore.jks b/app/dev_keystore.jks new file mode 100644 index 00000000..174cc95a Binary files /dev/null and b/app/dev_keystore.jks differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 17aecc5c..901c1bbe 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -8,17 +8,17 @@ + - + + +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; +} diff --git a/app/src/main/java/com/termux/app/BackgroundJob.java b/app/src/main/java/com/termux/app/BackgroundJob.java index 11c89851..5fef2bd1 100644 --- a/app/src/main/java/com/termux/app/BackgroundJob.java +++ b/app/src/main/java/com/termux/app/BackgroundJob.java @@ -139,14 +139,14 @@ public final class BackgroundJob { try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(TermuxService.PREFIX_PATH + "/etc/apt/sources.list")))) { String line; while ((line = in.readLine()) != null) { - if (!line.startsWith("#") && line.contains("https://dl.bintray.com/termux/termux-packages-24")) { - return false; + if (!line.startsWith("#") && line.contains("//termux.net stable")) { + return true; } } } catch (IOException e) { Log.e(LOG_TAG, "Error trying to read sources.list", e); } - return true; + return false; } public static int getPid(Process p) { diff --git a/app/src/main/java/com/termux/app/ExtraKeysView.java b/app/src/main/java/com/termux/app/ExtraKeysView.java index d0d6f91d..e2f76dc3 100644 --- a/app/src/main/java/com/termux/app/ExtraKeysView.java +++ b/app/src/main/java/com/termux/app/ExtraKeysView.java @@ -354,9 +354,13 @@ public final class ExtraKeysView extends GridLayout { if (Settings.System.getInt(getContext().getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 0) != 0) { - // Depending on DnD settings, value can be >1 but 0 means "disabled". - if (Settings.Global.getInt(getContext().getContentResolver(), "zen_mode", 0) < 1) { + if (Build.VERSION.SDK_INT >= 28) { 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; - case MotionEvent.ACTION_UP: 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); if (scheduledExecutor != null) { scheduledExecutor.shutdownNow(); @@ -427,11 +437,7 @@ public final class ExtraKeysView extends GridLayout { LayoutParams param = new GridLayout.LayoutParams(); param.width = 0; - if(Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) { // special handle api 21 - param.height = (int)(37.5 * getResources().getDisplayMetrics().density + 0.5); // 37.5 equal to R.id.viewpager layout_height / rows in DP - } else { - param.height = 0; - } + param.height = 0; param.setMargins(0, 0, 0, 0); param.columnSpec = GridLayout.spec(col, GridLayout.FILL, 1.f); param.rowSpec = GridLayout.spec(row, GridLayout.FILL, 1.f); diff --git a/app/src/main/java/com/termux/app/TermuxActivity.java b/app/src/main/java/com/termux/app/TermuxActivity.java index 28f968ce..0fe5b5df 100644 --- a/app/src/main/java/com/termux/app/TermuxActivity.java +++ b/app/src/main/java/com/termux/app/TermuxActivity.java @@ -127,6 +127,8 @@ public final class TermuxActivity extends Activity implements ServiceConnection */ boolean mIsVisible; + boolean mIsUsingBlackUI; + final SoundPool mBellSoundPool = new SoundPool.Builder().setMaxStreams(1).setAudioAttributes( new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .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. */ - @TargetApi(Build.VERSION_CODES.M) public boolean ensureStoragePermissionGranted() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - 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. + 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; } } @Override 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); - mSettings = new TermuxPreferences(this); - 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.setOnKeyListener(new TermuxViewClient(this)); @@ -434,7 +443,11 @@ public final class TermuxActivity extends Activity implements ServiceConnection boolean sessionRunning = sessionAtRow.isRunning(); 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 sessionTitle = sessionAtRow.getTitle(); @@ -454,7 +467,8 @@ public final class TermuxActivity extends Activity implements ServiceConnection } else { 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); return row; } diff --git a/app/src/main/java/com/termux/app/TermuxInstaller.java b/app/src/main/java/com/termux/app/TermuxInstaller.java index 74823b63..6e50b22d 100644 --- a/app/src/main/java/com/termux/app/TermuxInstaller.java +++ b/app/src/main/java/com/termux/app/TermuxInstaller.java @@ -4,7 +4,6 @@ import android.app.Activity; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.Context; -import android.os.Build; import android.os.Environment; import android.os.UserManager; import android.system.Os; @@ -16,14 +15,12 @@ import com.termux.R; import com.termux.terminal.EmulatorDebug; import java.io.BufferedReader; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; -import java.net.MalformedURLException; -import java.net.URL; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @@ -38,7 +35,7 @@ import java.util.zip.ZipInputStream; *

* (3) A staging folder, $STAGING_PREFIX, is {@link #deleteFolder(File)} if left over from broken installation below. *

- * (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. *

* (5) The zip, containing entries relative to the $PREFIX, is is downloaded and extracted by a zip input stream * continuously encountering zip file entries: @@ -82,8 +79,8 @@ final class TermuxInstaller { final byte[] buffer = new byte[8096]; final List> symlinks = new ArrayList<>(50); - final URL zipUrl = determineZipUrl(); - try (ZipInputStream zipInput = new ZipInputStream(zipUrl.openStream())) { + final byte[] zipBytes = loadZipBytes(); + try (ZipInputStream zipInput = new ZipInputStream(new ByteArrayInputStream(zipBytes))) { ZipEntry zipEntry; while ((zipEntry = zipInput.getNextEntry()) != null) { if (zipEntry.getName().equals("SYMLINKS.txt")) { @@ -167,34 +164,13 @@ final class TermuxInstaller { } } - /** Get bootstrap zip url for this systems cpu architecture. */ - private static URL determineZipUrl() throws MalformedURLException { - String archName = determineTermuxArchName(); - String url = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N - ? "https://termux.org/bootstrap-" + archName + ".zip" - : "https://termux.net/bootstrap/bootstrap-" + archName + ".zip"; - return new URL(url); + public static byte[] loadZipBytes() { + // Only load the shared library when necessary to save memory usage. + System.loadLibrary("termux-bootstrap"); + return getZip(); } - private static String determineTermuxArchName() { - // 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)); - } + public static native byte[] getZip(); /** Delete a folder and all its content or throw. Don't follow symlinks. */ static void deleteFolder(File fileOrDirectory) throws IOException { diff --git a/app/src/main/java/com/termux/app/TermuxPreferences.java b/app/src/main/java/com/termux/app/TermuxPreferences.java index 16a996ed..f6095837 100644 --- a/app/src/main/java/com/termux/app/TermuxPreferences.java +++ b/app/src/main/java/com/termux/app/TermuxPreferences.java @@ -58,6 +58,7 @@ final class TermuxPreferences { private static final String CURRENT_SESSION_KEY = "current_session"; private static final String SCREEN_ALWAYS_ON_KEY = "screen_always_on"; + private String mUseDarkUI; private boolean mScreenAlwaysOn; private int mFontSize; @@ -126,6 +127,10 @@ final class TermuxPreferences { return mScreenAlwaysOn; } + boolean isUsingBlackUI() { + return mUseDarkUI.toLowerCase().equals("true"); + } + void setScreenAlwaysOn(Context context, boolean newValue) { mScreenAlwaysOn = newValue; PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(SCREEN_ALWAYS_ON_KEY, newValue).apply(); @@ -173,6 +178,8 @@ final class TermuxPreferences { break; } + mUseDarkUI = props.getProperty("use-black-ui", "false"); + try { JSONArray arr = new JSONArray(props.getProperty("extra-keys", "[['ESC', 'TAB', 'CTRL', 'ALT', '-', 'DOWN', 'UP']]")); diff --git a/app/src/main/java/com/termux/app/TermuxService.java b/app/src/main/java/com/termux/app/TermuxService.java index 3bc2583b..b17643fe 100644 --- a/app/src/main/java/com/termux/app/TermuxService.java +++ b/app/src/main/java/com/termux/app/TermuxService.java @@ -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.acquire(); - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - String packageName = getPackageName(); - if (!pm.isIgnoringBatteryOptimizations(packageName)) { - Intent whitelist = new Intent(); - whitelist.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); - whitelist.setData(Uri.parse("package:" + packageName)); - whitelist.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + String packageName = getPackageName(); + if (!pm.isIgnoringBatteryOptimizations(packageName)) { + Intent whitelist = new Intent(); + whitelist.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); + whitelist.setData(Uri.parse("package:" + packageName)); + whitelist.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - try { - startActivity(whitelist); - } catch (ActivityNotFoundException e) { - Log.e(EmulatorDebug.LOG_TAG, "Failed to call ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS", e); - } + try { + startActivity(whitelist); + } catch (ActivityNotFoundException e) { + Log.e(EmulatorDebug.LOG_TAG, "Failed to call ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS", e); } } diff --git a/app/src/main/res/drawable/current_session.xml b/app/src/main/res/drawable/current_session.xml index e118aa01..90dd2818 100644 --- a/app/src/main/res/drawable/current_session.xml +++ b/app/src/main/res/drawable/current_session.xml @@ -1,4 +1,4 @@ - \ No newline at end of file + diff --git a/app/src/main/res/drawable/current_session_black.xml b/app/src/main/res/drawable/current_session_black.xml new file mode 100644 index 00000000..6a926499 --- /dev/null +++ b/app/src/main/res/drawable/current_session_black.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/res/drawable/selected_session_background_black.xml b/app/src/main/res/drawable/selected_session_background_black.xml new file mode 100644 index 00000000..25b7506f --- /dev/null +++ b/app/src/main/res/drawable/selected_session_background_black.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/session_ripple.xml b/app/src/main/res/drawable/session_ripple.xml index f38d75b6..9c4a1e79 100644 --- a/app/src/main/res/drawable/session_ripple.xml +++ b/app/src/main/res/drawable/session_ripple.xml @@ -4,4 +4,4 @@ - \ No newline at end of file + diff --git a/app/src/main/res/drawable/session_ripple_black.xml b/app/src/main/res/drawable/session_ripple_black.xml new file mode 100644 index 00000000..21423eb5 --- /dev/null +++ b/app/src/main/res/drawable/session_ripple_black.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index f7c5cc3a..1f352423 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,8 +1,6 @@ - - - + + + diff --git a/app/src/main/res/xml/backupscheme.xml b/app/src/main/res/xml/backupscheme.xml deleted file mode 100644 index 07566b9c..00000000 --- a/app/src/main/res/xml/backupscheme.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/build.gradle b/build.gradle index e8b19a7d..107f3030 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.5.1' + classpath 'com.android.tools.build:gradle:3.5.2' } } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 87b738cb..5c2d1cf0 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f23127f7..0ebb3108 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Sun Aug 25 01:57:11 CEST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip diff --git a/gradlew b/gradlew index af6708ff..83f2acfd 100755 --- a/gradlew +++ b/gradlew @@ -1,5 +1,21 @@ #!/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 @@ -28,7 +44,7 @@ APP_NAME="Gradle" 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. -DEFAULT_JVM_OPTS='"-Xmx64m"' +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. 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\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` diff --git a/gradlew.bat b/gradlew.bat index 0f8d5937..24467a14 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -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 @rem ########################################################################## @rem @@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0 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. -set DEFAULT_JVM_OPTS="-Xmx64m" +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome diff --git a/terminal-emulator/src/main/java/com/termux/terminal/TerminalEmulator.java b/terminal-emulator/src/main/java/com/termux/terminal/TerminalEmulator.java index f778cf92..3d9d7c8c 100644 --- a/terminal-emulator/src/main/java/com/termux/terminal/TerminalEmulator.java +++ b/terminal-emulator/src/main/java/com/termux/terminal/TerminalEmulator.java @@ -1376,10 +1376,10 @@ public final class TerminalEmulator { } break; 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; 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; case 'C': // "CSI${n}C" - Cursor forward (CUF). case 'a': // "CSI${n}a" - Horizontal position relative (HPR). From ISO-6428/ECMA-48. diff --git a/terminal-emulator/src/test/java/com/termux/terminal/CursorAndScreenTest.java b/terminal-emulator/src/test/java/com/termux/terminal/CursorAndScreenTest.java index 12635425..121cb223 100644 --- a/terminal-emulator/src/test/java/com/termux/terminal/CursorAndScreenTest.java +++ b/terminal-emulator/src/test/java/com/termux/terminal/CursorAndScreenTest.java @@ -133,8 +133,6 @@ public class CursorAndScreenTest extends TerminalTestCase { 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: 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() { @@ -143,8 +141,6 @@ public class CursorAndScreenTest extends TerminalTestCase { 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: 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() { diff --git a/terminal-emulator/src/test/java/com/termux/terminal/ScrollRegionTest.java b/terminal-emulator/src/test/java/com/termux/terminal/ScrollRegionTest.java index 260a481a..bbb5e7ac 100644 --- a/terminal-emulator/src/test/java/com/termux/terminal/ScrollRegionTest.java +++ b/terminal-emulator/src/test/java/com/termux/terminal/ScrollRegionTest.java @@ -107,4 +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 ", + " " + ); + + withTerminalSized(6, 4) + .enterString("\033[1;3r\033[3;1Haaa\033[Bxxx") + .assertLinesAre( + " ", + " ", + "aaa ", + " xxx" + ); + } }