Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BlurView breaks Compose recomposition #195

Open
zakrodionov opened this issue Jan 30, 2023 · 10 comments
Open

BlurView breaks Compose recomposition #195

zakrodionov opened this issue Jan 30, 2023 · 10 comments

Comments

@zakrodionov
Copy link

zakrodionov commented Jan 30, 2023

  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>
@Dimezis
Copy link
Owner

Dimezis commented Jan 30, 2023

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.

@zakrodionov
Copy link
Author

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,
    )
}

@Dimezis
Copy link
Owner

Dimezis commented Jan 31, 2023

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

@anthony-fresneau-kiplin

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

@Dimezis
Copy link
Owner

Dimezis commented Mar 23, 2023

@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.

@ruby-turn2cloud
Copy link

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.

@williankl
Copy link

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 👀

@ruby-turn2cloud
Copy link

@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>

@williankl
Copy link

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

@Dimezis
Copy link
Owner

Dimezis commented Apr 7, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants