You've already forked Tricky-Addon-Update-Target-List
mirror of
https://github.com/KOWX712/Tricky-Addon-Update-Target-List.git
synced 2025-09-06 06:37:09 +00:00
Compare commits
69 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9a00b9540 | ||
|
|
ed77c72a79 | ||
|
|
b5370ad088 | ||
|
|
bb7573f5dd | ||
|
|
ccffd2eaed | ||
|
|
d9c1cdc11d | ||
|
|
e1f9c8904a | ||
|
|
3b98a88c77 | ||
|
|
0b8dcd4503 | ||
|
|
913118ac60 | ||
|
|
cb4276643a | ||
|
|
0087575117 | ||
|
|
5f01b4fed1 | ||
|
|
1f7a4c174b | ||
|
|
19f6b156f6 | ||
|
|
63874eeb8b | ||
|
|
2c5082acbc | ||
|
|
1cb810405a | ||
|
|
142dfdbe81 | ||
|
|
5ef6293924 | ||
|
|
a6b35e8d85 | ||
|
|
51747f261e | ||
|
|
aa7147d115 | ||
|
|
5e9463d8d2 | ||
|
|
d1313d743c | ||
|
|
2542e1a18a | ||
|
|
aa0837ec49 | ||
|
|
684b38afef | ||
|
|
e348fbdd6f | ||
|
|
bb167b760d | ||
|
|
8909a73b59 | ||
|
|
d8848fe5c3 | ||
|
|
2d2cdd7672 | ||
|
|
c1818b6ba2 | ||
|
|
c69337501a | ||
|
|
12a39ce60a | ||
|
|
ec330a4b0b | ||
|
|
7e2c185c5c | ||
|
|
f641f388ad | ||
|
|
f7aee9dc34 | ||
|
|
c19b93ffa3 | ||
|
|
2dc69abea4 | ||
|
|
75ff61e4d2 | ||
|
|
bb3b0abb30 | ||
|
|
502e6f6ea6 | ||
|
|
1d5428e683 | ||
|
|
fe64f37361 | ||
|
|
24548025fe | ||
|
|
d3885966b2 | ||
|
|
ae32ccf973 | ||
|
|
77a6e06631 | ||
|
|
940d809ac5 | ||
|
|
1861e43a81 | ||
|
|
10c02a6924 | ||
|
|
b07c9f8f5a | ||
|
|
dad4bed07e | ||
|
|
a7214cfa81 | ||
|
|
82c489f787 | ||
|
|
de43439200 | ||
|
|
5347ac29bf | ||
|
|
37467411cf | ||
|
|
4fd5b47817 | ||
|
|
610f106913 | ||
|
|
dc46e5f12e | ||
|
|
f46cc964e4 | ||
|
|
a7963c53dc | ||
|
|
7c5b144492 | ||
|
|
4230929dc9 | ||
|
|
437237e3d4 |
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -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
|
||||
26
.github/workflows/build.yml
vendored
26
.github/workflows/build.yml
vendored
@@ -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
31
.github/workflows/json-lint.yml
vendored
Normal 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
|
||||
29
README.md
29
README.md
@@ -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
|
||||
[](https://github.com/KOWX712/Tricky-Addon-Update-Target-List/releases)
|
||||
|
||||
35
changelog.md
35
changelog.md
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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."
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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">✕</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">✕</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">✕</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">✕</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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
86
module/webui/locales/es-ES.json
Normal file
86
module/webui/locales/es-ES.json
Normal 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"
|
||||
}
|
||||
}
|
||||
@@ -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": "再起動に失敗しました。手動で再起動してください。"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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": "Не удалось перезагрузить, перезагрузите вручную"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
86
module/webui/locales/tr-TR.json
Normal file
86
module/webui/locales/tr-TR.json
Normal 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 Magisk’te 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 Demo’dan verifiedBootHash değerini alın. Abnormal boot durumunu ro.boot.vbmeta.digest’i 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 Hash’inizi 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 Store’a 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"
|
||||
}
|
||||
}
|
||||
@@ -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": "重启失败,请手动重启"
|
||||
}
|
||||
}
|
||||
@@ -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": "重啟失敗,請手動重啟"
|
||||
}
|
||||
}
|
||||
46
module/webui/scripts/about.js
Normal file
46
module/webui/scripts/about.js
Normal 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');
|
||||
});
|
||||
262
module/webui/scripts/applist.js
Normal file
262
module/webui/scripts/applist.js
Normal 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");
|
||||
}
|
||||
});
|
||||
}
|
||||
31
module/webui/scripts/help.js
Normal file
31
module/webui/scripts/help.js
Normal 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();
|
||||
}
|
||||
});
|
||||
136
module/webui/scripts/language.js
Normal file
136
module/webui/scripts/language.js
Normal 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);
|
||||
});
|
||||
}
|
||||
323
module/webui/scripts/main.js
Normal file
323
module/webui/scripts/main.js
Normal 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
6
module/webui/scripts/marked.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
109
module/webui/scripts/menu_option.js
Normal file
109
module/webui/scripts/menu_option.js
Normal 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);
|
||||
}
|
||||
});
|
||||
84
module/webui/scripts/search_menu.js
Normal file
84
module/webui/scripts/search_menu.js
Normal 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';
|
||||
}
|
||||
}
|
||||
113
module/webui/scripts/update.js
Normal file
113
module/webui/scripts/update.js
Normal 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
54
module/webui/scripts/vbmeta-digest.js
Normal file
54
module/webui/scripts/vbmeta-digest.js
Normal 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();
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
134
module/webui/styles/about.css
Normal file
134
module/webui/styles/about.css
Normal 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;
|
||||
}
|
||||
}
|
||||
370
module/webui/styles/applist.css
Normal file
370
module/webui/styles/applist.css
Normal 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;
|
||||
}
|
||||
}
|
||||
103
module/webui/styles/boot-hash.css
Normal file
103
module/webui/styles/boot-hash.css
Normal 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;
|
||||
}
|
||||
}
|
||||
141
module/webui/styles/global.css
Normal file
141
module/webui/styles/global.css
Normal 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;
|
||||
}
|
||||
}
|
||||
249
module/webui/styles/header.css
Normal file
249
module/webui/styles/header.css
Normal 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;
|
||||
}
|
||||
}
|
||||
205
module/webui/styles/search_menu.css
Normal file
205
module/webui/styles/search_menu.css
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
@@ -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"
|
||||
}
|
||||
Reference in New Issue
Block a user