Fan Zhang | 22a56d7 | 2016-09-27 17:52:00 -0700 | [diff] [blame] | 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 | * |
Fan Zhang | c8a5b79 | 2016-10-03 15:05:53 -0700 | [diff] [blame] | 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
Fan Zhang | 22a56d7 | 2016-09-27 17:52:00 -0700 | [diff] [blame] | 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 | package com.android.settingslib.drawer; |
| 17 | |
| 18 | import android.content.ComponentName; |
| 19 | import android.content.Context; |
| 20 | import android.util.ArrayMap; |
Fan Zhang | 5fa4af0 | 2016-11-16 12:40:35 -0800 | [diff] [blame] | 21 | import android.util.ArraySet; |
Fan Zhang | 22a56d7 | 2016-09-27 17:52:00 -0700 | [diff] [blame] | 22 | import android.util.Log; |
| 23 | import android.util.Pair; |
| 24 | |
Fan Zhang | 1b0dfa3 | 2018-07-20 12:57:55 -0700 | [diff] [blame] | 25 | import androidx.annotation.VisibleForTesting; |
| 26 | |
Fan Zhang | 5adc266 | 2016-10-04 15:12:58 -0700 | [diff] [blame] | 27 | import com.android.settingslib.applications.InterestingConfigChanges; |
| 28 | |
Fan Zhang | 31f4c54 | 2016-11-01 15:02:46 -0700 | [diff] [blame] | 29 | import java.util.ArrayList; |
| 30 | import java.util.HashMap; |
Fan Zhang | 22a56d7 | 2016-09-27 17:52:00 -0700 | [diff] [blame] | 31 | import java.util.List; |
| 32 | import java.util.Map; |
Fan Zhang | 31f4c54 | 2016-11-01 15:02:46 -0700 | [diff] [blame] | 33 | import java.util.Map.Entry; |
Fan Zhang | 22a56d7 | 2016-09-27 17:52:00 -0700 | [diff] [blame] | 34 | import java.util.Set; |
| 35 | |
| 36 | public class CategoryManager { |
| 37 | |
Fan Zhang | 1b0dfa3 | 2018-07-20 12:57:55 -0700 | [diff] [blame] | 38 | public static final String SETTING_PKG = "com.android.settings"; |
| 39 | |
Fan Zhang | 22a56d7 | 2016-09-27 17:52:00 -0700 | [diff] [blame] | 40 | private static final String TAG = "CategoryManager"; |
| 41 | |
| 42 | private static CategoryManager sInstance; |
Fan Zhang | 5adc266 | 2016-10-04 15:12:58 -0700 | [diff] [blame] | 43 | private final InterestingConfigChanges mInterestingConfigChanges; |
Fan Zhang | 22a56d7 | 2016-09-27 17:52:00 -0700 | [diff] [blame] | 44 | |
Fan Zhang | 22a56d7 | 2016-09-27 17:52:00 -0700 | [diff] [blame] | 45 | // Tile cache (key: <packageName, activityName>, value: tile) |
| 46 | private final Map<Pair<String, String>, Tile> mTileByComponentCache; |
| 47 | |
| 48 | // Tile cache (key: category key, value: category) |
| 49 | private final Map<String, DashboardCategory> mCategoryByKeyMap; |
| 50 | |
| 51 | private List<DashboardCategory> mCategories; |
Doris Ling | 485df11 | 2016-12-19 10:45:47 -0800 | [diff] [blame] | 52 | private String mExtraAction; |
Fan Zhang | 22a56d7 | 2016-09-27 17:52:00 -0700 | [diff] [blame] | 53 | |
Fan Zhang | 5adc266 | 2016-10-04 15:12:58 -0700 | [diff] [blame] | 54 | public static CategoryManager get(Context context) { |
Doris Ling | 485df11 | 2016-12-19 10:45:47 -0800 | [diff] [blame] | 55 | return get(context, null); |
| 56 | } |
| 57 | |
| 58 | public static CategoryManager get(Context context, String action) { |
Fan Zhang | 22a56d7 | 2016-09-27 17:52:00 -0700 | [diff] [blame] | 59 | if (sInstance == null) { |
Doris Ling | 485df11 | 2016-12-19 10:45:47 -0800 | [diff] [blame] | 60 | sInstance = new CategoryManager(context, action); |
Fan Zhang | 22a56d7 | 2016-09-27 17:52:00 -0700 | [diff] [blame] | 61 | } |
| 62 | return sInstance; |
| 63 | } |
| 64 | |
Doris Ling | 485df11 | 2016-12-19 10:45:47 -0800 | [diff] [blame] | 65 | CategoryManager(Context context, String action) { |
Fan Zhang | 22a56d7 | 2016-09-27 17:52:00 -0700 | [diff] [blame] | 66 | mTileByComponentCache = new ArrayMap<>(); |
| 67 | mCategoryByKeyMap = new ArrayMap<>(); |
Fan Zhang | 5adc266 | 2016-10-04 15:12:58 -0700 | [diff] [blame] | 68 | mInterestingConfigChanges = new InterestingConfigChanges(); |
| 69 | mInterestingConfigChanges.applyNewConfig(context.getResources()); |
Doris Ling | 485df11 | 2016-12-19 10:45:47 -0800 | [diff] [blame] | 70 | mExtraAction = action; |
Fan Zhang | 22a56d7 | 2016-09-27 17:52:00 -0700 | [diff] [blame] | 71 | } |
| 72 | |
Fan Zhang | c8a5b79 | 2016-10-03 15:05:53 -0700 | [diff] [blame] | 73 | public synchronized DashboardCategory getTilesByCategory(Context context, String categoryKey) { |
Fan Zhang | 1b0dfa3 | 2018-07-20 12:57:55 -0700 | [diff] [blame] | 74 | tryInitCategories(context, SETTING_PKG); |
Fan Zhang | 22a56d7 | 2016-09-27 17:52:00 -0700 | [diff] [blame] | 75 | |
Fan Zhang | 45c891b | 2016-10-10 18:11:49 -0700 | [diff] [blame] | 76 | return mCategoryByKeyMap.get(categoryKey); |
Fan Zhang | 22a56d7 | 2016-09-27 17:52:00 -0700 | [diff] [blame] | 77 | } |
| 78 | |
Fan Zhang | c8a5b79 | 2016-10-03 15:05:53 -0700 | [diff] [blame] | 79 | public synchronized List<DashboardCategory> getCategories(Context context) { |
Fan Zhang | 1b0dfa3 | 2018-07-20 12:57:55 -0700 | [diff] [blame] | 80 | tryInitCategories(context, SETTING_PKG); |
Fan Zhang | 22a56d7 | 2016-09-27 17:52:00 -0700 | [diff] [blame] | 81 | return mCategories; |
| 82 | } |
| 83 | |
roger xue | 8f06ab0 | 2016-12-08 14:09:50 -0800 | [diff] [blame] | 84 | public synchronized void reloadAllCategories(Context context, String settingPkg) { |
Fan Zhang | 5adc266 | 2016-10-04 15:12:58 -0700 | [diff] [blame] | 85 | final boolean forceClearCache = mInterestingConfigChanges.applyNewConfig( |
| 86 | context.getResources()); |
Fan Zhang | c8a5b79 | 2016-10-03 15:05:53 -0700 | [diff] [blame] | 87 | mCategories = null; |
roger xue | 8f06ab0 | 2016-12-08 14:09:50 -0800 | [diff] [blame] | 88 | tryInitCategories(context, forceClearCache, settingPkg); |
Fan Zhang | 22a56d7 | 2016-09-27 17:52:00 -0700 | [diff] [blame] | 89 | } |
| 90 | |
Fan Zhang | c8a5b79 | 2016-10-03 15:05:53 -0700 | [diff] [blame] | 91 | public synchronized void updateCategoryFromBlacklist(Set<ComponentName> tileBlacklist) { |
Fan Zhang | 22a56d7 | 2016-09-27 17:52:00 -0700 | [diff] [blame] | 92 | if (mCategories == null) { |
| 93 | Log.w(TAG, "Category is null, skipping blacklist update"); |
| 94 | } |
| 95 | for (int i = 0; i < mCategories.size(); i++) { |
| 96 | DashboardCategory category = mCategories.get(i); |
Doris Ling | b8d2cd4 | 2017-11-27 12:24:09 -0800 | [diff] [blame] | 97 | for (int j = 0; j < category.getTilesCount(); j++) { |
| 98 | Tile tile = category.getTile(j); |
Fan Zhang | 22a56d7 | 2016-09-27 17:52:00 -0700 | [diff] [blame] | 99 | if (tileBlacklist.contains(tile.intent.getComponent())) { |
Doris Ling | b8d2cd4 | 2017-11-27 12:24:09 -0800 | [diff] [blame] | 100 | category.removeTile(j--); |
Fan Zhang | 22a56d7 | 2016-09-27 17:52:00 -0700 | [diff] [blame] | 101 | } |
| 102 | } |
| 103 | } |
| 104 | } |
| 105 | |
roger xue | 8f06ab0 | 2016-12-08 14:09:50 -0800 | [diff] [blame] | 106 | private synchronized void tryInitCategories(Context context, String settingPkg) { |
Fan Zhang | 5adc266 | 2016-10-04 15:12:58 -0700 | [diff] [blame] | 107 | // Keep cached tiles by default. The cache is only invalidated when InterestingConfigChange |
| 108 | // happens. |
roger xue | 8f06ab0 | 2016-12-08 14:09:50 -0800 | [diff] [blame] | 109 | tryInitCategories(context, false /* forceClearCache */, settingPkg); |
Fan Zhang | 5adc266 | 2016-10-04 15:12:58 -0700 | [diff] [blame] | 110 | } |
| 111 | |
roger xue | 8f06ab0 | 2016-12-08 14:09:50 -0800 | [diff] [blame] | 112 | private synchronized void tryInitCategories(Context context, boolean forceClearCache, |
| 113 | String settingPkg) { |
Fan Zhang | 22a56d7 | 2016-09-27 17:52:00 -0700 | [diff] [blame] | 114 | if (mCategories == null) { |
Fan Zhang | 5adc266 | 2016-10-04 15:12:58 -0700 | [diff] [blame] | 115 | if (forceClearCache) { |
| 116 | mTileByComponentCache.clear(); |
| 117 | } |
Fan Zhang | 22a56d7 | 2016-09-27 17:52:00 -0700 | [diff] [blame] | 118 | mCategoryByKeyMap.clear(); |
| 119 | mCategories = TileUtils.getCategories(context, mTileByComponentCache, |
Fan Zhang | c75174b | 2018-07-19 14:07:58 -0700 | [diff] [blame] | 120 | mExtraAction, settingPkg); |
Fan Zhang | 22a56d7 | 2016-09-27 17:52:00 -0700 | [diff] [blame] | 121 | for (DashboardCategory category : mCategories) { |
| 122 | mCategoryByKeyMap.put(category.key, category); |
| 123 | } |
Fan Zhang | 914afbf | 2016-11-01 15:36:25 -0700 | [diff] [blame] | 124 | backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap); |
Doris Ling | ce31159 | 2017-03-13 12:56:10 -0700 | [diff] [blame] | 125 | sortCategories(context, mCategoryByKeyMap); |
Fan Zhang | 5fa4af0 | 2016-11-16 12:40:35 -0800 | [diff] [blame] | 126 | filterDuplicateTiles(mCategoryByKeyMap); |
Fan Zhang | 22a56d7 | 2016-09-27 17:52:00 -0700 | [diff] [blame] | 127 | } |
| 128 | } |
| 129 | |
Fan Zhang | 914afbf | 2016-11-01 15:36:25 -0700 | [diff] [blame] | 130 | @VisibleForTesting |
| 131 | synchronized void backwardCompatCleanupForCategory( |
| 132 | Map<Pair<String, String>, Tile> tileByComponentCache, |
| 133 | Map<String, DashboardCategory> categoryByKeyMap) { |
Fan Zhang | 31f4c54 | 2016-11-01 15:02:46 -0700 | [diff] [blame] | 134 | // A package can use a) CategoryKey, b) old category keys, c) both. |
| 135 | // Check if a package uses old category key only. |
| 136 | // If yes, map them to new category key. |
| 137 | |
| 138 | // Build a package name -> tile map first. |
| 139 | final Map<String, List<Tile>> packageToTileMap = new HashMap<>(); |
Fan Zhang | 914afbf | 2016-11-01 15:36:25 -0700 | [diff] [blame] | 140 | for (Entry<Pair<String, String>, Tile> tileEntry : tileByComponentCache.entrySet()) { |
Fan Zhang | 31f4c54 | 2016-11-01 15:02:46 -0700 | [diff] [blame] | 141 | final String packageName = tileEntry.getKey().first; |
| 142 | List<Tile> tiles = packageToTileMap.get(packageName); |
| 143 | if (tiles == null) { |
| 144 | tiles = new ArrayList<>(); |
| 145 | packageToTileMap.put(packageName, tiles); |
| 146 | } |
| 147 | tiles.add(tileEntry.getValue()); |
| 148 | } |
| 149 | |
| 150 | for (Entry<String, List<Tile>> entry : packageToTileMap.entrySet()) { |
| 151 | final List<Tile> tiles = entry.getValue(); |
| 152 | // Loop map, find if all tiles from same package uses old key only. |
| 153 | boolean useNewKey = false; |
| 154 | boolean useOldKey = false; |
| 155 | for (Tile tile : tiles) { |
| 156 | if (CategoryKey.KEY_COMPAT_MAP.containsKey(tile.category)) { |
| 157 | useOldKey = true; |
| 158 | } else { |
| 159 | useNewKey = true; |
| 160 | break; |
| 161 | } |
| 162 | } |
| 163 | // Uses only old key, map them to new keys one by one. |
| 164 | if (useOldKey && !useNewKey) { |
| 165 | for (Tile tile : tiles) { |
| 166 | final String newCategoryKey = CategoryKey.KEY_COMPAT_MAP.get(tile.category); |
| 167 | tile.category = newCategoryKey; |
| 168 | // move tile to new category. |
Fan Zhang | 914afbf | 2016-11-01 15:36:25 -0700 | [diff] [blame] | 169 | DashboardCategory newCategory = categoryByKeyMap.get(newCategoryKey); |
| 170 | if (newCategory == null) { |
| 171 | newCategory = new DashboardCategory(); |
| 172 | categoryByKeyMap.put(newCategoryKey, newCategory); |
| 173 | } |
Doris Ling | b8d2cd4 | 2017-11-27 12:24:09 -0800 | [diff] [blame] | 174 | newCategory.addTile(tile); |
Fan Zhang | 31f4c54 | 2016-11-01 15:02:46 -0700 | [diff] [blame] | 175 | } |
| 176 | } |
| 177 | } |
| 178 | } |
Fan Zhang | fc76ab3 | 2016-11-14 09:48:06 -0800 | [diff] [blame] | 179 | |
| 180 | /** |
Doris Ling | ce31159 | 2017-03-13 12:56:10 -0700 | [diff] [blame] | 181 | * Sort the tiles injected from all apps such that if they have the same priority value, |
| 182 | * they wil lbe sorted by package name. |
Fan Zhang | fc76ab3 | 2016-11-14 09:48:06 -0800 | [diff] [blame] | 183 | * <p/> |
Doris Ling | ce31159 | 2017-03-13 12:56:10 -0700 | [diff] [blame] | 184 | * A list of tiles are considered sorted when their priority value decreases in a linear |
Fan Zhang | fc76ab3 | 2016-11-14 09:48:06 -0800 | [diff] [blame] | 185 | * scan. |
| 186 | */ |
| 187 | @VisibleForTesting |
Doris Ling | ce31159 | 2017-03-13 12:56:10 -0700 | [diff] [blame] | 188 | synchronized void sortCategories(Context context, |
Fan Zhang | fc76ab3 | 2016-11-14 09:48:06 -0800 | [diff] [blame] | 189 | Map<String, DashboardCategory> categoryByKeyMap) { |
| 190 | for (Entry<String, DashboardCategory> categoryEntry : categoryByKeyMap.entrySet()) { |
Doris Ling | b8d2cd4 | 2017-11-27 12:24:09 -0800 | [diff] [blame] | 191 | categoryEntry.getValue().sortTiles(context.getPackageName()); |
Fan Zhang | fc76ab3 | 2016-11-14 09:48:06 -0800 | [diff] [blame] | 192 | } |
| 193 | } |
| 194 | |
| 195 | /** |
Fan Zhang | 5fa4af0 | 2016-11-16 12:40:35 -0800 | [diff] [blame] | 196 | * Filter out duplicate tiles from category. Duplicate tiles are the ones pointing to the |
| 197 | * same intent. |
| 198 | */ |
| 199 | @VisibleForTesting |
| 200 | synchronized void filterDuplicateTiles(Map<String, DashboardCategory> categoryByKeyMap) { |
| 201 | for (Entry<String, DashboardCategory> categoryEntry : categoryByKeyMap.entrySet()) { |
| 202 | final DashboardCategory category = categoryEntry.getValue(); |
Doris Ling | b8d2cd4 | 2017-11-27 12:24:09 -0800 | [diff] [blame] | 203 | final int count = category.getTilesCount(); |
Fan Zhang | 5fa4af0 | 2016-11-16 12:40:35 -0800 | [diff] [blame] | 204 | final Set<ComponentName> components = new ArraySet<>(); |
| 205 | for (int i = count - 1; i >= 0; i--) { |
Doris Ling | b8d2cd4 | 2017-11-27 12:24:09 -0800 | [diff] [blame] | 206 | final Tile tile = category.getTile(i); |
Fan Zhang | 5fa4af0 | 2016-11-16 12:40:35 -0800 | [diff] [blame] | 207 | if (tile.intent == null) { |
| 208 | continue; |
| 209 | } |
| 210 | final ComponentName tileComponent = tile.intent.getComponent(); |
| 211 | if (components.contains(tileComponent)) { |
Doris Ling | b8d2cd4 | 2017-11-27 12:24:09 -0800 | [diff] [blame] | 212 | category.removeTile(i); |
Fan Zhang | 5fa4af0 | 2016-11-16 12:40:35 -0800 | [diff] [blame] | 213 | } else { |
| 214 | components.add(tileComponent); |
| 215 | } |
| 216 | } |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | /** |
Doris Ling | ce31159 | 2017-03-13 12:56:10 -0700 | [diff] [blame] | 221 | * Sort priority value for tiles within a single {@code DashboardCategory}. |
Fan Zhang | fc76ab3 | 2016-11-14 09:48:06 -0800 | [diff] [blame] | 222 | * |
Doris Ling | ce31159 | 2017-03-13 12:56:10 -0700 | [diff] [blame] | 223 | * @see #sortCategories(Context, Map) |
Fan Zhang | fc76ab3 | 2016-11-14 09:48:06 -0800 | [diff] [blame] | 224 | */ |
Doris Ling | ce31159 | 2017-03-13 12:56:10 -0700 | [diff] [blame] | 225 | private synchronized void sortCategoriesForExternalTiles(Context context, |
Fan Zhang | fc76ab3 | 2016-11-14 09:48:06 -0800 | [diff] [blame] | 226 | DashboardCategory dashboardCategory) { |
Doris Ling | b8d2cd4 | 2017-11-27 12:24:09 -0800 | [diff] [blame] | 227 | dashboardCategory.sortTiles(context.getPackageName()); |
Fan Zhang | fc76ab3 | 2016-11-14 09:48:06 -0800 | [diff] [blame] | 228 | |
Fan Zhang | fc76ab3 | 2016-11-14 09:48:06 -0800 | [diff] [blame] | 229 | } |
Fan Zhang | 22a56d7 | 2016-09-27 17:52:00 -0700 | [diff] [blame] | 230 | } |