blob: df622b8f374c087d74684ae410f8b55b9711bb4e [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);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400393 BluetoothController getBluetoothController();
394 LocationController getLocationController();
395 RotationLockController getRotationLockController();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400396 NetworkController getNetworkController();
397 ZenModeController getZenModeController();
Jason Monk51e4dc02014-07-22 12:00:47 -0400398 HotspotController getHotspotController();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400399 CastController getCastController();
Adrian Roosb83777b2014-06-30 15:11:53 +0200400 FlashlightController getFlashlightController();
John Spurlock657c62c2014-07-22 12:18:09 -0400401 KeyguardMonitor getKeyguardMonitor();
Jason Monkabe19742015-09-29 09:47:06 -0400402 UserSwitcherController getUserSwitcherController();
403 UserInfoController getUserInfoController();
404 BatteryController getBatteryController();
Jason Monk66c89c12016-01-06 08:51:26 -0500405 TileServices getTileServices();
Jason Monk5dbd4aa2016-02-07 13:13:39 -0500406 NightModeController getNightModeController();
Jason Monkba2318e2015-12-08 09:04:23 -0500407 void removeTile(String tileSpec);
Jason Monkc3f42c12016-02-05 12:33:13 -0500408 ManagedProfileController getManagedProfileController();
John Spurlockbceed062014-08-10 18:04:16 -0400409
Jason Monk66c89c12016-01-06 08:51:26 -0500410
John Spurlockbceed062014-08-10 18:04:16 -0400411 public interface Callback {
412 void onTilesChanged();
413 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400414 }
415
John Spurlock2d695812014-10-30 13:25:21 -0400416 public static abstract class Icon {
417 abstract public Drawable getDrawable(Context context);
418
419 @Override
420 public int hashCode() {
421 return Icon.class.hashCode();
422 }
Jason Monkb53b6c52016-02-24 17:25:49 -0500423
424 public int getPadding() {
425 return 0;
426 }
John Spurlock2d695812014-10-30 13:25:21 -0400427 }
428
Jason Monk5db8a412015-10-21 15:16:23 -0700429 public static class DrawableIcon extends Icon {
430 protected final Drawable mDrawable;
431
432 public DrawableIcon(Drawable drawable) {
433 mDrawable = drawable;
434 }
435
436 @Override
437 public Drawable getDrawable(Context context) {
438 return mDrawable;
439 }
440 }
441
John Spurlock2d695812014-10-30 13:25:21 -0400442 public static class ResourceIcon extends Icon {
443 private static final SparseArray<Icon> ICONS = new SparseArray<Icon>();
444
Andrew Flynna478d702015-04-14 23:33:45 -0400445 protected final int mResId;
John Spurlock2d695812014-10-30 13:25:21 -0400446
447 private ResourceIcon(int resId) {
448 mResId = resId;
449 }
450
451 public static Icon get(int resId) {
452 Icon icon = ICONS.get(resId);
453 if (icon == null) {
454 icon = new ResourceIcon(resId);
455 ICONS.put(resId, icon);
456 }
457 return icon;
458 }
459
460 @Override
461 public Drawable getDrawable(Context context) {
Jason Monk66239fb2015-12-21 14:27:00 -0500462 return context.getDrawable(mResId);
John Spurlock2d695812014-10-30 13:25:21 -0400463 }
464
465 @Override
466 public boolean equals(Object o) {
467 return o instanceof ResourceIcon && ((ResourceIcon) o).mResId == mResId;
468 }
469
470 @Override
471 public String toString() {
472 return String.format("ResourceIcon[resId=0x%08x]", mResId);
473 }
474 }
475
476 protected class AnimationIcon extends ResourceIcon {
John Spurlock2d695812014-10-30 13:25:21 -0400477 public AnimationIcon(int resId) {
478 super(resId);
479 }
480
John Spurlock2d695812014-10-30 13:25:21 -0400481 @Override
482 public Drawable getDrawable(Context context) {
483 // workaround: get a clean state for every new AVD
Jason Monk66239fb2015-12-21 14:27:00 -0500484 return context.getDrawable(mResId).getConstantState().newDrawable();
John Spurlock2d695812014-10-30 13:25:21 -0400485 }
486 }
487
John Spurlockaf8d6c42014-05-07 17:49:08 -0400488 public static class State {
John Spurlock2d695812014-10-30 13:25:21 -0400489 public Icon icon;
Jason Monkbbadff82015-11-06 15:47:26 -0500490 public CharSequence label;
491 public CharSequence contentDescription;
492 public CharSequence dualLabelContentDescription;
Selim Cinek06d3bca2014-08-26 17:29:20 +0200493 public boolean autoMirrorDrawable = true;
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000494 public boolean disabledByPolicy;
495 public EnforcedAdmin enforcedAdmin;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400496
497 public boolean copyTo(State other) {
498 if (other == null) throw new IllegalArgumentException();
499 if (!other.getClass().equals(getClass())) throw new IllegalArgumentException();
Jason Monkba2318e2015-12-08 09:04:23 -0500500 final boolean changed = !Objects.equals(other.icon, icon)
John Spurlockaf8d6c42014-05-07 17:49:08 -0400501 || !Objects.equals(other.label, label)
Selim Cineke5557a92014-08-15 19:59:23 +0200502 || !Objects.equals(other.contentDescription, contentDescription)
Selim Cinek06d3bca2014-08-26 17:29:20 +0200503 || !Objects.equals(other.autoMirrorDrawable, autoMirrorDrawable)
Selim Cineke5557a92014-08-15 19:59:23 +0200504 || !Objects.equals(other.dualLabelContentDescription,
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000505 dualLabelContentDescription)
506 || !Objects.equals(other.disabledByPolicy, disabledByPolicy)
507 || !Objects.equals(other.enforcedAdmin, enforcedAdmin);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400508 other.icon = icon;
509 other.label = label;
510 other.contentDescription = contentDescription;
Selim Cineke5557a92014-08-15 19:59:23 +0200511 other.dualLabelContentDescription = dualLabelContentDescription;
Selim Cinek06d3bca2014-08-26 17:29:20 +0200512 other.autoMirrorDrawable = autoMirrorDrawable;
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000513 other.disabledByPolicy = disabledByPolicy;
Sudheer Shanka1c09db22016-01-18 11:18:35 +0000514 if (enforcedAdmin == null) {
515 other.enforcedAdmin = null;
516 } else if (other.enforcedAdmin == null) {
517 other.enforcedAdmin = new EnforcedAdmin(enforcedAdmin);
518 } else {
519 enforcedAdmin.copyTo(other.enforcedAdmin);
520 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400521 return changed;
522 }
523
524 @Override
525 public String toString() {
526 return toStringBuilder().toString();
527 }
528
529 protected StringBuilder toStringBuilder() {
John Spurlock2d695812014-10-30 13:25:21 -0400530 final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('[');
John Spurlockaf8d6c42014-05-07 17:49:08 -0400531 sb.append(",icon=").append(icon);
532 sb.append(",label=").append(label);
533 sb.append(",contentDescription=").append(contentDescription);
Selim Cineke5557a92014-08-15 19:59:23 +0200534 sb.append(",dualLabelContentDescription=").append(dualLabelContentDescription);
Selim Cinek06d3bca2014-08-26 17:29:20 +0200535 sb.append(",autoMirrorDrawable=").append(autoMirrorDrawable);
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000536 sb.append(",disabledByPolicy=").append(disabledByPolicy);
537 sb.append(",enforcedAdmin=").append(enforcedAdmin);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400538 return sb.append(']');
539 }
540 }
541
542 public static class BooleanState extends State {
543 public boolean value;
544
545 @Override
546 public boolean copyTo(State other) {
547 final BooleanState o = (BooleanState) other;
548 final boolean changed = super.copyTo(other) || o.value != value;
549 o.value = value;
550 return changed;
551 }
552
553 @Override
554 protected StringBuilder toStringBuilder() {
555 final StringBuilder rt = super.toStringBuilder();
556 rt.insert(rt.length() - 1, ",value=" + value);
557 return rt;
558 }
559 }
560
561 public static final class SignalState extends State {
562 public boolean enabled;
563 public boolean connected;
564 public boolean activityIn;
565 public boolean activityOut;
566 public int overlayIconId;
John Spurlock899f4392014-06-11 10:59:11 -0400567 public boolean filter;
Jorim Jaggic737b9b2014-09-08 23:57:20 +0200568 public boolean isOverlayIconWide;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400569
570 @Override
571 public boolean copyTo(State other) {
572 final SignalState o = (SignalState) other;
573 final boolean changed = o.enabled != enabled
574 || o.connected != connected || o.activityIn != activityIn
575 || o.activityOut != activityOut
Jorim Jaggic737b9b2014-09-08 23:57:20 +0200576 || o.overlayIconId != overlayIconId
577 || o.isOverlayIconWide != isOverlayIconWide;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400578 o.enabled = enabled;
579 o.connected = connected;
580 o.activityIn = activityIn;
581 o.activityOut = activityOut;
582 o.overlayIconId = overlayIconId;
John Spurlock899f4392014-06-11 10:59:11 -0400583 o.filter = filter;
Jorim Jaggic737b9b2014-09-08 23:57:20 +0200584 o.isOverlayIconWide = isOverlayIconWide;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400585 return super.copyTo(other) || changed;
586 }
587
588 @Override
589 protected StringBuilder toStringBuilder() {
590 final StringBuilder rt = super.toStringBuilder();
591 rt.insert(rt.length() - 1, ",enabled=" + enabled);
592 rt.insert(rt.length() - 1, ",connected=" + connected);
593 rt.insert(rt.length() - 1, ",activityIn=" + activityIn);
594 rt.insert(rt.length() - 1, ",activityOut=" + activityOut);
595 rt.insert(rt.length() - 1, ",overlayIconId=" + overlayIconId);
John Spurlock899f4392014-06-11 10:59:11 -0400596 rt.insert(rt.length() - 1, ",filter=" + filter);
Jorim Jaggic737b9b2014-09-08 23:57:20 +0200597 rt.insert(rt.length() - 1, ",wideOverlayIcon=" + isOverlayIconWide);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400598 return rt;
599 }
600 }
601}