Skip to content

Latest commit

 

History

History
389 lines (382 loc) · 37.8 KB

File metadata and controls

389 lines (382 loc) · 37.8 KB

Work Log

2026-03-29

  • Started the first actually functional editor pass after the single-task batch handoff stabilized:
    • replaced the dead editor shell with working Done/Share actions, interactive crop handles, live pen markup, and visible color swatches in MarkupEditorActivity.java, MarkupEditorStageView.java, activity_markup_editor.xml, and strings.xml
    • Done now opens a save/delete/cancel dialog instead of closing immediately
    • Share now saves the current edited image back to the screenshot Uri and 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
  • 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 to PNG
    • 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.2s between Preview tap intercepted and Launched 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:screenshot process still left too much handoff latency during screenshot bursts
  • 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 MarkupEditorLaunchActivity instead of launching the full editor directly
    • MarkupEditorLaunchActivity.java immediately forwards the existing batch payload, ClipData, and Uri grants into MarkupEditorActivity inside 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
  • Verified autonomously on-device after rebuilding, reinstalling, and restarting SystemUI:
    • Preview tap intercepted logged at 03-30 02:30:11.255
    • MarkupEditorActivity was reported as displayed at 03-30 02:30:11.882
    • effective tap-to-visible time dropped to about 0.63s
    • MarkupEditorActivity became 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 MarkupEditorActivity when 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 ImageExporter completions log Refreshed 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 10 and 5 of 10
    • root cause was twofold in ScreenshotHooks.java:
      • the editor could launch before all ImageExporter results 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
    • hardened the handoff by:
      • tracking current-burst capture count, saved-Uri count, 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 MarkupEditorActivity with an updated batch when late exports arrive after the initial launch
  • Verified autonomously on-device after rebuild/reinstall/restart:
    • a previously failing immediate-open burst case now refreshed the editor up to 9 screenshots, matching the 9 saved Uris 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
  • 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 same ScreenshotWindow / shelf objects after editor handoff
    • clearing the cached window reference broke later excluded-layer capture lookup, and deferring clearStackUi(...) with shelfView.post(...) let stale preview transform state survive into the reused shelf
    • fixed ScreenshotHooks.java to preserve the cached ScreenshotWindow across removeWindow() 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 ScreenshotWindow surface 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.java now forwards the full active screenshot batch into MarkupEditorActivity, grants read access for every Uri via batch ClipData, and dismisses the SystemUI screenshot shelf once the editor takes over
    • MarkupEditorActivity.java now reuses a single task instance, accepts batch extras in onNewIntent(...), and supports left/right swipe paging across the saved screenshots
    • HookState.java now exposes a copy of the active saved-screenshot Uri list 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 ScreenshotUI from the active window list
    • the editor subtitle reports Showing 1 of 2 on open
    • a left swipe advances to Showing 2 of 2 without the previous decode failure
    • repeated launch attempts do not create a second MarkupEditorActivity instance; the same task/activity record remains active
  • 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_RECENTS at launch time in ScreenshotHooks.java so the task is marked correctly even when started from SystemUI
  • 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 ms threshold.
  • Reduced the deliberate chooser hold threshold from 2500 ms to 500 ms in ScreenshotHooks.java after follow-up tuning.
  • Fixed the broken tap-interception hook in ScreenshotHooks.java:
    • the earlier ScreenshotShelfView.dispatchTouchEvent(...) hook was failing on-device with NoSuchMethodError
    • replaced it with a View.dispatchTouchEvent(...) hook filtered to screenshot_preview and screenshot_scrolling_scrim
    • this avoids relying on a method that is inherited rather than declared on this ROM's shelf view class
  • Removed module-resource lookup from the short-tap toast path:
    • Toast.makeText(..., R.string.preview_hold_for_actions, ...) was throwing Resources$NotFoundException inside the com.android.systemui:screenshot process
    • 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 intercepted and Preview tap consumed; waiting for deliberate hold before stock actions
    • a 3000 ms stationary swipe on the same region logs Preview 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
  • Increased the stock screenshot shelf timeout override from 5000 ms to 15000 ms in 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
  • 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 ms hold on the preview stack
    • kept the PreviewAction wrapper 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:
  • 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
    • Done simply 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-home then 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
  • 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 SystemUI on-device so the updated LSPosed hook would reload
  • 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_init and rebuilt successfully
    • reinstalled the renamed APK and restarted SystemUI
  • 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.systemui scope
    • enabled fuck.iosstackingscreenshots.droidvendorssuck and inserted com.android.systemui user 0 scope
    • restarted SystemUI after 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 SystemUI again.
  • Expanded README.md into a zero-to-active ADB deployment guide, including:
    • build/install steps
    • old-package cleanup
    • LSPosed activation and scope setup
    • SystemUI restart instructions
    • when a full reboot may still be necessary

