Merge "Provide more accurate feedback on nav bar region gestures during the back gesture tutorial." into tm-dev
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 097609f..0f3474e 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -42,7 +42,6 @@
 import static com.android.launcher3.config.FeatureFlags.ENABLE_SCRIM_FOR_APP_LAUNCH;
 import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION;
 import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
-import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_TRANSITIONS;
 import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
 import static com.android.launcher3.statehandlers.DepthController.DEPTH;
 import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
@@ -105,7 +104,6 @@
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.ActivityOptionsWrapper;
 import com.android.launcher3.util.DynamicResource;
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.launcher3.util.ObjectWrapper;
 import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.Themes;
@@ -195,9 +193,7 @@
     private static final int WIDGET_CROSSFADE_DURATION_MILLIS = 125;
 
     protected final BaseQuickstepLauncher mLauncher;
-
     private final DragLayer mDragLayer;
-    private final AlphaProperty mDragLayerAlpha;
 
     final Handler mHandler;
 
@@ -241,7 +237,6 @@
     public QuickstepTransitionManager(Context context) {
         mLauncher = Launcher.cast(Launcher.getLauncher(context));
         mDragLayer = mLauncher.getDragLayer();
-        mDragLayerAlpha = mDragLayer.getAlphaProperty(ALPHA_INDEX_TRANSITIONS);
         mHandler = new Handler(Looper.getMainLooper());
         mDeviceProfile = mLauncher.getDeviceProfile();
         mBackAnimationController = new LauncherBackAnimationController(mLauncher, this);
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
index a9ae7bd..b797807 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -176,7 +176,8 @@
         return revealAnim;
     }
 
-    public void onIsStashed(boolean isStashed) {
+    /** Called when taskbar is stashed or unstashed. */
+    public void onIsStashedChanged(boolean isStashed) {
         mRegionSamplingHelper.setWindowVisible(isStashed);
         if (isStashed) {
             mStashedHandleView.updateSampledRegion(mStashedHandleBounds);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 9561b74..b349637 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -559,7 +559,7 @@
             }
         }
         mWindowLayoutParams.height = height;
-        mControllers.taskbarInsetsController.onTaskbarWindowHeightChanged();
+        mControllers.taskbarInsetsController.onTaskbarWindowHeightOrInsetsChanged();
         mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index 5d3a152..ff08e3d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -160,6 +160,7 @@
         taskbarForceVisibleImmersiveController.onDestroy();
         taskbarAllAppsController.onDestroy();
         navButtonController.onDestroy();
+        taskbarInsetsController.onDestroy();
 
         mControllersToLog = null;
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
index 089c26d..c1a6185 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -83,7 +83,6 @@
     private void onComputeTaskbarInsets(InsetsInfo insetsInfo) {
         if (mControllerCallbacks != null) {
             mControllerCallbacks.updateInsetsTouchability(insetsInfo);
-            mControllerCallbacks.updateContentInsets(insetsInfo.contentInsets);
         }
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 307f674..3e2695c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -164,16 +164,6 @@
         }
 
         /**
-         * Called to update the {@link InsetsInfo#contentInsets}. This is reported to apps but our
-         * internal launcher will ignore these insets.
-         */
-        public void updateContentInsets(Rect outContentInsets) {
-            int contentHeight = mControllers.taskbarStashController
-                    .getContentHeightToReportToApps();
-            outContentInsets.top = mTaskbarDragLayer.getHeight() - contentHeight;
-        }
-
-        /**
          * Called when a child is removed from TaskbarDragLayer.
          */
         public void onDragLayerViewRemoved() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index a2ff780..9870a2e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -18,6 +18,7 @@
 import android.graphics.Insets
 import android.view.WindowManager
 import com.android.launcher3.AbstractFloatingView
+import com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_ALL_APPS
 import com.android.launcher3.R
 import com.android.launcher3.anim.AlphaUpdateListener
 import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController
@@ -53,20 +54,40 @@
             )
         )
 
+        windowLayoutParams.providedInternalInsets = arrayOfNulls<Insets>(ITYPE_SIZE)
         windowLayoutParams.providedInternalImeInsets = arrayOfNulls<Insets>(ITYPE_SIZE)
 
