Merge "Deleting invalid icons when an app is restored" into ub-launcher3-edmonton-polish
diff --git a/proguard.flags b/proguard.flags
index 555d13e..e401116 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -112,6 +112,11 @@
     public <init>(...);
 }
 
+# UserEventDispatcherExtension
+-keep class com.android.quickstep.logging.UserEventDispatcherExtension {
+    public <init>(...);
+}
+
 -keep interface com.android.launcher3.userevent.nano.LauncherLogProto.** {
   *;
 }
diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar
index 53a6ceb..308e92f 100644
--- a/quickstep/libs/sysui_shared.jar
+++ b/quickstep/libs/sysui_shared.jar
Binary files differ
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 7673f69..c294376 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -18,5 +18,5 @@
 
     <string name="overview_callbacks_class" translatable="false"></string>
 
-    <string name="user_event_dispatcher_class" translatable="false">com.google.quickstep.logging.UserEventDispatcherExtension</string>
+    <string name="user_event_dispatcher_class" translatable="false">com.android.quickstep.logging.UserEventDispatcherExtension</string>
 </resources>
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 2630edb..0674c61 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -72,6 +72,7 @@
 import com.android.quickstep.util.ClipAnimationHelper;
 import com.android.quickstep.util.MultiValueUpdateListener;
 import com.android.quickstep.util.RemoteAnimationProvider;
