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.content.Context; 20 import android.content.Intent; 21 import android.content.res.Resources; 22 import android.database.ContentObserver; 23 import android.net.Uri; 24 import android.os.Process; 25 import android.os.Handler; 26 import android.os.HandlerThread; 27 import android.os.Looper; 28 import android.provider.Settings.Secure; 29 import android.util.Log; 30 31 import com.android.systemui.R; 32 import com.android.systemui.qs.QSTile; 33 import com.android.systemui.qs.tiles.AirplaneModeTile; 34 import com.android.systemui.qs.tiles.BluetoothTile; 35 import com.android.systemui.qs.tiles.CastTile; 36 import com.android.systemui.qs.tiles.CellularTile; 37 import com.android.systemui.qs.tiles.ColorInversionTile; 38 import com.android.systemui.qs.tiles.FlashlightTile; 39 import com.android.systemui.qs.tiles.HotspotTile; 40 import com.android.systemui.qs.tiles.IntentTile; 41 import com.android.systemui.qs.tiles.LocationTile; 42 import com.android.systemui.qs.tiles.RotationLockTile; 43 import com.android.systemui.qs.tiles.WifiTile; 44 import com.android.systemui.settings.CurrentUserTracker; 45 import com.android.systemui.statusbar.policy.BluetoothController; 46 import com.android.systemui.statusbar.policy.CastController; 47 import com.android.systemui.statusbar.policy.FlashlightController; 48 import com.android.systemui.statusbar.policy.KeyguardMonitor; 49 import com.android.systemui.statusbar.policy.LocationController; 50 import com.android.systemui.statusbar.policy.NetworkController; 51 import com.android.systemui.statusbar.policy.RotationLockController; 52 import com.android.systemui.statusbar.policy.HotspotController; 53 import com.android.systemui.statusbar.policy.SecurityController; 54 import com.android.systemui.statusbar.policy.UserSwitcherController; 55 import com.android.systemui.statusbar.policy.ZenModeController; 56 57 import java.util.ArrayList; 58 import java.util.Arrays; 59 import java.util.Collection; 60 import java.util.LinkedHashMap; 61 import java.util.List; 62 import java.util.Map; 63 64 /** Platform implementation of the quick settings tile host **/ 65 public class QSTileHost implements QSTile.Host { 66 private static final String TAG = "QSTileHost"; 67 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 68 69 private static final String TILES_SETTING = "sysui_qs_tiles"; 70 71 private final Context mContext; 72 private final PhoneStatusBar mStatusBar; 73 private final LinkedHashMap<String, QSTile<?>> mTiles = new LinkedHashMap<>(); 74 private final Observer mObserver = new Observer(); 75 private final BluetoothController mBluetooth; 76 private final LocationController mLocation; 77 private final RotationLockController mRotation; 78 private final NetworkController mNetwork; 79 private final ZenModeController mZen; 80 private final HotspotController mHotspot; 81 private final CastController mCast; 82 private final Looper mLooper; 83 private final CurrentUserTracker mUserTracker; 84 private final FlashlightController mFlashlight; 85 private final UserSwitcherController mUserSwitcherController; 86 private final KeyguardMonitor mKeyguard; 87 private final SecurityController mSecurity; 88 89 private Callback mCallback; 90 91 public QSTileHost(Context context, PhoneStatusBar statusBar, 92 BluetoothController bluetooth, LocationController location, 93 RotationLockController rotation, NetworkController network, 94 ZenModeController zen, HotspotController hotspot, 95 CastController cast, FlashlightController flashlight, 96 UserSwitcherController userSwitcher, KeyguardMonitor keyguard, 97 SecurityController security) { 98 mContext = context; 99 mStatusBar = statusBar; 100 mBluetooth = bluetooth; 101 mLocation = location; 102 mRotation = rotation; 103 mNetwork = network; 104 mZen = zen; 105 mHotspot = hotspot; 106 mCast = cast; 107 mFlashlight = flashlight; 108 mUserSwitcherController = userSwitcher; 109 mKeyguard = keyguard; 110 mSecurity = security; 111 112 final HandlerThread ht = new HandlerThread(QSTileHost.class.getSimpleName(), 113 Process.THREAD_PRIORITY_BACKGROUND); 114 ht.start(); 115 mLooper = ht.getLooper(); 116 117 mUserTracker = new CurrentUserTracker(mContext) { 118 @Override 119 public void onUserSwitched(int newUserId) { 120 recreateTiles(); 121 for (QSTile<?> tile : mTiles.values()) { 122 tile.userSwitch(newUserId); 123 } 124 mSecurity.onUserSwitched(newUserId); 125 mNetwork.onUserSwitched(newUserId); 126 mObserver.register(); 127 } 128 }; 129 recreateTiles(); 130 131 mUserTracker.startTracking(); 132 mObserver.register(); 133 } 134 135 @Override 136 public void setCallback(Callback callback) { 137 mCallback = callback; 138 } 139 140 @Override 141 public Collection<QSTile<?>> getTiles() { 142 return mTiles.values(); 143 } 144 145 @Override 146 public void startSettingsActivity(final Intent intent) { 147 mStatusBar.postStartSettingsActivity(intent, 0); 148 } 149 150 @Override 151 public void warn(String message, Throwable t) { 152 // already logged 153 } 154 155 @Override 156 public void collapsePanels() { 157 mStatusBar.postAnimateCollapsePanels(); 158 } 159 160 @Override 161 public Looper getLooper() { 162 return mLooper; 163 } 164 165 @Override 166 public Context getContext() { 167 return mContext; 168 } 169 170 @Override 171 public BluetoothController getBluetoothController() { 172 return mBluetooth; 173 } 174 175 @Override 176 public LocationController getLocationController() { 177 return mLocation; 178 } 179 180 @Override 181 public RotationLockController getRotationLockController() { 182 return mRotation; 183 } 184 185 @Override 186 public NetworkController getNetworkController() { 187 return mNetwork; 188 } 189 190 @Override 191 public ZenModeController getZenModeController() { 192 return mZen; 193 } 194 195 @Override 196 public HotspotController getHotspotController() { 197 return mHotspot; 198 } 199 200 @Override 201 public CastController getCastController() { 202 return mCast; 203 } 204 205 @Override 206 public FlashlightController getFlashlightController() { 207 return mFlashlight; 208 } 209 210 @Override 211 public KeyguardMonitor getKeyguardMonitor() { 212 return mKeyguard; 213 } 214 215 public UserSwitcherController getUserSwitcherController() { 216 return mUserSwitcherController; 217 } 218 219 public SecurityController getSecurityController() { 220 return mSecurity; 221 } 222 223 private void recreateTiles() { 224 if (DEBUG) Log.d(TAG, "Recreating tiles"); 225 final List<String> tileSpecs = loadTileSpecs(); 226 for (Map.Entry<String, QSTile<?>> tile : mTiles.entrySet()) { 227 if (!tileSpecs.contains(tile.getKey())) { 228 if (DEBUG) Log.d(TAG, "Destroying tile: " + tile.getKey()); 229 tile.getValue().destroy(); 230 } 231 } 232 final LinkedHashMap<String, QSTile<?>> newTiles = new LinkedHashMap<>(); 233 for (String tileSpec : tileSpecs) { 234 if (mTiles.containsKey(tileSpec)) { 235 newTiles.put(tileSpec, mTiles.get(tileSpec)); 236 } else { 237 if (DEBUG) Log.d(TAG, "Creating tile: " + tileSpec); 238 try { 239 newTiles.put(tileSpec, createTile(tileSpec)); 240 } catch (Throwable t) { 241 Log.w(TAG, "Error creating tile for spec: " + tileSpec, t); 242 } 243 } 244 } 245 if (mTiles.equals(newTiles)) return; 246 mTiles.clear(); 247 mTiles.putAll(newTiles); 248 if (mCallback != null) { 249 mCallback.onTilesChanged(); 250 } 251 } 252 253 private QSTile<?> createTile(String tileSpec) { 254 if (tileSpec.equals("wifi")) return new WifiTile(this); 255 else if (tileSpec.equals("bt")) return new BluetoothTile(this); 256 else if (tileSpec.equals("inversion")) return new ColorInversionTile(this); 257 else if (tileSpec.equals("cell")) return new CellularTile(this); 258 else if (tileSpec.equals("airplane")) return new AirplaneModeTile(this); 259 else if (tileSpec.equals("rotation")) return new RotationLockTile(this); 260 else if (tileSpec.equals("flashlight")) return new FlashlightTile(this); 261 else if (tileSpec.equals("location")) return new LocationTile(this); 262 else if (tileSpec.equals("cast")) return new CastTile(this); 263 else if (tileSpec.equals("hotspot")) return new HotspotTile(this); 264 else if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(this,tileSpec); 265 else throw new IllegalArgumentException("Bad tile spec: " + tileSpec); 266 } 267 268 private List<String> loadTileSpecs() { 269 final Resources res = mContext.getResources(); 270 final String defaultTileList = res.getString(R.string.quick_settings_tiles_default); 271 String tileList = Secure.getStringForUser(mContext.getContentResolver(), TILES_SETTING, 272 mUserTracker.getCurrentUserId()); 273 if (tileList == null) { 274 tileList = res.getString(R.string.quick_settings_tiles); 275 if (DEBUG) Log.d(TAG, "Loaded tile specs from config: " + tileList); 276 } else { 277 if (DEBUG) Log.d(TAG, "Loaded tile specs from setting: " + tileList); 278 } 279 final ArrayList<String> tiles = new ArrayList<String>(); 280 boolean addedDefault = false; 281 for (String tile : tileList.split(",")) { 282 tile = tile.trim(); 283 if (tile.isEmpty()) continue; 284 if (tile.equals("default")) { 285 if (!addedDefault) { 286 tiles.addAll(Arrays.asList(defaultTileList.split(","))); 287 addedDefault = true; 288 } 289 } else { 290 tiles.add(tile); 291 } 292 } 293 return tiles; 294 } 295 296 private class Observer extends ContentObserver { 297 private boolean mRegistered; 298 299 public Observer() { 300 super(new Handler(Looper.getMainLooper())); 301 } 302 303 public void register() { 304 if (mRegistered) { 305 mContext.getContentResolver().unregisterContentObserver(this); 306 } 307 mContext.getContentResolver().registerContentObserver(Secure.getUriFor(TILES_SETTING), 308 false, this, mUserTracker.getCurrentUserId()); 309 mRegistered = true; 310 } 311 312 @Override 313 public void onChange(boolean selfChange, Uri uri) { 314 recreateTiles(); 315 } 316 } 317 } 318