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