blob: 0cd6490614bd275eb9a9f896aa57dab0cc16010a [file] [log] [blame]
Jason Monk62b63a02016-02-02 15:15:31 -05001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.qs.customize;
18
Jason Monkb53b6c52016-02-24 17:25:49 -050019import android.Manifest.permission;
Jason Monk62b63a02016-02-02 15:15:31 -050020import android.app.ActivityManager;
21import android.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.content.pm.PackageManager;
25import android.content.pm.ResolveInfo;
26import android.graphics.drawable.Drawable;
27import android.os.AsyncTask;
28import android.os.Handler;
29import android.os.Looper;
30import android.service.quicksettings.TileService;
Jason Monk50a352b2016-06-13 12:43:47 -040031import android.widget.Button;
32
Jason Monk62b63a02016-02-02 15:15:31 -050033import com.android.systemui.R;
34import com.android.systemui.qs.QSTile;
35import com.android.systemui.qs.QSTile.DrawableIcon;
Jason Monk0b349ad2016-04-04 11:18:03 -040036import com.android.systemui.qs.QSTile.State;
Jason Monk62b63a02016-02-02 15:15:31 -050037import com.android.systemui.qs.external.CustomTile;
38import com.android.systemui.statusbar.phone.QSTileHost;
39
40import java.util.ArrayList;
41import java.util.Collection;
42import java.util.List;
43
44public class TileQueryHelper {
45
46 private static final String TAG = "TileQueryHelper";
47
48 private final ArrayList<TileInfo> mTiles = new ArrayList<>();
Jason Monke24194f2016-02-05 14:55:05 -050049 private final ArrayList<String> mSpecs = new ArrayList<>();
Jason Monk62b63a02016-02-02 15:15:31 -050050 private final Context mContext;
51 private TileStateListener mListener;
52
53 public TileQueryHelper(Context context, QSTileHost host) {
54 mContext = context;
55 addSystemTiles(host);
56 // TODO: Live?
57 }
58
Jason Monk21582d42016-04-21 16:14:27 -040059 private void addSystemTiles(final QSTileHost host) {
Will Harmonbeb1f172016-06-06 14:52:40 -070060 String possible = mContext.getString(R.string.quick_settings_tiles_stock);
Jason Monk62b63a02016-02-02 15:15:31 -050061 String[] possibleTiles = possible.split(",");
62 final Handler qsHandler = new Handler(host.getLooper());
63 final Handler mainHandler = new Handler(Looper.getMainLooper());
64 for (int i = 0; i < possibleTiles.length; i++) {
65 final String spec = possibleTiles[i];
66 final QSTile<?> tile = host.createTile(spec);
Yuta Yamada99fd2772016-10-06 17:40:19 +090067 if (tile == null) {
68 continue;
69 } else if (!tile.isAvailable()) {
70 tile.destroy();
Jason Monk62b63a02016-02-02 15:15:31 -050071 continue;
72 }
Jason Monke5107a32016-05-31 15:40:58 -040073 tile.setListening(this, true);
Jason Monk62b63a02016-02-02 15:15:31 -050074 tile.clearState();
75 tile.refreshState();
Jason Monke5107a32016-05-31 15:40:58 -040076 tile.setListening(this, false);
Jason Monk62b63a02016-02-02 15:15:31 -050077 qsHandler.post(new Runnable() {
78 @Override
79 public void run() {
80 final QSTile.State state = tile.newTileState();
81 tile.getState().copyTo(state);
Jason Monk39c98e62016-03-16 09:18:35 -040082 // Ignore the current state and get the generic label instead.
83 state.label = tile.getTileLabel();
Yuta Yamada99fd2772016-10-06 17:40:19 +090084 tile.destroy();
Jason Monk62b63a02016-02-02 15:15:31 -050085 mainHandler.post(new Runnable() {
86 @Override
87 public void run() {
Jason Monk0b349ad2016-04-04 11:18:03 -040088 addTile(spec, null, state, true);
Jason Monk62b63a02016-02-02 15:15:31 -050089 mListener.onTilesChanged(mTiles);
90 }
91 });
92 }
93 });
94 }
95 qsHandler.post(new Runnable() {
96 @Override
97 public void run() {
Jason Monkcb654cb2016-02-25 15:43:07 -050098 mainHandler.post(new Runnable() {
99 @Override
100 public void run() {
Jason Monk21582d42016-04-21 16:14:27 -0400101 new QueryTilesTask().execute(host.getTiles());
Jason Monkcb654cb2016-02-25 15:43:07 -0500102 }
103 });
Jason Monk62b63a02016-02-02 15:15:31 -0500104 }
105 });
106 }
107
108 public void setListener(TileStateListener listener) {
109 mListener = listener;
110 }
111
Jason Monk0b349ad2016-04-04 11:18:03 -0400112 private void addTile(String spec, CharSequence appLabel, State state, boolean isSystem) {
Jason Monke24194f2016-02-05 14:55:05 -0500113 if (mSpecs.contains(spec)) {
114 return;
115 }
Jason Monk62b63a02016-02-02 15:15:31 -0500116 TileInfo info = new TileInfo();
117 info.state = state;
Jason Monk50a352b2016-06-13 12:43:47 -0400118 info.state.minimalAccessibilityClassName = info.state.expandedAccessibilityClassName =
119 Button.class.getName();
Jason Monk62b63a02016-02-02 15:15:31 -0500120 info.spec = spec;
Jason Monk0b349ad2016-04-04 11:18:03 -0400121 info.appLabel = appLabel;
122 info.isSystem = isSystem;
Jason Monke24194f2016-02-05 14:55:05 -0500123 mTiles.add(info);
124 mSpecs.add(spec);
Jason Monk62b63a02016-02-02 15:15:31 -0500125 }
126
Jason Monk0b349ad2016-04-04 11:18:03 -0400127 private void addTile(String spec, Drawable drawable, CharSequence label, CharSequence appLabel,
128 Context context) {
Jason Monk62b63a02016-02-02 15:15:31 -0500129 QSTile.State state = new QSTile.State();
130 state.label = label;
131 state.contentDescription = label;
132 state.icon = new DrawableIcon(drawable);
Yoshinori Hiranoeb093622016-08-17 14:09:58 +0900133 state.autoMirrorDrawable = false;
Jason Monk0b349ad2016-04-04 11:18:03 -0400134 addTile(spec, appLabel, state, false);
Jason Monk62b63a02016-02-02 15:15:31 -0500135 }
136
137 public static class TileInfo {
138 public String spec;
Jason Monk0b349ad2016-04-04 11:18:03 -0400139 public CharSequence appLabel;
Jason Monk62b63a02016-02-02 15:15:31 -0500140 public QSTile.State state;
Jason Monk0b349ad2016-04-04 11:18:03 -0400141 public boolean isSystem;
Jason Monk62b63a02016-02-02 15:15:31 -0500142 }
143
Jason Monk21582d42016-04-21 16:14:27 -0400144 private class QueryTilesTask extends
145 AsyncTask<Collection<QSTile<?>>, Void, Collection<TileInfo>> {
Jason Monk62b63a02016-02-02 15:15:31 -0500146 @Override
Jason Monk21582d42016-04-21 16:14:27 -0400147 protected Collection<TileInfo> doInBackground(Collection<QSTile<?>>... params) {
Jason Monk62b63a02016-02-02 15:15:31 -0500148 List<TileInfo> tiles = new ArrayList<>();
149 PackageManager pm = mContext.getPackageManager();
150 List<ResolveInfo> services = pm.queryIntentServicesAsUser(
151 new Intent(TileService.ACTION_QS_TILE), 0, ActivityManager.getCurrentUser());
Will Harmonbeb1f172016-06-06 14:52:40 -0700152 String stockTiles = mContext.getString(R.string.quick_settings_tiles_stock);
Jason Monk62b63a02016-02-02 15:15:31 -0500153 for (ResolveInfo info : services) {
154 String packageName = info.serviceInfo.packageName;
155 ComponentName componentName = new ComponentName(packageName, info.serviceInfo.name);
Will Harmonbeb1f172016-06-06 14:52:40 -0700156
157 // Don't include apps that are a part of the default tile set.
158 if (stockTiles.contains(componentName.flattenToString())) {
159 continue;
160 }
161
Jason Monk21582d42016-04-21 16:14:27 -0400162 final CharSequence appLabel = info.serviceInfo.applicationInfo.loadLabel(pm);
Jason Monk62b63a02016-02-02 15:15:31 -0500163 String spec = CustomTile.toSpec(componentName);
Jason Monk21582d42016-04-21 16:14:27 -0400164 State state = getState(params[0], spec);
165 if (state != null) {
166 addTile(spec, appLabel, state, false);
167 continue;
168 }
Jason Monka5f6ed32016-06-22 09:58:46 -0400169 if (info.serviceInfo.icon == 0 && info.serviceInfo.applicationInfo.icon == 0) {
Jason Monk4a906f92016-04-20 10:54:55 -0400170 continue;
171 }
Jason Monk62b63a02016-02-02 15:15:31 -0500172 Drawable icon = info.serviceInfo.loadIcon(pm);
Jason Monkb53b6c52016-02-24 17:25:49 -0500173 if (!permission.BIND_QUICK_SETTINGS_TILE.equals(info.serviceInfo.permission)) {
174 continue;
175 }
Jason Monk4a906f92016-04-20 10:54:55 -0400176 if (icon == null) {
177 continue;
Jason Monk62b63a02016-02-02 15:15:31 -0500178 }
Jason Monk4a906f92016-04-20 10:54:55 -0400179 icon.mutate();
180 icon.setTint(mContext.getColor(android.R.color.white));
Jason Monk62b63a02016-02-02 15:15:31 -0500181 CharSequence label = info.serviceInfo.loadLabel(pm);
Jason Monk0b349ad2016-04-04 11:18:03 -0400182 addTile(spec, icon, label != null ? label.toString() : "null", appLabel, mContext);
Jason Monk62b63a02016-02-02 15:15:31 -0500183 }
184 return tiles;
185 }
186
Jason Monk21582d42016-04-21 16:14:27 -0400187 private State getState(Collection<QSTile<?>> tiles, String spec) {
188 for (QSTile<?> tile : tiles) {
189 if (spec.equals(tile.getTileSpec())) {
190 final QSTile.State state = tile.newTileState();
191 tile.getState().copyTo(state);
192 return state;
193 }
194 }
195 return null;
196 }
197
Jason Monk62b63a02016-02-02 15:15:31 -0500198 @Override
199 protected void onPostExecute(Collection<TileInfo> result) {
200 mTiles.addAll(result);
201 mListener.onTilesChanged(mTiles);
202 }
203 }
204
205 public interface TileStateListener {
206 void onTilesChanged(List<TileInfo> tiles);
207 }
208}