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
17package com.android.launcher3.shortcuts;
18
19import android.support.annotation.VisibleForTesting;
20
21import java.util.ArrayList;
22import java.util.Collections;
23import java.util.Comparator;
24import java.util.List;
25
26/**
27 * Sorts and filters shortcuts.
28 */
29public class ShortcutFilter {
30
31    public static final int MAX_SHORTCUTS = 4;
32    @VisibleForTesting static final int NUM_DYNAMIC = 2;
33
34    /**
35     * Sorts shortcuts in rank order, with manifest shortcuts coming before dynamic shortcuts.
36     */
37    private static final Comparator<ShortcutInfoCompat> RANK_COMPARATOR
38            = new Comparator<ShortcutInfoCompat>() {
39        @Override
40        public int compare(ShortcutInfoCompat a, ShortcutInfoCompat b) {
41            if (a.isDeclaredInManifest() && !b.isDeclaredInManifest()) {
42                return -1;
43            }
44            if (!a.isDeclaredInManifest() && b.isDeclaredInManifest()) {
45                return 1;
46            }
47            return Integer.compare(a.getRank(), b.getRank());
48        }
49    };
50
51    /**
52     * Filters the shortcuts so that only MAX_SHORTCUTS or fewer shortcuts are retained.
53     * We want the filter to include both static and dynamic shortcuts, so we always
54     * include NUM_DYNAMIC dynamic shortcuts, if at least that many are present.
55     *
56     * @return a subset of shortcuts, in sorted order, with size <= MAX_SHORTCUTS.
57     */
58    public static List<ShortcutInfoCompat> sortAndFilterShortcuts(
59            List<ShortcutInfoCompat> shortcuts) {
60        Collections.sort(shortcuts, RANK_COMPARATOR);
61        if (shortcuts.size() <= MAX_SHORTCUTS) {
62            return shortcuts;
63        }
64
65        // The list of shortcuts is now sorted with static shortcuts followed by dynamic
66        // shortcuts. We want to preserve this order, but only keep MAX_SHORTCUTS.
67        List<ShortcutInfoCompat> filteredShortcuts = new ArrayList<>(MAX_SHORTCUTS);
68        int numDynamic = 0;
69        int size = shortcuts.size();
70        for (int i = 0; i < size; i++) {
71            ShortcutInfoCompat shortcut = shortcuts.get(i);
72            int filteredSize = filteredShortcuts.size();
73            if (filteredSize < MAX_SHORTCUTS) {
74                // Always add the first MAX_SHORTCUTS to the filtered list.
75                filteredShortcuts.add(shortcut);
76                if (shortcut.isDynamic()) {
77                    numDynamic++;
78                }
79                continue;
80            }
81            // At this point, we have MAX_SHORTCUTS already, but they may all be static.
82            // If there are dynamic shortcuts, remove static shortcuts to add them.
83            if (shortcut.isDynamic() && numDynamic < NUM_DYNAMIC) {
84                numDynamic++;
85                int lastStaticIndex = filteredSize - numDynamic;
86                filteredShortcuts.remove(lastStaticIndex);
87                filteredShortcuts.add(shortcut);
88            }
89        }
90        return filteredShortcuts;
91    }
92}
93