Compare commits

69 Commits
v2.8 ... v3.3

Author SHA1 Message Date
KOWX712
d9a00b9540 v3.3 release
changelog: fix typo
2025-02-07 14:35:19 +08:00
KOWX712
ed77c72a79 update with tricky store 1.2.1 2025-02-07 14:27:42 +08:00
KOWX712
b5370ad088 script opt 2025-02-07 10:55:54 +08:00
KOWX712
bb7573f5dd add denylist to target on boot
choose "Select from DenyList" in WebUI once to enable this feature
2025-02-06 22:32:36 +08:00
KOWX712
ccffd2eaed update username 2025-02-05 22:23:44 +08:00
KOWX712
d9c1cdc11d Update README.md 2025-02-05 14:57:01 +08:00
KOWX712
e1f9c8904a Update .extra 2025-02-05 14:49:43 +08:00
KOWX712
3b98a88c77 typo 2025-02-02 13:03:36 +08:00
KOWX712
0b8dcd4503 v3.2 release 2025-02-02 13:00:56 +08:00
KOWX712
913118ac60 handle vendor patch
set `ro.vendor.build.security_patch` same with `ro.build.version.security_patch`
2025-02-02 12:49:20 +08:00
KOWX712
cb4276643a add android and com.android.vending by default
make compatible with latest pif inject a4086509f9
https://github.com/5ec1cff/TrickyStore/pull/83
2025-02-01 11:51:32 +08:00
Berk Mirsat
0087575117 A13 check package name (#13) 2025-01-29 18:30:15 +08:00
KOWX712
5f01b4fed1 Update .extra 2025-01-29 13:44:10 +07:00
berkmirsat
1f7a4c174b add Turkish translation 2025-01-29 06:36:17 +07:00
KOWX712
19f6b156f6 wait for next 2025-01-23 07:35:29 +08:00
KOWX712
63874eeb8b Update README.md 2025-01-22 23:58:29 +08:00
KOWX712
2c5082acbc check json format on pull request 2025-01-19 22:23:02 +08:00
Re*Index. (ot_inc)
1cb810405a Update ja-JP.json 2025-01-19 18:59:40 +08:00
KOWX712
142dfdbe81 fix gsf app name missing 2025-01-19 16:29:13 +08:00
KOWX712
5ef6293924 v3.1 release 2025-01-19 15:38:22 +08:00
KOWX712
a6b35e8d85 fix update issue 2025-01-19 15:38:22 +08:00
KOWX712
51747f261e complete additional system app
complete aa7147d115 (#10)
2025-01-19 14:41:10 +08:00
ChiseWaguri
aa7147d115 Add Play Store and Service Framework for A13+ checks 2025-01-19 11:07:13 +08:00
KOWX712
5e9463d8d2 update to marked v15.0.6 2025-01-17 03:29:32 +08:00
KOWX712
d1313d743c Update .extra
Update .extra
2025-01-16 03:43:58 +08:00
KOWX712
2542e1a18a Backup before replacing kb on uninstallation 2025-01-15 22:36:39 +08:00
KOWX712
aa0837ec49 backup before putting a new kb
#8
2025-01-15 22:11:23 +08:00
KOWX712
684b38afef prevent deletion if tricky store add webui/action in future 2025-01-15 02:14:51 +08:00
KOWX712
e348fbdd6f Update .gitattributes 2025-01-13 00:35:49 +08:00
KOWX712
bb167b760d update zh language name 2025-01-10 03:38:10 +08:00
KOWX712
8909a73b59 opt code
- ensure auto install use latest ksuwebui standalone.
2025-01-09 01:33:07 +08:00
KOWX712
d8848fe5c3 fix multiple gms
fix multiple gms on device without gms as system app.
2025-01-06 18:24:25 +08:00
KOWX712
2d2cdd7672 fix multiple eventlistener for ripple effect 2025-01-04 15:13:03 +08:00
KOWX712
c1818b6ba2 remove ripple effect on mode menu 2025-01-03 14:47:58 +08:00
KOWX712
c69337501a update Chinese translation 2025-01-03 14:44:33 +08:00
KOWX712
12a39ce60a Complete markdown support
- Update markdown parser to v15.0.5
- Add credit to markedjs/marked
- UI adjust
2025-01-03 11:41:07 +08:00
KOWX712
ec330a4b0b Add markdown support in changelog 2025-01-02 17:16:10 +08:00
KOWX712
7e2c185c5c Update README.md 2025-01-02 12:27:37 +08:00
KOWX712
f641f388ad opt menu touch effect 2025-01-02 01:26:42 +08:00
KOWX712
f7aee9dc34 remove lite from update 2025-01-01 10:08:34 +08:00
KOWX712
c19b93ffa3 v3.0 Happy New Year 2025 2025-01-01 09:38:47 +08:00
KOWX712
2dc69abea4 change button color 2025-01-01 09:38:12 +08:00
KOWX712
75ff61e4d2 fix missing animation 2024-12-31 20:21:56 +08:00
KOWX712
bb3b0abb30 adjust update menu UI 2024-12-31 18:21:52 +08:00
KOWX712
502e6f6ea6 change update method 2024-12-31 17:02:04 +08:00
KOWX712
1d5428e683 Fix save button animation 2024-12-30 09:38:37 +08:00
KOWX712
fe64f37361 UI adjust + optimize
- adjust about page appearance
- change mode menu shape to round
- enlarge on selected mode
- improve color recognition algorithm
- code optimize
2024-12-30 03:06:28 +08:00
KOWX712
24548025fe Update A-template.json 2024-12-29 03:52:27 +08:00
KOWX712
d3885966b2 fix wrong error toast 2024-12-29 03:36:31 +08:00
KOWX712
ae32ccf973 Final touchup
- UI adjust: color, placement, size

#7
2024-12-29 03:08:34 +08:00
KOWX712
77a6e06631 Support ! and ? mode in WebUI
- Long press to open mode menu
- Show toast on error log

#7
2024-12-29 02:39:49 +08:00
KOWX712
940d809ac5 complete animation
- adjust position of prompt and save button on MMRL
- complete ripple animation
- new checkbox animation
2024-12-28 14:54:38 +08:00
KOWX712
1861e43a81 Simplify logic, add Material Design ripple effect 2024-12-26 20:52:01 +08:00
KOWX712
10c02a6924 Add trigger on pull request 2024-12-26 01:19:03 +08:00
KOWX712
b07c9f8f5a v2.9 Merry Christmas 2024-12-25 16:41:34 +08:00
KOWX712
dad4bed07e Restructure css 2024-12-25 16:22:39 +08:00
KOWX712
a7214cfa81 Preserve ! and ? during update target
#7
2024-12-25 15:32:49 +08:00
KOWX712
82c489f787 local icon
No longer need internet connection to load icon
2024-12-24 22:40:03 +08:00
KOWX712
de43439200 Restructure index.js, simplify translation process
No big change
2024-12-24 22:25:41 +08:00
KOWX712
5347ac29bf Restructure translation (no functional change) 2024-12-24 15:55:09 +08:00
Keinta15
37467411cf Update es-ES.json
Adjusted phrasing for better fluency and corrected minor grammar issues
2024-12-20 01:18:30 +00:00
KOWX712
4fd5b47817 add Spanish 2024-12-20 00:36:54 +08:00
KOWX712
610f106913 Weak connection fix + code optimize + fix typo
Don't show applist before finish check update. Slightly slower loading but remove freeze on weak connection device
2024-12-19 22:58:33 +08:00
KOWX712
dc46e5f12e Remove restriction on installation
But module will still removed automatically if tricky store module not found to keep the convenient for user to remove webui when uninstalling tricky store.
2024-12-16 02:38:54 +08:00
backslashxx
f46cc964e4 module: common/get_extra: reorder functions 2024-12-15 13:44:17 +08:00
backslashxx
a7963c53dc module: common/get_extra: remove bashism 2024-12-15 13:44:17 +08:00
backslashxx
7c5b144492 module: common/get_extra: use download instead 2024-12-15 13:44:17 +08:00
backslashxx
4230929dc9 module: common/get_extra: import download function from bindhosts 2024-12-15 13:44:17 +08:00
backslashxx
437237e3d4 module: common/get_extra: drop check_wget function 2024-12-15 13:44:17 +08:00
47 changed files with 3651 additions and 2263 deletions

2
.extra

File diff suppressed because one or more lines are too long

4
.gitattributes vendored
View File

@@ -3,7 +3,9 @@
*.prop text eol=lf
*.md text eol=lf
*.xml text eol=lf
*.json text eol=lf
.extra text eol=lf
META-INF/** text eol=lf
# Denote all files that are truly binary and should not be modified.
common/addon/**/tools/** binary
module/bin/**/** binary

View File

@@ -6,6 +6,9 @@ on:
- main
paths:
- 'module/**'
pull_request:
paths:
- 'module/**'
workflow_dispatch:
jobs:
@@ -40,12 +43,16 @@ jobs:
- name: Check if valid to release
id: check_tag
run: |
VERSION=$(grep '^version=' module/module.prop | sed 's/version=//')
git fetch --tags
if git tag | grep -qx "^$VERSION"; then
if [ "${{ github.event_name }}" = "pull_request" ]; then
echo "version_tag_exists=true" >> $GITHUB_OUTPUT
else
echo "version_tag_exists=false" >> $GITHUB_OUTPUT
VERSION=$(grep '^version=' module/module.prop | sed 's/version=//')
git fetch --tags
if git tag | grep -qx "^$VERSION"; then
echo "version_tag_exists=true" >> $GITHUB_OUTPUT
else
echo "version_tag_exists=false" >> $GITHUB_OUTPUT
fi
fi
release:
@@ -87,13 +94,12 @@ jobs:
FILES="TrickyAddonModule-${VERSION}.zip"
else
PRERELEASE=false
FORMATTED_RELEASE_NOTES=$(echo -e "### Tricky Addon module\n$RELEASE_NOTES\n---\n### Tricky Addon Lite - script only\n- Check on [changelog](https://github.com/KOWX712/Tricky-Addon-Update-Target-List/tree/main/lite-script_only#tricky-addon-lite-update-target-list-script)")
FILES="TrickyAddonModule-${VERSION}.zip,TrickyAddonLite_ScriptOnly_Extract-${VERSION}.zip"
FORMATTED_RELEASE_NOTES=$(echo -e "### Tricky Addon module\n$RELEASE_NOTES\n")
FILES="TrickyAddonModule-${VERSION}.zip"
fi
echo "CURRENT_TAG=$CURRENT_TAG" >> $GITHUB_ENV
echo "ZIP_NAME=TrickyAddonModule-${VERSION}.zip" >> $GITHUB_ENV
echo "LITE_SCRIPT_ONLY_ZIP_NAME=TrickyAddonLite_ScriptOnly_Extract-${VERSION}.zip" >> $GITHUB_ENV
echo "RELEASE_NOTES<<EOF" >> $GITHUB_ENV
echo "$FORMATTED_RELEASE_NOTES" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
@@ -105,12 +111,6 @@ jobs:
cd module && zip -r "../${{ env.ZIP_NAME }}" ./*
echo "Created zip file: ${{ env.ZIP_NAME }}"
- name: Compress lite-script_only folder
if: ${{ env.PRERELEASE == 'false' }}
run: |
cd lite-script_only && zip -r "../${{ env.LITE_SCRIPT_ONLY_ZIP_NAME }}" ./*
echo "Created zip file: ${{ env.LITE_SCRIPT_ONLY_ZIP_NAME }}"
- name: Create release
uses: softprops/action-gh-release@v2
env:

31
.github/workflows/json-lint.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
name: pr_jsonlint
on:
pull_request:
paths:
- 'module/webui/locales/*.json'
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- name: Install JSON linter
run: npm install -g jsonlint
- name: Lint JSON files
run: |
for file in $(find module/webui/locales -name "*.json"); do
echo "Checking $file"
if ! jsonlint "$file"; then
echo "Error in $file"
exit 1
fi
done

View File

@@ -16,28 +16,29 @@ Configure Tricky Store target.txt with KSU WebUI.
### Magisk
- Action button to open WebUI
- Support KSUWebUIStandalone and latest MMRL
- Automatic install [KSUWebUIStandalone](https://github.com/5ec1cff/KsuWebUIStandalone) if none of them are installed.
- Support [KSUWebUIStandalone](https://github.com/5ec1cff/KsuWebUIStandalone) and [MMRL](https://github.com/MMRLApp/MMRL)
- Automatic KSUWebUIStandalone install if none of them are installed.
### What Can This Module Do
| Feature | Status |
|:---|:---:|
| Configure target.txt with app name display | ✅ |
| Select apps from Magisk DenyList (optional) | ✅ |
| Deselect [unnecessary apps](https://github.com/KOWX712/Tricky-Addon-Update-Target-List/blob/main/more-excldue.json) (optional) | ✅ |
| Set verifiedBootHash (optional) | ✅ |
| Provide AOSP Keybox (optional) | ✅ |
| Valid Keybox (not guaranteed) | ❌ |
| Shamiko Whitelist switch ([Why?](https://github.com/rushizgithub/shamiko?tab=readme-ov-file#whitelist)) | ❌ |
| Add `!` or `?` to the target ([Not needed](https://github.com/5ec1cff/TrickyStore/releases/tag/1.1.0)) | ❌ |
| Periodically update target and add new apps | ❌ |
| Add system apps (GMS added by default) | ❌ |
| Feature | Status |
| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----: |
| Configure target.txt with app name display | ✅ |
| Long press to choose `!` or `?` mode for the app. [Auto](https://github.com/5ec1cff/TrickyStore/releases/tag/1.1.0)<br>Use this only when the app cannot work without this. | ✅ |
| Select apps from Magisk DenyList `optional` | ✅ |
| Deselect [unnecessary apps](https://github.com/KOWX712/Tricky-Addon-Update-Target-List/blob/main/more-exclude.json) `optional` | ✅ |
| Set verifiedBootHash `optional` | ✅ |
| Provide AOSP Keybox `optional` | ✅ |
| Valid Keybox `not guaranteed` | ❌ |
| Shamiko Whitelist switch. [Why?](https://github.com/rushizgithub/shamiko?tab=readme-ov-file#whitelist) | ❌ |
| Periodically add all app to target.txt | ❌ |
| Add system apps `GMS added by default` | ❌ |
## Localization
- Read [Translation Guide](https://github.com/KOWX712/Tricky-Addon-Update-Target-List/blob/main/module/webui/locales/A-translate.md)
## Acknowledgement
- [j-hc/zygisk-detach](https://github.com/j-hc/zygisk-detach) - KSU WebUI template
- [markedjs/marked](https://github.com/markedjs/marked) - Markdown Support
## Links
[![release](https://custom-icon-badges.demolab.com/badge/-Download-F25278?style=for-the-badge&logo=download&logoColor=white)](https://github.com/KOWX712/Tricky-Addon-Update-Target-List/releases)

View File

@@ -8,6 +8,39 @@ GitHub release: [Tricky Addon: Update Target List](https://github.com/KOWX712/Tr
Telegram channel: [KOW's Little World](https://t.me/kowchannel)
## Changelog
### v3.3
- Support auto config `security_patch.txt` for Tricky Store v1.2.1 or higher.
- No longer need to add `!` to Play Store for devices that have security patch older than one year to get strong integrity in new A13+ check.
- **Magisk:** automatically add apps from DenyList to `target.txt` on boot. To enable this feature, click "Select from DenyList" once in WebUI after update.
### v3.2
- Add `android` and `com.android.vending` by default.
- Handle `ro.vendor.build.security_patch` if the value is different.
- Updated Japanese translation (#11, @reindex-ot)
- Added Turkish translation (@berkmirsatk)
### v3.1
- Added `com.google.android.gsf` and `com.android.vending` into WebUI app list. (#10, @ChiseWaguri)
- Fixed multiple instances of GMS appeared in the app list when GMS isn't a system app.
- Added auto backup `keybox.xml` to `keybox.xml.bak` before replacing it.
- Minor animation improvements and code optimizations.
- [Markdown support](https://github.com/markedjs/marked) for future update changelog in WebUI.
### v3.0
- Animation improvement: new checkbox animation, new touch ripple animation, and more.
- Adjust save button and prompt position in MMRL.
- Long press on app to select `!` and `?` mode, use this only when the app cannot work without this.
- Display gms in app list.
- Show module version in header.
- Rewrite update method, you can now update module directly in WebUI.
### v2.9
- Preserve `!` and `?` during update target in WebUI.
- Optimized scripts, thanks to @backslashxx.
- Fixed freeze in weak connection.
- Added Spanish, thanks to @Keinta15.
- Removed restriction on installation but module will still be removed if Tricky Store is not found after reboot.
### v2.8
- Fixed all KSUWebUIStandalone freeze issue, removed visible option.
- Reduced WebUI loading time.
@@ -24,7 +57,7 @@ Telegram channel: [KOW's Little World](https://t.me/kowchannel)
- Press any position of app card to select/deselct.
### v2.6
- Invisible module, intergrate action button & webui on tricky store card. You can stil use visible option if you found any issue with invisble module. Thanks for idea from @backslashxx.
- Invisible module, integrate action button & WebUI on Tricky Store card. You can still use visible option if you found any issue with invisible module. Thanks for idea from @backslashxx.
- To uninstall invisble module, scroll down to the bottom of WebUI and press Uninstall WebUI.
- Add update prompt if found new version in webui, and show module if found an update. (invisible)
- Reduced WebUI loading time

View File

@@ -1,4 +1,4 @@
## Tricky Addon Lite: Update Target List Script
## Tricky Addon Lite: Update Target List Script (EOL)
- Script only, **NOT Module**
- Run with root priviledge
- Recommend to run with MT manager

View File

@@ -21,6 +21,8 @@ me.garfieldhan.apatch.next"
ADDITION="
com.google.android.gms
com.google.android.gsf
com.android.vending
io.github.vvb2060.keyattestation
io.github.vvb2060.mahoshojo
icu.nullptr.nativetest"

View File

@@ -3,70 +3,67 @@
###########################################
MODPATH="/data/adb/modules/.TA_utl"
COMPATH="$MODPATH/common"
ORG_PATH="$PATH"
TMP_DIR="$MODPATH/common/tmp"
SCRIPT_DIR="/data/adb/tricky_store"
URL="https://github.com/5ec1cff/KsuWebUIStandalone/releases/download/v1.0/KsuWebUI-1.0-34-release.apk"
APK_DIR="$COMPATH/tmp"
BBPATH="/data/adb/magisk/busybox \
/data/adb/ksu/bin/busybox \
/data/adb/ap/bin/busybox \
/data/adb/modules/busybox-ndk/system/*/busybox"
APK_PATH="$TMP_DIR/base.apk"
check_wget() {
for path in $BBPATH; do
[ -f "$path" ] && BUSYBOX="$path" && break
done
if ! command -v wget >/dev/null || grep -q "wget-curl" "$(command -v wget)"; then
if [ -n "$BUSYBOX" ]; then
wget() { "$BUSYBOX" wget "$@"; }
abort() {
echo "$1"
exit 1
}
download() {
download_type=${1#--}
download_url=$2
download_output=$3
PATH=/data/adb/magisk:/data/data/com.termux/files/usr/bin:$PATH
if command -v curl >/dev/null 2>&1; then
if [ "$download_type" = "output" ]; then
timeout 10 curl -Lo "$download_output" "$download_url"
else
exit 1
timeout 2 curl -s "$download_url"
fi
else
if [ "$download_type" = "output" ]; then
timeout 10 busybox wget --no-check-certificate -qO "$download_output" "$download_url"
else
timeout 2 busybox wget --no-check-certificate -qO- "$download_url"
fi
fi
PATH="$ORG_PATH"
}
get_webui() {
echo "- Downloading the WebUI APK..."
check_wget
if ! wget --no-check-certificate -P "$APK_DIR" "$URL"; then
echo "! Error: APK download failed."
exit 1
fi
echo "- Download complete."
APK_PATH=$(find "$APK_DIR" -type f -name "*.apk" | head -n 1)
if [ -z "$APK_PATH" ]; then
echo "! Error: No APK file found in $APK_DIR."
exit 1
fi
echo "- Downloading KSU WebUI Standalone..."
RESPONSE=$(download --fetch "https://api.github.com/repos/5ec1cff/KsuWebUIStandalone/releases/latest")
URL=$(echo "$RESPONSE" | grep -o '"browser_download_url": "[^"]*"' | cut -d '"' -f 4)
download --output "$URL" "$APK_PATH" || abort "! Error: APK download failed, please check your internet connection."
echo "- Installing..."
if ! pm install -r "$APK_PATH" >/dev/null 2>&1; then
echo "! Error: APK installation failed."
pm install -r "$APK_PATH" || {
rm -f "$APK_PATH"
exit 1
fi
abort "! Error: APK installation failed."
}
echo "- Done."
rm -f "$APK_PATH"
echo "- Launching..."
if ! am start -n "io.github.a13e300.ksuwebui/.WebUIActivity" -e id "tricky_store"; then
echo "! Error: WebUI launch failed."
exit 1
fi
echo "- Application launched successfully."
echo "- Launching WebUI..."
am start -n "io.github.a13e300.ksuwebui/.WebUIActivity" -e id "tricky_store" || abort "! Error: WebUI launch failed."
}
# Lunch KSUWebUI standalone or MMRL, install KSUWebUI standalone if both are not installed
if pm list packages | grep -q "io.github.a13e300.ksuwebui"; then
# Launch KSUWebUI standalone or MMRL, install KSUWebUI standalone if both are not installed
if pm path io.github.a13e300.ksuwebui >/dev/null 2>&1; then
echo "- Launching WebUI in KSUWebUIStandalone..."
am start -n "io.github.a13e300.ksuwebui/.WebUIActivity" -e id "tricky_store"
elif pm list packages | grep -q "com.dergoogler.mmrl"; then
elif pm path com.dergoogler.mmrl >/dev/null 2>&1; then
echo "- Launching WebUI in MMRL WebUI..."
am start -n "com.dergoogler.mmrl/.ui.activity.webui.WebUIActivity" -e MOD_ID "tricky_store"
else
echo "- Installing KSU WebUI..."
echo "! No WebUI app found"
get_webui
fi
echo "- WebUI launched successfully."

View File

@@ -1,71 +1,161 @@
#!/system/bin/sh
#!/bin/sh
MODPATH=${0%/*}
ORG_PATH="$PATH"
SKIPLIST="$MODPATH/tmp/skiplist"
OUTPUT="$MODPATH/tmp/exclude-list"
KBOUTPUT="$MODPATH/tmp/.extra"
BBPATH="/data/adb/magisk/busybox \
/data/adb/ksu/bin/busybox \
/data/adb/ap/bin/busybox \
/data/adb/modules/busybox-ndk/system/*/busybox"
check_wget() {
for path in $BBPATH; do
[ -f "$path" ] && BUSYBOX="$path" && break
done
if ! command -v wget >/dev/null || grep -q "wget-curl" "$(command -v wget)"; then
if [ -n "$BUSYBOX" ]; then
wget() { "$BUSYBOX" wget "$@"; }
else
exit 1
fi
fi
}
if [ "$MODPATH" = "/data/adb/modules/.TA_utl/common" ]; then
MODDIR="/data/adb/modules/.TA_utl"
MAGISK="true"
else
MODDIR="/data/adb/modules/TA_utl"
fi
aapt() { "$MODPATH/aapt" "$@"; }
get_kb() {
check_wget
wget --no-check-certificate -qO "$KBOUTPUT" "https://raw.githubusercontent.com/KOWX712/Tricky-Addon-Update-Target-List/main/.extra"
[ -s "$KBOUTPUT" ] || rm -f "$KBOUTPUT"
# probe for downloaders
# wget = low pref, no ssl.
# curl, has ssl on android, we use it if found
download() {
download_type=${1#--}
download_url=$2
download_output=$3
PATH=/data/adb/ap/bin:/data/adb/ksu/bin:/data/adb/magisk:/data/data/com.termux/files/usr/bin:$PATH
if command -v curl >/dev/null 2>&1; then
if [ "$download_type" = "output" ]; then
timeout 10 curl -Lo "$download_output" "$download_url"
else
timeout 3 curl -s "$download_url"
fi
else
if [ "$download_type" = "output" ]; then
timeout 10 busybox wget --no-check-certificate -qO "$download_output" "$download_url"
else
timeout 3 busybox wget --no-check-certificate -qO- "$download_url"
fi
fi
PATH="$ORG_PATH"
}
get_unnecessary() {
check_wget
if [ ! -s "$OUTPUT" ] || [ ! -f "$OUTPUT" ]; then
wget --no-check-certificate -q -O - "https://raw.githubusercontent.com/KOWX712/Tricky-Addon-Update-Target-List/main/more-excldue.json" 2>/dev/null | \
grep -o '"package-name": *"[^"]*"' | \
awk -F'"' '{print $4}' > "$OUTPUT"
fi
get_xposed
get_kb() {
download --output "https://raw.githubusercontent.com/KOWX712/Tricky-Addon-Update-Target-List/main/.extra" "$KBOUTPUT"
[ -s "$KBOUTPUT" ] || rm -f "$KBOUTPUT"
}
get_xposed() {
pm list packages -3 | cut -d':' -f2 | grep -vxF -f "$SKIPLIST" | grep -vxF -f "$OUTPUT" | while read -r PACKAGE; do
APK_PATH=$(pm path "$PACKAGE" | grep "base.apk" | cut -d':' -f2 | tr -d '\r')
if [[ -n "$APK_PATH" ]]; then
if [ -n "$APK_PATH" ]; then
if aapt dump xmltree "$APK_PATH" AndroidManifest.xml 2>/dev/null | grep -qE "xposed.category|xposeddescription"; then
echo "$PACKAGE" >> "$OUTPUT"
echo "$PACKAGE" >>"$OUTPUT"
fi
fi
done
}
get_unnecessary() {
if [ ! -s "$OUTPUT" ] || [ ! -f "$OUTPUT" ]; then
JSON=$(download --fetch "https://raw.githubusercontent.com/KOWX712/Tricky-Addon-Update-Target-List/main/more-exclude.json") || exit 1
echo "$JSON" | grep -o '"package-name": *"[^"]*"' | awk -F'"' '{print $4}' >"$OUTPUT"
fi
get_xposed
}
check_update() {
check_wget
if [ -d "$MODPATH/update" ]; then
JSON=$(wget --no-check-certificate -q -O - "https://raw.githubusercontent.com/KOWX712/Tricky-Addon-Update-Target-List/main/update.json") || exit 1
REMOTE_VERSION=$(echo "$JSON" | grep -o '"versionCode": *[0-9]*' | awk -F: '{print $2}' | tr -d ' ')
LOCAL_VERSION=$(grep -o 'versionCode=[0-9]*' "$MODPATH/update/module.prop" | awk -F= '{print $2}')
if [ "$REMOTE_VERSION" -gt "$LOCAL_VERSION" ]; then
echo "update"
[ -f "$MODDIR/disable" ] && rm -f "$MODDIR/disable"
JSON=$(download --fetch "https://raw.githubusercontent.com/KOWX712/Tricky-Addon-Update-Target-List/main/update.json") || exit 1
REMOTE_VERSION=$(echo "$JSON" | grep -o '"versionCode": *[0-9]*' | awk -F: '{print $2}' | tr -d ' ')
LOCAL_VERSION=$(grep -o 'versionCode=[0-9]*' "$MODPATH/update/module.prop" | awk -F= '{print $2}')
if [ "$REMOTE_VERSION" -gt "$LOCAL_VERSION" ] && [ ! -f "/data/adb/modules/TA_utl/update" ]; then
if [ "$MAGISK" = "true" ]; then
[ -d "/data/adb/modules/TA_utl" ] && rm -rf "/data/adb/modules/TA_utl"
cp -rf "$MODPATH/update" "/data/adb/modules/TA_utl"
else
cp -f "$MODPATH/update/module.prop" "/data/adb/modules/TA_utl/module.prop"
fi
echo "update"
fi
}
case "$1" in
--kb) get_kb; exit ;;
--unnecessary) get_unnecessary; exit ;;
--xposed) get_xposed; exit ;;
--update) check_update; exit ;;
esac
uninstall() {
if [ "$MAGISK" = "true" ]; then
cp -rf "$MODPATH/update" "/data/adb/modules/TA_utl"
else
cp -f "$MODPATH/update/module.prop" "/data/adb/modules/TA_utl/module.prop"
fi
touch "/data/adb/modules/TA_utl/remove"
}
get_update() {
JSON=$(download --fetch "https://raw.githubusercontent.com/KOWX712/Tricky-Addon-Update-Target-List/main/update.json") || exit 1
ZIP_URL=$(echo "$JSON" | grep -o '"zipUrl": "[^"]*"' | cut -d '"' -f 4) || exit 1
CHANGELOG_URL=$(echo "$JSON" | grep -o '"changelog": "[^"]*"' | cut -d '"' -f 4) || exit 1
download --output "$ZIP_URL" "$MODPATH/tmp/module.zip" || exit 1
download --output "$CHANGELOG_URL" "$MODPATH/tmp/changelog.md" || exit 1
}
install_update() {
command -v magisk >/dev/null 2>&1 && {
magisk --install-module "$MODPATH/tmp/module.zip"
} || command -v apd >/dev/null 2>&1 && {
apd module install "$MODPATH/tmp/module.zip"
} || command -v ksud >/dev/null 2>&1 && {
ksud module install "$MODPATH/tmp/module.zip"
} || exit 1
rm -f "$MODPATH/tmp/module.zip"
rm -f "$MODPATH/tmp/changelog.md"
}
release_note() {
awk '
/^### v[0-9]+\.[0-9]+$/ {
if (!found) {
version = $0;
found = 1;
next
} else {
exit
}
}
found && !/^###/ { content = content $0 "\n" }
END { if (found) { print version; print content } }
' "$MODPATH/tmp/changelog.md"
}
case "$1" in
--kb)
get_kb
exit
;;
--unnecessary)
get_unnecessary
exit
;;
--xposed)
get_xposed
exit
;;
--update)
check_update
exit
;;
--uninstall)
uninstall
exit
;;
--get-update)
get_update
exit
;;
--install-update)
install_update
exit
;;
--release-note)
release_note
exit
;;
esac

