- Device: POCO F1 (
beryllium) - ROM: crDroid 11.9
- Base: LineageOS-derived
- Android: 15
- Root/tooling: KernelSU, LSPosed, Shizuku
- Intercept the stock Android screenshot flow.
- Keep the screenshot preview visible on screen across repeated screenshots.
- Ensure the preview layer is excluded from future screenshots.
- Add a red border around the floating screenshot while the hook-based path is active.
- Reuse the stock SystemUI screenshot experience instead of building an unrelated overlay stack first.
- Hook the Android 15 SystemUI screenshot pipeline with LSPosed.
- Change the actual capture step so the preview layer is excluded by layer, not by hiding the UI.
- Mark the hooked mode visually by recoloring the existing screenshot preview border to red.
- The stock screenshot pipeline already handles gesture/key-chord initiation, sound, save/share/edit flow, dismissal, and animation timing.
- Reusing the existing screenshot shelf reduces UI work and reduces flicker risk.
- Android internal capture APIs already support excluded layers, which is the capability the X11 compositor plugin relied on conceptually.
- LSPosed is a better fit than a standalone app for this because the hook target is inside SystemUI and framework code.
TakeScreenshotServicereceives screenshot requests from the system.
TakeScreenshotExecutororTakeScreenshotExecutorImplselects the display and hands the request to the active screenshot handler.
RequestProcessor/ScreenshotRequestProcessorcan rewrite screenshot requests before capture.
ImageCaptureImpl.captureDisplay(...)callsIWindowManager.captureDisplay(...)usingScreenCapture.CaptureArgs.
ScreenshotShelfViewProxycontrols the screenshot shelf view shown to the user.- That code references
R.id.screenshot_preview_border, which is a direct anchor for the requested red border indicator.
- The actual device
SystemUI.apkconfirms all of these classes are present on-device, not just in AOSP references. TakeScreenshotServiceis declared inAndroidManifest.xmlwithandroid:process=":screenshot".ImageCaptureImpl.captureDisplay(int, Rect)is confirmed to callIWindowManager.captureDisplay(...)with aScreenCapture.CaptureArgs.Builder.ScreenshotWindowis real on this build and hosts the screenshot UI in a dedicated window titledScreenshotUI.screenshot_shelf.xmlcontains the existing preview border view@id/screenshot_preview_border.WindowManager.LayoutParams.TYPE_SCREENSHOT = 2036on this device framework.ScreenCapture.CaptureArgs.Builder.setExcludeLayers(...)is present in the device framework.DisplayContent.getLayerCaptureArgs(Set<Integer>)excludes windows by collecting matching windows'SurfaceControls.IWindowManagerdoes not directly expose the typedtakeAssistScreenshot(Set<Integer>)path; it does exposecaptureDisplay(...).- Live-device prototype findings:
- the LSPosed module loads in
com.android.systemui:screenshot ScreenshotShelfViewProxyborder recolor works- on screenshot N, the new screenshot shelf is not yet attached when
captureDisplay(...)runs - on screenshot N+1, the previous screenshot shelf is attached and reachable through
PhoneWindow.getRootSurfaceControl() - the hook can rebuild capture args with
setExcludeLayers(...)and successfully interceptcaptureDisplay(...) - the saved outputs from the repeated-shot test were byte-identical, strongly indicating the previous screenshot shelf was excluded from screenshot N+1
- the LSPosed module loads in
- Replace or wrap the call so capture args are built with excluded layers.
- Advantage: narrow hook close to the actual screenshot capture step.
- Risk: may still need a way to resolve the preview surface into a
SurfaceControlor compatible handle. - Status on this build: confirmed method exists exactly where expected.
- New status: this is now the preferred first implementation path.
- Intercept the call and redirect to hidden/internal layer-capture APIs.
- Advantage: centralizes capture behavior.
- Risk: binder interfaces and argument shapes are more fragile and harder to patch cleanly from LSPosed.
- On this build, this is probably unnecessary for the first prototype because
CaptureArgsitself already supportssetExcludeLayers(...).
- Intercept the framework path that creates
LayerCaptureArgsor equivalent internal capture args and inject the preview layer intoexcludeLayers. - Advantage: most faithful to Android's own internal exclusion model.
- Risk: this may require framework-process hooks rather than SystemUI-only hooks.
- Keep the preview inside stock SystemUI screenshot UI and identify its underlying window/surface.
- Because this build uses a dedicated
ScreenshotWindowtitledScreenshotUI, first test whether excluding that entire window is sufficient. - If excluding the whole screenshot UI window works, it is cleaner than tracking child surfaces.
- Current best bet: use the attached
ScreenshotWindowdecor view to retrieve its liveSurfaceControl, then exclude that surface during the next capture.
- Create a separate privileged overlay window managed by the hook module.
- Track its
SurfaceControlor window token. - Exclude that surface from capture.
- This is mechanically simpler in some cases, but risks visual mismatch with stock screenshot UI.
- Do not create a second debug overlay just for the border.
- Instead, modify the existing screenshot shelf border view inside SystemUI.
- The AOSP screenshot UI already exposes a preview border view via
R.id.screenshot_preview_border. - Hook that view after inflate/bind and recolor it red while hook mode is enabled.
- This gives a direct visual signal that the rooted hook path is active.
- Package hooks for:
com.android.systemui- possibly
android/ system_server if lower framework hooks are needed
- Small config store for:
- hook enabled/disabled
- red-border enabled
- logging level
- maybe selected capture strategy
- Provides settings UI, gallery/history, export/share, and diagnostics.
- Talks to the hook side over binder/content provider/file-based config.
- Shizuku can be used if the companion app needs privileged commands outside the hooked process.
- Hook the
com.android.systemui:screenshotprocess. - Hook
ScreenshotShelfViewProxyor nearby screenshot shelf UI code. - Confirm the border view can be recolored red reliably.
- Hook
ImageCaptureImpl.captureDisplay(...)and log when a screenshot is initiated. - Retrieve the live
SurfaceControlfor the attachedScreenshotUIwindow. - Rebuild the capture args with
setExcludeLayers(new SurfaceControl[]{ screenshotUiSurface }). - Call the existing
IWindowManager.captureDisplay(...). - Validate repeated screenshots while the previous preview remains visible.
- Steps 1 through 7 are now implemented in the LSPosed proof-of-concept module.
- Step 8 is partially validated:
- repeated screenshot capture used the attached previous
ScreenshotUIsurface - the saved screenshot pair from that test was byte-identical
- repeated screenshot capture used the attached previous
- The current UX-focused reentry fix is:
- on screenshot N+1, capture the visible screenshot shelf with
PixelCopy - place that snapshot into a short-lived continuity overlay window at the same bounds
- let stock SystemUI tear down and rebuild underneath while the user keeps seeing the old shelf snapshot
- on screenshot N+1, capture the visible screenshot shelf with
- A first stacked-shelf prototype now rides on top of that continuity path:
- reuse
R.id.screenshot_preview_bluras the first rear card - add one synthetic rear card for 3-shot bursts
- replace the stock badge slot with a numeric batch badge while the stack is active
- preserve the batch across stock
ScreenshotWindow.removeWindow()during screenshot reentry
- reuse
- Remaining validation:
- visually confirm there is no blink on the physical display during screenshot N+1
- visually confirm the stacked shelf looks correct for 2-shot and 3-shot bursts
- check whether excluding the whole
ScreenshotUIwindow removes any stock controls that should remain
- Taking screenshot N+1 does not visually hide screenshot N's preview.
- Screenshot N+1 does not contain screenshot N's preview.
- The preview remains interactive and visually stable.
- The red border appears only when the hook is active.
- No bootloop or SystemUI crash loop after enabling the module.
- crDroid/Lineage may have renamed or refactored AOSP classes.
- The screenshot shelf may not correspond to a conveniently excludable surface.
- Hidden API signatures may differ across Android 15 builds.
- Excluding the whole screenshot UI window may remove more than just the preview, which could or could not be acceptable.
- Some strategies may require hooks in both SystemUI and system_server.
SystemUI.apkinspection is done.- Framework/services inspection is done.
- First LSPosed module is built and verified on-device.
- Next: polish the prototype into a more robust module and validate the remaining UX details on the physical display.