Merge "Fixing generation of fake app-to-recents events" into ub-launcher3-master
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 0956048..9b6c053 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -25,6 +25,9 @@
     <dimen name="task_fade_length">20dp</dimen>
     <dimen name="recents_page_spacing">10dp</dimen>
 
+    <!-- The speed in dp/s at which the user needs to be scrolling in recents such that we start
+             loading full resolution screenshots. -->
+    <dimen name="recents_fast_fling_velocity">600dp</dimen>
 
     <dimen name="quickstep_fling_threshold_velocity">500dp</dimen>
     <dimen name="quickstep_fling_min_velocity">250dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 5259e04..a5af173 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -500,10 +500,8 @@
         mFloatingView = new View(mLauncher);
         if (isBubbleTextView && v.getTag() instanceof ItemInfoWithIcon ) {
             // Create a copy of the app icon
-            ItemInfoWithIcon info = (ItemInfoWithIcon) v.getTag();
-            FastBitmapDrawable d = DrawableFactory.get(mLauncher).newIcon(info);
-            d.setIsDisabled(info.isDisabled());
-            mFloatingView.setBackground(d);
+            mFloatingView.setBackground(
+                    DrawableFactory.get(mLauncher).newIcon((ItemInfoWithIcon) v.getTag()));
         }
 
         // Position the floating view exactly on top of the original
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index 7bd4366..71cdd10 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -29,6 +29,7 @@
 import com.android.launcher3.graphics.BitmapRenderer;
 import com.android.launcher3.util.TouchController;
 import com.android.quickstep.OverviewInteractionState;
+import com.android.quickstep.RecentsModel;
 import com.android.quickstep.RecentsView;
 import com.android.systemui.shared.recents.view.RecentsTransition;
 
@@ -88,4 +89,18 @@
         RecentsView recents = launcher.getOverviewPanel();
         recents.reset();
     }
+
+    public static void onStart(Launcher launcher) {
+        RecentsModel model = RecentsModel.getInstance(launcher);
+        if (model != null) {
+            model.onStart();
+        }
+    }
+
+    public static void onTrimMemory(Launcher launcher, int level) {
+        RecentsModel model = RecentsModel.getInstance(launcher);
+        if (model != null) {
+            model.onTrimMemory(level);
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 7fe7751..90e857c 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -16,6 +16,8 @@
 package com.android.quickstep;
 
 import android.annotation.TargetApi;
+import android.app.ActivityManager;
+import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
@@ -29,6 +31,7 @@
 import android.util.LruCache;
 import android.util.SparseArray;
 
+import com.android.launcher3.Launcher;
 import com.android.launcher3.MainThreadExecutor;
 import com.android.launcher3.R;
 import com.android.launcher3.util.Preconditions;
@@ -51,7 +54,6 @@
  */
 @TargetApi(Build.VERSION_CODES.O)
 public class RecentsModel extends TaskStackChangeListener {
-
     // We do not need any synchronization for this variable as its only written on UI thread.
     private static RecentsModel INSTANCE;
 
@@ -83,10 +85,16 @@
     private int mTaskChangeId;
     private ISystemUiProxy mSystemUiProxy;
     private boolean mClearAssistCacheOnStackChange = true;
+    private final boolean mPreloadTasksInBackground;
 
     private RecentsModel(Context context) {
         mContext = context;
 
+        ActivityManager activityManager =
+                (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+        mPreloadTasksInBackground = !activityManager.isLowRamDevice();
+        mMainThreadExecutor = new MainThreadExecutor();
+
         Resources res = context.getResources();
         mRecentsTaskLoader = new RecentsTaskLoader(mContext,
                 res.getInteger(R.integer.config_recentsMaxThumbnailCacheSize),
@@ -100,8 +108,6 @@
             }
         };
         mRecentsTaskLoader.startLoader(mContext);
-
-        mMainThreadExecutor = new MainThreadExecutor();
         ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
 
         mTaskChangeId = 1;
@@ -162,6 +168,31 @@
         }
     }
 
+    @Override
+    public void onTaskStackChangedBackground() {
+        int userId = UserHandle.myUserId();
+        if (!mPreloadTasksInBackground || !checkCurrentUserId(userId, false /* debug */)) {
+            // TODO: Only register this for the current user
+            return;
+        }
+
+        // Preload a fixed number of task icons/thumbnails in the background
+        ActivityManager.RunningTaskInfo runningTaskInfo =
+                ActivityManagerWrapper.getInstance().getRunningTask();
+        RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(mContext);
+        RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
+        launchOpts.runningTaskId = runningTaskInfo != null ? runningTaskInfo.id : -1;
+        launchOpts.numVisibleTasks = 2;
+        launchOpts.numVisibleTaskThumbnails = 2;
+        launchOpts.onlyLoadForCache = true;
+        launchOpts.onlyLoadPausedActivities = true;
+        launchOpts.loadThumbnails = true;
+        PreloadOptions preloadOpts = new PreloadOptions();
+        preloadOpts.loadTitles = false;
+        plan.preloadPlan(preloadOpts, mRecentsTaskLoader, -1, userId);
+        mRecentsTaskLoader.loadTasks(plan, launchOpts);
+    }
+
     public boolean isLoadPlanValid(int resultId) {
         return mTaskChangeId == resultId;
     }
@@ -178,6 +209,19 @@
         return mSystemUiProxy;
     }
 
+    public void onStart() {
+        mRecentsTaskLoader.startLoader(mContext);
+        mRecentsTaskLoader.getHighResThumbnailLoader().setVisible(true);
+    }
+
+    public void onTrimMemory(int level) {
+        if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
+            // We already stop the loader in UI_HIDDEN, so stop the high res loader as well
+            mRecentsTaskLoader.getHighResThumbnailLoader().setVisible(false);
+        }
+        mRecentsTaskLoader.onTrimMemory(level);
+    }
+
     @WorkerThread
     public void preloadAssistData(int taskId, Bundle data) {
         mMainThreadExecutor.execute(() -> {
diff --git a/quickstep/src/com/android/quickstep/RecentsView.java b/quickstep/src/com/android/quickstep/RecentsView.java
index 9e07b49..42677c1 100644
--- a/quickstep/src/com/android/quickstep/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/RecentsView.java
@@ -22,9 +22,11 @@
 import static com.android.quickstep.TaskView.CURVE_INTERPOLATOR;
 
 import android.animation.LayoutTransition;
+import android.animation.LayoutTransition.TransitionListener;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapShader;
 import android.graphics.Canvas;
@@ -38,8 +40,10 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.util.SparseBooleanArray;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
 import com.android.launcher3.DeviceProfile;
@@ -82,6 +86,8 @@
     private LayoutTransition mLayoutTransition;
     private Runnable mNextPageSwitchRunnable;
 
+    private float mFastFlingVelocity;
+
     /**
      * TODO: Call reloadIdNeeded in onTaskStackChanged.
      */
@@ -114,6 +120,7 @@
     private boolean mScrimOnLeft;
 
     private boolean mFirstTaskIconScaledDown = false;
+    private SparseBooleanArray mPrevVisibleTasks = new SparseBooleanArray();
 
     public RecentsView(Context context) {
         this(context, null);
@@ -167,19 +174,37 @@
 
     private void setupLayoutTransition() {
         // We want to show layout transitions when pages are deleted, to close the gap.
+        // TODO: We should this manually so we can control the animation (fill in the gap as the
+        // dismissing task is being tracked, and also so we can update the visible task data during
+        // the transition. For now, the workaround is to expand the visible tasks to load.
         mLayoutTransition = new LayoutTransition();
         mLayoutTransition.enableTransitionType(LayoutTransition.DISAPPEARING);
         mLayoutTransition.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
 
         mLayoutTransition.disableTransitionType(LayoutTransition.APPEARING);
         mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
+        mLayoutTransition.addTransitionListener(new TransitionListener() {
+            @Override
+            public void startTransition(LayoutTransition layoutTransition, ViewGroup viewGroup,
+                    View view, int i) {
+                loadVisibleTaskData();
+            }
+
+            @Override
+            public void endTransition(LayoutTransition layoutTransition, ViewGroup viewGroup,
+                    View view, int i) {
+                loadVisibleTaskData();
+            }
+        });
         setLayoutTransition(mLayoutTransition);
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+        Resources res = getResources();
         mFirstTaskIndex = getPageCount();
+        mFastFlingVelocity = res.getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
     }
 
     @Override
@@ -307,20 +332,25 @@
         }
         while (getChildCount() > requiredChildCount) {
             final TaskView taskView = (TaskView) getChildAt(getChildCount() - 1);
+            final Task task = taskView.getTask();
             removeView(taskView);
-            loader.unloadTaskData(taskView.getTask());
+            loader.unloadTaskData(task);
+            loader.getHighResThumbnailLoader().onTaskInvisible(task);
         }
         setLayoutTransition(mLayoutTransition);
 
         // Rebind and reset all task views
         for (int i = tasks.size() - 1; i >= 0; i--) {
+            final int pageIndex = tasks.size() - i - 1 + mFirstTaskIndex;
             final Task task = tasks.get(i);
-            final TaskView taskView = (TaskView) getChildAt(tasks.size() - i - 1 + mFirstTaskIndex);
+            final TaskView taskView = (TaskView) getChildAt(pageIndex);
             taskView.bind(task);
             taskView.resetVisualProperties();
-            loader.loadTaskData(task);
         }
         updateCurveProperties();
+        // Reload the set of visible task's data
+        mPrevVisibleTasks.clear();
+        loadVisibleTaskData();
         applyIconScale(false /* animate */);
 
         if (oldChildCount != getChildCount()) {
@@ -414,9 +444,24 @@
     }
 
     @Override
-    public void computeScroll() {
-        super.computeScroll();
+    protected boolean computeScrollHelper() {
+        boolean scrolling = super.computeScrollHelper();
+        boolean isFlingingFast = false;
         updateCurveProperties();
+        if (scrolling || (mTouchState == TOUCH_STATE_SCROLLING)) {
+            if (scrolling) {
+                // Check if we are flinging quickly to disable high res thumbnail loading
+                isFlingingFast = mScroller.getCurrVelocity() > mFastFlingVelocity;
+            }
+
+            // After scrolling, update the visible task's data
+            loadVisibleTaskData();
+        }
+
+        // Update the high res thumbnail loader
+        RecentsTaskLoader loader = mModel.getRecentsTaskLoader();
+        loader.getHighResThumbnailLoader().setFlingingFast(isFlingingFast);
+        return scrolling;
     }
 
     /**
@@ -444,6 +489,34 @@
         }
     }
 
+    /**
+     * Iterates through all thet asks, and loads the associated task data for newly visible tasks,
+     * and unloads the associated task data for tasks that are no longer visible.
+     */
+    private void loadVisibleTaskData() {
+        RecentsTaskLoader loader = mModel.getRecentsTaskLoader();
+        int centerPageIndex = getPageNearestToCenterOfScreen();
+        int lower = Math.max(mFirstTaskIndex, centerPageIndex - 2);
+        int upper = Math.min(centerPageIndex + 2, getChildCount() - 1);
+        for (int i = mFirstTaskIndex; i < getChildCount(); i++) {
+            TaskView taskView = (TaskView) getChildAt(i);
+            Task task = taskView.getTask();
+            boolean visible = lower <= i && i <= upper;
+            if (visible) {
+                if (!mPrevVisibleTasks.get(i)) {
+                    loader.loadTaskData(task);
+                    loader.getHighResThumbnailLoader().onTaskVisible(task);
+                }
+            } else {
+                if (mPrevVisibleTasks.get(i)) {
+                    loader.unloadTaskData(task);
+                    loader.getHighResThumbnailLoader().onTaskInvisible(task);
+                }
+            }
+            mPrevVisibleTasks.put(i, visible);
+        }
+    }
+
     public void onTaskDismissed(TaskView taskView) {
         ActivityManagerWrapper.getInstance().removeTask(taskView.getTask().key.id);
         removeView(taskView);
@@ -493,14 +566,16 @@
             addView(taskView, mFirstTaskIndex);
             setLayoutTransition(mLayoutTransition);
         }
+        mRunningTaskId = runningTaskId;
+        setCurrentPage(mFirstTaskIndex);
         if (!needsReload) {
             needsReload = !mModel.isLoadPlanValid(mLoadPlanId);
         }
         if (needsReload) {
             mLoadPlanId = mModel.loadTasks(runningTaskId, this::applyLoadPlan);
+        } else {
+            loadVisibleTaskData();
         }
-        mRunningTaskId = runningTaskId;
-        setCurrentPage(mFirstTaskIndex);
         if (mCurrentPage >= mFirstTaskIndex) {
             getPageAt(mCurrentPage).setAlpha(0);
         }
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index dbdb2dc..4f50d1a 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -231,7 +231,6 @@
         FastBitmapDrawable iconDrawable = DrawableFactory.get(getContext()).newIcon(info);
         mBadgeColor = IconPalette.getMutedColor(info.iconColor, 0.54f);
 
-        iconDrawable.setIsDisabled(info.isDisabled());
         setIcon(iconDrawable);
         setText(info.title);
         if (info.contentDescription != null) {
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index ba55b36..ea52324 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -250,6 +250,14 @@
     }
 
     /**
+     * Inverse of {@link #getMultiWindowProfile(Context, Point)}
+     * @return device profile corresponding to the current orientation in non multi-window mode.
+     */
+    public DeviceProfile getFullScreenProfile() {
+        return isLandscape ? inv.landscapeProfile : inv.portraitProfile;
+    }
+
+    /**
      * Adjusts the profile so that the labels on the Workspace are hidden.
      * It is important to call this method after the All Apps variables have been set.
      */
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 5ac53a8..38b0140 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -829,6 +829,7 @@
             mScrimAnimator.start();
         }
         mShouldFadeInScrim = false;
+        UiFactory.onStart(this);
     }
 
     @Override
@@ -2171,6 +2172,7 @@
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onTrimMemory(level);
         }
+        UiFactory.onTrimMemory(this, level);
     }
 
     @Override
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index ca8039c..6ec209a 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -552,7 +552,6 @@
                 // Notify the user when the page changes
                 announceForAccessibility(getCurrentPageDescription());
             }
