blob: 1f857ff7b5ead69eb16cc0b43ecd087cf504d36b [file] [log] [blame]
John Spurlockaf8d6c42014-05-07 17:49:08 -04001/*
Jason Monk702e2eb2017-03-03 16:53:44 -05002 * Copyright (C) 2017 The Android Open Source Project
John Spurlockaf8d6c42014-05-07 17:49:08 -04003 *
Jason Monk702e2eb2017-03-03 16:53:44 -05004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of the License at
John Spurlockaf8d6c42014-05-07 17:49:08 -04006 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
Jason Monk702e2eb2017-03-03 16:53:44 -05009 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11 * KIND, either express or implied. See the License for the specific language governing
12 * permissions and limitations under the License.
John Spurlockaf8d6c42014-05-07 17:49:08 -040013 */
14
Jason Monk702e2eb2017-03-03 16:53:44 -050015package com.android.systemui.qs.tileimpl;
16
Jason Monkfa452ef2018-12-26 17:26:10 -050017import static androidx.lifecycle.Lifecycle.State.DESTROYED;
18import static androidx.lifecycle.Lifecycle.State.RESUMED;
19
Jason Monk8c09ac72017-03-16 11:53:40 -040020import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_CLICK;
21import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_LONG_PRESS;
22import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_SECONDARY_CLICK;
Susi Kharraz-Post9b033672018-11-28 08:14:07 -050023import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_IS_FULL_QS;
Jason Monk8c09ac72017-03-16 11:53:40 -040024import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_QS_POSITION;
25import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_QS_VALUE;
Susi Kharraz-Post9b033672018-11-28 08:14:07 -050026import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_STATUS_BAR_STATE;
Jason Monk8c09ac72017-03-16 11:53:40 -040027import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_ACTION;
Jason Monk702e2eb2017-03-03 16:53:44 -050028import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
John Spurlockaf8d6c42014-05-07 17:49:08 -040029
Sudheer Shankab6fc9312016-01-27 19:59:03 +000030import android.app.ActivityManager;
John Spurlockaf8d6c42014-05-07 17:49:08 -040031import android.content.Context;
32import android.content.Intent;
John Spurlock444eb2e2014-05-14 13:32:14 -040033import android.graphics.drawable.Drawable;
Jason Monk8c09ac72017-03-16 11:53:40 -040034import android.metrics.LogMaker;
John Spurlockaf8d6c42014-05-07 17:49:08 -040035import android.os.Handler;
36import android.os.Looper;
37import android.os.Message;
Jason Monk32508852017-01-18 09:17:13 -050038import android.service.quicksettings.Tile;
Jason Monk1c6116c2017-09-06 17:33:01 -040039import android.text.format.DateUtils;
Jason Monk1bec6af2016-05-31 15:40:58 -040040import android.util.ArraySet;
John Spurlockaf8d6c42014-05-07 17:49:08 -040041import android.util.Log;
John Spurlock2d695812014-10-30 13:25:21 -040042import android.util.SparseArray;
Sudheer Shanka1c7cda82015-12-31 14:46:02 +000043
Jason Monkfa452ef2018-12-26 17:26:10 -050044import androidx.annotation.NonNull;
45import androidx.lifecycle.Lifecycle;
46import androidx.lifecycle.LifecycleOwner;
47import androidx.lifecycle.LifecycleRegistry;
48
Jason Monk1c6116c2017-09-06 17:33:01 -040049import com.android.internal.annotations.VisibleForTesting;
Jason Monk96defbe2016-03-29 16:51:03 -040050import com.android.internal.logging.MetricsLogger;
Sudheer Shanka1c7cda82015-12-31 14:46:02 +000051import com.android.settingslib.RestrictedLockUtils;
Philip P. Moltmann4e615e62018-08-28 14:57:49 -070052import com.android.settingslib.RestrictedLockUtilsInternal;
Jason Monk702e2eb2017-03-03 16:53:44 -050053import com.android.settingslib.Utils;
Jason Monk9c7844c2017-01-18 15:21:53 -050054import com.android.systemui.Dependency;
Fabian Kozynski00d494d2019-04-04 09:53:50 -040055import com.android.systemui.Dumpable;
Rohan Shahdb2cfa32018-02-20 11:27:22 -080056import com.android.systemui.Prefs;
Jason Monkec34da82017-02-24 15:57:05 -050057import com.android.systemui.plugins.ActivityStarter;
Jason Monke5b770e2017-03-03 21:49:29 -050058import com.android.systemui.plugins.qs.DetailAdapter;
Jason Monk702e2eb2017-03-03 16:53:44 -050059import com.android.systemui.plugins.qs.QSIconView;
60import com.android.systemui.plugins.qs.QSTile;
61import com.android.systemui.plugins.qs.QSTile.State;
Beverly8fdb5332019-02-04 14:29:49 -050062import com.android.systemui.plugins.statusbar.StatusBarStateController;
Jason Monkf8c2f7b2017-09-06 09:22:29 -040063import com.android.systemui.qs.PagedTileLayout.TilePage;
Jason Monk702e2eb2017-03-03 16:53:44 -050064import com.android.systemui.qs.QSHost;
Rohan Shahd3cf7562018-02-23 11:12:28 -080065import com.android.systemui.qs.QuickStatusBarHeader;
John Spurlockaf8d6c42014-05-07 17:49:08 -040066
Fabian Kozynski00d494d2019-04-04 09:53:50 -040067import java.io.FileDescriptor;
68import java.io.PrintWriter;
Jason Monkca894a02016-01-12 15:30:22 -050069import java.util.ArrayList;
Sudheer Shanka1c7cda82015-12-31 14:46:02 +000070
John Spurlockaf8d6c42014-05-07 17:49:08 -040071/**
72 * Base quick-settings tile, extend this to create a new tile.
73 *
74 * State management done on a looper provided by the host. Tiles should update state in
75 * handleUpdateState. Callbacks affecting state should use refreshState to trigger another
76 * state update pass on tile looper.
Susi Kharraz-Post9b033672018-11-28 08:14:07 -050077 *
78 * @param <TState> see above
John Spurlockaf8d6c42014-05-07 17:49:08 -040079 */
Fabian Kozynski00d494d2019-04-04 09:53:50 -040080public abstract class QSTileImpl<TState extends State> implements QSTile, LifecycleOwner, Dumpable {
Jason Monkbbadff82015-11-06 15:47:26 -050081 protected final String TAG = "Tile." + getClass().getSimpleName();
82 protected static final boolean DEBUG = Log.isLoggable("Tile", Log.DEBUG);
John Spurlockaf8d6c42014-05-07 17:49:08 -040083
Jason Monk1c6116c2017-09-06 17:33:01 -040084 private static final long DEFAULT_STALE_TIMEOUT = 10 * DateUtils.MINUTE_IN_MILLIS;
Amin Shaikhd03a7432018-03-01 15:46:55 -050085 protected static final Object ARG_SHOW_TRANSIENT_ENABLING = new Object();
Jason Monk1c6116c2017-09-06 17:33:01 -040086
Jason Monk702e2eb2017-03-03 16:53:44 -050087 protected final QSHost mHost;
John Spurlockaf8d6c42014-05-07 17:49:08 -040088 protected final Context mContext;
Jason Monk1c6116c2017-09-06 17:33:01 -040089 // @NonFinalForTesting
90 protected H mHandler = new H(Dependency.get(Dependency.BG_LOOPER));
John Spurlock76c43b92014-05-13 21:10:51 -040091 protected final Handler mUiHandler = new Handler(Looper.getMainLooper());
Jason Monk1bec6af2016-05-31 15:40:58 -040092 private final ArraySet<Object> mListeners = new ArraySet<>();
Jason Monk8c09ac72017-03-16 11:53:40 -040093 private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
Susi Kharraz-Post9b033672018-11-28 08:14:07 -050094 private final StatusBarStateController
95 mStatusBarStateController = Dependency.get(StatusBarStateController.class);
John Spurlockaf8d6c42014-05-07 17:49:08 -040096
Jason Monkca894a02016-01-12 15:30:22 -050097 private final ArrayList<Callback> mCallbacks = new ArrayList<>();
Jason Monk1c6116c2017-09-06 17:33:01 -040098 private final Object mStaleListener = new Object();
Jason Monka758ba62015-07-14 12:29:28 -040099 protected TState mState = newTileState();
100 private TState mTmpState = newTileState();
Selim Cinek4fda7b22014-08-18 22:07:25 +0200101 private boolean mAnnounceNextStateChange;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400102
Jason Monkbd6dbb02015-09-03 15:46:25 -0400103 private String mTileSpec;
Jason Monk702e2eb2017-03-03 16:53:44 -0500104 private EnforcedAdmin mEnforcedAdmin;
Jason Monkbe3235a2017-04-05 09:29:53 -0400105 private boolean mShowingDetail;
Amin Shaikh572230b2018-03-20 17:24:57 -0400106 private int mIsFullQs;
Jason Monkbd6dbb02015-09-03 15:46:25 -0400107
Jason Monkfa452ef2018-12-26 17:26:10 -0500108 private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
109
Jason Monk62b63a02016-02-02 15:15:31 -0500110 public abstract TState newTileState();
Jason Monk32508852017-01-18 09:17:13 -0500111
Chris Wren9e7283f2015-05-08 17:23:47 -0400112 abstract protected void handleClick();
Jason Monk32508852017-01-18 09:17:13 -0500113
John Spurlockaf8d6c42014-05-07 17:49:08 -0400114 abstract protected void handleUpdateState(TState state, Object arg);
115
Chris Wren457a21c2015-05-06 17:50:34 -0400116 /**
117 * Declare the category of this tile.
118 *
Jason Monk8c09ac72017-03-16 11:53:40 -0400119 * Categories are defined in {@link com.android.internal.logging.nano.MetricsProto.MetricsEvent}
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500120 * by editing frameworks/base/proto/src/metrics_constants.proto.
Chris Wren457a21c2015-05-06 17:50:34 -0400121 */
122 abstract public int getMetricsCategory();
123
Jason Monk702e2eb2017-03-03 16:53:44 -0500124 protected QSTileImpl(QSHost host) {
John Spurlockaf8d6c42014-05-07 17:49:08 -0400125 mHost = host;
126 mContext = host.getContext();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400127 }
Jason Monkbd6dbb02015-09-03 15:46:25 -0400128
Jason Monkfa452ef2018-12-26 17:26:10 -0500129 @NonNull
130 @Override
131 public Lifecycle getLifecycle() {
132 return mLifecycle;
133 }
134
Jason Monk1bec6af2016-05-31 15:40:58 -0400135 /**
136 * Adds or removes a listening client for the tile. If the tile has one or more
137 * listening client it will go into the listening state.
138 */
139 public void setListening(Object listener, boolean listening) {
Amin Shaikh572230b2018-03-20 17:24:57 -0400140 mHandler.obtainMessage(H.SET_LISTENING, listening ? 1 : 0, 0, listener).sendToTarget();
Jason Monk1bec6af2016-05-31 15:40:58 -0400141 }
142
Jason Monk1c6116c2017-09-06 17:33:01 -0400143 protected long getStaleTimeout() {
144 return DEFAULT_STALE_TIMEOUT;
145 }
146
147 @VisibleForTesting
148 protected void handleStale() {
149 setListening(mStaleListener, true);
150 }
151
Jason Monkbd6dbb02015-09-03 15:46:25 -0400152 public String getTileSpec() {
153 return mTileSpec;
154 }
155
156 public void setTileSpec(String tileSpec) {
157 mTileSpec = tileSpec;
158 }
159
Jason Monk702e2eb2017-03-03 16:53:44 -0500160 public QSHost getHost() {
John Spurlockaf8d6c42014-05-07 17:49:08 -0400161 return mHost;
162 }
163
Jason Monkdc35dcb2015-12-04 16:36:15 -0500164 public QSIconView createTileView(Context context) {
Jason Monk702e2eb2017-03-03 16:53:44 -0500165 return new QSIconViewImpl(context);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400166 }
167
John Spurlock7f8f22a2014-07-02 18:54:17 -0400168 public DetailAdapter getDetailAdapter() {
John Spurlockaf8d6c42014-05-07 17:49:08 -0400169 return null; // optional
170 }
171
Jason Monk32508852017-01-18 09:17:13 -0500172 protected DetailAdapter createDetailAdapter() {
173 throw new UnsupportedOperationException();
174 }
Muyuan Li0e9f5382016-04-27 15:51:15 -0700175
Jason Monkc3f42c12016-02-05 12:33:13 -0500176 /**
177 * Is a startup check whether this device currently supports this tile.
178 * Should not be used to conditionally hide tiles. Only checked on tile
179 * creation or whether should be shown in edit screen.
180 */
181 public boolean isAvailable() {
182 return true;
183 }
184
John Spurlockaf8d6c42014-05-07 17:49:08 -0400185 // safe to call from any thread
186
Jason Monkca894a02016-01-12 15:30:22 -0500187 public void addCallback(Callback callback) {
188 mHandler.obtainMessage(H.ADD_CALLBACK, callback).sendToTarget();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400189 }
190
Xiaohui Chen08e266c2016-04-18 12:53:28 -0700191 public void removeCallback(Callback callback) {
192 mHandler.obtainMessage(H.REMOVE_CALLBACK, callback).sendToTarget();
193 }
194
Jason Monk9d02a432016-01-20 16:33:46 -0500195 public void removeCallbacks() {
196 mHandler.sendEmptyMessage(H.REMOVE_CALLBACKS);
197 }
198
John Spurlockaf8d6c42014-05-07 17:49:08 -0400199 public void click() {
Susi Kharraz-Post9b033672018-11-28 08:14:07 -0500200 mMetricsLogger.write(populate(new LogMaker(ACTION_QS_CLICK).setType(TYPE_ACTION)
201 .addTaggedData(FIELD_STATUS_BAR_STATE,
202 mStatusBarStateController.getState())));
John Spurlockaf8d6c42014-05-07 17:49:08 -0400203 mHandler.sendEmptyMessage(H.CLICK);
204 }
205
206 public void secondaryClick() {
Susi Kharraz-Post9b033672018-11-28 08:14:07 -0500207 mMetricsLogger.write(populate(new LogMaker(ACTION_QS_SECONDARY_CLICK).setType(TYPE_ACTION)
208 .addTaggedData(FIELD_STATUS_BAR_STATE,
209 mStatusBarStateController.getState())));
John Spurlockaf8d6c42014-05-07 17:49:08 -0400210 mHandler.sendEmptyMessage(H.SECONDARY_CLICK);
211 }
212
John Spurlockc247b8f2014-11-06 23:06:25 -0500213 public void longClick() {
Susi Kharraz-Post9b033672018-11-28 08:14:07 -0500214 mMetricsLogger.write(populate(new LogMaker(ACTION_QS_LONG_PRESS).setType(TYPE_ACTION)
215 .addTaggedData(FIELD_STATUS_BAR_STATE,
216 mStatusBarStateController.getState())));
John Spurlockc247b8f2014-11-06 23:06:25 -0500217 mHandler.sendEmptyMessage(H.LONG_CLICK);
Rohan Shahdb2cfa32018-02-20 11:27:22 -0800218
219 Prefs.putInt(
220 mContext,
221 Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
Rohan Shahd3cf7562018-02-23 11:12:28 -0800222 QuickStatusBarHeader.MAX_TOOLTIP_SHOWN_COUNT);
John Spurlockc247b8f2014-11-06 23:06:25 -0500223 }
224
Jason Monkcb4b31d2017-05-03 10:37:34 -0400225 public LogMaker populate(LogMaker logMaker) {
Jason Monk8c09ac72017-03-16 11:53:40 -0400226 if (mState instanceof BooleanState) {
227 logMaker.addTaggedData(FIELD_QS_VALUE, ((BooleanState) mState).value ? 1 : 0);
228 }
229 return logMaker.setSubtype(getMetricsCategory())
Susi Kharraz-Post9b033672018-11-28 08:14:07 -0500230 .addTaggedData(FIELD_IS_FULL_QS, mIsFullQs)
Jason Monk8c09ac72017-03-16 11:53:40 -0400231 .addTaggedData(FIELD_QS_POSITION, mHost.indexOf(mTileSpec));
232 }
233
John Spurlockaf8d6c42014-05-07 17:49:08 -0400234 public void showDetail(boolean show) {
235 mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0).sendToTarget();
236 }
237
Evan Laird6b284732017-02-28 17:27:04 -0500238 public void refreshState() {
John Spurlockaf8d6c42014-05-07 17:49:08 -0400239 refreshState(null);
240 }
241
242 protected final void refreshState(Object arg) {
243 mHandler.obtainMessage(H.REFRESH_STATE, arg).sendToTarget();
244 }
245
246 public void userSwitch(int newUserId) {
Adrian Roos32d88e82014-09-24 17:08:22 +0200247 mHandler.obtainMessage(H.USER_SWITCH, newUserId, 0).sendToTarget();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400248 }
249
John Spurlock7f8f22a2014-07-02 18:54:17 -0400250 public void fireToggleStateChanged(boolean state) {
251 mHandler.obtainMessage(H.TOGGLE_STATE_CHANGED, state ? 1 : 0, 0).sendToTarget();
252 }
253
John Spurlock486b78e2014-07-07 08:37:56 -0400254 public void fireScanStateChanged(boolean state) {
255 mHandler.obtainMessage(H.SCAN_STATE_CHANGED, state ? 1 : 0, 0).sendToTarget();
256 }
257
John Spurlockbceed062014-08-10 18:04:16 -0400258 public void destroy() {
259 mHandler.sendEmptyMessage(H.DESTROY);
260 }
261
262 public TState getState() {
263 return mState;
264 }
265
Jason Monk0d6a1c42015-04-20 16:38:51 -0400266 public void setDetailListening(boolean listening) {
267 // optional
268 }
269
John Spurlockaf8d6c42014-05-07 17:49:08 -0400270 // call only on tile worker looper
271
Jason Monkca894a02016-01-12 15:30:22 -0500272 private void handleAddCallback(Callback callback) {
273 mCallbacks.add(callback);
Jason Monk968d2692016-06-17 13:48:44 -0400274 callback.onStateChanged(mState);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400275 }
276
Xiaohui Chen08e266c2016-04-18 12:53:28 -0700277 private void handleRemoveCallback(Callback callback) {
278 mCallbacks.remove(callback);
279 }
280
Jason Monk9d02a432016-01-20 16:33:46 -0500281 private void handleRemoveCallbacks() {
282 mCallbacks.clear();
283 }
284
John Spurlockaf8d6c42014-05-07 17:49:08 -0400285 protected void handleSecondaryClick() {
Jason Monkdc35dcb2015-12-04 16:36:15 -0500286 // Default to normal click.
287 handleClick();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400288 }
289
John Spurlockc247b8f2014-11-06 23:06:25 -0500290 protected void handleLongClick() {
Jason Monk9c7844c2017-01-18 15:21:53 -0500291 Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(
292 getLongClickIntent(), 0);
John Spurlockc247b8f2014-11-06 23:06:25 -0500293 }
294
Jason Monk76c67aa2016-02-19 14:49:42 -0500295 public abstract Intent getLongClickIntent();
296
John Spurlockaf8d6c42014-05-07 17:49:08 -0400297 protected void handleRefreshState(Object arg) {
298 handleUpdateState(mTmpState, arg);
299 final boolean changed = mTmpState.copyTo(mState);
300 if (changed) {
301 handleStateChanged();
302 }
Jason Monk1c6116c2017-09-06 17:33:01 -0400303 mHandler.removeMessages(H.STALE);
304 mHandler.sendEmptyMessageDelayed(H.STALE, getStaleTimeout());
305 setListening(mStaleListener, false);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400306 }
307
308 private void handleStateChanged() {
Selim Cinek4fda7b22014-08-18 22:07:25 +0200309 boolean delayAnnouncement = shouldAnnouncementBeDelayed();
Jason Monkca894a02016-01-12 15:30:22 -0500310 if (mCallbacks.size() != 0) {
311 for (int i = 0; i < mCallbacks.size(); i++) {
312 mCallbacks.get(i).onStateChanged(mState);
313 }
Selim Cinek4fda7b22014-08-18 22:07:25 +0200314 if (mAnnounceNextStateChange && !delayAnnouncement) {
315 String announcement = composeChangeAnnouncement();
316 if (announcement != null) {
Jason Monkca894a02016-01-12 15:30:22 -0500317 mCallbacks.get(0).onAnnouncementRequested(announcement);
Selim Cinek4fda7b22014-08-18 22:07:25 +0200318 }
319 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400320 }
Selim Cinek4fda7b22014-08-18 22:07:25 +0200321 mAnnounceNextStateChange = mAnnounceNextStateChange && delayAnnouncement;
322 }
323
324 protected boolean shouldAnnouncementBeDelayed() {
325 return false;
326 }
327
328 protected String composeChangeAnnouncement() {
329 return null;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400330 }
331
332 private void handleShowDetail(boolean show) {
Jason Monkbe3235a2017-04-05 09:29:53 -0400333 mShowingDetail = show;
Jason Monkca894a02016-01-12 15:30:22 -0500334 for (int i = 0; i < mCallbacks.size(); i++) {
335 mCallbacks.get(i).onShowDetail(show);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400336 }
337 }
338
Jason Monkbe3235a2017-04-05 09:29:53 -0400339 protected boolean isShowingDetail() {
340 return mShowingDetail;
341 }
342
John Spurlock7f8f22a2014-07-02 18:54:17 -0400343 private void handleToggleStateChanged(boolean state) {
Jason Monkca894a02016-01-12 15:30:22 -0500344 for (int i = 0; i < mCallbacks.size(); i++) {
345 mCallbacks.get(i).onToggleStateChanged(state);
John Spurlock7f8f22a2014-07-02 18:54:17 -0400346 }
347 }
348
John Spurlock486b78e2014-07-07 08:37:56 -0400349 private void handleScanStateChanged(boolean state) {
Jason Monkca894a02016-01-12 15:30:22 -0500350 for (int i = 0; i < mCallbacks.size(); i++) {
351 mCallbacks.get(i).onScanStateChanged(state);
John Spurlock486b78e2014-07-07 08:37:56 -0400352 }
353 }
354
John Spurlockaf8d6c42014-05-07 17:49:08 -0400355 protected void handleUserSwitch(int newUserId) {
356 handleRefreshState(null);
357 }
358
Amin Shaikh572230b2018-03-20 17:24:57 -0400359 private void handleSetListeningInternal(Object listener, boolean listening) {
360 if (listening) {
361 if (mListeners.add(listener) && mListeners.size() == 1) {
362 if (DEBUG) Log.d(TAG, "handleSetListening true");
Jason Monkfa452ef2018-12-26 17:26:10 -0500363 mLifecycle.markState(RESUMED);
Amin Shaikh572230b2018-03-20 17:24:57 -0400364 handleSetListening(listening);
365 refreshState(); // Ensure we get at least one refresh after listening.
366 }
367 } else {
368 if (mListeners.remove(listener) && mListeners.size() == 0) {
369 if (DEBUG) Log.d(TAG, "handleSetListening false");
Jason Monkfa452ef2018-12-26 17:26:10 -0500370 mLifecycle.markState(DESTROYED);
Amin Shaikh572230b2018-03-20 17:24:57 -0400371 handleSetListening(listening);
372 }
373 }
374 updateIsFullQs();
375 }
376
377 private void updateIsFullQs() {
378 for (Object listener : mListeners) {
379 if (TilePage.class.equals(listener.getClass())) {
380 mIsFullQs = 1;
381 return;
382 }
383 }
384 mIsFullQs = 0;
385 }
386
Jason Monk1c6116c2017-09-06 17:33:01 -0400387 protected abstract void handleSetListening(boolean listening);
Jason Monk1bec6af2016-05-31 15:40:58 -0400388
John Spurlockbceed062014-08-10 18:04:16 -0400389 protected void handleDestroy() {
Jason Monk794bcd22017-06-06 16:38:00 -0400390 if (mListeners.size() != 0) {
Jason Monk1c6116c2017-09-06 17:33:01 -0400391 handleSetListening(false);
Jason Monk794bcd22017-06-06 16:38:00 -0400392 }
Jason Monkca894a02016-01-12 15:30:22 -0500393 mCallbacks.clear();
John Spurlockbceed062014-08-10 18:04:16 -0400394 }
395
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000396 protected void checkIfRestrictionEnforcedByAdminOnly(State state, String userRestriction) {
Philip P. Moltmann4e615e62018-08-28 14:57:49 -0700397 EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(mContext,
Sudheer Shankaad143c02016-03-31 00:24:05 +0000398 userRestriction, ActivityManager.getCurrentUser());
Philip P. Moltmann4e615e62018-08-28 14:57:49 -0700399 if (admin != null && !RestrictedLockUtilsInternal.hasBaseUserRestriction(mContext,
Sudheer Shankaad143c02016-03-31 00:24:05 +0000400 userRestriction, ActivityManager.getCurrentUser())) {
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000401 state.disabledByPolicy = true;
Jason Monk702e2eb2017-03-03 16:53:44 -0500402 mEnforcedAdmin = admin;
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000403 } else {
404 state.disabledByPolicy = false;
Jason Monk702e2eb2017-03-03 16:53:44 -0500405 mEnforcedAdmin = null;
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000406 }
407 }
408
Jason Monk39c98e62016-03-16 09:18:35 -0400409 public abstract CharSequence getTileLabel();
410
Jason Monk32508852017-01-18 09:17:13 -0500411 public static int getColorForState(Context context, int state) {
412 switch (state) {
413 case Tile.STATE_UNAVAILABLE:
414 return Utils.getDisabled(context,
Jason Changb4e879d2018-04-11 11:17:58 +0800415 Utils.getColorAttrDefaultColor(context, android.R.attr.textColorSecondary));
Jason Monkb4cc7b12017-05-09 13:50:47 -0400416 case Tile.STATE_INACTIVE:
Jason Changb4e879d2018-04-11 11:17:58 +0800417 return Utils.getColorAttrDefaultColor(context, android.R.attr.textColorSecondary);
Jason Monk32508852017-01-18 09:17:13 -0500418 case Tile.STATE_ACTIVE:
Jason Changb4e879d2018-04-11 11:17:58 +0800419 return Utils.getColorAttrDefaultColor(context, android.R.attr.colorPrimary);
Jason Monk32508852017-01-18 09:17:13 -0500420 default:
421 Log.e("QSTile", "Invalid state " + state);
422 return 0;
423 }
424 }
425
John Spurlockaf8d6c42014-05-07 17:49:08 -0400426 protected final class H extends Handler {
Jason Monkca894a02016-01-12 15:30:22 -0500427 private static final int ADD_CALLBACK = 1;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400428 private static final int CLICK = 2;
429 private static final int SECONDARY_CLICK = 3;
John Spurlockc247b8f2014-11-06 23:06:25 -0500430 private static final int LONG_CLICK = 4;
431 private static final int REFRESH_STATE = 5;
432 private static final int SHOW_DETAIL = 6;
433 private static final int USER_SWITCH = 7;
434 private static final int TOGGLE_STATE_CHANGED = 8;
435 private static final int SCAN_STATE_CHANGED = 9;
436 private static final int DESTROY = 10;
Amin Shaikh299c45c2018-08-16 10:49:48 -0400437 private static final int REMOVE_CALLBACKS = 11;
438 private static final int REMOVE_CALLBACK = 12;
439 private static final int SET_LISTENING = 13;
440 private static final int STALE = 14;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400441
Jason Monk1c6116c2017-09-06 17:33:01 -0400442 @VisibleForTesting
443 protected H(Looper looper) {
John Spurlockaf8d6c42014-05-07 17:49:08 -0400444 super(looper);
445 }
446
447 @Override
448 public void handleMessage(Message msg) {
449 String name = null;
450 try {
Jason Monkca894a02016-01-12 15:30:22 -0500451 if (msg.what == ADD_CALLBACK) {
452 name = "handleAddCallback";
Jason Monk9d02a432016-01-20 16:33:46 -0500453 handleAddCallback((QSTile.Callback) msg.obj);
454 } else if (msg.what == REMOVE_CALLBACKS) {
455 name = "handleRemoveCallbacks";
456 handleRemoveCallbacks();
Xiaohui Chen08e266c2016-04-18 12:53:28 -0700457 } else if (msg.what == REMOVE_CALLBACK) {
458 name = "handleRemoveCallback";
459 handleRemoveCallback((QSTile.Callback) msg.obj);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400460 } else if (msg.what == CLICK) {
461 name = "handleClick";
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000462 if (mState.disabledByPolicy) {
463 Intent intent = RestrictedLockUtils.getShowAdminSupportDetailsIntent(
Jason Monk702e2eb2017-03-03 16:53:44 -0500464 mContext, mEnforcedAdmin);
Jason Monk9c7844c2017-01-18 15:21:53 -0500465 Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(
466 intent, 0);
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000467 } else {
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000468 handleClick();
469 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400470 } else if (msg.what == SECONDARY_CLICK) {
471 name = "handleSecondaryClick";
472 handleSecondaryClick();
John Spurlockc247b8f2014-11-06 23:06:25 -0500473 } else if (msg.what == LONG_CLICK) {
474 name = "handleLongClick";
475 handleLongClick();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400476 } else if (msg.what == REFRESH_STATE) {
477 name = "handleRefreshState";
478 handleRefreshState(msg.obj);
479 } else if (msg.what == SHOW_DETAIL) {
480 name = "handleShowDetail";
481 handleShowDetail(msg.arg1 != 0);
482 } else if (msg.what == USER_SWITCH) {
483 name = "handleUserSwitch";
484 handleUserSwitch(msg.arg1);
John Spurlock7f8f22a2014-07-02 18:54:17 -0400485 } else if (msg.what == TOGGLE_STATE_CHANGED) {
486 name = "handleToggleStateChanged";
487 handleToggleStateChanged(msg.arg1 != 0);
John Spurlock486b78e2014-07-07 08:37:56 -0400488 } else if (msg.what == SCAN_STATE_CHANGED) {
489 name = "handleScanStateChanged";
490 handleScanStateChanged(msg.arg1 != 0);
John Spurlockbceed062014-08-10 18:04:16 -0400491 } else if (msg.what == DESTROY) {
492 name = "handleDestroy";
493 handleDestroy();
Jason Monk1bec6af2016-05-31 15:40:58 -0400494 } else if (msg.what == SET_LISTENING) {
Amin Shaikh572230b2018-03-20 17:24:57 -0400495 name = "handleSetListeningInternal";
496 handleSetListeningInternal(msg.obj, msg.arg1 != 0);
Jason Monk1c6116c2017-09-06 17:33:01 -0400497 } else if (msg.what == STALE) {
498 name = "handleStale";
499 handleStale();
John Spurlockbceed062014-08-10 18:04:16 -0400500 } else {
501 throw new IllegalArgumentException("Unknown msg: " + msg.what);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400502 }
503 } catch (Throwable t) {
504 final String error = "Error in " + name;
505 Log.w(TAG, error, t);
506 mHost.warn(error, t);
507 }
508 }
509 }
510
Jason Monk5db8a412015-10-21 15:16:23 -0700511 public static class DrawableIcon extends Icon {
512 protected final Drawable mDrawable;
Kensuke Matsui5a58cf92017-07-05 14:53:05 +0900513 protected final Drawable mInvisibleDrawable;
Jason Monk5db8a412015-10-21 15:16:23 -0700514
515 public DrawableIcon(Drawable drawable) {
516 mDrawable = drawable;
Kensuke Matsui5a58cf92017-07-05 14:53:05 +0900517 mInvisibleDrawable = drawable.getConstantState().newDrawable();
Jason Monk5db8a412015-10-21 15:16:23 -0700518 }
519
520 @Override
521 public Drawable getDrawable(Context context) {
522 return mDrawable;
523 }
Kensuke Matsui5a58cf92017-07-05 14:53:05 +0900524
525 @Override
526 public Drawable getInvisibleDrawable(Context context) {
527 return mInvisibleDrawable;
528 }
Jason Monkd2274f82016-12-12 12:02:16 -0500529 }
530
531 public static class DrawableIconWithRes extends DrawableIcon {
532 private final int mId;
533
534 public DrawableIconWithRes(Drawable drawable, int id) {
535 super(drawable);
536 mId = id;
537 }
Jason Monk1aec93f2016-03-01 09:39:30 -0500538
539 @Override
Jason Monkd2274f82016-12-12 12:02:16 -0500540 public boolean equals(Object o) {
541 return o instanceof DrawableIconWithRes && ((DrawableIconWithRes) o).mId == mId;
Jason Monk1aec93f2016-03-01 09:39:30 -0500542 }
Jason Monk5db8a412015-10-21 15:16:23 -0700543 }
544
John Spurlock2d695812014-10-30 13:25:21 -0400545 public static class ResourceIcon extends Icon {
546 private static final SparseArray<Icon> ICONS = new SparseArray<Icon>();
547
Andrew Flynna478d702015-04-14 23:33:45 -0400548 protected final int mResId;
John Spurlock2d695812014-10-30 13:25:21 -0400549
550 private ResourceIcon(int resId) {
551 mResId = resId;
552 }
553
554 public static Icon get(int resId) {
555 Icon icon = ICONS.get(resId);
556 if (icon == null) {
557 icon = new ResourceIcon(resId);
558 ICONS.put(resId, icon);
559 }
560 return icon;
561 }
562
563 @Override
564 public Drawable getDrawable(Context context) {
Jason Monk66239fb2015-12-21 14:27:00 -0500565 return context.getDrawable(mResId);
John Spurlock2d695812014-10-30 13:25:21 -0400566 }
567
568 @Override
Jason Monk1aec93f2016-03-01 09:39:30 -0500569 public Drawable getInvisibleDrawable(Context context) {
570 return context.getDrawable(mResId);
571 }
572
573 @Override
John Spurlock2d695812014-10-30 13:25:21 -0400574 public boolean equals(Object o) {
575 return o instanceof ResourceIcon && ((ResourceIcon) o).mResId == mResId;
576 }
577
578 @Override
579 public String toString() {
580 return String.format("ResourceIcon[resId=0x%08x]", mResId);
581 }
582 }
583
Jason Monk1c6116c2017-09-06 17:33:01 -0400584 protected static class AnimationIcon extends ResourceIcon {
Jason Monk1aec93f2016-03-01 09:39:30 -0500585 private final int mAnimatedResId;
586
587 public AnimationIcon(int resId, int staticResId) {
588 super(staticResId);
589 mAnimatedResId = resId;
John Spurlock2d695812014-10-30 13:25:21 -0400590 }
591
John Spurlock2d695812014-10-30 13:25:21 -0400592 @Override
593 public Drawable getDrawable(Context context) {
594 // workaround: get a clean state for every new AVD
Jason Monk1aec93f2016-03-01 09:39:30 -0500595 return context.getDrawable(mAnimatedResId).getConstantState().newDrawable();
John Spurlock2d695812014-10-30 13:25:21 -0400596 }
597 }
Fabian Kozynski00d494d2019-04-04 09:53:50 -0400598
599 @Override
600 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
601 pw.println(this.getClass().getSimpleName() + ":");
602 pw.print(" "); pw.println(getState().toString());
603 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400604}