-        onTaskbarWindowHeightChanged()
+        onTaskbarWindowHeightOrInsetsChanged()
 
         windowLayoutParams.insetsRoundedCornerFrame = true
     }
 
-    fun onTaskbarWindowHeightChanged() {
-        val reducingSize = Insets.of(0, windowLayoutParams.height - taskbarHeightForIme, 0, 0)
+    fun onDestroy() {}
+
+    fun onTaskbarWindowHeightOrInsetsChanged() {
+        var reducingSize = getReducingInsetsForTaskbarInsetsHeight(
+            controllers.taskbarStashController.contentHeightToReportToApps)
+        windowLayoutParams.providedInternalInsets[ITYPE_EXTRA_NAVIGATION_BAR] = reducingSize
+        reducingSize = getReducingInsetsForTaskbarInsetsHeight(
+            controllers.taskbarStashController.tappableHeightToReportToApps)
+        windowLayoutParams.providedInternalInsets[ITYPE_BOTTOM_TAPPABLE_ELEMENT] = reducingSize
+
+        reducingSize = getReducingInsetsForTaskbarInsetsHeight(taskbarHeightForIme)
         windowLayoutParams.providedInternalImeInsets[ITYPE_EXTRA_NAVIGATION_BAR] = reducingSize
         windowLayoutParams.providedInternalImeInsets[ITYPE_BOTTOM_TAPPABLE_ELEMENT] = reducingSize
     }
 
     /**
+     * WindowLayoutParams.providedInternal*Insets expects Insets that subtract from the window frame
+     * height (i.e. WindowLayoutParams#height). So for Taskbar to report bottom insets to apps, it
+     * actually provides insets from the top of its window frame.
+     * @param height The number of pixels from the bottom of the screen that Taskbar insets.
+     */
+    private fun getReducingInsetsForTaskbarInsetsHeight(height: Int): Insets {
+        return Insets.of(0, windowLayoutParams.height - height, 0, 0)
+    }
+
+    /**
      * Called to update the touchable insets.
      * @see InsetsInfo.setTouchableInsets
      */
@@ -88,18 +109,11 @@
         } else if (controllers.taskbarDragController.isSystemDragInProgress) {
             // Let touches pass through us.
             insetsInfo.setTouchableInsets(InsetsInfo.TOUCHABLE_INSETS_REGION)
-        } else if (AbstractFloatingView.getOpenView<AbstractFloatingView?>(
-                context,
-                AbstractFloatingView.TYPE_TASKBAR_ALL_APPS
-            ) != null
-        ) {
+        } else if (AbstractFloatingView.hasOpenView(context, TYPE_TASKBAR_ALL_APPS)) {
             // Let touches pass through us.
             insetsInfo.setTouchableInsets(InsetsInfo.TOUCHABLE_INSETS_REGION)
         } else if (controllers.taskbarViewController.areIconsVisible()
-            || AbstractFloatingView.getOpenView<AbstractFloatingView?>(
-                context,
-                AbstractFloatingView.TYPE_ALL
-            ) != null
+            || AbstractFloatingView.hasOpenView(context, AbstractFloatingView.TYPE_ALL)
             || context.isNavBarKidsModeActive
         ) {
             // Taskbar has some touchable elements, take over the full taskbar area
@@ -120,6 +134,10 @@
     override fun dumpLogs(prefix: String, pw: PrintWriter) {
         pw.println(prefix + "TaskbarInsetsController:")
         pw.println("$prefix\twindowHeight=${windowLayoutParams.height}")
+        pw.println("$prefix\tprovidedInternalInsets[ITYPE_EXTRA_NAVIGATION_BAR]=" +
+                "${windowLayoutParams.providedInternalInsets[ITYPE_EXTRA_NAVIGATION_BAR]}")
+        pw.println("$prefix\tprovidedInternalInsets[ITYPE_BOTTOM_TAPPABLE_ELEMENT]=" +
+                "${windowLayoutParams.providedInternalInsets[ITYPE_BOTTOM_TAPPABLE_ELEMENT]}")
         pw.println("$prefix\tprovidedInternalImeInsets[ITYPE_EXTRA_NAVIGATION_BAR]=" +
                 "${windowLayoutParams.providedInternalImeInsets[ITYPE_EXTRA_NAVIGATION_BAR]}")
         pw.println("$prefix\tprovidedInternalImeInsets[ITYPE_BOTTOM_TAPPABLE_ELEMENT]=" +
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index be67136..f34759d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -30,6 +30,7 @@
 import android.content.SharedPreferences;
 import android.util.Log;
 import android.view.ViewConfiguration;
+import android.view.WindowInsets;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Utilities;
@@ -280,6 +281,8 @@
 
     /**
      * Returns the height that taskbar will inset when inside apps.
+     * @see WindowInsets.Type#navigationBars()
+     * @see WindowInsets.Type#systemBars()
      */
     public int getContentHeightToReportToApps() {
         if (supportsVisualStashing() && hasAnyFlag(FLAGS_REPORT_STASHED_INSETS_TO_APP)) {
@@ -304,6 +307,15 @@
         return mUnstashedHeight;
     }
 
+    /**
+     * Returns the height that taskbar will inset when inside apps.
+     * @see WindowInsets.Type#tappableElement()
+     */
+    public int getTappableHeightToReportToApps() {
+        int contentHeight = getContentHeightToReportToApps();
+        return contentHeight <= mStashedHeight ? 0 : contentHeight;
+    }
+
     public int getStashedHeight() {
         return mStashedHeight;
     }
@@ -442,7 +454,7 @@
             @Override
             public void onAnimationStart(Animator animation) {
                 mIsStashed = isStashed;
-                onIsStashed(mIsStashed);
+                onIsStashedChanged(mIsStashed);
             }
 
             @Override
@@ -489,8 +501,11 @@
                 .setDuration(TASKBAR_HINT_STASH_DURATION).start();
     }
 
-    private void onIsStashed(boolean isStashed) {
-        mControllers.stashedHandleViewController.onIsStashed(isStashed);
+    private void onIsStashedChanged(boolean isStashed) {
+        mControllers.runAfterInit(() -> {
+            mControllers.stashedHandleViewController.onIsStashedChanged(isStashed);
+            mControllers.taskbarInsetsController.onTaskbarWindowHeightOrInsetsChanged();
+        });
     }
 
     public void applyState() {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 947d3e2..86f26c3 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -107,7 +107,6 @@
      */
     private void handleSplitSelectionState(@NonNull LauncherState toState,
             @Nullable PendingAnimation builder) {
-        LauncherState currentState = mLauncher.getStateManager().getState();
         boolean animate = builder != null;
         PagedOrientationHandler orientationHandler =
                 ((RecentsView) mLauncher.getOverviewPanel()).getPagedOrientationHandler();
@@ -116,7 +115,7 @@
                         TASK_PRIMARY_SPLIT_TRANSLATION, TASK_SECONDARY_SPLIT_TRANSLATION,
                         mLauncher.getDeviceProfile());
 
-        if (isSplitSelectionState(currentState, toState)) {
+        if (toState == OVERVIEW_SPLIT_SELECT) {
             // Animation to "dismiss" selected taskView
             PendingAnimation splitSelectInitAnimation = mRecentsView.createSplitSelectInitAnimation(
                     toState.getTransitionDuration(mLauncher));
@@ -125,30 +124,18 @@
                     toState.getSplitSelectTranslation(mLauncher), LINEAR);
             splitSelectInitAnimation.setFloat(mRecentsView, taskViewsFloat.second, 0, LINEAR);
 
-            if (!animate && isSplitSelectionState(currentState, toState)) {
+            if (!animate) {
                 splitSelectInitAnimation.buildAnim().start();
-            } else if (animate &&
-                    isSplitSelectionState(currentState, toState)) {
+            } else {
                 builder.add(splitSelectInitAnimation.buildAnim());
             }
-        }
 
-        if (isSplitSelectionState(currentState, toState)) {
             mRecentsView.applySplitPrimaryScrollOffset();
         } else {
             mRecentsView.resetSplitPrimaryScrollOffset();
         }
     }
 
-    /**
-     * @return true if {@param toState} is {@link LauncherState#OVERVIEW_SPLIT_SELECT}
-     *          and {@param fromState} is not {@link LauncherState#OVERVIEW_SPLIT_SELECT}
-     */
-    private boolean isSplitSelectionState(@NonNull LauncherState fromState,
-            @NonNull LauncherState toState) {
-        return fromState != OVERVIEW_SPLIT_SELECT && toState == OVERVIEW_SPLIT_SELECT;
-    }
-
     private void setAlphas(PropertySetter propertySetter, StateAnimationConfig config,
             LauncherState state) {
         float clearAllButtonAlpha = state.areElementsVisible(mLauncher, CLEAR_ALL_BUTTON) ? 1 : 0;
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 4386f41..90869c2 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -208,6 +208,13 @@
     }
 
     /**
+     * Returns whether there is at least one view of the given type where {@link #isOpen()} == true.
+     */
+    public static boolean hasOpenView(ActivityContext activity, @FloatingViewType int type) {
+        return getOpenView(activity, type) != null;
+    }
+
+    /**
      * Returns a view matching FloatingViewType, and {@link #isOpen()} may be false (if animating
      * closed).
      */
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 6302739..74ec7ee 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -96,6 +96,7 @@
     private static final int MAX_SEARCH_LOOP_COUNT = 20;
 
     private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed};
+    private static final float HIGHLIGHT_SCALE = 1.16f;
 
     private final PointF mTranslationForReorderBounce = new PointF(0, 0);
     private final PointF mTranslationForReorderPreview = new PointF(0, 0);
@@ -258,12 +259,6 @@
         mDotParams.scale = 0f;
         mForceHideDot = false;
         setBackground(null);
-
-        setTag(null);
-        if (mIconLoadRequest != null) {
-            mIconLoadRequest.cancel();
-            mIconLoadRequest = null;
-        }
     }
 
     private void cancelDotScaleAnim() {
@@ -368,7 +363,8 @@
         }
     }
 
-    public void setBubbleTextHolder(BubbleTextHolder bubbleTextHolder) {
+    public void setBubbleTextHolder(
+            BubbleTextHolder bubbleTextHolder) {
         mBubbleTextHolder = bubbleTextHolder;
     }
 
@@ -1024,6 +1020,19 @@
         getIconBounds(mIconSize, bounds);
     }
 
+    private int getIconSizeForDisplay(int display) {
+        DeviceProfile grid = mActivity.getDeviceProfile();
+        switch (display) {
+            case DISPLAY_ALL_APPS:
+                return grid.allAppsIconSizePx;
+            case DISPLAY_FOLDER:
+                return grid.folderChildIconSizePx;
+            case DISPLAY_WORKSPACE:
+            default:
+                return grid.iconSizePx;
+        }
+    }
+
     public void getSourceVisualDragBounds(Rect bounds) {
         getIconBounds(mIconSize, bounds);
     }
@@ -1036,8 +1045,8 @@
     }
 
     private void resetIconScale() {
-        if (mIcon != null) {
-            mIcon.resetScale();
+        if (mIcon instanceof FastBitmapDrawable) {
+            ((FastBitmapDrawable) mIcon).resetScale();
         }
     }
 
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index b6a05b0..4b42ecb 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -42,7 +42,6 @@
 import static com.android.launcher3.LauncherState.SPRING_LOADED;
 import static com.android.launcher3.Utilities.postAsyncCallback;
 import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.getSupportedActions;
-import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_LAUNCHER_LOAD;
 import static com.android.launcher3.logging.StatsLogManager.EventEnum;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
@@ -65,7 +64,6 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
 import android.app.Notification;
@@ -109,7 +107,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnPreDrawListener;
 import android.view.WindowManager.LayoutParams;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.OvershootInterpolator;
@@ -130,7 +128,6 @@
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.allapps.BaseAllAppsContainerView;
 import com.android.launcher3.allapps.DiscoveryBounce;
-import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.anim.PropertyListBuilder;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
@@ -186,8 +183,6 @@
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.MultiValueAlpha;
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PackageUserKey;
@@ -341,6 +336,7 @@
     private Runnable mOnDeferredActivityLaunchCallback;
 
     private ViewOnDrawExecutor mPendingExecutor;
+    private OnPreDrawListener mOnInitialBindListener;
 
     private LauncherModel mModel;
     private ModelWriter mModelWriter;
@@ -503,11 +499,10 @@
 
         if (!mModel.addCallbacksAndLoad(this)) {
             if (!internalStateHandled) {
-                Log.d(BAD_STATE, "Launcher onCreate not binding sync, setting DragLayer alpha "
-                        + "ALPHA_INDEX_LAUNCHER_LOAD to 0");
-                // If we are not binding synchronously, show a fade in animation when
-                // the first page bind completes.
-                mDragLayer.getAlphaProperty(ALPHA_INDEX_LAUNCHER_LOAD).setValue(0);
+                Log.d(BAD_STATE, "Launcher onCreate not binding sync, prevent drawing");
+                // If we are not binding synchronously, pause drawing until initial bind complete,
+                // so that the system could continue to show the device loading prompt
+                mOnInitialBindListener = Boolean.FALSE::booleanValue;
             }
         }
 
@@ -515,25 +510,9 @@
         setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
 
         setContentView(getRootView());
-        getRootView().getViewTreeObserver().addOnPreDrawListener(
-                new ViewTreeObserver.OnPreDrawListener() {
-                    @Override
-                    public boolean onPreDraw() {
-                        // Checks the status of fade in animation.
-                        final AlphaProperty property =
-                                mDragLayer.getAlphaProperty(ALPHA_INDEX_LAUNCHER_LOAD);
-                        if (property.getValue() == 0) {
-                            Log.d(BAD_STATE, "Launcher onPreDraw ALPHA_INDEX_LAUNCHER_LOAD not"
-                                    + " started yet, cancelling draw.");
-                            // Animation haven't started yet; suspend.
-                            return false;
-                        } else {
-                            // The animation is started; start drawing.
-                            getRootView().getViewTreeObserver().removeOnPreDrawListener(this);
-                            return true;
-                        }
-                    }
-                });
+        if (mOnInitialBindListener != null) {
+            getRootView().getViewTreeObserver().addOnPreDrawListener(mOnInitialBindListener);
+        }
         getRootView().dispatchInsets();
 
         // Listen for broadcasts
@@ -2692,36 +2671,12 @@
                     AllAppsStore.DEFER_UPDATES_NEXT_DRAW));
         }
 
