diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/DetailAdapter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/DetailAdapter.java index 95c2d2efcd89b..6d1408d5d2124 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/DetailAdapter.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/DetailAdapter.java @@ -72,7 +72,8 @@ public interface DetailAdapter { } /** - * @return if detail panel should animate when shown or closed + * Indicates whether the detail view wants to animate when shown. This has no affect over the + * closing animation. Detail panels will always animate when closed. */ default boolean shouldAnimate() { return true; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java index 1d6c7c94dcc33..929927e5d4e43 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java @@ -212,6 +212,11 @@ public class QSDetail extends LinearLayout { Dependency.get(CommandQueue.class).animateCollapsePanels(); mTriggeredExpand = false; } + // Always animate on close, even if the last opened detail adapter had shouldAnimate() + // return false. This is necessary to avoid a race condition which could leave the + // keyguard in a bad state where QS remains visible underneath the notifications, clock, + // and status area. + mShouldAnimate = true; } boolean visibleDiff = wasShowingDetail != showingDetail; @@ -245,10 +250,15 @@ public class QSDetail extends LinearLayout { mClosingDetail = true; mDetailAdapter = null; listener = mTeardownDetailWhenDone; - mHeader.setVisibility(View.VISIBLE); - mFooter.setVisibility(View.VISIBLE); - mQsPanelController.setGridContentVisibility(true); - mQsPanelCallback.onScanStateChanged(false); + // Only update visibility if already expanded. Otherwise, a race condition can cause the + // keyguard to enter a bad state where the QS tiles are displayed underneath the + // notifications, clock, and status area. + if (mQsPanelController.isExpanded()) { + mHeader.setVisibility(View.VISIBLE); + mFooter.setVisibility(View.VISIBLE); + mQsPanelController.setGridContentVisibility(true); + mQsPanelCallback.onScanStateChanged(false); + } } sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); animateDetailVisibleDiff(x, y, visibleDiff, listener); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index f52aad96ce693..6fc6730eaddd6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -1571,7 +1571,10 @@ public class NotificationPanelViewController extends PanelViewController { public void expandWithQsDetail(DetailAdapter qsDetailAdapter) { traceQsJank(true /* startTracing */, false /* wasCancelled */); flingSettings(0 /* velocity */, FLING_EXPAND); - mQSDetailDisplayer.showDetailAdapter(qsDetailAdapter, 0, 0); + // When expanding with a panel, there's no meaningful touch point to correspond to. Set the + // origin to somewhere above the screen. This is used for animations. + int x = mQsFrame.getWidth() / 2; + mQSDetailDisplayer.showDetailAdapter(qsDetailAdapter, x, -getHeight()); if (mAccessibilityManager.isEnabled()) { mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java index c2bd024f0375e..84776c7eb18f2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java @@ -148,8 +148,10 @@ public class QSDetailTest extends SysuiTestCase { eq(true) /* in */, any()); clearInvocations(mQsDetail.mClipper); + // Detail adapters should always animate on close. shouldAnimate() should only affect the + // open transition mQsDetail.handleShowingDetail(null, 0, 0, false); - verify(mQsDetail.mClipper).updateCircularClip(eq(false) /* animate */, anyInt(), anyInt(), + verify(mQsDetail.mClipper).updateCircularClip(eq(true) /* animate */, anyInt(), anyInt(), eq(false) /* in */, any()); }