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.DesksUtils; 22 import com.android.quickstep.util.GroupTask; 23 import com.android.quickstep.views.TaskViewType; 24 import com.android.systemui.shared.recents.model.Task; 25 26 import java.util.HashMap; 27 import java.util.List; 28 import java.util.Map; 29 import java.util.Objects; 30 import java.util.function.Predicate; 31 32 /** 33 * Keeps track of the state of {@code RecentsView}. 34 * 35 * <p> More specifically, used for keeping track of the state of filters applied on tasks 36 * in {@code RecentsView} for multi-instance management. 37 */ 38 public class RecentsFilterState { 39 // the minimum number of tasks per package present to allow filtering 40 public static final int MIN_FILTERING_TASK_COUNT = 2; 41 42 // default filter that returns true for any input 43 public static final Predicate<GroupTask> EMPTY_FILTER = (groupTask -> true); 44 45 // the package name to filter recent tasks by 46 @Nullable 47 private String mPackageNameToFilter = null; 48 49 // the callback that gets executed upon filter change 50 @Nullable 51 private Runnable mOnFilterUpdatedListener = null; 52 53 // map maintaining the count for each unique base activity package name currently in the recents 54 @Nullable 55 private Map<String, Integer> mInstanceCountMap; 56 57 /** 58 * Returns {@code true} if {@code RecentsView} filters tasks by some package name. 59 */ isFiltered()60 public boolean isFiltered() { 61 return mPackageNameToFilter != null; 62 } 63 64 /** 65 * Returns the package name that tasks are filtered by. 66 */ 67 @Nullable getPackageNameToFilter()68 public String getPackageNameToFilter() { 69 return mPackageNameToFilter; 70 } 71 72 73 /** 74 * Sets a listener on any changes to the filter. 75 * 76 * @param callback listener to be executed upon filter updates 77 */ setOnFilterUpdatedListener(@ullable Runnable callback)78 public void setOnFilterUpdatedListener(@Nullable Runnable callback) { 79 mOnFilterUpdatedListener = callback; 80 } 81 82 /** 83 * Updates the filter such that tasks are filtered by a certain package name. 84 * 85 * @param packageName package name of the base activity to filter tasks by; 86 * if null, filter is turned off 87 */ setFilterBy(@ullable String packageName)88 public void setFilterBy(@Nullable String packageName) { 89 if (Objects.equals(packageName, mPackageNameToFilter)) { 90 return; 91 } 92 93 mPackageNameToFilter = packageName; 94 95 if (mOnFilterUpdatedListener != null) { 96 mOnFilterUpdatedListener.run(); 97 } 98 } 99 100 /** 101 * Updates the map of package names to their count in the most recent list of tasks. 102 * 103 * @param groupTaskList the list of tasks that map update is be based on 104 */ updateInstanceCountMap(List<GroupTask> groupTaskList)105 public void updateInstanceCountMap(List<GroupTask> groupTaskList) { 106 mInstanceCountMap = getInstanceCountMap(groupTaskList); 107 } 108 109 /** 110 * Returns the map of package names to their count in the most recent list of tasks. 111 */ 112 @Nullable getInstanceCountMap()113 public Map<String, Integer> getInstanceCountMap() { 114 return mInstanceCountMap; 115 } 116 117 /** 118 * Returns a predicate for filtering out GroupTasks by package name. 119 * 120 * @param packageName package name to filter GroupTasks by 121 * if null, Predicate filters out desktop tasks with no non-minimized tasks, 122 * unless the multiple desks feature is enabled, which allows empty desks. 123 */ getFilter(@ullable String packageName)124 public static Predicate<GroupTask> getFilter(@Nullable String packageName) { 125 if (packageName == null) { 126 return getDesktopTaskFilter(); 127 } 128 129 return (groupTask) -> (groupTask.containsPackage(packageName) 130 && shouldKeepGroupTask(groupTask)); 131 } 132 133 /** 134 * Returns a predicate that filters out desk tasks that contain no non-minimized desktop tasks, 135 * unless the multiple desks feature is enabled, which allows empty desks. 136 */ getDesktopTaskFilter()137 public static Predicate<GroupTask> getDesktopTaskFilter() { 138 return (groupTask -> shouldKeepGroupTask(groupTask)); 139 } 140 141 /** 142 * Returns true if the given `groupTask` should be kept, and false if it should be filtered out. 143 * Desks will be filtered out if they are empty unless the multiple desks feature is enabled. 144 * 145 * @param groupTask The group task to check. 146 */ shouldKeepGroupTask(GroupTask groupTask)147 private static boolean shouldKeepGroupTask(GroupTask groupTask) { 148 if (groupTask.taskViewType != TaskViewType.DESKTOP) { 149 return true; 150 } 151 152 if (DesksUtils.areMultiDesksFlagsEnabled()) { 153 return true; 154 } 155 156 return groupTask.getTasks().stream() 157 .anyMatch(task -> !task.isMinimized); 158 } 159 160 /** 161 * Returns a map of package names to their frequencies in a list of GroupTasks. 162 * 163 * @param groupTasks the list to go through to create the map 164 */ getInstanceCountMap(List<GroupTask> groupTasks)165 public static Map<String, Integer> getInstanceCountMap(List<GroupTask> groupTasks) { 166 Map<String, Integer> instanceCountMap = new HashMap<>(); 167 168 for (GroupTask groupTask : groupTasks) { 169 for (Task t : groupTask.getTasks()) { 170 final String taskPkgName = t.key.getPackageName(); 171 incrementOrAddIfNotExists(instanceCountMap, taskPkgName); 172 } 173 } 174 175 return instanceCountMap; 176 } 177 178 /** 179 * Returns true if tasks of provided package name should show filter UI. 180 * 181 * @param taskPackageName package name of the task in question 182 */ shouldShowFilterUI(String taskPackageName)183 public boolean shouldShowFilterUI(String taskPackageName) { 184 // number of occurrences in recents overview with the package name of this task 185 int instanceCount = getInstanceCountMap().get(taskPackageName); 186 187 // if the number of occurrences isn't enough make sure tasks can't be filtered by 188 // the package name of this task 189 return !(isFiltered() || instanceCount < MIN_FILTERING_TASK_COUNT); 190 } 191 incrementOrAddIfNotExists(Map<String, Integer> map, String pkgName)192 private static void incrementOrAddIfNotExists(Map<String, Integer> map, String pkgName) { 193 if (!map.containsKey(pkgName)) { 194 map.put(pkgName, 0); 195 } 196 map.put(pkgName, map.get(pkgName) + 1); 197 } 198 } 199