Merge "Add auto tiles by config" into rvc-dev

This commit is contained in:
Fabian Kozynski
2020-05-05 15:04:14 +00:00
committed by Android (Google) Code Review
8 changed files with 200 additions and 65 deletions

View File

@@ -3603,12 +3603,6 @@
<!-- Whether the device supports quick settings and its associated APIs -->
<bool name="config_quickSettingsSupported">true</bool>
<!-- Comma separated list of extra quick settings tiles to be added to the default set as
defined in SystemUi (com.android.systemui.R.string.quick_settings_tiles_default).
Custom tiles (TileService) must be specified as "custom(pkg_name/class_in_package)"
(without the quotes, both absolute and relative class works). -->
<string name="config_defaultExtraQuickSettingsTiles" translatable="false"></string>
<!-- The component name, flattened to a string, for the default autofill service
to enabled for an user. This service must be trusted, as it can be activated
without explicit consent of the user. If no autofill service with the

View File

@@ -3447,7 +3447,6 @@
<java-symbol type="string" name="etws_primary_default_message_others" />
<java-symbol type="bool" name="config_quickSettingsSupported" />
<java-symbol type="string" name="config_defaultExtraQuickSettingsTiles" />
<java-symbol type="style" name="Theme.DeviceDefault.QuickSettings" />

View File

@@ -128,6 +128,13 @@
night,dark,dnd,flashlight,rotation,location
</string>
<!-- Tiles to auto add to Quick Settings upon first change of a given secure setting.
The syntax is setting-name:spec. If the tile is a TileService, the spec should be specified
as custom(package/class). Relative class name is supported. -->
<string-array name="config_quickSettingsAutoAdd" translatable="false">
<item>accessibility_display_inversion_enabled:inversion</item>
</string-array>
<!-- Whether or not the RSSI tile is capitalized or not. -->
<bool name="quick_settings_rssi_tile_capitalization">true</bool>

View File

@@ -454,11 +454,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
final Resources res = context.getResources();
final String defaultTileList = res.getString(R.string.quick_settings_tiles_default);
final String extraTileList = res.getString(
com.android.internal.R.string.config_defaultExtraQuickSettingsTiles);
tiles.addAll(Arrays.asList(defaultTileList.split(",")));
tiles.addAll(Arrays.asList(extraTileList.split(",")));
if (Build.IS_DEBUGGABLE
&& GarbageMonitor.MemoryTile.ADD_TO_DEFAULT_ON_DEBUGGABLE_BUILDS) {
tiles.add(GarbageMonitor.MemoryTile.TILE_SPEC);

View File

@@ -88,4 +88,12 @@ public abstract class SecureSetting extends ContentObserver implements Listenabl
public int getCurrentUser() {
return mUserId;
}
public String getKey() {
return mSettingName;
}
public boolean isListening() {
return mListening;
}
}

View File