View File

@@ -23,10 +23,7 @@ else
abort " ";
fi
if [ ! -d "$TS" ]; then
ui_print "! Tricky store module is not installed"
abort
fi
[ -d "$TS" ] || ui_print "! Warning: Tricky store module not found"
. "$MODPATH/install_func.sh"

View File

@@ -16,10 +16,7 @@ initialize() {
fi
#Set specific path
sed -i "s|\"set-path\"|\"/data/adb/modules/$NEW_MODID/\"|" "$MODPATH/webui/index.js" || {
ui_print "! Failed to set path"
abort
}
sed -i "s|\"set-path\"|\"/data/adb/modules/$NEW_MODID/\"|" "$MODPATH/webui/scripts/main.js" || abort "! Failed to set path"
# Set aapt binary
cp "$MODPATH/module.prop" "$COMPATH/update/module.prop"

View File

@@ -1,7 +1,7 @@
id=TA_utl
name=Tricky Addon - Update Target List
version=v2.8
versionCode=280
version=v3.3
versionCode=330
author=KOWX712
description=A WebUI to conifgure tricky store target.txt
updateJson=https://raw.githubusercontent.com/KOWX712/Tricky-Addon-Update-Target-List/main/update.json

View File

@@ -15,5 +15,5 @@ if [ ! -d "$TS" ] || [ -f "$TS/remove" ]; then
fi
fi
rm -f "$TS/action.sh"
rm -rf "$TS/webroot"
[ -L "$TS/webroot" ] && rm -f "$TS/webroot"
[ -L "$TS/action.sh" ] && rm -f "$TS/action.sh"

View File

@@ -1,17 +1,46 @@
MODPATH=${0%/*}
HIDE_DIR="/data/adb/modules/.TA_utl"
TS="/data/adb/modules/tricky_store"
SCRIPT_DIR="/data/adb/tricky_store"
TARGET_DIR="/data/adb/tricky_store"
TSPA="/data/adb/modules/tsupport-advance"
aapt() { "$MODPATH/common/aapt" "$@"; }
add_denylist_to_target() {
exclamation_target=$(grep '!' "/data/adb/tricky_store/target.txt" | sed 's/!$//')
question_target=$(grep '?' "/data/adb/tricky_store/target.txt" | sed 's/?$//')
target=$(sed 's/[!?]$//' /data/adb/tricky_store/target.txt)
denylist=$(magisk --denylist ls 2>/dev/null | awk -F'|' '{print $1}' | grep -v "isolated")
printf "%s\n" "$target" "$denylist" | sort -u > "/data/adb/tricky_store/target.txt"
for target in $exclamation_target; do
sed -i "s/^$target$/$target!/" "/data/adb/tricky_store/target.txt"
done
for target in $question_target; do
sed -i "s/^$target$/$target?/" "/data/adb/tricky_store/target.txt"
done
}
# Reset verified Boot Hash
hash_value=$(grep -v '^#' "/data/adb/boot_hash" | tr -d '[:space:]')
if [ -n "$hash_value" ]; then
resetprop -n ro.boot.vbmeta.digest "$hash_value"
fi
# Reset vendor patch if different with security patch
security_patch=$(getprop ro.build.version.security_patch)
vendor_patch=$(getprop ro.vendor.build.security_patch)
if [ "$vendor_patch" != "$security_patch" ]; then
TS_version=$(grep "versionCode=" "$TS/module.prop" | cut -d'=' -f2)
if [ "$TS_version" -lt 158 ]; then
resetprop -n ro.vendor.build.security_patch "$security_patch"
else
printf "boot=%s\nvendor=%s\n" "$security_patch" "$security_patch" > "$TARGET_DIR/security_patch.txt"
fi
fi
# Disable TSupport-A auto update target to prevent overwrite
if [ -d "$TSPA" ]; then
touch "/storage/emulated/0/stop-tspa-auto-target"
@@ -19,21 +48,32 @@ elif [ ! -d "$TSPA" ] && [ -f "/storage/emulated/0/stop-tspa-auto-target" ]; the
rm -f "/storage/emulated/0/stop-tspa-auto-target"
fi
# Hide module
# Magisk operation
if [ -f "$MODPATH/action.sh" ]; then
if [[ "$MODPATH" != "$HIDE_DIR" ]]; then
# Hide module from Magisk manager
if [ "$MODPATH" != "$HIDE_DIR" ]; then
rm -rf "$HIDE_DIR"
mv "$MODPATH" "$HIDE_DIR"
fi
MODPATH="$HIDE_DIR"
elif [ -d "$HIDE_DIR" ]; then
rm -rf "$HIDE_DIR"
# Add target from denylist
# To trigger this, choose "Select from DenyList" in WebUI once
[ -f "/data/adb/tricky_store/target_from_denylist" ] && add_denylist_to_target
else
[ -d "$HIDE_DIR" ] && rm -rf "$HIDE_DIR"
fi
# Hide module from APatch, KernelSU, KSUWebUIStandalone, MMRL
rm -f "$MODPATH/module.prop"
# Symlink tricky store
[ -f "$MODPATH/action.sh" ] && ln -s "$MODPATH/action.sh" "$TS/action.sh"
ln -s "$MODPATH/webui" "$TS/webroot"
if [ -f "$MODPATH/action.sh" ] && [ ! -f "$TS/action.sh" ] && [ ! -L "$TS/action.sh" ]; then
ln -s "$MODPATH/action.sh" "$TS/action.sh"
fi
if [ ! -d "$TS/webroot" ] && [ ! -L "$TS/webroot" ]; then
ln -s "$MODPATH/webui" "$TS/webroot"
fi
# Optimization
OUTPUT_APP="$MODPATH/common/tmp/applist"
@@ -43,18 +83,36 @@ until [ "$(getprop sys.boot_completed)" = "1" ]; do
sleep 1
done
# Create temporary directory
mkdir -p "$MODPATH/common/tmp"
# Additional system apps
SYSTEM_APP="com.google.android.gms|com.google.android.gsf|com.android.vending"
# Initialize cache files to save app list and skip list
echo "# This file is generated from service.sh to speed up load time" > "$OUTPUT_APP"
echo "# This file is generated from service.sh to speed up load time" > "$OUTPUT_SKIP"
pm list packages -3 </dev/null 2>&1 | cat | awk -F: '{print $2}' | while read -r PACKAGE; do
APK_PATH=$(pm path "$PACKAGE" </dev/null 2>&1 | cat | grep "base.apk" | awk -F: '{print $2}' | tr -d '\r')
# Get list of third party apps and specific system apps, then cache app name
# Check Xposed module
{
pm list packages -3 2>/dev/null
pm list package -s | grep -E "$SYSTEM_APP"
} | awk -F: '{print $2}' | while read -r PACKAGE; do
# Get APK path for the package
APK_PATH=$(pm path "$PACKAGE" 2>/dev/null | grep "base.apk" | awk -F: '{print $2}' | tr -d '\r')
[ -z "$APK_PATH" ] && APK_PATH=$(pm path "$PACKAGE" 2>/dev/null | grep ".apk" | awk -F: '{print $2}' | tr -d '\r')
if [ -n "$APK_PATH" ]; then
APP_NAME=$(aapt dump badging "$APK_PATH" </dev/null 2>&1 | cat | grep "application-label:" | sed "s/application-label://g; s/'//g")
# Extract app name and save package info
APP_NAME=$(aapt dump badging "$APK_PATH" 2>/dev/null | grep "application-label:" | sed "s/application-label://g; s/'//g")
echo "app-name: $APP_NAME, package-name: $PACKAGE" >> "$OUTPUT_APP"
else
echo "app-name: Unknown App package-name: $PACKAGE" >> "$OUTPUT_APP"
fi
if ! aapt dump xmltree "$APK_PATH" AndroidManifest.xml </dev/null 2>&1 | cat | grep -qE "xposed.category|xposeddescription"; then
# Check if app is Xposed module and add to skip list if not
if ! aapt dump xmltree "$APK_PATH" AndroidManifest.xml 2>/dev/null | grep -qE "xposed.category|xposeddescription"; then
echo "$PACKAGE" >> "$OUTPUT_SKIP"
fi
done

View File

@@ -10,8 +10,10 @@ fi
# Remove residue and restore aosp keybox.
rm -rf "/data/adb/modules/.TA_utl"
rm -f "/data/adb/boot_hash"
rm -f "/data/adb/tricky_store/target_from_denylist"
if [ -d "$TS" ]; then
rm -f "$TS/action.sh"
rm -rf "$TS/webroot"
[ -L "$TS/webroot" ] && rm -f "$TS/webroot"
[ -L "$TS/action.sh" ] && rm -f "$TS/action.sh"
fi
mv -f "$SCRIPT_DIR/keybox.xml" "$SCRIPT_DIR/keybox.xml.bak"
xxd -r -p "$MODPATH/common/.default" | base64 -d > "$SCRIPT_DIR/keybox.xml"

View File

@@ -4,75 +4,81 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title data-i18n="title">Document</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link rel="stylesheet" href="/styles.css" type="text/css">
<title data-i18n="title">TrickyAddon</title>
<link rel="stylesheet" type="text/css" href="/mmrl/insets.css" />
<script type="module" crossorigin src="/index.js"></script>
<link rel="stylesheet" href="styles/global.css" type="text/css">
<link rel="stylesheet" href="styles/about.css" type="text/css">
<link rel="stylesheet" href="styles/applist.css" type="text/css">
<link rel="stylesheet" href="styles/boot-hash.css" type="text/css">
<link rel="stylesheet" href="styles/header.css" type="text/css">
<link rel="stylesheet" href="styles/search_menu.css" type="text/css">
<script type="module" crossorigin src="scripts/main.js"></script>
<script type="module" crossorigin src="scripts/about.js"></script>
<script type="module" crossorigin src="scripts/help.js"></script>
<script type="module" crossorigin src="scripts/vbmeta-digest.js"></script>
<script src="scripts/marked.min.js"></script>
</head>
<body>
<!-- Header -->
<div class="header-block"></div>
<div class="header">
<div id="title" data-i18n="title"></div>
<button id="help-button" class="help-button"><svg xmlns="http://www.w3.org/2000/svg" height="21px" viewBox="0 -1060 960 990" width="21px" fill="#6E6E6E"><path d="M478-240q21 0 35.5-14.5T528-290q0-21-14.5-35.5T478-340q-21 0-35.5 14.5T428-290q0 21 14.5 35.5T478-240Zm-36-154h74q0-33 7.5-52t42.5-52q26-26 41-49.5t15-56.5q0-56-41-86t-97-30q-57 0-92.5 30T342-618l66 26q5-18 22.5-39t53.5-21q32 0 48 17.5t16 38.5q0 20-12 37.5T506-526q-44 39-54 59t-10 73Zm38 314q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg></button>
<div id="title" data-i18n="header.title"></div><span id="module-version"></span>
<button id="help-button" class="help-button">
<svg xmlns="http://www.w3.org/2000/svg" height="21px" viewBox="0 -1060 960 990" width="21px" fill="#6E6E6E"><path d="M478-240q21 0 35.5-14.5T528-290q0-21-14.5-35.5T478-340q-21 0-35.5 14.5T428-290q0 21 14.5 35.5T478-240Zm-36-154h74q0-33 7.5-52t42.5-52q26-26 41-49.5t15-56.5q0-56-41-86t-97-30q-57 0-92.5 30T342-618l66 26q5-18 22.5-39t53.5-21q32 0 48 17.5t16 38.5q0 20-12 37.5T506-526q-44 39-54 59t-10 73Zm38 314q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z" /></svg>
</button>
<div class="no-connection">
<svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -920 960 960" width="20px" fill="#6E6E6E"><path d="M790-56 414-434q-47 11-87.5 33T254-346l-84-86q32-32 69-56t79-42l-90-90q-41 21-76.5 46.5T84-516L0-602q32-32 66.5-57.5T140-708l-84-84 56-56 736 736-58 56Zm-310-64q-42 0-71-29.5T380-220q0-42 29-71t71-29q42 0 71 29t29 71q0 41-29 70.5T480-120Zm236-238-29-29-29-29-144-144q81 8 151.5 41T790-432l-74 74Zm160-158q-77-77-178.5-120.5T480-680q-21 0-40.5 1.5T400-674L298-776q44-12 89.5-18t92.5-6q142 0 265 53t215 145l-84 86Z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -920 960 960" width="20px" fill="#6E6E6E"><path d="M790-56 414-434q-47 11-87.5 33T254-346l-84-86q32-32 69-56t79-42l-90-90q-41 21-76.5 46.5T84-516L0-602q32-32 66.5-57.5T140-708l-84-84 56-56 736 736-58 56Zm-310-64q-42 0-71-29.5T380-220q0-42 29-71t71-29q42 0 71 29t29 71q0 41-29 70.5T480-120Zm236-238-29-29-29-29-144-144q81 8 151.5 41T790-432l-74 74Zm160-158q-77-77-178.5-120.5T480-680q-21 0-40.5 1.5T400-674L298-776q44-12 89.5-18t92.5-6q142 0 265 53t215 145l-84 86Z" /></svg>
</div>
<div class="language-dropdown">
<button class="language-button">
<i class="material-icons">language</i>
<i class="language-icon">
<svg xmlns="http://www.w3.org/2000/svg" height="22px" viewBox="0 -960 960 960" width="22px"><path d="M480-80q-82 0-155-31.5t-127.5-86Q143-252 111.5-325T80-480q0-83 31.5-155.5t86-127Q252-817 325-848.5T480-880q83 0 155.5 31.5t127 86q54.5 54.5 86 127T880-480q0 82-31.5 155t-86 127.5q-54.5 54.5-127 86T480-80Zm0-82q26-36 45-75t31-83H404q12 44 31 83t45 75Zm-104-16q-18-33-31.5-68.5T322-320H204q29 50 72.5 87t99.5 55Zm208 0q56-18 99.5-55t72.5-87H638q-9 38-22.5 73.5T584-178ZM170-400h136q-3-20-4.5-39.5T300-480q0-21 1.5-40.5T306-560H170q-5 20-7.5 39.5T160-480q0 21 2.5 40.5T170-400Zm216 0h188q3-20 4.5-39.5T580-480q0-21-1.5-40.5T574-560H386q-3 20-4.5 39.5T380-480q0 21 1.5 40.5T386-400Zm268 0h136q5-20 7.5-39.5T800-480q0-21-2.5-40.5T790-560H654q3 20 4.5 39.5T660-480q0 21-1.5 40.5T654-400Zm-16-240h118q-29-50-72.5-87T584-782q18 33 31.5 68.5T638-640Zm-234 0h152q-12-44-31-83t-45-75q-26 36-45 75t-31 83Zm-200 0h118q9-38 22.5-73.5T376-782q-56 18-99.5 55T204-640Z"/></svg>
</i>
</button>
<div class="language-menu">
<button class="language-option" data-lang="en-US">English</button>
<button class="language-option" data-lang="ja-JP">日本語</button>
<button class="language-option" data-lang="ru-RU">Русский</button>
<button class="language-option" data-lang="tl-PH">Tagalog</button>
<button class="language-option" data-lang="zh-CN">中文(简体)</button>
<button class="language-option" data-lang="zh-TW">中文(繁体)</button>
</div>
<div class="language-menu"></div>
<div id="language-overlay" class="language-overlay"></div>
</div>
</div>
<!-- Loading Element -->
<div class="loading" data-i18n="loading"></div>
<div class="loading" data-i18n="loading.loading"></div>
<!-- Prompt Element -->
<div id="prompt" class="prompt"></div>
<!-- Floating Button Element -->
<div class="floating-card">
<button class="floating-btn" id="save" data-i18n="save_and_update_button"></button>
<button class="floating-btn" id="save" data-i18n="functional_button.save_and_update_button"></button>
</div>
<!-- Menu Options -->
<div class="search-menu-container">
<div class="search-card">
<span class="search-icon">
<svg xmlns="http://www.w3.org/2000/svg" height="19px" viewBox="0 -960 960 960" width="24px" fill="#6E6E6E"><path d="M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" height="19px" viewBox="0 -960 960 960" width="24px" fill="#6E6E6E"><path d="M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z" /></svg>
</span>
<input type="text" class="search-input" id="search" placeholder="Search" data-i18n="search_placeholder">
<input type="text" class="search-input" id="search" placeholder="" data-i18n="search_bar.search_placeholder">
<button class="clear-btn" id="clear-btn">&#x2715;</button>
</div>
<div class="menu">
<input type="checkbox" id="menu-toggle" class="menu-toggle">
<label for="menu-toggle" id="menu-button">
<span class="menu-icon"><i class="fa fa-bars"></i></span>
<label for="menu-toggle" class="menu-button" id="menu-button">
<i class="menu-icon">
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -1060 960 960" width="24px"><path d="M120-240v-80h720v80H120Zm0-200v-80h720v80H120Zm0-200v-80h720v80H120Z"/></svg>
</i>
</label>
<div id="menu-options" class="menu-options">
<ul>
<li id="refresh" data-i18n="refresh"></li>
<li id="select-all" data-i18n="select_all"></li>
<li id="deselect-all" data-i18n="deselect_all"></li>
<li id="select-denylist" data-i18n="select_denylist"></li>
<li id="deselect-unnecessary" data-i18n="deselect_unnecessary"></li>
<li id="aospkb" data-i18n="set_aosp_keybox"></li>
<li id="extrakb" data-i18n="set_valid_keybox"></li>
<li id="boot-hash" data-i18n="set_verified_boot_hash"></li>
<li id="about" data-i18n="about"></li>
<li id="refresh" data-i18n="menu.refresh"></li>
<li id="select-all" data-i18n="menu.select_all"></li>
<li id="deselect-all" data-i18n="menu.deselect_all"></li>
<li id="select-denylist" data-i18n="menu.select_denylist"></li>
<li id="deselect-unnecessary" data-i18n="menu.deselect_unnecessary"></li>
<li id="aospkb" data-i18n="menu.set_aosp_keybox"></li>
<li id="extrakb" data-i18n="menu.set_valid_keybox"></li>
<li id="boot-hash" data-i18n="menu.set_verified_boot_hash"></li>
<li id="about" data-i18n="menu.about"></li>
</ul>
</div>
</div>
@@ -82,15 +88,49 @@
<!-- Applist Display -->
<div id="apps-list"></div>
<div class="update-card" id="update-card">
<p id="update-available" data-i18n="update_available"></p>
<p id="redirect-to-release" data-i18n="redirect_to_release"></p>
<p id="update-available" data-i18n="update.update_available"></p>
<p id="redirect-to-release" data-i18n="update.redirect_to_release"></p>
</div>
<div class="mode-overlay"></div>
<template id="app-template">
<div class="card-box">
<div class="card">
<div class="card" id="card">
<div class="content" data-package="">
<div class="mode">
<label class="mode-switch" id="normal">
<input type="radio" class="mode-input" id="normal-mode">
<i class="mode-icon">
<div class="status-indicator" id="normal-indicator">
<svg xmlns="http://www.w3.org/2000/svg" height="30px" viewBox="0 -960 960 960" width="30px" fill="#ffffff"><path d="M480-480Zm0 280q-116 0-198-82t-82-198q0-116 82-198t198-82q116 0 198 82t82 198q0 116-82 198t-198 82Zm0-80q83 0 141.5-58.5T680-480q0-83-58.5-141.5T480-680q-83 0-141.5 58.5T280-480q0 83 58.5 141.5T480-280Z"/></svg>
</div>
</i>
</label>
<label class="mode-switch" id="generate">
<input type="radio" class="mode-input" id="generate-mode">
<i class="mode-icon">
<div class="status-indicator" id="generate-indicator">
<svg xmlns="http://www.w3.org/2000/svg" height="18px" viewBox="0 -960 960 960" width="18px" fill="#ffffff"><path d="M480-120q-33 0-56.5-23.5T400-200q0-33 23.5-56.5T480-280q33 0 56.5 23.5T560-200q0 33-23.5 56.5T480-120Zm-80-240v-480h160v480H400Z"/></svg>
</div>
</i>
</label>
<label class="mode-switch" id="hack">
<input type="radio" class="mode-input" id="hack-mode">
<i class="mode-icon">
<div class="status-indicator" id="hack-indicator">
<svg xmlns="http://www.w3.org/2000/svg" height="19px" viewBox="0 -960 960 960" width="19px" fill="#ffffff"><path d="M424-320q0-81 14.5-116.5T500-514q41-36 62.5-62.5T584-637q0-41-27.5-68T480-732q-51 0-77.5 31T365-638l-103-44q21-64 77-111t141-47q105 0 161.5 58.5T698-641q0 50-21.5 85.5T609-475q-49 47-59.5 71.5T539-320H424Zm56 240q-33 0-56.5-23.5T400-160q0-33 23.5-56.5T480-240q33 0 56.5 23.5T560-160q0 33-23.5 56.5T480-80Z"/></svg>
</div>
</i>
</label>
</div>
<p class="name"></p>
<input type="checkbox" class="checkbox">
<div class="checkbox-wrapper">
<input type="checkbox" class="checkbox" id="checkbox1" disabled />
<label for="checkbox1" class="custom-checkbox">
<span class="tick-symbol">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -3 26 26" width="16px" height="16px" fill="#fff"><path d="M 22.566406 4.730469 L 20.773438 3.511719 C 20.277344 3.175781 19.597656 3.304688 19.265625 3.796875 L 10.476563 16.757813 L 6.4375 12.71875 C 6.015625 12.296875 5.328125 12.296875 4.90625 12.71875 L 3.371094 14.253906 C 2.949219 14.675781 2.949219 15.363281 3.371094 15.789063 L 9.582031 22 C 9.929688 22.347656 10.476563 22.613281 10.96875 22.613281 C 11.460938 22.613281 11.957031 22.304688 12.277344 21.839844 L 22.855469 6.234375 C 23.191406 5.742188 23.0625 5.066406 22.566406 4.730469 Z"/></svg>
</span>
</label>
</div>
</div>
</div>
</div>
@@ -101,8 +141,58 @@
<div class="help-menu">
<button id="close-help" class="close-help">&#x2715;</button>
<div class="help-content">
<p data-i18n="help_instructions"></p>
<ul id="help-list"></ul>
<p data-i18n="help.help_instructions"></p>
<ul id="helpList">
<li id="save_and_update_button">
<strong data-i18n="help.save_and_update"></strong>
<ul>
<li data-i18n="help.save_and_update_description"></li>
</ul>
</li>
<br>
<li id="refresh">
<strong data-i18n="help.refresh"></strong>
<ul>
<li data-i18n="help.refresh_description"></li>
</ul>
</li>
<br>
<li id="select_deselect">
<strong data-i18n="help.select_deselect"></strong>
<ul>
<li data-i18n="help.select_description"></li>
</ul>
</li>
<br>
<li id="select_denylist">
<strong data-i18n="help.select_denylist"></strong>
<ul>
<li data-i18n="help.select_denylist_description"></li>
</ul>
</li>
<br>
<li id="deselect_unnecessary">
<strong data-i18n="help.deselect_unnecessary"></strong>
<ul>
<li data-i18n="help.deselect_unnecessary_description"></li>
</ul>
</li>
<br>
<li id="set_keybox">
<strong data-i18n="help.set_keybox"></strong>
<ul>
<li data-i18n="help.set_keybox_description"></li>
</ul>
</li>
<br>
<li id="set_verified_boot_hash">
<strong data-i18n="help.set_verified_boot_hash"></strong>
<ul>
<li data-i18n="help.set_verified_boot_hash_description"></li>
</ul>
</li>
<br>
</ul>
</div>
</div>
</div>
@@ -110,10 +200,11 @@
<!-- BootHash Input Overlay -->
<div id="boot-hash-overlay" class="boot-hash-overlay"></div>
<div id="boot-hash-card" class="boot-hash-card">
<textarea id="boot-hash-input" class="input-box" placeholder="Paste your verified Boot Hash here"
data-i18n="boot_hash_input_placeholder"></textarea>
<div class="boot-hash-value">
<textarea id="boot-hash-input" class="input-box" placeholder="Paste your verified Boot Hash here" data-i18n="reset_vbmeta.boot_hash_input_placeholder"></textarea>
</div>
<div class="button-container">
<button id="boot-hash-save-button" class="boot-hash-save-button" data-i18n="boot_hash_save_button"></button>
<button id="boot-hash-save-button" class="boot-hash-save-button" data-i18n="reset_vbmeta.boot_hash_save_button"></button>
</div>
</div>
@@ -122,21 +213,43 @@
<div id="about-menu" class="about-menu">
<button id="close-about" class="close-about">&#x2715;</button>
<div class="about-content">
<p data-i18n="module_name_line1"></p>
<p data-i18n="module_name_line2"></p>
<p><span data-i18n="by"></span>KOWX712</p>
<p id="module_name_line1" data-i18n="about.module_name_line1"></p>
<p id="module_name_line2" data-i18n="about.module_name_line2"></p>
<p><span id="authored" data-i18n="about.by"></span> KOWX712</p>
<br>
<p data-i18n="disclaimer"></p>
<p id="disclaimer" data-i18n="about.disclaimer"></p>
<br>
<p>
<div class="link">
<i class="fa fa-telegram" id="telegram" aria-hidden="true"> <span id="link-text" data-i18n="telegram_channel"></span></i>
<i class="fa fa-github" id="github" aria-hidden="true"> <span id="link-text" data-i18n="github"></span></i>
</div>
<div class="link">
<i class="link-icon" id="telegram" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 0 496 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M248 8C111 8 0 119 0 256S111 504 248 504 496 393 496 256 385 8 248 8zM363 176.7c-3.7 39.2-19.9 134.4-28.1 178.3-3.5 18.6-10.3 24.8-16.9 25.4-14.4 1.3-25.3-9.5-39.3-18.7-21.8-14.3-34.2-23.2-55.3-37.2-24.5-16.1-8.6-25 5.3-39.5 3.7-3.8 67.1-61.5 68.3-66.7 .2-.7 .3-3.1-1.2-4.4s-3.6-.8-5.1-.5q-3.3 .7-104.6 69.1-14.8 10.2-26.9 9.9c-8.9-.2-25.9-5-38.6-9.1-15.5-5-27.9-7.7-26.8-16.3q.8-6.7 18.5-13.7 108.4-47.2 144.6-62.3c68.9-28.6 83.2-33.6 92.5-33.8 2.1 0 6.6 .5 9.6 2.9a10.5 10.5 0 0 1 3.5 6.7A43.8 43.8 0 0 1 363 176.7z"/></svg>
<span id="link-text" data-i18n="about.telegram_channel"></span>
</i>
<i class="link-icon" id="github" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 0 496 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3 .3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5 .3-6.2 2.3zm44.2-1.7c-2.9 .7-4.9 2.6-4.6 4.9 .3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3 .7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3 .3 2.9 2.3 3.9 1.6 1 3.6 .7 4.3-.7 .7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3 .7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3 .7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/></svg>
<span id="link-text" data-i18n="about.github"></span>
</i>
</div>
</p>
<br>
<p data-i18n="acknowledgment"></p>
<p id="acknowledgment" data-i18n="about.acknowledgment"></p>
<p>j-hc/zygisk-detach: WebUI template</p>
<p>markedjs/marked: Markdown Support</p>
</div>
</div>
</div>
<!-- Update Overlay -->
<div class="update-overlay">
<div class="update-menu">
<button class="close-update">&#x2715;</button>
<div class="update-content">
<h1 data-i18n="update.changelog"></h1>
<div class="changelog"></div>
<div class="update-button-container">
<button class="install" data-i18n="update.install"></button>
<button class="reboot" data-i18n="update.reboot"></button>
</div>
</div>
</div>
</div>
@@ -144,8 +257,8 @@
<!-- Footer -->
<div class="footer">
<div class="uninstall-container hidden-uninstall">
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#FFFFFF"><path d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z"/></svg>
<span data-i18n="uninstall_webui"></span>
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#FFFFFF"><path d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z" /></svg>
<span data-i18n="functional_button.uninstall_webui"></span>
</div>
</div>
</body>

View File

@@ -1,851 +0,0 @@
// Header Elements
const headerBlock = document.querySelector('.header-block');
const title = document.querySelector('.header');
const helpButton = document.getElementById('help-button');
const noConnection = document.querySelector('.no-connection');
const languageButton = document.querySelector('.language-button');
const languageMenu = document.querySelector('.language-menu');
const languageOptions = document.querySelectorAll('.language-option');
const languageOverlay = document.getElementById('language-overlay');
// Help Overlay Elements
const helpOverlay = document.getElementById('help-overlay');
const closeHelp = document.getElementById('close-help');
const helpList = document.getElementById('help-list');
// Search and Menu Elements
const searchInput = document.getElementById('search');
const clearBtn = document.getElementById('clear-btn');
const searchMenuContainer = document.querySelector('.search-menu-container');
const searchCard = document.querySelector('.search-card');
const menu = document.querySelector('.menu');
const menuButton = document.getElementById('menu-button');
const menuOptions = document.getElementById('menu-options');
const selectDenylistElement = document.getElementById('select-denylist');
const menuOverlay = document.getElementById('menu-overlay');
const menuIcon = menuButton.querySelector('.menu-icon');
// BootHash Overlay Elements
const bootHashOverlay = document.getElementById('boot-hash-overlay');
const card = document.getElementById('boot-hash-card');
const inputBox = document.getElementById('boot-hash-input');
const saveButton = document.getElementById('boot-hash-save-button');
// Applist Elements
const appTemplate = document.getElementById('app-template').content;
const appListContainer = document.getElementById('apps-list');
const updateCard = document.getElementById('update-card');
// Loading, Save and Prompt Elements
const loadingIndicator = document.querySelector('.loading');
const floatingBtn = document.querySelector('.floating-btn');
const prompt = document.getElementById('prompt');
// About Elements
const telegramLink = document.getElementById('telegram');
const githubLink = document.getElementById('github');
const basePath = "set-path";
const ADDITIONAL_APPS = [
"com.google.android.gms",
"io.github.vvb2060.keyattestation",
"io.github.vvb2060.mahoshojo",
"icu.nullptr.nativetest"
];
// Variables
let e = 0;
let excludeList = [];
let isRefreshing = false;
let translations = {};
let currentLang = 'en-US';
let availableLanguages = ['en-US'];
// Function to check for available language
async function initializeAvailableLanguages() {
try {
const multiLang = await execCommand(`find ${basePath}webui/locales -type f -name "*.json" ! -name "A-template.json" -exec basename -s .json {} \\;`);
availableLanguages = multiLang.trim().split('\n');
} catch (error) {
console.error('Failed to fetch available languages:', error);
availableLanguages = ['en-US'];
}
}
// Function to detect user's default language
function detectUserLanguage() {
const userLang = navigator.language || navigator.userLanguage;
const langCode = userLang.split('-')[0];
if (availableLanguages.includes(userLang)) {
return userLang;
} else if (availableLanguages.includes(langCode)) {
return langCode;
} else {
return 'en-US';
}
}
// Load translations dynamically based on the selected language
async function loadTranslations(lang) {
try {
const response = await fetch(`/locales/${lang}.json`);
translations = await response.json();
applyTranslations();
} catch (error) {
console.error(`Error loading translations for ${lang}:`, error);
if (lang !== 'en-US') {
console.log("Falling back to English.");
loadTranslations('en-US');
}
}
}
// Function to apply translations to all elements with data-i18n attributes
function applyTranslations() {
document.querySelectorAll("[data-i18n]").forEach((el) => {
const key = el.getAttribute("data-i18n");
if (translations[key]) {
if (el.hasAttribute("placeholder")) {
el.setAttribute("placeholder", translations[key]);
} else {
el.textContent = translations[key];
}
}
});
updateHelpMenu();
}
// Language selection event listener
document.querySelectorAll(".language-option").forEach((button) => {
button.addEventListener("click", (e) => {
const lang = e.target.getAttribute("data-lang");
loadTranslations(lang);
});
});
// Function to setup the language menu
function setupLanguageMenu() {
languageButton.addEventListener("click", (event) => {
event.stopPropagation();
const isVisible = languageMenu.classList.contains("show");
if (isVisible) {
closeLanguageMenu();
} else {
openLanguageMenu();
}
});
document.addEventListener("click", (event) => {
if (!languageButton.contains(event.target) && !languageMenu.contains(event.target)) {
closeLanguageMenu();
}
});
languageOptions.forEach(option => {
option.addEventListener("click", () => {
closeLanguageMenu();
});
});
window.addEventListener('scroll', () => {
if (languageMenu.classList.contains("show")) {
closeLanguageMenu();
}
});
function openLanguageMenu() {
languageMenu.classList.add("show");
languageOverlay.style.display = 'flex';
}
function closeLanguageMenu() {
languageMenu.classList.remove("show");
languageOverlay.style.display = 'none';
}
}
// Focus on search input when search card is clicked
searchCard.addEventListener("click", () => {
searchInput.focus();
});
// Search functionality
searchInput.addEventListener("input", (e) => {
const searchQuery = e.target.value.toLowerCase();
const apps = appListContainer.querySelectorAll(".card");
apps.forEach(app => {
const name = app.querySelector(".name").textContent.toLowerCase();
app.style.display = name.includes(searchQuery) ? "block" : "none";
window.scrollTo(0, 0);
});
if (searchQuery !== "") {
clearBtn.style.display = "block";
} else {
clearBtn.style.display = "none";
}
});
// Clear search input
clearBtn.addEventListener("click", () => {
searchInput.value = "";
clearBtn.style.display = "none";
window.scrollTo(0, 0);
const apps = appListContainer.querySelectorAll(".card");
apps.forEach(app => {
app.style.display = "block";
});
});
// Function to dynamically update the help menu
function updateHelpMenu() {
helpList.innerHTML = "";
const helpSections = [
{ title: "save_and_update_button", description: "save_and_update_description" },
{ title: "refresh", description: "refresh_description" },
{ title: "select_deselect", description: "select_description" },
{ title: "select_denylist", description: "select_denylist_description" },
{ title: "deselect_unnecessary", description: "deselect_unnecessary_description" },
{ title: "set_keybox", description: "set_aosp_keybox_description" },
{ title: "set_verified_boot_hash", description: "set_verified_boot_hash_description" }
];
helpSections.forEach((section) => {
const listItem = document.createElement("li");
listItem.innerHTML = `<strong data-i18n="${section.title}">${translations[section.title]}</strong>`;
const description = document.createElement("ul");
const descriptionItem = document.createElement("li");
descriptionItem.textContent = translations[section.description] || `[Missing: ${section.description}]`;
description.appendChild(descriptionItem);
listItem.appendChild(description);
helpList.appendChild(listItem);
const emptyLine = document.createElement("li");
emptyLine.innerHTML = "<br>";
helpList.appendChild(emptyLine);
});
}
// Function to setup the help menu
function setupHelpOverlay() {
helpButton.addEventListener("click", () => {
helpOverlay.classList.remove("hide");
helpOverlay.style.display = "flex";
requestAnimationFrame(() => {
helpOverlay.classList.add("show");
});
document.body.classList.add("no-scroll");
});
const hideHelpOverlay = () => {
helpOverlay.classList.remove("show");
helpOverlay.classList.add("hide");
document.body.classList.remove("no-scroll");
setTimeout(() => {
helpOverlay.style.display = "none";
}, 200);
};
closeHelp.addEventListener("click", hideHelpOverlay);
helpOverlay.addEventListener("click", (event) => {
if (event.target === helpOverlay) {
hideHelpOverlay();
}
});
}
// Function to toggle menu option
function setupMenuToggle() {
let menuOpen = false;
let menuAnimating = false;
menuButton.addEventListener('click', (event) => {
if (menuAnimating) return;
event.stopPropagation();
if (menuOptions.classList.contains('visible')) {
closeMenu();
} else {
openMenu();
}
});
document.addEventListener('click', (event) => {
if (!menuOptions.contains(event.target) && event.target !== menuButton) {
closeMenu();
}
});
window.addEventListener('scroll', () => {
if (menuOptions.classList.contains('visible')) {
closeMenu();
}
});
const menuOptionsList = document.querySelectorAll('#menu-options li');
menuOptionsList.forEach(option => {
option.addEventListener('click', (event) => {
event.stopPropagation();
closeMenu();
});
});
function openMenu() {
menuAnimating = true;
menuOptions.style.display = 'block';
setTimeout(() => {
menuOptions.classList.remove('hidden');
menuOptions.classList.add('visible');
menuIcon.classList.add('menu-open');
menuIcon.classList.remove('menu-closed');
menuOverlay.style.display = 'flex';
menuOpen = true;
menuAnimating = false;
}, 10);
}
function closeMenu() {
if (menuOptions.classList.contains('visible')) {
menuAnimating = true;
menuOptions.classList.remove('visible');
menuOptions.classList.add('hidden');
menuIcon.classList.remove('menu-open');
menuIcon.classList.add('menu-closed');
menuOverlay.style.display = 'none';
setTimeout(() => {
menuOptions.style.display = 'none';
menuOpen = false;
menuAnimating = false;
}, 200);
}
}
}
// Function to refresh app list
async function refreshAppList() {
isRefreshing = true;
title.style.transform = 'translateY(0)';
searchMenuContainer.style.transform = 'translateY(0)';
floatingBtn.style.transform = 'translateY(0)';
searchInput.value = '';
clearBtn.style.display = "none";
appListContainer.innerHTML = '';
loadingIndicator.style.display = 'flex';
document.querySelector('.uninstall-container').classList.add('hidden-uninstall');
await new Promise(resolve => setTimeout(resolve, 500));
window.scrollTo(0, 0);
if (noConnection.style.display === "flex") {
try {
await updateCheck();
await execCommand(`[ -f ${basePath}common/tmp/exclude-list ] && rm -f "${basePath}common/tmp/exclude-list"`);
} catch (error) {
console.error("Error occurred:", error);
}
}
await fetchAppList();
loadingIndicator.style.display = 'none';
document.querySelector('.uninstall-container').classList.remove('hidden-uninstall');
isRefreshing = false;
}
// Function to select all visible apps
function selectAllApps() {
document.querySelectorAll(".card").forEach(card => {
if (card.style.display !== "none") {
card.querySelector(".checkbox").checked = true;
}
});
}
// Function to deselect all visible apps
function deselectAllApps() {
document.querySelectorAll(".card").forEach(card => {
if (card.style.display !== "none") {
card.querySelector(".checkbox").checked = false;
}
});
}
// Function to run the update check
async function updateCheck() {
try {
const scriptPath = `sh ${basePath}common/get_extra.sh --update`;
const output = await execCommand(scriptPath);
console.log("update script executed successfully.");
noConnection.style.display = "none";
if (output.includes("update")) {
console.log("Update detected from extra script.");
showPrompt("new_update");
updateCard.style.display = "flex";
await execCommand(`
su -c "
if [ -f '${basePath}action.sh' ]; then
if [ -d "/data/adb/modules/TA_utl" ]; then
rm -rf "/data/adb/modules/TA_utl"
fi
cp -rf '${basePath}common/update' '/data/adb/modules/TA_utl'
else
cp '${basePath}common/update/module.prop' '/data/adb/modules/TA_utl/module.prop'
fi
"
`);
} else {
console.log("No update detected from extra script.");
}
} catch (error) {
console.error("Failed to execute update script:", error);
showPrompt("no_internet", false);
noConnection.style.display = "flex";
}
}
// Function to read the exclude list and uncheck corresponding apps
async function deselectUnnecessaryApps() {
try {
const fileCheck = await execCommand(`test -f ${basePath}common/tmp/exclude-list && echo "exists" || echo "not found"`);
if (fileCheck.trim() === "not found") {
setTimeout(async () => {
await execCommand(`sh ${basePath}common/get_extra.sh --unnecessary`);
}, 0);
console.log("Exclude list not found. Running the unnecessary apps script.");
} else {
setTimeout(async () => {
await execCommand(`sh ${basePath}common/get_extra.sh --xposed`);
}, 0);
console.log("Exclude list found. Running xposed script.");
}
await new Promise(resolve => setTimeout(resolve, 100));
const result = await execCommand(`cat ${basePath}common/tmp/exclude-list`);
const UnnecessaryApps = result.split("\n").map(app => app.trim()).filter(Boolean);
const apps = document.querySelectorAll(".card");
apps.forEach(app => {
const contentElement = app.querySelector(".content");
const packageName = contentElement.getAttribute("data-package");
const checkbox = app.querySelector(".checkbox");
if (UnnecessaryApps.includes(packageName)) {
checkbox.checked = false;
}
});
console.log("Unnecessary apps deselected successfully.");
} catch (error) {
console.error("Failed to deselect unnecessary apps:", error);
}
}
// Function to check if Magisk
async function checkMagisk() {
try {
const hasDenylistCondition = await execCommand(`
if [ ! -f "/data/adb/apd" ] && [ ! -f "/data/adb/ksud" ]; then
echo "OK"
else
echo ""
fi
`);
if (hasDenylistCondition.trim() === "OK") {
console.log("Denylist conditions met, displaying element.");
selectDenylistElement.style.display = "flex";
} else {
console.log("ksud or apd detected, leaving denylist element hidden.");
}
} catch (error) {
console.error("Error while checking denylist conditions:", error);
}
}
// Function to read the denylist and check corresponding apps
async function selectDenylistApps() {
try {
const result = await execCommand(`
magisk --denylist ls 2>/dev/null | \
awk -F'|' '{print $1}' | \
grep -v "isolated" | \
sort | uniq
`);
const denylistApps = result.split("\n").map(app => app.trim()).filter(Boolean);
const apps = document.querySelectorAll(".card");
await deselectAllApps();
apps.forEach(app => {
const contentElement = app.querySelector(".content");
const packageName = contentElement.getAttribute("data-package");
const checkbox = app.querySelector(".checkbox");
if (denylistApps.includes(packageName)) {
checkbox.checked = true; // Select the app if found in denylist
}
});
console.log("Denylist apps selected successfully.");
} catch (error) {
console.error("Failed to select Denylist apps:", error);
}
}
// Function to replace aosp kb
async function aospkb() {
try {
const sourcePath = `${basePath}common/.default`;
const destinationPath = "/data/adb/tricky_store/keybox.xml";
await execCommand(`xxd -r -p ${sourcePath} | base64 -d > ${destinationPath}`);
console.log("AOSP keybox copied successfully.");
showPrompt("aosp_key_set");
} catch (error) {
console.error("Failed to copy AOSP keybox:", error);
showPrompt("key_set_error", false);
}
}
// Function to replace valid kb
async function extrakb() {
setTimeout(async () => {
await execCommand(`sh ${basePath}common/get_extra.sh --kb`);
}, 100);
const sourcePath = `${basePath}common/tmp/.extra`;
const destinationPath = "/data/adb/tricky_store/keybox.xml";
try {
await new Promise(resolve => setTimeout(resolve, 300));
const fileExists = await execCommand(`[ -f ${sourcePath} ] && echo "exists"`);
if (fileExists.trim() !== "exists") {
throw new Error(".extra file not found");
}
await execCommand(`xxd -r -p ${sourcePath} | base64 -d > ${destinationPath}`);
console.log("Valid keybox copied successfully.");
showPrompt("valid_key_set");
} catch (error) {
console.error("Failed to copy valid keybox:", error);
await aospkb();
showPrompt("no_valid_fallback", false);
}
}
// Function to handle Verified Boot Hash
async function setBootHash() {
const showCard = () => {
bootHashOverlay.style.display = "flex";
card.style.display = "flex";
requestAnimationFrame(() => {
bootHashOverlay.classList.add("show");
card.classList.add("show");
});
document.body.style.overflow = "hidden";
};
const closeCard = () => {
bootHashOverlay.classList.remove("show");
card.classList.remove("show");
setTimeout(() => {
bootHashOverlay.style.display = "none";
card.style.display = "none";
document.body.style.overflow = "auto";
}, 200);
};
showCard();
try {
const bootHashContent = await execCommand("cat /data/adb/boot_hash");
const validHash = bootHashContent
.split("\n")
.filter(line => !line.startsWith("#") && line.trim())[0];
inputBox.value = validHash || "";
} catch (error) {
console.warn("Failed to read boot_hash file. Defaulting to empty input.");
inputBox.value = "";
}
saveButton.addEventListener("click", async () => {
const inputValue = inputBox.value.trim();
try {
await execCommand(`echo "${inputValue}" > /data/adb/boot_hash`);
await execCommand(`su -c resetprop -n ro.boot.vbmeta.digest ${inputValue}`);
showPrompt("boot_hash_set");
closeCard();
} catch (error) {
console.error("Failed to update boot_hash:", error);
showPrompt("boot_hash_set_error", false);
}
});
bootHashOverlay.addEventListener("click", (event) => {
if (event.target === bootHashOverlay) closeCard();
});
}
// Function to show about overlay
function aboutMenu() {
const aboutOverlay = document.getElementById('about-overlay');
const aboutMenu = document.getElementById('about-menu');
const closeAbout = document.getElementById('close-about');
const showMenu = () => {
aboutOverlay.style.display = 'flex';
setTimeout(() => {
aboutOverlay.style.opacity = '1';
aboutMenu.style.opacity = '1';
}, 10);
document.body.style.overflow = 'hidden';
};
const hideMenu = () => {
aboutOverlay.style.opacity = '0';
aboutMenu.style.opacity = '0';
setTimeout(() => {
aboutOverlay.style.display = 'none';
document.body.style.overflow = 'auto';
}, 200);
};
showMenu();
closeAbout.addEventListener('click', (event) => {
event.stopPropagation();
hideMenu();
});
aboutOverlay.addEventListener('click', (event) => {
if (!aboutMenu.contains(event.target)) {
hideMenu();
}
});
menu.addEventListener('click', (event) => event.stopPropagation());
}
// Fetch and render applist
async function fetchAppList() {
try {
let targetList = [];
try {
const targetFileContent = await execCommand('cat /data/adb/tricky_store/target.txt');
targetList = targetFileContent.split("\n").filter(app => app.trim() !== ''); // Filter out empty lines
console.log("Current target list:", targetList);
} catch (error) {
console.error("Failed to read target.txt file:", error);
}
let applistMap = {};
try {
const applistResult = await execCommand(`cat ${basePath}common/tmp/applist`);
applistMap = applistResult
.split("\n")
.reduce((map, line) => {
const match = line.match(/app-name:\s*(.+),\s*package-name:\s*(.+)/);
if (match) {
const appName = match[1].trim();
const packageName = match[2].trim();
map[packageName] = appName;
}
return map;
}, {});
console.log("Applist loaded successfully.");
} catch (error) {
console.warn("Applist file not found or could not be loaded. Skipping applist lookup.");
}
const result = await execCommand("pm list packages -3");
const appEntries = result
.split("\n")
.map(line => {
const packageName = line.replace("package:", "").trim();
const appName = applistMap[packageName] || null;
return { appName, packageName };
})
.filter(entry => entry.packageName);
for (const entry of appEntries) {
if (!entry.appName) {
try {
const apkPath = await execCommand(`pm path ${entry.packageName} | grep "base.apk" | awk -F: '{print $2}' | tr -d '\\r'`);
if (apkPath) {
const appName = await execCommand(`${basePath}common/aapt dump badging ${apkPath.trim()} 2>/dev/null | grep "application-label:" | sed "s/application-label://; s/'//g"`);
entry.appName = appName.trim() || "Unknown App";
} else {
entry.appName = "Unknown App";
}
} catch (error) {
entry.appName = "Unknown App";
}
}
}
// Sort
const sortedApps = appEntries.sort((a, b) => {
const aChecked = targetList.includes(a.packageName);
const bChecked = targetList.includes(b.packageName);
if (aChecked !== bChecked) {
return aChecked ? -1 : 1;
}
return (a.appName || "").localeCompare(b.appName || "");
});
// Render
appListContainer.innerHTML = "";
sortedApps.forEach(({ appName, packageName }) => {
const appElement = document.importNode(appTemplate, true);
const contentElement = appElement.querySelector(".content");
contentElement.setAttribute("data-package", packageName);
const nameElement = appElement.querySelector(".name");
nameElement.innerHTML = `<strong>${appName || "Unknown App"}</strong><br>${packageName}`;
const checkbox = appElement.querySelector(".checkbox");
checkbox.checked = targetList.includes(packageName);
appListContainer.appendChild(appElement);
});
console.log("App list with names and packages rendered successfully.");
} catch (error) {
console.error("Failed to fetch or render app list with names:", error);
}
floatingBtn.style.transform = "translateY(-120px)";
toggleableCheckbox();
if (appListContainer.firstChild !== updateCard) {
appListContainer.insertBefore(updateCard, appListContainer.firstChild);
}
}
// Make checkboxes toggleable
function toggleableCheckbox() {
const appElements = appListContainer.querySelectorAll(".card");
appElements.forEach(card => {
const content = card.querySelector(".content");
const checkbox = content.querySelector(".checkbox");
content.addEventListener("click", (event) => {
if (event.target !== checkbox) {
checkbox.checked = !checkbox.checked;
}
});
});
}
// Function to show the prompt with a success or error message
function showPrompt(key, isSuccess = true) {
const message = translations[key] || key;
prompt.textContent = message;
prompt.classList.toggle('error', !isSuccess);
if (window.promptTimeout) {
clearTimeout(window.promptTimeout);
}
setTimeout(() => {
prompt.classList.add('visible');
prompt.classList.remove('hidden');
window.promptTimeout = setTimeout(() => {
prompt.classList.remove('visible');
prompt.classList.add('hidden');
}, 3000);
}, 500);
}
// Save configure
document.getElementById("save").addEventListener("click", async () => {
const selectedApps = Array.from(appListContainer.querySelectorAll(".checkbox:checked"))
.map(checkbox => checkbox.closest(".card").querySelector(".content").getAttribute("data-package"));
let finalAppsList = new Set(selectedApps);
ADDITIONAL_APPS.forEach(app => {
finalAppsList.add(app);
});
finalAppsList = Array.from(finalAppsList);
try {
const updatedTargetContent = finalAppsList.join("\n");
await execCommand(`echo "${updatedTargetContent}" > /data/adb/tricky_store/target.txt`);
console.log("target.txt updated successfully.");
showPrompt("saved_target");
} catch (error) {
console.error("Failed to update target.txt:", error);
showPrompt("save_error", false);
}
await refreshAppList();
});
// Uninstall WebUI
document.querySelector(".uninstall-container").addEventListener("click", async () => {
try {
await execCommand(`
su -c "
if [ -f '${basePath}action.sh' ]; then
if [ -d "/data/adb/modules/TA_utl" ]; then
rm -rf "/data/adb/modules/TA_utl"
fi
cp -rf '${basePath}common/update' '/data/adb/modules/TA_utl' &&
touch '/data/adb/modules/TA_utl/remove'
else
touch '${basePath}remove'
fi
"
`);
showPrompt("uninstall_prompt");
} catch (error) {
console.error("Failed to execute uninstall command:", error);
console.log("Error message:", error.message);
showPrompt("uninstall_failed", false);
}
});
// Function to check if running in MMRL
function adjustHeaderForMMRL() {
if (typeof ksu !== 'undefined' && ksu.mmrl) {
console.log("Running in MMRL");
title.style.top = 'var(--window-inset-top)';
const insetTop = getComputedStyle(document.documentElement).getPropertyValue('--window-inset-top');
const insetTopValue = parseInt(insetTop, 10);
searchMenuContainer.style.top = `${insetTopValue + 40}px`;
headerBlock.style.display = 'block';
}
}
// Scroll event
let lastScrollY = window.scrollY;
const scrollThreshold = 40;
window.addEventListener('scroll', () => {
if (isRefreshing) return;
if (window.scrollY > lastScrollY && window.scrollY > scrollThreshold) {
title.style.transform = 'translateY(-80px)';
headerBlock.style.transform = 'translateY(-80px)';
searchMenuContainer.style.transform = 'translateY(-40px)';
floatingBtn.style.transform = 'translateY(0)';
} else if (window.scrollY < lastScrollY) {
headerBlock.style.transform = 'translateY(0)';
title.style.transform = 'translateY(0)';
searchMenuContainer.style.transform = 'translateY(0)';
floatingBtn.style.transform = 'translateY(-120px)';
}
lastScrollY = window.scrollY;
});
// Initial load
document.addEventListener('DOMContentLoaded', async () => {
adjustHeaderForMMRL();
await initializeAvailableLanguages();
const userLang = detectUserLanguage();
await loadTranslations(userLang);
setupMenuToggle();
setupLanguageMenu();
setupHelpOverlay();
document.getElementById("refresh").addEventListener("click", refreshAppList);
document.getElementById("select-all").addEventListener("click", selectAllApps);
document.getElementById("deselect-all").addEventListener("click", deselectAllApps);
document.getElementById("select-denylist").addEventListener("click", selectDenylistApps);
document.getElementById("deselect-unnecessary").addEventListener("click", deselectUnnecessaryApps);
document.getElementById("aospkb").addEventListener("click", aospkb);
document.getElementById("extrakb").addEventListener("click", extrakb);
document.getElementById("boot-hash").addEventListener("click", setBootHash);
document.getElementById("about").addEventListener("click", aboutMenu);
await fetchAppList();
checkMagisk();
loadingIndicator.style.display = "none";
document.querySelector('.uninstall-container').classList.remove('hidden-uninstall');
setTimeout(updateCheck, 0);
});
// Redirect to GitHub release page
updateCard.addEventListener('click', async () => {
try {
await execCommand('am start -a android.intent.action.VIEW -d https://github.com/KOWX712/Tricky-Addon-Update-Target-List/releases/latest');
} catch (error) {
console.error('Error opening GitHub Release link:', error);
}
});
telegramLink.addEventListener('click', async () => {
try {
await execCommand('am start -a android.intent.action.VIEW -d https://t.me/kowchannel');
} catch (error) {
console.error('Error opening Telegram link:', error);
}
});
githubLink.addEventListener('click', async () => {
try {
await execCommand('am start -a android.intent.action.VIEW -d https://github.com/KOWX712/Tricky-Addon-Update-Target-List');
} catch (error) {
console.error('Error opening GitHub link:', error);
}
});
// Function to execute shell commands
async function execCommand(command) {
return new Promise((resolve, reject) => {
const callbackName = `exec_callback_${Date.now()}_${e++}`;
window[callbackName] = (errno, stdout, stderr) => {
delete window[callbackName];
if (errno === 0) {
resolve(stdout);
} else {
console.error(`Error executing command: ${stderr}`);
reject(stderr);
}
};
try {
ksu.exec(command, "{}", callbackName);
} catch (error) {
console.error(`Execution error: ${error}`);
reject(error);
}
});
}

View File

@@ -1,54 +1,86 @@
{
"title": "Tricky Addon - Update Target List",
"search_placeholder": "Search",
"save_and_update_button": "Save",
"boot_hash_save_button": "Save",
"loading": "Loading...",
"boot_hash_input_placeholder": "Paste your verified Boot Hash here",
"uninstall_webui": "Uninstall WebUI",
"update_available": "A new version is ready",
"redirect_to_release": "tap to download the latest version",
"refresh": "Refresh",
"select_all": "Select All",
"deselect_all": "Deselect All",
"select_denylist": "Select From DenyList",
"deselect_unnecessary": "Deselect Unnecessary",
"set_aosp_keybox": "Set AOSP Keybox",
"set_valid_keybox": "Set Valid Keybox",
"set_verified_boot_hash": "Set Verified Boot Hash",
"about": "About",
"help_instructions": "Instructions",
"save_and_update_description": "Save current configure to target.txt.",
"refresh_description": "Refresh app list and exclude list.",
"select_deselect": "Select & Deselect All",
"select_description": "Select or deselect all apps in the current interface.",
"select_denylist_description": "Available in Magisk only, select apps that are in the DenyList. Recommended.",
"deselect_unnecessary_description": "Unnecessary category: Xposed module, root manager, root-related apps, and general apps that never check bootloader status. This option requires an Internet connection.",
"set_keybox": "Set AOSP & Valid Keybox",
"set_aosp_keybox_description": "Replace tricky store keybox.xml. AOSP keybox will be replaced if there's no more valid keybox. Valid keybox option requires Internet connection.",
"set_verified_boot_hash_description": "Get verifiedBootHash value from Key Attestation Demo. Fix abnormal boot state by resetting ro.boot.vbmeta.digest.",
"module_name_line1": "Tricky Addon",
"module_name_line2": "Update Target List",
"by": "by ",
"telegram_channel": "Telegram Channel",
"github": "GitHub",
"disclaimer": "This module is not a part of the Tricky Store module. DO NOT report any issues to Tricky Store if encountered.",
"acknowledgment": "Acknowledgment",
"no_internet": "Please check your Internet connection",
"aosp_key_set": "AOSP keybox set successfully",
"key_set_error": "Failed to update keybox",
"valid_key_set": "Valid keybox set successfully",
"no_valid_fallback": "No valid keybox found, replaced with AOSP keybox.",
"boot_hash_set": "Verified Boot Hash saved successfully",
"boot_hash_set_error": "Failed to update Verified Boot Hash",
"saved_target": "Config saved to target.txt",
"save_error": "Failed to save config",
"uninstall_prompt": "WebUI will be removed after reboot",
"uninstall_failed": "Failed to uninstall WebUI",
"new_update": "A new update is available!"
"language": "English",
"header": {
"title": "Tricky Addon"
},
"help": {
"help_instructions": "Instructions",
"save_and_update": "Save",
"save_and_update_description": "Save current configure to target.txt.",
"refresh": "Refresh",
"refresh_description": "Refresh app list and exclude list.",
"select_deselect": "Select & Deselect All",
"select_description": "Select or deselect all apps in the current interface.",
"select_denylist": "Select From DenyList",
"select_denylist_description": "Available in Magisk only, select apps that are in the DenyList. Recommended.",
"deselect_unnecessary": "Deselect Unnecessary",
"deselect_unnecessary_description": "Unnecessary category: Xposed module, root manager, root-related apps, and general apps that never check bootloader status. This option requires Internet connection.",
"set_keybox": "Set AOSP & Valid Keybox",
"set_keybox_description": "Replace tricky store keybox.xml. AOSP keybox will be replaced if there's no more valid keybox. Valid keybox option requires Internet connection.",
"set_verified_boot_hash": "Set Verified Boot Hash",
"set_verified_boot_hash_description": "Get verifiedBootHash value from Key Attestation Demo. Fix abnormal boot state by resetting ro.boot.vbmeta.digest."
},
"update": {
"update_available": "A new version is ready",
"redirect_to_release": "tap to download the latest version",
"changelog": "Changelog",
"install": "Install",
"reboot": "Reboot"
},
"search_bar": {
"search_placeholder": "Search"
},
"functional_button": {
"save_and_update_button": "Save",
"uninstall_webui": "Uninstall WebUI"
},
"loading": {
"loading": "Loading..."
},
"menu": {
"refresh": "Refresh",
"select_all": "Select All",
"deselect_all": "Deselect All",
"select_denylist": "Select From DenyList",
"deselect_unnecessary": "Deselect Unnecessary",
"set_aosp_keybox": "Set AOSP Keybox",
"set_valid_keybox": "Set Valid Keybox",
"set_verified_boot_hash": "Set Verified Boot Hash",
"about": "About"
},
"reset_vbmeta": {
"boot_hash_input_placeholder": "Paste your verified Boot Hash here",
"boot_hash_save_button": "Save"
},
"about": {
"module_name_line1": "Tricky Addon",
"module_name_line2": "Update Target List",
"by": "by",
"telegram_channel": "Telegram Channel",
"github": "GitHub",
"disclaimer": "This module is not a part of the Tricky Store module. DO NOT report any issues to Tricky Store if encountered.",
"acknowledgment": "Acknowledgment"
},
"prompt": {
"no_internet": "Please check your Internet connection",
"aosp_key_set": "AOSP keybox set successfully",
"key_set_error": "Failed to update keybox",
"valid_key_set": "Valid keybox set successfully",
"no_valid_fallback": "No valid keybox found, replaced with AOSP keybox.",
"boot_hash_set": "Verified Boot Hash saved successfully",
"boot_hash_set_error": "Failed to update Verified Boot Hash",
"saved_target": "Config saved to target.txt",
"save_error": "Failed to save config",
"uninstall_prompt": "WebUI will be removed after reboot",
"uninstall_failed": "Failed to uninstall WebUI",
"new_update": "A new update is available!",
"downloading": "Downloading new update...",
"downloaded": "Download completed",
"download_fail": "Fail to download update",
"installing": "Installing update...",
"installed": "Installed successfully, reboot now.",
"install_fail": "Fail to install, please update manual",
"rebooting": "Rebooting...",
"reboot_fail": "Fail to reboot, please reboot manually"
}
}

View File

@@ -15,17 +15,4 @@
2. Make a copy of `/module/webroot/locales/A-template.json`
3. Rename it to `language_code-COUNTRY_CODE.json`, e.g., `en-US.json`.
4. Translate the string value inside.
5. Add `langauge-option` into `/module/webroot/index.html`.
Format:
```xml
<button class="language-option" data-lang="language_code-COUNTRY_CODE">languageName</button>
```
Example:
```xml
<div class="language-menu">
<button class="language-option" data-lang="en-US">English</button>
</div>
```
Finally, Create a Pull Request.
6. Create a Pull Request.

View File

@@ -1,54 +1,86 @@
{
"title": "Tricky Addon - Update Target List",
"search_placeholder": "Search",
"save_and_update_button": "Save",
"boot_hash_save_button": "Save",
"loading": "Loading...",
"boot_hash_input_placeholder": "Paste your verified Boot Hash here",
"uninstall_webui": "Uninstall WebUI",
"update_available": "A new version is ready",
"redirect_to_release": "tap to download the latest version",
"refresh": "Refresh",
"select_all": "Select All",
"deselect_all": "Deselect All",
"select_denylist": "Select From DenyList",
"deselect_unnecessary": "Deselect Unnecessary",
"set_aosp_keybox": "Set AOSP Keybox",
"set_valid_keybox": "Set Valid Keybox",
"set_verified_boot_hash": "Set Verified Boot Hash",
"about": "About",
"help_instructions": "Instructions",
"save_and_update_description": "Save current configure to target.txt.",
"refresh_description": "Refresh app list and exclude list.",
"select_deselect": "Select & Deselect All",
"select_description": "Select or deselect all apps in the current interface.",
"select_denylist_description": "Available in Magisk only, select apps that are in the DenyList. Recommended.",
"deselect_unnecessary_description": "Unnecessary category: Xposed module, root manager, root-related apps, and general apps that never check bootloader status. This option requires an Internet connection.",
"set_keybox": "Set AOSP & Valid Keybox",
"set_aosp_keybox_description": "Replace tricky store keybox.xml. AOSP keybox will be replaced if there's no more valid keybox. Valid keybox option requires Internet connection.",
"set_verified_boot_hash_description": "Get verifiedBootHash value from Key Attestation Demo. Fix abnormal boot state by resetting ro.boot.vbmeta.digest.",
"module_name_line1": "Tricky Addon",
"module_name_line2": "Update Target List",
"by": "by ",
"telegram_channel": "Telegram Channel",
"github": "GitHub",
"disclaimer": "This module is not a part of the Tricky Store module. DO NOT report any issues to Tricky Store if encountered.",
"acknowledgment": "Acknowledgment",
"no_internet": "Please check your Internet connection",
"aosp_key_set": "AOSP keybox set successfully",
"key_set_error": "Failed to update keybox",
"valid_key_set": "Valid keybox set successfully",
"no_valid_fallback": "No valid keybox found, replaced with AOSP keybox.",
"boot_hash_set": "Verified Boot Hash saved successfully",
"boot_hash_set_error": "Failed to update Verified Boot Hash",
"saved_target": "Config saved to target.txt",
"save_error": "Failed to save config",
"uninstall_prompt": "WebUI will be removed after reboot",
"uninstall_failed": "Failed to uninstall WebUI",
"new_update": "A new update is available!"
"language": "English",
"header": {
"title": "Tricky Addon"
},
"help": {
"help_instructions": "Instructions",
"save_and_update": "Save",
"save_and_update_description": "Save current configure to target.txt.",
"refresh": "Refresh",
"refresh_description": "Refresh app list and exclude list.",
"select_deselect": "Select & Deselect All",
"select_description": "Select or deselect all apps in the current interface.",
"select_denylist": "Select From DenyList",
"select_denylist_description": "Available in Magisk only, select apps that are in the DenyList. Recommended.",
"deselect_unnecessary": "Deselect Unnecessary",
"deselect_unnecessary_description": "Unnecessary category: Xposed module, root manager, root-related apps, and general apps that never check bootloader status. This option requires Internet connection.",
"set_keybox": "Set AOSP & Valid Keybox",
"set_keybox_description": "Replace tricky store keybox.xml. AOSP keybox will be replaced if there's no more valid keybox. Valid keybox option requires Internet connection.",
"set_verified_boot_hash": "Set Verified Boot Hash",
"set_verified_boot_hash_description": "Get verifiedBootHash value from Key Attestation Demo. Fix abnormal boot state by resetting ro.boot.vbmeta.digest."
},
"update": {
"update_available": "A new version is ready",
"redirect_to_release": "tap to download the latest version",
"changelog": "Changelog",
"install": "Install",
"reboot": "Reboot"
},
"search_bar": {
"search_placeholder": "Search"
},
"functional_button": {
"save_and_update_button": "Save",
"uninstall_webui": "Uninstall WebUI"
},
"loading": {
"loading": "Loading..."
},
"menu": {
"refresh": "Refresh",
"select_all": "Select All",
"deselect_all": "Deselect All",
"select_denylist": "Select From DenyList",
"deselect_unnecessary": "Deselect Unnecessary",
"set_aosp_keybox": "Set AOSP Keybox",
"set_valid_keybox": "Set Valid Keybox",
"set_verified_boot_hash": "Set Verified Boot Hash",
"about": "About"
},
"reset_vbmeta": {
"boot_hash_input_placeholder": "Paste your verified Boot Hash here",
"boot_hash_save_button": "Save"
},
"about": {
"module_name_line1": "Tricky Addon",
"module_name_line2": "Update Target List",
"by": "by",
"telegram_channel": "Telegram Channel",
"github": "GitHub",
"disclaimer": "This module is not a part of the Tricky Store module. DO NOT report any issues to Tricky Store if encountered.",
"acknowledgment": "Acknowledgment"
},
"prompt": {
"no_internet": "Please check your Internet connection",
"aosp_key_set": "AOSP keybox set successfully",
"key_set_error": "Failed to update keybox",
"valid_key_set": "Valid keybox set successfully",
"no_valid_fallback": "No valid keybox found, replaced with AOSP keybox.",
"boot_hash_set": "Verified Boot Hash saved successfully",
"boot_hash_set_error": "Failed to update Verified Boot Hash",
"saved_target": "Config saved to target.txt",
"save_error": "Failed to save config",
"uninstall_prompt": "WebUI will be removed after reboot",
"uninstall_failed": "Failed to uninstall WebUI",
"new_update": "A new update is available!",
"downloading": "Downloading new update...",
"downloaded": "Download completed",
"download_fail": "Fail to download update",
"installing": "Installing update...",
"installed": "Installed successfully, reboot now.",
"install_fail": "Fail to install, please update manual",
"rebooting": "Rebooting...",
"reboot_fail": "Fail to reboot, please reboot manually"
}
}

View File

@@ -0,0 +1,86 @@
{
"language": "Español",
"header": {
"title": "Tricky Addon"
},
"help": {
"help_instructions": "Instrucciones",
"save_and_update": "Guardar",
"save_and_update_description": "Guardar la configuración actual en target.txt.",
"refresh": "Actualizar",
"refresh_description": "Actualizar lista de aplicaciones y lista de exclusión.",
"select_deselect": "Seleccionar y Deseleccionar Todo",
"select_description": "Seleccionar o deseleccionar todas las aplicaciones en la interfaz actual.",
"select_denylist": "Seleccionar desde DenyList",
"select_denylist_description": "Disponible solo en Magisk, selecciona aplicaciones que están en la DenyList. Recomendado.",
"deselect_unnecessary": "Deseleccionar innecesarios",
"deselect_unnecessary_description": "Categorías innecesarias: módulos Xposed, gestores de root, aplicaciones relacionadas con root y aplicaciones generales que nunca verifican el estado del bootloader. Esta opción requiere conexión a Internet.",
"set_keybox": "Configurar AOSP y Keybox Válido",
"set_keybox_description": "Reemplazar el archivo keybox.xml de Tricky Store. El AOSP Keybox será reemplazado si no hay un keybox válido. Esta opción requiere conexión a Internet.",
"set_verified_boot_hash": "Configurar Boot Hash Verificado",
"set_verified_boot_hash_description": "Obtén el valor de verifiedBootHash del Key Attestation Demo. Corrige un estado de arranque anormal reiniciando ro.boot.vbmeta.digest."
},
"update": {
"update_available": "Una nueva versión está lista",
"redirect_to_release": "toca para descargar la última versión",
"changelog": "Registro de cambios",
"install": "Instalar",
"reboot": "Reiniciar"
},
"search_bar": {
"search_placeholder": "Buscar"
},
"functional_button": {
"save_and_update_button": "Guardar",
"uninstall_webui": "Desinstalar WebUI"
},
"loading": {
"loading": "Cargando..."
},
"menu": {
"refresh": "Actualizar",
"select_all": "Seleccionar Todo",
"deselect_all": "Deseleccionar Todo",
"select_denylist": "Seleccionar desde DenyList",
"deselect_unnecessary": "Deseleccionar innecesarios",
"set_aosp_keybox": "Configurar AOSP Keybox",
"set_valid_keybox": "Configurar Keybox Válido",
"set_verified_boot_hash": "Configurar Boot Hash Verificado",
"about": "Acerca de"
},
"reset_vbmeta": {
"boot_hash_input_placeholder": "Pega aquí tu Boot Hash verificado",
"boot_hash_save_button": "Guardar"
},
"about": {
"module_name_line1": "Tricky Addon",
"module_name_line2": "Update Target List",
"by": "por",
"telegram_channel": "Canal de Telegram",
"github": "GitHub",
"disclaimer": "Este módulo no es parte del módulo Tricky Store. NO reportes problemas al autor de Tricky Store si los encuentras.",
"acknowledgment": "Agradecimientos"
},
"prompt": {
"no_internet": "Por favor, verifica tu conexión a Internet",
"aosp_key_set": "AOSP Keybox configurado correctamente",
"key_set_error": "Error al actualizar el Keybox",
"valid_key_set": "Keybox válido configurado correctamente",
"no_valid_fallback": "No se encontró un keybox válido, reemplazado con AOSP Keybox.",
"boot_hash_set": "Boot Hash verificado guardado correctamente",
"boot_hash_set_error": "Error al actualizar el Boot Hash verificado",
"saved_target": "Configuración guardada en target.txt",
"save_error": "Error al guardar la configuración",
"uninstall_prompt": "El WebUI se eliminará después de reiniciar",
"uninstall_failed": "Error al desinstalar el WebUI",
"new_update": "¡Una nueva actualización está disponible!",
"downloading": "Descargando nueva actualización...",
"downloaded": "Descarga completada",
"download_fail": "Error al descargar la actualización",
"installing": "Instalando actualización...",
"installed": "Instalado con éxito, reinicia ahora.",
"install_fail": "Error al instalar, actualiza manualmente",
"rebooting": "Reiniciando...",
"reboot_fail": "Error al reiniciar, reinicia manualmente"
}
}

View File

@@ -1,54 +1,86 @@
{
"title": "Tricky Addon - Update Target List",
"search_placeholder": "検索",
"save_and_update_button": "保存",
"boot_hash_save_button": "保存",
"loading": "読み込み中...",
"boot_hash_input_placeholder": "確認付きブートハッシュをここに貼り付け",
"uninstall_webui": "WebUI をアンインストール",
"update_available": "新しいバージョンの準備完了",
"redirect_to_release": "タップで最新のバージョンをダウンロード",
"refresh": "更新",
"select_all": "すべて選択",
"deselect_all": "すべての選択を解除",
"select_denylist": "DenyList から選択",
"deselect_unnecessary": "不要な選択を解除",
"set_aosp_keybox": "AOSP Keybox を設定",
"set_valid_keybox": "有効な Keybox を設定",
"set_verified_boot_hash": "確認付きブートハッシュを設定",
"about": "このアドオンについて",
"help_instructions": "使い方",
"save_and_update_description": "現在の設定を target.txt に保存します。",
"refresh_description": "アプリリストと除外リストを更新します。",
"select_deselect": "すべてを選択と解除",
"select_description": "現在のインターフェースのすべてのアプリを選択または解除します。",
"select_denylist_description": "Magisk の環境でのみ使用可能です。Deny List 内のアプリを選択します(推奨)。",
"deselect_unnecessary_description": "不要なカテゴリー: Xposed モジュール、root マネージャー、root 関連アプリ、Bootloader の状態を確認しない一般的なアプリです。このオプションはインターネット接続が必要です。",
"set_keybox": "AOSP と 有効な Keybox",
"set_aosp_keybox_description": "Tricky Store の keybox.xml を置き換えます。有効な Keybox がなくなった場合は、AOSP Keybox に置き換えられます。インターネット接続が必要です。",
"set_verified_boot_hash_description": "Key Attestation Demo から確認付きブートハッシュの値を取得します。ro.boot.vbmeta.digest をリセットして異常なブート状態を修正します。",
"module_name_line1": "Tricky Addon",
"module_name_line2": "Update Target List",
"by": "開発者: ",
"telegram_channel": "Telegram チャンネル",
"github": "GitHub",
"disclaimer": "このモジュールは、Tricky Store モジュールの一部ではありません。Tricky Store 公式に問題を報告しないでください。",
"acknowledgment": "謝辞",
"no_internet": "インターネット接続を確認してください。",
"aosp_key_set": "AOSP Keybox の設定に成功しました。",
"key_set_error": "Keybox の更新に失敗しました。",
"valid_key_set": "有効な Keybox の設定に成功しました。",
"no_valid_fallback": "有効な Keybox がありません。AOSP Keybox に置き換えます。",
"boot_hash_set": "確認付きブートハッシュの更新に成功しました。",
"boot_hash_set_error": "確認付きブートハッシュの更新に失敗しました。",
"saved_target": "設定を target.txt に保存しました。",
"save_error": "設定の保存に失敗しました。",
"uninstall_prompt": "WebUI は再起動後に削除されます。",
"uninstall_failed": "WebUI のアンインストールに失敗しました。",
"new_update": "新しいバージョンがあります!"
}
"language": "日本語",
"header": {
"title": "Tricky Addon"
},
"help": {
"help_instructions": "使い方",
"save_and_update": "保存",
"save_and_update_description": "現在の設定を target.txt に保存します。",
"refresh": "更新",
"refresh_description": "アプリリストと除外リストを更新します。",
"select_deselect": "すべてを選択と解除",
"select_description": "現在のインターフェースのすべてのアプリを選択または解除します。",
"select_denylist": "DenyList から選択",
"select_denylist_description": "Magisk の環境でのみ使用可能です。Deny List 内のアプリを選択します(推奨)。",
"deselect_unnecessary": "不要な選択を解除",
"deselect_unnecessary_description": "不要なカテゴリー: Xposed モジュール、root マネージャー、root 関連アプリ、Bootloader の状態を確認しない一般的なアプリです。このオプションはインターネット接続が必要です。",
"set_keybox": "AOSP と 有効な Keybox",
"set_keybox_description": "Tricky Store の keybox.xml を置き換えます。有効な Keybox がなくなった場合は、AOSP Keybox に置き換えられます。インターネット接続が必要です。",
"set_verified_boot_hash": "確認付きブートハッシュを設定",
"set_verified_boot_hash_description": "Key Attestation Demo から確認付きブートハッシュの値を取得します。ro.boot.vbmeta.digest をリセットして異常なブート状態を修正します。"
},
"update": {
"update_available": "新しいバージョンの準備完了",
"redirect_to_release": "タップで最新のバージョンをダウンロード",
"changelog": "変更履歴",
"install": "インストール",
"reboot": "再起動"
},
"search_bar": {
"search_placeholder": "検索"
},
"functional_button": {
"save_and_update_button": "保存",
"uninstall_webui": "WebUI をアンインストール"
},
"loading": {
"loading": "読み込み中..."
},
"menu": {
"refresh": "更新",
"select_all": "すべて選択",
"deselect_all": "すべての選択を解除",
"select_denylist": "DenyList から選択",
"deselect_unnecessary": "不要な選択を解除",
"set_aosp_keybox": "AOSP Keybox を設定",
"set_valid_keybox": "有効な Keybox を設定",
"set_verified_boot_hash": "確認付きブートハッシュを設定",
"about": "このアドオンについて"
},
"reset_vbmeta": {
"boot_hash_input_placeholder": "確認付きブートハッシュをここに貼り付け",
"boot_hash_save_button": "保存"
},
"about": {
"module_name_line1": "Tricky Addon",
"module_name_line2": "ターゲットリストを更新",
"by": "開発者: ",
"telegram_channel": "Telegram チャンネル",
"github": "GitHub",
"disclaimer": "このモジュールは、Tricky Store モジュールの一部ではありません。Tricky Store 公式に問題を報告しないでください。",
"acknowledgment": "謝辞"
},
"prompt": {
"no_internet": "インターネット接続を確認してください。",
"aosp_key_set": "AOSP Keybox の設定に成功しました。",
"key_set_error": "Keybox の更新に失敗しました。",
"valid_key_set": "有効な Keybox の設定に成功しました。",
"no_valid_fallback": "有効な Keybox がありません。AOSP Keybox に置き換えます。",
"boot_hash_set": "確認付きブートハッシュの更新に成功しました。",
"boot_hash_set_error": "確認付きブートハッシュの更新に失敗しました。",
"saved_target": "設定を target.txt に保存しました。",
"save_error": "設定の保存に失敗しました。",
"uninstall_prompt": "WebUI は再起動後に削除されます。",
"uninstall_failed": "WebUI のアンインストールに失敗しました。",
"new_update": "新しいバージョンがあります!",
"downloading": "新しい更新をダウンロード中...",
"downloaded": "ダウンロードが完了しました",
"download_fail": "更新のダウンロードに失敗しました",
"installing": "更新をインストール中...",
"installed": "正常にインストールされました。再起動してください。",
"install_fail": "インストールに失敗しました。手動で更新してください。",
"rebooting": "再起動中...",
"reboot_fail": "再起動に失敗しました。手動で再起動してください。"
}
}

View File

@@ -1,54 +1,86 @@
{
"title": "Tricky Addon - Обновить список целей",
"search_placeholder": "Поиск",
"save_and_update_button": "Сохранить",
"boot_hash_save_button": "Сохранить",
"loading": "Загрузка...",
"boot_hash_input_placeholder": "Вставьте свой проверенный Boot Hash сюда",
"uninstall_webui": "Удалить WebUI",
"update_available": "Доступна новая версия",
"redirect_to_release": "нажмите, чтобы скачать последнюю версию",
"refresh": "Обновить",
"select_all": "Выбрать все",
"deselect_all": "Отменить выбор всех",
"select_denylist": "Выбрать из DenyList",
"deselect_unnecessary": "Отменить выбор ненужных",
"set_aosp_keybox": "Установить AOSP Keybox",
"set_valid_keybox": "Установить действующий Keybox",
"set_verified_boot_hash": "Установить Verified Boot Hash",
"about": "О программе",
"help_instructions": "Инструкции",
"save_and_update_description": "Сохранить текущую конфигурацию в target.txt.",
"refresh_description": "Обновить список приложений и список исключений.",
"select_deselect": "Выбрать и отменить выбор всех",
"select_description": "Выбрать или отменить выбор всех приложений в текущем интерфейсе.",
"select_denylist_description": "Доступно только в Magisk, выберите приложения, которые находятся в DenyList. Рекомендуется.",
"deselect_unnecessary_description": "Ненужные категории: модули Xposed, менеджеры root, приложения, связанные с root, и общие приложения, которые никогда не проверяют статус загрузчика. Этот параметр требует подключения к интернету.",
"set_keybox": "Установить AOSP и действующий Keybox",
"set_aosp_keybox_description": "Замените tricky store keybox.xml. AOSP keybox будет заменен, если не будет найден действующий keybox. Опция с действующим keybox требует подключения к интернету.",
"set_verified_boot_hash_description": "Получите значение verifiedBootHash из Key Attestation Demo. Исправьте аномальное состояние загрузки, сбросив ro.boot.vbmeta.digest.",
"module_name_line1": "Tricky Addon",
"module_name_line2": "Обновить список целей",
"by": "от ",
"telegram_channel": "Канал в Telegram",
"github": "GitHub",
"disclaimer": "Этот WebUI не является частью Tricky Store, НЕ сообщайте автору Tricky Store о любых возникающих проблемах.",
"acknowledgment": "Благодарности",
"no_internet": "Пожалуйста, проверьте ваше подключение к интернету",
"aosp_key_set": "AOSP keybox успешно установлен",
"key_set_error": "Не удалось обновить keybox",
"valid_key_set": "Действующий keybox успешно установлен",
"no_valid_fallback": "Не найден действующий keybox, заменен на AOSP keybox.",
"boot_hash_set": "Verified Boot Hash успешно сохранен",
"boot_hash_set_error": "Не удалось обновить Verified Boot Hash",
"saved_target": "Конфигурация сохранена в target.txt",
"save_error": "Не удалось сохранить конфигурацию",
"uninstall_prompt": "WebUI будет удален после перезагрузки",
"uninstall_failed": "Не удалось удалить WebUI",
"new_update": "Доступно новое обновление!"
"language": "Русский",
"header": {
"title": "Tricky Addon"
},
"help": {
"help_instructions": "Инструкции",
"save_and_update": "Сохранить",
"save_and_update_description": "Сохранить текущую конфигурацию в target.txt.",
"refresh": "Обновить",
"refresh_description": "Обновить список приложений и список исключений.",
"select_deselect": "Выбрать и отменить выбор всех",
"select_description": "Выбрать или отменить выбор всех приложений в текущем интерфейсе.",
"select_denylist": "Выбрать из DenyList",
"select_denylist_description": "Доступно только в Magisk, выберите приложения, которые находятся в DenyList. Рекомендуется.",
"deselect_unnecessary": "Отменить выбор ненужных",
"deselect_unnecessary_description": "Ненужные категории: модули Xposed, менеджеры root, приложения, связанные с root, и общие приложения, которые никогда не проверяют статус загрузчика. Этот параметр требует подключения к интернету.",
"set_keybox": "Установить AOSP и действующий Keybox",
"set_keybox_description": "Замените tricky store keybox.xml. AOSP keybox будет заменен, если не будет найден действующий keybox. Опция с действующим keybox требует подключения к интернету.",
"set_verified_boot_hash": "Установить Verified Boot Hash",
"set_verified_boot_hash_description": "Получите значение verifiedBootHash из Key Attestation Demo. Исправьте аномальное состояние загрузки, сбросив ro.boot.vbmeta.digest."
},
"update": {
"update_available": "Доступна новая версия",
"redirect_to_release": "нажмите, чтобы скачать последнюю версию",
"changelog": "Список изменений",
"install": "Установить",
"reboot": "Перезагрузить"
},
"search_bar": {
"search_placeholder": "Поиск"
},
"functional_button": {
"save_and_update_button": "Сохранить",
"uninstall_webui": "Удалить WebUI"
},
"loading": {
"loading": "Загрузка..."
},
"menu": {
"refresh": "Обновить",
"select_all": "Выбрать все",
"deselect_all": "Отменить выбор всех",
"select_denylist": "Выбрать из DenyList",
"deselect_unnecessary": "Отменить выбор ненужных",
"set_aosp_keybox": "Установить AOSP Keybox",
"set_valid_keybox": "Установить действующий Keybox",
"set_verified_boot_hash": "Установить Verified Boot Hash",
"about": "О программе"
},
"reset_vbmeta": {
"boot_hash_input_placeholder": "Вставьте свой проверенный Boot Hash сюда",
"boot_hash_save_button": "Сохранить"
},
"about": {
"module_name_line1": "Tricky Addon",
"module_name_line2": "Обновить список целей",
"by": "от",
"telegram_channel": "Канал в Telegram",
"github": "GitHub",
"disclaimer": "Этот WebUI не является частью Tricky Store, НЕ сообщайте автору Tricky Store о любых возникающих проблемах.",
"acknowledgment": "Благодарности"
},
"prompt": {
"no_internet": "Пожалуйста, проверьте ваше подключение к интернету",
"aosp_key_set": "AOSP keybox успешно установлен",
"key_set_error": "Не удалось обновить keybox",
"valid_key_set": "Действующий keybox успешно установлен",
"no_valid_fallback": "Не найден действующий keybox, заменен на AOSP keybox.",
"boot_hash_set": "Verified Boot Hash успешно сохранен",
"boot_hash_set_error": "Не удалось обновить Verified Boot Hash",
"saved_target": "Конфигурация сохранена в target.txt",
"save_error": "Не удалось сохранить конфигурацию",
"uninstall_prompt": "WebUI будет удален после перезагрузки",
"uninstall_failed": "Не удалось удалить WebUI",
"new_update": "Доступно новое обновление!",
"downloading": "Загрузка нового обновления...",
"downloaded": "Загрузка завершена",
"download_fail": "Не удалось загрузить обновление",
"installing": "Установка обновления...",
"installed": "Успешно установлено, перезагрузите устройство.",
"install_fail": "Не удалось установить, обновите вручную",
"rebooting": "Перезагрузка...",
"reboot_fail": "Не удалось перезагрузить, перезагрузите вручную"
}
}

View File

@@ -1,54 +1,86 @@
{
"title": "Tricky Addon - I-update ang Target List",
"search_placeholder": "Maghanap",
"save_and_update_button": "I-save",
"boot_hash_save_button": "I-save",
"loading": "Naglo-load...",
"boot_hash_input_placeholder": "I-paste ang iyong verified Boot Hash dito",
"uninstall_webui": "I-uninstall ang WebUI",
"update_available": "Handa na ang bagong bersyon",
"redirect_to_release": "i-tap para i-download ang pinakabagong bersyon",
"refresh": "I-refresh",
"select_all": "Piliin Lahat",
"deselect_all": "Huwag Pumili ng Lahat",
"select_denylist": "Piliin mula sa DenyList",
"deselect_unnecessary": "Huwag Pumili ng Hindi Kinakailangan",
"set_aosp_keybox": "I-set ang AOSP Keybox",
"set_valid_keybox": "I-set ang Valid Keybox",
"set_verified_boot_hash": "I-set ang Verified Boot Hash",
"about": "Tungkol",
"help_instructions": "Mga Tagubilin",
"save_and_update_description": "I-save ang kasalukuyang configuration sa target.txt.",
"refresh_description": "I-refresh ang listahan ng apps at exclude list.",
"select_deselect": "Piliin & Huwag Pumili ng Lahat",
"select_description": "Piliin o huwag piliin ang lahat ng apps sa kasalukuyang interface.",
"select_denylist_description": "Available lang sa Magisk, piliin ang mga app na nasa DenyList. Inirerekomenda.",
"deselect_unnecessary_description": "Hindi kinakailangang kategorya: Xposed module, root manager, root-related apps, at mga karaniwang apps na hindi kailanman nire-refresh ang bootloader status. Nangangailangan ng koneksyon sa internet.",
"set_keybox": "I-set ang AOSP at Valid Keybox",
"set_aosp_keybox_description": "Palitan ang tricky store keybox.xml. Palitan ang AOSP keybox kung walang valid keybox. Nangangailangan ng koneksyon sa internet ang valid keybox option.",
"set_verified_boot_hash_description": "Kunin ang verifiedBootHash mula sa Key Attestation Demo. Ayusin ang abnormal na boot state sa pamamagitan ng pag-reset ng ro.boot.vbmeta.digest.",
"module_name_line1": "Tricky Addon",
"module_name_line2": "I-update ang Target List",
"by": "ni ",
"telegram_channel": "Telegram Channel",
"github": "GitHub",
"disclaimer": "Ang WebUI na ito ay hindi bahagi ng Tricky Store, HUWAG i-report sa may-akda ng Tricky Store kung makaranas ka ng anumang isyu.",
"acknowledgment": "Pagkilala",
"no_internet": "Pakitingnan ang iyong koneksyon sa Internet",
"aosp_key_set": "Matagumpay na na-set ang AOSP Keybox",
"key_set_error": "Nabigong i-update ang keybox",
"valid_key_set": "Matagumpay na na-set ang Valid Keybox",
"no_valid_fallback": "Walang valid na keybox na natagpuan, pinalitan ng AOSP keybox.",
"boot_hash_set": "Matagumpay na na-save ang Verified Boot Hash",
"boot_hash_set_error": "Nabigong i-update ang Verified Boot Hash",
"saved_target": "Na-save ang configuration sa target.txt",
"save_error": "Nabigong i-save ang config",
"uninstall_prompt": "Mawawala ang WebUI pagkatapos ng reboot",
"uninstall_failed": "Nabigong i-uninstall ang WebUI",
"new_update": "May bagong update na available!"
"language": "Tagalog",
"header": {
"title": "Tricky Addon"
},
"help": {
"help_instructions": "Mga Tagubilin",
"save_and_update": "I-save",
"save_and_update_description": "I-save ang kasalukuyang configuration sa target.txt.",
"refresh": "I-refresh",
"refresh_description": "I-refresh ang listahan ng apps at exclude list.",
"select_deselect": "Piliin & Huwag Pumili ng Lahat",
"select_description": "Piliin o huwag piliin ang lahat ng apps sa kasalukuyang interface.",
"select_denylist": "Piliin mula sa DenyList",
"select_denylist_description": "Available lang sa Magisk, piliin ang mga app na nasa DenyList. Inirerekomenda.",
"deselect_unnecessary": "Huwag Pumili ng Hindi Kinakailangan",
"deselect_unnecessary_description": "Hindi kinakailangang kategorya: Xposed module, root manager, root-related apps, at mga karaniwang apps na hindi kailanman nire-refresh ang bootloader status. Nangangailangan ng koneksyon sa internet.",
"set_keybox": "I-set ang AOSP at Valid Keybox",
"set_keybox_description": "Palitan ang tricky store keybox.xml. Palitan ang AOSP keybox kung walang valid keybox. Nangangailangan ng koneksyon sa internet ang valid keybox option.",
"set_verified_boot_hash": "I-set ang Verified Boot Hash",
"set_verified_boot_hash_description": "Kunin ang verifiedBootHash mula sa Key Attestation Demo. Ayusin ang abnormal na boot state sa pamamagitan ng pag-reset ng ro.boot.vbmeta.digest."
},
"update": {
"update_available": "Handa na ang bagong bersyon",
"redirect_to_release": "i-tap para i-download ang pinakabagong bersyon",
"changelog": "Mga Pagbabago",
"install": "I-install",
"reboot": "I-reboot"
},
"search_bar": {
"search_placeholder": "Maghanap"
},
"functional_button": {
"save_and_update_button": "I-save",
"uninstall_webui": "I-uninstall ang WebUI"
},
"loading": {
"loading": "Naglo-load..."
},
"menu": {
"refresh": "I-refresh",
"select_all": "Piliin Lahat",
"deselect_all": "Huwag Pumili ng Lahat",
"select_denylist": "Piliin mula sa DenyList",
"deselect_unnecessary": "Huwag Pumili ng Hindi Kinakailangan",
"set_aosp_keybox": "I-set ang AOSP Keybox",
"set_valid_keybox": "I-set ang Valid Keybox",
"set_verified_boot_hash": "I-set ang Verified Boot Hash",
"about": "Tungkol"
},
"reset_vbmeta": {
"boot_hash_input_placeholder": "I-paste ang iyong verified Boot Hash dito",
"boot_hash_save_button": "I-save"
},
"about": {
"module_name_line1": "Tricky Addon",
"module_name_line2": "I-update ang Target List",
"by": "ni",
"telegram_channel": "Telegram Channel",
"github": "GitHub",
"disclaimer": "Ang WebUI na ito ay hindi bahagi ng Tricky Store, HUWAG i-report sa may-akda ng Tricky Store kung makaranas ka ng anumang isyu.",
"acknowledgment": "Pagkilala"
},
"prompt": {
"no_internet": "Pakitingnan ang iyong koneksyon sa Internet",
"aosp_key_set": "Matagumpay na na-set ang AOSP Keybox",
"key_set_error": "Nabigong i-update ang keybox",
"valid_key_set": "Matagumpay na na-set ang Valid Keybox",
"no_valid_fallback": "Walang valid na keybox na natagpuan, pinalitan ng AOSP keybox.",
"boot_hash_set": "Matagumpay na na-save ang Verified Boot Hash",
"boot_hash_set_error": "Nabigong i-update ang Verified Boot Hash",
"saved_target": "Na-save ang configuration sa target.txt",
"save_error": "Nabigong i-save ang config",
"uninstall_prompt": "Mawawala ang WebUI pagkatapos ng reboot",
"uninstall_failed": "Nabigong i-uninstall ang WebUI",
"new_update": "May bagong update na available!",
"downloading": "Nagda-download ng bagong update...",
"downloaded": "Natapos ang pag-download",
"download_fail": "Nabigo ang pag-download ng update",
"installing": "Nag-i-install ng update...",
"installed": "Matagumpay na na-install, mag-reboot na ngayon.",
"install_fail": "Nabigo ang pag-install, pakisubukang mag-update nang manu-mano",
"rebooting": "Nag-re-reboot...",
"reboot_fail": "Nabigo ang pag-reboot, pakisubukang mag-reboot nang manu-mano"
}
}

View File

@@ -0,0 +1,86 @@
{
"language": "Türkçe",
"header": {
"title": "Tricky Addon"
},
"help": {
"help_instructions": "Talimatlar",
"save_and_update": "Kaydet",
"save_and_update_description": "Mevcut yapılandırmayı target.txt dosyasına kaydet.",
"refresh": "Yenile",
"refresh_description": "Uygulama ve hariç tutma listesini yenile.",
"select_deselect": "Tümünü Seç & Seçimi Kaldır",
"select_description": "Mevcut arayüzdeki tüm uygulamaları seç veya seçimini kaldır.",
"select_denylist": "Reddetme Listesinden Seç",
"select_denylist_description": "Yalnızca Magiskte mevcut, Reddetme Listesindeki uygulamaları seç. Tavsiye edilir.",
"deselect_unnecessary": "Gereksizleri Seçme",
"deselect_unnecessary_description": "Gereksiz kategori: Xposed modülü, root yöneticisi, root ile ilgili uygulamalar ve asla bootloader durumunu kontrol etmeyen genel uygulamalar. Bu seçenek internet bağlantısı gerektirir.",
"set_keybox": "AOSP & Geçerli Keybox Ayarla",
"set_keybox_description": "Tricky Store'daki keybox.xml dosyasını değiştirir. Eğer geçerli bir keybox yoksa AOSP keybox ile değiştirilecektir. Geçerli keybox seçeneği internet bağlantısı gerektirir.",
"set_verified_boot_hash": "Doğrulanmış Boot Hash Ayarla",
"set_verified_boot_hash_description": "Key Attestation Demodan verifiedBootHash değerini alın. Abnormal boot durumunu ro.boot.vbmeta.digesti sıfırlayarak düzeltin."
},
"update": {
"update_available": "Yeni bir sürüm hazır",
"redirect_to_release": "Son sürümü indirmek için dokunun",
"changelog": "Değişiklik Günlüğü",
"install": "Yükle",
"reboot": "Yeniden Başlat"
},
"search_bar": {
"search_placeholder": "Ara"
},
"functional_button": {
"save_and_update_button": "Kaydet",
"uninstall_webui": "WebUIı Kaldır"
},
"loading": {
"loading": "Yükleniyor..."
},
"menu": {
"refresh": "Yenile",
"select_all": "Tümünü Seç",
"deselect_all": "Tüm Seçimleri Kaldır",
"select_denylist": "Reddetme Listesinden Seç",
"deselect_unnecessary": "Gereksizleri Seçme",
"set_aosp_keybox": "AOSP Keybox Ayarla",
"set_valid_keybox": "Geçerli Keybox Ayarla",
"set_verified_boot_hash": "Doğrulanmış Boot Hash Ayarla",
"about": "Hakkında"
},
"reset_vbmeta": {
"boot_hash_input_placeholder": "Doğrulanmış Boot Hashinizi buraya yapıştırın",
"boot_hash_save_button": "Kaydet"
},
"about": {
"module_name_line1": "Tricky Addon",
"module_name_line2": "Target List'i Güncelle",
"by": "tarafından",
"telegram_channel": "Telegram Kanalı",
"github": "GitHub",
"disclaimer": "Bu modül, Tricky Store modülünün bir parçası değildir. Herhangi bir sorun yaşarsanız, lütfen bunu Tricky Storea rapor etmeyin.",
"acknowledgment": "Teşekkür"
},
"prompt": {
"no_internet": "Lütfen internet bağlantınızı kontrol edin",
"aosp_key_set": "AOSP keybox başarıyla ayarlandı",
"key_set_error": "Keybox güncellenemedi",
"valid_key_set": "Geçerli keybox başarıyla ayarlandı",
"no_valid_fallback": "Geçerli keybox bulunamadı, AOSP keybox ile değiştirildi.",
"boot_hash_set": "Doğrulanmış Boot Hash başarıyla kaydedildi",
"boot_hash_set_error": "Doğrulanmış Boot Hash güncellenemedi",
"saved_target": "Yapılandırma target.txt dosyasına kaydedildi",
"save_error": "Yapılandırma kaydedilemedi",
"uninstall_prompt": "WebUI yeniden başlatma sonrasında kaldırılacak",
"uninstall_failed": "WebUI kaldırılamadı",
"new_update": "Yeni bir güncelleme mevcut!",
"downloading": "Yeni güncelleme indiriliyor...",
"downloaded": "İndirme tamamlandı",
"download_fail": "Güncelleme indirilemedi",
"installing": "Güncelleme yükleniyor...",
"installed": "Başarıyla yüklendi, şimdi yeniden başlatın.",
"install_fail": "Yükleme başarısız oldu, lütfen manuel olarak güncelleyin",
"rebooting": "Yeniden başlatılıyor...",
"reboot_fail": "Yeniden başlatma başarısız, lütfen manuel olarak yeniden başlatın"
}
}

View File

@@ -1,54 +1,86 @@
{
"title": "TS插件 - 更新目标列表",
"search_placeholder": "搜索",
"save_and_update_button": "保存",
"boot_hash_save_button": "保存",
"loading": "加载中...",
"boot_hash_input_placeholder": "在此粘贴您的哈希值",
"uninstall_webui": "卸载 WebUI",
"update_available": "发现新的版本",
"redirect_to_release": "点击跳转 GitHub 下载最新版本",
"refresh": "刷新",
"select_all": "全选",
"deselect_all": "取消全选",
"select_denylist": "从排除列表中选择",
"deselect_unnecessary": "取消选择非必应用",
"set_aosp_keybox": "设置 AOSP 密钥",
"set_valid_keybox": "设置有效密钥",
"set_verified_boot_hash": "设置哈希值",
"about": "关于",
"help_instructions": "使用指南",
"save_and_update_description": "保存当前配置到目标列表target.txt",
"refresh_description": "刷新应用列表和排除列表。",
"select_deselect": "全选 & 取消全选",
"select_description": "选择或取消选择当前界面中的所有应用。",
"select_denylist_description": "仅适用于 Magisk选择在排除列表中的应用。推荐使用。",
"deselect_unnecessary_description": "非必要分类Xposed 模块、root 管理器、与 root 相关的应用,以及从不检查 bootloader 状态的通用应用。此功能需连网使用。",
"set_keybox": "设置 AOSP & 有效密钥",
"set_aosp_keybox_description": "替换 Tricky Store 的密钥keybox.xml。如果没有有效密钥将替换为 AOSP 密钥。有效密钥选项需连网使用。",
"set_verified_boot_hash_description": "从 Key Attestation Demo 获取 verifiedBootHash哈希值。通过重置 ro.boot.vbmeta.digest 修复异常 boot 状态。",
"module_name_line1": "TS插件",
"module_name_line2": "更新目标列表",
"by": "作者:",
"telegram_channel": "TG频道",
"github": "GitHub",
"disclaimer": "此 WebUI 不是 Tricky Store 的一部分,遇到任何问题请勿向 Tricky Store 作者反馈。",
"acknowledgment": "特别鸣谢",
"no_internet": "请检查您的网络连接",
"aosp_key_set": "成功设置 AOSP 密钥",
"key_set_error": "更新密钥失败",
"valid_key_set": "成功设置有效密钥",
"no_valid_fallback": "未找到有效密钥,已替换为 AOSP 密钥",
"boot_hash_set": "哈希值重置成功",
"boot_hash_set_error": "哈希值重置失败",
"saved_target": "成功保存配置",
"save_error": "保存配置失败",
"uninstall_prompt": "WebUI 将在重启后被移除",
"uninstall_failed": "卸载 WebUI 失败",
"new_update": "发现新的版本!"
"language": "简体中文",
"header": {
"title": "TS 插件"
},
"help": {
"help_instructions": "使用指南",
"save_and_update": "保存",
"save_and_update_description": "保存当前配置到目标列表target.txt。",
"refresh": "刷新",
"refresh_description": "刷新应用列表和排除列表。",
"select_deselect": "全选 & 取消全选",
"select_description": "选择或取消选择当前界面中的所有应用。",
"select_denylist": "从排除列表中选择",
"select_denylist_description": "仅适用于 Magisk选择在排除列表中的应用。推荐使用。",
"deselect_unnecessary": "取消选择非必应用",
"deselect_unnecessary_description": "非必要分类Xposed 模块、root 管理器、与 root 相关的应用,以及从不检查 bootloader 状态的通用应用。此功能需连网使用。",
"set_keybox": "设置 AOSP & 有效密钥",
"set_keybox_description": "替换 Tricky Store 的密钥keybox.xml。如果没有有效密钥将替换为 AOSP 密钥。有效密钥选项需连网使用。",
"set_verified_boot_hash": "设置哈希值",
"set_verified_boot_hash_description": "从 Key Attestation Demo 获取 verifiedBootHash哈希值。通过重置 ro.boot.vbmeta.digest 修复异常 boot 状态。"
},
"update": {
"update_available": "发现新的版本",
"redirect_to_release": "点击下载最新版本",
"changelog": "更新日志",
"install": "安装",
"reboot": "重启"
},
"search_bar": {
"search_placeholder": "搜索"
},
"functional_button": {
"save_and_update_button": "保存",
"uninstall_webui": "卸载 WebUI"
},
"loading": {
"loading": "加载中..."
},
"menu": {
"refresh": "刷新",
"select_all": "全选",
"deselect_all": "取消全选",
"select_denylist": "从排除列表中选择",
"deselect_unnecessary": "取消选择非必应用",
"set_aosp_keybox": "设置 AOSP 密钥",
"set_valid_keybox": "设置有效密钥",
"set_verified_boot_hash": "设置哈希值",
"about": "关于"
},
"reset_vbmeta": {
"boot_hash_input_placeholder": "在此粘贴您的哈希值",
"boot_hash_save_button": "保存"
},
"about": {
"module_name_line1": "TS插件",
"module_name_line2": "更新目标列表",
"by": "作者:",
"telegram_channel": "TG频道",
"github": "GitHub",
"disclaimer": "此 WebUI 不是 Tricky Store 的一部分,遇到任何问题请勿向 Tricky Store 作者反馈。",
"acknowledgment": "特别鸣谢"
},
"prompt": {
"no_internet": "请检查您的网络连接",
"aosp_key_set": "成功设置 AOSP 密钥",
"key_set_error": "更新密钥失败",
"valid_key_set": "成功设置有效密钥",
"no_valid_fallback": "未找到有效密钥,已替换为 AOSP 密钥。",
"boot_hash_set": "哈希值重置成功",
"boot_hash_set_error": "哈希值重置失败",
"saved_target": "成功保存配置",
"save_error": "保存配置失败",
"uninstall_prompt": "WebUI 将在重启后被移除",
"uninstall_failed": "卸载 WebUI 失败",
"new_update": "发现新的版本!",
"downloading": "正在下载...",
"downloaded": "下载完成",
"download_fail": "下载失败",
"installing": "正常安装...",
"installed": "安装完成,重启生效",
"install_fail": "安装失败,请手动更新",
"rebooting": "正在重启...",
"reboot_fail": "重启失败,请手动重启"
}
}

View File

@@ -1,54 +1,86 @@
{
"title": "TS插件 - 更新目標列表",
"search_placeholder": "搜尋",
"save_and_update_button": "保存",
"boot_hash_save_button": "保存",
"loading": "加載中...",
"boot_hash_input_placeholder": "在此粘貼您的哈希值",
"uninstall_webui": "卸載 WebUI",
"update_available": "發現新的版本",
"redirect_to_release": "點擊跳轉至 GitHub 下載最新版本",
"refresh": "刷新",
"select_all": "全選",
"deselect_all": "取消全選",
"select_denylist": "從排除列表中選擇",
"deselect_unnecessary": "取消選擇非必應用",
"set_aosp_keybox": "設置 AOSP 密鑰",
"set_valid_keybox": "設置有效密鑰",
"set_verified_boot_hash": "設置哈希值",
"about": "關於",
"help_instructions": "使用指南",
"save_and_update_description": "保存當前配置到目標列表target.txt",
"refresh_description": "刷新應用列表和排除列表。",
"select_deselect": "全選 & 取消全選",
"select_description": "選擇或取消選擇當前界面中的所有應用。",
"select_denylist_description": "僅適用於 Magisk選擇在排除列表中的應用。推薦使用。",
"deselect_unnecessary_description": "非必要分類Xposed 模塊、root 管理器、與 root 相關的應用,以及從不檢查 bootloader 狀態的通用應用。此功能需連網使用。",
"set_keybox": "設置 AOSP & 有效密鑰",
"set_aosp_keybox_description": "替換 Tricky Store 的密鑰keybox.xml。如果沒有有效密鑰將替換為 AOSP 密鑰。有效密鑰選項需連網使用。",
"set_verified_boot_hash_description": "從 Key Attestation Demo 獲取 verifiedBootHash哈希值。通過重置 ro.boot.vbmeta.digest 修復異常 boot 狀態。",
"module_name_line1": "TS插件",
"module_name_line2": "更新目標列表",
"by": "作者:",
"telegram_channel": "TG頻道",
"github": "GitHub",
"disclaimer": "此 WebUI 不是 Tricky Store 的一部分,遇到任何問題請勿向 Tricky Store 作者反饋。",
"acknowledgment": "特別鳴謝",
"no_internet": "請檢查您的網路連接",
"aosp_key_set": "成功設置 AOSP 密鑰",
"key_set_error": "更新密鑰失敗",
"valid_key_set": "成功設置有效密鑰",
"no_valid_fallback": "未找到有效密鑰,已替換為 AOSP 密鑰",
"boot_hash_set": "哈希值重置成功",
"boot_hash_set_error": "哈希值重置失敗",
"saved_target": "成功保存配置",
"save_error": "保存配置失敗",
"uninstall_prompt": "WebUI 將在重啟後被移除",
"uninstall_failed": "卸載 WebUI 失敗",
"new_update": "發現新的版本!"
"language": "繁體中文",
"header": {
"title": "TS 插件"
},
"help": {
"help_instructions": "使用指南",
"save_and_update": "保存",
"save_and_update_description": "保存當前配置到目標列表target.txt。",
"refresh": "刷新",
"refresh_description": "刷新應用列表和排除列表。",
"select_deselect": "全選 & 取消全選",
"select_description": "選擇或取消選擇當前界面中的所有應用。",
"select_denylist": "從排除列表中選擇",
"select_denylist_description": "僅適用於 Magisk選擇在排除列表中的應用。推薦使用。",
"deselect_unnecessary": "取消選擇非必要應用",
"deselect_unnecessary_description": "非必要分類Xposed 模組、root 管理器、與 root 相關的應用,以及從不檢查 bootloader 狀態的通用應用。此功能需連網使用。",
"set_keybox": "設置 AOSP & 有效密鑰",
"set_keybox_description": "替換 Tricky Store 的密鑰keybox.xml。如果沒有有效密鑰將替換為 AOSP 密鑰。有效密鑰選項需連網使用。",
"set_verified_boot_hash": "設置哈希值",
"set_verified_boot_hash_description": "從 Key Attestation Demo 獲取 verifiedBootHash哈希值。通過重置 ro.boot.vbmeta.digest 修復異常 boot 狀態。"
},
"update": {
"update_available": "發現新版本",
"redirect_to_release": "點擊下載最新版本",
"changelog": "更新日誌",
"install": "安裝",
"reboot": "重啟"
},
"search_bar": {
"search_placeholder": "搜索"
},
"functional_button": {
"save_and_update_button": "保存",
"uninstall_webui": "卸載 WebUI"
},
"loading": {
"loading": "加載中..."
},
"menu": {
"refresh": "刷新",
"select_all": "全選",
"deselect_all": "取消全選",
"select_denylist": "從排除列表中選擇",
"deselect_unnecessary": "取消選擇非必要應用",
"set_aosp_keybox": "設置 AOSP 密鑰",
"set_valid_keybox": "設置有效密鑰",
"set_verified_boot_hash": "設置哈希值",
"about": "關於"
},
"reset_vbmeta": {
"boot_hash_input_placeholder": "在此貼上您的哈希值",
"boot_hash_save_button": "保存"
},
"about": {
"module_name_line1": "TS 插件",
"module_name_line2": "更新目標列表",
"by": "作者:",
"telegram_channel": "Telegram 頻道",
"github": "GitHub",
"disclaimer": "此 WebUI 並非 Tricky Store 的一部分,如遇任何問題請勿向 Tricky Store 作者反饋。",
"acknowledgment": "特別鳴謝"
},
"prompt": {
"no_internet": "請檢查您的網絡連接",
"aosp_key_set": "成功設置 AOSP 密鑰",
"key_set_error": "更新密鑰失敗",
"valid_key_set": "成功設置有效密鑰",
"no_valid_fallback": "未找到有效密鑰,已替換為 AOSP 密鑰。",
"boot_hash_set": "哈希值重置成功",
"boot_hash_set_error": "哈希值重置失敗",
"saved_target": "成功保存配置",
"save_error": "保存配置失敗",
"uninstall_prompt": "WebUI 將在重啟後被移除",
"uninstall_failed": "卸載 WebUI 失敗",
"new_update": "發現新版本!",
"downloading": "正在下載...",
"downloaded": "下載完成",
"download_fail": "下載失敗",
"installing": "正在安裝...",
"installed": "安裝完成,重啟生效",
"install_fail": "安裝失敗,請手動更新",
"rebooting": "正在重啟...",
"reboot_fail": "重啟失敗,請手動重啟"
}
}

View File

@@ -0,0 +1,46 @@
import { execCommand, linkRedirect } from './main.js';
const telegramLink = document.getElementById('telegram');
const githubLink = document.getElementById('github');
// Function to show about overlay
document.getElementById("about").addEventListener("click", () => {
const aboutOverlay = document.getElementById('about-overlay');
const aboutMenu = document.getElementById('about-menu');
const closeAbout = document.getElementById('close-about');
const showMenu = () => {
aboutOverlay.style.display = 'flex';
setTimeout(() => {
aboutOverlay.style.opacity = '1';
aboutMenu.style.opacity = '1';
}, 10);
document.body.style.overflow = 'hidden';
};
const hideMenu = () => {
aboutOverlay.style.opacity = '0';
aboutMenu.style.opacity = '0';
setTimeout(() => {
aboutOverlay.style.display = 'none';
document.body.style.overflow = 'auto';
}, 200);
};
showMenu();
closeAbout.addEventListener('click', (event) => {
event.stopPropagation();
hideMenu();
});
aboutOverlay.addEventListener('click', (event) => {
if (!aboutMenu.contains(event.target)) {
hideMenu();
}
});
menu.addEventListener('click', (event) => event.stopPropagation());
});
// Event listener for link redirect
telegramLink.addEventListener('click', function() {
linkRedirect('https://t.me/kowchannel');
});
githubLink.addEventListener('click', function() {
linkRedirect('https://github.com/KOWX712/Tricky-Addon-Update-Target-List');
});

View File

@@ -0,0 +1,262 @@
import { basePath, execCommand, floatingBtn, appsWithExclamation, appsWithQuestion, toast } from './main.js';
const appTemplate = document.getElementById('app-template').content;
const modeOverlay = document.querySelector('.mode-overlay');
export const appListContainer = document.getElementById('apps-list');
export const updateCard = document.getElementById('update-card');
export let modeActive = false;
// Fetch and render applist
export async function fetchAppList() {
try {
let targetList = [];
try {
const targetFileContent = await execCommand('cat /data/adb/tricky_store/target.txt');
targetList = processTargetList(targetFileContent);
console.log("Current target list:", targetList);
} catch (error) {
toast("Failed to read target.txt!");
console.error("Failed to read target.txt file:", error);
}
let applistMap = {};
try {
const applistResult = await execCommand(`cat ${basePath}common/tmp/applist`);
applistMap = applistResult
.split("\n")
.reduce((map, line) => {
const match = line.match(/app-name:\s*(.+),\s*package-name:\s*(.+)/);
if (match) {
const appName = match[1].trim();
const packageName = match[2].trim();
map[packageName] = appName;
}
return map;
}, {});
console.log("Applist loaded successfully.");
} catch (error) {
console.warn("Applist file not found or could not be loaded. Skipping applist lookup.");
}
const result = await execCommand("pm list packages -3 | awk -F: '{print $2}'; pm list packages -s | awk -F: '{print $2}' | grep -Ex 'com.google.android.gms|com.google.android.gsf|com.android.vending'");
const appEntries = result
.split("\n")
.map(line => {
const packageName = line.trim();
const appName = applistMap[packageName] || null;
return { appName, packageName };
})
.filter(entry => entry.packageName);
for (const entry of appEntries) {
if (!entry.appName) {
try {
const apkPath = await execCommand(`pm path ${entry.packageName} | grep "base.apk" | awk -F: '{print $2}' | tr -d '\\r'`);
if (apkPath) {
const appName = await execCommand(`${basePath}common/aapt dump badging ${apkPath.trim()} 2>/dev/null | grep "application-label:" | sed "s/application-label://; s/'//g"`);
entry.appName = appName.trim() || "Unknown App";
} else {
entry.appName = "Unknown App";
}
} catch (error) {
entry.appName = "Unknown App";
}
}
}
// Sort
const sortedApps = appEntries.sort((a, b) => {
const aChecked = targetList.includes(a.packageName);
const bChecked = targetList.includes(b.packageName);
if (aChecked !== bChecked) {
return aChecked ? -1 : 1;
}
return (a.appName || "").localeCompare(b.appName || "");
});
// Render
appListContainer.innerHTML = "";
sortedApps.forEach(({ appName, packageName }) => {
const appElement = document.importNode(appTemplate, true);
const contentElement = appElement.querySelector(".content");
contentElement.setAttribute("data-package", packageName);
// Set unique names for radio button groups
const radioButtons = appElement.querySelectorAll('input[type="radio"]');
radioButtons.forEach((radio) => {
radio.name = `mode-radio-${packageName}`;
});
// Preselect the radio button based on the package name
const generateRadio = appElement.querySelector('#generate-mode');
const hackRadio = appElement.querySelector('#hack-mode');
const normalRadio = appElement.querySelector('#normal-mode');
if (appsWithExclamation.includes(packageName)) {
generateRadio.checked = true;
} else if (appsWithQuestion.includes(packageName)) {
hackRadio.checked = true;
} else {
normalRadio.checked = true;
}
const nameElement = appElement.querySelector(".name");
nameElement.innerHTML = `<strong>${appName || "Unknown App"}</strong><br>${packageName}`;
const checkbox = appElement.querySelector(".checkbox");
checkbox.checked = targetList.includes(packageName);
appListContainer.appendChild(appElement);
});
console.log("App list with names and packages rendered successfully.");
} catch (error) {
toast("Failed to fetch app list!");
console.error("Failed to fetch or render app list with names:", error);
}
floatingBtn.style.transform = 'translateY(0)';
toggleableCheckbox();
if (appListContainer.firstChild !== updateCard) {
appListContainer.insertBefore(updateCard, appListContainer.firstChild);
}
const checkboxes = appListContainer.querySelectorAll(".checkbox");
setupRadioButtonListeners();
setupModeMenu();
updateCheckboxColor();
}
// Function to save app with ! and ? then process target list
function processTargetList(targetFileContent) {
appsWithExclamation.length = 0;
appsWithQuestion.length = 0;
const targetList = targetFileContent
.split("\n")
.map(app => {
const trimmedApp = app.trim();
if (trimmedApp.endsWith('!')) {
appsWithExclamation.push(trimmedApp.slice(0, -1));
} else if (trimmedApp.endsWith('?')) {
appsWithQuestion.push(trimmedApp.slice(0, -1));
}
return trimmedApp.replace(/[!?]/g, '');
})
.filter(app => app.trim() !== '');
return targetList;
}
// Make checkboxes toggleable
function toggleableCheckbox() {
const appElements = appListContainer.querySelectorAll(".card");
appElements.forEach(card => {
const content = card.querySelector(".content");
const checkbox = content.querySelector(".checkbox");
content.addEventListener("click", (event) => {
checkbox.checked = !checkbox.checked;
});
});
}
// Add eventlistener to mode button
function setupRadioButtonListeners() {
const radioButtons = appListContainer.querySelectorAll('input[type="radio"]');
radioButtons.forEach((radioButton) => {
radioButton.addEventListener('change', (event) => {
const card = radioButton.closest(".card");
const packageName = card.querySelector(".content").getAttribute("data-package");
if (radioButton.id === 'generate-mode') {
if (!appsWithExclamation.includes(packageName)) {
appsWithExclamation.push(packageName);
}
const indexInQuestion = appsWithQuestion.indexOf(packageName);
if (indexInQuestion > -1) {
appsWithQuestion.splice(indexInQuestion, 1);
}
} else if (radioButton.id === 'hack-mode') {
if (!appsWithQuestion.includes(packageName)) {
appsWithQuestion.push(packageName);
}
const indexInExclamation = appsWithExclamation.indexOf(packageName);
if (indexInExclamation > -1) {
appsWithExclamation.splice(indexInExclamation, 1);
}
} else if (radioButton.id === 'normal-mode') {
const indexInExclamation = appsWithExclamation.indexOf(packageName);
if (indexInExclamation > -1) {
appsWithExclamation.splice(indexInExclamation, 1);
}
const indexInQuestion = appsWithQuestion.indexOf(packageName);
if (indexInQuestion > -1) {
appsWithQuestion.splice(indexInQuestion, 1);
}
}
updateCheckboxColor();
console.log("Updated appsWithExclamation:", appsWithExclamation);
console.log("Updated appsWithQuestion:", appsWithQuestion);
});
});
}
// Hold to open menu
function setupModeMenu() {
let holdTimeout;
function showMode(card) {
const modeElement = card.querySelector(".mode");
if (modeElement) {
modeActive = true;
modeElement.style.display = "flex";
modeOverlay.style.display = "flex";
setTimeout(() => {
modeElement.classList.add('show');
}, 10);
}
}
function hideAllModes() {
const allModeElements = appListContainer.querySelectorAll(".mode");
allModeElements.forEach((modeElement) => {
modeActive = false;
modeElement.classList.remove('show');
modeOverlay.style.display = "none";
setTimeout(() => {
modeElement.style.display = "none";
}, 200);
});
}
const cards = appListContainer.querySelectorAll(".card");
cards.forEach((card) => {
card.addEventListener("pointerdown", () => {
const checkbox = card.querySelector(".checkbox");
if (checkbox && checkbox.checked) {
holdTimeout = setTimeout(() => {
showMode(card);
}, 500);
}
});
card.addEventListener("pointerup", () => clearTimeout(holdTimeout));
card.addEventListener("pointercancel", () => clearTimeout(holdTimeout));
});
document.addEventListener("click", (event) => {
if (!event.target.closest(".mode") || modeOverlay.contains(event.target)) {
hideAllModes();
} else if (event.target.closest(".status-indicator")) {
setTimeout(() => {
hideAllModes();
}, 300);
}
});
window.addEventListener("scroll", hideAllModes);
}
// Function to update card borders color
function updateCheckboxColor() {
const cards = appListContainer.querySelectorAll(".card");
cards.forEach((card) => {
const packageName = card.querySelector(".content").getAttribute("data-package");
const checkbox = card.querySelector(".checkbox");
checkbox.classList.remove("checkbox-checked-generate", "checkbox-checked-hack");
if (appsWithExclamation.includes(packageName)) {
checkbox.classList.add("checkbox-checked-generate");
} else if (appsWithQuestion.includes(packageName)) {
checkbox.classList.add("checkbox-checked-hack");
} else if (checkbox.checked) {
checkbox.classList.remove("checkbox-checked-generate", "checkbox-checked-hack");
}
});
}

View File

@@ -0,0 +1,31 @@
const helpButton = document.getElementById('help-button');
const helpOverlay = document.getElementById('help-overlay');
const closeHelp = document.getElementById('close-help');
const helpList = document.getElementById('help-list');
// Open help menu
helpButton.addEventListener("click", () => {
helpOverlay.classList.remove("hide");
helpOverlay.style.display = "flex";
requestAnimationFrame(() => {
helpOverlay.classList.add("show");
});
document.body.classList.add("no-scroll");
});
const hideHelpOverlay = () => {
helpOverlay.classList.remove("show");
helpOverlay.classList.add("hide");
document.body.classList.remove("no-scroll");
setTimeout(() => {
helpOverlay.style.display = "none";
}, 200);
};
// Close help menu
closeHelp.addEventListener("click", hideHelpOverlay);
helpOverlay.addEventListener("click", (event) => {
if (event.target === helpOverlay) {
hideHelpOverlay();
}
});

View File

@@ -0,0 +1,136 @@
import { basePath, execCommand, toast } from './main.js';
const languageButton = document.querySelector('.language-button');
const languageMenu = document.querySelector('.language-menu');
const languageOptions = document.querySelectorAll('.language-option');
const languageOverlay = document.getElementById('language-overlay');
export let translations = {};
let currentLang = 'en-US';
let availableLanguages = ['en-US'];
// Function to check for available language
export async function initializeAvailableLanguages() {
try {
const multiLang = await execCommand(`find ${basePath}webui/locales -type f -name "*.json" ! -name "A-template.json" -exec basename -s .json {} \\;`);
availableLanguages = multiLang.trim().split('\n');
generateLanguageMenu();
} catch (error) {
toast("Failed to get available langauge!");
console.error('Failed to fetch available languages:', error);
availableLanguages = ['en-US'];
}
}
// Function to detect user's default language
export function detectUserLanguage() {
const userLang = navigator.language || navigator.userLanguage;
const langCode = userLang.split('-')[0];
if (availableLanguages.includes(userLang)) {
return userLang;
} else if (availableLanguages.includes(langCode)) {
return langCode;
} else {
return 'en-US';
}
}
// Load translations dynamically based on the selected language
export async function loadTranslations(lang) {
try {
const response = await fetch(`/locales/${lang}.json`);
translations = await response.json();
applyTranslations();
} catch (error) {
toast(`Failed to load translation for ${lang}!`);
console.error(`Error loading translations for ${lang}:`, error);
if (lang !== 'en-US') {
console.log("Falling back to English.");
loadTranslations('en-US');
}
}
}
// Function to apply translations to all elements with data-i18n attributes
function applyTranslations() {
document.querySelectorAll("[data-i18n]").forEach((el) => {
const keyString = el.getAttribute("data-i18n");
const translation = keyString.split('.').reduce((acc, key) => acc && acc[key], translations);
if (translation) {
if (el.hasAttribute("placeholder")) {
el.setAttribute("placeholder", translation);
} else {
el.textContent = translation;
}
}
});
}
// Function to setup the language menu
export function setupLanguageMenu() {
languageButton.addEventListener("click", (event) => {
event.stopPropagation();
const isVisible = languageMenu.classList.contains("show");
if (isVisible) {
closeLanguageMenu();
} else {
openLanguageMenu();
}
});
document.addEventListener("click", (event) => {
if (!languageButton.contains(event.target) && !languageMenu.contains(event.target)) {
closeLanguageMenu();
}
});
languageOptions.forEach(option => {
option.addEventListener("click", () => {
closeLanguageMenu();
});
});
window.addEventListener('scroll', () => {
if (languageMenu.classList.contains("show")) {
closeLanguageMenu();
}
});
function openLanguageMenu() {
languageOverlay.style.display = 'flex';
setTimeout(() => {
languageMenu.classList.add("show");
}, 10);
}
function closeLanguageMenu() {
languageMenu.classList.remove("show");
languageOverlay.style.display = 'none';
}
languageMenu.addEventListener("click", (e) => {
if (e.target.classList.contains("language-option")) {
const lang = e.target.getAttribute("data-lang");
loadTranslations(lang);
closeLanguageMenu();
}
});
}
// Function to generate the language menu dynamically
async function generateLanguageMenu() {
languageMenu.innerHTML = '';
const languagePromises = availableLanguages.map(async (lang) => {
try {
const response = await fetch(`/locales/${lang}.json`);
const data = await response.json();
return { lang, name: data.language || lang };
} catch (error) {
console.error(`Error fetching language name for ${lang}:`, error);
return { lang, name: lang };
}
});
const languageData = await Promise.all(languagePromises);
const sortedLanguages = languageData.sort((a, b) => a.name.localeCompare(b.name));
sortedLanguages.forEach(({ lang, name }) => {
const button = document.createElement('button');
button.classList.add('language-option');
button.setAttribute('data-lang', lang);
button.textContent = name;
languageMenu.appendChild(button);
});
}

View File

@@ -0,0 +1,323 @@
import { appListContainer, fetchAppList, modeActive } from './applist.js';
import { initializeAvailableLanguages, detectUserLanguage, loadTranslations, setupLanguageMenu, translations } from './language.js';
import { aospkb } from './menu_option.js';
import { searchMenuContainer, searchInput, clearBtn, setupMenuToggle } from './search_menu.js';
import { updateCheck } from './update.js';
// Header Elements
const headerBlock = document.querySelector('.header-block');
const title = document.querySelector('.header');
export const noConnection = document.querySelector('.no-connection');
// Loading, Save and Prompt Elements
const loadingIndicator = document.querySelector('.loading');
const prompt = document.getElementById('prompt');
const floatingCard = document.querySelector('.floating-card');
export const floatingBtn = document.querySelector('.floating-btn');
export const basePath = "set-path";
export const appsWithExclamation = [];
export const appsWithQuestion = [];
const ADDITIONAL_APPS = [ "android", "com.android.vending", "com.google.android.gms", "io.github.vvb2060.keyattestation", "io.github.vvb2060.mahoshojo", "icu.nullptr.nativetest" ]; // Always keep default apps in target.txt
const rippleClasses = ['.language-option', '.menu-button', '.menu-options li', '.search-card', '.card', '.update-card', '.link-icon', '.floating-btn', '.uninstall-container', '.boot-hash-save-button', '.boot-hash-value', '.reboot', '.install'];
// Variables
let e = 0;
let isRefreshing = false;
// Function to load the version from module.prop
async function getModuleVersion() {
const moduleVersion = document.getElementById('module-version');
try {
const version = await execCommand(`grep '^version=' ${basePath}common/update/module.prop | cut -d'=' -f2`);
moduleVersion.textContent = version;
} catch (error) {
console.error("Failed to read version from module.prop:", error);
updateVersion("Error");
}
}
// Function to refresh app list
async function refreshAppList() {
isRefreshing = true;
title.style.transform = 'translateY(0)';
searchMenuContainer.style.transform = 'translateY(0)';
hideFloatingBtn();
searchInput.value = '';
clearBtn.style.display = "none";
appListContainer.innerHTML = '';
loadingIndicator.style.display = 'flex';
document.querySelector('.uninstall-container').classList.add('hidden-uninstall');
await new Promise(resolve => setTimeout(resolve, 500));
window.scrollTo(0, 0);
if (noConnection.style.display === "flex") {
try {
updateCheck();
await execCommand(`[ -f ${basePath}common/tmp/exclude-list ] && rm -f "${basePath}common/tmp/exclude-list"`);
} catch (error) {
toast("Failed!");
console.error("Error occurred:", error);
}
}
await fetchAppList();
applyRippleEffect();
loadingIndicator.style.display = 'none';
document.querySelector('.uninstall-container').classList.remove('hidden-uninstall');
isRefreshing = false;
}
// Function to check if Magisk
async function checkMagisk() {
const selectDenylistElement = document.getElementById('select-denylist');
try {
const magiskEnv = await execCommand(`command -v magisk >/dev/null 2>&1 || echo "NO"`);
if (magiskEnv.trim() !== "NO") {
console.log("Denylist conditions met, displaying element.");
selectDenylistElement.style.display = "flex";
} else {
console.log("not running on Magisk, leaving denylist element hidden.");
}
} catch (error) {
toast("Failed to check Magisk!");
console.error("Error while checking denylist conditions:", error);
}
}
// Function to show the prompt with a success or error message
export function showPrompt(key, isSuccess = true, duration = 3000) {
const message = key.split('.').reduce((acc, k) => acc && acc[k], translations) || key;
prompt.textContent = message;
prompt.classList.toggle('error', !isSuccess);
if (window.promptTimeout) {
clearTimeout(window.promptTimeout);
}
setTimeout(() => {
if (typeof ksu !== 'undefined' && ksu.mmrl) {
prompt.style.transform = 'translateY(calc((var(--window-inset-bottom) + 60%) * -1))';
} else {
prompt.style.transform = 'translateY(-60%)';
}
window.promptTimeout = setTimeout(() => {
prompt.style.transform = 'translateY(100%)';
}, duration);
}, 100);
}
// Function to redirect link on external browser
export async function linkRedirect(link) {
try {
await execCommand(`am start -a android.intent.action.VIEW -d ${link}`);
} catch (error) {
toast("Failed!");
console.error('Error redirect link:', error);
}
}
// Save configure and preserve ! and ? in target.txt
document.getElementById("save").addEventListener("click", async () => {
const selectedApps = Array.from(appListContainer.querySelectorAll(".checkbox:checked"))
.map(checkbox => checkbox.closest(".card").querySelector(".content").getAttribute("data-package"));
let finalAppsList = new Set(selectedApps);
ADDITIONAL_APPS.forEach(app => {
finalAppsList.add(app);
});
finalAppsList = Array.from(finalAppsList);
try {
const modifiedAppsList = finalAppsList.map(app => {
if (appsWithExclamation.includes(app)) {
return `${app}!`;
} else if (appsWithQuestion.includes(app)) {
return `${app}?`;
}
return app;
});
const updatedTargetContent = modifiedAppsList.join("\n");
await execCommand(`echo "${updatedTargetContent}" | sort -u > /data/adb/tricky_store/target.txt`);
console.log("target.txt updated successfully.");
showPrompt("prompt.saved_target");
for (const app of appsWithExclamation) {
await execCommand(`sed -i 's/^${app}$/${app}!/' /data/adb/tricky_store/target.txt`);
}
for (const app of appsWithQuestion) {
await execCommand(`sed -i 's/^${app}$/${app}?/' /data/adb/tricky_store/target.txt`);
}
console.log("App names modified in target.txt.");
} catch (error) {
console.error("Failed to update target.txt:", error);
showPrompt("prompt.save_error", false);
}
await refreshAppList();
});
// Uninstall WebUI
document.querySelector(".uninstall-container").addEventListener("click", async () => {
try {
await execCommand(`sh ${basePath}common/get_extra.sh --uninstall`);
console.log("uninstall script executed successfully.");
showPrompt("prompt.uninstall_prompt");
} catch (error) {
console.error("Failed to execute uninstall command:", error);
showPrompt("prompt.uninstall_failed", false);
}
});
// Function to check if running in MMRL
function adjustHeaderForMMRL() {
if (typeof ksu !== 'undefined' && ksu.mmrl) {
console.log("Running in MMRL");
title.style.top = 'var(--window-inset-top)';
const insetTop = getComputedStyle(document.documentElement).getPropertyValue('--window-inset-top');
const insetTopValue = parseInt(insetTop, 10);
searchMenuContainer.style.top = `${insetTopValue + 40}px`;
headerBlock.style.display = 'block';
floatingCard.style.bottom = 'calc(var(--window-inset-bottom) + 50px)';
}
}
// Funtion to adapt floating button hide in MMRL
function hideFloatingBtn() {
if (typeof ksu !== 'undefined' && ksu.mmrl) {
floatingBtn.style.transform = 'translateY(calc(var(--window-inset-bottom) + 120px))';
} else {
floatingBtn.style.transform = 'translateY(120px)';
}
}
// Function to apply ripple effect
function applyRippleEffect() {
rippleClasses.forEach(selector => {
document.querySelectorAll(selector).forEach(element => {
if (element.dataset.rippleListener !== "true") {
element.addEventListener("pointerdown", function (event) {
if (isScrolling) return;
if (modeActive) return;
const ripple = document.createElement("span");
ripple.classList.add("ripple");
// Calculate ripple size and position
const rect = element.getBoundingClientRect();
const width = rect.width;
const size = Math.max(rect.width, rect.height);
const x = event.clientX - rect.left - size / 2;
const y = event.clientY - rect.top - size / 2;
// Determine animation duration
let duration = 0.3 + (width / 800) * 0.3;
duration = Math.min(0.8, Math.max(0.2, duration));
// Set ripple styles
ripple.style.width = ripple.style.height = `${size}px`;
ripple.style.left = `${x}px`;
ripple.style.top = `${y}px`;
ripple.style.animationDuration = `${duration}s`;
ripple.style.transition = `opacity ${duration}s ease`;
// Adaptive color
const computedStyle = window.getComputedStyle(element);
const bgColor = computedStyle.backgroundColor || "rgba(0, 0, 0, 0)";
const textColor = computedStyle.color;
const isDarkColor = (color) => {
const rgb = color.match(/\d+/g);
if (!rgb) return false;
const [r, g, b] = rgb.map(Number);
return (r * 0.299 + g * 0.587 + b * 0.114) < 96; // Luma formula
};
ripple.style.backgroundColor = isDarkColor(bgColor) ? "rgba(255, 255, 255, 0.2)" : "";
// Append ripple and handle cleanup
element.appendChild(ripple);
const handlePointerUp = () => {
ripple.classList.add("end");
setTimeout(() => {
ripple.classList.remove("end");
ripple.remove();
}, duration * 1000);
element.removeEventListener("pointerup", handlePointerUp);
element.removeEventListener("pointercancel", handlePointerUp);
};
element.addEventListener("pointerup", handlePointerUp);
element.addEventListener("pointercancel", handlePointerUp);
});
element.dataset.rippleListener = "true";
}
});
});
}
// Scroll event
let lastScrollY = window.scrollY;
let isScrolling = false;
let scrollTimeout;
const scrollThreshold = 40;
window.addEventListener('scroll', () => {
isScrolling = true;
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(() => {
isScrolling = false;
}, 200);
if (isRefreshing) return;
if (window.scrollY > lastScrollY && window.scrollY > scrollThreshold) {
title.style.transform = 'translateY(-80px)';
headerBlock.style.transform = 'translateY(-80px)';
searchMenuContainer.style.transform = 'translateY(-40px)';
hideFloatingBtn();
} else if (window.scrollY < lastScrollY) {
headerBlock.style.transform = 'translateY(0)';
title.style.transform = 'translateY(0)';
searchMenuContainer.style.transform = 'translateY(0)';
floatingBtn.style.transform = 'translateY(0)';
}
lastScrollY = window.scrollY;
});
// Initial load
document.addEventListener('DOMContentLoaded', async () => {
hideFloatingBtn();
adjustHeaderForMMRL();
getModuleVersion();
await initializeAvailableLanguages();
const userLang = detectUserLanguage();
await loadTranslations(userLang);
setupMenuToggle();
setupLanguageMenu();
await fetchAppList();
applyRippleEffect();
checkMagisk();
updateCheck();
loadingIndicator.style.display = "none";
floatingBtn.style.opacity = '1';
setTimeout(() => {
floatingBtn.style.transform = 'translateY(0)';
}, 10);
document.getElementById("refresh").addEventListener("click", refreshAppList);
document.getElementById("aospkb").addEventListener("click", aospkb);
document.querySelector('.uninstall-container').classList.remove('hidden-uninstall');
});
// Function to execute shell commands
export async function execCommand(command) {
return new Promise((resolve, reject) => {
const callbackName = `exec_callback_${Date.now()}_${e++}`;
window[callbackName] = (errno, stdout, stderr) => {
delete window[callbackName];
if (errno === 0) {
resolve(stdout);
} else {
console.error(`Error executing command: ${stderr}`);
reject(stderr);
}
};
try {
ksu.exec(command, "{}", callbackName);
} catch (error) {
console.error(`Execution error: ${error}`);
reject(error);
}
});
}
// Function to toast message
export function toast(message) {
ksu.toast(message);
}

6
module/webui/scripts/marked.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,109 @@
import { basePath, execCommand, showPrompt, toast } from './main.js';
// Function to check or uncheck all app
function toggleCheckboxes(shouldCheck) {
document.querySelectorAll(".card").forEach(card => {
if (card.style.display !== "none") {
card.querySelector(".checkbox").checked = shouldCheck;
}
});
}
// Function to select all visible apps
document.getElementById("select-all").addEventListener("click", () => toggleCheckboxes(true));
// Function to deselect all visible apps
document.getElementById("deselect-all").addEventListener("click", () => toggleCheckboxes(false));
// Function to read the denylist and check corresponding apps
document.getElementById("select-denylist").addEventListener("click", async () => {
try {
const result = await execCommand(`magisk --denylist ls 2>/dev/null | awk -F'|' '{print $1}' | grep -v "isolated" | sort -u`);
const denylistApps = result.split("\n").map(app => app.trim()).filter(Boolean);
const apps = document.querySelectorAll(".card");
apps.forEach(app => {
const contentElement = app.querySelector(".content");
const packageName = contentElement.getAttribute("data-package");
const checkbox = app.querySelector(".checkbox");
if (denylistApps.includes(packageName)) {
checkbox.checked = true;
}
});
await execCommand('touch "/data/adb/tricky_store/target_from_denylist"');
console.log("Denylist apps selected successfully.");
} catch (error) {
toast("Failed to read DenyList!");
console.error("Failed to select Denylist apps:", error);
}
});
// Function to read the exclude list and uncheck corresponding apps
document.getElementById("deselect-unnecessary").addEventListener("click", async () => {
try {
const fileCheck = await execCommand(`test -f ${basePath}common/tmp/exclude-list && echo "exists" || echo "not found"`);
if (fileCheck.trim() === "not found") {
setTimeout(async () => {
await execCommand(`sh ${basePath}common/get_extra.sh --unnecessary`);
}, 0);
console.log("Exclude list not found. Running the unnecessary apps script.");
} else {
setTimeout(async () => {
await execCommand(`sh ${basePath}common/get_extra.sh --xposed`);
}, 0);
console.log("Exclude list found. Running xposed script.");
}
await new Promise(resolve => setTimeout(resolve, 100));
const result = await execCommand(`cat ${basePath}common/tmp/exclude-list`);
const UnnecessaryApps = result.split("\n").map(app => app.trim()).filter(Boolean);
const apps = document.querySelectorAll(".card");
apps.forEach(app => {
const contentElement = app.querySelector(".content");
const packageName = contentElement.getAttribute("data-package");
const checkbox = app.querySelector(".checkbox");
if (UnnecessaryApps.includes(packageName)) {
checkbox.checked = false;
}
});
console.log("Unnecessary apps deselected successfully.");
} catch (error) {
toast("Failed!");
console.error("Failed to deselect unnecessary apps:", error);
}
});
// Function to replace aosp kb
export async function aospkb() {
try {
const sourcePath = `${basePath}common/.default`;
const destinationPath = "/data/adb/tricky_store/keybox.xml";
await execCommand(`mv -f ${destinationPath} ${destinationPath}.bak && xxd -r -p ${sourcePath} | base64 -d > ${destinationPath}`);
console.log("AOSP keybox copied successfully.");
showPrompt("prompt.aosp_key_set");
} catch (error) {
console.error("Failed to copy AOSP keybox:", error);
showPrompt("prompt.key_set_error", false);
}
}
// Function to replace valid kb
document.getElementById("extrakb").addEventListener("click", async () => {
setTimeout(async () => {
await execCommand(`sh ${basePath}common/get_extra.sh --kb`);
}, 100);
const sourcePath = `${basePath}common/tmp/.extra`;
const destinationPath = "/data/adb/tricky_store/keybox.xml";
try {
await new Promise(resolve => setTimeout(resolve, 300));
const fileExists = await execCommand(`[ -f ${sourcePath} ] && echo "exists"`);
if (fileExists.trim() !== "exists") {
throw new Error(".extra file not found");
}
await execCommand(`mv -f ${destinationPath} ${destinationPath}.bak && xxd -r -p ${sourcePath} | base64 -d > ${destinationPath}`);
console.log("Valid keybox copied successfully.");
showPrompt("prompt.valid_key_set");
} catch (error) {
console.error("Failed to copy valid keybox:", error);
await aospkb();
showPrompt("prompt.no_valid_fallback", false);
}
});

View File

@@ -0,0 +1,84 @@
import { appListContainer } from './applist.js';
const searchCard = document.querySelector('.search-card');
export const searchInput = document.getElementById('search');
export const clearBtn = document.getElementById('clear-btn');
export const searchMenuContainer = document.querySelector('.search-menu-container');
const menu = document.querySelector('.menu');
const menuButton = document.getElementById('menu-button');
const menuOptions = document.getElementById('menu-options');
const menuOverlay = document.getElementById('menu-overlay');
const menuIcon = menuButton.querySelector('.menu-icon');
// Focus on search input when search card is clicked
searchCard.addEventListener("click", () => {
searchInput.focus();
});
// Search functionality
searchInput.addEventListener("input", (e) => {
const searchQuery = e.target.value.toLowerCase();
const apps = appListContainer.querySelectorAll(".card");
apps.forEach(app => {
const name = app.querySelector(".name").textContent.toLowerCase();
app.style.display = name.includes(searchQuery) ? "block" : "none";
window.scrollTo(0, 0);
});
if (searchQuery !== "") {
clearBtn.style.display = "block";
} else {
clearBtn.style.display = "none";
}
});
// Clear search input
clearBtn.addEventListener("click", () => {
searchInput.value = "";
clearBtn.style.display = "none";
window.scrollTo(0, 0);
const apps = appListContainer.querySelectorAll(".card");
apps.forEach(app => {
app.style.display = "block";
});
});
// Function to toggle menu option
export function setupMenuToggle() {
menuButton.addEventListener('click', (event) => {
event.stopPropagation();
if (menuOptions.classList.contains('visible')) {
closeMenu();
} else {
openMenu();
}
});
document.addEventListener('click', (event) => {
if (!menuOptions.contains(event.target) && event.target !== menuButton) {
closeMenu();
}
});
window.addEventListener('scroll', () => {
if (menuOptions.classList.contains('visible')) {
closeMenu();
}
});
const menuOptionsList = document.querySelectorAll('#menu-options li');
menuOptionsList.forEach(option => {
option.addEventListener('click', (event) => {
event.stopPropagation();
closeMenu();
});
});
function openMenu() {
setTimeout(() => {
menuOptions.classList.add('visible');
menuIcon.classList.add('menu-open');
menuOverlay.style.display = 'flex';
}, 10);
}
function closeMenu() {
menuOptions.classList.remove('visible');
menuIcon.classList.remove('menu-open');
menuOverlay.style.display = 'none';
}
}

View File

@@ -0,0 +1,113 @@
import { basePath, execCommand, showPrompt, noConnection, linkRedirect } from './main.js';
import { updateCard } from './applist.js';
const updateCardText = document.getElementById('redirect-to-release');
const UpdateMenu = document.querySelector('.update-overlay');
const closeUpdate = document.querySelector('.close-update');
const releaseNotes = document.querySelector('.changelog');
const installButton = document.querySelector('.install');
const rebootButton = document.querySelector('.reboot');
// Function to run the update check
export async function updateCheck() {
try {
const output = await execCommand(`sh ${basePath}common/get_extra.sh --update`);
console.log("update script executed successfully.");
noConnection.style.display = "none";
if (output.includes("update")) {
console.log("Update detected from extra script.");
showPrompt("prompt.new_update", true, 2000);
updateCard.style.display = "flex";
setupUpdateMenu();
} else {
console.log("No update detected from extra script.");
}
} catch (error) {
console.error("Failed to execute update script:", error);
showPrompt("prompt.no_internet", false);
noConnection.style.display = "flex";
}
}
// Function to setup update menu
function setupUpdateMenu() {
function openUpdateMenu() {
UpdateMenu.style.display = "flex";
setTimeout(async () => {
UpdateMenu.style.opacity = "1";
}, 10);
document.body.classList.add("no-scroll");
}
function closeUpdateMenu() {
UpdateMenu.style.opacity = "0";
document.body.classList.remove("no-scroll");
setTimeout(async () => {
UpdateMenu.style.display = "none";
}, 200);
}
updateCard.addEventListener('click', async () => {
try {
const module = await execCommand(`[ -f ${basePath}common/tmp/module.zip ] || echo "false"`);
if (module.trim() === "false") {
showPrompt("prompt.downloading");
await new Promise(resolve => setTimeout(resolve, 200));
await execCommand(`sh ${basePath}common/get_extra.sh --get-update`);
showPrompt("prompt.downloaded");
}
const changelog = await execCommand(`sh ${basePath}common/get_extra.sh --release-note`);
window.linkRedirect = linkRedirect;
marked.setOptions({
sanitize: true,
walkTokens(token) {
if (token.type === 'link') {
const href = token.href;
token.href = "javascript:void(0);";
token.type = "html";
token.text = `<a href="javascript:void(0);" onclick="linkRedirect('${href}')">${token.text}</a>`;
}
}
});
const cleanedChangelog = changelog
.split('\n')
.filter(line => line.trim() !== '')
.join('\n');
const formattedChangelog = marked.parse(cleanedChangelog);
releaseNotes.innerHTML = formattedChangelog;
openUpdateMenu();
} catch (error) {
showPrompt("prompt.download_fail", false);
console.error('Error download module update:', error);
}
});
closeUpdate.addEventListener("click", closeUpdateMenu);
UpdateMenu.addEventListener("click", (event) => {
if (event.target === UpdateMenu) {
closeUpdateMenu();
}
});
installButton.addEventListener('click', async () => {
try {
showPrompt("prompt.installing");
setTimeout(async () => {
await execCommand(`su -c 'sh ${basePath}common/get_extra.sh --install-update'`);
showPrompt("prompt.installed");
installButton.style.display = "none";
rebootButton.style.display = "flex";
}, 300);
} catch (error) {
showPrompt("prompt.install_fail", false);
console.error('Fail to execute installation script:', error);
}
});
rebootButton.addEventListener('click', async () => {
try {
showPrompt("prompt.rebooting");
setTimeout(async () => {
await execCommand("svc power reboot");
}, 1000);
} catch (error) {
showPrompt("prompt.reboot_fail", false);
console.error('Fail to reboot:', error);
}
});
}

View File

@@ -0,0 +1,54 @@
import { execCommand, showPrompt } from './main.js';
const bootHashOverlay = document.getElementById('boot-hash-overlay');
const card = document.getElementById('boot-hash-card');
const inputBox = document.getElementById('boot-hash-input');
const saveButton = document.getElementById('boot-hash-save-button');
// Function to handle Verified Boot Hash
document.getElementById("boot-hash").addEventListener("click", async () => {
const showCard = () => {
bootHashOverlay.style.display = "flex";
card.style.display = "flex";
requestAnimationFrame(() => {
bootHashOverlay.classList.add("show");
card.classList.add("show");
});
document.body.style.overflow = "hidden";
};
const closeCard = () => {
bootHashOverlay.classList.remove("show");
card.classList.remove("show");
setTimeout(() => {
bootHashOverlay.style.display = "none";
card.style.display = "none";
document.body.style.overflow = "auto";
}, 200);
};
showCard();
try {
const bootHashContent = await execCommand("cat /data/adb/boot_hash");
const validHash = bootHashContent
.split("\n")
.filter(line => !line.startsWith("#") && line.trim())[0];
inputBox.value = validHash || "";
} catch (error) {
console.warn("Failed to read boot_hash file. Defaulting to empty input.");
inputBox.value = "";
}
saveButton.addEventListener("click", async () => {
const inputValue = inputBox.value.trim();
try {
await execCommand(`echo "${inputValue}" > /data/adb/boot_hash`);
await execCommand(`su -c resetprop -n ro.boot.vbmeta.digest ${inputValue}`);
showPrompt("prompt.boot_hash_set");
closeCard();
} catch (error) {
console.error("Failed to update boot_hash:", error);
showPrompt("prompt.boot_hash_set_error", false);
}
});
bootHashOverlay.addEventListener("click", (event) => {
if (event.target === bootHashOverlay) closeCard();
});
});

View File

@@ -1,833 +0,0 @@
body {
background-color: #F5F5F5;
padding-top: var(--window-inset-top);
padding-bottom: var(--window-inset-bottom);
}
.no-scroll {
overflow: hidden;
}
.header {
display: flex;
align-items: center;
justify-content: space-between;
position: fixed;
top: 0;
height: 40px;
width: calc(100% - 10px);
max-width: 1100px;
background-color: #F5F5F5;
transition: transform 0.3s ease;
z-index: 1100;
margin-left: auto;
margin-right: auto;
left: 0;
right: 0;
}
.header-block {
background-color: #F5F5F5;
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 1100;
transition: transform 0.3s ease;
height: var(--window-inset-top);
}
#title {
padding-left: 5px;
font-size: 16.5px;
font-weight: bold;
}
.no-connection {
padding: 0;
display: none;
margin-right: 0px;
background: none;
border: none;
}
.language-dropdown {
position: relative;
display: inline-block;
}
.language-button {
padding-top: 5px;
background: none;
border: none;
}
.language-menu {
display: flex;
padding: 3px 10px;
flex-direction: column;
position: absolute;
right: 0;
background-color: #fff;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
z-index: 1800;
border: 1px solid #ccc;
border-radius: 8px;
opacity: 0;
visibility: hidden;
max-height: calc(100vh - 50px);
overflow-y: auto;
transform: translateY(-10px);
transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s ease;
}
.language-menu.show {
display: block;
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.language-option {
padding: 8px 5px;
text-align: left;
background: none;
border: none;
font-size: 16px;
color: #333;
width: 100%;
white-space: nowrap;
border-bottom: 1px solid #ccc;
transition: background-color 0.2s ease;
}
.language-option:last-child {
border-bottom: none;
}
.language-overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: none;
z-index: 1100;
display: none;
}
.help-button {
padding-left: 5px;
margin-right: auto;
background: none;
border: none;
}
.help-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.5);
z-index: 2000;
justify-content: center;
align-items: center;
opacity: 0;
transition: opacity 0.2s ease;
}
.help-overlay.show {
display: flex;
opacity: 1;
}
.help-overlay.hide {
opacity: 0;
}
.help-menu {
position: relative;
width: 75vw;
max-width: 800px;
background-color: white;
padding: 0 10px;
border-radius: 15px;
text-align: left;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}
.close-help {
position: absolute;
top: 15px;
right: 12px;
background: none;
border: none;
font-size: 20px;
color: #ccc;
}
.help-content {
max-height: 85vh;
padding: 0 20px;
overflow-y: auto;
}
.help-content p {
font-size: 26px;
}
.help-content ul {
padding-left: 0;
list-style-type: none;
}
.help-content ul li {
font-weight: bold;
font-size: 18px;
}
.help-content ul ul li {
font-weight: normal;
font-size: 16px;
}
.help-content ul ul ul li {
color: #777777;
font-weight: normal;
font-size: 14px;
}
.help-content ul ul ul li a {
color: inherit;
}
.boot-hash-overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1200;
justify-content: center;
align-items: center;
opacity: 0;
visibility: hidden;
transition: opacity 0.2s ease, visibility 0.2s ease;
}
.boot-hash-card {
position: fixed;
top: 30%;
left: 50%;
transform: translate(-50%, -50%);
width: 80vw;
max-width: 600px;
background-color: #fff;
border-radius: 18px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
z-index: 1200;
padding: 20px;
display: none;
flex-direction: column;
gap: 15px;
opacity: 0;
transition: opacity 0.2s ease;
}
.boot-hash-overlay.show {
visibility: visible;
opacity: 1;
}
.boot-hash-card.show {
display: flex;
opacity: 1;
}
.input-box {
width: calc(100% - 20px);
height: 100px;
resize: none;
padding: 9px;
font-size: 16px;
background-color: #FFF;
border: 1px solid #ccc;
border-radius: 10px;
}
.button-container {
display: flex;
justify-content: flex-start;
}
.boot-hash-save-button {
padding: 10px 20px;
border: none;
border-radius: 38px;
font-size: 18px;
font-weight: bold;
background-color: #007bff;
color: white;
margin-left: auto;
transition: background-color 0.2s ease;
}
.about-overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1100;
display: none;
justify-content: center;
align-items: center;
opacity: 0;
transition: opacity 0.2s ease;
}
.about-menu {
position: fixed;
top: 50%;
left: 50%;
width: 75vw;
max-width: 800px;
transform: translate(-50%, -50%);
background: #fff;
border-radius: 8px;
padding: 25px 30px;
z-index: 1200;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
z-index: 1200;
opacity: 0;
display: flex;
flex-direction: column;
gap: 15px;
transition: opacity 0.2s ease;
}
.close-about {
position: absolute;
top: 15px;
right: 12px;
background: none;
border: none;
font-size: 18px;
color: #ccc;
}
.about-content p {
margin: 0;
font-size: 16px;
text-align: left;
}
.about-content p[data-i18n="module_name_line1"] {
font-size: 26px;
}
.about-content p[data-i18n="module_name_line2"] {
font-size: 22px;
}
.about-content p span[data-i18n="by"] {
font-size: 14px;
}
.about-content p[data-i18n="disclaimer"] {
font-style: italic;
}
.about-content p[data-i18n="acknowledgment"] {
font-weight: bold;
font-size: 18px;
}
.about-content p:not([data-i18n]) {
font-size: 16px;
}
.link{
flex: 0 1 auto;
}
#telegram {
font-size: 18px;
padding: 5px 10px;
background-color: #38A7ED;
color: #fff;
border-radius: 8px;
margin-right: 3px;
margin-bottom: 5px;
transition: background-color 0.2s ease;
}
#github {
font-size: 18px;
padding: 5px 10px;
background-color: #606060;
color: #fff;
border-radius: 8px;
margin-bottom: 5px;
transition: background-color 0.2s ease;
}
#link-text {
font-size: 17px;
font-weight: bold;
}
.search-menu-container {
display: flex;
position: fixed;
top: 40px;
height: 50px;
width: calc(100% - 20px);
max-width: 1100px;
z-index: 1000;
transition: transform 0.3s ease;
margin-left: auto;
margin-right: auto;
left: 0;
right: 0;
}
.search-card {
background-color: white;
border: 1px solid #ccc;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
display: flex;
align-items: center;
border-radius: 50px;
left: 0;
height: calc(100% - 2px);
width: calc(100% - 60px);
position: absolute;
}
.search-icon {
position: absolute;
padding-top: 5px;
left: 15px;
z-index: 1000;
}
.search-input {
position: absolute;
border: none;
font-size: 17px;
outline: none;
left: 10px;
padding: 0 30px;
width: calc(100% - 10);
}
.clear-btn {
position: absolute;
color: #ccc;
padding-bottom: 3px;
right: 10px;
border: none;
background: none;
font-size: 18px;
cursor: pointer;
display: none;
z-index: 10;
}
.menu {
display: flex;
right: 0;
position: absolute;
height: 100%;
}
.menu-toggle {
display: none;
}
#menu-button {
background-color: white;
border: 1px solid #ccc;
border-radius: 50%;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
width: 48px;
display: flex;
justify-content: center;
align-items: center;
}
.menu-icon {
display: inline-block;
transition: transform 0.2s ease;
}
.menu-icon.menu-open {
transform: rotate(90deg);
}
.menu-icon.menu-closed {
transform: rotate(0deg);
}
.menu-options {
background-color: white;
border: 1px solid #ccc;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
display: none;
position: absolute;
padding: 5px 12px;
top: 110%;
right: 0;
z-index: 1200;
transform: translateX(120%);
transition: transform 0.2s ease;
width: auto;
max-height: calc(100vh - 120px);
overflow-y: auto;
white-space: nowrap;
}
#select-denylist {
display: none;
}
.menu-options.visible {
display: block;
transform: translateX(0);
}
.menu-options.hidden {
transform: translateX(140%);
}
.menu-options ul {
list-style: none;
margin: 0;
padding: 0;
}
.menu-options li {
cursor: default;
padding: 12px 4px;
text-align: left;
border-bottom: 1px solid #ccc;
transition: background-color 0.2s ease;
}
.menu-options li:last-child {
border-bottom: none;
}
.menu-overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: none;
z-index: 1000;
display: none;
}
.card-box {
display: flex;
justify-content: center;
align-items: center;
}
#apps-list {
margin-top: 100px;
flex-direction: column;
}
.update-card {
display: none;
flex-direction: column;
justify-content: space-between;
align-items: center;
background-color: #DCDCDC;
border: none;
border-radius: 10px;
margin: 0 auto;
margin-bottom: 10px;
outline: none;
padding: 12px;
width: calc(100% - 30px);
max-width: 900px;
transition: background-color 0.2s ease;
}
#update-available {
font-size: 20px;
font-weight: bold;
margin-top: 15px;
margin-bottom: 0;
}
#redirect-to-release {
margin-top: 5px;
margin-bottom: 15px;
}
.card {
background-color: white;
border: none;
border-radius: 12px;
margin: 0 auto;
margin-bottom: 10px;
outline: none;
padding: 12px;
width: calc(100% - 30px);
max-width: 900px;
transition: background-color 0.2s ease;
}
.content {
display: flex;
justify-content: space-between;
align-items: center;
}
.name {
display: inline-block;
margin: 0;
font-size: 15.5px;
max-width: calc(100% - 30px);
overflow-wrap: break-word;
word-break: break-word;
}
.checkbox {
margin-left: auto;
transform: scale(1.15);
}
.prompt {
position: fixed;
bottom: 0;
left: 10px;
background-color: #4CAF50;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
color: white;
font-size: 15px;
padding: 5px 15px;
z-index: 2000;
transform: translateY(100%);
transition: transform 0.5s ease;
white-space: nowrap;
}
.prompt.visible {
animation: YbounceIn 0.4s forwards;
}
.prompt.hidden {
animation: YbounceOut 0.4s forwards;
}
.prompt.error {
background-color: #f44336;
}
@keyframes YbounceIn {
0% {
transform: translateY(100%);
}
50% {
transform: translateY(-80%);
}
100% {
transform: translateY(-60%);
}
}
@keyframes YbounceOut {
0% {
transform: translateY(-60%);
}
50% {
transform: translateY(-80%);
}
100% {
transform: translateY(100%);
}
}
.floating-card {
display: flex;
justify-content: center;
position: fixed;
bottom: -70px;
left: 50%;
transform: translateX(-50%);
z-index: 3;
}
.floating-btn {
flex-shrink: 0;
background-color: #007bff;
border: none;
box-shadow: 0 4px 8px #0003;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
padding: 10px 20px;
font-size: 20px;
font-weight: bold;
transition: transform 0.3s ease-in-out, background-color 0.2s ease;
border-top-left-radius: 50px;
border-top-right-radius: 50px;
border-bottom-left-radius: 50px;
border-bottom-right-radius: 50px;
}
.loading {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
color: #6E6E6E;
display: flex;
justify-content: center;
align-items: center;
font-size: 24px;
z-index: 1000;
}
.footer {
display: flex;
justify-content: space-between;
align-items: center;
width: calc(100% - 10px);
max-width: 1100px;
padding: 25px 0;
position: relative;
margin-left: auto;
margin-right: auto;
}
.uninstall-container {
padding: 10px 10px;
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
display: flex;
align-items: center;
border-radius: 8px;
background-color: #CE0000;
white-space: nowrap;
transition: background-color 0.2s ease;
}
.uninstall-container i {
margin-right: 5px;
}
.uninstall-container span {
font-size: 16px;
font-weight: bold;
color: #fff;
}
.uninstall-container.hidden-uninstall {
display: none;
}
.language-option:active,
.menu-options li:active,
.card:active,
.update-card:active {
background-color: #C8C8C8;
}
.boot-hash-save-button:active,
.floating-btn:active {
background-color: #003d80;
}
.uninstall-container:active {
background-color: #830000;
}
#telegram:active {
background-color: #1A78B3;
}
#github:active {
background-color: #4D4D4D;
}
@media (prefers-color-scheme: dark) {
body {
background-color: #121212;
color: #fff;
}
.header-block,
.header {
background-color: #121212;
}
.language-button,
.language-option,
.input-box,
.help-button {
color: #fff;
}
.help-menu,
.about-menu,
.boot-hash-card,
.card,
.search-input,
.search-card {
background-color: #343434;
}
.update-card {
background-color: #4D4D4D;
}
.search-card {
border: 1px solid #6E6E6E;
}
.search-input {
color: white;
}
.language-menu,
.input-box,
.menu-options,
#menu-button {
background-color: #343434;
border: 1px solid #6E6E6E;
}
.language-option,
.menu-options li {
border-bottom: 1px solid #6E6E6E;
}
.language-option:active,
.menu-options li:active,
.card:active,
.update-card:active {
background-color: #616161;
}
}

View File

@@ -0,0 +1,134 @@
.about-overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1100;
display: none;
justify-content: center;
align-items: center;
opacity: 0;
transition: opacity 0.2s ease;
}
.about-menu {
position: fixed;
top: 50%;
left: 50%;
width: 90vw;
max-width: 800px;
transform: translate(-50%, -50%);
background: #fff;
border-radius: 15px;
padding: 30px 0;
z-index: 1200;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
z-index: 1200;
opacity: 0;
display: flex;
flex-direction: column;
gap: 15px;
transition: opacity 0.2s ease;
}
.close-about {
position: absolute;
top: 15px;
right: 12px;
background: none;
border: none;
font-size: 18px;
color: #ccc;
}
.link,
.about-content p {
margin: 0;
padding: 0 30px;
font-size: 16px;
text-align: left;
}
#module_name_line1 {
font-size: 26px;
user-select: none;
}
#module_name_line2 {
font-size: 22px;
user-select: none;
}
#authored {
font-size: 14px;
user-select: none;
}
#disclaimer {
font-family: serif;
width: calc(100% - 80px);
padding: 8px 10px;
left: 0;
right: 0;
margin: auto;
border-radius: 10px;
background-color: #F5F5F5;
}
#acknowledgment {
font-weight: bold;
font-size: 18px;
user-select: none;
}
.link-icon {
display: inline-block;
font-style: normal;
border-radius: 8px;
box-sizing: border-box;
margin-bottom: 5px;
transition: background-color 0.2s ease;
position: relative;
overflow: hidden;
}
.link-icon svg {
padding-bottom: 3px;
vertical-align: bottom;
height: 17px;
}
#telegram {
font-size: 18px;
padding: 3px 10px;
background-color: #38A7ED;
color: #fff;
fill: #fff;
user-select: none;
}
#github {
font-size: 18px;
padding: 3px 10px;
background-color: #606060;
color: #fff;
fill: #fff;
user-select: none;
}
#link-text {
font-size: 17px;
font-weight: bold;
}
@media (prefers-color-scheme: dark) {
.about-menu {
background-color: #343434;
}
#disclaimer {
background-color: #6E6E6E;
}
}

View File

@@ -0,0 +1,370 @@
.card-box {
display: flex;
justify-content: center;
align-items: center;
}
#apps-list {
margin-top: 100px;
flex-direction: column;
}
.update-card {
display: none;
flex-direction: column;
justify-content: space-between;
align-items: center;
background-color: #DCDCDC;
border: none;
border-radius: 10px;
margin: 0 auto;
margin-bottom: 10px;
outline: none;
padding: 12px;
width: calc(100% - 30px);
max-width: 900px;
position: relative;
overflow: hidden;
}
#update-available {
font-size: 20px;
font-weight: bold;
text-align: center;
margin-top: 15px;
margin-bottom: 0;
user-select: none;
}
#redirect-to-release {
text-align: center;
margin-top: 5px;
margin-bottom: 15px;
user-select: none;
}
.update-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1800;
justify-content: center;
align-items: center;
opacity: 0;
transition: opacity 0.2s ease;
}
.update-menu {
position: relative;
width: 90vw;
max-width: 800px;
background-color: white;
padding: 10px 0;
border-radius: 15px;
text-align: left;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}
.close-update {
position: absolute;
top: 15px;
right: 12px;
background: none;
border: none;
font-size: 20px;
color: #ccc;
}
.update-content {
padding: 0 30px;
}
.update-content h3 {
font-size: 22px;
margin: 0;
}
.changelog {
max-height: 65vh;
overflow-y: auto;
}
.changelog ul {
padding-left: 0;
margin: 0;
}
.changelog ul li {
margin-left: 15px;
margin-bottom: 5px;
list-style-type: disc;
}
.changelog a {
color: #6E6E6E;
cursor: none;
}
.changelog a:active {
color: blue;
}
.update-button-container {
width: 100%;
padding-top: 10px;
display: flex;
justify-content: flex-end;
}
.install,
.reboot {
margin-top: 10px;
margin-bottom: 15px;
font-weight: bold;
color: #333;
background-color: #fff;
border: none;
padding: 10px 15px;
border: 1px solid #3B3B3B;
border-radius: 50px 50px;
font-size: 18px;
position: relative;
overflow: hidden;
user-select: none;
}
.reboot {
display: none;
color: #fff;
background-color: #007bff;
border: 1px solid #007bff;
}
.card {
background-color: #fff;
border: none;
border-radius: 12px;
margin: 0 auto;
margin-bottom: 10px;
outline: none;
padding: 13px;
width: calc(100% - 30px);
max-width: 900px;
transition: background-color 0.2s ease;
position: relative;
overflow: hidden;
}
.content {
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
}
.mode {
display: none;
flex-direction: row;
gap: 10px;
padding: 10px 12px;
position: absolute;
left: 0;
right: 0;
margin: auto;
width: fit-content;
background-color: #B1B1B1;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
border: 1px solid #ccc;
border-radius: 50px 50px;
opacity: 0;
transform: scale(0);
transition: all 0.4s cubic-bezier(0.22, 1, 0.36, 1);
z-index: 1200;
}
.mode.show {
opacity: 1;
transform: scale(1);
}
.mode-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: none;
z-index: 1100;
}
.mode-switch {
position: relative;
display: inline-block;
}
.mode-input {
opacity: 0;
position: absolute;
width: 0;
height: 0;
}
.status-indicator {
display: flex;
justify-content: center;
align-items: center;
width: 40px;
height: 40px;
border-radius: 50px 50px;
box-sizing: border-box;
transition: all 0.2s ease;
border: 3px solid transparent;
position: relative;
overflow: hidden;
}
#normal-indicator {
background-color: #007bff;
}
#hack-indicator {
background-color: #FF8400;
}
#generate-indicator {
background-color: #36DB2B;
}
.mode-input[type="radio"]:checked ~ .mode-icon .status-indicator {
transform: scale(1.1);
border-color: #fff;
}
.name {
display: inline-block;
margin: 0;
font-size: 15.5px;
max-width: calc(100% - 30px);
overflow-wrap: break-word;
word-break: break-word;
user-select: none;
}
.checkbox-wrapper {
position: relative;
display: inline-block;
width: 20px;
height: 20px;
margin-left: auto;
}
.checkbox {
opacity: 0;
position: absolute;
width: 0;
height: 0;
}
.custom-checkbox {
position: relative;
display: inline-block;
width: 100%;
height: 100%;
border: 2px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
transition: border-color 1s ease, transform 0.3s ease, background-color 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}
.tick-symbol {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(0);
opacity: 0;
transition: transform 0.2s ease-out, opacity 0.3s ease;
}
.checkbox:checked + .custom-checkbox {
border-color: #007bff;
background-color: #007bff;
transition: border-color 0.1s ease;
animation: checked-bounce 0.3s ease-out;
}
.checkbox-checked-generate:checked + .custom-checkbox {
border-color: #36DB2B;
background-color: #36DB2B;
}
.checkbox-checked-hack:checked + .custom-checkbox {
border-color: #FF8400;
background-color: #FF8400;
}
.checkbox:not(:checked) + .custom-checkbox {
animation: unchecked-bounce 0.3s ease-out;
}
.checkbox:checked + .custom-checkbox .tick-symbol {
transform: translate(-50%, -50%) scale(1);
opacity: 1;
}
@keyframes checked-bounce {
0% {
transform: scale(1);
}
50% {
transform: scale(0.8);
}
100% {
transform: scale(1);
}
}
@keyframes unchecked-bounce {
0% {
transform: scale(1);
}
50% {
transform: scale(0.8);
}
100% {
transform: scale(1);
}
}
@media (prefers-color-scheme: dark) {
.card {
background-color: #343434;
}
.update-card {
background-color: #4D4D4D;
}
.mode {
background-color: #343434;
border: 1px solid #6E6E6E;
}
.install {
color: #eee;
background-color: #343434;
border: 1px solid #C2C2C2;
}
.update-menu {
background-color: #343434;
}
.update-content a {
color: #C2C2C2;
}
}

View File

@@ -0,0 +1,103 @@
.boot-hash-overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1200;
justify-content: center;
align-items: center;
opacity: 0;
visibility: hidden;
transition: opacity 0.2s ease, visibility 0.2s ease;
}
.boot-hash-card {
position: fixed;
top: 30%;
left: 50%;
transform: translate(-50%, -50%);
width: 80vw;
max-width: 600px;
background-color: #fff;
border-radius: 18px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
z-index: 1200;
padding: 20px;
display: none;
flex-direction: column;
gap: 15px;
opacity: 0;
transition: opacity 0.2s ease;
}
.boot-hash-overlay.show {
visibility: visible;
opacity: 1;
}
.boot-hash-card.show {
display: flex;
opacity: 1;
}
.boot-hash-value {
width: 100%;
height: 100px;
font-size: 16px;
background-color: #FFF;
border: 1px solid #ccc;
border-radius: 10px;
box-sizing: border-box;
position: relative;
overflow: hidden;
}
.input-box {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
padding: 10px;
font-size: 16px;
border-radius: 9px;
box-sizing: border-box;
background-color: transparent;
outline-color: #007bff;
}
.button-container {
display: flex;
justify-content: flex-start;
}
.boot-hash-save-button {
padding: 10px 20px;
border: none;
border-radius: 38px;
font-size: 18px;
font-weight: bold;
background-color: #007bff;
color: white;
margin-left: auto;
position: relative;
overflow: hidden;
user-select: none;
}
@media (prefers-color-scheme: dark) {
.boot-hash-card {
background-color: #343434;
}
.input-box {
color: #fff;
}
.boot-hash-value {
background-color: #343434;
border: 1px solid #6E6E6E;
}
}

View File

@@ -0,0 +1,141 @@
body {
background-color: #F5F5F5;
padding-top: var(--window-inset-top);
padding-bottom: var(--window-inset-bottom);
}
.no-scroll {
overflow: hidden;
}
.floating-card {
display: flex;
justify-content: center;
position: fixed;
bottom: 50px;
left: 50%;
transform: translateX(-50%);
z-index: 10;
}
.floating-btn {
flex-shrink: 0;
background-color: #007bff;
border: none;
box-shadow: 0 4px 8px #0003;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
opacity: 0;
padding: 10px 20px;
font-size: 20px;
font-weight: bold;
transition: transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
border-radius: 50px 50px;
overflow: hidden;
user-select: none;
}
.prompt {
position: fixed;
bottom: 0;
left: 10px;
background-color: #4CAF50;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
color: white;
font-size: 15px;
padding: 5px 10px;
z-index: 2000;
width: auto;
max-width: calc(100% - 40px);
transform: translateY(100%);
transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}
.prompt.error {
background-color: #f44336;
}
.loading {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
color: #6E6E6E;
display: flex;
justify-content: center;
align-items: center;
font-size: 24px;
z-index: 1000;
}
.footer {
display: flex;
justify-content: space-between;
align-items: center;
width: calc(100% - 10px);
max-width: 1100px;
padding: 25px 0;
position: relative;
margin-left: auto;
margin-right: auto;
}
.uninstall-container {
padding: 10px 10px;
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
display: flex;
align-items: center;
border-radius: 8px;
background-color: #CE0000;
white-space: nowrap;
overflow: hidden;
user-select: none;
}
.uninstall-container i {
margin-right: 5px;
}
.uninstall-container span {
font-size: 16px;
font-weight: bold;
color: #fff;
}
.uninstall-container.hidden-uninstall {
display: none;
}
.ripple {
position: absolute;
border-radius: 50%;
transform: scale(0);
opacity: 1;
animation: ripple-animation ease-out forwards;
pointer-events: none;
background: rgba(0, 0, 0, 0.2);
}
.ripple.end {
opacity: 0;
}
@keyframes ripple-animation {
to {
transform: scale(3);
}
}
@media (prefers-color-scheme: dark) {
body {
background-color: #121212;
color: #fff;
}
}

View File

@@ -0,0 +1,249 @@
.header {
display: flex;
align-items: center;
justify-content: space-between;
position: fixed;
top: 0;
height: 40px;
width: calc(100% - 10px);
max-width: 1100px;
background-color: #F5F5F5;
transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
z-index: 1100;
margin-left: auto;
margin-right: auto;
left: 0;
right: 0;
user-select: none;
}
.header-block {
background-color: #F5F5F5;
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 1100;
transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
height: var(--window-inset-top);
}
#module-version,
#title {
padding-left: 5px;
font-size: 16.5px;
font-weight: bold;
}
.no-connection {
padding: 0;
display: none;
margin-right: 0px;
background: none;
border: none;
}
.language-dropdown {
position: relative;
display: inline-block;
}
.language-button {
padding-top: 5px;
background: none;
border: none;
}
.language-icon {
fill: #000;
}
.language-menu {
display: flex;
flex-direction: column;
position: absolute;
right: 0;
background-color: white;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
z-index: 1800;
border: 1px solid #ccc;
border-radius: 8px;
box-sizing: border-box;
opacity: 0;
max-height: calc(100vh - 50px);
overflow-y: auto;
transform: translateY(-30px) scale(0);
transform-origin: top right;
transition: all 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}
.language-menu.show {
opacity: 1;
transform: translateY(0) scale(1);
}
.language-option {
padding: 8px 10px;
text-align: left;
color: #333;
background-color: white;
border: none;
font-size: 16px;
width: 100%;
white-space: nowrap;
position: relative;
overflow: hidden;
user-select: none;
}
.language-option::after {
content: "";
position: absolute;
bottom: 0;
left: 10px;
width: calc(100% - 20px);
height: 1px;
background-color: #ccc;
}
.language-option:last-child::after {
content: none;
}
.language-option:first-child {
padding-top: 10px;
}
.language-option:last-child {
padding-bottom: 10px;
}
.language-overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: none;
z-index: 1100;
display: none;
}
.help-button {
padding-left: 5px;
margin-right: auto;
background: none;
border: none;
}
.help-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.5);
z-index: 2000;
justify-content: center;
align-items: center;
opacity: 0;
transition: opacity 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}
.help-overlay.show {
display: flex;
opacity: 1;
}
.help-overlay.hide {
opacity: 0;
}
.help-menu {
position: relative;
width: 90vw;
max-width: 800px;
background-color: white;
padding: 10px 0;
border-radius: 15px;
text-align: left;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}
.close-help {
position: absolute;
top: 15px;
right: 12px;
background: none;
border: none;
font-size: 20px;
color: #ccc;
}
.help-content {
max-height: 85vh;
padding: 0 30px;
overflow-y: auto;
}
.help-content p {
font-size: 26px;
user-select: none;
}
.help-content ul {
padding-left: 0;
list-style-type: none;
}
.help-content ul li {
font-weight: bold;
font-size: 18px;
}
.help-content ul ul li {
font-weight: normal;
font-size: 16px;
}
.help-content ul ul ul li {
color: #777777;
font-weight: normal;
font-size: 14px;
}
.help-content ul ul ul li a {
color: inherit;
}
@media (prefers-color-scheme: dark) {
.header-block,
.header {
background-color: #121212;
}
.help-button {
color: #fff;
}
.language-icon {
fill: #eee;
}
.language-option,
.help-menu {
color: #eee;
background-color: #343434;
}
.language-menu {
background-color: #343434;
border: 1px solid #6E6E6E;
}
.language-option::after {
background-color: #6E6E6E;
}
}

View File

@@ -0,0 +1,205 @@
.search-menu-container {
display: flex;
position: fixed;
top: 40px;
height: 50px;
width: calc(100% - 20px);
max-width: 1100px;
z-index: 1000;
transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
margin-left: auto;
margin-right: auto;
left: 0;
right: 0;
}
.search-card {
background-color: white;
border: 1px solid #ccc;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
display: flex;
align-items: center;
border-radius: 50px;
left: 0;
height: calc(100% - 2px);
width: calc(100% - 60px);
position: absolute;
overflow: hidden;
}
.search-icon {
position: absolute;
padding-top: 5px;
left: 15px;
z-index: 1000;
}
.search-input {
position: absolute;
border: none;
font-size: 17px;
outline: none;
left: 10px;
padding: 0 30px;
width: calc(100% - 10);
}
.clear-btn {
position: absolute;
color: #ccc;
padding-bottom: 3px;
right: 10px;
border: none;
background: none;
font-size: 18px;
cursor: pointer;
display: none;
z-index: 10;
}
.menu {
display: flex;
right: 0;
position: absolute;
height: 100%;
}
.menu-toggle {
display: none;
}
.menu-button {
background-color: white;
border: 1px solid #ccc;
border-radius: 50%;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
width: 48px;
display: flex;
justify-content: center;
z-index: 200;
align-items: center;
position: relative;
overflow: hidden;
}
.menu-icon {
display: inline-block;
fill: #000;
transform: rotate(0deg);
transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}
.menu-icon.menu-open {
transform: rotate(90deg);
}
.menu-options {
background-color: white;
border: 1px solid #ccc;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
box-sizing: border-box;
display: flex;
position: absolute;
top: 110%;
right: 0;
z-index: 1200;
width: auto;
max-height: calc(100vh - 120px);
overflow-y: auto;
white-space: nowrap;
visibility: hidden;
transform: translateX(120%);
transition: all 0.4s cubic-bezier(0.22, 1, 0.36, 1);
user-select: none;
}
#select-denylist {
display: none;
}
.menu-options.visible {
display: block;
visibility: visible;
transform: translateX(0);
}
.menu-options ul {
list-style: none;
margin: 0;
padding: 0;
}
.menu-options li {
padding: 12px 15px;
text-align: left;
background-color: white;
position: relative;
overflow: hidden;
}
.menu-options li::after {
content: "";
position: absolute;
bottom: 0;
left: 15px;
width: calc(100% - 30px);
height: 1px;
background-color: #ccc;
}
.menu-options li:last-child::after {
content: none;
}
.menu-options li:first-child {
padding-top: 16px;
}
.menu-options li:last-child {
padding-bottom: 16px;
}
.menu-overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: none;
z-index: 100;
display: none;
}
@media (prefers-color-scheme: dark) {
.menu-icon {
fill: #eee;
}
.search-input,
.search-card {
background-color: #343434;
}
.search-card {
border: 1px solid #6E6E6E;
}
.search-input {
color: white;
}
.menu-options,
#menu-button {
background-color: #343434;
border: 1px solid #6E6E6E;
}
.menu-options li {
background-color: #343434;
}
.menu-options li::after {
background-color: #6E6E6E
}
}

View File

@@ -1,7 +1,7 @@
{
"description": "Unnecessary app list",
"repo-link": "https://github.com/KOWX712/Tricky-Addon-Update-Target-List",
"json-link": "https://raw.githubusercontent.com/KOWX712/Tricky-Addon-Update-Target-List/main/more-excldue.json",
"json-link": "https://raw.githubusercontent.com/KOWX712/Tricky-Addon-Update-Target-List/main/more-exclude.json",
"data": [
{
"info": "Root manager",

View File

@@ -1,6 +1,6 @@
{
"versionCode": 280,
"version": "v2.8",
"zipUrl": "https://github.com/KOWX712/Tricky-Addon-Update-Target-List/releases/download/v2.8/TrickyAddonModule-v2.8.zip",
"versionCode": 330,
"version": "v3.3",
"zipUrl": "https://github.com/KOWX712/Tricky-Addon-Update-Target-List/releases/download/v3.3/TrickyAddonModule-v3.3.zip",
"changelog": "https://raw.githubusercontent.com/KOWX712/Tricky-Addon-Update-Target-List/main/changelog.md"
}