diff --git a/README.md b/README.md index df1e0ba..8927aa5 100755 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ An addon module for tricky store - ADDITION and EXCLUDE in `/data/adb/tricky_store/target_list_config` - EXCLUDE for removing unnecessary apps - ADDITION for adding back system app excluded by default -- Configuration list with **KSU WebUI** (For KernelSU and Apatch, ) +- Configuration list with **KSU WebUI** (KernelSU & Apatch, For Magisk users: [KSU WebUI standalone](https://github.com/5ec1cff/KsuWebUIStandalone)) ## Instructions ### Automatic update diff --git a/changelog.md b/changelog.md index 61022ec..c99e7e5 100755 --- a/changelog.md +++ b/changelog.md @@ -10,6 +10,15 @@ GitHub release: [Tricky Addon: Update Target List](https://github.com/KOWX712/Tr Telegram channel: [KOW's Little World](https://t.me/kowchannel) ## Changelog +### v2.1 +- Added curl binary to fetch Xposed module package name from LSPosed webside + +**KSU WebUI** +- Added feature to exclude Xposed module package name +- Fixed abnormal color in dark mode +- Combined save config and update target.txt button +- Fixed some more known bugs + ### v2.0 - Added WebUI for configuration diff --git a/lite-script_only/README.md b/lite-script_only/README.md index 30696f9..57dbd87 100644 --- a/lite-script_only/README.md +++ b/lite-script_only/README.md @@ -4,7 +4,7 @@ - Recommend to run with MT manager ## Changelog -### v2.0 +### v2.0, v2.1 - More clarify remark in code - Remove useless code diff --git a/lite-script_only/UpdateTargetList.sh b/lite-script_only/UpdateTargetList.sh index ac3efc9..9a56140 100644 --- a/lite-script_only/UpdateTargetList.sh +++ b/lite-script_only/UpdateTargetList.sh @@ -1,6 +1,6 @@ #!/bin/sh -# Tricky Addon Lite: Update Target List Script v2.0 +# Tricky Addon Lite: Update Target List Script v2.1 # GitHub Repository: https://github.com/KOWX712/Tricky-Addon-Update-Target-List/blob/master/lite-script_only/README.md # Telegram channel: https://t.me/kowchannel diff --git a/module/bin/arm64-v8a/curl b/module/bin/arm64-v8a/curl new file mode 100644 index 0000000..6305598 Binary files /dev/null and b/module/bin/arm64-v8a/curl differ diff --git a/module/bin/armeabi-v7a/curl b/module/bin/armeabi-v7a/curl new file mode 100644 index 0000000..70ad236 Binary files /dev/null and b/module/bin/armeabi-v7a/curl differ diff --git a/module/bin/x86/curl b/module/bin/x86/curl new file mode 100644 index 0000000..549c933 Binary files /dev/null and b/module/bin/x86/curl differ diff --git a/module/bin/x86_64/curl b/module/bin/x86_64/curl new file mode 100644 index 0000000..f4415ca Binary files /dev/null and b/module/bin/x86_64/curl differ diff --git a/module/common/get_xposed.sh b/module/common/get_xposed.sh new file mode 100644 index 0000000..d75b0d0 --- /dev/null +++ b/module/common/get_xposed.sh @@ -0,0 +1,9 @@ +#!/system/bin/sh + +# Get all xposed app package name +MODPATH=${0%/*} +OUTPUT="$MODPATH/xposed-list" + +curl -s "https://modules.lsposed.org/modules.json" | \ + grep -o '"name":"[^"]*","description":' | \ + awk -F'"' '{print $4}' > "$OUTPUT" \ No newline at end of file diff --git a/module/customize.sh b/module/customize.sh index 06e9444..3107484 100644 --- a/module/customize.sh +++ b/module/customize.sh @@ -79,7 +79,18 @@ fi cp "$MODPATH/module.prop" "$COMPATH/module.prop.orig" mv "$COMPATH/UpdateTargetList.sh" "$SCRIPT_DIR/UpdateTargetList.sh" +sed -i "s|\"set-path\"|\"/data/adb/modules/$MODNAME/common/\"|" "$MODPATH/webroot/index.js" || ui_print "! fail to replace path" + +# Curl binary is used to fetch xposed module package name list from https://modules.lsposed.org/modules.json +if [ ! -f "/system/bin/curl" ]; then + mkdir -p "$MODPATH/system/bin" + mv "$MODPATH/bin/$(getprop ro.product.cpu.abi)/curl" "$MODPATH/system/bin/curl" + set_perm "$MODPATH/system/bin/curl" 0 2000 0777 +fi +rm -rf "$MODPATH/bin" + set_perm $SCRIPT_DIR/UpdateTargetList.sh 0 2000 0755 +set_perm $COMPATH/get_xposed.sh 0 2000 0755 if [ -d "$CONFIG_DIR" ]; then if [ ! -f "$CONFIG_DIR/EXCLUDE" ] && [ ! -f "$CONFIG_DIR/ADDITION" ]; then diff --git a/module/module.prop b/module/module.prop index 5057182..388578a 100644 --- a/module/module.prop +++ b/module/module.prop @@ -1,7 +1,7 @@ id=TA_utl name=Tricky Addon - Update Target List -version=v2.0 -versionCode=200 +version=v2.1 +versionCode=210 author=KOWX712 -description=Update tricky store target list with action button. Custom config: ADDITION and EXCLUDE in /data/adb/tricky_store/target_list_config +description=Update tricky store target.txt. Custom config: ADDITION and EXCLUDE in /data/adb/tricky_store/target_list_config updateJson=https://raw.githubusercontent.com/KOWX712/Tricky-Addon-Update-Target-List/master/update.json diff --git a/module/webroot/index.html b/module/webroot/index.html index 01bc520..38acbb6 100644 --- a/module/webroot/index.html +++ b/module/webroot/index.html @@ -11,22 +11,25 @@
Tricky Addon - Update Target List
+
- +
-
- Credit to j-hc/zygisk-detach WebUI -
+
Loading...
+
Credit to j-hc/zygisk-detach WebUI
\ No newline at end of file diff --git a/module/webroot/index.js b/module/webroot/index.js index 7d4091b..8c7eb86 100644 --- a/module/webroot/index.js +++ b/module/webroot/index.js @@ -1,6 +1,14 @@ let e = 0; const appTemplate = document.getElementById("app-template").content; const appListContainer = document.getElementById("apps-list"); +const loadingIndicator = document.querySelector(".loading"); +const searchInput = document.getElementById("search"); +const clearBtn = document.getElementById("clear-btn"); +const title = document.getElementById('title'); +const searchCard = document.querySelector('.search-card'); +const menu = document.querySelector('.menu'); +const floatingBtn = document.querySelector('.floating-btn'); +const basePath = "set-path"; let excludeList = []; // Function to execute shell commands @@ -36,101 +44,131 @@ async function readExcludeFile() { } } -// Function to fetch the app list using the package manager +// Function to fetch, sort, and render the app list async function fetchAppList() { try { + await readExcludeFile(); const result = await execCommand("pm list packages -3 &1 | cat"); - return result.split("\n").map(line => line.replace("package:", "").trim()).filter(Boolean); + const packageList = result.split("\n").map(line => line.replace("package:", "").trim()).filter(Boolean); + const sortedApps = packageList.sort((a, b) => { + const aInExclude = excludeList.includes(a); + const bInExclude = excludeList.includes(b); + return aInExclude === bInExclude ? a.localeCompare(b) : aInExclude ? 1 : -1; + }); + appListContainer.innerHTML = ""; + sortedApps.forEach(appName => { + const appElement = document.importNode(appTemplate, true); + appElement.querySelector(".name").textContent = appName; + const checkbox = appElement.querySelector(".checkbox"); + checkbox.checked = !excludeList.includes(appName); + appListContainer.appendChild(appElement); + }); + console.log("App list fetched, sorted, and rendered successfully."); } catch (error) { - console.error("Failed to fetch app list:", error); - return []; + console.error("Failed to fetch or render app list:", error); + } + floatingBtn.style.transform = 'translateY(-100px)'; +} + +// Function to refresh app list +let isRefreshing = false; +async function refreshAppList() { + isRefreshing = true; + title.style.transform = 'translateY(0)'; + searchCard.style.transform = 'translateY(0)'; + menu.style.transform = 'translateY(0)'; + floatingBtn.style.transform = 'translateY(0)'; + const searchInput = document.getElementById("search"); + searchInput.value = ''; + clearBtn.style.display = "none"; + appListContainer.innerHTML = ''; + loadingIndicator.style.display = 'flex'; + await new Promise(resolve => setTimeout(resolve, 500)); + loadingIndicator.style.display = 'none'; + window.scrollTo(0, 0); + await fetchAppList(); + isRefreshing = false; +} + +// Function to run the Xposed script +async function runXposedScript() { + try { + const scriptPath = `${basePath}get_xposed.sh`; + await execCommand(scriptPath); + console.log("Xposed script executed successfully."); + } catch (error) { + console.error("Failed to execute Xposed script:", error); } } -// Function to render apps -async function renderAppList() { - await readExcludeFile(); - const apps = await fetchAppList(); - const sortedApps = apps.sort((a, b) => { - const aInExclude = excludeList.includes(a); - const bInExclude = excludeList.includes(b); - return aInExclude === bInExclude ? a.localeCompare(b) : aInExclude ? 1 : -1; - }); - appListContainer.innerHTML = ""; - sortedApps.forEach(appName => { - const appElement = document.importNode(appTemplate, true); - appElement.querySelector(".name").textContent = appName; - const checkbox = appElement.querySelector(".checkbox"); - checkbox.checked = !excludeList.includes(appName); - appListContainer.appendChild(appElement); - }); +// Function to read the xposed list and uncheck corresponding apps +async function deselectXposedApps() { + try { + const result = await execCommand(`cat ${basePath}xposed-list`); + const xposedApps = result.split("\n").map(app => app.trim()).filter(Boolean); + const apps = document.querySelectorAll(".card"); + apps.forEach(app => { + const appName = app.querySelector(".name").textContent.trim(); + const checkbox = app.querySelector(".checkbox"); + if (xposedApps.includes(appName)) { + checkbox.checked = false; // Uncheck if found in xposed-list + } + }); + console.log("Xposed apps deselected successfully."); + } catch (error) { + console.error("Failed to deselect Xposed apps:", error); + } } -// Function to refresh the app list and clear the search input -async function refreshAppList() { - const searchInput = document.getElementById("search"); - searchInput.value = ''; - const apps = appListContainer.querySelectorAll(".card"); - apps.forEach(app => app.style.display = "block"); - await renderAppList(); -} - -// Function to select all apps +// Function to select all visible apps function selectAllApps() { - document.querySelectorAll(".checkbox").forEach(checkbox => checkbox.checked = true); + document.querySelectorAll(".card").forEach(card => { + if (card.style.display !== "none") { + card.querySelector(".checkbox").checked = true; + } + }); } -// Function to deselect all apps +// Function to deselect all visible apps function deselectAllApps() { - document.querySelectorAll(".checkbox").forEach(checkbox => checkbox.checked = false); + document.querySelectorAll(".card").forEach(card => { + if (card.style.display !== "none") { + card.querySelector(".checkbox").checked = false; + } + }); } -let promptTimeout; // Variable to store the current timeout - // Function to show the prompt with a success or error message function showPrompt(message, isSuccess = true) { const prompt = document.getElementById('prompt'); prompt.textContent = message; - prompt.classList.toggle('error', !isSuccess); // Apply error class if not success - prompt.style.display = 'block'; - - if (promptTimeout) { - clearTimeout(promptTimeout); - } - - promptTimeout = setTimeout(() => { - prompt.style.display = 'none'; - }, 2000); -} - -// Function to update the target list by executing a script -async function updateTargetList() { - try { - await execCommand("/data/adb/tricky_store/UpdateTargetList.sh"); - showPrompt("Successfully updated target.txt"); - } catch (error) { - console.error("Failed to update target list:", error); - showPrompt("Failed to update target.txt !", false); + 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); } // Menu toggle functionality function setupMenuToggle() { const menuButton = document.getElementById('menu-button'); + const menuIcon = menuButton.querySelector('.menu-icon'); const menuOptions = document.getElementById('menu-options'); + let menuOpen = false; menuButton.addEventListener('click', (event) => { event.stopPropagation(); if (menuOptions.classList.contains('visible')) { closeMenu(); } else { - menuOptions.style.display = 'block'; - setTimeout(() => { - menuOptions.classList.remove('hidden'); - menuOptions.classList.add('visible'); - menuButton.classList.add('menu-open'); - menuButton.classList.remove('menu-closed'); - }, 10); + openMenu(); } }); @@ -140,7 +178,13 @@ function setupMenuToggle() { } }); - const closeMenuItems = ['refresh', 'select-all', 'deselect-all', 'update']; + window.addEventListener('scroll', () => { + if (menuOptions.classList.contains('visible')) { + closeMenu(); + } + }); + + const closeMenuItems = ['refresh', 'select-all', 'deselect-all', 'deselect-xposed']; closeMenuItems.forEach(id => { const item = document.getElementById(id); if (item) { @@ -151,30 +195,58 @@ function setupMenuToggle() { } }); + function openMenu() { + menuOptions.style.display = 'block'; + setTimeout(() => { + menuOptions.classList.remove('hidden'); + menuOptions.classList.add('visible'); + menuIcon.classList.add('menu-open'); + menuIcon.classList.remove('menu-closed'); + menuOpen = true; + }, 10); + } + function closeMenu() { if (menuOptions.classList.contains('visible')) { menuOptions.classList.remove('visible'); menuOptions.classList.add('hidden'); - menuButton.classList.remove('menu-open'); - menuButton.classList.add('menu-closed'); + menuIcon.classList.remove('menu-open'); + menuIcon.classList.add('menu-closed'); setTimeout(() => { menuOptions.style.display = 'none'; - }, 300); + }, 400); + menuOpen = false; } } } // Search functionality -document.getElementById("search").addEventListener("input", (e) => { +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"; }); + if (searchQuery !== "") { + clearBtn.style.display = "block"; + } else { + clearBtn.style.display = "none"; + } }); -// Add button click event to update EXCLUDE file +// Clear search input +clearBtn.addEventListener("click", () => { + searchInput.value = ""; + clearBtn.style.display = "none"; + searchInput.focus(); + const apps = appListContainer.querySelectorAll(".card"); + apps.forEach(app => { + app.style.display = "block"; + }); +}); + +// Add button click event to update EXCLUDE file and run UpdateTargetList.sh document.getElementById("save").addEventListener("click", async () => { await readExcludeFile(); const deselectedApps = Array.from(appListContainer.querySelectorAll(".checkbox:not(:checked)")) @@ -199,44 +271,53 @@ document.getElementById("save").addEventListener("click", async () => { } try { + // Save the EXCLUDE file const updatedExcludeContent = excludeList.join("\n"); await execCommand(`echo "${updatedExcludeContent}" > /data/adb/tricky_store/target_list_config/EXCLUDE`); console.log("EXCLUDE file updated successfully."); - showPrompt("Config saved successfully"); + + // Execute UpdateTargetList.sh + try { + await execCommand("/data/adb/tricky_store/UpdateTargetList.sh"); + showPrompt("Config and target.txt updated"); + } catch (error) { + console.error("Failed to update target list:", error); + showPrompt("Config saved, but failed to update target list", false); + } } catch (error) { console.error("Failed to update EXCLUDE file:", error); showPrompt("Failed to save config", false); } await readExcludeFile(); + await refreshAppList(); }); -// Event listener for the "Update Target List" menu option -document.getElementById('update').addEventListener('click', updateTargetList); - // Initial load -document.addEventListener('DOMContentLoaded', () => { +document.addEventListener('DOMContentLoaded', async () => { setupMenuToggle(); document.getElementById("refresh").addEventListener("click", refreshAppList); document.getElementById("select-all").addEventListener("click", selectAllApps); document.getElementById("deselect-all").addEventListener("click", deselectAllApps); - renderAppList(); + document.getElementById("deselect-xposed").addEventListener("click", deselectXposedApps); + await runXposedScript(); + await fetchAppList(); + loadingIndicator.style.display = "none"; }); // Scroll event let lastScrollY = window.scrollY; -const title = document.getElementById('title'); -const searchCard = document.querySelector('.search-card'); -const menu = document.querySelector('.menu'); - window.addEventListener('scroll', () => { + if (isRefreshing) return; if (window.scrollY > lastScrollY) { title.style.transform = 'translateY(-100%)'; searchCard.style.transform = 'translateY(-35px)'; menu.style.transform = 'translateY(-35px)'; + floatingBtn.style.transform = 'translateY(0)'; } else { title.style.transform = 'translateY(0)'; searchCard.style.transform = 'translateY(0)'; menu.style.transform = 'translateY(0)'; + floatingBtn.style.transform = 'translateY(-100px)'; } lastScrollY = window.scrollY; }); \ No newline at end of file diff --git a/module/webroot/styles.css b/module/webroot/styles.css index 9b4dcdf..0a9ce3f 100644 --- a/module/webroot/styles.css +++ b/module/webroot/styles.css @@ -47,6 +47,18 @@ body { width: 100%; } +.clear-btn { + position: absolute; + right: 10px; + border: none; + background: none; + color: #ccc; + font-size: 18px; + cursor: pointer; + display: none; + z-index: 10; +} + .menu { display: flex; position: fixed; @@ -67,9 +79,23 @@ body { box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); padding: 13px 17px; text-align: center; + position: relative; + z-index: 1000; +} + +.menu-icon { + display: inline-block; transition: transform 0.3s ease; } +.menu-icon.menu-open { + transform: rotate(90deg); +} + +.menu-icon.menu-closed { + transform: rotate(0deg); +} + .menu-options { background-color: white; border: 1px solid #ccc; @@ -79,9 +105,10 @@ body { position: absolute; top: 110%; right: 0; - transform: translateX(100%); + transform: translateX(120%); transition: transform 0.3s ease; - width: 170px; + width: auto; + white-space: nowrap; } .menu-options.visible { @@ -90,7 +117,7 @@ body { } .menu-options.hidden { - transform: translateX(110%); + transform: translateX(140%); } .menu-options ul { @@ -101,18 +128,10 @@ body { .menu-options li { cursor: pointer; - padding: 15px 16px; + padding: 15px 18px; text-align: left; } -.menu-open { - transform: rotate(90deg); -} - -.menu-closed { - transform: rotate(0deg); -} - .card { background-color: white; border: none; @@ -141,29 +160,62 @@ body { } .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; - display: none; font-size: 15px; - left: 10px; padding: 5px 15px; - position: fixed; - bottom: 15px; z-index: 1000; - transition: opacity 1s ease; + 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: 45px; + bottom: -50px; left: 50%; transform: translateX(-50%); z-index: 3; @@ -180,7 +232,16 @@ body { align-items: center; padding: 10px 20px; font-size: 16px; - transition: transform .3s ease-in-out; + transition: transform 0.3s ease-in-out; +} + +.loading { + color: #6E6E6E; + display: flex; + justify-content: center; + align-items: center; + height: calc(100vh - 164px); + font-size: 24px; } .acknowledgment { @@ -209,11 +270,11 @@ body { .search-card { border: 1px solid #6E6E6E; } - - .search-input { + + .search-input{ color: white; } - + .menu-options, #menu-button { background-color: #343434;