• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.systemui.qs.customize;
18 
19 import android.Manifest.permission;
20 import android.app.ActivityManager;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.pm.PackageManager;
25 import android.content.pm.ResolveInfo;
26 import android.graphics.drawable.Drawable;
27 import android.os.AsyncTask;
28 import android.os.Handler;
29 import android.os.Looper;
30 import android.service.quicksettings.TileService;
31 import android.widget.Button;
32 
33 import com.android.systemui.R;
34 import com.android.systemui.qs.QSTile;
35 import com.android.systemui.qs.QSTile.DrawableIcon;
36 import com.android.systemui.qs.QSTile.State;
37 import com.android.systemui.qs.external.CustomTile;
38 import com.android.systemui.statusbar.phone.QSTileHost;
39 
40 import java.util.ArrayList;
41 import java.util.Collection;
42 import java.util.List;
43 
44 public class TileQueryHelper {
45 
46     private static final String TAG = "TileQueryHelper";
47 
48     private final ArrayList<TileInfo> mTiles = new ArrayList<>();
49     private final ArrayList<String> mSpecs = new ArrayList<>();
50     private final Context mContext;
51     private TileStateListener mListener;
52 
TileQueryHelper(Context context, QSTileHost host)53     public TileQueryHelper(Context context, QSTileHost host) {
54         mContext = context;
55         addSystemTiles(host);
56         // TODO: Live?
57     }
58 
addSystemTiles(final QSTileHost host)59     private void addSystemTiles(final QSTileHost host) {
60         String possible = mContext.getString(R.string.quick_settings_tiles_stock);
61         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);
67             if (tile == null || !tile.isAvailable()) {
68                 continue;
69             }
70             tile.setListening(this, true);
71             tile.clearState();
72             tile.refreshState();
73             tile.setListening(this, false);
74             qsHandler.post(new Runnable() {
75                 @Override
76                 public void run() {
77                     final QSTile.State state = tile.newTileState();
78                     tile.getState().copyTo(state);
79                     // Ignore the current state and get the generic label instead.
80                     state.label = tile.getTileLabel();
81                     mainHandler.post(new Runnable() {
82                         @Override
83                         public void run() {
84                             addTile(spec, null, state, true);
85                             mListener.onTilesChanged(mTiles);
86                         }
87                     });
88                 }
89             });
90         }
91         qsHandler.post(new Runnable() {
92             @Override
93             public void run() {
94                 mainHandler.post(new Runnable() {
95                     @Override
96                     public void run() {
97                         new QueryTilesTask().execute(host.getTiles());
98                     }
99                 });
100             }
101         });
102     }
103 
setListener(TileStateListener listener)104     public void setListener(TileStateListener listener) {
105         mListener = listener;
106     }
107 
addTile(String spec, CharSequence appLabel, State state, boolean isSystem)108     private void addTile(String spec, CharSequence appLabel, State state, boolean isSystem) {
109         if (mSpecs.contains(spec)) {
110             return;
111         }
112         TileInfo info = new TileInfo();
113         info.state = state;
114         info.state.minimalAccessibilityClassName = info.state.expandedAccessibilityClassName =
115                 Button.class.getName();
116         info.spec = spec;
117         info.appLabel = appLabel;
118         info.isSystem = isSystem;
119         mTiles.add(info);
120         mSpecs.add(spec);
121     }
122 
addTile(String spec, Drawable drawable, CharSequence label, CharSequence appLabel, Context context)123     private void addTile(String spec, Drawable drawable, CharSequence label, CharSequence appLabel,
124             Context context) {
125         QSTile.State state = new QSTile.State();
126         state.label = label;
127         state.contentDescription = label;
128         state.icon = new DrawableIcon(drawable);
129         addTile(spec, appLabel, state, false);
130     }
131 
132     public static class TileInfo {
133         public String spec;
134         public CharSequence appLabel;
135         public QSTile.State state;
136         public boolean isSystem;
137     }
138 
139     private class QueryTilesTask extends
140             AsyncTask<Collection<QSTile<?>>, Void, Collection<TileInfo>> {
141         @Override
doInBackground(Collection<QSTile<?>>.... params)142         protected Collection<TileInfo> doInBackground(Collection<QSTile<?>>... params) {
143             List<TileInfo> tiles = new ArrayList<>();
144             PackageManager pm = mContext.getPackageManager();
145             List<ResolveInfo> services = pm.queryIntentServicesAsUser(
146                     new Intent(TileService.ACTION_QS_TILE), 0, ActivityManager.getCurrentUser());
147             String stockTiles = mContext.getString(R.string.quick_settings_tiles_stock);
148             for (ResolveInfo info : services) {
149                 String packageName = info.serviceInfo.packageName;
150                 ComponentName componentName = new ComponentName(packageName, info.serviceInfo.name);
151 
152                 // Don't include apps that are a part of the default tile set.
153                 if (stockTiles.contains(componentName.flattenToString())) {
154                     continue;
155                 }
156 
157                 final CharSequence appLabel = info.serviceInfo.applicationInfo.loadLabel(pm);
158                 String spec = CustomTile.toSpec(componentName);
159                 State state = getState(params[0], spec);
160                 if (state != null) {
161                     addTile(spec, appLabel, state, false);
162                     continue;
163                 }
164                 if (info.serviceInfo.icon == 0 && info.serviceInfo.applicationInfo.icon == 0) {
165                     continue;
166                 }
167                 Drawable icon = info.serviceInfo.loadIcon(pm);
168                 if (!permission.BIND_QUICK_SETTINGS_TILE.equals(info.serviceInfo.permission)) {
169                     continue;
170                 }
171                 if (icon == null) {
172                     continue;
173                 }
174                 icon.mutate();
175                 icon.setTint(mContext.getColor(android.R.color.white));
176                 CharSequence label = info.serviceInfo.loadLabel(pm);
177                 addTile(spec, icon, label != null ? label.toString() : "null", appLabel, mContext);
178             }
179             return tiles;
180         }
181 
getState(Collection<QSTile<?>> tiles, String spec)182         private State getState(Collection<QSTile<?>> tiles, String spec) {
183             for (QSTile<?> tile : tiles) {
184                 if (spec.equals(tile.getTileSpec())) {
185                     final QSTile.State state = tile.newTileState();
186                     tile.getState().copyTo(state);
187                     return state;
188                 }
189             }
190             return null;
191         }
192 
193         @Override
onPostExecute(Collection<TileInfo> result)194         protected void onPostExecute(Collection<TileInfo> result) {
195             mTiles.addAll(result);
196             mListener.onTilesChanged(mTiles);
197         }
198     }
199 
200     public interface TileStateListener {
onTilesChanged(List<TileInfo> tiles)201         void onTilesChanged(List<TileInfo> tiles);
202     }
203 }
204