Merge "Prevent blinking when user presses home." into ub-launcher3-master
diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar
index d230de6..74d3cba 100644
--- a/quickstep/libs/sysui_shared.jar
+++ b/quickstep/libs/sysui_shared.jar
Binary files differ
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 35246b6..11bc883 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -30,7 +30,6 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
-import android.graphics.Bitmap;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.os.Build;
@@ -41,13 +40,13 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.animation.Interpolator;
-import android.widget.ImageView;
 
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.InsettableFrameLayout.LayoutParams;
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.graphics.DrawableFactory;
 import com.android.systemui.shared.system.ActivityCompat;
 import com.android.systemui.shared.system.ActivityOptionsCompat;
 import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
@@ -85,7 +84,7 @@
     private final float mContentTransY;
     private final float mWorkspaceTransY;
 
-    private ImageView mFloatingView;
+    private View mFloatingView;
     private boolean mIsRtl;
 
     private LauncherTransitionAnimator mCurrentAnimator;
@@ -243,11 +242,11 @@
      */
     private AnimatorSet getIconAnimator(View v) {
         boolean isBubbleTextView = v instanceof BubbleTextView;
-        mFloatingView = new ImageView(mLauncher);
-        if (isBubbleTextView) {
+        mFloatingView = new View(mLauncher);
+        if (isBubbleTextView && v.getTag() instanceof ItemInfoWithIcon ) {
             // Create a copy of the app icon
-            Bitmap iconBitmap = ((FastBitmapDrawable) ((BubbleTextView) v).getIcon()).getBitmap();
-            mFloatingView.setImageDrawable(new FastBitmapDrawable(iconBitmap));
+            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/quickstep/LauncherLayoutListener.java b/quickstep/src/com/android/quickstep/LauncherLayoutListener.java
index 2a7e5c4..fbdbe7a 100644
--- a/quickstep/src/com/android/quickstep/LauncherLayoutListener.java
+++ b/quickstep/src/com/android/quickstep/LauncherLayoutListener.java
@@ -42,12 +42,6 @@
 
     @Override
     public void setInsets(Rect insets) {
-        requestLayout();
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        super.onLayout(changed, l, t, r, b);
         if (mHandler != null) {
             mHandler.onLauncherLayoutChanged();
         }
diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
index 86e28f2..61d4790 100644
--- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
+++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
@@ -279,10 +279,11 @@
                 new RecentsAnimationListener() {
                     public void onAnimationStart(
                             RecentsAnimationControllerCompat controller,
-                            RemoteAnimationTargetCompat[] apps) {
+                            RemoteAnimationTargetCompat[] apps, Rect homeContentInsets,
+                            Rect minimizedHomeBounds) {
                         if (mInteractionHandler == handler) {
-                            handler.setRecentsAnimation(controller, apps);
-
+                            handler.setRecentsAnimation(controller, apps, homeContentInsets,
+                                    minimizedHomeBounds);
                         } else {
                             controller.finish(false /* toHome */);
                         }
@@ -290,7 +291,7 @@
 
                     public void onAnimationCanceled() {
                         if (mInteractionHandler == handler) {
-                            handler.setRecentsAnimation(null, null);
+                            handler.setRecentsAnimation(null, null, null, null);
                         }
                     }
                 }, null, null);
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 028af9a..6082aea 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -34,6 +34,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Matrix;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Build;
 import android.os.Handler;
@@ -91,10 +92,23 @@
 
     private static final float MIN_PROGRESS_FOR_OVERVIEW = 0.5f;
 
-    private final Rect mStableInsets = new Rect();
+    // The bounds of the source app in device coordinates
+    private final Rect mSourceStackBounds = new Rect();
+    // The insets of the source app
+    private final Rect mSourceInsets = new Rect();
+    // The source app bounds with the source insets applied, in the source app window coordinates
     private final Rect mSourceRect = new Rect();
+    // The insets to be used for clipping the app window, which can be larger than mSourceInsets
+    // if the aspect ratio of the target is smaller than the aspect ratio of the source rect. In
+    // app window coordinates.
+    private final Rect mSourceWindowClipInsets = new Rect();
+    // The bounds of launcher (not including insets) in device coordinates
+    private final Rect mHomeStackBounds = new Rect();
+    // The bounds of the task view in launcher window coordinates
     private final Rect mTargetRect = new Rect();
+    // The interpolated rect from the source app rect to the target rect
     private final Rect mCurrentRect = new Rect();
+    // The clip rect in source app window coordinates
     private final Rect mClipRect = new Rect();
     private final RectEvaluator mRectEvaluator = new RectEvaluator(mCurrentRect);
     private DeviceProfile mDp;
@@ -135,16 +149,6 @@
     WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context) {
         mContext = context;
         mRunningTaskId = runningTaskInfo.id;
-
-        WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
-
-        DeviceProfile dp = LauncherAppState.getIDP(mContext).getDeviceProfile(mContext);
-        // TODO: If in multi window mode, dp = dp.getMultiWindowProfile()
-        dp = dp.copy(mContext);
-        // TODO: Use different insets for multi-window mode
-        dp.updateInsets(mStableInsets);
-
-        initTransitionEndpoints(dp);
         initStateCallbacks();
     }
 
@@ -154,6 +158,8 @@
                 this::initializeLauncherAnimationController);
         mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN,
                 this::launcherFrameDrawn);
+        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_GESTURE_STARTED,
+                this::notifyGestureStarted);
 
         mStateCallback.addCallback(STATE_SCALED_CONTROLLER_APP | STATE_APP_CONTROLLER_RECEIVED,
                 this::resumeLastTask);
