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.os.HandlerThread;
23import android.os.Looper;
24import android.os.Process;
25import android.util.Log;
26
27import com.android.systemui.R;
28import com.android.systemui.qs.QSTile;
29import com.android.systemui.qs.tiles.AirplaneModeTile;
30import com.android.systemui.qs.tiles.BluetoothTile;
31import com.android.systemui.qs.tiles.CastTile;
32import com.android.systemui.qs.tiles.CellularTile;
33import com.android.systemui.qs.tiles.ColorInversionTile;
34import com.android.systemui.qs.tiles.DndTile;
35import com.android.systemui.qs.tiles.FlashlightTile;
36import com.android.systemui.qs.tiles.HotspotTile;
37import com.android.systemui.qs.tiles.IntentTile;
38import com.android.systemui.qs.tiles.LocationTile;
39import com.android.systemui.qs.tiles.RotationLockTile;
40import com.android.systemui.qs.tiles.WifiTile;
41import com.android.systemui.statusbar.policy.BluetoothController;
42import com.android.systemui.statusbar.policy.CastController;
43import com.android.systemui.statusbar.policy.FlashlightController;
44import com.android.systemui.statusbar.policy.HotspotController;
45import com.android.systemui.statusbar.policy.KeyguardMonitor;
46import com.android.systemui.statusbar.policy.LocationController;
47import com.android.systemui.statusbar.policy.NetworkController;
48import com.android.systemui.statusbar.policy.RotationLockController;
49import com.android.systemui.statusbar.policy.SecurityController;
50import com.android.systemui.statusbar.policy.UserSwitcherController;
51import com.android.systemui.statusbar.policy.ZenModeController;
52import com.android.systemui.tuner.TunerService;
53import com.android.systemui.tuner.TunerService.Tunable;
54
55import java.util.ArrayList;
56import java.util.Arrays;
57import java.util.Collection;
58import java.util.LinkedHashMap;
59import java.util.List;
60import java.util.Map;
61
62/** Platform implementation of the quick settings tile host **/
63public 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