Compare commits
54 Commits
v0.119.0-b
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 6e1995ce69 | |||
| a749a89431 | |||
|
|
3df69d1da1 | ||
|
|
401bbe54b8 | ||
|
|
30ebb2dee3 | ||
|
|
3f0dec3574 | ||
|
|
ef4775b651 | ||
|
|
e50ac9e019 | ||
|
|
5f578d56fc | ||
|
|
1937595c57 | ||
|
|
a1457d1254 | ||
|
|
2ad6112803 | ||
|
|
93c99973cc | ||
|
|
7d87ed7629 | ||
|
|
53f75a8da3 | ||
|
|
bcb61c387c | ||
|
|
c6ec1cb64f | ||
|
|
382c9708b3 | ||
|
|
edd44d2ce9 | ||
|
|
4183ffa122 | ||
|
|
8aca6dbbf4 | ||
|
|
1812a405ee | ||
|
|
338fb66e5c | ||
|
|
f852dca0ef | ||
|
|
8174a995ae | ||
|
|
7bceab88e2 | ||
|
|
2c1bd650b0 | ||
|
|
4a9ad910dd | ||
|
|
9d2ac14557 | ||
|
|
c6e2ca7fde | ||
|
|
da3a0ac4e2 | ||
|
|
4de0caac5b | ||
|
|
bc321d0a7c | ||
|
|
9ee1c9d5ad | ||
|
|
a988383e01 | ||
|
|
d2cd6ac2e5 | ||
|
|
52da00e5c2 | ||
|
|
b0e1dbc3da | ||
|
|
078eea2b74 | ||
|
|
fb01127ff2 | ||
|
|
b84dc703e8 | ||
|
|
8be53336c3 | ||
|
|
d90e3fbca1 | ||
|
|
245158ceb9 | ||
|
|
6c00f1fc61 | ||
|
|
03142590ff | ||
|
|
4443b657bf | ||
|
|
a8a69c6d80 | ||
|
|
438cd73fff | ||
|
|
36d811ea7d | ||
|
|
c2d57f2ed8 | ||
|
|
661c37501f | ||
|
|
f80b46487d | ||
|
|
a5de3d3a2b |
3
.github/ISSUE_TEMPLATE/01-bug-report.yml
vendored
3
.github/ISSUE_TEMPLATE/01-bug-report.yml
vendored
@@ -6,7 +6,8 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
This is a bug tracker of the Termux app. If you have issues with a package inside the app, then please open an issue at [termux-packages](https://github.com/termux/termux-packages) instead.
|
||||
**THIS IS A BUG TRACKER OF THE TERMUX APP. IF YOU HAVE ISSUES WITH A PACKAGE INSIDE THE APP, THEN PLEASE OPEN AN ISSUE AT [TERMUX/TERMUX-PACKAGES](https://github.com/termux/termux-packages) INSTEAD.**
|
||||
If you are unsure if this is a bug with the Termux app itself or any of the packages, kindly open up the bug at [termux/termux-packages](https://github.com/termux/termux-packages) as it's very likely that it belongs there.
|
||||
|
||||
Use search before you open an issue to check whether your issue has been already reported and perhaps solved.
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ env.GITHUB_REF }}
|
||||
|
||||
@@ -71,13 +71,14 @@ jobs:
|
||||
fi
|
||||
|
||||
echo "Attaching APKs to github release"
|
||||
if ! gh release upload "$RELEASE_VERSION_NAME" \
|
||||
"$APK_DIR_PATH/${APK_BASENAME_PREFIX}_universal.apk" \
|
||||
"$APK_DIR_PATH/${APK_BASENAME_PREFIX}_arm64-v8a.apk" \
|
||||
"$APK_DIR_PATH/${APK_BASENAME_PREFIX}_armeabi-v7a.apk" \
|
||||
"$APK_DIR_PATH/${APK_BASENAME_PREFIX}_x86_64.apk" \
|
||||
"$APK_DIR_PATH/${APK_BASENAME_PREFIX}_x86.apk" \
|
||||
"$APK_DIR_PATH/${APK_BASENAME_PREFIX}_sha256sums" \
|
||||
; then
|
||||
if ! hub release edit \
|
||||
-m "" \
|
||||
-a "$APK_DIR_PATH/${APK_BASENAME_PREFIX}_universal.apk" \
|
||||
-a "$APK_DIR_PATH/${APK_BASENAME_PREFIX}_arm64-v8a.apk" \
|
||||
-a "$APK_DIR_PATH/${APK_BASENAME_PREFIX}_armeabi-v7a.apk" \
|
||||
-a "$APK_DIR_PATH/${APK_BASENAME_PREFIX}_x86_64.apk" \
|
||||
-a "$APK_DIR_PATH/${APK_BASENAME_PREFIX}_x86.apk" \
|
||||
-a "$APK_DIR_PATH/${APK_BASENAME_PREFIX}_sha256sums" \
|
||||
"$RELEASE_VERSION_NAME"; then
|
||||
exit_on_error "Attach APKs to release failed for '$APK_VERSION_TAG' release."
|
||||
fi
|
||||
|
||||
20
.github/workflows/debug_build.yml
vendored
20
.github/workflows/debug_build.yml
vendored
@@ -19,7 +19,13 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup java 17
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
|
||||
- name: Build APKs
|
||||
shell: bash {0}
|
||||
@@ -79,7 +85,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Attach universal APK file
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ env.APK_BASENAME_PREFIX }}_universal
|
||||
path: |
|
||||
@@ -87,7 +93,7 @@ jobs:
|
||||
${{ env.APK_DIR_PATH }}/output-metadata.json
|
||||
|
||||
- name: Attach arm64-v8a APK file
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ env.APK_BASENAME_PREFIX }}_arm64-v8a
|
||||
path: |
|
||||
@@ -95,7 +101,7 @@ jobs:
|
||||
${{ env.APK_DIR_PATH }}/output-metadata.json
|
||||
|
||||
- name: Attach armeabi-v7a APK file
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ env.APK_BASENAME_PREFIX }}_armeabi-v7a
|
||||
path: |
|
||||
@@ -103,7 +109,7 @@ jobs:
|
||||
${{ env.APK_DIR_PATH }}/output-metadata.json
|
||||
|
||||
- name: Attach x86_64 APK file
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ env.APK_BASENAME_PREFIX }}_x86_64
|
||||
path: |
|
||||
@@ -111,7 +117,7 @@ jobs:
|
||||
${{ env.APK_DIR_PATH }}/output-metadata.json
|
||||
|
||||
- name: Attach x86 APK file
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ env.APK_BASENAME_PREFIX }}_x86
|
||||
path: |
|
||||
@@ -119,7 +125,7 @@ jobs:
|
||||
${{ env.APK_DIR_PATH }}/output-metadata.json
|
||||
|
||||
- name: Attach sha256sums file
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ env.APK_BASENAME_PREFIX }}_sha256sums
|
||||
path: |
|
||||
|
||||
23
.github/workflows/dependency-submission.yml
vendored
Normal file
23
.github/workflows/dependency-submission.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: Automatic Dependency Submission
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ 'master' ]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
dependency-submission:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v6
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
- name: Generate and submit dependency graph
|
||||
uses: gradle/actions/dependency-submission@v5
|
||||
@@ -15,5 +15,5 @@ jobs:
|
||||
name: "Validation"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: gradle/wrapper-validation-action@v3
|
||||
- uses: actions/checkout@v6
|
||||
- uses: gradle/actions/wrapper-validation@5
|
||||
|
||||
7
.github/workflows/run_tests.yml
vendored
7
.github/workflows/run_tests.yml
vendored
@@ -15,7 +15,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
- name: Setup java 17
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
- name: Execute tests
|
||||
run: |
|
||||
./gradlew test
|
||||
|
||||
30
README.md
30
README.md
@@ -7,7 +7,7 @@
|
||||
[](https://jitpack.io/#termux/termux-app)
|
||||
|
||||
|
||||
[Termux](https://termux.com) is an Android terminal application and Linux environment.
|
||||
[Termux](https://termux.dev) is an Android terminal application and Linux environment.
|
||||
|
||||
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).
|
||||
|
||||
@@ -29,6 +29,7 @@ Quick how-to about Termux package management is available at [Package Management
|
||||
- [Debugging](#debugging)
|
||||
- [For Maintainers and Contributors](#for-maintainers-and-contributors)
|
||||
- [Forking](#forking)
|
||||
- [Sponsors and Funders](#sponsors-and-funders)
|
||||
##
|
||||
|
||||
|
||||
@@ -49,7 +50,7 @@ The core [Termux](https://github.com/termux/termux-app) app comes with the follo
|
||||
|
||||
## Installation
|
||||
|
||||
Latest version is `v0.118.1`.
|
||||
Latest version is `v0.118.3`.
|
||||
|
||||
**NOTICE: It is highly recommended that you update to `v0.118.0` or higher ASAP for various bug fixes, including a critical world-readable vulnerability reported [here](https://termux.github.io/general/2022/02/15/termux-apps-vulnerability-disclosures.html). See [below](#google-play-store-experimental-branch) for information regarding Termux on Google Play.**
|
||||
|
||||
@@ -59,7 +60,7 @@ Support for both app and packages was dropped for Android `5` and `6` on [2020-0
|
||||
|
||||
The APK files of different sources are signed with different signature keys. The `Termux` app and all its plugins use the same [`sharedUserId`](https://developer.android.com/guide/topics/manifest/manifest-element) `com.termux` and so all their APKs installed on a device must have been signed with the same signature key to work together and so they must all be installed from the same source. Do not attempt to mix them together, i.e do not try to install an app or plugin from `F-Droid` and another one from a different source like `GitHub`. Android Package Manager will also normally not allow installation of APKs with different signatures and you will get errors on installation like `App not installed`, `Failed to install due to an unknown error`, `INSTALL_FAILED_UPDATE_INCOMPATIBLE`, `INSTALL_FAILED_SHARED_USER_INCOMPATIBLE`, `signatures do not match previously installed version`, etc. This restriction can be bypassed with root or with custom roms.
|
||||
|
||||
If you wish to install from a different source, then you must **uninstall any and all existing Termux or its plugin app APKs** from your device first, then install all new APKs from the same new source. Check [Uninstallation](#uninstallation) section for details. You may also want to consider [Backing up Termux](https://wiki.termux.com/wiki/Backing_up_Termux) before the uninstallation so that you can restore it after re-installing from Termux different source.
|
||||
If you wish to install from a different source, then you must **uninstall any and all existing Termux or its plugin app APKs** from your device first, then install all new APKs from the same new source. Check [Uninstallation](#uninstallation) section for details. You may also want to consider [Backing up Termux](https://wiki.termux.dev/wiki/Backing_up_Termux) before the uninstallation so that you can restore it after re-installing from Termux different source.
|
||||
|
||||
In the following paragraphs, *"bootstrap"* refers to the minimal packages that are shipped with the `termux-app` itself to start a working shell environment. Its zips are built and released [here](https://github.com/termux/termux-packages/releases).
|
||||
|
||||
@@ -264,3 +265,26 @@ Commit messages **must** use the [Conventional Commits](https://www.conventional
|
||||
- You also need to recompile bootstrap zip for the new package name. Check [building bootstrap](https://github.com/termux/termux-packages/wiki/For-maintainers#build-bootstrap-archives), [here](https://github.com/termux/termux-app/issues/1983) and [here](https://github.com/termux/termux-app/issues/2081#issuecomment-865280111).
|
||||
- Currently, not all plugins use `TermuxConstants` from `termux-shared` library and have hardcoded `com.termux` values and will need to be manually patched.
|
||||
- If forking termux plugins, check [Forking and Local Development](https://github.com/termux/termux-app/wiki/Termux-Libraries#forking-and-local-development) for info on how to use termux libraries for plugins.
|
||||
##
|
||||
|
||||
|
||||
|
||||
## Sponsors and Funders
|
||||
|
||||
[<img alt="GitHub Accelerator" width="25%" src="site/assets/sponsors/github.png" />](https://github.com)
|
||||
*[GitHub Accelerator](https://github.com/accelerator) ([1](https://github.blog/2023-04-12-github-accelerator-our-first-cohort-and-whats-next))*
|
||||
|
||||
|
||||
|
||||
[<img alt="GitHub Secure Open Source Fund" width="25%" src="site/assets/sponsors/github.png" />](https://github.com)
|
||||
*[GitHub Secure Open Source Fund](https://resources.github.com/github-secure-open-source-fund) ([1](https://github.blog/open-source/maintainers/securing-the-supply-chain-at-scale-starting-with-71-important-open-source-projects), [2](https://termux.dev/en/posts/general/2025/08/11/termux-selected-for-github-secure-open-source-fund-session-2.html))*
|
||||
|
||||
|
||||
|
||||
[<img alt="NLnet NGI Mobifree" width="25%" src="site/assets/sponsors/nlnet-ngi-mobifree.png" />](https://nlnet.nl/mobifree)
|
||||
*[NLnet NGI Mobifree](https://nlnet.nl/mobifree) ([1](https://nlnet.nl/news/2024/20241111-NGI-Mobifree-grants.html), [2](https://termux.dev/en/posts/general/2024/11/11/termux-selected-for-nlnet-ngi-mobifree-grant.html))*
|
||||
|
||||
|
||||
|
||||
[<img alt="Cloudflare" width="25%" src="site/assets/sponsors/cloudflare.png" />](https://www.cloudflare.com)
|
||||
*[Cloudflare](https://www.cloudflare.com) ([1](https://packages-cf.termux.dev))*
|
||||
|
||||
1
SECURITY.md
Normal file
1
SECURITY.md
Normal file
@@ -0,0 +1 @@
|
||||
Check https://termux.dev/security for info on Termux security policies and how to report vulnerabilities.
|
||||
@@ -10,16 +10,11 @@ ext {
|
||||
// by replacing $PREFIX since app code is dependant on the variant used to build the APK.
|
||||
// Currently supported values are: [ "apt-android-7" "apt-android-5" ]
|
||||
packageVariant = System.getenv("TERMUX_PACKAGE_VARIANT") ?: "apt-android-7" // Default: "apt-android-7"
|
||||
bootstrapMinSdk = packageVariant == "apt-android-5" ? 21 : 24
|
||||
bootstrapMinRelease = packageVariant == "apt-android-5" ? "5.0" : "7.0"
|
||||
bootstrapMaxSdk = packageVariant == "apt-android-5" ? 23 : null
|
||||
bootstrapMaxRelease = packageVariant == "apt-android-5" ? "6.0" : null
|
||||
|
||||
buildMinSdk = bootstrapMinSdk
|
||||
buildTargetSdk = project.properties.targetSdkVersion.toInteger()
|
||||
}
|
||||
|
||||
android {
|
||||
namespace "com.termux"
|
||||
|
||||
compileSdkVersion project.properties.compileSdkVersion.toInteger()
|
||||
ndkVersion = System.getenv("JITPACK_NDK_VERSION") ?: project.properties.ndkVersion
|
||||
def appVersionName = System.getenv("TERMUX_APP_VERSION_NAME") ?: ""
|
||||
@@ -28,38 +23,32 @@ android {
|
||||
def splitAPKsForReleaseBuilds = System.getenv("TERMUX_SPLIT_APKS_FOR_RELEASE_BUILDS") ?: "0" // F-Droid does not support split APKs #1904
|
||||
|
||||
dependencies {
|
||||
implementation "androidx.annotation:annotation:1.3.0"
|
||||
implementation "androidx.core:core:1.6.0"
|
||||
implementation "androidx.drawerlayout:drawerlayout:1.1.1"
|
||||
implementation "androidx.preference:preference:1.1.1"
|
||||
implementation "androidx.annotation:annotation:1.9.0"
|
||||
implementation "androidx.core:core:1.13.1"
|
||||
implementation "androidx.drawerlayout:drawerlayout:1.2.0"
|
||||
implementation "androidx.preference:preference:1.2.1"
|
||||
implementation "androidx.viewpager:viewpager:1.0.0"
|
||||
implementation "com.google.android.material:material:1.4.0"
|
||||
implementation "com.google.android.material:material:1.12.0"
|
||||
implementation "com.google.guava:guava:24.1-jre"
|
||||
implementation "io.noties.markwon:core:$markwonVersion"
|
||||
implementation "io.noties.markwon:ext-strikethrough:$markwonVersion"
|
||||
implementation "io.noties.markwon:linkify:$markwonVersion"
|
||||
implementation "io.noties.markwon:recycler:$markwonVersion"
|
||||
implementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'
|
||||
|
||||
implementation project(":terminal-view")
|
||||
implementation project(":termux-shared")
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.termux"
|
||||
minSdk buildMinSdk
|
||||
targetSdk buildTargetSdk
|
||||
versionCode 1021
|
||||
versionName "0.119.0-beta.2"
|
||||
minSdkVersion project.properties.minSdkVersion.toInteger()
|
||||
targetSdkVersion project.properties.targetSdkVersion.toInteger()
|
||||
versionCode 1024
|
||||
versionName "0.119.0-titan2.2"
|
||||
|
||||
if (appVersionName) versionName = appVersionName
|
||||
validateVersionName(versionName)
|
||||
|
||||
buildConfigField "Integer", "TERMUX_APP__BOOTSTRAP_MIN_SDK", project.ext.bootstrapMinSdk.toString()
|
||||
buildConfigField "String", "TERMUX_APP__BOOTSTRAP_MIN_RELEASE",
|
||||
project.ext.bootstrapMinRelease ? "\"" + project.ext.bootstrapMinRelease + "\"" : "null"
|
||||
buildConfigField "Integer", "TERMUX_APP__BOOTSTRAP_MAX_SDK", project.ext.bootstrapMaxSdk.toString()
|
||||
buildConfigField "String", "TERMUX_APP__BOOTSTRAP_MAX_RELEASE",
|
||||
project.ext.bootstrapMaxRelease ? "\"" + project.ext.bootstrapMaxRelease + "\"" : "null"
|
||||
buildConfigField "String", "TERMUX_PACKAGE_VARIANT", "\"" + project.ext.packageVariant + "\"" // Used by TermuxApplication class
|
||||
|
||||
manifestPlaceholders.TERMUX_PACKAGE_NAME = "com.termux"
|
||||
@@ -123,7 +112,7 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
lint {
|
||||
disable 'ProtectedPermissions'
|
||||
}
|
||||
|
||||
@@ -151,6 +140,9 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
buildConfig true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -227,17 +219,17 @@ task downloadBootstraps() {
|
||||
doLast {
|
||||
def packageVariant = project.ext.packageVariant
|
||||
if (packageVariant == "apt-android-7") {
|
||||
def version = "2025.03.28-r1" + "+" + packageVariant
|
||||
downloadBootstrap("aarch64", "c8d702b6f742935001c37cda81b8ac69504a95d5cf28f2899532dd8cd4b057eb", version)
|
||||
downloadBootstrap("arm", "f3bb9d1b32552b34fff41861dbf193ec5ba2848d67d779ac1c7256da6640f85d", version)
|
||||
downloadBootstrap("i686", "36db3e1ac3547f9a174fd763bd9a484fa1a3449cdd81e1cf2408ff0454f839c6", version)
|
||||
downloadBootstrap("x86_64", "1c124ec2396ee70a51b0b0a574f29aa659526aa2b9f558f993b2fb05d1e51855", version)
|
||||
def version = "2026.02.12-r1" + "%2B" + "apt.android-7"
|
||||
downloadBootstrap("aarch64", "ea2aeba8819e517db711f8c32369e89e7c52cee73e07930ff91185e1ab93f4f3", version)
|
||||
downloadBootstrap("arm", "a38f4d3b2f735f83be2bf54eff463e86dc32a3e2f9f861c1557c4378d249c018", version)
|
||||
downloadBootstrap("i686", "f5bc0b025b9f3b420b5fcaeefc064f888f5f22a0d6fd7090f4aac0c33eb3555b", version)
|
||||
downloadBootstrap("x86_64", "b7fd0f2e3a4de534be3144f9f91acc768630fc463eaf134ab2e64c545e834f7a", version)
|
||||
} else if (packageVariant == "apt-android-5") {
|
||||
def version = "2025.03.28-r3" + "+" + packageVariant
|
||||
downloadBootstrap("aarch64", "147c98e610a30588665a89776314833c293006b12c70e65dcde6eb54c2344113", version)
|
||||
downloadBootstrap("arm", "363c28dd4b70c995302498beae79fb5917af7d3b0ca9fbd9da7de96ab64c6122", version)
|
||||
downloadBootstrap("i686", "a2e742381ab24cf8c9a78ae4e2425ee44d8fb9625a2b0ef63e4cd32e292f7186", version)
|
||||
downloadBootstrap("x86_64", "4f6866c222b0f1ae1b180220ffc6e8e1afbc10b9cbb7a462c062c490eda90044", version)
|
||||
def version = "2022.04.28-r6" + "+" + packageVariant
|
||||
downloadBootstrap("aarch64", "913609d439415c828c5640be1b0561467e539cb1c7080662decaaca2fb4820e7", version)
|
||||
downloadBootstrap("arm", "26bfb45304c946170db69108e5eb6e3641aad751406ce106c80df80cad2eccf8", version)
|
||||
downloadBootstrap("i686", "46dcfeb5eef67ba765498db9fe4c50dc4690805139aa0dd141a9d8ee0693cd27", version)
|
||||
downloadBootstrap("x86_64", "615b590679ee6cd885b7fd2ff9473c845e920f9b422f790bb158c63fe42b8481", version)
|
||||
} else {
|
||||
throw new GradleException("Unsupported TERMUX_PACKAGE_VARIANT \"" + packageVariant + "\"")
|
||||
}
|
||||
|
||||
5
app/proguard-rules.pro
vendored
5
app/proguard-rules.pro
vendored
@@ -10,8 +10,3 @@
|
||||
-dontobfuscate
|
||||
#-renamesourcefileattribute SourceFile
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# Temp fix for androidx.window:window:1.0.0-alpha09 imported by termux-shared
|
||||
# https://issuetracker.google.com/issues/189001730
|
||||
# https://android-review.googlesource.com/c/platform/frameworks/support/+/1757630
|
||||
-keep class androidx.window.** { *; }
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.termux"
|
||||
android:installLocation="internalOnly"
|
||||
android:sharedUserId="${TERMUX_PACKAGE_NAME}"
|
||||
android:sharedUserLabel="@string/shared_user_label">
|
||||
@@ -42,7 +41,6 @@
|
||||
android:name=".app.TermuxApplication"
|
||||
android:allowBackup="false"
|
||||
android:banner="@drawable/banner"
|
||||
android:extractNativeLibs="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/application_name"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
|
||||
@@ -774,7 +774,7 @@ public final class TermuxActivity extends AppCompatActivity implements ServiceCo
|
||||
|
||||
// If permission is granted, then also setup storage symlinks.
|
||||
if(PermissionUtils.checkAndRequestLegacyOrManageExternalStoragePermission(
|
||||
TermuxActivity.this, requestCode, true, !isPermissionCallback)) {
|
||||
TermuxActivity.this, requestCode, !isPermissionCallback)) {
|
||||
if (isPermissionCallback)
|
||||
Logger.logInfoAndShowToast(TermuxActivity.this, LOG_TAG,
|
||||
getString(com.termux.shared.R.string.msg_storage_permission_granted_on_request));
|
||||
|
||||
@@ -52,7 +52,7 @@ public class TermuxApplication extends Application {
|
||||
boolean isTermuxFilesDirectoryAccessible = error == null;
|
||||
if (isTermuxFilesDirectoryAccessible) {
|
||||
Logger.logInfo(LOG_TAG, "Termux files directory is accessible");
|
||||
/*
|
||||
|
||||
error = TermuxFileUtils.isAppsTermuxAppDirectoryAccessible(true, true);
|
||||
if (error != null) {
|
||||
Logger.logErrorExtended(LOG_TAG, "Create apps/termux-app directory failed\n" + error);
|
||||
@@ -61,7 +61,6 @@ public class TermuxApplication extends Application {
|
||||
|
||||
// Setup termux-am-socket server
|
||||
TermuxAmSocketServer.setupTermuxAmSocketServer(context);
|
||||
*/
|
||||
} else {
|
||||
Logger.logErrorExtended(LOG_TAG, "Termux files directory is not accessible\n" + error);
|
||||
}
|
||||
|
||||
@@ -10,12 +10,8 @@ import android.system.Os;
|
||||
import android.util.Pair;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.termux.BuildConfig;
|
||||
import com.termux.R;
|
||||
import com.termux.shared.file.FileUtils;
|
||||
import com.termux.shared.shell.command.ExecutionCommand;
|
||||
import com.termux.shared.shell.command.runner.app.AppShell;
|
||||
import com.termux.shared.termux.TermuxBootstrap;
|
||||
import com.termux.shared.termux.crash.TermuxCrashUtils;
|
||||
import com.termux.shared.termux.file.TermuxFileUtils;
|
||||
import com.termux.shared.interact.MessageDialogUtils;
|
||||
@@ -106,12 +102,6 @@ final class TermuxInstaller {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!checkIfMinOrMaxSdkVersionIsIncompatible(activity,
|
||||
BuildConfig.TERMUX_APP__BOOTSTRAP_MIN_SDK, BuildConfig.TERMUX_APP__BOOTSTRAP_MIN_RELEASE,
|
||||
BuildConfig.TERMUX_APP__BOOTSTRAP_MAX_SDK, BuildConfig.TERMUX_APP__BOOTSTRAP_MAX_RELEASE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If prefix directory exists, even if its a symlink to a valid directory and symlink is not broken/dangling
|
||||
if (FileUtils.directoryFileExists(TERMUX_PREFIX_DIR_PATH, true)) {
|
||||
if (TermuxFileUtils.isTermuxPrefixDirectoryEmpty()) {
|
||||
@@ -205,8 +195,7 @@ final class TermuxInstaller {
|
||||
outStream.write(buffer, 0, readBytes);
|
||||
}
|
||||
if (zipEntryName.startsWith("bin/") || zipEntryName.startsWith("libexec") ||
|
||||
zipEntryName.startsWith("lib/apt/apt-helper") || zipEntryName.startsWith("lib/apt/methods") ||
|
||||
zipEntryName.equals("etc/termux/bootstrap/termux-bootstrap-second-stage.sh")) {
|
||||
zipEntryName.startsWith("lib/apt/apt-helper") || zipEntryName.startsWith("lib/apt/methods")) {
|
||||
//noinspection OctalInteger
|
||||
Os.chmod(targetFile.getAbsolutePath(), 0700);
|
||||
}
|
||||
@@ -227,35 +216,6 @@ final class TermuxInstaller {
|
||||
throw new RuntimeException("Moving termux prefix staging to prefix directory failed");
|
||||
}
|
||||
|
||||
// Run Termux bootstrap second stage.
|
||||
String termuxBootstrapSecondStageFile = TERMUX_PREFIX_DIR_PATH + "/etc/termux/bootstrap/termux-bootstrap-second-stage.sh";
|
||||
if (!FileUtils.fileExists(termuxBootstrapSecondStageFile, false)) {
|
||||
Logger.logInfo(LOG_TAG, "Not running Termux bootstrap second stage since script not found at \"" + termuxBootstrapSecondStageFile + "\" path.");
|
||||
} else {
|
||||
if (!FileUtils.fileExists(TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH + "/bash", true)) {
|
||||
Logger.logInfo(LOG_TAG, "Not running Termux bootstrap second stage since bash not found.");
|
||||
}
|
||||
Logger.logInfo(LOG_TAG, "Running Termux bootstrap second stage.");
|
||||
|
||||
ExecutionCommand executionCommand = new ExecutionCommand(-1,
|
||||
termuxBootstrapSecondStageFile, null, null,
|
||||
null, ExecutionCommand.Runner.APP_SHELL.getName(), false);
|
||||
executionCommand.commandLabel = "Termux Bootstrap Second Stage Command";
|
||||
executionCommand.backgroundCustomLogLevel = Logger.LOG_LEVEL_NORMAL;
|
||||
AppShell appShell = AppShell.execute(activity, executionCommand, null, new TermuxShellEnvironment(), null, true);
|
||||
if (appShell == null || !executionCommand.isSuccessful() || executionCommand.resultData.exitCode != 0) {
|
||||
// Generate debug report before deleting broken prefix directory to get `stat` info at time of failure.
|
||||
showBootstrapErrorDialog(activity, whenDone, MarkdownUtils.getMarkdownCodeForString(executionCommand.toString(), true));
|
||||
|
||||
// Delete prefix directory as otherwise when app is restarted, the broken prefix directory would be used and logged into.
|
||||
Logger.logInfo(LOG_TAG, "Deleting broken termux prefix.");
|
||||
error = FileUtils.deleteFile("termux prefix directory", TERMUX_PREFIX_DIR_PATH, true);
|
||||
if (error != null)
|
||||
Logger.logErrorExtended(LOG_TAG, error.toString());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.logInfo(LOG_TAG, "Bootstrap packages installed successfully.");
|
||||
|
||||
// Recreate env file since termux prefix was wiped earlier
|
||||
@@ -279,42 +239,6 @@ final class TermuxInstaller {
|
||||
}.start();
|
||||
}
|
||||
|
||||
public static boolean checkIfMinOrMaxSdkVersionIsIncompatible(Activity activity,
|
||||
Integer minSdk, String minRelease,
|
||||
Integer maxSdk, String maxRelease) {
|
||||
if (minSdk != null && Build.VERSION.SDK_INT < minSdk) {
|
||||
String bootstrapErrorMessage = activity.getString(R.string.bootstrap_error_apk_bootstrap_variant_min_sdk_incompatible,
|
||||
MarkdownUtils.getMarkdownCodeForString(TermuxBootstrap.TERMUX_APP_PACKAGE_VARIANT.getName(), false),
|
||||
MarkdownUtils.getMarkdownCodeForString(Build.VERSION.RELEASE, false),
|
||||
Build.VERSION.SDK_INT,
|
||||
MarkdownUtils.getMarkdownCodeForString(minRelease, false),
|
||||
minSdk);
|
||||
Logger.logError(LOG_TAG, bootstrapErrorMessage);
|
||||
sendBootstrapCrashReportNotification(activity, bootstrapErrorMessage);
|
||||
MessageDialogUtils.exitAppWithErrorMessage(activity,
|
||||
activity.getString(R.string.bootstrap_error_title),
|
||||
bootstrapErrorMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (maxSdk != null && Build.VERSION.SDK_INT > maxSdk) {
|
||||
String bootstrapErrorMessage = activity.getString(R.string.bootstrap_error_apk_bootstrap_variant_max_sdk_incompatible,
|
||||
MarkdownUtils.getMarkdownCodeForString(TermuxBootstrap.TERMUX_APP_PACKAGE_VARIANT.getName(), false),
|
||||
MarkdownUtils.getMarkdownCodeForString(Build.VERSION.RELEASE, false),
|
||||
Build.VERSION.SDK_INT,
|
||||
MarkdownUtils.getMarkdownCodeForString(maxRelease, false),
|
||||
maxSdk);
|
||||
Logger.logError(LOG_TAG, bootstrapErrorMessage);
|
||||
sendBootstrapCrashReportNotification(activity, bootstrapErrorMessage);
|
||||
MessageDialogUtils.exitAppWithErrorMessage(activity,
|
||||
activity.getString(R.string.bootstrap_error_title),
|
||||
bootstrapErrorMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void showBootstrapErrorDialog(Activity activity, Runnable whenDone, String message) {
|
||||
Logger.logErrorExtended(LOG_TAG, "Bootstrap Error:\n" + message);
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@ public final class HelpActivity extends AppCompatActivity {
|
||||
mWebView = new WebView(this);
|
||||
WebSettings settings = mWebView.getSettings();
|
||||
settings.setCacheMode(WebSettings.LOAD_NO_CACHE);
|
||||
settings.setAppCacheEnabled(false);
|
||||
setContentView(progressLayout);
|
||||
mWebView.clearCache(true);
|
||||
|
||||
|
||||
@@ -271,7 +271,7 @@ public class TermuxTerminalSessionActivityClient extends TermuxTerminalSessionCl
|
||||
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION).build()).build();
|
||||
|
||||
try {
|
||||
mBellSoundId = mBellSoundPool.load(mActivity, R.raw.bell, 1);
|
||||
mBellSoundId = mBellSoundPool.load(mActivity, com.termux.shared.R.raw.bell, 1);
|
||||
} catch (Exception e){
|
||||
// Catch java.lang.RuntimeException: Unable to resume activity {com.termux/com.termux.app.TermuxActivity}: android.content.res.Resources$NotFoundException: File res/raw/bell.ogg from drawable resource ID
|
||||
Logger.logStackTraceWithMessage(LOG_TAG, "Failed to load bell sound pool", e);
|
||||
|
||||
@@ -735,8 +735,8 @@ public class TermuxTerminalViewClient extends TermuxTerminalViewClientBase {
|
||||
|
||||
MessageDialogUtils.showMessage(mActivity, TermuxConstants.TERMUX_APP_NAME + " Report Issue",
|
||||
mActivity.getString(R.string.msg_add_termux_debug_info),
|
||||
mActivity.getString(R.string.action_yes), (dialog, which) -> reportIssueFromTranscript(transcriptText, true),
|
||||
mActivity.getString(R.string.action_no), (dialog, which) -> reportIssueFromTranscript(transcriptText, false),
|
||||
mActivity.getString(com.termux.shared.R.string.action_yes), (dialog, which) -> reportIssueFromTranscript(transcriptText, true),
|
||||
mActivity.getString(com.termux.shared.R.string.action_no), (dialog, which) -> reportIssueFromTranscript(transcriptText, false),
|
||||
null);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,18 +27,10 @@
|
||||
|
||||
<!-- Termux Bootstrap Packages Installation -->
|
||||
<string name="bootstrap_installer_body">Installing bootstrap packages…</string>
|
||||
<string name="bootstrap_error_title">&TERMUX_APP_NAME; Bootstrap Error</string>
|
||||
<string name="bootstrap_error_title">Unable to install bootstrap</string>
|
||||
<string name="bootstrap_error_body">&TERMUX_APP_NAME; was unable to install the bootstrap packages.</string>
|
||||
<string name="bootstrap_error_abort">Abort</string>
|
||||
<string name="bootstrap_error_try_again">Try again</string>
|
||||
<string name="bootstrap_error_apk_bootstrap_variant_min_sdk_incompatible">The APK bootstrap variant %1$s
|
||||
of currently installed &TERMUX_APP_NAME; app is not compatible with the Android version %2$s
|
||||
(sdk `%3$d`) of the device and it requires minimum Android version %4$s (sdk `%5$d`).
|
||||
\n\nUninstall the &TERMUX_APP_NAME; app and reinstall the correct APK build variant.</string>
|
||||
<string name="bootstrap_error_apk_bootstrap_variant_max_sdk_incompatible">The APK bootstrap variant %1$s
|
||||
of currently installed &TERMUX_APP_NAME; app is not compatible with the Android version %2$s
|
||||
(sdk `%3$d`) of the device and it requires maximum Android version %4$s (sdk `%5$d`).
|
||||
\n\nUninstall the &TERMUX_APP_NAME; app and reinstall the correct APK build variant.</string>
|
||||
<string name="bootstrap_error_not_primary_user_message">&TERMUX_APP_NAME; can only be run as the primary user.
|
||||
\nBootstrap binaries compiled for &TERMUX_APP_NAME; have hardcoded $PREFIX path and cannot be installed
|
||||
under any path other than:\n%1$s.</string>
|
||||
|
||||
@@ -4,7 +4,7 @@ buildscript {
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath "com.android.tools.build:gradle:4.2.2"
|
||||
classpath "com.android.tools.build:gradle:8.13.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,3 @@ allprojects {
|
||||
maven { url "https://jitpack.io" }
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
|
||||
@@ -12,18 +12,12 @@
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
org.gradle.jvmargs=-Xmx2048M \
|
||||
--add-exports=java.base/sun.nio.ch=ALL-UNNAMED \
|
||||
--add-opens=java.base/java.lang=ALL-UNNAMED \
|
||||
--add-opens=java.base/java.lang.reflect=ALL-UNNAMED \
|
||||
--add-opens=java.base/java.io=ALL-UNNAMED \
|
||||
--add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED
|
||||
org.gradle.jvmargs=-Xmx2048M
|
||||
android.useAndroidX=true
|
||||
|
||||
minSdkVersion=21
|
||||
targetSdkVersion=28
|
||||
ndkVersion=22.1.7171670
|
||||
compileSdkVersion=30
|
||||
ndkVersion=29.0.14206865
|
||||
compileSdkVersion=36
|
||||
|
||||
markwonVersion=4.6.2
|
||||
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
295
gradlew
vendored
295
gradlew
vendored
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env sh
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
# Copyright © 2015 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -15,81 +15,114 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
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"'
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
@@ -98,88 +131,118 @@ Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
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
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# 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"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
||||
40
gradlew.bat
vendored
40
gradlew.bat
vendored
@@ -13,8 +13,10 @@
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@@ -25,7 +27,8 @@
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
@@ -56,32 +59,33 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
jdk:
|
||||
- openjdk17
|
||||
env:
|
||||
JITPACK_NDK_VERSION: "21.1.6352462"
|
||||
JITPACK_NDK_VERSION: "29.0.14206865"
|
||||
|
||||
BIN
site/assets/sponsors/cloudflare.png
Normal file
BIN
site/assets/sponsors/cloudflare.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
BIN
site/assets/sponsors/github.png
Normal file
BIN
site/assets/sponsors/github.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
BIN
site/assets/sponsors/nlnet-ngi-mobifree.png
Normal file
BIN
site/assets/sponsors/nlnet-ngi-mobifree.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
@@ -2,6 +2,8 @@ apply plugin: 'com.android.library'
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
android {
|
||||
namespace "com.termux.emulator"
|
||||
|
||||
compileSdkVersion project.properties.compileSdkVersion.toInteger()
|
||||
ndkVersion = System.getenv("JITPACK_NDK_VERSION") ?: project.properties.ndkVersion
|
||||
|
||||
@@ -41,6 +43,14 @@ android {
|
||||
testOptions {
|
||||
unitTests.returnDefaultValues = true
|
||||
}
|
||||
|
||||
publishing {
|
||||
multipleVariants {
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
allVariants()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(Test) {
|
||||
@@ -50,13 +60,13 @@ tasks.withType(Test) {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "androidx.annotation:annotation:1.3.0"
|
||||
implementation "androidx.annotation:annotation:1.9.0"
|
||||
testImplementation "junit:junit:4.13.2"
|
||||
}
|
||||
|
||||
task sourceJar(type: Jar) {
|
||||
from android.sourceSets.main.java.srcDirs
|
||||
classifier "sources"
|
||||
archiveClassifier = "sources"
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
@@ -64,7 +74,7 @@ afterEvaluate {
|
||||
publications {
|
||||
// Creates a Maven publication called "release".
|
||||
release(MavenPublication) {
|
||||
from components.release
|
||||
from components.default
|
||||
groupId = 'com.termux'
|
||||
artifactId = 'terminal-emulator'
|
||||
version = '0.118.0'
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
<manifest package="com.termux.terminal">
|
||||
<manifest>
|
||||
</manifest>
|
||||
|
||||
@@ -120,8 +120,8 @@ public final class KeyHandler {
|
||||
TERMCAP_TO_KEYCODE.put("kDN", KEYMOD_SHIFT | KEYCODE_DPAD_DOWN); // non-standard shifted arrow down
|
||||
TERMCAP_TO_KEYCODE.put("kF", KEYMOD_SHIFT | KEYCODE_DPAD_DOWN); // terminfo=kind, scroll-forward key
|
||||
TERMCAP_TO_KEYCODE.put("kI", KEYCODE_INSERT);
|
||||
TERMCAP_TO_KEYCODE.put("kN", KEYCODE_PAGE_UP);
|
||||
TERMCAP_TO_KEYCODE.put("kP", KEYCODE_PAGE_DOWN);
|
||||
TERMCAP_TO_KEYCODE.put("kP", KEYCODE_PAGE_UP);
|
||||
TERMCAP_TO_KEYCODE.put("kN", KEYCODE_PAGE_DOWN);
|
||||
TERMCAP_TO_KEYCODE.put("kR", KEYMOD_SHIFT | KEYCODE_DPAD_UP); // terminfo=kri, scroll-backward key
|
||||
TERMCAP_TO_KEYCODE.put("kUP", KEYMOD_SHIFT | KEYCODE_DPAD_UP); // non-standard shifted up
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ public final class TerminalSession extends TerminalOutput {
|
||||
* A queue written to from a separate thread when the process outputs, and read by main thread to process by
|
||||
* terminal emulator.
|
||||
*/
|
||||
final ByteQueue mProcessToTerminalIOQueue = new ByteQueue(4096);
|
||||
final ByteQueue mProcessToTerminalIOQueue = new ByteQueue(64 * 1024);
|
||||
/**
|
||||
* A queue written to from the main thread due to user interaction, and read by another thread which forwards by
|
||||
* writing to the {@link #mTerminalFileDescriptor}.
|
||||
@@ -336,7 +336,7 @@ public final class TerminalSession extends TerminalOutput {
|
||||
@SuppressLint("HandlerLeak")
|
||||
class MainThreadHandler extends Handler {
|
||||
|
||||
final byte[] mReceiveBuffer = new byte[4 * 1024];
|
||||
final byte[] mReceiveBuffer = new byte[64 * 1024];
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
|
||||
@@ -2,10 +2,11 @@ apply plugin: 'com.android.library'
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
android {
|
||||
namespace "com.termux.view"
|
||||
compileSdkVersion project.properties.compileSdkVersion.toInteger()
|
||||
|
||||
dependencies {
|
||||
implementation "androidx.annotation:annotation:1.3.0"
|
||||
implementation "androidx.annotation:annotation:1.9.0"
|
||||
api project(":terminal-emulator")
|
||||
}
|
||||
|
||||
@@ -26,6 +27,14 @@ android {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
publishing {
|
||||
multipleVariants {
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
allVariants()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -34,7 +43,7 @@ dependencies {
|
||||
|
||||
task sourceJar(type: Jar) {
|
||||
from android.sourceSets.main.java.srcDirs
|
||||
classifier "sources"
|
||||
archiveClassifier = "sources"
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
@@ -42,7 +51,7 @@ afterEvaluate {
|
||||
publications {
|
||||
// Creates a Maven publication called "release".
|
||||
release(MavenPublication) {
|
||||
from components.release
|
||||
from components.default
|
||||
groupId = 'com.termux'
|
||||
artifactId = 'terminal-view'
|
||||
version = '0.118.0'
|
||||
@@ -51,3 +60,4 @@ afterEvaluate {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
<manifest package="com.termux.view">
|
||||
<manifest>
|
||||
</manifest>
|
||||
|
||||
@@ -68,6 +68,15 @@ public final class TerminalView extends View {
|
||||
|
||||
/** The top row of text to display. Ranges from -activeTranscriptRows to 0. */
|
||||
int mTopRow;
|
||||
|
||||
/** TITAN2 PATCH: user scrolled away from the bottom; hold the viewport until they return. */
|
||||
boolean mUserScrollLock;
|
||||
|
||||
/** TITAN2 PATCH: latched hardware Shift/Alt state. The Titan 2 keyboard layer re-injects
|
||||
* synthetic key events (FLAG_FROM_SYSTEM) whose letter events often arrive without meta
|
||||
* state, so the modifier state is tracked from the modifier key events themselves. */
|
||||
boolean mHardwareShiftDown;
|
||||
boolean mHardwareAltDown;
|
||||
int[] mDefaultSelectors = new int[]{-1,-1,-1,-1};
|
||||
|
||||
float mScaleFactor = 1.f;
|
||||
@@ -290,6 +299,20 @@ public final class TerminalView extends View {
|
||||
public boolean attachSession(TerminalSession session) {
|
||||
if (session == mTermSession) return false;
|
||||
mTopRow = 0;
|
||||
mUserScrollLock = false; // TITAN2 PATCH
|
||||
mHardwareShiftDown = false; // TITAN2 PATCH
|
||||
mHardwareAltDown = false; // TITAN2 PATCH
|
||||
|
||||
// TITAN2 PATCH: discard the session's scroll-delta that accumulated while it was not
|
||||
// being viewed. Output keeps flowing in the background, but onTextChanged() skips the
|
||||
// screen update - and with it clearScrollCounter() - when the session is not current or
|
||||
// the activity is not visible. If that stale counter survived until the first
|
||||
// onScreenUpdated() after this attach, the viewport-hold logic below would treat all of
|
||||
// it as "rows appended just now" and pin the view thousands of lines deep into the
|
||||
// scrollback, stranding the user far from the live output (scrolling appears dead in
|
||||
// this session while other sessions behave normally). Start from the live bottom clean.
|
||||
TerminalEmulator emulator = session.getEmulator();
|
||||
if (emulator != null) emulator.clearScrollCounter();
|
||||
|
||||
mTermSession = session;
|
||||
mEmulator = null;
|
||||
@@ -479,6 +502,17 @@ public final class TerminalView extends View {
|
||||
mTopRow -= rowShift;
|
||||
decrementYTextSelectionCursors(rowShift);
|
||||
}
|
||||
} else if (mUserScrollLock && mTopRow != 0) {
|
||||
// TITAN2 PATCH: the user scrolled up, so hold the viewport instead of snapping
|
||||
// back to the bottom on every screen update (e.g. while an app streams output).
|
||||
// Keep the same content in view when new lines are appended.
|
||||
int rowShift = mEmulator.getScrollCounter();
|
||||
if (-mTopRow + rowShift > rowsInHistory) {
|
||||
mTopRow = -rowsInHistory;
|
||||
} else {
|
||||
mTopRow -= rowShift;
|
||||
}
|
||||
skipScrolling = true;
|
||||
}
|
||||
|
||||
if (!skipScrolling && mTopRow != 0) {
|
||||
@@ -583,6 +617,9 @@ public final class TerminalView extends View {
|
||||
handleKeyCode(up ? KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN, 0);
|
||||
} else {
|
||||
mTopRow = Math.min(0, Math.max(-(mEmulator.getScreen().getActiveTranscriptRows()), mTopRow + (up ? -1 : 1)));
|
||||
// TITAN2 PATCH: scrolling away from the bottom pauses auto-follow;
|
||||
// scrolling back to the bottom (mTopRow == 0) resumes it.
|
||||
mUserScrollLock = mTopRow != 0;
|
||||
if (!awakenScrollBars()) invalidate();
|
||||
}
|
||||
}
|
||||
@@ -770,6 +807,21 @@ public final class TerminalView extends View {
|
||||
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED)
|
||||
mClient.logInfo(LOG_TAG, "onKeyDown(keyCode=" + keyCode + ", isSystem()=" + event.isSystem() + ", event=" + event + ")");
|
||||
if (mEmulator == null) return true;
|
||||
|
||||
// TITAN2 PATCH: latch hardware Shift/Alt state from the modifier key events
|
||||
// themselves. The synthetic letter events injected by the Titan 2 keyboard layer
|
||||
// are flagged FLAG_FROM_SYSTEM and often arrive without meta state.
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_SHIFT_LEFT:
|
||||
case KeyEvent.KEYCODE_SHIFT_RIGHT:
|
||||
mHardwareShiftDown = true;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_ALT_LEFT:
|
||||
case KeyEvent.KEYCODE_ALT_RIGHT:
|
||||
mHardwareAltDown = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isSelectingText()) {
|
||||
stopTextSelectionMode();
|
||||
}
|
||||
@@ -777,17 +829,24 @@ public final class TerminalView extends View {
|
||||
if (mClient.onKeyDown(keyCode, event, mTermSession)) {
|
||||
invalidate();
|
||||
return true;
|
||||
} else if (event.isSystem() && (!mClient.shouldBackButtonBeMappedToEscape() || keyCode != KeyEvent.KEYCODE_BACK)) {
|
||||
} else if (event.isSystem() && isRealSystemKey(keyCode)
|
||||
&& (!mClient.shouldBackButtonBeMappedToEscape() || keyCode != KeyEvent.KEYCODE_BACK)) {
|
||||
// TITAN2 PATCH: only bubble *real* system keys (back, home, volume, ...). The
|
||||
// Titan 2 keyboard layer re-injects letter events with FLAG_FROM_SYSTEM, which
|
||||
// must reach the KCM path below instead of being dropped on the activity.
|
||||
return super.onKeyDown(keyCode, event);
|
||||
} else if (event.getAction() == KeyEvent.ACTION_MULTIPLE && keyCode == KeyEvent.KEYCODE_UNKNOWN) {
|
||||
mTermSession.write(event.getCharacters());
|
||||
return true;
|
||||
} else if (keyCode == KeyEvent.KEYCODE_LANGUAGE_SWITCH) {
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
final int metaState = event.getMetaState();
|
||||
final boolean controlDown = event.isCtrlPressed() || mClient.readControlKey();
|
||||
final boolean leftAltDown = (metaState & KeyEvent.META_ALT_LEFT_ON) != 0 || mClient.readAltKey();
|
||||
final boolean shiftDown = event.isShiftPressed() || mClient.readShiftKey();
|
||||
// TITAN2 PATCH: also use the latched hardware modifier state (injected events lack meta).
|
||||
final boolean leftAltDown = (metaState & KeyEvent.META_ALT_LEFT_ON) != 0 || mClient.readAltKey() || mHardwareAltDown;
|
||||
final boolean shiftDown = event.isShiftPressed() || mClient.readShiftKey() || mHardwareShiftDown;
|
||||
final boolean rightAltDownFromEvent = (metaState & KeyEvent.META_ALT_RIGHT_ON) != 0;
|
||||
|
||||
int keyMod = 0;
|
||||
@@ -815,8 +874,19 @@ public final class TerminalView extends View {
|
||||
if (mClient.readFnKey()) effectiveMetaState |= KeyEvent.META_FUNCTION_ON;
|
||||
|
||||
int result = event.getUnicodeChar(effectiveMetaState);
|
||||
boolean altLayerUsed = false;
|
||||
if (!rightAltDownFromEvent && leftAltDown && result != 0) {
|
||||
// TITAN2 PATCH: consult the kcm alt layer (e.g. Alt+W -> '1' in TitanKey.kcm).
|
||||
// The base lookup above strips left-alt from the meta state, so upstream never
|
||||
// uses the alt layer and instead sends ESC + <char>.
|
||||
int altLayerResult = event.getUnicodeChar(effectiveMetaState | KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_ON);
|
||||
if (altLayerResult != 0 && altLayerResult != result) {
|
||||
result = altLayerResult;
|
||||
altLayerUsed = true;
|
||||
}
|
||||
}
|
||||
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED)
|
||||
mClient.logInfo(LOG_TAG, "KeyEvent#getUnicodeChar(" + effectiveMetaState + ") returned: " + result);
|
||||
mClient.logInfo(LOG_TAG, "KeyEvent#getUnicodeChar(" + effectiveMetaState + ") returned: " + result + (altLayerUsed ? " (kcm alt layer)" : ""));
|
||||
if (result == 0) {
|
||||
return false;
|
||||
}
|
||||
@@ -833,7 +903,7 @@ public final class TerminalView extends View {
|
||||
if (combinedChar > 0) result = combinedChar;
|
||||
mCombiningAccent = 0;
|
||||
}
|
||||
inputCodePoint(event.getDeviceId(), result, controlDown, leftAltDown);
|
||||
inputCodePoint(event.getDeviceId(), result, controlDown, leftAltDown, altLayerUsed);
|
||||
}
|
||||
|
||||
if (mCombiningAccent != oldCombiningAccent) invalidate();
|
||||
@@ -842,6 +912,10 @@ public final class TerminalView extends View {
|
||||
}
|
||||
|
||||
public void inputCodePoint(int eventSource, int codePoint, boolean controlDownFromEvent, boolean leftAltDownFromEvent) {
|
||||
inputCodePoint(eventSource, codePoint, controlDownFromEvent, leftAltDownFromEvent, false);
|
||||
}
|
||||
|
||||
public void inputCodePoint(int eventSource, int codePoint, boolean controlDownFromEvent, boolean leftAltDownFromEvent, boolean altConsumedByKcm) {
|
||||
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED) {
|
||||
mClient.logInfo(LOG_TAG, "inputCodePoint(eventSource=" + eventSource + ", codePoint=" + codePoint + ", controlDownFromEvent=" + controlDownFromEvent + ", leftAltDownFromEvent="
|
||||
+ leftAltDownFromEvent + ")");
|
||||
@@ -854,7 +928,11 @@ public final class TerminalView extends View {
|
||||
mEmulator.setCursorBlinkState(true);
|
||||
|
||||
final boolean controlDown = controlDownFromEvent || mClient.readControlKey();
|
||||
final boolean altDown = leftAltDownFromEvent || mClient.readAltKey();
|
||||
// TITAN2 PATCH: if the kcm alt layer already produced the character (altConsumedByKcm),
|
||||
// do not also send the ESC alt-prefix.
|
||||
final boolean altDown = !altConsumedByKcm && (leftAltDownFromEvent || mClient.readAltKey());
|
||||
|
||||
mUserScrollLock = false; // TITAN2 PATCH: input returns the view to the live bottom
|
||||
|
||||
if (mClient.onCodePoint(codePoint, controlDown, mTermSession)) return;
|
||||
|
||||
@@ -919,6 +997,7 @@ public final class TerminalView extends View {
|
||||
String code = KeyHandler.getCode(keyCode, keyMod, term.isCursorKeysApplicationMode(), term.isKeypadApplicationMode());
|
||||
if (code == null) return false;
|
||||
mTermSession.write(code);
|
||||
mUserScrollLock = false; // TITAN2 PATCH: input returns the view to the live bottom
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -954,6 +1033,18 @@ public final class TerminalView extends View {
|
||||
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED)
|
||||
mClient.logInfo(LOG_TAG, "onKeyUp(keyCode=" + keyCode + ", event=" + event + ")");
|
||||
|
||||
// TITAN2 PATCH: release latched hardware modifier state (see onKeyDown).
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_SHIFT_LEFT:
|
||||
case KeyEvent.KEYCODE_SHIFT_RIGHT:
|
||||
mHardwareShiftDown = false;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_ALT_LEFT:
|
||||
case KeyEvent.KEYCODE_ALT_RIGHT:
|
||||
mHardwareAltDown = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Do not return for KEYCODE_BACK and send it to the client since user may be trying
|
||||
// to exit the activity.
|
||||
if (mEmulator == null && keyCode != KeyEvent.KEYCODE_BACK) return true;
|
||||
@@ -969,6 +1060,60 @@ public final class TerminalView extends View {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasWindowFocus) {
|
||||
super.onWindowFocusChanged(hasWindowFocus);
|
||||
// TITAN2 PATCH: drop latched hardware modifier state when focus is lost, in case
|
||||
// the keyup was delivered elsewhere (app switch, notification shade, dialog, ...).
|
||||
if (!hasWindowFocus) {
|
||||
mHardwareShiftDown = false;
|
||||
mHardwareAltDown = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TITAN2 PATCH: mirror of the @hide {@code KeyEvent.isSystemKey(int)}. Used to tell
|
||||
* real system keys (back, home, volume, ...) apart from the synthetic letter events
|
||||
* that the Titan 2 keyboard layer injects with FLAG_FROM_SYSTEM.
|
||||
*/
|
||||
private static boolean isRealSystemKey(int keyCode) {
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_MENU:
|
||||
case KeyEvent.KEYCODE_SOFT_RIGHT:
|
||||
case KeyEvent.KEYCODE_HOME:
|
||||
case KeyEvent.KEYCODE_BACK:
|
||||
case KeyEvent.KEYCODE_CALL:
|
||||
case KeyEvent.KEYCODE_ENDCALL:
|
||||
case KeyEvent.KEYCODE_VOLUME_UP:
|
||||
case KeyEvent.KEYCODE_VOLUME_DOWN:
|
||||
case KeyEvent.KEYCODE_MUTE:
|
||||
case KeyEvent.KEYCODE_HEADSETHOOK:
|
||||
case KeyEvent.KEYCODE_MEDIA_PLAY:
|
||||
case KeyEvent.KEYCODE_MEDIA_PAUSE:
|
||||
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
|
||||
case KeyEvent.KEYCODE_MEDIA_STOP:
|
||||
case KeyEvent.KEYCODE_MEDIA_NEXT:
|
||||
case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
|
||||
case KeyEvent.KEYCODE_MEDIA_REWIND:
|
||||
case KeyEvent.KEYCODE_MEDIA_RECORD:
|
||||
case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
|
||||
case KeyEvent.KEYCODE_CAMERA:
|
||||
case KeyEvent.KEYCODE_FOCUS:
|
||||
case KeyEvent.KEYCODE_SEARCH:
|
||||
case KeyEvent.KEYCODE_BRIGHTNESS_DOWN:
|
||||
case KeyEvent.KEYCODE_BRIGHTNESS_UP:
|
||||
case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK:
|
||||
case KeyEvent.KEYCODE_SLEEP:
|
||||
case KeyEvent.KEYCODE_WAKEUP:
|
||||
case KeyEvent.KEYCODE_VOICE_ASSIST:
|
||||
case KeyEvent.KEYCODE_ASSIST:
|
||||
case KeyEvent.KEYCODE_APP_SWITCH:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called during layout when the size of this view has changed. If you were just added to the view
|
||||
* hierarchy, you're called with the old values of 0.
|
||||
@@ -998,6 +1143,7 @@ public final class TerminalView extends View {
|
||||
mTerminalCursorBlinkerRunnable.setEmulator(mEmulator);
|
||||
|
||||
mTopRow = 0;
|
||||
mUserScrollLock = false; // TITAN2 PATCH
|
||||
scrollTo(0, 0);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@@ -2,23 +2,24 @@ apply plugin: 'com.android.library'
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
android {
|
||||
namespace = "com.termux.shared"
|
||||
|
||||
compileSdkVersion project.properties.compileSdkVersion.toInteger()
|
||||
ndkVersion = System.getenv("JITPACK_NDK_VERSION") ?: project.properties.ndkVersion
|
||||
|
||||
dependencies {
|
||||
implementation "androidx.appcompat:appcompat:1.3.1"
|
||||
implementation "androidx.annotation:annotation:1.3.0"
|
||||
implementation "androidx.core:core:1.6.0"
|
||||
implementation "com.google.android.material:material:1.4.0"
|
||||
implementation "androidx.appcompat:appcompat:1.6.1"
|
||||
implementation "androidx.annotation:annotation:1.9.0"
|
||||
implementation "androidx.core:core:1.13.1"
|
||||
implementation "com.google.android.material:material:1.12.0"
|
||||
implementation "com.google.guava:guava:24.1-jre"
|
||||
implementation "io.noties.markwon:core:$markwonVersion"
|
||||
implementation "io.noties.markwon:ext-strikethrough:$markwonVersion"
|
||||
implementation "io.noties.markwon:linkify:$markwonVersion"
|
||||
implementation "io.noties.markwon:recycler:$markwonVersion"
|
||||
implementation "org.lsposed.hiddenapibypass:hiddenapibypass:5.0"
|
||||
implementation "org.lsposed.hiddenapibypass:hiddenapibypass:6.1"
|
||||
|
||||
// Do not increment version higher than 1.0.0-alpha09 since it will break ViewUtils and needs to be looked into
|
||||
// noinspection GradleDependency
|
||||
implementation "androidx.window:window:1.0.0-alpha09"
|
||||
implementation "androidx.window:window:1.1.0"
|
||||
|
||||
// Do not increment version higher than 2.5 or there
|
||||
// will be runtime exceptions on android < 8
|
||||
@@ -31,6 +32,7 @@ android {
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
compileSdkVersion project.properties.compileSdkVersion.toInteger()
|
||||
minSdkVersion project.properties.minSdkVersion.toInteger()
|
||||
targetSdkVersion project.properties.targetSdkVersion.toInteger()
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
@@ -55,23 +57,31 @@ android {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
path file('src/main/cpp/Android.mk')
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
multipleVariants {
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
allVariants()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation "junit:junit:4.13.2"
|
||||
androidTestImplementation "androidx.test.ext:junit:1.1.3"
|
||||
androidTestImplementation "androidx.test.espresso:espresso-core:3.4.0"
|
||||
androidTestImplementation "androidx.test.ext:junit:1.1.5"
|
||||
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.1.5"
|
||||
}
|
||||
|
||||
task sourceJar(type: Jar) {
|
||||
from android.sourceSets.main.java.srcDirs
|
||||
classifier "sources"
|
||||
archiveClassifier = "sources"
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
@@ -79,7 +89,7 @@ afterEvaluate {
|
||||
publications {
|
||||
// Creates a Maven publication called "release".
|
||||
release(MavenPublication) {
|
||||
from components.release
|
||||
from components.default
|
||||
groupId = 'com.termux'
|
||||
artifactId = 'termux-shared'
|
||||
version = '0.118.0'
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.termux.shared">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
</manifest>
|
||||
|
||||
@@ -5,8 +5,6 @@ import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.os.Build;
|
||||
import android.system.Os;
|
||||
import android.system.OsConstants;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
@@ -160,19 +158,7 @@ public class AndroidUtils {
|
||||
appendPropertyToMarkdown(markdownString, "BOARD", Build.BOARD);
|
||||
appendPropertyToMarkdown(markdownString, "HARDWARE", Build.HARDWARE);
|
||||
appendPropertyToMarkdown(markdownString, "DEVICE", Build.DEVICE);
|
||||
|
||||
appendPropertyToMarkdown(markdownString, "SUPPORTED_ABIS", Joiner.on(", ").skipNulls().join(Build.SUPPORTED_ABIS));
|
||||
appendPropertyToMarkdown(markdownString, "SUPPORTED_32_BIT_ABIS", Joiner.on(", ").skipNulls().join(Build.SUPPORTED_32_BIT_ABIS));
|
||||
appendPropertyToMarkdown(markdownString, "SUPPORTED_64_BIT_ABIS", Joiner.on(", ").skipNulls().join(Build.SUPPORTED_64_BIT_ABIS));
|
||||
|
||||
// If on Android >= 15
|
||||
if (Build.VERSION.SDK_INT >= 35) {
|
||||
try {
|
||||
appendPropertyToMarkdownIfSet(markdownString, "PAGE_SIZE", Os.sysconf(OsConstants._SC_PAGESIZE));
|
||||
} catch (Throwable t) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
markdownString.append("\n##\n");
|
||||
|
||||
|
||||
@@ -209,44 +209,31 @@ public class PermissionUtils {
|
||||
|
||||
/** If path is under primary external storage directory and storage permission is missing,
|
||||
* then legacy or manage external storage permission will be requested from the user via a call
|
||||
* to {@link #checkAndRequestLegacyOrManageExternalStoragePermission(Context, int, boolean, boolean)}.
|
||||
* to {@link #checkAndRequestLegacyOrManageExternalStoragePermission(Context, int, boolean)}.
|
||||
*
|
||||
* @param context The context for operations.
|
||||
* @param filePath The path to check.
|
||||
* @param requestCode The request code to use while asking for permission.
|
||||
* @param prioritizeManageExternalStoragePermission If {@link Manifest.permission#MANAGE_EXTERNAL_STORAGE}
|
||||
* permission should be requested if on
|
||||
* Android `>= 11` instead of getting legacy
|
||||
* storage permission.
|
||||
* @param showErrorMessage If an error message toast should be shown if permission is not granted.
|
||||
* @return Returns {@code true} if permission is granted, otherwise {@code false}.
|
||||
*/
|
||||
@SuppressLint("SdCardPath")
|
||||
public static boolean checkAndRequestLegacyOrManageExternalStoragePermissionIfPathOnPrimaryExternalStorage(
|
||||
@NonNull Context context, String filePath, int requestCode,
|
||||
boolean prioritizeManageExternalStoragePermission, boolean showErrorMessage) {
|
||||
@NonNull Context context, String filePath, int requestCode, boolean showErrorMessage) {
|
||||
// If path is under primary external storage directory, then check for missing permissions.
|
||||
if (!FileUtils.isPathInDirPaths(filePath,
|
||||
Arrays.asList(Environment.getExternalStorageDirectory().getAbsolutePath(), "/sdcard"), true))
|
||||
return true;
|
||||
|
||||
return checkAndRequestLegacyOrManageExternalStoragePermission(context, requestCode, prioritizeManageExternalStoragePermission, showErrorMessage);
|
||||
return checkAndRequestLegacyOrManageExternalStoragePermission(context, requestCode, showErrorMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if legacy or manage external storage permissions has been granted.
|
||||
*
|
||||
* - If `prioritizeManageExternalStoragePermission` is `true and running on Android `>= 11`, then
|
||||
* it will be checked if app has been granted the
|
||||
* {@link Manifest.permission#MANAGE_EXTERNAL_STORAGE}.
|
||||
* - If `prioritizeManageExternalStoragePermission` is `false` and running on Android `>= 11`, then
|
||||
* if {@link #isLegacyExternalStoragePossible(Context)} returns `true`, them it will be
|
||||
* checked if app has has been granted {@link Manifest.permission#READ_EXTERNAL_STORAGE} and
|
||||
* {@link Manifest.permission#WRITE_EXTERNAL_STORAGE} permissions, otherwise it will be checked
|
||||
* if app has been granted the {@link Manifest.permission#MANAGE_EXTERNAL_STORAGE} permission.
|
||||
* - If running on Android `< 11`, then it will only be checked if app has been granted
|
||||
* {@link Manifest.permission#READ_EXTERNAL_STORAGE} and
|
||||
* {@link Manifest.permission#WRITE_EXTERNAL_STORAGE} permissions.
|
||||
* Check if legacy or manage external storage permissions has been granted. If
|
||||
* {@link #isLegacyExternalStoragePossible(Context)} returns {@code true}, them it will be
|
||||
* checked if app has has been granted {@link Manifest.permission#READ_EXTERNAL_STORAGE} and
|
||||
* {@link Manifest.permission#WRITE_EXTERNAL_STORAGE} permissions, otherwise it will be checked
|
||||
* if app has been granted the {@link Manifest.permission#MANAGE_EXTERNAL_STORAGE} permission.
|
||||
*
|
||||
* If storage permission is missing, it will be requested from the user if {@code context} is an
|
||||
* instance of {@link Activity} or {@link AppCompatActivity} and {@code requestCode}
|
||||
@@ -269,34 +256,16 @@ public class PermissionUtils {
|
||||
*}
|
||||
* @param context The context for operations.
|
||||
* @param requestCode The request code to use while asking for permission.
|
||||
* @param prioritizeManageExternalStoragePermission If {@link Manifest.permission#MANAGE_EXTERNAL_STORAGE}
|
||||
* permission should be requested if on
|
||||
* Android `>= 11` instead of getting legacy
|
||||
* storage permission.
|
||||
* @param showErrorMessage If an error message toast should be shown if permission is not granted.
|
||||
* @return Returns {@code true} if permission is granted, otherwise {@code false}.
|
||||
*/
|
||||
public static boolean checkAndRequestLegacyOrManageExternalStoragePermission(@NonNull Context context,
|
||||
int requestCode,
|
||||
boolean prioritizeManageExternalStoragePermission,
|
||||
boolean showErrorMessage) {
|
||||
Logger.logVerbose(LOG_TAG, "Checking storage permission");
|
||||
|
||||
String errmsg;
|
||||
Boolean requestLegacyStoragePermission = null;
|
||||
|
||||
if (prioritizeManageExternalStoragePermission && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
|
||||
requestLegacyStoragePermission = false;
|
||||
|
||||
if (requestLegacyStoragePermission == null)
|
||||
requestLegacyStoragePermission = isLegacyExternalStoragePossible(context);
|
||||
|
||||
boolean requestLegacyStoragePermission = isLegacyExternalStoragePossible(context);
|
||||
boolean checkIfHasRequestedLegacyExternalStorage = checkIfHasRequestedLegacyExternalStorage(context);
|
||||
|
||||
Logger.logVerbose(LOG_TAG, "prioritizeManageExternalStoragePermission=" + prioritizeManageExternalStoragePermission +
|
||||
", requestLegacyStoragePermission=" + requestLegacyStoragePermission +
|
||||
", checkIfHasRequestedLegacyExternalStorage=" + checkIfHasRequestedLegacyExternalStorage);
|
||||
|
||||
if (requestLegacyStoragePermission && checkIfHasRequestedLegacyExternalStorage) {
|
||||
// Check if requestLegacyExternalStorage is set to true in app manifest
|
||||
if (!hasRequestedLegacyExternalStorage(context, showErrorMessage))
|
||||
|
||||
@@ -51,7 +51,7 @@ public class MessageDialogUtils {
|
||||
final DialogInterface.OnClickListener onNegativeButton,
|
||||
final DialogInterface.OnDismissListener onDismiss) {
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.Theme_AppCompat_Light_Dialog);
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(context, androidx.appcompat.R.style.Theme_AppCompat_Light_Dialog);
|
||||
|
||||
LayoutInflater inflater = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE );
|
||||
View view = inflater.inflate(R.layout.dialog_show_message, null);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.termux.shared.models;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
|
||||
import com.termux.shared.markdown.MarkdownUtils;
|
||||
import com.termux.shared.android.AndroidUtils;
|
||||
|
||||
@@ -10,6 +12,25 @@ import java.io.Serializable;
|
||||
*/
|
||||
public class ReportInfo implements Serializable {
|
||||
|
||||
/**
|
||||
* Explicitly define `serialVersionUID` to prevent exceptions on deserialization.
|
||||
*
|
||||
* Like when calling `Bundle.getSerializable()` on Android.
|
||||
* `android.os.BadParcelableException: Parcelable encountered IOException reading a Serializable object` (name = <class_name>)
|
||||
* `java.io.InvalidClassException: <class_name>; local class incompatible`
|
||||
*
|
||||
* The `@Keep` annotation is necessary to prevent the field from being removed by proguard when
|
||||
* app is compiled, even if its kept during library compilation.
|
||||
*
|
||||
* **See Also:**
|
||||
* - https://docs.oracle.com/javase/8/docs/platform/serialization/spec/version.html#a6678
|
||||
* - https://docs.oracle.com/javase/8/docs/platform/serialization/spec/class.html#a4100
|
||||
*/
|
||||
@Keep
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
|
||||
/** The user action that was being processed for which the report was generated. */
|
||||
public final String userAction;
|
||||
/** The internal app component that sent the report. */
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.termux.shared.models;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Typeface;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.termux.shared.activities.TextIOActivity;
|
||||
@@ -19,6 +20,25 @@ import java.io.Serializable;
|
||||
*/
|
||||
public class TextIOInfo implements Serializable {
|
||||
|
||||
/**
|
||||
* Explicitly define `serialVersionUID` to prevent exceptions on deserialization.
|
||||
*
|
||||
* Like when calling `Bundle.getSerializable()` on Android.
|
||||
* `android.os.BadParcelableException: Parcelable encountered IOException reading a Serializable object` (name = <class_name>)
|
||||
* `java.io.InvalidClassException: <class_name>; local class incompatible`
|
||||
*
|
||||
* The `@Keep` annotation is necessary to prevent the field from being removed by proguard when
|
||||
* app is compiled, even if its kept during library compilation.
|
||||
*
|
||||
* **See Also:**
|
||||
* - https://docs.oracle.com/javase/8/docs/platform/serialization/spec/version.html#a6678
|
||||
* - https://docs.oracle.com/javase/8/docs/platform/serialization/spec/class.html#a4100
|
||||
*/
|
||||
@Keep
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
|
||||
public static final int GENERAL_DATA_SIZE_LIMIT_IN_BYTES = 1000;
|
||||
public static final int LABEL_SIZE_LIMIT_IN_BYTES = 4000;
|
||||
public static final int TEXT_SIZE_LIMIT_IN_BYTES = 100000 - GENERAL_DATA_SIZE_LIMIT_IN_BYTES - LABEL_SIZE_LIMIT_IN_BYTES; // < 100KB
|
||||
|
||||
@@ -71,7 +71,9 @@ public class NotificationUtils {
|
||||
Notification.Builder builder = new Notification.Builder(context);
|
||||
builder.setContentTitle(title);
|
||||
builder.setContentText(notificationText);
|
||||
builder.setStyle(new Notification.BigTextStyle().bigText(notificationBigText));
|
||||
if (notificationBigText != null) {
|
||||
builder.setStyle(new Notification.BigTextStyle().bigText(notificationBigText));
|
||||
}
|
||||
builder.setContentIntent(contentIntent);
|
||||
builder.setDeleteIntent(deleteIntent);
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.termux.shared.shell.command.environment;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
@@ -21,20 +20,6 @@ import java.util.HashMap;
|
||||
*/
|
||||
public class AndroidShellEnvironment extends UnixShellEnvironment {
|
||||
|
||||
/** Environment variable scope for Android. */
|
||||
public static final String ANDROID_ENV_SCOPE = "ANDROID__"; // Default: "ANDROID__"
|
||||
|
||||
/**
|
||||
* Environment variable for the Android build SDK version currently running on the device that
|
||||
* is defined by {@link Build.VERSION#SDK_INT} and `ro.build.version.sdk` system property.
|
||||
*
|
||||
* - https://developer.android.com/reference/android/os/Build.VERSION#SDK_INT
|
||||
* - https://developer.android.com/reference/android/os/Build.VERSION_CODES
|
||||
*
|
||||
* Default value: `ANDROID__BUILD_VERSION_SDK`
|
||||
*/
|
||||
public static final String ENV_ANDROID__BUILD_VERSION_SDK = ANDROID_ENV_SCOPE + "BUILD_VERSION_SDK";
|
||||
|
||||
protected ShellCommandShellEnvironment shellCommandShellEnvironment;
|
||||
|
||||
public AndroidShellEnvironment() {
|
||||
@@ -76,8 +61,6 @@ public class AndroidShellEnvironment extends UnixShellEnvironment {
|
||||
ShellEnvironmentUtils.putToEnvIfInSystemEnv(environment, "DEX2OATBOOTCLASSPATH");
|
||||
ShellEnvironmentUtils.putToEnvIfInSystemEnv(environment, "SYSTEMSERVERCLASSPATH");
|
||||
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_ANDROID__BUILD_VERSION_SDK, String.valueOf(Build.VERSION.SDK_INT));
|
||||
|
||||
return environment;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,13 +33,13 @@ public class ShellCommandShellEnvironment {
|
||||
public static final String ENV_SHELL_CMD__APP_SHELL_NUMBER_SINCE_BOOT = SHELL_CMD_ENV_PREFIX + "APP_SHELL_NUMBER_SINCE_BOOT";
|
||||
|
||||
/** Environment variable for the {{@link ExecutionCommand.Runner#TERMINAL_SESSION} number since boot. */
|
||||
public static final String ENV_SHELL_CMD__APP_TERMINAL_SESSION_NUMBER_SINCE_BOOT = SHELL_CMD_ENV_PREFIX + "APP_TERMINAL_SESSION_NUMBER_SINCE_BOOT";
|
||||
public static final String ENV_SHELL_CMD__TERMINAL_SESSION_NUMBER_SINCE_BOOT = SHELL_CMD_ENV_PREFIX + "TERMINAL_SESSION_NUMBER_SINCE_BOOT";
|
||||
|
||||
/** Environment variable for the {@link ExecutionCommand.Runner#APP_SHELL} number since app start. */
|
||||
public static final String ENV_SHELL_CMD__APP_SHELL_NUMBER_SINCE_APP_START = SHELL_CMD_ENV_PREFIX + "APP_SHELL_NUMBER_SINCE_APP_START";
|
||||
|
||||
/** Environment variable for the {@link ExecutionCommand.Runner#TERMINAL_SESSION} number since app start. */
|
||||
public static final String ENV_SHELL_CMD__APP_TERMINAL_SESSION_NUMBER_SINCE_APP_START = SHELL_CMD_ENV_PREFIX + "APP_TERMINAL_SESSION_NUMBER_SINCE_APP_START";
|
||||
public static final String ENV_SHELL_CMD__TERMINAL_SESSION_NUMBER_SINCE_APP_START = SHELL_CMD_ENV_PREFIX + "TERMINAL_SESSION_NUMBER_SINCE_APP_START";
|
||||
|
||||
|
||||
/** Get shell environment containing info for {@link ExecutionCommand}. */
|
||||
|
||||
@@ -326,7 +326,7 @@ public final class TermuxPropertyConstants {
|
||||
/** Defines the key for extra keys */
|
||||
public static final String KEY_EXTRA_KEYS = "extra-keys"; // Default: "extra-keys"
|
||||
//public static final String DEFAULT_IVALUE_EXTRA_KEYS = "[[ESC, TAB, CTRL, ALT, {key: '-', popup: '|'}, DOWN, UP]]"; // Single row
|
||||
public static final String DEFAULT_IVALUE_EXTRA_KEYS = "[['ESC',{key: 'DRAWER', popup: 'PASTE'},'SCROLL','HOME','UP','END','PGUP'], ['TAB','CTRL','ALT','LEFT','DOWN','RIGHT','PGDN']]"; // Double row
|
||||
public static final String DEFAULT_IVALUE_EXTRA_KEYS = "[['ESC','/',{key: '-', popup: '|'},'HOME','UP','END','PGUP'], ['TAB','CTRL','ALT','LEFT','DOWN','RIGHT','PGDN']]"; // Double row
|
||||
|
||||
/** Defines the key for extra keys style */
|
||||
public static final String KEY_EXTRA_KEYS_STYLE = "extra-keys-style"; // Default: "extra-keys-style"
|
||||
|
||||
@@ -27,12 +27,6 @@ public class TermuxAppShellEnvironment {
|
||||
/** Termux app environment variables. */
|
||||
public static HashMap<String, String> termuxAppEnvironment;
|
||||
|
||||
/** Environment variable root scope. */
|
||||
public static final String TERMUX_ENV__S_ROOT = "TERMUX_"; // Default: "TERMUX_"
|
||||
|
||||
/** Environment variable scope for Termux. */
|
||||
public static final String TERMUX_ENV__S_TERMUX = TERMUX_ENV__S_ROOT + "_"; // Default: "TERMUX__"
|
||||
|
||||
/** Environment variable for the Termux app version. */
|
||||
public static final String ENV_TERMUX_VERSION = TermuxConstants.TERMUX_ENV_PREFIX_ROOT + "_VERSION";
|
||||
|
||||
@@ -40,15 +34,15 @@ public class TermuxAppShellEnvironment {
|
||||
public static final String TERMUX_APP_ENV_PREFIX = TermuxConstants.TERMUX_ENV_PREFIX_ROOT + "_APP__";
|
||||
|
||||
/** Environment variable for the Termux app version name. */
|
||||
public static final String ENV_TERMUX_APP__APP_VERSION_NAME = TERMUX_APP_ENV_PREFIX + "APP_VERSION_NAME";
|
||||
public static final String ENV_TERMUX_APP__VERSION_NAME = TERMUX_APP_ENV_PREFIX + "VERSION_NAME";
|
||||
/** Environment variable for the Termux app version code. */
|
||||
public static final String ENV_TERMUX_APP__APP_VERSION_CODE = TERMUX_APP_ENV_PREFIX + "APP_VERSION_CODE";
|
||||
public static final String ENV_TERMUX_APP__VERSION_CODE = TERMUX_APP_ENV_PREFIX + "VERSION_CODE";
|
||||
/** Environment variable for the Termux app package name. */
|
||||
public static final String ENV_TERMUX_APP__PACKAGE_NAME = TERMUX_APP_ENV_PREFIX + "PACKAGE_NAME";
|
||||
/** Environment variable for the Termux app process id. */
|
||||
public static final String ENV_TERMUX_APP__PID = TERMUX_APP_ENV_PREFIX + "PID";
|
||||
/** Environment variable for the Termux app uid. */
|
||||
public static final String ENV_TERMUX__UID = TERMUX_ENV__S_TERMUX + "UID";
|
||||
public static final String ENV_TERMUX_APP__UID = TERMUX_APP_ENV_PREFIX + "UID";
|
||||
/** Environment variable for the Termux app targetSdkVersion. */
|
||||
public static final String ENV_TERMUX_APP__TARGET_SDK = TERMUX_APP_ENV_PREFIX + "TARGET_SDK";
|
||||
/** Environment variable for the Termux app is debuggable apk build. */
|
||||
@@ -56,29 +50,28 @@ public class TermuxAppShellEnvironment {
|
||||
/** Environment variable for the Termux app {@link TermuxConstants} APK_RELEASE_*. */
|
||||
public static final String ENV_TERMUX_APP__APK_RELEASE = TERMUX_APP_ENV_PREFIX + "APK_RELEASE";
|
||||
/** Environment variable for the Termux app install path. */
|
||||
public static final String ENV_TERMUX_APP__APK_FILE = TERMUX_APP_ENV_PREFIX + "APK_FILE";
|
||||
public static final String ENV_TERMUX_APP__APK_PATH = TERMUX_APP_ENV_PREFIX + "APK_PATH";
|
||||
/** Environment variable for the Termux app is installed on external/portable storage. */
|
||||
public static final String ENV_TERMUX_APP__IS_INSTALLED_ON_EXTERNAL_STORAGE = TERMUX_APP_ENV_PREFIX + "IS_INSTALLED_ON_EXTERNAL_STORAGE";
|
||||
|
||||
/** Environment variable for the Termux app process selinux context. */
|
||||
public static final String ENV_TERMUX_APP__SE_PROCESS_CONTEXT = TERMUX_APP_ENV_PREFIX + "SE_PROCESS_CONTEXT";
|
||||
/** Environment variable for the Termux app data files selinux context. */
|
||||
public static final String ENV_TERMUX_APP__SE_FILE_CONTEXT = TERMUX_APP_ENV_PREFIX + "SE_FILE_CONTEXT";
|
||||
/** Environment variable for the Termux app seInfo tag found in selinux policy used to set app process and app data files selinux context. */
|
||||
public static final String ENV_TERMUX_APP__SE_INFO = TERMUX_APP_ENV_PREFIX + "SE_INFO";
|
||||
|
||||
/** Environment variable for the current Termux process selinux context. */
|
||||
public static final String ENV_TERMUX__SE_PROCESS_CONTEXT = TERMUX_ENV__S_TERMUX + "SE_PROCESS_CONTEXT";
|
||||
/** Environment variable for the Termux app user id. */
|
||||
public static final String ENV_TERMUX__USER_ID = TERMUX_ENV__S_TERMUX + "USER_ID";
|
||||
public static final String ENV_TERMUX_APP__USER_ID = TERMUX_APP_ENV_PREFIX + "USER_ID";
|
||||
/** Environment variable for the Termux app profile owner. */
|
||||
public static final String ENV_TERMUX__PROFILE_OWNER = TERMUX_ENV__S_TERMUX + "PROFILE_OWNER";
|
||||
public static final String ENV_TERMUX_APP__PROFILE_OWNER = TERMUX_APP_ENV_PREFIX + "PROFILE_OWNER";
|
||||
|
||||
/** Environment variable for the Termux app {@link TermuxBootstrap#TERMUX_APP_PACKAGE_MANAGER}. */
|
||||
public static final String ENV_TERMUX_APP__PACKAGE_MANAGER = TERMUX_APP_ENV_PREFIX + "PACKAGE_MANAGER";
|
||||
/** Environment variable for the Termux app {@link TermuxBootstrap#TERMUX_APP_PACKAGE_VARIANT}. */
|
||||
public static final String ENV_TERMUX_APP__PACKAGE_VARIANT = TERMUX_APP_ENV_PREFIX + "PACKAGE_VARIANT";
|
||||
/** Environment variable for the Termux app files directory. */
|
||||
public static final String ENV_TERMUX_APP__DATA_DIR = TERMUX_APP_ENV_PREFIX + "DATA_DIR";
|
||||
public static final String ENV_TERMUX_APP__LEGACY_DATA_DIR = TERMUX_APP_ENV_PREFIX + "LEGACY_DATA_DIR";
|
||||
public static final String ENV_TERMUX_APP__BUILD_DATA_DIR = TERMUX_APP_ENV_PREFIX + "BUILD_DATA_DIR";
|
||||
public static final String ENV_TERMUX_APP__FILES_DIR = TERMUX_APP_ENV_PREFIX + "FILES_DIR";
|
||||
|
||||
|
||||
/** Environment variable for the Termux app {@link TermuxAmSocketServer#getTermuxAppAMSocketServerEnabled(Context)}. */
|
||||
public static final String ENV_TERMUX_APP__AM_SOCKET_SERVER_ENABLED = TERMUX_APP_ENV_PREFIX + "AM_SOCKET_SERVER_ENABLED";
|
||||
@@ -112,22 +105,21 @@ public class TermuxAppShellEnvironment {
|
||||
HashMap<String, String> environment = new HashMap<>();
|
||||
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX_VERSION, PackageUtils.getVersionNameForPackage(packageInfo));
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX_APP__APP_VERSION_NAME, PackageUtils.getVersionNameForPackage(packageInfo));
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX_APP__APP_VERSION_CODE, String.valueOf(PackageUtils.getVersionCodeForPackage(packageInfo)));
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX_APP__VERSION_NAME, PackageUtils.getVersionNameForPackage(packageInfo));
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX_APP__VERSION_CODE, String.valueOf(PackageUtils.getVersionCodeForPackage(packageInfo)));
|
||||
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX_APP__PACKAGE_NAME, packageName);
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX_APP__PID, TermuxUtils.getTermuxAppPID(currentPackageContext));
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX__UID, String.valueOf(PackageUtils.getUidForPackage(applicationInfo)));
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX_APP__UID, String.valueOf(PackageUtils.getUidForPackage(applicationInfo)));
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX_APP__TARGET_SDK, String.valueOf(PackageUtils.getTargetSDKForPackage(applicationInfo)));
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX_APP__IS_DEBUGGABLE_BUILD, PackageUtils.isAppForPackageADebuggableBuild(applicationInfo));
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX_APP__APK_FILE, PackageUtils.getBaseAPKPathForPackage(applicationInfo));
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX_APP__APK_PATH, PackageUtils.getBaseAPKPathForPackage(applicationInfo));
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX_APP__IS_INSTALLED_ON_EXTERNAL_STORAGE, PackageUtils.isAppInstalledOnExternalStorage(applicationInfo));
|
||||
|
||||
putTermuxAPKSignature(currentPackageContext, environment);
|
||||
|
||||
Context termuxPackageContext = TermuxUtils.getTermuxPackageContext(currentPackageContext);
|
||||
if (termuxPackageContext != null) {
|
||||
/*
|
||||
// An app that does not have the same sharedUserId as termux app will not be able to get
|
||||
// get termux context's classloader to get BuildConfig.TERMUX_PACKAGE_VARIANT via reflection.
|
||||
// Check TermuxBootstrap.setTermuxPackageManagerAndVariantFromTermuxApp()
|
||||
@@ -135,28 +127,24 @@ public class TermuxAppShellEnvironment {
|
||||
environment.put(ENV_TERMUX_APP__PACKAGE_MANAGER, TermuxBootstrap.TERMUX_APP_PACKAGE_MANAGER.getName());
|
||||
if (TermuxBootstrap.TERMUX_APP_PACKAGE_VARIANT != null)
|
||||
environment.put(ENV_TERMUX_APP__PACKAGE_VARIANT, TermuxBootstrap.TERMUX_APP_PACKAGE_VARIANT.getName());
|
||||
*/
|
||||
|
||||
/*
|
||||
// Will not be set for plugins
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX_APP__AM_SOCKET_SERVER_ENABLED,
|
||||
TermuxAmSocketServer.getTermuxAppAMSocketServerEnabled(currentPackageContext));
|
||||
*/
|
||||
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX_APP__DATA_DIR, applicationInfo.dataDir);
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX_APP__LEGACY_DATA_DIR, "/data/data/" + applicationInfo.packageName);
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX_APP__BUILD_DATA_DIR, TermuxConstants.TERMUX_INTERNAL_PRIVATE_APP_DATA_DIR_PATH);
|
||||
String filesDirPath = currentPackageContext.getFilesDir().getAbsolutePath();
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX_APP__FILES_DIR, filesDirPath);
|
||||
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX__SE_PROCESS_CONTEXT, SELinuxUtils.getContext());
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX_APP__SE_FILE_CONTEXT, SELinuxUtils.getFileContext(applicationInfo.dataDir));
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX_APP__SE_PROCESS_CONTEXT, SELinuxUtils.getContext());
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX_APP__SE_FILE_CONTEXT, SELinuxUtils.getFileContext(filesDirPath));
|
||||
|
||||
String seInfoUser = PackageUtils.getApplicationInfoSeInfoUserForPackage(applicationInfo);
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX_APP__SE_INFO, PackageUtils.getApplicationInfoSeInfoForPackage(applicationInfo) +
|
||||
(DataUtils.isNullOrEmpty(seInfoUser) ? "" : seInfoUser));
|
||||
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX__USER_ID, String.valueOf(PackageUtils.getUserIdForPackage(currentPackageContext)));
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX__PROFILE_OWNER, PackageUtils.getProfileOwnerPackageNameForUser(currentPackageContext));
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX_APP__USER_ID, String.valueOf(PackageUtils.getUserIdForPackage(currentPackageContext)));
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_TERMUX_APP__PROFILE_OWNER, PackageUtils.getProfileOwnerPackageNameForUser(currentPackageContext));
|
||||
}
|
||||
|
||||
termuxAppEnvironment = environment;
|
||||
|
||||
@@ -34,9 +34,9 @@ public class TermuxShellCommandShellEnvironment extends ShellCommandShellEnviron
|
||||
String.valueOf(TermuxShellManager.getAndIncrementAppShellNumberSinceAppStart()));
|
||||
|
||||
} else if (ExecutionCommand.Runner.TERMINAL_SESSION.equalsRunner(executionCommand.runner)) {
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_SHELL_CMD__APP_TERMINAL_SESSION_NUMBER_SINCE_BOOT,
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_SHELL_CMD__TERMINAL_SESSION_NUMBER_SINCE_BOOT,
|
||||
String.valueOf(preferences.getAndIncrementTerminalSessionNumberSinceBoot()));
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_SHELL_CMD__APP_TERMINAL_SESSION_NUMBER_SINCE_APP_START,
|
||||
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_SHELL_CMD__TERMINAL_SESSION_NUMBER_SINCE_APP_START,
|
||||
String.valueOf(TermuxShellManager.getAndIncrementTerminalSessionNumberSinceAppStart()));
|
||||
} else {
|
||||
return environment;
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
package com.termux.shared.termux.shell.command.environment;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.termux.shared.android.PackageUtils;
|
||||
import com.termux.shared.errors.Error;
|
||||
import com.termux.shared.file.FileUtils;
|
||||
import com.termux.shared.logger.Logger;
|
||||
@@ -74,25 +71,12 @@ public class TermuxShellEnvironment extends AndroidShellEnvironment {
|
||||
if (termuxAppEnvironment != null)
|
||||
environment.putAll(termuxAppEnvironment);
|
||||
|
||||
/*
|
||||
HashMap<String, String> termuxApiAppEnvironment = TermuxAPIShellEnvironment.getEnvironment(currentPackageContext);
|
||||
if (termuxApiAppEnvironment != null)
|
||||
environment.putAll(termuxApiAppEnvironment);
|
||||
*/
|
||||
|
||||
ApplicationInfo applicationInfo = PackageUtils.getApplicationInfoForPackage(currentPackageContext, TermuxConstants.TERMUX_PACKAGE_NAME);
|
||||
if (applicationInfo != null && !applicationInfo.enabled) {
|
||||
applicationInfo = null;
|
||||
}
|
||||
|
||||
if (applicationInfo != null) {
|
||||
environment.put("TERMUX__APPS_DIR", applicationInfo.dataDir + "/termux/apps");
|
||||
}
|
||||
environment.put("TERMUX__ROOTFS", TermuxConstants.TERMUX_FILES_DIR_PATH);
|
||||
environment.put(ENV_HOME, TermuxConstants.TERMUX_HOME_DIR_PATH);
|
||||
environment.put("TERMUX__HOME", TermuxConstants.TERMUX_HOME_DIR_PATH);
|
||||
environment.put(ENV_PREFIX, TermuxConstants.TERMUX_PREFIX_DIR_PATH);
|
||||
environment.put("TERMUX__PREFIX", TermuxConstants.TERMUX_PREFIX_DIR_PATH);
|
||||
|
||||
// If failsafe is not enabled, then we keep default PATH and TMPDIR so that system binaries can be used
|
||||
if (!isFailSafe) {
|
||||
|
||||
@@ -185,12 +185,11 @@ public class ViewUtils {
|
||||
public static Point getDisplaySize( @NonNull Context context, boolean activitySize) {
|
||||
// android.view.WindowManager.getDefaultDisplay() and Display.getSize() are deprecated in
|
||||
// API 30 and give wrong values in API 30 for activitySize=false in multi-window
|
||||
androidx.window.WindowManager windowManager = new androidx.window.WindowManager(context);
|
||||
androidx.window.WindowMetrics windowMetrics;
|
||||
androidx.window.layout.WindowMetrics windowMetrics;
|
||||
if (activitySize)
|
||||
windowMetrics = windowManager.getCurrentWindowMetrics();
|
||||
windowMetrics = androidx.window.layout.WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(context);
|
||||
else
|
||||
windowMetrics = windowManager.getMaximumWindowMetrics();
|
||||
windowMetrics = androidx.window.layout.WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(context);
|
||||
return new Point(windowMetrics.getBounds().width(), windowMetrics.getBounds().height());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user