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