1 /* 2 * Copyright (C) 2018 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 package com.android.launcher3.allapps; 17 18 import static com.android.launcher3.model.data.AppInfo.COMPONENT_KEY_COMPARATOR; 19 import static com.android.launcher3.model.data.AppInfo.EMPTY_ARRAY; 20 21 import android.content.Context; 22 import android.os.UserHandle; 23 import android.util.Log; 24 import android.view.View; 25 import android.view.ViewGroup; 26 27 import androidx.annotation.NonNull; 28 import androidx.annotation.Nullable; 29 30 import com.android.launcher3.BubbleTextView; 31 import com.android.launcher3.model.data.AppInfo; 32 import com.android.launcher3.model.data.ItemInfo; 33 import com.android.launcher3.recyclerview.AllAppsRecyclerViewPool; 34 import com.android.launcher3.util.ComponentKey; 35 import com.android.launcher3.util.PackageUserKey; 36 import com.android.launcher3.views.ActivityContext; 37 38 import java.io.PrintWriter; 39 import java.util.ArrayList; 40 import java.util.Arrays; 41 import java.util.Collections; 42 import java.util.Comparator; 43 import java.util.List; 44 import java.util.Locale; 45 import java.util.Map; 46 import java.util.concurrent.CopyOnWriteArrayList; 47 import java.util.function.Consumer; 48 import java.util.function.Predicate; 49 50 /** 51 * A utility class to maintain the collection of all apps. 52 * 53 * @param <T> The type of the context. 54 */ 55 public class AllAppsStore<T extends Context & ActivityContext> { 56 57 private static final String TAG = "AllAppsStore"; 58 // Defer updates flag used to defer all apps updates to the next draw. 59 public static final int DEFER_UPDATES_NEXT_DRAW = 1 << 0; 60 // Defer updates flag used to defer all apps updates by a test's request. 61 public static final int DEFER_UPDATES_TEST = 1 << 1; 62 63 private PackageUserKey mTempKey = new PackageUserKey(null, null); 64 private AppInfo mTempInfo = new AppInfo(); 65 66 private @NonNull AppInfo[] mApps = EMPTY_ARRAY; 67 68 private final List<OnUpdateListener> mUpdateListeners = new CopyOnWriteArrayList<>(); 69 private final ArrayList<ViewGroup> mIconContainers = new ArrayList<>(); 70 private Map<PackageUserKey, Integer> mPackageUserKeytoUidMap = Collections.emptyMap(); 71 private int mModelFlags; 72 private int mDeferUpdatesFlags = 0; 73 private boolean mUpdatePending = false; 74 private final AllAppsRecyclerViewPool mAllAppsRecyclerViewPool = new AllAppsRecyclerViewPool(); 75 76 private final T mContext; 77 getApps()78 public AppInfo[] getApps() { 79 return mApps; 80 } 81 AllAppsStore(@onNull T context)82 public AllAppsStore(@NonNull T context) { 83 mContext = context; 84 } 85 86 /** 87 * Calling {@link #setApps(AppInfo[], int, Map, boolean)} with shouldPreinflate set to 88 * {@code true}. This method should be called in launcher (not for taskbar). 89 */ setApps(@ullable AppInfo[] apps, int flags, Map<PackageUserKey, Integer> map)90 public void setApps(@Nullable AppInfo[] apps, int flags, Map<PackageUserKey, Integer> map) { 91 setApps(apps, flags, map, /* shouldPreinflate= */ true); 92 } 93 94 /** 95 * Sets the current set of apps and sets mapping for {@link PackageUserKey} to Uid for 96 * the current set of apps. 97 * 98 * <p> Note that shouldPreinflate param should be set to {@code false} for taskbar, because 99 * this method is too late to preinflate all apps, as user will open all apps in the frame 100 * 101 * <p>Param: apps are required to be sorted using the comparator COMPONENT_KEY_COMPARATOR 102 * in order to enable binary search on the mApps store 103 */ setApps(@ullable AppInfo[] apps, int flags, Map<PackageUserKey, Integer> map, boolean shouldPreinflate)104 public void setApps(@Nullable AppInfo[] apps, int flags, Map<PackageUserKey, Integer> map, 105 boolean shouldPreinflate) { 106 mApps = apps == null ? EMPTY_ARRAY : apps; 107 Log.d(TAG, "setApps: apps.length=" + mApps.length); 108 mModelFlags = flags; 109 notifyUpdate(); 110 mPackageUserKeytoUidMap = map; 111 // Preinflate all apps RV when apps has changed, which can happen after unlocking screen, 112 // rotating screen, or downloading/upgrading apps. 113 if (shouldPreinflate) { 114 mAllAppsRecyclerViewPool.preInflateAllAppsViewHolders(mContext); 115 } 116 } 117 getRecyclerViewPool()118 AllAppsRecyclerViewPool getRecyclerViewPool() { 119 return mAllAppsRecyclerViewPool; 120 } 121 122 /** 123 * Look up for Uid using package name and user handle for the current set of apps. 124 */ lookUpForUid(String packageName, UserHandle user)125 public int lookUpForUid(String packageName, UserHandle user) { 126 return mPackageUserKeytoUidMap.getOrDefault(new PackageUserKey(packageName, user), -1); 127 } 128 129 /** 130 * @see com.android.launcher3.model.BgDataModel.Callbacks#FLAG_QUIET_MODE_ENABLED 131 * @see com.android.launcher3.model.BgDataModel.Callbacks#FLAG_HAS_SHORTCUT_PERMISSION 132 * @see com.android.launcher3.model.BgDataModel.Callbacks#FLAG_QUIET_MODE_CHANGE_PERMISSION 133 * @see com.android.launcher3.model.BgDataModel.Callbacks#FLAG_WORK_PROFILE_QUIET_MODE_ENABLED 134 * @see 135 * com.android.launcher3.model.BgDataModel.Callbacks#FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED 136 */ hasModelFlag(int mask)137 public boolean hasModelFlag(int mask) { 138 return (mModelFlags & mask) != 0; 139 } 140 141 /** 142 * Returns {@link AppInfo} if any apps matches with provided {@link ComponentKey}, otherwise 143 * null. 144 * 145 * Uses {@link AppInfo#COMPONENT_KEY_COMPARATOR} as a default comparator. 146 */ 147 @Nullable getApp(ComponentKey key)148 public AppInfo getApp(ComponentKey key) { 149 return getApp(key, COMPONENT_KEY_COMPARATOR); 150 } 151 152 /** 153 * Generic version of {@link #getApp(ComponentKey)} that allows comparator to be specified. 154 */ 155 @Nullable getApp(ComponentKey key, Comparator<AppInfo> comparator)156 public AppInfo getApp(ComponentKey key, Comparator<AppInfo> comparator) { 157 mTempInfo.componentName = key.componentName; 158 mTempInfo.user = key.user; 159 int index = Arrays.binarySearch(mApps, mTempInfo, comparator); 160 return index < 0 ? null : mApps[index]; 161 } 162 enableDeferUpdates(int flag)163 public void enableDeferUpdates(int flag) { 164 mDeferUpdatesFlags |= flag; 165 Log.d(TAG, "enableDeferUpdates: " + flag + " mDeferUpdatesFlags=" + mDeferUpdatesFlags); 166 } 167 disableDeferUpdates(int flag)168 public void disableDeferUpdates(int flag) { 169 mDeferUpdatesFlags &= ~flag; 170 Log.d(TAG, "disableDeferUpdates: " + flag + " mDeferUpdatesFlags=" + mDeferUpdatesFlags); 171 if (mDeferUpdatesFlags == 0 && mUpdatePending) { 172 notifyUpdate(); 173 mUpdatePending = false; 174 } 175 } 176 disableDeferUpdatesSilently(int flag)177 public void disableDeferUpdatesSilently(int flag) { 178 mDeferUpdatesFlags &= ~flag; 179 Log.d(TAG, "disableDeferUpdatesSilently: " + flag 180 + " mDeferUpdatesFlags=" + mDeferUpdatesFlags); 181 182 } 183 getDeferUpdatesFlags()184 public int getDeferUpdatesFlags() { 185 return mDeferUpdatesFlags; 186 } 187 notifyUpdate()188 private void notifyUpdate() { 189 if (mDeferUpdatesFlags != 0) { 190 Log.d(TAG, "notifyUpdate: deferring update"); 191 mUpdatePending = true; 192 return; 193 } 194 Log.d(TAG, "notifyUpdate: notifying listeners"); 195 for (OnUpdateListener listener : mUpdateListeners) { 196 listener.onAppsUpdated(); 197 } 198 } 199 addUpdateListener(OnUpdateListener listener)200 public void addUpdateListener(OnUpdateListener listener) { 201 mUpdateListeners.add(listener); 202 } 203 removeUpdateListener(OnUpdateListener listener)204 public void removeUpdateListener(OnUpdateListener listener) { 205 mUpdateListeners.remove(listener); 206 } 207 registerIconContainer(ViewGroup container)208 public void registerIconContainer(ViewGroup container) { 209 if (container != null && !mIconContainers.contains(container)) { 210 mIconContainers.add(container); 211 } 212 } 213 unregisterIconContainer(ViewGroup container)214 public void unregisterIconContainer(ViewGroup container) { 215 mIconContainers.remove(container); 216 } 217 updateNotificationDots(Predicate<PackageUserKey> updatedDots)218 public void updateNotificationDots(Predicate<PackageUserKey> updatedDots) { 219 updateAllIcons((child) -> { 220 if (child.getTag() instanceof ItemInfo) { 221 ItemInfo info = (ItemInfo) child.getTag(); 222 if (mTempKey.updateFromItemInfo(info) && updatedDots.test(mTempKey)) { 223 child.applyDotState(info, true /* animate */); 224 } 225 } 226 }); 227 } 228 229 /** 230 * Sets the AppInfo's associated icon's progress bar. 231 * 232 * If this app is installed and supports incremental downloads, the progress bar will be updated 233 * the app's total download progress. Otherwise, the progress bar will be updated to the app's 234 * installation progress. 235 * 236 * If this app is fully downloaded, the app icon will be reapplied. 237 */ updateProgressBar(AppInfo app)238 public void updateProgressBar(AppInfo app) { 239 updateAllIcons((child) -> { 240 if (child.getTag() == app) { 241 child.applyFromApplicationInfo(app); 242 } 243 }); 244 } 245 updateAllIcons(Consumer<BubbleTextView> action)246 private void updateAllIcons(Consumer<BubbleTextView> action) { 247 for (int i = mIconContainers.size() - 1; i >= 0; i--) { 248 ViewGroup parent = mIconContainers.get(i); 249 int childCount = parent.getChildCount(); 250 251 for (int j = 0; j < childCount; j++) { 252 View child = parent.getChildAt(j); 253 if (child instanceof BubbleTextView) { 254 action.accept((BubbleTextView) child); 255 } 256 } 257 } 258 } 259 260 public interface OnUpdateListener { onAppsUpdated()261 void onAppsUpdated(); 262 } 263 264 /** Generate a dumpsys for each app package name and position in the apps list */ dump(String prefix, PrintWriter writer)265 public void dump(String prefix, PrintWriter writer) { 266 writer.println(prefix + "\tAllAppsStore Apps[] size: " + mApps.length); 267 for (int i = 0; i < mApps.length; i++) { 268 writer.println(String.format(Locale.getDefault(), 269 "%s\tPackage index, name, class, description, bitmap flag: %d/%s:%s, %s, %s+%s", 270 prefix, 271 i, 272 mApps[i].componentName.getPackageName(), 273 mApps[i].componentName.getClassName(), 274 mApps[i].contentDescription, 275 Integer.toBinaryString(mApps[i].bitmap.flags), 276 Integer.toBinaryString(mApps[i].bitmap.creationFlags))); 277 } 278 } 279 } 280