feat(ui): Add Miuix blur and custom card backdrop blur support - #219
feat(ui): Add Miuix blur and custom card backdrop blur support#219fanziyun wants to merge 21 commits into
Conversation
9c46563 to
a7e0439
Compare
xingguangcuicanbot
left a comment
There was a problem hiding this comment.
Automated PR Review
Summary
Found two actionable issues: reduced surface alpha persists after disabling the custom background, and optional card blur decodes unbounded source images with OOM risk.
Decision
request_changes
Notes
Codex recommendation: request_changes
Posted 2 inline review comment(s).
Co-authored-by: xingguangcuican_bot <xingguangcuican821@gmail.com>
xingguangcuicanbot
left a comment
There was a problem hiding this comment.
Automated PR Review
Summary
Two regressions: opaque-surface fallback is broken when no background image is enabled, and the software card blur fallback is incorrectly disabled on non-shader devices.
Decision
request_changes
Notes
Codex recommendation: request_changes
Posted 2 inline review comment(s).
…st.kt Co-authored-by: xingguangcuican_bot <xingguangcuican821@gmail.com>
Co-authored-by: xingguangcuican_bot <xingguangcuican821@gmail.com>
xingguangcuicanbot
left a comment
There was a problem hiding this comment.
Automated PR Review
Summary
The PR introduces a risky minSdk override for the new blur dependency, makes the default blur path effectively opaque unless UI opacity is lowered, and unintentionally disables the new software card-blur feature on pre-runtime-shader devices.
Decision
request_changes
Notes
Codex recommendation: request_changes
Posted 3 inline review comment(s).
Make frosted glass visible and the software card-blur path usable on all APIs: - Cap the bar blur tint alpha (AbkBlurTintAlpha) and keep card surfaces translucent when a pre-blurred custom background is present, instead of blending an opaque surfaceContainer over the backdrop at the default surface opacity, so the Blur switch has a visible effect by default. - Decouple BlurConfig.wantsBackgroundPainter from the AGSL master switch and stop hiding the "render custom background into blur" toggle behind isBlurCapableDevice(), so API 26-32 devices can enable the StackBlur path. - Turn off the bottom-bar backdrop and its blur pipeline while the bar is off-screen (child page or OOBE) to drop the invisible full-screen recordLayer + AGSL blur pass every frame. - Replace per-card localToScreen/screenToLocal round-trips with LayoutCoordinates.localPositionOf; align blurSource/blurEffect guards to the actual API 33 runtime-shader capability instead of API 31. - Debounce the card-background decode + StackBlur reload across viewport changes so rotation/split-screen do not re-run it per intermediate size. - Share one background painter from AppBackgroundHost across all blur hosts instead of one Coil request per screen scaffold. - Fix the surface-alpha preview state: an interrupted slider drag no longer sticks the in-memory preview for the session, and a late DataStore emission no longer yanks the slider thumb mid-drag. - Drop dead LocalBlurredSurfaceDepth plumbing; route the extension manager through BlurConfig instead of duplicating its condition.
xingguangcuicanbot
left a comment
There was a problem hiding this comment.
Automated PR Review
Summary
The blur integration is close, but the shared background rendering path in AppBackgroundHost introduces two regressions that affect wallpaper rendering and device stability.
Decision
request_changes
Notes
Codex recommendation: request_changes
Posted 2 inline review comment(s).
The painter-sharing change dropped the contentScale from the background
Image, so wallpapers with a mismatched aspect ratio were letterboxed with
the default Fit and the blur backdrops / card sampling could read the wrong
region of the wallpaper. Render with ContentScale.Crop to match the previous
AsyncImage behavior and the blur pipeline's crop math.
Coil's AsyncImagePainter sizes its request from the draw viewport
(size { drawSize...first() }), so the shared painter stays bounded to screen
resolution; a large user wallpaper is not decoded at its original dimensions.
Follow-up recheck of the blur subsystem (independent multi-agent audit + manual verification): - Fix the bottom-bar blur gate direction: navProgress is 1f when the bar is shown and animates to 0f when a child page hides it, so the previous `navProgress < 1f` gate disabled blur in the normal tab state and kept it running while the bar was fully off-screen. Gate on `navProgress > 0.02f`. - Restore the master Blur switch as the overall on/off for every blur surface: show it on all API levels and re-tie BlurConfig.wantsBackgroundPainter to blurEnabled, so turning the master off disables the card blur too (it was decoupled in a prior commit, leaving frosted cards with opaque bars). - Stop swallowing CancellationException and OutOfMemoryError in the card-blur reload: catch Exception only and rethrow CancellationException so a viewport restart cannot overwrite the newer effect's result with a stale one, and an OOM from the StackBlur buffers is not masked as a silent fallback. - Reset the un-persisted surface-alpha preview when the app is backgrounded (Home/recents mid-drag never fires onValueChangeFinished), and re-check the dirty flag after the DataStore read to avoid stomping a newer drag. - Clear the blur composition locals under the opaque OOBE overlay so its cards render opaque instead of showing a frosted backdrop. - Only draw the background dim overlay when a wallpaper painter is present. - Make the master blur switch description match its new master-toggle role.
xingguangcuicanbot
left a comment
There was a problem hiding this comment.
Automated PR Review
Summary
No actionable issues found in the PR diff. Static review and diff checks passed; Kotlin compilation could not be run because the repository has no gradle wrapper at ./gradlew.
Decision
request_changes
Notes
Codex recommendation: approve
This PR has 1826 changed lines, so policy requires request changes.
xingguangcuicanbot
left a comment
There was a problem hiding this comment.
Automated PR Review
Summary
The shared background painter can regress memory usage by loading full-resolution custom images instead of sizing requests to the screen.
Decision
request_changes
Notes
Codex recommendation: request_changes
Posted 1 inline review comment(s).
Address the review comment on the shared rememberAsyncImagePainter: instead of relying on the implicit draw-time viewport sizing, build the painter's request from an ImageRequest explicitly sized to this host's onSizeChanged viewport, so a large user wallpaper is decoded at screen resolution rather than its original dimensions. The request is cached on (uri, viewportSize) so it is rebuilt only when the wallpaper or the viewport changes, and the painter stays shared across the visible image and every blur backdrop (no N+1 decode).
xingguangcuicanbot
left a comment
There was a problem hiding this comment.
Automated PR Review
Summary
Found one UI regression: the SUSFS settings page does not enable the new top-bar blur. Gradle verification could not run because this checkout does not contain a Gradle wrapper.
Decision
request_changes
Notes
Codex recommendation: request_changes
Posted 1 inline review comment(s).
| @@ -583,9 +607,9 @@ | |||
There was a problem hiding this comment.
Location: app/src/main/java/com/abk/kernel/ui/screens/SettingsScreen.kt:642
Issue: The SUSFS child page was migrated to BlurScreenScaffold, but its ExpressiveTopBar does not pass enableBlur = state.blurEnabled, unlike the other migrated screens. As a result, enabling Blur leaves this page's top bar opaque and inconsistent with the rest of the UI.
Suggestion: Pass enableBlur = state.blurEnabled to the SUSFS page's ExpressiveTopBar.
The SUSFS child page was migrated to BlurScreenScaffold but its ExpressiveTopBar did not forward enableBlur, so its top bar stayed opaque while every other migrated page frosted its bar. Add enableBlur = state.blurEnabled for consistency across the UI.
Opening the soft keyboard resizes the window (the bottom bar rides above the IME), which recomposed the wallpaper request and restarted the card-blur loader. Both flashed to an empty/opaque state before reloading: - AppBackgroundHost: size the shared background painter's request to the stable full-screen resolution (resources.displayMetrics) instead of the onSizeChanged viewport, so a transient IME inset change no longer re-decodes the wallpaper (which blanked the background while it reloaded). Full-screen is always >= any window viewport, so the crop stays correct. - rememberBlurredCardBackground: keep the previous frosted backdrop while the replacement decodes + StackBlurs, and only swap it in once ready, instead of nulling it first (which flashed every card to an opaque/transparent surface).
Port ReSukiSU's approach for the software card-blur backdrop: instead of a Composable-remembered bitmap that is re-decoded and re-blurred whenever the viewport changes, keep the pre-blurred wallpaper in a module-level cache and persist it to filesDir. Combined with the existing keep-until-replaced swap, transient viewport changes (e.g. the soft keyboard resizing the window on adjustResize) no longer flash cards opaque or re-run the full decode + StackBlur pass; matching viewports are restored from disk on the next launch / re-entry.
Port ReSukiSU's approach to avoiding the visible blur-loading flash: its card alpha is a synchronous config that only depends on the custom background being enabled, not on the async pre-blurred bitmap being ready. Add a synchronous LocalBlurredCardBackgroundEnabled signal (feature on, bitmap may still be decoding) and use it in blurredCardSurfaceColor and ExpressiveListItem's drawsOwnBlurSurface instead of checking LocalBlurredCardBackground != null. Surfaces are now translucent from the very first frame and only gain the frosted backdrop when it lands, so opening the app no longer flashes cards from opaque to translucent while the wallpaper decode + StackBlur runs. OOBE clears the new signal too.
The soft keyboard opening used to resize the window (adjustResize), which shrank the AppBackgroundHost layer and re-ran the frosted-backdrop blur for every show/hide. Switch to adjustNothing so the window (and backdrop layer) keeps its size and the blur is generated once per real size change: - Manifest: adjustNothing on MainActivity and AbkExtensionManagerActivity. - AppBackgroundHost: wrap app content in Modifier.imePadding() so content is lifted over the keyboard while the wallpaper/backdrop layer stays put. - API 26-29 do not deliver full IME insets to imePadding, so those devices fall back to adjustResize at runtime (Android 10 and below). Compose Dialog windows are unaffected (they keep their own UNSPECIFIED soft-input behavior).
xingguangcuicanbot
left a comment
There was a problem hiding this comment.
Automated PR Review
Summary
The on-disk blurred-background cache is unconditionally invalidated at startup, so its intended cross-launch reuse never occurs.
Decision
request_changes
Notes
Codex recommendation: request_changes
Posted 1 inline review comment(s).
Co-authored-by: xingguangcuican_bot <xingguangcuican821@gmail.com>
The bot's cache fix (f8011d6) left the old delete call in place, so the pre-blurred card cache was still deleted before being read on every launch. Key the cache file by uri.hashCode() so a stale wallpaper's blur is never reused across launches, and only delete the old cache on a real in-process wallpaper switch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ain thread - Mirror the background anchor's size into snapshot state. LayoutCoordinates.size is not observable, so split-screen / freeform / IME resize never re-ran the blur and cards drew a stale viewport stretched across the new size. - Run saveBlurCache (JPEG compress + disk write) on Dispatchers.IO instead of the main thread. - Stop resetting the nested 'render background into blur' pref when the master blur switch is toggled off, so the user's setting is preserved.
xingguangcuicanbot
left a comment
There was a problem hiding this comment.
Automated PR Review
Summary
The PR has two actionable cache issues: URI hash collisions can display the wrong blurred background, and URI changes unnecessarily delete reusable cache entries. The Gradle unit-test command could not be run because this checkout has no gradlew wrapper.
Decision
request_changes
Notes
Codex recommendation: request_changes
Posted 2 inline review comment(s).
… switches - Key the on-disk cache by SHA-256(uri) instead of String.hashCode(), so two distinct background URIs cannot collide into the same file and serve the wrong wallpaper's blur. - Stop deleting the previous URI's cache on wallpaper change; switching back now reuses it. Bound disk usage with a count-based prune (keep 4 newest). - Include the blur version/radius/downsample in the filename so a future algorithm change cannot silently reuse an incompatible cache.
…avior - Revert windowSoftInputMode to adjustResize and drop the now-redundant imePadding(): adjustNothing without edge-to-edge never delivered IME insets, so the keyboard covered content on Android 11+. - Key the card-blur re-blur on width (not height) so a height-only IME resize no longer re-runs the decode + StackBlur pass. - Skip drawing the blurred strip while the cached blur's width is stale (rotation / split-screen), instead of stretching the old-viewport bitmap. - Prune orphaned .tmp cache files and delete corrupt cache files on decode failure.
xingguangcuicanbot
left a comment
There was a problem hiding this comment.
Automated PR Review
Summary
The blur integration is generally well-structured, but I found two correctness issues in the new card-backdrop pipeline that can leave the UI showing the wrong blurred image under common resize/background-change races.
Decision
request_changes
Notes
Codex recommendation: request_changes
Posted 2 inline review comment(s).
The width-only key avoided IME reloads but also missed real height growth (freeform taller, fold unfold). Key on both dimensions and skip the expensive pass only while the cached bitmap still covers the new viewport: growth past the cached size re-blurs, shrink-only changes (IME) keep the taller bitmap which still samples the correct region.
Overview
This PR ports and integrates the
miuix-blur-androidlibrary to add real-time frosted-glass blur effects to top bars and bottom navigation bars, along with software-based StackBlur support for custom background images on cards and tiles.Key Changes
1. Miuix Blur Architecture (
app/src/main/java/com/abk/kernel/ui/blur/)BlurScreenScaffold&BlurHost: Redesigned page scaffold layouts to place body content inside the capture backdrop while keeping top bars outside the capture region, preventing render thread crashes (SIGSEGV) caused by self-referential blur sampling.blurSource&blurEffect: Provided AGSL runtime shader blur (API 33+) for top bars and bottom navigation bars, automatically falling back to surface colors on unsupported devices.2. Custom Background Card Blur (
BlurredCardBackdrop.kt)rememberBlurredCardBackground: Downsamples custom background images and pre-blurs them onDispatchers.Defaultusing StackBlur when "Render custom background into blur" is enabled.blurredCardBackground: Allows cards and list items to sample their corresponding viewport rectangle for consistent background translucency.3. Preferences & UI Performance (
SettingsScreen.kt&MainViewModel.kt)uiSurfaceAlphaPreview: Keeps drag state local during surface alpha slider drag, updating memory state per frame and deferring DataStore disk writes until the drag finishes (onValueChangeFinished).Verification & Testing
BlurConfigTestto verify blur flag behavior.