122a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang/**
222a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang * Copyright (C) 2016 The Android Open Source Project
322a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang *
422a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang * Licensed under the Apache License, Version 2.0 (the "License");
522a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang * you may not use this file except in compliance with the License.
622a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang * You may obtain a copy of the License at
722a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang *
8c8a5b790fa13826b21aee97ed5d1960d785333daFan Zhang * http://www.apache.org/licenses/LICENSE-2.0
922a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang *
1022a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang * Unless required by applicable law or agreed to in writing, software
1122a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang * distributed under the License is distributed on an "AS IS" BASIS,
1222a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1322a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang * See the License for the specific language governing permissions and
1422a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang * limitations under the License.
1522a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang */
1622a56d775d047a1926f2a6d9c855c802d561d487Fan Zhangpackage com.android.settingslib.drawer;
1722a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang
1822a56d775d047a1926f2a6d9c855c802d561d487Fan Zhangimport android.content.ComponentName;
1922a56d775d047a1926f2a6d9c855c802d561d487Fan Zhangimport android.content.Context;
20914afbfbd048721fb9ece683b9e881b79a01e34eFan Zhangimport android.support.annotation.VisibleForTesting;
21fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhangimport android.text.TextUtils;
2222a56d775d047a1926f2a6d9c855c802d561d487Fan Zhangimport android.util.ArrayMap;
235fa4af08c603607d7df7470dc10d3af2d2f04137Fan Zhangimport android.util.ArraySet;
2422a56d775d047a1926f2a6d9c855c802d561d487Fan Zhangimport android.util.Log;
2522a56d775d047a1926f2a6d9c855c802d561d487Fan Zhangimport android.util.Pair;
2622a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang
275adc26695fb4282125d4b6b641e04820a028d87bFan Zhangimport com.android.settingslib.applications.InterestingConfigChanges;
285adc26695fb4282125d4b6b641e04820a028d87bFan Zhang
2931f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhangimport java.util.ArrayList;
30fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhangimport java.util.Collections;
3131f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhangimport java.util.HashMap;
3222a56d775d047a1926f2a6d9c855c802d561d487Fan Zhangimport java.util.List;
3322a56d775d047a1926f2a6d9c855c802d561d487Fan Zhangimport java.util.Map;
3431f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhangimport java.util.Map.Entry;
3522a56d775d047a1926f2a6d9c855c802d561d487Fan Zhangimport java.util.Set;
3622a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang
37fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhangimport static java.lang.String.CASE_INSENSITIVE_ORDER;
38fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhang
3922a56d775d047a1926f2a6d9c855c802d561d487Fan Zhangpublic class CategoryManager {
4022a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang
4122a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang    private static final String TAG = "CategoryManager";
4222a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang
4322a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang    private static CategoryManager sInstance;
445adc26695fb4282125d4b6b641e04820a028d87bFan Zhang    private final InterestingConfigChanges mInterestingConfigChanges;
4522a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang
4622a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang    // Tile cache (key: <packageName, activityName>, value: tile)
4722a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang    private final Map<Pair<String, String>, Tile> mTileByComponentCache;
4822a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang
4922a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang    // Tile cache (key: category key, value: category)
5022a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang    private final Map<String, DashboardCategory> mCategoryByKeyMap;
5122a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang
5222a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang    private List<DashboardCategory> mCategories;
53485df11f7770edc6c7d0f87ade5b2e0aa40e7e59Doris Ling    private String mExtraAction;
5422a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang
555adc26695fb4282125d4b6b641e04820a028d87bFan Zhang    public static CategoryManager get(Context context) {
56485df11f7770edc6c7d0f87ade5b2e0aa40e7e59Doris Ling        return get(context, null);
57485df11f7770edc6c7d0f87ade5b2e0aa40e7e59Doris Ling    }
58485df11f7770edc6c7d0f87ade5b2e0aa40e7e59Doris Ling
59485df11f7770edc6c7d0f87ade5b2e0aa40e7e59Doris Ling    public static CategoryManager get(Context context, String action) {
6022a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang        if (sInstance == null) {
61485df11f7770edc6c7d0f87ade5b2e0aa40e7e59Doris Ling            sInstance = new CategoryManager(context, action);
6222a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang        }
6322a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang        return sInstance;
6422a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang    }
6522a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang
66485df11f7770edc6c7d0f87ade5b2e0aa40e7e59Doris Ling    CategoryManager(Context context, String action) {
6722a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang        mTileByComponentCache = new ArrayMap<>();
6822a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang        mCategoryByKeyMap = new ArrayMap<>();
695adc26695fb4282125d4b6b641e04820a028d87bFan Zhang        mInterestingConfigChanges = new InterestingConfigChanges();
705adc26695fb4282125d4b6b641e04820a028d87bFan Zhang        mInterestingConfigChanges.applyNewConfig(context.getResources());
71485df11f7770edc6c7d0f87ade5b2e0aa40e7e59Doris Ling        mExtraAction = action;
7222a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang    }
7322a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang
74c8a5b790fa13826b21aee97ed5d1960d785333daFan Zhang    public synchronized DashboardCategory getTilesByCategory(Context context, String categoryKey) {
758f06ab0774d44c49fbb5b122b7266192c49271c0roger xue        return getTilesByCategory(context, categoryKey, TileUtils.SETTING_PKG);
768f06ab0774d44c49fbb5b122b7266192c49271c0roger xue    }
778f06ab0774d44c49fbb5b122b7266192c49271c0roger xue
788f06ab0774d44c49fbb5b122b7266192c49271c0roger xue    public synchronized DashboardCategory getTilesByCategory(Context context, String categoryKey,
798f06ab0774d44c49fbb5b122b7266192c49271c0roger xue            String settingPkg) {
808f06ab0774d44c49fbb5b122b7266192c49271c0roger xue        tryInitCategories(context, settingPkg);
8122a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang
8245c891b7c5fb6eab719cb9a92c38bbdb25b28ab3Fan Zhang        return mCategoryByKeyMap.get(categoryKey);
8322a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang    }
8422a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang
85c8a5b790fa13826b21aee97ed5d1960d785333daFan Zhang    public synchronized List<DashboardCategory> getCategories(Context context) {
868f06ab0774d44c49fbb5b122b7266192c49271c0roger xue        return getCategories(context, TileUtils.SETTING_PKG);
878f06ab0774d44c49fbb5b122b7266192c49271c0roger xue    }
888f06ab0774d44c49fbb5b122b7266192c49271c0roger xue
898f06ab0774d44c49fbb5b122b7266192c49271c0roger xue    public synchronized List<DashboardCategory> getCategories(Context context, String settingPkg) {
908f06ab0774d44c49fbb5b122b7266192c49271c0roger xue        tryInitCategories(context, settingPkg);
9122a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang        return mCategories;
9222a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang    }
9322a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang
948f06ab0774d44c49fbb5b122b7266192c49271c0roger xue    public synchronized void reloadAllCategories(Context context, String settingPkg) {
955adc26695fb4282125d4b6b641e04820a028d87bFan Zhang        final boolean forceClearCache = mInterestingConfigChanges.applyNewConfig(
965adc26695fb4282125d4b6b641e04820a028d87bFan Zhang                context.getResources());
97c8a5b790fa13826b21aee97ed5d1960d785333daFan Zhang        mCategories = null;
988f06ab0774d44c49fbb5b122b7266192c49271c0roger xue        tryInitCategories(context, forceClearCache, settingPkg);
9922a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang    }
10022a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang
101c8a5b790fa13826b21aee97ed5d1960d785333daFan Zhang    public synchronized void updateCategoryFromBlacklist(Set<ComponentName> tileBlacklist) {
10222a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang        if (mCategories == null) {
10322a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang            Log.w(TAG, "Category is null, skipping blacklist update");
10422a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang        }
10522a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang        for (int i = 0; i < mCategories.size(); i++) {
10622a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang            DashboardCategory category = mCategories.get(i);
10722a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang            for (int j = 0; j < category.tiles.size(); j++) {
10822a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang                Tile tile = category.tiles.get(j);
10922a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang                if (tileBlacklist.contains(tile.intent.getComponent())) {
11022a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang                    category.tiles.remove(j--);
11122a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang                }
11222a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang            }
11322a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang        }
11422a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang    }
11522a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang
1168f06ab0774d44c49fbb5b122b7266192c49271c0roger xue    private synchronized void tryInitCategories(Context context, String settingPkg) {
1175adc26695fb4282125d4b6b641e04820a028d87bFan Zhang        // Keep cached tiles by default. The cache is only invalidated when InterestingConfigChange
1185adc26695fb4282125d4b6b641e04820a028d87bFan Zhang        // happens.
1198f06ab0774d44c49fbb5b122b7266192c49271c0roger xue        tryInitCategories(context, false /* forceClearCache */, settingPkg);
1205adc26695fb4282125d4b6b641e04820a028d87bFan Zhang    }
1215adc26695fb4282125d4b6b641e04820a028d87bFan Zhang
1228f06ab0774d44c49fbb5b122b7266192c49271c0roger xue    private synchronized void tryInitCategories(Context context, boolean forceClearCache,
1238f06ab0774d44c49fbb5b122b7266192c49271c0roger xue            String settingPkg) {
12422a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang        if (mCategories == null) {
1255adc26695fb4282125d4b6b641e04820a028d87bFan Zhang            if (forceClearCache) {
1265adc26695fb4282125d4b6b641e04820a028d87bFan Zhang                mTileByComponentCache.clear();
1275adc26695fb4282125d4b6b641e04820a028d87bFan Zhang            }
12822a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang            mCategoryByKeyMap.clear();
12922a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang            mCategories = TileUtils.getCategories(context, mTileByComponentCache,
1308f06ab0774d44c49fbb5b122b7266192c49271c0roger xue                    false /* categoryDefinedInManifest */, mExtraAction, settingPkg);
13122a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang            for (DashboardCategory category : mCategories) {
13222a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang                mCategoryByKeyMap.put(category.key, category);
13322a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang            }
134914afbfbd048721fb9ece683b9e881b79a01e34eFan Zhang            backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap);
135ce31159be781f5b94ed24013fcf1db7b9aa0cf4aDoris Ling            sortCategories(context, mCategoryByKeyMap);
1365fa4af08c603607d7df7470dc10d3af2d2f04137Fan Zhang            filterDuplicateTiles(mCategoryByKeyMap);
13722a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang        }
13822a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang    }
13922a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang
140914afbfbd048721fb9ece683b9e881b79a01e34eFan Zhang    @VisibleForTesting
141914afbfbd048721fb9ece683b9e881b79a01e34eFan Zhang    synchronized void backwardCompatCleanupForCategory(
142914afbfbd048721fb9ece683b9e881b79a01e34eFan Zhang            Map<Pair<String, String>, Tile> tileByComponentCache,
143914afbfbd048721fb9ece683b9e881b79a01e34eFan Zhang            Map<String, DashboardCategory> categoryByKeyMap) {
14431f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang        // A package can use a) CategoryKey, b) old category keys, c) both.
14531f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang        // Check if a package uses old category key only.
14631f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang        // If yes, map them to new category key.
14731f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang
14831f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang        // Build a package name -> tile map first.
14931f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang        final Map<String, List<Tile>> packageToTileMap = new HashMap<>();
150914afbfbd048721fb9ece683b9e881b79a01e34eFan Zhang        for (Entry<Pair<String, String>, Tile> tileEntry : tileByComponentCache.entrySet()) {
15131f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang            final String packageName = tileEntry.getKey().first;
15231f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang            List<Tile> tiles = packageToTileMap.get(packageName);
15331f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang            if (tiles == null) {
15431f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang                tiles = new ArrayList<>();
15531f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang                packageToTileMap.put(packageName, tiles);
15631f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang            }
15731f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang            tiles.add(tileEntry.getValue());
15831f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang        }
15931f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang
16031f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang        for (Entry<String, List<Tile>> entry : packageToTileMap.entrySet()) {
16131f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang            final List<Tile> tiles = entry.getValue();
16231f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang            // Loop map, find if all tiles from same package uses old key only.
16331f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang            boolean useNewKey = false;
16431f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang            boolean useOldKey = false;
16531f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang            for (Tile tile : tiles) {
16631f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang                if (CategoryKey.KEY_COMPAT_MAP.containsKey(tile.category)) {
16731f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang                    useOldKey = true;
16831f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang                } else {
16931f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang                    useNewKey = true;
17031f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang                    break;
17131f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang                }
17231f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang            }
17331f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang            // Uses only old key, map them to new keys one by one.
17431f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang            if (useOldKey && !useNewKey) {
17531f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang                for (Tile tile : tiles) {
17631f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang                    final String newCategoryKey = CategoryKey.KEY_COMPAT_MAP.get(tile.category);
17731f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang                    tile.category = newCategoryKey;
17831f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang                    // move tile to new category.
179914afbfbd048721fb9ece683b9e881b79a01e34eFan Zhang                    DashboardCategory newCategory = categoryByKeyMap.get(newCategoryKey);
180914afbfbd048721fb9ece683b9e881b79a01e34eFan Zhang                    if (newCategory == null) {
181914afbfbd048721fb9ece683b9e881b79a01e34eFan Zhang                        newCategory = new DashboardCategory();
182914afbfbd048721fb9ece683b9e881b79a01e34eFan Zhang                        categoryByKeyMap.put(newCategoryKey, newCategory);
183914afbfbd048721fb9ece683b9e881b79a01e34eFan Zhang                    }
18431f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang                    newCategory.tiles.add(tile);
18531f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang                }
18631f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang            }
18731f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang        }
18831f4c5444490cd7db7c9089e9ca3e8a2904c6d5bFan Zhang    }
189fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhang
190fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhang    /**
191ce31159be781f5b94ed24013fcf1db7b9aa0cf4aDoris Ling     * Sort the tiles injected from all apps such that if they have the same priority value,
192ce31159be781f5b94ed24013fcf1db7b9aa0cf4aDoris Ling     * they wil lbe sorted by package name.
193fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhang     * <p/>
194ce31159be781f5b94ed24013fcf1db7b9aa0cf4aDoris Ling     * A list of tiles are considered sorted when their priority value decreases in a linear
195fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhang     * scan.
196fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhang     */
197fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhang    @VisibleForTesting
198ce31159be781f5b94ed24013fcf1db7b9aa0cf4aDoris Ling    synchronized void sortCategories(Context context,
199fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhang            Map<String, DashboardCategory> categoryByKeyMap) {
200fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhang        for (Entry<String, DashboardCategory> categoryEntry : categoryByKeyMap.entrySet()) {
201ce31159be781f5b94ed24013fcf1db7b9aa0cf4aDoris Ling            sortCategoriesForExternalTiles(context, categoryEntry.getValue());
202fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhang        }
203fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhang    }
204fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhang
205fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhang    /**
2065fa4af08c603607d7df7470dc10d3af2d2f04137Fan Zhang     * Filter out duplicate tiles from category. Duplicate tiles are the ones pointing to the
2075fa4af08c603607d7df7470dc10d3af2d2f04137Fan Zhang     * same intent.
2085fa4af08c603607d7df7470dc10d3af2d2f04137Fan Zhang     */
2095fa4af08c603607d7df7470dc10d3af2d2f04137Fan Zhang    @VisibleForTesting
2105fa4af08c603607d7df7470dc10d3af2d2f04137Fan Zhang    synchronized void filterDuplicateTiles(Map<String, DashboardCategory> categoryByKeyMap) {
2115fa4af08c603607d7df7470dc10d3af2d2f04137Fan Zhang        for (Entry<String, DashboardCategory> categoryEntry : categoryByKeyMap.entrySet()) {
2125fa4af08c603607d7df7470dc10d3af2d2f04137Fan Zhang            final DashboardCategory category = categoryEntry.getValue();
2135fa4af08c603607d7df7470dc10d3af2d2f04137Fan Zhang            final int count = category.tiles.size();
2145fa4af08c603607d7df7470dc10d3af2d2f04137Fan Zhang            final Set<ComponentName> components = new ArraySet<>();
2155fa4af08c603607d7df7470dc10d3af2d2f04137Fan Zhang            for (int i = count - 1; i >= 0; i--) {
2165fa4af08c603607d7df7470dc10d3af2d2f04137Fan Zhang                final Tile tile = category.tiles.get(i);
2175fa4af08c603607d7df7470dc10d3af2d2f04137Fan Zhang                if (tile.intent == null) {
2185fa4af08c603607d7df7470dc10d3af2d2f04137Fan Zhang                    continue;
2195fa4af08c603607d7df7470dc10d3af2d2f04137Fan Zhang                }
2205fa4af08c603607d7df7470dc10d3af2d2f04137Fan Zhang                final ComponentName tileComponent = tile.intent.getComponent();
2215fa4af08c603607d7df7470dc10d3af2d2f04137Fan Zhang                if (components.contains(tileComponent)) {
2225fa4af08c603607d7df7470dc10d3af2d2f04137Fan Zhang                    category.tiles.remove(i);
2235fa4af08c603607d7df7470dc10d3af2d2f04137Fan Zhang                } else {
2245fa4af08c603607d7df7470dc10d3af2d2f04137Fan Zhang                    components.add(tileComponent);
2255fa4af08c603607d7df7470dc10d3af2d2f04137Fan Zhang                }
2265fa4af08c603607d7df7470dc10d3af2d2f04137Fan Zhang            }
2275fa4af08c603607d7df7470dc10d3af2d2f04137Fan Zhang        }
2285fa4af08c603607d7df7470dc10d3af2d2f04137Fan Zhang    }
2295fa4af08c603607d7df7470dc10d3af2d2f04137Fan Zhang
2305fa4af08c603607d7df7470dc10d3af2d2f04137Fan Zhang    /**
231ce31159be781f5b94ed24013fcf1db7b9aa0cf4aDoris Ling     * Sort priority value for tiles within a single {@code DashboardCategory}.
232fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhang     *
233ce31159be781f5b94ed24013fcf1db7b9aa0cf4aDoris Ling     * @see #sortCategories(Context, Map)
234fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhang     */
235ce31159be781f5b94ed24013fcf1db7b9aa0cf4aDoris Ling    private synchronized void sortCategoriesForExternalTiles(Context context,
236fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhang            DashboardCategory dashboardCategory) {
237fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhang        final String skipPackageName = context.getPackageName();
238fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhang
239ce31159be781f5b94ed24013fcf1db7b9aa0cf4aDoris Ling        // Sort tiles based on [priority, package within priority]
240fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhang        Collections.sort(dashboardCategory.tiles, (tile1, tile2) -> {
241fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhang            final String package1 = tile1.intent.getComponent().getPackageName();
242fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhang            final String package2 = tile2.intent.getComponent().getPackageName();
243fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhang            final int packageCompare = CASE_INSENSITIVE_ORDER.compare(package1, package2);
244ce31159be781f5b94ed24013fcf1db7b9aa0cf4aDoris Ling            // First sort by priority
245ce31159be781f5b94ed24013fcf1db7b9aa0cf4aDoris Ling            final int priorityCompare = tile2.priority - tile1.priority;
246ce31159be781f5b94ed24013fcf1db7b9aa0cf4aDoris Ling            if (priorityCompare != 0) {
247ce31159be781f5b94ed24013fcf1db7b9aa0cf4aDoris Ling                return priorityCompare;
248ce31159be781f5b94ed24013fcf1db7b9aa0cf4aDoris Ling            }
249ce31159be781f5b94ed24013fcf1db7b9aa0cf4aDoris Ling            // Then sort by package name, skip package take precedence
250fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhang            if (packageCompare != 0) {
251ce31159be781f5b94ed24013fcf1db7b9aa0cf4aDoris Ling                if (TextUtils.equals(package1, skipPackageName)) {
252ce31159be781f5b94ed24013fcf1db7b9aa0cf4aDoris Ling                    return -1;
253ce31159be781f5b94ed24013fcf1db7b9aa0cf4aDoris Ling                }
254ce31159be781f5b94ed24013fcf1db7b9aa0cf4aDoris Ling                if (TextUtils.equals(package2, skipPackageName)) {
255ce31159be781f5b94ed24013fcf1db7b9aa0cf4aDoris Ling                    return 1;
256ce31159be781f5b94ed24013fcf1db7b9aa0cf4aDoris Ling                }
257fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhang            }
258ce31159be781f5b94ed24013fcf1db7b9aa0cf4aDoris Ling            return packageCompare;
259fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhang        });
260fc76ab3cbf1794d2d72a92c87afc1cf9f62cc03aFan Zhang    }
26122a56d775d047a1926f2a6d9c855c802d561d487Fan Zhang}
262