blob: 4008918e267c61f04b2de8a3311cf557ef8eb8f2 [file] [log] [blame]
John Spurlockaf8d6c42014-05-07 17:49:08 -04001/*
Jason Monke5b770e2017-03-03 21:49:29 -05002 * Copyright (C) 2017 The Android Open Source Project
John Spurlockaf8d6c42014-05-07 17:49:08 -04003 *
Jason Monke5b770e2017-03-03 21:49:29 -05004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of the License at
John Spurlockaf8d6c42014-05-07 17:49:08 -04006 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
Jason Monke5b770e2017-03-03 21:49:29 -05009 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11 * KIND, either express or implied. See the License for the specific language governing
12 * permissions and limitations under the License.
John Spurlockaf8d6c42014-05-07 17:49:08 -040013 */
14
Jason Monke5b770e2017-03-03 21:49:29 -050015package com.android.systemui.qs;
John Spurlockaf8d6c42014-05-07 17:49:08 -040016
Jason Monkba2318e2015-12-08 09:04:23 -050017import android.app.ActivityManager;
Jason Monk7e53f202016-01-28 10:40:20 -050018import android.content.ComponentName;
John Spurlockaf8d6c42014-05-07 17:49:08 -040019import android.content.Context;
20import android.content.Intent;
John Spurlockbceed062014-08-10 18:04:16 -040021import android.content.res.Resources;
Dan Sandler1e5d0122019-06-06 23:35:22 -040022import android.os.Build;
Jason Monk7e53f202016-01-28 10:40:20 -050023import android.os.Handler;
Jason Monk30b64fa2018-12-27 13:19:34 -050024import android.os.Looper;
Jason Monk7e53f202016-01-28 10:40:20 -050025import android.os.UserHandle;
Sudheer Shankaad790492016-06-03 10:48:27 -070026import android.os.UserManager;
Jason Monkba2318e2015-12-08 09:04:23 -050027import android.provider.Settings;
Jason Monk7e53f202016-01-28 10:40:20 -050028import android.provider.Settings.Secure;
Jason Monka3453b8b2016-06-17 12:42:59 -040029import android.service.quicksettings.Tile;
Jason Monkba2318e2015-12-08 09:04:23 -050030import android.text.TextUtils;
Fabian Kozynski4ba3f9a2020-02-21 11:02:42 -050031import android.util.ArraySet;
John Spurlockbceed062014-08-10 18:04:16 -040032import android.util.Log;
Winsonc0d70582016-01-29 10:24:39 -080033
Fabian Kozynski2ff6df92020-04-24 12:00:49 -040034import com.android.internal.logging.InstanceId;
35import com.android.internal.logging.InstanceIdSequence;
36import com.android.internal.logging.UiEventLogger;
Fabian Kozynski00d494d2019-04-04 09:53:50 -040037import com.android.systemui.Dumpable;
John Spurlockbceed062014-08-10 18:04:16 -040038import com.android.systemui.R;
Fabian Kozynski5ca7a512019-10-16 19:56:11 +000039import com.android.systemui.broadcast.BroadcastDispatcher;
Dave Mankoff00e8a2f2019-12-18 16:59:49 -050040import com.android.systemui.dagger.qualifiers.Background;
41import com.android.systemui.dagger.qualifiers.Main;
Ned Burnsaaeb44b2020-02-12 23:48:26 -050042import com.android.systemui.dump.DumpManager;
Jason Monk702e2eb2017-03-03 16:53:44 -050043import com.android.systemui.plugins.PluginListener;
Jason Monk702e2eb2017-03-03 16:53:44 -050044import com.android.systemui.plugins.qs.QSFactory;
Jason Monk702e2eb2017-03-03 16:53:44 -050045import com.android.systemui.plugins.qs.QSTile;
Gus Prevasab336792018-11-14 13:52:20 -050046import com.android.systemui.plugins.qs.QSTileView;
Jason Monkd5a204f2015-12-21 08:50:01 -050047import com.android.systemui.qs.external.CustomTile;
Jason Monk7e53f202016-01-28 10:40:20 -050048import com.android.systemui.qs.external.TileLifecycleManager;
Jason Monkd5a204f2015-12-21 08:50:01 -050049import com.android.systemui.qs.external.TileServices;
Fabian Kozynskica30f572019-10-24 10:01:04 -040050import com.android.systemui.qs.logging.QSLogger;
Gus Prevasab336792018-11-14 13:52:20 -050051import com.android.systemui.shared.plugins.PluginManager;
Jason Monke5b770e2017-03-03 21:49:29 -050052import com.android.systemui.statusbar.phone.AutoTileManager;
53import com.android.systemui.statusbar.phone.StatusBar;
54import com.android.systemui.statusbar.phone.StatusBarIconController;
Jason Monk5e745172015-06-02 19:14:44 -040055import com.android.systemui.tuner.TunerService;
56import com.android.systemui.tuner.TunerService.Tunable;
Dan Sandler1e5d0122019-06-06 23:35:22 -040057import com.android.systemui.util.leak.GarbageMonitor;
John Spurlockaf8d6c42014-05-07 17:49:08 -040058
Fabian Kozynski00d494d2019-04-04 09:53:50 -040059import java.io.FileDescriptor;
60import java.io.PrintWriter;
John Spurlockaf8d6c42014-05-07 17:49:08 -040061import java.util.ArrayList;
John Spurlockbceed062014-08-10 18:04:16 -040062import java.util.Arrays;
63import java.util.Collection;
64import java.util.LinkedHashMap;
John Spurlockaf8d6c42014-05-07 17:49:08 -040065import java.util.List;
Dave Mankoff4dd47fa2019-11-07 10:44:48 -050066import java.util.Optional;
Fabian Kozynski4ba3f9a2020-02-21 11:02:42 -050067import java.util.Set;
Amin Shaikha062beb2018-07-25 11:21:54 -040068import java.util.function.Predicate;
John Spurlockaf8d6c42014-05-07 17:49:08 -040069
Jason Monk5d577202018-12-26 15:43:06 -050070import javax.inject.Inject;
Jason Monka26641f2018-12-27 14:20:53 -050071import javax.inject.Provider;
Jason Monk5d577202018-12-26 15:43:06 -050072import javax.inject.Singleton;
73
John Spurlockaf8d6c42014-05-07 17:49:08 -040074/** Platform implementation of the quick settings tile host **/
Jason Monk5d577202018-12-26 15:43:06 -050075@Singleton
Fabian Kozynski00d494d2019-04-04 09:53:50 -040076public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, Dumpable {
John Spurlockbceed062014-08-10 18:04:16 -040077 private static final String TAG = "QSTileHost";
78 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Fabian Kozynski2ff6df92020-04-24 12:00:49 -040079 private static final int MAX_QS_INSTANCE_ID = 1 << 20;
John Spurlockbceed062014-08-10 18:04:16 -040080
Jason Monkbaade752016-08-25 15:57:14 -040081 public static final String TILES_SETTING = Secure.QS_TILES;
John Spurlockaf8d6c42014-05-07 17:49:08 -040082
83 private final Context mContext;
Jason Monk702e2eb2017-03-03 16:53:44 -050084 private final LinkedHashMap<String, QSTile> mTiles = new LinkedHashMap<>();
Jason Monk5e745172015-06-02 19:14:44 -040085 protected final ArrayList<String> mTileSpecs = new ArrayList<>();
Jason Monkd5a204f2015-12-21 08:50:01 -050086 private final TileServices mServices;
Jason Monk30b64fa2018-12-27 13:19:34 -050087 private final TunerService mTunerService;
88 private final PluginManager mPluginManager;
Ned Burnsaaeb44b2020-02-12 23:48:26 -050089 private final DumpManager mDumpManager;
Fabian Kozynski5ca7a512019-10-16 19:56:11 +000090 private final BroadcastDispatcher mBroadcastDispatcher;
Fabian Kozynskica30f572019-10-24 10:01:04 -040091 private final QSLogger mQSLogger;
Fabian Kozynski2ff6df92020-04-24 12:00:49 -040092 private final UiEventLogger mUiEventLogger;
93 private final InstanceIdSequence mInstanceIdSequence;
John Spurlockaf8d6c42014-05-07 17:49:08 -040094
Jason Monkbbadff82015-11-06 15:47:26 -050095 private final List<Callback> mCallbacks = new ArrayList<>();
Jason Monka26641f2018-12-27 14:20:53 -050096 private AutoTileManager mAutoTiles;
Jason Monk9c7844c2017-01-18 15:21:53 -050097 private final StatusBarIconController mIconController;
Jason Monk702e2eb2017-03-03 16:53:44 -050098 private final ArrayList<QSFactory> mQsFactories = new ArrayList<>();
Jason Monk1ffa11b2016-03-08 14:44:23 -050099 private int mCurrentUser;
Dave Mankoff4dd47fa2019-11-07 10:44:48 -0500100 private final Optional<StatusBar> mStatusBarOptional;
Fabian Kozynski4ba3f9a2020-02-21 11:02:42 -0500101 private Context mUserContext;
John Spurlockbceed062014-08-10 18:04:16 -0400102
Jason Monk5d577202018-12-26 15:43:06 -0500103 @Inject
104 public QSTileHost(Context context,
105 StatusBarIconController iconController,
John Li4e3dddc2020-03-04 21:35:43 +0800106 QSFactory defaultFactory,
Dave Mankoff00e8a2f2019-12-18 16:59:49 -0500107 @Main Handler mainHandler,
108 @Background Looper bgLooper,
Jason Monk30b64fa2018-12-27 13:19:34 -0500109 PluginManager pluginManager,
Jason Monka26641f2018-12-27 14:20:53 -0500110 TunerService tunerService,
Fabian Kozynski00d494d2019-04-04 09:53:50 -0400111 Provider<AutoTileManager> autoTiles,
Ned Burnsaaeb44b2020-02-12 23:48:26 -0500112 DumpManager dumpManager,
Dave Mankoff4dd47fa2019-11-07 10:44:48 -0500113 BroadcastDispatcher broadcastDispatcher,
Fabian Kozynskica30f572019-10-24 10:01:04 -0400114 Optional<StatusBar> statusBarOptional,
Fabian Kozynski2ff6df92020-04-24 12:00:49 -0400115 QSLogger qsLogger,
116 UiEventLogger uiEventLogger) {
Jason Monk9c7844c2017-01-18 15:21:53 -0500117 mIconController = iconController;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400118 mContext = context;
Fabian Kozynski4ba3f9a2020-02-21 11:02:42 -0500119 mUserContext = context;
Jason Monk30b64fa2018-12-27 13:19:34 -0500120 mTunerService = tunerService;
121 mPluginManager = pluginManager;
Ned Burnsaaeb44b2020-02-12 23:48:26 -0500122 mDumpManager = dumpManager;
Fabian Kozynskica30f572019-10-24 10:01:04 -0400123 mQSLogger = qsLogger;
Fabian Kozynski2ff6df92020-04-24 12:00:49 -0400124 mUiEventLogger = uiEventLogger;
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000125 mBroadcastDispatcher = broadcastDispatcher;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400126
Fabian Kozynski2ff6df92020-04-24 12:00:49 -0400127 mInstanceIdSequence = new InstanceIdSequence(MAX_QS_INSTANCE_ID);
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000128 mServices = new TileServices(this, bgLooper, mBroadcastDispatcher);
Dave Mankoff4dd47fa2019-11-07 10:44:48 -0500129 mStatusBarOptional = statusBarOptional;
Jason Monkd5a204f2015-12-21 08:50:01 -0500130
Jason Monk5d577202018-12-26 15:43:06 -0500131 mQsFactories.add(defaultFactory);
Jason Monk30b64fa2018-12-27 13:19:34 -0500132 pluginManager.addPluginListener(this, QSFactory.class, true);
Ned Burnsaaeb44b2020-02-12 23:48:26 -0500133 mDumpManager.registerDumpable(TAG, this);
Jason Monk702e2eb2017-03-03 16:53:44 -0500134
Jason Monk5d577202018-12-26 15:43:06 -0500135 mainHandler.post(() -> {
136 // This is technically a hack to avoid circular dependency of
137 // QSTileHost -> XXXTile -> QSTileHost. Posting ensures creation
138 // finishes before creating any tiles.
Jason Monk30b64fa2018-12-27 13:19:34 -0500139 tunerService.addTunable(this, TILES_SETTING);
Jason Monka26641f2018-12-27 14:20:53 -0500140 // AutoTileManager can modify mTiles so make sure mTiles has already been initialized.
141 mAutoTiles = autoTiles.get();
Jason Monk5d577202018-12-26 15:43:06 -0500142 });
Jason Monk5e745172015-06-02 19:14:44 -0400143 }
John Spurlockbceed062014-08-10 18:04:16 -0400144
Jason Monk9c7844c2017-01-18 15:21:53 -0500145 public StatusBarIconController getIconController() {
146 return mIconController;
Jason Monkca894a02016-01-12 15:30:22 -0500147 }
148
Fabian Kozynski2ff6df92020-04-24 12:00:49 -0400149 @Override
150 public InstanceId getNewInstanceId() {
151 return mInstanceIdSequence.newInstanceId();
152 }
153
Jason Monk5e745172015-06-02 19:14:44 -0400154 public void destroy() {
Jason Monk88529052016-11-04 13:29:58 -0400155 mTiles.values().forEach(tile -> tile.destroy());
Jason Monkc3f42c12016-02-05 12:33:13 -0500156 mAutoTiles.destroy();
Jason Monk30b64fa2018-12-27 13:19:34 -0500157 mTunerService.removeTunable(this);
Jason Monk88529052016-11-04 13:29:58 -0400158 mServices.destroy();
Jason Monk30b64fa2018-12-27 13:19:34 -0500159 mPluginManager.removePluginListener(this);
Ned Burnsaaeb44b2020-02-12 23:48:26 -0500160 mDumpManager.unregisterDumpable(TAG);
Jason Monk702e2eb2017-03-03 16:53:44 -0500161 }
162
163 @Override
164 public void onPluginConnected(QSFactory plugin, Context pluginContext) {
165 // Give plugins priority over creation so they can override if they wish.
166 mQsFactories.add(0, plugin);
Jason Monk30b64fa2018-12-27 13:19:34 -0500167 String value = mTunerService.getValue(TILES_SETTING);
Jason Monk702e2eb2017-03-03 16:53:44 -0500168 // Force remove and recreate of all tiles.
169 onTuningChanged(TILES_SETTING, "");
170 onTuningChanged(TILES_SETTING, value);
171 }
172
173 @Override
174 public void onPluginDisconnected(QSFactory plugin) {
175 mQsFactories.remove(plugin);
176 // Force remove and recreate of all tiles.
Jason Monk30b64fa2018-12-27 13:19:34 -0500177 String value = mTunerService.getValue(TILES_SETTING);
Jason Monk702e2eb2017-03-03 16:53:44 -0500178 onTuningChanged(TILES_SETTING, "");
179 onTuningChanged(TILES_SETTING, value);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400180 }
181
Fabian Kozynskica30f572019-10-24 10:01:04 -0400182 public QSLogger getQSLogger() {
183 return mQSLogger;
184 }
185
John Spurlockaf8d6c42014-05-07 17:49:08 -0400186 @Override
Fabian Kozynski2ff6df92020-04-24 12:00:49 -0400187 public UiEventLogger getUiEventLogger() {
188 return mUiEventLogger;
189 }
190
191 @Override
Jason Monkbbadff82015-11-06 15:47:26 -0500192 public void addCallback(Callback callback) {
193 mCallbacks.add(callback);
John Spurlockbceed062014-08-10 18:04:16 -0400194 }
195
196 @Override
Jason Monk46dbfb42016-02-25 14:59:20 -0500197 public void removeCallback(Callback callback) {
198 mCallbacks.remove(callback);
199 }
200
201 @Override
Jason Monk702e2eb2017-03-03 16:53:44 -0500202 public Collection<QSTile> getTiles() {
John Spurlockbceed062014-08-10 18:04:16 -0400203 return mTiles.values();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400204 }
205
206 @Override
John Spurlockaf8d6c42014-05-07 17:49:08 -0400207 public void warn(String message, Throwable t) {
208 // already logged
209 }
210
211 @Override
212 public void collapsePanels() {
Dave Mankoff4dd47fa2019-11-07 10:44:48 -0500213 mStatusBarOptional.ifPresent(StatusBar::postAnimateCollapsePanels);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400214 }
215
216 @Override
Yoshinori Hiranoc673fbc2017-04-14 11:29:38 +0900217 public void forceCollapsePanels() {
Dave Mankoff4dd47fa2019-11-07 10:44:48 -0500218 mStatusBarOptional.ifPresent(StatusBar::postAnimateForceCollapsePanels);
Yoshinori Hiranoc673fbc2017-04-14 11:29:38 +0900219 }
220
221 @Override
Jason Monkba2318e2015-12-08 09:04:23 -0500222 public void openPanels() {
Dave Mankoff4dd47fa2019-11-07 10:44:48 -0500223 mStatusBarOptional.ifPresent(StatusBar::postAnimateOpenPanels);
Jason Monkba2318e2015-12-08 09:04:23 -0500224 }
225
226 @Override
John Spurlockaf8d6c42014-05-07 17:49:08 -0400227 public Context getContext() {
228 return mContext;
229 }
230
John Li4e3dddc2020-03-04 21:35:43 +0800231 @Override
Fabian Kozynski4ba3f9a2020-02-21 11:02:42 -0500232 public Context getUserContext() {
233 return mUserContext;
234 }
Jason Monkbbadff82015-11-06 15:47:26 -0500235
John Li4e3dddc2020-03-04 21:35:43 +0800236 @Override
Jason Monkd5a204f2015-12-21 08:50:01 -0500237 public TileServices getTileServices() {
238 return mServices;
239 }
240
Jason Monk8c09ac72017-03-16 11:53:40 -0400241 public int indexOf(String spec) {
242 return mTileSpecs.indexOf(spec);
243 }
244
Jason Monk5e745172015-06-02 19:14:44 -0400245 @Override
246 public void onTuningChanged(String key, String newValue) {
247 if (!TILES_SETTING.equals(key)) {
248 return;
249 }
Fabian Kozynski23e000f2019-09-06 15:22:24 -0400250 Log.d(TAG, "Recreating tiles");
Sudheer Shankaad790492016-06-03 10:48:27 -0700251 if (newValue == null && UserManager.isDeviceInDemoMode(mContext)) {
252 newValue = mContext.getResources().getString(R.string.quick_settings_tiles_retail_mode);
253 }
Jason Monkbbadff82015-11-06 15:47:26 -0500254 final List<String> tileSpecs = loadTileSpecs(mContext, newValue);
Jason Monk1ffa11b2016-03-08 14:44:23 -0500255 int currentUser = ActivityManager.getCurrentUser();
Fabian Kozynski4ba3f9a2020-02-21 11:02:42 -0500256 if (currentUser != mCurrentUser) {
257 mUserContext = mContext.createContextAsUser(UserHandle.of(currentUser), 0);
258 }
Jason Monk1ffa11b2016-03-08 14:44:23 -0500259 if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return;
Jason Monk88529052016-11-04 13:29:58 -0400260 mTiles.entrySet().stream().filter(tile -> !tileSpecs.contains(tile.getKey())).forEach(
261 tile -> {
Fabian Kozynski23e000f2019-09-06 15:22:24 -0400262 Log.d(TAG, "Destroying tile: " + tile.getKey());
Fabian Kozynskica30f572019-10-24 10:01:04 -0400263 mQSLogger.logTileDestroyed(tile.getKey(), "Tile removed");
Jason Monk88529052016-11-04 13:29:58 -0400264 tile.getValue().destroy();
265 });
Jason Monk702e2eb2017-03-03 16:53:44 -0500266 final LinkedHashMap<String, QSTile> newTiles = new LinkedHashMap<>();
John Spurlockbceed062014-08-10 18:04:16 -0400267 for (String tileSpec : tileSpecs) {
Jason Monk702e2eb2017-03-03 16:53:44 -0500268 QSTile tile = mTiles.get(tileSpec);
Jason Monk1ffa11b2016-03-08 14:44:23 -0500269 if (tile != null && (!(tile instanceof CustomTile)
270 || ((CustomTile) tile).getUser() == currentUser)) {
Akitaka Kimuraefd1c542016-09-27 20:06:00 +0900271 if (tile.isAvailable()) {
272 if (DEBUG) Log.d(TAG, "Adding " + tile);
273 tile.removeCallbacks();
Jason Monkf120cf32016-12-06 11:34:12 -0500274 if (!(tile instanceof CustomTile) && mCurrentUser != currentUser) {
275 tile.userSwitch(currentUser);
276 }
Akitaka Kimuraefd1c542016-09-27 20:06:00 +0900277 newTiles.put(tileSpec, tile);
Fabian Kozynskica30f572019-10-24 10:01:04 -0400278 mQSLogger.logTileAdded(tileSpec);
Akitaka Kimuraefd1c542016-09-27 20:06:00 +0900279 } else {
280 tile.destroy();
Fabian Kozynski23e000f2019-09-06 15:22:24 -0400281 Log.d(TAG, "Destroying not available tile: " + tileSpec);
Fabian Kozynskica30f572019-10-24 10:01:04 -0400282 mQSLogger.logTileDestroyed(tileSpec, "Tile not available");
Justin Klaassendd32d902016-07-31 10:25:36 -0700283 }
John Spurlockbceed062014-08-10 18:04:16 -0400284 } else {
Fabian Kozynski4ba3f9a2020-02-21 11:02:42 -0500285 // This means that the tile is a CustomTile AND the user is different, so let's
286 // destroy it
287 if (tile != null) {
288 tile.destroy();
289 Log.d(TAG, "Destroying tile for wrong user: " + tileSpec);
290 mQSLogger.logTileDestroyed(tileSpec, "Tile for wrong user");
291 }
Fabian Kozynski23e000f2019-09-06 15:22:24 -0400292 Log.d(TAG, "Creating tile: " + tileSpec);
John Spurlockbceed062014-08-10 18:04:16 -0400293 try {
Jason Monk1ffa11b2016-03-08 14:44:23 -0500294 tile = createTile(tileSpec);
Jason Monk88529052016-11-04 13:29:58 -0400295 if (tile != null) {
Fabian Kozynski687da672020-03-06 11:17:41 -0500296 tile.setTileSpec(tileSpec);
Jason Monk88529052016-11-04 13:29:58 -0400297 if (tile.isAvailable()) {
Jason Monk88529052016-11-04 13:29:58 -0400298 newTiles.put(tileSpec, tile);
Fabian Kozynskica30f572019-10-24 10:01:04 -0400299 mQSLogger.logTileAdded(tileSpec);
Jason Monk88529052016-11-04 13:29:58 -0400300 } else {
301 tile.destroy();
Fabian Kozynski23e000f2019-09-06 15:22:24 -0400302 Log.d(TAG, "Destroying not available tile: " + tileSpec);
Fabian Kozynskica30f572019-10-24 10:01:04 -0400303 mQSLogger.logTileDestroyed(tileSpec, "Tile not available");
Jason Monk88529052016-11-04 13:29:58 -0400304 }
Jason Monkba2318e2015-12-08 09:04:23 -0500305 }
John Spurlockbceed062014-08-10 18:04:16 -0400306 } catch (Throwable t) {
307 Log.w(TAG, "Error creating tile for spec: " + tileSpec, t);
308 }
309 }
310 }
Jason Monk1ffa11b2016-03-08 14:44:23 -0500311 mCurrentUser = currentUser;
Fabian Kozynski4ba3f9a2020-02-21 11:02:42 -0500312 List<String> currentSpecs = new ArrayList<>(mTileSpecs);
Jason Monk11a77442015-05-12 19:29:02 -0400313 mTileSpecs.clear();
314 mTileSpecs.addAll(tileSpecs);
John Spurlockbceed062014-08-10 18:04:16 -0400315 mTiles.clear();
316 mTiles.putAll(newTiles);
Fabian Kozynski6ff5c182019-06-20 14:57:47 -0400317 if (newTiles.isEmpty() && !tileSpecs.isEmpty()) {
318 // If we didn't manage to create any tiles, set it to empty (default)
Fabian Kozynski23e000f2019-09-06 15:22:24 -0400319 Log.d(TAG, "No valid tiles on tuning changed. Setting to default.");
Fabian Kozynski6ff5c182019-06-20 14:57:47 -0400320 changeTiles(currentSpecs, loadTileSpecs(mContext, ""));
321 } else {
322 for (int i = 0; i < mCallbacks.size(); i++) {
323 mCallbacks.get(i).onTilesChanged();
324 }
John Spurlockbceed062014-08-10 18:04:16 -0400325 }
326 }
327
Jason Monkbbadff82015-11-06 15:47:26 -0500328 @Override
Amin Shaikha062beb2018-07-25 11:21:54 -0400329 public void removeTile(String spec) {
330 changeTileSpecs(tileSpecs-> tileSpecs.remove(spec));
Jason Monkba2318e2015-12-08 09:04:23 -0500331 }
332
arangelov5fe95982018-08-17 18:43:31 +0100333 @Override
334 public void unmarkTileAsAutoAdded(String spec) {
Jason Monka26641f2018-12-27 14:20:53 -0500335 if (mAutoTiles != null) mAutoTiles.unmarkTileAsAutoAdded(spec);
arangelov5fe95982018-08-17 18:43:31 +0100336 }
337
Jason Monkc3f42c12016-02-05 12:33:13 -0500338 public void addTile(String spec) {
Fabian Kozynski4ba3f9a2020-02-21 11:02:42 -0500339 changeTileSpecs(tileSpecs-> !tileSpecs.contains(spec) && tileSpecs.add(spec));
Amin Shaikha062beb2018-07-25 11:21:54 -0400340 }
341
Fabian Kozynski1fcc8c42020-05-04 13:31:47 -0400342 private void saveTilesToSettings(List<String> tileSpecs) {
343 Settings.Secure.putStringForUser(mContext.getContentResolver(), TILES_SETTING,
344 TextUtils.join(",", tileSpecs), null /* tag */,
345 false /* default */, mCurrentUser, true /* overrideable by restore */);
346 }
347
Amin Shaikha062beb2018-07-25 11:21:54 -0400348 private void changeTileSpecs(Predicate<List<String>> changeFunction) {
Ricky Waib96c1eac2016-06-02 11:42:16 +0100349 final String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
Fabian Kozynski1fcc8c42020-05-04 13:31:47 -0400350 TILES_SETTING, mCurrentUser);
Ricky Waib96c1eac2016-06-02 11:42:16 +0100351 final List<String> tileSpecs = loadTileSpecs(mContext, setting);
Amin Shaikha062beb2018-07-25 11:21:54 -0400352 if (changeFunction.test(tileSpecs)) {
Fabian Kozynski1fcc8c42020-05-04 13:31:47 -0400353 saveTilesToSettings(tileSpecs);
Amin Shaikha062beb2018-07-25 11:21:54 -0400354 }
Jason Monkc3f42c12016-02-05 12:33:13 -0500355 }
356
Jason Monk7e53f202016-01-28 10:40:20 -0500357 public void addTile(ComponentName tile) {
Fabian Kozynski4ba3f9a2020-02-21 11:02:42 -0500358 String spec = CustomTile.toSpec(tile);
359 if (!mTileSpecs.contains(spec)) {
360 List<String> newSpecs = new ArrayList<>(mTileSpecs);
361 newSpecs.add(0, spec);
362 changeTiles(mTileSpecs, newSpecs);
363 }
Jason Monk7e53f202016-01-28 10:40:20 -0500364 }
365
366 public void removeTile(ComponentName tile) {
367 List<String> newSpecs = new ArrayList<>(mTileSpecs);
368 newSpecs.remove(CustomTile.toSpec(tile));
369 changeTiles(mTileSpecs, newSpecs);
370 }
371
372 public void changeTiles(List<String> previousTiles, List<String> newTiles) {
Fabian Kozynski9e40da92020-02-25 13:00:04 -0500373 final List<String> copy = new ArrayList<>(previousTiles);
374 final int NP = copy.size();
Jason Monk7e53f202016-01-28 10:40:20 -0500375 for (int i = 0; i < NP; i++) {
Fabian Kozynski9e40da92020-02-25 13:00:04 -0500376 String tileSpec = copy.get(i);
Jason Monk7e53f202016-01-28 10:40:20 -0500377 if (!tileSpec.startsWith(CustomTile.PREFIX)) continue;
378 if (!newTiles.contains(tileSpec)) {
Jason Monka3453b8b2016-06-17 12:42:59 -0400379 ComponentName component = CustomTile.getComponentFromSpec(tileSpec);
380 Intent intent = new Intent().setComponent(component);
Jason Monk7e53f202016-01-28 10:40:20 -0500381 TileLifecycleManager lifecycleManager = new TileLifecycleManager(new Handler(),
Jason Monkee68fd82016-06-23 13:12:23 -0400382 mContext, mServices, new Tile(), intent,
Fabian Kozynski1fcc8c42020-05-04 13:31:47 -0400383 new UserHandle(mCurrentUser),
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000384 mBroadcastDispatcher);
Jason Monk7e53f202016-01-28 10:40:20 -0500385 lifecycleManager.onStopListening();
386 lifecycleManager.onTileRemoved();
Jason Monkbaade752016-08-25 15:57:14 -0400387 TileLifecycleManager.setTileAdded(mContext, component, false);
Jason Monk7e53f202016-01-28 10:40:20 -0500388 lifecycleManager.flushMessagesAndUnbind();
389 }
390 }
391 if (DEBUG) Log.d(TAG, "saveCurrentTiles " + newTiles);
Fabian Kozynski1fcc8c42020-05-04 13:31:47 -0400392 saveTilesToSettings(newTiles);
Jason Monk7e53f202016-01-28 10:40:20 -0500393 }
394
Jason Monk702e2eb2017-03-03 16:53:44 -0500395 public QSTile createTile(String tileSpec) {
396 for (int i = 0; i < mQsFactories.size(); i++) {
397 QSTile t = mQsFactories.get(i).createTile(tileSpec);
398 if (t != null) {
399 return t;
400 }
Jason Monk2b37190f2016-01-06 10:19:04 -0500401 }
Jason Monk702e2eb2017-03-03 16:53:44 -0500402 return null;
403 }
404
405 public QSTileView createTileView(QSTile tile, boolean collapsedView) {
406 for (int i = 0; i < mQsFactories.size(); i++) {
407 QSTileView view = mQsFactories.get(i).createTileView(tile, collapsedView);
408 if (view != null) {
409 return view;
410 }
411 }
412 throw new RuntimeException("Default factory didn't create view for " + tile.getTileSpec());
John Spurlockbceed062014-08-10 18:04:16 -0400413 }
414
Amin Shaikha062beb2018-07-25 11:21:54 -0400415 protected static List<String> loadTileSpecs(Context context, String tileList) {
Jason Monkbbadff82015-11-06 15:47:26 -0500416 final Resources res = context.getResources();
Fabian Kozynski1c0e33b2020-01-10 17:02:30 -0500417
Fabian Kozynski7f370dd2018-12-05 10:41:07 -0500418 if (TextUtils.isEmpty(tileList)) {
John Spurlockbceed062014-08-10 18:04:16 -0400419 tileList = res.getString(R.string.quick_settings_tiles);
420 if (DEBUG) Log.d(TAG, "Loaded tile specs from config: " + tileList);
421 } else {
422 if (DEBUG) Log.d(TAG, "Loaded tile specs from setting: " + tileList);
423 }
424 final ArrayList<String> tiles = new ArrayList<String>();
425 boolean addedDefault = false;
Fabian Kozynski4ba3f9a2020-02-21 11:02:42 -0500426 Set<String> addedSpecs = new ArraySet<>();
John Spurlockbceed062014-08-10 18:04:16 -0400427 for (String tile : tileList.split(",")) {
428 tile = tile.trim();
429 if (tile.isEmpty()) continue;
430 if (tile.equals("default")) {
431 if (!addedDefault) {
Fabian Kozynski4ba3f9a2020-02-21 11:02:42 -0500432 List<String> defaultSpecs = getDefaultSpecs(context);
433 for (String spec : defaultSpecs) {
434 if (!addedSpecs.contains(spec)) {
435 tiles.add(spec);
436 addedSpecs.add(spec);
437 }
438 }
John Spurlockbceed062014-08-10 18:04:16 -0400439 addedDefault = true;
440 }
441 } else {
Fabian Kozynski4ba3f9a2020-02-21 11:02:42 -0500442 if (!addedSpecs.contains(tile)) {
443 tiles.add(tile);
444 addedSpecs.add(tile);
445 }
John Spurlockbceed062014-08-10 18:04:16 -0400446 }
447 }
448 return tiles;
449 }
Fabian Kozynski00d494d2019-04-04 09:53:50 -0400450
Fabian Kozynski1c0e33b2020-01-10 17:02:30 -0500451 /**
452 * Returns the default QS tiles for the context.
453 * @param context the context to obtain the resources from
454 * @return a list of specs of the default tiles
455 */
456 public static List<String> getDefaultSpecs(Context context) {
457 final ArrayList<String> tiles = new ArrayList<String>();
458
459 final Resources res = context.getResources();
460 final String defaultTileList = res.getString(R.string.quick_settings_tiles_default);
Fabian Kozynski1c0e33b2020-01-10 17:02:30 -0500461
462 tiles.addAll(Arrays.asList(defaultTileList.split(",")));
Fabian Kozynski1c0e33b2020-01-10 17:02:30 -0500463 if (Build.IS_DEBUGGABLE
464 && GarbageMonitor.MemoryTile.ADD_TO_DEFAULT_ON_DEBUGGABLE_BUILDS) {
465 tiles.add(GarbageMonitor.MemoryTile.TILE_SPEC);
466 }
467 return tiles;
468 }
469
Fabian Kozynski00d494d2019-04-04 09:53:50 -0400470 @Override
471 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
472 pw.println("QSTileHost:");
473 mTiles.values().stream().filter(obj -> obj instanceof Dumpable)
474 .forEach(o -> ((Dumpable) o).dump(fd, pw, args));
475 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400476}