@@ -296,13 +296,66 @@ class FeedbackShakeIntegrationTest {
296296 }
297297
298298 @Test
299- fun `setDialog without startShakeDetection only tracks the dialog` () {
299+ fun `setDialog without startShakeDetection tracks the dialog but does not start detection` () {
300+ whenever(fixture.application.getSystemService(any())).thenReturn(null )
300301 val sut = fixture.getSut(useShakeGesture = false )
301302 sut.register(fixture.scopes, fixture.options)
302303
303304 sut.setDialog(createShakeDialog(), false )
304305
305- verify(fixture.application, never()).registerActivityLifecycleCallbacks(any())
306+ // Callbacks are registered to release the dialog on activity destroy, but the shake
307+ // detector itself is not started.
308+ verify(fixture.application).registerActivityLifecycleCallbacks(any())
309+ verify(fixture.application, never()).getSystemService(eq(Context .SENSOR_SERVICE ))
310+ }
311+
312+ @Test
313+ fun `clearing a tracking-only dialog unregisters the callbacks` () {
314+ val sut = fixture.getSut(useShakeGesture = false )
315+ sut.register(fixture.scopes, fixture.options)
316+ sut.setDialog(createShakeDialog(), false )
317+
318+ sut.setDialog(null , false )
319+
320+ verify(fixture.application).unregisterActivityLifecycleCallbacks(any())
321+ }
322+
323+ @Test
324+ fun `destroying the host activity of a tracking-only dialog releases it` () {
325+ // Guards against leaking the dialog (and its activity) through the strong reference when
326+ // detection is not running.
327+ val sut = fixture.getSut(useShakeGesture = false )
328+ sut.register(fixture.scopes, fixture.options)
329+ val dialog = createShakeDialog()
330+ sut.setDialog(dialog, false )
331+
332+ sut.onActivityDestroyed(dialog.activity)
333+
334+ verify(fixture.application).unregisterActivityLifecycleCallbacks(any())
335+ }
336+
337+ @Test
338+ fun `creating a different activity releases the tracked dialog` () {
339+ val sut = fixture.getSut(useShakeGesture = false )
340+ sut.register(fixture.scopes, fixture.options)
341+ sut.setDialog(createShakeDialog(), false )
342+
343+ val otherActivity = Robolectric .buildActivity(Activity ::class .java).setup().get()
344+ sut.onActivityCreated(otherActivity, null )
345+
346+ verify(fixture.application).unregisterActivityLifecycleCallbacks(any())
347+ }
348+
349+ @Test
350+ fun `creating the dialog's own host activity keeps the tracked dialog` () {
351+ val sut = fixture.getSut(useShakeGesture = false )
352+ sut.register(fixture.scopes, fixture.options)
353+ val dialog = createShakeDialog()
354+ sut.setDialog(dialog, false )
355+
356+ sut.onActivityCreated(dialog.activity, null )
357+
358+ verify(fixture.application, never()).unregisterActivityLifecycleCallbacks(any())
306359 }
307360
308361 @Test
@@ -345,13 +398,18 @@ class FeedbackShakeIntegrationTest {
345398 }
346399
347400 @Test
348- fun `disable stops shake detection when the tracked dialog did not opt in ` () {
401+ fun `disable keeps callbacks registered while a tracking-only dialog is set ` () {
349402 val sut = fixture.getSut(useShakeGesture = true )
350403 sut.register(fixture.scopes, fixture.options)
351- sut.setDialog(createShakeDialog(), false )
404+ val dialog = createShakeDialog()
405+ sut.setDialog(dialog, false )
352406
353407 sut.disable()
354408
409+ // Detection stops, but the callbacks stay registered to release the tracked dialog once
410+ // its host activity goes away.
411+ verify(fixture.application, never()).unregisterActivityLifecycleCallbacks(any())
412+ sut.onActivityDestroyed(dialog.activity)
355413 verify(fixture.application).unregisterActivityLifecycleCallbacks(any())
356414 }
357415
@@ -370,14 +428,18 @@ class FeedbackShakeIntegrationTest {
370428 }
371429
372430 @Test
373- fun `replacing an opted-in dialog with a tracking-only one stops detection when globally disabled ` () {
431+ fun `replacing an opted-in dialog with a tracking-only one drops the opt-in ` () {
374432 val sut = fixture.getSut(useShakeGesture = false )
375433 sut.register(fixture.scopes, fixture.options)
376434 sut.setDialog(createShakeDialog(), true )
377435
378- // A different dialog only reporting visibility no longer justifies detection
379- sut.setDialog(createShakeDialog(), false )
436+ // A different dialog only reporting visibility no longer justifies detection; the
437+ // callbacks stay registered only to track the new dialog's host activity.
438+ val dialog = createShakeDialog()
439+ sut.setDialog(dialog, false )
440+ verify(fixture.application, never()).unregisterActivityLifecycleCallbacks(any())
380441
442+ sut.setDialog(null , false )
381443 verify(fixture.application).unregisterActivityLifecycleCallbacks(any())
382444 }
383445
0 commit comments