@@ -9,12 +9,15 @@ import android.view.MotionEvent
9
9
import android.view.View
10
10
import android.view.View.OnAttachStateChangeListener
11
11
import android.view.ViewTreeObserver.OnGlobalLayoutListener
12
+ import androidx.core.view.doOnAttach
13
+ import androidx.lifecycle.DefaultLifecycleObserver
12
14
import androidx.lifecycle.LifecycleOwner
13
15
import androidx.lifecycle.ViewTreeLifecycleOwner
14
16
import com.squareup.workflow1.ui.Compatible
15
17
import com.squareup.workflow1.ui.ViewEnvironment
16
18
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
17
19
import com.squareup.workflow1.ui.androidx.WorkflowAndroidXSupport
20
+ import com.squareup.workflow1.ui.androidx.WorkflowAndroidXSupport.lifecycleOwnerFromContext
18
21
import com.squareup.workflow1.ui.androidx.WorkflowLifecycleOwner
19
22
import com.squareup.workflow1.ui.androidx.WorkflowSavedStateRegistryAggregator
20
23
import com.squareup.workflow1.ui.container.DialogSession.KeyAndBundle
@@ -277,11 +280,11 @@ public class LayeredDialogSessions private constructor(
277
280
): LayeredDialogSessions {
278
281
val boundsRect = Rect ()
279
282
if (view.isAttachedToWindow) view.getGlobalVisibleRect(boundsRect)
280
- val bounds = MutableStateFlow (Rect (boundsRect))
283
+ val boundsStateFlow = MutableStateFlow (Rect (boundsRect))
281
284
282
285
return LayeredDialogSessions (
283
286
context = view.context,
284
- bounds = bounds ,
287
+ bounds = boundsStateFlow ,
285
288
cancelEvents = {
286
289
// Note similar code in DialogSession.
287
290
@@ -298,16 +301,36 @@ public class LayeredDialogSessions private constructor(
298
301
" Expected a ViewTreeLifecycleOwner on $view "
299
302
}
300
303
}.also { dialogs ->
304
+ fun closeAll () {
305
+ dialogs.update(emptyList(), ViewEnvironment .EMPTY ) {}
306
+ }
301
307
302
- val boundsListener = OnGlobalLayoutListener {
303
- if (view.getGlobalVisibleRect(boundsRect) && boundsRect != bounds.value) {
304
- bounds.value = Rect (boundsRect)
305
- }
306
- // Should we close the dialogs if getGlobalVisibleRect returns false?
307
- // https://github.com/square/workflow-kotlin/issues/599
308
+ // We rely on the hosting View's WorkflowLifecycleOwner to tell us to tear things down.
309
+ // WorkflowLifecycleOwner gets hooked up when the View is attached to its window.
310
+ // But the Activity might finish before the hosting view is ever attached. And we have
311
+ // lots of time to show Dialogs before then. They will leak.
312
+ //
313
+ // To guard against that we hang a default observer directly off of the Activity that
314
+ // will close all Dialogs when it is destroyed; and we remove it as soon as the hosting
315
+ // view is attached for the first time.
316
+ val failsafe = object : DefaultLifecycleObserver {
317
+ override fun onDestroy (owner : LifecycleOwner ) = closeAll()
318
+ }
319
+ lifecycleOwnerFromContext(view.context).lifecycle.addObserver(failsafe)
320
+ view.doOnAttach {
321
+ lifecycleOwnerFromContext(it.context).lifecycle.removeObserver(failsafe)
308
322
}
309
323
324
+ // While the hosting view is attached, monitor its bounds and report them
325
+ // through boundsStateFlow so that managed Dialogs can constrain themselves
326
+ // accordingly.
310
327
val attachStateChangeListener = object : OnAttachStateChangeListener {
328
+ val boundsListener = OnGlobalLayoutListener {
329
+ if (view.getGlobalVisibleRect(boundsRect) && boundsRect != boundsStateFlow.value) {
330
+ boundsStateFlow.value = Rect (boundsRect)
331
+ }
332
+ }
333
+
311
334
override fun onViewAttachedToWindow (v : View ) {
312
335
boundsListener.onGlobalLayout()
313
336
v.viewTreeObserver.addOnGlobalLayoutListener(boundsListener)
@@ -316,9 +339,9 @@ public class LayeredDialogSessions private constructor(
316
339
override fun onViewDetachedFromWindow (v : View ) {
317
340
// Don't leak the dialogs if we're suddenly yanked out of view.
318
341
// https://github.com/square/workflow-kotlin/issues/314
319
- dialogs.update(emptyList(), ViewEnvironment . EMPTY ) {}
342
+ closeAll()
320
343
v.viewTreeObserver.removeOnGlobalLayoutListener(boundsListener)
321
- bounds .value = Rect ()
344
+ boundsStateFlow .value = Rect ()
322
345
}
323
346
}
324
347
0 commit comments