BlurView icon indicating copy to clipboard operation
BlurView copied to clipboard

BlurView breaks Compose recomposition

Open zakrodionov opened this issue 2 years ago • 10 comments

  1. 2.0.3
  2. All devices / Android 12, Poco X3 Pro

I'm trying to display a notification with a blur on top of all views. When a notification is shown, Compose stops updating, does not respond to clicks. I wrote a small sample where this can be reproduced: https://github.com/zakrodionov/ComposeBugStuckComposition

NotificationView.kt

class NotificationView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {

    private val view: View = View.inflate(context, R.layout.view_notification, this)
    private val blurView = view.findViewById<BlurView>(R.id.blurView)

    init {
        val decorView = getActivityDecorView()
        val rootView = decorView?.findViewById(android.R.id.content) as ViewGroup
        val windowBackground = decorView.background

        blurView.setupWith(rootView, RenderScriptBlur(context)) // or RenderEffectBlur
            .setFrameClearDrawable(windowBackground)
            .setBlurRadius(2f)
    }

    private fun getActivityDecorView(): View? {
        var ctx = context
        var i = 0
        while (i < 4 && ctx != null && ctx !is Activity && ctx is ContextWrapper) {
            ctx = ctx.baseContext
            i++
        }
        return if (ctx is Activity) {
            ctx.window.decorView
        } else {
            null
        }
    }
}

ComposeFragment.kt

private fun showNotification() {
    val viewGroup =
        requireActivity().window?.decorView?.findViewById(android.R.id.content) as? ViewGroup
    val view =
        NotificationView(requireContext()).apply {
            setOnClickListener { viewGroup?.removeView(it) }
        }
    val params =
        FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.MATCH_PARENT,
            FrameLayout.LayoutParams.WRAP_CONTENT
        )
    params.gravity = Gravity.TOP or Gravity.CENTER
    viewGroup?.addView(view, params)
    view.bringToFront()
}

view_notification.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="300dp"
    android:orientation="vertical">

    <eightbitlab.com.blurview.BlurView
        android:id="@+id/blurView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:backgroundTint="@color/purple_200"
        app:blurOverlayColor="#A353536C">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="center"
            android:gravity="center"
            android:text="Notification title"
            android:textSize="30sp" />

    </eightbitlab.com.blurview.BlurView>

</LinearLayout>

zakrodionov avatar Jan 30 '23 07:01 zakrodionov

Fascinating. I haven't tested this kind of scenario, but a BlurView embedded in a Compose hierarchy should work, if that suits your needs. I'm assuming this breaks because of the software rendering that BlurView performs on a Compose hierarchy, but I'm not entirely sure how to debug or fix it.

Dimezis avatar Jan 30 '23 10:01 Dimezis

Thanks for the quick response! The problem is very strange and the Compose itself seems to be to blame. Wrapping the compositing screen and specifying setLayerType(View.LAYER_TYPE_SOFTWARE, null) fixes the problem, but the performance is terrible.

@Composable
fun SoftwareLayerComposable(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit,
) {
    AndroidView(
        factory = { context ->
            ComposeView(context).apply {
                setLayerType(View.LAYER_TYPE_SOFTWARE, null)
            }
        },
        update = { composeView ->
            composeView.setContent(content)
        },
        modifier = modifier,
    )
}

zakrodionov avatar Jan 31 '23 09:01 zakrodionov

Yeah, I wouldn't consider setting a software layer even if it fixes the problem

Dimezis avatar Jan 31 '23 11:01 Dimezis

Hi, any updates on this breaking compose recomposition problem? Thanks

anthony-fresneau-kiplin avatar Mar 23 '23 14:03 anthony-fresneau-kiplin

@anthony-fresneau-kiplin no updates. I'm not investigating it though. The only way to fix it on my side is to move to hardware rendering for UI snapshots, which comes with a whole bunch of caveats because of the API constraints.

You can try to report it to Compose team, but not sure if they'd want to investigate it either.

Dimezis avatar Mar 23 '23 14:03 Dimezis

I tried handling it using a combination of View and Compose and it works. I built the BlurView inside a View and used ComposeView to handle all the other views below or inside it.

ruby-turn2cloud avatar Apr 14 '23 02:04 ruby-turn2cloud

I tried handling it using a combination of View and Compose and it works. I built the BlurView inside a View and used ComposeView to handle all the other views below or inside it.

Can you elaborate on that @ruby-turn2cloud?
I'm facing the same issue but not really getting anywhere :eyes:

williankl avatar May 09 '23 12:05 williankl

@williankl I separate compose part and blur part like this:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.compose.ui.platform.ComposeView
        android:id="@+id/content_compose_view"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <eightbitlab.com.blurview.BlurView
        android:id="@+id/blur_view"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        app:blurOverlayColor="#66FFFFFF"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <androidx.compose.ui.platform.ComposeView
            android:id="@+id/compose_view_inside_blur_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </eightbitlab.com.blurview.BlurView>
</androidx.constraintlayout.widget.ConstraintLayout>

ruby-turn2cloud avatar May 10 '23 01:05 ruby-turn2cloud

Yup, I've tried this but still isn't working for me, unfortunately. Any other ideas or news?

williankl avatar May 10 '23 07:05 williankl

I reported this bug to Compose team, but they might just dismiss it and say I'm trying to do something stupid

Dimezis avatar Apr 07 '24 10:04 Dimezis