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