diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..a83ef38514 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: +- package-ecosystem: github-actions + directory: / + schedule: + interval: daily diff --git a/.github/workflows/attach_debug_apks_to_release.yml b/.github/workflows/attach_debug_apks_to_release.yml index 5c95e788ba..e64d838fab 100644 --- a/.github/workflows/attach_debug_apks_to_release.yml +++ b/.github/workflows/attach_debug_apks_to_release.yml @@ -6,7 +6,7 @@ on: - published jobs: - attach-apks: + build: runs-on: ubuntu-latest strategy: fail-fast: false @@ -17,27 +17,19 @@ jobs: steps: - name: Clone repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: ref: ${{ env.GITHUB_REF }} - - name: Build and attach APKs to release + - name: Build shell: bash {0} env: PACKAGE_VARIANT: ${{ matrix.package_variant }} run: | - exit_on_error() { - echo "$1" - echo "Deleting '$RELEASE_VERSION_NAME' release and '$GITHUB_REF' tag" - hub release delete "$RELEASE_VERSION_NAME" - git push --delete origin "$GITHUB_REF" - exit 1 - } - - echo "Setting vars" RELEASE_VERSION_NAME="${GITHUB_REF/refs\/tags\//}" if ! printf "%s" "${RELEASE_VERSION_NAME/v/}" | grep -qP '^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$'; then - exit_on_error "The versionName '${RELEASE_VERSION_NAME/v/}' is not a valid version as per semantic version '2.0.0' spec in the format 'major.minor.patch(-prerelease)(+buildmetadata)'. https://semver.org/spec/v2.0.0.html." + echo "The versionName '${RELEASE_VERSION_NAME/v/}' is not a valid version as per semantic version '2.0.0' spec in the format 'major.minor.patch(-prerelease)(+buildmetadata)'. https://semver.org/spec/v2.0.0.html." + exit 1 fi APK_DIR_PATH="./app/build/outputs/apk/debug" @@ -47,38 +39,35 @@ jobs: echo "Building APKs for 'APK_VERSION_TAG' release" export TERMUX_APK_VERSION_TAG="$APK_VERSION_TAG" # Used by app/build.gradle export TERMUX_PACKAGE_VARIANT="${{ env.PACKAGE_VARIANT }}" # Used by app/build.gradle - if ! ./gradlew assembleDebug; then - exit_on_error "Build failed for '$APK_VERSION_TAG' release." - fi + ./gradlew assembleDebug echo "Validating APKs" for abi in universal arm64-v8a armeabi-v7a x86_64 x86; do if ! test -f "$APK_DIR_PATH/${APK_BASENAME_PREFIX}_$abi.apk"; then files_found="$(ls "$APK_DIR_PATH")" - exit_on_error "Failed to find built APK at '$APK_DIR_PATH/${APK_BASENAME_PREFIX}_$abi.apk'. Files found: "$'\n'"$files_found" + echo "Failed to find built APK at '$APK_DIR_PATH/${APK_BASENAME_PREFIX}_$abi.apk'. Files found: "$'\n'"$files_found" + exit 1 fi done - - echo "Generating sha25sums file" - if ! (cd "$APK_DIR_PATH"; sha256sum \ - "${APK_BASENAME_PREFIX}_universal.apk" \ - "${APK_BASENAME_PREFIX}_arm64-v8a.apk" \ - "${APK_BASENAME_PREFIX}_armeabi-v7a.apk" \ - "${APK_BASENAME_PREFIX}_x86_64.apk" \ - "${APK_BASENAME_PREFIX}_x86.apk" \ - > "${APK_BASENAME_PREFIX}_sha256sums"); then - exit_on_error "Generate sha25sums failed for '$APK_VERSION_TAG' release." - fi - - echo "Attaching APKs to github release" - 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 + - name: Upload APKs to GitHub artifact + uses: actions/upload-artifact@v3 + with: + name: ${{ matrix.package_variant }} + path: ./app/build/outputs/apk/debug/*.apk + upload: + runs-on: ubuntu-latest + needs: build + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - name: Download artifact + uses: actions/download-artifact@v3 + - name: Upload APKs to release + uses: termux/upload-release-action@v4.1.0 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: "**/*.apk" + file_glob: true + release_name: + tag: ${{ github.event.release.tag_name }} + checksums: sha256,sha512,md5 diff --git a/.github/workflows/debug_build.yml b/.github/workflows/debug_build.yml index efd08e8de7..8384a8d49b 100644 --- a/.github/workflows/debug_build.yml +++ b/.github/workflows/debug_build.yml @@ -18,7 +18,7 @@ jobs: steps: - name: Clone repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Build APKs shell: bash {0} @@ -78,7 +78,7 @@ jobs: fi - name: Attach universal APK file - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: ${{ env.APK_BASENAME_PREFIX }}_universal path: | @@ -86,7 +86,7 @@ jobs: ${{ env.APK_DIR_PATH }}/output-metadata.json - name: Attach arm64-v8a APK file - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: ${{ env.APK_BASENAME_PREFIX }}_arm64-v8a path: | @@ -94,7 +94,7 @@ jobs: ${{ env.APK_DIR_PATH }}/output-metadata.json - name: Attach armeabi-v7a APK file - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: ${{ env.APK_BASENAME_PREFIX }}_armeabi-v7a path: | @@ -102,7 +102,7 @@ jobs: ${{ env.APK_DIR_PATH }}/output-metadata.json - name: Attach x86_64 APK file - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: ${{ env.APK_BASENAME_PREFIX }}_x86_64 path: | @@ -110,7 +110,7 @@ jobs: ${{ env.APK_DIR_PATH }}/output-metadata.json - name: Attach x86 APK file - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: ${{ env.APK_BASENAME_PREFIX }}_x86 path: | @@ -118,7 +118,7 @@ jobs: ${{ env.APK_DIR_PATH }}/output-metadata.json - name: Attach sha256sums file - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: ${{ env.APK_BASENAME_PREFIX }}_sha256sums path: | diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml index 7c8edafc63..b384f85868 100644 --- a/.github/workflows/gradle-wrapper-validation.yml +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -15,5 +15,5 @@ jobs: name: "Validation" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: gradle/wrapper-validation-action@v1 diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 2647499554..fdc20787a0 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Clone repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Execute tests run: | ./gradlew test diff --git a/README.md b/README.md index 9f9c18d847..799182f5f8 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Quick how-to about Termux package management is available at [Package Management *** -**NOTICE: Termux is broken on Android 12.** Android OS will kill any (phantom) processes greater than 32 (limit is for all apps combined) and also kill any processes using excessive CPU. You may get `[Process completed (signal 9) - press Enter]` message in the terminal without actually exiting the shell process yourself. Check the related issue [#2366](https://github.com/termux/termux-app/issues/2366), [issue tracker](https://issuetracker.google.com/u/1/issues/205156966), [gist with details](https://gist.github.com/agnostic-apollo/dc7e47991c512755ff26bd2d31e72ca8) and [this TLDR comment](https://github.com/termux/termux-app/issues/2366#issuecomment-1009269410) on how to disable trimming of phantom processes. A proper docs page will be added later. An option to disable the killing should be available in Android 12L or 13, so upgrade at your own risk if you are on Android 11, specially if you are not rooted. +**NOTICE: Termux may be unstable on Android 12+.** Android OS will kill any (phantom) processes greater than 32 (limit is for all apps combined) and also kill any processes using excessive CPU. You may get `[Process completed (signal 9) - press Enter]` message in the terminal without actually exiting the shell process yourself. Check the related issue [#2366](https://github.com/termux/termux-app/issues/2366), [issue tracker](https://issuetracker.google.com/u/1/issues/205156966), [phantom cached and empty processes docs](https://github.com/agnostic-apollo/Android-Docs/blob/master/en/docs/apps/processes/phantom-cached-and-empty-processes.md) and [this TLDR comment](https://github.com/termux/termux-app/issues/2366#issuecomment-1237468220) on how to disable trimming of phantom and excessive cpu usage processes. A proper docs page will be added later. An option to disable the killing should be available in Android 12L or 13, so upgrade at your own risk if you are on Android 11, specially if you are not rooted. *** @@ -55,11 +55,11 @@ The core [Termux](https://github.com/termux/termux-app) app comes with the follo Latest version is `v0.118.0`. -**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 at https://termux.github.io/general/2022/02/15/termux-apps-vulnerability-disclosures.html. Also reminding [again](https://www.reddit.com/r/termux/comments/pkujfa/important_deprecation_notice_for_google_play) to users who have installed termux apps from google playstore that playstore builds are [deprecated](#google-play-store-deprecated) and no longer supported. It is recommended that you shift to F-Droid or Github releases.** +**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). Also reminding [again](https://www.reddit.com/r/termux/comments/pkujfa/important_deprecation_notice_for_google_play) to users who have installed termux apps from google playstore that playstore builds are [deprecated](#google-play-store-deprecated) and no longer supported. It is recommended that you shift to F-Droid or GitHub releases.** Termux can be obtained through various sources listed below for **only** Android `>= 7`. Support was dropped for Android `5` and `6` on [2020-01-01](https://www.reddit.com/r/termux/comments/dnzdbs/end_of_android56_support_on_20200101/) at `v0.83`, old builds are available on [archive.org](https://archive.org/details/termux-repositories-legacy). -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. +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. @@ -71,37 +71,60 @@ Termux application can be obtained from `F-Droid` from [here](https://f-droid.or You **do not** need to download the `F-Droid` app (via the `Download F-Droid` link) to install Termux. You can download the Termux APK directly from the site by clicking the `Download APK` link at the bottom of each version section. -It usually takes a few days (or even a week or more) for updates to be available on `F-Droid` once an update has been released on `Github`. The `F-Droid` releases are built and published by `F-Droid` once they [detect](https://gitlab.com/fdroid/fdroiddata/-/blob/master/metadata/com.termux.yml) a new `Github` release. The Termux maintainers **do not** have any control over the building and publishing of the Termux apps on `F-Droid`. Moreover, the Termux maintainers also do not have access to the APK signing keys of `F-Droid` releases, so we cannot release an APK ourselves on `Github` that would be compatible with `F-Droid` releases. +It usually takes a few days (or even a week or more) for updates to be available on `F-Droid` once an update has been released on `GitHub`. The `F-Droid` releases are built and published by `F-Droid` once they [detect](https://gitlab.com/fdroid/fdroiddata/-/blob/master/metadata/com.termux.yml) a new `GitHub` release. The Termux maintainers **do not** have any control over the building and publishing of the Termux apps on `F-Droid`. Moreover, the Termux maintainers also do not have access to the APK signing keys of `F-Droid` releases, so we cannot release an APK ourselves on `GitHub` that would be compatible with `F-Droid` releases. The `F-Droid` app often may not notify you of updates and you will manually have to do a pull down swipe action in the `Updates` tab of the app for it to check updates. Make sure battery optimizations are disabled for the app, check https://dontkillmyapp.com/ for details on how to do that. Only a universal APK is released, which will work on all supported architectures. The APK and bootstrap installation size will be `~180MB`. `F-Droid` does [not support](https://github.com/termux/termux-app/pull/1904) architecture specific APKs. -### Github +### GitHub -Termux application can be obtained on `Github` either from [`Github Releases`](https://github.com/termux/termux-app/releases) for version `>= 0.118.0` or from [`Github Build`](https://github.com/termux/termux-app/actions/workflows/debug_build.yml) action workflows. +Termux application can be obtained on `GitHub` either from [`GitHub Releases`](https://github.com/termux/termux-app/releases) for version `>= 0.118.0` or from [`GitHub Build`](https://github.com/termux/termux-app/actions/workflows/debug_build.yml) action workflows. -The APKs for `Github Releases` will be listed under `Assets` drop-down of a release. These are automatically attached when a new version is released. +The APKs for `GitHub Releases` will be listed under `Assets` drop-down of a release. These are automatically attached when a new version is released. -The APKs for `Github Build` action workflows will be listed under `Artifacts` section of a workflow run. These are created for each commit/push done to the repository and can be used by users who don't want to wait for releases and want to try out the latest features immediately or want to test their pull requests. Note that for action workflows, you need to be [**logged into a `Github` account**](https://github.com/login) for the `Artifacts` links to be enabled/clickable. If you are using the [`Github` app](https://github.com/mobile), then make sure to open workflow link in a browser like Chrome or Firefox that has your Github account logged in since the in-app browser may not be logged in. +The APKs for `GitHub Build` action workflows will be listed under `Artifacts` section of a workflow run. These are created for each commit/push done to the repository and can be used by users who don't want to wait for releases and want to try out the latest features immediately or want to test their pull requests. Note that for action workflows, you need to be [**logged into a `GitHub` account**](https://github.com/login) for the `Artifacts` links to be enabled/clickable. If you are using the [`GitHub` app](https://github.com/mobile), then make sure to open workflow link in a browser like Chrome or Firefox that has your GitHub account logged in since the in-app browser may not be logged in. The APKs for both of these are [`debuggable`](https://developer.android.com/studio/debug) and are compatible with each other but they are not compatible with other sources. Both universal and architecture specific APKs are released. The APK and bootstrap installation size will be `~180MB` if using universal and `~120MB` if using architecture specific. Check [here](https://github.com/termux/termux-app/issues/2153) for details. +**Security warning**: APK files on GitHub are signed with a test key that has been [shared with community](https://github.com/termux/termux-app/blob/master/app/testkey_untrusted.jks). This IS NOT an official developer key and everyone can use it to generate releases for own testing. Be very careful when using Termux GitHub builds obtained elsewhere except https://github.com/termux/termux-app. Everyone is able to use it to forge a malicious Termux update installable over the GitHub build. Think twice about installing Termux builds distributed via Telegram or other social media. If your device get caught by malware, we will not be able to help you. + +The [test key](https://github.com/termux/termux-app/blob/master/app/testkey_untrusted.jks) shall not be used to impersonate @termux and can't be used for this anyway. This key is not trusted by us and it is quite easy to detect its use in user generated content. + +Keystore information: +``` +Alias name: alias +Creation date: Oct 4, 2019 +Entry type: PrivateKeyEntry +Certificate chain length: 1 +Certificate[1]: +Owner: CN=APK Signer, OU=Earth, O=Earth +Issuer: CN=APK Signer, OU=Earth, O=Earth +Serial number: 29be297b +Valid from: Wed Sep 04 02:03:24 EEST 2019 until: Tue Oct 26 02:03:24 EEST 2049 +Certificate fingerprints: + SHA1: 51:79:55:EA:BF:69:FC:05:7C:41:C7:D3:79:DB:BC:EF:20:AD:85:F2 + SHA256: B6:DA:01:48:0E:EF:D5:FB:F2:CD:37:71:B8:D1:02:1E:C7:91:30:4B:DD:6C:4B:F4:1D:3F:AA:BA:D4:8E:E5:E1 +Signature algorithm name: SHA1withRSA (disabled) +Subject Public Key Algorithm: 2048-bit RSA key +Version: 3 +``` + ### Google Play Store **(Deprecated)** **Termux and its plugins are no longer updated on [Google Play Store](https://play.google.com/store/apps/details?id=com.termux) due to [android 10 issues](https://github.com/termux/termux-packages/wiki/Termux-and-Android-10) and have been deprecated.** The last version released for Android `>= 7` was `v0.101`. **It is highly recommended to not install Termux apps from Play Store any more.** -There are plans for **unpublishing** the Termux app and all its plugins on Play Store soon so that new users cannot install it and for **disabling** the Termux apps with updates so that existing users **cannot continue using outdated versions**. You are encouraged to move to `F-Droid` or `Github` builds as soon as possible. +**Termux developers do not have access to Play Store Console account where Termux is published and therefore can't remove the app.** You are encouraged to move to `F-Droid` or `GitHub` builds as soon as possible and suggest doing so for other users via social media. -You **will not need to buy plugins again** if you bought them on Play Store. All plugins are free on `F-Droid` and `Github`. +You **will not need to buy plugins again** if you bought them on Play Store. All plugins are free on `F-Droid` and `GitHub`. You can backup all your data under `$HOME/` and `$PREFIX/` before changing installation source, and then restore it afterwards, by following instructions at [Backing up Termux](https://wiki.termux.com/wiki/Backing_up_Termux) before the uninstallation. -There is currently no work being done to solve android `10` issues and *working* updates will not be resumed on Google Play Store any time soon. We will continue targeting sdk `28` for now. So there is not much point in staying on Play Store builds and waiting for updates to be resumed. If for some reason you don't want to move to `F-Droid` or `Github` sources for now, then at least check [Package Management](https://github.com/termux/termux-packages/wiki/Package-Management) to **change your mirror**, otherwise, you will get **`repository is under maintenance or down`** errors when running `apt` or `pkg` commands. After that, it is also **highly advisable** to run `pkg upgrade` command to update all packages to the latest available versions, or at least update `termux-tools` package with `pkg install termux-tools` command. +There is currently no work being done to solve android `10` issues and *working* updates will not be resumed on Google Play Store any time soon. We will continue targeting sdk `28` for now. So there is not much point in staying on Play Store builds and waiting for updates to be resumed. If for some reason you don't want to move to `F-Droid` or `GitHub` sources for now, then at least check [Package Management](https://github.com/termux/termux-packages/wiki/Package-Management) to **change your mirror**, otherwise, you will get **`repository is under maintenance or down`** errors when running `apt` or `pkg` commands. After that, it is also **highly advisable** to run `pkg upgrade` command to update all packages to the latest available versions, or at least update `termux-tools` package with `pkg install termux-tools` command. -Note that by upgrading old packages to latest versions, like that of `python` may break your setups/scripts since they may not be compatible anymore. Moreover, you will not be able to downgrade the package versions since termux repos only keep the latest version and you will have to manually rebuild the old versions of the packages if required as per https://github.com/termux/termux-packages/wiki/Building-packages. +Note that by upgrading old packages to latest versions, like that of `python` may break your setups/scripts since they may not be compatible anymore. Moreover, you will not be able to downgrade the package versions since termux repos only keep the latest version and you will have to manually rebuild the old versions of the packages if required as per [Building packages](https://github.com/termux/termux-packages/wiki/Building-packages). If you plan on staying on Play Store sources in future as well, then you may want to **disable automatic updates in Play Store** for Termux apps, since if and when updates to disable Termux apps are released, then **you will not be able to downgrade** and **will be forced** to move since apps won't work anymore. Only a way to backup `termux-app` data may be provided. The `termux-tools` [version `>= 0.135`](https://github.com/termux/termux-packages/pull/7493) will also show a banner at the top of the terminal saying `You are likely using a very old version of Termux, probably installed from the Google Play Store.`, you can remove it by running `rm -f /data/data/com.termux/files/usr/etc/motd-playstore` and restarting the app. @@ -114,7 +137,7 @@ If you plan on staying on Play Store sources in future as well, then you may wan - They should be disabled because deprecated things get removed and are not supported after some time, its the standard practice. It has been many months now since deprecation was announced and updates have not been released on Play Store since after `29 September 2020`. -- The new versions have lots of **new features and fixes** which you can mostly check out in the Changelog of [`Github Releases`](https://github.com/termux/termux-app/releases) that you may be missing out. Extra detail is usually provided in [commit messages](https://github.com/termux/termux-app/commits/master). +- The new versions have lots of **new features and fixes** which you can mostly check out in the Changelog of [`GitHub Releases`](https://github.com/termux/termux-app/releases) that you may be missing out. Extra detail is usually provided in [commit messages](https://github.com/termux/termux-app/commits/master). - Users on old versions are quite often reporting issues in multiple repositories and support forums that were **fixed months ago**, which we then have to deal with. The maintainers of @termux work in their free time, majorly for free, to work on development and provide support and having to re-re-deal with old issues takes away the already limited time from current work and is not possible to continue doing. Play Store page of `termux-app` has been filled with bad reviews of *"broken app"*, even though its clearly mentioned on the page that app is not being updated, yet users don't read and still install and report issues. @@ -251,7 +274,7 @@ Commit messages **must** use [Conventional Commits](https://www.conventionalcomm Changelogs for releases are generated based on [Keep a Changelog](https://github.com/olivierlacan/keep-a-changelog) specs. -The `versionName` in `build.gradle` files of Termux and its plugin apps must follow the [semantic version `2.0.0` spec](https://semver.org/spec/v2.0.0.html) in the format `major.minor.patch(-prerelease)(+buildmetadata)`. When bumping `versionName` in `build.gradle` files and when creating a tag for new releases on github, make sure to include the patch number as well, like `v0.1.0` instead of just `v0.1`. The `build.gradle` files and `attach_debug_apks_to_release` workflow validates the version as well and the build/attachment will fail if `versionName` does not follow the spec. +The `versionName` in `build.gradle` files of Termux and its plugin apps must follow the [semantic version `2.0.0` spec](https://semver.org/spec/v2.0.0.html) in the format `major.minor.patch(-prerelease)(+buildmetadata)`. When bumping `versionName` in `build.gradle` files and when creating a tag for new releases on GitHub, make sure to include the patch number as well, like `v0.1.0` instead of just `v0.1`. The `build.gradle` files and `attach_debug_apks_to_release` workflow validates the version as well and the build/attachment will fail if `versionName` does not follow the spec. ## diff --git a/app/build.gradle b/app/build.gradle index 1071633d04..3df93fef10 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,7 @@ android { signingConfigs { debug { - storeFile file('dev_keystore.jks') + storeFile file('testkey_untrusted.jks') keyAlias 'alias' storePassword 'xrj45yWGLbsO7W0v' keyPassword 'xrj45yWGLbsO7W0v' @@ -142,7 +142,7 @@ android { dependencies { testImplementation "junit:junit:4.13.2" - testImplementation "org.robolectric:robolectric:4.4" + testImplementation "org.robolectric:robolectric:4.10" coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.1.5" } diff --git a/app/src/main/java/com/termux/app/TermuxActivity.java b/app/src/main/java/com/termux/app/TermuxActivity.java index 1aa35a32e1..a2bb11e8c4 100644 --- a/app/src/main/java/com/termux/app/TermuxActivity.java +++ b/app/src/main/java/com/termux/app/TermuxActivity.java @@ -38,6 +38,7 @@ import com.termux.shared.activity.media.AppCompatActivityUtils; import com.termux.shared.data.IntentUtils; import com.termux.shared.android.PermissionUtils; +import com.termux.shared.data.DataUtils; import com.termux.shared.termux.TermuxConstants; import com.termux.shared.termux.TermuxConstants.TERMUX_APP.TERMUX_ACTIVITY; import com.termux.app.activities.HelpActivity; @@ -179,6 +180,7 @@ public final class TermuxActivity extends AppCompatActivity implements ServiceCo private static final int CONTEXT_MENU_SELECT_URL_ID = 0; private static final int CONTEXT_MENU_SHARE_TRANSCRIPT_ID = 1; + private static final int CONTEXT_MENU_SHARE_SELECTED_TEXT = 10; private static final int CONTEXT_MENU_AUTOFILL_ID = 2; private static final int CONTEXT_MENU_RESET_TERMINAL_ID = 3; private static final int CONTEXT_MENU_KILL_PROCESS_ID = 4; @@ -640,7 +642,10 @@ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuIn menu.add(Menu.NONE, CONTEXT_MENU_SELECT_URL_ID, Menu.NONE, R.string.action_select_url); menu.add(Menu.NONE, CONTEXT_MENU_SHARE_TRANSCRIPT_ID, Menu.NONE, R.string.action_share_transcript); - if (addAutoFillMenu) menu.add(Menu.NONE, CONTEXT_MENU_AUTOFILL_ID, Menu.NONE, R.string.action_autofill_password); + if (!DataUtils.isNullOrEmpty(mTerminalView.getStoredSelectedText())) + menu.add(Menu.NONE, CONTEXT_MENU_SHARE_SELECTED_TEXT, Menu.NONE, R.string.action_share_selected_text); + if (addAutoFillMenu) + menu.add(Menu.NONE, CONTEXT_MENU_AUTOFILL_ID, Menu.NONE, R.string.action_autofill_password); menu.add(Menu.NONE, CONTEXT_MENU_RESET_TERMINAL_ID, Menu.NONE, R.string.action_reset_terminal); menu.add(Menu.NONE, CONTEXT_MENU_KILL_PROCESS_ID, Menu.NONE, getResources().getString(R.string.action_kill_process, getCurrentSession().getPid())).setEnabled(currentSession.isRunning()); menu.add(Menu.NONE, CONTEXT_MENU_STYLING_ID, Menu.NONE, R.string.action_style_terminal); @@ -668,6 +673,9 @@ public boolean onContextItemSelected(MenuItem item) { case CONTEXT_MENU_SHARE_TRANSCRIPT_ID: mTermuxTerminalViewClient.shareSessionTranscript(); return true; + case CONTEXT_MENU_SHARE_SELECTED_TEXT: + mTermuxTerminalViewClient.shareSelectedText(); + return true; case CONTEXT_MENU_AUTOFILL_ID: requestAutoFill(); return true; @@ -697,6 +705,13 @@ public boolean onContextItemSelected(MenuItem item) { } } + @Override + public void onContextMenuClosed(Menu menu) { + super.onContextMenuClosed(menu); + // onContextMenuClosed() is triggered twice if back button is pressed to dismiss instead of tap for some reason + mTerminalView.onContextMenuClosed(menu); + } + private void showKillSessionDialog(TerminalSession session) { if (session == null) return; diff --git a/app/src/main/java/com/termux/app/terminal/TermuxTerminalSessionActivityClient.java b/app/src/main/java/com/termux/app/terminal/TermuxTerminalSessionActivityClient.java index e507cd192a..cd38163116 100644 --- a/app/src/main/java/com/termux/app/terminal/TermuxTerminalSessionActivityClient.java +++ b/app/src/main/java/com/termux/app/terminal/TermuxTerminalSessionActivityClient.java @@ -17,6 +17,7 @@ import androidx.annotation.Nullable; import com.termux.R; +import com.termux.shared.interact.ShareUtils; import com.termux.shared.termux.shell.command.runner.terminal.TermuxSession; import com.termux.shared.termux.interact.TextInputDialogUtils; import com.termux.app.TermuxActivity; @@ -183,20 +184,16 @@ public void onSessionFinished(@NonNull TerminalSession finishedSession) { public void onCopyTextToClipboard(@NonNull TerminalSession session, String text) { if (!mActivity.isVisible()) return; - ClipboardManager clipboard = (ClipboardManager) mActivity.getSystemService(Context.CLIPBOARD_SERVICE); - clipboard.setPrimaryClip(new ClipData(null, new String[]{"text/plain"}, new ClipData.Item(text))); + ShareUtils.copyTextToClipboard(mActivity, text); } @Override public void onPasteTextFromClipboard(@Nullable TerminalSession session) { if (!mActivity.isVisible()) return; - ClipboardManager clipboard = (ClipboardManager) mActivity.getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clipData = clipboard.getPrimaryClip(); - if (clipData != null) { - CharSequence paste = clipData.getItemAt(0).coerceToText(mActivity); - if (!TextUtils.isEmpty(paste)) mActivity.getTerminalView().mEmulator.paste(paste.toString()); - } + String text = ShareUtils.getTextStringFromClipboardIfSet(mActivity, true); + if (text != null) + mActivity.getTerminalView().mEmulator.paste(text); } @Override diff --git a/app/src/main/java/com/termux/app/terminal/TermuxTerminalViewClient.java b/app/src/main/java/com/termux/app/terminal/TermuxTerminalViewClient.java index 9c38cdbc08..a3d09d3d52 100644 --- a/app/src/main/java/com/termux/app/terminal/TermuxTerminalViewClient.java +++ b/app/src/main/java/com/termux/app/terminal/TermuxTerminalViewClient.java @@ -684,6 +684,13 @@ public void shareSessionTranscript() { transcriptText, mActivity.getString(R.string.title_share_transcript_with)); } + public void shareSelectedText() { + String selectedText = mActivity.getTerminalView().getStoredSelectedText(); + if (DataUtils.isNullOrEmpty(selectedText)) return; + ShareUtils.shareText(mActivity, mActivity.getString(R.string.title_share_selected_text), + selectedText, mActivity.getString(R.string.title_share_selected_text_with)); + } + public void showUrlSelection() { TerminalSession session = mActivity.getCurrentSession(); if (session == null) return; @@ -702,9 +709,7 @@ public void showUrlSelection() { // Click to copy url to clipboard: final AlertDialog dialog = new AlertDialog.Builder(mActivity).setItems(urls, (di, which) -> { String url = (String) urls[which]; - ClipboardManager clipboard = (ClipboardManager) mActivity.getSystemService(Context.CLIPBOARD_SERVICE); - clipboard.setPrimaryClip(new ClipData(null, new String[]{"text/plain"}, new ClipData.Item(url))); - Toast.makeText(mActivity, R.string.msg_select_url_copied_to_clipboard, Toast.LENGTH_LONG).show(); + ShareUtils.copyTextToClipboard(mActivity, url, mActivity.getString(R.string.msg_select_url_copied_to_clipboard)); }).setTitle(R.string.title_select_url_dialog).create(); // Long press to open URL: @@ -789,12 +794,9 @@ public void doPaste() { if (session == null) return; if (!session.isRunning()) return; - ClipboardManager clipboard = (ClipboardManager) mActivity.getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clipData = clipboard.getPrimaryClip(); - if (clipData == null) return; - CharSequence paste = clipData.getItemAt(0).coerceToText(mActivity); - if (!TextUtils.isEmpty(paste)) - session.getEmulator().paste(paste.toString()); + String text = ShareUtils.getTextStringFromClipboardIfSet(mActivity, true); + if (text != null) + session.getEmulator().paste(text); } } diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index 6192469fc4..61bc6d97e3 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -2,4 +2,5 @@ + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index 6192469fc4..61bc6d97e3 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -2,4 +2,5 @@ + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 642fbb5e07..794d8df3e0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -69,6 +69,10 @@ Terminal transcript Send transcript to: + Share selected text + Terminal Text + Send selected text to: + Autofill password Reset diff --git a/app/dev_keystore.jks b/app/testkey_untrusted.jks similarity index 100% rename from app/dev_keystore.jks rename to app/testkey_untrusted.jks diff --git a/terminal-emulator/src/main/java/com/termux/terminal/KeyHandler.java b/terminal-emulator/src/main/java/com/termux/terminal/KeyHandler.java index 254f5e3a7c..e6323c5992 100644 --- a/terminal-emulator/src/main/java/com/termux/terminal/KeyHandler.java +++ b/terminal-emulator/src/main/java/com/termux/terminal/KeyHandler.java @@ -227,9 +227,9 @@ public static String getCode(int keyCode, int keyMode, boolean cursorApp, boolea return transformForModifiers("\033[3", keyMode, '~'); case KEYCODE_PAGE_UP: - return "\033[5~"; + return transformForModifiers("\033[5", keyMode, '~'); case KEYCODE_PAGE_DOWN: - return "\033[6~"; + return transformForModifiers("\033[6", keyMode, '~'); case KEYCODE_DEL: String prefix = ((keyMode & KEYMOD_ALT) == 0) ? "" : "\033"; // Just do what xterm and gnome-terminal does: diff --git a/terminal-emulator/src/main/java/com/termux/terminal/WcWidth.java b/terminal-emulator/src/main/java/com/termux/terminal/WcWidth.java index 25660b30e8..5981c4a882 100644 --- a/terminal-emulator/src/main/java/com/termux/terminal/WcWidth.java +++ b/terminal-emulator/src/main/java/com/termux/terminal/WcWidth.java @@ -1,7 +1,7 @@ package com.termux.terminal; /** - * Implementation of wcwidth(3) for Unicode 9. + * Implementation of wcwidth(3) for Unicode 15. * * Implementation from https://github.com/jquast/wcwidth but we return 0 for unprintable characters. * @@ -9,12 +9,13 @@ * Must be kept in sync with the following: * https://github.com/termux/wcwidth * https://github.com/termux/libandroid-support - * https://github.com/termux/termux-packages/tree/master/libandroid-support + * https://github.com/termux/termux-packages/tree/master/packages/libandroid-support */ public final class WcWidth { // From https://github.com/jquast/wcwidth/blob/master/wcwidth/table_zero.py - // at commit b29897e5a1b403a0e36f7fc991614981cbc42475 (2020-07-14): + // from https://github.com/jquast/wcwidth/pull/64 + // at commit 1b9b6585b0080ea5cb88dc9815796505724793fe (2022-12-16): private static final int[][] ZERO_WIDTH = { {0x00300, 0x0036f}, // Combining Grave Accent ..Combining Latin Small Le {0x00483, 0x00489}, // Combining Cyrillic Titlo..Combining Cyrillic Milli @@ -40,7 +41,8 @@ public final class WcWidth { {0x00825, 0x00827}, // Samaritan Vowel Sign Sho..Samaritan Vowel Sign U {0x00829, 0x0082d}, // Samaritan Vowel Sign Lon..Samaritan Mark Nequdaa {0x00859, 0x0085b}, // Mandaic Affrication Mark..Mandaic Gemination Mark - {0x008d3, 0x008e1}, // Arabic Small Low Waw ..Arabic Small High Sign S + {0x00898, 0x0089f}, // Arabic Small High Word A..Arabic Half Madda Over M + {0x008ca, 0x008e1}, // Arabic Small High Farsi ..Arabic Small High Sign S {0x008e3, 0x00902}, // Arabic Turned Damma Belo..Devanagari Sign Anusvara {0x0093a, 0x0093a}, // Devanagari Vowel Sign Oe..Devanagari Vowel Sign Oe {0x0093c, 0x0093c}, // Devanagari Sign Nukta ..Devanagari Sign Nukta @@ -74,13 +76,14 @@ public final class WcWidth { {0x00b3f, 0x00b3f}, // Oriya Vowel Sign I ..Oriya Vowel Sign I {0x00b41, 0x00b44}, // Oriya Vowel Sign U ..Oriya Vowel Sign Vocalic {0x00b4d, 0x00b4d}, // Oriya Sign Virama ..Oriya Sign Virama - {0x00b55, 0x00b56}, // (nil) ..Oriya Ai Length Mark + {0x00b55, 0x00b56}, // Oriya Sign Overline ..Oriya Ai Length Mark {0x00b62, 0x00b63}, // Oriya Vowel Sign Vocalic..Oriya Vowel Sign Vocalic {0x00b82, 0x00b82}, // Tamil Sign Anusvara ..Tamil Sign Anusvara {0x00bc0, 0x00bc0}, // Tamil Vowel Sign Ii ..Tamil Vowel Sign Ii {0x00bcd, 0x00bcd}, // Tamil Sign Virama ..Tamil Sign Virama {0x00c00, 0x00c00}, // Telugu Sign Combining Ca..Telugu Sign Combining Ca {0x00c04, 0x00c04}, // Telugu Sign Combining An..Telugu Sign Combining An + {0x00c3c, 0x00c3c}, // Telugu Sign Nukta ..Telugu Sign Nukta {0x00c3e, 0x00c40}, // Telugu Vowel Sign Aa ..Telugu Vowel Sign Ii {0x00c46, 0x00c48}, // Telugu Vowel Sign E ..Telugu Vowel Sign Ai {0x00c4a, 0x00c4d}, // Telugu Vowel Sign O ..Telugu Sign Virama @@ -97,7 +100,7 @@ public final class WcWidth { {0x00d41, 0x00d44}, // Malayalam Vowel Sign U ..Malayalam Vowel Sign Voc {0x00d4d, 0x00d4d}, // Malayalam Sign Virama ..Malayalam Sign Virama {0x00d62, 0x00d63}, // Malayalam Vowel Sign Voc..Malayalam Vowel Sign Voc - {0x00d81, 0x00d81}, // (nil) ..(nil) + {0x00d81, 0x00d81}, // Sinhala Sign Candrabindu..Sinhala Sign Candrabindu {0x00dca, 0x00dca}, // Sinhala Sign Al-lakuna ..Sinhala Sign Al-lakuna {0x00dd2, 0x00dd4}, // Sinhala Vowel Sign Ketti..Sinhala Vowel Sign Ketti {0x00dd6, 0x00dd6}, // Sinhala Vowel Sign Diga ..Sinhala Vowel Sign Diga @@ -106,7 +109,7 @@ public final class WcWidth { {0x00e47, 0x00e4e}, // Thai Character Maitaikhu..Thai Character Yamakkan {0x00eb1, 0x00eb1}, // Lao Vowel Sign Mai Kan ..Lao Vowel Sign Mai Kan {0x00eb4, 0x00ebc}, // Lao Vowel Sign I ..Lao Semivowel Sign Lo - {0x00ec8, 0x00ecd}, // Lao Tone Mai Ek ..Lao Niggahita + {0x00ec8, 0x00ece}, // Lao Tone Mai Ek ..(nil) {0x00f18, 0x00f19}, // Tibetan Astrological Sig..Tibetan Astrological Sig {0x00f35, 0x00f35}, // Tibetan Mark Ngas Bzung ..Tibetan Mark Ngas Bzung {0x00f37, 0x00f37}, // Tibetan Mark Ngas Bzung ..Tibetan Mark Ngas Bzung @@ -130,7 +133,7 @@ public final class WcWidth { {0x0109d, 0x0109d}, // Myanmar Vowel Sign Aiton..Myanmar Vowel Sign Aiton {0x0135d, 0x0135f}, // Ethiopic Combining Gemin..Ethiopic Combining Gemin {0x01712, 0x01714}, // Tagalog Vowel Sign I ..Tagalog Sign Virama - {0x01732, 0x01734}, // Hanunoo Vowel Sign I ..Hanunoo Sign Pamudpod + {0x01732, 0x01733}, // Hanunoo Vowel Sign I ..Hanunoo Vowel Sign U {0x01752, 0x01753}, // Buhid Vowel Sign I ..Buhid Vowel Sign U {0x01772, 0x01773}, // Tagbanwa Vowel Sign I ..Tagbanwa Vowel Sign U {0x017b4, 0x017b5}, // Khmer Vowel Inherent Aq ..Khmer Vowel Inherent Aa @@ -139,6 +142,7 @@ public final class WcWidth { {0x017c9, 0x017d3}, // Khmer Sign Muusikatoan ..Khmer Sign Bathamasat {0x017dd, 0x017dd}, // Khmer Sign Atthacan ..Khmer Sign Atthacan {0x0180b, 0x0180d}, // Mongolian Free Variation..Mongolian Free Variation + {0x0180f, 0x0180f}, // Mongolian Free Variation..Mongolian Free Variation {0x01885, 0x01886}, // Mongolian Letter Ali Gal..Mongolian Letter Ali Gal {0x018a9, 0x018a9}, // Mongolian Letter Ali Gal..Mongolian Letter Ali Gal {0x01920, 0x01922}, // Limbu Vowel Sign A ..Limbu Vowel Sign U @@ -154,7 +158,7 @@ public final class WcWidth { {0x01a65, 0x01a6c}, // Tai Tham Vowel Sign I ..Tai Tham Vowel Sign Oa B {0x01a73, 0x01a7c}, // Tai Tham Vowel Sign Oa A..Tai Tham Sign Khuen-lue {0x01a7f, 0x01a7f}, // Tai Tham Combining Crypt..Tai Tham Combining Crypt - {0x01ab0, 0x01ac0}, // Combining Doubled Circum..(nil) + {0x01ab0, 0x01ace}, // Combining Doubled Circum..Combining Latin Small Le {0x01b00, 0x01b03}, // Balinese Sign Ulu Ricem ..Balinese Sign Surang {0x01b34, 0x01b34}, // Balinese Sign Rerekan ..Balinese Sign Rerekan {0x01b36, 0x01b3a}, // Balinese Vowel Sign Ulu ..Balinese Vowel Sign Ra R @@ -177,8 +181,7 @@ public final class WcWidth { {0x01ced, 0x01ced}, // Vedic Sign Tiryak ..Vedic Sign Tiryak {0x01cf4, 0x01cf4}, // Vedic Tone Candra Above ..Vedic Tone Candra Above {0x01cf8, 0x01cf9}, // Vedic Tone Ring Above ..Vedic Tone Double Ring A - {0x01dc0, 0x01df9}, // Combining Dotted Grave A..Combining Wide Inverted - {0x01dfb, 0x01dff}, // Combining Deletion Mark ..Combining Right Arrowhea + {0x01dc0, 0x01dff}, // Combining Dotted Grave A..Combining Right Arrowhea {0x020d0, 0x020f0}, // Combining Left Harpoon A..Combining Asterisk Above {0x02cef, 0x02cf1}, // Coptic Combining Ni Abov..Coptic Combining Spiritu {0x02d7f, 0x02d7f}, // Tifinagh Consonant Joine..Tifinagh Consonant Joine @@ -193,7 +196,7 @@ public final class WcWidth { {0x0a806, 0x0a806}, // Syloti Nagri Sign Hasant..Syloti Nagri Sign Hasant {0x0a80b, 0x0a80b}, // Syloti Nagri Sign Anusva..Syloti Nagri Sign Anusva {0x0a825, 0x0a826}, // Syloti Nagri Vowel Sign ..Syloti Nagri Vowel Sign - {0x0a82c, 0x0a82c}, // (nil) ..(nil) + {0x0a82c, 0x0a82c}, // Syloti Nagri Sign Altern..Syloti Nagri Sign Altern {0x0a8c4, 0x0a8c5}, // Saurashtra Sign Virama ..Saurashtra Sign Candrabi {0x0a8e0, 0x0a8f1}, // Combining Devanagari Dig..Combining Devanagari Sig {0x0a8ff, 0x0a8ff}, // Devanagari Vowel Sign Ay..Devanagari Vowel Sign Ay @@ -233,13 +236,18 @@ public final class WcWidth { {0x10a3f, 0x10a3f}, // Kharoshthi Virama ..Kharoshthi Virama {0x10ae5, 0x10ae6}, // Manichaean Abbreviation ..Manichaean Abbreviation {0x10d24, 0x10d27}, // Hanifi Rohingya Sign Har..Hanifi Rohingya Sign Tas - {0x10eab, 0x10eac}, // (nil) ..(nil) + {0x10eab, 0x10eac}, // Yezidi Combining Hamza M..Yezidi Combining Madda M + {0x10efd, 0x10eff}, // (nil) ..(nil) {0x10f46, 0x10f50}, // Sogdian Combining Dot Be..Sogdian Combining Stroke + {0x10f82, 0x10f85}, // Old Uyghur Combining Dot..Old Uyghur Combining Two {0x11001, 0x11001}, // Brahmi Sign Anusvara ..Brahmi Sign Anusvara {0x11038, 0x11046}, // Brahmi Vowel Sign Aa ..Brahmi Virama + {0x11070, 0x11070}, // Brahmi Sign Old Tamil Vi..Brahmi Sign Old Tamil Vi + {0x11073, 0x11074}, // Brahmi Vowel Sign Old Ta..Brahmi Vowel Sign Old Ta {0x1107f, 0x11081}, // Brahmi Number Joiner ..Kaithi Sign Anusvara {0x110b3, 0x110b6}, // Kaithi Vowel Sign U ..Kaithi Vowel Sign Ai {0x110b9, 0x110ba}, // Kaithi Sign Virama ..Kaithi Sign Nukta + {0x110c2, 0x110c2}, // Kaithi Vowel Sign Vocali..Kaithi Vowel Sign Vocali {0x11100, 0x11102}, // Chakma Sign Candrabindu ..Chakma Sign Visarga {0x11127, 0x1112b}, // Chakma Vowel Sign A ..Chakma Vowel Sign Uu {0x1112d, 0x11134}, // Chakma Vowel Sign Ai ..Chakma Maayyaa @@ -247,11 +255,12 @@ public final class WcWidth { {0x11180, 0x11181}, // Sharada Sign Candrabindu..Sharada Sign Anusvara {0x111b6, 0x111be}, // Sharada Vowel Sign U ..Sharada Vowel Sign O {0x111c9, 0x111cc}, // Sharada Sandhi Mark ..Sharada Extra Short Vowe - {0x111cf, 0x111cf}, // (nil) ..(nil) + {0x111cf, 0x111cf}, // Sharada Sign Inverted Ca..Sharada Sign Inverted Ca {0x1122f, 0x11231}, // Khojki Vowel Sign U ..Khojki Vowel Sign Ai {0x11234, 0x11234}, // Khojki Sign Anusvara ..Khojki Sign Anusvara {0x11236, 0x11237}, // Khojki Sign Nukta ..Khojki Sign Shadda {0x1123e, 0x1123e}, // Khojki Sign Sukun ..Khojki Sign Sukun + {0x11241, 0x11241}, // (nil) ..(nil) {0x112df, 0x112df}, // Khudawadi Sign Anusvara ..Khudawadi Sign Anusvara {0x112e3, 0x112ea}, // Khudawadi Vowel Sign U ..Khudawadi Sign Virama {0x11300, 0x11301}, // Grantha Sign Combining A..Grantha Sign Candrabindu @@ -283,9 +292,9 @@ public final class WcWidth { {0x11727, 0x1172b}, // Ahom Vowel Sign Aw ..Ahom Sign Killer {0x1182f, 0x11837}, // Dogra Vowel Sign U ..Dogra Sign Anusvara {0x11839, 0x1183a}, // Dogra Sign Virama ..Dogra Sign Nukta - {0x1193b, 0x1193c}, // (nil) ..(nil) - {0x1193e, 0x1193e}, // (nil) ..(nil) - {0x11943, 0x11943}, // (nil) ..(nil) + {0x1193b, 0x1193c}, // Dives Akuru Sign Anusvar..Dives Akuru Sign Candrab + {0x1193e, 0x1193e}, // Dives Akuru Virama ..Dives Akuru Virama + {0x11943, 0x11943}, // Dives Akuru Sign Nukta ..Dives Akuru Sign Nukta {0x119d4, 0x119d7}, // Nandinagari Vowel Sign U..Nandinagari Vowel Sign V {0x119da, 0x119db}, // Nandinagari Vowel Sign E..Nandinagari Vowel Sign A {0x119e0, 0x119e0}, // Nandinagari Sign Virama ..Nandinagari Sign Virama @@ -313,12 +322,20 @@ public final class WcWidth { {0x11d95, 0x11d95}, // Gunjala Gondi Sign Anusv..Gunjala Gondi Sign Anusv {0x11d97, 0x11d97}, // Gunjala Gondi Virama ..Gunjala Gondi Virama {0x11ef3, 0x11ef4}, // Makasar Vowel Sign I ..Makasar Vowel Sign U + {0x11f00, 0x11f01}, // (nil) ..(nil) + {0x11f36, 0x11f3a}, // (nil) ..(nil) + {0x11f40, 0x11f40}, // (nil) ..(nil) + {0x11f42, 0x11f42}, // (nil) ..(nil) + {0x13440, 0x13440}, // (nil) ..(nil) + {0x13447, 0x13455}, // (nil) ..(nil) {0x16af0, 0x16af4}, // Bassa Vah Combining High..Bassa Vah Combining High {0x16b30, 0x16b36}, // Pahawh Hmong Mark Cim Tu..Pahawh Hmong Mark Cim Ta {0x16f4f, 0x16f4f}, // Miao Sign Consonant Modi..Miao Sign Consonant Modi {0x16f8f, 0x16f92}, // Miao Tone Right ..Miao Tone Below - {0x16fe4, 0x16fe4}, // (nil) ..(nil) + {0x16fe4, 0x16fe4}, // Khitan Small Script Fill..Khitan Small Script Fill {0x1bc9d, 0x1bc9e}, // Duployan Thick Letter Se..Duployan Double Mark + {0x1cf00, 0x1cf2d}, // Znamenny Combining Mark ..Znamenny Combining Mark + {0x1cf30, 0x1cf46}, // Znamenny Combining Tonal..Znamenny Priznak Modifie {0x1d167, 0x1d169}, // Musical Symbol Combining..Musical Symbol Combining {0x1d17b, 0x1d182}, // Musical Symbol Combining..Musical Symbol Combining {0x1d185, 0x1d18b}, // Musical Symbol Combining..Musical Symbol Combining @@ -335,15 +352,19 @@ public final class WcWidth { {0x1e01b, 0x1e021}, // Combining Glagolitic Let..Combining Glagolitic Let {0x1e023, 0x1e024}, // Combining Glagolitic Let..Combining Glagolitic Let {0x1e026, 0x1e02a}, // Combining Glagolitic Let..Combining Glagolitic Let + {0x1e08f, 0x1e08f}, // (nil) ..(nil) {0x1e130, 0x1e136}, // Nyiakeng Puachue Hmong T..Nyiakeng Puachue Hmong T + {0x1e2ae, 0x1e2ae}, // Toto Sign Rising Tone ..Toto Sign Rising Tone {0x1e2ec, 0x1e2ef}, // Wancho Tone Tup ..Wancho Tone Koini + {0x1e4ec, 0x1e4ef}, // (nil) ..(nil) {0x1e8d0, 0x1e8d6}, // Mende Kikakui Combining ..Mende Kikakui Combining {0x1e944, 0x1e94a}, // Adlam Alif Lengthener ..Adlam Nukta {0xe0100, 0xe01ef}, // Variation Selector-17 ..Variation Selector-256 }; // https://github.com/jquast/wcwidth/blob/master/wcwidth/table_wide.py - // at commit b29897e5a1b403a0e36f7fc991614981cbc42475 (2020-07-14): + // from https://github.com/jquast/wcwidth/pull/64 + // at commit 1b9b6585b0080ea5cb88dc9815796505724793fe (2022-12-16): private static final int[][] WIDE_EASTASIAN = { {0x01100, 0x0115f}, // Hangul Choseong Kiyeok ..Hangul Choseong Filler {0x0231a, 0x0231b}, // Watch ..Hourglass @@ -392,7 +413,7 @@ public final class WcWidth { {0x03190, 0x031e3}, // Ideographic Annotation L..Cjk Stroke Q {0x031f0, 0x0321e}, // Katakana Letter Small Ku..Parenthesized Korean Cha {0x03220, 0x03247}, // Parenthesized Ideograph ..Circled Ideograph Koto - {0x03250, 0x04dbf}, // Partnership Sign ..(nil) + {0x03250, 0x04dbf}, // Partnership Sign ..Cjk Unified Ideograph-4d {0x04e00, 0x0a48c}, // Cjk Unified Ideograph-4e..Yi Syllable Yyr {0x0a490, 0x0a4c6}, // Yi Radical Qot ..Yi Radical Ke {0x0a960, 0x0a97c}, // Hangul Choseong Tikeut-m..Hangul Choseong Ssangyeo @@ -404,13 +425,18 @@ public final class WcWidth { {0x0fe68, 0x0fe6b}, // Small Reverse Solidus ..Small Commercial At {0x0ff01, 0x0ff60}, // Fullwidth Exclamation Ma..Fullwidth Right White Pa {0x0ffe0, 0x0ffe6}, // Fullwidth Cent Sign ..Fullwidth Won Sign - {0x16fe0, 0x16fe4}, // Tangut Iteration Mark ..(nil) - {0x16ff0, 0x16ff1}, // (nil) ..(nil) + {0x16fe0, 0x16fe4}, // Tangut Iteration Mark ..Khitan Small Script Fill + {0x16ff0, 0x16ff1}, // Vietnamese Alternate Rea..Vietnamese Alternate Rea {0x17000, 0x187f7}, // (nil) ..(nil) - {0x18800, 0x18cd5}, // Tangut Component-001 ..(nil) + {0x18800, 0x18cd5}, // Tangut Component-001 ..Khitan Small Script Char {0x18d00, 0x18d08}, // (nil) ..(nil) - {0x1b000, 0x1b11e}, // Katakana Letter Archaic ..Hentaigana Letter N-mu-m + {0x1aff0, 0x1aff3}, // Katakana Letter Minnan T..Katakana Letter Minnan T + {0x1aff5, 0x1affb}, // Katakana Letter Minnan T..Katakana Letter Minnan N + {0x1affd, 0x1affe}, // Katakana Letter Minnan N..Katakana Letter Minnan N + {0x1b000, 0x1b122}, // Katakana Letter Archaic ..Katakana Letter Archaic + {0x1b132, 0x1b132}, // (nil) ..(nil) {0x1b150, 0x1b152}, // Hiragana Letter Small Wi..Hiragana Letter Small Wo + {0x1b155, 0x1b155}, // (nil) ..(nil) {0x1b164, 0x1b167}, // Katakana Letter Small Wi..Katakana Letter Small N {0x1b170, 0x1b2fb}, // Nushu Character-1b170 ..Nushu Character-1b2fb {0x1f004, 0x1f004}, // Mahjong Tile Red Dragon ..Mahjong Tile Red Dragon @@ -443,24 +469,24 @@ public final class WcWidth { {0x1f680, 0x1f6c5}, // Rocket ..Left Luggage {0x1f6cc, 0x1f6cc}, // Sleeping Accommodation ..Sleeping Accommodation {0x1f6d0, 0x1f6d2}, // Place Of Worship ..Shopping Trolley - {0x1f6d5, 0x1f6d7}, // Hindu Temple ..(nil) + {0x1f6d5, 0x1f6d7}, // Hindu Temple ..Elevator + {0x1f6dc, 0x1f6df}, // (nil) ..Ring Buoy {0x1f6eb, 0x1f6ec}, // Airplane Departure ..Airplane Arriving - {0x1f6f4, 0x1f6fc}, // Scooter ..(nil) + {0x1f6f4, 0x1f6fc}, // Scooter ..Roller Skate {0x1f7e0, 0x1f7eb}, // Large Orange Circle ..Large Brown Square - {0x1f90c, 0x1f93a}, // (nil) ..Fencer + {0x1f7f0, 0x1f7f0}, // Heavy Equals Sign ..Heavy Equals Sign + {0x1f90c, 0x1f93a}, // Pinched Fingers ..Fencer {0x1f93c, 0x1f945}, // Wrestlers ..Goal Net - {0x1f947, 0x1f978}, // First Place Medal ..(nil) - {0x1f97a, 0x1f9cb}, // Face With Pleading Eyes ..(nil) - {0x1f9cd, 0x1f9ff}, // Standing Person ..Nazar Amulet - {0x1fa70, 0x1fa74}, // Ballet Shoes ..(nil) - {0x1fa78, 0x1fa7a}, // Drop Of Blood ..Stethoscope - {0x1fa80, 0x1fa86}, // Yo-yo ..(nil) - {0x1fa90, 0x1faa8}, // Ringed Planet ..(nil) - {0x1fab0, 0x1fab6}, // (nil) ..(nil) - {0x1fac0, 0x1fac2}, // (nil) ..(nil) - {0x1fad0, 0x1fad6}, // (nil) ..(nil) + {0x1f947, 0x1f9ff}, // First Place Medal ..Nazar Amulet + {0x1fa70, 0x1fa7c}, // Ballet Shoes ..Crutch + {0x1fa80, 0x1fa88}, // Yo-yo ..(nil) + {0x1fa90, 0x1fabd}, // Ringed Planet ..(nil) + {0x1fabf, 0x1fac5}, // (nil) ..Person With Crown + {0x1face, 0x1fadb}, // (nil) ..(nil) + {0x1fae0, 0x1fae8}, // Melting Face ..(nil) + {0x1faf0, 0x1faf8}, // Hand With Index Finger A..(nil) {0x20000, 0x2fffd}, // Cjk Unified Ideograph-20..(nil) - {0x30000, 0x3fffd}, // (nil) ..(nil) + {0x30000, 0x3fffd}, // Cjk Unified Ideograph-30..(nil) }; diff --git a/terminal-view/src/main/java/com/termux/view/TerminalView.java b/terminal-view/src/main/java/com/termux/view/TerminalView.java index b96b0426b1..586cd63391 100644 --- a/terminal-view/src/main/java/com/termux/view/TerminalView.java +++ b/terminal-view/src/main/java/com/termux/view/TerminalView.java @@ -2,6 +2,7 @@ import android.annotation.SuppressLint; import android.annotation.TargetApi; +import android.app.Activity; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; @@ -10,6 +11,7 @@ import android.os.Build; import android.os.Handler; import android.os.Looper; +import android.os.SystemClock; import android.text.Editable; import android.text.InputType; import android.text.TextUtils; @@ -19,6 +21,7 @@ import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyEvent; +import android.view.Menu; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; @@ -30,6 +33,7 @@ import android.view.inputmethod.InputConnection; import android.widget.Scroller; +import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import com.termux.terminal.KeyHandler; @@ -456,6 +460,14 @@ public void onScreenUpdated(boolean skipScrolling) { if (mAccessibilityEnabled) setContentDescription(getText()); } + /** This must be called by the hosting activity in {@link Activity#onContextMenuClosed(Menu)} + * when context menu for the {@link TerminalView} is started by + * {@link TextSelectionCursorController#ACTION_MORE} is closed. */ + public void onContextMenuClosed(Menu menu) { + // Unset the stored text since it shouldn't be used anymore and should be cleared from memory + unsetStoredSelectedText(); + } + /** * Sets the text size, which in turn sets the number of rows and columns. * @@ -566,11 +578,14 @@ public boolean onTouchEvent(MotionEvent event) { if (action == MotionEvent.ACTION_DOWN) showContextMenu(); return true; } else if (event.isButtonPressed(MotionEvent.BUTTON_TERTIARY)) { - ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clipData = clipboard.getPrimaryClip(); + ClipboardManager clipboardManager = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clipData = clipboardManager.getPrimaryClip(); if (clipData != null) { - CharSequence paste = clipData.getItemAt(0).coerceToText(getContext()); - if (!TextUtils.isEmpty(paste)) mEmulator.paste(paste.toString()); + ClipData.Item clipItem = clipData.getItemAt(0); + if (clipItem != null) { + CharSequence text = clipItem.coerceToText(getContext()); + if (!TextUtils.isEmpty(text)) mEmulator.paste(text.toString()); + } } } else if (mEmulator.isMouseTrackingActive()) { // BUTTON_PRIMARY. switch (event.getAction()) { @@ -858,6 +873,9 @@ public boolean handleKeyCode(int keyCode, int keyMod) { if (mEmulator != null) mEmulator.setCursorBlinkState(true); + if (handleKeyCodeAction(keyCode, keyMod)) + return true; + TerminalEmulator term = mTermSession.getEmulator(); String code = KeyHandler.getCode(keyCode, keyMod, term.isCursorKeysApplicationMode(), term.isKeypadApplicationMode()); if (code == null) return false; @@ -865,6 +883,26 @@ public boolean handleKeyCode(int keyCode, int keyMod) { return true; } + public boolean handleKeyCodeAction(int keyCode, int keyMod) { + boolean shiftDown = (keyMod & KeyHandler.KEYMOD_SHIFT) != 0; + + switch (keyCode) { + case KeyEvent.KEYCODE_PAGE_UP: + case KeyEvent.KEYCODE_PAGE_DOWN: + // shift+page_up and shift+page_down should scroll scrollback history instead of + // scrolling command history or changing pages + if (shiftDown) { + long time = SystemClock.uptimeMillis(); + MotionEvent motionEvent = MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN, 0, 0, 0); + doScroll(motionEvent, keyCode == KeyEvent.KEYCODE_PAGE_UP ? -1 : 1); + motionEvent.recycle(); + return true; + } + } + + return false; + } + /** * Called when a key is released in the view. * @@ -1203,6 +1241,25 @@ public boolean isSelectingText() { } } + /** Get the currently selected text if selecting. */ + public String getSelectedText() { + if (isSelectingText() && mTextSelectionCursorController != null) + return mTextSelectionCursorController.getSelectedText(); + else + return null; + } + + /** Get the selected text stored before "MORE" button was pressed on the context menu. */ + @Nullable + public String getStoredSelectedText() { + return mTextSelectionCursorController != null ? mTextSelectionCursorController.getStoredSelectedText() : null; + } + + /** Unset the selected text stored before "MORE" button was pressed on the context menu. */ + public void unsetStoredSelectedText() { + if (mTextSelectionCursorController != null) mTextSelectionCursorController.unsetStoredSelectedText(); + } + private ActionMode getTextSelectionActionMode() { if (mTextSelectionCursorController != null) { return mTextSelectionCursorController.getActionMode(); diff --git a/terminal-view/src/main/java/com/termux/view/textselection/TextSelectionCursorController.java b/terminal-view/src/main/java/com/termux/view/textselection/TextSelectionCursorController.java index c08dca4470..c2cd7c6c0a 100644 --- a/terminal-view/src/main/java/com/termux/view/textselection/TextSelectionCursorController.java +++ b/terminal-view/src/main/java/com/termux/view/textselection/TextSelectionCursorController.java @@ -11,6 +11,8 @@ import android.view.MotionEvent; import android.view.View; +import androidx.annotation.Nullable; + import com.termux.terminal.TerminalBuffer; import com.termux.terminal.WcWidth; import com.termux.view.R; @@ -20,6 +22,7 @@ public class TextSelectionCursorController implements CursorController { private final TerminalView terminalView; private final TextSelectionHandleView mStartHandle, mEndHandle; + private String mStoredSelectedText; private boolean mIsSelectingText = false; private long mShowStartTime = System.currentTimeMillis(); @@ -27,9 +30,9 @@ public class TextSelectionCursorController implements CursorController { private int mSelX1 = -1, mSelX2 = -1, mSelY1 = -1, mSelY2 = -1; private ActionMode mActionMode; - private final int ACTION_COPY = 1; - private final int ACTION_PASTE = 2; - private final int ACTION_MORE = 3; + public final int ACTION_COPY = 1; + public final int ACTION_PASTE = 2; + public final int ACTION_MORE = 3; public TextSelectionCursorController(TerminalView terminalView) { this.terminalView = terminalView; @@ -112,7 +115,7 @@ public boolean onCreateActionMode(ActionMode mode, Menu menu) { ClipboardManager clipboard = (ClipboardManager) terminalView.getContext().getSystemService(Context.CLIPBOARD_SERVICE); menu.add(Menu.NONE, ACTION_COPY, Menu.NONE, R.string.copy_text).setShowAsAction(show); - menu.add(Menu.NONE, ACTION_PASTE, Menu.NONE, R.string.paste_text).setEnabled(clipboard.hasPrimaryClip()).setShowAsAction(show); + menu.add(Menu.NONE, ACTION_PASTE, Menu.NONE, R.string.paste_text).setEnabled(clipboard != null && clipboard.hasPrimaryClip()).setShowAsAction(show); menu.add(Menu.NONE, ACTION_MORE, Menu.NONE, R.string.text_selection_more); return true; } @@ -131,7 +134,7 @@ public boolean onActionItemClicked(ActionMode mode, MenuItem item) { switch (item.getItemId()) { case ACTION_COPY: - String selectedText = terminalView.mEmulator.getSelectedText(mSelX1, mSelY1, mSelX2, mSelY2).trim(); + String selectedText = getSelectedText(); terminalView.mTermSession.onCopyTextToClipboard(selectedText); terminalView.stopTextSelectionMode(); break; @@ -140,7 +143,13 @@ public boolean onActionItemClicked(ActionMode mode, MenuItem item) { terminalView.mTermSession.onPasteTextFromClipboard(); break; case ACTION_MORE: - terminalView.stopTextSelectionMode(); //we stop text selection first, otherwise handles will show above popup + // We first store the selected text in case TerminalViewClient needs the + // selected text before MORE button was pressed since we are going to + // stop selection mode + mStoredSelectedText = getSelectedText(); + // The text selection needs to be stopped before showing context menu, + // otherwise handles will show above popup + terminalView.stopTextSelectionMode(); terminalView.showContextMenu(); break; } @@ -361,6 +370,22 @@ public void getSelectors(int[] sel) { sel[3] = mSelX2; } + /** Get the currently selected text. */ + public String getSelectedText() { + return terminalView.mEmulator.getSelectedText(mSelX1, mSelY1, mSelX2, mSelY2); + } + + /** Get the selected text stored before "MORE" button was pressed on the context menu. */ + @Nullable + public String getStoredSelectedText() { + return mStoredSelectedText; + } + + /** Unset the selected text stored before "MORE" button was pressed on the context menu. */ + public void unsetStoredSelectedText() { + mStoredSelectedText = null; + } + public ActionMode getActionMode() { return mActionMode; } diff --git a/termux-shared/src/main/java/com/termux/shared/interact/ShareUtils.java b/termux-shared/src/main/java/com/termux/shared/interact/ShareUtils.java index 4d17af86f6..969502447c 100644 --- a/termux-shared/src/main/java/com/termux/shared/interact/ShareUtils.java +++ b/termux-shared/src/main/java/com/termux/shared/interact/ShareUtils.java @@ -12,7 +12,6 @@ import android.os.Environment; import androidx.appcompat.app.AppCompatActivity; -import androidx.core.content.ContextCompat; import com.termux.shared.R; import com.termux.shared.data.DataUtils; @@ -81,26 +80,82 @@ public static void shareText(final Context context, final String subject, final openSystemAppChooser(context, shareTextIntent, DataUtils.isNullOrEmpty(title) ? context.getString(R.string.title_share_with) : title); } + + + /** Wrapper for {@link #copyTextToClipboard(Context, String, String, String)} with `null` `clipDataLabel` and `toastString`. */ + public static void copyTextToClipboard(Context context, final String text) { + copyTextToClipboard(context, null, text, null); + } + + /** Wrapper for {@link #copyTextToClipboard(Context, String, String, String)} with `null` `clipDataLabel`. */ + public static void copyTextToClipboard(Context context, final String text, final String toastString) { + copyTextToClipboard(context, null, text, toastString); + } + /** - * Copy the text to clipboard. + * Copy the text to primary clip of the clipboard. * * @param context The context for operations. + * @param clipDataLabel The label to show to the user describing the copied text. * @param text The text to copy. * @param toastString If this is not {@code null} or empty, then a toast is shown if copying to * clipboard is successful. */ - public static void copyTextToClipboard(final Context context, final String text, final String toastString) { + public static void copyTextToClipboard(Context context, @Nullable final String clipDataLabel, + final String text, final String toastString) { if (context == null || text == null) return; - final ClipboardManager clipboardManager = ContextCompat.getSystemService(context, ClipboardManager.class); + ClipboardManager clipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); + if (clipboardManager == null) return; - if (clipboardManager != null) { - clipboardManager.setPrimaryClip(ClipData.newPlainText(null, DataUtils.getTruncatedCommandOutput(text, DataUtils.TRANSACTION_SIZE_LIMIT_IN_BYTES, true, false, false))); - if (toastString != null && !toastString.isEmpty()) - Logger.showToast(context, toastString, true); - } + clipboardManager.setPrimaryClip(ClipData.newPlainText(clipDataLabel, + DataUtils.getTruncatedCommandOutput(text, DataUtils.TRANSACTION_SIZE_LIMIT_IN_BYTES, + true, false, false))); + + if (toastString != null && !toastString.isEmpty()) + Logger.showToast(context, toastString, true); + } + + + + /** + * Wrapper for {@link #getTextFromClipboard(Context, boolean)} that returns primary text {@link String} + * if its set and not empty. + */ + @Nullable + public static String getTextStringFromClipboardIfSet(Context context, boolean coerceToText) { + CharSequence textCharSequence = getTextFromClipboard(context, coerceToText); + if (textCharSequence == null) return null; + String textString = textCharSequence.toString(); + return !textString.isEmpty() ? textString : null; } + /** + * Get the text from primary clip of the clipboard. + * + * @param context The context for operations. + * @param coerceToText Whether to call {@link ClipData.Item#coerceToText(Context)} to coerce + * non-text data to text. + * @return Returns the {@link CharSequence} of primary text. This will be `null` if failed to get it. + */ + @Nullable + public static CharSequence getTextFromClipboard(Context context, boolean coerceToText) { + if (context == null) return null; + + ClipboardManager clipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); + if (clipboardManager == null) return null; + + ClipData clipData = clipboardManager.getPrimaryClip(); + if (clipData == null) return null; + + ClipData.Item clipItem = clipData.getItemAt(0); + if (clipItem == null) return null; + + return coerceToText ? clipItem.coerceToText(context) : clipItem.getText(); + } + + + /** * Open a url. * diff --git a/termux-shared/src/main/java/com/termux/shared/settings/preferences/AppSharedPreferences.java b/termux-shared/src/main/java/com/termux/shared/settings/preferences/AppSharedPreferences.java new file mode 100644 index 0000000000..03a75e03b5 --- /dev/null +++ b/termux-shared/src/main/java/com/termux/shared/settings/preferences/AppSharedPreferences.java @@ -0,0 +1,49 @@ +package com.termux.shared.settings.preferences; + +import android.content.Context; +import android.content.SharedPreferences; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +/** A class that holds {@link SharedPreferences} objects for apps. */ +public class AppSharedPreferences { + + /** The {@link Context} for operations. */ + protected final Context mContext; + + /** The {@link SharedPreferences} that ideally should be created with {@link SharedPreferenceUtils#getPrivateSharedPreferences(Context, String)}. */ + protected final SharedPreferences mSharedPreferences; + + /** The {@link SharedPreferences}that ideally should be created with {@link SharedPreferenceUtils#getPrivateAndMultiProcessSharedPreferences(Context, String)}. */ + protected final SharedPreferences mMultiProcessSharedPreferences; + + protected AppSharedPreferences(@NonNull Context context, @Nullable SharedPreferences sharedPreferences) { + this(context, sharedPreferences, null); + } + + protected AppSharedPreferences(@NonNull Context context, @Nullable SharedPreferences sharedPreferences, + @Nullable SharedPreferences multiProcessSharedPreferences) { + mContext = context; + mSharedPreferences = sharedPreferences; + mMultiProcessSharedPreferences = multiProcessSharedPreferences; + } + + + + /** Get {@link #mContext}. */ + public Context getContext() { + return mContext; + } + + /** Get {@link #mSharedPreferences}. */ + public SharedPreferences getSharedPreferences() { + return mSharedPreferences; + } + + /** Get {@link #mMultiProcessSharedPreferences}. */ + public SharedPreferences getMultiProcessSharedPreferences() { + return mMultiProcessSharedPreferences; + } + +} diff --git a/termux-shared/src/main/java/com/termux/shared/termux/TermuxConstants.java b/termux-shared/src/main/java/com/termux/shared/termux/TermuxConstants.java index a3cc5dd334..59f523af72 100644 --- a/termux-shared/src/main/java/com/termux/shared/termux/TermuxConstants.java +++ b/termux-shared/src/main/java/com/termux/shared/termux/TermuxConstants.java @@ -325,9 +325,9 @@ public final class TermuxConstants { * Termux organization variables. */ - /** Termux Github organization name */ + /** Termux GitHub organization name */ public static final String TERMUX_GITHUB_ORGANIZATION_NAME = "termux"; // Default: "termux" - /** Termux Github organization url */ + /** Termux GitHub organization url */ public static final String TERMUX_GITHUB_ORGANIZATION_URL = "https://github.com" + "/" + TERMUX_GITHUB_ORGANIZATION_NAME; // Default: "https://github.com/termux" /** F-Droid packages base url */ @@ -345,11 +345,11 @@ public final class TermuxConstants { public static final String TERMUX_APP_NAME = "Termux"; // Default: "Termux" /** Termux package name */ public static final String TERMUX_PACKAGE_NAME = "com.termux"; // Default: "com.termux" - /** Termux Github repo name */ + /** Termux GitHub repo name */ public static final String TERMUX_GITHUB_REPO_NAME = "termux-app"; // Default: "termux-app" - /** Termux Github repo url */ + /** Termux GitHub repo url */ public static final String TERMUX_GITHUB_REPO_URL = TERMUX_GITHUB_ORGANIZATION_URL + "/" + TERMUX_GITHUB_REPO_NAME; // Default: "https://github.com/termux/termux-app" - /** Termux Github issues repo url */ + /** Termux GitHub issues repo url */ public static final String TERMUX_GITHUB_ISSUES_REPO_URL = TERMUX_GITHUB_REPO_URL + "/issues"; // Default: "https://github.com/termux/termux-app/issues" /** Termux F-Droid package url */ public static final String TERMUX_FDROID_PACKAGE_URL = FDROID_PACKAGES_BASE_URL + "/" + TERMUX_PACKAGE_NAME; // Default: "https://f-droid.org/en/packages/com.termux" @@ -359,11 +359,11 @@ public final class TermuxConstants { public static final String TERMUX_API_APP_NAME = "Termux:API"; // Default: "Termux:API" /** Termux:API app package name */ public static final String TERMUX_API_PACKAGE_NAME = TERMUX_PACKAGE_NAME + ".api"; // Default: "com.termux.api" - /** Termux:API Github repo name */ + /** Termux:API GitHub repo name */ public static final String TERMUX_API_GITHUB_REPO_NAME = "termux-api"; // Default: "termux-api" - /** Termux:API Github repo url */ + /** Termux:API GitHub repo url */ public static final String TERMUX_API_GITHUB_REPO_URL = TERMUX_GITHUB_ORGANIZATION_URL + "/" + TERMUX_API_GITHUB_REPO_NAME; // Default: "https://github.com/termux/termux-api" - /** Termux:API Github issues repo url */ + /** Termux:API GitHub issues repo url */ public static final String TERMUX_API_GITHUB_ISSUES_REPO_URL = TERMUX_API_GITHUB_REPO_URL + "/issues"; // Default: "https://github.com/termux/termux-api/issues" /** Termux:API F-Droid package url */ public static final String TERMUX_API_FDROID_PACKAGE_URL = FDROID_PACKAGES_BASE_URL + "/" + TERMUX_API_PACKAGE_NAME; // Default: "https://f-droid.org/en/packages/com.termux.api" @@ -373,11 +373,11 @@ public final class TermuxConstants { public static final String TERMUX_BOOT_APP_NAME = "Termux:Boot"; // Default: "Termux:Boot" /** Termux:Boot app package name */ public static final String TERMUX_BOOT_PACKAGE_NAME = TERMUX_PACKAGE_NAME + ".boot"; // Default: "com.termux.boot" - /** Termux:Boot Github repo name */ + /** Termux:Boot GitHub repo name */ public static final String TERMUX_BOOT_GITHUB_REPO_NAME = "termux-boot"; // Default: "termux-boot" - /** Termux:Boot Github repo url */ + /** Termux:Boot GitHub repo url */ public static final String TERMUX_BOOT_GITHUB_REPO_URL = TERMUX_GITHUB_ORGANIZATION_URL + "/" + TERMUX_BOOT_GITHUB_REPO_NAME; // Default: "https://github.com/termux/termux-boot" - /** Termux:Boot Github issues repo url */ + /** Termux:Boot GitHub issues repo url */ public static final String TERMUX_BOOT_GITHUB_ISSUES_REPO_URL = TERMUX_BOOT_GITHUB_REPO_URL + "/issues"; // Default: "https://github.com/termux/termux-boot/issues" /** Termux:Boot F-Droid package url */ public static final String TERMUX_BOOT_FDROID_PACKAGE_URL = FDROID_PACKAGES_BASE_URL + "/" + TERMUX_BOOT_PACKAGE_NAME; // Default: "https://f-droid.org/en/packages/com.termux.boot" @@ -387,11 +387,11 @@ public final class TermuxConstants { public static final String TERMUX_FLOAT_APP_NAME = "Termux:Float"; // Default: "Termux:Float" /** Termux:Float app package name */ public static final String TERMUX_FLOAT_PACKAGE_NAME = TERMUX_PACKAGE_NAME + ".window"; // Default: "com.termux.window" - /** Termux:Float Github repo name */ + /** Termux:Float GitHub repo name */ public static final String TERMUX_FLOAT_GITHUB_REPO_NAME = "termux-float"; // Default: "termux-float" - /** Termux:Float Github repo url */ + /** Termux:Float GitHub repo url */ public static final String TERMUX_FLOAT_GITHUB_REPO_URL = TERMUX_GITHUB_ORGANIZATION_URL + "/" + TERMUX_FLOAT_GITHUB_REPO_NAME; // Default: "https://github.com/termux/termux-float" - /** Termux:Float Github issues repo url */ + /** Termux:Float GitHub issues repo url */ public static final String TERMUX_FLOAT_GITHUB_ISSUES_REPO_URL = TERMUX_FLOAT_GITHUB_REPO_URL + "/issues"; // Default: "https://github.com/termux/termux-float/issues" /** Termux:Float F-Droid package url */ public static final String TERMUX_FLOAT_FDROID_PACKAGE_URL = FDROID_PACKAGES_BASE_URL + "/" + TERMUX_FLOAT_PACKAGE_NAME; // Default: "https://f-droid.org/en/packages/com.termux.window" @@ -401,11 +401,11 @@ public final class TermuxConstants { public static final String TERMUX_STYLING_APP_NAME = "Termux:Styling"; // Default: "Termux:Styling" /** Termux:Styling app package name */ public static final String TERMUX_STYLING_PACKAGE_NAME = TERMUX_PACKAGE_NAME + ".styling"; // Default: "com.termux.styling" - /** Termux:Styling Github repo name */ + /** Termux:Styling GitHub repo name */ public static final String TERMUX_STYLING_GITHUB_REPO_NAME = "termux-styling"; // Default: "termux-styling" - /** Termux:Styling Github repo url */ + /** Termux:Styling GitHub repo url */ public static final String TERMUX_STYLING_GITHUB_REPO_URL = TERMUX_GITHUB_ORGANIZATION_URL + "/" + TERMUX_STYLING_GITHUB_REPO_NAME; // Default: "https://github.com/termux/termux-styling" - /** Termux:Styling Github issues repo url */ + /** Termux:Styling GitHub issues repo url */ public static final String TERMUX_STYLING_GITHUB_ISSUES_REPO_URL = TERMUX_STYLING_GITHUB_REPO_URL + "/issues"; // Default: "https://github.com/termux/termux-styling/issues" /** Termux:Styling F-Droid package url */ public static final String TERMUX_STYLING_FDROID_PACKAGE_URL = FDROID_PACKAGES_BASE_URL + "/" + TERMUX_STYLING_PACKAGE_NAME; // Default: "https://f-droid.org/en/packages/com.termux.styling" @@ -415,11 +415,11 @@ public final class TermuxConstants { public static final String TERMUX_TASKER_APP_NAME = "Termux:Tasker"; // Default: "Termux:Tasker" /** Termux:Tasker app package name */ public static final String TERMUX_TASKER_PACKAGE_NAME = TERMUX_PACKAGE_NAME + ".tasker"; // Default: "com.termux.tasker" - /** Termux:Tasker Github repo name */ + /** Termux:Tasker GitHub repo name */ public static final String TERMUX_TASKER_GITHUB_REPO_NAME = "termux-tasker"; // Default: "termux-tasker" - /** Termux:Tasker Github repo url */ + /** Termux:Tasker GitHub repo url */ public static final String TERMUX_TASKER_GITHUB_REPO_URL = TERMUX_GITHUB_ORGANIZATION_URL + "/" + TERMUX_TASKER_GITHUB_REPO_NAME; // Default: "https://github.com/termux/termux-tasker" - /** Termux:Tasker Github issues repo url */ + /** Termux:Tasker GitHub issues repo url */ public static final String TERMUX_TASKER_GITHUB_ISSUES_REPO_URL = TERMUX_TASKER_GITHUB_REPO_URL + "/issues"; // Default: "https://github.com/termux/termux-tasker/issues" /** Termux:Tasker F-Droid package url */ public static final String TERMUX_TASKER_FDROID_PACKAGE_URL = FDROID_PACKAGES_BASE_URL + "/" + TERMUX_TASKER_PACKAGE_NAME; // Default: "https://f-droid.org/en/packages/com.termux.tasker" @@ -429,11 +429,11 @@ public final class TermuxConstants { public static final String TERMUX_WIDGET_APP_NAME = "Termux:Widget"; // Default: "Termux:Widget" /** Termux:Widget app package name */ public static final String TERMUX_WIDGET_PACKAGE_NAME = TERMUX_PACKAGE_NAME + ".widget"; // Default: "com.termux.widget" - /** Termux:Widget Github repo name */ + /** Termux:Widget GitHub repo name */ public static final String TERMUX_WIDGET_GITHUB_REPO_NAME = "termux-widget"; // Default: "termux-widget" - /** Termux:Widget Github repo url */ + /** Termux:Widget GitHub repo url */ public static final String TERMUX_WIDGET_GITHUB_REPO_URL = TERMUX_GITHUB_ORGANIZATION_URL + "/" + TERMUX_WIDGET_GITHUB_REPO_NAME; // Default: "https://github.com/termux/termux-widget" - /** Termux:Widget Github issues repo url */ + /** Termux:Widget GitHub issues repo url */ public static final String TERMUX_WIDGET_GITHUB_ISSUES_REPO_URL = TERMUX_WIDGET_GITHUB_REPO_URL + "/issues"; // Default: "https://github.com/termux/termux-widget/issues" /** Termux:Widget F-Droid package url */ public static final String TERMUX_WIDGET_FDROID_PACKAGE_URL = FDROID_PACKAGES_BASE_URL + "/" + TERMUX_WIDGET_PACKAGE_NAME; // Default: "https://f-droid.org/en/packages/com.termux.widget" @@ -476,10 +476,10 @@ public final class TermuxConstants { /** F-Droid APK release signing certificate SHA-256 digest */ public static final String APK_RELEASE_FDROID_SIGNING_CERTIFICATE_SHA256_DIGEST = "228FB2CFE90831C1499EC3CCAF61E96E8E1CE70766B9474672CE427334D41C42"; // Default: "228FB2CFE90831C1499EC3CCAF61E96E8E1CE70766B9474672CE427334D41C42" - /** Github APK release */ + /** GitHub APK release */ public static final String APK_RELEASE_GITHUB = "Github"; // Default: "Github" - /** Github APK release signing certificate SHA-256 digest */ + /** GitHub APK release signing certificate SHA-256 digest */ public static final String APK_RELEASE_GITHUB_SIGNING_CERTIFICATE_SHA256_DIGEST = "B6DA01480EEFD5FBF2CD3771B8D1021EC791304BDD6C4BF41D3FAABAD48EE5E1"; // Default: "B6DA01480EEFD5FBF2CD3771B8D1021EC791304BDD6C4BF41D3FAABAD48EE5E1" /** Google Play Store APK release */ @@ -502,21 +502,21 @@ public final class TermuxConstants { * Termux packages urls. */ - /** Termux Packages Github repo name */ + /** Termux Packages GitHub repo name */ public static final String TERMUX_PACKAGES_GITHUB_REPO_NAME = "termux-packages"; // Default: "termux-packages" - /** Termux Packages Github repo url */ + /** Termux Packages GitHub repo url */ public static final String TERMUX_PACKAGES_GITHUB_REPO_URL = TERMUX_GITHUB_ORGANIZATION_URL + "/" + TERMUX_PACKAGES_GITHUB_REPO_NAME; // Default: "https://github.com/termux/termux-packages" - /** Termux Packages Github issues repo url */ + /** Termux Packages GitHub issues repo url */ public static final String TERMUX_PACKAGES_GITHUB_ISSUES_REPO_URL = TERMUX_PACKAGES_GITHUB_REPO_URL + "/issues"; // Default: "https://github.com/termux/termux-packages/issues" /** Termux API apt package name */ public static final String TERMUX_API_APT_PACKAGE_NAME = "termux-api"; // Default: "termux-api" - /** Termux API apt Github repo name */ + /** Termux API apt GitHub repo name */ public static final String TERMUX_API_APT_GITHUB_REPO_NAME = "termux-api-package"; // Default: "termux-api-package" - /** Termux API apt Github repo url */ + /** Termux API apt GitHub repo url */ public static final String TERMUX_API_APT_GITHUB_REPO_URL = TERMUX_GITHUB_ORGANIZATION_URL + "/" + TERMUX_API_APT_GITHUB_REPO_NAME; // Default: "https://github.com/termux/termux-api-package" - /** Termux API apt Github issues repo url */ + /** Termux API apt GitHub issues repo url */ public static final String TERMUX_API_APT_GITHUB_ISSUES_REPO_URL = TERMUX_API_APT_GITHUB_REPO_URL + "/issues"; // Default: "https://github.com/termux/termux-api-package/issues" @@ -539,7 +539,7 @@ public final class TermuxConstants { /** Termux Wiki url */ public static final String TERMUX_WIKI_URL = "https://wiki.termux.com"; // Default: "https://wiki.termux.com" - /** Termux Github wiki repo url */ + /** Termux GitHub wiki repo url */ public static final String TERMUX_GITHUB_WIKI_REPO_URL = TERMUX_GITHUB_REPO_URL + "/wiki"; // Default: "https://github.com/termux/termux-app/wiki" /** Termux Packages wiki repo url */ diff --git a/termux-shared/src/main/java/com/termux/shared/termux/TermuxUtils.java b/termux-shared/src/main/java/com/termux/shared/termux/TermuxUtils.java index 793968afcb..8ac94da162 100644 --- a/termux-shared/src/main/java/com/termux/shared/termux/TermuxUtils.java +++ b/termux-shared/src/main/java/com/termux/shared/termux/TermuxUtils.java @@ -518,7 +518,7 @@ public static String getReportIssueMarkdownString(@NonNull final Context context markdownString.append("\n\n### Reddit\n"); markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_REDDIT_SUBREDDIT, TermuxConstants.TERMUX_REDDIT_SUBREDDIT_URL)).append(" "); - markdownString.append("\n\n### Github Issues for Termux apps\n"); + markdownString.append("\n\n### GitHub Issues for Termux apps\n"); markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_APP_NAME, TermuxConstants.TERMUX_GITHUB_ISSUES_REPO_URL)).append(" "); markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_API_APP_NAME, TermuxConstants.TERMUX_API_GITHUB_ISSUES_REPO_URL)).append(" "); markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_BOOT_APP_NAME, TermuxConstants.TERMUX_BOOT_GITHUB_ISSUES_REPO_URL)).append(" "); @@ -527,7 +527,7 @@ public static String getReportIssueMarkdownString(@NonNull final Context context markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_TASKER_APP_NAME, TermuxConstants.TERMUX_TASKER_GITHUB_ISSUES_REPO_URL)).append(" "); markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_WIDGET_APP_NAME, TermuxConstants.TERMUX_WIDGET_GITHUB_ISSUES_REPO_URL)).append(" "); - markdownString.append("\n\n### Github Issues for Termux packages\n"); + markdownString.append("\n\n### GitHub Issues for Termux packages\n"); markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_PACKAGES_GITHUB_REPO_NAME, TermuxConstants.TERMUX_PACKAGES_GITHUB_ISSUES_REPO_URL)).append(" "); markdownString.append("\n##\n"); @@ -548,7 +548,7 @@ public static String getImportantLinksMarkdownString(@NonNull final Context cont markdownString.append("## Important Links"); - markdownString.append("\n\n### Github\n"); + markdownString.append("\n\n### GitHub\n"); markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_APP_NAME, TermuxConstants.TERMUX_GITHUB_REPO_URL)).append(" "); markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_API_APP_NAME, TermuxConstants.TERMUX_API_GITHUB_REPO_URL)).append(" "); markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_BOOT_APP_NAME, TermuxConstants.TERMUX_BOOT_GITHUB_REPO_URL)).append(" "); diff --git a/termux-shared/src/main/java/com/termux/shared/termux/extrakeys/ExtraKeysConstants.java b/termux-shared/src/main/java/com/termux/shared/termux/extrakeys/ExtraKeysConstants.java index e625db5cd3..b747381c02 100644 --- a/termux-shared/src/main/java/com/termux/shared/termux/extrakeys/ExtraKeysConstants.java +++ b/termux-shared/src/main/java/com/termux/shared/termux/extrakeys/ExtraKeysConstants.java @@ -10,7 +10,10 @@ public class ExtraKeysConstants { /** Defines the repetitive keys that can be passed to {@link ExtraKeysView#setRepetitiveKeys(List)}. */ - public static List PRIMARY_REPETITIVE_KEYS = Arrays.asList("UP", "DOWN", "LEFT", "RIGHT", "BKSP", "DEL"); + public static List PRIMARY_REPETITIVE_KEYS = Arrays.asList( + "UP", "DOWN", "LEFT", "RIGHT", + "BKSP", "DEL", + "PGUP", "PGDN"); diff --git a/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxAPIAppSharedPreferences.java b/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxAPIAppSharedPreferences.java index 228f9cc4f3..67c00a752f 100644 --- a/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxAPIAppSharedPreferences.java +++ b/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxAPIAppSharedPreferences.java @@ -1,31 +1,28 @@ package com.termux.shared.termux.settings.preferences; import android.content.Context; -import android.content.SharedPreferences; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.termux.shared.logger.Logger; import com.termux.shared.android.PackageUtils; +import com.termux.shared.settings.preferences.AppSharedPreferences; import com.termux.shared.settings.preferences.SharedPreferenceUtils; import com.termux.shared.termux.TermuxUtils; import com.termux.shared.termux.settings.preferences.TermuxPreferenceConstants.TERMUX_API_APP; import com.termux.shared.termux.TermuxConstants; -public class TermuxAPIAppSharedPreferences { - - private final Context mContext; - private final SharedPreferences mSharedPreferences; - private final SharedPreferences mMultiProcessSharedPreferences; - +public class TermuxAPIAppSharedPreferences extends AppSharedPreferences { private static final String LOG_TAG = "TermuxAPIAppSharedPreferences"; private TermuxAPIAppSharedPreferences(@NonNull Context context) { - mContext = context; - mSharedPreferences = getPrivateSharedPreferences(mContext); - mMultiProcessSharedPreferences = getPrivateAndMultiProcessSharedPreferences(mContext); + super(context, + SharedPreferenceUtils.getPrivateSharedPreferences(context, + TermuxConstants.TERMUX_API_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION), + SharedPreferenceUtils.getPrivateAndMultiProcessSharedPreferences(context, + TermuxConstants.TERMUX_API_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION)); } /** @@ -61,16 +58,6 @@ public static TermuxAPIAppSharedPreferences build(@NonNull final Context context return new TermuxAPIAppSharedPreferences(termuxAPIPackageContext); } - private static SharedPreferences getPrivateSharedPreferences(Context context) { - if (context == null) return null; - return SharedPreferenceUtils.getPrivateSharedPreferences(context, TermuxConstants.TERMUX_API_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION); - } - - private static SharedPreferences getPrivateAndMultiProcessSharedPreferences(Context context) { - if (context == null) return null; - return SharedPreferenceUtils.getPrivateAndMultiProcessSharedPreferences(context, TermuxConstants.TERMUX_API_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION); - } - public int getLogLevel(boolean readFromFile) { @@ -85,4 +72,13 @@ public void setLogLevel(Context context, int logLevel, boolean commitToFile) { SharedPreferenceUtils.setInt(mSharedPreferences, TERMUX_API_APP.KEY_LOG_LEVEL, logLevel, commitToFile); } + + public int getLastPendingIntentRequestCode() { + return SharedPreferenceUtils.getInt(mSharedPreferences, TERMUX_API_APP.KEY_LAST_PENDING_INTENT_REQUEST_CODE, TERMUX_API_APP.DEFAULT_VALUE_KEY_LAST_PENDING_INTENT_REQUEST_CODE); + } + + public void setLastPendingIntentRequestCode(int lastPendingIntentRequestCode) { + SharedPreferenceUtils.setInt(mSharedPreferences, TERMUX_API_APP.KEY_LAST_PENDING_INTENT_REQUEST_CODE, lastPendingIntentRequestCode, true); + } + } diff --git a/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxAppSharedPreferences.java b/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxAppSharedPreferences.java index a33be8e68e..cd3812fe49 100644 --- a/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxAppSharedPreferences.java +++ b/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxAppSharedPreferences.java @@ -1,13 +1,13 @@ package com.termux.shared.termux.settings.preferences; import android.content.Context; -import android.content.SharedPreferences; import android.util.TypedValue; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.termux.shared.android.PackageUtils; +import com.termux.shared.settings.preferences.AppSharedPreferences; import com.termux.shared.settings.preferences.SharedPreferenceUtils; import com.termux.shared.termux.TermuxConstants; import com.termux.shared.logger.Logger; @@ -15,11 +15,7 @@ import com.termux.shared.termux.TermuxUtils; import com.termux.shared.termux.settings.preferences.TermuxPreferenceConstants.TERMUX_APP; -public class TermuxAppSharedPreferences { - - private final Context mContext; - private final SharedPreferences mSharedPreferences; - private final SharedPreferences mMultiProcessSharedPreferences; +public class TermuxAppSharedPreferences extends AppSharedPreferences { private int MIN_FONTSIZE; private int MAX_FONTSIZE; @@ -28,10 +24,11 @@ public class TermuxAppSharedPreferences { private static final String LOG_TAG = "TermuxAppSharedPreferences"; private TermuxAppSharedPreferences(@NonNull Context context) { - mContext = context; - mSharedPreferences = getPrivateSharedPreferences(mContext); - mMultiProcessSharedPreferences = getPrivateAndMultiProcessSharedPreferences(mContext); - + super(context, + SharedPreferenceUtils.getPrivateSharedPreferences(context, + TermuxConstants.TERMUX_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION), + SharedPreferenceUtils.getPrivateAndMultiProcessSharedPreferences(context, + TermuxConstants.TERMUX_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION)); setFontVariables(context); } @@ -69,17 +66,6 @@ public static TermuxAppSharedPreferences build(@NonNull final Context context, f return new TermuxAppSharedPreferences(termuxPackageContext); } - private static SharedPreferences getPrivateSharedPreferences(Context context) { - if (context == null) return null; - return SharedPreferenceUtils.getPrivateSharedPreferences(context, TermuxConstants.TERMUX_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION); - } - - - private static SharedPreferences getPrivateAndMultiProcessSharedPreferences(Context context) { - if (context == null) return null; - return SharedPreferenceUtils.getPrivateAndMultiProcessSharedPreferences(context, TermuxConstants.TERMUX_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION); - } - public boolean shouldShowTerminalToolbar() { diff --git a/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxBootAppSharedPreferences.java b/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxBootAppSharedPreferences.java index 49db9cef16..317f93ce1e 100644 --- a/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxBootAppSharedPreferences.java +++ b/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxBootAppSharedPreferences.java @@ -1,31 +1,28 @@ package com.termux.shared.termux.settings.preferences; import android.content.Context; -import android.content.SharedPreferences; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.termux.shared.logger.Logger; import com.termux.shared.android.PackageUtils; +import com.termux.shared.settings.preferences.AppSharedPreferences; import com.termux.shared.settings.preferences.SharedPreferenceUtils; import com.termux.shared.termux.TermuxUtils; import com.termux.shared.termux.settings.preferences.TermuxPreferenceConstants.TERMUX_BOOT_APP; import com.termux.shared.termux.TermuxConstants; -public class TermuxBootAppSharedPreferences { - - private final Context mContext; - private final SharedPreferences mSharedPreferences; - private final SharedPreferences mMultiProcessSharedPreferences; - +public class TermuxBootAppSharedPreferences extends AppSharedPreferences { private static final String LOG_TAG = "TermuxBootAppSharedPreferences"; private TermuxBootAppSharedPreferences(@NonNull Context context) { - mContext = context; - mSharedPreferences = getPrivateSharedPreferences(mContext); - mMultiProcessSharedPreferences = getPrivateAndMultiProcessSharedPreferences(mContext); + super(context, + SharedPreferenceUtils.getPrivateSharedPreferences(context, + TermuxConstants.TERMUX_BOOT_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION), + SharedPreferenceUtils.getPrivateAndMultiProcessSharedPreferences(context, + TermuxConstants.TERMUX_BOOT_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION)); } /** @@ -61,16 +58,6 @@ public static TermuxBootAppSharedPreferences build(@NonNull final Context contex return new TermuxBootAppSharedPreferences(termuxBootPackageContext); } - private static SharedPreferences getPrivateSharedPreferences(Context context) { - if (context == null) return null; - return SharedPreferenceUtils.getPrivateSharedPreferences(context, TermuxConstants.TERMUX_BOOT_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION); - } - - private static SharedPreferences getPrivateAndMultiProcessSharedPreferences(Context context) { - if (context == null) return null; - return SharedPreferenceUtils.getPrivateAndMultiProcessSharedPreferences(context, TermuxConstants.TERMUX_BOOT_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION); - } - public int getLogLevel(boolean readFromFile) { diff --git a/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxFloatAppSharedPreferences.java b/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxFloatAppSharedPreferences.java index 4c0c7939d1..e56e29acdd 100644 --- a/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxFloatAppSharedPreferences.java +++ b/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxFloatAppSharedPreferences.java @@ -1,7 +1,6 @@ package com.termux.shared.termux.settings.preferences; import android.content.Context; -import android.content.SharedPreferences; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -9,16 +8,13 @@ import com.termux.shared.data.DataUtils; import com.termux.shared.logger.Logger; import com.termux.shared.android.PackageUtils; +import com.termux.shared.settings.preferences.AppSharedPreferences; import com.termux.shared.settings.preferences.SharedPreferenceUtils; import com.termux.shared.termux.TermuxUtils; import com.termux.shared.termux.settings.preferences.TermuxPreferenceConstants.TERMUX_FLOAT_APP; import com.termux.shared.termux.TermuxConstants; -public class TermuxFloatAppSharedPreferences { - - private final Context mContext; - private final SharedPreferences mSharedPreferences; - private final SharedPreferences mMultiProcessSharedPreferences; +public class TermuxFloatAppSharedPreferences extends AppSharedPreferences { private int MIN_FONTSIZE; private int MAX_FONTSIZE; @@ -27,9 +23,11 @@ public class TermuxFloatAppSharedPreferences { private static final String LOG_TAG = "TermuxFloatAppSharedPreferences"; private TermuxFloatAppSharedPreferences(@NonNull Context context) { - mContext = context; - mSharedPreferences = getPrivateSharedPreferences(mContext); - mMultiProcessSharedPreferences = getPrivateAndMultiProcessSharedPreferences(mContext); + super(context, + SharedPreferenceUtils.getPrivateSharedPreferences(context, + TermuxConstants.TERMUX_FLOAT_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION), + SharedPreferenceUtils.getPrivateAndMultiProcessSharedPreferences(context, + TermuxConstants.TERMUX_FLOAT_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION)); setFontVariables(context); } @@ -67,16 +65,6 @@ public static TermuxFloatAppSharedPreferences build(@NonNull final Context conte return new TermuxFloatAppSharedPreferences(termuxFloatPackageContext); } - private static SharedPreferences getPrivateSharedPreferences(Context context) { - if (context == null) return null; - return SharedPreferenceUtils.getPrivateSharedPreferences(context, TermuxConstants.TERMUX_FLOAT_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION); - } - - private static SharedPreferences getPrivateAndMultiProcessSharedPreferences(Context context) { - if (context == null) return null; - return SharedPreferenceUtils.getPrivateAndMultiProcessSharedPreferences(context, TermuxConstants.TERMUX_FLOAT_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION); - } - public int getWindowX() { diff --git a/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxPreferenceConstants.java b/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxPreferenceConstants.java index 80226914d7..15bc74c9f2 100644 --- a/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxPreferenceConstants.java +++ b/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxPreferenceConstants.java @@ -194,6 +194,13 @@ public static final class TERMUX_API_APP { */ public static final String KEY_LOG_LEVEL = "log_level"; + + /** + * Defines the key for last used PendingIntent request code. + */ + public static final String KEY_LAST_PENDING_INTENT_REQUEST_CODE = "last_pending_intent_request_code"; + public static final int DEFAULT_VALUE_KEY_LAST_PENDING_INTENT_REQUEST_CODE = 0; + } diff --git a/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxStylingAppSharedPreferences.java b/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxStylingAppSharedPreferences.java index 5e6b25a268..0e1b539ee9 100644 --- a/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxStylingAppSharedPreferences.java +++ b/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxStylingAppSharedPreferences.java @@ -1,31 +1,28 @@ package com.termux.shared.termux.settings.preferences; import android.content.Context; -import android.content.SharedPreferences; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.termux.shared.logger.Logger; import com.termux.shared.android.PackageUtils; +import com.termux.shared.settings.preferences.AppSharedPreferences; import com.termux.shared.settings.preferences.SharedPreferenceUtils; import com.termux.shared.termux.TermuxUtils; import com.termux.shared.termux.settings.preferences.TermuxPreferenceConstants.TERMUX_STYLING_APP; import com.termux.shared.termux.TermuxConstants; -public class TermuxStylingAppSharedPreferences { - - private final Context mContext; - private final SharedPreferences mSharedPreferences; - private final SharedPreferences mMultiProcessSharedPreferences; - +public class TermuxStylingAppSharedPreferences extends AppSharedPreferences { private static final String LOG_TAG = "TermuxStylingAppSharedPreferences"; private TermuxStylingAppSharedPreferences(@NonNull Context context) { - mContext = context; - mSharedPreferences = getPrivateSharedPreferences(mContext); - mMultiProcessSharedPreferences = getPrivateAndMultiProcessSharedPreferences(mContext); + super(context, + SharedPreferenceUtils.getPrivateSharedPreferences(context, + TermuxConstants.TERMUX_STYLING_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION), + SharedPreferenceUtils.getPrivateAndMultiProcessSharedPreferences(context, + TermuxConstants.TERMUX_STYLING_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION)); } /** @@ -61,16 +58,6 @@ public static TermuxStylingAppSharedPreferences build(@NonNull final Context con return new TermuxStylingAppSharedPreferences(termuxStylingPackageContext); } - private static SharedPreferences getPrivateSharedPreferences(Context context) { - if (context == null) return null; - return SharedPreferenceUtils.getPrivateSharedPreferences(context, TermuxConstants.TERMUX_STYLING_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION); - } - - private static SharedPreferences getPrivateAndMultiProcessSharedPreferences(Context context) { - if (context == null) return null; - return SharedPreferenceUtils.getPrivateAndMultiProcessSharedPreferences(context, TermuxConstants.TERMUX_STYLING_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION); - } - public int getLogLevel(boolean readFromFile) { diff --git a/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxTaskerAppSharedPreferences.java b/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxTaskerAppSharedPreferences.java index b647f09661..5c518a67c4 100644 --- a/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxTaskerAppSharedPreferences.java +++ b/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxTaskerAppSharedPreferences.java @@ -1,31 +1,28 @@ package com.termux.shared.termux.settings.preferences; import android.content.Context; -import android.content.SharedPreferences; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.termux.shared.android.PackageUtils; +import com.termux.shared.settings.preferences.AppSharedPreferences; import com.termux.shared.settings.preferences.SharedPreferenceUtils; import com.termux.shared.termux.TermuxConstants; import com.termux.shared.termux.TermuxUtils; import com.termux.shared.termux.settings.preferences.TermuxPreferenceConstants.TERMUX_TASKER_APP; import com.termux.shared.logger.Logger; -public class TermuxTaskerAppSharedPreferences { - - private final Context mContext; - private final SharedPreferences mSharedPreferences; - private final SharedPreferences mMultiProcessSharedPreferences; - +public class TermuxTaskerAppSharedPreferences extends AppSharedPreferences { private static final String LOG_TAG = "TermuxTaskerAppSharedPreferences"; private TermuxTaskerAppSharedPreferences(@NonNull Context context) { - mContext = context; - mSharedPreferences = getPrivateSharedPreferences(mContext); - mMultiProcessSharedPreferences = getPrivateAndMultiProcessSharedPreferences(mContext); + super(context, + SharedPreferenceUtils.getPrivateSharedPreferences(context, + TermuxConstants.TERMUX_TASKER_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION), + SharedPreferenceUtils.getPrivateAndMultiProcessSharedPreferences(context, + TermuxConstants.TERMUX_TASKER_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION)); } /** @@ -61,16 +58,6 @@ public static TermuxTaskerAppSharedPreferences build(@NonNull final Context con return new TermuxTaskerAppSharedPreferences(termuxTaskerPackageContext); } - private static SharedPreferences getPrivateSharedPreferences(Context context) { - if (context == null) return null; - return SharedPreferenceUtils.getPrivateSharedPreferences(context, TermuxConstants.TERMUX_TASKER_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION); - } - - private static SharedPreferences getPrivateAndMultiProcessSharedPreferences(Context context) { - if (context == null) return null; - return SharedPreferenceUtils.getPrivateAndMultiProcessSharedPreferences(context, TermuxConstants.TERMUX_TASKER_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION); - } - public int getLogLevel(boolean readFromFile) { diff --git a/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxWidgetAppSharedPreferences.java b/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxWidgetAppSharedPreferences.java index 301feec299..40ce92264a 100644 --- a/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxWidgetAppSharedPreferences.java +++ b/termux-shared/src/main/java/com/termux/shared/termux/settings/preferences/TermuxWidgetAppSharedPreferences.java @@ -1,13 +1,13 @@ package com.termux.shared.termux.settings.preferences; import android.content.Context; -import android.content.SharedPreferences; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.termux.shared.logger.Logger; import com.termux.shared.android.PackageUtils; +import com.termux.shared.settings.preferences.AppSharedPreferences; import com.termux.shared.settings.preferences.SharedPreferenceUtils; import com.termux.shared.termux.TermuxUtils; import com.termux.shared.termux.settings.preferences.TermuxPreferenceConstants.TERMUX_WIDGET_APP; @@ -15,19 +15,16 @@ import java.util.UUID; -public class TermuxWidgetAppSharedPreferences { - - private final Context mContext; - private final SharedPreferences mSharedPreferences; - private final SharedPreferences mMultiProcessSharedPreferences; - +public class TermuxWidgetAppSharedPreferences extends AppSharedPreferences { private static final String LOG_TAG = "TermuxWidgetAppSharedPreferences"; private TermuxWidgetAppSharedPreferences(@NonNull Context context) { - mContext = context; - mSharedPreferences = getPrivateSharedPreferences(mContext); - mMultiProcessSharedPreferences = getPrivateAndMultiProcessSharedPreferences(mContext); + super(context, + SharedPreferenceUtils.getPrivateSharedPreferences(context, + TermuxConstants.TERMUX_WIDGET_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION), + SharedPreferenceUtils.getPrivateAndMultiProcessSharedPreferences(context, + TermuxConstants.TERMUX_WIDGET_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION)); } /** @@ -63,16 +60,6 @@ public static TermuxWidgetAppSharedPreferences build(@NonNull final Context cont return new TermuxWidgetAppSharedPreferences(termuxWidgetPackageContext); } - private static SharedPreferences getPrivateSharedPreferences(Context context) { - if (context == null) return null; - return SharedPreferenceUtils.getPrivateSharedPreferences(context, TermuxConstants.TERMUX_WIDGET_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION); - } - - private static SharedPreferences getPrivateAndMultiProcessSharedPreferences(Context context) { - if (context == null) return null; - return SharedPreferenceUtils.getPrivateAndMultiProcessSharedPreferences(context, TermuxConstants.TERMUX_WIDGET_DEFAULT_PREFERENCES_FILE_BASENAME_WITHOUT_EXTENSION); - } - public static String getGeneratedToken(@NonNull Context context) { diff --git a/termux-shared/src/main/res/values/strings.xml b/termux-shared/src/main/res/values/strings.xml index 88f555b9a8..d585e24f97 100644 --- a/termux-shared/src/main/res/values/strings.xml +++ b/termux-shared/src/main/res/values/strings.xml @@ -84,12 +84,12 @@ an issue on one of the following links. \n\nIf you are posting a Termux app crash report, then please provide details on what you were doing that caused the crash and how to reproduce it, if possible. - \n\nIf you are posting an issue on Github, then post it in the repository at which the report belongs at. + \n\nIf you are posting an issue on GitHub, then post it in the repository at which the report belongs at. Issues opened or emails sent with **(partial) screenshots** instead of copied text or a file of this report **will likely be automatically closed/deleted**. You may optionally remove any device specific info that you consider private or don\'t want to share or that is not relevant to the issue. \n\nWe do not provide support for any hacking related tools/scripts. Any questions asked about them over email, - on github or other official termux community forums **will likely be automatically closed/deleted** and may + on GitHub or other official termux community forums **will likely be automatically closed/deleted** and may even result in **temporary or permanent** ban. Check %1$s/wiki/Hacking for details. The &TERMUX_APP_NAME; is required by the %1$s app to run termux commands.