Do not use mStaleListener if tile is listening
In that case, just refresh the tile directly. This will schedule a new stale state. Additionally, make sure that QQS/QS are only listening when QS is actually visible. Test: atest SystemUITestsi Test: QSLog Fixes: 232427992 Change-Id: I44d8f23f1ceea37bcd479ee23c7cdd4b0c1c14e0
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -161,9 +161,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
|
||||
/** */
|
||||
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.
|
||||
|
||||
@@ -308,12 +308,17 @@ public abstract class QSPanelControllerBase<T extends QSPanel> 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) {
|
||||
|
||||
@@ -104,15 +104,6 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel>
|
||||
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();
|
||||
|
||||
@@ -175,9 +175,6 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
|
||||
mListening = listening;
|
||||
|
||||
mQuickQSPanelController.setListening(listening);
|
||||
if (mQuickQSPanelController.isListening()) {
|
||||
mQuickQSPanelController.refreshAllTiles();
|
||||
}
|
||||
|
||||
if (mQuickQSPanelController.switchTileLayout(false)) {
|
||||
mView.updateResources();
|
||||
|
||||
@@ -233,7 +233,13 @@ public abstract class QSTileImpl<TState extends State> 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() {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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<QSTile.BooleanState> {
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user