-            return true;
         }
         return false;
     }
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index d8a9f63..416469f 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -131,7 +131,7 @@
                 mSnappedScrolledY = currentScrollY - mMaxTranslation;
             } else if (mTranslationY <= -mMaxTranslation) { // hide or stay hidden
                 mHeaderCollapsed = true;
-                mSnappedScrolledY = currentScrollY;
+                mSnappedScrolledY = -mMaxTranslation;
             }
         }
     }
diff --git a/src/com/android/launcher3/graphics/DrawableFactory.java b/src/com/android/launcher3/graphics/DrawableFactory.java
index 32d9e41..3393469 100644
--- a/src/com/android/launcher3/graphics/DrawableFactory.java
+++ b/src/com/android/launcher3/graphics/DrawableFactory.java
@@ -65,7 +65,9 @@
      * Returns a FastBitmapDrawable with the icon.
      */
     public FastBitmapDrawable newIcon(ItemInfoWithIcon info) {
-        return new FastBitmapDrawable(info);
+        FastBitmapDrawable drawable = new FastBitmapDrawable(info);
+        drawable.setIsDisabled(info.isDisabled());
+        return drawable;
     }
 
     /**
diff --git a/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java b/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java
index 37e5efcb..3a24c3d 100644
--- a/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java
@@ -44,7 +44,8 @@
         mPaint = new TextPaint();
         mPaint.setColor(Color.WHITE);
         mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,
-                mLauncher.getDeviceProfile().iconTextSizePx, getResources().getDisplayMetrics()));
+                mLauncher.getDeviceProfile().getFullScreenProfile().iconTextSizePx,
+                getResources().getDisplayMetrics()));
         setBackgroundResource(R.drawable.bg_deferred_app_widget);
     }
 
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
index 64a29ea..a16ae48 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
@@ -64,4 +64,8 @@
     public static void resetOverview(Launcher launcher) { }
 
     public static void onLauncherStateOrFocusChanged(Launcher launcher) { }
+
+    public static void onStart(Launcher launcher) { }
+
+    public static void onTrimMemory(Launcher launcher, int level) { }
 }