Skip to content

feat: upgrade to xposed api 101#157

Merged
parallelcc merged 1 commit intomainfrom
feat/xposed_101
Apr 4, 2026
Merged

feat: upgrade to xposed api 101#157
parallelcc merged 1 commit intomainfrom
feat/xposed_101

Conversation

@parallelcc
Copy link
Copy Markdown
Owner

@parallelcc parallelcc commented Apr 4, 2026

Fix #155

@qodo-code-review
Copy link
Copy Markdown

Review Summary by Qodo

Upgrade to Xposed API 101 with new hooker pattern

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Upgrade Xposed API from version 100 to 101
• Refactor hooker implementations to use new Chain-based intercept pattern
• Remove libxposed-compat compatibility layer module
• Update logging calls to include log level and tag parameters
• Migrate callback-based hooks to instance-based Hooker implementations
• Update requestScope calls to accept List instead of String parameter
Diagram
flowchart LR
  A["Xposed API 100"] -->|upgrade| B["Xposed API 101"]
  C["Annotation-based Hookers"] -->|refactor| D["Chain-based Hookers"]
  E["libxposed-compat module"] -->|remove| F["Direct API usage"]
  G["Old logging API"] -->|update| H["New logging with level/tag"]
  B --> D
  F --> D
  H --> D
Loading

Grey Divider

File Changes

1. gradle/libs.versions.toml Dependencies +2/-2

Update Xposed API dependencies to version 101

gradle/libs.versions.toml


2. app/src/main/resources/META-INF/xposed/module.prop ⚙️ Configuration changes +2/-3

Update module API version to 101

app/src/main/resources/META-INF/xposed/module.prop


3. app/build.gradle.kts Dependencies +0/-1

Remove libxposed-compat dependency reference

app/build.gradle.kts


View more (18)
4. settings.gradle.kts ⚙️ Configuration changes +1/-1

Remove libxposed-compat module from project

settings.gradle.kts


5. app/src/main/java/com/parallelc/micts/ModuleMain.kt ✨ Enhancement +20/-18

Refactor to new XposedModule pattern and update logging

app/src/main/java/com/parallelc/micts/ModuleMain.kt


6. app/src/main/java/com/parallelc/micts/hooker/CSMSHooker.kt ✨ Enhancement +21/-34

Convert to Chain-based intercept pattern with instance hookers

app/src/main/java/com/parallelc/micts/hooker/CSMSHooker.kt


7. app/src/main/java/com/parallelc/micts/hooker/InvokeOmniHooker.kt ✨ Enhancement +11/-18

Migrate from annotation-based to instance-based hooker

app/src/main/java/com/parallelc/micts/hooker/InvokeOmniHooker.kt


8. app/src/main/java/com/parallelc/micts/hooker/LongPressHomeHooker.kt ✨ Enhancement +24/-36

Refactor nested hookers to use Chain intercept pattern

app/src/main/java/com/parallelc/micts/hooker/LongPressHomeHooker.kt


9. app/src/main/java/com/parallelc/micts/hooker/NavBarActionsConfigHooker.kt ✨ Enhancement +15/-23

Convert to Chain-based intercept with instance hooker

app/src/main/java/com/parallelc/micts/hooker/NavBarActionsConfigHooker.kt


10. app/src/main/java/com/parallelc/micts/hooker/NavBarEventHelperHooker.kt ✨ Enhancement +17/-23

Migrate to Chain-based intercept pattern

app/src/main/java/com/parallelc/micts/hooker/NavBarEventHelperHooker.kt


11. app/src/main/java/com/parallelc/micts/hooker/NavStubGestureEventManagerHooker.kt ✨ Enhancement +17/-22

Refactor to instance-based Chain intercept hooker

app/src/main/java/com/parallelc/micts/hooker/NavStubGestureEventManagerHooker.kt


12. app/src/main/java/com/parallelc/micts/hooker/NavStubViewHooker.kt ✨ Enhancement +39/-52

Convert multiple nested hookers to Chain-based pattern

app/src/main/java/com/parallelc/micts/hooker/NavStubViewHooker.kt


