Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 213dfa6c authored by Fabian Kozynski's avatar Fabian Kozynski Committed by Android (Google) Code Review
Browse files

Merge "Wait for state refreshed in TileQueryHelper"

parents 0f861baf 59e7a219
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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;
+74 −13
Original line number Diff line number Diff line
@@ -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(() -> {
        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);
                }
            notifyTilesChanged(false);
        });
                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) {
+22 −1
Original line number Diff line number Diff line
@@ -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:
+136 −5
Original line number Diff line number Diff line
@@ -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;
        }
    }
}