From 09c7cc27f1d7c001c9ee3918b9f36f7d2a00f49e Mon Sep 17 00:00:00 2001 From: KOWX712 Date: Mon, 4 Nov 2024 01:59:31 +0800 Subject: [PATCH] ksu webui - initial webui template from j-hc/zygisk-detach --- module/webroot/index.html | 30 +++++++++++ module/webroot/index.js | 108 ++++++++++++++++++++++++++++++++++++++ module/webroot/styles.css | 107 +++++++++++++++++++++++++++++++++++++ 3 files changed, 245 insertions(+) create mode 100644 module/webroot/index.html create mode 100644 module/webroot/index.js create mode 100644 module/webroot/styles.css diff --git a/module/webroot/index.html b/module/webroot/index.html new file mode 100644 index 0000000..1bf7b0e --- /dev/null +++ b/module/webroot/index.html @@ -0,0 +1,30 @@ + + + + + + Document + + + + +
+ +
+
+
+ +
+ +
+ Acknowledgment to j-hc/zygisk-detach WebUI +
+ + \ No newline at end of file diff --git a/module/webroot/index.js b/module/webroot/index.js new file mode 100644 index 0000000..74dd041 --- /dev/null +++ b/module/webroot/index.js @@ -0,0 +1,108 @@ +let e = 0; +const appTemplate = document.getElementById("app-template").content; +const appListContainer = document.getElementById("apps-list"); +let excludeList = []; + +// 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); + } + }); +} + +// Function to read the EXCLUDE file and return its contents as an array +async function readExcludeFile() { + try { + const result = await execCommand('cat /data/adb/tricky_store/target_list_config/EXCLUDE'); + excludeList = result.split("\n").filter(app => app.trim() !== ''); // Filter out empty lines + console.log("Current EXCLUDE list:", excludeList); + } catch (error) { + console.error("Failed to read EXCLUDE file:", error); + } +} + +// Function to fetch the app list using the package manager +async function fetchAppList() { + try { + const result = await execCommand("pm list packages -3 &1 | cat"); + return result.split("\n").map(line => line.replace("package:", "").trim()).filter(Boolean); + } catch (error) { + console.error("Failed to fetch app list:", error); + return []; + } +} + +// Function to render apps +async function renderAppList() { + await readExcludeFile(); + const apps = await fetchAppList(); + apps.forEach(appName => { + const appElement = document.importNode(appTemplate, true); + appElement.querySelector(".name").textContent = appName; + const checkbox = appElement.querySelector(".checkbox"); + checkbox.checked = !excludeList.includes(appName); // Deselect if in EXCLUDE + appListContainer.appendChild(appElement); + }); +} + +// Add button click event to update EXCLUDE file +document.getElementById("add").addEventListener("click", async () => { + await readExcludeFile(); + const deselectedApps = Array.from(appListContainer.querySelectorAll(".checkbox:not(:checked)")) + .map(checkbox => checkbox.closest(".card").querySelector(".name").textContent); + const selectedApps = Array.from(appListContainer.querySelectorAll(".checkbox:checked")) + .map(checkbox => checkbox.closest(".card").querySelector(".name").textContent); + // Add deselected apps to EXCLUDE if not already present + for (const app of deselectedApps) { + if (!excludeList.includes(app)) { + excludeList.push(app); // Add to the local list + console.log("Added to EXCLUDE list:", app); + } else { + console.log("App already in EXCLUDE file, skipping:", app); + } + } + // Remove selected apps from EXCLUDE + if (selectedApps.length > 0) { + selectedApps.forEach(app => { + excludeList = excludeList.filter(excludedApp => excludedApp !== app); // Remove from local list + console.log("Removed from EXCLUDE list:", app); + }); + } + // Overwrite the EXCLUDE file with the updated list + try { + const updatedExcludeContent = excludeList.join("\n"); + await execCommand(`echo "${updatedExcludeContent}" > /data/adb/tricky_store/target_list_config/EXCLUDE`); + console.log("EXCLUDE file updated successfully."); + } catch (error) { + console.error("Failed to update EXCLUDE file:", error); + } + await readExcludeFile(); +}); + +// Search functionality +document.getElementById("search").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"; + }); +}); + +// Initial load +renderAppList(); \ No newline at end of file diff --git a/module/webroot/styles.css b/module/webroot/styles.css new file mode 100644 index 0000000..96bf27c --- /dev/null +++ b/module/webroot/styles.css @@ -0,0 +1,107 @@ +body { + background-color: #F5F5F5; +} +#apps-list { + margin-top: 70px; +} +.card { + border: none; + outline: none; + margin-bottom: 10px; + padding: 20px; + background-color: white; + border-radius: 8px; +} +.content { + justify-content: space-between; + align-items: center; + display: flex; +} +.name { + overflow-wrap: break-word; + word-break: break-word; + max-width: calc(100% - 30px); + margin: 0; + display: inline-block; +} +.checkbox { + margin-left: auto; +} +.floating-card { + display: flex; + justify-content: center; + z-index: 3; + position: fixed; + bottom: 45px; + left: 50%; + transform: translateX(-50%); +} +.floating-btn { + color: #fff; + cursor: pointer; + background-color: #007bff; + border: none; + border-radius: 24px; + justify-content: center; + align-items: center; + width: auto; + height: auto; + padding: 10px 20px; + font-size: 16px; + transition: transform .3s ease-in-out; + display: flex; + box-shadow: 0 4px 8px #0003; +} +.search-card { + position: fixed; + top: 10px; + width: calc(100% - 35px); + left: 50%; + transform: translateX(-50%); + padding: 10px; + background-color: white; + border: 1px solid #ccc; + border-radius: 25px; + display: flex; + align-items: center; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + z-index: 1000; +} +.search-input { + width: 100%; + border: none; + outline: none; + padding: 5px; + font-size: 14px; + background-color: #fff; + box-sizing: border-box; + text-align: left; +} +.acknowledgment { + width: auto; + background-color: #F5F5F5; + color: #6E6E6E; + text-align: center; + padding: 20px; + font-size: 14px; +} +@media (prefers-color-scheme: dark) { + body { + color: #eee; + background-color: #121212; + } + .card { + background-color: #343434; + } + .search-card { + border: 1px solid #6E6E6E; + background-color: #343434; + } + .search-input { + color: #eee; + background-color: #343434; + } + .acknowledgment { + background-color: #121212; + } +} \ No newline at end of file