@@ -15,16 +15,19 @@
package com.android.systemui.statusbar.phone;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.display.ColorDisplayManager;
import android.hardware.display.NightDisplayListener;
import android.os.Handler;
import android.provider.Settings.Secure;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.qs.AutoAddTracker;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.SecureSetting;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
import com.android.systemui.statusbar.policy.DataSaverController;
@@ -32,18 +35,24 @@ import com.android.systemui.statusbar.policy.DataSaverController.Listener;
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.HotspotController.Callback;
import java.util.ArrayList;
import java.util.Objects;
import javax.inject.Inject;
/**
* Manages which tiles should be automatically added to QS.
*/
public class AutoTileManager {
private static final String TAG = "AutoTileManager";
public static final String HOTSPOT = "hotspot";
public static final String SAVER = "saver";
public static final String INVERSION = "inversion";
public static final String WORK = "work";
public static final String NIGHT = "night";
public static final String CAST = "cast";
public static final String SETTING_SEPARATOR = ":";
private final Context mContext;
private final QSTileHost mHost;
@@ -54,6 +63,7 @@ public class AutoTileManager {
private final ManagedProfileController mManagedProfileController;
private final NightDisplayListener mNightDisplayListener;
private final CastController mCastController;
private final ArrayList<AutoAddSetting> mAutoAddSettingList = new ArrayList<>();
@Inject
public AutoTileManager(Context context, AutoAddTracker autoAddTracker, QSTileHost host,
@@ -78,21 +88,6 @@ public class AutoTileManager {
if (!mAutoTracker.isAdded(SAVER)) {
dataSaverController.addCallback(mDataSaverListener);
}
if (!mAutoTracker.isAdded(INVERSION)) {
mColorsSetting = new SecureSetting(mContext, mHandler,
Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED) {
@Override
protected void handleValueChanged(int value, boolean observedChange) {
if (mAutoTracker.isAdded(INVERSION)) return;
if (value != 0) {
mHost.addTile(INVERSION);
mAutoTracker.setTileAdded(INVERSION);
mHandler.post(() -> mColorsSetting.setListening(false));
}
}
};
mColorsSetting.setListening(true);
}
if (!mAutoTracker.isAdded(WORK)) {
managedProfileController.addCallback(mProfileCallback);
}
@@ -103,12 +98,10 @@ public class AutoTileManager {
if (!mAutoTracker.isAdded(CAST)) {
castController.addCallback(mCastCallback);
}
populateSettingsList();
}
public void destroy() {
if (mColorsSetting != null) {
mColorsSetting.setListening(false);
}
mAutoTracker.destroy();
mHotspotController.removeCallback(mHotspotCallback);
mDataSaverController.removeCallback(mDataSaverListener);
@@ -117,6 +110,42 @@ public class AutoTileManager {
mNightDisplayListener.setCallback(null);
}
mCastController.removeCallback(mCastCallback);
int settingsN = mAutoAddSettingList.size();
for (int i = 0; i < settingsN; i++) {
mAutoAddSettingList.get(i).setListening(false);
}
}
/**
* Populates a list with the pairs setting:spec in the config resource.
* <p>
* This will only create {@link AutoAddSetting} objects for those tiles that have not been
* auto-added before, and set the corresponding {@link ContentObserver} to listening.
*/
private void populateSettingsList() {
String [] autoAddList;
try {
autoAddList = mContext.getResources().getStringArray(
R.array.config_quickSettingsAutoAdd);
} catch (Resources.NotFoundException e) {
Log.w(TAG, "Missing config resource");
return;
}
// getStringArray returns @NotNull, so if we got here, autoAddList is not null
for (String tile : autoAddList) {
String[] split = tile.split(SETTING_SEPARATOR);
if (split.length == 2) {
String setting = split[0];
String spec = split[1];
if (!mAutoTracker.isAdded(spec)) {
AutoAddSetting s = new AutoAddSetting(mContext, mHandler, setting, spec);
mAutoAddSettingList.add(s);
s.setListening(true);
}
} else {
Log.w(TAG, "Malformed item in array: " + tile);
}
}
}
public void unmarkTileAsAutoAdded(String tabSpec) {
@@ -139,8 +168,6 @@ public class AutoTileManager {
}
};
private SecureSetting mColorsSetting;
private final DataSaverController.Listener mDataSaverListener = new Listener() {
@Override
public void onDataSaverChanged(boolean isDataSaving) {
@@ -213,4 +240,47 @@ public class AutoTileManager {
}
}
};
@VisibleForTesting
protected SecureSetting getSecureSettingForKey(String key) {
for (SecureSetting s : mAutoAddSettingList) {
if (Objects.equals(key, s.getKey())) {
return s;
}
}
return null;
}
/**
* Tracks tiles that should be auto added when a setting changes.
* <p>
* When the setting changes to a value different from 0, if the tile has not been auto added
* before, it will be added and the listener will be stopped.
*/
private class AutoAddSetting extends SecureSetting {
private final String mSpec;
AutoAddSetting(Context context, Handler handler, String setting, String tileSpec) {
super(context, handler, setting);
mSpec = tileSpec;
}
@Override
protected void handleValueChanged(int value, boolean observedChange) {
if (mAutoTracker.isAdded(mSpec)) {
// This should not be listening anymore
mHandler.post(() -> setListening(false));
return;
}
if (value != 0) {
if (mSpec.startsWith(CustomTile.PREFIX)) {
mHost.addTile(CustomTile.getComponentFromSpec(mSpec));
} else {
mHost.addTile(mSpec);
}
mAutoTracker.setTileAdded(mSpec);
mHandler.post(() -> setListening(false));
}
}
}
}

View File

@@ -122,11 +122,6 @@ public class QSTileHostTest extends SysuiTestCase {
mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger);
setUpTileFactory();
// Override this config so there are no unexpected tiles
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.string.config_defaultExtraQuickSettingsTiles,
"");
Settings.Secure.putStringForUser(mContext.getContentResolver(), QSTileHost.TILES_SETTING,
"", ActivityManager.getCurrentUser());
}
@@ -209,34 +204,6 @@ public class QSTileHostTest extends SysuiTestCase {
verify(mQSLogger).logTileAdded("spec1");
}
@Test
public void testDefaultAndExtra() {
mContext.getOrCreateTestableResources()
.addOverride(R.string.quick_settings_tiles_default, "spec1");
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.string.config_defaultExtraQuickSettingsTiles, "spec2");
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "default");
assertEquals(2, mQSTileHost.getTiles().size());
QSTile[] elements = mQSTileHost.getTiles().toArray(new QSTile[0]);
assertTrue(elements[0] instanceof TestTile1);
assertTrue(elements[1] instanceof TestTile2);
verify(mQSLogger).logTileAdded("spec1");
verify(mQSLogger).logTileAdded("spec2");
}
@Test
public void testExtraCustom() {
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.string.config_defaultExtraQuickSettingsTiles,
CUSTOM_TILE_SPEC);
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "default");
assertEquals(1, mQSTileHost.getTiles().size());
assertEquals(mCustomTile, CollectionUtils.firstOrNull(mQSTileHost.getTiles()));
verify(mQSLogger).logTileAdded(CUSTOM_TILE_SPEC);
}
@Test
public void testNoRepeatedSpecs_addTile() {
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec2");

View File

@@ -20,19 +20,24 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.ComponentName;
import android.hardware.display.ColorDisplayManager;
import android.hardware.display.NightDisplayListener;
import android.os.Handler;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.qs.AutoAddTracker;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.SecureSetting;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
import com.android.systemui.statusbar.policy.DataSaverController;
@@ -52,6 +57,13 @@ import java.util.List;
@SmallTest
public class AutoTileManagerTest extends SysuiTestCase {
private static final String TEST_SETTING = "setting";
private static final String TEST_SPEC = "spec";
private static final String TEST_SETTING_COMPONENT = "setting_component";
private static final String TEST_COMPONENT = "test_pkg/test_cls";
private static final String TEST_CUSTOM_SPEC = "custom(" + TEST_COMPONENT + ")";
private static final String SEPARATOR = AutoTileManager.SETTING_SEPARATOR;
@Mock private QSTileHost mQsTileHost;
@Mock private AutoAddTracker mAutoAddTracker;
@Mock private CastController mCastController;
@@ -61,7 +73,20 @@ public class AutoTileManagerTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mAutoTileManager = new AutoTileManager(mContext, mAutoAddTracker, mQsTileHost,
mContext.getOrCreateTestableResources().addOverride(
R.array.config_quickSettingsAutoAdd,
new String[] {
TEST_SETTING + SEPARATOR + TEST_SPEC,
TEST_SETTING_COMPONENT + SEPARATOR + TEST_CUSTOM_SPEC
}
);
mAutoTileManager = createAutoTileManager();
}
private AutoTileManager createAutoTileManager() {
return new AutoTileManager(mContext, mAutoAddTracker, mQsTileHost,
Handler.createAsync(TestableLooper.get(this).getLooper()),
mock(HotspotController.class),
mock(DataSaverController.class),
@@ -137,4 +162,72 @@ public class AutoTileManagerTest extends SysuiTestCase {
mAutoTileManager.mCastCallback.onCastDevicesChanged();
verify(mQsTileHost, never()).addTile("cast");
}
@Test
public void testSettingTileAdded_onChanged() {
changeValue(TEST_SETTING, 1);
waitForIdleSync();
verify(mAutoAddTracker).setTileAdded(TEST_SPEC);
verify(mQsTileHost).addTile(TEST_SPEC);
}
@Test
public void testSettingTileAddedComponent_onChanged() {
changeValue(TEST_SETTING_COMPONENT, 1);
waitForIdleSync();
verify(mAutoAddTracker).setTileAdded(TEST_CUSTOM_SPEC);
verify(mQsTileHost).addTile(ComponentName.unflattenFromString(TEST_COMPONENT));
}
@Test
public void testSettingTileAdded_onlyOnce() {
changeValue(TEST_SETTING, 1);
waitForIdleSync();
TestableLooper.get(this).processAllMessages();
changeValue(TEST_SETTING, 2);
waitForIdleSync();
verify(mAutoAddTracker).setTileAdded(TEST_SPEC);
verify(mQsTileHost).addTile(TEST_SPEC);
}
@Test
public void testSettingTileNotAdded_onChangedTo0() {
changeValue(TEST_SETTING, 0);
waitForIdleSync();
verify(mAutoAddTracker, never()).setTileAdded(TEST_SPEC);
verify(mQsTileHost, never()).addTile(TEST_SPEC);
}
@Test
public void testSettingTileNotAdded_ifPreviouslyAdded() {
when(mAutoAddTracker.isAdded(TEST_SPEC)).thenReturn(true);
changeValue(TEST_SETTING, 1);
waitForIdleSync();
verify(mAutoAddTracker, never()).setTileAdded(TEST_SPEC);
verify(mQsTileHost, never()).addTile(TEST_SPEC);
}
@Test
public void testEmptyArray_doesNotCrash() {
mContext.getOrCreateTestableResources().addOverride(
R.array.config_quickSettingsAutoAdd, new String[0]);
createAutoTileManager();
}
@Test
public void testMissingConfig_doesNotCrash() {
mContext.getOrCreateTestableResources().addOverride(
R.array.config_quickSettingsAutoAdd, null);
createAutoTileManager();
}
// Will only notify if it's listening
private void changeValue(String key, int value) {
SecureSetting s = mAutoTileManager.getSecureSettingForKey(key);
Settings.Secure.putInt(mContext.getContentResolver(), key, value);
if (s != null && s.isListening()) {
s.onChange(false);
}
}
}