-        AlphaProperty property = mDragLayer.getAlphaProperty(ALPHA_INDEX_LAUNCHER_LOAD);
-        if (property.getValue() < 1) {
-            ObjectAnimator anim = ObjectAnimator.ofFloat(property, MultiValueAlpha.VALUE, 1);
-
-            Log.d(BAD_STATE, "Launcher onInitialBindComplete toAlpha=" + 1);
-            anim.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    Log.d(BAD_STATE, "Launcher onInitialBindComplete onStart");
-                }
-
-                @Override
-                public void onAnimationCancel(Animator animation) {
-                    float alpha = mDragLayer == null
-                            ? -1
-                            : mDragLayer.getAlphaProperty(ALPHA_INDEX_LAUNCHER_LOAD).getValue();
-                    Log.d(BAD_STATE, "Launcher onInitialBindComplete onCancel, alpha=" + alpha);
-                }
-
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    Log.d(BAD_STATE, "Launcher onInitialBindComplete onEnd");
-                }
-            });
-
-            anim.addListener(AnimatorListeners.forEndCallback(executor::onLoadAnimationCompleted));
-            anim.start();
-        } else {
-            executor.onLoadAnimationCompleted();
+        if (mOnInitialBindListener != null) {
+            getRootView().getViewTreeObserver().removeOnPreDrawListener(mOnInitialBindListener);
+            mOnInitialBindListener = null;
         }
+
+        executor.onLoadAnimationCompleted();
         executor.attachTo(this);
         if (Utilities.ATLEAST_S) {
             Trace.endAsyncSection(DISPLAY_WORKSPACE_TRACE_METHOD_NAME,
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 00156b1..7687fea 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -18,9 +18,6 @@
 
 import android.content.Context;
 
-import androidx.recyclerview.widget.DiffUtil;
-import androidx.recyclerview.widget.DiffUtil.DiffResult;
-
 import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.data.AppInfo;
@@ -46,6 +43,10 @@
 
     public static final String TAG = "AlphabeticalAppsList";
 
+    private static final int FAST_SCROLL_FRACTION_DISTRIBUTE_BY_ROWS_FRACTION = 0;
+    private static final int FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS = 1;
+
+    private final int mFastScrollDistributionMode = FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS;
     private final WorkAdapterProvider mWorkAdapterProvider;
 
     /**
@@ -197,11 +198,8 @@
 
     public boolean appendSearchResults(ArrayList<AdapterItem> results) {
         if (hasFilter() && results != null && results.size() > 0) {
-            int pos = mSearchResults.size();
-            updateSearchAdapterItems(results, pos);
-            if (mAdapter != null) {
-                mAdapter.notifyItemRangeInserted(pos, results.size());
-            }
+            updateSearchAdapterItems(results, mSearchResults.size());
+            refreshRecyclerView();
             return true;
         }
         return false;
@@ -275,6 +273,10 @@
      */
     public void updateAdapterItems() {
         refillAdapterItems();
+        refreshRecyclerView();
+    }
+
+    private void refreshRecyclerView() {
         if (mAdapter != null) {
             mAdapter.notifyDataSetChanged();
         }
@@ -284,9 +286,9 @@
         String lastSectionName = null;
         FastScrollSectionInfo lastFastScrollerSectionInfo = null;
         int position = 0;
+        int appIndex = 0;
 
         // Prepare to update the list of sections, filtered apps, etc.
-        ArrayList<AdapterItem> oldList = new ArrayList<>(mAdapterItems);
         mAccessibilityResultsCount = 0;
         mFastScrollerSections.clear();
         mAdapterItems.clear();
@@ -313,7 +315,8 @@
                 }
 
                 // Create an app item
-                AdapterItem appItem = AdapterItem.asApp(position++, info);
+                AdapterItem appItem = AdapterItem.asApp(position++, sectionName, info,
+                        appIndex++);
                 if (lastFastScrollerSectionInfo.fastScrollToItem == null) {
                     lastFastScrollerSectionInfo.fastScrollToItem = appItem;
                 }
@@ -339,7 +342,6 @@
             int numAppsInSection = 0;
             int numAppsInRow = 0;
             int rowIndex = -1;
-
             for (AdapterItem item : mAdapterItems) {
                 item.rowIndex = 0;
                 if (BaseAllAppsAdapter.isDividerViewType(item.viewType)) {
@@ -358,50 +360,35 @@
             mNumAppRowsInAdapter = rowIndex + 1;
 
             // Pre-calculate all the fast scroller fractions
-            float perSectionTouchFraction = 1f / mFastScrollerSections.size();
-            float cumulativeTouchFraction = 0f;
-            for (FastScrollSectionInfo info : mFastScrollerSections) {
-                AdapterItem item = info.fastScrollToItem;
-                if (!BaseAllAppsAdapter.isIconViewType(item.viewType)) {
-                    info.touchFraction = 0f;
-                    continue;
-                }
-                info.touchFraction = cumulativeTouchFraction;
-                cumulativeTouchFraction += perSectionTouchFraction;
+            switch (mFastScrollDistributionMode) {
+                case FAST_SCROLL_FRACTION_DISTRIBUTE_BY_ROWS_FRACTION:
+                    float rowFraction = 1f / mNumAppRowsInAdapter;
+                    for (FastScrollSectionInfo info : mFastScrollerSections) {
+                        AdapterItem item = info.fastScrollToItem;
+                        if (!BaseAllAppsAdapter.isIconViewType(item.viewType)) {
+                            info.touchFraction = 0f;
+                            continue;
+                        }
+
+                        float subRowFraction =
+                                item.rowAppIndex * (rowFraction / mNumAppsPerRowAllApps);
+                        info.touchFraction = item.rowIndex * rowFraction + subRowFraction;
+                    }
+                    break;
+                case FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS:
+                    float perSectionTouchFraction = 1f / mFastScrollerSections.size();
+                    float cumulativeTouchFraction = 0f;
+                    for (FastScrollSectionInfo info : mFastScrollerSections) {
+                        AdapterItem item = info.fastScrollToItem;
+                        if (!BaseAllAppsAdapter.isIconViewType(item.viewType)) {
+                            info.touchFraction = 0f;
+                            continue;
+                        }
+                        info.touchFraction = cumulativeTouchFraction;
+                        cumulativeTouchFraction += perSectionTouchFraction;
+                    }
+                    break;
             }
         }
-
-        DiffResult result = DiffUtil.calculateDiff(new DiffCallback(oldList, mAdapterItems));
-    }
-
-    private static class DiffCallback extends DiffUtil.Callback {
-        private final List<AdapterItem> mOldItems;
-        private final List<AdapterItem> mNewItems;
-
-        DiffCallback(List<AdapterItem> oldItems, List<AdapterItem> newItems) {
-            mOldItems = oldItems;
-            mNewItems = newItems;
-        }
-
-        @Override
-        public int getOldListSize() {
-            return mOldItems.size();
-        }
-
-        @Override
-        public int getNewListSize() {
-            return mNewItems.size();
-        }
-
-        @Override
-        public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
-            return mOldItems.get(oldItemPosition).getStableId()
-                    == mNewItems.get(newItemPosition).getStableId();
-        }
-
-        @Override
-        public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
-            return mOldItems.get(oldItemPosition).isContentSame(mNewItems.get(newItemPosition));
-        }
     }
 }
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
index e912cf2..976284d 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
@@ -36,6 +36,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.views.ActivityContext;
 
 import java.util.Arrays;
@@ -93,21 +94,31 @@
         // The type of this item
         public int viewType;
 
+        // The section name of this item.  Note that there can be multiple items with different
+        // sectionNames in the same section
+        public String sectionName = null;
         // The row that this item shows up on
         public int rowIndex;
         // The index of this app in the row
         public int rowAppIndex;
         // The associated ItemInfoWithIcon for the item
-        public AppInfo itemInfo = null;
+        public ItemInfoWithIcon itemInfo = null;
+        // The index of this app not including sections
+        public int appIndex = -1;
+        // Search section associated to result
+        public DecorationInfo decorationInfo = null;
 
         /**
          * Factory method for AppIcon AdapterItem
          */
-        public static AdapterItem asApp(int pos, AppInfo appInfo) {
+        public static AdapterItem asApp(int pos, String sectionName, AppInfo appInfo,
+                int appIndex) {
             AdapterItem item = new AdapterItem();
             item.viewType = VIEW_TYPE_ICON;
             item.position = pos;
+            item.sectionName = sectionName;
             item.itemInfo = appInfo;
+            item.appIndex = appIndex;
             return item;
         }
 
@@ -144,23 +155,6 @@
         protected boolean isCountedForAccessibility() {
             return viewType == VIEW_TYPE_ICON || viewType == VIEW_TYPE_SEARCH_MARKET;
         }
-
-        public long getStableId() {
-            return viewType;
-        }
-
-        /**
-         * Called to check if the content of the item is same as the other item. This is called only
-         * if the {@link #getStableId()} matches for both the items.
-         */
-        public boolean isContentSame(AdapterItem other) {
-            // We can use itemInfo for diff, but since ItemInfo objects are singleton per Model,
-            // this could prevent updates within this itemInfo object itself (like title change
-            // or flag changes). We can create a better diffing logic if we store a clone a snapshot
-            // of the itemInfo, but that would cause icons to be loaded lazily on the cloned object
-            // instead of the singleton object.
-            return false;
-        }
     }
 
     protected final T mActivityContext;
@@ -273,7 +267,11 @@
                 AdapterItem adapterItem = mApps.getAdapterItems().get(position);
                 BubbleTextView icon = (BubbleTextView) holder.itemView;
                 icon.reset();
-                icon.applyFromApplicationInfo(adapterItem.itemInfo);
+                if (adapterItem.itemInfo instanceof AppInfo) {
+                    icon.applyFromApplicationInfo((AppInfo) adapterItem.itemInfo);
+                } else {
+                    icon.applyFromItemInfoWithIcon(adapterItem.itemInfo);
+                }
                 break;
             case VIEW_TYPE_EMPTY_SEARCH:
                 TextView emptyViewText = (TextView) holder.itemView;
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index 31c0c69..f913aa9 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
@@ -45,7 +45,6 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.core.graphics.ColorUtils;
 import androidx.recyclerview.widget.RecyclerView;
-import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DeviceProfile.DeviceProfileListenable;
@@ -728,12 +727,6 @@
                             : new BaseAdapterProvider[]{mMainAdapterProvider};
 
             adapter = getAdapter(mAppsList, adapterProviders);
-            adapter.registerAdapterDataObserver(new AdapterDataObserver() {
-                @Override
-                public void onChanged() {
-                    Log.e("Hello", "On changed", new Exception());
-                }
-            });
             mAppsList.setAdapter(adapter);
             mLayoutManager = adapter.getLayoutManager();
         }
