diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java index f78046d381686..57193e7060f95 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java @@ -33,7 +33,7 @@ public interface QS extends FragmentBase { String ACTION = "com.android.systemui.action.PLUGIN_QS"; - int VERSION = 14; + int VERSION = 15; String TAG = "QS"; @@ -50,6 +50,14 @@ public interface QS extends FragmentBase { void setOverscrolling(boolean overscrolling); void setExpanded(boolean qsExpanded); void setListening(boolean listening); + + /** + * Set whether QQS/QS is visible or not. + * + * This is different from setExpanded, as it will be true when QQS is visible. In particular, + * it should be false when device is locked and only notifications (in lockscreen) are visible. + */ + void setQsVisible(boolean qsVisible); boolean isShowingDetail(); void closeDetail(); void animateHeaderSlidingOut(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index f87cb29a87581..a92c99ea3bc9f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -72,6 +72,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca private static final boolean DEBUG = false; private static final String EXTRA_EXPANDED = "expanded"; private static final String EXTRA_LISTENING = "listening"; + private static final String EXTRA_VISIBLE = "visible"; private final Rect mQsBounds = new Rect(); private final StatusBarStateController mStatusBarStateController; @@ -148,6 +149,10 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca private boolean mOverScrolling; + // Whether QQS or QS is visible. When in lockscreen, this is true if and only if QQS or QS is + // visible; + private boolean mQsVisible; + @Inject public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler, QSTileHost qsTileHost, @@ -224,6 +229,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca mQSCustomizerController.init(); mQSCustomizerController.setQs(this); if (savedInstanceState != null) { + setQsVisible(savedInstanceState.getBoolean(EXTRA_VISIBLE)); setExpanded(savedInstanceState.getBoolean(EXTRA_EXPANDED)); setListening(savedInstanceState.getBoolean(EXTRA_LISTENING)); setEditLocation(view); @@ -285,6 +291,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca super.onSaveInstanceState(outState); outState.putBoolean(EXTRA_EXPANDED, mQsExpanded); outState.putBoolean(EXTRA_LISTENING, mListening); + outState.putBoolean(EXTRA_VISIBLE, mQsVisible); if (mQSCustomizerController != null) { mQSCustomizerController.saveInstanceState(outState); } @@ -303,6 +310,11 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca return mQsExpanded; } + @VisibleForTesting + boolean isQsVisible() { + return mQsVisible; + } + @Override public View getHeader() { return mHeader; @@ -458,7 +470,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca public void setExpanded(boolean expanded) { if (DEBUG) Log.d(TAG, "setExpanded " + expanded); mQsExpanded = expanded; - mQSPanelController.setListening(mListening, mQsExpanded); + updateQsPanelControllerListening(); updateQsState(); } @@ -486,9 +498,20 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca public void setListening(boolean listening) { if (DEBUG) Log.d(TAG, "setListening " + listening); mListening = listening; - mQSContainerImplController.setListening(listening); - mQSFooterActionController.setListening(listening); - mQSPanelController.setListening(mListening, mQsExpanded); + mQSContainerImplController.setListening(listening && mQsVisible); + mQSFooterActionController.setListening(listening && mQsVisible); + updateQsPanelControllerListening(); + } + + private void updateQsPanelControllerListening() { + mQSPanelController.setListening(mListening && mQsVisible, mQsExpanded); + } + + @Override + public void setQsVisible(boolean visible) { + if (DEBUG) Log.d(TAG, "setQsVisible " + visible); + mQsVisible = visible; + setListening(mListening); } @Override @@ -836,6 +859,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca indentingPw.println("mHeaderAnimating: " + mHeaderAnimating); indentingPw.println("mStackScrollerOverscrolling: " + mStackScrollerOverscrolling); indentingPw.println("mListening: " + mListening); + indentingPw.println("mQsVisible: " + mQsVisible); indentingPw.println("mLayoutDirection: " + mLayoutDirection); indentingPw.println("mLastQSExpansion: " + mLastQSExpansion); indentingPw.println("mLastPanelFraction: " + mLastPanelFraction); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java index 851307ac615f6..918c6be582e76 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java @@ -161,9 +161,6 @@ public class QSPanelController extends QSPanelControllerBase { /** */ public void setListening(boolean listening, boolean expanded) { setListening(listening && expanded); - if (mView.isListening()) { - refreshAllTiles(); - } // Set the listening as soon as the QS fragment starts listening regardless of the //expansion, so it will update the current brightness before the slider is visible. diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java index 93f245b9698a7..ec61ea6fffa82 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java @@ -308,12 +308,17 @@ public abstract class QSPanelControllerBase extends ViewContr } void setListening(boolean listening) { + if (mView.isListening() == listening) return; mView.setListening(listening); if (mView.getTileLayout() != null) { mQSLogger.logAllTilesChangeListening(listening, mView.getDumpableTag(), mCachedSpecs); mView.getTileLayout().setListening(listening, mUiEventLogger); } + + if (mView.isListening()) { + refreshAllTiles(); + } } boolean switchTileLayout(boolean force) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java index c6ebd732518cc..833573dd38730 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java @@ -104,15 +104,6 @@ public class QuickQSPanelController extends QSPanelControllerBase mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener); } - @Override - void setListening(boolean listening) { - super.setListening(listening); - } - - public boolean isListening() { - return mView.isListening(); - } - private void setMaxTiles(int parseNumTiles) { mView.setMaxTiles(parseNumTiles); setTiles(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java index 2c6972a158906..ec0d0811ee760 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java @@ -175,9 +175,6 @@ class QuickStatusBarHeaderController extends ViewController implements QSTile, Lifecy @VisibleForTesting protected void handleStale() { - setListening(mStaleListener, true); + if (!mListeners.isEmpty()) { + // If the tile is already listening (it's been a long time since it refreshed), just + // force a refresh. Don't add the staleListener because there's already a listener there + refreshState(); + } else { + setListening(mStaleListener, true); + } } public String getTileSpec() { 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 f15ea627af479..ac72304eec1ed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -2569,6 +2569,7 @@ public class NotificationPanelViewController extends PanelViewController { mQsClipTop = (int) (top - currentTranslation - mQsFrame.getTop()); mQsClipBottom = (int) (bottom - currentTranslation - mQsFrame.getTop()); mQsVisible = qsVisible; + mQs.setQsVisible(mQsVisible); mQs.setFancyClipping( mQsClipTop, mQsClipBottom, diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java index a518b808688e4..664af75a6529a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java @@ -20,6 +20,8 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -161,6 +163,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { QSFragment qs = (QSFragment) mFragment; qs.setListening(true); qs.setExpanded(true); + qs.setQsVisible(true); processAllMessages(); recreateFragment(); processAllMessages(); @@ -169,6 +172,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { qs = (QSFragment) mFragment; assertTrue(qs.isListening()); assertTrue(qs.isExpanded()); + assertTrue(qs.isQsVisible()); } @Test @@ -333,6 +337,54 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { assertThat(mQsFragmentView.getTranslationY()).isEqualTo(0); } + @Test + public void setListeningFalse_notVisible() { + QSFragment fragment = resumeAndGetFragment(); + fragment.setQsVisible(false); + clearInvocations(mQSContainerImplController, mQSPanelController, mQSFooterActionController); + + fragment.setListening(false); + verify(mQSContainerImplController).setListening(false); + verify(mQSFooterActionController).setListening(false); + verify(mQSPanelController).setListening(eq(false), anyBoolean()); + } + + @Test + public void setListeningTrue_notVisible() { + QSFragment fragment = resumeAndGetFragment(); + fragment.setQsVisible(false); + clearInvocations(mQSContainerImplController, mQSPanelController, mQSFooterActionController); + + fragment.setListening(true); + verify(mQSContainerImplController).setListening(false); + verify(mQSFooterActionController).setListening(false); + verify(mQSPanelController).setListening(eq(false), anyBoolean()); + } + + @Test + public void setListeningFalse_visible() { + QSFragment fragment = resumeAndGetFragment(); + fragment.setQsVisible(true); + clearInvocations(mQSContainerImplController, mQSPanelController, mQSFooterActionController); + + fragment.setListening(false); + verify(mQSContainerImplController).setListening(false); + verify(mQSFooterActionController).setListening(false); + verify(mQSPanelController).setListening(eq(false), anyBoolean()); + } + + @Test + public void setListeningTrue_visible() { + QSFragment fragment = resumeAndGetFragment(); + fragment.setQsVisible(true); + clearInvocations(mQSContainerImplController, mQSPanelController, mQSFooterActionController); + + fragment.setListening(true); + verify(mQSContainerImplController).setListening(true); + verify(mQSFooterActionController).setListening(true); + verify(mQSPanelController).setListening(eq(true), anyBoolean()); + } + @Override protected Fragment instantiate(Context context, String className, Bundle arguments) { MockitoAnnotations.initMocks(this); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java index 4dbf3d8cde1b2..c127a6b87da7a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java @@ -137,6 +137,10 @@ public class QSPanelControllerBaseTest extends SysuiTestCase { when(mMediaHost.getDisappearParameters()).thenReturn(new DisappearParameters()); when(mQSPanel.getResources()).thenReturn(mResources); when(mResources.getConfiguration()).thenReturn(mConfiguration); + doAnswer(invocation -> { + when(mQSPanel.isListening()).thenReturn(invocation.getArgument(0)); + return null; + }).when(mQSPanel).setListening(anyBoolean()); mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost, mQSCustomizerController, mMediaHost, diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt index 69d3f8b313c9d..c0944efcffca9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt @@ -21,6 +21,7 @@ import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.any @@ -62,6 +63,9 @@ class QSPanelControllerTest : SysuiTestCase() { whenever(brightnessControllerFactory.create(any())).thenReturn(brightnessController) whenever(qsPanel.resources).thenReturn(mContext.orCreateTestableResources.resources) whenever(statusBarKeyguardViewManager.isBouncerInTransit()).thenReturn(false) + whenever(qsPanel.setListening(anyBoolean())).then { + whenever(qsPanel.isListening).thenReturn(it.getArgument(0)) + } controller = QSPanelController( qsPanel, @@ -100,7 +104,6 @@ class QSPanelControllerTest : SysuiTestCase() { controller.setTiles() whenever(tile.isListening()).thenReturn(false) whenever(otherTile.isListening()).thenReturn(true) - whenever(qsPanel.isListening).thenReturn(true) controller.setListening(true, true) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java index c31a971cc7756..5336ef09f368b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java @@ -379,6 +379,34 @@ public class QSTileImplTest extends SysuiTestCase { assertFalse(mTile.isListening()); } + @Test + public void testStaleTriggeredWhileListening() throws Exception { + Object o = new Object(); + mTile.clearRefreshes(); + + mTile.setListening(o, true); // +1 refresh + mTestableLooper.processAllMessages(); + + mTestableLooper.runWithLooper(() -> mTile.handleStale()); // +1 refresh + mTestableLooper.processAllMessages(); + + mTile.setListening(o, false); + mTestableLooper.processAllMessages(); + assertFalse(mTile.isListening()); + assertThat(mTile.mRefreshes).isEqualTo(2); + } + + @Test + public void testStaleTriggeredWhileNotListening() throws Exception { + mTile.clearRefreshes(); + + mTestableLooper.runWithLooper(() -> mTile.handleStale()); // +1 refresh + mTestableLooper.processAllMessages(); + + assertFalse(mTile.isListening()); + assertThat(mTile.mRefreshes).isEqualTo(1); + } + private void assertEvent(UiEventLogger.UiEventEnum eventType, UiEventLoggerFake.FakeUiEvent fakeEvent) { assertEquals(eventType.getId(), fakeEvent.eventId); @@ -418,9 +446,9 @@ public class QSTileImplTest extends SysuiTestCase { return mInvalid; } } - private static class TileImpl extends QSTileImpl { boolean mClicked; + int mRefreshes = 0; protected TileImpl( QSHost host, @@ -453,6 +481,11 @@ public class QSTileImplTest extends SysuiTestCase { @Override protected void handleUpdateState(BooleanState state, Object arg) { + mRefreshes++; + } + + void clearRefreshes() { + mRefreshes = 0; } @Override