15db8a4142e35d62073d81806ff7317e840e30ebcJason Monk/* 25db8a4142e35d62073d81806ff7317e840e30ebcJason Monk * Copyright (C) 2015 The Android Open Source Project 35db8a4142e35d62073d81806ff7317e840e30ebcJason Monk * 45db8a4142e35d62073d81806ff7317e840e30ebcJason Monk * Licensed under the Apache License, Version 2.0 (the "License"); 55db8a4142e35d62073d81806ff7317e840e30ebcJason Monk * you may not use this file except in compliance with the License. 65db8a4142e35d62073d81806ff7317e840e30ebcJason Monk * You may obtain a copy of the License at 75db8a4142e35d62073d81806ff7317e840e30ebcJason Monk * 85db8a4142e35d62073d81806ff7317e840e30ebcJason Monk * http://www.apache.org/licenses/LICENSE-2.0 95db8a4142e35d62073d81806ff7317e840e30ebcJason Monk * 105db8a4142e35d62073d81806ff7317e840e30ebcJason Monk * Unless required by applicable law or agreed to in writing, software 115db8a4142e35d62073d81806ff7317e840e30ebcJason Monk * distributed under the License is distributed on an "AS IS" BASIS, 125db8a4142e35d62073d81806ff7317e840e30ebcJason Monk * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 135db8a4142e35d62073d81806ff7317e840e30ebcJason Monk * See the License for the specific language governing permissions and 14d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk * limitations under the License 155db8a4142e35d62073d81806ff7317e840e30ebcJason Monk */ 16d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monkpackage com.android.systemui.qs.external; 175db8a4142e35d62073d81806ff7317e840e30ebcJason Monk 18724214a87828ad0fe8a44db796a1f4e74eeae521Jason Monkimport android.app.ActivityManager; 195db8a4142e35d62073d81806ff7317e840e30ebcJason Monkimport android.content.ComponentName; 2076c67aa361f65dfb2f5e03d06cc1ccebce9cecd9Jason Monkimport android.content.Intent; 215db8a4142e35d62073d81806ff7317e840e30ebcJason Monkimport android.content.pm.PackageManager; 22724214a87828ad0fe8a44db796a1f4e74eeae521Jason Monkimport android.content.pm.ResolveInfo; 235db8a4142e35d62073d81806ff7317e840e30ebcJason Monkimport android.content.pm.ServiceInfo; 24068cb8baa08eadcd2e1250a5f7b94c9f98cc9921Jason Monkimport android.graphics.drawable.Drawable; 258c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport android.metrics.LogMaker; 2676c67aa361f65dfb2f5e03d06cc1ccebce9cecd9Jason Monkimport android.net.Uri; 278f7f3184d55850b1c4d2036319794151f564e9c0Jason Monkimport android.os.Binder; 28bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monkimport android.os.IBinder; 298f7f3184d55850b1c4d2036319794151f564e9c0Jason Monkimport android.os.RemoteException; 3076c67aa361f65dfb2f5e03d06cc1ccebce9cecd9Jason Monkimport android.provider.Settings; 31bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monkimport android.service.quicksettings.IQSTileService; 32bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monkimport android.service.quicksettings.Tile; 33fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monkimport android.service.quicksettings.TileService; 341c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monkimport android.text.format.DateUtils; 35bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monkimport android.util.Log; 368f7f3184d55850b1c4d2036319794151f564e9c0Jason Monkimport android.view.IWindowManager; 378f7f3184d55850b1c4d2036319794151f564e9c0Jason Monkimport android.view.WindowManagerGlobal; 385db8a4142e35d62073d81806ff7317e840e30ebcJason Monkimport com.android.internal.logging.MetricsLogger; 39383db5ebcc3a4a615faf249bf4f126f42e80b82eTamas Berghammerimport com.android.internal.logging.nano.MetricsProto.MetricsEvent; 409c7844cb91b43929d0a86b1c90aa1efb37f5463aJason Monkimport com.android.systemui.Dependency; 41ec34da8f6f31c5ee1e543c0c7999a067487e0ce9Jason Monkimport com.android.systemui.plugins.ActivityStarter; 42702e2ebf9fbfb715c1d9b716683c9e4d182b575eJason Monkimport com.android.systemui.plugins.qs.QSTile; 43702e2ebf9fbfb715c1d9b716683c9e4d182b575eJason Monkimport com.android.systemui.plugins.qs.QSTile.State; 44702e2ebf9fbfb715c1d9b716683c9e4d182b575eJason Monkimport com.android.systemui.qs.tileimpl.QSTileImpl; 45624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monkimport com.android.systemui.qs.external.TileLifecycleManager.TileChangeListener; 46e5b770e47d44a40d412c7d42010b2cf67920d9e3Jason Monkimport com.android.systemui.qs.QSTileHost; 47607223f3b7a1c4dc3ac995f742f8d2da50d85ffcNarayan Kamathimport java.util.Objects; 485db8a4142e35d62073d81806ff7317e840e30ebcJason Monk 493250885c9feb9b42d60058a823c57a027c0dcf58Jason Monkimport static android.view.Display.DEFAULT_DISPLAY; 503250885c9feb9b42d60058a823c57a027c0dcf58Jason Monkimport static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG; 513250885c9feb9b42d60058a823c57a027c0dcf58Jason Monk 52702e2ebf9fbfb715c1d9b716683c9e4d182b575eJason Monkpublic class CustomTile extends QSTileImpl<State> implements TileChangeListener { 535db8a4142e35d62073d81806ff7317e840e30ebcJason Monk public static final String PREFIX = "custom("; 545db8a4142e35d62073d81806ff7317e840e30ebcJason Monk 551c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk private static final long CUSTOM_STALE_TIMEOUT = DateUtils.HOUR_IN_MILLIS; 561c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk 578f7f3184d55850b1c4d2036319794151f564e9c0Jason Monk private static final boolean DEBUG = false; 588f7f3184d55850b1c4d2036319794151f564e9c0Jason Monk 59bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk // We don't want to thrash binding and unbinding if the user opens and closes the panel a lot. 60bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk // So instead we have a period of waiting. 61bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk private static final long UNBIND_DELAY = 30000; 62bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk 635db8a4142e35d62073d81806ff7317e840e30ebcJason Monk private final ComponentName mComponent; 64bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk private final Tile mTile; 658f7f3184d55850b1c4d2036319794151f564e9c0Jason Monk private final IWindowManager mWindowManager; 668f7f3184d55850b1c4d2036319794151f564e9c0Jason Monk private final IBinder mToken = new Binder(); 67d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk private final IQSTileService mService; 68d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk private final TileServiceManager mServiceManager; 691ffa11b9b2d647d2cbfbc5a0bb8b4ee6c188ccc3Jason Monk private final int mUser; 70624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk private android.graphics.drawable.Icon mDefaultIcon; 71bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk 72bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk private boolean mListening; 73bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk private boolean mBound; 748f7f3184d55850b1c4d2036319794151f564e9c0Jason Monk private boolean mIsTokenGranted; 758f7f3184d55850b1c4d2036319794151f564e9c0Jason Monk private boolean mIsShowingDialog; 765db8a4142e35d62073d81806ff7317e840e30ebcJason Monk 77bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk private CustomTile(QSTileHost host, String action) { 785db8a4142e35d62073d81806ff7317e840e30ebcJason Monk super(host); 798f7f3184d55850b1c4d2036319794151f564e9c0Jason Monk mWindowManager = WindowManagerGlobal.getWindowManagerService(); 805db8a4142e35d62073d81806ff7317e840e30ebcJason Monk mComponent = ComponentName.unflattenFromString(action); 81ee68fd889c2dfcd895b8e73fc39d7b97826dc3d8Jason Monk mTile = new Tile(); 82a3453b8bd9af44566c6a31fd0156cc76e6028f6dJason Monk setTileIcon(); 83d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk mServiceManager = host.getTileServices().getTileWrapper(this); 84d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk mService = mServiceManager.getTileService(); 85624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk mServiceManager.setTileChangeListener(this); 861ffa11b9b2d647d2cbfbc5a0bb8b4ee6c188ccc3Jason Monk mUser = ActivityManager.getCurrentUser(); 87624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk } 88624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk 891c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk @Override 901c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk protected long getStaleTimeout() { 911c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk return CUSTOM_STALE_TIMEOUT + DateUtils.MINUTE_IN_MILLIS * mHost.indexOf(getTileSpec()); 921c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk } 931c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk 94624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk private void setTileIcon() { 95bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk try { 96bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk PackageManager pm = mContext.getPackageManager(); 97dd9bda81f698043a0110d4e2178d20cb160ba829Jeff Sharkey int flags = PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_DIRECT_BOOT_AWARE; 98294af233a9c653e2166e0be6c61f082253ff5bcaWill Harmon if (isSystemApp(pm)) { 99294af233a9c653e2166e0be6c61f082253ff5bcaWill Harmon flags |= PackageManager.MATCH_DISABLED_COMPONENTS; 100294af233a9c653e2166e0be6c61f082253ff5bcaWill Harmon } 101294af233a9c653e2166e0be6c61f082253ff5bcaWill Harmon ServiceInfo info = pm.getServiceInfo(mComponent, flags); 102a5f6ed355eb19a64fc881f0159b8a8cd3a4fb5f5Jason Monk int icon = info.icon != 0 ? info.icon 103a5f6ed355eb19a64fc881f0159b8a8cd3a4fb5f5Jason Monk : info.applicationInfo.icon; 104624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk // Update the icon if its not set or is the default icon. 105624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk boolean updateIcon = mTile.getIcon() == null 106624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk || iconEquals(mTile.getIcon(), mDefaultIcon); 107a5f6ed355eb19a64fc881f0159b8a8cd3a4fb5f5Jason Monk mDefaultIcon = icon != 0 ? android.graphics.drawable.Icon 108a5f6ed355eb19a64fc881f0159b8a8cd3a4fb5f5Jason Monk .createWithResource(mComponent.getPackageName(), icon) : null; 109624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk if (updateIcon) { 110624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk mTile.setIcon(mDefaultIcon); 111624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk } 112624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk // Update the label if there is no label. 113624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk if (mTile.getLabel() == null) { 114624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk mTile.setLabel(info.loadLabel(pm)); 115624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk } 116bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk } catch (Exception e) { 117624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk mDefaultIcon = null; 118bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk } 119624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk } 120624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk 121294af233a9c653e2166e0be6c61f082253ff5bcaWill Harmon private boolean isSystemApp(PackageManager pm) throws PackageManager.NameNotFoundException { 122294af233a9c653e2166e0be6c61f082253ff5bcaWill Harmon return pm.getApplicationInfo(mComponent.getPackageName(), 0).isSystemApp(); 123294af233a9c653e2166e0be6c61f082253ff5bcaWill Harmon } 124294af233a9c653e2166e0be6c61f082253ff5bcaWill Harmon 125624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk /** 126624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk * Compare two icons, only works for resources. 127624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk */ 128624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk private boolean iconEquals(android.graphics.drawable.Icon icon1, 129624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk android.graphics.drawable.Icon icon2) { 130624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk if (icon1 == icon2) { 131624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk return true; 132624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk } 133624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk if (icon1 == null || icon2 == null) { 134624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk return false; 135624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk } 136624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk if (icon1.getType() != android.graphics.drawable.Icon.TYPE_RESOURCE 137624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk || icon2.getType() != android.graphics.drawable.Icon.TYPE_RESOURCE) { 138624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk return false; 139fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk } 140624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk if (icon1.getResId() != icon2.getResId()) { 141624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk return false; 142624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk } 143607223f3b7a1c4dc3ac995f742f8d2da50d85ffcNarayan Kamath if (!Objects.equals(icon1.getResPackage(), icon2.getResPackage())) { 144624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk return false; 145624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk } 146624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk return true; 147624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk } 148624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk 149624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk @Override 150624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk public void onTileChanged(ComponentName tile) { 151624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk setTileIcon(); 1525db8a4142e35d62073d81806ff7317e840e30ebcJason Monk } 1535db8a4142e35d62073d81806ff7317e840e30ebcJason Monk 1541c2fea8df7054d1bc23945c38460a123ce453c4aJason Monk @Override 1551c2fea8df7054d1bc23945c38460a123ce453c4aJason Monk public boolean isAvailable() { 1564a906f9da6f49ef26f6bd4690338a19ade7cd55fJason Monk return mDefaultIcon != null; 1571c2fea8df7054d1bc23945c38460a123ce453c4aJason Monk } 1581c2fea8df7054d1bc23945c38460a123ce453c4aJason Monk 1591ffa11b9b2d647d2cbfbc5a0bb8b4ee6c188ccc3Jason Monk public int getUser() { 1601ffa11b9b2d647d2cbfbc5a0bb8b4ee6c188ccc3Jason Monk return mUser; 1611ffa11b9b2d647d2cbfbc5a0bb8b4ee6c188ccc3Jason Monk } 1621ffa11b9b2d647d2cbfbc5a0bb8b4ee6c188ccc3Jason Monk 163bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk public ComponentName getComponent() { 164bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk return mComponent; 165bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk } 166bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk 1678c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk @Override 168cb4b31d396df49bd55db64b1c40122656c6ec534Jason Monk public LogMaker populate(LogMaker logMaker) { 1698c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk return super.populate(logMaker).setComponentName(mComponent); 1708c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk } 1718c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 172bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk public Tile getQsTile() { 173bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk return mTile; 174bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk } 175bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk 176bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk public void updateState(Tile tile) { 177bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk mTile.setIcon(tile.getIcon()); 178bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk mTile.setLabel(tile.getLabel()); 179bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk mTile.setContentDescription(tile.getContentDescription()); 1809429513cc3ea6e58e330865bd621b57cb3477551Jason Monk mTile.setState(tile.getState()); 1815db8a4142e35d62073d81806ff7317e840e30ebcJason Monk } 1825db8a4142e35d62073d81806ff7317e840e30ebcJason Monk 1838f7f3184d55850b1c4d2036319794151f564e9c0Jason Monk public void onDialogShown() { 1848f7f3184d55850b1c4d2036319794151f564e9c0Jason Monk mIsShowingDialog = true; 1858f7f3184d55850b1c4d2036319794151f564e9c0Jason Monk } 1868f7f3184d55850b1c4d2036319794151f564e9c0Jason Monk 18734a5cef6298cc44fc0614c4747c4b17137cff441Jason Monk public void onDialogHidden() { 18834a5cef6298cc44fc0614c4747c4b17137cff441Jason Monk mIsShowingDialog = false; 18934a5cef6298cc44fc0614c4747c4b17137cff441Jason Monk try { 19034a5cef6298cc44fc0614c4747c4b17137cff441Jason Monk if (DEBUG) Log.d(TAG, "Removing token"); 191ac2561e8206ac42921bb6ddbb0a5972fb360e394Wale Ogunwale mWindowManager.removeWindowToken(mToken, DEFAULT_DISPLAY); 19234a5cef6298cc44fc0614c4747c4b17137cff441Jason Monk } catch (RemoteException e) { 19334a5cef6298cc44fc0614c4747c4b17137cff441Jason Monk } 19434a5cef6298cc44fc0614c4747c4b17137cff441Jason Monk } 19534a5cef6298cc44fc0614c4747c4b17137cff441Jason Monk 1965db8a4142e35d62073d81806ff7317e840e30ebcJason Monk @Override 1971c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk public void handleSetListening(boolean listening) { 198bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk if (mListening == listening) return; 199bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk mListening = listening; 200d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk try { 201d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk if (listening) { 202624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk setTileIcon(); 203624cbe2d8c99f78487870be3390bfe8d23c32c5fJason Monk refreshState(); 20497d2272be0bf3e77474e7ff9984217dfe247f4d0Jason Monk if (!mServiceManager.isActiveTile()) { 205fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk mServiceManager.setBindRequested(true); 206fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk mService.onStartListening(); 207fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk } 208dc35dcb14a8b6c4f221fccafffa5ca603894dbaaJason Monk } else { 209bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk mService.onStopListening(); 210d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk if (mIsTokenGranted && !mIsShowingDialog) { 211d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk try { 212d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk if (DEBUG) Log.d(TAG, "Removing token"); 213ac2561e8206ac42921bb6ddbb0a5972fb360e394Wale Ogunwale mWindowManager.removeWindowToken(mToken, DEFAULT_DISPLAY); 214d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk } catch (RemoteException e) { 215d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk } 216d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk mIsTokenGranted = false; 2178f7f3184d55850b1c4d2036319794151f564e9c0Jason Monk } 218d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk mIsShowingDialog = false; 219d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk mServiceManager.setBindRequested(false); 2208f7f3184d55850b1c4d2036319794151f564e9c0Jason Monk } 221d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk } catch (RemoteException e) { 222d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk // Called through wrapper, won't happen here. 223bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk } 224bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk } 2258f7f3184d55850b1c4d2036319794151f564e9c0Jason Monk 226bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk @Override 227bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk protected void handleDestroy() { 228bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk super.handleDestroy(); 2298f7f3184d55850b1c4d2036319794151f564e9c0Jason Monk if (mIsTokenGranted) { 2308f7f3184d55850b1c4d2036319794151f564e9c0Jason Monk try { 2318f7f3184d55850b1c4d2036319794151f564e9c0Jason Monk if (DEBUG) Log.d(TAG, "Removing token"); 232ac2561e8206ac42921bb6ddbb0a5972fb360e394Wale Ogunwale mWindowManager.removeWindowToken(mToken, DEFAULT_DISPLAY); 2338f7f3184d55850b1c4d2036319794151f564e9c0Jason Monk } catch (RemoteException e) { 2348f7f3184d55850b1c4d2036319794151f564e9c0Jason Monk } 2358f7f3184d55850b1c4d2036319794151f564e9c0Jason Monk } 23666c89c15a0baabf001e69498dbc09903f72cc63dJason Monk mHost.getTileServices().freeService(this, mServiceManager); 2375db8a4142e35d62073d81806ff7317e840e30ebcJason Monk } 2385db8a4142e35d62073d81806ff7317e840e30ebcJason Monk 2395db8a4142e35d62073d81806ff7317e840e30ebcJason Monk @Override 24062b63a02d7ca630e3ad39991ea6550cab57e5d22Jason Monk public State newTileState() { 241eb09362c729aaf5f91597d88de49d099f9f95c60Yoshinori Hirano State state = new State(); 242eb09362c729aaf5f91597d88de49d099f9f95c60Yoshinori Hirano return state; 2435db8a4142e35d62073d81806ff7317e840e30ebcJason Monk } 2445db8a4142e35d62073d81806ff7317e840e30ebcJason Monk 2455db8a4142e35d62073d81806ff7317e840e30ebcJason Monk @Override 24676c67aa361f65dfb2f5e03d06cc1ccebce9cecd9Jason Monk public Intent getLongClickIntent() { 247724214a87828ad0fe8a44db796a1f4e74eeae521Jason Monk Intent i = new Intent(TileService.ACTION_QS_TILE_PREFERENCES); 248724214a87828ad0fe8a44db796a1f4e74eeae521Jason Monk i.setPackage(mComponent.getPackageName()); 249724214a87828ad0fe8a44db796a1f4e74eeae521Jason Monk i = resolveIntent(i); 250724214a87828ad0fe8a44db796a1f4e74eeae521Jason Monk if (i != null) { 2511a74942fbfa36bf62013b51d8ad9fb4c0e44c6f4Jeff Sharkey i.putExtra(Intent.EXTRA_COMPONENT_NAME, mComponent); 2528d2e9a9b304f06a13f949d5eb1951f63fbba2cbbAkira Oshimi i.putExtra(TileService.EXTRA_STATE, mTile.getState()); 253724214a87828ad0fe8a44db796a1f4e74eeae521Jason Monk return i; 254724214a87828ad0fe8a44db796a1f4e74eeae521Jason Monk } 25576c67aa361f65dfb2f5e03d06cc1ccebce9cecd9Jason Monk return new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).setData( 25676c67aa361f65dfb2f5e03d06cc1ccebce9cecd9Jason Monk Uri.fromParts("package", mComponent.getPackageName(), null)); 2575db8a4142e35d62073d81806ff7317e840e30ebcJason Monk } 2585db8a4142e35d62073d81806ff7317e840e30ebcJason Monk 259724214a87828ad0fe8a44db796a1f4e74eeae521Jason Monk private Intent resolveIntent(Intent i) { 260724214a87828ad0fe8a44db796a1f4e74eeae521Jason Monk ResolveInfo result = mContext.getPackageManager().resolveActivityAsUser(i, 0, 261724214a87828ad0fe8a44db796a1f4e74eeae521Jason Monk ActivityManager.getCurrentUser()); 262724214a87828ad0fe8a44db796a1f4e74eeae521Jason Monk return result != null ? new Intent(TileService.ACTION_QS_TILE_PREFERENCES) 263724214a87828ad0fe8a44db796a1f4e74eeae521Jason Monk .setClassName(result.activityInfo.packageName, result.activityInfo.name) : null; 264724214a87828ad0fe8a44db796a1f4e74eeae521Jason Monk } 265724214a87828ad0fe8a44db796a1f4e74eeae521Jason Monk 2665db8a4142e35d62073d81806ff7317e840e30ebcJason Monk @Override 2675db8a4142e35d62073d81806ff7317e840e30ebcJason Monk protected void handleClick() { 2689429513cc3ea6e58e330865bd621b57cb3477551Jason Monk if (mTile.getState() == Tile.STATE_UNAVAILABLE) { 2699429513cc3ea6e58e330865bd621b57cb3477551Jason Monk return; 2709429513cc3ea6e58e330865bd621b57cb3477551Jason Monk } 271fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk try { 272fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk if (DEBUG) Log.d(TAG, "Adding token"); 273ac2561e8206ac42921bb6ddbb0a5972fb360e394Wale Ogunwale mWindowManager.addWindowToken(mToken, TYPE_QS_DIALOG, DEFAULT_DISPLAY); 274fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk mIsTokenGranted = true; 275fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk } catch (RemoteException e) { 276fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk } 277fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk try { 27897d2272be0bf3e77474e7ff9984217dfe247f4d0Jason Monk if (mServiceManager.isActiveTile()) { 279fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk mServiceManager.setBindRequested(true); 280fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk mService.onStartListening(); 281d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk } 282fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk mService.onClick(mToken); 283fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk } catch (RemoteException e) { 284fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk // Called through wrapper, won't happen here. 285bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk } 2865db8a4142e35d62073d81806ff7317e840e30ebcJason Monk } 2875db8a4142e35d62073d81806ff7317e840e30ebcJason Monk 2885db8a4142e35d62073d81806ff7317e840e30ebcJason Monk @Override 28939c98e652fd3e0e3ea3a334d60ba6ee979985978Jason Monk public CharSequence getTileLabel() { 29039c98e652fd3e0e3ea3a334d60ba6ee979985978Jason Monk return getState().label; 29139c98e652fd3e0e3ea3a334d60ba6ee979985978Jason Monk } 29239c98e652fd3e0e3ea3a334d60ba6ee979985978Jason Monk 29339c98e652fd3e0e3ea3a334d60ba6ee979985978Jason Monk @Override 2945db8a4142e35d62073d81806ff7317e840e30ebcJason Monk protected void handleUpdateState(State state, Object arg) { 2951c2fea8df7054d1bc23945c38460a123ce453c4aJason Monk int tileState = mTile.getState(); 2961c2fea8df7054d1bc23945c38460a123ce453c4aJason Monk if (mServiceManager.hasPendingBind()) { 2971c2fea8df7054d1bc23945c38460a123ce453c4aJason Monk tileState = Tile.STATE_UNAVAILABLE; 2981c2fea8df7054d1bc23945c38460a123ce453c4aJason Monk } 2993250885c9feb9b42d60058a823c57a027c0dcf58Jason Monk state.state = tileState; 3004a906f9da6f49ef26f6bd4690338a19ade7cd55fJason Monk Drawable drawable; 3014a906f9da6f49ef26f6bd4690338a19ade7cd55fJason Monk try { 3023250885c9feb9b42d60058a823c57a027c0dcf58Jason Monk drawable = mTile.getIcon().loadDrawable(mContext); 3034a906f9da6f49ef26f6bd4690338a19ade7cd55fJason Monk } catch (Exception e) { 3044a906f9da6f49ef26f6bd4690338a19ade7cd55fJason Monk Log.w(TAG, "Invalid icon, forcing into unavailable state"); 3056f352aab0e191d784befbd9aded1e7985a207b55Jason Monk state.state = Tile.STATE_UNAVAILABLE; 3063250885c9feb9b42d60058a823c57a027c0dcf58Jason Monk drawable = mDefaultIcon.loadDrawable(mContext); 3074a906f9da6f49ef26f6bd4690338a19ade7cd55fJason Monk } 308b3daf2b88c86243db85dd8425df8d3a59d3b3d4dEvan Laird 309b3daf2b88c86243db85dd8425df8d3a59d3b3d4dEvan Laird final Drawable drawableF = drawable; 310b3daf2b88c86243db85dd8425df8d3a59d3b3d4dEvan Laird state.iconSupplier = () -> { 311b3daf2b88c86243db85dd8425df8d3a59d3b3d4dEvan Laird Drawable.ConstantState cs = drawableF.getConstantState(); 312b3daf2b88c86243db85dd8425df8d3a59d3b3d4dEvan Laird if (cs != null) { 313b3daf2b88c86243db85dd8425df8d3a59d3b3d4dEvan Laird return new DrawableIcon(cs.newDrawable()); 314b3daf2b88c86243db85dd8425df8d3a59d3b3d4dEvan Laird } 315b3daf2b88c86243db85dd8425df8d3a59d3b3d4dEvan Laird return null; 316b3daf2b88c86243db85dd8425df8d3a59d3b3d4dEvan Laird }; 317bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk state.label = mTile.getLabel(); 318bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk if (mTile.getContentDescription() != null) { 319bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk state.contentDescription = mTile.getContentDescription(); 320bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk } else { 3215db8a4142e35d62073d81806ff7317e840e30ebcJason Monk state.contentDescription = state.label; 3225db8a4142e35d62073d81806ff7317e840e30ebcJason Monk } 3235db8a4142e35d62073d81806ff7317e840e30ebcJason Monk } 3245db8a4142e35d62073d81806ff7317e840e30ebcJason Monk 3255db8a4142e35d62073d81806ff7317e840e30ebcJason Monk @Override 3265db8a4142e35d62073d81806ff7317e840e30ebcJason Monk public int getMetricsCategory() { 327f6e9228b8a97603d3ceb8f0d61e8d87cf19bd21fChris Wren return MetricsEvent.QS_CUSTOM; 3285db8a4142e35d62073d81806ff7317e840e30ebcJason Monk } 329bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk 3309429513cc3ea6e58e330865bd621b57cb3477551Jason Monk public void startUnlockAndRun() { 3319c7844cb91b43929d0a86b1c90aa1efb37f5463aJason Monk Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() -> { 3329c7844cb91b43929d0a86b1c90aa1efb37f5463aJason Monk try { 3339c7844cb91b43929d0a86b1c90aa1efb37f5463aJason Monk mService.onUnlockComplete(); 3349c7844cb91b43929d0a86b1c90aa1efb37f5463aJason Monk } catch (RemoteException e) { 3359429513cc3ea6e58e330865bd621b57cb3477551Jason Monk } 3369429513cc3ea6e58e330865bd621b57cb3477551Jason Monk }); 3379429513cc3ea6e58e330865bd621b57cb3477551Jason Monk } 3389429513cc3ea6e58e330865bd621b57cb3477551Jason Monk 3397e53f20c835ae2614c92717a6fc222b345c75036Jason Monk public static String toSpec(ComponentName name) { 3407e53f20c835ae2614c92717a6fc222b345c75036Jason Monk return PREFIX + name.flattenToShortString() + ")"; 3417e53f20c835ae2614c92717a6fc222b345c75036Jason Monk } 3427e53f20c835ae2614c92717a6fc222b345c75036Jason Monk 343bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk public static ComponentName getComponentFromSpec(String spec) { 344bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk final String action = spec.substring(PREFIX.length(), spec.length() - 1); 345bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk if (action.isEmpty()) { 346bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk throw new IllegalArgumentException("Empty custom tile spec action"); 347bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk } 348bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk return ComponentName.unflattenFromString(action); 349bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk } 350bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk 3518c4a19c8c6b57d4c6dbbc652d887881e21bf4ec8Amin Shaikh public static CustomTile create(QSTileHost host, String spec) { 352bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk if (spec == null || !spec.startsWith(PREFIX) || !spec.endsWith(")")) { 353bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk throw new IllegalArgumentException("Bad custom tile spec: " + spec); 354bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk } 355bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk final String action = spec.substring(PREFIX.length(), spec.length() - 1); 356bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk if (action.isEmpty()) { 357bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk throw new IllegalArgumentException("Empty custom tile spec action"); 358bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk } 359bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk return new CustomTile(host, action); 360bbadff8603ca6922a0ef89338bee5b59d6dcf641Jason Monk } 3615db8a4142e35d62073d81806ff7317e840e30ebcJason Monk} 362