@@ -187,13 +193,33 @@
 
     private void initTransitionEndpoints(DeviceProfile dp) {
         mDp = dp;
-        RecentsView.getPageRect(dp, mContext, mTargetRect);
-        mSourceRect.set(0, 0, dp.widthPx - mStableInsets.left - mStableInsets.right,
-                dp.heightPx - mStableInsets.top - mStableInsets.bottom);
 
-        mTransitionDragLength = dp.hotseatBarSizePx + (dp.isVerticalBarLayout()
-                ? (dp.hotseatBarSidePaddingPx + (dp.isSeascape() ? mStableInsets.left : mStableInsets.right))
-                : mStableInsets.bottom);
+        mSourceRect.set(0, 0, dp.widthPx - mSourceInsets.left - mSourceInsets.right,
+                dp.heightPx - mSourceInsets.top - mSourceInsets.bottom);
+        RecentsView.getPageRect(dp, mContext, mTargetRect);
+        mTargetRect.offset(mHomeStackBounds.left - mSourceStackBounds.left,
+                mHomeStackBounds.top - mSourceStackBounds.top);
+
+        // Calculate the clip based on the target rect (since the content insets and the
+        // launcher insets may differ, so the aspect ratio of the target rect can differ
+        // from the source rect. The difference between the target rect (scaled to the
+        // source rect) is the amount to clip on each edge.
+        Rect scaledTargetRect = new Rect(mTargetRect);
+        Utilities.scaleRectAboutCenter(scaledTargetRect,
+                (float) mSourceRect.width() / mTargetRect.width());
+        scaledTargetRect.offsetTo(mSourceInsets.left, mSourceInsets.top);
+        mSourceWindowClipInsets.set(scaledTargetRect.left, scaledTargetRect.top,
+                mDp.widthPx - scaledTargetRect.right,
+                mDp.heightPx - scaledTargetRect.bottom);
+
+        Rect targetInsets = dp.getInsets();
+        mTransitionDragLength = dp.hotseatBarSizePx;
+        if (dp.isVerticalBarLayout()) {
+            int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
+            mTransitionDragLength += dp.hotseatBarSidePaddingPx + hotseatInset;
+        } else {
+            mTransitionDragLength += targetInsets.bottom;
+        }
     }
 
     private long getFadeInDuration() {
@@ -220,6 +246,7 @@
             mStateCallback.setState(oldState);
             mLauncherLayoutListener.setHandler(null);
         }
+        mWasLauncherAlreadyVisible = alreadyOnHome;
         mLauncher = launcher;
 
         LauncherState startState = mLauncher.getStateManager().getState();
@@ -229,7 +256,6 @@
         mLauncher.getStateManager().setRestState(startState);
 
         AbstractFloatingView.closeAllOpenViews(launcher, alreadyOnHome);