2026-03-28

  • 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 main com.android.systemui process
    • verified the timeout firing through LSPosed logs after restarting the correct screenshot process
    • removed the extra shelf dispatchTouchEvent hook and stopped suppressing onTouchOutside, so touch handling returns to stock while keeping the delayed stock-timeout auto-dismiss path
  • Reworked the auto-dismiss implementation again after the delayed-dismiss approach still left a half-dismissed, input-blocking shelf window:
    • decompiled the local SystemUI.apk with jadx and confirmed ScreenshotController sets screenshotHandler.mDefaultTimeout = 3000
    • changed the LSPosed hook to override that stock timeout to 5000 ms in ScreenshotController construction instead of intercepting SCREENSHOT_INTERACTION_TIMEOUT
    • removed the custom delayed-dismiss helper so stock dismissal animation and input teardown run on the normal SystemUI path
  • Started swipe-to-delete behavior for active screenshot batches:
    • hooked ImageExporter.export(...) to track the saved Uri for each screenshot in the active SystemUI batch
    • detect SCREENSHOT_EXPLICIT_DISMISSAL with negative swipe velocity as a left-swipe delete gesture
    • on left-swipe dismissal, delete the tracked batch Uris through ContentResolver and also delete any late-arriving export result from the same batch

2026-03-28

  • 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 to 192.168.2.56:5555, and restarted SystemUI.
  • 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.

2026-03-28

  • 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 screencap dumps 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_preview image with a newly composed framed bitmap on each update.
  • Fixed the front-preview path by stopping that custom bitmap rewrite:
    • leave screenshot_preview bound to the stock/current screenshot bitmap
    • restore and style the stock screenshot_preview_border view instead of hiding it
  • Rebuilt the LSPosed module with ./gradlew assembleDebug, installed the updated APK, restarted SystemUI, 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, then stack=2 total=3)
  • Also extended the device screen timeout over ADB during testing to keep the display awake across repeated manual captures.

2026-03-28

  • 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 SystemUI so the com.android.systemui:screenshot process 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 SystemUI again 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: PixelCopy is asynchronous, so the old-shelf continuity overlay could still be added after setScreenshot(...) 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 SystemUI again so the stale-overlay race fix is now live on-device.

