skydoves / Balloon

:balloon: Modernized and sophisticated tooltips, fully customizable with an arrow and animations for Android.
https://skydoves.github.io/libraries/balloon/html/balloon/com.skydoves.balloon/index.html
Apache License 2.0
3.69k stars 289 forks source link

android.view.WindowManager$BadTokenException: Unable to add window -- token android.view.ViewRootImpl$W@1a464cb is not valid; is your activity running? #530

Closed faizazharr closed 8 months ago

faizazharr commented 10 months ago

Please complete the following information:

Describe the Bug: all balloon feature run well in another screen except ModalBottomSheet

Expected Behavior:

when I use balloonWindow.showAlignTop() or balloonWindow.showAlignBottom() on compose in ModalBottomSheet the balloon can display

FATAL EXCEPTION: main Process: com.example.app, PID: 26263 android.view.WindowManager$BadTokenException: Unable to add window -- token android.view.ViewRootImpl$W@1a464cb is not valid; is your activity running? at android.view.ViewRootImpl.setView(ViewRootImpl.java:1764) at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:567) at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:148) at android.widget.PopupWindow.invokePopup(PopupWindow.java:1687) at android.widget.PopupWindow.showAtLocation(PopupWindow.java:1407) at android.widget.PopupWindow.showAtLocation(PopupWindow.java:1373) at com.skydoves.balloon.Balloon.showOverlayWindow(Balloon.kt:905) at com.skydoves.balloon.Balloon.show$lambda$30(Balloon.kt:809) at com.skydoves.balloon.Balloon.$r8$lambda$yqtXp6c5cDdGPmFqxhm9SUPDAlk(Unknown Source:0) at com.skydoves.balloon.Balloon$$ExternalSyntheticLambda5.run(Unknown Source:6) at android.os.Handler.handleCallback(Handler.java:942) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loopOnce(Looper.java:226) at android.os.Looper.loop(Looper.java:313) at android.app.ActivityThread.main(ActivityThread.java:8762) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:604) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)

skydoves commented 9 months ago

Hey @faizazharr, apologies for the delayed response. It seems that you might be attaching the Balloon to the wrong lifecycle.

There are several use cases for displaying a balloon over a modal bottom sheet. However, ensure the following key points:

  1. If the Balloon should be dismissed along with the modal bottom sheet, make sure the Balloon is dismissed correctly before the modal bottom sheet is dismissed. Since the balloon uses the window token, if the token provider (modal bottom sheet) is dismissed from memory, the children will lose access to the provider's resources. You can dismiss Balloon using DisposableEffect or something like that, it must be dismissed earlier than the modal bottom sheet.

  2. If the Balloon needs to be shown regardless of the modal bottom sheet, set a valid lifecycle for the balloon as follows:

val lifecycleOwner = LocalLifecycleOwner.current
val builder = rememberBalloonBuilder {
  ..
  setLifecycleOwner(lifecycleOwner)
}

Ensure that you obtain the lifecycleOwner from a higher scope than the modal bottom sheet's lifecycle, as it must persist longer than the modal bottom sheet.

I hope these suggestions work well for your case.

fanjavaid commented 8 months ago

Ensure that you obtain the lifecycleOwner from a higher scope than the modal bottom sheet's lifecycle, as it must persist longer than the modal bottom sheet.

Is it applicable for regular Fragment too? use lifecycleOwner instead of viewLifecycleOwner?

skydoves commented 8 months ago

It really depends on the lifecycle of your component and balloon. If the balloon must belong to the lifecycle of your particular fragment, it must use viewLifecycleOwner over lifeycycleOwner.

fanjavaid commented 8 months ago

Okay thank you. Anyway, what context should i use if i call inside Fragment or custom view?

skydoves commented 8 months ago

For the Fragment, you should use viewLifecycleOwner. In case of Activity/Fragment/View, you can just use the balloon() extension, then it will automatically find the correct lifecycle owner, so you don't need to specify it manually.

private val editBalloon by balloon<EditBalloonFactory>()
skydoves commented 8 months ago

Hey guys, please feel free to reopen this issue if you still face the same problem after applying my suggestion. Thank you!

waheedkhan-dev commented 3 months ago

still getting the same issue after adding LocalLifecycleOwner.current in BalloonBuilder.

val lifecycleOwner = LocalLifecycleOwner.current val builder = rememberBalloonBuilder { setLifecycleOwner(lifecycleOwner) setArrowSize(10) setArrowPosition(0.5f) setArrowPositionRules(ArrowPositionRules.ALIGN_ANCHOR) setWidth(BalloonSizeSpec.WRAP) setHeight(BalloonSizeSpec.WRAP) setPadding(12) setMarginHorizontal(12) setCornerRadius(8f) setBackgroundColorResource(R.color.white) setBalloonAnimation(BalloonAnimation.ELASTIC) setIsVisibleOverlay(true) setOverlayColorResource(R.color.app_secondary) }