diff --git a/src/com/android/launcher3/allapps/DecorationInfo.java b/src/com/android/launcher3/allapps/DecorationInfo.java
new file mode 100644
index 0000000..50b250c
--- /dev/null
+++ b/src/com/android/launcher3/allapps/DecorationInfo.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 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.launcher3.allapps;
+
+public class DecorationInfo {
+}
diff --git a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
index 33d0082..222c8fe 100644
--- a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
+++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
@@ -85,7 +85,7 @@
         for (int i = 0; i < total && resultCount < MAX_RESULTS_COUNT; i++) {
             AppInfo info = apps.get(i);
             if (StringMatcherUtility.matches(queryTextLower, info.title.toString(), matcher)) {
-                AdapterItem appItem = AdapterItem.asApp(resultCount, info);
+                AdapterItem appItem = AdapterItem.asApp(resultCount, "", info, resultCount);
                 result.add(appItem);
                 resultCount++;
             }
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 14e10918..8eeca7d 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -65,9 +65,7 @@
 public class DragLayer extends BaseDragLayer<Launcher> {
 
     public static final int ALPHA_INDEX_OVERLAY = 0;
-    public static final int ALPHA_INDEX_LAUNCHER_LOAD = 1;
-    public static final int ALPHA_INDEX_TRANSITIONS = 2;
-    private static final int ALPHA_CHANNEL_COUNT = 3;
+    private static final int ALPHA_CHANNEL_COUNT = 1;
 
     public static final int ANIMATION_END_DISAPPEAR = 0;
     public static final int ANIMATION_END_REMAIN_VISIBLE = 2;
diff --git a/src/com/android/launcher3/util/Executors.java b/src/com/android/launcher3/util/Executors.java
index 6978e0c..8485371 100644
--- a/src/com/android/launcher3/util/Executors.java
+++ b/src/com/android/launcher3/util/Executors.java
@@ -15,12 +15,17 @@
  */
 package com.android.launcher3.util;
 
+import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
+
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Process;
 
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.ThreadPoolExecutor;
@@ -37,7 +42,7 @@
     private static final int KEEP_ALIVE = 1;
 
     /** Dedicated executor instances for work depending on other packages. */
-    private static final Map<String, LooperExecutor> PACKAGE_EXECUTORS = new ConcurrentHashMap<>();
+    private static final Map<String, ExecutorService> PACKAGE_EXECUTORS = new ConcurrentHashMap<>();
 
     /**
      * An {@link ThreadPoolExecutor} to be used with async task with no limit on the queue size.
@@ -85,10 +90,11 @@
      *
      * @param packageName Package associated with the executor.
      */
-    public static LooperExecutor getPackageExecutor(String packageName) {
+    public static ExecutorService getPackageExecutor(String packageName) {
         return PACKAGE_EXECUTORS.computeIfAbsent(
-                packageName, p -> new LooperExecutor(
-                        createAndStartNewLooper(p, Process.THREAD_PRIORITY_DEFAULT)));
+                packageName,
+                p -> newSingleThreadExecutor(
+                        new SimpleThreadFactory(p, THREAD_PRIORITY_BACKGROUND)));
     }
 
     /**