Merge "Wait for state refreshed in TileQueryHelper"
This commit is contained in:
committed by
Android (Google) Code Review
commit
213dfa6cbd
@@ -86,6 +86,10 @@ public interface QSTile {
|
||||
*/
|
||||
InstanceId getInstanceId();
|
||||
|
||||
default boolean isTileReady() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ProvidesInterface(version = Callback.VERSION)
|
||||
public interface Callback {
|
||||
public static final int VERSION = 1;
|
||||
|
||||
@@ -80,8 +80,6 @@ public class TileQueryHelper {
|
||||
mFinished = false;
|
||||
// Enqueue jobs to fetch every system tile and then ever package tile.
|
||||
addCurrentAndStockTiles(host);
|
||||
|
||||
addPackageTiles(host);
|
||||
}
|
||||
|
||||
public boolean isFinished() {
|
||||
@@ -122,23 +120,86 @@ public class TileQueryHelper {
|
||||
tile.destroy();
|
||||
continue;
|
||||
}
|
||||
tile.setListening(this, true);
|
||||
tile.refreshState();
|
||||
tile.setListening(this, false);
|
||||
tile.setTileSpec(spec);
|
||||
tilesToAdd.add(tile);
|
||||
}
|
||||
|
||||
mBgExecutor.execute(() -> {
|
||||
for (QSTile tile : tilesToAdd) {
|
||||
final QSTile.State state = tile.getState().copy();
|
||||
// Ignore the current state and get the generic label instead.
|
||||
state.label = tile.getTileLabel();
|
||||
tile.destroy();
|
||||
addTile(tile.getTileSpec(), null, state, true);
|
||||
new TileCollector(tilesToAdd, host).startListening();
|
||||
}
|
||||
|
||||
private static class TilePair {
|
||||
QSTile mTile;
|
||||
boolean mReady = false;
|
||||
}
|
||||
|
||||
private class TileCollector implements QSTile.Callback {
|
||||
|
||||
private final List<TilePair> mQSTileList = new ArrayList<>();
|
||||
private final QSTileHost mQSTileHost;
|
||||
|
||||
TileCollector(List<QSTile> tilesToAdd, QSTileHost host) {
|
||||
for (QSTile tile: tilesToAdd) {
|
||||
TilePair pair = new TilePair();
|
||||
pair.mTile = tile;
|
||||
mQSTileList.add(pair);
|
||||
}
|
||||
mQSTileHost = host;
|
||||
if (tilesToAdd.isEmpty()) {
|
||||
mBgExecutor.execute(this::finished);
|
||||
}
|
||||
}
|
||||
|
||||
private void finished() {
|
||||
notifyTilesChanged(false);
|
||||
});
|
||||
addPackageTiles(mQSTileHost);
|
||||
}
|
||||
|
||||
private void startListening() {
|
||||
for (TilePair pair: mQSTileList) {
|
||||
pair.mTile.addCallback(this);
|
||||
pair.mTile.setListening(this, true);
|
||||
// Make sure that at least one refresh state happens
|
||||
pair.mTile.refreshState();
|
||||
}
|
||||
}
|
||||
|
||||
// This is called in the Bg thread
|
||||
@Override
|
||||
public void onStateChanged(State s) {
|
||||
boolean allReady = true;
|
||||
for (TilePair pair: mQSTileList) {
|
||||
if (!pair.mReady && pair.mTile.isTileReady()) {
|
||||
pair.mTile.removeCallback(this);
|
||||
pair.mTile.setListening(this, false);
|
||||
pair.mReady = true;
|
||||
} else if (!pair.mReady) {
|
||||
allReady = false;
|
||||
}
|
||||
}
|
||||
if (allReady) {
|
||||
for (TilePair pair : mQSTileList) {
|
||||
QSTile tile = pair.mTile;
|
||||
final QSTile.State state = tile.getState().copy();
|
||||
// Ignore the current state and get the generic label instead.
|
||||
state.label = tile.getTileLabel();
|
||||
tile.destroy();
|
||||
addTile(tile.getTileSpec(), null, state, true);
|
||||
}
|
||||
finished();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShowDetail(boolean show) {}
|
||||
|
||||
@Override
|
||||
public void onToggleStateChanged(boolean state) {}
|
||||
|
||||
@Override
|
||||
public void onScanStateChanged(boolean state) {}
|
||||
|
||||
@Override
|
||||
public void onAnnouncementRequested(CharSequence announcement) {}
|
||||
}
|
||||
|
||||
private void addPackageTiles(final QSTileHost host) {
|
||||
|
||||
@@ -90,6 +90,10 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
|
||||
private static final long DEFAULT_STALE_TIMEOUT = 10 * DateUtils.MINUTE_IN_MILLIS;
|
||||
protected static final Object ARG_SHOW_TRANSIENT_ENABLING = new Object();
|
||||
|
||||
private static final int READY_STATE_NOT_READY = 0;
|
||||
private static final int READY_STATE_READYING = 1;
|
||||
private static final int READY_STATE_READY = 2;
|
||||
|
||||
protected final QSHost mHost;
|
||||
protected final Context mContext;
|
||||
// @NonFinalForTesting
|
||||
@@ -101,6 +105,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
|
||||
protected final ActivityStarter mActivityStarter;
|
||||
private final UiEventLogger mUiEventLogger;
|
||||
private final QSLogger mQSLogger;
|
||||
private volatile int mReadyState;
|
||||
|
||||
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
|
||||
private final Object mStaleListener = new Object();
|
||||
@@ -386,7 +391,11 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
|
||||
|
||||
protected void handleRefreshState(Object arg) {
|
||||
handleUpdateState(mTmpState, arg);
|
||||
final boolean changed = mTmpState.copyTo(mState);
|
||||
boolean changed = mTmpState.copyTo(mState);
|
||||
if (mReadyState == READY_STATE_READYING) {
|
||||
mReadyState = READY_STATE_READY;
|
||||
changed = true;
|
||||
}
|
||||
if (changed) {
|
||||
mQSLogger.logTileUpdated(mTileSpec, mState);
|
||||
handleStateChanged();
|
||||
@@ -459,6 +468,9 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
|
||||
// should not refresh it anymore.
|
||||
if (mLifecycle.getCurrentState().equals(DESTROYED)) return;
|
||||
mLifecycle.setCurrentState(RESUMED);
|
||||
if (mReadyState == READY_STATE_NOT_READY) {
|
||||
mReadyState = READY_STATE_READYING;
|
||||
}
|
||||
refreshState(); // Ensure we get at least one refresh after listening.
|
||||
});
|
||||
}
|
||||
@@ -531,6 +543,15 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
|
||||
*/
|
||||
public abstract CharSequence getTileLabel();
|
||||
|
||||
/**
|
||||
* @return {@code true} if the tile has refreshed state at least once after having set its
|
||||
* lifecycle to {@link Lifecycle.State#RESUMED}.
|
||||
*/
|
||||
@Override
|
||||
public boolean isTileReady() {
|
||||
return mReadyState == READY_STATE_READY;
|
||||
}
|
||||
|
||||
public static int getColorForState(Context context, int state) {
|
||||
switch (state) {
|
||||
case Tile.STATE_UNAVAILABLE:
|
||||
|
||||
@@ -34,6 +34,7 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
@@ -47,8 +48,11 @@ import android.util.ArraySet;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.internal.logging.InstanceId;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.plugins.qs.DetailAdapter;
|
||||
import com.android.systemui.plugins.qs.QSIconView;
|
||||
import com.android.systemui.plugins.qs.QSTile;
|
||||
import com.android.systemui.qs.QSTileHost;
|
||||
import com.android.systemui.util.concurrency.FakeExecutor;
|
||||
@@ -68,6 +72,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@@ -116,11 +121,9 @@ public class TileQueryHelperTest extends SysuiTestCase {
|
||||
doAnswer(invocation -> {
|
||||
String spec = (String) invocation.getArguments()[0];
|
||||
if (FACTORY_TILES.contains(spec)) {
|
||||
QSTile m = mock(QSTile.class);
|
||||
when(m.isAvailable()).thenReturn(true);
|
||||
when(m.getTileSpec()).thenReturn(spec);
|
||||
when(m.getState()).thenReturn(mState);
|
||||
return m;
|
||||
FakeQSTile tile = new FakeQSTile(mBgExecutor, mMainExecutor);
|
||||
tile.setState(mState);
|
||||
return tile;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@@ -292,4 +295,132 @@ public class TileQueryHelperTest extends SysuiTestCase {
|
||||
verifier.verify(t).setTileSpec("hotspot");
|
||||
verifier.verify(t).destroy();
|
||||
}
|
||||
|
||||
private static class FakeQSTile implements QSTile {
|
||||
|
||||
private String mSpec = "";
|
||||
private List<Callback> mCallbacks = new ArrayList<>();
|
||||
private boolean mRefreshed;
|
||||
private boolean mListening;
|
||||
private State mState = new State();
|
||||
private final Executor mBgExecutor;
|
||||
private final Executor mMainExecutor;
|
||||
|
||||
FakeQSTile(Executor bgExecutor, Executor mainExecutor) {
|
||||
mBgExecutor = bgExecutor;
|
||||
mMainExecutor = mainExecutor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTileSpec() {
|
||||
return mSpec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTileSpec(String tileSpec) {
|
||||
mSpec = tileSpec;
|
||||
}
|
||||
|
||||
public void setState(State state) {
|
||||
mState = state;
|
||||
notifyChangedState(mState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshState() {
|
||||
mBgExecutor.execute(() -> {
|
||||
mRefreshed = true;
|
||||
notifyChangedState(mState);
|
||||
});
|
||||
}
|
||||
|
||||
private void notifyChangedState(State state) {
|
||||
List<Callback> callbacks = new ArrayList<>(mCallbacks);
|
||||
callbacks.forEach(callback -> callback.onStateChanged(state));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCallback(Callback callback) {
|
||||
mCallbacks.add(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCallback(Callback callback) {
|
||||
mCallbacks.remove(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCallbacks() {
|
||||
mCallbacks.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setListening(Object client, boolean listening) {
|
||||
if (listening) {
|
||||
mMainExecutor.execute(() -> {
|
||||
mListening = true;
|
||||
refreshState();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getTileLabel() {
|
||||
return mSpec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public State getState() {
|
||||
return mState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTileReady() {
|
||||
return mListening && mRefreshed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QSIconView createTileView(Context context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void click() {}
|
||||
|
||||
@Override
|
||||
public void secondaryClick() {}
|
||||
|
||||
@Override
|
||||
public void longClick() {}
|
||||
|
||||
@Override
|
||||
public void userSwitch(int currentUser) {}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstanceId getInstanceId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDetailListening(boolean show) {}
|
||||
|
||||
@Override
|
||||
public void destroy() {}
|
||||
|
||||
|
||||
@Override
|
||||
public DetailAdapter getDetailAdapter() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user