blob: 3e32905d64cd76b41b23f81ebaa76f3d2f74c925 [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
Jason Monk96defbe2016-03-29 16:51:03 -040032import com.android.internal.logging.MetricsLogger;
33import com.android.internal.logging.MetricsProto.MetricsEvent;
Sudheer Shanka1c7cda82015-12-31 14:46:02 +000034import com.android.settingslib.RestrictedLockUtils;
John Spurlockaf8d6c42014-05-07 17:49:08 -040035import com.android.systemui.qs.QSTile.State;
Jason Monk66c89c12016-01-06 08:51:26 -050036import com.android.systemui.qs.external.TileServices;
Jason Monkc3f42c12016-02-05 12:33:13 -050037import com.android.systemui.statusbar.phone.ManagedProfileController;
Jason Monk66239fb2015-12-21 14:27:00 -050038import com.android.systemui.statusbar.policy.BatteryController;
39import com.android.systemui.statusbar.policy.BluetoothController;
40import com.android.systemui.statusbar.policy.CastController;
Jason Monk5dbd4aa2016-02-07 13:13:39 -050041import com.android.systemui.statusbar.policy.NightModeController;
Jason Monk66239fb2015-12-21 14:27:00 -050042import com.android.systemui.statusbar.policy.FlashlightController;
43import com.android.systemui.statusbar.policy.HotspotController;
44import com.android.systemui.statusbar.policy.KeyguardMonitor;
45import com.android.systemui.statusbar.policy.Listenable;
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.UserInfoController;
50import com.android.systemui.statusbar.policy.UserSwitcherController;
51import com.android.systemui.statusbar.policy.ZenModeController;
John Spurlockaf8d6c42014-05-07 17:49:08 -040052
Jason Monkca894a02016-01-12 15:30:22 -050053import java.util.ArrayList;
John Spurlockbceed062014-08-10 18:04:16 -040054import java.util.Collection;
John Spurlockaf8d6c42014-05-07 17:49:08 -040055import java.util.Objects;
56
Sudheer Shanka1c7cda82015-12-31 14:46:02 +000057import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
58
John Spurlockaf8d6c42014-05-07 17:49:08 -040059/**
60 * Base quick-settings tile, extend this to create a new tile.
61 *
62 * State management done on a looper provided by the host. Tiles should update state in
63 * handleUpdateState. Callbacks affecting state should use refreshState to trigger another
64 * state update pass on tile looper.
65 */
John Spurlockccb6b9a2014-05-17 15:54:40 -040066public abstract class QSTile<TState extends State> implements Listenable {
Jason Monkbbadff82015-11-06 15:47:26 -050067 protected final String TAG = "Tile." + getClass().getSimpleName();
68 protected static final boolean DEBUG = Log.isLoggable("Tile", Log.DEBUG);
John Spurlockaf8d6c42014-05-07 17:49:08 -040069
70 protected final Host mHost;
71 protected final Context mContext;
72 protected final H mHandler;
John Spurlock76c43b92014-05-13 21:10:51 -040073 protected final Handler mUiHandler = new Handler(Looper.getMainLooper());
John Spurlockaf8d6c42014-05-07 17:49:08 -040074
Jason Monkca894a02016-01-12 15:30:22 -050075 private final ArrayList<Callback> mCallbacks = new ArrayList<>();
Jason Monka758ba62015-07-14 12:29:28 -040076 protected TState mState = newTileState();
77 private TState mTmpState = newTileState();
Selim Cinek4fda7b22014-08-18 22:07:25 +020078 private boolean mAnnounceNextStateChange;
John Spurlockaf8d6c42014-05-07 17:49:08 -040079
Jason Monkbd6dbb02015-09-03 15:46:25 -040080 private String mTileSpec;
81
Jason Monk62b63a02016-02-02 15:15:31 -050082 public abstract TState newTileState();
Chris Wren9e7283f2015-05-08 17:23:47 -040083 abstract protected void handleClick();
John Spurlockaf8d6c42014-05-07 17:49:08 -040084 abstract protected void handleUpdateState(TState state, Object arg);
85
Chris Wren457a21c2015-05-06 17:50:34 -040086 /**
87 * Declare the category of this tile.
88 *
Chris Wrenf6e9228b2016-01-26 18:04:35 -050089 * Categories are defined in {@link com.android.internal.logging.MetricsProto.MetricsEvent}
90 * by editing frameworks/base/proto/src/metrics_constants.proto.
Chris Wren457a21c2015-05-06 17:50:34 -040091 */
92 abstract public int getMetricsCategory();
93
John Spurlockaf8d6c42014-05-07 17:49:08 -040094 protected QSTile(Host host) {
95 mHost = host;
96 mContext = host.getContext();
97 mHandler = new H(host.getLooper());
98 }
Jason Monkbd6dbb02015-09-03 15:46:25 -040099
100 public String getTileSpec() {
101 return mTileSpec;
102 }
103
104 public void setTileSpec(String tileSpec) {
105 mTileSpec = tileSpec;
106 }
107
John Spurlockaf8d6c42014-05-07 17:49:08 -0400108 public Host getHost() {
109 return mHost;
110 }
111
Jason Monkdc35dcb2015-12-04 16:36:15 -0500112 public QSIconView createTileView(Context context) {
113 return new QSIconView(context);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400114 }
115
John Spurlock7f8f22a2014-07-02 18:54:17 -0400116 public DetailAdapter getDetailAdapter() {
John Spurlockaf8d6c42014-05-07 17:49:08 -0400117 return null; // optional
118 }
119
Jason Monkc3f42c12016-02-05 12:33:13 -0500120 /**
121 * Is a startup check whether this device currently supports this tile.
122 * Should not be used to conditionally hide tiles. Only checked on tile
123 * creation or whether should be shown in edit screen.
124 */
125 public boolean isAvailable() {
126 return true;
127 }
128
John Spurlock7f8f22a2014-07-02 18:54:17 -0400129 public interface DetailAdapter {
Jason Monkc06fbb12016-01-08 14:12:18 -0500130 CharSequence getTitle();
John Spurlock7f8f22a2014-07-02 18:54:17 -0400131 Boolean getToggleState();
132 View createDetailView(Context context, View convertView, ViewGroup parent);
133 Intent getSettingsIntent();
134 void setToggleState(boolean state);
Chris Wren457a21c2015-05-06 17:50:34 -0400135 int getMetricsCategory();
John Spurlock7f8f22a2014-07-02 18:54:17 -0400136 }
137
John Spurlockaf8d6c42014-05-07 17:49:08 -0400138 // safe to call from any thread
139
Jason Monkca894a02016-01-12 15:30:22 -0500140 public void addCallback(Callback callback) {
141 mHandler.obtainMessage(H.ADD_CALLBACK, callback).sendToTarget();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400142 }
143
Jason Monk9d02a432016-01-20 16:33:46 -0500144 public void removeCallbacks() {
145 mHandler.sendEmptyMessage(H.REMOVE_CALLBACKS);
146 }
147
John Spurlockaf8d6c42014-05-07 17:49:08 -0400148 public void click() {
149 mHandler.sendEmptyMessage(H.CLICK);
150 }
151
152 public void secondaryClick() {
153 mHandler.sendEmptyMessage(H.SECONDARY_CLICK);
154 }
155
John Spurlockc247b8f2014-11-06 23:06:25 -0500156 public void longClick() {
157 mHandler.sendEmptyMessage(H.LONG_CLICK);
158 }
159
John Spurlockaf8d6c42014-05-07 17:49:08 -0400160 public void showDetail(boolean show) {
161 mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0).sendToTarget();
162 }
163
Jason Monk5db8a412015-10-21 15:16:23 -0700164 public final void refreshState() {
John Spurlockaf8d6c42014-05-07 17:49:08 -0400165 refreshState(null);
166 }
167
168 protected final void refreshState(Object arg) {
169 mHandler.obtainMessage(H.REFRESH_STATE, arg).sendToTarget();
170 }
171
Jason Monka758ba62015-07-14 12:29:28 -0400172 public final void clearState() {
173 mHandler.sendEmptyMessage(H.CLEAR_STATE);
174 }
175
John Spurlockaf8d6c42014-05-07 17:49:08 -0400176 public void userSwitch(int newUserId) {
Adrian Roos32d88e82014-09-24 17:08:22 +0200177 mHandler.obtainMessage(H.USER_SWITCH, newUserId, 0).sendToTarget();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400178 }
179
John Spurlock7f8f22a2014-07-02 18:54:17 -0400180 public void fireToggleStateChanged(boolean state) {
181 mHandler.obtainMessage(H.TOGGLE_STATE_CHANGED, state ? 1 : 0, 0).sendToTarget();
182 }
183
John Spurlock486b78e2014-07-07 08:37:56 -0400184 public void fireScanStateChanged(boolean state) {
185 mHandler.obtainMessage(H.SCAN_STATE_CHANGED, state ? 1 : 0, 0).sendToTarget();
186 }
187
John Spurlockbceed062014-08-10 18:04:16 -0400188 public void destroy() {
189 mHandler.sendEmptyMessage(H.DESTROY);
190 }
191
192 public TState getState() {
193 return mState;
194 }
195
Jason Monk0d6a1c42015-04-20 16:38:51 -0400196 public void setDetailListening(boolean listening) {
197 // optional
198 }
199
John Spurlockaf8d6c42014-05-07 17:49:08 -0400200 // call only on tile worker looper
201
Jason Monkca894a02016-01-12 15:30:22 -0500202 private void handleAddCallback(Callback callback) {
203 mCallbacks.add(callback);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400204 handleRefreshState(null);
205 }
206
Jason Monk9d02a432016-01-20 16:33:46 -0500207 private void handleRemoveCallbacks() {
208 mCallbacks.clear();
209 }
210
John Spurlockaf8d6c42014-05-07 17:49:08 -0400211 protected void handleSecondaryClick() {
Jason Monkdc35dcb2015-12-04 16:36:15 -0500212 // Default to normal click.
213 handleClick();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400214 }
215
John Spurlockc247b8f2014-11-06 23:06:25 -0500216 protected void handleLongClick() {
Jason Monk96defbe2016-03-29 16:51:03 -0400217 MetricsLogger.action(mContext, MetricsEvent.ACTION_QS_LONG_PRESS, getTileSpec());
Jason Monk76c67aa2016-02-19 14:49:42 -0500218 mHost.startActivityDismissingKeyguard(getLongClickIntent());
John Spurlockc247b8f2014-11-06 23:06:25 -0500219 }
220
Jason Monk76c67aa2016-02-19 14:49:42 -0500221 public abstract Intent getLongClickIntent();
222
Jason Monka758ba62015-07-14 12:29:28 -0400223 protected void handleClearState() {
224 mTmpState = newTileState();
225 mState = newTileState();
226 }
227
John Spurlockaf8d6c42014-05-07 17:49:08 -0400228 protected void handleRefreshState(Object arg) {
229 handleUpdateState(mTmpState, arg);
230 final boolean changed = mTmpState.copyTo(mState);
231 if (changed) {
232 handleStateChanged();
233 }
234 }
235
236 private void handleStateChanged() {
Selim Cinek4fda7b22014-08-18 22:07:25 +0200237 boolean delayAnnouncement = shouldAnnouncementBeDelayed();
Jason Monkca894a02016-01-12 15:30:22 -0500238 if (mCallbacks.size() != 0) {
239 for (int i = 0; i < mCallbacks.size(); i++) {
240 mCallbacks.get(i).onStateChanged(mState);
241 }
Selim Cinek4fda7b22014-08-18 22:07:25 +0200242 if (mAnnounceNextStateChange && !delayAnnouncement) {
243 String announcement = composeChangeAnnouncement();
244 if (announcement != null) {
Jason Monkca894a02016-01-12 15:30:22 -0500245 mCallbacks.get(0).onAnnouncementRequested(announcement);
Selim Cinek4fda7b22014-08-18 22:07:25 +0200246 }
247 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400248 }
Selim Cinek4fda7b22014-08-18 22:07:25 +0200249 mAnnounceNextStateChange = mAnnounceNextStateChange && delayAnnouncement;
250 }
251
252 protected boolean shouldAnnouncementBeDelayed() {
253 return false;
254 }
255
256 protected String composeChangeAnnouncement() {
257 return null;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400258 }
259
260 private void handleShowDetail(boolean show) {
Jason Monkca894a02016-01-12 15:30:22 -0500261 for (int i = 0; i < mCallbacks.size(); i++) {
262 mCallbacks.get(i).onShowDetail(show);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400263 }
264 }
265
John Spurlock7f8f22a2014-07-02 18:54:17 -0400266 private void handleToggleStateChanged(boolean state) {
Jason Monkca894a02016-01-12 15:30:22 -0500267 for (int i = 0; i < mCallbacks.size(); i++) {
268 mCallbacks.get(i).onToggleStateChanged(state);
John Spurlock7f8f22a2014-07-02 18:54:17 -0400269 }
270 }
271
John Spurlock486b78e2014-07-07 08:37:56 -0400272 private void handleScanStateChanged(boolean state) {
Jason Monkca894a02016-01-12 15:30:22 -0500273 for (int i = 0; i < mCallbacks.size(); i++) {
274 mCallbacks.get(i).onScanStateChanged(state);
John Spurlock486b78e2014-07-07 08:37:56 -0400275 }
276 }
277
John Spurlockaf8d6c42014-05-07 17:49:08 -0400278 protected void handleUserSwitch(int newUserId) {
279 handleRefreshState(null);
280 }
281
John Spurlockbceed062014-08-10 18:04:16 -0400282 protected void handleDestroy() {
283 setListening(false);
Jason Monkca894a02016-01-12 15:30:22 -0500284 mCallbacks.clear();
John Spurlockbceed062014-08-10 18:04:16 -0400285 }
286
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000287 protected void checkIfRestrictionEnforcedByAdminOnly(State state, String userRestriction) {
Sudheer Shankaad143c02016-03-31 00:24:05 +0000288 EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext,
289 userRestriction, ActivityManager.getCurrentUser());
290 if (admin != null && !RestrictedLockUtils.hasBaseUserRestriction(mContext,
291 userRestriction, ActivityManager.getCurrentUser())) {
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000292 state.disabledByPolicy = true;
Sudheer Shankaad143c02016-03-31 00:24:05 +0000293 state.enforcedAdmin = admin;
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000294 } else {
295 state.disabledByPolicy = false;
296 state.enforcedAdmin = null;
297 }
298 }
299
Jason Monk39c98e62016-03-16 09:18:35 -0400300 public abstract CharSequence getTileLabel();
301
John Spurlockaf8d6c42014-05-07 17:49:08 -0400302 protected final class H extends Handler {
Jason Monkca894a02016-01-12 15:30:22 -0500303 private static final int ADD_CALLBACK = 1;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400304 private static final int CLICK = 2;
305 private static final int SECONDARY_CLICK = 3;
John Spurlockc247b8f2014-11-06 23:06:25 -0500306 private static final int LONG_CLICK = 4;
307 private static final int REFRESH_STATE = 5;
308 private static final int SHOW_DETAIL = 6;
309 private static final int USER_SWITCH = 7;
310 private static final int TOGGLE_STATE_CHANGED = 8;
311 private static final int SCAN_STATE_CHANGED = 9;
312 private static final int DESTROY = 10;
Jason Monka758ba62015-07-14 12:29:28 -0400313 private static final int CLEAR_STATE = 11;
Jason Monk9d02a432016-01-20 16:33:46 -0500314 private static final int REMOVE_CALLBACKS = 12;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400315
316 private H(Looper looper) {
317 super(looper);
318 }
319
320 @Override
321 public void handleMessage(Message msg) {
322 String name = null;
323 try {
Jason Monkca894a02016-01-12 15:30:22 -0500324 if (msg.what == ADD_CALLBACK) {
325 name = "handleAddCallback";
Jason Monk9d02a432016-01-20 16:33:46 -0500326 handleAddCallback((QSTile.Callback) msg.obj);
327 } else if (msg.what == REMOVE_CALLBACKS) {
328 name = "handleRemoveCallbacks";
329 handleRemoveCallbacks();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400330 } else if (msg.what == CLICK) {
331 name = "handleClick";
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000332 if (mState.disabledByPolicy) {
333 Intent intent = RestrictedLockUtils.getShowAdminSupportDetailsIntent(
334 mContext, mState.enforcedAdmin);
335 mHost.startActivityDismissingKeyguard(intent);
336 } else {
337 mAnnounceNextStateChange = true;
338 handleClick();
339 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400340 } else if (msg.what == SECONDARY_CLICK) {
341 name = "handleSecondaryClick";
342 handleSecondaryClick();
John Spurlockc247b8f2014-11-06 23:06:25 -0500343 } else if (msg.what == LONG_CLICK) {
344 name = "handleLongClick";
345 handleLongClick();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400346 } else if (msg.what == REFRESH_STATE) {
347 name = "handleRefreshState";
348 handleRefreshState(msg.obj);
349 } else if (msg.what == SHOW_DETAIL) {
350 name = "handleShowDetail";
351 handleShowDetail(msg.arg1 != 0);
352 } else if (msg.what == USER_SWITCH) {
353 name = "handleUserSwitch";
354 handleUserSwitch(msg.arg1);
John Spurlock7f8f22a2014-07-02 18:54:17 -0400355 } else if (msg.what == TOGGLE_STATE_CHANGED) {
356 name = "handleToggleStateChanged";
357 handleToggleStateChanged(msg.arg1 != 0);
John Spurlock486b78e2014-07-07 08:37:56 -0400358 } else if (msg.what == SCAN_STATE_CHANGED) {
359 name = "handleScanStateChanged";
360 handleScanStateChanged(msg.arg1 != 0);
John Spurlockbceed062014-08-10 18:04:16 -0400361 } else if (msg.what == DESTROY) {
362 name = "handleDestroy";
363 handleDestroy();
Jason Monka758ba62015-07-14 12:29:28 -0400364 } else if (msg.what == CLEAR_STATE) {
365 name = "handleClearState";
366 handleClearState();
John Spurlockbceed062014-08-10 18:04:16 -0400367 } else {
368 throw new IllegalArgumentException("Unknown msg: " + msg.what);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400369 }
370 } catch (Throwable t) {
371 final String error = "Error in " + name;
372 Log.w(TAG, error, t);
373 mHost.warn(error, t);
374 }
375 }
376 }
377
378 public interface Callback {
379 void onStateChanged(State state);
380 void onShowDetail(boolean show);
John Spurlock7f8f22a2014-07-02 18:54:17 -0400381 void onToggleStateChanged(boolean state);
John Spurlock486b78e2014-07-07 08:37:56 -0400382 void onScanStateChanged(boolean state);
Selim Cinek4fda7b22014-08-18 22:07:25 +0200383 void onAnnouncementRequested(CharSequence announcement);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400384 }
385
386 public interface Host {
Jason Monkee43cdf2015-06-19 14:20:46 -0400387 void startActivityDismissingKeyguard(Intent intent);
Adrian Roos62692b22015-09-11 17:46:23 -0700388 void startActivityDismissingKeyguard(PendingIntent intent);
Jason Monkba2318e2015-12-08 09:04:23 -0500389 void startRunnableDismissingKeyguard(Runnable runnable);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400390 void warn(String message, Throwable t);
391 void collapsePanels();
Jason Monkca894a02016-01-12 15:30:22 -0500392 void animateExpandQS();
Jason Monkba2318e2015-12-08 09:04:23 -0500393 void openPanels();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400394 Looper getLooper();
395 Context getContext();
John Spurlockbceed062014-08-10 18:04:16 -0400396 Collection<QSTile<?>> getTiles();
Jason Monkbbadff82015-11-06 15:47:26 -0500397 void addCallback(Callback callback);
Jason Monk46dbfb42016-02-25 14:59:20 -0500398 void removeCallback(Callback callback);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400399 BluetoothController getBluetoothController();
400 LocationController getLocationController();
401 RotationLockController getRotationLockController();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400402 NetworkController getNetworkController();
403 ZenModeController getZenModeController();
Jason Monk51e4dc02014-07-22 12:00:47 -0400404 HotspotController getHotspotController();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400405 CastController getCastController();
Adrian Roosb83777b2014-06-30 15:11:53 +0200406 FlashlightController getFlashlightController();
John Spurlock657c62c2014-07-22 12:18:09 -0400407 KeyguardMonitor getKeyguardMonitor();
Jason Monkabe19742015-09-29 09:47:06 -0400408 UserSwitcherController getUserSwitcherController();
409 UserInfoController getUserInfoController();
410 BatteryController getBatteryController();
Jason Monk66c89c12016-01-06 08:51:26 -0500411 TileServices getTileServices();
Jason Monk5dbd4aa2016-02-07 13:13:39 -0500412 NightModeController getNightModeController();
Jason Monkba2318e2015-12-08 09:04:23 -0500413 void removeTile(String tileSpec);
Jason Monkc3f42c12016-02-05 12:33:13 -0500414 ManagedProfileController getManagedProfileController();
John Spurlockbceed062014-08-10 18:04:16 -0400415
Jason Monk66c89c12016-01-06 08:51:26 -0500416
John Spurlockbceed062014-08-10 18:04:16 -0400417 public interface Callback {
418 void onTilesChanged();
419 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400420 }
421
John Spurlock2d695812014-10-30 13:25:21 -0400422 public static abstract class Icon {
423 abstract public Drawable getDrawable(Context context);
424
Jason Monk1aec93f2016-03-01 09:39:30 -0500425 public Drawable getInvisibleDrawable(Context context) {
426 return getDrawable(context);
427 }
428
John Spurlock2d695812014-10-30 13:25:21 -0400429 @Override
430 public int hashCode() {
431 return Icon.class.hashCode();
432 }
Jason Monkb53b6c52016-02-24 17:25:49 -0500433
434 public int getPadding() {
435 return 0;
436 }
John Spurlock2d695812014-10-30 13:25:21 -0400437 }
438
Jason Monk5db8a412015-10-21 15:16:23 -0700439 public static class DrawableIcon extends Icon {
440 protected final Drawable mDrawable;
441
442 public DrawableIcon(Drawable drawable) {
443 mDrawable = drawable;
444 }
445
446 @Override
447 public Drawable getDrawable(Context context) {
448 return mDrawable;
449 }
Jason Monk1aec93f2016-03-01 09:39:30 -0500450
451 @Override
452 public Drawable getInvisibleDrawable(Context context) {
453 return mDrawable;
454 }
Jason Monk5db8a412015-10-21 15:16:23 -0700455 }
456
John Spurlock2d695812014-10-30 13:25:21 -0400457 public static class ResourceIcon extends Icon {
458 private static final SparseArray<Icon> ICONS = new SparseArray<Icon>();
459
Andrew Flynna478d702015-04-14 23:33:45 -0400460 protected final int mResId;
John Spurlock2d695812014-10-30 13:25:21 -0400461
462 private ResourceIcon(int resId) {
463 mResId = resId;
464 }
465
466 public static Icon get(int resId) {
467 Icon icon = ICONS.get(resId);
468 if (icon == null) {
469 icon = new ResourceIcon(resId);
470 ICONS.put(resId, icon);
471 }
472 return icon;
473 }
474
475 @Override
476 public Drawable getDrawable(Context context) {
Jason Monk66239fb2015-12-21 14:27:00 -0500477 return context.getDrawable(mResId);
John Spurlock2d695812014-10-30 13:25:21 -0400478 }
479
480 @Override
Jason Monk1aec93f2016-03-01 09:39:30 -0500481 public Drawable getInvisibleDrawable(Context context) {
482 return context.getDrawable(mResId);
483 }
484
485 @Override
John Spurlock2d695812014-10-30 13:25:21 -0400486 public boolean equals(Object o) {
487 return o instanceof ResourceIcon && ((ResourceIcon) o).mResId == mResId;
488 }
489
490 @Override
491 public String toString() {
492 return String.format("ResourceIcon[resId=0x%08x]", mResId);
493 }
494 }
495
496 protected class AnimationIcon extends ResourceIcon {
Jason Monk1aec93f2016-03-01 09:39:30 -0500497 private final int mAnimatedResId;
498
499 public AnimationIcon(int resId, int staticResId) {
500 super(staticResId);
501 mAnimatedResId = resId;
John Spurlock2d695812014-10-30 13:25:21 -0400502 }
503
John Spurlock2d695812014-10-30 13:25:21 -0400504 @Override
505 public Drawable getDrawable(Context context) {
506 // workaround: get a clean state for every new AVD
Jason Monk1aec93f2016-03-01 09:39:30 -0500507 return context.getDrawable(mAnimatedResId).getConstantState().newDrawable();
John Spurlock2d695812014-10-30 13:25:21 -0400508 }
509 }
510
John Spurlockaf8d6c42014-05-07 17:49:08 -0400511 public static class State {
John Spurlock2d695812014-10-30 13:25:21 -0400512 public Icon icon;
Jason Monkbbadff82015-11-06 15:47:26 -0500513 public CharSequence label;
514 public CharSequence contentDescription;
515 public CharSequence dualLabelContentDescription;
Selim Cinek06d3bca2014-08-26 17:29:20 +0200516 public boolean autoMirrorDrawable = true;
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000517 public boolean disabledByPolicy;
518 public EnforcedAdmin enforcedAdmin;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400519
520 public boolean copyTo(State other) {
521 if (other == null) throw new IllegalArgumentException();
522 if (!other.getClass().equals(getClass())) throw new IllegalArgumentException();
Jason Monkba2318e2015-12-08 09:04:23 -0500523 final boolean changed = !Objects.equals(other.icon, icon)
John Spurlockaf8d6c42014-05-07 17:49:08 -0400524 || !Objects.equals(other.label, label)
Selim Cineke5557a92014-08-15 19:59:23 +0200525 || !Objects.equals(other.contentDescription, contentDescription)
Selim Cinek06d3bca2014-08-26 17:29:20 +0200526 || !Objects.equals(other.autoMirrorDrawable, autoMirrorDrawable)
Selim Cineke5557a92014-08-15 19:59:23 +0200527 || !Objects.equals(other.dualLabelContentDescription,
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000528 dualLabelContentDescription)
529 || !Objects.equals(other.disabledByPolicy, disabledByPolicy)
530 || !Objects.equals(other.enforcedAdmin, enforcedAdmin);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400531 other.icon = icon;
532 other.label = label;
533 other.contentDescription = contentDescription;
Selim Cineke5557a92014-08-15 19:59:23 +0200534 other.dualLabelContentDescription = dualLabelContentDescription;
Selim Cinek06d3bca2014-08-26 17:29:20 +0200535 other.autoMirrorDrawable = autoMirrorDrawable;
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000536 other.disabledByPolicy = disabledByPolicy;
Sudheer Shanka1c09db22016-01-18 11:18:35 +0000537 if (enforcedAdmin == null) {
538 other.enforcedAdmin = null;
539 } else if (other.enforcedAdmin == null) {
540 other.enforcedAdmin = new EnforcedAdmin(enforcedAdmin);
541 } else {
542 enforcedAdmin.copyTo(other.enforcedAdmin);
543 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400544 return changed;
545 }
546
547 @Override
548 public String toString() {
549 return toStringBuilder().toString();
550 }
551
552 protected StringBuilder toStringBuilder() {
John Spurlock2d695812014-10-30 13:25:21 -0400553 final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('[');
John Spurlockaf8d6c42014-05-07 17:49:08 -0400554 sb.append(",icon=").append(icon);
555 sb.append(",label=").append(label);
556 sb.append(",contentDescription=").append(contentDescription);
Selim Cineke5557a92014-08-15 19:59:23 +0200557 sb.append(",dualLabelContentDescription=").append(dualLabelContentDescription);
Selim Cinek06d3bca2014-08-26 17:29:20 +0200558 sb.append(",autoMirrorDrawable=").append(autoMirrorDrawable);
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000559 sb.append(",disabledByPolicy=").append(disabledByPolicy);
560 sb.append(",enforcedAdmin=").append(enforcedAdmin);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400561 return sb.append(']');
562 }
563 }
564
565 public static class BooleanState extends State {
566 public boolean value;
567
568 @Override
569 public boolean copyTo(State other) {
570 final BooleanState o = (BooleanState) other;
571 final boolean changed = super.copyTo(other) || o.value != value;
572 o.value = value;
573 return changed;
574 }
575
576 @Override
577 protected StringBuilder toStringBuilder() {
578 final StringBuilder rt = super.toStringBuilder();
579 rt.insert(rt.length() - 1, ",value=" + value);
580 return rt;
581 }
582 }
583
584 public static final class SignalState extends State {
585 public boolean enabled;
586 public boolean connected;
587 public boolean activityIn;
588 public boolean activityOut;
589 public int overlayIconId;
John Spurlock899f4392014-06-11 10:59:11 -0400590 public boolean filter;
Jorim Jaggic737b9b2014-09-08 23:57:20 +0200591 public boolean isOverlayIconWide;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400592
593 @Override
594 public boolean copyTo(State other) {
595 final SignalState o = (SignalState) other;
596 final boolean changed = o.enabled != enabled
597 || o.connected != connected || o.activityIn != activityIn
598 || o.activityOut != activityOut
Jorim Jaggic737b9b2014-09-08 23:57:20 +0200599 || o.overlayIconId != overlayIconId
600 || o.isOverlayIconWide != isOverlayIconWide;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400601 o.enabled = enabled;
602 o.connected = connected;
603 o.activityIn = activityIn;
604 o.activityOut = activityOut;
605 o.overlayIconId = overlayIconId;
John Spurlock899f4392014-06-11 10:59:11 -0400606 o.filter = filter;
Jorim Jaggic737b9b2014-09-08 23:57:20 +0200607 o.isOverlayIconWide = isOverlayIconWide;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400608 return super.copyTo(other) || changed;
609 }
610
611 @Override
612 protected StringBuilder toStringBuilder() {
613 final StringBuilder rt = super.toStringBuilder();
614 rt.insert(rt.length() - 1, ",enabled=" + enabled);
615 rt.insert(rt.length() - 1, ",connected=" + connected);
616 rt.insert(rt.length() - 1, ",activityIn=" + activityIn);
617 rt.insert(rt.length() - 1, ",activityOut=" + activityOut);
618 rt.insert(rt.length() - 1, ",overlayIconId=" + overlayIconId);
John Spurlock899f4392014-06-11 10:59:11 -0400619 rt.insert(rt.length() - 1, ",filter=" + filter);
Jorim Jaggic737b9b2014-09-08 23:57:20 +0200620 rt.insert(rt.length() - 1, ",wideOverlayIcon=" + isOverlayIconWide);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400621 return rt;
622 }
623 }
624}