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