1 /*
2  * Copyright (C) 2022 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 
17 package com.android.quickstep;
18 
19 import androidx.annotation.Nullable;
20 
21 import com.android.quickstep.util.GroupTask;
22 
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Objects;
27 import java.util.function.Predicate;
28 
29 /**
30  * Keeps track of the state of {@code RecentsView}.
31  *
32  * <p> More specifically, used for keeping track of the state of filters applied on tasks
33  * in {@code RecentsView} for multi-instance management.
34  */
35 public class RecentsFilterState {
36     // the minimum number of tasks per package present to allow filtering
37     public static final int MIN_FILTERING_TASK_COUNT = 2;
38 
39     // default filter that returns true for any input
40     public static final Predicate<GroupTask> DEFAULT_FILTER = (groupTask -> true);
41 
42     // the package name to filter recent tasks by
43     @Nullable
44     private String mPackageNameToFilter = null;
45 
46     // the callback that gets executed upon filter change
47     @Nullable
48     private Runnable mOnFilterUpdatedListener = null;
49 
50     // map maintaining the count for each unique base activity package name currently in the recents
51     @Nullable
52     private Map<String, Integer> mInstanceCountMap;
53 
54     /**
55      * Returns {@code true} if {@code RecentsView} filters tasks by some package name.
56      */
isFiltered()57     public boolean isFiltered() {
58         return mPackageNameToFilter != null;
59     }
60 
61     /**
62      * Returns the package name that tasks are filtered by.
63      */
64     @Nullable
getPackageNameToFilter()65     public String getPackageNameToFilter() {
66         return mPackageNameToFilter;
67     }
68 
69 
70     /**
71      * Sets a listener on any changes to the filter.
72      *
73      * @param callback listener to be executed upon filter updates
74      */
setOnFilterUpdatedListener(@ullable Runnable callback)75     public void setOnFilterUpdatedListener(@Nullable Runnable callback) {
76         mOnFilterUpdatedListener = callback;
77     }
78 
79     /**
80      * Updates the filter such that tasks are filtered by a certain package name.
81      *
82      * @param packageName package name of the base activity to filter tasks by;
83      *                    if null, filter is turned off
84      */
setFilterBy(@ullable String packageName)85     public void setFilterBy(@Nullable String packageName) {
86         if (Objects.equals(packageName, mPackageNameToFilter)) {
87             return;
88         }
89 
90         mPackageNameToFilter = packageName;
91 
92         if (mOnFilterUpdatedListener != null) {
93             mOnFilterUpdatedListener.run();
94         }
95     }
96 
97     /**
98      * Updates the map of package names to their count in the most recent list of tasks.
99      *
100      * @param groupTaskList the list of tasks that map update is be based on
101      */
updateInstanceCountMap(List<GroupTask> groupTaskList)102     public void updateInstanceCountMap(List<GroupTask> groupTaskList) {
103         mInstanceCountMap = getInstanceCountMap(groupTaskList);
104     }
105 
106     /**
107      * Returns the map of package names to their count in the most recent list of tasks.
108      */
109     @Nullable
getInstanceCountMap()110     public Map<String, Integer> getInstanceCountMap() {
111         return mInstanceCountMap;
112     }
113 
114     /**
115      * Returns a predicate for filtering out GroupTasks by package name.
116      *
117      * @param packageName package name to filter GroupTasks by
118      *                    if null, Predicate always returns true.
119      */
getFilter(@ullable String packageName)120     public static Predicate<GroupTask> getFilter(@Nullable String packageName) {
121         if (packageName == null) {
122             return DEFAULT_FILTER;
123         }
124 
125         return (groupTask) -> (groupTask.task2 != null
126                 && groupTask.task2.key.getPackageName().equals(packageName))
127                 || groupTask.task1.key.getPackageName().equals(packageName);
128     }
129 
130     /**
131      * Returns a map of package names to their frequencies in a list of GroupTasks.
132      *
133      * @param groupTasks the list to go through to create the map
134      */
getInstanceCountMap(List<GroupTask> groupTasks)135     public static Map<String, Integer> getInstanceCountMap(List<GroupTask> groupTasks) {
136         Map<String, Integer> instanceCountMap = new HashMap<>();
137 
138         for (GroupTask groupTask : groupTasks) {
139             final String firstTaskPkgName = groupTask.task1.key.getPackageName();
140             final String secondTaskPkgName =
141                     groupTask.task2 == null ? null : groupTask.task2.key.getPackageName();
142 
143             // increment the instance count for the first task's base activity package name
144             incrementOrAddIfNotExists(instanceCountMap, firstTaskPkgName);
145 
146             // check if second task is non existent
147             if (secondTaskPkgName != null) {
148                 // increment the instance count for the second task's base activity package name
149                 incrementOrAddIfNotExists(instanceCountMap, secondTaskPkgName);
150             }
151         }
152 
153         return instanceCountMap;
154     }
155 
156     /**
157      * Returns true if tasks of provided package name should show filter UI.
158      *
159      * @param taskPackageName package name of the task in question
160      */
shouldShowFilterUI(String taskPackageName)161     public boolean shouldShowFilterUI(String taskPackageName) {
162         // number of occurrences in recents overview with the package name of this task
163         int instanceCount = getInstanceCountMap().get(taskPackageName);
164 
165         // if the number of occurrences isn't enough make sure tasks can't be filtered by
166         // the package name of this task
167         return !(isFiltered() || instanceCount < MIN_FILTERING_TASK_COUNT);
168     }
169 
incrementOrAddIfNotExists(Map<String, Integer> map, String pkgName)170     private static void incrementOrAddIfNotExists(Map<String, Integer> map, String pkgName) {
171         if (!map.containsKey(pkgName)) {
172             map.put(pkgName, 0);
173         }
174         map.put(pkgName, map.get(pkgName) + 1);
175     }
176 }
177