- Started the first actually functional editor pass after the single-task batch handoff stabilized:
- replaced the dead editor shell with working
Done/Shareactions, interactive crop handles, live pen markup, and visible color swatches in MarkupEditorActivity.java, MarkupEditorStageView.java, activity_markup_editor.xml, and strings.xml Donenow opens a save/delete/cancel dialog instead of closing immediatelySharenow saves the current edited image back to the screenshotUriand opens the Android share sheet- crop handles now drag corners, edges, and the crop body itself, and export uses the cropped result instead of the full image
- pen strokes are now rendered/exported in bitmap-relative coordinates so markup persists correctly through crop/export
- the launch handoff now grants write permission in addition to read permission so edited screenshots can be overwritten or deleted from the editor
- replaced the dead editor shell with working
- Updated TODO.md with the next requested work:
- add a companion/settings UI inside the same APK/package
- add configurable auto-dismiss and drag-to-dismiss shelf behavior
- investigate
JXL/JPEG XL as the default screenshot save format and define when edited outputs should fall back toPNG - queue more iOS-style editor features such as undo/redo, text/signature/magnifier, and richer shape tools
- Investigated why the markup editor still felt slow to appear even after switching burst handling back to immediate open:
- measured a real tap-to-launch delay of roughly
3.2sbetweenPreview tap interceptedandLaunched markup editor - confirmed the delay was no longer caused by the old hold threshold or export-settling logic
- determined that launching the editor directly from the overloaded
com.android.systemui:screenshotprocess still left too much handoff latency during screenshot bursts
- measured a real tap-to-launch delay of roughly
- Tried an app-process broadcast trampoline first, but Android 15 background-activity-launch hardening blocked the receiver from starting
MarkupEditorActivity - Replaced that with a transparent exported trampoline activity:
- ScreenshotHooks.java now starts
MarkupEditorLaunchActivityinstead of launching the full editor directly - MarkupEditorLaunchActivity.java immediately forwards the existing batch payload,
ClipData, andUrigrants intoMarkupEditorActivityinside the app process, then finishes with no visible chrome - AndroidManifest.xml now declares the trampoline activity as exported, translucent, no-history, and excluded from recents
- ScreenshotHooks.java now starts
- Verified autonomously on-device after rebuilding, reinstalling, and restarting
SystemUI:Preview tap interceptedlogged at03-30 02:30:11.255MarkupEditorActivitywas reported as displayed at03-30 02:30:11.882- effective tap-to-visible time dropped to about
0.63s MarkupEditorActivitybecame the top resumed activity- the markup editor process was force-stopped after the timing run to keep the test isolated
- Changed the markup-editor burst UX from delayed launch to immediate launch with live batch extension:
- removed the pre-launch wait loop from ScreenshotHooks.java
- kept the single-task editor reuse path and now refresh the running
MarkupEditorActivitywhen late screenshot exports from the same burst finish saving - preserved saved-batch tracking across editor-triggered shelf dismissal so late exports can still join the existing editor batch
- Verified autonomously on-device:
- the editor opens immediately after tapping the screenshot stack
- follow-up
ImageExportercompletions logRefreshed markup editor batch after late export - the running editor updates its subtitle/batch payload without spawning a second activity instance
- Investigated rapid-burst stack tracking for the markup editor after a report that very fast screenshot bursts could open with fewer items than expected:
- reproduced the race on-device by firing a 10-shot burst and opening the editor immediately, which initially produced cases like
9 of 10and5 of 10 - root cause was twofold in ScreenshotHooks.java:
- the editor could launch before all
ImageExporterresults for the current burst had been registered and saved - once the shelf was dismissed for editor handoff, late exports from that same burst were no longer able to extend the already-open editor batch cleanly
- the editor could launch before all
- hardened the handoff by:
- tracking current-burst capture count, saved-
Uricount, pending export count, and recent batch activity in HookState.java - delaying initial editor launch until the burst has settled or the timeout is reached
- preserving batch tracking across editor-triggered
ScreenshotWindow.removeWindow() - refreshing the single-task
MarkupEditorActivitywith an updated batch when late exports arrive after the initial launch
- tracking current-burst capture count, saved-
- reproduced the race on-device by firing a 10-shot burst and opening the editor immediately, which initially produced cases like
- Verified autonomously on-device after rebuild/reinstall/restart:
- a previously failing immediate-open burst case now refreshed the editor up to
9 screenshots, matching the9savedUris actually tracked in that run - the logs now show
Refreshed markup editor batch after late export, confirming that late saves are propagated into the already-open editor - the editor process was force-stopped after each automated burst test to keep runs isolated
- a previously failing immediate-open burst case now refreshed the editor up to
- Investigated and fixed a post-editor shelf-position regression in the stacked screenshot preview:
- reproduction showed the thumbnail could shift partly off-screen after opening the markup editor, closing it, and taking screenshots again
- root cause was the
ScreenshotWindow.removeWindow()hook treating the window as disposable even though this ROM reuses the sameScreenshotWindow/ shelf objects after editor handoff - clearing the cached window reference broke later excluded-layer capture lookup, and deferring
clearStackUi(...)withshelfView.post(...)let stale preview transform state survive into the reused shelf - fixed ScreenshotHooks.java to preserve the cached
ScreenshotWindowacrossremoveWindow()and clear the stacked UI synchronously when the editor dismisses the shelf
- Verified autonomously on-device after rebuilding, reinstalling, and restarting
SystemUI:- baseline repeated screenshots and post-editor repeated screenshots now land in the same lower-left position again
- the follow-up screenshot after leaving the editor once again resolves the cached
ScreenshotWindowsurface and uses the excluded-layer capture path - the markup editor process was force-stopped after each automated test run to avoid cross-test contamination
- Finished the first batch-aware markup-editor handoff and navigation pass:
ScreenshotHooks.javanow forwards the full active screenshot batch intoMarkupEditorActivity, grants read access for everyUrivia batchClipData, and dismisses the SystemUI screenshot shelf once the editor takes overMarkupEditorActivity.javanow reuses a single task instance, accepts batch extras inonNewIntent(...), and supports left/right swipe paging across the saved screenshotsHookState.javanow exposes a copy of the active saved-screenshotUrilist so the editor can preserve stack context after the shelf is dismissed
- Verified autonomously on-device after rebuilding, reinstalling, and restarting
SystemUI:- opening the editor from a 2-shot stack removes
ScreenshotUIfrom the active window list - the editor subtitle reports
Showing 1 of 2on open - a left swipe advances to
Showing 2 of 2without the previous decode failure - repeated launch attempts do not create a second
MarkupEditorActivityinstance; the same task/activity record remains active
- opening the editor from a 2-shot stack removes
- Followed up on the markup editor task behavior after device testing showed it still appeared in recents:
- kept
android:excludeFromRecents="true"on the activity - also added
Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTSat launch time in ScreenshotHooks.java so the task is marked correctly even when started fromSystemUI
- kept
- Re-enabled markup editor launch on short tap in ScreenshotHooks.java after the tap interception path was validated on-device.
- Preserved the hold gesture as the explicit route to the stock chooser, now with the tuned
500 msthreshold. - Reduced the deliberate chooser hold threshold from
2500 msto500 msin ScreenshotHooks.java after follow-up tuning. - Fixed the broken tap-interception hook in ScreenshotHooks.java:
- the earlier
ScreenshotShelfView.dispatchTouchEvent(...)hook was failing on-device withNoSuchMethodError - replaced it with a
View.dispatchTouchEvent(...)hook filtered toscreenshot_previewandscreenshot_scrolling_scrim - this avoids relying on a method that is inherited rather than declared on this ROM's shelf view class
- the earlier
- Removed module-resource lookup from the short-tap toast path:
Toast.makeText(..., R.string.preview_hold_for_actions, ...)was throwingResources$NotFoundExceptioninside thecom.android.systemui:screenshotprocess- switched the temporary fallback toast to a literal string so it works reliably from the hooked process context
- Rebuilt, reinstalled, and restarted
SystemUI, then verified autonomously over ADB:- short tap on the shelf touch region now logs
Preview tap interceptedandPreview tap consumed; waiting for deliberate hold before stock actions - a
3000 msstationary swipe on the same region logsPreview hold reached chooser threshold at 2503ms - that long hold then launches
android/com.android.internal.app.ResolverActivity, confirming the stock chooser path is still reachable only via deliberate hold
- short tap on the shelf touch region now logs
- Increased the stock screenshot shelf timeout override from
5000 msto15000 msin ScreenshotHooks.java so the thumbnail remains visible long enough for autonomous ADB-driven interaction tests. - Investigated why tapping the stacked screenshot shelf still opened the stock chooser instead of the custom editor/toast path.
- Root cause found in the interaction hooks:
- touch duration was being recorded in
ScreenshotShelfView.dispatchTouchEvent(...) - but the stock click path was still allowed to run, so short taps could still reach the chooser
- touch duration was being recorded in
- Reworked ScreenshotHooks.java to use a stricter fallback interaction model:
- preview/scrim touch sequences are now consumed directly in the shelf touch hook
- short tap shows a toast hint instead of falling through to stock actions
- stock chooser is only synthesized after a deliberate
2500 mshold on the preview stack - kept the
PreviewActionwrapper as a second guard so unplanned short-click paths are still intercepted
- Added strings.xml text for the temporary tap hint:
Tap ignored. Hold for actions. - Re-checked repository state before work. Untracked device artifacts remain:
- Added the first markup-editor implementation:
- created MarkupEditorActivity.java
- created MarkupEditorStageView.java
- added activity_markup_editor.xml
- registered the editor activity in AndroidManifest.xml
- Replaced the debug short-tap toast path in ScreenshotHooks.java with editor launch for the latest saved screenshot
Uri, while keeping long-press routed to the stock chooser/open action. - The first editor cut intentionally keeps scope narrow:
- only renders the saved screenshot on a dedicated stage
Donesimply closes the activity- bottom editing tools are placeholders only
- crop handles are currently visual only
- Documented the markup editor direction in docs/markup-editor-architecture.md, including the choice to defer Lua/JS scripting and use a native view plus structured edit-document model first.
- Re-checked repository state before work. Untracked device artifacts remain:
- Re-read the current project docs and inspected the in-progress LSPosed hook changes before editing.
- Continued the screenshot-batch deletion path in the rooted prototype:
- finished batch-aware saved-URI tracking in HookState.java
- registered each
ImageExporter.export(...)future against the current screenshot batch - treated a screenshot with no attached prior shelf as the start of a fresh batch in ScreenshotHooks.java
- preserved left-swipe explicit dismissal behavior so already-saved screenshots are deleted immediately and late export completions from that same dismissed batch are also deleted
- Attempted verification with
./gradlew :app:assembleDebug, but sandbox limits prevented a full compile pass:- default Gradle home hit a permission error on the wrapper lock under
/home/duda/.gradle - rerunning with
GRADLE_USER_HOME=/tmp/gradle-homethen failed because the sandbox blocks the wrapper's network download - direct execution of the locally installed Gradle binary also failed in this environment while loading
libnative-platform.so
- default Gradle home hit a permission error on the wrapper lock under
- Followed up with full-permission local tooling after the user confirmed Gradle and ADB access:
- built the module successfully with
./gradlew :app:assembleDebug - installed app-debug.apk to
192.168.2.56:5555 - restarted
SystemUIon-device so the updated LSPosed hook would reload
- built the module successfully with
- Added README.md with:
- current rooted-architecture and prototype status
- build/install instructions
- Android platform-constraint summary using the local offline docs mirror
- a feature map for the observed iOS screenshot UX and rough implementation difficulty on this LSPosed path
- Updated module identity per user request:
- renamed the Java package / namespace / application id to
fuck.iosstackingscreenshots.droidvendorssuck - changed the visible app label to
iOS Stacking Screenshots - updated
xposed_initand rebuilt successfully - reinstalled the renamed APK and restarted
SystemUI
- renamed the Java package / namespace / application id to
- Revised README.md again:
- removed local offline-doc references in favor of public Android documentation URLs
- added a short quick-install section
- Verified and fixed LSPosed state for the renamed package:
- confirmed the old package had been removed from the device
- inspected
/data/adb/lspd/config/modules_config.db - found the renamed module installed but disabled and missing
com.android.systemuiscope - enabled
fuck.iosstackingscreenshots.droidvendorssuckand insertedcom.android.systemuiuser0scope - restarted
SystemUIafter the scope change
- Added a tap-to-toast debug interaction in ScreenshotHooks.java:
- tapping the front screenshot preview now shows a short toast
- the toast includes burst count when multiple screenshots are stacked
- Rebuilt and reinstalled the updated APK, then restarted
SystemUIagain. - Expanded README.md into a zero-to-active ADB deployment guide, including:
- build/install steps
- old-package cleanup
- LSPosed activation and scope setup
SystemUIrestart instructions- when a full reboot may still be necessary
- Re-checked repository state before work. Untracked device artifacts remain:
- Re-read the current project docs and inspected the LSPosed screenshot shelf hook before changing dismissal behavior.
- Added a shelf inactivity timeout to ScreenshotHooks.java:
- new screenshots now reset a 5-second auto-dismiss timer
- touches on the screenshot shelf also reset that timer
- when the timer expires, the hook clears the custom stacked state and calls
ScreenshotWindow.removeWindow()so the shelf disappears using the existing teardown path
- Followed up on a regression in second-shot interaction after the first timeout implementation:
- confirmed the screenshot hook lives in
com.android.systemui:screenshot, not the maincom.android.systemuiprocess - verified the timeout firing through LSPosed logs after restarting the correct screenshot process
- removed the extra shelf
dispatchTouchEventhook and stopped suppressingonTouchOutside, so touch handling returns to stock while keeping the delayed stock-timeout auto-dismiss path
- confirmed the screenshot hook lives in
- Reworked the auto-dismiss implementation again after the delayed-dismiss approach still left a half-dismissed, input-blocking shelf window:
- decompiled the local
SystemUI.apkwithjadxand confirmedScreenshotControllersetsscreenshotHandler.mDefaultTimeout = 3000 - changed the LSPosed hook to override that stock timeout to
5000 msinScreenshotControllerconstruction instead of interceptingSCREENSHOT_INTERACTION_TIMEOUT - removed the custom delayed-dismiss helper so stock dismissal animation and input teardown run on the normal SystemUI path
- decompiled the local
- Started swipe-to-delete behavior for active screenshot batches:
- hooked
ImageExporter.export(...)to track the savedUrifor each screenshot in the active SystemUI batch - detect
SCREENSHOT_EXPLICIT_DISMISSALwith negative swipe velocity as a left-swipe delete gesture - on left-swipe dismissal, delete the tracked batch
Uris throughContentResolverand also delete any late-arriving export result from the same batch
- hooked
- Re-checked repository state before work. Untracked device artifacts remain:
- Re-read the current project docs and rooted-hook implementation notes before touching the screenshot shelf renderer.
- Investigated a visual defect in the screenshot thumbnail where transparent pixels in the preview were revealing the white frame background, most visibly along the right edge.
- Fixed ScreenshotHooks.java by changing the shared card background helper from a fully white fill to a two-layer card:
- white outer frame and stroke
- black inner fill behind the actual screenshot content
- This change applies to both the main front thumbnail and the composed stacked-card bitmaps, so alpha/empty regions should now read as black instead of leaking white through the body of the card.
- Rebuilt the LSPosed module with
./gradlew assembleDebug, installed the updated APK to192.168.2.56:5555, and restartedSystemUI. - Followed up on residual alpha bleed between stacked cards by making the card frame drawable fully opaque:
- added a black base layer under the white frame so rounded corners no longer remain transparent
- Forced the composed card bitmap content area to be opaque by pre-filling the screenshot draw rect with black before drawing the bitmap.
- Tuned the card frame base layer to match the white frame fill to avoid dark halos at rounded corners while keeping the inner content rect black.
- Re-checked repository state before work. Untracked device artifacts remain:
- Re-read the current project docs and rooted-hook implementation notes before touching the Android build/deploy flow.
- Investigated a new repeated-shot rendering bug reported on-device:
- screenshot 1 preview looked correct
- screenshot 2 preview looked like screenshot 1 with partial white background
- screenshot 3 preview looked like screenshot 2 with partial white background
- Reproduced the issue on-device with ADB-assisted
screencapdumps and LSPosed hook logs after each screenshot state. - Narrowed the failure to the custom front-preview composition path in ScreenshotHooks.java: the hook was replacing the stock
screenshot_previewimage with a newly composed framed bitmap on each update. - Fixed the front-preview path by stopping that custom bitmap rewrite:
- leave
screenshot_previewbound to the stock/current screenshot bitmap - restore and style the stock
screenshot_preview_borderview instead of hiding it
- leave
- Rebuilt the LSPosed module with
./gradlew assembleDebug, installed the updated APK, restartedSystemUI, and cleared logs. - Re-ran a fresh 3-shot sequence on-device after the patch.
- Verified from screen dumps and hook logs that:
- screenshot 2 now shows the current second screenshot in the lower-left preview instead of a stale screenshot 1 front card
- screenshot 3 now shows the current third screenshot in the lower-left preview instead of a stale screenshot 2 front card
- the rear stack still advances as expected (
updateStackUi: stack=1 total=2, thenstack=2 total=3)
- Also extended the device screen timeout over ADB during testing to keep the display awake across repeated manual captures.
- Re-checked repository state before work. Untracked device artifacts remain:
- Re-read the current project docs and rooted-hook implementation notes before touching the Android build/deploy flow.
- Verified the current tree already builds successfully with
./gradlew assembleDebug. - Confirmed the connected target device is still the rooted POCO F1 at
192.168.2.56:5555. - Installed the current debug APK to the device with
adb -s 192.168.2.56:5555 install -r app/build/outputs/apk/debug/app-debug.apk. - Investigated a reported stacked-shelf ordering bug where older screenshots were painting above newer rear cards.
- Fixed the draw order in ScreenshotHooks.java by inserting rear-card overlay drawables from deepest/oldest to newest so the newer card remains visually on top.
- Rebuilt the LSPosed module with
./gradlew assembleDebug. - Installed the updated debug APK to the device and restarted
SystemUIso thecom.android.systemui:screenshotprocess would reload the hook. - Investigated a follow-up report that the second-newest screenshot still appeared on top immediately after capture.
- Root cause: the continuity overlay was still showing a snapshot of the previous shelf for
1200 ms, so it visually covered the freshly updated shelf even after the new screenshot had already been bound underneath. - Fixed the continuity-overlay handoff in ScreenshotHooks.java so the old overlay is removed shortly after
setScreenshot(...)updates the new preview, while keeping the longer timeout as a fallback safety path. - Rebuilt, reinstalled, and restarted
SystemUIagain so the new handoff behavior is live on-device. - Investigated a final report that the previous screenshot could still appear on top even after the shorter handoff delay.
- Root cause:
PixelCopyis asynchronous, so the old-shelf continuity overlay could still be added aftersetScreenshot(...)had already bound the new preview. In that order, the previous screenshot would be reintroduced above the newest one and only disappear later. - Fixed the race by tracking whether the new preview has already bound during the active reentry window and refusing to add a stale continuity overlay after that point.
- Rebuilt, reinstalled, and restarted
SystemUIagain so the stale-overlay race fix is now live on-device.
- Inspected repository state. Found only one untracked file: screenshot_plugin.c.
- Read the existing X11 compositor plugin as the conceptual reference: it keeps a thumbnail visible while excluding it during subsequent captures by controlling composition.
- Investigated Android public APIs and AOSP internals.
- Conclusion: exact "visible overlay but filtered out from full-screen capture without flicker" behavior is not available to ordinary third-party apps through the public SDK.
- Documented the likely split between:
- stock-app approach using
MediaProjectionand overlay windows, with compromises - system/OEM approach using private screenshot APIs with excluded layers
- stock-app approach using
- Added repo instructions so future LLMs start by checking uncommitted files and keep documentation current.
- New constraint discovered: target device is rooted with KernelSU and also has LSPosed and Shizuku available.
- This makes rooted/privileged designs first-class options and substantially reduces the need to constrain the design to public SDK APIs.
- Additional device detail: POCO F1 (
beryllium), crDroid 11.9, Android 15, LineageOS-based. - Investigated modern Android 15 AOSP screenshot flow:
TakeScreenshotServicereceives requestsTakeScreenshotExecutordispatchesScreenshotRequestProcessor/RequestProcessorcan rewrite requestsImageCaptureImpl.captureDisplay(...)currently callsIWindowManager.captureDisplay(...)- internal WM capture paths support
setExcludeLayers(...)
- Found that modern SystemUI screenshot UI includes a dedicated preview border view (
R.id.screenshot_preview_border), which is a natural place to apply a red "hook active" indicator. - Tried to inspect the connected device via
adb, but sandbox restrictions prevented starting the adb daemon in this environment. Device-side APK/framework inspection remains a next step. - ADB access was later enabled. Confirmed live device:
- transport over
192.168.2.56:5555 ro.build.version.release_or_codename=15ro.build.version.sdk=35ro.crdroid.version=15.0
- transport over
- Pulled
/system_ext/priv-app/SystemUI/SystemUI.apkfrom the device and inspected it locally. - Confirmed actual screenshot classes on this build:
TakeScreenshotServiceTakeScreenshotExecutorandTakeScreenshotExecutorImplImageCaptureImplPolicyRequestProcessorScreenshotControllerScreenshotShelfViewProxyScreenshotWindow
- Confirmed
TakeScreenshotServiceruns in the dedicated:screenshotprocess. - Confirmed
ImageCaptureImpl.captureDisplay(int, Rect)buildsScreenCapture.CaptureArgsand callsIWindowManager.captureDisplay(...). - Confirmed screenshot shelf layout contains
@id/screenshot_preview_borderbacked by@drawable/overlay_border. - Confirmed screenshot UI is hosted in a dedicated
ScreenshotWindowtitledScreenshotUI, created with system window type0x7f4. - This strongly suggests the first rooted prototype should hook the
com.android.systemui:screenshotprocess, recolor the border, and attempt exclusion at the screenshot window level before attempting finer-grained surface exclusion. - Pulled
/system/framework/framework.jarand/system/framework/services.jarfrom the device and inspected them locally. - Confirmed framework-side facts on this ROM:
android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT = 2036PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY = 1048576android.window.ScreenCapture.CaptureArgs.Builder.setExcludeLayers(...)existsandroid.window.ScreenCapture.captureLayersExcluding(...)exists
- Confirmed
DisplayContent.getLayerCaptureArgs(Set<Integer>)excludes layers purely bywindowState.getWindowType()and forwards those windows'SurfaceControls intosetExcludeLayers(...). - Confirmed
WindowManagerService.takeAssistScreenshot(Set<Integer>)uses that exclusion path, butandroid.view.IWindowManagerdoes not expose that method directly; it only exposescaptureDisplay(...)andrequestAssistScreenshot(...). - New preferred prototype strategy:
- stay inside
com.android.systemui:screenshot - get the live
SurfaceControlfor the attachedScreenshotUIwindow - hook
ImageCaptureImpl.captureDisplay(...) - rebuild capture args with
setExcludeLayers(...) - reuse the existing
IWindowManager.captureDisplay(...)binder call
- stay inside
- Confirmed surface access path on the device framework:
Window.getRootSurfaceControl()existsView.getRootSurfaceControl()exists- concrete attached root is
ViewRootImpl ViewRootImplcontains a realmSurfaceControl
- Added docs/lsposed-hook-blueprint.md with the first concrete LSPosed implementation design.
- Created a minimal Android/LSPosed module project under app.
- Implemented first hooks:
ScreenshotShelfViewProxyborder recolor to redScreenshotController/ScreenshotWindowstate captureImageCaptureImpl.captureDisplay(...)interception scaffold usingsetExcludeLayers(...)
- Added a repo-local Gradle wrapper.
- Verified build success with
gradle assembleDebug. - Built debug APK at app/build/outputs/apk/debug/app-debug.apk.
- Installed the LSPosed module on-device, enabled it for
com.android.systemui, and confirmed LSPosed loadsdev.duda.screenshotdroid.HookEntryincom.android.systemui:screenshot. - Confirmed on-device proof-of-life hooks:
- screenshot shelf border is recolored red
ImageCaptureImpl.captureDisplay(...)interception runs
- Narrowed the timing behavior:
- on screenshot N, the screenshot shelf is not yet attached when capture runs
- on screenshot N+1, the previous screenshot shelf is attached and its
SurfaceControlis available fromPhoneWindow.getRootSurfaceControl()
- Updated the resolver to prefer the actual attached-view path on this ROM:
PhoneWindow.getRootSurfaceControl()ViewRootImpl.getSurfaceControl()- fallback
mSurfaceControlreflection
- Verified repeated-shot behavior with saved files:
Screenshot_20260328-022446_Apps2SD PRO.pngScreenshot_20260328-022450_Apps2SD PRO.png- files were byte-identical (
sha256andcmpmatched) - the second shot was taken during
captureDisplay intercepted with excluded screenshot surface
- Strong inference: on this ROM, excluding the prior
ScreenshotUIsurface fromcaptureDisplay(...)successfully removes the visible previous screenshot shelf from the next saved screenshot. - New UX issue reported by the user: during screenshot N+1, the previous screenshot shelf was vanishing from the display instead of staying visible until replaced.
- Investigated
ScreenshotController.handleScreenshot(...)and confirmed it callsScreenshotShelfViewProxy.reset()before setting the new screenshot, which likely explains the visible vanish. - Added a targeted reentry hook:
- arm a one-shot reset suppression when
handleScreenshot(...)starts and the old shelf is still attached - suppress the next
ScreenshotShelfViewProxy.reset()only for that reentry path
- arm a one-shot reset suppression when
- Re-enabled the LSPosed module after the user had disabled it.
- Verified on-device on a true rapid two-shot sequence:
- screenshot N+1 still used excluded-layer capture for the prior
ScreenshotUIsurface Armed reset suppression for screenshot reentryfiredSuppressing ScreenshotShelfViewProxy.reset() during screenshot reentryfiredScreenshotWindow.removeWindow()happened later on the normal shelf timeout, not immediately during reentry
- screenshot N+1 still used excluded-layer capture for the prior
- Pulled the resulting screenshot pair:
Screenshot_20260328-031644_System-UI.pngScreenshot_20260328-031644_System-UI (1).png- files were byte-identical (
sha256andcmpmatched), so exclusion still worked after the UX change
- User reported the old shelf still disappeared visually, even after white-flash removal and reset suppression.
- Replaced the main UX mitigation with a continuity-overlay prototype:
- on screenshot reentry, snapshot the currently visible shelf with
PixelCopy - place that snapshot into a temporary transparent
TYPE_SCREENSHOToverlay window at the same on-screen bounds - remove the temporary overlay after a short timeout
- continue excluding the stock
ScreenshotUIsurface from screenshot N+1
- on screenshot reentry, snapshot the currently visible shelf with
- Fixed the first continuity-overlay attempt after it failed on hardware bitmaps during
View.draw(...); switched toPixelCopyfrom the real screenshot window instead. - Verified on-device from LSPosed logs that, on screenshot N+1:
Prepared continuity overlay for screenshot reentryfiredContinuity overlay addedfired- the overlay was later removed on its timeout
- The visual result on the physical display still needs user confirmation; the reentry animation bypass log has not yet been observed on this ROM.
- Reworked the first stacked-shelf patch to avoid an AndroidX/class-loader dependency inside the LSPosed module.
- Kept the stack inside the stock shelf by reusing:
R.id.screenshot_preview_bluras the first rear cardR.id.screenshot_badgeas the count badge slot- one synthetic extra rear
ImageViewinserted with runtime-cloned layout params for 3-shot bursts
- Changed stack accumulation to happen only during actual screenshot reentry, which prevents stale screenshots from leaking into a new batch after timeout or dismissal.
- Changed stack storage to keep bounded, downscaled bitmap copies instead of strong references to full screenshot bitmaps in the SystemUI screenshot process.
- Fixed the batch lifecycle so the preview stack survives
ScreenshotWindow.removeWindow()during reentry and only clears on non-reentry dismissal/timeout. - Verified the updated module builds successfully with Gradle 8.13 via
:app:assembleDebug. - Iterated on the screenshot shelf card rendering and placement on-device:
- replaced mismatched frame/background composition with composed framed card bitmaps so visible border width is controlled directly
- unified the visible rear-card path so the 2nd screenshot uses the same overlay-based renderer as the 3rd
- removed the extra delayed stack reapply that was snapping the stack back toward the lower-left after initial placement
- diagnosed the false "can't take screenshots" error as a crash in
createCardBitmap(...)after capture had already succeeded and the file had already been saved - fixed that crash by converting hardware preview bitmaps before drawing them into software-backed framed card bitmaps
- current visual state: 2-shot stacking is aligned with the 3-shot renderer, and screenshot capture no longer fails due to the composed-frame path
- Updated
README.mdto addrecording.mp4at the top, move install instructions higher, and remove the olddev.duda.screenshotdroidpackage reference.