• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.statusbar.phone;
18 
19 import android.app.ActivityManager;
20 import android.app.PendingIntent;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.res.Resources;
25 import android.os.Handler;
26 import android.os.HandlerThread;
27 import android.os.Looper;
28 import android.os.Process;
29 import android.os.UserHandle;
30 import android.os.UserManager;
31 import android.provider.Settings;
32 import android.provider.Settings.Secure;
33 import android.service.quicksettings.Tile;
34 import android.text.TextUtils;
35 import android.util.Log;
36 import android.view.View;
37 
38 import com.android.systemui.R;
39 import com.android.systemui.qs.QSTile;
40 import com.android.systemui.qs.external.CustomTile;
41 import com.android.systemui.qs.external.TileLifecycleManager;
42 import com.android.systemui.qs.external.TileServices;
43 import com.android.systemui.qs.tiles.AirplaneModeTile;
44 import com.android.systemui.qs.tiles.BatteryTile;
45 import com.android.systemui.qs.tiles.BluetoothTile;
46 import com.android.systemui.qs.tiles.CastTile;
47 import com.android.systemui.qs.tiles.CellularTile;
48 import com.android.systemui.qs.tiles.ColorInversionTile;
49 import com.android.systemui.qs.tiles.DataSaverTile;
50 import com.android.systemui.qs.tiles.DndTile;
51 import com.android.systemui.qs.tiles.FlashlightTile;
52 import com.android.systemui.qs.tiles.HotspotTile;
53 import com.android.systemui.qs.tiles.IntentTile;
54 import com.android.systemui.qs.tiles.LocationTile;
55 import com.android.systemui.qs.tiles.NightDisplayTile;
56 import com.android.systemui.qs.tiles.RotationLockTile;
57 import com.android.systemui.qs.tiles.UserTile;
58 import com.android.systemui.qs.tiles.WifiTile;
59 import com.android.systemui.qs.tiles.WorkModeTile;
60 import com.android.systemui.statusbar.policy.BatteryController;
61 import com.android.systemui.statusbar.policy.BluetoothController;
62 import com.android.systemui.statusbar.policy.CastController;
63 import com.android.systemui.statusbar.policy.NextAlarmController;
64 import com.android.systemui.statusbar.policy.FlashlightController;
65 import com.android.systemui.statusbar.policy.HotspotController;
66 import com.android.systemui.statusbar.policy.KeyguardMonitor;
67 import com.android.systemui.statusbar.policy.LocationController;
68 import com.android.systemui.statusbar.policy.NetworkController;
69 import com.android.systemui.statusbar.policy.RotationLockController;
70 import com.android.systemui.statusbar.policy.SecurityController;
71 import com.android.systemui.statusbar.policy.UserInfoController;
72 import com.android.systemui.statusbar.policy.UserSwitcherController;
73 import com.android.systemui.statusbar.policy.ZenModeController;
74 import com.android.systemui.tuner.TunerService;
75 import com.android.systemui.tuner.TunerService.Tunable;
76 
77 import java.util.ArrayList;
78 import java.util.Arrays;
79 import java.util.Collection;
80 import java.util.LinkedHashMap;
81 import java.util.List;
82 import java.util.Map;
83 
84 /** Platform implementation of the quick settings tile host **/
85 public class QSTileHost implements QSTile.Host, Tunable {
86     private static final String TAG = "QSTileHost";
87     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
88 
89     public static final String TILES_SETTING = Secure.QS_TILES;
90 
91     private final Context mContext;
92     private final PhoneStatusBar mStatusBar;
93     private final LinkedHashMap<String, QSTile<?>> mTiles = new LinkedHashMap<>();
94     protected final ArrayList<String> mTileSpecs = new ArrayList<>();
95     private final BluetoothController mBluetooth;
96     private final LocationController mLocation;
97     private final RotationLockController mRotation;
98     private final NetworkController mNetwork;
99     private final ZenModeController mZen;
100     private final HotspotController mHotspot;
101     private final CastController mCast;
102     private final Looper mLooper;
103     private final FlashlightController mFlashlight;
104     private final UserSwitcherController mUserSwitcherController;
105     private final UserInfoController mUserInfoController;
106     private final KeyguardMonitor mKeyguard;
107     private final SecurityController mSecurity;
108     private final BatteryController mBattery;
109     private final StatusBarIconController mIconController;
110     private final TileServices mServices;
111 
112     private final List<Callback> mCallbacks = new ArrayList<>();
113     private final AutoTileManager mAutoTiles;
114     private final ManagedProfileController mProfileController;
115     private final NextAlarmController mNextAlarmController;
116     private View mHeader;
117     private int mCurrentUser;
118 
QSTileHost(Context context, PhoneStatusBar statusBar, BluetoothController bluetooth, LocationController location, RotationLockController rotation, NetworkController network, ZenModeController zen, HotspotController hotspot, CastController cast, FlashlightController flashlight, UserSwitcherController userSwitcher, UserInfoController userInfo, KeyguardMonitor keyguard, SecurityController security, BatteryController battery, StatusBarIconController iconController, NextAlarmController nextAlarmController)119     public QSTileHost(Context context, PhoneStatusBar statusBar,
120             BluetoothController bluetooth, LocationController location,
121             RotationLockController rotation, NetworkController network,
122             ZenModeController zen, HotspotController hotspot,
123             CastController cast, FlashlightController flashlight,
124             UserSwitcherController userSwitcher, UserInfoController userInfo,
125             KeyguardMonitor keyguard, SecurityController security,
126             BatteryController battery, StatusBarIconController iconController,
127             NextAlarmController nextAlarmController) {
128         mContext = context;
129         mStatusBar = statusBar;
130         mBluetooth = bluetooth;
131         mLocation = location;
132         mRotation = rotation;
133         mNetwork = network;
134         mZen = zen;
135         mHotspot = hotspot;
136         mCast = cast;
137         mFlashlight = flashlight;
138         mUserSwitcherController = userSwitcher;
139         mUserInfoController = userInfo;
140         mKeyguard = keyguard;
141         mSecurity = security;
142         mBattery = battery;
143         mIconController = iconController;
144         mNextAlarmController = nextAlarmController;
145         mProfileController = new ManagedProfileController(this);
146 
147         final HandlerThread ht = new HandlerThread(QSTileHost.class.getSimpleName(),
148                 Process.THREAD_PRIORITY_BACKGROUND);
149         ht.start();
150         mLooper = ht.getLooper();
151 
152         mServices = new TileServices(this, mLooper);
153 
154         TunerService.get(mContext).addTunable(this, TILES_SETTING);
155         // AutoTileManager can modify mTiles so make sure mTiles has already been initialized.
156         mAutoTiles = new AutoTileManager(context, this);
157     }
158 
getNextAlarmController()159     public NextAlarmController getNextAlarmController() {
160         return mNextAlarmController;
161     }
162 
setHeaderView(View view)163     public void setHeaderView(View view) {
164         mHeader = view;
165     }
166 
getPhoneStatusBar()167     public PhoneStatusBar getPhoneStatusBar() {
168         return mStatusBar;
169     }
170 
destroy()171     public void destroy() {
172         mAutoTiles.destroy();
173         TunerService.get(mContext).removeTunable(this);
174     }
175 
176     @Override
addCallback(Callback callback)177     public void addCallback(Callback callback) {
178         mCallbacks.add(callback);
179     }
180 
181     @Override
removeCallback(Callback callback)182     public void removeCallback(Callback callback) {
183         mCallbacks.remove(callback);
184     }
185 
186     @Override
getTiles()187     public Collection<QSTile<?>> getTiles() {
188         return mTiles.values();
189     }
190 
191     @Override
startActivityDismissingKeyguard(final Intent intent)192     public void startActivityDismissingKeyguard(final Intent intent) {
193         mStatusBar.postStartActivityDismissingKeyguard(intent, 0);
194     }
195 
196     @Override
startActivityDismissingKeyguard(PendingIntent intent)197     public void startActivityDismissingKeyguard(PendingIntent intent) {
198         mStatusBar.postStartActivityDismissingKeyguard(intent);
199     }
200 
201     @Override
startRunnableDismissingKeyguard(Runnable runnable)202     public void startRunnableDismissingKeyguard(Runnable runnable) {
203         mStatusBar.postQSRunnableDismissingKeyguard(runnable);
204     }
205 
206     @Override
warn(String message, Throwable t)207     public void warn(String message, Throwable t) {
208         // already logged
209     }
210 
animateToggleQSExpansion()211     public void animateToggleQSExpansion() {
212         // TODO: Better path to animated panel expansion.
213         mHeader.callOnClick();
214     }
215 
216     @Override
collapsePanels()217     public void collapsePanels() {
218         mStatusBar.postAnimateCollapsePanels();
219     }
220 
221     @Override
openPanels()222     public void openPanels() {
223         mStatusBar.postAnimateOpenPanels();
224     }
225 
226     @Override
getLooper()227     public Looper getLooper() {
228         return mLooper;
229     }
230 
231     @Override
getContext()232     public Context getContext() {
233         return mContext;
234     }
235 
236     @Override
getBluetoothController()237     public BluetoothController getBluetoothController() {
238         return mBluetooth;
239     }
240 
241     @Override
getLocationController()242     public LocationController getLocationController() {
243         return mLocation;
244     }
245 
246     @Override
getRotationLockController()247     public RotationLockController getRotationLockController() {
248         return mRotation;
249     }
250 
251     @Override
getNetworkController()252     public NetworkController getNetworkController() {
253         return mNetwork;
254     }
255 
256     @Override
getZenModeController()257     public ZenModeController getZenModeController() {
258         return mZen;
259     }
260 
261     @Override
getHotspotController()262     public HotspotController getHotspotController() {
263         return mHotspot;
264     }
265 
266     @Override
getCastController()267     public CastController getCastController() {
268         return mCast;
269     }
270 
271     @Override
getFlashlightController()272     public FlashlightController getFlashlightController() {
273         return mFlashlight;
274     }
275 
276     @Override
getKeyguardMonitor()277     public KeyguardMonitor getKeyguardMonitor() {
278         return mKeyguard;
279     }
280 
281     @Override
getUserSwitcherController()282     public UserSwitcherController getUserSwitcherController() {
283         return mUserSwitcherController;
284     }
285 
286     @Override
getUserInfoController()287     public UserInfoController getUserInfoController() {
288         return mUserInfoController;
289     }
290 
291     @Override
getBatteryController()292     public BatteryController getBatteryController() {
293         return mBattery;
294     }
295 
getSecurityController()296     public SecurityController getSecurityController() {
297         return mSecurity;
298     }
299 
getTileServices()300     public TileServices getTileServices() {
301         return mServices;
302     }
303 
getIconController()304     public StatusBarIconController getIconController() {
305         return mIconController;
306     }
307 
getManagedProfileController()308     public ManagedProfileController getManagedProfileController() {
309         return mProfileController;
310     }
311 
312     @Override
onTuningChanged(String key, String newValue)313     public void onTuningChanged(String key, String newValue) {
314         if (!TILES_SETTING.equals(key)) {
315             return;
316         }
317         if (DEBUG) Log.d(TAG, "Recreating tiles");
318         if (newValue == null && UserManager.isDeviceInDemoMode(mContext)) {
319             newValue = mContext.getResources().getString(R.string.quick_settings_tiles_retail_mode);
320         }
321         final List<String> tileSpecs = loadTileSpecs(mContext, newValue);
322         int currentUser = ActivityManager.getCurrentUser();
323         if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return;
324         for (Map.Entry<String, QSTile<?>> tile : mTiles.entrySet()) {
325             if (!tileSpecs.contains(tile.getKey())) {
326                 if (DEBUG) Log.d(TAG, "Destroying tile: " + tile.getKey());
327                 tile.getValue().destroy();
328             }
329         }
330         final LinkedHashMap<String, QSTile<?>> newTiles = new LinkedHashMap<>();
331         for (String tileSpec : tileSpecs) {
332             QSTile<?> tile = mTiles.get(tileSpec);
333             if (tile != null && (!(tile instanceof CustomTile)
334                     || ((CustomTile) tile).getUser() == currentUser)) {
335                 if (DEBUG) Log.d(TAG, "Adding " + tile);
336                 tile.removeCallbacks();
337                 if (!(tile instanceof CustomTile) && mCurrentUser != currentUser) {
338                     tile.userSwitch(currentUser);
339                 }
340                 newTiles.put(tileSpec, tile);
341             } else {
342                 if (DEBUG) Log.d(TAG, "Creating tile: " + tileSpec);
343                 try {
344                     tile = createTile(tileSpec);
345                     if (tile != null && tile.isAvailable()) {
346                         tile.setTileSpec(tileSpec);
347                         newTiles.put(tileSpec, tile);
348                     }
349                 } catch (Throwable t) {
350                     Log.w(TAG, "Error creating tile for spec: " + tileSpec, t);
351                 }
352             }
353         }
354         mCurrentUser = currentUser;
355         mTileSpecs.clear();
356         mTileSpecs.addAll(tileSpecs);
357         mTiles.clear();
358         mTiles.putAll(newTiles);
359         for (int i = 0; i < mCallbacks.size(); i++) {
360             mCallbacks.get(i).onTilesChanged();
361         }
362     }
363 
364     @Override
removeTile(String tileSpec)365     public void removeTile(String tileSpec) {
366         ArrayList<String> specs = new ArrayList<>(mTileSpecs);
367         specs.remove(tileSpec);
368         Settings.Secure.putStringForUser(mContext.getContentResolver(), TILES_SETTING,
369                 TextUtils.join(",", specs), ActivityManager.getCurrentUser());
370     }
371 
addTile(String spec)372     public void addTile(String spec) {
373         final String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
374                 TILES_SETTING, ActivityManager.getCurrentUser());
375         final List<String> tileSpecs = loadTileSpecs(mContext, setting);
376         if (tileSpecs.contains(spec)) {
377             return;
378         }
379         tileSpecs.add(spec);
380         Settings.Secure.putStringForUser(mContext.getContentResolver(), TILES_SETTING,
381                 TextUtils.join(",", tileSpecs), ActivityManager.getCurrentUser());
382     }
383 
addTile(ComponentName tile)384     public void addTile(ComponentName tile) {
385         List<String> newSpecs = new ArrayList<>(mTileSpecs);
386         newSpecs.add(0, CustomTile.toSpec(tile));
387         changeTiles(mTileSpecs, newSpecs);
388     }
389 
removeTile(ComponentName tile)390     public void removeTile(ComponentName tile) {
391         List<String> newSpecs = new ArrayList<>(mTileSpecs);
392         newSpecs.remove(CustomTile.toSpec(tile));
393         changeTiles(mTileSpecs, newSpecs);
394     }
395 
changeTiles(List<String> previousTiles, List<String> newTiles)396     public void changeTiles(List<String> previousTiles, List<String> newTiles) {
397         final int NP = previousTiles.size();
398         final int NA = newTiles.size();
399         for (int i = 0; i < NP; i++) {
400             String tileSpec = previousTiles.get(i);
401             if (!tileSpec.startsWith(CustomTile.PREFIX)) continue;
402             if (!newTiles.contains(tileSpec)) {
403                 ComponentName component = CustomTile.getComponentFromSpec(tileSpec);
404                 Intent intent = new Intent().setComponent(component);
405                 TileLifecycleManager lifecycleManager = new TileLifecycleManager(new Handler(),
406                         mContext, mServices, new Tile(), intent,
407                         new UserHandle(ActivityManager.getCurrentUser()));
408                 lifecycleManager.onStopListening();
409                 lifecycleManager.onTileRemoved();
410                 TileLifecycleManager.setTileAdded(mContext, component, false);
411                 lifecycleManager.flushMessagesAndUnbind();
412             }
413         }
414         if (DEBUG) Log.d(TAG, "saveCurrentTiles " + newTiles);
415         Secure.putStringForUser(getContext().getContentResolver(), QSTileHost.TILES_SETTING,
416                 TextUtils.join(",", newTiles), ActivityManager.getCurrentUser());
417     }
418 
createTile(String tileSpec)419     public QSTile<?> createTile(String tileSpec) {
420         if (tileSpec.equals("wifi")) return new WifiTile(this);
421         else if (tileSpec.equals("bt")) return new BluetoothTile(this);
422         else if (tileSpec.equals("cell")) return new CellularTile(this);
423         else if (tileSpec.equals("dnd")) return new DndTile(this);
424         else if (tileSpec.equals("inversion")) return new ColorInversionTile(this);
425         else if (tileSpec.equals("airplane")) return new AirplaneModeTile(this);
426         else if (tileSpec.equals("work")) return new WorkModeTile(this);
427         else if (tileSpec.equals("rotation")) return new RotationLockTile(this);
428         else if (tileSpec.equals("flashlight")) return new FlashlightTile(this);
429         else if (tileSpec.equals("location")) return new LocationTile(this);
430         else if (tileSpec.equals("cast")) return new CastTile(this);
431         else if (tileSpec.equals("hotspot")) return new HotspotTile(this);
432         else if (tileSpec.equals("user")) return new UserTile(this);
433         else if (tileSpec.equals("battery")) return new BatteryTile(this);
434         else if (tileSpec.equals("saver")) return new DataSaverTile(this);
435         else if (tileSpec.equals("night")) return new NightDisplayTile(this);
436         // Intent tiles.
437         else if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(this,tileSpec);
438         else if (tileSpec.startsWith(CustomTile.PREFIX)) return CustomTile.create(this,tileSpec);
439         else {
440             Log.w(TAG, "Bad tile spec: " + tileSpec);
441             return null;
442         }
443     }
444 
loadTileSpecs(Context context, String tileList)445     protected List<String> loadTileSpecs(Context context, String tileList) {
446         final Resources res = context.getResources();
447         final String defaultTileList = res.getString(R.string.quick_settings_tiles_default);
448         if (tileList == null) {
449             tileList = res.getString(R.string.quick_settings_tiles);
450             if (DEBUG) Log.d(TAG, "Loaded tile specs from config: " + tileList);
451         } else {
452             if (DEBUG) Log.d(TAG, "Loaded tile specs from setting: " + tileList);
453         }
454         final ArrayList<String> tiles = new ArrayList<String>();
455         boolean addedDefault = false;
456         for (String tile : tileList.split(",")) {
457             tile = tile.trim();
458             if (tile.isEmpty()) continue;
459             if (tile.equals("default")) {
460                 if (!addedDefault) {
461                     tiles.addAll(Arrays.asList(defaultTileList.split(",")));
462                     addedDefault = true;
463                 }
464             } else {
465                 tiles.add(tile);
466             }
467         }
468         return tiles;
469     }
470 }
471