+import com.android.quickstep.util.RemoteAnimationTargetSet;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.system.ActivityCompat;
@@ -80,7 +81,8 @@
 import com.android.systemui.shared.system.RemoteAnimationDefinitionCompat;
 import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.TransactionCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier.SurfaceParams;
 import com.android.systemui.shared.system.WindowManagerWrapper;
 
 /**
@@ -558,22 +560,21 @@
         Rect crop = new Rect();
         Matrix matrix = new Matrix();
 
+        RemoteAnimationTargetSet openingTargets = new RemoteAnimationTargetSet(targets,
+                MODE_OPENING);
+        RemoteAnimationTargetSet closingTargets = new RemoteAnimationTargetSet(targets,
+                MODE_CLOSING);
+        SyncRtSurfaceTransactionApplier surfaceApplier = new SyncRtSurfaceTransactionApplier(
+                mFloatingView);
+
         ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
         appAnimator.setDuration(APP_LAUNCH_DURATION);
         appAnimator.addUpdateListener(new MultiValueUpdateListener() {
             // Fade alpha for the app window.
             FloatProp mAlpha = new FloatProp(0f, 1f, 0, 60, LINEAR);
-            boolean isFirstFrame = true;
 
             @Override
             public void onUpdate(float percent) {
-                final Surface surface = getSurface(mFloatingView);
-                final long frameNumber = surface != null ? getNextFrameNumber(surface) : -1;
-                if (frameNumber == -1) {
-                    // Booo, not cool! Our surface got destroyed, so no reason to animate anything.
-                    Log.w(TAG, "Failed to animate, surface got destroyed.");
-                    return;
-                }
                 final float easePercent = AGGRESSIVE_EASE.getInterpolation(percent);
 
                 // Calculate app icon size.
@@ -584,7 +585,6 @@
                 float scaleX = iconWidth / windowTargetBounds.width();
                 float scaleY = iconHeight / windowTargetBounds.height();
                 float scale = Math.min(1f, Math.min(scaleX, scaleY));
-                matrix.setScale(scale, scale);
 
                 // Position the scaled window on top of the icon
                 int windowWidth = windowTargetBounds.width();
@@ -598,7 +598,6 @@
 
                 float transX0 = floatingViewBounds[0] - offsetX;
                 float transY0 = floatingViewBounds[1] - offsetY;
-                matrix.postTranslate(transX0, transY0);
 
                 // Animate the window crop so that it starts off as a square, and then reveals
                 // horizontally.
@@ -609,23 +608,27 @@
                 crop.right = windowWidth;
                 crop.bottom = (int) (crop.top + cropHeight);
 
-                TransactionCompat t = new TransactionCompat();
-                if (isFirstFrame) {
-                    RemoteAnimationProvider.prepareTargetsForFirstFrame(targets, t, MODE_OPENING);
-                    isFirstFrame = false;
-                }
-                for (RemoteAnimationTargetCompat target : targets) {
-                    if (target.mode == MODE_OPENING) {
-                        t.setAlpha(target.leash, mAlpha.value);
-                        t.setMatrix(target.leash, matrix);
-                        t.setWindowCrop(target.leash, crop);
-                        t.deferTransactionUntil(target.leash, surface, getNextFrameNumber(surface));
-                    }
-                }
-                t.setEarlyWakeup();
-                t.apply();
+                SurfaceParams[] params = new SurfaceParams[targets.length];
+                for (int i = targets.length - 1; i >= 0; i--) {
+                    RemoteAnimationTargetCompat target = targets[i];
 
-                matrix.reset();
+                    Rect targetCrop;
+                    float alpha;
+                    if (target.mode == MODE_OPENING) {
+                        matrix.setScale(scale, scale);
+                        matrix.postTranslate(transX0, transY0);
+                        targetCrop = crop;
+                        alpha = mAlpha.value;
+                    } else {
+                        matrix.reset();
+                        alpha = 1f;
+                        targetCrop = target.sourceContainerBounds;
+                    }
+
+                    params[i] = new SurfaceParams(target.leash, alpha, matrix, targetCrop,
+                            RemoteAnimationProvider.getLayer(target, MODE_OPENING));
+                }
+                surfaceApplier.scheduleApply(params);
             }
         });
         return appAnimator;
@@ -705,6 +708,8 @@
      * Animator that controls the transformations of the windows the targets that are closing.
      */
     private Animator getClosingWindowAnimators(RemoteAnimationTargetCompat[] targets) {
+        SyncRtSurfaceTransactionApplier surfaceApplier =
+                new SyncRtSurfaceTransactionApplier(mDragLayer);
         Matrix matrix = new Matrix();
         ValueAnimator closingAnimator = ValueAnimator.ofFloat(0, 1);
         int duration = CLOSING_TRANSITION_DURATION_MS;
@@ -714,30 +719,28 @@
             FloatProp mScale = new FloatProp(1f, 1f, 0, duration, DEACCEL_1_7);
             FloatProp mAlpha = new FloatProp(1f, 0f, 25, 125, LINEAR);
 
-            boolean isFirstFrame = true;
-
             @Override
             public void onUpdate(float percent) {
-                TransactionCompat t = new TransactionCompat();
-                if (isFirstFrame) {
-                    RemoteAnimationProvider.prepareTargetsForFirstFrame(targets, t, MODE_CLOSING);
-                    isFirstFrame = false;
-                }
-                for (RemoteAnimationTargetCompat app : targets) {
-                    if (app.mode == RemoteAnimationTargetCompat.MODE_CLOSING) {
-                        t.setAlpha(app.leash, mAlpha.value);
+                SurfaceParams[] params = new SurfaceParams[targets.length];
+                for (int i = targets.length - 1; i >= 0; i--) {
+                    RemoteAnimationTargetCompat target = targets[i];
+                    float alpha;
+                    if (target.mode == MODE_CLOSING) {
                         matrix.setScale(mScale.value, mScale.value,
-                                app.sourceContainerBounds.centerX(),
-                                app.sourceContainerBounds.centerY());
+                                target.sourceContainerBounds.centerX(),
+                                target.sourceContainerBounds.centerY());
                         matrix.postTranslate(0, mDy.value);
-                        matrix.postTranslate(app.position.x, app.position.y);
-                        t.setMatrix(app.leash, matrix);
+                        matrix.postTranslate(target.position.x, target.position.y);
+                        alpha = mAlpha.value;
+                    } else {
+                        matrix.reset();
+                        alpha = 1f;
                     }
+                    params[i] = new SurfaceParams(target.leash, alpha, matrix,
+                            target.sourceContainerBounds,
+                            RemoteAnimationProvider.getLayer(target, MODE_CLOSING));
                 }
-                t.setEarlyWakeup();
-                t.apply();
-
-                matrix.reset();
+                surfaceApplier.scheduleApply(params);
             }
         });
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
index cd92314..2645302 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
@@ -34,7 +34,7 @@
     /**
      * Vertical transition of the task previews relative to the full container.
      */
-    public static final float OVERVIEW_TRANSLATION_FACTOR = 0.5f;
+    public static final float OVERVIEW_TRANSLATION_FACTOR = 0.4f;
 
     private static final int STATE_FLAGS = FLAG_DISABLE_RESTORE | FLAG_DISABLE_INTERACTION
             | FLAG_OVERVIEW_UI | FLAG_HIDE_BACK_BUTTON | FLAG_DISABLE_ACCESSIBILITY;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index dd5dcbe..ac9f863 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -25,7 +25,6 @@
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
 import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
 
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
@@ -46,7 +45,6 @@
 import com.android.launcher3.util.TouchController;
 import com.android.quickstep.OverviewInteractionState;
 import com.android.quickstep.RecentsModel;
-import com.android.quickstep.util.RemoteAnimationTargetSet;
 import com.android.quickstep.util.RemoteFadeOutAnimationListener;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.system.ActivityCompat;
