Conversation
Rework BatteryRemapper into a module with a Material settings screen, and migrate it from the legacy Xposed API to the modern libxposed API (102). New - Material 3 settings screen: a two-thumb range slider with a live preview of the resulting mapping, and Save / Reset pinned to the bottom so the primary action is always reachable. - Module status card: framework name, version and API level, whether com.android.systemui is in the module's scope, and whether the module is actually loaded in the System UI process (with PID and load state), refreshed while the screen is open. - Configurable mapping window: any physical 0%..100% onto displayed 0%..100%, default 20%..80% (the original behaviour). - Configurable shutdown countdown trigger, expressed on the DISPLAYED scale (0%..50%, default 0%), with the physical equivalent shown live as the window changes. - Bilingual settings screen (English + Simplified Chinese) with an in-app switch, stored as a per-app language so it also appears in the system app settings. - The shutdown dialog text is bilingual and comes from the module's own resources. Changed - Module entry and metadata now follow the modern API: META-INF/xposed/java_init.list, scope.list and module.prop replace assets/xposed_init and the xposed meta-data; the module name and description come from android:label / android:description. - The hook is an interceptor chain on Intent.getIntExtra instead of XC_MethodHook, and the module entry class extends XposedModule. - Settings are shared with the hooked process through LSPosed remote preferences; the legacy XSharedPreferences is no longer supported by current LSPosed. - Battery saver hysteresis is unchanged: forced on at 20% or below of the displayed level, off above 50%, always off while charging. Fixed - The battery display updates immediately after saving. System UI only recomputes the displayed percentage when an ACTION_BATTERY_CHANGED broadcast arrives, so a changed window now re-sends the current sticky battery intent, scoped to System UI itself. - The shutdown countdown cancels reliably when the charger is connected: the module watches the battery directly instead of relying on System UI to read a level, and cancellation no longer depends on a flag that could disagree with what is on screen. - The settings screen no longer reports the module as missing after the activity is recreated (for example on a language change); the framework service is held process-wide. - Module hooks are deoptimised before being installed (XposedInterface.deoptimize). ART inlines small framework methods such as ContextWrapper.attachBaseContext into their callers, and without deoptimising, the hook installs and then never runs. - The module no longer hot-reloads itself on save. XposedService.hotReloadModule() loads the module classes through a NEW classloader, so every save left a live ghost instance behind with its own configuration, its own battery receiver and its own dialog reference. A JVM system property now carries an ownership token, and any leftover instance goes inert. Build - compileSdk 37, AGP 8.13.2, Gradle 8.13, Java 17. minSdk 29 and targetSdk 34 are unchanged. The libxposed 102 artifacts require minCompileSdk 37, which the existing CI already installs. - The module carries an adaptive launcher icon, since it is now a launchable app. BREAKING CHANGE: the module is built against the modern Xposed API (libxposed 102) and declares minApiVersion 102, so it requires an LSPosed build that supports the modern API. It no longer installs on frameworks that only provide the legacy API.
Rillwyn
force-pushed
the
feat/settings-ui-1.2.0
branch
from
September 16, 2026 10:37
372b1e1 to
68f3e9b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Reworks BatteryRemapper into a module with a Material settings screen, and migrates it from the legacy Xposed API to the modern libxposed API (102).
The module previously had no UI: the 20%..80% → 0%..100% window was hard-coded, and there was no way to see whether the hook was actually running. This PR makes the window configurable, adds a status card that answers "is this working?", and carries several fixes that came out of log analysis on a real device (MIUI + KernelSU + LSPosed).
New
Settings screen
Module status card
com.android.systemuiis in the module's scope.UP_TO_DATE/STALE/RELOADING/FAILED), refreshed while the screen is open.Battery protection
Language
Changed
META-INF/xposed/java_init.list+scope.list+module.propreplaceassets/xposed_initand the xposed meta-data; the module name and description come fromandroid:label/android:description.hook(...).intercept(chain -> ...)) instead ofXC_MethodHook, and the entry class extendsXposedModule.XposedInterface.getRemotePreferencesin the hook,XposedService.getRemotePreferencesin the UI). The legacyXSharedPreferencesis unsupported by current LSPosed (the official comparison table marks it unsupported since 2.1.0).Mapping.remap()is a dependency-free function shared by the hook and the UI preview, and it is verified against the original formula (every level 0..100 identical at the default window).Fixed
Every one of these was a silent failure mode, found by reading logs from a real device:
ACTION_BATTERY_CHANGEDbroadcast arrives, so a window that changes while the battery is steady stayed invisible until the next real battery event — which is why plugging in a charger appeared to "apply" it. Every path that changes the window now re-sends the current sticky battery intent, scoped to System UI itself.XposedService.hotReloadModule()does not reload in place: it loads the module classes through a new classloader, and statics are per-classloader, so every save left a live ghost instance behind with its own configuration, its own battery receiver and its own dialog reference. A countdown could be armed by a trigger the user had already lowered. A JVM system property — the one piece of state every classloader shares — now carries an ownership token, and leftovers go inert and take their own dialog down.XposedInterface.deoptimize). ART inlines small framework methods such asContextWrapper.attachBaseContext; without deoptimising, the hook installs successfully and then never runs, which silently disabled battery saver automation and the shutdown dialog.Contextis recovered throughActivityThread.currentApplication()as a fallback, which is what the legacyAndroidAppHelperdid — needed because a module loaded late never seesApplication#onCreate().Build
compileSdk 37, AGP 8.13.2, Gradle 8.13, Java 17.minSdk 29/targetSdk 34unchanged.compileSdk 37is required by theio.github.libxposed102 artifacts (minCompileSdk 37). The existing workflow already installsplatforms;android-37, so CI needs no change..gitignore(build output was previously untracked noise) and an adaptive launcher icon, since the module is now a launchable app.Breaking change
The module is built against the modern Xposed API and declares
minApiVersion 102, so it needs an LSPosed build that supports it, and it will not install on frameworks that only provide the legacy API.minSdkstays at 29, so device compatibility is unaffected.How to test
com.android.systemuiin scope, reboot.