13. app/src/main/java/com/parallelc/micts/hooker/VIMSHooker.kt ✨ Enhancement +31/-47

Refactor to Chain-based intercept with HookHandle management

app/src/main/java/com/parallelc/micts/hooker/VIMSHooker.kt


14. app/src/main/java/com/parallelc/micts/ui/activity/MainActivity.kt ✨ Enhancement +2/-2

Update logging calls with level and tag parameters

app/src/main/java/com/parallelc/micts/ui/activity/MainActivity.kt


15. app/src/main/java/com/parallelc/micts/ui/viewmodel/SettingsViewModel.kt ✨ Enhancement +5/-5

Update requestScope calls to accept List parameter

app/src/main/java/com/parallelc/micts/ui/viewmodel/SettingsViewModel.kt


16. .github/workflows/ci_build.yml ⚙️ Configuration changes +3/-29

Simplify CI build by removing local dependency builds

.github/workflows/ci_build.yml


17. libxposed-compat/build.gradle.kts Miscellaneous +0/-12

Remove compatibility module build configuration

libxposed-compat/build.gradle.kts


18. libxposed-compat/src/main/AndroidManifest.xml Miscellaneous +0/-4

Remove compatibility module manifest

libxposed-compat/src/main/AndroidManifest.xml


19. libxposed-compat/src/main/java/io/github/libxposed/api/annotations/AfterInvocation.java Miscellaneous +0/-11

Remove deprecated AfterInvocation annotation

libxposed-compat/src/main/java/io/github/libxposed/api/annotations/AfterInvocation.java


20. libxposed-compat/src/main/java/io/github/libxposed/api/annotations/BeforeInvocation.java Miscellaneous +0/-11

Remove deprecated BeforeInvocation annotation

libxposed-compat/src/main/java/io/github/libxposed/api/annotations/BeforeInvocation.java


21. libxposed-compat/src/main/java/io/github/libxposed/api/annotations/XposedHooker.java Miscellaneous +0/-11

Remove deprecated XposedHooker annotation

libxposed-compat/src/main/java/io/github/libxposed/api/annotations/XposedHooker.java


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review bot commented Apr 4, 2026

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX Issues (0)

Grey Divider


Action required

1. Identity not restored 🐞 Bug ⛨ Security
Description
VIMSHooker.ShowSessionHooker clears the Binder calling identity but never restores it, so the rest
of showSessionFromSession can execute under the wrong identity. The CSService early-return path also
bypasses any restoration, risking permission-check bypass and identity leakage for the transaction.
Code

app/src/main/java/com/parallelc/micts/hooker/VIMSHooker.kt[R34-52]

+            override fun intercept(chain: Chain): Any? {
+                var hookHandle: HookHandle? = null
+                runCatching {
+                    val bundle = chain.args[1] as Bundle
+                    if (!bundle.getBoolean("micts_trigger", false)) return@runCatching null
+                    Binder.clearCallingIdentity()
+                    val triggerService = module!!.getRemotePreferences(CONFIG_NAME).getInt(KEY_TRIGGER_SERVICE, DEFAULT_CONFIG[KEY_TRIGGER_SERVICE] as Int)
+                    if (triggerService == TriggerService.CSService.ordinal) {
+                        return CSMSHooker.startContextualSearch(bundle.getInt("omni.entry_point"))
+                    } else {
+                        hookHandle = module!!.hook(Resources::class.java.getDeclaredMethod("getString", Int::class.java)).intercept(GetStringHooker())
                   }
-                    return null
-                }
-
-                @JvmStatic
-                @AfterInvocation
-                fun after(callback: AfterHookCallback, unhooker: MethodUnhooker<Method>?) {
-                    unhooker?.unhook()
+                }.onFailure { e ->
+                    module!!.log(Log.ERROR, "MiCTS", "hook resources fail", e)
               }
+                val result = chain.proceed()
+                hookHandle?.unhook()
+                return result
           }
Evidence
The hook calls Binder.clearCallingIdentity() but does not store the returned token and never calls
Binder.restoreCallingIdentity(...). There is also an early return from intercept() (CSService
branch) after clearing identity, which skips any potential cleanup.

