Merge changes from topic "FK_168687572" into sc-qpr1-dev
* changes: Improve restore of QS_AUTO_ADDED_TILES Fix support for USER_ALL in FakeSettings
This commit is contained in:
committed by
Android (Google) Code Review
commit
3ab3c04d28
@@ -101,6 +101,7 @@ public class SecureSettings {
|
|||||||
Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED,
|
Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED,
|
||||||
Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED,
|
Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED,
|
||||||
Settings.Secure.QS_TILES,
|
Settings.Secure.QS_TILES,
|
||||||
|
Settings.Secure.QS_AUTO_ADDED_TILES,
|
||||||
Settings.Secure.CONTROLS_ENABLED,
|
Settings.Secure.CONTROLS_ENABLED,
|
||||||
Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT,
|
Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT,
|
||||||
Settings.Secure.DOZE_ENABLED,
|
Settings.Secure.DOZE_ENABLED,
|
||||||
@@ -118,7 +119,6 @@ public class SecureSettings {
|
|||||||
Settings.Secure.VR_DISPLAY_MODE,
|
Settings.Secure.VR_DISPLAY_MODE,
|
||||||
Settings.Secure.NOTIFICATION_BADGING,
|
Settings.Secure.NOTIFICATION_BADGING,
|
||||||
Settings.Secure.NOTIFICATION_DISMISS_RTL,
|
Settings.Secure.NOTIFICATION_DISMISS_RTL,
|
||||||
Settings.Secure.QS_AUTO_ADDED_TILES,
|
|
||||||
Settings.Secure.SCREENSAVER_ENABLED,
|
Settings.Secure.SCREENSAVER_ENABLED,
|
||||||
Settings.Secure.SCREENSAVER_COMPONENTS,
|
Settings.Secure.SCREENSAVER_COMPONENTS,
|
||||||
Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
|
Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
|
||||||
|
|||||||
@@ -72,8 +72,9 @@ public class SettingsHelper {
|
|||||||
* {@hide}
|
* {@hide}
|
||||||
*/
|
*/
|
||||||
private static final ArraySet<String> sBroadcastOnRestore;
|
private static final ArraySet<String> sBroadcastOnRestore;
|
||||||
|
private static final ArraySet<String> sBroadcastOnRestoreSystemUI;
|
||||||
static {
|
static {
|
||||||
sBroadcastOnRestore = new ArraySet<String>(4);
|
sBroadcastOnRestore = new ArraySet<String>(9);
|
||||||
sBroadcastOnRestore.add(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
|
sBroadcastOnRestore.add(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
|
||||||
sBroadcastOnRestore.add(Settings.Secure.ENABLED_VR_LISTENERS);
|
sBroadcastOnRestore.add(Settings.Secure.ENABLED_VR_LISTENERS);
|
||||||
sBroadcastOnRestore.add(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
|
sBroadcastOnRestore.add(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
|
||||||
@@ -83,6 +84,9 @@ public class SettingsHelper {
|
|||||||
sBroadcastOnRestore.add(Settings.Secure.DARK_THEME_CUSTOM_END_TIME);
|
sBroadcastOnRestore.add(Settings.Secure.DARK_THEME_CUSTOM_END_TIME);
|
||||||
sBroadcastOnRestore.add(Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
|
sBroadcastOnRestore.add(Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
|
||||||
sBroadcastOnRestore.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
|
sBroadcastOnRestore.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
|
||||||
|
sBroadcastOnRestoreSystemUI = new ArraySet<String>(2);
|
||||||
|
sBroadcastOnRestoreSystemUI.add(Settings.Secure.QS_TILES);
|
||||||
|
sBroadcastOnRestoreSystemUI.add(Settings.Secure.QS_AUTO_ADDED_TILES);
|
||||||
}
|
}
|
||||||
|
|
||||||
private interface SettingsLookup {
|
private interface SettingsLookup {
|
||||||
@@ -133,6 +137,7 @@ public class SettingsHelper {
|
|||||||
// Will we need a post-restore broadcast for this element?
|
// Will we need a post-restore broadcast for this element?
|
||||||
String oldValue = null;
|
String oldValue = null;
|
||||||
boolean sendBroadcast = false;
|
boolean sendBroadcast = false;
|
||||||
|
boolean sendBroadcastSystemUI = false;
|
||||||
final SettingsLookup table;
|
final SettingsLookup table;
|
||||||
|
|
||||||
if (destination.equals(Settings.Secure.CONTENT_URI)) {
|
if (destination.equals(Settings.Secure.CONTENT_URI)) {
|
||||||
@@ -143,10 +148,12 @@ public class SettingsHelper {
|
|||||||
table = sGlobalLookup;
|
table = sGlobalLookup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sBroadcastOnRestore.contains(name)) {
|
sendBroadcast = sBroadcastOnRestore.contains(name);
|
||||||
|
sendBroadcastSystemUI = sBroadcastOnRestoreSystemUI.contains(name);
|
||||||
|
|
||||||
|
if (sendBroadcast || sendBroadcastSystemUI) {
|
||||||
// TODO: http://b/22388012
|
// TODO: http://b/22388012
|
||||||
oldValue = table.lookup(cr, name, UserHandle.USER_SYSTEM);
|
oldValue = table.lookup(cr, name, UserHandle.USER_SYSTEM);
|
||||||
sendBroadcast = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -193,19 +200,29 @@ public class SettingsHelper {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// If we fail to apply the setting, by definition nothing happened
|
// If we fail to apply the setting, by definition nothing happened
|
||||||
sendBroadcast = false;
|
sendBroadcast = false;
|
||||||
|
sendBroadcastSystemUI = false;
|
||||||
} finally {
|
} finally {
|
||||||
// If this was an element of interest, send the "we just restored it"
|
// If this was an element of interest, send the "we just restored it"
|
||||||
// broadcast with the historical value now that the new value has
|
// broadcast with the historical value now that the new value has
|
||||||
// been committed and observers kicked off.
|
// been committed and observers kicked off.
|
||||||
if (sendBroadcast) {
|
if (sendBroadcast || sendBroadcastSystemUI) {
|
||||||
Intent intent = new Intent(Intent.ACTION_SETTING_RESTORED)
|
Intent intent = new Intent(Intent.ACTION_SETTING_RESTORED)
|
||||||
.setPackage("android").addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
|
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
|
||||||
.putExtra(Intent.EXTRA_SETTING_NAME, name)
|
.putExtra(Intent.EXTRA_SETTING_NAME, name)
|
||||||
.putExtra(Intent.EXTRA_SETTING_NEW_VALUE, value)
|
.putExtra(Intent.EXTRA_SETTING_NEW_VALUE, value)
|
||||||
.putExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE, oldValue)
|
.putExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE, oldValue)
|
||||||
.putExtra(Intent.EXTRA_SETTING_RESTORED_FROM_SDK_INT, restoredFromSdkInt);
|
.putExtra(Intent.EXTRA_SETTING_RESTORED_FROM_SDK_INT, restoredFromSdkInt);
|
||||||
|
|
||||||
|
if (sendBroadcast) {
|
||||||
|
intent.setPackage("android");
|
||||||
context.sendBroadcastAsUser(intent, UserHandle.SYSTEM, null);
|
context.sendBroadcastAsUser(intent, UserHandle.SYSTEM, null);
|
||||||
}
|
}
|
||||||
|
if (sendBroadcastSystemUI) {
|
||||||
|
intent.setPackage(
|
||||||
|
context.getString(com.android.internal.R.string.config_systemUi));
|
||||||
|
context.sendBroadcastAsUser(intent, UserHandle.SYSTEM, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,156 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2017 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
|
|
||||||
* except in compliance with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software distributed under the
|
|
||||||
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the specific language governing
|
|
||||||
* permissions and limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.systemui.qs;
|
|
||||||
|
|
||||||
import static com.android.systemui.statusbar.phone.AutoTileManager.HOTSPOT;
|
|
||||||
import static com.android.systemui.statusbar.phone.AutoTileManager.INVERSION;
|
|
||||||
import static com.android.systemui.statusbar.phone.AutoTileManager.NIGHT;
|
|
||||||
import static com.android.systemui.statusbar.phone.AutoTileManager.SAVER;
|
|
||||||
import static com.android.systemui.statusbar.phone.AutoTileManager.WORK;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.database.ContentObserver;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.UserHandle;
|
|
||||||
import android.provider.Settings.Secure;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.ArraySet;
|
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
|
||||||
import com.android.systemui.Prefs;
|
|
||||||
import com.android.systemui.Prefs.Key;
|
|
||||||
import com.android.systemui.util.UserAwareController;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
public class AutoAddTracker implements UserAwareController {
|
|
||||||
|
|
||||||
private static final String[][] CONVERT_PREFS = {
|
|
||||||
{Key.QS_HOTSPOT_ADDED, HOTSPOT},
|
|
||||||
{Key.QS_DATA_SAVER_ADDED, SAVER},
|
|
||||||
{Key.QS_INVERT_COLORS_ADDED, INVERSION},
|
|
||||||
{Key.QS_WORK_ADDED, WORK},
|
|
||||||
{Key.QS_NIGHTDISPLAY_ADDED, NIGHT},
|
|
||||||
};
|
|
||||||
|
|
||||||
private final ArraySet<String> mAutoAdded;
|
|
||||||
private final Context mContext;
|
|
||||||
private int mUserId;
|
|
||||||
|
|
||||||
public AutoAddTracker(Context context, int userId) {
|
|
||||||
mContext = context;
|
|
||||||
mUserId = userId;
|
|
||||||
mAutoAdded = new ArraySet<>(getAdded());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Init method must be called after construction to start listening
|
|
||||||
*/
|
|
||||||
public void initialize() {
|
|
||||||
// TODO: remove migration code and shared preferences keys after P release
|
|
||||||
if (mUserId == UserHandle.USER_SYSTEM) {
|
|
||||||
for (String[] convertPref : CONVERT_PREFS) {
|
|
||||||
if (Prefs.getBoolean(mContext, convertPref[0], false)) {
|
|
||||||
setTileAdded(convertPref[1]);
|
|
||||||
Prefs.remove(mContext, convertPref[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mContext.getContentResolver().registerContentObserver(
|
|
||||||
Secure.getUriFor(Secure.QS_AUTO_ADDED_TILES), false, mObserver,
|
|
||||||
UserHandle.USER_ALL);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void changeUser(UserHandle newUser) {
|
|
||||||
if (newUser.getIdentifier() == mUserId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mUserId = newUser.getIdentifier();
|
|
||||||
mAutoAdded.clear();
|
|
||||||
mAutoAdded.addAll(getAdded());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getCurrentUserId() {
|
|
||||||
return mUserId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isAdded(String tile) {
|
|
||||||
return mAutoAdded.contains(tile);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTileAdded(String tile) {
|
|
||||||
if (mAutoAdded.add(tile)) {
|
|
||||||
saveTiles();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTileRemoved(String tile) {
|
|
||||||
if (mAutoAdded.remove(tile)) {
|
|
||||||
saveTiles();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void destroy() {
|
|
||||||
mContext.getContentResolver().unregisterContentObserver(mObserver);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveTiles() {
|
|
||||||
Secure.putStringForUser(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES,
|
|
||||||
TextUtils.join(",", mAutoAdded), mUserId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Collection<String> getAdded() {
|
|
||||||
String current = Secure.getStringForUser(mContext.getContentResolver(),
|
|
||||||
Secure.QS_AUTO_ADDED_TILES, mUserId);
|
|
||||||
if (current == null) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
return Arrays.asList(current.split(","));
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
protected final ContentObserver mObserver = new ContentObserver(new Handler()) {
|
|
||||||
@Override
|
|
||||||
public void onChange(boolean selfChange) {
|
|
||||||
mAutoAdded.clear();
|
|
||||||
mAutoAdded.addAll(getAdded());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static class Builder {
|
|
||||||
private final Context mContext;
|
|
||||||
private int mUserId;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public Builder(Context context) {
|
|
||||||
mContext = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setUserId(int userId) {
|
|
||||||
mUserId = userId;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AutoAddTracker build() {
|
|
||||||
return new AutoAddTracker(mContext, mUserId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
285
packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
Normal file
285
packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.systemui.qs
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
|
import android.database.ContentObserver
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.UserHandle
|
||||||
|
import android.provider.Settings
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.util.ArraySet
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.annotation.GuardedBy
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
|
import com.android.systemui.Dumpable
|
||||||
|
import com.android.systemui.broadcast.BroadcastDispatcher
|
||||||
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.dagger.qualifiers.Background
|
||||||
|
import com.android.systemui.dagger.qualifiers.Main
|
||||||
|
import com.android.systemui.dump.DumpManager
|
||||||
|
import com.android.systemui.util.UserAwareController
|
||||||
|
import com.android.systemui.util.settings.SecureSettings
|
||||||
|
import java.io.FileDescriptor
|
||||||
|
import java.io.PrintWriter
|
||||||
|
import java.util.concurrent.Executor
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
private const val TAG = "AutoAddTracker"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class to track tiles that have been auto-added
|
||||||
|
*
|
||||||
|
* The list is backed by [Settings.Secure.QS_AUTO_ADDED_TILES].
|
||||||
|
*
|
||||||
|
* It also handles restore gracefully.
|
||||||
|
*/
|
||||||
|
class AutoAddTracker @VisibleForTesting constructor(
|
||||||
|
private val secureSettings: SecureSettings,
|
||||||
|
private val broadcastDispatcher: BroadcastDispatcher,
|
||||||
|
private val qsHost: QSHost,
|
||||||
|
private val dumpManager: DumpManager,
|
||||||
|
private val mainHandler: Handler?,
|
||||||
|
private val backgroundExecutor: Executor,
|
||||||
|
private var userId: Int
|
||||||
|
) : UserAwareController, Dumpable {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val FILTER = IntentFilter(Intent.ACTION_SETTING_RESTORED)
|
||||||
|
}
|
||||||
|
|
||||||
|
@GuardedBy("autoAdded")
|
||||||
|
private val autoAdded = ArraySet<String>()
|
||||||
|
private var restoredTiles: Set<String>? = null
|
||||||
|
|
||||||
|
override val currentUserId: Int
|
||||||
|
get() = userId
|
||||||
|
|
||||||
|
private val contentObserver = object : ContentObserver(mainHandler) {
|
||||||
|
override fun onChange(
|
||||||
|
selfChange: Boolean,
|
||||||
|
uris: Collection<Uri>,
|
||||||
|
flags: Int,
|
||||||
|
_userId: Int
|
||||||
|
) {
|
||||||
|
if (_userId != userId) {
|
||||||
|
// Ignore changes outside of our user. We'll load the correct value on user change
|
||||||
|
return
|
||||||
|
}
|
||||||
|
loadTiles()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val restoreReceiver = object : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
if (intent.action != Intent.ACTION_SETTING_RESTORED) return
|
||||||
|
processRestoreIntent(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processRestoreIntent(intent: Intent) {
|
||||||
|
when (intent.getStringExtra(Intent.EXTRA_SETTING_NAME)) {
|
||||||
|
Settings.Secure.QS_TILES -> {
|
||||||
|
restoredTiles = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)
|
||||||
|
?.split(",")
|
||||||
|
?.toSet()
|
||||||
|
?: run {
|
||||||
|
Log.w(TAG, "Null restored tiles for user $userId")
|
||||||
|
emptySet()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Settings.Secure.QS_AUTO_ADDED_TILES -> {
|
||||||
|
restoredTiles?.let { tiles ->
|
||||||
|
val restoredAutoAdded = intent
|
||||||
|
.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)
|
||||||
|
?.split(",")
|
||||||
|
?: emptyList()
|
||||||
|
val autoAddedBeforeRestore = intent
|
||||||
|
.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE)
|
||||||
|
?.split(",")
|
||||||
|
?: emptyList()
|
||||||
|
|
||||||
|
val tilesToRemove = restoredAutoAdded.filter { it !in tiles }
|
||||||
|
if (tilesToRemove.isNotEmpty()) {
|
||||||
|
qsHost.removeTiles(tilesToRemove)
|
||||||
|
}
|
||||||
|
val tiles = synchronized(autoAdded) {
|
||||||
|
autoAdded.clear()
|
||||||
|
autoAdded.addAll(restoredAutoAdded + autoAddedBeforeRestore)
|
||||||
|
getTilesFromListLocked()
|
||||||
|
}
|
||||||
|
saveTiles(tiles)
|
||||||
|
} ?: run {
|
||||||
|
Log.w(TAG, "${Settings.Secure.QS_AUTO_ADDED_TILES} restored before " +
|
||||||
|
"${Settings.Secure.QS_TILES} for user $userId")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {} // Do nothing for other Settings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init method must be called after construction to start listening
|
||||||
|
*/
|
||||||
|
fun initialize() {
|
||||||
|
dumpManager.registerDumpable(TAG, this)
|
||||||
|
loadTiles()
|
||||||
|
secureSettings.registerContentObserverForUser(
|
||||||
|
secureSettings.getUriFor(Settings.Secure.QS_AUTO_ADDED_TILES),
|
||||||
|
contentObserver,
|
||||||
|
UserHandle.USER_ALL
|
||||||
|
)
|
||||||
|
registerBroadcastReceiver()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister listeners, receivers and observers
|
||||||
|
*/
|
||||||
|
fun destroy() {
|
||||||
|
dumpManager.unregisterDumpable(TAG)
|
||||||
|
secureSettings.unregisterContentObserver(contentObserver)
|
||||||
|
unregisterBroadcastReceiver()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun registerBroadcastReceiver() {
|
||||||
|
broadcastDispatcher.registerReceiver(
|
||||||
|
restoreReceiver,
|
||||||
|
FILTER,
|
||||||
|
backgroundExecutor,
|
||||||
|
UserHandle.of(userId)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unregisterBroadcastReceiver() {
|
||||||
|
broadcastDispatcher.unregisterReceiver(restoreReceiver)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun changeUser(newUser: UserHandle) {
|
||||||
|
if (newUser.identifier == userId) return
|
||||||
|
unregisterBroadcastReceiver()
|
||||||
|
userId = newUser.identifier
|
||||||
|
restoredTiles = null
|
||||||
|
loadTiles()
|
||||||
|
registerBroadcastReceiver()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns `true` if the tile has been auto-added before
|
||||||
|
*/
|
||||||
|
fun isAdded(tile: String): Boolean {
|
||||||
|
return synchronized(autoAdded) {
|
||||||
|
tile in autoAdded
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a tile as auto-added.
|
||||||
|
*
|
||||||
|
* From here on, [isAdded] will return true for that tile.
|
||||||
|
*/
|
||||||
|
fun setTileAdded(tile: String) {
|
||||||
|
val tiles = synchronized(autoAdded) {
|
||||||
|
if (autoAdded.add(tile)) {
|
||||||
|
getTilesFromListLocked()
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tiles?.let { saveTiles(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a tile from the list of auto-added.
|
||||||
|
*
|
||||||
|
* This allows for this tile to be auto-added again in the future.
|
||||||
|
*/
|
||||||
|
fun setTileRemoved(tile: String) {
|
||||||
|
val tiles = synchronized(autoAdded) {
|
||||||
|
if (autoAdded.remove(tile)) {
|
||||||
|
getTilesFromListLocked()
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tiles?.let { saveTiles(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getTilesFromListLocked(): String {
|
||||||
|
return TextUtils.join(",", autoAdded)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveTiles(tiles: String) {
|
||||||
|
secureSettings.putStringForUser(
|
||||||
|
Settings.Secure.QS_AUTO_ADDED_TILES,
|
||||||
|
tiles,
|
||||||
|
/* tag */ null,
|
||||||
|
/* makeDefault */ false,
|
||||||
|
userId,
|
||||||
|
/* overrideableByRestore */ true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadTiles() {
|
||||||
|
synchronized(autoAdded) {
|
||||||
|
autoAdded.clear()
|
||||||
|
autoAdded.addAll(getAdded())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAdded(): Collection<String> {
|
||||||
|
val current = secureSettings.getStringForUser(Settings.Secure.QS_AUTO_ADDED_TILES, userId)
|
||||||
|
return current?.split(",") ?: emptySet()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
|
||||||
|
pw.println("Current user: $userId")
|
||||||
|
pw.println("Added tiles: $autoAdded")
|
||||||
|
}
|
||||||
|
|
||||||
|
@SysUISingleton
|
||||||
|
class Builder @Inject constructor(
|
||||||
|
private val secureSettings: SecureSettings,
|
||||||
|
private val broadcastDispatcher: BroadcastDispatcher,
|
||||||
|
private val qsHost: QSHost,
|
||||||
|
private val dumpManager: DumpManager,
|
||||||
|
@Main private val handler: Handler,
|
||||||
|
@Background private val executor: Executor
|
||||||
|
) {
|
||||||
|
private var userId: Int = 0
|
||||||
|
|
||||||
|
fun setUserId(_userId: Int): Builder {
|
||||||
|
userId = _userId
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun build(): AutoAddTracker {
|
||||||
|
return AutoAddTracker(
|
||||||
|
secureSettings,
|
||||||
|
broadcastDispatcher,
|
||||||
|
qsHost,
|
||||||
|
dumpManager,
|
||||||
|
handler,
|
||||||
|
executor,
|
||||||
|
userId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,6 +37,7 @@ public interface QSHost {
|
|||||||
void removeCallback(Callback callback);
|
void removeCallback(Callback callback);
|
||||||
TileServices getTileServices();
|
TileServices getTileServices();
|
||||||
void removeTile(String tileSpec);
|
void removeTile(String tileSpec);
|
||||||
|
void removeTiles(Collection<String> specs);
|
||||||
void unmarkTileAsAutoAdded(String tileSpec);
|
void unmarkTileAsAutoAdded(String tileSpec);
|
||||||
|
|
||||||
int indexOf(String tileSpec);
|
int indexOf(String tileSpec);
|
||||||
|
|||||||
@@ -347,6 +347,17 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
|
|||||||
changeTileSpecs(tileSpecs-> tileSpecs.remove(spec));
|
changeTileSpecs(tileSpecs-> tileSpecs.remove(spec));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove many tiles at once.
|
||||||
|
*
|
||||||
|
* It will only save to settings once (as opposed to {@link QSTileHost#removeTile} called
|
||||||
|
* multiple times).
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void removeTiles(Collection<String> specs) {
|
||||||
|
changeTileSpecs(tileSpecs -> tileSpecs.removeAll(specs));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unmarkTileAsAutoAdded(String spec) {
|
public void unmarkTileAsAutoAdded(String spec) {
|
||||||
if (mAutoTiles != null) mAutoTiles.unmarkTileAsAutoAdded(spec);
|
if (mAutoTiles != null) mAutoTiles.unmarkTileAsAutoAdded(spec);
|
||||||
@@ -368,6 +379,7 @@ 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");
|
||||||
changeTileSpecs(tileSpecs -> {
|
changeTileSpecs(tileSpecs -> {
|
||||||
if (tileSpecs.contains(spec)) return false;
|
if (tileSpecs.contains(spec)) return false;
|
||||||
|
|
||||||
@@ -382,6 +394,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
|
|||||||
}
|
}
|
||||||
|
|
||||||
void saveTilesToSettings(List<String> tileSpecs) {
|
void saveTilesToSettings(List<String> tileSpecs) {
|
||||||
|
if (tileSpecs.contains("work")) Log.wtfStack(TAG, "Saving work tile");
|
||||||
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 */);
|
||||||
|
|||||||
@@ -14,13 +14,19 @@
|
|||||||
|
|
||||||
package com.android.systemui.qs;
|
package com.android.systemui.qs;
|
||||||
|
|
||||||
import static com.android.systemui.statusbar.phone.AutoTileManager.INVERSION;
|
|
||||||
import static com.android.systemui.statusbar.phone.AutoTileManager.SAVER;
|
import static com.android.systemui.statusbar.phone.AutoTileManager.SAVER;
|
||||||
import static com.android.systemui.statusbar.phone.AutoTileManager.WORK;
|
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.any;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.provider.Settings.Secure;
|
import android.provider.Settings.Secure;
|
||||||
import android.testing.AndroidTestingRunner;
|
import android.testing.AndroidTestingRunner;
|
||||||
@@ -28,13 +34,24 @@ import android.testing.TestableLooper.RunWithLooper;
|
|||||||
|
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
import com.android.systemui.Prefs;
|
|
||||||
import com.android.systemui.Prefs.Key;
|
|
||||||
import com.android.systemui.SysuiTestCase;
|
import com.android.systemui.SysuiTestCase;
|
||||||
|
import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||||
|
import com.android.systemui.dump.DumpManager;
|
||||||
|
import com.android.systemui.util.settings.FakeSettings;
|
||||||
|
import com.android.systemui.util.settings.SecureSettings;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Captor;
|
||||||
|
import org.mockito.InOrder;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
@RunWith(AndroidTestingRunner.class)
|
@RunWith(AndroidTestingRunner.class)
|
||||||
@RunWithLooper
|
@RunWithLooper
|
||||||
@@ -43,42 +60,38 @@ public class AutoAddTrackerTest extends SysuiTestCase {
|
|||||||
|
|
||||||
private static final int USER = 0;
|
private static final int USER = 0;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private BroadcastDispatcher mBroadcastDispatcher;
|
||||||
|
@Mock
|
||||||
|
private QSHost mQSHost;
|
||||||
|
@Mock
|
||||||
|
private DumpManager mDumpManager;
|
||||||
|
@Captor
|
||||||
|
private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverArgumentCaptor;
|
||||||
|
@Captor
|
||||||
|
private ArgumentCaptor<IntentFilter> mIntentFilterArgumentCaptor;
|
||||||
|
|
||||||
|
private Executor mBackgroundExecutor = Runnable::run; // Direct executor
|
||||||
private AutoAddTracker mAutoTracker;
|
private AutoAddTracker mAutoTracker;
|
||||||
|
private SecureSettings mSecureSettings;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
Secure.putString(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES, "");
|
MockitoAnnotations.initMocks(this);
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
mSecureSettings = new FakeSettings();
|
||||||
public void testMigration() {
|
|
||||||
Prefs.putBoolean(mContext, Key.QS_DATA_SAVER_ADDED, true);
|
mSecureSettings.putStringForUser(Secure.QS_AUTO_ADDED_TILES, null, USER);
|
||||||
Prefs.putBoolean(mContext, Key.QS_WORK_ADDED, true);
|
|
||||||
mAutoTracker = new AutoAddTracker(mContext, USER);
|
mAutoTracker = createAutoAddTracker(USER);
|
||||||
mAutoTracker.initialize();
|
mAutoTracker.initialize();
|
||||||
|
|
||||||
assertTrue(mAutoTracker.isAdded(SAVER));
|
|
||||||
assertTrue(mAutoTracker.isAdded(WORK));
|
|
||||||
assertFalse(mAutoTracker.isAdded(INVERSION));
|
|
||||||
|
|
||||||
// These keys have been removed; retrieving their values should always return the default.
|
|
||||||
assertTrue(Prefs.getBoolean(mContext, Key.QS_DATA_SAVER_ADDED, true ));
|
|
||||||
assertFalse(Prefs.getBoolean(mContext, Key.QS_DATA_SAVER_ADDED, false));
|
|
||||||
assertTrue(Prefs.getBoolean(mContext, Key.QS_WORK_ADDED, true));
|
|
||||||
assertFalse(Prefs.getBoolean(mContext, Key.QS_WORK_ADDED, false));
|
|
||||||
|
|
||||||
mAutoTracker.destroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testChangeFromBackup() {
|
public void testChangeFromBackup() {
|
||||||
mAutoTracker = new AutoAddTracker(mContext, USER);
|
|
||||||
mAutoTracker.initialize();
|
|
||||||
|
|
||||||
assertFalse(mAutoTracker.isAdded(SAVER));
|
assertFalse(mAutoTracker.isAdded(SAVER));
|
||||||
|
|
||||||
Secure.putString(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES, SAVER);
|
mSecureSettings.putStringForUser(Secure.QS_AUTO_ADDED_TILES, SAVER, USER);
|
||||||
mAutoTracker.mObserver.onChange(false);
|
|
||||||
|
|
||||||
assertTrue(mAutoTracker.isAdded(SAVER));
|
assertTrue(mAutoTracker.isAdded(SAVER));
|
||||||
|
|
||||||
@@ -87,9 +100,6 @@ public class AutoAddTrackerTest extends SysuiTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetAdded() {
|
public void testSetAdded() {
|
||||||
mAutoTracker = new AutoAddTracker(mContext, USER);
|
|
||||||
mAutoTracker.initialize();
|
|
||||||
|
|
||||||
assertFalse(mAutoTracker.isAdded(SAVER));
|
assertFalse(mAutoTracker.isAdded(SAVER));
|
||||||
mAutoTracker.setTileAdded(SAVER);
|
mAutoTracker.setTileAdded(SAVER);
|
||||||
|
|
||||||
@@ -100,14 +110,12 @@ public class AutoAddTrackerTest extends SysuiTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPersist() {
|
public void testPersist() {
|
||||||
mAutoTracker = new AutoAddTracker(mContext, USER);
|
|
||||||
mAutoTracker.initialize();
|
|
||||||
|
|
||||||
assertFalse(mAutoTracker.isAdded(SAVER));
|
assertFalse(mAutoTracker.isAdded(SAVER));
|
||||||
mAutoTracker.setTileAdded(SAVER);
|
mAutoTracker.setTileAdded(SAVER);
|
||||||
|
|
||||||
mAutoTracker.destroy();
|
mAutoTracker.destroy();
|
||||||
mAutoTracker = new AutoAddTracker(mContext, USER);
|
mAutoTracker = createAutoAddTracker(USER);
|
||||||
|
mAutoTracker.initialize();
|
||||||
|
|
||||||
assertTrue(mAutoTracker.isAdded(SAVER));
|
assertTrue(mAutoTracker.isAdded(SAVER));
|
||||||
|
|
||||||
@@ -116,22 +124,158 @@ public class AutoAddTrackerTest extends SysuiTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIndependentUsers() {
|
public void testIndependentUsers() {
|
||||||
mAutoTracker = new AutoAddTracker(mContext, USER);
|
|
||||||
mAutoTracker.initialize();
|
|
||||||
mAutoTracker.setTileAdded(SAVER);
|
mAutoTracker.setTileAdded(SAVER);
|
||||||
|
|
||||||
mAutoTracker = new AutoAddTracker(mContext, USER + 1);
|
mAutoTracker = createAutoAddTracker(USER + 1);
|
||||||
|
mAutoTracker.initialize();
|
||||||
assertFalse(mAutoTracker.isAdded(SAVER));
|
assertFalse(mAutoTracker.isAdded(SAVER));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testChangeUser() {
|
public void testChangeUser() {
|
||||||
mAutoTracker = new AutoAddTracker(mContext, USER);
|
|
||||||
mAutoTracker.initialize();
|
|
||||||
mAutoTracker.setTileAdded(SAVER);
|
mAutoTracker.setTileAdded(SAVER);
|
||||||
|
|
||||||
mAutoTracker = new AutoAddTracker(mContext, USER + 1);
|
mAutoTracker = createAutoAddTracker(USER + 1);
|
||||||
mAutoTracker.changeUser(UserHandle.of(USER));
|
mAutoTracker.changeUser(UserHandle.of(USER));
|
||||||
assertTrue(mAutoTracker.isAdded(SAVER));
|
assertTrue(mAutoTracker.isAdded(SAVER));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBroadcastReceiverRegistered() {
|
||||||
|
verify(mBroadcastDispatcher).registerReceiver(
|
||||||
|
any(), mIntentFilterArgumentCaptor.capture(), any(), eq(UserHandle.of(USER)));
|
||||||
|
|
||||||
|
assertTrue(
|
||||||
|
mIntentFilterArgumentCaptor.getValue().hasAction(Intent.ACTION_SETTING_RESTORED));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBroadcastReceiverChangesWithUser() {
|
||||||
|
mAutoTracker.changeUser(UserHandle.of(USER + 1));
|
||||||
|
|
||||||
|
InOrder inOrder = Mockito.inOrder(mBroadcastDispatcher);
|
||||||
|
inOrder.verify(mBroadcastDispatcher).unregisterReceiver(any());
|
||||||
|
inOrder.verify(mBroadcastDispatcher)
|
||||||
|
.registerReceiver(any(), any(), any(), eq(UserHandle.of(USER + 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSettingRestoredWithTilesNotRemovedInSource_noAutoAddedInTarget() {
|
||||||
|
verify(mBroadcastDispatcher).registerReceiver(
|
||||||
|
mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any());
|
||||||
|
|
||||||
|
// These tiles were present in the original device
|
||||||
|
String restoredTiles = "saver,work,internet,cast";
|
||||||
|
Intent restoreTilesIntent = makeRestoreIntent(Secure.QS_TILES, null, restoredTiles);
|
||||||
|
mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
|
||||||
|
|
||||||
|
// And these tiles have been auto-added in the original device
|
||||||
|
// (no auto-added before restore)
|
||||||
|
String restoredAutoAddTiles = "work";
|
||||||
|
Intent restoreAutoAddTilesIntent =
|
||||||
|
makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, null, restoredAutoAddTiles);
|
||||||
|
mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent);
|
||||||
|
|
||||||
|
// Then, don't remove any current tiles
|
||||||
|
verify(mQSHost, never()).removeTiles(any());
|
||||||
|
assertEquals(restoredAutoAddTiles,
|
||||||
|
mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSettingRestoredWithTilesRemovedInSource_noAutoAddedInTarget() {
|
||||||
|
verify(mBroadcastDispatcher)
|
||||||
|
.registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any());
|
||||||
|
|
||||||
|
// These tiles were present in the original device
|
||||||
|
String restoredTiles = "saver,internet,cast";
|
||||||
|
Intent restoreTilesIntent = makeRestoreIntent(Secure.QS_TILES, null, restoredTiles);
|
||||||
|
mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
|
||||||
|
|
||||||
|
// And these tiles have been auto-added in the original device
|
||||||
|
// (no auto-added before restore)
|
||||||
|
String restoredAutoAddTiles = "work";
|
||||||
|
Intent restoreAutoAddTilesIntent =
|
||||||
|
makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, null, restoredAutoAddTiles);
|
||||||
|
mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent);
|
||||||
|
|
||||||
|
// Then, remove work tile
|
||||||
|
verify(mQSHost).removeTiles(List.of("work"));
|
||||||
|
assertEquals(restoredAutoAddTiles,
|
||||||
|
mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSettingRestoredWithTilesRemovedInSource_sameAutoAddedinTarget() {
|
||||||
|
verify(mBroadcastDispatcher)
|
||||||
|
.registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any());
|
||||||
|
|
||||||
|
// These tiles were present in the original device
|
||||||
|
String restoredTiles = "saver,internet,cast";
|
||||||
|
Intent restoreTilesIntent =
|
||||||
|
makeRestoreIntent(Secure.QS_TILES, "saver, internet, cast, work", restoredTiles);
|
||||||
|
mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
|
||||||
|
|
||||||
|
// And these tiles have been auto-added in the original device
|
||||||
|
// (no auto-added before restore)
|
||||||
|
String restoredAutoAddTiles = "work";
|
||||||
|
Intent restoreAutoAddTilesIntent =
|
||||||
|
makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, "work", restoredAutoAddTiles);
|
||||||
|
mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent);
|
||||||
|
|
||||||
|
// Then, remove work tile
|
||||||
|
verify(mQSHost).removeTiles(List.of("work"));
|
||||||
|
assertEquals(restoredAutoAddTiles,
|
||||||
|
mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSettingRestoredWithTilesRemovedInSource_othersAutoAddedinTarget() {
|
||||||
|
verify(mBroadcastDispatcher)
|
||||||
|
.registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any());
|
||||||
|
|
||||||
|
// These tiles were present in the original device
|
||||||
|
String restoredTiles = "saver,internet,cast";
|
||||||
|
Intent restoreTilesIntent =
|
||||||
|
makeRestoreIntent(Secure.QS_TILES, "saver, internet, cast, work", restoredTiles);
|
||||||
|
mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
|
||||||
|
|
||||||
|
// And these tiles have been auto-added in the original device
|
||||||
|
// (no auto-added before restore)
|
||||||
|
String restoredAutoAddTiles = "work";
|
||||||
|
Intent restoreAutoAddTilesIntent =
|
||||||
|
makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, "inversion", restoredAutoAddTiles);
|
||||||
|
mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent);
|
||||||
|
|
||||||
|
// Then, remove work tile
|
||||||
|
verify(mQSHost).removeTiles(List.of("work"));
|
||||||
|
|
||||||
|
String setting = mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER);
|
||||||
|
assertEquals(2, setting.split(",").length);
|
||||||
|
assertTrue(setting.contains("work"));
|
||||||
|
assertTrue(setting.contains("inversion"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Intent makeRestoreIntent(
|
||||||
|
String settingName, String previousValue, String restoredValue) {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_SETTING_RESTORED);
|
||||||
|
intent.putExtra(Intent.EXTRA_SETTING_NAME, settingName);
|
||||||
|
intent.putExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE, previousValue);
|
||||||
|
intent.putExtra(Intent.EXTRA_SETTING_NEW_VALUE, restoredValue);
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AutoAddTracker createAutoAddTracker(int user) {
|
||||||
|
// Null handler wil dispatch sync.
|
||||||
|
return new AutoAddTracker(
|
||||||
|
mSecureSettings,
|
||||||
|
mBroadcastDispatcher,
|
||||||
|
mQSHost,
|
||||||
|
mDumpManager,
|
||||||
|
null,
|
||||||
|
mBackgroundExecutor,
|
||||||
|
user
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -18,14 +18,11 @@ package com.android.systemui.qs;
|
|||||||
|
|
||||||
|
|
||||||
import static junit.framework.Assert.assertEquals;
|
import static junit.framework.Assert.assertEquals;
|
||||||
|
import static junit.framework.Assert.assertFalse;
|
||||||
import static junit.framework.Assert.assertTrue;
|
import static junit.framework.Assert.assertTrue;
|
||||||
import static junit.framework.TestCase.assertFalse;
|
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
|
||||||
import static org.mockito.ArgumentMatchers.isNull;
|
import static org.mockito.ArgumentMatchers.isNull;
|
||||||
import static org.mockito.Mockito.atLeastOnce;
|
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
@@ -67,12 +64,12 @@ import com.android.systemui.statusbar.phone.AutoTileManager;
|
|||||||
import com.android.systemui.statusbar.phone.StatusBar;
|
import com.android.systemui.statusbar.phone.StatusBar;
|
||||||
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.settings.FakeSettings;
|
||||||
import com.android.systemui.util.settings.SecureSettings;
|
import com.android.systemui.util.settings.SecureSettings;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.ArgumentCaptor;
|
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
@@ -120,7 +117,6 @@ public class QSTileHostTest extends SysuiTestCase {
|
|||||||
private UiEventLogger mUiEventLogger;
|
private UiEventLogger mUiEventLogger;
|
||||||
@Mock
|
@Mock
|
||||||
private UserTracker mUserTracker;
|
private UserTracker mUserTracker;
|
||||||
@Mock
|
|
||||||
private SecureSettings mSecureSettings;
|
private SecureSettings mSecureSettings;
|
||||||
@Mock
|
@Mock
|
||||||
private CustomTileStatePersister mCustomTileStatePersister;
|
private CustomTileStatePersister mCustomTileStatePersister;
|
||||||
@@ -134,14 +130,15 @@ public class QSTileHostTest extends SysuiTestCase {
|
|||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
mLooper = TestableLooper.get(this);
|
mLooper = TestableLooper.get(this);
|
||||||
mHandler = new Handler(mLooper.getLooper());
|
mHandler = new Handler(mLooper.getLooper());
|
||||||
|
|
||||||
|
mSecureSettings = new FakeSettings();
|
||||||
|
mSecureSettings.putStringForUser(
|
||||||
|
QSTileHost.TILES_SETTING, "", "", false, mUserTracker.getUserId(), false);
|
||||||
mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler,
|
mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler,
|
||||||
mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager,
|
mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager,
|
||||||
mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger, mUserTracker,
|
mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger, mUserTracker,
|
||||||
mSecureSettings, mCustomTileStatePersister);
|
mSecureSettings, mCustomTileStatePersister);
|
||||||
setUpTileFactory();
|
setUpTileFactory();
|
||||||
|
|
||||||
when(mSecureSettings.getStringForUser(eq(QSTileHost.TILES_SETTING), anyInt()))
|
|
||||||
.thenReturn("");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setUpTileFactory() {
|
private void setUpTileFactory() {
|
||||||
@@ -364,6 +361,16 @@ public class QSTileHostTest extends SysuiTestCase {
|
|||||||
.removeState(new TileServiceKey(CUSTOM_TILE, mQSTileHost.getUserId()));
|
.removeState(new TileServiceKey(CUSTOM_TILE, mQSTileHost.getUserId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveTiles() {
|
||||||
|
List<String> tiles = List.of("spec1", "spec2", "spec3");
|
||||||
|
mQSTileHost.saveTilesToSettings(tiles);
|
||||||
|
|
||||||
|
mQSTileHost.removeTiles(List.of("spec1", "spec2"));
|
||||||
|
|
||||||
|
assertEquals(List.of("spec3"), mQSTileHost.mTileSpecs);
|
||||||
|
}
|
||||||
|
|
||||||
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, Handler mainHandler, Looper bgLooper,
|
||||||
@@ -389,14 +396,11 @@ public class QSTileHostTest extends SysuiTestCase {
|
|||||||
@Override
|
@Override
|
||||||
void saveTilesToSettings(List<String> tileSpecs) {
|
void saveTilesToSettings(List<String> tileSpecs) {
|
||||||
super.saveTilesToSettings(tileSpecs);
|
super.saveTilesToSettings(tileSpecs);
|
||||||
|
|
||||||
ArgumentCaptor<String> specs = ArgumentCaptor.forClass(String.class);
|
|
||||||
verify(mSecureSettings, atLeastOnce()).putStringForUser(eq(QSTileHost.TILES_SETTING),
|
|
||||||
specs.capture(), isNull(), eq(false), anyInt(), eq(true));
|
|
||||||
|
|
||||||
// After tiles are changed, make sure to call onTuningChanged with the new setting if it
|
// After tiles are changed, make sure to call onTuningChanged with the new setting if it
|
||||||
// changed
|
// changed
|
||||||
onTuningChanged(TILES_SETTING, specs.getValue());
|
String specs = mSecureSettings.getStringForUser(
|
||||||
|
QSTileHost.TILES_SETTING, mUserTracker.getUserId());
|
||||||
|
onTuningChanged(TILES_SETTING, specs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ public class FakeSettings implements SecureSettings, GlobalSettings, SystemSetti
|
|||||||
private final Map<SettingsKey, String> mValues = new HashMap<>();
|
private final Map<SettingsKey, String> mValues = new HashMap<>();
|
||||||
private final Map<SettingsKey, List<ContentObserver>> mContentObservers =
|
private final Map<SettingsKey, List<ContentObserver>> mContentObservers =
|
||||||
new HashMap<>();
|
new HashMap<>();
|
||||||
|
private final Map<String, List<ContentObserver>> mContentObserversAllUsers = new HashMap<>();
|
||||||
|
|
||||||
public static final Uri CONTENT_URI = Uri.parse("content://settings/fake");
|
public static final Uri CONTENT_URI = Uri.parse("content://settings/fake");
|
||||||
|
|
||||||
@@ -55,9 +56,15 @@ public class FakeSettings implements SecureSettings, GlobalSettings, SystemSetti
|
|||||||
@Override
|
@Override
|
||||||
public void registerContentObserverForUser(Uri uri, boolean notifyDescendents,
|
public void registerContentObserverForUser(Uri uri, boolean notifyDescendents,
|
||||||
ContentObserver settingsObserver, int userHandle) {
|
ContentObserver settingsObserver, int userHandle) {
|
||||||
|
List<ContentObserver> observers;
|
||||||
|
if (userHandle == UserHandle.USER_ALL) {
|
||||||
|
mContentObserversAllUsers.putIfAbsent(uri.toString(), new ArrayList<>());
|
||||||
|
observers = mContentObserversAllUsers.get(uri.toString());
|
||||||
|
} else {
|
||||||
SettingsKey key = new SettingsKey(userHandle, uri.toString());
|
SettingsKey key = new SettingsKey(userHandle, uri.toString());
|
||||||
mContentObservers.putIfAbsent(key, new ArrayList<>());
|
mContentObservers.putIfAbsent(key, new ArrayList<>());
|
||||||
List<ContentObserver> observers = mContentObservers.get(key);
|
observers = mContentObservers.get(key);
|
||||||
|
}
|
||||||
observers.add(settingsObserver);
|
observers.add(settingsObserver);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,6 +74,10 @@ public class FakeSettings implements SecureSettings, GlobalSettings, SystemSetti
|
|||||||
List<ContentObserver> observers = mContentObservers.get(key);
|
List<ContentObserver> observers = mContentObservers.get(key);
|
||||||
observers.remove(settingsObserver);
|
observers.remove(settingsObserver);
|
||||||
}
|
}
|
||||||
|
for (String key : mContentObserversAllUsers.keySet()) {
|
||||||
|
List<ContentObserver> observers = mContentObserversAllUsers.get(key);
|
||||||
|
observers.remove(settingsObserver);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -114,6 +125,10 @@ public class FakeSettings implements SecureSettings, GlobalSettings, SystemSetti
|
|||||||
for (ContentObserver observer : mContentObservers.getOrDefault(key, new ArrayList<>())) {
|
for (ContentObserver observer : mContentObservers.getOrDefault(key, new ArrayList<>())) {
|
||||||
observer.dispatchChange(false, List.of(uri), userHandle);
|
observer.dispatchChange(false, List.of(uri), userHandle);
|
||||||
}
|
}
|
||||||
|
for (ContentObserver observer :
|
||||||
|
mContentObserversAllUsers.getOrDefault(uri.toString(), new ArrayList<>())) {
|
||||||
|
observer.dispatchChange(false, List.of(uri), userHandle);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import static org.mockito.Mockito.never;
|
|||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
import android.database.ContentObserver;
|
import android.database.ContentObserver;
|
||||||
|
import android.os.UserHandle;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.testing.AndroidTestingRunner;
|
import android.testing.AndroidTestingRunner;
|
||||||
|
|
||||||
@@ -88,6 +89,16 @@ public class FakeSettingsTest extends SysuiTestCase {
|
|||||||
verify(mContentObserver).dispatchChange(anyBoolean(), any(Collection.class), anyInt());
|
verify(mContentObserver).dispatchChange(anyBoolean(), any(Collection.class), anyInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRegisterContentObserverAllUsers() {
|
||||||
|
mFakeSettings.registerContentObserverForUser(
|
||||||
|
mFakeSettings.getUriFor("cat"), false, mContentObserver, UserHandle.USER_ALL);
|
||||||
|
|
||||||
|
mFakeSettings.putString("cat", "hat");
|
||||||
|
|
||||||
|
verify(mContentObserver).dispatchChange(anyBoolean(), any(Collection.class), anyInt());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUnregisterContentObserver() {
|
public void testUnregisterContentObserver() {
|
||||||
mFakeSettings.registerContentObserver("cat", mContentObserver);
|
mFakeSettings.registerContentObserver("cat", mContentObserver);
|
||||||
@@ -98,4 +109,16 @@ public class FakeSettingsTest extends SysuiTestCase {
|
|||||||
verify(mContentObserver, never()).dispatchChange(
|
verify(mContentObserver, never()).dispatchChange(
|
||||||
anyBoolean(), any(Collection.class), anyInt());
|
anyBoolean(), any(Collection.class), anyInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnregisterContentObserverAllUsers() {
|
||||||
|
mFakeSettings.registerContentObserverForUser(
|
||||||
|
mFakeSettings.getUriFor("cat"), false, mContentObserver, UserHandle.USER_ALL);
|
||||||
|
mFakeSettings.unregisterContentObserver(mContentObserver);
|
||||||
|
|
||||||
|
mFakeSettings.putString("cat", "hat");
|
||||||
|
|
||||||
|
verify(mContentObserver, never()).dispatchChange(
|
||||||
|
anyBoolean(), any(Collection.class), anyInt());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user