blob: ad7ccbe0c6cb0fac4f49c6bd83bb1f4321526028 [file] [log] [blame]
Jason Monk5db8a412015-10-21 15:16:23 -07001/*
2 * Copyright (C) 2015 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
Jason Monkd5a204f2015-12-21 08:50:01 -050014 * limitations under the License
Jason Monk5db8a412015-10-21 15:16:23 -070015 */
Jason Monkd5a204f2015-12-21 08:50:01 -050016package com.android.systemui.qs.external;
Jason Monk5db8a412015-10-21 15:16:23 -070017
Gus Prevasab336792018-11-14 13:52:20 -050018import static android.view.Display.DEFAULT_DISPLAY;
19import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
20
Jason Monk724214a2016-02-19 16:43:00 -050021import android.app.ActivityManager;
Jason Monk5db8a412015-10-21 15:16:23 -070022import android.content.ComponentName;
Fabian Kozynski4ba3f9a2020-02-21 11:02:42 -050023import android.content.Context;
Jason Monk76c67aa2016-02-19 14:49:42 -050024import android.content.Intent;
Jason Monk5db8a412015-10-21 15:16:23 -070025import android.content.pm.PackageManager;
Jason Monk724214a2016-02-19 16:43:00 -050026import android.content.pm.ResolveInfo;
Jason Monk5db8a412015-10-21 15:16:23 -070027import android.content.pm.ServiceInfo;
Jason Monk068cb8b2015-12-02 11:30:36 -050028import android.graphics.drawable.Drawable;
Jason Monk8c09ac72017-03-16 11:53:40 -040029import android.metrics.LogMaker;
Jason Monk76c67aa2016-02-19 14:49:42 -050030import android.net.Uri;
Jason Monk8f7f3182015-11-18 16:35:14 -050031import android.os.Binder;
Jason Monkbbadff82015-11-06 15:47:26 -050032import android.os.IBinder;
Jason Monk8f7f3182015-11-18 16:35:14 -050033import android.os.RemoteException;
Jason Monk76c67aa2016-02-19 14:49:42 -050034import android.provider.Settings;
Jason Monkbbadff82015-11-06 15:47:26 -050035import android.service.quicksettings.IQSTileService;
36import android.service.quicksettings.Tile;
Jason Monkfe8f6822015-12-21 15:12:01 -050037import android.service.quicksettings.TileService;
Amin Shaikhabcca632018-08-29 15:15:08 -040038import android.text.TextUtils;
Jason Monk1c6116c2017-09-06 17:33:01 -040039import android.text.format.DateUtils;
Jason Monkbbadff82015-11-06 15:47:26 -050040import android.util.Log;
Jason Monk8f7f3182015-11-18 16:35:14 -050041import android.view.IWindowManager;
Jason Monk8f7f3182015-11-18 16:35:14 -050042import android.view.WindowManagerGlobal;
Fabian Kozynski05843f02019-06-28 13:19:57 -040043import android.widget.Switch;
Amin Shaikhabcca632018-08-29 15:15:08 -040044
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010045import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Jason Monk9c7844c2017-01-18 15:21:53 -050046import com.android.systemui.Dependency;
Jason Monkec34da82017-02-24 15:57:05 -050047import com.android.systemui.plugins.ActivityStarter;
Jason Monk702e2eb2017-03-03 16:53:44 -050048import com.android.systemui.plugins.qs.QSTile.State;
John Li4e3dddc2020-03-04 21:35:43 +080049import com.android.systemui.qs.QSHost;
Gus Prevasab336792018-11-14 13:52:20 -050050import com.android.systemui.qs.external.TileLifecycleManager.TileChangeListener;
51import com.android.systemui.qs.tileimpl.QSTileImpl;
Jason Monk5db8a412015-10-21 15:16:23 -070052
Gus Prevasab336792018-11-14 13:52:20 -050053import java.util.Objects;
Jason Monk32508852017-01-18 09:17:13 -050054
Jason Monk702e2eb2017-03-03 16:53:44 -050055public class CustomTile extends QSTileImpl<State> implements TileChangeListener {
Jason Monk5db8a412015-10-21 15:16:23 -070056 public static final String PREFIX = "custom(";
57
Jason Monk1c6116c2017-09-06 17:33:01 -040058 private static final long CUSTOM_STALE_TIMEOUT = DateUtils.HOUR_IN_MILLIS;
59
Jason Monk8f7f3182015-11-18 16:35:14 -050060 private static final boolean DEBUG = false;
61
Jason Monkbbadff82015-11-06 15:47:26 -050062 // We don't want to thrash binding and unbinding if the user opens and closes the panel a lot.
63 // So instead we have a period of waiting.
64 private static final long UNBIND_DELAY = 30000;
Jason Monk5db8a412015-10-21 15:16:23 -070065
Jason Monkbbadff82015-11-06 15:47:26 -050066 private final ComponentName mComponent;
67 private final Tile mTile;
Jason Monk8f7f3182015-11-18 16:35:14 -050068 private final IWindowManager mWindowManager;
69 private final IBinder mToken = new Binder();
Jason Monkd5a204f2015-12-21 08:50:01 -050070 private final IQSTileService mService;
71 private final TileServiceManager mServiceManager;
Jason Monk1ffa11b2016-03-08 14:44:23 -050072 private final int mUser;
Jason Monk624cbe22016-05-02 10:42:17 -040073 private android.graphics.drawable.Icon mDefaultIcon;
Amin Shaikhabcca632018-08-29 15:15:08 -040074 private CharSequence mDefaultLabel;
Jason Monkbbadff82015-11-06 15:47:26 -050075
Fabian Kozynski4ba3f9a2020-02-21 11:02:42 -050076 private final Context mUserContext;
77
Jason Monkbbadff82015-11-06 15:47:26 -050078 private boolean mListening;
Jason Monk8f7f3182015-11-18 16:35:14 -050079 private boolean mIsTokenGranted;
80 private boolean mIsShowingDialog;
quakentsai(58374)32171292021-05-27 11:48:54 +080081 private boolean mEnable = false;
Jason Monkbbadff82015-11-06 15:47:26 -050082
John Li4e3dddc2020-03-04 21:35:43 +080083 private CustomTile(QSHost host, String action, Context userContext) {
Jason Monk5db8a412015-10-21 15:16:23 -070084 super(host);
Jason Monk8f7f3182015-11-18 16:35:14 -050085 mWindowManager = WindowManagerGlobal.getWindowManagerService();
Jason Monk5db8a412015-10-21 15:16:23 -070086 mComponent = ComponentName.unflattenFromString(action);
Jason Monkee68fd82016-06-23 13:12:23 -040087 mTile = new Tile();
Fabian Kozynski4ba3f9a2020-02-21 11:02:42 -050088 mUserContext = userContext;
89 mUser = mUserContext.getUserId();
Amin Shaikhabcca632018-08-29 15:15:08 -040090 updateDefaultTileAndIcon();
Jason Monkd5a204f2015-12-21 08:50:01 -050091 mServiceManager = host.getTileServices().getTileWrapper(this);
Fabian Kozynski489ad0d2020-02-07 10:30:45 -050092 if (mServiceManager.isToggleableTile()) {
Fabian Kozynski05843f02019-06-28 13:19:57 -040093 // Replace states with BooleanState
94 resetStates();
95 }
96
Jason Monkd5a204f2015-12-21 08:50:01 -050097 mService = mServiceManager.getTileService();
Jason Monk624cbe22016-05-02 10:42:17 -040098 mServiceManager.setTileChangeListener(this);
Jason Monk5db8a412015-10-21 15:16:23 -070099 }
100
Jason Monk1c6116c2017-09-06 17:33:01 -0400101 @Override
102 protected long getStaleTimeout() {
103 return CUSTOM_STALE_TIMEOUT + DateUtils.MINUTE_IN_MILLIS * mHost.indexOf(getTileSpec());
104 }
105
Amin Shaikhabcca632018-08-29 15:15:08 -0400106 private void updateDefaultTileAndIcon() {
Jason Monk624cbe22016-05-02 10:42:17 -0400107 try {
Fabian Kozynski4ba3f9a2020-02-21 11:02:42 -0500108 PackageManager pm = mUserContext.getPackageManager();
Jeff Sharkeydd9bda82017-02-23 17:38:31 -0700109 int flags = PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_DIRECT_BOOT_AWARE;
quakentsai(58374)32171292021-05-27 11:48:54 +0800110
111 mEnable = pm.getServiceInfo(mComponent, flags) != null;
112
Will Harmon294af232016-06-24 17:02:34 -0700113 if (isSystemApp(pm)) {
114 flags |= PackageManager.MATCH_DISABLED_COMPONENTS;
115 }
Amin Shaikhabcca632018-08-29 15:15:08 -0400116
Will Harmon294af232016-06-24 17:02:34 -0700117 ServiceInfo info = pm.getServiceInfo(mComponent, flags);
Jason Monka5f6ed32016-06-22 09:58:46 -0400118 int icon = info.icon != 0 ? info.icon
119 : info.applicationInfo.icon;
Jason Monk624cbe22016-05-02 10:42:17 -0400120 // Update the icon if its not set or is the default icon.
121 boolean updateIcon = mTile.getIcon() == null
122 || iconEquals(mTile.getIcon(), mDefaultIcon);
Jason Monka5f6ed32016-06-22 09:58:46 -0400123 mDefaultIcon = icon != 0 ? android.graphics.drawable.Icon
124 .createWithResource(mComponent.getPackageName(), icon) : null;
Jason Monk624cbe22016-05-02 10:42:17 -0400125 if (updateIcon) {
126 mTile.setIcon(mDefaultIcon);
127 }
Amin Shaikhabcca632018-08-29 15:15:08 -0400128 // Update the label if there is no label or it is the default label.
129 boolean updateLabel = mTile.getLabel() == null
130 || TextUtils.equals(mTile.getLabel(), mDefaultLabel);
131 mDefaultLabel = info.loadLabel(pm);
132 if (updateLabel) {
133 mTile.setLabel(mDefaultLabel);
Jason Monk624cbe22016-05-02 10:42:17 -0400134 }
Amin Shaikhabcca632018-08-29 15:15:08 -0400135 } catch (PackageManager.NameNotFoundException e) {
Jason Monk624cbe22016-05-02 10:42:17 -0400136 mDefaultIcon = null;
Amin Shaikhabcca632018-08-29 15:15:08 -0400137 mDefaultLabel = null;
Jason Monk624cbe22016-05-02 10:42:17 -0400138 }
139 }
140
Will Harmon294af232016-06-24 17:02:34 -0700141 private boolean isSystemApp(PackageManager pm) throws PackageManager.NameNotFoundException {
142 return pm.getApplicationInfo(mComponent.getPackageName(), 0).isSystemApp();
143 }
144
Jason Monk624cbe22016-05-02 10:42:17 -0400145 /**
146 * Compare two icons, only works for resources.
147 */
148 private boolean iconEquals(android.graphics.drawable.Icon icon1,
149 android.graphics.drawable.Icon icon2) {
150 if (icon1 == icon2) {
151 return true;
152 }
153 if (icon1 == null || icon2 == null) {
154 return false;
155 }
156 if (icon1.getType() != android.graphics.drawable.Icon.TYPE_RESOURCE
157 || icon2.getType() != android.graphics.drawable.Icon.TYPE_RESOURCE) {
158 return false;
159 }
160 if (icon1.getResId() != icon2.getResId()) {
161 return false;
162 }
Narayan Kamath607223f2018-02-19 14:09:02 +0000163 if (!Objects.equals(icon1.getResPackage(), icon2.getResPackage())) {
Jason Monk624cbe22016-05-02 10:42:17 -0400164 return false;
165 }
166 return true;
167 }
168
169 @Override
170 public void onTileChanged(ComponentName tile) {
Amin Shaikhabcca632018-08-29 15:15:08 -0400171 updateDefaultTileAndIcon();
Jason Monk624cbe22016-05-02 10:42:17 -0400172 }
173
Jason Monk1c2fea82016-03-11 11:33:36 -0500174 @Override
175 public boolean isAvailable() {
quakentsai(58374)32171292021-05-27 11:48:54 +0800176 return mEnable && mDefaultIcon != null;
Jason Monk1c2fea82016-03-11 11:33:36 -0500177 }
178
Jason Monk1ffa11b2016-03-08 14:44:23 -0500179 public int getUser() {
180 return mUser;
181 }
182
Jason Monkbbadff82015-11-06 15:47:26 -0500183 public ComponentName getComponent() {
184 return mComponent;
185 }
186
Jason Monk8c09ac72017-03-16 11:53:40 -0400187 @Override
Jason Monkcb4b31d2017-05-03 10:37:34 -0400188 public LogMaker populate(LogMaker logMaker) {
Jason Monk8c09ac72017-03-16 11:53:40 -0400189 return super.populate(logMaker).setComponentName(mComponent);
190 }
191
Jason Monkbbadff82015-11-06 15:47:26 -0500192 public Tile getQsTile() {
Amin Shaikhabcca632018-08-29 15:15:08 -0400193 updateDefaultTileAndIcon();
Jason Monkbbadff82015-11-06 15:47:26 -0500194 return mTile;
195 }
196
197 public void updateState(Tile tile) {
Jason Monkbbadff82015-11-06 15:47:26 -0500198 mTile.setIcon(tile.getIcon());
199 mTile.setLabel(tile.getLabel());
Evan Laird66a8e732018-12-12 13:52:28 -0500200 mTile.setSubtitle(tile.getSubtitle());
Jason Monkbbadff82015-11-06 15:47:26 -0500201 mTile.setContentDescription(tile.getContentDescription());
yingleiw5e0f9f42020-02-06 11:12:56 -0800202 mTile.setStateDescription(tile.getStateDescription());
Jason Monk94295132016-01-12 11:27:02 -0500203 mTile.setState(tile.getState());
Jason Monk5db8a412015-10-21 15:16:23 -0700204 }
205
Jason Monk8f7f3182015-11-18 16:35:14 -0500206 public void onDialogShown() {
207 mIsShowingDialog = true;
208 }
209
Jason Monk34a5cef2016-01-29 11:28:44 -0500210 public void onDialogHidden() {
211 mIsShowingDialog = false;
212 try {
213 if (DEBUG) Log.d(TAG, "Removing token");
Wale Ogunwaleac2561e2016-11-01 15:43:46 -0700214 mWindowManager.removeWindowToken(mToken, DEFAULT_DISPLAY);
Jason Monk34a5cef2016-01-29 11:28:44 -0500215 } catch (RemoteException e) {
216 }
217 }
218
Jason Monk5db8a412015-10-21 15:16:23 -0700219 @Override
Jason Monk1c6116c2017-09-06 17:33:01 -0400220 public void handleSetListening(boolean listening) {
Fabian Kozynskica30f572019-10-24 10:01:04 -0400221 super.handleSetListening(listening);
Jason Monkbbadff82015-11-06 15:47:26 -0500222 if (mListening == listening) return;
223 mListening = listening;
Fabian Kozynskica30f572019-10-24 10:01:04 -0400224
Jason Monkd5a204f2015-12-21 08:50:01 -0500225 try {
226 if (listening) {
Amin Shaikhabcca632018-08-29 15:15:08 -0400227 updateDefaultTileAndIcon();
Jason Monk624cbe22016-05-02 10:42:17 -0400228 refreshState();
Jason Monk97d22722016-04-07 11:41:47 -0400229 if (!mServiceManager.isActiveTile()) {
Jason Monkfe8f6822015-12-21 15:12:01 -0500230 mServiceManager.setBindRequested(true);
231 mService.onStartListening();
232 }
Jason Monkdc35dcb2015-12-04 16:36:15 -0500233 } else {
Jason Monkbbadff82015-11-06 15:47:26 -0500234 mService.onStopListening();
Jason Monkd5a204f2015-12-21 08:50:01 -0500235 if (mIsTokenGranted && !mIsShowingDialog) {
236 try {
237 if (DEBUG) Log.d(TAG, "Removing token");
Wale Ogunwaleac2561e2016-11-01 15:43:46 -0700238 mWindowManager.removeWindowToken(mToken, DEFAULT_DISPLAY);
Jason Monkd5a204f2015-12-21 08:50:01 -0500239 } catch (RemoteException e) {
240 }
241 mIsTokenGranted = false;
Jason Monk8f7f3182015-11-18 16:35:14 -0500242 }
Jason Monkd5a204f2015-12-21 08:50:01 -0500243 mIsShowingDialog = false;
244 mServiceManager.setBindRequested(false);
Jason Monk8f7f3182015-11-18 16:35:14 -0500245 }
Jason Monkd5a204f2015-12-21 08:50:01 -0500246 } catch (RemoteException e) {
247 // Called through wrapper, won't happen here.
Jason Monkbbadff82015-11-06 15:47:26 -0500248 }
249 }
Jason Monk8f7f3182015-11-18 16:35:14 -0500250
Jason Monkbbadff82015-11-06 15:47:26 -0500251 @Override
252 protected void handleDestroy() {
253 super.handleDestroy();
Jason Monk8f7f3182015-11-18 16:35:14 -0500254 if (mIsTokenGranted) {
255 try {
256 if (DEBUG) Log.d(TAG, "Removing token");
Wale Ogunwaleac2561e2016-11-01 15:43:46 -0700257 mWindowManager.removeWindowToken(mToken, DEFAULT_DISPLAY);
Jason Monk8f7f3182015-11-18 16:35:14 -0500258 } catch (RemoteException e) {
259 }
260 }
Jason Monk66c89c12016-01-06 08:51:26 -0500261 mHost.getTileServices().freeService(this, mServiceManager);
Jason Monk5db8a412015-10-21 15:16:23 -0700262 }
263
264 @Override
Jason Monk62b63a02016-02-02 15:15:31 -0500265 public State newTileState() {
Fabian Kozynski489ad0d2020-02-07 10:30:45 -0500266 if (mServiceManager != null && mServiceManager.isToggleableTile()) {
Fabian Kozynski05843f02019-06-28 13:19:57 -0400267 return new BooleanState();
268 }
269 return new State();
Jason Monk5db8a412015-10-21 15:16:23 -0700270 }
271
272 @Override
Jason Monk76c67aa2016-02-19 14:49:42 -0500273 public Intent getLongClickIntent() {
Jason Monk724214a2016-02-19 16:43:00 -0500274 Intent i = new Intent(TileService.ACTION_QS_TILE_PREFERENCES);
275 i.setPackage(mComponent.getPackageName());
276 i = resolveIntent(i);
277 if (i != null) {
Jeff Sharkey1a749422017-04-28 14:19:50 -0600278 i.putExtra(Intent.EXTRA_COMPONENT_NAME, mComponent);
Akira Oshimi8d2e9a92017-01-24 16:50:53 +0900279 i.putExtra(TileService.EXTRA_STATE, mTile.getState());
Jason Monk724214a2016-02-19 16:43:00 -0500280 return i;
281 }
Jason Monk76c67aa2016-02-19 14:49:42 -0500282 return new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).setData(
283 Uri.fromParts("package", mComponent.getPackageName(), null));
Jason Monk5db8a412015-10-21 15:16:23 -0700284 }
285
Jason Monk724214a2016-02-19 16:43:00 -0500286 private Intent resolveIntent(Intent i) {
287 ResolveInfo result = mContext.getPackageManager().resolveActivityAsUser(i, 0,
288 ActivityManager.getCurrentUser());
289 return result != null ? new Intent(TileService.ACTION_QS_TILE_PREFERENCES)
290 .setClassName(result.activityInfo.packageName, result.activityInfo.name) : null;
291 }
292
Jason Monk5db8a412015-10-21 15:16:23 -0700293 @Override
294 protected void handleClick() {
Jason Monk94295132016-01-12 11:27:02 -0500295 if (mTile.getState() == Tile.STATE_UNAVAILABLE) {
296 return;
297 }
Jason Monkfe8f6822015-12-21 15:12:01 -0500298 try {
299 if (DEBUG) Log.d(TAG, "Adding token");
Wale Ogunwaleac2561e2016-11-01 15:43:46 -0700300 mWindowManager.addWindowToken(mToken, TYPE_QS_DIALOG, DEFAULT_DISPLAY);
Jason Monkfe8f6822015-12-21 15:12:01 -0500301 mIsTokenGranted = true;
302 } catch (RemoteException e) {
303 }
304 try {
Jason Monk97d22722016-04-07 11:41:47 -0400305 if (mServiceManager.isActiveTile()) {
Jason Monkfe8f6822015-12-21 15:12:01 -0500306 mServiceManager.setBindRequested(true);
307 mService.onStartListening();
Jason Monk8f7f3182015-11-18 16:35:14 -0500308 }
Jason Monkfe8f6822015-12-21 15:12:01 -0500309 mService.onClick(mToken);
310 } catch (RemoteException e) {
311 // Called through wrapper, won't happen here.
Jason Monkbbadff82015-11-06 15:47:26 -0500312 }
Jason Monk5db8a412015-10-21 15:16:23 -0700313 }
314
315 @Override
Jason Monk39c98e62016-03-16 09:18:35 -0400316 public CharSequence getTileLabel() {
317 return getState().label;
318 }
319
320 @Override
Jason Monk5db8a412015-10-21 15:16:23 -0700321 protected void handleUpdateState(State state, Object arg) {
Jason Monk1c2fea82016-03-11 11:33:36 -0500322 int tileState = mTile.getState();
323 if (mServiceManager.hasPendingBind()) {
324 tileState = Tile.STATE_UNAVAILABLE;
325 }
Jason Monk32508852017-01-18 09:17:13 -0500326 state.state = tileState;
Jason Monk4a906f92016-04-20 10:54:55 -0400327 Drawable drawable;
328 try {
Fabian Kozynski4ba3f9a2020-02-21 11:02:42 -0500329 drawable = mTile.getIcon().loadDrawable(mUserContext);
Jason Monk4a906f92016-04-20 10:54:55 -0400330 } catch (Exception e) {
331 Log.w(TAG, "Invalid icon, forcing into unavailable state");
Jason Monk6f352aa2017-03-03 09:10:50 -0500332 state.state = Tile.STATE_UNAVAILABLE;
Fabian Kozynski4ba3f9a2020-02-21 11:02:42 -0500333 drawable = mDefaultIcon.loadDrawable(mUserContext);
Jason Monk4a906f92016-04-20 10:54:55 -0400334 }
Evan Lairdb3daf2b2017-10-17 16:00:29 -0400335
336 final Drawable drawableF = drawable;
337 state.iconSupplier = () -> {
Fabian Kozynski2648a2a2020-02-21 13:20:07 -0500338 if (drawableF == null) return null;
Evan Lairdb3daf2b2017-10-17 16:00:29 -0400339 Drawable.ConstantState cs = drawableF.getConstantState();
340 if (cs != null) {
341 return new DrawableIcon(cs.newDrawable());
342 }
343 return null;
344 };
Jason Monkbbadff82015-11-06 15:47:26 -0500345 state.label = mTile.getLabel();
Evan Laird66a8e732018-12-12 13:52:28 -0500346
347 CharSequence subtitle = mTile.getSubtitle();
348 if (subtitle != null && subtitle.length() > 0) {
349 state.secondaryLabel = subtitle;
350 } else {
351 state.secondaryLabel = null;
352 }
353
Jason Monkbbadff82015-11-06 15:47:26 -0500354 if (mTile.getContentDescription() != null) {
355 state.contentDescription = mTile.getContentDescription();
356 } else {
Jason Monk5db8a412015-10-21 15:16:23 -0700357 state.contentDescription = state.label;
Jason Monk5db8a412015-10-21 15:16:23 -0700358 }
Fabian Kozynski05843f02019-06-28 13:19:57 -0400359
yingleiw5e0f9f42020-02-06 11:12:56 -0800360 if (mTile.getStateDescription() != null) {
361 state.stateDescription = mTile.getStateDescription();
362 } else {
363 state.stateDescription = null;
364 }
365
Fabian Kozynski05843f02019-06-28 13:19:57 -0400366 if (state instanceof BooleanState) {
367 state.expandedAccessibilityClassName = Switch.class.getName();
368 ((BooleanState) state).value = (state.state == Tile.STATE_ACTIVE);
369 }
370
Jason Monk5db8a412015-10-21 15:16:23 -0700371 }
372
373 @Override
374 public int getMetricsCategory() {
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500375 return MetricsEvent.QS_CUSTOM;
Jason Monk5db8a412015-10-21 15:16:23 -0700376 }
Jason Monkbbadff82015-11-06 15:47:26 -0500377
Fabian Kozynski2ff6df92020-04-24 12:00:49 -0400378 @Override
379 public final String getMetricsSpec() {
380 return mComponent.getPackageName();
381 }
382
Jason Monk94295132016-01-12 11:27:02 -0500383 public void startUnlockAndRun() {
Jason Monk9c7844c2017-01-18 15:21:53 -0500384 Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() -> {
385 try {
386 mService.onUnlockComplete();
387 } catch (RemoteException e) {
Jason Monk94295132016-01-12 11:27:02 -0500388 }
389 });
390 }
391
Jason Monk7e53f202016-01-28 10:40:20 -0500392 public static String toSpec(ComponentName name) {
393 return PREFIX + name.flattenToShortString() + ")";
394 }
395
Jason Monkbbadff82015-11-06 15:47:26 -0500396 public static ComponentName getComponentFromSpec(String spec) {
397 final String action = spec.substring(PREFIX.length(), spec.length() - 1);
398 if (action.isEmpty()) {
399 throw new IllegalArgumentException("Empty custom tile spec action");
400 }
401 return ComponentName.unflattenFromString(action);
402 }
403
John Li4e3dddc2020-03-04 21:35:43 +0800404 public static CustomTile create(QSHost host, String spec, Context userContext) {
Jason Monkbbadff82015-11-06 15:47:26 -0500405 if (spec == null || !spec.startsWith(PREFIX) || !spec.endsWith(")")) {
406 throw new IllegalArgumentException("Bad custom tile spec: " + spec);
407 }
408 final String action = spec.substring(PREFIX.length(), spec.length() - 1);
409 if (action.isEmpty()) {
410 throw new IllegalArgumentException("Empty custom tile spec action");
411 }
Fabian Kozynski4ba3f9a2020-02-21 11:02:42 -0500412 return new CustomTile(host, action, userContext);
Jason Monkbbadff82015-11-06 15:47:26 -0500413 }
Jason Monk5db8a412015-10-21 15:16:23 -0700414}