Merge "Merging task icon cache with content description cache" into ub-launcher3-master
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 8e9c898..63688b1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -72,7 +72,6 @@
 import com.android.quickstep.BaseActivityInterface.AnimationFactory.ShelfAnimState;
 import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
 import com.android.quickstep.GestureState.GestureEndTarget;
-import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.inputconsumers.OverviewInputConsumer;
 import com.android.quickstep.util.ActiveGestureLog;
 import com.android.quickstep.util.AppWindowAnimationHelper.TargetAlphaProvider;
@@ -1192,8 +1191,7 @@
         }
         mRecentsView.onSwipeUpAnimationSuccess();
 
-        RecentsModel.INSTANCE.get(mContext).onOverviewShown(false, TAG);
-
+        SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG);
         doLogGesture(RECENTS);
         reset();
     }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
index 39b0f8d..3cb0088 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
@@ -14,7 +14,7 @@
 import com.android.launcher3.touch.SingleAxisSwipeDetector;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.quickstep.RecentsModel;
+import com.android.quickstep.SystemUiProxy;
 
 /**
  * Touch controller for handling edge swipes in landscape/seascape UI
@@ -73,7 +73,7 @@
     protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
         super.onSwipeInteractionCompleted(targetState, logAction);
         if (mStartState == NORMAL && targetState == OVERVIEW) {
-            RecentsModel.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
+            SystemUiProxy.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
         }
     }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index 28bd3d7..99b2a81 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -47,7 +47,6 @@
 import com.android.launcher3.uioverrides.states.OverviewState;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.quickstep.RecentsModel;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TouchInteractionService;
 import com.android.quickstep.util.LayoutUtils;
@@ -300,7 +299,7 @@
     protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
         super.onSwipeInteractionCompleted(targetState, logAction);
         if (mStartState == NORMAL && targetState == OVERVIEW) {
-            RecentsModel.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
+            SystemUiProxy.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/NormalizedIconLoader.java b/quickstep/src/com/android/quickstep/NormalizedIconLoader.java
deleted file mode 100644
index bd6204a..0000000
--- a/quickstep/src/com/android/quickstep/NormalizedIconLoader.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep;
-
-import android.annotation.TargetApi;
-import android.app.ActivityManager.TaskDescription;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.os.UserHandle;
-import android.util.LruCache;
-import android.util.SparseArray;
-
-import com.android.launcher3.FastBitmapDrawable;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.graphics.DrawableFactory;
-import com.android.launcher3.icons.LauncherIcons;
-import com.android.systemui.shared.recents.model.IconLoader;
-import com.android.systemui.shared.recents.model.TaskKeyLruCache;
-
-/**
- * Extension of {@link IconLoader} with icon normalization support
- */
-@TargetApi(Build.VERSION_CODES.O)
-public class NormalizedIconLoader extends IconLoader {
-
-    private final SparseArray<BitmapInfo> mDefaultIcons = new SparseArray<>();
-    private final DrawableFactory mDrawableFactory;
-    private final boolean mDisableColorExtraction;
-
-    public NormalizedIconLoader(Context context, TaskKeyLruCache<Drawable> iconCache,
-            LruCache<ComponentName, ActivityInfo> activityInfoCache,
-            boolean disableColorExtraction) {
-        super(context, iconCache, activityInfoCache);
-        mDrawableFactory = DrawableFactory.INSTANCE.get(context);
-        mDisableColorExtraction = disableColorExtraction;
-    }
-
-    @Override
-    public Drawable getDefaultIcon(int userId) {
-        synchronized (mDefaultIcons) {
-            BitmapInfo info = mDefaultIcons.get(userId);
-            if (info == null) {
-                info = getBitmapInfo(Resources.getSystem()
-                        .getDrawable(android.R.drawable.sym_def_app_icon), userId, 0, false);
-                mDefaultIcons.put(userId, info);
-            }
-
-            return new FastBitmapDrawable(info);
-        }
-    }
-
-    @Override
-    protected Drawable createBadgedDrawable(Drawable drawable, int userId, TaskDescription desc) {
-        return new FastBitmapDrawable(getBitmapInfo(drawable, userId, desc.getPrimaryColor(),
-                false));
-    }
-
-    private BitmapInfo getBitmapInfo(Drawable drawable, int userId,
-            int primaryColor, boolean isInstantApp) {
-        try (LauncherIcons la = LauncherIcons.obtain(mContext)) {
-            if (mDisableColorExtraction) {
-                la.disableColorExtraction();
-            }
-            la.setWrapperBackgroundColor(primaryColor);
-
-            // User version code O, so that the icon is always wrapped in an adaptive icon container
-            return la.createBadgedIconBitmap(drawable, UserHandle.of(userId),
-                    Build.VERSION_CODES.O, isInstantApp);
-        }
-    }
-
-    @Override
-    protected Drawable getBadgedActivityIcon(ActivityInfo activityInfo, int userId,
-            TaskDescription desc) {
-        BitmapInfo bitmapInfo = getBitmapInfo(
-                activityInfo.loadUnbadgedIcon(mContext.getPackageManager()),
-                userId,
-                desc.getPrimaryColor(),
-                activityInfo.applicationInfo.isInstantApp());
-        return mDrawableFactory.newIcon(mContext, bitmapInfo, activityInfo);
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 465d464..f248423 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -25,11 +25,9 @@
 import android.app.ActivityManager;
 import android.content.ComponentCallbacks2;
 import android.content.Context;
-import android.content.pm.LauncherApps;
 import android.os.Build;
 import android.os.Looper;
 import android.os.Process;
-import android.os.UserHandle;
 
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.systemui.shared.recents.model.Task;
@@ -48,8 +46,6 @@
 @TargetApi(Build.VERSION_CODES.O)
 public class RecentsModel extends TaskStackChangeListener {
 
-    private static final String TAG = "RecentsModel";
-
     // We do not need any synchronization for this variable as its only written on UI thread.
     public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
             new MainThreadInitializedObject<>(RecentsModel::new);
@@ -70,7 +66,6 @@
         mIconCache = new TaskIconCache(context, looper);
         mThumbnailCache = new TaskThumbnailCache(context, looper);
         ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
-        setupPackageListener();
     }
 
     public TaskIconCache getIconCache() {
@@ -183,35 +178,6 @@
         }
     }
 
-    public void onOverviewShown(boolean fromHome, String tag) {
-        SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(fromHome, tag);
-    }
-
-    private void setupPackageListener() {
-        mContext.getSystemService(LauncherApps.class).registerCallback(new LauncherApps.Callback() {
-            @Override
-            public void onPackageRemoved(String packageName, UserHandle user) {
-                mIconCache.invalidatePackage(packageName);
-            }
-
-            @Override
-            public void onPackageChanged(String packageName, UserHandle user) {
-                mIconCache.invalidatePackage(packageName);
-            }
-
-            @Override
-            public void onPackageAdded(String packageName, UserHandle user) { }
-
-            @Override
-            public void onPackagesAvailable(
-                    String[] packageNames, UserHandle user, boolean replacing) { }
-
-            @Override
-            public void onPackagesUnavailable(
-                    String[] packageNames, UserHandle user, boolean replacing) { }
-        });
-    }
-
     public void addThumbnailChangeListener(TaskThumbnailChangeListener listener) {
         mThumbnailChangeListeners.add(listener);
     }
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index 4f7d53b..873f29c 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -18,64 +18,58 @@
 import static com.android.launcher3.uioverrides.QuickstepLauncher.GO_LOW_RAM_RECENTS_ENABLED;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
-import android.content.ComponentName;
+import android.app.ActivityManager.TaskDescription;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
-import android.util.LruCache;
+import android.os.UserHandle;
+import android.util.SparseArray;
 import android.view.accessibility.AccessibilityManager;
 
+import androidx.annotation.WorkerThread;
+
+import com.android.launcher3.FastBitmapDrawable;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.graphics.DrawableFactory;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.icons.cache.HandlerRunnable;
 import com.android.launcher3.util.Preconditions;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.Task.TaskKey;
 import com.android.systemui.shared.recents.model.TaskKeyLruCache;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.PackageManagerWrapper;
 
-import java.util.Map;
 import java.util.function.Consumer;
 
 /**
  * Manages the caching of task icons and related data.
- * TODO(b/138944598): This class should later be merged into IconCache.
  */
 public class TaskIconCache {
 
     private final Handler mBackgroundHandler;
     private final AccessibilityManager mAccessibilityManager;
 
-    private final NormalizedIconLoader mIconLoader;
-
-    private final TaskKeyLruCache<Drawable> mIconCache;
-    private final TaskKeyLruCache<String> mContentDescriptionCache;
-    private final LruCache<ComponentName, ActivityInfo> mActivityInfoCache;
-
-    private TaskKeyLruCache.EvictionCallback mClearActivityInfoOnEviction =
-            new TaskKeyLruCache.EvictionCallback() {
-        @Override
-        public void onEntryEvicted(Task.TaskKey key) {
-            if (key != null) {
-                mActivityInfoCache.remove(key.getComponent());
-            }
-        }
-    };
+    private final Context mContext;
+    private final TaskKeyLruCache<TaskCacheEntry> mIconCache;
+    private final SparseArray<BitmapInfo> mDefaultIcons = new SparseArray<>();
 
     public TaskIconCache(Context context, Looper backgroundLooper) {
+        mContext = context;
         mBackgroundHandler = new Handler(backgroundLooper);
         mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
 
         Resources res = context.getResources();
         int cacheSize = res.getInteger(R.integer.recentsIconCacheSize);
-        mIconCache = new TaskKeyLruCache<>(cacheSize, mClearActivityInfoOnEviction);
-        mContentDescriptionCache = new TaskKeyLruCache<>(cacheSize, mClearActivityInfoOnEviction);
-        mActivityInfoCache = new LruCache<>(cacheSize);
-        mIconLoader = new NormalizedIconLoader(context, mIconCache, mActivityInfoCache,
-                true /* disableColorExtraction */);
+        mIconCache = new TaskKeyLruCache<>(cacheSize);
     }
 
     /**
@@ -96,15 +90,14 @@
         IconLoadRequest request = new IconLoadRequest(mBackgroundHandler) {
             @Override
             public void run() {
-                Drawable icon = mIconLoader.getIcon(task);
-                String contentDescription = loadContentDescriptionInBackground(task);
+                TaskCacheEntry entry = getCacheEntry(task);
                 if (isCanceled()) {
                     // We don't call back to the provided callback in this case
                     return;
                 }
                 MAIN_EXECUTOR.execute(() -> {
-                    task.icon = icon;
-                    task.titleDescription = contentDescription;
+                    task.icon = entry.icon;
+                    task.titleDescription = entry.contentDescription;
                     callback.accept(task);
                     onEnd();
                 });
@@ -116,51 +109,94 @@
 
     public void clear() {
         mIconCache.evictAll();
-        mContentDescriptionCache.evictAll();
     }
 
-    /**
-     * Loads the content description for the given {@param task}.
-     */
-    private String loadContentDescriptionInBackground(Task task) {
-        // Return the cached content description if it exists
-        String label = mContentDescriptionCache.getAndInvalidateIfModified(task.key);
-        if (label != null) {
-            return label;
-        }
-
-        // Skip loading content descriptions if accessibility is disabled unless low RAM recents
-        // is enabled.
-        if (!GO_LOW_RAM_RECENTS_ENABLED && !mAccessibilityManager.isEnabled()) {
-            return "";
-        }
-
-        // Skip loading the content description if the activity no longer exists
-        ActivityInfo activityInfo = mIconLoader.getAndUpdateActivityInfo(task.key);
-        if (activityInfo == null) {
-            return "";
-        }
-
-        // Load the label otherwise
-        label = ActivityManagerWrapper.getInstance().getBadgedContentDescription(activityInfo,
-                task.key.userId, task.taskDescription);
-        mContentDescriptionCache.put(task.key, label);
-        return label;
-    }
-
-
     void onTaskRemoved(TaskKey taskKey) {
         mIconCache.remove(taskKey);
     }
 
-    void invalidatePackage(String packageName) {
-        // TODO(b/138944598): Merge this class into IconCache so we can do this at the base level
-        Map<ComponentName, ActivityInfo> activityInfoCache = mActivityInfoCache.snapshot();
-        for (ComponentName cn : activityInfoCache.keySet()) {
-            if (cn.getPackageName().equals(packageName)) {
-                mActivityInfoCache.remove(cn);
+    @WorkerThread
+    private TaskCacheEntry getCacheEntry(Task task) {
+        TaskCacheEntry entry = mIconCache.getAndInvalidateIfModified(task.key);
+        if (entry != null) {
+            return entry;
+        }
+
+        TaskDescription desc = task.taskDescription;
+        TaskKey key = task.key;
+        ActivityInfo activityInfo = null;
+
+        // Create new cache entry
+        entry = new TaskCacheEntry();
+
+        // Load icon
+        // TODO: Load icon resource (b/143363444)
+        Bitmap icon = desc.getIcon();
+        if (icon != null) {
+            entry.icon = new FastBitmapDrawable(getBitmapInfo(
+                    new BitmapDrawable(mContext.getResources(), icon),
+                    key.userId,
+                    desc.getPrimaryColor(),
+                    false /* isInstantApp */));
+        } else {
+            activityInfo = PackageManagerWrapper.getInstance().getActivityInfo(
+                    key.getComponent(), key.userId);
+            if (activityInfo != null) {
+                BitmapInfo bitmapInfo = getBitmapInfo(
+                        activityInfo.loadUnbadgedIcon(mContext.getPackageManager()),
+                        key.userId,
+                        desc.getPrimaryColor(),
+                        activityInfo.applicationInfo.isInstantApp());
+                entry.icon = DrawableFactory.INSTANCE.get(mContext).newIcon(
+                        mContext, bitmapInfo, activityInfo);
+            } else {
+                entry.icon = getDefaultIcon(key.userId);
             }
         }
+
+        // Loading content descriptions if accessibility or low RAM recents is enabled.
+        if (GO_LOW_RAM_RECENTS_ENABLED || mAccessibilityManager.isEnabled()) {
+            // Skip loading the content description if the activity no longer exists
+            if (activityInfo == null) {
+                activityInfo = PackageManagerWrapper.getInstance().getActivityInfo(
+                        key.getComponent(), key.userId);
+            }
+            if (activityInfo != null) {
+                entry.contentDescription = ActivityManagerWrapper.getInstance()
+                        .getBadgedContentDescription(activityInfo, task.key.userId,
+                                task.taskDescription);
+            }
+        }
+
+        mIconCache.put(task.key, entry);
+        return entry;
+    }
+
+    @WorkerThread
+    private Drawable getDefaultIcon(int userId) {
+        synchronized (mDefaultIcons) {
+            BitmapInfo info = mDefaultIcons.get(userId);
+            if (info == null) {
+                try (LauncherIcons la = LauncherIcons.obtain(mContext)) {
+                    info = la.makeDefaultIcon(UserHandle.of(userId));
+                }
+                mDefaultIcons.put(userId, info);
+            }
+            return new FastBitmapDrawable(info);
+        }
+    }
+
+    @WorkerThread
+    private BitmapInfo getBitmapInfo(Drawable drawable, int userId,
+            int primaryColor, boolean isInstantApp) {
+        try (LauncherIcons la = LauncherIcons.obtain(mContext)) {
+            la.disableColorExtraction();
+            la.setWrapperBackgroundColor(primaryColor);
+
+            // User version code O, so that the icon is always wrapped in an adaptive icon container
+            return la.createBadgedIconBitmap(drawable, UserHandle.of(userId),
+                    Build.VERSION_CODES.O, isInstantApp);
+        }
     }
 
     public static abstract class IconLoadRequest extends HandlerRunnable {
@@ -168,4 +204,9 @@
             super(handler, null);
         }
     }
+
+    private static class TaskCacheEntry {
+        public Drawable icon;
+        public String contentDescription = "";
+    }
 }