Compare commits

7 Commits

Author SHA1 Message Date
KOWX712
cfa17eea26 v2.6-beta.1 pre-release 2024-11-24 22:36:45 +08:00
KOWX712
dbed7f9f90 Fix an issue that may happen in Apatch
and update translation guide
2024-11-24 22:25:27 +08:00
KOWX712
5352e8604e Add translation guide and translation template 2024-11-24 21:47:48 +08:00
KOWX712
ff18585543 Initial support for multiple language, add save boot hash in WebUI
Language available: en-US, ru-RU, tl-PH, zh-CN, zh-TW. Add feature to save verifiedBootHash in WebUI
2024-11-24 21:46:49 +08:00
KOWX712
b50e9aa086 Optimize loading time x2
Reduce load time by preload skiplist in service.sh.
2024-11-24 20:33:09 +08:00
KOWX712
213c4a2f13 Optimize + set verified boot hash in WebUI
Reduce load time by preload applist in service.sh. New function in WebUI: set verified boot hash in WebUI
2024-11-23 21:22:23 +08:00
KOWX712
f4860d1a00 Improve xposed detection logic
Detect Xposed module from AndroidMainfest.xml instead of relying on LSPosed module repository since not all Xposed module will available in the repo.
2024-11-22 17:02:30 +08:00
18 changed files with 967 additions and 167 deletions

View File