-        mWasLauncherAlreadyVisible = alreadyOnHome;
 
         mRecentsView = mLauncher.getOverviewPanel();
         mLauncherLayoutListener = new LauncherLayoutListener(mLauncher);
@@ -268,18 +294,14 @@
                 }
             });
             state = STATE_LAUNCHER_PRESENT;
+
+            // Optimization, hide the all apps view to prevent layout while initializing
+            mLauncher.getAppsView().setVisibility(View.GONE);
         }
 
         mRecentsView.showTask(mRunningTaskId);
         mLauncherLayoutListener.open();
 
-        // Optimization
-        // We are using the internal device profile as launcher may not have got the insets yet.
-        if (!mDp.isVerticalBarLayout()) {
-            // All-apps search box is visible in vertical bar layout.
-            mLauncher.getAppsView().setVisibility(View.GONE);
-        }
-
         mStateCallback.setState(state);
         return true;
     }
@@ -351,7 +373,6 @@
      * Called by {@link #mLauncherLayoutListener} when launcher layout changes
      */
     public void onLauncherLayoutChanged() {
-        WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
         initTransitionEndpoints(mLauncher.getDeviceProfile());
 
         if (!mWasLauncherAlreadyVisible) {
@@ -392,14 +413,14 @@
                 mRectEvaluator.evaluate(shift, mSourceRect, mTargetRect);
                 float scale = (float) mCurrentRect.width() / mSourceRect.width();
 
-                mClipRect.left = mSourceRect.left;
-                mClipRect.top = (int) (mStableInsets.top * shift);
-                mClipRect.bottom = (int) (mDp.heightPx - (mStableInsets.bottom * shift));
-                mClipRect.right = mSourceRect.right;
+                mClipRect.left = (int) (mSourceWindowClipInsets.left * shift);
+                mClipRect.top = (int) (mSourceWindowClipInsets.top * shift);
+                mClipRect.right = (int) (mDp.widthPx - (mSourceWindowClipInsets.right * shift));
+                mClipRect.bottom = (int) (mDp.heightPx - (mSourceWindowClipInsets.bottom * shift));
 
                 mTmpMatrix.setScale(scale, scale, 0, 0);
-                mTmpMatrix.postTranslate(mCurrentRect.left - mStableInsets.left * scale * shift,
-                        mCurrentRect.top - mStableInsets.top * scale * shift);
+                mTmpMatrix.postTranslate(mCurrentRect.left - mSourceInsets.left * scale * shift,
+                        mCurrentRect.top - mSourceInsets.top * scale * shift);
                 TransactionCompat transaction = new TransactionCompat();
                 for (RemoteAnimationTargetCompat app : mRecentsAnimationWrapper.targets) {
                     if (app.mode == MODE_CLOSING) {
@@ -423,16 +444,63 @@
     }
 
     public void setRecentsAnimation(RecentsAnimationControllerCompat controller,
-            RemoteAnimationTargetCompat[] apps) {
+            RemoteAnimationTargetCompat[] apps, Rect homeContentInsets, Rect minimizedHomeBounds) {
+        if (apps != null) {
+            // Use the top closing app to determine the insets for the animation
+            for (RemoteAnimationTargetCompat target : apps) {
+                if (target.mode == MODE_CLOSING) {
+                    DeviceProfile dp = LauncherAppState.getIDP(mContext).getDeviceProfile(mContext);
+                    if (minimizedHomeBounds != null) {
+                        mHomeStackBounds.set(minimizedHomeBounds);
+                        dp = dp.getMultiWindowProfile(mContext,
+                                new Point(minimizedHomeBounds.width(), minimizedHomeBounds.height()));
+                        dp.updateInsets(homeContentInsets);
+                    } else {
+                        mHomeStackBounds.set(new Rect(0, 0, dp.widthPx, dp.heightPx));
+                        // TODO: Workaround for an existing issue where the home content insets are
+                        // not valid immediately after rotation, just use the stable insets for now
+                        Rect insets = new Rect();
+                        WindowManagerWrapper.getInstance().getStableInsets(insets);
+                        dp.updateInsets(insets);
+                    }
+
+                    // Initialize the start and end animation bounds
+                    // TODO: Remove once platform is updated
+                    try {
+                        mSourceInsets.set(target.getContentInsets());
+                    } catch (Error e) {
+                        // TODO: Remove once platform is updated, use stable insets as fallback
+                        WindowManagerWrapper.getInstance().getStableInsets(mSourceInsets);
+                    }
+                    mSourceStackBounds.set(target.sourceContainerBounds);
+
+                    initTransitionEndpoints(dp);
+                    break;
+                }
+            }
+        }
+
         mRecentsAnimationWrapper.setController(controller, apps);
         setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
     }
 
     public void onGestureStarted() {
+        if (mLauncher != null) {
+            notifyGestureStarted();
+        }
+
         setStateOnUiThread(STATE_GESTURE_STARTED);
         mGestureStarted = true;
     }
 
+    /**
+     * Notifies the launcher that the swipe gesture has started. This can be called multiple times
+     * on both background and UI threads
+     */
+    private void notifyGestureStarted() {
+        mLauncher.onQuickstepGestureStarted(mWasLauncherAlreadyVisible);
+    }
+
     @WorkerThread
     public void onGestureEnded(float endVelocity) {
         Resources res = mContext.getResources();
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 2227bfd..950c7f7 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -217,7 +217,7 @@
         return new DeviceProfile(context, inv, size, size, widthPx, heightPx, isLandscape);
     }
 
-    DeviceProfile getMultiWindowProfile(Context context, Point mwSize) {
+    public DeviceProfile getMultiWindowProfile(Context context, Point mwSize) {
         // We take the minimum sizes of this profile and it's multi-window variant to ensure that
         // the system decor is always excluded.
         mwSize.set(Math.min(availableWidthPx, mwSize.x), Math.min(availableHeightPx, mwSize.y));
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 5da4944..4d820bc 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -150,7 +150,6 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
-import java.util.concurrent.Executor;
 
 /**
  * Default launcher application.
@@ -1166,7 +1165,7 @@
 
     public void updateIconBadges(final Set<PackageUserKey> updatedBadges) {
         mWorkspace.updateIconBadges(updatedBadges);
-        mAppsView.updateIconBadges(updatedBadges);
+        mAppsView.getAppsStore().updateIconBadges(updatedBadges);
 
         PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(Launcher.this);
         if (popup != null) {
@@ -1193,6 +1192,12 @@
         }
     }
 
+    public void onQuickstepGestureStarted(boolean isVisible) {
+        if (mLauncherCallbacks != null) {
+            mLauncherCallbacks.onQuickstepGestureStarted(isVisible);
+        }
+    }
+
     public AllAppsTransitionController getAllAppsController() {
         return mAllAppsController;
     }
@@ -2521,6 +2526,11 @@
             mPendingExecutor.markCompleted();
         }
         mPendingExecutor = executor;
+        if (!isInState(ALL_APPS)) {
+            mAppsView.getAppsStore().setDeferUpdates(true);
+            mPendingExecutor.execute(() -> mAppsView.getAppsStore().setDeferUpdates(false));
+        }
+
         executor.attachTo(this);
     }
 
@@ -2582,30 +2592,14 @@
      * Implementation of the method from LauncherModel.Callbacks.
      */
     public void bindAllApplications(ArrayList<AppInfo> apps) {
-        if (mAppsView != null) {
-            Executor pendingExecutor = getPendingExecutor();
-            if (pendingExecutor != null && !isInState(ALL_APPS)) {
-                // Wait until the fade in animation has finished before setting all apps list.
-                pendingExecutor.execute(() -> bindAllApplications(apps));
-                return;
-            }
+        mAppsView.getAppsStore().setApps(apps);
 
-            mAppsView.setApps(apps);
-        }
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.bindAllApplications(apps);
         }
     }
 
     /**
-     * Returns an Executor that will run after the launcher is first drawn (including after the
-     * initial fade in animation). Returns null if the first draw has already occurred.
-     */
-    public @Nullable Executor getPendingExecutor() {
-        return mPendingExecutor != null && mPendingExecutor.canQueue() ? mPendingExecutor : null;
-    }
-
-    /**
      * Copies LauncherModel's map of activities to shortcut ids to Launcher's. This is necessary
      * because LauncherModel's map is updated in the background, while Launcher runs on the UI.
      */
@@ -2621,16 +2615,12 @@
      */
     @Override
     public void bindAppsAddedOrUpdated(ArrayList<AppInfo> apps) {
-        if (mAppsView != null) {
-            mAppsView.addOrUpdateApps(apps);
-        }
+        mAppsView.getAppsStore().addOrUpdateApps(apps);
     }
 
     @Override
     public void bindPromiseAppProgressUpdated(PromiseAppInfo app) {
-        if (mAppsView != null) {
-            mAppsView.updatePromiseAppProgress(app);
-        }
+        mAppsView.getAppsStore().updatePromiseAppProgress(app);
     }
 
     @Override
@@ -2676,10 +2666,7 @@
 
     @Override
     public void bindAppInfosRemoved(final ArrayList<AppInfo> appInfos) {
-        // Update AllApps
-        if (mAppsView != null) {
-            mAppsView.removeApps(appInfos);
-        }
+        mAppsView.getAppsStore().removeApps(appInfos);
     }
 
     @Override
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index 914d9eb..ed7bf3d 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -71,4 +71,12 @@
      * Extensions points for adding / replacing some other aspects of the Launcher experience.
      */
     boolean hasSettings();
+
+    /**
+     * Called when launcher integrated quickstep and some quickstep gesture started. It can be
+     * called multiple times for a single gesture an UI or background thread.
+     *
+     * @param isVisible if Launcher was visible when the gesture started.
+     */
+    void onQuickstepGestureStarted(boolean isVisible);
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index de08eb6..d4277db 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -116,6 +116,8 @@
         mAH = new AdapterHolder[2];
         mAH[AdapterHolder.MAIN] = new AdapterHolder(false /* isWork */);
         mAH[AdapterHolder.WORK] = new AdapterHolder(true /* isWork */);
+
+        mAllAppsStore.addUpdateListener(this::onAppsUpdated);
     }
 
     @Override
@@ -150,43 +152,25 @@
         }
     }
 
+    private void onAppsUpdated() {
+        if (FeatureFlags.ALL_APPS_TABS_ENABLED) {
+            boolean hasWorkApps = false;
+            for (AppInfo app : mAllAppsStore.getApps()) {
+                if (mWorkMatcher.matches(app, null)) {
+                    hasWorkApps = true;
+                    break;
+                }
+            }
+            rebindAdapters(hasWorkApps);
+        }
+    }
+
     @Override
     public void setPressedIcon(BubbleTextView icon, Bitmap background) {
         mTouchFeedbackView.setPressedIcon(icon, background);
     }
 
     /**
-     * Sets the current set of apps.
-     */
-    public void setApps(List<AppInfo> apps) {
-        boolean hasWorkProfileApp = hasWorkProfileApp(apps);
-        rebindAdapters(hasWorkProfileApp);
-        mAllAppsStore.setApps(apps);
-    }
-
-    /**
-     * Adds or updates existing apps in the list
-     */
-    public void addOrUpdateApps(List<AppInfo> apps) {
-        mAllAppsStore.addOrUpdateApps(apps);
-    }
-
-    /**
-     * Removes some apps from the list.
-     */
-    public void removeApps(List<AppInfo> apps) {
-        mAllAppsStore.removeApps(apps);
-    }
-
-    public void updatePromiseAppProgress(PromiseAppInfo app) {
-        mAllAppsStore.updateAllIcons((child) -> {
-            if (child.getTag() == app) {
-                child.applyProgressLevel(app.level);
-            }
-        });
-    }
-
-    /**
      * Returns whether the view itself will handle the touch event or not.
      */
     public boolean shouldContainerScroll(MotionEvent ev) {
@@ -324,18 +308,6 @@
         InsettableFrameLayout.dispatchInsets(this, insets);
     }
 
-    public void updateIconBadges(Set<PackageUserKey> updatedBadges) {
-        PackageUserKey tempKey = new PackageUserKey(null, null);
-        mAllAppsStore.updateAllIcons((child) -> {
-            if (child.getTag() instanceof ItemInfo) {
-                ItemInfo info = (ItemInfo) child.getTag();
-                if (tempKey.updateFromItemInfo(info) && updatedBadges.contains(tempKey)) {
-                    child.applyBadgeState(info, true /* animate */);
-                }
-            }
-        });
-    }
-
     public SpringAnimationHandler getSpringAnimationHandler() {
         return mUsingTabs ? null : mAH[AdapterHolder.MAIN].animationHandler;
     }
@@ -371,17 +343,6 @@
         applyTouchDelegate();
     }
 
-    private boolean hasWorkProfileApp(List<AppInfo> apps) {
-        if (FeatureFlags.ALL_APPS_TABS_ENABLED) {
-            for (AppInfo app : apps) {
-                if (mWorkMatcher.matches(app, null)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
     private void replaceRVContainer(boolean showTabs) {
         for (int i = 0; i < mAH.length; i++) {
             if (mAH[i].recyclerView != null) {
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index 846b6a9..dc34892 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -20,22 +20,30 @@
 
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.PromiseAppInfo;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.PackageUserKey;
 
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Set;
 
 /**
  * A utility class to maintain the collection of all apps.
  */
 public class AllAppsStore {
 
+    private PackageUserKey mTempKey = new PackageUserKey(null, null);
     private final HashMap<ComponentKey, AppInfo> mComponentToAppMap = new HashMap<>();
     private final List<OnUpdateListener> mUpdateListeners = new ArrayList<>();
     private final ArrayList<ViewGroup> mIconContainers = new ArrayList<>();
 
+    private boolean mDeferUpdates = false;
+    private boolean mUpdatePending = false;
+
     public Collection<AppInfo> getApps() {
         return mComponentToAppMap.values();
     }
@@ -52,6 +60,17 @@
         return mComponentToAppMap.get(key);
     }
 
+    public void setDeferUpdates(boolean deferUpdates) {
+        if (mDeferUpdates != deferUpdates) {
+            mDeferUpdates = deferUpdates;
+
+            if (!mDeferUpdates && mUpdatePending) {
+                notifyUpdate();
+                mUpdatePending = false;
+            }
+        }
+    }
+
     /**
      * Adds or updates existing apps in the list
      */
@@ -74,6 +93,10 @@
 
 
     private void notifyUpdate() {
+        if (mDeferUpdates) {
+            mUpdatePending = true;
+            return;
+        }
         int count = mUpdateListeners.size();
         for (int i = 0; i < count; i++) {
             mUpdateListeners.get(i).onAppsUpdated();
@@ -98,7 +121,26 @@
         mIconContainers.remove(container);
     }
 
-    public void updateAllIcons(IconAction action) {
+    public void updateIconBadges(Set<PackageUserKey> updatedBadges) {
+        updateAllIcons((child) -> {
+            if (child.getTag() instanceof ItemInfo) {
+                ItemInfo info = (ItemInfo) child.getTag();
+                if (mTempKey.updateFromItemInfo(info) && updatedBadges.contains(mTempKey)) {
+                    child.applyBadgeState(info, true /* animate */);
+                }
+            }
+        });
+    }
+
+    public void updatePromiseAppProgress(PromiseAppInfo app) {
+        updateAllIcons((child) -> {
+            if (child.getTag() == app) {
+                child.applyProgressLevel(app.level);
+            }
+        });
+    }
+
+    private void updateAllIcons(IconAction action) {
         for (int i = mIconContainers.size() - 1; i >= 0; i--) {
             ViewGroup parent = mIconContainers.get(i);
             int childCount = parent.getChildCount();
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index a32f6b1..1cf407e 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -159,6 +159,10 @@
         } else if (action == MotionEvent.ACTION_DOWN) {
             mLauncher.finishAutoCancelActionMode();
         }
+        return findActiveController(ev);
+    }
+
+    private boolean findActiveController(MotionEvent ev) {
         mActiveController = null;
 
         AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mLauncher);
@@ -285,8 +289,10 @@
 
         if (mActiveController != null) {
             return mActiveController.onControllerTouchEvent(ev);
+        } else {
+            // In case no child view handled the touch event, we may not get onIntercept anymore
+            return findActiveController(ev);
         }
-        return false;
     }
 
     /**