Fix race condition in QSTileHost
QSTileHost had three problems that caused a race condition:
* Calls to add/remove tiles could come and be executed in any thread.
* Different paths to add/remove tiles used different ground truths on
what tiles were currently active.
* The list of tiles contained in mTileSpecs was not necessarily the
same as the visible tiles.
This caused the following problem:
* If two tiles (usually CustomTile) were removed roughly at the same
time, only one of them would actually be removed from the Setting, as
both of them would race to remove from the same list. This caused...
* There would be leftover tiles that cannot connect to a TileService
and therefore show empty UI.
The solution is as follows:
* Make sure that all operations that modify the tiles are performed in
the main thread.
* Make sure that all operations that modify the tiles use the same
truth for the current state.
* Make sure that mTileSpecs always reflects the setting at the end of
onTuningChanged. This means that tiles that are destroyed or removed
will never be in this list.
Test: atest com.android.systemui.qs
Test: manual, start Guest user and notice that there are no blank tiles.
Fixes: 235561921
Change-Id: I6ffbb7510e6854f18bc427d1ae1e782918c799dc
Merged-In: I6ffbb7510e6854f18bc427d1ae1e782918c799dc
(cherry picked from commit 7f2bb15f4e)
This commit is contained in:
@@ -19,8 +19,6 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Looper;
|
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
import android.provider.Settings.Secure;
|
import android.provider.Settings.Secure;
|
||||||
@@ -28,6 +26,7 @@ import android.text.TextUtils;
|
|||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.MainThread;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.internal.logging.InstanceId;
|
import com.android.internal.logging.InstanceId;
|
||||||
@@ -35,9 +34,7 @@ import com.android.internal.logging.InstanceIdSequence;
|
|||||||
import com.android.internal.logging.UiEventLogger;
|
import com.android.internal.logging.UiEventLogger;
|
||||||
import com.android.systemui.Dumpable;
|
import com.android.systemui.Dumpable;
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
|
||||||
import com.android.systemui.dagger.SysUISingleton;
|
import com.android.systemui.dagger.SysUISingleton;
|
||||||
import com.android.systemui.dagger.qualifiers.Background;
|
|
||||||
import com.android.systemui.dagger.qualifiers.Main;
|
import com.android.systemui.dagger.qualifiers.Main;
|
||||||
import com.android.systemui.dump.DumpManager;
|
import com.android.systemui.dump.DumpManager;
|
||||||
import com.android.systemui.plugins.PluginListener;
|
import com.android.systemui.plugins.PluginListener;
|
||||||
@@ -68,12 +65,20 @@ import java.util.LinkedHashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
|
||||||
/** Platform implementation of the quick settings tile host **/
|
/** Platform implementation of the quick settings tile host
|
||||||
|
*
|
||||||
|
* This class keeps track of the set of current tiles and is the in memory source of truth
|
||||||
|
* (ground truth is kept in {@link Secure#QS_TILES}). When the ground truth changes,
|
||||||
|
* {@link #onTuningChanged} will be called and the tiles will be re-created as needed.
|
||||||
|
*
|
||||||
|
* This class also provides the interface for adding/removing/changing tiles.
|
||||||
|
*/
|
||||||
@SysUISingleton
|
@SysUISingleton
|
||||||
public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, Dumpable {
|
public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, Dumpable {
|
||||||
private static final String TAG = "QSTileHost";
|
private static final String TAG = "QSTileHost";
|
||||||
@@ -89,11 +94,11 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
|
|||||||
private final TunerService mTunerService;
|
private final TunerService mTunerService;
|
||||||
private final PluginManager mPluginManager;
|
private final PluginManager mPluginManager;
|
||||||
private final DumpManager mDumpManager;
|
private final DumpManager mDumpManager;
|
||||||
private final BroadcastDispatcher mBroadcastDispatcher;
|
|
||||||
private final QSLogger mQSLogger;
|
private final QSLogger mQSLogger;
|
||||||
private final UiEventLogger mUiEventLogger;
|
private final UiEventLogger mUiEventLogger;
|
||||||
private final InstanceIdSequence mInstanceIdSequence;
|
private final InstanceIdSequence mInstanceIdSequence;
|
||||||
private final CustomTileStatePersister mCustomTileStatePersister;
|
private final CustomTileStatePersister mCustomTileStatePersister;
|
||||||
|
private final Executor mMainExecutor;
|
||||||
|
|
||||||
private final List<Callback> mCallbacks = new ArrayList<>();
|
private final List<Callback> mCallbacks = new ArrayList<>();
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -113,13 +118,11 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
|
|||||||
public QSTileHost(Context context,
|
public QSTileHost(Context context,
|
||||||
StatusBarIconController iconController,
|
StatusBarIconController iconController,
|
||||||
QSFactory defaultFactory,
|
QSFactory defaultFactory,
|
||||||
@Main Handler mainHandler,
|
@Main Executor mainExecutor,
|
||||||
@Background Looper bgLooper,
|
|
||||||
PluginManager pluginManager,
|
PluginManager pluginManager,
|
||||||
TunerService tunerService,
|
TunerService tunerService,
|
||||||
Provider<AutoTileManager> autoTiles,
|
Provider<AutoTileManager> autoTiles,
|
||||||
DumpManager dumpManager,
|
DumpManager dumpManager,
|
||||||
BroadcastDispatcher broadcastDispatcher,
|
|
||||||
Optional<CentralSurfaces> centralSurfacesOptional,
|
Optional<CentralSurfaces> centralSurfacesOptional,
|
||||||
QSLogger qsLogger,
|
QSLogger qsLogger,
|
||||||
UiEventLogger uiEventLogger,
|
UiEventLogger uiEventLogger,
|
||||||
@@ -137,7 +140,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
|
|||||||
mDumpManager = dumpManager;
|
mDumpManager = dumpManager;
|
||||||
mQSLogger = qsLogger;
|
mQSLogger = qsLogger;
|
||||||
mUiEventLogger = uiEventLogger;
|
mUiEventLogger = uiEventLogger;
|
||||||
mBroadcastDispatcher = broadcastDispatcher;
|
mMainExecutor = mainExecutor;
|
||||||
mTileServiceRequestController = tileServiceRequestControllerBuilder.create(this);
|
mTileServiceRequestController = tileServiceRequestControllerBuilder.create(this);
|
||||||
mTileLifeCycleManagerFactory = tileLifecycleManagerFactory;
|
mTileLifeCycleManagerFactory = tileLifecycleManagerFactory;
|
||||||
|
|
||||||
@@ -151,7 +154,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
|
|||||||
mSecureSettings = secureSettings;
|
mSecureSettings = secureSettings;
|
||||||
mCustomTileStatePersister = customTileStatePersister;
|
mCustomTileStatePersister = customTileStatePersister;
|
||||||
|
|
||||||
mainHandler.post(() -> {
|
mainExecutor.execute(() -> {
|
||||||
// This is technically a hack to avoid circular dependency of
|
// This is technically a hack to avoid circular dependency of
|
||||||
// QSTileHost -> XXXTile -> QSTileHost. Posting ensures creation
|
// QSTileHost -> XXXTile -> QSTileHost. Posting ensures creation
|
||||||
// finishes before creating any tiles.
|
// finishes before creating any tiles.
|
||||||
@@ -258,6 +261,33 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
|
|||||||
return mTileSpecs.indexOf(spec);
|
return mTileSpecs.indexOf(spec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whenever the Secure Setting keeping track of the current tiles changes (or upon start) this
|
||||||
|
* will be called with the new value of the setting.
|
||||||
|
*
|
||||||
|
* This method will do the following:
|
||||||
|
* <ol>
|
||||||
|
* <li>Destroy any existing tile that's not one of the current tiles (in the setting)</li>
|
||||||
|
* <li>Create new tiles for those that don't already exist. If this tiles end up being
|
||||||
|
* not available, they'll also be destroyed.</li>
|
||||||
|
* <li>Save the resolved list of tiles (current tiles that are available) into the setting.
|
||||||
|
* This means that after this call ends, the tiles in the Setting, {@link #mTileSpecs},
|
||||||
|
* and visible tiles ({@link #mTiles}) must match.
|
||||||
|
* </li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* Additionally, if the user has changed, it'll do the following:
|
||||||
|
* <ul>
|
||||||
|
* <li>Change the user for SystemUI tiles: {@link QSTile#userSwitch}.</li>
|
||||||
|
* <li>Destroy any {@link CustomTile} and recreate it for the new user.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* This happens in main thread as {@link com.android.systemui.tuner.TunerServiceImpl} dispatches
|
||||||
|
* in main thread.
|
||||||
|
*
|
||||||
|
* @see QSTile#isAvailable
|
||||||
|
*/
|
||||||
|
@MainThread
|
||||||
@Override
|
@Override
|
||||||
public void onTuningChanged(String key, String newValue) {
|
public void onTuningChanged(String key, String newValue) {
|
||||||
if (!TILES_SETTING.equals(key)) {
|
if (!TILES_SETTING.equals(key)) {
|
||||||
@@ -330,34 +360,44 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
|
|||||||
mCurrentUser = currentUser;
|
mCurrentUser = currentUser;
|
||||||
List<String> currentSpecs = new ArrayList<>(mTileSpecs);
|
List<String> currentSpecs = new ArrayList<>(mTileSpecs);
|
||||||
mTileSpecs.clear();
|
mTileSpecs.clear();
|
||||||
mTileSpecs.addAll(tileSpecs);
|
mTileSpecs.addAll(newTiles.keySet()); // Only add the valid (available) tiles.
|
||||||
mTiles.clear();
|
mTiles.clear();
|
||||||
mTiles.putAll(newTiles);
|
mTiles.putAll(newTiles);
|
||||||
if (newTiles.isEmpty() && !tileSpecs.isEmpty()) {
|
if (newTiles.isEmpty() && !tileSpecs.isEmpty()) {
|
||||||
// If we didn't manage to create any tiles, set it to empty (default)
|
// If we didn't manage to create any tiles, set it to empty (default)
|
||||||
Log.d(TAG, "No valid tiles on tuning changed. Setting to default.");
|
Log.d(TAG, "No valid tiles on tuning changed. Setting to default.");
|
||||||
changeTiles(currentSpecs, loadTileSpecs(mContext, ""));
|
changeTilesByUser(currentSpecs, loadTileSpecs(mContext, ""));
|
||||||
} else {
|
} else {
|
||||||
|
String resolvedTiles = TextUtils.join(",", mTileSpecs);
|
||||||
|
if (!resolvedTiles.equals(newValue)) {
|
||||||
|
// If the resolved tiles (those we actually ended up with) are different than
|
||||||
|
// the ones that are in the setting, update the Setting.
|
||||||
|
saveTilesToSettings(mTileSpecs);
|
||||||
|
}
|
||||||
for (int i = 0; i < mCallbacks.size(); i++) {
|
for (int i = 0; i < mCallbacks.size(); i++) {
|
||||||
mCallbacks.get(i).onTilesChanged();
|
mCallbacks.get(i).onTilesChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only use with [CustomTile] if the tile doesn't exist anymore (and therefore doesn't need
|
||||||
|
* its lifecycle terminated).
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void removeTile(String spec) {
|
public void removeTile(String spec) {
|
||||||
changeTileSpecs(tileSpecs-> tileSpecs.remove(spec));
|
mMainExecutor.execute(() -> changeTileSpecs(tileSpecs-> tileSpecs.remove(spec)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove many tiles at once.
|
* Remove many tiles at once.
|
||||||
*
|
*
|
||||||
* It will only save to settings once (as opposed to {@link QSTileHost#removeTile} called
|
* It will only save to settings once (as opposed to {@link QSTileHost#removeTileByUser} called
|
||||||
* multiple times).
|
* multiple times).
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void removeTiles(Collection<String> specs) {
|
public void removeTiles(Collection<String> specs) {
|
||||||
changeTileSpecs(tileSpecs -> tileSpecs.removeAll(specs));
|
mMainExecutor.execute(() -> changeTileSpecs(tileSpecs -> tileSpecs.removeAll(specs)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -381,27 +421,30 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
|
|||||||
* @param requestPosition -1 for end, 0 for beginning, or X for insertion at position X
|
* @param requestPosition -1 for end, 0 for beginning, or X for insertion at position X
|
||||||
*/
|
*/
|
||||||
public void addTile(String spec, int requestPosition) {
|
public void addTile(String spec, int requestPosition) {
|
||||||
if (spec.equals("work")) Log.wtfStack(TAG, "Adding work tile");
|
mMainExecutor.execute(() ->
|
||||||
changeTileSpecs(tileSpecs -> {
|
changeTileSpecs(tileSpecs -> {
|
||||||
if (tileSpecs.contains(spec)) return false;
|
if (tileSpecs.contains(spec)) return false;
|
||||||
|
|
||||||
int size = tileSpecs.size();
|
int size = tileSpecs.size();
|
||||||
if (requestPosition == POSITION_AT_END || requestPosition >= size) {
|
if (requestPosition == POSITION_AT_END || requestPosition >= size) {
|
||||||
tileSpecs.add(spec);
|
tileSpecs.add(spec);
|
||||||
} else {
|
} else {
|
||||||
tileSpecs.add(requestPosition, spec);
|
tileSpecs.add(requestPosition, spec);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void saveTilesToSettings(List<String> tileSpecs) {
|
|
||||||
if (tileSpecs.contains("work")) Log.wtfStack(TAG, "Saving work tile");
|
@MainThread
|
||||||
|
private void saveTilesToSettings(List<String> tileSpecs) {
|
||||||
mSecureSettings.putStringForUser(TILES_SETTING, TextUtils.join(",", tileSpecs),
|
mSecureSettings.putStringForUser(TILES_SETTING, TextUtils.join(",", tileSpecs),
|
||||||
null /* tag */, false /* default */, mCurrentUser,
|
null /* tag */, false /* default */, mCurrentUser,
|
||||||
true /* overrideable by restore */);
|
true /* overrideable by restore */);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainThread
|
||||||
private void changeTileSpecs(Predicate<List<String>> changeFunction) {
|
private void changeTileSpecs(Predicate<List<String>> changeFunction) {
|
||||||
final String setting = mSecureSettings.getStringForUser(TILES_SETTING, mCurrentUser);
|
final String setting = mSecureSettings.getStringForUser(TILES_SETTING, mCurrentUser);
|
||||||
final List<String> tileSpecs = loadTileSpecs(mContext, setting);
|
final List<String> tileSpecs = loadTileSpecs(mContext, setting);
|
||||||
@@ -421,29 +464,32 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
|
|||||||
*/
|
*/
|
||||||
public void addTile(ComponentName tile, boolean end) {
|
public void addTile(ComponentName tile, boolean end) {
|
||||||
String spec = CustomTile.toSpec(tile);
|
String spec = CustomTile.toSpec(tile);
|
||||||
if (!mTileSpecs.contains(spec)) {
|
addTile(spec, end ? POSITION_AT_END : 0);
|
||||||
List<String> newSpecs = new ArrayList<>(mTileSpecs);
|
|
||||||
if (end) {
|
|
||||||
newSpecs.add(spec);
|
|
||||||
} else {
|
|
||||||
newSpecs.add(0, spec);
|
|
||||||
}
|
|
||||||
changeTiles(mTileSpecs, newSpecs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeTile(ComponentName tile) {
|
/**
|
||||||
List<String> newSpecs = new ArrayList<>(mTileSpecs);
|
* This will call through {@link #changeTilesByUser}. It should only be used when a tile is
|
||||||
newSpecs.remove(CustomTile.toSpec(tile));
|
* removed by a <b>user action</b> like {@code adb}.
|
||||||
changeTiles(mTileSpecs, newSpecs);
|
*/
|
||||||
|
public void removeTileByUser(ComponentName tile) {
|
||||||
|
mMainExecutor.execute(() -> {
|
||||||
|
List<String> newSpecs = new ArrayList<>(mTileSpecs);
|
||||||
|
if (newSpecs.remove(CustomTile.toSpec(tile))) {
|
||||||
|
changeTilesByUser(mTileSpecs, newSpecs);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change the tiles triggered by the user editing.
|
* Change the tiles triggered by the user editing.
|
||||||
* <p>
|
* <p>
|
||||||
* This is not called on device start, or on user change.
|
* This is not called on device start, or on user change.
|
||||||
|
*
|
||||||
|
* {@link android.service.quicksettings.TileService#onTileRemoved} will be called for tiles
|
||||||
|
* that are removed.
|
||||||
*/
|
*/
|
||||||
public void changeTiles(List<String> previousTiles, List<String> newTiles) {
|
@MainThread
|
||||||
|
public void changeTilesByUser(List<String> previousTiles, List<String> newTiles) {
|
||||||
final List<String> copy = new ArrayList<>(previousTiles);
|
final List<String> copy = new ArrayList<>(previousTiles);
|
||||||
final int NP = copy.size();
|
final int NP = copy.size();
|
||||||
for (int i = 0; i < NP; i++) {
|
for (int i = 0; i < NP; i++) {
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
|
|||||||
for (int i = 1; i < mTiles.size() && mTiles.get(i) != null; i++) {
|
for (int i = 1; i < mTiles.size() && mTiles.get(i) != null; i++) {
|
||||||
newSpecs.add(mTiles.get(i).spec);
|
newSpecs.add(mTiles.get(i).spec);
|
||||||
}
|
}
|
||||||
host.changeTiles(mCurrentSpecs, newSpecs);
|
host.changeTilesByUser(mCurrentSpecs, newSpecs);
|
||||||
mCurrentSpecs = newSpecs;
|
mCurrentSpecs = newSpecs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,7 +200,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
|
|||||||
/** */
|
/** */
|
||||||
public void resetTileSpecs(List<String> specs) {
|
public void resetTileSpecs(List<String> specs) {
|
||||||
// Notify the host so the tiles get removed callbacks.
|
// Notify the host so the tiles get removed callbacks.
|
||||||
mHost.changeTiles(mCurrentSpecs, specs);
|
mHost.changeTilesByUser(mCurrentSpecs, specs);
|
||||||
setTileSpecs(specs);
|
setTileSpecs(specs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -289,7 +289,7 @@ public class TileServiceManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mServices.getHost().removeTile(component);
|
mServices.getHost().removeTile(CustomTile.toSpec(component));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -428,7 +428,7 @@ public class AutoTileManager implements UserAwareController {
|
|||||||
if (isSafetyCenterEnabled && !mAutoTracker.isAdded(mSafetySpec)) {
|
if (isSafetyCenterEnabled && !mAutoTracker.isAdded(mSafetySpec)) {
|
||||||
initSafetyTile();
|
initSafetyTile();
|
||||||
} else if (!isSafetyCenterEnabled && mAutoTracker.isAdded(mSafetySpec)) {
|
} else if (!isSafetyCenterEnabled && mAutoTracker.isAdded(mSafetySpec)) {
|
||||||
mHost.removeTile(CustomTile.getComponentFromSpec(mSafetySpec));
|
mHost.removeTile(mSafetySpec);
|
||||||
mHost.unmarkTileAsAutoAdded(mSafetySpec);
|
mHost.unmarkTileAsAutoAdded(mSafetySpec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
|
|||||||
public void remQsTile(ComponentName tile) {
|
public void remQsTile(ComponentName tile) {
|
||||||
QSPanelController qsPanelController = mCentralSurfaces.getQSPanelController();
|
QSPanelController qsPanelController = mCentralSurfaces.getQSPanelController();
|
||||||
if (qsPanelController != null && qsPanelController.getHost() != null) {
|
if (qsPanelController != null && qsPanelController.getHost() != null) {
|
||||||
qsPanelController.getHost().removeTile(tile);
|
qsPanelController.getHost().removeTileByUser(tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,8 +32,6 @@ import android.app.Fragment;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Looper;
|
|
||||||
import android.testing.AndroidTestingRunner;
|
import android.testing.AndroidTestingRunner;
|
||||||
import android.testing.TestableLooper.RunWithLooper;
|
import android.testing.TestableLooper.RunWithLooper;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@@ -42,36 +40,23 @@ import android.view.ViewGroup;
|
|||||||
|
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
import com.android.internal.logging.UiEventLogger;
|
|
||||||
import com.android.keyguard.BouncerPanelExpansionCalculator;
|
import com.android.keyguard.BouncerPanelExpansionCalculator;
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.SysuiBaseFragmentTest;
|
import com.android.systemui.SysuiBaseFragmentTest;
|
||||||
import com.android.systemui.animation.ShadeInterpolation;
|
import com.android.systemui.animation.ShadeInterpolation;
|
||||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
|
||||||
import com.android.systemui.dump.DumpManager;
|
import com.android.systemui.dump.DumpManager;
|
||||||
import com.android.systemui.media.MediaHost;
|
import com.android.systemui.media.MediaHost;
|
||||||
import com.android.systemui.plugins.FalsingManager;
|
import com.android.systemui.plugins.FalsingManager;
|
||||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||||
import com.android.systemui.qs.customize.QSCustomizerController;
|
import com.android.systemui.qs.customize.QSCustomizerController;
|
||||||
import com.android.systemui.qs.dagger.QSFragmentComponent;
|
import com.android.systemui.qs.dagger.QSFragmentComponent;
|
||||||
import com.android.systemui.qs.external.CustomTileStatePersister;
|
|
||||||
import com.android.systemui.qs.external.TileLifecycleManager;
|
|
||||||
import com.android.systemui.qs.external.TileServiceRequestController;
|
import com.android.systemui.qs.external.TileServiceRequestController;
|
||||||
import com.android.systemui.qs.logging.QSLogger;
|
|
||||||
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
|
|
||||||
import com.android.systemui.settings.UserTracker;
|
|
||||||
import com.android.systemui.shared.plugins.PluginManager;
|
|
||||||
import com.android.systemui.statusbar.CommandQueue;
|
import com.android.systemui.statusbar.CommandQueue;
|
||||||
import com.android.systemui.statusbar.StatusBarState;
|
import com.android.systemui.statusbar.StatusBarState;
|
||||||
import com.android.systemui.statusbar.phone.AutoTileManager;
|
|
||||||
import com.android.systemui.statusbar.phone.CentralSurfaces;
|
|
||||||
import com.android.systemui.statusbar.phone.KeyguardBypassController;
|
import com.android.systemui.statusbar.phone.KeyguardBypassController;
|
||||||
import com.android.systemui.statusbar.phone.StatusBarIconController;
|
|
||||||
import com.android.systemui.statusbar.policy.ConfigurationController;
|
import com.android.systemui.statusbar.policy.ConfigurationController;
|
||||||
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
|
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
|
||||||
import com.android.systemui.tuner.TunerService;
|
|
||||||
import com.android.systemui.util.animation.UniqueObjectHostView;
|
import com.android.systemui.util.animation.UniqueObjectHostView;
|
||||||
import com.android.systemui.util.settings.SecureSettings;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -79,8 +64,6 @@ import org.junit.runner.RunWith;
|
|||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
@RunWith(AndroidTestingRunner.class)
|
@RunWith(AndroidTestingRunner.class)
|
||||||
@RunWithLooper(setAsMainLooper = true)
|
@RunWithLooper(setAsMainLooper = true)
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@@ -125,34 +108,11 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
|
|||||||
mFragments.dispatchResume();
|
mFragments.dispatchResume();
|
||||||
processAllMessages();
|
processAllMessages();
|
||||||
|
|
||||||
QSTileHost host =
|
|
||||||
new QSTileHost(
|
|
||||||
mContext,
|
|
||||||
mock(StatusBarIconController.class),
|
|
||||||
mock(QSFactoryImpl.class),
|
|
||||||
new Handler(),
|
|
||||||
Looper.myLooper(),
|
|
||||||
mock(PluginManager.class),
|
|
||||||
mock(TunerService.class),
|
|
||||||
() -> mock(AutoTileManager.class),
|
|
||||||
mock(DumpManager.class),
|
|
||||||
mock(BroadcastDispatcher.class),
|
|
||||||
Optional.of(mock(CentralSurfaces.class)),
|
|
||||||
mock(QSLogger.class),
|
|
||||||
mock(UiEventLogger.class),
|
|
||||||
mock(UserTracker.class),
|
|
||||||
mock(SecureSettings.class),
|
|
||||||
mock(CustomTileStatePersister.class),
|
|
||||||
mTileServiceRequestControllerBuilder,
|
|
||||||
mock(TileLifecycleManager.Factory.class));
|
|
||||||
|
|
||||||
qs.setListening(true);
|
qs.setListening(true);
|
||||||
processAllMessages();
|
processAllMessages();
|
||||||
|
|
||||||
qs.setListening(false);
|
qs.setListening(false);
|
||||||
processAllMessages();
|
processAllMessages();
|
||||||
host.destroy();
|
|
||||||
processAllMessages();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -32,12 +32,11 @@ import static org.mockito.Mockito.when;
|
|||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.database.ContentObserver;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.testing.AndroidTestingRunner;
|
import android.testing.AndroidTestingRunner;
|
||||||
import android.testing.TestableLooper;
|
|
||||||
import android.testing.TestableLooper.RunWithLooper;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@@ -48,7 +47,6 @@ import com.android.internal.logging.UiEventLogger;
|
|||||||
import com.android.internal.util.CollectionUtils;
|
import com.android.internal.util.CollectionUtils;
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.SysuiTestCase;
|
import com.android.systemui.SysuiTestCase;
|
||||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
|
||||||
import com.android.systemui.classifier.FalsingManagerFake;
|
import com.android.systemui.classifier.FalsingManagerFake;
|
||||||
import com.android.systemui.dump.DumpManager;
|
import com.android.systemui.dump.DumpManager;
|
||||||
import com.android.systemui.plugins.ActivityStarter;
|
import com.android.systemui.plugins.ActivityStarter;
|
||||||
@@ -68,8 +66,10 @@ import com.android.systemui.statusbar.phone.AutoTileManager;
|
|||||||
import com.android.systemui.statusbar.phone.CentralSurfaces;
|
import com.android.systemui.statusbar.phone.CentralSurfaces;
|
||||||
import com.android.systemui.statusbar.phone.StatusBarIconController;
|
import com.android.systemui.statusbar.phone.StatusBarIconController;
|
||||||
import com.android.systemui.tuner.TunerService;
|
import com.android.systemui.tuner.TunerService;
|
||||||
|
import com.android.systemui.util.concurrency.FakeExecutor;
|
||||||
import com.android.systemui.util.settings.FakeSettings;
|
import com.android.systemui.util.settings.FakeSettings;
|
||||||
import com.android.systemui.util.settings.SecureSettings;
|
import com.android.systemui.util.settings.SecureSettings;
|
||||||
|
import com.android.systemui.util.time.FakeSystemClock;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -81,18 +81,19 @@ import java.io.PrintWriter;
|
|||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
|
||||||
@RunWith(AndroidTestingRunner.class)
|
@RunWith(AndroidTestingRunner.class)
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@RunWithLooper(setAsMainLooper = true)
|
|
||||||
public class QSTileHostTest extends SysuiTestCase {
|
public class QSTileHostTest extends SysuiTestCase {
|
||||||
|
|
||||||
private static String MOCK_STATE_STRING = "MockState";
|
private static String MOCK_STATE_STRING = "MockState";
|
||||||
private static ComponentName CUSTOM_TILE =
|
private static ComponentName CUSTOM_TILE =
|
||||||
ComponentName.unflattenFromString("TEST_PKG/.TEST_CLS");
|
ComponentName.unflattenFromString("TEST_PKG/.TEST_CLS");
|
||||||
private static final String CUSTOM_TILE_SPEC = CustomTile.toSpec(CUSTOM_TILE);
|
private static final String CUSTOM_TILE_SPEC = CustomTile.toSpec(CUSTOM_TILE);
|
||||||
|
private static final String SETTING = QSTileHost.TILES_SETTING;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private StatusBarIconController mIconController;
|
private StatusBarIconController mIconController;
|
||||||
@@ -107,8 +108,6 @@ public class QSTileHostTest extends SysuiTestCase {
|
|||||||
@Mock
|
@Mock
|
||||||
private DumpManager mDumpManager;
|
private DumpManager mDumpManager;
|
||||||
@Mock
|
@Mock
|
||||||
private BroadcastDispatcher mBroadcastDispatcher;
|
|
||||||
@Mock
|
|
||||||
private QSTile.State mMockState;
|
private QSTile.State mMockState;
|
||||||
@Mock
|
@Mock
|
||||||
private CentralSurfaces mCentralSurfaces;
|
private CentralSurfaces mCentralSurfaces;
|
||||||
@@ -132,31 +131,47 @@ public class QSTileHostTest extends SysuiTestCase {
|
|||||||
@Mock
|
@Mock
|
||||||
private TileLifecycleManager mTileLifecycleManager;
|
private TileLifecycleManager mTileLifecycleManager;
|
||||||
|
|
||||||
private Handler mHandler;
|
private FakeExecutor mMainExecutor;
|
||||||
private TestableLooper mLooper;
|
|
||||||
private QSTileHost mQSTileHost;
|
private QSTileHost mQSTileHost;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
mLooper = TestableLooper.get(this);
|
mMainExecutor = new FakeExecutor(new FakeSystemClock());
|
||||||
mHandler = new Handler(mLooper.getLooper());
|
|
||||||
when(mTileServiceRequestControllerBuilder.create(any()))
|
when(mTileServiceRequestControllerBuilder.create(any()))
|
||||||
.thenReturn(mTileServiceRequestController);
|
.thenReturn(mTileServiceRequestController);
|
||||||
when(mTileLifecycleManagerFactory.create(any(Intent.class), any(UserHandle.class)))
|
when(mTileLifecycleManagerFactory.create(any(Intent.class), any(UserHandle.class)))
|
||||||
.thenReturn(mTileLifecycleManager);
|
.thenReturn(mTileLifecycleManager);
|
||||||
|
|
||||||
mSecureSettings = new FakeSettings();
|
mSecureSettings = new FakeSettings();
|
||||||
mSecureSettings.putStringForUser(
|
saveSetting("");
|
||||||
QSTileHost.TILES_SETTING, "", "", false, mUserTracker.getUserId(), false);
|
mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mMainExecutor,
|
||||||
mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler,
|
mPluginManager, mTunerService, mAutoTiles, mDumpManager, mCentralSurfaces,
|
||||||
mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager,
|
mQSLogger, mUiEventLogger, mUserTracker, mSecureSettings, mCustomTileStatePersister,
|
||||||
mBroadcastDispatcher, mCentralSurfaces, mQSLogger, mUiEventLogger, mUserTracker,
|
mTileServiceRequestControllerBuilder, mTileLifecycleManagerFactory);
|
||||||
mSecureSettings, mCustomTileStatePersister, mTileServiceRequestControllerBuilder,
|
|
||||||
mTileLifecycleManagerFactory);
|
mSecureSettings.registerContentObserverForUser(SETTING, new ContentObserver(null) {
|
||||||
|
@Override
|
||||||
|
public void onChange(boolean selfChange) {
|
||||||
|
super.onChange(selfChange);
|
||||||
|
mMainExecutor.execute(() -> mQSTileHost.onTuningChanged(SETTING, getSetting()));
|
||||||
|
mMainExecutor.runAllReady();
|
||||||
|
}
|
||||||
|
}, mUserTracker.getUserId());
|
||||||
setUpTileFactory();
|
setUpTileFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void saveSetting(String value) {
|
||||||
|
mSecureSettings.putStringForUser(
|
||||||
|
SETTING, value, "", false, mUserTracker.getUserId(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSetting() {
|
||||||
|
return mSecureSettings.getStringForUser(SETTING, mUserTracker.getUserId());
|
||||||
|
}
|
||||||
|
|
||||||
private void setUpTileFactory() {
|
private void setUpTileFactory() {
|
||||||
when(mMockState.toString()).thenReturn(MOCK_STATE_STRING);
|
when(mMockState.toString()).thenReturn(MOCK_STATE_STRING);
|
||||||
// Only create this kind of tiles
|
// Only create this kind of tiles
|
||||||
@@ -173,6 +188,10 @@ public class QSTileHostTest extends SysuiTestCase {
|
|||||||
return new NotAvailableTile(mQSTileHost);
|
return new NotAvailableTile(mQSTileHost);
|
||||||
} else if (CUSTOM_TILE_SPEC.equals(spec)) {
|
} else if (CUSTOM_TILE_SPEC.equals(spec)) {
|
||||||
return mCustomTile;
|
return mCustomTile;
|
||||||
|
} else if ("internet".equals(spec)
|
||||||
|
|| "wifi".equals(spec)
|
||||||
|
|| "cell".equals(spec)) {
|
||||||
|
return new TestTile1(mQSTileHost);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -196,14 +215,14 @@ public class QSTileHostTest extends SysuiTestCase {
|
|||||||
public void testInvalidSpecUsesDefault() {
|
public void testInvalidSpecUsesDefault() {
|
||||||
mContext.getOrCreateTestableResources()
|
mContext.getOrCreateTestableResources()
|
||||||
.addOverride(R.string.quick_settings_tiles, "spec1,spec2");
|
.addOverride(R.string.quick_settings_tiles, "spec1,spec2");
|
||||||
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "not-valid");
|
saveSetting("not-valid");
|
||||||
|
|
||||||
assertEquals(2, mQSTileHost.getTiles().size());
|
assertEquals(2, mQSTileHost.getTiles().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRemoveWifiAndCellularWithoutInternet() {
|
public void testRemoveWifiAndCellularWithoutInternet() {
|
||||||
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "wifi, spec1, cell, spec2");
|
saveSetting("wifi, spec1, cell, spec2");
|
||||||
|
|
||||||
assertEquals("internet", mQSTileHost.mTileSpecs.get(0));
|
assertEquals("internet", mQSTileHost.mTileSpecs.get(0));
|
||||||
assertEquals("spec1", mQSTileHost.mTileSpecs.get(1));
|
assertEquals("spec1", mQSTileHost.mTileSpecs.get(1));
|
||||||
@@ -212,7 +231,7 @@ public class QSTileHostTest extends SysuiTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRemoveWifiAndCellularWithInternet() {
|
public void testRemoveWifiAndCellularWithInternet() {
|
||||||
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "wifi, spec1, cell, spec2, internet");
|
saveSetting("wifi, spec1, cell, spec2, internet");
|
||||||
|
|
||||||
assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
|
assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
|
||||||
assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
|
assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
|
||||||
@@ -221,7 +240,7 @@ public class QSTileHostTest extends SysuiTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRemoveWifiWithoutInternet() {
|
public void testRemoveWifiWithoutInternet() {
|
||||||
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1, wifi, spec2");
|
saveSetting("spec1, wifi, spec2");
|
||||||
|
|
||||||
assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
|
assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
|
||||||
assertEquals("internet", mQSTileHost.mTileSpecs.get(1));
|
assertEquals("internet", mQSTileHost.mTileSpecs.get(1));
|
||||||
@@ -230,7 +249,7 @@ public class QSTileHostTest extends SysuiTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRemoveCellWithInternet() {
|
public void testRemoveCellWithInternet() {
|
||||||
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1, spec2, cell, internet");
|
saveSetting("spec1, spec2, cell, internet");
|
||||||
|
|
||||||
assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
|
assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
|
||||||
assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
|
assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
|
||||||
@@ -239,7 +258,7 @@ public class QSTileHostTest extends SysuiTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNoWifiNoCellularNoInternet() {
|
public void testNoWifiNoCellularNoInternet() {
|
||||||
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec2");
|
saveSetting("spec1,spec2");
|
||||||
|
|
||||||
assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
|
assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
|
||||||
assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
|
assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
|
||||||
@@ -249,7 +268,7 @@ public class QSTileHostTest extends SysuiTestCase {
|
|||||||
public void testSpecWithInvalidDoesNotUseDefault() {
|
public void testSpecWithInvalidDoesNotUseDefault() {
|
||||||
mContext.getOrCreateTestableResources()
|
mContext.getOrCreateTestableResources()
|
||||||
.addOverride(R.string.quick_settings_tiles, "spec1,spec2");
|
.addOverride(R.string.quick_settings_tiles, "spec1,spec2");
|
||||||
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec2,not-valid");
|
saveSetting("spec2,not-valid");
|
||||||
|
|
||||||
assertEquals(1, mQSTileHost.getTiles().size());
|
assertEquals(1, mQSTileHost.getTiles().size());
|
||||||
QSTile element = CollectionUtils.firstOrNull(mQSTileHost.getTiles());
|
QSTile element = CollectionUtils.firstOrNull(mQSTileHost.getTiles());
|
||||||
@@ -258,7 +277,7 @@ public class QSTileHostTest extends SysuiTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDump() {
|
public void testDump() {
|
||||||
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec2");
|
saveSetting("spec1,spec2");
|
||||||
StringWriter w = new StringWriter();
|
StringWriter w = new StringWriter();
|
||||||
PrintWriter pw = new PrintWriter(w);
|
PrintWriter pw = new PrintWriter(w);
|
||||||
mQSTileHost.dump(pw, new String[]{});
|
mQSTileHost.dump(pw, new String[]{});
|
||||||
@@ -274,7 +293,7 @@ public class QSTileHostTest extends SysuiTestCase {
|
|||||||
public void testDefault() {
|
public void testDefault() {
|
||||||
mContext.getOrCreateTestableResources()
|
mContext.getOrCreateTestableResources()
|
||||||
.addOverride(R.string.quick_settings_tiles_default, "spec1");
|
.addOverride(R.string.quick_settings_tiles_default, "spec1");
|
||||||
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "default");
|
saveSetting("default");
|
||||||
assertEquals(1, mQSTileHost.getTiles().size());
|
assertEquals(1, mQSTileHost.getTiles().size());
|
||||||
QSTile element = CollectionUtils.firstOrNull(mQSTileHost.getTiles());
|
QSTile element = CollectionUtils.firstOrNull(mQSTileHost.getTiles());
|
||||||
assertTrue(element instanceof TestTile1);
|
assertTrue(element instanceof TestTile1);
|
||||||
@@ -285,7 +304,7 @@ public class QSTileHostTest extends SysuiTestCase {
|
|||||||
public void testNoRepeatedSpecs_addTile() {
|
public void testNoRepeatedSpecs_addTile() {
|
||||||
mContext.getOrCreateTestableResources()
|
mContext.getOrCreateTestableResources()
|
||||||
.addOverride(R.string.quick_settings_tiles, "spec1,spec2");
|
.addOverride(R.string.quick_settings_tiles, "spec1,spec2");
|
||||||
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec2");
|
saveSetting("spec1,spec2");
|
||||||
|
|
||||||
mQSTileHost.addTile("spec1");
|
mQSTileHost.addTile("spec1");
|
||||||
|
|
||||||
@@ -298,9 +317,10 @@ public class QSTileHostTest extends SysuiTestCase {
|
|||||||
public void testAddTileAtValidPosition() {
|
public void testAddTileAtValidPosition() {
|
||||||
mContext.getOrCreateTestableResources()
|
mContext.getOrCreateTestableResources()
|
||||||
.addOverride(R.string.quick_settings_tiles, "spec1,spec3");
|
.addOverride(R.string.quick_settings_tiles, "spec1,spec3");
|
||||||
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec3");
|
saveSetting("spec1,spec3");
|
||||||
|
|
||||||
mQSTileHost.addTile("spec2", 1);
|
mQSTileHost.addTile("spec2", 1);
|
||||||
|
mMainExecutor.runAllReady();
|
||||||
|
|
||||||
assertEquals(3, mQSTileHost.mTileSpecs.size());
|
assertEquals(3, mQSTileHost.mTileSpecs.size());
|
||||||
assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
|
assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
|
||||||
@@ -312,9 +332,10 @@ public class QSTileHostTest extends SysuiTestCase {
|
|||||||
public void testAddTileAtInvalidPositionAddsToEnd() {
|
public void testAddTileAtInvalidPositionAddsToEnd() {
|
||||||
mContext.getOrCreateTestableResources()
|
mContext.getOrCreateTestableResources()
|
||||||
.addOverride(R.string.quick_settings_tiles, "spec1,spec3");
|
.addOverride(R.string.quick_settings_tiles, "spec1,spec3");
|
||||||
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec3");
|
saveSetting("spec1,spec3");
|
||||||
|
|
||||||
mQSTileHost.addTile("spec2", 100);
|
mQSTileHost.addTile("spec2", 100);
|
||||||
|
mMainExecutor.runAllReady();
|
||||||
|
|
||||||
assertEquals(3, mQSTileHost.mTileSpecs.size());
|
assertEquals(3, mQSTileHost.mTileSpecs.size());
|
||||||
assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
|
assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
|
||||||
@@ -326,9 +347,10 @@ public class QSTileHostTest extends SysuiTestCase {
|
|||||||
public void testAddTileAtEnd() {
|
public void testAddTileAtEnd() {
|
||||||
mContext.getOrCreateTestableResources()
|
mContext.getOrCreateTestableResources()
|
||||||
.addOverride(R.string.quick_settings_tiles, "spec1,spec3");
|
.addOverride(R.string.quick_settings_tiles, "spec1,spec3");
|
||||||
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec3");
|
saveSetting("spec1,spec3");
|
||||||
|
|
||||||
mQSTileHost.addTile("spec2", QSTileHost.POSITION_AT_END);
|
mQSTileHost.addTile("spec2", QSTileHost.POSITION_AT_END);
|
||||||
|
mMainExecutor.runAllReady();
|
||||||
|
|
||||||
assertEquals(3, mQSTileHost.mTileSpecs.size());
|
assertEquals(3, mQSTileHost.mTileSpecs.size());
|
||||||
assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
|
assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
|
||||||
@@ -338,9 +360,10 @@ public class QSTileHostTest extends SysuiTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNoRepeatedSpecs_customTile() {
|
public void testNoRepeatedSpecs_customTile() {
|
||||||
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, CUSTOM_TILE_SPEC);
|
saveSetting(CUSTOM_TILE_SPEC);
|
||||||
|
|
||||||
mQSTileHost.addTile(CUSTOM_TILE, /* end */ false);
|
mQSTileHost.addTile(CUSTOM_TILE, /* end */ false);
|
||||||
|
mMainExecutor.runAllReady();
|
||||||
|
|
||||||
assertEquals(1, mQSTileHost.mTileSpecs.size());
|
assertEquals(1, mQSTileHost.mTileSpecs.size());
|
||||||
assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.mTileSpecs.get(0));
|
assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.mTileSpecs.get(0));
|
||||||
@@ -348,9 +371,10 @@ public class QSTileHostTest extends SysuiTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddedAtBeginningOnDefault_customTile() {
|
public void testAddedAtBeginningOnDefault_customTile() {
|
||||||
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1"); // seed
|
saveSetting("spec1"); // seed
|
||||||
|
|
||||||
mQSTileHost.addTile(CUSTOM_TILE);
|
mQSTileHost.addTile(CUSTOM_TILE);
|
||||||
|
mMainExecutor.runAllReady();
|
||||||
|
|
||||||
assertEquals(2, mQSTileHost.mTileSpecs.size());
|
assertEquals(2, mQSTileHost.mTileSpecs.size());
|
||||||
assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.mTileSpecs.get(0));
|
assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.mTileSpecs.get(0));
|
||||||
@@ -358,9 +382,10 @@ public class QSTileHostTest extends SysuiTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddedAtBeginning_customTile() {
|
public void testAddedAtBeginning_customTile() {
|
||||||
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1"); // seed
|
saveSetting("spec1"); // seed
|
||||||
|
|
||||||
mQSTileHost.addTile(CUSTOM_TILE, /* end */ false);
|
mQSTileHost.addTile(CUSTOM_TILE, /* end */ false);
|
||||||
|
mMainExecutor.runAllReady();
|
||||||
|
|
||||||
assertEquals(2, mQSTileHost.mTileSpecs.size());
|
assertEquals(2, mQSTileHost.mTileSpecs.size());
|
||||||
assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.mTileSpecs.get(0));
|
assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.mTileSpecs.get(0));
|
||||||
@@ -368,9 +393,10 @@ public class QSTileHostTest extends SysuiTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddedAtEnd_customTile() {
|
public void testAddedAtEnd_customTile() {
|
||||||
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1"); // seed
|
saveSetting("spec1"); // seed
|
||||||
|
|
||||||
mQSTileHost.addTile(CUSTOM_TILE, /* end */ true);
|
mQSTileHost.addTile(CUSTOM_TILE, /* end */ true);
|
||||||
|
mMainExecutor.runAllReady();
|
||||||
|
|
||||||
assertEquals(2, mQSTileHost.mTileSpecs.size());
|
assertEquals(2, mQSTileHost.mTileSpecs.size());
|
||||||
assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.mTileSpecs.get(1));
|
assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.mTileSpecs.get(1));
|
||||||
@@ -409,13 +435,13 @@ public class QSTileHostTest extends SysuiTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNotAvailableTile_specNotNull() {
|
public void testNotAvailableTile_specNotNull() {
|
||||||
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "na");
|
saveSetting("na");
|
||||||
verify(mQSLogger, never()).logTileDestroyed(isNull(), anyString());
|
verify(mQSLogger, never()).logTileDestroyed(isNull(), anyString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCustomTileRemoved_stateDeleted() {
|
public void testCustomTileRemoved_stateDeleted() {
|
||||||
mQSTileHost.changeTiles(List.of(CUSTOM_TILE_SPEC), List.of());
|
mQSTileHost.changeTilesByUser(List.of(CUSTOM_TILE_SPEC), List.of());
|
||||||
|
|
||||||
verify(mCustomTileStatePersister)
|
verify(mCustomTileStatePersister)
|
||||||
.removeState(new TileServiceKey(CUSTOM_TILE, mQSTileHost.getUserId()));
|
.removeState(new TileServiceKey(CUSTOM_TILE, mQSTileHost.getUserId()));
|
||||||
@@ -423,29 +449,99 @@ public class QSTileHostTest extends SysuiTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRemoveTiles() {
|
public void testRemoveTiles() {
|
||||||
List<String> tiles = List.of("spec1", "spec2", "spec3");
|
saveSetting("spec1,spec2,spec3");
|
||||||
mQSTileHost.saveTilesToSettings(tiles);
|
|
||||||
|
|
||||||
mQSTileHost.removeTiles(List.of("spec1", "spec2"));
|
mQSTileHost.removeTiles(List.of("spec1", "spec2"));
|
||||||
|
|
||||||
|
mMainExecutor.runAllReady();
|
||||||
assertEquals(List.of("spec3"), mQSTileHost.mTileSpecs);
|
assertEquals(List.of("spec3"), mQSTileHost.mTileSpecs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTilesRemovedInQuickSuccession() {
|
||||||
|
saveSetting("spec1,spec2,spec3");
|
||||||
|
mQSTileHost.removeTile("spec1");
|
||||||
|
mQSTileHost.removeTile("spec3");
|
||||||
|
|
||||||
|
mMainExecutor.runAllReady();
|
||||||
|
assertEquals(List.of("spec2"), mQSTileHost.mTileSpecs);
|
||||||
|
assertEquals("spec2", getSetting());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddTileInMainThread() {
|
||||||
|
saveSetting("spec1,spec2");
|
||||||
|
|
||||||
|
mQSTileHost.addTile("spec3");
|
||||||
|
assertEquals(List.of("spec1", "spec2"), mQSTileHost.mTileSpecs);
|
||||||
|
|
||||||
|
mMainExecutor.runAllReady();
|
||||||
|
assertEquals(List.of("spec1", "spec2", "spec3"), mQSTileHost.mTileSpecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveTileInMainThread() {
|
||||||
|
saveSetting("spec1,spec2");
|
||||||
|
|
||||||
|
mQSTileHost.removeTile("spec1");
|
||||||
|
assertEquals(List.of("spec1", "spec2"), mQSTileHost.mTileSpecs);
|
||||||
|
|
||||||
|
mMainExecutor.runAllReady();
|
||||||
|
assertEquals(List.of("spec2"), mQSTileHost.mTileSpecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveTilesInMainThread() {
|
||||||
|
saveSetting("spec1,spec2,spec3");
|
||||||
|
|
||||||
|
mQSTileHost.removeTiles(List.of("spec3", "spec1"));
|
||||||
|
assertEquals(List.of("spec1", "spec2", "spec3"), mQSTileHost.mTileSpecs);
|
||||||
|
|
||||||
|
mMainExecutor.runAllReady();
|
||||||
|
assertEquals(List.of("spec2"), mQSTileHost.mTileSpecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveTileByUserInMainThread() {
|
||||||
|
saveSetting("spec1," + CUSTOM_TILE_SPEC);
|
||||||
|
|
||||||
|
mQSTileHost.removeTileByUser(CUSTOM_TILE);
|
||||||
|
assertEquals(List.of("spec1", CUSTOM_TILE_SPEC), mQSTileHost.mTileSpecs);
|
||||||
|
|
||||||
|
mMainExecutor.runAllReady();
|
||||||
|
assertEquals(List.of("spec1"), mQSTileHost.mTileSpecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNonValidTileNotStoredInSettings() {
|
||||||
|
saveSetting("spec1,not-valid");
|
||||||
|
|
||||||
|
assertEquals(List.of("spec1"), mQSTileHost.mTileSpecs);
|
||||||
|
assertEquals("spec1", getSetting());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNotAvailableTileNotStoredInSettings() {
|
||||||
|
saveSetting("spec1,na");
|
||||||
|
|
||||||
|
assertEquals(List.of("spec1"), mQSTileHost.mTileSpecs);
|
||||||
|
assertEquals("spec1", getSetting());
|
||||||
|
}
|
||||||
|
|
||||||
private class TestQSTileHost extends QSTileHost {
|
private class TestQSTileHost extends QSTileHost {
|
||||||
TestQSTileHost(Context context, StatusBarIconController iconController,
|
TestQSTileHost(Context context, StatusBarIconController iconController,
|
||||||
QSFactory defaultFactory, Handler mainHandler, Looper bgLooper,
|
QSFactory defaultFactory, Executor mainExecutor,
|
||||||
PluginManager pluginManager, TunerService tunerService,
|
PluginManager pluginManager, TunerService tunerService,
|
||||||
Provider<AutoTileManager> autoTiles, DumpManager dumpManager,
|
Provider<AutoTileManager> autoTiles, DumpManager dumpManager,
|
||||||
BroadcastDispatcher broadcastDispatcher, CentralSurfaces centralSurfaces,
|
CentralSurfaces centralSurfaces, QSLogger qsLogger, UiEventLogger uiEventLogger,
|
||||||
QSLogger qsLogger, UiEventLogger uiEventLogger, UserTracker userTracker,
|
UserTracker userTracker, SecureSettings secureSettings,
|
||||||
SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister,
|
CustomTileStatePersister customTileStatePersister,
|
||||||
TileServiceRequestController.Builder tileServiceRequestControllerBuilder,
|
TileServiceRequestController.Builder tileServiceRequestControllerBuilder,
|
||||||
TileLifecycleManager.Factory tileLifecycleManagerFactory) {
|
TileLifecycleManager.Factory tileLifecycleManagerFactory) {
|
||||||
super(context, iconController, defaultFactory, mainHandler, bgLooper, pluginManager,
|
super(context, iconController, defaultFactory, mainExecutor, pluginManager,
|
||||||
tunerService, autoTiles, dumpManager, broadcastDispatcher,
|
tunerService, autoTiles, dumpManager, Optional.of(centralSurfaces), qsLogger,
|
||||||
Optional.of(centralSurfaces), qsLogger, uiEventLogger, userTracker,
|
uiEventLogger, userTracker, secureSettings, customTileStatePersister,
|
||||||
secureSettings, customTileStatePersister, tileServiceRequestControllerBuilder,
|
tileServiceRequestControllerBuilder, tileLifecycleManagerFactory);
|
||||||
tileLifecycleManagerFactory);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -455,25 +551,16 @@ public class QSTileHostTest extends SysuiTestCase {
|
|||||||
@Override
|
@Override
|
||||||
public void onPluginDisconnected(QSFactory plugin) {
|
public void onPluginDisconnected(QSFactory plugin) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
void saveTilesToSettings(List<String> tileSpecs) {
|
|
||||||
super.saveTilesToSettings(tileSpecs);
|
|
||||||
// After tiles are changed, make sure to call onTuningChanged with the new setting if it
|
|
||||||
// changed
|
|
||||||
String specs = mSecureSettings.getStringForUser(
|
|
||||||
QSTileHost.TILES_SETTING, mUserTracker.getUserId());
|
|
||||||
onTuningChanged(TILES_SETTING, specs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private class TestTile extends QSTileImpl<QSTile.State> {
|
private class TestTile extends QSTileImpl<QSTile.State> {
|
||||||
|
|
||||||
protected TestTile(QSHost host) {
|
protected TestTile(QSHost host) {
|
||||||
super(
|
super(
|
||||||
host,
|
host,
|
||||||
mLooper.getLooper(),
|
mock(Looper.class),
|
||||||
new Handler(mLooper.getLooper()),
|
mock(Handler.class),
|
||||||
new FalsingManagerFake(),
|
new FalsingManagerFake(),
|
||||||
mock(MetricsLogger.class),
|
mock(MetricsLogger.class),
|
||||||
mock(StatusBarStateController.class),
|
mock(StatusBarStateController.class),
|
||||||
|
|||||||
@@ -55,6 +55,6 @@ public class TileAdapterTest extends SysuiTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testResetNotifiesHost() {
|
public void testResetNotifiesHost() {
|
||||||
mTileAdapter.resetTileSpecs(Collections.emptyList());
|
mTileAdapter.resetTileSpecs(Collections.emptyList());
|
||||||
verify(mQSTileHost).changeTiles(any(), any());
|
verify(mQSTileHost).changeTilesByUser(any(), any());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import static org.mockito.Mockito.when;
|
|||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.os.HandlerExecutor;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.service.quicksettings.IQSTileService;
|
import android.service.quicksettings.IQSTileService;
|
||||||
@@ -65,6 +66,7 @@ import org.mockito.MockitoAnnotations;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
|
||||||
@@ -130,17 +132,16 @@ public class TileServicesTest extends SysuiTestCase {
|
|||||||
.thenReturn(mTileLifecycleManager);
|
.thenReturn(mTileLifecycleManager);
|
||||||
|
|
||||||
Provider<Handler> provider = () -> new Handler(mTestableLooper.getLooper());
|
Provider<Handler> provider = () -> new Handler(mTestableLooper.getLooper());
|
||||||
|
Executor executor = new HandlerExecutor(provider.get());
|
||||||
|
|
||||||
QSTileHost host = new QSTileHost(mContext,
|
QSTileHost host = new QSTileHost(mContext,
|
||||||
mStatusBarIconController,
|
mStatusBarIconController,
|
||||||
mQSFactory,
|
mQSFactory,
|
||||||
provider.get(),
|
executor,
|
||||||
mTestableLooper.getLooper(),
|
|
||||||
mPluginManager,
|
mPluginManager,
|
||||||
mTunerService,
|
mTunerService,
|
||||||
() -> mAutoTileManager,
|
() -> mAutoTileManager,
|
||||||
mDumpManager,
|
mDumpManager,
|
||||||
mock(BroadcastDispatcher.class),
|
|
||||||
Optional.of(mCentralSurfaces),
|
Optional.of(mCentralSurfaces),
|
||||||
mQSLogger,
|
mQSLogger,
|
||||||
mUiEventLogger,
|
mUiEventLogger,
|
||||||
|
|||||||
@@ -490,7 +490,7 @@ public class AutoTileManagerTest extends SysuiTestCase {
|
|||||||
mAutoTileManager.init();
|
mAutoTileManager.init();
|
||||||
when(mAutoAddTracker.isAdded(TEST_CUSTOM_SAFETY_SPEC)).thenReturn(true);
|
when(mAutoAddTracker.isAdded(TEST_CUSTOM_SAFETY_SPEC)).thenReturn(true);
|
||||||
mAutoTileManager.mSafetyCallback.onSafetyCenterEnableChanged(false);
|
mAutoTileManager.mSafetyCallback.onSafetyCenterEnableChanged(false);
|
||||||
verify(mQsTileHost, times(1)).removeTile(safetyComponent);
|
verify(mQsTileHost, times(1)).removeTile(TEST_CUSTOM_SAFETY_SPEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user