| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License |
| */ |
| package com.android.settingslib.drawer; |
| |
| import android.app.ActivityManager; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.ActivityInfo; |
| import android.content.pm.ApplicationInfo; |
| import android.content.pm.PackageManager; |
| import android.content.pm.ResolveInfo; |
| import android.content.res.Resources; |
| import android.graphics.drawable.Icon; |
| import android.os.Bundle; |
| import android.os.UserHandle; |
| import android.os.UserManager; |
| import android.text.TextUtils; |
| import android.util.Log; |
| import android.util.Pair; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| public class TileUtils { |
| |
| private static final boolean DEBUG = false; |
| private static final boolean DEBUG_TIMING = false; |
| |
| private static final String LOG_TAG = "TileUtils"; |
| |
| /** |
| * Settings will search for system activities of this action and add them as a top level |
| * settings tile using the following parameters. |
| * |
| * <p>A category must be specified in the meta-data for the activity named |
| * {@link #EXTRA_CATEGORY_KEY} |
| * |
| * <p>The title may be defined by meta-data named {@link #META_DATA_PREFERENCE_TITLE} |
| * otherwise the label for the activity will be used. |
| * |
| * <p>The icon may be defined by meta-data named {@link #META_DATA_PREFERENCE_ICON} |
| * otherwise the icon for the activity will be used. |
| * |
| * <p>A summary my be defined by meta-data named {@link #META_DATA_PREFERENCE_SUMMARY} |
| */ |
| private static final String EXTRA_SETTINGS_ACTION = |
| "com.android.settings.action.EXTRA_SETTINGS"; |
| |
| /** |
| * Same as #EXTRA_SETTINGS_ACTION but used for the platform Settings activities. |
| */ |
| private static final String SETTINGS_ACTION = |
| "com.android.settings.action.SETTINGS"; |
| |
| private static final String OPERATOR_SETTINGS = |
| "com.android.settings.OPERATOR_APPLICATION_SETTING"; |
| |
| private static final String OPERATOR_DEFAULT_CATEGORY = |
| "com.android.settings.category.wireless"; |
| |
| private static final String MANUFACTURER_SETTINGS = |
| "com.android.settings.MANUFACTURER_APPLICATION_SETTING"; |
| |
| private static final String MANUFACTURER_DEFAULT_CATEGORY = |
| "com.android.settings.category.device"; |
| |
| /** |
| * The key used to get the category from metadata of activities of action |
| * {@link #EXTRA_SETTINGS_ACTION} |
| * The value must be one of: |
| * <li>com.android.settings.category.wireless</li> |
| * <li>com.android.settings.category.device</li> |
| * <li>com.android.settings.category.personal</li> |
| * <li>com.android.settings.category.system</li> |
| */ |
| private static final String EXTRA_CATEGORY_KEY = "com.android.settings.category"; |
| |
| /** |
| * Name of the meta-data item that should be set in the AndroidManifest.xml |
| * to specify the icon that should be displayed for the preference. |
| */ |
| public static final String META_DATA_PREFERENCE_ICON = "com.android.settings.icon"; |
| |
| /** |
| * Name of the meta-data item that should be set in the AndroidManifest.xml |
| * to specify the title that should be displayed for the preference. |
| */ |
| public static final String META_DATA_PREFERENCE_TITLE = "com.android.settings.title"; |
| |
| /** |
| * Name of the meta-data item that should be set in the AndroidManifest.xml |
| * to specify the summary text that should be displayed for the preference. |
| */ |
| public static final String META_DATA_PREFERENCE_SUMMARY = "com.android.settings.summary"; |
| |
| private static final String SETTING_PKG = "com.android.settings"; |
| |
| public static List<DashboardCategory> getCategories(Context context, |
| HashMap<Pair<String, String>, Tile> cache) { |
| final long startTime = System.currentTimeMillis(); |
| ArrayList<Tile> tiles = new ArrayList<>(); |
| UserManager userManager = UserManager.get(context); |
| for (UserHandle user : userManager.getUserProfiles()) { |
| // TODO: Needs much optimization, too many PM queries going on here. |
| if (user.getIdentifier() == ActivityManager.getCurrentUser()) { |
| // Only add Settings for this user. |
| getTilesForAction(context, user, SETTINGS_ACTION, cache, null, tiles, true); |
| getTilesForAction(context, user, OPERATOR_SETTINGS, cache, |
| OPERATOR_DEFAULT_CATEGORY, tiles, false); |
| getTilesForAction(context, user, MANUFACTURER_SETTINGS, cache, |
| MANUFACTURER_DEFAULT_CATEGORY, tiles, false); |
| } |
| getTilesForAction(context, user, EXTRA_SETTINGS_ACTION, cache, null, tiles, false); |
| } |
| HashMap<String, DashboardCategory> categoryMap = new HashMap<>(); |
| for (Tile tile : tiles) { |
| DashboardCategory category = categoryMap.get(tile.category); |
| if (category == null) { |
| category = createCategory(context, tile.category); |
| if (category == null) { |
| Log.w(LOG_TAG, "Couldn't find category " + tile.category); |
| continue; |
| } |
| categoryMap.put(category.key, category); |
| } |
| category.addTile(tile); |
| } |
| ArrayList<DashboardCategory> categories = new ArrayList<>(categoryMap.values()); |
| for (DashboardCategory category : categories) { |
| Collections.sort(category.tiles, TILE_COMPARATOR); |
| } |
| Collections.sort(categories, CATEGORY_COMPARATOR); |
| if (DEBUG_TIMING) Log.d(LOG_TAG, "getCategories took " |
| + (System.currentTimeMillis() - startTime) + " ms"); |
| return categories; |
| } |
| |
| private static DashboardCategory createCategory(Context context, String categoryKey) { |
| DashboardCategory category = new DashboardCategory(); |
| category.key = categoryKey; |
| PackageManager pm = context.getPackageManager(); |
| List<ResolveInfo> results = pm.queryIntentActivities(new Intent(categoryKey), 0); |
| if (results.size() == 0) { |
| return null; |
| } |
| for (ResolveInfo resolved : results) { |
| if (!resolved.system) { |
| // Do not allow any app to add to settings, only system ones. |
| continue; |
| } |
| category.title = resolved.activityInfo.loadLabel(pm); |
| category.priority = SETTING_PKG.equals( |
| resolved.activityInfo.applicationInfo.packageName) ? resolved.priority : 0; |
| if (DEBUG) Log.d(LOG_TAG, "Adding category " + category.title); |
| } |
| |
| return category; |
| } |
| |
| private static void getTilesForAction(Context context, |
| UserHandle user, String action, Map<Pair<String, String>, Tile> addedCache, |
| String defaultCategory, ArrayList<Tile> outTiles, boolean requireSettings) { |
| Intent intent = new Intent(action); |
| if (requireSettings) { |
| intent.setPackage(SETTING_PKG); |
| } |
| getTilesForIntent(context, user, intent, addedCache, defaultCategory, outTiles, |
| requireSettings, true); |
| } |
| |
| public static void getTilesForIntent(Context context, UserHandle user, Intent intent, |
| Map<Pair<String, String>, Tile> addedCache, String defaultCategory, List<Tile> outTiles, |
| boolean usePriority, boolean checkCategory) { |
| PackageManager pm = context.getPackageManager(); |
| List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent, |
| PackageManager.GET_META_DATA, user.getIdentifier()); |
| for (ResolveInfo resolved : results) { |
| if (!resolved.system) { |
| // Do not allow any app to add to settings, only system ones. |
| continue; |
| } |
| ActivityInfo activityInfo = resolved.activityInfo; |
| Bundle metaData = activityInfo.metaData; |
| String categoryKey = defaultCategory; |
| if (checkCategory && ((metaData == null) || !metaData.containsKey(EXTRA_CATEGORY_KEY)) |
| && categoryKey == null) { |
| Log.w(LOG_TAG, "Found " + resolved.activityInfo.name + " for intent " |
| + intent + " missing metadata " |
| + (metaData == null ? "" : EXTRA_CATEGORY_KEY)); |
| continue; |
| } else { |
| categoryKey = metaData.getString(EXTRA_CATEGORY_KEY); |
| } |
| Pair<String, String> key = new Pair<String, String>(activityInfo.packageName, |
| activityInfo.name); |
| Tile tile = addedCache.get(key); |
| if (tile == null) { |
| tile = new Tile(); |
| tile.intent = new Intent().setClassName( |
| activityInfo.packageName, activityInfo.name); |
| tile.category = categoryKey; |
| tile.priority = usePriority ? resolved.priority : 0; |
| tile.metaData = activityInfo.metaData; |
| updateTileData(context, tile, activityInfo, activityInfo.applicationInfo, |
| pm); |
| if (DEBUG) Log.d(LOG_TAG, "Adding tile " + tile.title); |
| |
| addedCache.put(key, tile); |
| } |
| if (!tile.userHandle.contains(user)) { |
| tile.userHandle.add(user); |
| } |
| if (!outTiles.contains(tile)) { |
| outTiles.add(tile); |
| } |
| } |
| } |
| |
| private static DashboardCategory getCategory(List<DashboardCategory> target, |
| String categoryKey) { |
| for (DashboardCategory category : target) { |
| if (categoryKey.equals(category.key)) { |
| return category; |
| } |
| } |
| return null; |
| } |
| |
| private static boolean updateTileData(Context context, Tile tile, |
| ActivityInfo activityInfo, ApplicationInfo applicationInfo, PackageManager pm) { |
| if (applicationInfo.isSystemApp()) { |
| int icon = 0; |
| CharSequence title = null; |
| String summary = null; |
| |
| // Get the activity's meta-data |
| try { |
| Resources res = pm.getResourcesForApplication( |
| applicationInfo.packageName); |
| Bundle metaData = activityInfo.metaData; |
| |
| if (res != null && metaData != null) { |
| if (metaData.containsKey(META_DATA_PREFERENCE_ICON)) { |
| icon = metaData.getInt(META_DATA_PREFERENCE_ICON); |
| } |
| if (metaData.containsKey(META_DATA_PREFERENCE_TITLE)) { |
| if (metaData.get(META_DATA_PREFERENCE_TITLE) instanceof Integer) { |
| title = res.getString(metaData.getInt(META_DATA_PREFERENCE_TITLE)); |
| } else { |
| title = metaData.getString(META_DATA_PREFERENCE_TITLE); |
| } |
| } |
| if (metaData.containsKey(META_DATA_PREFERENCE_SUMMARY)) { |
| if (metaData.get(META_DATA_PREFERENCE_SUMMARY) instanceof Integer) { |
| summary = res.getString(metaData.getInt(META_DATA_PREFERENCE_SUMMARY)); |
| } else { |
| summary = metaData.getString(META_DATA_PREFERENCE_SUMMARY); |
| } |
| } |
| } |
| } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) { |
| if (DEBUG) Log.d(LOG_TAG, "Couldn't find info", e); |
| } |
| |
| // Set the preference title to the activity's label if no |
| // meta-data is found |
| if (TextUtils.isEmpty(title)) { |
| title = activityInfo.loadLabel(pm).toString(); |
| } |
| if (icon == 0) { |
| icon = activityInfo.icon; |
| } |
| |
| // Set icon, title and summary for the preference |
| tile.icon = Icon.createWithResource(activityInfo.packageName, icon); |
| tile.title = title; |
| tile.summary = summary; |
| // Replace the intent with this specific activity |
| tile.intent = new Intent().setClassName(activityInfo.packageName, |
| activityInfo.name); |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| private static final Comparator<Tile> TILE_COMPARATOR = |
| new Comparator<Tile>() { |
| @Override |
| public int compare(Tile lhs, Tile rhs) { |
| return rhs.priority - lhs.priority; |
| } |
| }; |
| |
| private static final Comparator<DashboardCategory> CATEGORY_COMPARATOR = |
| new Comparator<DashboardCategory>() { |
| @Override |
| public int compare(DashboardCategory lhs, DashboardCategory rhs) { |
| return rhs.priority - lhs.priority; |
| } |
| }; |
| } |