app/src/main/java/com/parallelc/micts/hooker/VIMSHooker.kt[34-52]
app/src/main/java/com/parallelc/micts/hooker/VIMSHooker.kt[37-45]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`Binder.clearCallingIdentity()` is called without restoring the identity token, including an early-return path. This can cause downstream code in the same Binder transaction to run with the wrong calling identity.
### Issue Context
In the new Xposed v101 `intercept(chain)` refactor, the previous before/after hook split was collapsed into a single method, so cleanup must be done explicitly (typically via `try/finally`).
### Fix Focus Areas
- app/src/main/java/com/parallelc/micts/hooker/VIMSHooker.kt[34-52]
### What to change
- Capture the token: `val token = Binder.clearCallingIdentity()`.
- Ensure `Binder.restoreCallingIdentity(token)` runs in a `finally` block that covers:
- the `CSService` early-return branch
- the normal `chain.proceed()` path
- exception paths

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. CSMS hooks may be null 🐞 Bug ☼ Reliability
Description
CSMSHooker.startContextualSearch() force-unwraps enforcePermission/getContextualSearchPackageName
even though they are only initialized in CSMSHooker.hook(). If the stored preference selects
CSService while CSService support is absent (or CSMSHooker.hook failed/was skipped), the CSService
path will crash with KotlinNullPointerException.
Code

