blob: f54ba8c947b63f50eea9519bde96c19149f02e38 [file] [log] [blame]
Jason Monk744b6362015-11-03 18:24:29 -05001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
Jason Monkf509d7e2016-01-07 16:22:53 -05004 * 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
Jason Monk744b6362015-11-03 18:24:29 -05007 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
Jason Monkf509d7e2016-01-07 16:22:53 -050010 * 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
Jason Monk744b6362015-11-03 18:24:29 -050015 */
Jason Monk744b6362015-11-03 18:24:29 -050016package com.android.settingslib.drawer;
17
Jason Monk0d72d202015-11-04 13:16:00 -050018import android.app.ActivityManager;
Jason Monk744b6362015-11-03 18:24:29 -050019import android.content.Context;
Shahriyar Amini676add42016-12-16 11:29:39 -080020import android.content.IContentProvider;
Jason Monk744b6362015-11-03 18:24:29 -050021import android.content.Intent;
22import android.content.pm.ActivityInfo;
Jason Monke79790b2015-12-02 15:39:19 -050023import android.content.pm.ApplicationInfo;
Jason Monk744b6362015-11-03 18:24:29 -050024import android.content.pm.PackageManager;
25import android.content.pm.ResolveInfo;
26import android.content.res.Resources;
27import android.graphics.drawable.Icon;
Shahriyar Amini676add42016-12-16 11:29:39 -080028import android.net.Uri;
Jason Monk744b6362015-11-03 18:24:29 -050029import android.os.Bundle;
Shahriyar Amini676add42016-12-16 11:29:39 -080030import android.os.RemoteException;
Jason Monk744b6362015-11-03 18:24:29 -050031import android.os.UserHandle;
32import android.os.UserManager;
Jason Monk64600cf2016-06-30 13:15:48 -040033import android.provider.Settings.Global;
Jason Monk744b6362015-11-03 18:24:29 -050034import android.text.TextUtils;
35import android.util.Log;
36import android.util.Pair;
37
38import java.util.ArrayList;
39import java.util.Collections;
40import java.util.Comparator;
41import java.util.HashMap;
42import java.util.List;
43import java.util.Map;
44
45public class TileUtils {
46
Jason Monk0d72d202015-11-04 13:16:00 -050047 private static final boolean DEBUG = false;
Joe Onorato93dcff02016-02-01 17:44:14 -080048 private static final boolean DEBUG_TIMING = false;
Jason Monk744b6362015-11-03 18:24:29 -050049
50 private static final String LOG_TAG = "TileUtils";
51
52 /**
53 * Settings will search for system activities of this action and add them as a top level
54 * settings tile using the following parameters.
55 *
56 * <p>A category must be specified in the meta-data for the activity named
57 * {@link #EXTRA_CATEGORY_KEY}
58 *
59 * <p>The title may be defined by meta-data named {@link #META_DATA_PREFERENCE_TITLE}
60 * otherwise the label for the activity will be used.
61 *
62 * <p>The icon may be defined by meta-data named {@link #META_DATA_PREFERENCE_ICON}
63 * otherwise the icon for the activity will be used.
64 *
65 * <p>A summary my be defined by meta-data named {@link #META_DATA_PREFERENCE_SUMMARY}
66 */
Lujiang Xue770ca1a2017-10-24 09:49:48 -070067 public static final String EXTRA_SETTINGS_ACTION =
Jason Monk744b6362015-11-03 18:24:29 -050068 "com.android.settings.action.EXTRA_SETTINGS";
69
70 /**
Fan Zhangca60fac2016-11-02 15:54:53 -070071 * @See {@link #EXTRA_SETTINGS_ACTION}.
72 */
73 private static final String IA_SETTINGS_ACTION =
74 "com.android.settings.action.IA_SETTINGS";
75
76
77 /**
Jason Monk744b6362015-11-03 18:24:29 -050078 * Same as #EXTRA_SETTINGS_ACTION but used for the platform Settings activities.
79 */
80 private static final String SETTINGS_ACTION =
81 "com.android.settings.action.SETTINGS";
82
83 private static final String OPERATOR_SETTINGS =
84 "com.android.settings.OPERATOR_APPLICATION_SETTING";
85
86 private static final String OPERATOR_DEFAULT_CATEGORY =
87 "com.android.settings.category.wireless";
88
89 private static final String MANUFACTURER_SETTINGS =
90 "com.android.settings.MANUFACTURER_APPLICATION_SETTING";
91
92 private static final String MANUFACTURER_DEFAULT_CATEGORY =
93 "com.android.settings.category.device";
94
95 /**
96 * The key used to get the category from metadata of activities of action
97 * {@link #EXTRA_SETTINGS_ACTION}
Fan Zhang6f6e9562018-06-18 11:30:00 -070098 * The value must be from {@link CategoryKey}.
Jason Monk744b6362015-11-03 18:24:29 -050099 */
100 private static final String EXTRA_CATEGORY_KEY = "com.android.settings.category";
101
102 /**
Shahriyar Amini778cf1d2017-01-18 18:52:27 -0800103 * The key used to get the package name of the icon resource for the preference.
104 */
105 private static final String EXTRA_PREFERENCE_ICON_PACKAGE =
106 "com.android.settings.icon_package";
107
108 /**
Jason Monk744b6362015-11-03 18:24:29 -0500109 * Name of the meta-data item that should be set in the AndroidManifest.xml
Shahriyar Amini6b32ae32016-11-22 14:49:04 -0800110 * to specify the key that should be used for the preference.
111 */
112 public static final String META_DATA_PREFERENCE_KEYHINT = "com.android.settings.keyhint";
113
114 /**
115 * Name of the meta-data item that should be set in the AndroidManifest.xml
Jason Monk744b6362015-11-03 18:24:29 -0500116 * to specify the icon that should be displayed for the preference.
117 */
118 public static final String META_DATA_PREFERENCE_ICON = "com.android.settings.icon";
119
120 /**
121 * Name of the meta-data item that should be set in the AndroidManifest.xml
Fan Zhanga8d23aa2018-04-12 14:01:26 -0700122 * to specify the icon background color. The value may or may not be used by Settings app.
123 */
124 public static final String META_DATA_PREFERENCE_ICON_BACKGROUND_HINT =
125 "com.android.settings.bg.hint";
126
127 /**
128 * Name of the meta-data item that should be set in the AndroidManifest.xml
Shahriyar Amini676add42016-12-16 11:29:39 -0800129 * to specify the content provider providing the icon that should be displayed for
130 * the preference.
131 *
132 * Icon provided by the content provider overrides any static icon.
133 */
134 public static final String META_DATA_PREFERENCE_ICON_URI = "com.android.settings.icon_uri";
135
136 /**
137 * Name of the meta-data item that should be set in the AndroidManifest.xml
Maurice Lamb10c6ff2017-06-08 20:29:21 -0700138 * to specify whether the icon is tintable. This should be a boolean value {@code true} or
139 * {@code false}, set using {@code android:value}
Jason Monk744b6362015-11-03 18:24:29 -0500140 */
Maurice Lamb10c6ff2017-06-08 20:29:21 -0700141 public static final String META_DATA_PREFERENCE_ICON_TINTABLE =
142 "com.android.settings.icon_tintable";
Jason Monk744b6362015-11-03 18:24:29 -0500143
144 /**
145 * Name of the meta-data item that should be set in the AndroidManifest.xml
William Luh4c978a32017-03-31 15:08:16 -0700146 * to specify the title that should be displayed for the preference.
Maurice Lamb10c6ff2017-06-08 20:29:21 -0700147 *
148 * <p>Note: It is preferred to provide this value using {@code android:resource} with a string
149 * resource for localization.
William Luh4c978a32017-03-31 15:08:16 -0700150 */
Maurice Lamb10c6ff2017-06-08 20:29:21 -0700151 public static final String META_DATA_PREFERENCE_TITLE = "com.android.settings.title";
152
153 /**
William Luh4c978a32017-03-31 15:08:16 -0700154 * Name of the meta-data item that should be set in the AndroidManifest.xml
Jason Monk744b6362015-11-03 18:24:29 -0500155 * to specify the summary text that should be displayed for the preference.
156 */
157 public static final String META_DATA_PREFERENCE_SUMMARY = "com.android.settings.summary";
158
Shahriyar Amini676add42016-12-16 11:29:39 -0800159 /**
160 * Name of the meta-data item that should be set in the AndroidManifest.xml
161 * to specify the content provider providing the summary text that should be displayed for the
162 * preference.
163 *
164 * Summary provided by the content provider overrides any static summary.
165 */
166 public static final String META_DATA_PREFERENCE_SUMMARY_URI =
167 "com.android.settings.summary_uri";
168
Fan Zhang22a56d72016-09-27 17:52:00 -0700169 /**
arangelov24eec2f2018-05-30 18:24:23 +0100170 * Value for {@link #META_DATA_KEY_PROFILE}. When the device has a managed profile,
171 * the app will always be run in the primary profile.
172 *
173 * @see #META_DATA_KEY_PROFILE
174 */
175 public static final String PROFILE_PRIMARY = "primary_profile_only";
176
177 /**
178 * Value for {@link #META_DATA_KEY_PROFILE}. When the device has a managed profile, the user
179 * will be presented with a dialog to choose the profile the app will be run in.
180 *
181 * @see #META_DATA_KEY_PROFILE
182 */
183 public static final String PROFILE_ALL = "all_profiles";
184
185 /**
186 * Name of the meta-data item that should be set in the AndroidManifest.xml
187 * to specify the profile in which the app should be run when the device has a managed profile.
188 * The default value is {@link #PROFILE_ALL} which means the user will be presented with a
189 * dialog to choose the profile. If set to {@link #PROFILE_PRIMARY} the app will always be
190 * run in the primary profile.
191 *
192 * @see #PROFILE_PRIMARY
193 * @see #PROFILE_ALL
194 */
195 public static final String META_DATA_KEY_PROFILE = "com.android.settings.profile";
196
197 /**
Fan Zhang22a56d72016-09-27 17:52:00 -0700198 * Build a list of DashboardCategory.
roger xue8f06ab02016-12-08 14:09:50 -0800199 * @param extraAction additional intent filter action to be usetileutild to build the dashboard
Doris Ling485df112016-12-19 10:45:47 -0800200 * categories
201 */
202 public static List<DashboardCategory> getCategories(Context context,
Fan Zhangc75174b2018-07-19 14:07:58 -0700203 Map<Pair<String, String>, Tile> cache, String extraAction, String settingPkg) {
Jason Monke79790b2015-12-02 15:39:19 -0500204 final long startTime = System.currentTimeMillis();
Jason Monk64600cf2016-06-30 13:15:48 -0400205 boolean setup = Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0)
206 != 0;
Jason Monkf509d7e2016-01-07 16:22:53 -0500207 ArrayList<Tile> tiles = new ArrayList<>();
roger xue8f06ab02016-12-08 14:09:50 -0800208 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
Jason Monk744b6362015-11-03 18:24:29 -0500209 for (UserHandle user : userManager.getUserProfiles()) {
210 // TODO: Needs much optimization, too many PM queries going on here.
Jason Monk0d72d202015-11-04 13:16:00 -0500211 if (user.getIdentifier() == ActivityManager.getCurrentUser()) {
212 // Only add Settings for this user.
roger xue8f06ab02016-12-08 14:09:50 -0800213 getTilesForAction(context, user, SETTINGS_ACTION, cache, null, tiles, true,
214 settingPkg);
Jason Monk0d72d202015-11-04 13:16:00 -0500215 getTilesForAction(context, user, OPERATOR_SETTINGS, cache,
roger xue8f06ab02016-12-08 14:09:50 -0800216 OPERATOR_DEFAULT_CATEGORY, tiles, false, true, settingPkg);
Jason Monk0d72d202015-11-04 13:16:00 -0500217 getTilesForAction(context, user, MANUFACTURER_SETTINGS, cache,
roger xue8f06ab02016-12-08 14:09:50 -0800218 MANUFACTURER_DEFAULT_CATEGORY, tiles, false, true, settingPkg);
Jason Monk0d72d202015-11-04 13:16:00 -0500219 }
Jason Monk64600cf2016-06-30 13:15:48 -0400220 if (setup) {
roger xue8f06ab02016-12-08 14:09:50 -0800221 getTilesForAction(context, user, EXTRA_SETTINGS_ACTION, cache, null, tiles, false,
222 settingPkg);
Fan Zhang8bfb3ea2017-01-05 14:19:30 -0800223 getTilesForAction(context, user, IA_SETTINGS_ACTION, cache, null, tiles, false,
roger xue8f06ab02016-12-08 14:09:50 -0800224 settingPkg);
Fan Zhang8bfb3ea2017-01-05 14:19:30 -0800225 if (extraAction != null) {
226 getTilesForAction(context, user, extraAction, cache, null, tiles, false,
227 settingPkg);
228 }
Jason Monk64600cf2016-06-30 13:15:48 -0400229 }
Jason Monk744b6362015-11-03 18:24:29 -0500230 }
Fan Zhang22a56d72016-09-27 17:52:00 -0700231
Jason Monk744b6362015-11-03 18:24:29 -0500232 HashMap<String, DashboardCategory> categoryMap = new HashMap<>();
Jason Monkf509d7e2016-01-07 16:22:53 -0500233 for (Tile tile : tiles) {
Jason Monk744b6362015-11-03 18:24:29 -0500234 DashboardCategory category = categoryMap.get(tile.category);
235 if (category == null) {
Fan Zhangc75174b2018-07-19 14:07:58 -0700236 category = new DashboardCategory();
237 category.key = tile.category;
238
Jason Monk744b6362015-11-03 18:24:29 -0500239 if (category == null) {
240 Log.w(LOG_TAG, "Couldn't find category " + tile.category);
241 continue;
242 }
243 categoryMap.put(category.key, category);
244 }
245 category.addTile(tile);
246 }
247 ArrayList<DashboardCategory> categories = new ArrayList<>(categoryMap.values());
248 for (DashboardCategory category : categories) {
Doris Lingb8d2cd42017-11-27 12:24:09 -0800249 category.sortTiles();
Jason Monk744b6362015-11-03 18:24:29 -0500250 }
251 Collections.sort(categories, CATEGORY_COMPARATOR);
Jason Monke79790b2015-12-02 15:39:19 -0500252 if (DEBUG_TIMING) Log.d(LOG_TAG, "getCategories took "
253 + (System.currentTimeMillis() - startTime) + " ms");
Jason Monk744b6362015-11-03 18:24:29 -0500254 return categories;
255 }
256
Jason Monk744b6362015-11-03 18:24:29 -0500257 private static void getTilesForAction(Context context,
Jason Monkf509d7e2016-01-07 16:22:53 -0500258 UserHandle user, String action, Map<Pair<String, String>, Tile> addedCache,
roger xue8f06ab02016-12-08 14:09:50 -0800259 String defaultCategory, ArrayList<Tile> outTiles, boolean requireSettings,
260 String settingPkg) {
Yoshinori Hirano4adbbfc2016-06-06 15:47:47 +0900261 getTilesForAction(context, user, action, addedCache, defaultCategory, outTiles,
roger xue8f06ab02016-12-08 14:09:50 -0800262 requireSettings, requireSettings, settingPkg);
Yoshinori Hirano4adbbfc2016-06-06 15:47:47 +0900263 }
264
265 private static void getTilesForAction(Context context,
266 UserHandle user, String action, Map<Pair<String, String>, Tile> addedCache,
267 String defaultCategory, ArrayList<Tile> outTiles, boolean requireSettings,
roger xue8f06ab02016-12-08 14:09:50 -0800268 boolean usePriority, String settingPkg) {
Jason Monk744b6362015-11-03 18:24:29 -0500269 Intent intent = new Intent(action);
Jason Monkf509d7e2016-01-07 16:22:53 -0500270 if (requireSettings) {
roger xue8f06ab02016-12-08 14:09:50 -0800271 intent.setPackage(settingPkg);
Jason Monkf509d7e2016-01-07 16:22:53 -0500272 }
273 getTilesForIntent(context, user, intent, addedCache, defaultCategory, outTiles,
Maurice Lamb10c6ff2017-06-08 20:29:21 -0700274 usePriority, true, true);
Jason Monkf509d7e2016-01-07 16:22:53 -0500275 }
276
Maurice Lamb10c6ff2017-06-08 20:29:21 -0700277 public static void getTilesForIntent(
278 Context context, UserHandle user, Intent intent,
Jason Monkf509d7e2016-01-07 16:22:53 -0500279 Map<Pair<String, String>, Tile> addedCache, String defaultCategory, List<Tile> outTiles,
Maurice Lamb10c6ff2017-06-08 20:29:21 -0700280 boolean usePriority, boolean checkCategory, boolean forceTintExternalIcon) {
Jason Monkf509d7e2016-01-07 16:22:53 -0500281 PackageManager pm = context.getPackageManager();
Jason Monk744b6362015-11-03 18:24:29 -0500282 List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
283 PackageManager.GET_META_DATA, user.getIdentifier());
Jaewoong Jung78c5e5d2017-06-15 18:02:44 -0700284 Map<String, IContentProvider> providerMap = new HashMap<>();
Jason Monk744b6362015-11-03 18:24:29 -0500285 for (ResolveInfo resolved : results) {
Jason Monkf509d7e2016-01-07 16:22:53 -0500286 if (!resolved.system) {
287 // Do not allow any app to add to settings, only system ones.
288 continue;
Jason Monk744b6362015-11-03 18:24:29 -0500289 }
290 ActivityInfo activityInfo = resolved.activityInfo;
291 Bundle metaData = activityInfo.metaData;
292 String categoryKey = defaultCategory;
Fan Zhangb12e1972016-09-29 14:43:43 -0700293
294 // Load category
295 if (checkCategory && ((metaData == null) || !metaData.containsKey(EXTRA_CATEGORY_KEY))
296 && categoryKey == null) {
Jason Monkf509d7e2016-01-07 16:22:53 -0500297 Log.w(LOG_TAG, "Found " + resolved.activityInfo.name + " for intent "
298 + intent + " missing metadata "
Jason Monk744b6362015-11-03 18:24:29 -0500299 + (metaData == null ? "" : EXTRA_CATEGORY_KEY));
300 continue;
Fan Zhangb12e1972016-09-29 14:43:43 -0700301 } else {
302 categoryKey = metaData.getString(EXTRA_CATEGORY_KEY);
Jason Monk744b6362015-11-03 18:24:29 -0500303 }
Fan Zhangb12e1972016-09-29 14:43:43 -0700304
Fan Zhang1b0dfa32018-07-20 12:57:55 -0700305 Pair<String, String> key = new Pair<>(activityInfo.packageName, activityInfo.name);
Jason Monkf509d7e2016-01-07 16:22:53 -0500306 Tile tile = addedCache.get(key);
Jason Monk744b6362015-11-03 18:24:29 -0500307 if (tile == null) {
Jason Monkf509d7e2016-01-07 16:22:53 -0500308 tile = new Tile();
Jason Monk744b6362015-11-03 18:24:29 -0500309 tile.intent = new Intent().setClassName(
310 activityInfo.packageName, activityInfo.name);
311 tile.category = categoryKey;
Jason Monkf509d7e2016-01-07 16:22:53 -0500312 tile.priority = usePriority ? resolved.priority : 0;
Jason Monke79790b2015-12-02 15:39:19 -0500313 tile.metaData = activityInfo.metaData;
314 updateTileData(context, tile, activityInfo, activityInfo.applicationInfo,
Maurice Lamb10c6ff2017-06-08 20:29:21 -0700315 pm, providerMap, forceTintExternalIcon);
Jason Monk744b6362015-11-03 18:24:29 -0500316 if (DEBUG) Log.d(LOG_TAG, "Adding tile " + tile.title);
Jason Monk744b6362015-11-03 18:24:29 -0500317 addedCache.put(key, tile);
318 }
Ajay Nadathurd1b730b2017-09-14 17:12:27 -0700319
Jason Monk744b6362015-11-03 18:24:29 -0500320 if (!tile.userHandle.contains(user)) {
321 tile.userHandle.add(user);
322 }
323 if (!outTiles.contains(tile)) {
324 outTiles.add(tile);
325 }
326 }
327 }
328
Jason Monkf509d7e2016-01-07 16:22:53 -0500329 private static boolean updateTileData(Context context, Tile tile,
Jaewoong Jung78c5e5d2017-06-15 18:02:44 -0700330 ActivityInfo activityInfo, ApplicationInfo applicationInfo, PackageManager pm,
Maurice Lamb10c6ff2017-06-08 20:29:21 -0700331 Map<String, IContentProvider> providerMap, boolean forceTintExternalIcon) {
Jason Monke79790b2015-12-02 15:39:19 -0500332 if (applicationInfo.isSystemApp()) {
Maurice Lamb10c6ff2017-06-08 20:29:21 -0700333 boolean forceTintIcon = false;
Jason Monke79790b2015-12-02 15:39:19 -0500334 int icon = 0;
Shahriyar Amini778cf1d2017-01-18 18:52:27 -0800335 Pair<String, Integer> iconFromUri = null;
Jason Monke79790b2015-12-02 15:39:19 -0500336 CharSequence title = null;
337 String summary = null;
Shahriyar Amini6b32ae32016-11-22 14:49:04 -0800338 String keyHint = null;
Maurice Lamb10c6ff2017-06-08 20:29:21 -0700339 boolean isIconTintable = false;
Jason Monk744b6362015-11-03 18:24:29 -0500340
Jason Monke79790b2015-12-02 15:39:19 -0500341 // Get the activity's meta-data
342 try {
Maurice Lamb10c6ff2017-06-08 20:29:21 -0700343 Resources res = pm.getResourcesForApplication(applicationInfo.packageName);
Jason Monke79790b2015-12-02 15:39:19 -0500344 Bundle metaData = activityInfo.metaData;
Jason Monk744b6362015-11-03 18:24:29 -0500345
Maurice Lamb10c6ff2017-06-08 20:29:21 -0700346 if (forceTintExternalIcon
347 && !context.getPackageName().equals(applicationInfo.packageName)) {
348 isIconTintable = true;
349 forceTintIcon = true;
350 }
351
Jason Monke79790b2015-12-02 15:39:19 -0500352 if (res != null && metaData != null) {
William Luh204af1c2017-02-23 11:10:05 -0800353 if (metaData.containsKey(META_DATA_PREFERENCE_ICON)) {
Jason Monke79790b2015-12-02 15:39:19 -0500354 icon = metaData.getInt(META_DATA_PREFERENCE_ICON);
Jason Monk744b6362015-11-03 18:24:29 -0500355 }
Maurice Lamb10c6ff2017-06-08 20:29:21 -0700356 if (metaData.containsKey(META_DATA_PREFERENCE_ICON_TINTABLE)) {
357 if (forceTintIcon) {
358 Log.w(LOG_TAG, "Ignoring icon tintable for " + activityInfo);
359 } else {
360 isIconTintable =
361 metaData.getBoolean(META_DATA_PREFERENCE_ICON_TINTABLE);
362 }
363 }
Fan Zhangdadfd502017-07-26 11:00:51 -0700364 if (metaData.containsKey(META_DATA_PREFERENCE_TITLE)) {
Jason Monkf509d7e2016-01-07 16:22:53 -0500365 if (metaData.get(META_DATA_PREFERENCE_TITLE) instanceof Integer) {
366 title = res.getString(metaData.getInt(META_DATA_PREFERENCE_TITLE));
367 } else {
368 title = metaData.getString(META_DATA_PREFERENCE_TITLE);
369 }
Jason Monk744b6362015-11-03 18:24:29 -0500370 }
William Luh204af1c2017-02-23 11:10:05 -0800371 if (metaData.containsKey(META_DATA_PREFERENCE_SUMMARY)) {
Jason Monkf509d7e2016-01-07 16:22:53 -0500372 if (metaData.get(META_DATA_PREFERENCE_SUMMARY) instanceof Integer) {
373 summary = res.getString(metaData.getInt(META_DATA_PREFERENCE_SUMMARY));
374 } else {
375 summary = metaData.getString(META_DATA_PREFERENCE_SUMMARY);
376 }
Jason Monk744b6362015-11-03 18:24:29 -0500377 }
Shahriyar Amini6b32ae32016-11-22 14:49:04 -0800378 if (metaData.containsKey(META_DATA_PREFERENCE_KEYHINT)) {
379 if (metaData.get(META_DATA_PREFERENCE_KEYHINT) instanceof Integer) {
380 keyHint = res.getString(metaData.getInt(META_DATA_PREFERENCE_KEYHINT));
381 } else {
382 keyHint = metaData.getString(META_DATA_PREFERENCE_KEYHINT);
383 }
384 }
Jason Monk744b6362015-11-03 18:24:29 -0500385 }
Jason Monke79790b2015-12-02 15:39:19 -0500386 } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
387 if (DEBUG) Log.d(LOG_TAG, "Couldn't find info", e);
Jason Monk744b6362015-11-03 18:24:29 -0500388 }
Jason Monke79790b2015-12-02 15:39:19 -0500389
390 // Set the preference title to the activity's label if no
391 // meta-data is found
392 if (TextUtils.isEmpty(title)) {
393 title = activityInfo.loadLabel(pm).toString();
394 }
Shahriyar Amini778cf1d2017-01-18 18:52:27 -0800395
396 // Set the icon
Fan Zhangdadfd502017-07-26 11:00:51 -0700397 if (icon == 0) {
398 // Only fallback to activityinfo.icon if metadata does not contain ICON_URI.
399 // ICON_URI should be loaded in app UI when need the icon object.
400 if (!tile.metaData.containsKey(META_DATA_PREFERENCE_ICON_URI)) {
Shahriyar Amini778cf1d2017-01-18 18:52:27 -0800401 icon = activityInfo.icon;
402 }
Fan Zhangdadfd502017-07-26 11:00:51 -0700403 }
404 if (icon != 0) {
Shahriyar Amini778cf1d2017-01-18 18:52:27 -0800405 tile.icon = Icon.createWithResource(activityInfo.packageName, icon);
Jason Monke79790b2015-12-02 15:39:19 -0500406 }
407
Shahriyar Amini778cf1d2017-01-18 18:52:27 -0800408 // Set title and summary for the preference
Jason Monke79790b2015-12-02 15:39:19 -0500409 tile.title = title;
410 tile.summary = summary;
411 // Replace the intent with this specific activity
412 tile.intent = new Intent().setClassName(activityInfo.packageName,
413 activityInfo.name);
Shahriyar Amini6b32ae32016-11-22 14:49:04 -0800414 // Suggest a key for this tile
415 tile.key = keyHint;
Maurice Lamb10c6ff2017-06-08 20:29:21 -0700416 tile.isIconTintable = isIconTintable;
Jason Monke79790b2015-12-02 15:39:19 -0500417
418 return true;
Jason Monk744b6362015-11-03 18:24:29 -0500419 }
420
421 return false;
422 }
423
Shahriyar Amini1fc3aee2016-12-28 08:51:04 -0800424 /**
Shahriyar Amini778cf1d2017-01-18 18:52:27 -0800425 * Gets the icon package name and resource id from content provider.
Fan Zhangdadfd502017-07-26 11:00:51 -0700426 * @param context context
Shahriyar Amini778cf1d2017-01-18 18:52:27 -0800427 * @param packageName package name of the target activity
Shahriyar Amini1fc3aee2016-12-28 08:51:04 -0800428 * @param uriString URI for the content provider
429 * @param providerMap Maps URI authorities to providers
Shahriyar Amini778cf1d2017-01-18 18:52:27 -0800430 * @return package name and resource id of the icon specified
Shahriyar Amini1fc3aee2016-12-28 08:51:04 -0800431 */
Shahriyar Amini778cf1d2017-01-18 18:52:27 -0800432 public static Pair<String, Integer> getIconFromUri(Context context, String packageName,
433 String uriString, Map<String, IContentProvider> providerMap) {
Shahriyar Amini676add42016-12-16 11:29:39 -0800434 Bundle bundle = getBundleFromUri(context, uriString, providerMap);
Shahriyar Amini778cf1d2017-01-18 18:52:27 -0800435 if (bundle == null) {
436 return null;
437 }
438 String iconPackageName = bundle.getString(EXTRA_PREFERENCE_ICON_PACKAGE);
439 if (TextUtils.isEmpty(iconPackageName)) {
440 return null;
441 }
442 int resId = bundle.getInt(META_DATA_PREFERENCE_ICON, 0);
443 if (resId == 0) {
444 return null;
445 }
446 // Icon can either come from the target package or from the Settings app.
447 if (iconPackageName.equals(packageName)
448 || iconPackageName.equals(context.getPackageName())) {
449 return Pair.create(iconPackageName, bundle.getInt(META_DATA_PREFERENCE_ICON, 0));
450 }
451 return null;
Shahriyar Amini676add42016-12-16 11:29:39 -0800452 }
453
Shahriyar Amini1fc3aee2016-12-28 08:51:04 -0800454 /**
455 * Gets text associated with the input key from the content provider.
Fan Zhangdadfd502017-07-26 11:00:51 -0700456 * @param context context
Shahriyar Amini1fc3aee2016-12-28 08:51:04 -0800457 * @param uriString URI for the content provider
458 * @param providerMap Maps URI authorities to providers
459 * @param key Key mapping to the text in bundle returned by the content provider
460 * @return Text associated with the key, if returned by the content provider
461 */
462 public static String getTextFromUri(Context context, String uriString,
Shahriyar Amini676add42016-12-16 11:29:39 -0800463 Map<String, IContentProvider> providerMap, String key) {
464 Bundle bundle = getBundleFromUri(context, uriString, providerMap);
465 return (bundle != null) ? bundle.getString(key) : null;
466 }
467
468 private static Bundle getBundleFromUri(Context context, String uriString,
469 Map<String, IContentProvider> providerMap) {
470 if (TextUtils.isEmpty(uriString)) {
471 return null;
472 }
473 Uri uri = Uri.parse(uriString);
474 String method = getMethodFromUri(uri);
475 if (TextUtils.isEmpty(method)) {
476 return null;
477 }
478 IContentProvider provider = getProviderFromUri(context, uri, providerMap);
479 if (provider == null) {
480 return null;
481 }
482 try {
483 return provider.call(context.getPackageName(), method, uriString, null);
484 } catch (RemoteException e) {
485 return null;
486 }
487 }
488
Ajay Nadathur00932ebb2017-08-30 14:56:46 -0700489 private static String getString(Bundle bundle, String key) {
490 return bundle == null ? null : bundle.getString(key);
491 }
492
Shahriyar Amini676add42016-12-16 11:29:39 -0800493 private static IContentProvider getProviderFromUri(Context context, Uri uri,
494 Map<String, IContentProvider> providerMap) {
495 if (uri == null) {
496 return null;
497 }
498 String authority = uri.getAuthority();
499 if (TextUtils.isEmpty(authority)) {
500 return null;
501 }
502 if (!providerMap.containsKey(authority)) {
William Luh5a0a0d82017-04-18 11:34:38 -0700503 providerMap.put(authority, context.getContentResolver().acquireUnstableProvider(uri));
Shahriyar Amini676add42016-12-16 11:29:39 -0800504 }
505 return providerMap.get(authority);
506 }
507
508 /** Returns the first path segment of the uri if it exists as the method, otherwise null. */
509 static String getMethodFromUri(Uri uri) {
510 if (uri == null) {
511 return null;
512 }
513 List<String> pathSegments = uri.getPathSegments();
514 if ((pathSegments == null) || pathSegments.isEmpty()) {
515 return null;
516 }
517 return pathSegments.get(0);
518 }
519
Jason Monk744b6362015-11-03 18:24:29 -0500520 private static final Comparator<DashboardCategory> CATEGORY_COMPARATOR =
521 new Comparator<DashboardCategory>() {
522 @Override
523 public int compare(DashboardCategory lhs, DashboardCategory rhs) {
524 return rhs.priority - lhs.priority;
525 }
526 };
527}