- Generate your commit message as
.gitmessageindicates. - New code must be written in Kotlin. Java is only permitted for emergency fixes to existing Java infrastructure (e.g., BaseHookModule.java, HookManager.java).
- Explicitly assign UTF-8 as the decode format for all project files to handle Chinese characters.
- JDK is available only on Windows in most scenarios. If you cannot find Java in your workspace on Windows, notify the user to config their JAVA_HOME and Path variable, then restart the terminal or system.
- If you are on WSL2 and you have to use Java related tools such as gradlew.bat, use Windows environment by explicitly calling cmd or PowerShell. For example,
cmd.exe /c ".\gradlew.bat assembleDebug" - To conduct compilation test on WSL2, the command is
bash cd ./ && cmd.exe /c ".\gradlew.bat assembleDebug" - To commit your work, use
git.exe commit <commit_messages> - If any of these Windows-specific commands failed, stop the work and ask the user to confirm what went wrong.
ZUX-ZTool is a single-module Android project for ZUX OS/ZUI devices. It combines a normal Android app UI, LSPosed/Xposed Hook modules, root/shell utilities, foreground log collection, configuration backup/restore, and embedded Magisk module assets.
The Android module is :app, package com.qimian233.ztool. The app UI is largely Jetpack Compose based: MainActivity hosts the main Compose routes for Home, Features, Audit, and Settings. Feature detail entry points are still Android Activity contracts declared in the Manifest and must remain stable unless the user explicitly approves a breaking change.
Use English for new agent-facing documentation where practical. Existing user-facing strings and legacy Chinese logs may remain Chinese.
- Root project name:
ZTool - Android module:
:app - Android Gradle Plugin:
9.1.1 - Kotlin:
2.3.21 - Compose BOM:
2025.10.00 - Miuix:
top.yukonga.miuix.kmp:miuix-ui-android:0.9.2 compileSdk/targetSdk:37minSdk:27- Java/Kotlin JVM target:
11 - Xposed API: libxposed API 102
- Hidden API bypass:
org.lsposed.hiddenapibypass:hiddenapibypass:4.3 - Main verification command:
.\gradlew.bat assembleDebug(Pure Windows) orcmd.exe /c ".\gradlew.bat assembleDebug"(WSL2)
For any Miuix API or component behavior, consult the official Dokka site instead of decompiling or reading locally downloaded dependencies:
https://compose-miuix-ui.github.io/miuix/dokka/index.html
.
app/ Android app module
docs_archive/compose_refactor/ Compose migration notes and scoped agent guide
docs_archive/gradle_upgrade/ Gradle upgrade notes
docs_archive/hook_guide/ Backend Hook module + frontend Hook setting guides
docs_archive/material_enhancement/ Material UI notes
reference/ Reference AAR files
gradle/ Gradle wrapper and version catalog
libs/ Root local dependencies
Add_New_Preference_Key_zh-CN.md Guide for adding preference keys in the centralized system
AGENTS.MD This root agent guide
README.md
UpdateCheck.json
ZToolLogo.png
ZToolLogoForeground.svg
ZToolUploadKey.jks Release signing file; do not touch casually
更新日志.txt
The Android module has this high-level shape:
app/
build.gradle.kts
proguard-rules.pro
libs/XposedBridgeAPI-82.jar
src/main/
AndroidManifest.xml
assets/
xposed_init
embedding/
java/com/qimian233/ztool/
res/
src/test/
src/androidTest/
com.qimian233.ztool
MainActivity.kt, root Compose routes, shell executor, activation probe
audit
Hook log parsing and structured audit data
config
Module config constants
data
Repository layer for preferences, root/shell operations, logs, OTA, theme, and feature settings
hook
Xposed entry point, Hook manager, base Hook API, preference helpers, and Hook modules
service
Foreground Hook log collection service and service manager
settingactivity
Feature detail Activities and remaining helper screens
ui
Shared Compose components and theme system
utils
File, permission, Magisk, embedding, firmware, font, dialog, and config utilities
viewmodel
UiState and state management for main routes and feature detail pages
Main route files:
MainActivity.kt: app entry point and Compose host.HomeRoute.kt: environment status, module activation, device info, update prompt, reboot actions.FeaturesRoute.kt: feature entry list grouped by target app/scope.AuditRoute.kt: Hook log filtering, search, stats, export, clear, and details.SettingsRoute.kt: log service, detailed logging, backup/restore, theme settings, about dialog.
Keep these stable unless the task explicitly targets them:
- Existing Activity package/class names used as Manifest launch contracts.
- Existing SharedPreferences/config keys used by Hook modules.
app/src/main/resources/META-INF/xposed/java_init.listHook entry class, currently points tocom.qimian233.ztool.hook.HookInit.app/src/main/resources/META-INF/xposed/module.propModule properties, such as minimum API version.app/src/main/resources/META-INF/xposed/scope.listModule scope.
Hook scope packages include Android framework/system apps and ZUI apps such as android, com.android.systemui, com.android.settings, com.zui.launcher, and com.lenovo.ota.
Core files:
app/src/main/java/com/qimian233/ztool/hook/HookInit.java: Xposed entry point.app/src/main/java/com/qimian233/ztool/hook/base/BaseHookModule.java: base class for Hook modules.app/src/main/java/com/qimian233/ztool/hook/base/HookManager.java: registration and dispatch for Hook modules.app/src/main/java/com/qimian233/ztool/data/keys/PreferenceKeys.kt: single source of truth for all preference keys — every key inxposed_module_configis defined here, organized by data type. SeeAdd_New_Preference_Key_zh-CN.md.app/src/main/java/com/qimian233/ztool/data/keys/ScopeKeys.kt: single source of truth for all scope package names — every package LSPosed may inject into (Scope(packageName, HowToRestart)), plus its preferred restart method. Hooks,ScopeUtils, and frontend entry points must referenceScopeKeysinstead of hardcoding package name strings. See "Scope Management" below.app/src/main/java/com/qimian233/ztool/utils/ModulePreferencesUtils.kt: app-side SharedPreferences read/write utility; usesPreferenceKeyslist-based matching for backup/restore type inference.
DexKit lookups must NOT run inside handleLoadPackage (they break LSPosed hot reload —
replay re-creates the bridge for the same APK and the native state conflicts). Instead,
use the offline index pipeline:
- App side (
com.qimian233.ztool.dexindex, pure Kotlin, no libxposed dependency): oneDexIndexerper scope package (seeDexIndexRegistry);DexIndexManagerruns them after install/update (triggered byDexIndexReceiver), on app start when the target APK fingerprint changed, or via the Settings manual refresh entry. Results are written atomically to<scopePackage>.jsonin the module's privatefilesDirroot (Remote Files cannot address subdirectories). - Hook side (
hook/base/DexIndexStore.kt, the only index class allowed to touch libxposed): reads the JSON viaXposedInterface.openRemoteFile(...)(Remote Files — the LSPosed daemon reads the module's privatefilesDirwith privileges, no chmod needed). Any failure returns null and the hook falls back to hardcoded names. - Never read the index inside hook lambdas (
hookWithIdcallbacks) — resolve values inhandleLoadPackageand capture them. - Constants (module keys, field keys, file layout):
DexIndexConstants.kt. Full guide:docs_archive/dex_index/README.md.
Hook module folders:
hook/modules/
documentsui/
gametool/
launcher/
mobiledesktop/
ota/
packageinstaller/
safecenter/
setting/
SharedPreferencesTool/
systemFramework/
systemui/
wallpaper/
When adding a Hook switch or other Hook-facing setting in the app UI, read and follow:
docs_archive/hook_guide/Add_Frontend_Item.md
When adding a new backend Hook module, read and follow:
docs_archive/hook_guide/Add_New_Hook_Module.md
For preference key management, all keys are now centrally defined in PreferenceKeys.kt. When adding a new key, read:
Add_New_Preference_Key_zh-CN.md
The durable rules are:
- Do not read or write SharedPreferences directly from Composables.
- Put persistence in the appropriate
data/**/**Repository.kt. - Expose values through
UiStateand mutate them through the matchingViewModel. - Hook-related settings must use
ModulePreferencesUtilsand the sharedxposed_module_configpreference file. - Preference keys must be registered in
PreferenceKeys.ktbefore use in Repository or Hook code. Reference them asPreferenceKeys.CONSTANT_NAME.name— never write key strings by hand. - Preference keys and default values must match the Hook-side reads exactly.
- Use existing shared setting components such as
SettingItem.Switch, dropdowns, sliders, and text inputs where applicable.
To add a new Hook implementation on the backend:
- Create a Hook class that extends
AppHookModulefor non-system-framework-hook, and extendSystemHookModulefor system-framework-hook. All new Hooks must be written in Kotlin (.kt). Java is only permitted for emergency fixes to existing Java infrastructure. - Implement
getModuleName(),getTargetPackages()andhandleLoadPackage(...). ReturnScopeKeys.CONSTANT.packageNamereferences fromgetTargetPackages()— register any new target package indata/keys/ScopeKeys.ktfirst (see "Scope Management"), and add it toscope.list. For system server (system framework) hooks, implementhandleSystemServerStarting(...), too. When implementing test hook, return "hook_test" or "test_hook" in getModuleName() will always enable this test hook without bothering to add frontend switch. - Read Hook settings using
PreferenceKeysconstants (seeAdd_New_Preference_Key_zh-CN.md). Usexposed.getRemotePreferences("xposed_module_config").getXxx(PreferenceKeys.CONSTANT_NAME.name, PreferenceKeys.CONSTANT_NAME.default). Do not hand-write key strings. - Register the Hook class in
app/src/main/java/com/qimian233/ztool/hook/base/HookManager.java. - If the Hook needs a user-facing toggle, add the frontend state according to
docs_archive/hook_guide/Add_Frontend_Item.md.
Use logger.<level> for module logs when possible, see docs_archive/new_log_system/migrate_and_use_new_logging_system.md for more information.
ScopeKeys (app/src/main/java/com/qimian233/ztool/data/keys/ScopeKeys.kt) is the single source of truth for scope package names in code. Every place that manages scope packages — Hook getTargetPackages() implementations, ScopeUtils.getScopes() / getScopePackages(), and frontend feature entries (FeaturesRoute, MainActivity) — must reference ScopeKeys.CONSTANT.packageName and never hardcode package name strings. The HowToRestart registered on each Scope also drives the unified restart logic in ScopeUtils.restartScope().
When adding a Hook that targets a package:
- Register the package in
ScopeKeys.kt(with its preferredHowToRestart) before using it anywhere. - Reference
ScopeKeys.CONSTANT.packageNamein the Hook'sgetTargetPackages(), and add it toScopeUtils.getScopes(...)if the package belongs to a frontend feature entry. - Add the package to the build-time
scope.listresource (see below).
Build-time resource files intentionally keep package names hardcoded and are NOT driven by ScopeKeys:
app/src/main/resources/META-INF/xposed/scope.list— the LSPosed injection scope declaration. When a new Hook targets a package not already in the scope, add it here;getTargetPackages()returned by each module must be a subset of the declared scope.app/src/main/resources/META-INF/xposed/module.prop— module metadata (e.g.minApiVersion,staticScope); keep hardcoded.
Other scope rules:
androidandsystem(system framework) are already in scope — no change needed for system-framework hooks.- Always verify scope coverage after adding a new Hook module. If a target package is missing from scope, the Hook will silently never execute.
- Do NOT remove scope entries unless their corresponding Hook modules are also removed.
- The scope is a build-time declaration; users must also configure it in LSPosed Manager.
- LSPosed sends a bind when module starts up.
ModuleActivationProbehas a onBind receiver that setsactivefield in this class totrue.- Frontend reads this value and shows the activated user interface.
When creating hooks that protect or interact with Android system services (e.g. keeping daemon processes alive, preventing service GC):
- Target
android(system framework) for hooks that intercept AMS, process management, or Binder behavior. - Use
hookMethodBySignaturewhen hooking AMS or ActiveServices methods — Android version differences change method parameter counts across releases. Iterate declared methods by name to cover all overloads. - Wrap every hook attempt in its own try-catch — missing methods on a particular ROM/version should not prevent other hooks from applying.
- For binder anti-GC: hold
ServiceManager.getService(...)references in a staticList<IBinder>to prevent weak-reference-only GC. - For death recipient protection: hook
Binder.unlinkToDeathand inspect the call stack for LSPosed-related classes before deciding to intercept. - Log each defense layer independently so the Audit UI can show which protections are active.
- Such hooks typically require a system reboot to take effect after enabling.
- The only current service is
LogCollectorService(foreground,specialUse, not exported). Declared inAndroidManifest.xml:40-44. - New services must be declared with appropriate
foregroundServiceTypefor the target SDK. - Services that interact with Hook modules should stay in
com.qimian233.ztool.service.
The current UI direction is Compose-first. For Compose migration-specific work, also read:
docs/compose_refactor/AGENTS.MD
General UI rules:
- Prefer shared components in
app/src/main/java/com/qimian233/ztool/ui/components. - Keep business screens style-agnostic; route Material/Miuix differences through theme and shared component adapters.
- Use
ZToolThemeandMaterialTheme.colorSchemefor normal app colors. - Keep frontend style, theme mode, dynamic color, manual seed color, and AMOLED behavior in the theme settings layer, not in feature pages.
- Do not reintroduce XML navigation, View-based screen scaffolding, old adapters, or Fragment navigation for new work.
- Do not put shell/root operations, raw threads, preference I/O, or imperative dialogs directly in Composables. Use Repository/ViewModel/state.
- Preserve detail Activity launch contracts even if the screen body is Compose.
res/values/: strings, colors, themes, arrays.res/values-night/: night resources.res/drawable/: in-app drawables and icons.res/mipmap-*: launcher icons.res/raw/: demo video resources and user agreement.res/xml/: backup, extraction, and locale config.assets/embedding/: embedded Magisk module configuration and scripts.assets/embedding/zuxos_embedding/: module package contents such asmodule.prop, shell scripts, sepolicy rules, and installer metadata.
Do not edit embedded module assets unless the task is specifically about Magisk/embedding behavior.
- When adding new user-facing strings, always add entries to both
res/values/strings.xml(default/Chinese) AND non-default language files such asres/values-en/strings.xml(English). Use the samenameattribute in both files. - Developer-only or debug strings that should never be translated must use
translatable="false".
Follow the steps below:
-
If the code changes, run
.\gradlew.bat assembleDebugafter code changes that touch:- Compose UI, themes, shared components, or navigation.
- ViewModels/Repositories used by UI.
- Hook classes, Hook registration, or Hook preference wiring.
- Manifest, resources, Gradle files, or embedded assets.
For documentation-only changes, a build is usually unnecessary; verify with
git diff --checkat minimum. -
Commit your changes.
- Start by checking
git status --short. - The working tree may contain user changes. Do not revert unrelated changes.
- Keep edits scoped to the request.
- Do not commit build outputs, IDE files, Gradle caches, keystore, or unrelated untracked files.
- Completed user-requested tasks must be committed unless the user explicitly says not to commit.
- Before committing, review
git diff --checkand the staged diff. - Use concise commit messages that describe the actual scoped change.
- If Git commit times out with GPG signature, retry it for once. If the commit still fails, commit without GPG signing.
git status --short
.\gradlew.bat assembleDebug
git diff --check
git commit ...- User assigns a task
- Analyze user's demand. If you receive an ambiguous demand with too little information, provide a question list for the user to submit missing information.
- Make a plan for the task. If the plan consists of more than 10 steps or more than 10 files are estimated to be added, modified and removed, write the plan into a Markdown file under the root directory with AGENT-oriented format
- Conduct the plan:
- Work with files
- Do necessary verifications noted in the Verification chapter
- Commit changes
- Mark the corresponding todo item in your list or the Markdown file written in step 3 as completed
- Continue until at least one of the following condition is/are satisfied. Notify the user with the current situation after encountering these situations:
- The work is done, no todo item left unchecked.
- Some step requires additional information and these information cannot be fetched via Internet, build cache and user input.
- Unable to access critical tools: gradlew.bat, git, javap, javac for 3 times.