Jetpack Compose : Crash when adding view to window manager

Viewed 1783

I tried to add a jetpack compose view to the window manager for an App overlay view. But the app is crashing. I have used the following code for adding the jetpack compose view. It is working properly when I tried the XML layout. Anyone else faced this issue? Thanks in advance.

private val overlayWindowManager by lazy {
    getSystemService(Context.WINDOW_SERVICE)
            as WindowManager
}

 private fun initWindowManager() {
    val params = WindowManager.LayoutParams(
        WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
        WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
        PixelFormat.TRANSLUCENT
    )
    params.gravity = Gravity.RIGHT or Gravity.TOP
    overlayWindowManager.addView(ComposeView(this).apply {
        setContent {  Text("WINDOW") }
    }, params)
}

Error log

 java.lang.IllegalStateException: ViewTreeLifecycleOwner is not present in this window. Use ComponentActivity, FragmentActivity or AppCompatActivity to configure ViewTreeLifecycleOwner automatically, or call ViewTreeLifecycleOwner.set() for this View or an ancestor in the same window.
    at androidx.compose.ui.platform.AbstractComposeView.onAttachedToWindow(ComposeView.kt:176)
    at android.view.View.dispatchAttachedToWindow(View.java:20479)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3489)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2417)
    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1952)
    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8171)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:972)
    at android.view.Choreographer.doCallbacks(Choreographer.java:796)
    at android.view.Choreographer.doFrame(Choreographer.java:731)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:957)
    at android.os.Handler.handleCallback(Handler.java:938)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:223)
    at android.app.ActivityThread.main(ActivityThread.java:7656)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
3 Answers

ComposeView requires a LifecycleOwner, but we don't have it here, so we must create it.

internal class MyLifecycleOwner : SavedStateRegistryOwner {

    private var mLifecycleRegistry: LifecycleRegistry = LifecycleRegistry(this)
    private var mSavedStateRegistryController: SavedStateRegistryController = SavedStateRegistryController.create(this)

    val isInitialized: Boolean
        get() = true

    override fun getLifecycle(): Lifecycle {
        return mLifecycleRegistry
    }

    fun setCurrentState(state: Lifecycle.State) {
        mLifecycleRegistry.currentState = state
    }

    fun handleLifecycleEvent(event: Lifecycle.Event) {
        mLifecycleRegistry.handleLifecycleEvent(event)
    }

    override fun getSavedStateRegistry(): SavedStateRegistry {
        return mSavedStateRegistryController.savedStateRegistry
    }

    fun performRestore(savedState: Bundle?) {
        mSavedStateRegistryController.performRestore(savedState)
    }

    fun performSave(outBundle: Bundle) {
        mSavedStateRegistryController.performSave(outBundle)
    }
}

And then use MyLifecycleOwner as ViewTreeLifecycleOwner of composeView.

private fun initWindowManager() {
    val params = ...
    val composeView = ComposeView(context = context)
    composeView.setContent { . . . }

    val lifecycleOwner = MyLifecycleOwner()
    lifecycleOwner.performRestore(null)
    lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
    ViewTreeLifecycleOwner.set(composeView, lifecycleOwner)
    ViewTreeSavedStateRegistryOwner.set(composeView, lifecycleOwner)

    val viewModelStore = ViewModelStore()
    ViewTreeViewModelStoreOwner.set(composeView) { viewModelStore }

    overlayWindowManager.addView(composeView, params) 
}

You can check it with more details here https://gist.github.com/handstandsam/6ecff2f39da72c0b38c07aa80bbb5a2f

Use ViewTreeLifecycleOwner.set():

windowManager.addView(ComposeView(this).apply {
    ViewTreeLifecycleOwner.set(this, activity)
    setContent {  Text("WINDOW") }
}, params)

Which class does your main activity extend? looks like your answer is in the first line of the stack trace Use ComponentActivity, FragmentActivity or AppCompatActivity to configure ViewTreeLifecycleOwner automatically

or

call ViewTreeLifecycleOwner.set() for this View or an ancestor in the same window

Related