blob: 42e98aa1ee0151419f22ba0c0420e5564cdb585f [file] [log] [blame]
John Spurlockaf8d6c42014-05-07 17:49:08 -04001/*
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.qs;
18
Sudheer Shankab6fc9312016-01-27 19:59:03 +000019import android.app.ActivityManager;
Adrian Roos62692b22015-09-11 17:46:23 -070020import android.app.PendingIntent;
John Spurlockaf8d6c42014-05-07 17:49:08 -040021import android.content.Context;
22import android.content.Intent;
John Spurlock444eb2e2014-05-14 13:32:14 -040023import android.graphics.drawable.Drawable;
John Spurlockaf8d6c42014-05-07 17:49:08 -040024import android.os.Handler;
25import android.os.Looper;
26import android.os.Message;
27import android.util.Log;
John Spurlock2d695812014-10-30 13:25:21 -040028import android.util.SparseArray;
John Spurlockaf8d6c42014-05-07 17:49:08 -040029import android.view.View;
30import android.view.ViewGroup;
Sudheer Shanka1c7cda82015-12-31 14:46:02 +000031
32import com.android.settingslib.RestrictedLockUtils;
John Spurlockaf8d6c42014-05-07 17:49:08 -040033import com.android.systemui.qs.QSTile.State;
Jason Monk66c89c12016-01-06 08:51:26 -050034import com.android.systemui.qs.external.TileServices;
Jason Monkc3f42c12016-02-05 12:33:13 -050035import com.android.systemui.statusbar.phone.ManagedProfileController;
Jason Monk66239fb2015-12-21 14:27:00 -050036import com.android.systemui.statusbar.policy.BatteryController;
37import com.android.systemui.statusbar.policy.BluetoothController;
38import com.android.systemui.statusbar.policy.CastController;
Jason Monk5dbd4aa2016-02-07 13:13:39 -050039import com.android.systemui.statusbar.policy.NightModeController;
Jason Monk66239fb2015-12-21 14:27:00 -050040import com.android.systemui.statusbar.policy.FlashlightController;
41import com.android.systemui.statusbar.policy.HotspotController;
42import com.android.systemui.statusbar.policy.KeyguardMonitor;
43import com.android.systemui.statusbar.policy.Listenable;
44import com.android.systemui.statusbar.policy.LocationController;
45import com.android.systemui.statusbar.policy.NetworkController;
46import com.android.systemui.statusbar.policy.RotationLockController;
47import com.android.systemui.statusbar.policy.UserInfoController;
48import com.android.systemui.statusbar.policy.UserSwitcherController;
49import com.android.systemui.statusbar.policy.ZenModeController;
John Spurlockaf8d6c42014-05-07 17:49:08 -040050
Jason Monkca894a02016-01-12 15:30:22 -050051import java.util.ArrayList;
John Spurlockbceed062014-08-10 18:04:16 -040052import java.util.Collection;
John Spurlockaf8d6c42014-05-07 17:49:08 -040053import java.util.Objects;
54
Sudheer Shanka1c7cda82015-12-31 14:46:02 +000055import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
56
John Spurlockaf8d6c42014-05-07 17:49:08 -040057/**
58 * Base quick-settings tile, extend this to create a new tile.
59 *
60 * State management done on a looper provided by the host. Tiles should update state in
61 * handleUpdateState. Callbacks affecting state should use refreshState to trigger another
62 * state update pass on tile looper.
63 */
John Spurlockccb6b9a2014-05-17 15:54:40 -040064public abstract class QSTile<TState extends State> implements Listenable {
Jason Monkbbadff82015-11-06 15:47:26 -050065 protected final String TAG = "Tile." + getClass().getSimpleName();
66 protected static final boolean DEBUG = Log.isLoggable("Tile", Log.DEBUG);
John Spurlockaf8d6c42014-05-07 17:49:08 -040067
68 protected final Host mHost;
69 protected final Context mContext;
70 protected final H mHandler;
John Spurlock76c43b92014-05-13 21:10:51 -040071 protected final Handler mUiHandler = new Handler(Looper.getMainLooper());
John Spurlockaf8d6c42014-05-07 17:49:08 -040072
Jason Monkca894a02016-01-12 15:30:22 -050073 private final ArrayList<Callback> mCallbacks = new ArrayList<>();
Jason Monka758ba62015-07-14 12:29:28 -040074 protected TState mState = newTileState();
75 private TState mTmpState = newTileState();
Selim Cinek4fda7b22014-08-18 22:07:25 +020076 private boolean mAnnounceNextStateChange;
John Spurlockaf8d6c42014-05-07 17:49:08 -040077
Jason Monkbd6dbb02015-09-03 15:46:25 -040078 private String mTileSpec;
79
Jason Monk62b63a02016-02-02 15:15:31 -050080 public abstract TState newTileState();
Chris Wren9e7283f2015-05-08 17:23:47 -040081 abstract protected void handleClick();
John Spurlockaf8d6c42014-05-07 17:49:08 -040082 abstract protected void handleUpdateState(TState state, Object arg);
83
Chris Wren457a21c2015-05-06 17:50:34 -040084 /**
85 * Declare the category of this tile.
86 *
Chris Wrenf6e9228b2016-01-26 18:04:35 -050087 * Categories are defined in {@link com.android.internal.logging.MetricsProto.MetricsEvent}
88 * by editing frameworks/base/proto/src/metrics_constants.proto.
Chris Wren457a21c2015-05-06 17:50:34 -040089 */
90 abstract public int getMetricsCategory();
91
John Spurlockaf8d6c42014-05-07 17:49:08 -040092 protected QSTile(Host host) {
93 mHost = host;
94 mContext = host.getContext();
95 mHandler = new H(host.getLooper());
96 }
Jason Monkbd6dbb02015-09-03 15:46:25 -040097
98 public String getTileSpec() {
99 return mTileSpec;
100 }
101
102 public void setTileSpec(String tileSpec) {
103 mTileSpec = tileSpec;
104 }
105
John Spurlockaf8d6c42014-05-07 17:49:08 -0400106 public Host getHost() {
107 return mHost;
108 }
109
Jason Monkdc35dcb2015-12-04 16:36:15 -0500110 public QSIconView createTileView(Context context) {
111 return new QSIconView(context);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400112 }
113
John Spurlock7f8f22a2014-07-02 18:54:17 -0400114 public DetailAdapter getDetailAdapter() {
John Spurlockaf8d6c42014-05-07 17:49:08 -0400115 return null; // optional
116 }
117
Jason Monkc3f42c12016-02-05 12:33:13 -0500118 /**
119 * Is a startup check whether this device currently supports this tile.
120 * Should not be used to conditionally hide tiles. Only checked on tile
121 * creation or whether should be shown in edit screen.
122 */
123 public boolean isAvailable() {
124 return true;
125 }
126
John Spurlock7f8f22a2014-07-02 18:54:17 -0400127 public interface DetailAdapter {
Jason Monkc06fbb12016-01-08 14:12:18 -0500128 CharSequence getTitle();
John Spurlock7f8f22a2014-07-02 18:54:17 -0400129 Boolean getToggleState();
130 View createDetailView(Context context, View convertView, ViewGroup parent);
131 Intent getSettingsIntent();
132 void setToggleState(boolean state);
Chris Wren457a21c2015-05-06 17:50:34 -0400133 int getMetricsCategory();
John Spurlock7f8f22a2014-07-02 18:54:17 -0400134 }
135
John Spurlockaf8d6c42014-05-07 17:49:08 -0400136 // safe to call from any thread
137
Jason Monkca894a02016-01-12 15:30:22 -0500138 public void addCallback(Callback callback) {
139 mHandler.obtainMessage(H.ADD_CALLBACK, callback).sendToTarget();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400140 }
141
Jason Monk9d02a432016-01-20 16:33:46 -0500142 public void removeCallbacks() {
143 mHandler.sendEmptyMessage(H.REMOVE_CALLBACKS);
144 }
145
John Spurlockaf8d6c42014-05-07 17:49:08 -0400146 public void click() {
147 mHandler.sendEmptyMessage(H.CLICK);
148 }
149
150 public void secondaryClick() {
151 mHandler.sendEmptyMessage(H.SECONDARY_CLICK);
152 }
153
John Spurlockc247b8f2014-11-06 23:06:25 -0500154 public void longClick() {
155 mHandler.sendEmptyMessage(H.LONG_CLICK);
156 }
157
John Spurlockaf8d6c42014-05-07 17:49:08 -0400158 public void showDetail(boolean show) {
159 mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0).sendToTarget();
160 }
161
Jason Monk5db8a412015-10-21 15:16:23 -0700162 public final void refreshState() {
John Spurlockaf8d6c42014-05-07 17:49:08 -0400163 refreshState(null);
164 }
165
166 protected final void refreshState(Object arg) {
167 mHandler.obtainMessage(H.REFRESH_STATE, arg).sendToTarget();
168 }
169
Jason Monka758ba62015-07-14 12:29:28 -0400170 public final void clearState() {
171 mHandler.sendEmptyMessage(H.CLEAR_STATE);
172 }
173
John Spurlockaf8d6c42014-05-07 17:49:08 -0400174 public void userSwitch(int newUserId) {
Adrian Roos32d88e82014-09-24 17:08:22 +0200175 mHandler.obtainMessage(H.USER_SWITCH, newUserId, 0).sendToTarget();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400176 }
177
John Spurlock7f8f22a2014-07-02 18:54:17 -0400178 public void fireToggleStateChanged(boolean state) {
179 mHandler.obtainMessage(H.TOGGLE_STATE_CHANGED, state ? 1 : 0, 0).sendToTarget();
180 }
181
John Spurlock486b78e2014-07-07 08:37:56 -0400182 public void fireScanStateChanged(boolean state) {
183 mHandler.obtainMessage(H.SCAN_STATE_CHANGED, state ? 1 : 0, 0).sendToTarget();
184 }
185
John Spurlockbceed062014-08-10 18:04:16 -0400186 public void destroy() {
187 mHandler.sendEmptyMessage(H.DESTROY);
188 }
189
190 public TState getState() {
191 return mState;
192 }
193
Jason Monk0d6a1c42015-04-20 16:38:51 -0400194 public void setDetailListening(boolean listening) {
195 // optional
196 }
197
John Spurlockaf8d6c42014-05-07 17:49:08 -0400198 // call only on tile worker looper
199
Jason Monkca894a02016-01-12 15:30:22 -0500200 private void handleAddCallback(Callback callback) {
201 mCallbacks.add(callback);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400202 handleRefreshState(null);
203 }
204
Jason Monk9d02a432016-01-20 16:33:46 -0500205 private void handleRemoveCallbacks() {
206 mCallbacks.clear();
207 }
208
John Spurlockaf8d6c42014-05-07 17:49:08 -0400209 protected void handleSecondaryClick() {
Jason Monkdc35dcb2015-12-04 16:36:15 -0500210 // Default to normal click.
211 handleClick();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400212 }
213
John Spurlockc247b8f2014-11-06 23:06:25 -0500214 protected void handleLongClick() {
Jason Monk76c67aa2016-02-19 14:49:42 -0500215 mHost.startActivityDismissingKeyguard(getLongClickIntent());
John Spurlockc247b8f2014-11-06 23:06:25 -0500216 }
217
Jason Monk76c67aa2016-02-19 14:49:42 -0500218 public abstract Intent getLongClickIntent();
219
Jason Monka758ba62015-07-14 12:29:28 -0400220 protected void handleClearState() {
221 mTmpState = newTileState();
222 mState = newTileState();
223 }
224
John Spurlockaf8d6c42014-05-07 17:49:08 -0400225 protected void handleRefreshState(Object arg) {
226 handleUpdateState(mTmpState, arg);
227 final boolean changed = mTmpState.copyTo(mState);
228 if (changed) {
229 handleStateChanged();
230 }
231 }
232
233 private void handleStateChanged() {
Selim Cinek4fda7b22014-08-18 22:07:25 +0200234 boolean delayAnnouncement = shouldAnnouncementBeDelayed();
Jason Monkca894a02016-01-12 15:30:22 -0500235 if (mCallbacks.size() != 0) {
236 for (int i = 0; i < mCallbacks.size(); i++) {
237 mCallbacks.get(i).onStateChanged(mState);
238 }
Selim Cinek4fda7b22014-08-18 22:07:25 +0200239 if (mAnnounceNextStateChange && !delayAnnouncement) {
240 String announcement = composeChangeAnnouncement();
241 if (announcement != null) {
Jason Monkca894a02016-01-12 15:30:22 -0500242 mCallbacks.get(0).onAnnouncementRequested(announcement);
Selim Cinek4fda7b22014-08-18 22:07:25 +0200243 }
244 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400245 }
Selim Cinek4fda7b22014-08-18 22:07:25 +0200246 mAnnounceNextStateChange = mAnnounceNextStateChange && delayAnnouncement;
247 }
248
249 protected boolean shouldAnnouncementBeDelayed() {
250 return false;
251 }
252
253 protected String composeChangeAnnouncement() {
254 return null;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400255 }
256
257 private void handleShowDetail(boolean show) {
Jason Monkca894a02016-01-12 15:30:22 -0500258 for (int i = 0; i < mCallbacks.size(); i++) {
259 mCallbacks.get(i).onShowDetail(show);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400260 }
261 }
262
John Spurlock7f8f22a2014-07-02 18:54:17 -0400263 private void handleToggleStateChanged(boolean state) {
Jason Monkca894a02016-01-12 15:30:22 -0500264 for (int i = 0; i < mCallbacks.size(); i++) {
265 mCallbacks.get(i).onToggleStateChanged(state);
John Spurlock7f8f22a2014-07-02 18:54:17 -0400266 }
267 }
268
John Spurlock486b78e2014-07-07 08:37:56 -0400269 private void handleScanStateChanged(boolean state) {
Jason Monkca894a02016-01-12 15:30:22 -0500270 for (int i = 0; i < mCallbacks.size(); i++) {
271 mCallbacks.get(i).onScanStateChanged(state);
John Spurlock486b78e2014-07-07 08:37:56 -0400272 }
273 }
274
John Spurlockaf8d6c42014-05-07 17:49:08 -0400275 protected void handleUserSwitch(int newUserId) {
276 handleRefreshState(null);
277 }
278
John Spurlockbceed062014-08-10 18:04:16 -0400279 protected void handleDestroy() {
280 setListening(false);
Jason Monkca894a02016-01-12 15:30:22 -0500281 mCallbacks.clear();
John Spurlockbceed062014-08-10 18:04:16 -0400282 }
283
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000284 protected void checkIfRestrictionEnforcedByAdminOnly(State state, String userRestriction) {
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000285 EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext,
Sudheer Shankab6fc9312016-01-27 19:59:03 +0000286 userRestriction, ActivityManager.getCurrentUser());
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000287 if (admin != null && !RestrictedLockUtils.hasBaseUserRestriction(mContext,
288 userRestriction, ActivityManager.getCurrentUser())) {
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000289 state.disabledByPolicy = true;
290 state.enforcedAdmin = admin;
291 } else {
292 state.disabledByPolicy = false;
293 state.enforcedAdmin = null;
294 }
295 }
296
John Spurlockaf8d6c42014-05-07 17:49:08 -0400297 protected final class H extends Handler {
Jason Monkca894a02016-01-12 15:30:22 -0500298 private static final int ADD_CALLBACK = 1;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400299 private static final int CLICK = 2;
300 private static final int SECONDARY_CLICK = 3;
John Spurlockc247b8f2014-11-06 23:06:25 -0500301 private static final int LONG_CLICK = 4;
302 private static final int REFRESH_STATE = 5;
303 private static final int SHOW_DETAIL = 6;
304 private static final int USER_SWITCH = 7;
305 private static final int TOGGLE_STATE_CHANGED = 8;
306 private static final int SCAN_STATE_CHANGED = 9;
307 private static final int DESTROY = 10;
Jason Monka758ba62015-07-14 12:29:28 -0400308 private static final int CLEAR_STATE = 11;
Jason Monk9d02a432016-01-20 16:33:46 -0500309 private static final int REMOVE_CALLBACKS = 12;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400310
311 private H(Looper looper) {
312 super(looper);
313 }
314
315 @Override
316 public void handleMessage(Message msg) {
317 String name = null;
318 try {
Jason Monkca894a02016-01-12 15:30:22 -0500319 if (msg.what == ADD_CALLBACK) {
320 name = "handleAddCallback";
Jason Monk9d02a432016-01-20 16:33:46 -0500321 handleAddCallback((QSTile.Callback) msg.obj);
322 } else if (msg.what == REMOVE_CALLBACKS) {
323 name = "handleRemoveCallbacks";
324 handleRemoveCallbacks();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400325 } else if (msg.what == CLICK) {
326 name = "handleClick";
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000327 if (mState.disabledByPolicy) {
328 Intent intent = RestrictedLockUtils.getShowAdminSupportDetailsIntent(
329 mContext, mState.enforcedAdmin);
330 mHost.startActivityDismissingKeyguard(intent);
331 } else {
332 mAnnounceNextStateChange = true;
333 handleClick();
334 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400335 } else if (msg.what == SECONDARY_CLICK) {
336 name = "handleSecondaryClick";
337 handleSecondaryClick();
John Spurlockc247b8f2014-11-06 23:06:25 -0500338 } else if (msg.what == LONG_CLICK) {
339 name = "handleLongClick";
340 handleLongClick();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400341 } else if (msg.what == REFRESH_STATE) {
342 name = "handleRefreshState";
343 handleRefreshState(msg.obj);
344 } else if (msg.what == SHOW_DETAIL) {
345 name = "handleShowDetail";
346 handleShowDetail(msg.arg1 != 0);
347 } else if (msg.what == USER_SWITCH) {
348 name = "handleUserSwitch";
349 handleUserSwitch(msg.arg1);
John Spurlock7f8f22a2014-07-02 18:54:17 -0400350 } else if (msg.what == TOGGLE_STATE_CHANGED) {
351 name = "handleToggleStateChanged";
352 handleToggleStateChanged(msg.arg1 != 0);
John Spurlock486b78e2014-07-07 08:37:56 -0400353 } else if (msg.what == SCAN_STATE_CHANGED) {
354 name = "handleScanStateChanged";
355 handleScanStateChanged(msg.arg1 != 0);
John Spurlockbceed062014-08-10 18:04:16 -0400356 } else if (msg.what == DESTROY) {
357 name = "handleDestroy";
358 handleDestroy();
Jason Monka758ba62015-07-14 12:29:28 -0400359 } else if (msg.what == CLEAR_STATE) {
360 name = "handleClearState";
361 handleClearState();
John Spurlockbceed062014-08-10 18:04:16 -0400362 } else {
363 throw new IllegalArgumentException("Unknown msg: " + msg.what);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400364 }
365 } catch (Throwable t) {
366 final String error = "Error in " + name;
367 Log.w(TAG, error, t);
368 mHost.warn(error, t);
369 }
370 }
371 }
372
373 public interface Callback {
374 void onStateChanged(State state);
375 void onShowDetail(boolean show);
John Spurlock7f8f22a2014-07-02 18:54:17 -0400376 void onToggleStateChanged(boolean state);
John Spurlock486b78e2014-07-07 08:37:56 -0400377 void onScanStateChanged(boolean state);
Selim Cinek4fda7b22014-08-18 22:07:25 +0200378 void onAnnouncementRequested(CharSequence announcement);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400379 }
380
381 public interface Host {
Jason Monkee43cdf2015-06-19 14:20:46 -0400382 void startActivityDismissingKeyguard(Intent intent);
Adrian Roos62692b22015-09-11 17:46:23 -0700383 void startActivityDismissingKeyguard(PendingIntent intent);
Jason Monkba2318e2015-12-08 09:04:23 -0500384 void startRunnableDismissingKeyguard(Runnable runnable);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400385 void warn(String message, Throwable t);
386 void collapsePanels();
Jason Monkca894a02016-01-12 15:30:22 -0500387 void animateExpandQS();
Jason Monkba2318e2015-12-08 09:04:23 -0500388 void openPanels();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400389 Looper getLooper();
390 Context getContext();
John Spurlockbceed062014-08-10 18:04:16 -0400391 Collection<QSTile<?>> getTiles();
Jason Monkbbadff82015-11-06 15:47:26 -0500392 void addCallback(Callback callback);
Jason Monk46dbfb42016-02-25 14:59:20 -0500393 void removeCallback(Callback callback);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400394 BluetoothController getBluetoothController();
395 LocationController getLocationController();
396 RotationLockController getRotationLockController();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400397 NetworkController getNetworkController();
398 ZenModeController getZenModeController();
Jason Monk51e4dc02014-07-22 12:00:47 -0400399 HotspotController getHotspotController();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400400 CastController getCastController();
Adrian Roosb83777b2014-06-30 15:11:53 +0200401 FlashlightController getFlashlightController();
John Spurlock657c62c2014-07-22 12:18:09 -0400402 KeyguardMonitor getKeyguardMonitor();
Jason Monkabe19742015-09-29 09:47:06 -0400403 UserSwitcherController getUserSwitcherController();
404 UserInfoController getUserInfoController();
405 BatteryController getBatteryController();
Jason Monk66c89c12016-01-06 08:51:26 -0500406 TileServices getTileServices();
Jason Monk5dbd4aa2016-02-07 13:13:39 -0500407 NightModeController getNightModeController();
Jason Monkba2318e2015-12-08 09:04:23 -0500408 void removeTile(String tileSpec);
Jason Monkc3f42c12016-02-05 12:33:13 -0500409 ManagedProfileController getManagedProfileController();
John Spurlockbceed062014-08-10 18:04:16 -0400410
Jason Monk66c89c12016-01-06 08:51:26 -0500411
John Spurlockbceed062014-08-10 18:04:16 -0400412 public interface Callback {
413 void onTilesChanged();
414 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400415 }
416
John Spurlock2d695812014-10-30 13:25:21 -0400417 public static abstract class Icon {
418 abstract public Drawable getDrawable(Context context);
419
Jason Monk1aec93f2016-03-01 09:39:30 -0500420 public Drawable getInvisibleDrawable(Context context) {
421 return getDrawable(context);
422 }
423
John Spurlock2d695812014-10-30 13:25:21 -0400424 @Override
425 public int hashCode() {
426 return Icon.class.hashCode();
427 }
Jason Monkb53b6c52016-02-24 17:25:49 -0500428
429 public int getPadding() {
430 return 0;
431 }
John Spurlock2d695812014-10-30 13:25:21 -0400432 }
433
Jason Monk5db8a412015-10-21 15:16:23 -0700434 public static class DrawableIcon extends Icon {
435 protected final Drawable mDrawable;
436
437 public DrawableIcon(Drawable drawable) {
438 mDrawable = drawable;
439 }
440
441 @Override
442 public Drawable getDrawable(Context context) {
443 return mDrawable;
444 }
Jason Monk1aec93f2016-03-01 09:39:30 -0500445
446 @Override
447 public Drawable getInvisibleDrawable(Context context) {
448 return mDrawable;
449 }
Jason Monk5db8a412015-10-21 15:16:23 -0700450 }
451
John Spurlock2d695812014-10-30 13:25:21 -0400452 public static class ResourceIcon extends Icon {
453 private static final SparseArray<Icon> ICONS = new SparseArray<Icon>();
454
Andrew Flynna478d702015-04-14 23:33:45 -0400455 protected final int mResId;
John Spurlock2d695812014-10-30 13:25:21 -0400456
457 private ResourceIcon(int resId) {
458 mResId = resId;
459 }
460
461 public static Icon get(int resId) {
462 Icon icon = ICONS.get(resId);
463 if (icon == null) {
464 icon = new ResourceIcon(resId);
465 ICONS.put(resId, icon);
466 }
467 return icon;
468 }
469
470 @Override
471 public Drawable getDrawable(Context context) {
Jason Monk66239fb2015-12-21 14:27:00 -0500472 return context.getDrawable(mResId);
John Spurlock2d695812014-10-30 13:25:21 -0400473 }
474
475 @Override
Jason Monk1aec93f2016-03-01 09:39:30 -0500476 public Drawable getInvisibleDrawable(Context context) {
477 return context.getDrawable(mResId);
478 }
479
480 @Override
John Spurlock2d695812014-10-30 13:25:21 -0400481 public boolean equals(Object o) {
482 return o instanceof ResourceIcon && ((ResourceIcon) o).mResId == mResId;
483 }
484
485 @Override
486 public String toString() {
487 return String.format("ResourceIcon[resId=0x%08x]", mResId);
488 }
489 }
490
491 protected class AnimationIcon extends ResourceIcon {
Jason Monk1aec93f2016-03-01 09:39:30 -0500492 private final int mAnimatedResId;
493
494 public AnimationIcon(int resId, int staticResId) {
495 super(staticResId);
496 mAnimatedResId = resId;
John Spurlock2d695812014-10-30 13:25:21 -0400497 }
498
John Spurlock2d695812014-10-30 13:25:21 -0400499 @Override
500 public Drawable getDrawable(Context context) {
501 // workaround: get a clean state for every new AVD
Jason Monk1aec93f2016-03-01 09:39:30 -0500502 return context.getDrawable(mAnimatedResId).getConstantState().newDrawable();
John Spurlock2d695812014-10-30 13:25:21 -0400503 }
504 }
505
John Spurlockaf8d6c42014-05-07 17:49:08 -0400506 public static class State {
John Spurlock2d695812014-10-30 13:25:21 -0400507 public Icon icon;
Jason Monkbbadff82015-11-06 15:47:26 -0500508 public CharSequence label;
509 public CharSequence contentDescription;
510 public CharSequence dualLabelContentDescription;
Selim Cinek06d3bca2014-08-26 17:29:20 +0200511 public boolean autoMirrorDrawable = true;
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000512 public boolean disabledByPolicy;
513 public EnforcedAdmin enforcedAdmin;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400514
515 public boolean copyTo(State other) {
516 if (other == null) throw new IllegalArgumentException();
517 if (!other.getClass().equals(getClass())) throw new IllegalArgumentException();
Jason Monkba2318e2015-12-08 09:04:23 -0500518 final boolean changed = !Objects.equals(other.icon, icon)
John Spurlockaf8d6c42014-05-07 17:49:08 -0400519 || !Objects.equals(other.label, label)
Selim Cineke5557a92014-08-15 19:59:23 +0200520 || !Objects.equals(other.contentDescription, contentDescription)
Selim Cinek06d3bca2014-08-26 17:29:20 +0200521 || !Objects.equals(other.autoMirrorDrawable, autoMirrorDrawable)
Selim Cineke5557a92014-08-15 19:59:23 +0200522 || !Objects.equals(other.dualLabelContentDescription,
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000523 dualLabelContentDescription)
524 || !Objects.equals(other.disabledByPolicy, disabledByPolicy)
525 || !Objects.equals(other.enforcedAdmin, enforcedAdmin);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400526 other.icon = icon;
527 other.label = label;
528 other.contentDescription = contentDescription;
Selim Cineke5557a92014-08-15 19:59:23 +0200529 other.dualLabelContentDescription = dualLabelContentDescription;
Selim Cinek06d3bca2014-08-26 17:29:20 +0200530 other.autoMirrorDrawable = autoMirrorDrawable;
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000531 other.disabledByPolicy = disabledByPolicy;
Sudheer Shanka1c09db22016-01-18 11:18:35 +0000532 if (enforcedAdmin == null) {
533 other.enforcedAdmin = null;
534 } else if (other.enforcedAdmin == null) {
535 other.enforcedAdmin = new EnforcedAdmin(enforcedAdmin);
536 } else {
537 enforcedAdmin.copyTo(other.enforcedAdmin);
538 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400539 return changed;
540 }
541
542 @Override
543 public String toString() {
544 return toStringBuilder().toString();
545 }
546
547 protected StringBuilder toStringBuilder() {
John Spurlock2d695812014-10-30 13:25:21 -0400548 final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('[');
John Spurlockaf8d6c42014-05-07 17:49:08 -0400549 sb.append(",icon=").append(icon);
550 sb.append(",label=").append(label);
551 sb.append(",contentDescription=").append(contentDescription);
Selim Cineke5557a92014-08-15 19:59:23 +0200552 sb.append(",dualLabelContentDescription=").append(dualLabelContentDescription);
Selim Cinek06d3bca2014-08-26 17:29:20 +0200553 sb.append(",autoMirrorDrawable=").append(autoMirrorDrawable);
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000554 sb.append(",disabledByPolicy=").append(disabledByPolicy);
555 sb.append(",enforcedAdmin=").append(enforcedAdmin);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400556 return sb.append(']');
557 }
558 }
559
560 public static class BooleanState extends State {
561 public boolean value;
562
563 @Override
564 public boolean copyTo(State other) {
565 final BooleanState o = (BooleanState) other;
566 final boolean changed = super.copyTo(other) || o.value != value;
567 o.value = value;
568 return changed;
569 }
570
571 @Override
572 protected StringBuilder toStringBuilder() {
573 final StringBuilder rt = super.toStringBuilder();
574 rt.insert(rt.length() - 1, ",value=" + value);
575 return rt;
576 }
577 }
578
579 public static final class SignalState extends State {
580 public boolean enabled;
581 public boolean connected;
582 public boolean activityIn;
583 public boolean activityOut;
584 public int overlayIconId;
John Spurlock899f4392014-06-11 10:59:11 -0400585 public boolean filter;
Jorim Jaggic737b9b2014-09-08 23:57:20 +0200586 public boolean isOverlayIconWide;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400587
588 @Override
589 public boolean copyTo(State other) {
590 final SignalState o = (SignalState) other;
591 final boolean changed = o.enabled != enabled
592 || o.connected != connected || o.activityIn != activityIn
593 || o.activityOut != activityOut
Jorim Jaggic737b9b2014-09-08 23:57:20 +0200594 || o.overlayIconId != overlayIconId
595 || o.isOverlayIconWide != isOverlayIconWide;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400596 o.enabled = enabled;
597 o.connected = connected;
598 o.activityIn = activityIn;
599 o.activityOut = activityOut;
600 o.overlayIconId = overlayIconId;
John Spurlock899f4392014-06-11 10:59:11 -0400601 o.filter = filter;
Jorim Jaggic737b9b2014-09-08 23:57:20 +0200602 o.isOverlayIconWide = isOverlayIconWide;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400603 return super.copyTo(other) || changed;
604 }
605
606 @Override
607 protected StringBuilder toStringBuilder() {
608 final StringBuilder rt = super.toStringBuilder();
609 rt.insert(rt.length() - 1, ",enabled=" + enabled);
610 rt.insert(rt.length() - 1, ",connected=" + connected);
611 rt.insert(rt.length() - 1, ",activityIn=" + activityIn);
612 rt.insert(rt.length() - 1, ",activityOut=" + activityOut);
613 rt.insert(rt.length() - 1, ",overlayIconId=" + overlayIconId);
John Spurlock899f4392014-06-11 10:59:11 -0400614 rt.insert(rt.length() - 1, ",filter=" + filter);
Jorim Jaggic737b9b2014-09-08 23:57:20 +0200615 rt.insert(rt.length() - 1, ",wideOverlayIcon=" + isOverlayIconWide);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400616 return rt;
617 }
618 }
619}