app/src/main/java/com/parallelc/micts/hooker/CSMSHooker.kt[R34-39]

       fun startContextualSearch(entryPoint: Int): Boolean {
-            var unhookers = mutableListOf<MethodUnhooker<Method>>()
+            var hooks = mutableListOf<HookHandle>()
           return runCatching {
-                unhookers += module!!.hook(enforcePermission!!, EnforcePermissionHooker::class.java)
-                unhookers += module!!.hook(getContextualSearchPackageName!!, GetCSPackageNameHooker::class.java)
+                hooks += module!!.hook(enforcePermission!!).intercept(EnforcePermissionHooker())
+                hooks += module!!.hook(getContextualSearchPackageName!!).intercept(GetCSPackageNameHooker())
Evidence
enforcePermission and getContextualSearchPackageName are nullable and only assigned in
hook(...), but startContextualSearch(...) uses !! to hook them. ModuleMain only calls
CSMSHooker.hook(...) when TriggerService.getSupportedServices() contains CSService, while
VIMSHooker dispatches to startContextualSearch solely based on the persisted preference ordinal.
Since CSHelper and CSService support are detected independently, this creates a real state mismatch
that can trigger the force-unwrapping crash.

app/src/main/java/com/parallelc/micts/hooker/CSMSHooker.kt[16-49]
app/src/main/java/com/parallelc/micts/hooker/VIMSHooker.kt[40-45]
app/src/main/java/com/parallelc/micts/ModuleMain.kt[38-53]
app/src/main/java/com/parallelc/micts/config/XposedConfig.kt[7-25]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`CSMSHooker.startContextualSearch()` assumes `CSMSHooker.hook()` successfully ran by force-unwrapping nullable `Method` fields. When the stored trigger-service preference points to `CSService` but CSMS initialization was skipped/failed (or CSService support is absent), this will crash.
### Issue Context
Support for `CSHelper` and `CSService` is determined by different runtime checks, and the trigger-service selection is persisted as an ordinal in preferences.
### Fix Focus Areas
- app/src/main/java/com/parallelc/micts/hooker/CSMSHooker.kt[16-49]
- app/src/main/java/com/parallelc/micts/hooker/VIMSHooker.kt[40-45]
- app/src/main/java/com/parallelc/micts/ModuleMain.kt[38-53]
- app/src/main/java/com/parallelc/micts/config/XposedConfig.kt[7-25]
### What to change
- In `CSMSHooker.startContextualSearch`, replace `enforcePermission!!` / `getContextualSearchPackageName!!` with a null-guard; if missing, log and return `false`.
- Optionally, before calling `startContextualSearch` in `VIMSHooker`, verify `TriggerService.CSService.isSupported` (or that CSMSHooker is initialized) and fall back to the non-CSService path when unsupported.
- Consider hardening the persisted preference: if the configured ordinal isn’t supported, coerce it to a supported default.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Hook leaks on exception 🐞 Bug ☼ Reliability
Description
VIMSHooker.ShowSessionHooker unhooks the temporary Resources.getString hook only after
chain.proceed() returns; if chain.proceed() throws, the unhook is skipped and the process-wide
Resources hook remains installed. This can cause persistent, hard-to-debug behavior changes beyond
the intended call.
Code

app/src/main/java/com/parallelc/micts/hooker/VIMSHooker.kt[R34-51]

+            override fun intercept(chain: Chain): Any? {
+                var hookHandle: HookHandle? = null
+                runCatching {
+                    val bundle = chain.args[1] as Bundle
+                    if (!bundle.getBoolean("micts_trigger", false)) return@runCatching null
+                    Binder.clearCallingIdentity()
+                    val triggerService = module!!.getRemotePreferences(CONFIG_NAME).getInt(KEY_TRIGGER_SERVICE, DEFAULT_CONFIG[KEY_TRIGGER_SERVICE] as Int)
+                    if (triggerService == TriggerService.CSService.ordinal) {
+                        return CSMSHooker.startContextualSearch(bundle.getInt("omni.entry_point"))
+                    } else {
+                        hookHandle = module!!.hook(Resources::class.java.getDeclaredMethod("getString", Int::class.java)).intercept(GetStringHooker())
                   }
-                    return null
-                }
-
-                @JvmStatic
-                @AfterInvocation
-                fun after(callback: AfterHookCallback, unhooker: MethodUnhooker<Method>?) {
-                    unhooker?.unhook()
+                }.onFailure { e ->
+                    module!!.log(Log.ERROR, "MiCTS", "hook resources fail", e)
               }
+                val result = chain.proceed()
+                hookHandle?.unhook()
+                return result
Evidence
The code installs a temporary hook into Resources.getString and unhooks it only on the normal return
path after chain.proceed(). There is no try/finally to guarantee cleanup if the original method
throws.

app/src/main/java/com/parallelc/micts/hooker/VIMSHooker.kt[34-51]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A temporary `Resources.getString(Int)` hook is installed but not guaranteed to be removed if `chain.proceed()` throws, leaving a process-wide hook active.
### Issue Context
With the v101 `intercept(chain)` style, cleanup must be done in a `finally` block to ensure it runs on both success and failure.
### Fix Focus Areas
- app/src/main/java/com/parallelc/micts/hooker/VIMSHooker.kt[34-51]
### What to change
- Wrap the `chain.proceed()` call in `try/finally` and always call `hookHandle?.unhook()` in `finally`.
- Combine with the Binder identity fix so both `restoreCallingIdentity(...)` and `hookHandle?.unhook()` are guaranteed even when exceptions occur.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Grey Divider

Previous review results

Review updated until commit ad66615

Results up to commit 696e796


🐞 Bugs (3) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX Issues (0)

Grey Divider
Action required
1. Identity not restored 🐞 Bug ⛨ Security
Description
VIMSHooker.ShowSessionHooker clears the Binder calling identity but never restores it, so the rest
of showSessionFromSession can execute under the wrong identity. The CSService early-return path also
bypasses any restoration, risking permission-check bypass and identity leakage for the transaction.
Code

app/src/main/java/com/parallelc/micts/hooker/VIMSHooker.kt[R34-52]

+            override fun intercept(chain: Chain): Any? {
+                var hookHandle: HookHandle? = null
+                runCatching {
+                    val bundle = chain.args[1] as Bundle
+                    if (!bundle.getBoolean("micts_trigger", false)) return@runCatching null
+                    Binder.clearCallingIdentity()
+                    val triggerService = module!!.getRemotePreferences(CONFIG_NAME).getInt(KEY_TRIGGER_SERVICE, DEFAULT_CONFIG[KEY_TRIGGER_SERVICE] as Int)
+                    if (triggerService == TriggerService.CSService.ordinal) {
+                        return CSMSHooker.startContextualSearch(bundle.getInt("omni.entry_point"))
+                    } else {
+                        hookHandle = module!!.hook(Resources::class.java.getDeclaredMethod("getString", Int::class.java)).intercept(GetStringHooker())
                    }
-                    return null
-                }
-
-                @JvmStatic
-                @AfterInvocation
-                fun after(callback: AfterHookCallback, unhooker: MethodUnhooker<Method>?) {
-                    unhooker?.unhook()
+                }.onFailure { e ->
+                    module!!.log(Log.ERROR, "MiCTS", "hook resources fail", e)
                }
+                val result = chain.proceed()
+                hookHandle?.unhook()
+                return result
            }
Evidence
The hook calls Binder.clearCallingIdentity() but does not store the returned token and never calls
Binder.restoreCallingIdentity(...). There is also an early return from intercept() (CSService
branch) after clearing identity, which skips any potential cleanup.

app/src/main/java/com/parallelc/micts/hooker/VIMSHooker.kt[34-52]
app/src/main/java/com/parallelc/micts/hooker/VIMSHooker.kt[37-45]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`Binder.clearCallingIdentity()` is called without restoring the identity token, including an early-return path. This can cause downstream code in the same Binder transaction to run with the wrong calling identity.

### Issue Context
In the new Xposed v101 `intercept(chain)` refactor, the previous before/after hook split was collapsed into a single method, so cleanup must be done explicitly (typically via `try/finally`).

### Fix Focus Areas
- app/src/main/java/com/parallelc/micts/hooker/VIMSHooker.kt[34-52]

### What to change
- Capture the token: `val token = Binder.clearCallingIdentity()`.
- Ensure `Binder.restoreCallingIdentity(token)` runs in a `finally` block that covers:
 - the `CSService` early-return branch
 - the normal `chain.proceed()` path
 - exception paths

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. CSMS hooks may be null 🐞 Bug ☼ Reliability
Description
CSMSHooker.startContextualSearch() force-unwraps enforcePermission/getContextualSearchPackageName
even though they are only initialized in CSMSHooker.hook(). If the stored preference selects
CSService while CSService support is absent (or CSMSHooker.hook failed/was skipped), the CSService
path will crash with KotlinNullPointerException.
Code

app/src/main/java/com/parallelc/micts/hooker/CSMSHooker.kt[R34-39]

        fun startContextualSearch(entryPoint: Int): Boolean {
-            var unhookers = mutableListOf<MethodUnhooker<Method>>()
+            var hooks = mutableListOf<HookHandle>()
            return runCatching {
-                unhookers += module!!.hook(enforcePermission!!, EnforcePermissionHooker::class.java)
-                unhookers += module!!.hook(getContextualSearchPackageName!!, GetCSPackageNameHooker::class.java)
+                hooks += module!!.hook(enforcePermission!!).intercept(EnforcePermissionHooker())
+                hooks += module!!.hook(getContextualSearchPackageName!!).intercept(GetCSPackageNameHooker())
Evidence
enforcePermission and getContextualSearchPackageName are nullable and only assigned in
hook(...), but startContextualSearch(...) uses !! to hook them. ModuleMain only calls
CSMSHooker.hook(...) when TriggerService.getSupportedServices() contains CSService, while
VIMSHooker dispatches to startContextualSearch solely based on the persisted preference ordinal.
Since CSHelper and CSService support are detected independently, this creates a real state mismatch
that can trigger the force-unwrapping crash.

app/src/main/java/com/parallelc/micts/hooker/CSMSHooker.kt[16-49]
app/src/main/java/com/parallelc/micts/hooker/VIMSHooker.kt[40-45]
app/src/main/java/com/parallelc/micts/ModuleMain.kt[38-53]
app/src/main/java/com/parallelc/micts/config/XposedConfig.kt[7-25]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`CSMSHooker.startContextualSearch()` assumes `CSMSHooker.hook()` successfully ran by force-unwrapping nullable `Method` fields. When the stored trigger-service preference points to `CSService` but CSMS initialization was skipped/failed (or CSService support is absent), this will crash.

### Issue Context
Support for `CSHelper` and `CSService` is determined by different runtime checks, and the trigger-service selection is persisted as an ordinal in preferences.

### Fix Focus Areas
- app/src/main/java/com/parallelc/micts/hooker/CSMSHooker.kt[16-49]
- app/src/main/java/com/parallelc/micts/hooker/VIMSHooker.kt[40-45]
- app/src/main/java/com/parallelc/micts/ModuleMain.kt[38-53]
- app/src/main/java/com/parallelc/micts/config/XposedConfig.kt[7-25]

### What to change
- In `CSMSHooker.startContextualSearch`, replace `enforcePermission!!` / `getContextualSearchPackageName!!` with a null-guard; if missing, log and return `false`.
- Optionally, before calling `startContextualSearch` in `VIMSHooker`, verify `TriggerService.CSService.isSupported` (or that CSMSHooker is initialized) and fall back to the non-CSService path when unsupported.
- Consider hardening the persisted preference: if the configured ordinal isn’t supported, coerce it to a supported default.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended
3. Hook leaks on exception 🐞 Bug ☼ Reliability
Description
VIMSHooker.ShowSessionHooker unhooks the temporary Resources.getString hook only after
chain.proceed() returns; if chain.proceed() throws, the unhook is skipped and the process-wide
Resources hook remains installed. This can cause persistent, hard-to-debug behavior changes beyond
the intended call.
Code

app/src/main/java/com/parallelc/micts/hooker/VIMSHooker.kt[R34-51]

+            override fun intercept(chain: Chain): Any? {
+                var hookHandle: HookHandle? = null
+                runCatching {
+                    val bundle = chain.args[1] as Bundle
+                    if (!bundle.getBoolean("micts_trigger", false)) return@runCatching null
+                    Binder.clearCallingIdentity()
+                    val triggerService = module!!.getRemotePreferences(CONFIG_NAME).getInt(KEY_TRIGGER_SERVICE, DEFAULT_CONFIG[KEY_TRIGGER_SERVICE] as Int)
+                    if (triggerService == TriggerService.CSService.ordinal) {
+                        return CSMSHooker.startContextualSearch(bundle.getInt("omni.entry_point"))
+                    } else {
+                        hookHandle = module!!.hook(Resources::class.java.getDeclaredMethod("getString", Int::class.java)).intercept(GetStringHooker())
                    }
-                    return null
-                }
-
-                @JvmStatic
-                @AfterInvocation
-                fun after(callback: AfterHookCallback, unhooker: MethodUnhooker<Method>?) {
-                    unhooker?.unhook()
+                }.onFailure { e ->
+                    module!!.log(Log.ERROR, "MiCTS", "hook resources fail", e)
                }
+                val result = chain.proceed()
+                hookHandle?.unhook()
+                return result
Evidence
The code installs a temporary hook into Resources.getString and unhooks it only on the normal return
path after chain.proceed(). There is no try/finally to guarantee cleanup if the original method
throws.

app/src/main/java/com/parallelc/micts/hooker/VIMSHooker.kt[34-51]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
A temporary `Resources.getString(Int)` hook is installed but not guaranteed to be removed if `chain.proceed()` throws, leaving a process-wide hook active.

### Issue Context
With the v101 `intercept(chain)` style, cleanup must be done in a `finally` block to ensure it runs on both success and failure.

### Fix Focus Areas
- app/src/main/java/com/parallelc/micts/hooker/VIMSHooker.kt[34-51]

### What to change
- Wrap the `chain.proceed()` call in `try/finally` and always call `hookHandle?.unhook()` in `finally`.
- Combine with the Binder identity fix so both `restoreCallingIdentity(...)` and `hookHandle?.unhook()` are guaranteed even when exceptions occur.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider Grey Divider

Qodo Logo

@parallelcc
Copy link
Copy Markdown
Owner Author

/review

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review bot commented Apr 4, 2026

Persistent review updated to latest commit ad66615

@parallelcc parallelcc merged commit 7ab83fd into main Apr 4, 2026
1 check passed
@parallelcc parallelcc deleted the feat/xposed_101 branch April 5, 2026 06:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

请求更新api

1 participant