Launcher app close transition.

Bug: 70220260

Change-Id: I0a3a6153dc1cba53546f792bf3ec037b1a5f6d90
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 40436d1..9cc7973 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -34,6 +34,9 @@
     <!-- TODO: This can be calculated using other resource values -->
     <dimen name="all_apps_search_box_full_height">90dp</dimen>
 
-    <dimen name="drag_layer_trans_y">25dp</dimen>
+    <!-- Launcher app transition -->
+    <dimen name="content_trans_y">25dp</dimen>
+    <dimen name="workspace_trans_y">80dp</dimen>
 
+    <dimen name="shelf_min_value">-2.857dp</dimen>
 </resources>
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
new file mode 100644
index 0000000..489e55b
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3;
+
+import android.animation.AnimatorSet;
+import android.os.Handler;
+
+import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
+
+import static com.android.systemui.shared.recents.utilities.Utilities.postAtFrontOfQueueAsynchronously;
+
+public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCompat {
+
+    AnimatorSet mAnimator;
+    private Launcher mLauncher;
+
+    LauncherAnimationRunner(Launcher launcher) {
+        mLauncher = launcher;
+    }
+
+    @Override
+    public void onAnimationCancelled() {
+        postAtFrontOfQueueAsynchronously(mLauncher.getWindow().getDecorView().getHandler(), () -> {
+            if (mAnimator != null) {
+                mAnimator.cancel();
+            }
+        });
+    }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManager.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManager.java
index d56666a..256e926 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManager.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.views.AllAppsScrim.SCRIM_PROGRESS;
 import static com.android.systemui.shared.recents.utilities.Utilities.getNextFrameNumber;
 import static com.android.systemui.shared.recents.utilities.Utilities.getSurface;
 import static com.android.systemui.shared.recents.utilities.Utilities.postAtFrontOfQueueAsynchronously;
@@ -25,10 +26,12 @@
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.os.Bundle;
+import android.os.Handler;
 import android.util.Log;
 import android.view.Surface;
 import android.view.View;
@@ -39,25 +42,34 @@
 import com.android.launcher3.InsettableFrameLayout.LayoutParams;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.views.AllAppsScrim;
+import com.android.systemui.shared.system.ActivityCompat;
 import com.android.systemui.shared.system.ActivityOptionsCompat;
 import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
+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.WindowManagerWrapper;
 
 /**
- * Manages the opening app animations from Launcher.
+ * Manages the opening and closing app transitions from Launcher.
  */
 public class LauncherAppTransitionManager {
 
     private static final String TAG = "LauncherTransition";
     private static final int REFRESH_RATE_MS = 16;
 
+    private static final int CLOSING_TRANSITION_DURATION_MS = 350;
+
     private final DragLayer mDragLayer;
     private final Launcher mLauncher;
     private final DeviceProfile mDeviceProfile;
 
-    private final float mDragLayerTransY;
+    private final float mContentTransY;
+    private final float mWorkspaceTransY;
+    // The smallest y-value the shelf will reach on screen, before overshooting back down to 0.
+    private final float mShelfMinValue;
 
     private ImageView mFloatingView;
     private boolean mIsRtl;
@@ -67,24 +79,30 @@
         mDragLayer = launcher.getDragLayer();
         mDeviceProfile = launcher.getDeviceProfile();
 
-        mDragLayerTransY =
-                launcher.getResources().getDimensionPixelSize(R.dimen.drag_layer_trans_y);
-
         mIsRtl = Utilities.isRtl(launcher.getResources());
+
+        Resources res = launcher.getResources();
+        mContentTransY = res.getDimensionPixelSize(R.dimen.content_trans_y);
+        mWorkspaceTransY = res.getDimensionPixelSize(R.dimen.workspace_trans_y);
+        mShelfMinValue = res.getDimensionPixelSize(R.dimen.shelf_min_value);
     }
 
+    /**
+     * @return A Bundle with remote animations that controls how the window of the opening
+     *         targets are displayed.
+     */
     public Bundle getActivityLauncherOptions(View v) {
-        RemoteAnimationRunnerCompat runner = new RemoteAnimationRunnerCompat() {
+        RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mLauncher) {
             @Override
             public void onAnimationStart(RemoteAnimationTargetCompat[] targets,
                     Runnable finishedCallback) {
                 // Post at front of queue ignoring sync barriers to make sure it gets processed
                 // before the next frame.
                 postAtFrontOfQueueAsynchronously(v.getHandler(), () -> {
-                    AnimatorSet both = new AnimatorSet();
-                    both.play(getLauncherAnimators(v));
-                    both.play(getAppWindowAnimators(v, targets));
-                    both.addListener(new AnimatorListenerAdapter() {
+                    mAnimator = new AnimatorSet();
+                    mAnimator.play(getLauncherAnimators(v));
+                    mAnimator.play(getWindowAnimators(v, targets));
+                    mAnimator.addListener(new AnimatorListenerAdapter() {
                         @Override
                         public void onAnimationEnd(Animator animation) {
                             // Reset launcher to normal state
@@ -101,51 +119,62 @@
                             finishedCallback.run();
                         }
                     });
-                    both.start();
+                    mAnimator.start();
                     // Because t=0 has the app icon in its original spot, we can skip the first
                     // frame and have the same movement one frame earlier.
-                    both.setCurrentPlayTime(REFRESH_RATE_MS);
+                    mAnimator.setCurrentPlayTime(REFRESH_RATE_MS);
                 });
             }
-
-            @Override
-            public void onAnimationCancelled() {
-            }
         };
 
         return ActivityOptionsCompat.makeRemoteAnimation(
                 new RemoteAnimationAdapterCompat(runner, 500, 380)).toBundle();
     }
 
+    /**
+     * @return Animators that control the movements of the Launcher and icon of the opening target.
+     */
     private AnimatorSet getLauncherAnimators(View v) {
         AnimatorSet launcherAnimators = new AnimatorSet();
-        launcherAnimators.play(getHideLauncherAnimator());
-        launcherAnimators.play(getAppIconAnimator(v));
+        launcherAnimators.play(getLauncherContentAnimator(false /* show */));
+        launcherAnimators.play(getIconAnimator(v));
         return launcherAnimators;
     }
 
-    private AnimatorSet getHideLauncherAnimator() {
+    /**
+     * Content is everything on screen except the background and the floating view (if any).
+     *
+     * @param show If true: Animate the content so that it moves upwards and fades in.
+     *             Else: Animate the content so that it moves downwards and fades out.
+     */
+    private AnimatorSet getLauncherContentAnimator(boolean show) {
         AnimatorSet hideLauncher = new AnimatorSet();
 
-        // Animate the background content so that it moves downwards and fades out.
-        if (mLauncher.isInState(LauncherState.ALL_APPS)) {
+        float[] alphas = show
+                ? new float[] {0, 1}
+                : new float[] {1, 0};
+        float[] trans = show
+                ? new float[] {mContentTransY, 0,}
+                : new float[] {0, mContentTransY};
+
+        if (mLauncher.isInState(LauncherState.ALL_APPS) && !mDeviceProfile.isVerticalBarLayout()) {
+            // All Apps in portrait mode is full screen, so we only animate AllAppsContainerView.
             View appsView = mLauncher.getAppsView();
-            ObjectAnimator alpha = ObjectAnimator.ofFloat(appsView, View.ALPHA, 1f, 0f);
+            ObjectAnimator alpha = ObjectAnimator.ofFloat(appsView, View.ALPHA, alphas);
             alpha.setDuration(217);
             alpha.setInterpolator(Interpolators.LINEAR);
-            ObjectAnimator transY = ObjectAnimator.ofFloat(appsView, View.TRANSLATION_Y, 0,
-                    mDragLayerTransY);
+            ObjectAnimator transY = ObjectAnimator.ofFloat(appsView, View.TRANSLATION_Y, trans);
             transY.setInterpolator(Interpolators.AGGRESSIVE_EASE);
             transY.setDuration(350);
 
             hideLauncher.play(alpha);
             hideLauncher.play(transY);
         } else {
-            ObjectAnimator dragLayerAlpha = ObjectAnimator.ofFloat(mDragLayer, View.ALPHA, 1f, 0f);
+            ObjectAnimator dragLayerAlpha = ObjectAnimator.ofFloat(mDragLayer, View.ALPHA, alphas);
             dragLayerAlpha.setDuration(217);
             dragLayerAlpha.setInterpolator(Interpolators.LINEAR);
             ObjectAnimator dragLayerTransY = ObjectAnimator.ofFloat(mDragLayer, View.TRANSLATION_Y,
-                    0, mDragLayerTransY);
+                    trans);
             dragLayerTransY.setInterpolator(Interpolators.AGGRESSIVE_EASE);
             dragLayerTransY.setDuration(350);
 
@@ -155,7 +184,10 @@
         return hideLauncher;
     }
 
-    private AnimatorSet getAppIconAnimator(View v) {
+    /**
+     * @return Animator that controls the icon used to launch the target.
+     */
+    private AnimatorSet getIconAnimator(View v) {
         boolean isBubbleTextView = v instanceof BubbleTextView;
         mFloatingView = new ImageView(mLauncher);
         if (isBubbleTextView) {
@@ -230,7 +262,10 @@
         return appIconAnimatorSet;
     }
 
-    private ValueAnimator getAppWindowAnimators(View v, RemoteAnimationTargetCompat[] targets) {
+    /**
+     * @return Animator that controls the window of the opening targets.
+     */
+    private ValueAnimator getWindowAnimators(View v, RemoteAnimationTargetCompat[] targets) {
         Rect bounds = new Rect();
         if (v instanceof BubbleTextView) {
             ((BubbleTextView) v).getIconBounds(bounds);
@@ -285,7 +320,7 @@
                 // Fade in the app window.
                 float alphaDelay = 0;
                 float alphaDuration = 50;
-                float alpha = getValue(1f, 0f, alphaDelay, alphaDuration,
+                float alpha = getValue(0f, 1f, alphaDelay, alphaDuration,
                         appAnimator.getDuration() * percent, Interpolators.AGGRESSIVE_EASE);
 
                 // Animate the window crop so that it starts off as a square, and then reveals
@@ -318,19 +353,168 @@
                 matrix.reset();
                 isFirstFrame = false;
             }
-
-            /**
-             * Helper method that allows us to get interpolated values for embedded
-             * animations with a delay and/or different duration.
-             */
-            private float getValue(float start, float end, float delay, float duration,
-                                   float currentPlayTime, Interpolator i) {
-                float time = Math.max(0, currentPlayTime - delay);
-                float newPercent = Math.min(1f, time / duration);
-                newPercent = i.getInterpolation(newPercent);
-                return start * newPercent + end * (1 - newPercent);
-            }
         });
         return appAnimator;
     }
+
+    /**
+     * Registers remote animations used when closing apps to home screen.
+     */
+    public void registerRemoteAnimations() {
+        RemoteAnimationDefinitionCompat definition = new RemoteAnimationDefinitionCompat();
+        definition.addRemoteAnimation(WindowManagerWrapper.TRANSIT_WALLPAPER_OPEN,
+                new RemoteAnimationAdapterCompat(getWallpaperOpenRunner(), 0,
+                        CLOSING_TRANSITION_DURATION_MS));
+
+//      TODO: App controlled transition for unlock to home TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER
+
+        new ActivityCompat(mLauncher).registerRemoteAnimations(definition);
+    }
+
+    /**
+     * @return Runner that plays when user goes to Launcher
+     *         ie. pressing home, swiping up from nav bar.
+     */
+    private RemoteAnimationRunnerCompat getWallpaperOpenRunner() {
+        return new LauncherAnimationRunner(mLauncher) {
+            @Override
+            public void onAnimationStart(RemoteAnimationTargetCompat[] targets,
+                                         Runnable finishedCallback) {
+                Handler handler = mLauncher.getWindow().getDecorView().getHandler();
+                postAtFrontOfQueueAsynchronously(handler, () -> {
+                    // We use a separate transition for Overview mode.
+                    if (mLauncher.isInState(LauncherState.OVERVIEW)) {
+                        finishedCallback.run();
+                        return;
+                    }
+
+                    mAnimator = new AnimatorSet();
+                    mAnimator.addListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            finishedCallback.run();
+                        }
+                    });
+                    mAnimator.play(getClosingWindowAnimators(targets));
+                    mAnimator.play(getLauncherResumeAnimation());
+                    mAnimator.start();
+                });
+            }
+        };
+    }
+
+    /**
+     * Animator that controls the transformations of the windows the targets that are closing.
+     */
+    private Animator getClosingWindowAnimators(RemoteAnimationTargetCompat[] targets) {
+        Matrix matrix = new Matrix();
+        float height = mLauncher.getDeviceProfile().heightPx;
+        float width = mLauncher.getDeviceProfile().widthPx;
+        float endX = Utilities.isRtl(mLauncher.getResources()) ? -width : width;
+
+        ValueAnimator closingAnimator = ValueAnimator.ofFloat(0, 1);
+        closingAnimator.setDuration(CLOSING_TRANSITION_DURATION_MS);
+
+        closingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            boolean isFirstFrame = true;
+
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                final float percent = animation.getAnimatedFraction();
+                float currentPlayTime = percent * closingAnimator.getDuration();
+
+                float scale = getValue(1f, 0.8f, 0, 267, currentPlayTime,
+                        Interpolators.AGGRESSIVE_EASE);
+                matrix.setScale(scale, scale);
+
+                float dX = getValue(0, endX, 0, 350, currentPlayTime,
+                        Interpolators.AGGRESSIVE_EASE_IN_OUT);
+
+                TransactionCompat t = new TransactionCompat();
+                for (RemoteAnimationTargetCompat app : targets) {
+                    if (app.mode == RemoteAnimationTargetCompat.MODE_CLOSING) {
+                        t.setAlpha(app.leash, 1f - percent);
+
+                        float dY = (height - (app.clipRect.height() * scale)) / 2f;
+                        matrix.postTranslate(dX, dY);
+                        t.setMatrix(app.leash, matrix);
+                    }
+                    // TODO: Layer should be set only once, but there is possibly a race condition
+                    // where WindowManager is also calling setLayer.
+                    int layer = app.mode == RemoteAnimationTargetCompat.MODE_CLOSING
+                            ? Integer.MAX_VALUE
+                            : app.prefixOrderIndex;
+                    t.setLayer(app.leash, layer);
+                    if (isFirstFrame) {
+                        t.show(app.leash);
+                    }
+                }
+                t.apply();
+
+                matrix.reset();
+                isFirstFrame = false;
+            }
+        });
+        return closingAnimator;
+    }
+
+    /**
+     * @return Animator that modifies Launcher as a result from {@link #getWallpaperOpenRunner}.
+     */
+    private AnimatorSet getLauncherResumeAnimation() {
+        if (mLauncher.isInState(LauncherState.ALL_APPS)
+                || mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+            return getLauncherContentAnimator(true /* show */);
+        } else {
+            AnimatorSet workspaceAnimator = new AnimatorSet();
+            mLauncher.getWorkspace().setTranslationY(mWorkspaceTransY);
+            mLauncher.getWorkspace().setAlpha(0f);
+            workspaceAnimator.play(ObjectAnimator.ofFloat(mLauncher.getWorkspace(),
+                    View.TRANSLATION_Y, mWorkspaceTransY, 0));
+            workspaceAnimator.play(ObjectAnimator.ofFloat(mLauncher.getWorkspace(), View.ALPHA,
+                    0, 1f));
+            workspaceAnimator.setStartDelay(150);
+            workspaceAnimator.setDuration(333);
+            workspaceAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+
+            // Animate the shelf
+            AllAppsScrim allAppsScrim = mLauncher.findViewById(R.id.all_apps_scrim);
+            View hotseat = mLauncher.getHotseat();
+            final float endY = mShelfMinValue;
+            int startY = hotseat.getMeasuredHeight()
+                    + (allAppsScrim.getShadowBitmap().getHeight() / 2);
+            hotseat.setTranslationY(startY);
+            allAppsScrim.setTranslationY(startY);
+
+            AnimatorSet hotseatSlideIn = new AnimatorSet();
+            hotseatSlideIn.play(ObjectAnimator.ofFloat(hotseat, View.TRANSLATION_Y, startY, endY));
+            hotseatSlideIn.play(ObjectAnimator.ofFloat(allAppsScrim, SCRIM_PROGRESS, startY, endY));
+            hotseatSlideIn.setStartDelay(150);
+            hotseatSlideIn.setDuration(317);
+            hotseatSlideIn.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+
+            AnimatorSet hotseatOvershoot = new AnimatorSet();
+            hotseatOvershoot.play(ObjectAnimator.ofFloat(hotseat, View.TRANSLATION_Y, endY, 0));
+            hotseatOvershoot.play(ObjectAnimator.ofFloat(allAppsScrim, SCRIM_PROGRESS, endY, 0));
+            hotseatOvershoot.setDuration(153);
+            hotseatOvershoot.setInterpolator(Interpolators.OVERSHOOT_0);
+
+            AnimatorSet resumeLauncherAnimation = new AnimatorSet();
+            resumeLauncherAnimation.play(workspaceAnimator);
+            resumeLauncherAnimation.playSequentially(hotseatSlideIn, hotseatOvershoot);
+            return resumeLauncherAnimation;
+        }
+    }
+
+    /**
+     * Helper method that allows us to get interpolated values for embedded
+     * animations with a delay and/or different duration.
+     */
+    private static float getValue(float start, float end, float delay, float duration,
+            float currentPlayTime, Interpolator i) {
+        float time = Math.max(0, currentPlayTime - delay);
+        float newPercent = Math.min(1f, time / duration);
+        newPercent = i.getInterpolation(newPercent);
+        return end * newPercent + start * (1 - newPercent);
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index c4ffc0f..d086e74 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -31,7 +31,6 @@
 import com.android.launcher3.util.TouchController;
 import com.android.quickstep.RecentsView;
 import com.android.systemui.shared.recents.view.RecentsTransition;
-import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
 
 public class UiFactory {
 
@@ -89,4 +88,12 @@
             return launcher.getDefaultActivityLaunchOptions(v);
         }
     }
+
+    public static void registerRemoteAnimations(Launcher launcher) {
+        try {
+            new LauncherAppTransitionManager(launcher).registerRemoteAnimations();
+        } catch (NoClassDefFoundError e) {
+            // Gracefully fall back if the user's platform doesn't have the latest changes
+        }
+    }
 }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 32342b2..3d5b7d1 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -403,6 +403,10 @@
         getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
                 Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
 
