diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt index 9e0bbb7624bdd..46f13cc6b0376 100644 --- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt @@ -206,31 +206,44 @@ constructor( } override fun animateViewIn(view: ViewGroup) { - ViewHierarchyAnimator.animateAddition( - view.getInnerView(), - ViewHierarchyAnimator.Hotspot.TOP, - Interpolators.EMPHASIZED_DECELERATE, - duration = ANIMATION_IN_DURATION, - includeMargins = true, - includeFadeIn = true, - // We can only request focus once the animation finishes. - onAnimationEnd = { - maybeGetAccessibilityFocus(view.getTag(INFO_TAG) as ChipbarInfo?, view) - }, - ) + val onAnimationEnd = Runnable { + maybeGetAccessibilityFocus(view.getTag(INFO_TAG) as ChipbarInfo?, view) + } + val added = + ViewHierarchyAnimator.animateAddition( + view.getInnerView(), + ViewHierarchyAnimator.Hotspot.TOP, + Interpolators.EMPHASIZED_DECELERATE, + duration = ANIMATION_IN_DURATION, + includeMargins = true, + includeFadeIn = true, + // We can only request focus once the animation finishes. + onAnimationEnd = onAnimationEnd, + ) + // If the view doesn't get animated, the [onAnimationEnd] runnable won't get run. So, just + // run it immediately. + if (!added) { + onAnimationEnd.run() + } } override fun animateViewOut(view: ViewGroup, removalReason: String?, onAnimationEnd: Runnable) { val innerView = view.getInnerView() innerView.accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_NONE - ViewHierarchyAnimator.animateRemoval( - innerView, - ViewHierarchyAnimator.Hotspot.TOP, - Interpolators.EMPHASIZED_ACCELERATE, - ANIMATION_OUT_DURATION, - includeMargins = true, - onAnimationEnd, - ) + val removed = + ViewHierarchyAnimator.animateRemoval( + innerView, + ViewHierarchyAnimator.Hotspot.TOP, + Interpolators.EMPHASIZED_ACCELERATE, + ANIMATION_OUT_DURATION, + includeMargins = true, + onAnimationEnd, + ) + // If the view doesn't get animated, the [onAnimationEnd] runnable won't get run. So, just + // run it immediately. + if (!removed) { + onAnimationEnd.run() + } updateGestureListening() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt index 400a504e4d023..c63ca3d0ec315 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt @@ -47,7 +47,6 @@ import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.temporarydisplay.TemporaryViewDisplayController import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator import com.android.systemui.temporarydisplay.chipbar.ChipbarLogger -import com.android.systemui.temporarydisplay.chipbar.FakeChipbarCoordinator import com.android.systemui.temporarydisplay.chipbar.SwipeChipbarAwayGestureHandler import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any @@ -137,7 +136,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() { uiEventLogger = MediaTttSenderUiEventLogger(uiEventLoggerFake) chipbarCoordinator = - FakeChipbarCoordinator( + ChipbarCoordinator( context, chipbarLogger, windowManager, diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt index 45eb1f9ec4310..dd04ac4d79f6b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt @@ -66,7 +66,7 @@ import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper class ChipbarCoordinatorTest : SysuiTestCase() { - private lateinit var underTest: FakeChipbarCoordinator + private lateinit var underTest: ChipbarCoordinator @Mock private lateinit var logger: ChipbarLogger @Mock private lateinit var accessibilityManager: AccessibilityManager @@ -100,7 +100,7 @@ class ChipbarCoordinatorTest : SysuiTestCase() { uiEventLoggerFake = UiEventLoggerFake() underTest = - FakeChipbarCoordinator( + ChipbarCoordinator( context, logger, windowManager, @@ -436,6 +436,23 @@ class ChipbarCoordinatorTest : SysuiTestCase() { verify(logger).logViewUpdate(eq(WINDOW_TITLE), eq("new title text"), any()) } + /** Regression test for b/266209420. */ + @Test + fun displayViewThenImmediateRemoval_viewStillRemoved() { + underTest.displayView( + createChipbarInfo( + Icon.Resource(R.drawable.ic_cake, contentDescription = null), + Text.Loaded("title text"), + endItem = ChipbarEndItem.Error, + ), + ) + val chipbarView = getChipbarView() + + underTest.removeView(DEVICE_ID, "test reason") + + verify(windowManager).removeView(chipbarView) + } + @Test fun swipeToDismiss_false_neverListensForGesture() { underTest.displayView( diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt deleted file mode 100644 index ffac8f6aabe68..0000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.temporarydisplay.chipbar - -import android.content.Context -import android.os.PowerManager -import android.view.ViewGroup -import android.view.WindowManager -import android.view.accessibility.AccessibilityManager -import com.android.systemui.classifier.FalsingCollector -import com.android.systemui.dump.DumpManager -import com.android.systemui.plugins.FalsingManager -import com.android.systemui.statusbar.VibratorHelper -import com.android.systemui.statusbar.policy.ConfigurationController -import com.android.systemui.util.concurrency.DelayableExecutor -import com.android.systemui.util.time.SystemClock -import com.android.systemui.util.view.ViewUtil -import com.android.systemui.util.wakelock.WakeLock - -/** A fake implementation of [ChipbarCoordinator] for testing. */ -class FakeChipbarCoordinator( - context: Context, - logger: ChipbarLogger, - windowManager: WindowManager, - mainExecutor: DelayableExecutor, - accessibilityManager: AccessibilityManager, - configurationController: ConfigurationController, - dumpManager: DumpManager, - powerManager: PowerManager, - falsingManager: FalsingManager, - falsingCollector: FalsingCollector, - swipeChipbarAwayGestureHandler: SwipeChipbarAwayGestureHandler, - viewUtil: ViewUtil, - vibratorHelper: VibratorHelper, - wakeLockBuilder: WakeLock.Builder, - systemClock: SystemClock, -) : - ChipbarCoordinator( - context, - logger, - windowManager, - mainExecutor, - accessibilityManager, - configurationController, - dumpManager, - powerManager, - falsingManager, - falsingCollector, - swipeChipbarAwayGestureHandler, - viewUtil, - vibratorHelper, - wakeLockBuilder, - systemClock, - ) { - override fun animateViewOut(view: ViewGroup, removalReason: String?, onAnimationEnd: Runnable) { - // Just bypass the animation in tests - onAnimationEnd.run() - } -}