2026-03-27

  • 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 MediaProjection and overlay windows, with compromises
    • system/OEM approach using private screenshot APIs with excluded layers
  • 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:
    • TakeScreenshotService receives requests
    • TakeScreenshotExecutor dispatches
    • ScreenshotRequestProcessor / RequestProcessor can rewrite requests
    • ImageCaptureImpl.captureDisplay(...) currently calls IWindowManager.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=15
    • ro.build.version.sdk=35
    • ro.crdroid.version=15.0
  • Pulled /system_ext/priv-app/SystemUI/SystemUI.apk from the device and inspected it locally.
  • Confirmed actual screenshot classes on this build:
    • TakeScreenshotService
    • TakeScreenshotExecutor and TakeScreenshotExecutorImpl
    • ImageCaptureImpl
    • PolicyRequestProcessor
    • ScreenshotController
    • ScreenshotShelfViewProxy
    • ScreenshotWindow
  • Confirmed TakeScreenshotService runs in the dedicated :screenshot process.
  • Confirmed ImageCaptureImpl.captureDisplay(int, Rect) builds ScreenCapture.CaptureArgs and calls IWindowManager.captureDisplay(...).
  • Confirmed screenshot shelf layout contains @id/screenshot_preview_border backed by @drawable/overlay_border.
  • Confirmed screenshot UI is hosted in a dedicated ScreenshotWindow titled ScreenshotUI, created with system window type 0x7f4.
  • This strongly suggests the first rooted prototype should hook the com.android.systemui:screenshot process, recolor the border, and attempt exclusion at the screenshot window level before attempting finer-grained surface exclusion.
  • Pulled /system/framework/framework.jar and /system/framework/services.jar from the device and inspected them locally.
  • Confirmed framework-side facts on this ROM:
    • android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT = 2036
    • PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY = 1048576
    • android.window.ScreenCapture.CaptureArgs.Builder.setExcludeLayers(...) exists
    • android.window.ScreenCapture.captureLayersExcluding(...) exists
  • Confirmed DisplayContent.getLayerCaptureArgs(Set<Integer>) excludes layers purely by windowState.getWindowType() and forwards those windows' SurfaceControls into setExcludeLayers(...).
  • Confirmed WindowManagerService.takeAssistScreenshot(Set<Integer>) uses that exclusion path, but android.view.IWindowManager does not expose that method directly; it only exposes captureDisplay(...) and requestAssistScreenshot(...).
  • New preferred prototype strategy:
    • stay inside com.android.systemui:screenshot
    • get the live SurfaceControl for the attached ScreenshotUI window
    • hook ImageCaptureImpl.captureDisplay(...)
    • rebuild capture args with setExcludeLayers(...)
    • reuse the existing IWindowManager.captureDisplay(...) binder call
  • Confirmed surface access path on the device framework:
    • Window.getRootSurfaceControl() exists
    • View.getRootSurfaceControl() exists
    • concrete attached root is ViewRootImpl
    • ViewRootImpl contains a real mSurfaceControl
  • 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:
    • ScreenshotShelfViewProxy border recolor to red
    • ScreenshotController / ScreenshotWindow state capture
    • ImageCaptureImpl.captureDisplay(...) interception scaffold using setExcludeLayers(...)
  • 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 loads dev.duda.screenshotdroid.HookEntry in com.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 SurfaceControl is available from PhoneWindow.getRootSurfaceControl()
  • Updated the resolver to prefer the actual attached-view path on this ROM:
    • PhoneWindow.getRootSurfaceControl()
    • ViewRootImpl.getSurfaceControl()
    • fallback mSurfaceControl reflection
  • Verified repeated-shot behavior with saved files:
    • Screenshot_20260328-022446_Apps2SD PRO.png
    • Screenshot_20260328-022450_Apps2SD PRO.png
    • files were byte-identical (sha256 and cmp matched)
    • the second shot was taken during captureDisplay intercepted with excluded screenshot surface
  • Strong inference: on this ROM, excluding the prior ScreenshotUI surface from captureDisplay(...) 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 calls ScreenshotShelfViewProxy.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
  • 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 ScreenshotUI surface
    • Armed reset suppression for screenshot reentry fired
    • Suppressing ScreenshotShelfViewProxy.reset() during screenshot reentry fired
    • ScreenshotWindow.removeWindow() happened later on the normal shelf timeout, not immediately during reentry
  • Pulled the resulting screenshot pair:
    • Screenshot_20260328-031644_System-UI.png
    • Screenshot_20260328-031644_System-UI (1).png
    • files were byte-identical (sha256 and cmp matched), 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_SCREENSHOT overlay window at the same on-screen bounds
    • remove the temporary overlay after a short timeout
    • continue excluding the stock ScreenshotUI surface from screenshot N+1
  • Fixed the first continuity-overlay attempt after it failed on hardware bitmaps during View.draw(...); switched to PixelCopy from the real screenshot window instead.
  • Verified on-device from LSPosed logs that, on screenshot N+1:
    • Prepared continuity overlay for screenshot reentry fired
    • Continuity overlay added fired
    • 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_blur as the first rear card
    • R.id.screenshot_badge as the count badge slot
    • one synthetic extra rear ImageView inserted 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

2026-03-29

  • Updated README.md to add recording.mp4 at the top, move install instructions higher, and remove the old dev.duda.screenshotdroid package reference.