@@ -171,7 +169,8 @@
         LauncherState state = launcher.getStateManager().getState();
         DeviceProfile profile = launcher.getDeviceProfile();
         WindowManagerWrapper.getInstance().setShelfHeight(
-                state != ALL_APPS && launcher.isUserActive() && !profile.isVerticalBarLayout(),
+                (state == NORMAL || state == OVERVIEW) && launcher.isUserActive()
+                        && !profile.isVerticalBarLayout(),
                 profile.hotseatBarSizePx);
 
         if (state == NORMAL) {
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 41a4550..eff94fc 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -67,6 +67,7 @@
 import com.android.systemui.shared.system.LatencyTrackerCompat;
 import com.android.systemui.shared.system.PackageManagerWrapper;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier;
 import com.android.systemui.shared.system.TransactionCompat;
 
 import java.util.ArrayList;
@@ -350,11 +351,14 @@
             clipHelper.updateTargetRect(targetRect);
             clipHelper.prepareAnimation(false /* isOpening */);
 
+            SyncRtSurfaceTransactionApplier syncTransactionApplier =
+                    new SyncRtSurfaceTransactionApplier(rootView);
             ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
             valueAnimator.setDuration(RECENTS_LAUNCH_DURATION);
             valueAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
             valueAnimator.addUpdateListener((v) ->
-                    clipHelper.applyTransform(targetSet, (float) v.getAnimatedValue()));
+                    clipHelper.applyTransform(targetSet, (float) v.getAnimatedValue(),
+                            syncTransactionApplier));
 
             if (targetSet.isAnimatingHome()) {
                 // If we are animating home, fade in the opening targets
diff --git a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
index aed9959..2c3f77f 100644
--- a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
+++ b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
@@ -20,6 +20,7 @@
 import com.android.launcher3.MainProcessInitializer;
 import com.android.systemui.shared.system.ThreadedRendererCompat;
 
+@SuppressWarnings("unused")
 public class QuickstepProcessInitializer extends MainProcessInitializer {
 
     public QuickstepProcessInitializer(Context context) { }
diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java
index 2b0c98f..ec2c318 100644
--- a/quickstep/src/com/android/quickstep/TaskUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskUtils.java
@@ -46,6 +46,7 @@
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier;
 
 import java.util.List;
 
@@ -144,6 +145,8 @@
      */
     public static ValueAnimator getRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
             RemoteAnimationTargetCompat[] targets, final ClipAnimationHelper inOutHelper) {
+        SyncRtSurfaceTransactionApplier syncTransactionApplier =
+                new SyncRtSurfaceTransactionApplier(v);
         final ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
         appAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
         appAnimator.addUpdateListener(new MultiValueUpdateListener() {
@@ -155,18 +158,10 @@
             final RemoteAnimationTargetSet mTargetSet;
 
             final RectF mThumbnailRect;
-            private Surface mSurface;
-            private long mFrameNumber;
 
             {
                 mTargetSet = new RemoteAnimationTargetSet(targets, MODE_OPENING);
-                inOutHelper.setTaskTransformCallback((t, app) -> {
-                    t.setAlpha(app.leash, mTaskAlpha.value);
-
-                    if (!skipViewChanges) {
-                        t.deferTransactionUntil(app.leash, mSurface, mFrameNumber);
-                    }
-                });
+                inOutHelper.setTaskAlphaCallback((t, alpha) -> mTaskAlpha.value);
 
                 inOutHelper.prepareAnimation(true /* isOpening */);
                 inOutHelper.fromTaskThumbnailView(v.getThumbnail(), (RecentsView) v.getParent(),
@@ -179,15 +174,8 @@
 
             @Override
             public void onUpdate(float percent) {
-                mSurface = getSurface(v);
-                mFrameNumber = mSurface != null ? getNextFrameNumber(mSurface) : -1;
-                if (mFrameNumber == -1) {
-                    // Booo, not cool! Our surface got destroyed, so no reason to animate anything.
-                    Log.w(TAG, "Failed to animate, surface got destroyed.");
-                    return;
-                }
-
-                RectF taskBounds = inOutHelper.applyTransform(mTargetSet, 1 - percent);
+                RectF taskBounds = inOutHelper.applyTransform(mTargetSet, 1 - percent,
+                        syncTransactionApplier);
                 if (!skipViewChanges) {
                     float scale = taskBounds.width() / mThumbnailRect.width();
                     v.setScaleX(scale);
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index aecb66c..49a4ac8 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -49,6 +49,7 @@
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.ChoreographerCompat;
 import com.android.systemui.shared.system.NavigationBarCompat.HitTarget;
 
 /**
@@ -406,6 +407,6 @@
             sRemoteUiThread.start();
         }
         new Handler(sRemoteUiThread.getLooper()).post(() ->
-                mBackgroundThreadChoreographer = Choreographer.getInstance());
+                mBackgroundThreadChoreographer = ChoreographerCompat.getSfInstance());
     }
 }
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index b1663b1..c94174b 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -79,6 +79,7 @@
 import com.android.systemui.shared.system.LatencyTrackerCompat;
 import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier;
 import com.android.systemui.shared.system.WindowCallbacksCompat;
 import com.android.systemui.shared.system.WindowManagerWrapper;
 
@@ -192,6 +193,7 @@
     private T mActivity;
     private LayoutListener mLayoutListener;
     private RecentsView mRecentsView;
+    private SyncRtSurfaceTransactionApplier mSyncTransactionApplier;
     private QuickScrubController mQuickScrubController;
     private AnimationFactory mAnimationFactory = (t, i) -> { };
 
@@ -360,6 +362,7 @@
         }
 
         mRecentsView = activity.getOverviewPanel();
+        mSyncTransactionApplier = new SyncRtSurfaceTransactionApplier(mRecentsView);
         mQuickScrubController = mRecentsView.getQuickScrubController();
         mLayoutListener = mActivityControlHelper.createLayoutListener(mActivity);
 
@@ -546,7 +549,11 @@
 
         RecentsAnimationControllerCompat controller = mRecentsAnimationWrapper.getController();
         if (controller != null) {
-            mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet, shift);
+
+            mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet, shift,
+                    Looper.myLooper() == mMainThreadHandler.getLooper()
+                            ? mSyncTransactionApplier
+                            : null);
 
             // TODO: This logic is spartanic!
             boolean passedThreshold = shift > 0.12f;
@@ -676,15 +683,20 @@
     }
 
     private void doLogGesture(boolean toLauncher) {
+        DeviceProfile dp = mDp;
+        if (dp == null) {
+            // We probably never received an animation controller, skip logging.
+            return;
+        }
         final int direction;
-        if (mDp.isVerticalBarLayout()) {
-            direction = (mDp.isSeascape() ^ toLauncher) ? Direction.LEFT : Direction.RIGHT;
+        if (dp.isVerticalBarLayout()) {
+            direction = (dp.isSeascape() ^ toLauncher) ? Direction.LEFT : Direction.RIGHT;
         } else {
             direction = toLauncher ? Direction.UP : Direction.DOWN;
         }
 
         int dstContainerType = toLauncher ? ContainerType.TASKSWITCHER : ContainerType.APP;
-        UserEventDispatcher.newInstance(mContext, mDp).logStateChangeAction(
+        UserEventDispatcher.newInstance(mContext, dp).logStateChangeAction(
                 mLogAction, direction,
                 ContainerType.NAVBAR, ContainerType.APP,
                 dstContainerType,
diff --git a/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java b/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java
index 04153cc..6dff187 100644
--- a/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java
+++ b/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java
@@ -15,10 +15,9 @@
  */
 package com.android.quickstep.logging;
 
+import android.content.Context;
 import android.util.Log;
 
-import static com.android.launcher3.logging.LoggerUtils.newAction;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
 import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
 import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.CANCEL_TARGET;
 import static com.android.systemui.shared.system.LauncherEventUtil.VISIBLE;
@@ -27,20 +26,20 @@
 import static com.android.systemui.shared.system.LauncherEventUtil.RECENTS_SWIPE_UP_ONBOARDING_TIP;
 
 import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.model.nano.LauncherDumpProto;
-import com.android.launcher3.userevent.nano.LauncherLogExtensions;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.systemui.shared.system.LauncherEventUtil;
 import com.android.systemui.shared.system.MetricsLoggerCompat;
 
 /**
  * This class handles AOSP MetricsLogger function calls and logging around
  * quickstep interactions.
  */
+@SuppressWarnings("unused")
 public class UserEventDispatcherExtension extends UserEventDispatcher {
 
     private static final String TAG = "UserEventDispatcher";
 
+    public UserEventDispatcherExtension(Context context) { }
+
     public void logStateChangeAction(int action, int dir, int srcChildTargetType,
                                      int srcParentContainerType, int dstContainerType,
                                      int pageIndex) {
diff --git a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
index a654482..df70a8a 100644
--- a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
+++ b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
@@ -30,6 +30,7 @@
 import android.os.Build;
 import android.os.RemoteException;
 import android.support.annotation.Nullable;
+import android.view.Surface;
 import android.view.animation.Interpolator;
 
 import com.android.launcher3.BaseDraggingActivity;
@@ -44,10 +45,13 @@
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.recents.utilities.RectFEvaluator;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier.SurfaceParams;
 import com.android.systemui.shared.system.TransactionCompat;
 import com.android.systemui.shared.system.WindowManagerWrapper;
 
 import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
 
 /**
  * Utility class to handle window clip animation
@@ -90,8 +94,8 @@
     // Wether or not applyTransform has been called yet since prepareAnimation()
     private boolean mIsFirstFrame = true;
 
-    private BiConsumer<TransactionCompat, RemoteAnimationTargetCompat> mTaskTransformCallback =
-            (t, a) -> { };
+    private BiFunction<RemoteAnimationTargetCompat, Float, Float> mTaskAlphaCallback =
+            (t, a1) -> a1;
 
     private void updateSourceStack(RemoteAnimationTargetCompat target) {
         mSourceInsets.set(target.contentInsets);
@@ -134,11 +138,11 @@
     }
 
     public void prepareAnimation(boolean isOpening) {
-        mIsFirstFrame = true;
         mBoostModeTargetLayers = isOpening ? MODE_OPENING : MODE_CLOSING;
     }
 
-    public RectF applyTransform(RemoteAnimationTargetSet targetSet, float progress) {
+    public RectF applyTransform(RemoteAnimationTargetSet targetSet, float progress,
+            @Nullable SyncRtSurfaceTransactionApplier syncTransactionApplier) {
         RectF currentRect;
         mTmpRectF.set(mTargetRect);
         Utilities.scaleRectFAboutCenter(mTmpRectF, mTargetScale);
@@ -159,35 +163,51 @@
         mClipRect.bottom = (int)
                 (mSourceStackBounds.height() - (mSourceWindowClipInsets.bottom * progress));
 
-        TransactionCompat transaction = new TransactionCompat();
-        if (mIsFirstFrame) {
-            RemoteAnimationProvider.prepareTargetsForFirstFrame(targetSet.unfilteredApps,
-                    transaction, mBoostModeTargetLayers);
-            mIsFirstFrame = false;
-        }
-        for (RemoteAnimationTargetCompat app : targetSet.apps) {
-            if (app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
-                mTmpMatrix.setRectToRect(mSourceRect, currentRect, ScaleToFit.FILL);
-                mTmpMatrix.postTranslate(app.position.x, app.position.y);
-                transaction.setMatrix(app.leash, mTmpMatrix)
-                        .setWindowCrop(app.leash, mClipRect);
+        SurfaceParams[] params = new SurfaceParams[targetSet.unfilteredApps.length];
+        for (int i = 0; i < targetSet.unfilteredApps.length; i++) {
+            RemoteAnimationTargetCompat app = targetSet.unfilteredApps[i];
+            mTmpMatrix.setTranslate(app.position.x, app.position.y);
+            Rect crop = app.sourceContainerBounds;
+            float alpha = 1f;
+            if (app.mode == targetSet.targetMode) {
+                if (app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+                    mTmpMatrix.setRectToRect(mSourceRect, currentRect, ScaleToFit.FILL);
+                    mTmpMatrix.postTranslate(app.position.x, app.position.y);
+                    crop = mClipRect;
+                }
+
+                if (app.isNotInRecents
+                        || app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+                    alpha = 1 - progress;
+                }
+
+                alpha = mTaskAlphaCallback.apply(app, alpha);
             }
 
-            if (app.isNotInRecents
-                    || app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
-                transaction.setAlpha(app.leash, 1 - progress);
-            }
-
-            mTaskTransformCallback.accept(transaction, app);
+            params[i] = new SurfaceParams(app.leash, alpha, mTmpMatrix, crop,
+                    RemoteAnimationProvider.getLayer(app, mBoostModeTargetLayers));
         }
-        transaction.setEarlyWakeup();
-        transaction.apply();
+        applyParams(syncTransactionApplier, params);
         return currentRect;
     }
 
-    public void setTaskTransformCallback
-            (BiConsumer<TransactionCompat, RemoteAnimationTargetCompat> callback) {
-        mTaskTransformCallback = callback;
+    private void applyParams(@Nullable SyncRtSurfaceTransactionApplier syncTransactionApplier,
+            SurfaceParams[] params) {
+        if (syncTransactionApplier != null) {
+            syncTransactionApplier.scheduleApply(params);
+        } else {
+            TransactionCompat t = new TransactionCompat();
+            for (SurfaceParams param : params) {
+                SyncRtSurfaceTransactionApplier.applyParams(t, param);
+            }
+            t.setEarlyWakeup();
+            t.apply();
+        }
+    }
+
+    public void setTaskAlphaCallback(
+            BiFunction<RemoteAnimationTargetCompat, Float, Float> callback) {
+        mTaskAlphaCallback = callback;
     }
 
     public void offsetTarget(float scale, float offsetX, float offsetY, Interpolator interpolator) {
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
index bbf223d..a7e6d74 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
@@ -28,6 +28,8 @@
 @FunctionalInterface
 public interface RemoteAnimationProvider {
 
+    static final int Z_BOOST_BASE = 800570000;
+
     AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targets);
 
     default ActivityOptions toActivityOptions(Handler handler, long duration) {
@@ -54,11 +56,14 @@
     static void prepareTargetsForFirstFrame(RemoteAnimationTargetCompat[] targets,
             TransactionCompat t, int boostModeTargets) {
         for (RemoteAnimationTargetCompat target : targets) {
-            int layer = target.mode == boostModeTargets
-                    ? Integer.MAX_VALUE
-                    : target.prefixOrderIndex;
-            t.setLayer(target.leash, layer);
+            t.setLayer(target.leash, getLayer(target, boostModeTargets));
             t.show(target.leash);
         }
     }
+
+    static int getLayer(RemoteAnimationTargetCompat target, int boostModeTarget) {
+        return target.mode == boostModeTarget
+                ? Z_BOOST_BASE + target.prefixOrderIndex
+                : target.prefixOrderIndex;
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationTargetSet.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationTargetSet.java
index 04b8be5..c372485 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationTargetSet.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationTargetSet.java
@@ -26,6 +26,7 @@
 
     public final RemoteAnimationTargetCompat[] unfilteredApps;
     public final RemoteAnimationTargetCompat[] apps;
+    public final int targetMode;
 
     public RemoteAnimationTargetSet(RemoteAnimationTargetCompat[] apps, int targetMode) {
         ArrayList<RemoteAnimationTargetCompat> filteredApps = new ArrayList<>();
@@ -39,6 +40,7 @@
 
         this.unfilteredApps = apps;
         this.apps = filteredApps.toArray(new RemoteAnimationTargetCompat[filteredApps.size()]);
+        this.targetMode = targetMode;
     }
 
     public RemoteAnimationTargetCompat findTask(int taskId) {
diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
index 1ea112d..c780b62 100644
--- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
+++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
@@ -45,10 +45,6 @@
  */
 public class ShelfScrimView extends ScrimView {
 
-    private static final int THRESHOLD_ALPHA_DARK = 102;
-    private static final int THRESHOLD_ALPHA_LIGHT = 46;
-    private static final int THRESHOLD_ALPHA_SUPER_LIGHT = 128;
-
     // In transposed layout, we simply draw a flat color.
     private boolean mDrawingFlatColor;
 
@@ -77,13 +73,7 @@
         mMaxScrimAlpha = OVERVIEW.getWorkspaceScrimAlpha(mLauncher);
 
         mEndAlpha = Color.alpha(mEndScrim);
-        if (Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark)) {
-            mThresholdAlpha = THRESHOLD_ALPHA_DARK;
-        } else if (Themes.getAttrBoolean(mLauncher, R.attr.isWorkspaceDarkText)) {
-            mThresholdAlpha = THRESHOLD_ALPHA_SUPER_LIGHT;
-        } else {
-            mThresholdAlpha = THRESHOLD_ALPHA_LIGHT;
-        }
+        mThresholdAlpha = Themes.getAttrInteger(context, R.attr.allAppsInterimScrimAlpha);
         mRadius = mLauncher.getResources().getDimension(R.dimen.shelf_surface_radius);
         mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index 128a19e..d9dfd18 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -29,6 +29,7 @@
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.Shader;
+import android.support.v4.graphics.ColorUtils;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.util.Property;
@@ -37,8 +38,10 @@
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.SystemUiController;
+import com.android.launcher3.util.Themes;
 import com.android.quickstep.TaskOverlayFactory;
 import com.android.quickstep.TaskOverlayFactory.TaskOverlay;
 import com.android.systemui.shared.recents.model.Task;
@@ -50,6 +53,7 @@
 public class TaskThumbnailView extends View {
 
     private static final LightingColorFilter[] sDimFilterCache = new LightingColorFilter[256];
+    private static final LightingColorFilter[] sHighlightFilterCache = new LightingColorFilter[256];
 
     public static final Property<TaskThumbnailView, Float> DIM_ALPHA_MULTIPLIER =
             new FloatProperty<TaskThumbnailView>("dimAlphaMultiplier") {
@@ -68,6 +72,7 @@
 
     private final BaseActivity mActivity;
     private final TaskOverlay mOverlay;
+    private final boolean mIsDarkTextTheme;
     private final Paint mPaint = new Paint();
     private final Paint mBackgroundPaint = new Paint();
 
@@ -97,6 +102,7 @@
         mPaint.setFilterBitmap(true);
         mBackgroundPaint.setColor(Color.WHITE);
         mActivity = BaseActivity.fromContext(context);
+        mIsDarkTextTheme = Themes.getAttrBoolean(mActivity, R.attr.isWorkspaceDarkText);
     }
 
     public void bind() {
@@ -198,7 +204,7 @@
     private void updateThumbnailPaintFilter() {
         int mul = (int) ((1 - mDimAlpha * mDimAlphaMultiplier) * 255);
         if (mBitmapShader != null) {
-            LightingColorFilter filter = getLightingColorFilter(mul);
+            LightingColorFilter filter = getDimmingColorFilter(mul, mIsDarkTextTheme);
             mPaint.setColorFilter(filter);
             mBackgroundPaint.setColorFilter(filter);
         } else {
@@ -287,16 +293,25 @@
         updateThumbnailMatrix();
     }
 
-    private static LightingColorFilter getLightingColorFilter(int dimColor) {
-        if (dimColor < 0) {
-            dimColor = 0;
-        } else if (dimColor > 255) {
-            dimColor = 255;
+    private static LightingColorFilter getDimmingColorFilter(int intensity, boolean shouldLighten) {
+        intensity = Utilities.boundToRange(intensity, 0, 255);
+        if (intensity == 255) {
+            return null;
         }
-        if (sDimFilterCache[dimColor] == null) {
-            sDimFilterCache[dimColor] =
-                    new LightingColorFilter(Color.argb(255, dimColor, dimColor, dimColor), 0);
+        if (shouldLighten) {
+            if (sHighlightFilterCache[intensity] == null) {
+                int colorAdd = 255 - intensity;
+                sHighlightFilterCache[intensity] = new LightingColorFilter(
+                        Color.argb(255, intensity, intensity, intensity),
+                        Color.argb(255, colorAdd, colorAdd, colorAdd));
+            }
+            return sHighlightFilterCache[intensity];
+        } else {
+            if (sDimFilterCache[intensity] == null) {
+                sDimFilterCache[intensity] = new LightingColorFilter(
+                        Color.argb(255, intensity, intensity, intensity), 0);
+            }
+            return sDimFilterCache[intensity];
         }
-        return sDimFilterCache[dimColor];
     }
 }
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 64ca05e..30091a5 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -20,6 +20,7 @@
 
     <!-- Attributes used for launcher theme -->
     <attr name="allAppsScrimColor" format="color" />
+    <attr name="allAppsInterimScrimAlpha" format="integer" />
     <attr name="allAppsNavBarScrimColor" format="color" />
     <attr name="popupColorPrimary" format="color" />
     <attr name="popupColorSecondary" format="color" />
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 2b760f3..31cbaa1 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -29,6 +29,7 @@
 
     <style name="BaseLauncherThemeWithCustomAttrs" parent="@style/BaseLauncherTheme">
         <item name="allAppsScrimColor">#EAFFFFFF</item>
+        <item name="allAppsInterimScrimAlpha">46</item>
         <item name="allAppsNavBarScrimColor">#66FFFFFF</item>
         <item name="popupColorPrimary">#FFF</item>
         <item name="popupColorSecondary">#F5F5F5</item> <!-- Gray 100 -->
@@ -47,6 +48,7 @@
 
     <style name="LauncherTheme.DarkText" parent="@style/LauncherTheme">
         <item name="workspaceTextColor">#FF212121</item>
+        <item name="allAppsInterimScrimAlpha">128</item>
         <item name="workspaceShadowColor">@android:color/transparent</item>
         <item name="workspaceAmbientShadowColor">@android:color/transparent</item>
         <item name="workspaceKeyShadowColor">@android:color/transparent</item>
@@ -62,6 +64,7 @@
         <item name="android:colorControlHighlight">#A0FFFFFF</item>
         <item name="android:colorPrimary">#FF212121</item>
         <item name="allAppsScrimColor">#EA212121</item>
+        <item name="allAppsInterimScrimAlpha">102</item>
         <item name="allAppsNavBarScrimColor">#80000000</item>
         <item name="popupColorPrimary">?android:attr/colorPrimary</item>
         <item name="popupColorSecondary">#424242</item> <!-- Gray 800 -->
@@ -71,6 +74,7 @@
     </style>
 
     <style name="LauncherThemeDark.DarKText" parent="@style/LauncherThemeDark">
+        <item name="allAppsInterimScrimAlpha">25</item>
         <item name="workspaceTextColor">#FF212121</item>
         <item name="workspaceShadowColor">@android:color/transparent</item>
         <item name="workspaceAmbientShadowColor">@android:color/transparent</item>
diff --git a/src/com/android/launcher3/provider/ImportDataTask.java b/src/com/android/launcher3/provider/ImportDataTask.java
index b83d3c0..b1dd003 100644
--- a/src/com/android/launcher3/provider/ImportDataTask.java
+++ b/src/com/android/launcher3/provider/ImportDataTask.java
@@ -142,6 +142,8 @@
                 // First row of first screen is not empty
                 createEmptyRowOnFirstScreen = c.moveToNext();
             }
+        } else {
+            createEmptyRowOnFirstScreen = false;
         }
 
         ArrayList<ContentProviderOperation> insertOperations = new ArrayList<>(BATCH_INSERT_SIZE);
diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java
index 65acaa9..b26d39f 100644
--- a/src/com/android/launcher3/qsb/QsbContainerView.java
+++ b/src/com/android/launcher3/qsb/QsbContainerView.java
@@ -36,7 +36,6 @@
 
 import com.android.launcher3.AppWidgetResizeFrame;
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
@@ -68,6 +67,10 @@
         super.setPadding(0, 0, 0, 0);
     }
 
+    protected void setPaddingUnchecked(int left, int top, int right, int bottom) {
+        super.setPadding(left, top, right, bottom);
+    }
+
     /**
      * A fragment to display the QSB.
      */
@@ -100,7 +103,7 @@
             mWrapper = new FrameLayout(getActivity());
 
             // Only add the view when enabled
-            if (FeatureFlags.QSB_ON_FIRST_SCREEN) {
+            if (isQsbEnabled()) {
                 mWrapper.addView(createQsb(mWrapper));
             }
             return mWrapper;
@@ -212,7 +215,7 @@
 
         private void rebindFragment() {
             // Exit if the embedded qsb is disabled
-            if (!FeatureFlags.QSB_ON_FIRST_SCREEN) {
+            if (!isQsbEnabled()) {
                 return;
             }
 
@@ -221,6 +224,10 @@
                 mWrapper.addView(createQsb(mWrapper));
             }
         }
+
+        public boolean isQsbEnabled() {
+            return FeatureFlags.QSB_ON_FIRST_SCREEN;
+        }
     }
 
     /**
diff --git a/src/com/android/launcher3/states/InternalStateHandler.java b/src/com/android/launcher3/states/InternalStateHandler.java
index cf7c6ba..c6370c5 100644
--- a/src/com/android/launcher3/states/InternalStateHandler.java
+++ b/src/com/android/launcher3/states/InternalStateHandler.java
@@ -96,10 +96,12 @@
         private WeakReference<InternalStateHandler> mPendingHandler = new WeakReference<>(null);
         private MainThreadExecutor mMainThreadExecutor;
 
-        public synchronized void schedule(InternalStateHandler handler) {
-            mPendingHandler = new WeakReference<>(handler);
-            if (mMainThreadExecutor == null) {
-                mMainThreadExecutor = new MainThreadExecutor();
+        public void schedule(InternalStateHandler handler) {
+            synchronized (this) {
+                mPendingHandler = new WeakReference<>(handler);
+                if (mMainThreadExecutor == null) {
+                    mMainThreadExecutor = new MainThreadExecutor();
+                }
             }
             mMainThreadExecutor.execute(this);
         }
@@ -118,23 +120,25 @@
             initIfPending(launcher, launcher.isStarted());
         }
 
-        public synchronized boolean initIfPending(Launcher launcher, boolean alreadyOnHome) {
+        public boolean initIfPending(Launcher launcher, boolean alreadyOnHome) {
             InternalStateHandler pendingHandler = mPendingHandler.get();
             if (pendingHandler != null) {
                 if (!pendingHandler.init(launcher, alreadyOnHome)) {
-                    mPendingHandler.clear();
+                    clearReference(pendingHandler);
                 }
                 return true;
             }
             return false;
         }
 
-        public synchronized boolean clearReference(InternalStateHandler handler) {
-            if (mPendingHandler.get() == handler) {
-                mPendingHandler.clear();
-                return true;
+        public boolean clearReference(InternalStateHandler handler) {
+            synchronized (this) {
+                if (mPendingHandler.get() == handler) {
+                    mPendingHandler.clear();
+                    return true;
+                }
+                return false;
             }
-            return false;
         }
 
         public boolean hasPending() {
diff --git a/src/com/android/launcher3/util/Themes.java b/src/com/android/launcher3/util/Themes.java
index d225093..5f965a3 100644
--- a/src/com/android/launcher3/util/Themes.java
+++ b/src/com/android/launcher3/util/Themes.java
@@ -52,6 +52,13 @@
         return value;
     }
 
+    public static int getAttrInteger(Context context, int attr) {
+        TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
+        int value = ta.getInteger(0, 0);
+        ta.recycle();
+        return value;
+    }
+
     /**
      * Returns the alpha corresponding to the theme attribute {@param attr}, in the range [0, 255].
      */