+        if (!isInMultiWindowModeCompat()) {
+            UiFactory.registerRemoteAnimations(this);
+        }
+
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onCreate(savedInstanceState);
         }
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index f3a3539..ee0dba6 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -20,6 +20,7 @@
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 import android.view.animation.LinearInterpolator;
+import android.view.animation.OvershootInterpolator;
 import android.view.animation.PathInterpolator;
 
 
@@ -43,6 +44,9 @@
     public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
 
     public static final Interpolator AGGRESSIVE_EASE = new PathInterpolator(0.2f, 0f, 0f, 1f);
+    public static final Interpolator AGGRESSIVE_EASE_IN_OUT = new PathInterpolator(0.8f,0, 0.4f, 1);
+
+    public static final Interpolator OVERSHOOT_0 = new OvershootInterpolator(0);
 
     /**
      * Inversion of zInterpolate, compounded with an ease-out.
diff --git a/src/com/android/launcher3/views/AllAppsScrim.java b/src/com/android/launcher3/views/AllAppsScrim.java
index cc73182..6cd40fd 100644
--- a/src/com/android/launcher3/views/AllAppsScrim.java
+++ b/src/com/android/launcher3/views/AllAppsScrim.java
@@ -23,6 +23,7 @@
 import android.graphics.Rect;
 import android.support.v4.graphics.ColorUtils;
 import android.util.AttributeSet;
+import android.util.Property;
 import android.view.View;
 
 import com.android.launcher3.DeviceProfile;
@@ -60,11 +61,25 @@
 
     private final NinePatchDrawHelper mShadowHelper = new NinePatchDrawHelper();
 
+    private float mProgress;
     private int mFillAlpha;
 
     private float mDrawHeight;
     private float mDrawOffsetY;
 
+    public static final Property<AllAppsScrim, Float> SCRIM_PROGRESS =
+            new Property<AllAppsScrim, Float>(Float.class, "allAppsScrimProgress") {
+                @Override
+                public Float get(AllAppsScrim allAppsScrim) {
+                    return allAppsScrim.getProgress();
+                }
+
+                @Override
+                public void set(AllAppsScrim allAppsScrim, Float progress) {
+                    allAppsScrim.setProgress(progress);
+                }
+            };
+
     public AllAppsScrim(Context context) {
         this(context, null);
     }
@@ -109,6 +124,10 @@
         return result;
     }
 
+    public Bitmap getShadowBitmap() {
+        return mShadowBitmap;
+    }
+
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
@@ -154,18 +173,24 @@
     }
 
     public void setProgress(float translateY, float alpha) {
-        float newAlpha = Math.round(alpha * mAlphaRange + mMinAlpha);
+        int newAlpha = Math.round(alpha * mAlphaRange + mMinAlpha);
+        if (newAlpha != mFillAlpha) {
+            mFillAlpha = newAlpha;
+            mFillPaint.setAlpha(mFillAlpha);
+            invalidateDrawRect();
+        }
+
+        setProgress(translateY);
+    }
+
+    public void setProgress(float translateY) {
         // Negative translation means the scrim is moving up. For negative translation, we change
         // draw offset as it requires redraw (since more area of the scrim needs to be shown). For
         // position translation, we simply translate the scrim down as it avoids invalidate and
         // hence could be optimized by the platform.
         float drawOffsetY = Math.min(translateY, 0);
 
-        if (newAlpha != mFillAlpha || drawOffsetY != mDrawOffsetY) {
-            invalidateDrawRect();
-
-            mFillAlpha = Math.round(alpha * mAlphaRange + mMinAlpha);
-            mFillPaint.setAlpha(mFillAlpha);
+        if (drawOffsetY != mDrawOffsetY) {
             mDrawOffsetY = drawOffsetY;
             invalidateDrawRect();
         }
@@ -173,6 +198,10 @@
         setTranslationY(Math.max(translateY, 0));
     }
 
+    public float getProgress() {
+        return mProgress;
+    }
+
     private void invalidateDrawRect() {
         mDrawRect.top = (int) (getHeight()
                 + mDrawOffsetY - mDrawHeight + mPadding.top - mShadowBlur - 0.5f);
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
index 0488fae..c857bf6 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
@@ -18,21 +18,15 @@
 
 import static com.android.launcher3.LauncherState.OVERVIEW;
 
-import android.app.ActivityOptions;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.view.View;
 import android.view.View.AccessibilityDelegate;
 
-import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherStateManager.StateHandler;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.graphics.BitmapRenderer;
 import com.android.launcher3.util.TouchController;
 
@@ -71,4 +65,6 @@
     public static Bundle getActivityLaunchOptions(Launcher launcher, View v) {
         return launcher.getDefaultActivityLaunchOptions(v);
     }
+
+    public static void registerRemoteAnimations(Launcher launcher) { }
 }