Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0a0822843 | ||
|
|
08b08d1c47 | ||
|
|
c76cec186a |
@@ -8,6 +8,7 @@ build/
|
||||
*.so
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
*.zip
|
||||
|
||||
# Crashlytics configuations
|
||||
com_crashlytics_export_strings.xml
|
||||
|
||||
+79
-4
@@ -1,4 +1,6 @@
|
||||
apply plugin: 'com.android.application'
|
||||
plugins {
|
||||
id "com.android.application"
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
@@ -12,10 +14,10 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.termux"
|
||||
minSdkVersion 21
|
||||
minSdkVersion 24
|
||||
targetSdkVersion 28
|
||||
versionCode 83
|
||||
versionName "0.83"
|
||||
versionCode 76
|
||||
versionName "0.76"
|
||||
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
@@ -26,6 +28,7 @@ android {
|
||||
ndk {
|
||||
abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
@@ -53,6 +56,12 @@ android {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
path "src/main/cpp/Android.mk"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -64,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 = 17
|
||||
downloadBootstrap("aarch64", "1846c453e7d4399559683e9a74eb8db048d1e88872668361652752b40ea2f3a2", version)
|
||||
downloadBootstrap("arm", "2f5a8061a1c13d48b659cb57c3767d59f5151f247ea032e2401135701dbd8058", version)
|
||||
downloadBootstrap("i686", "ca0e3559c5ac925528a7c8725a83a752ca63d5c1e0ff306e5ecd6ea9dd4c1de5", version)
|
||||
downloadBootstrap("x86_64", "9de2ac75ed0f3370418d3c55b470c176f69180079ba9461034bc7f6195fc5872", version)
|
||||
}
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
android.applicationVariants.all { variant ->
|
||||
variant.javaCompiler.dependsOn(downloadBootstraps)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
|
||||
<application
|
||||
android:extractNativeLibs="true"
|
||||
android:allowBackup="false"
|
||||
android:allowBackup="true"
|
||||
android:fullBackupContent="@xml/backupscheme"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:banner="@drawable/banner"
|
||||
android:label="@string/application_name"
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -123,7 +123,9 @@ public final class BackgroundJob {
|
||||
// Keep the default path so that system binaries can be used in the failsafe session.
|
||||
environment.add("PATH= " + System.getenv("PATH"));
|
||||
} else {
|
||||
environment.add("LD_LIBRARY_PATH=" + TermuxService.PREFIX_PATH + "/lib");
|
||||
if (shouldAddLdLibraryPath()) {
|
||||
environment.add("LD_LIBRARY_PATH=" + TermuxService.PREFIX_PATH + "/lib");
|
||||
}
|
||||
environment.add("LANG=en_US.UTF-8");
|
||||
environment.add("PATH=" + TermuxService.PREFIX_PATH + "/bin:" + TermuxService.PREFIX_PATH + "/bin/applets");
|
||||
environment.add("PWD=" + cwd);
|
||||
@@ -133,6 +135,20 @@ public final class BackgroundJob {
|
||||
return environment.toArray(new String[0]);
|
||||
}
|
||||
|
||||
private static boolean shouldAddLdLibraryPath() {
|
||||
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("//termux.net stable")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(LOG_TAG, "Error trying to read sources.list", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static int getPid(Process p) {
|
||||
try {
|
||||
Field f = p.getClass().getDeclaredField("pid");
|
||||
|
||||
@@ -354,13 +354,9 @@ public final class ExtraKeysView extends GridLayout {
|
||||
if (Settings.System.getInt(getContext().getContentResolver(),
|
||||
Settings.System.HAPTIC_FEEDBACK_ENABLED, 0) != 0) {
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
// Depending on DnD settings, value can be >1 but 0 means "disabled".
|
||||
if (Settings.Global.getInt(getContext().getContentResolver(), "zen_mode", 0) < 1) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,14 +401,8 @@ public final class ExtraKeysView extends GridLayout {
|
||||
}
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
v.setBackgroundColor(BUTTON_COLOR);
|
||||
if (scheduledExecutor != null) {
|
||||
scheduledExecutor.shutdownNow();
|
||||
scheduledExecutor = null;
|
||||
}
|
||||
return true;
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
v.setBackgroundColor(BUTTON_COLOR);
|
||||
if (scheduledExecutor != null) {
|
||||
scheduledExecutor.shutdownNow();
|
||||
|
||||
@@ -16,6 +16,7 @@ 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;
|
||||
@@ -38,7 +39,7 @@ import java.util.zip.ZipInputStream;
|
||||
* <p/>
|
||||
* (3) A staging folder, $STAGING_PREFIX, is {@link #deleteFolder(File)} if left over from broken installation below.
|
||||
* <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/>
|
||||
* (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 +83,8 @@ final class TermuxInstaller {
|
||||
final byte[] buffer = new byte[8096];
|
||||
final List<Pair<String, String>> 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,32 +168,13 @@ final class TermuxInstaller {
|
||||
}
|
||||
}
|
||||
|
||||
/** Get bootstrap zip url for this systems cpu architecture. */
|
||||
private static URL determineZipUrl() throws MalformedURLException {
|
||||
String archName = determineTermuxArchName();
|
||||
String url = "https://termux.net/bootstrap/new/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 {
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<full-backup-content>
|
||||
<!-- See https://developer.android.com/training/backup/autosyncapi.html -->
|
||||
<include domain="file" path="home/backup" />
|
||||
</full-backup-content>
|
||||
+1
-1
@@ -4,7 +4,7 @@ buildscript {
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.5.2'
|
||||
classpath 'com.android.tools.build:gradle:3.5.1'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+2
-1
@@ -1,5 +1,6 @@
|
||||
#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
|
||||
|
||||
@@ -1,21 +1,5 @@
|
||||
#!/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
|
||||
@@ -44,7 +28,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" "-Xms64m"'
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
@@ -125,8 +109,8 @@ if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
Vendored
+1
-17
@@ -1,19 +1,3 @@
|
||||
@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
|
||||
@@ -30,7 +14,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" "-Xms64m"
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
@@ -1376,10 +1376,10 @@ public final class TerminalEmulator {
|
||||
}
|
||||
break;
|
||||
case 'A': // "CSI${n}A" - Cursor up (CUU) ${n} rows.
|
||||
setCursorRow(Math.max(0, mCursorRow - getArg0(1)));
|
||||
setCursorRow(Math.max(mTopMargin, mCursorRow - getArg0(1)));
|
||||
break;
|
||||
case 'B': // "CSI${n}B" - Cursor down (CUD) ${n} rows.
|
||||
setCursorRow(Math.min(mRows - 1, mCursorRow + getArg0(1)));
|
||||
setCursorRow(Math.min(mBottomMargin - 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.
|
||||
|
||||
@@ -133,6 +133,8 @@ 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() {
|
||||
@@ -141,6 +143,8 @@ 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() {
|
||||
|
||||
@@ -107,24 +107,4 @@ 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"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user