androidkotlinandroid-jetpack-composeoverlay

ViewTreeLifecycleOwner not found from ComposeView


I'm making a password entry overlay in my Android app when someone tries to open the selected app, but i got this error:

FATAL EXCEPTION: main
Process: com.example, PID: 10078
java.lang.IllegalStateException: ViewTreeLifecycleOwner not found from androidx.compose.ui.platform.ComposeView{cad8567 V.E...... ......I. 0,0-0,0}
    at androidx.compose.ui.internal.InlineClassHelperKt.throwIllegalStateExceptionForNullCheck(InlineClassHelper.kt:32)
    at androidx.compose.ui.platform.WindowRecomposer_androidKt.createLifecycleAwareWindowRecomposer(WindowRecomposer.android.kt:455)
    at androidx.compose.ui.platform.WindowRecomposer_androidKt.createLifecycleAwareWindowRecomposer$default(WindowRecomposer.android.kt:321)
    at androidx.compose.ui.platform.WindowRecomposerFactory$Companion.LifecycleAware$lambda$0(WindowRecomposer.android.kt:165)
    at androidx.compose.ui.platform.WindowRecomposerFactory$Companion.$r8$lambda$FWAPLXs0qWMqekhMr83xkKattCY(Unknown Source:0)
    at androidx.compose.ui.platform.WindowRecomposerFactory$Companion$$ExternalSyntheticLambda0.createRecomposer(D8$$SyntheticClass:0)
    at androidx.compose.ui.platform.WindowRecomposerPolicy.createAndInstallWindowRecomposer$ui_release(WindowRecomposer.android.kt:217)
    at androidx.compose.ui.platform.WindowRecomposer_androidKt.getWindowRecomposer(WindowRecomposer.android.kt:297)
    at androidx.compose.ui.platform.AbstractComposeView.resolveParentCompositionContext(ComposeView.android.kt:243)
    at androidx.compose.ui.platform.AbstractComposeView.ensureCompositionCreated(ComposeView.android.kt:250)
    at androidx.compose.ui.platform.AbstractComposeView.onAttachedToWindow(ComposeView.android.kt:280)
    at android.view.View.dispatchAttachedToWindow(View.java:23105)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3520)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3618)
    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:3077)
    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:10644)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1570)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1579)
    at android.view.Choreographer.doCallbacks(Choreographer.java:1179)
    at android.view.Choreographer.doFrame(Choreographer.java:1108)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1553)
    at android.os.Handler.handleCallback(Handler.java:995)
    at android.os.Handler.dispatchMessage(Handler.java:103)
    at android.os.Looper.loopOnce(Looper.java:248)
    at android.os.Looper.loop(Looper.java:338)
    at android.app.ActivityThread.main(ActivityThread.java:9067)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:593)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:932)

I searched for solutions on this site and found this question. I tried all the solutions in that question, but i got an error:

Cannot access 'fun performRestore(savedState: Bundle?): Unit': it is internal in 'androidx/savedstate/SavedStateRegistry'.

Here's my current code:

package com.example

import android.view.View
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner
import androidx.savedstate.SavedStateRegistryController
import androidx.savedstate.SavedStateRegistryOwner

class OverlayLifecycle : LifecycleOwner, ViewModelStoreOwner, SavedStateRegistryOwner {
    //Lifecylce Methods

    override val lifecycle get() = lifecycleRegistry
    override val viewModelStore get() = store

    private var lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(this)


    private fun handleLifecycleEvent(event: Lifecycle.Event) =
        lifecycleRegistry.handleLifecycleEvent(event)

    override fun onCreate() {
        super.onCreate()
        savedStateRegistry.performRestore(null)
        handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
    }

    override fun onDestroy() {
        super.onDestroy()
        handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    }


    //ViewModelStore Methods
    private val store = ViewModelStore()

    //SaveStateRegestry Methods

    override val savedStateRegistry get() = SavedStateRegistryController.create(this).savedStateRegistry
}

Also i don't know how to implement this properly inside a random function in the application. For example:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    // set the layout parameters of the window
    val params = WindowManager.LayoutParams( // Shrink the window to wrap the content rather
        // than filling the screen
        WindowManager.LayoutParams.WRAP_CONTENT,
        WindowManager.LayoutParams.WRAP_CONTENT,  // Display it on top of other application windows
        WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,  // Don't let it grab the input focus
        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,  // Make the underlying application window visible
        // through any transparent parts
        PixelFormat.TRANSLUCENT
    )

    val view = ComposeView {
        Text("gherdiguvofwfehgoesg")
    }


    // Define the position of the
    // window within the screen
    params.gravity = Gravity.CENTER
    val windowManager = context.getSystemService(WINDOW_SERVICE) as WindowManager
    runOrLog("Applocker") {
        // check if the view is already
        // inflated or present in the window
        if (view.windowToken == null && view.parent == null) {
            windowManager.addView(view, params);
        }
    }
}

I need some help with implementing the lifecycle owner class.


Solution

  • Use the following code:

    val view = ComposeView(this).apply {
        setContent {
            // content
        }
    
        // Trick The ComposeView into thinking we are tracking lifecycle
        val lifecycleOwner = ComposeLifecycleOwner()
        lifecycleOwner.performRestore(null)
        lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
        setViewTreeLifecycleOwner(lifecycleOwner)
        setViewTreeSavedStateRegistryOwner(lifecycleOwner)
    }
    

    ComposeLifecycleOwner:

    import android.os.Bundle
    import androidx.lifecycle.Lifecycle
    import androidx.lifecycle.LifecycleRegistry
    import androidx.savedstate.SavedStateRegistry
    import androidx.savedstate.SavedStateRegistryController
    import androidx.savedstate.SavedStateRegistryOwner
    
    class ComposeLifecycleOwner : SavedStateRegistryOwner {
        private var mLifecycleRegistry: LifecycleRegistry = LifecycleRegistry(this)
        private var mSavedStateRegistryController: SavedStateRegistryController = SavedStateRegistryController.create(this)
    
        /**
         * @return True if the Lifecycle has been initialized.
         */
        val isInitialized: Boolean
            get() = true
    
        override val lifecycle = mLifecycleRegistry
    
        fun setCurrentState(state: Lifecycle.State) {
            mLifecycleRegistry.currentState = state
        }
    
        fun handleLifecycleEvent(event: Lifecycle.Event) {
            mLifecycleRegistry.handleLifecycleEvent(event)
        }
    
        override val savedStateRegistry: SavedStateRegistry
            get() = mSavedStateRegistryController.savedStateRegistry
    
        fun performRestore(savedState: Bundle?) {
            mSavedStateRegistryController.performRestore(savedState)
        }
    
        fun performSave(outBundle: Bundle) {
            mSavedStateRegistryController.performSave(outBundle)
        }
    }
    

    Source: https://gist.github.com/handstandsam/6ecff2f39da72c0b38c07aa80bbb5a2f

    Thanks to @CommonsWare for giving me the idea!