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

[BottomSheetDialog] AbstractComposeView can't be inserted a layout file used to a BottomSheetDialog. #4207

Open
bytebeats opened this issue Jun 25, 2024 · 0 comments · May be fixed by #4208
Labels

Comments

@bytebeats
Copy link

Description:

  • I have one BottomSheetDialog#setContentView(R.layout.earn_more_view).
  • Inside the layout file, I inserted ComposeEarnMoreView who extends from AbstractComposeView, and Of course, the @Composable override fun Content() was implemented by Composable functions.
  • My app just crashed when I tried to pop up the dialog.

And the main log looks like below:

FATAL EXCEPTION: main
Process: org.alias.staging, PID: 8224
java.lang.IllegalStateException: ViewTreeLifecycleOwner not found from android.widget.FrameLayout{b3ad65d V.E...... ......I. 0,0-0,0 #7f0902db app:id/container}
at androidx.compose.ui.platform.WindowRecomposer_androidKt.createLifecycleAwareWindowRecomposer(WindowRecomposer.android.kt:352)
at androidx.compose.ui.platform.WindowRecomposer_androidKt.createLifecycleAwareWindowRecomposer$default(WindowRecomposer.android.kt:325)
at androidx.compose.ui.platform.WindowRecomposerFactory$Companion.LifecycleAware$lambda$0(WindowRecomposer.android.kt:168)
at androidx.compose.ui.platform.WindowRecomposerFactory$Companion.$r8$lambda$PmWZXv-2LDhDmANvYhil4YZYJuQ(Unknown Source:0)
at androidx.compose.ui.platform.WindowRecomposerFactory$Companion$$ExternalSyntheticLambda0.createRecomposer(Unknown Source:0)
at androidx.compose.ui.platform.WindowRecomposerPolicy.createAndInstallWindowRecomposer$ui_release(WindowRecomposer.android.kt:224)
at androidx.compose.ui.platform.WindowRecomposer_androidKt.getWindowRecomposer(WindowRecomposer.android.kt:300)
at androidx.compose.ui.platform.AbstractComposeView.resolveParentCompositionContext(ComposeView.android.kt:244)
at androidx.compose.ui.platform.AbstractComposeView.ensureCompositionCreated(ComposeView.android.kt:251)
at androidx.compose.ui.platform.AbstractComposeView.onAttachedToWindow(ComposeView.android.kt:283)
at android.view.View.dispatchAttachedToWindow(View.java:21748)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3732)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3739)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3739)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3739)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3739)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3739)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3739)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3739)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3739)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3859)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:3146)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:11127)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1599)
at android.view.Choreographer.doCallbacks(Choreographer.java:1263)
at android.view.Choreographer.doFrame(Choreographer.java:1127)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1549)
at android.os.Handler.handleCallback(Handler.java:966)
at android.os.Handler.dispatchMessage(Handler.java:110)
at android.os.Looper.loopOnce(Looper.java:205)
at android.os.Looper.loop(Looper.java:293)
at android.app.ActivityThread.loopProcess(ActivityThread.java:9986)
at android.app.ActivityThread.main(ActivityThread.java:9975)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:586)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1240)

Expected behavior: My app won't crash and the layout file should be shown in the BottomSheetDailog.

Source code: BottomSheetDialog or its parent AppCompatDialog or its delegate AppCompatDelegateImpl has no LifecycleOwner.

If we check the source code of androidx.activity.ComponentDialog, which implements LifecycleOwner and calledwindow!!.decorView.setViewTreeLifecycleOwner(this) when add/setContentView.

But BottomSheetDialog/AppCompatDialog/AppCompatDelegateImpl didn't.

Minimal sample app repro:

class CrashBottomSheetDialog(context: Context) : BottomSheetDialog(context) {
  init {
    setContentView(object : AbstractComposeView(context) {
      @Composable
      override fun Content() {
        Text(text = "Hello, World")
      }
    })
  }
}

Android API version: Android 10

Material Library version: com.google.android.material:material:1.9.0~1.12.0

Device: Huawei Mate 50

Solution:: I found this issue around May, 2023. I thought google team would fix this soon but actually they didn't. So I create an Issue for it to see if I could push google team to fix it inside the material library.

I had a solution like below, hoping it could inspire you guys somehow. Also I could create a PR like the solution below or create a PR inside AppCompatDelegateImpl. Tell me what you think.

class ComponentBottomSheetDialog @JvmOverloads constructor(
  context: Context,
  @StyleRes themeResId: Int = 0
) : BottomSheetDialog(context, themeResId),
  LifecycleOwner,
  SavedStateRegistryOwner {

  private var _lifecycleRegistry: LifecycleRegistry? = null
  private val lifecycleRegistry: LifecycleRegistry
    get() = _lifecycleRegistry ?: LifecycleRegistry(this).also {
      _lifecycleRegistry = it
    }

  private val savedStateRegistryController: SavedStateRegistryController =
    SavedStateRegistryController.create(this)
  override val savedStateRegistry: SavedStateRegistry
    get() = savedStateRegistryController.savedStateRegistry

  override val lifecycle: Lifecycle
    get() = lifecycleRegistry

  override fun onSaveInstanceState(): Bundle {
    val bundle = super.onSaveInstanceState()
    savedStateRegistryController.performSave(bundle)
    return bundle
  }

  @Suppress("ClassVerificationFailure") // needed for onBackInvokedDispatcher call
  @CallSuper
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    savedStateRegistryController.performRestore(savedInstanceState)
    lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
  }

  @CallSuper
  override fun onStart() {
    super.onStart()
    lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
  }

  @CallSuper
  override fun onStop() {
    // This is the closest thing to onDestroy that a Dialog has
    lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    _lifecycleRegistry = null
    super.onStop()
  }

  override fun setContentView(layoutResID: Int) {
    initViewTreeOwners()
    super.setContentView(layoutResID)
  }

  override fun setContentView(view: View) {
    initViewTreeOwners()
    super.setContentView(view)
  }

  override fun setContentView(view: View, params: ViewGroup.LayoutParams?) {
    initViewTreeOwners()
    super.setContentView(view, params)
  }

  override fun addContentView(view: View, params: ViewGroup.LayoutParams?) {
    initViewTreeOwners()
    super.addContentView(view, params)
  }

  private fun initViewTreeOwners() {
    window!!.decorView.setViewTreeLifecycleOwner(this)
    window!!.decorView.setViewTreeSavedStateRegistryOwner(this)
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
1 participant