Copyright 2023 Chris Banes
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
On Android 12 (API 31) the blur is frozen while scrolling.
Only when the bottom navigation appears or disappears the blur quickly updates and is then frozen again.
Seems like HazeNode31.onObservedReadsChanged() is not called. Probably a bug in Compose or can we do something about it?
See attached video of the android-jetpack sample on API 31. It is working as expected on API 32.
Many thanks to you Chris and other contributors for this awesome library! It works like charm, unlike numerous fancy, but intangible solutions available out there.
This is not an issue, but rather a feature request. How likely it is that this library may support RenderScript-based blur for the older Android versions (pre-Android 12)? Is a pull request to add such functionality something that you guys would consider?
Manifest merger failed : uses-sdk:minSdkVersion 21 cannot be smaller than version 24 declared in library [dev.chrisbanes.haze:haze-jetpack-compose:0.4.1] /Users/yd-sz-dn0588/.gradle/caches/transforms-3/b2ddb0cf5d948a8541195c91900041ce/transformed/haze-jetpack-compose-0.4.1/AndroidManifest.xml as the library might be using APIs not available in 21
Suggestion: use a compatible library with a minSdk of at most 21,
or increase this project's minSdk version to at least 24,
or use tools:overrideLibrary="dev.chrisbanes.haze.jetpackcompose" to force usage (may lead to runtime failures)
The Skiko platforms (iOS and Desktop) are trickier to fix, as we don't have the same control over the drawing pipeline (no RenderNode). The problem is that we use ImageFilter.makeMerge() to composite all of our filters together, including the source content. The filters tend to include transparent/translucent pixels, which means that the effects drawn on top will nearly always be translucent. The source content is drawn behind all of the filters, so the non-blurred parts you see is actually just translucent blurred effects with the content drawn again behind.
The fix for Android (#113) is to clipPath() the source content, so that only the effect OR content is drawn at a given coordinate. There might be some clever stuff we can do with BlendMode to achieve similar with Skiko, but it needs some thought.
I'm trying to apply the blur effect on the below case:
I have a face to face call, the feed is almost edge-to-edge with a control bar at the bottom. I'm trying to have Haze blur the rounded background but I get this effect instead.
I can still see the feed through, but the controls are being blurred instead.
Here is how I'm achieving this:
// On the root composable
val hazeState = remember { HazeState() }
BoxWithConstraints(
contentAlignment = Alignment.Center,
modifier = Modifier
.fillMaxSize()
.background(MyTheme.colors.background)
.haze(
state = hazeState,
backgroundColor = MyTheme.colors.backgroundControls,
)
// On the control background
Box(
modifier = Modifier
.matchParentSize()
.padding(
start = horizontalPadding,
end = horizontalPadding,
bottom = MyTheme.grid.unitFactor(2.5f),
)
.hazeChild(
state = hazeState,
shape = MyTheme.shapes.roundRad16,
)
.background(
color = MyTheme.colors.backgroundControls,
shape = MyTheme.shapes.roundRad16,
)
.padding(
top = MyTheme.grid.unitFactor(2.5f),
start = MyTheme.grid.unitFactor(3f),
end = MyTheme.grid.unitFactor(3f),
bottom = MyTheme.grid.unitFactor(2.5f),
),
) {}
Is it even possible to do it with haze? can you exclude children composables?
Is the SurfaceView underneath disrupting the modifier?
Looks like haze is having issues with Webviews, Our QA's found this in our real app code we we had to revert for now, but I've created a simple example to reproduce here: Just a fork with a webview sample added. Had to drop toolchain to 17 but that's nothing to do with the issue just my current setup.
For our usecase: most of the app is native which works fine with the bottombar and haze. But some screens are still been migrated away from webviews, when using haze with webviews we see this repeated flashing issue.
The background behind the haze rectangle does not seem to honor the specified color.
Details
In version 0.2.0 the background color specified in the haze modifier does not appear to render correctly. In my use case the background is MaterialTheme.colorScheme.surface and I configure haze as shown here and below
Position mismatch with android predictive back gesture. The blur moves along with the back animation while other UI elements are on their respective positions.
Screenshot testing will be useful in ensuring now regressions in the rendered effect. We'll need to use emulator/simulators due to the way that the effects are implemented, which makes it tricky on GitHub Actions.
Supplying a transparent tint crashes (due to lack of alpha, I'm guessing?), so I'm currently supplying white with the lowest possible alpha value to achieve the effect I want (as close as I can).
My use case is a hazy bottom bar in a scaffold. Screens slide in and out of the scaffold. I'm animating the bottom bar to slide off screen when it's conditionally hidden. When a screen containing a haze child (scrollable content) slide-exits the scaffold while simultaneously hiding the bottom bar, the tint, although visually belonging to the bottom bar, can be sliding away with the exiting content. If tint were absent, I could just set a translucent background on the bottom bar. Was there a reason for requiring tint?
Hey i tried your examples with top bar , i want to achieve something like the below image with android compose and scaffold, but top bar does not show the content below when it is inside scaffold even in the sample you have with scaffold
privatefun Color.boostAlphaForBlurRadius(blurRadius:Dp): Color {
Expected Behavior
The tint color provided to Haze should not change implicitly or this logic could be replaced adhoc.
Actual Behavior
The color (alpha) is not as provided.
Motivation
Our theme demands completely different background colors for blur-ready and non-blur states. As we already use haze, we don't want to introduce this then if(blurAvailable) Modifier.hazeChild(...) else Modifier.background(...) style but rather decide this on the color scheme level. Currently, we overcome boosting behaviour by setting blur radius on the theme-level local providable, but this is very implicit logic.
Hi, I've just found your library and this is something we have been looking for in multiple projects but were never able to implement for Android. Thanks for building this. I did find a bug or maybe something that has not been implemented yet and wanted to let you know.
Expected Behavior
When using hazyChild in a scrollable it should move with the item when overscroll effect gets triggered.
Actual Behavior
When adding hazyChild to lazy column items and scrolling everything works as expected.
However when you pull a bit further and the overscroll effect gets triggered the blur stays in the same place while the item moves, showing the blur next to the item.
The Preview in Android Studio for Jetpack Compose fails to render when using the .haze(...) Modifier:
java.lang.IllegalArgumentException: Software rendering doesn't support drawRenderNode
ย ย at android.graphics.Canvas.drawRenderNode(Canvas.java:2329)
ย ย at dev.chrisbanes.haze.HazeNode31.draw(HazeNode31.kt:125)
This could be solved by wrapping the haze modifier in a if (!LocalInspectionMode.current) { ... } but it's not the most elegant solution to repeat this over and over again on caller side.
In general the Preview support could be improved.
We are facing the following issues challenges:
Preview does not work at all
(see exception above)
Background for Preview of hazeChild
When applying the haze Modifier only if !LocalInspectionMode.current, then the hazeChild has a transparent background in the preview, since the background/tint color is configured on haze Modifier side. Adding a background for Preview purposes, could be solved by using if (!LocalInspectionMode.current) { hazeChild(...) } else { background(backgroundColor) }. Which is not optimal since we need to duplicate the background color.
Preview across components/files
Sometimes we do have the haze Modifier in one file (e.g. the screen with a lazy column) and the hazeChild in another (e.g. the bottom navigation which is embedded in multiple screens). With this setup it is also difficult to provide a proper preview with a background for the hazeChild component. Currently we are passing the HazeState as a parameter to this component, provide a dummy state in the Preview and switch to a solid background if we are in inspection mode.
Tests are working fine in 0.5.4 so seems to be a regression in 0.6.0
Haze version: 0.6.0
Platform: Android/Compose v1.6
Expected Behavior
Espresso UI Test doesn't crash when ran, app runs ok it's only tests which are regressed. (Removing the haze view from the test allows it to pass.)
Actual Behavior
java.lang.IllegalStateException: Recording currently in progress - missing #endRecording() call?
at android.graphics.RenderNode.beginRecording(RenderNode.java:446)
at android.graphics.RenderNode.beginRecording(RenderNode.java:465)
at androidx.compose.ui.platform.RenderNodeApi29.record(RenderNodeApi29.android.kt:204)
at androidx.compose.ui.platform.RenderNodeLayer.updateDisplayList(RenderNodeLayer.android.kt:335)
at androidx.compose.ui.platform.RenderNodeLayer.drawLayer(RenderNodeLayer.android.kt:277)
at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:348)
at androidx.compose.ui.node.LayoutNode.draw$ui_release(LayoutNode.kt:926)
at androidx.compose.ui.node.InnerNodeCoordinator.performDraw(InnerNodeCoordinator.kt:174)
at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:361)
at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:353)
at androidx.compose.ui.node.LayoutNode.draw$ui_release(LayoutNode.kt:926)
at androidx.compose.ui.platform.AndroidComposeView.dispatchDraw(AndroidComposeView.android.kt:1231)
at android.view.View.draw(View.java:23892)
at android.view.View.updateDisplayListIfDirty(View.java:22756)
at android.view.View.draw(View.java:23620)
at android.view.ViewGroup.drawChild(ViewGroup.java:4556)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4317)
at android.view.View.updateDisplayListIfDirty(View.java:22747)
at android.view.View.draw(View.java:23620)
at android.view.ViewGroup.drawChild(ViewGroup.java:4556)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4317)
at android.view.View.updateDisplayListIfDirty(View.java:22747)
at android.view.View.draw(View.java:23620)
at android.view.ViewGroup.drawChild(ViewGroup.java:4556)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4317)
at android.view.View.updateDisplayListIfDirty(View.java:22747)
at android.view.View.draw(View.java:23620)
at android.view.ViewGroup.drawChild(ViewGroup.java:4556)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4317)
at android.view.View.updateDisplayListIfDirty(View.java:22747)
at android.view.View.draw(View.java:23620)
at android.view.ViewGroup.drawChild(ViewGroup.java:4556)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4317)
at android.view.View.updateDisplayListIfDirty(View.java:22747)
at android.view.View.draw(View.java:23620)
at android.view.ViewGroup.drawChild(ViewGroup.java:4556)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4317)
at android.view.View.draw(View.java:23892)
at com.android.internal.policy.DecorView.draw(DecorView.java:809)
at android.view.View.updateDisplayListIfDirty(View.java:22756)
at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:694)
at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:700)
at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:798)
at android.view.ViewRootImpl.draw(ViewRootImpl.java:4939)
at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:4643)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3822)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2465)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:9305)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1339)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1348)
at android.view.Choreographer.doCallbacks(Choreographer.java:952)
at android.view.Choreographer.doFrame(Choreographer.java:882)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1322)
at android.os.Handler.handleCallback(Handler.java:958)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:205)
at android.os.Looper.loop(Looper.java:294)
at android.app.ActivityThread.main(ActivityThread.java:8177)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
A HorizontalPager/LazyRow/Scrollable Row with items that have a background blur and clip applied
Actual Behavior
They do have background blur but also some glitchy effects, it occurs more often with a background image loaded with Coil and the blurred contained has shape applied.
When the items have background blur with Rectangle shape applied the same happens only on the background with a color, at least I was not able to replicate it when the background is an image and the shape is a Rectangle
When we try to dynamically change the tint color, the haze modifier does not reflect this change on Android < API 32 (the ones that use HazeNodeBase).
A change of the tint color correctly calls HazeNodeElement.update() and changes all properties and then calls node.onUpdate().
This then calls invalidateDraw(), so we end up in HazeNodeBase.ContentDrawScope.draw() which will again draw the paths.
โ However, the draw function will only update the paths, including their tint color, if the paths were marked as dirty with pathsDirty, which never happens in this process. So we are stuck with the old color.
Currently we need to maintain 2 separate libraries due to Compose Multiplatform still targeting JC 1.5.x. To implement blurring on Android, we need access to a new drawing API added in Jetpack Compose Foundation 1.6.0 (currently in beta).
This is a tracking bug to perform that merge, once Compose Multiplatform is updated and we have access to that API (tracking issue: JetBrains/compose-multiplatform#4052).
I want to blur a screen with multiple Rects with different haze background colors.
I found it impossible to put multiple haze modifiers in one composable, but it works with wrapper layouts with only one haze modifier of each.
Is there convenient API that provides multiple complex haze effects in one modifier? For example:
Hi Chris,
first, thanks to this amazing library that does an incredible job, and also thanks to all the other contributors for making the impossible - possible in Android Blur issues.
This is not a classic issue or issue at all it is more like a feature request, if I missed the doc about it (I hope not), I couldn't blend/level the corners, so when using shapes (unique or regular) the edges are very noticeable.
it would be great if the library could offer such a mechanic to make the edges less noticeable.
it will help a lot if it will be possible
hazeChild(shape = RoundedCornerShape(...) could be applied to component with zero size.
Actual Behavior
Crash
java.lang.IllegalArgumentException: Android does not support arbitrary transforms
at androidx.compose.ui.graphics.AndroidMatrixConversions_androidKt.setFrom-EL8BTi8(AndroidMatrixConversions.android.kt:57)
at androidx.compose.ui.graphics.AndroidPath.transform-58bKbWc(AndroidPath.android.kt:205)
at dev.chrisbanes.haze.RenderNodeImpl.updatePaths(AndroidHazeNode.kt:455)
at dev.chrisbanes.haze.RenderNodeImpl.getUpdatedContentClipPath(AndroidHazeNode.kt:443)
at dev.chrisbanes.haze.RenderNodeImpl.access$getUpdatedContentClipPath(AndroidHazeNode.kt:253)
at dev.chrisbanes.haze.RenderNodeImpl$draw$2$1$1.invoke(AndroidHazeNode.kt:290)
at dev.chrisbanes.haze.RenderNodeImpl$draw$2$1$1.invoke(AndroidHazeNode.kt:289)
at dev.chrisbanes.haze.AndroidHazeNodeKt.clipShape-SI_Vg3w(AndroidHazeNode.kt:534)
at dev.chrisbanes.haze.AndroidHazeNodeKt.access$clipShape-SI_Vg3w(AndroidHazeNode.kt:1)
at dev.chrisbanes.haze.RenderNodeImpl.draw(AndroidHazeNode.kt:289)
at dev.chrisbanes.haze.AndroidHazeNode.draw(AndroidHazeNode.kt:135)
at androidx.compose.ui.node.LayoutNodeDrawScope.drawDirect-x_KDEd0$ui_release(LayoutNodeDrawScope.kt:105)
Hi,
The new APIs are great and more friendly, but there's an issue when part of the parent gets invisible (When the parent is inside a LazyColumn for example), the boundsInRoot() that is being used to make the position calculations is not the best option for this case because it's ignoring the invisible area of the composable, I'll change it with positionInRoot() which fixes the issue.
Not sure if this is due to Dialog window, but when displaying a compose Dialog containing a Surface, the haze doesn't follow the dialog, but remains frozen in the top left corner of the screen. .hazeChild() is set on the Surface.
Hello @chrisbanes, thank you for the very useful library.
I'm attempting to implement the logic for enabling and disabling the blur effect, but it doesn't seem to be functioning as expected.
Here's the simple code I'm using:
When adding the haze dependency (without using it anywhere) and running my app with a CircularProgressIndicator being shown it crashes. After removing the dependency it doesn't crash. Tested it multiple times and same result.
Steps to Reproduce the Problem
java.lang.NoSuchMethodError: No virtual method at(Ljava/lang/Object;I)
Landroidx/compose/animation/core/KeyframesSpec$KeyframeEntity; in class
Landroidx/compose/animation/core/KeyframesSpec$KeyframesSpecConfig; or its super classes (declaration of 'androidx.compose.animation.core.KeyframesSpec$KeyframesSpecConfig' appears in /data/app/~~i_o5gVWscTtLwqcpHsHQ4Q==/
at androidx.compose.material3.ProgressIndicatorKt$CircularProgressIndicator$endAngle$1.invoke(ProgressIndicator.kt:371)
at androidx.compose.material3.ProgressIndicatorKt$CircularProgressIndicator$endAngle$1.invoke(ProgressIndicator.kt:369)
Currently the tests will use the system default font, meaning that the results will be different on each platform. We should bundle a font in the tests and use that for consistency.
Hi Chris,
Thanks for this amazing Modifier, it's really helpful.
I have similar use case but I need to add a border radius to the area, I'm working on implementing this feature and I will create a PR when it's ready.
I'm going to create a new public composable that accepts a List<RoundRect>