- Goal: Android screenshot app with a persistent floating preview of the last screenshot.
- Requirement: when the user takes another screenshot, the floating preview must remain visible on the device display and must not appear in the new screenshot.
- Strong requirement: no visible blink or hide/show cycle while the new screenshot is taken.
- The target device is rooted with KernelSU.
- LSPosed is installed.
- Shizuku is available.
- Device: POCO F1 (
beryllium). - ROM: crDroid 11.9, LineageOS-derived.
- Android version: 15.
- This materially changes feasibility: private APIs and framework hooks are now realistic, even if they remain non-portable and version-sensitive.
SystemUI.apklives at/system_ext/priv-app/SystemUI/SystemUI.apk.TakeScreenshotServiceis declared inAndroidManifest.xmland runs in the:screenshotprocess.- This build contains the expected screenshot classes:
TakeScreenshotServiceTakeScreenshotExecutorTakeScreenshotExecutorImplImageCaptureImplPolicyRequestProcessorScreenshotControllerScreenshotShelfViewProxyScreenshotWindow
ImageCaptureImpl.captureDisplay(int, Rect)is confirmed to:- build
new ScreenCapture.CaptureArgs.Builder().setSourceCrop(rect).build() - call
IWindowManager.captureDisplay(displayId, captureArgs, listener) - return
listener.getBuffer()?.asBitmap()
- build
screenshot_shelf.xmlcontains@id/screenshot_preview_borderusing@drawable/overlay_border.ScreenshotWindowis a dedicated screenshot UI window titledScreenshotUIand created with system window type0x7f4.- Framework inspection on this ROM confirms:
WindowManager.LayoutParams.TYPE_SCREENSHOT = 2036ScreenCapture.CaptureArgs.Builder.setExcludeLayers(...)existsScreenCapture.captureLayersExcluding(...)existsDisplayContent.getLayerCaptureArgs(Set<Integer>)excludes windows by type by collecting theirSurfaceControlsWindowManagerService.takeAssistScreenshot(Set<Integer>)feeds that exclusion set into layer captureIWindowManagerdoes not directly exposetakeAssistScreenshot(Set<Integer>)
- On-device LSPosed prototype results confirm:
- the module loads in
com.android.systemui:screenshot ScreenshotShelfViewProxycan recolorscreenshot_preview_borderredImageCaptureImpl.captureDisplay(...)can be intercepted successfully- on the first screenshot, the screenshot shelf is not yet attached when capture runs
- on a rapid follow-up screenshot while the previous shelf is still visible,
PhoneWindow.getRootSurfaceControl()returns a live attached root and the hook can callsetExcludeLayers(...) - two saved screenshots from that repeated-shot test were byte-identical, which strongly indicates the previous visible screenshot shelf was excluded from the second saved image
- the visible shelf-clearing problem comes from
ScreenshotController.handleScreenshot(...)callingScreenshotShelfViewProxy.reset()before the new screenshot is fully ready - the current UX mitigation path is:
- on screenshot N+1, the module snapshots screenshot N's currently visible shelf with
PixelCopy - it displays that snapshot in a temporary transparent overlay positioned over the original shelf
- this covers the stock teardown/rebuild gap while the new shelf is prepared underneath
- on screenshot N+1, the module snapshots screenshot N's currently visible shelf with
- a stacked-shelf prototype is also now implemented inside the stock shelf:
screenshot_preview_bluris reused as the first rear card- one extra synthetic rear card is inserted behind it for 3-shot bursts
screenshot_badgeis repurposed as a count badge while the batch is active- the stack survives stock
removeWindow()during screenshot reentry and resets on non-reentry dismissal/timeout
- LSPosed logs on-device confirm that the continuity overlay is added during screenshot N+1 on this ROM
- the module loads in
- screenshot_plugin.c shows the X11/compositor-style solution already used elsewhere.
- That approach works because the compositor can decide what enters the captured composition and can exclude the thumbnail overlay during capture.
- Public full-screen capture is done with
MediaProjection. - On Android 14+,
MediaProjectioncan also capture a single app window instead of the whole display.
- A normal app can show a floating overlay with
TYPE_APPLICATION_OVERLAYif it hasSYSTEM_ALERT_WINDOW. - An accessibility service can use
TYPE_ACCESSIBILITY_OVERLAY.
FLAG_SECUREprevents a window from appearing in screenshots or non-secure displays.View.setContentSensitivity(...)can cause a window to become secure during media projection.
- Public
MediaProjectioncaptures a display or app window, not "everything except these specific overlay layers." - Public overlay APIs let an app draw above other apps, but they do not provide a public exclusion list for screenshots.
FLAG_SECUREhides the secure window's content from capture, but the public docs do not describe it as a compositor filter that reveals the pixels behind that window.- Inference from AOSP internals: true layer exclusion is handled by private/system capture APIs, which exist specifically because secure-content handling is not the same as "subtract this overlay and keep the underlying scene."
- AOSP
DisplayContent.getLayerCaptureArgs(...)builds capture args and callssetExcludeLayers(...)for matching windows. - Hidden/internal
ScreenCaptureAPIs support excluding specificSurfaceControllayers from capture.
- AOSP contains platform behavior where windows marked with
PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAYare excluded from screenshots by default. - This is very close in spirit to the requested feature: a visible overlay layer that is not part of the captured result.
- Feasible:
- capture screenshots with
MediaProjection - show the last screenshot in a floating overlay
- update that overlay without blinking by double-buffering the app's own UI
- capture screenshots with
- Not feasible through public SDK alone:
- keep the overlay visible on the physical display
- exclude it from full-display screenshots
- preserve the pixels underneath that overlay area
- Feasible in principle:
- run the screenshot feature as a privileged/system component
- own the overlay surface in SystemUI or another platform-signed component
- mark or track that surface
- exclude it in the screenshot capture path using internal exclude-layer support
- This is the Android architecture that best matches the existing X11 compositor-plugin approach.
- Feasible in principle:
- hook screenshot initiation in SystemUI or framework code using LSPosed
- create or control the preview overlay from a privileged context
- route capture through hidden/internal screenshot APIs that support excluded layers
- or patch capture args before the screenshot is taken
- This is likely the best path for the stated device environment because it does not require shipping a full custom ROM, but still permits framework-level behavior.
- For Android 15 specifically, the likely hook surface is SystemUI's screenshot pipeline rather than the older pre-refactor controller classes.
- On this specific build, the
com.android.systemui:screenshotprocess is the primary hook target. - The lowest-risk first prototype is now: hook the screenshot process only, fetch the screenshot window's live
SurfaceControl, and inject it intoCaptureArgs.setExcludeLayers(...)before calling the existingIWindowManager.captureDisplay(...). - The prototype has now validated that this works for screenshot N+1 on crDroid 15 when the prior screenshot shelf is already attached.
- Use
MediaProjectionfor capture. - Use
TYPE_APPLICATION_OVERLAYfor the floating preview. - Keep the overlay visible between screenshots.
- Accept that exact filtering is not available publicly.
- Value: shippable without OS modification.
- Cost: does not meet the exact no-overlay-in-screenshot requirement.
- Put the thumbnail overlay in a secure window.
- Test what the screenshot contains in the covered region.
- This may hide the thumbnail itself from capture, but it is not yet validated as a correct "show underlying pixels instead" solution.
- Treat this as an experiment, not an assumed fix.
- Use app-window sharing instead of full-display sharing.
- Keep the thumbnail overlay outside the selected target app window.
- This can avoid capturing some non-target UI, but it is not equivalent to a normal device-wide screenshot.
- Add the screenshot preview as a system-managed overlay surface.
- Exclude that surface from the capture args.
- Update the preview atomically so the on-screen thumbnail never disappears while a new screenshot is captured.
- This is the only path found so far that matches the requirement precisely.
- Hook the stock screenshot flow in SystemUI or window manager code.
- Keep the preview surface owned by a privileged or hooked component.
- Use hidden screenshot APIs or patched capture args to exclude the preview layer.
- Use Shizuku if binder access is needed from the app side.
- Value: much closer to exact behavior on the target device without building a full ROM.
- Cost: Android-version fragility, private API churn, SELinux / permission edge cases, and more maintenance than an ordinary app.
- On Android 15, also consider reusing the existing SystemUI screenshot shelf UI instead of drawing a second independent overlay. That minimizes flicker risk and makes it easy to add a red diagnostic border while testing hooks.
- Because the device is rooted, audit SystemUI/framework screenshot flow first.
- Identify the exact classes/methods used on the target Android version for screenshot initiation and capture arg construction.
- Build a tiny rooted spike that proves one of these:
- an LSPosed hook can exclude a known overlay layer from capture
- a Shizuku-backed privileged call can capture while excluding that layer
- This LSPosed proof now exists for the stock
ScreenshotUIwindow on screenshot N+1. - Keep the secure-overlay test as a fallback, not the primary plan.
- Inspect the real crDroid/Lineage
SystemUI.apkto confirm whether AOSP class names still match the expected Android 15 pipeline. - Next concrete check: inspect framework and services jars on-device to see which window types or titles can be excluded through the window manager capture path, and whether
ScreenshotUIcan be filtered wholesale. - Next concrete implementation task: harden the prototype for production use, reduce diagnostic logging, and verify the remaining UX requirement that the previous shelf stays continuously visible with no blink during screenshot N+1.
- Next concrete implementation task: validate the new stacked-shelf rendering on-device for 2-shot and 3-shot bursts and decide the first batch interaction model.
- Android Developers: Media projection
- Android Developers:
WindowManager.LayoutParams - Android Developers:
View - Android Developers:
AccessibilityWindowInfo - AOSP:
DisplayContent.getLayerCaptureArgs(...)withsetExcludeLayers(...) - AOSP:
WindowManagerService.takeAssistScreenshot(...) - AOSP hidden API:
ScreenCapturelayer exclusion support - AOSP native capture args: exclude handles
- AOSP commit note: rounded-corner overlays excluded from screenshots by default