@@ -28,9 +28,8 @@ A **KSU WebUI** to configure tricky store target.txt
- Run `UpdateTargetList.sh` under `/data/adb/tricky_store` manually.
- MT manager is recommened for this method
## More
**Support to pass abnormal boot state**
- Paste Verfied Boot Hash to `boot_hash` in `/data/adb/`, reboot.
## Translation
- Read [Translation Guide](https://github.com/KOWX712/Tricky-Addon-Update-Target-List/tree/master/module/webroot/locales/A-translate.md)
## Acknowledgement
- [j-hc/zygisk-detach](https://github.com/j-hc/zygisk-detach) - KSU WebUI template

View File

@@ -3,15 +3,14 @@ A **KSU WebUI** to configure tricky store target.txt
Requirement: Tricky Store module installed
Manually add VerifiedBootHash to /data/adb/boot_hash (optional)
GitHub release: [Tricky Addon: Update Target List](https://github.com/KOWX712/Tricky-Addon-Update-Target-List/releases/latest)
Telegram channel: [KOW's Little World](https://t.me/kowchannel)
Starting from v2.4, WebUI will take longer time to load due to reading app name from every base.apk. The more apps installed, the longer time it takes to load the WebUI.
## Changelog
### v2.6-beta.1
- Check in (release notes)[https://github.com/KOWX712/Tricky-Addon-Update-Target-List/releases/tag/v2.6-beta.1].
### v2.5
- Remove kb prompt on installation, moved into WebUI
- Restore to AOSP keybox during uninstallation

View File

@@ -1,6 +1,7 @@
#!/system/bin/sh
MODPATH=${0%/*}
SKIPLIST="$MODPATH/skiplist"
OUTPUT="$MODPATH/exclude-list"
KBOUTPUT="$MODPATH/.extra"
@@ -9,24 +10,31 @@ KBOUTPUT="$MODPATH/.extra"
find_busybox
check_wget
# Fetch Xposed module package names
wget --no-check-certificate -q -O - "https://modules.lsposed.org/modules.json" 2>/dev/null | \
grep -o '"name":"[^"]*","description":' | \
awk -F'"' '{print $4}' > "$OUTPUT"
# Fetch additional package names
wget --no-check-certificate -q -O - "https://raw.githubusercontent.com/KOWX712/Tricky-Addon-Update-Target-List/master/more-excldue.json" 2>/dev/null | \
grep -o '"package-name": *"[^"]*"' | \
awk -F'"' '{print $4}' >> "$OUTPUT"
awk -F'"' '{print $4}' > "$OUTPUT"
if [ ! -s "$OUTPUT" ]; then
echo "Error: Failed to fetch data." > "$OUTPUT"
rm -f "$KBOUTPUT"
exit 1
skipkb=true
fi
wget --no-check-certificate -qO "$KBOUTPUT" "https://raw.githubusercontent.com/KOWX712/Tricky-Addon-Update-Target-List/master/.extra"
# Find xposed package name
pm list packages -3 </dev/null 2>&1 | cat | awk -F: '{print $2}' | while read -r PACKAGE; do
if ! grep -Fq "$PACKAGE" "$SKIPLIST"; then
pm path "$PACKAGE" | grep "base.apk" | awk -F: '{print $2}' | tr -d '\r' | while read -r APK_PATH; do
aapt dump xmltree "$APK_PATH" AndroidManifest.xml 2>/dev/null | grep -qE "xposed.category|xposeddescription" && echo "$PACKAGE" >> "$OUTPUT"
done
fi
done
if [ ! -s "$KBOUTPUT" ]; then
rm -f "$KBOUTPUT"
if [ "$skipkb" != "true" ]; then
wget --no-check-certificate -qO "$KBOUTPUT" "https://raw.githubusercontent.com/KOWX712/Tricky-Addon-Update-Target-List/master/.extra"
if [ ! -s "$KBOUTPUT" ]; then
rm -f "$KBOUTPUT"
fi
else
exit 1
fi

View File

@@ -26,6 +26,8 @@ check_wget() {
fi
}
aapt() { "$MODPATH/aapt" "$@"; }
download_webui() {
wget --no-check-certificate -P "$APK_DIR" "$URL"
if [ $? -ne 0 ]; then

View File

@@ -17,7 +17,6 @@ initialize() {
abort
}
mkdir -p "$MODPATH/system/bin"
mv "$MODPATH/bin/$(getprop ro.product.cpu.abi)/aapt" "$COMPATH/aapt"
rm -rf "$MODPATH/bin"

View File

@@ -1,6 +1,6 @@
id=TA_utl
name=Tricky Addon - Update Target List
version=v2.5
version=v2.6-beta.1
versionCode=250
author=KOWX712
description=A WebUI to conifgure tricky store target.txt

View File

@@ -1,8 +1,12 @@
MODPATH=${0%/*}
OUTPUT_APP="$MODPATH/common/applist"
OUTPUT_SKIP="$MODPATH/common/skiplist"
TS="/data/adb/modules/tricky_store"
SCRIPT_DIR="/data/adb/tricky_store"
TSPA="/data/adb/modules/tsupport-advance"
aapt() { "$MODPATH/common/aapt" "$@"; }
hash_value=$(grep -v '^#' "/data/adb/boot_hash" | tr -d '[:space:]')
if [ -n "$hash_value" ]; then
resetprop -n ro.boot.vbmeta.digest "$hash_value"
@@ -35,5 +39,19 @@ else
until [ "$(getprop sys.boot_completed)" = "1" ]; do
sleep 1
done
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')
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")
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
echo "$PACKAGE" >> "$OUTPUT_SKIP"
fi
done
. "$SCRIPT_DIR/UpdateTargetList.sh"
fi

View File

@@ -1,86 +1,51 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<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="/styles.css" type="text/css">
<script type="module" crossorigin src="/index.js"></script>
</head>
<body>
<div class="title-container">
<div id="title">Tricky Addon - Update Target List</div>
<!-- Header -->
<div class="header">
<div id="title" data-i18n="title">Tricky Addon - Update Target List</div>
<button id="help-button" class="help-button"><i class="fa fa-question-circle"></i></button>
<div class="no-connection">
<img src="wifi-slash.svg" alt="No Connection Icon" class="wifi-icon">
</div>
</div>
<div id="help-overlay" class="help-overlay">
<div class="help-menu">
<button id="close-help" class="close-help">&#x2715;</button>
<div class="help-content">
<p>Instructions</p>
<ul>
<li>Save and Update
<ul>
<li>Save the current configuration and update target.txt immediately.</li>
<li><br></li>
</ul>
</li>
<li>Refresh
<ul>
<li>Refresh app list and exclude list.</li>
<li><br></li>
</ul>
</li>
<li>Select All & Deselect All
<ul>
<li>Select or deselect all apps in the current interface.</li>
<li><br></li>
</ul>
</li>
<li>Select From DenyList
<ul>
<li>Available in Magisk only, select apps that in the DenyList. Recommended.</li>
<li><br></li>
</ul>
</li>
<li>Deselect Unnecessary
<ul>
<li>Unnecessary category: Xposed module, root manager, root-related apps, and general apps
that never check bootloader status. This option requrie Internet connection.</li>
<ul>
<li>Contribute to <a
href="https://raw.githubusercontent.com/KOWX712/Tricky-Addon-Update-Target-List/master/more-excldue.json"
target="_blank">unnecessary app list</a>? Create an <a
href="https://github.com/KOWX712/Tricky-Addon-Update-Target-List/issues"
target="_blank">issue</a> or <a
href="https://github.com/KOWX712/Tricky-Addon-Update-Target-List/pulls"
target="_blank">pull request</a>.</li>
</ul>
<li><br></li>
</ul>
</li>
<li>Set AOSP & Valid Keybox
<ul>
<li>Replace tricky store keybox.xml<br><strong>AOSP keybox:</strong> Device
Integrity.<br><strong>Valid keybox:</strong> Strong Integrity, fallback to Basic
Integrity if revoked.<br>AOSP keybox will be replaced if there's no more valid keybox.
Valid keybox option require Internet connection.</li>
<li><br></li>
</ul>
</li>
</ul>
<div class="language-dropdown">
<button class="language-button">
<i class="fa fa-compass"></i>
</button>
<div class="language-menu">
<button class="language-option" data-lang="en-US" data-i18n="language_english_us">English</button>
<button class="language-option" data-lang="ru-RU" data-i18n="language_russian">Русский</button>
<button class="language-option" data-lang="tl-PH" data-i18n="language_tagalog">Tagalog</button>
<button class="language-option" data-lang="zh-CN"data-i18n="language_chinese_simplified">中文(简体)</button>
<button class="language-option" data-lang="zh-TW"data-i18n="language_chinese_traditional">中文(繁体)</button>
</div>
</div>
</div>
<!-- Loading Element -->
<div class="loading" data-i18n="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>
</div>
<!-- Menu Options -->
<div class="search-menu-container">
<div class="search-card">
<span class="search-icon"><i class="fa fa-search"></i></span>
<input type="text" class="search-input" id="search" placeholder="Search">
<input type="text" class="search-input" id="search" placeholder="Search" data-i18n="search_placeholder">
<button class="clear-btn" id="clear-btn">&#x2715;</button>
</div>
<div class="menu">
@@ -90,21 +55,22 @@
</label>
<div id="menu-options" class="menu-options">
<ul>
<li id="refresh">Refresh</li>
<li id="select-all">Select All</li>
<li id="deselect-all">Deselect All</li>
<li id="select-denylist">Select From DenyList</li>
<li id="deselect-unnecessary">Deselect Unnecessary</li>
<li id="aospkb">Set AOSP Keybox</li>
<li id="extrakb">Set Valid Keybox</li>
<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>
</ul>
</div>
</div>
</div>
<!-- Applist Display -->
<div id="apps-list"></div>
<div class="floating-card">
<button class="floating-btn" id="save">Save and Update</button>
</div>
<template id="app-template">
<div class="card" onclick="toggleCheckbox(event)">
<div class="content" data-package="">
@@ -113,9 +79,53 @@
</div>
</div>
</template>
<div class="loading">Loading...</div>
<div class="acknowledgment">Credit to j-hc/zygisk-detach WebUI</div>
<div id="prompt" class="prompt"></div>
</body>
<!-- Help Overlay -->
<div id="help-overlay" class="help-overlay">
<div class="help-menu">
<button id="close-help" class="close-help">&#x2715;</button>
<div class="help-content">
<p data-i18n="help_instructions"></p>
<ul id="help-list"></ul>
</div>
</div>
</div>
<!-- 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="button-container">
<button id="boot-hash-save-button" class="boot-hash-save-button" data-i18n="boot_hash_save_button"></button>
</div>
</div>
<!-- About Overlay -->
<div id="about-overlay" class="about-overlay">
<div id="about-menu" class="about-menu">
<button id="close-about" class="close-about">&#x2715;</button>
<div class="about-content">
<p data-i18n="module_name_line1">Module Name Line 1</p>
<p data-i18n="module_name_line2">Module Name Line 2</p>
<p><span data-i18n="by">by </span>KOWX712</p>
<br>
<p>
<span data-i18n="telegram_channel"></span>:
<span>t.me/kowchannel</span>
</p>
<p>
<span data-i18n="github">GitHub</span>:
<span>github.com/KOWX712/Tricky-Addon-Update-Target-List</span>
</p>
<br>
<p data-i18n="acknowledgment">Acknowledgment</p>
<p>j-hc/zygisk-detach: WebUI template</p>
</div>
</div>
</div>
<!-- Footer -->
<div class="footer"></div>
</body>
</html>

View File

@@ -1,22 +1,132 @@
let e = 0;
const appTemplate = document.getElementById("app-template").content;
const appListContainer = document.getElementById("apps-list");
const loadingIndicator = document.querySelector(".loading");
// Header Elements
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');
// Loading and Prompt Elements
const loadingIndicator = document.querySelector('.loading');
const prompt = document.getElementById('prompt');
// Floating Button
const floatingBtn = document.querySelector('.floating-btn');
// Search and Menu Elements
const searchInput = document.getElementById('search');
const clearBtn = document.getElementById('clear-btn');
const searchMenuContainer = document.querySelector('.search-menu-container');
const searchInput = document.getElementById("search");
const clearBtn = document.getElementById("clear-btn");
const title = document.querySelector('.title-container');
const noConnection = document.querySelector(".no-connection");
const helpButton = document.getElementById("help-button");
const helpOverlay = document.getElementById("help-overlay");
const closeHelp = document.getElementById("close-help");
const searchCard = document.querySelector('.search-card');
const menu = document.querySelector('.menu');
const selectDenylistElement = document.getElementById("select-denylist");
const floatingBtn = document.querySelector('.floating-btn');
const menuButton = document.getElementById('menu-button');
const menuOptions = document.getElementById('menu-options');
const selectDenylistElement = document.getElementById('select-denylist');
// Applist Elements
const appTemplate = document.getElementById('app-template').content;
const appListContainer = document.getElementById('apps-list');
// Help Overlay Elements
const helpOverlay = document.getElementById('help-overlay');
const closeHelp = document.getElementById('close-help');
const helpList = document.getElementById('help-list');
// 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');
const basePath = "set-path";
// Variables
let e = 0;
let excludeList = [];
let isRefreshing = false;
let translations = {};
let currentLang = 'en-US';
// Function to detect user's default language
function detectUserLanguage() {
const userLang = navigator.language || navigator.userLanguage;
const langCode = userLang.split('-')[0];
const availableLanguages = ['en-US', 'ru-RU', 'tl-PH', 'zh-CN', 'zh-TW'];
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 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 execute shell commands
async function execCommand(command) {
@@ -60,28 +170,54 @@ function isExcluded(appName) {
async function fetchAppList() {
try {
await readExcludeFile();
const result = await execCommand(`
pm list packages -3 | while read -r line; do
PACKAGE=$(echo "$line" | awk -F: '{print $2}')
APK_PATH=$(pm path "$PACKAGE" | grep "base.apk" | awk -F: '{print $2}' | tr -d '\\r')
if [ -n "$APK_PATH" ]; then
APP_NAME=$( ${basePath}aapt dump badging "$APK_PATH" 2>/dev/null | grep "application-label:" | sed "s/application-label://; s/'//g" )
echo "$APP_NAME|$PACKAGE"
else
echo "Unknown App|$PACKAGE"
fi
done
`);
const appEntries = result.split("\n").map(line => {
const [appName, packageName] = line.split("|").map(item => item.trim());
return { appName, packageName };
}).filter(entry => entry.packageName); // Remove invalid entries
let applistMap = {};
try {
const applistResult = await execCommand(`cat ${basePath}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}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";
}
}
}
const sortedApps = appEntries.sort((a, b) => {
const aInExclude = isExcluded(a.packageName);
const bInExclude = isExcluded(b.packageName);
return aInExclude === bInExclude ? a.appName.localeCompare(b.appName) : aInExclude ? 1 : -1;
});
appListContainer.innerHTML = ""; // Clear the container before rendering
appListContainer.innerHTML = "";
sortedApps.forEach(({ appName, packageName }) => {
const appElement = document.importNode(appTemplate, true);
const contentElement = appElement.querySelector(".content");
@@ -96,7 +232,6 @@ async function fetchAppList() {
} catch (error) {
console.error("Failed to fetch or render app list with names:", error);
}
floatingBtn.style.transform = "translateY(-100px)";
}
@@ -147,7 +282,7 @@ async function runExtraScript() {
noConnection.style.display = "none";
} catch (error) {
console.error("Failed to execute Extra script:", error);
showPrompt("Please check your Internet connection", false);
showPrompt("no_internet", false);
noConnection.style.display = "flex";
}
}
@@ -226,10 +361,10 @@ async function aospkb() {
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 keybox set successfully.");
showPrompt("aosp_key_set");
} catch (error) {
console.error("Failed to copy AOSP keybox:", error);
showPrompt("Failed to update keybox.", false);
showPrompt("key_set_error", false);
}
}
@@ -244,17 +379,99 @@ async function extrakb() {
}
await execCommand(`xxd -r -p ${sourcePath} | base64 -d > ${destinationPath}`);
console.log("Valid keybox copied successfully.");
showPrompt("Valid keybox set successfully.");
showPrompt("valid_key_set");
} catch (error) {
console.error("Failed to copy valid keybox:", error);
await aospkb();
showPrompt("No valid keybox found, replaced with AOSP keybox.", false);
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 menu = document.getElementById('about-menu');
const closeAbout = document.getElementById('close-about');
const showMenu = () => {
aboutOverlay.style.display = 'flex';
setTimeout(() => {
aboutOverlay.style.opacity = '1';
menu.style.opacity = '1';
}, 10);
document.body.style.overflow = 'hidden';
};
const hideMenu = () => {
aboutOverlay.style.opacity = '0';
menu.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 (!menu.contains(event.target)) {
hideMenu();
}
});
menu.addEventListener('click', (event) => event.stopPropagation());
}
// Function to show the prompt with a success or error message
function showPrompt(message, isSuccess = true) {
const prompt = document.getElementById('prompt');
function showPrompt(key, isSuccess = true) {
const message = translations[key] || key;
prompt.textContent = message;
prompt.classList.toggle('error', !isSuccess);
if (window.promptTimeout) {
@@ -272,9 +489,7 @@ function showPrompt(message, isSuccess = true) {
// Function to toggle menu option
function setupMenuToggle() {
const menuButton = document.getElementById('menu-button');
const menuIcon = menuButton.querySelector('.menu-icon');
const menuOptions = document.getElementById('menu-options');
let menuOpen = false;
let menuAnimating = false;
@@ -300,7 +515,7 @@ function setupMenuToggle() {
}
});
const closeMenuItems = ['refresh', 'select-all', 'deselect-all', 'select-denylist', 'deselect-unnecessary', 'aospkb', 'extrakb'];
const closeMenuItems = ['refresh', 'select-all', 'deselect-all', 'select-denylist', 'deselect-unnecessary', 'aospkb', 'extrakb', 'boot-hash', 'about'];
closeMenuItems.forEach(id => {
const item = document.getElementById(id);
if (item) {
@@ -404,14 +619,14 @@ document.getElementById("save").addEventListener("click", async () => {
// Execute UpdateTargetList.sh
try {
await execCommand("/data/adb/tricky_store/UpdateTargetList.sh");
showPrompt("Config and target.txt updated");
showPrompt("saved_and_updated");
} catch (error) {
console.error("Failed to update target list:", error);
showPrompt("Config saved, but failed to update target list", false);
showPrompt("saved_not_updated", false);
}
} catch (error) {
console.error("Failed to update EXCLUDE file:", error);
showPrompt("Failed to save config", false);
showPrompt("save_error", false);
}
await readExcludeFile();
await refreshAppList();
@@ -419,6 +634,8 @@ document.getElementById("save").addEventListener("click", async () => {
// Initial load
document.addEventListener('DOMContentLoaded', async () => {
const userLang = detectUserLanguage();
await loadTranslations(userLang);
setupMenuToggle();
document.getElementById("refresh").addEventListener("click", refreshAppList);
document.getElementById("select-all").addEventListener("click", selectAllApps);
@@ -427,10 +644,33 @@ document.addEventListener('DOMContentLoaded', async () => {
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();
runExtraScript();
loadingIndicator.style.display = "none";
runExtraScript();
});
// Toggle the visibility of the language menu when clicking the button
languageButton.addEventListener("click", (event) => {
event.stopPropagation();
const isVisible = languageMenu.classList.contains("show");
if (isVisible) {
languageMenu.classList.remove("show");
} else {
languageMenu.classList.add("show");
}
});
document.addEventListener("click", (event) => {
if (!languageButton.contains(event.target) && !languageMenu.contains(event.target)) {
languageMenu.classList.remove("show");
}
});
languageOptions.forEach(option => {
option.addEventListener("click", () => {
languageMenu.classList.remove("show");
});
});
// Scroll event
@@ -447,6 +687,9 @@ window.addEventListener('scroll', () => {
searchMenuContainer.style.transform = 'translateY(0)';
floatingBtn.style.transform = 'translateY(-100px)';
}
if (languageMenu.classList.contains("show")) {
languageMenu.classList.remove("show");
}
lastScrollY = window.scrollY;
});

View File

@@ -0,0 +1,47 @@
{
"title": "Tricky Addon - Update Target List",
"search_placeholder": "Search",
"save_and_update_button": "Save and Update",
"boot_hash_save_button": "Save",
"loading": "Loading...",
"boot_hash_input_placeholder": "Paste your verified Boot Hash here",
"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 the current configuration and update target.txt immediately.",
"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",
"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_and_updated": "Config and target.txt updated",
"saved_not_updated": "Config saved, but failed to update target list",
"save_error": "Failed to save config"
}

View File

@@ -0,0 +1,34 @@
# Translation Guide
## Fix Translation Error
1. Fork this repository.
2. Find your language string file in `/module/webroot/locales/`.
3. Edit the string value with translated incorrectly.
4. Create a Pull Request.
---
## Create a New Language
### Simple
- Contact me in Telegram to create a new translation langauge for you.
### Advanced
1. Fork this repository.
2. Rename `/module/webroot/locales/A-translate.json` to `language_code-COUNTRY_CODE.json`, e.g., `en-US.json`.
3. Translate the string value inside.
4. Add `langauge-option` into `/module/webroot/index.html`.
```xml
<button class="language-option" data-lang="language_code-COUNTRY_CODE" data-i18n="language_languageName">languageName</button>
```
Format:
```xml
<div class="language-menu">
<button class="language-option" data-lang="en-US" data-i18n="language_english_us">English</button>
</div>
```
5. Add language_code-COUNTRY_CODE in `/module/webroot/index.js` under `function detectUserLanguage()`
Format:
```js
function detectUserLanguage() {
const availableLanguages = ['en-US', 'ru-RU', 'tl-PH', 'zh-CN', 'zh-TW'];
}
```
6. Create a Pull Request

View File

@@ -0,0 +1,47 @@
{
"title": "Tricky Addon - Update Target List",
"search_placeholder": "Search",
"save_and_update_button": "Save and Update",
"boot_hash_save_button": "Save",
"loading": "Loading...",
"boot_hash_input_placeholder": "Paste your verified Boot Hash here",
"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 the current configuration and update target.txt immediately.",
"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",
"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_and_updated": "Config and target.txt updated",
"saved_not_updated": "Config saved, but failed to update target list",
"save_error": "Failed to save config"
}

View File

@@ -0,0 +1,47 @@
{
"title": "Tricky Addon - Обновить список целей",
"search_placeholder": "Поиск",
"save_and_update_button": "Сохранить и обновить",
"boot_hash_save_button": "Сохранить",
"loading": "Загрузка...",
"boot_hash_input_placeholder": "Вставьте свой проверенный Boot Hash сюда",
"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",
"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_and_updated": "Конфигурация и target.txt обновлены",
"saved_not_updated": "Конфигурация сохранена, но не удалось обновить список целей",
"save_error": "Не удалось сохранить конфигурацию"
}

View File

@@ -0,0 +1,47 @@
{
"title": "Tricky Addon - I-update ang Target List",
"search_placeholder": "Maghanap",
"save_and_update_button": "I-save at I-update",
"boot_hash_save_button": "I-save",
"loading": "Naglo-load...",
"boot_hash_input_placeholder": "I-paste ang iyong verified Boot Hash dito",
"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 at i-update ang target.txt agad.",
"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",
"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_and_updated": "Na-save ang config at na-update ang target.txt",
"saved_not_updated": "Na-save ang config, ngunit nabigong i-update ang target list",
"save_error": "Nabigong i-save ang config"
}

View File

@@ -0,0 +1,47 @@
{
"title": "TS插件 - 更新目标列表",
"search_placeholder": "搜索",
"save_and_update_button": "保存并更新",
"boot_hash_save_button": "保存",
"loading": "加载中...",
"boot_hash_input_placeholder": "在此粘贴您的哈希值",
"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",
"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_and_updated": "成功保存配置和更新目标列表",
"saved_not_updated": "配置已保存,但更新目标列表失败",
"save_error": "保存配置失败"
}

View File

@@ -0,0 +1,47 @@
{
"title": "TS插件 - 更新目標列表",
"search_placeholder": "搜尋",
"save_and_update_button": "保存並更新",
"boot_hash_save_button": "保存",
"loading": "加載中...",
"boot_hash_input_placeholder": "在此粘貼您的哈希值",
"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",
"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_and_updated": "成功保存配置和更新目標列表",
"saved_not_updated": "配置已保存,但更新目標列表失敗",
"save_error": "保存配置失敗"
}

View File

@@ -6,7 +6,7 @@ body {
overflow: hidden;
}
.title-container {
.header {
display: flex;
align-items: center;
justify-content: space-between;
@@ -16,13 +16,12 @@ body {
width: calc(100% - 17px);
background-color: #F5F5F5;
transition: transform 0.3s ease;
z-index: 1000;
z-index: 1100;
}
#title {
font-size: 18px;
font-weight: bold;
padding-left: 10px;
}
.no-connection {
@@ -32,18 +31,68 @@ body {
}
.no-connection .wifi-icon {
width: 20px;
height: 20px;
margin-right: 5px;
width: 18px;
height: 18px;
margin-right: 0;
filter: invert(0.6) sepia(0) saturate(0) hue-rotate(180deg) brightness(0.8) contrast(1);
}
.language-dropdown {
position: relative;
display: inline-block;
}
.language-button {
background: none;
border: none;
font-size: 24px;
color: #333;
margin-left: 10px;
}
.language-menu {
display: flex;
padding: 5px;
flex-direction: column;
position: absolute;
right: 0;
background-color: #fff;
min-width: 120px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
z-index: 2000;
border: 1px solid #ccc;
border-radius: 8px;
opacity: 0;
visibility: hidden;
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: 10px;
text-align: left;
background: none;
border: none;
width: 100%;
font-size: 16px;
color: #333;
width: auto;
white-space: nowrap;
}
.help-button {
margin-right: auto;
padding: 0 10px;
padding: 0 7px;
background: none;
border: none;
font-size: 24px;
font-size: 23px;
align-items: center;
justify-content: center;
}
@@ -128,6 +177,146 @@ body {
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;
background-color: #007bff;
color: white;
margin-left: auto;
}
.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%;
transform: translate(-50%, -50%);
background: #fff;
border-radius: 8px;
padding: 25px 30px;
padding-right: 50px;
z-index: 1200;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
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="acknowledgment"] {
font-size: 18px;
text-align: left;
}
.about-content p:not([data-i18n]) {
font-size: 16px;
}
#apps-list {
margin-top: 100px;
}
@@ -257,7 +446,7 @@ body {
.menu-options li {
cursor: default;
padding: 15px 12px;
padding: 13px 12px;
text-align: left;
}
@@ -372,19 +561,21 @@ body {
}
.loading {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
color: #6E6E6E;
display: flex;
justify-content: center;
align-items: center;
height: calc(100vh - 164px);
font-size: 24px;
z-index: 1000;
}
.acknowledgment {
color: #6E6E6E;
padding: 20px;
text-align: center;
font-size: 14px;
.footer {
padding: 25px;
}
@media (prefers-color-scheme: dark) {
@@ -393,15 +584,20 @@ body {
color: #eee;
}
.title-container {
.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 {
@@ -416,6 +612,8 @@ body {
color: white;
}
.language-menu,
.input-box,
.menu-options,
#menu-button {
background-color: #343434;

View File

@@ -56,22 +56,14 @@
"package-name": "xzr.konabess"
},
{
"name": "KsuWebUI",
"name": "KSUWebUI",
"package-name": "io.github.a13e300.ksuwebui"
},
{
"name": "ShortX",
"package-name": "tornaco.apps.shortx"
}
]
},
{
"info": "General app that won't detect bootloader",
"apps": [
{
"name": "Scene",
"package-name": "com.omarea.vtools"
},
{
"name": "Swift Backup",
"package-name": "org.swiftapps.swiftbackup"
@@ -88,6 +80,10 @@
"name": "Google Slides",
"package-name": "com.google.android.apps.docs.editors.slides"
},
{
"name": "Keep Notes",
"package-name": "com.google.android.keep"
},
{
"name": "Google Maps",
"package-name": "com.google.android.apps.maps"
@@ -103,6 +99,18 @@
{
"name": "Chrome",
"package-name": "com.android.chrome"
},
{
"name": "YouTube",
"package-name": "com.google.android.youtube"
},
{
"name": "YouTube Music",
"package-name": "com.google.android.apps.youtube.music"
},
{
"name": "Speedtest",
"package-name": "org.zwanoo.android.speedtest"
}
]
}