Initial changes to support blur

- Add a new controller to update the background blur on either the
  launcher or app surfaces based on state or transition

Bug: 149792636

Change-Id: I6103cd3d53a00c8025558dd49bb73137e2980014
diff --git a/quickstep/recents_ui_overrides/res/values/colors.xml b/quickstep/recents_ui_overrides/res/values/colors.xml
index 7426e30..361f5f7 100644
--- a/quickstep/recents_ui_overrides/res/values/colors.xml
+++ b/quickstep/recents_ui_overrides/res/values/colors.xml
@@ -1,4 +1,18 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
 <resources>
     <color name="chip_hint_foreground_color">#fff</color>
 
diff --git a/quickstep/recents_ui_overrides/res/values/config.xml b/quickstep/recents_ui_overrides/res/values/config.xml
new file mode 100644
index 0000000..527eec6
--- /dev/null
+++ b/quickstep/recents_ui_overrides/res/values/config.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<resources>
+    <integer name="app_background_blur_radius">150</integer>
+    <integer name="allapps_background_blur_radius">90</integer>
+    <integer name="overview_background_blur_radius">50</integer>
+    <integer name="folder_background_blur_radius_adjustment">20</integer>
+</resources>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/res/values/dimens.xml b/quickstep/recents_ui_overrides/res/values/dimens.xml
index 61c576e..20b1485 100644
--- a/quickstep/recents_ui_overrides/res/values/dimens.xml
+++ b/quickstep/recents_ui_overrides/res/values/dimens.xml
@@ -1,4 +1,18 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
 <resources>
     <dimen name="chip_hint_border_width">1dp</dimen>
     <dimen name="chip_hint_corner_radius">20dp</dimen>
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 7728207..7bc656a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -91,8 +91,9 @@
 
         AppWindowAnimationHelper helper =
             new AppWindowAnimationHelper(recentsView.getPagedViewOrientedState(), mLauncher);
-        anim.play(getRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets,
-                wallpaperTargets, helper).setDuration(RECENTS_LAUNCH_DURATION));
+        Animator recentsAnimator = getRecentsWindowAnimator(taskView, skipLauncherChanges,
+                appTargets, wallpaperTargets, mLauncher.getBackgroundBlurController(), helper);
+        anim.play(recentsAnimator.setDuration(RECENTS_LAUNCH_DURATION));
 
         Animator childStateAnimation = null;
         // Found a visible recents task that matches the opening app, lets launch the app from there
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 48a2f32..5bac964 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -15,8 +15,11 @@
  */
 package com.android.launcher3.uioverrides.states;
 
+import android.content.Context;
+
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.quickstep.util.LayoutUtils;
@@ -102,4 +105,9 @@
         }
         return super.getHotseatScaleAndTranslation(launcher);
     }
+
+    @Override
+    public int getBackgroundBlurRadius(Context context) {
+        return context.getResources().getInteger(R.integer.app_background_blur_radius);
+    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 7895bac..bfbb630 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -35,6 +35,7 @@
 import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
 import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
 
+import android.content.Context;
 import android.graphics.Rect;
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
@@ -206,6 +207,11 @@
     }
 
     @Override
+    public int getBackgroundBlurRadius(Context context) {
+        return context.getResources().getInteger(R.integer.overview_background_blur_radius);
+    }
+
+    @Override
     public void onBackPressed(Launcher launcher) {
         TaskView taskView = launcher.<RecentsView>getOverviewPanel().getRunningTaskView();
         if (taskView != null) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index 375f160..e1ff4f4 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -15,13 +15,17 @@
  */
 package com.android.quickstep;
 
+import static com.android.launcher3.LauncherState.BACKGROUND_APP;
+import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
+import static com.android.launcher3.uioverrides.BackgroundBlurController.BACKGROUND_BLUR;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 
 import android.animation.Animator;
 import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.graphics.Rect;
 import android.util.Log;
@@ -30,7 +34,9 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.uioverrides.BackgroundBlurController;
 import com.android.quickstep.util.AppWindowAnimationHelper;
+import com.android.quickstep.util.AppWindowAnimationHelper.TransformParams;
 import com.android.quickstep.util.RemoteAnimationProvider;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -38,7 +44,7 @@
 import com.android.systemui.shared.system.TransactionCompat;
 
 /**
- * Provider for the atomic remote window animation from the app to the overview.
+ * Provider for the atomic (for 3-button mode) remote window animation from the app to the overview.
  *
  * @param <T> activity that contains the overview
  */
@@ -48,15 +54,15 @@
     private static final long RECENTS_LAUNCH_DURATION = 250;
     private static final String TAG = "AppToOverviewAnimationProvider";
 
-    private final BaseActivityInterface<T> mHelper;
+    private final BaseActivityInterface<T> mActivityInterface;
     // The id of the currently running task that is transitioning to overview.
     private final int mTargetTaskId;
 
     private T mActivity;
     private RecentsView mRecentsView;
 
-    AppToOverviewAnimationProvider(BaseActivityInterface<T> helper, int targetTaskId) {
-        mHelper = helper;
+    AppToOverviewAnimationProvider(BaseActivityInterface<T> activityInterface, int targetTaskId) {
+        mActivityInterface = activityInterface;
         mTargetTaskId = targetTaskId;
     }
 
@@ -70,7 +76,7 @@
         activity.<RecentsView>getOverviewPanel().showCurrentTask(mTargetTaskId);
         AbstractFloatingView.closeAllOpenViews(activity, wasVisible);
         BaseActivityInterface.AnimationFactory factory =
-                mHelper.prepareRecentsUI(wasVisible,
+                mActivityInterface.prepareRecentsUI(wasVisible,
                 false /* animate activity */, (controller) -> {
                     controller.dispatchOnStart();
                     ValueAnimator anim = controller.getAnimationPlayer()
@@ -98,11 +104,18 @@
         if (mRecentsView != null) {
             mRecentsView.setRunningTaskIconScaledDown(true);
         }
+
+        BackgroundBlurController blurController = mActivityInterface.getBackgroundBlurController();
+        if (blurController != null) {
+            // Update the surface to be the lowest closing app surface
+            blurController.setSurfaceToLauncher(mRecentsView);
+        }
+
         AnimatorSet anim = new AnimatorSet();
         anim.addListener(new AnimationSuccessListener() {
             @Override
             public void onAnimationSuccess(Animator animator) {
-                mHelper.onSwipeUpToRecentsComplete();
+                mActivityInterface.onSwipeUpToRecentsComplete();
                 if (mRecentsView != null) {
                     mRecentsView.animateUpRunningTaskIconScale();
                 }
@@ -110,7 +123,8 @@
         });
         if (mActivity == null) {
             Log.e(TAG, "Animation created, before activity");
-            anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION));
+            anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION))
+                    .with(createBackgroundBlurAnimator(blurController));
             return anim;
         }
 
@@ -121,7 +135,8 @@
         RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(mTargetTaskId);
         if (runningTaskTarget == null) {
             Log.e(TAG, "No closing app");
-            anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION));
+            anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION))
+                    .with(createBackgroundBlurAnimator(blurController));
             return anim;
         }
 
@@ -138,11 +153,12 @@
         clipHelper.updateSource(homeBounds, runningTaskTarget);
 
         Rect targetRect = new Rect();
-        mHelper.getSwipeUpDestinationAndLength(mActivity.getDeviceProfile(), mActivity, targetRect);
+        mActivityInterface.getSwipeUpDestinationAndLength(mActivity.getDeviceProfile(), mActivity,
+                targetRect);
         clipHelper.updateTargetRect(targetRect);
         clipHelper.prepareAnimation(mActivity.getDeviceProfile(), false /* isOpening */);
 
-        AppWindowAnimationHelper.TransformParams params = new AppWindowAnimationHelper.TransformParams()
+        TransformParams params = new TransformParams()
                 .setSyncTransactionApplier(new SyncRtSurfaceTransactionApplierCompat(rootView));
         ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
         valueAnimator.setDuration(RECENTS_LAUNCH_DURATION);
@@ -167,7 +183,8 @@
                 transaction.apply();
             });
         }
-        anim.play(valueAnimator);
+        anim.play(valueAnimator)
+                .with(createBackgroundBlurAnimator(blurController));
         return anim;
     }
 
@@ -179,4 +196,15 @@
     long getRecentsLaunchDuration() {
         return RECENTS_LAUNCH_DURATION;
     }
+
+    private Animator createBackgroundBlurAnimator(BackgroundBlurController blurController) {
+        if (blurController == null) {
+            // Dummy animation
+            return ValueAnimator.ofInt(0);
+        }
+        return ObjectAnimator.ofInt(blurController, BACKGROUND_BLUR,
+                BACKGROUND_APP.getBackgroundBlurRadius(mActivity),
+                OVERVIEW.getBackgroundBlurRadius(mActivity))
+                .setDuration(RECENTS_LAUNCH_DURATION);
+    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
index ccc2150..d402a75 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
@@ -56,6 +56,8 @@
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.appprediction.PredictionUiStateManager;
 import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.uioverrides.BackgroundBlurController;
+import com.android.launcher3.uioverrides.BackgroundBlurController.ClampedBlurProperty;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.views.FloatingIconView;
 import com.android.quickstep.SysUINavigationMode.Mode;
@@ -327,8 +329,19 @@
                     fromState.getVerticalProgress(activity),
                     endState.getVerticalProgress(activity)));
         }
+
+        // Animate the blur
+        BackgroundBlurController blurController = getBackgroundBlurController();
+        int fromBlurRadius = fromState.getBackgroundBlurRadius(activity);
+        int toBlurRadius = endState.getBackgroundBlurRadius(activity);
+        Animator backgroundBlur = ObjectAnimator.ofInt(blurController,
+                new ClampedBlurProperty(toBlurRadius, fromBlurRadius),
+                fromBlurRadius, toBlurRadius);
+        anim.play(backgroundBlur);
+
         playScaleDownAnim(anim, activity, fromState, endState);
 
+
         anim.setDuration(transitionLength * 2);
         anim.setInterpolator(LINEAR);
         AnimatorPlaybackController controller =
@@ -542,4 +555,14 @@
         PredictionUiStateManager.INSTANCE.get(launcher).switchClient(
                 PredictionUiStateManager.Client.OVERVIEW);
     }
+
+    @Nullable
+    @Override
+    public BackgroundBlurController getBackgroundBlurController() {
+        BaseQuickstepLauncher launcher = getCreatedActivity();
+        if (launcher == null) {
+            return null;
+        }
+        return launcher.getBackgroundBlurController();
+    }
 }
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
index 3807e45..4b6241c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
@@ -294,6 +294,7 @@
         }
 
         setupRecentsViewUi();
+        mActivityInterface.getBackgroundBlurController().setSurfaceToLauncher(mRecentsView);
 
         if (mDeviceState.getNavMode() == TWO_BUTTONS) {
             // If the device is in two button mode, swiping up will show overview with predictions
@@ -1015,7 +1016,9 @@
             HomeAnimationFactory homeAnimationFactory) {
         RectFSpringAnim anim =
                 super.createWindowAnimationToHome(startProgress, homeAnimationFactory);
-        anim.addOnUpdateListener((r, p) -> updateSysUiFlags(Math.max(p, mCurrentShift.value)));
+        anim.addOnUpdateListener((r, p) -> {
+            updateSysUiFlags(Math.max(p, mCurrentShift.value));
+        });
         anim.addAnimatorListener(new AnimationSuccessListener() {
             @Override
             public void onAnimationStart(Animator animation) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
index 94b0051..f2e8f96 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
@@ -185,8 +185,10 @@
         boolean activityClosing = taskIsATargetWithMode(appTargets, getTaskId(), MODE_CLOSING);
         AppWindowAnimationHelper helper = new AppWindowAnimationHelper(
             mFallbackRecentsView.getPagedViewOrientedState(), this);
-        target.play(getRecentsWindowAnimator(taskView, !activityClosing, appTargets,
-                wallpaperTargets, helper).setDuration(RECENTS_LAUNCH_DURATION));
+        Animator recentsAnimator = getRecentsWindowAnimator(taskView, !activityClosing, appTargets,
+                wallpaperTargets, null /* backgroundBlurController */,
+                helper);
+        target.play(recentsAnimator.setDuration(RECENTS_LAUNCH_DURATION));
 
         // Found a visible recents task that matches the opening app, lets launch the app from there
         if (activityClosing) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
index 8d73591..38b86ce 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
@@ -15,13 +15,17 @@
  */
 package com.android.quickstep;
 
+import static com.android.launcher3.LauncherState.BACKGROUND_APP;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.uioverrides.BackgroundBlurController.BACKGROUND_BLUR;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.content.ComponentName;
 import android.graphics.RectF;
@@ -31,6 +35,7 @@
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.uioverrides.BackgroundBlurController;
 import com.android.quickstep.util.AppWindowAnimationHelper;
 import com.android.quickstep.util.MultiValueUpdateListener;
 import com.android.quickstep.views.RecentsView;
@@ -115,9 +120,11 @@
      * @return Animator that controls the window of the opening targets for the recents launch
      * animation.
      */
-    public static ValueAnimator getRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
+    public static Animator getRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
             RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets, final AppWindowAnimationHelper inOutHelper) {
+            RemoteAnimationTargetCompat[] wallpaperTargets,
+            BackgroundBlurController backgroundBlurController,
+            final AppWindowAnimationHelper inOutHelper) {
         SyncRtSurfaceTransactionApplierCompat applier =
                 new SyncRtSurfaceTransactionApplierCompat(v);
         final RemoteAnimationTargets targets =
@@ -129,6 +136,7 @@
                     .setTargetSet(targets)
                     .setLauncherOnTop(true);
 
+        AnimatorSet animatorSet = new AnimatorSet();
         final RecentsView recentsView = v.getRecentsView();
         final ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
         appAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
@@ -137,8 +145,6 @@
             // Defer fading out the view until after the app window gets faded in
             final FloatProp mViewAlpha = new FloatProp(1f, 0f, 75, 75, LINEAR);
             final FloatProp mTaskAlpha = new FloatProp(0f, 1f, 0, 75, LINEAR);
-
-
             final RectF mThumbnailRect;
 
             {
@@ -208,6 +214,14 @@
                 targets.release();
             }
         });
-        return appAnimator;
+
+        if (backgroundBlurController != null) {
+            ObjectAnimator backgroundRadiusAnim = ObjectAnimator.ofInt(backgroundBlurController,
+                    BACKGROUND_BLUR, BACKGROUND_APP.getBackgroundBlurRadius(v.getContext()));
+            animatorSet.playTogether(appAnimator, backgroundRadiusAnim);
+        } else {
+            animatorSet.play(appAnimator);
+        }
+        return animatorSet;
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
index 91af156..681ce02 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
@@ -17,6 +17,7 @@
 
 import android.annotation.TargetApi;
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Matrix;
 import android.graphics.Matrix.ScaleToFit;
 import android.graphics.Rect;
@@ -104,9 +105,10 @@
     private TargetAlphaProvider mBaseAlphaCallback = (t, a) -> 1;
 
     public AppWindowAnimationHelper(PagedViewOrientedState orientedState, Context context) {
+        Resources res = context.getResources();
         mOrientedState = orientedState;
-        mWindowCornerRadius = getWindowCornerRadius(context.getResources());
-        mSupportsRoundedCornersOnWindows = supportsRoundedCornersOnWindows(context.getResources());
+        mWindowCornerRadius = getWindowCornerRadius(res);
+        mSupportsRoundedCornersOnWindows = supportsRoundedCornersOnWindows(res);
         mTaskCornerRadius = TaskCornerRadius.get(context);
         mUseRoundedCornersOnWindows = mSupportsRoundedCornersOnWindows;
     }
@@ -196,14 +198,15 @@
         SurfaceParams[] surfaceParams = new SurfaceParams[params.mTargetSet.unfilteredApps.length];
         for (int i = 0; i < params.mTargetSet.unfilteredApps.length; i++) {
             RemoteAnimationTargetCompat app = params.mTargetSet.unfilteredApps[i];
+            SurfaceParams.Builder builder = new SurfaceParams.Builder(app.leash);
             mTmpMatrix.setTranslate(app.position.x, app.position.y);
             Rect crop = mTmpRect;
             crop.set(app.sourceContainerBounds);
             crop.offsetTo(0, 0);
             float alpha;
-            int layer = RemoteAnimationProvider.getLayer(app, mBoostModeTargetLayers);
             float cornerRadius = 0f;
             float scale = Math.max(mCurrentRect.width(), mTargetRect.width()) / crop.width();
+            int layer = RemoteAnimationProvider.getLayer(app, mBoostModeTargetLayers);
             if (app.mode == params.mTargetSet.targetMode) {
                 alpha = mTaskAlphaCallback.getAlpha(app, params.mTargetAlpha);
                 if (app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
@@ -239,10 +242,14 @@
                     layer = Integer.MAX_VALUE;
                 }
             }
-            // Since radius is in Surface space, but we draw the rounded corners in screen space, we
-            // have to undo the scale.
-            surfaceParams[i] = new SurfaceParams(app.leash, alpha, mTmpMatrix, crop, layer,
-                    cornerRadius / scale);
+            builder.withAlpha(alpha)
+                    .withMatrix(mTmpMatrix)
+                    .withWindowCrop(crop)
+                    .withLayer(layer)
+                    // Since radius is in Surface space, but we draw the rounded corners in screen
+                    // space, we have to undo the scale
+                    .withCornerRadius(cornerRadius / scale);
+            surfaceParams[i] = builder.build();
         }
         return surfaceParams;
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
index ab8b02f..f60da18 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -48,6 +48,7 @@
 import com.android.launcher3.appprediction.PredictionUiStateManager;
 import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
 import com.android.launcher3.states.RotationHelper;
+import com.android.launcher3.uioverrides.BackgroundBlurController;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.launcher3.util.TraceHelper;
 import com.android.launcher3.views.ScrimView;
@@ -405,4 +406,9 @@
             mRecentsExtraViewContainer.setAlpha(alpha);
         }
     }
+
+    @Override
+    protected BackgroundBlurController getBackgroundBlurController() {
+        return mActivity.getBackgroundBlurController();
+    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index 872e690..9a11f6e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -22,6 +22,7 @@
 import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
+import static com.android.launcher3.LauncherState.BACKGROUND_APP;
 import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
 import static com.android.launcher3.Utilities.squaredHypot;
 import static com.android.launcher3.Utilities.squaredTouchSlop;
@@ -32,6 +33,7 @@
 import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
+import static com.android.launcher3.uioverrides.BackgroundBlurController.BACKGROUND_BLUR;
 import static com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController.SUCCESS_TRANSITION_PROGRESS;
 import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
 import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.CLEAR_ALL_BUTTON;
@@ -103,6 +105,7 @@
 import com.android.launcher3.graphics.RotationMode;
 import com.android.launcher3.states.RotationHelper;
 import com.android.launcher3.touch.PagedOrientationHandler.CurveProperties;
+import com.android.launcher3.uioverrides.BackgroundBlurController;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -1767,8 +1770,16 @@
         appWindowAnimationHelper.fromTaskThumbnailView(tv.getThumbnail(), this);
         appWindowAnimationHelper.prepareAnimation(mActivity.getDeviceProfile(), true /* isOpening */);
         AnimatorSet anim = createAdjacentPageAnimForTaskLaunch(tv, appWindowAnimationHelper);
+
+        BackgroundBlurController blurController = getBackgroundBlurController();
+        if (blurController != null) {
+            ObjectAnimator backgroundBlur = ObjectAnimator.ofInt(blurController, BACKGROUND_BLUR,
+                    BACKGROUND_APP.getBackgroundBlurRadius(mActivity));
+            anim.play(backgroundBlur);
+        }
         anim.play(progressAnim);
-        anim.setDuration(duration).setInterpolator(interpolator);
+        anim.setDuration(duration)
+                .setInterpolator(interpolator);
 
         Consumer<Boolean> onTaskLaunchFinish = this::onTaskLaunched;
 
@@ -2060,6 +2071,11 @@
         super.removeView(view);
     }
 
+    @Nullable
+    protected BackgroundBlurController getBackgroundBlurController() {
+        return null;
+    }
+
     private boolean isExtraCardView(View view, int index) {
         return !(view instanceof TaskView) && !(view instanceof ClearAllButton)
                 && index <= mTaskViewStartIndex;
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index fb0cd17..36624fb 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -234,6 +234,7 @@
         return new StateHandler[] {
                 getAllAppsController(),
                 getWorkspace(),
+                getBackgroundBlurController(),
                 new RecentsViewStateController(this),
                 new BackButtonAlphaHandler(this)};
     }
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index 2df490e..f691359 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -21,6 +21,7 @@
 import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
 import static com.android.launcher3.BaseActivity.PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
 import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.BACKGROUND_APP;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.Utilities.postAsyncCallback;
 import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
@@ -29,6 +30,7 @@
 import static com.android.launcher3.anim.Interpolators.EXAGGERATED_EASE;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_TRANSITIONS;
+import static com.android.launcher3.uioverrides.BackgroundBlurController.BACKGROUND_BLUR;
 import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
 import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
 import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
@@ -66,6 +68,7 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.shortcuts.DeepShortcutView;
+import com.android.launcher3.uioverrides.BackgroundBlurController;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.launcher3.views.FloatingIconView;
@@ -87,7 +90,7 @@
 
 /**
  * {@link LauncherAppTransitionManager} with Quickstep-specific app transitions for launching from
- * home and/or all-apps.
+ * home and/or all-apps.  Not used for 3p launchers.
  */
 @TargetApi(Build.VERSION_CODES.O)
 @SuppressWarnings("unused")
@@ -136,7 +139,7 @@
     // Progress = 0: All apps is fully pulled up, Progress = 1: All apps is fully pulled down.
     public static final float ALL_APPS_PROGRESS_OFF_SCREEN = 1.3059858f;
 
-    protected final Launcher mLauncher;
+    protected final BaseQuickstepLauncher mLauncher;
 
     private final DragLayer mDragLayer;
     private final AlphaProperty mDragLayerAlpha;
@@ -168,7 +171,7 @@
     };
 
     public QuickstepAppTransitionManagerImpl(Context context) {
-        mLauncher = Launcher.getLauncher(context);
+        mLauncher = Launcher.cast(Launcher.getLauncher(context));
         mDragLayer = mLauncher.getDragLayer();
         mDragLayerAlpha = mDragLayer.getAlphaProperty(ALPHA_INDEX_TRANSITIONS);
         mHandler = new Handler(Looper.getMainLooper());
@@ -402,9 +405,9 @@
             float[] alphas, float[] trans);
 
     /**
-     * @return Animator that controls the window of the opening targets.
+     * @return Animator that controls the window of the opening targets from app icons.
      */
-    private ValueAnimator getOpeningWindowAnimators(View v,
+    private Animator getOpeningWindowAnimators(View v,
             RemoteAnimationTargetCompat[] appTargets,
             RemoteAnimationTargetCompat[] wallpaperTargets,
             Rect windowTargetBounds, boolean toggleVisibility) {
@@ -458,6 +461,7 @@
         RectF currentBounds = new RectF();
         RectF temp = new RectF();
 
+        AnimatorSet animatorSet = new AnimatorSet();
         ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
         appAnimator.setDuration(APP_LAUNCH_DURATION);
         appAnimator.setInterpolator(LINEAR);
@@ -542,15 +546,10 @@
                 SurfaceParams[] params = new SurfaceParams[appTargets.length];
                 for (int i = appTargets.length - 1; i >= 0; i--) {
                     RemoteAnimationTargetCompat target = appTargets[i];
-                    Rect targetCrop;
-                    final float alpha;
-                    final float cornerRadius;
+                    SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
                     if (target.mode == MODE_OPENING) {
                         matrix.setScale(scale, scale);
                         matrix.postTranslate(transX0, transY0);
-                        targetCrop = crop;
-                        alpha = 1f - mIconAlpha.value;
-                        cornerRadius = mWindowRadius.value;
                         matrix.mapRect(currentBounds, targetBounds);
                         if (mDeviceProfile.isVerticalBarLayout()) {
                             currentBounds.right -= croppedWidth;
@@ -558,22 +557,44 @@
                             currentBounds.bottom -= croppedHeight;
                         }
                         floatingView.update(currentBounds, mIconAlpha.value, percent, 0f,
-                                cornerRadius * scale, true /* isOpening */);
+                                mWindowRadius.value * scale, true /* isOpening */);
+                        builder.withMatrix(matrix)
+                                .withWindowCrop(crop)
+                                .withAlpha(1f - mIconAlpha.value)
+                                .withCornerRadius(mWindowRadius.value);
                     } else {
                         matrix.setTranslate(target.position.x, target.position.y);
-                        targetCrop = target.sourceContainerBounds;
-                        alpha = 1f;
-                        cornerRadius = 0;
+                        builder.withMatrix(matrix)
+                                .withWindowCrop(target.sourceContainerBounds)
+                                .withAlpha(1f);
                     }
-
-                    params[i] = new SurfaceParams(target.leash, alpha, matrix, targetCrop,
-                            RemoteAnimationProvider.getLayer(target, MODE_OPENING),
-                            cornerRadius);
+                    builder.withLayer(RemoteAnimationProvider.getLayer(target, MODE_OPENING));
+                    params[i] = builder.build();
                 }
                 surfaceApplier.scheduleApply(params);
             }
         });
-        return appAnimator;
+
+        // When launching an app from overview that doesn't map to a task, we still want to just
+        // blur the wallpaper instead of the launcher surface as well
+        boolean allowBlurringLauncher = mLauncher.getStateManager().getState() != OVERVIEW;
+        BackgroundBlurController blurController = mLauncher.getBackgroundBlurController();
+        ObjectAnimator backgroundRadiusAnim = ObjectAnimator.ofInt(blurController, BACKGROUND_BLUR,
+                BACKGROUND_APP.getBackgroundBlurRadius(mLauncher))
+                .setDuration(APP_LAUNCH_DURATION);
+        if (allowBlurringLauncher) {
+            blurController.setSurfaceToApp(RemoteAnimationProvider.findLowestOpaqueLayerTarget(
+                    appTargets, MODE_OPENING));
+            backgroundRadiusAnim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    blurController.setSurfaceToLauncher(mLauncher.getDragLayer());
+                }
+            });
+        }
+
+        animatorSet.playTogether(appAnimator, backgroundRadiusAnim);
+        return animatorSet;
     }
 
     /**
@@ -639,9 +660,12 @@
                 SurfaceParams[] params = new SurfaceParams[appTargets.length];
                 for (int i = appTargets.length - 1; i >= 0; i--) {
                     RemoteAnimationTargetCompat target = appTargets[i];
-                    params[i] = new SurfaceParams(target.leash, 1f, null,
-                            target.sourceContainerBounds,
-                            RemoteAnimationProvider.getLayer(target, MODE_OPENING), cornerRadius);
+                    params[i] = new SurfaceParams.Builder(target.leash)
+                            .withAlpha(1f)
+                            .withWindowCrop(target.sourceContainerBounds)
+                            .withLayer(RemoteAnimationProvider.getLayer(target, MODE_OPENING))
+                            .withCornerRadius(cornerRadius)
+                            .build();
                 }
                 surfaceApplier.scheduleApply(params);
             }
@@ -672,25 +696,25 @@
                 SurfaceParams[] params = new SurfaceParams[appTargets.length];
                 for (int i = appTargets.length - 1; i >= 0; i--) {
                     RemoteAnimationTargetCompat target = appTargets[i];
-                    final float alpha;
-                    final float cornerRadius;
+                    SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
                     if (target.mode == MODE_CLOSING) {
                         matrix.setScale(mScale.value, mScale.value,
                                 target.sourceContainerBounds.centerX(),
                                 target.sourceContainerBounds.centerY());
                         matrix.postTranslate(0, mDy.value);
                         matrix.postTranslate(target.position.x, target.position.y);
-                        alpha = mAlpha.value;
-                        cornerRadius = windowCornerRadius;
+                        builder.withMatrix(matrix)
+                                .withAlpha(mAlpha.value)
+                                .withCornerRadius(windowCornerRadius);
                     } else {
                         matrix.setTranslate(target.position.x, target.position.y);
-                        alpha = 1f;
-                        cornerRadius = 0f;
+                        builder.withMatrix(matrix)
+                                .withAlpha(1f);
                     }
-                    params[i] = new SurfaceParams(target.leash, alpha, matrix,
-                            target.sourceContainerBounds,
-                            RemoteAnimationProvider.getLayer(target, MODE_CLOSING),
-                            cornerRadius);
+                    params[i] = builder
+                            .withWindowCrop(target.sourceContainerBounds)
+                            .withLayer(RemoteAnimationProvider.getLayer(target, MODE_CLOSING))
+                            .build();
                 }
                 surfaceApplier.scheduleApply(params);
             }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BackgroundBlurController.java b/quickstep/src/com/android/launcher3/uioverrides/BackgroundBlurController.java
new file mode 100644
index 0000000..9e4ada7
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/BackgroundBlurController.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2020 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.uioverrides;
+
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+
+import android.util.IntProperty;
+import android.view.View;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.anim.PropertySetter;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SurfaceControlCompat;
+import com.android.systemui.shared.system.TransactionCompat;
+
+/**
+ * Controls the blur, for the Launcher surface only.
+ */
+public class BackgroundBlurController implements LauncherStateManager.StateHandler {
+
+    public static final IntProperty<BackgroundBlurController> BACKGROUND_BLUR =
+            new IntProperty<BackgroundBlurController>("backgroundBlur") {
+                @Override
+                public void setValue(BackgroundBlurController blurController, int blurRadius) {
+                    blurController.setBackgroundBlurRadius(blurRadius);
+                }
+
+                @Override
+                public Integer get(BackgroundBlurController blurController) {
+                    return blurController.mBackgroundBlurRadius;
+                }
+            };
+
+    /**
+     * A property that updates the background blur within a given range of values (ie. even if the
+     * animator goes beyond 0..1, the interpolated value will still be bounded).
+     */
+    public static class ClampedBlurProperty extends IntProperty<BackgroundBlurController> {
+        private final int mMinValue;
+        private final int mMaxValue;
+
+        public ClampedBlurProperty(int minValue, int maxValue) {
+            super(("backgroundBlurClamped"));
+            mMinValue = minValue;
+            mMaxValue = maxValue;
+        }
+
+        @Override
+        public void setValue(BackgroundBlurController blurController, int blurRadius) {
+            blurController.setBackgroundBlurRadius(Utilities.boundToRange(blurRadius,
+                    mMinValue, mMaxValue));
+        }
+
+        @Override
+        public Integer get(BackgroundBlurController blurController) {
+            return blurController.mBackgroundBlurRadius;
+        }
+    }
+
+    private final Launcher mLauncher;
+    private SurfaceControlCompat mSurface;
+    private int mBackgroundBlurRadius;
+
+    public BackgroundBlurController(Launcher l) {
+        mLauncher = l;
+    }
+
+    /**
+     * @return the background blur adjustment for folders
+     */
+    public int getFolderBackgroundBlurAdjustment() {
+        return mLauncher.getResources().getInteger(
+                R.integer.folder_background_blur_radius_adjustment);
+    }
+
+    /**
+     * Sets the specified app target surface to apply the blur to.
+     */
+    public void setSurfaceToApp(RemoteAnimationTargetCompat target) {
+        if (target != null) {
+            setSurface(target.leash);
+        }
+    }
+
+    /**
+     * Sets the surface to apply the blur to as the launcher surface.
+     */
+    public void setSurfaceToLauncher(View v) {
+        setSurface(v != null ? new SurfaceControlCompat(v) : null);
+    }
+
+    private void setSurface(SurfaceControlCompat surface) {
+        if (mSurface != surface) {
+            mSurface = surface;
+            if (surface != null) {
+                setBackgroundBlurRadius(mBackgroundBlurRadius);
+            } else {
+                // If there is no surface, then reset the blur radius
+                setBackgroundBlurRadius(0);
+            }
+        }
+    }
+
+    @Override
+    public void setState(LauncherState toState) {
+        if (mSurface == null) {
+            return;
+        }
+
+        int toBackgroundBlurRadius = toState.getBackgroundBlurRadius(mLauncher);
+        if (mBackgroundBlurRadius != toBackgroundBlurRadius) {
+            setBackgroundBlurRadius(toBackgroundBlurRadius);
+        }
+    }
+
+    @Override
+    public void setStateWithAnimation(LauncherState toState, AnimatorSetBuilder builder,
+            LauncherStateManager.AnimationConfig config) {
+        if (mSurface == null || !config.playNonAtomicComponent()) {
+            return;
+        }
+
+        int toBackgroundBlurRadius = toState.getBackgroundBlurRadius(mLauncher);
+        if (mBackgroundBlurRadius != toBackgroundBlurRadius) {
+            PropertySetter propertySetter = config.getPropertySetter(builder);
+            propertySetter.setInt(this, BACKGROUND_BLUR, toBackgroundBlurRadius, LINEAR);
+        }
+    }
+
+    private void setBackgroundBlurRadius(int blurRadius) {
+        // TODO: Do nothing if the shadows are not enabled
+        // Always update the background blur as it will be reapplied when a surface is next
+        // available
+        mBackgroundBlurRadius = blurRadius;
+        if (mSurface == null || !mSurface.isValid()) {
+            return;
+        }
+        new TransactionCompat()
+                .setBackgroundBlurRadius(mSurface, blurRadius)
+                .apply();
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index b5e05ee..971d917 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -17,9 +17,12 @@
 
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
 
+import android.content.Context;
+
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.R;
 import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.quickstep.SysUINavigationMode;
@@ -85,6 +88,11 @@
     }
 
     @Override
+    public int getBackgroundBlurRadius(Context context) {
+        return context.getResources().getInteger(R.integer.allapps_background_blur_radius);
+    }
+
+    @Override
     public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
         return PAGE_ALPHA_PROVIDER;
     }
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 1d71fe2..be0bdd8 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -33,6 +33,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.uioverrides.BackgroundBlurController;
 import com.android.quickstep.util.ActivityInitListener;
 import com.android.quickstep.util.ShelfPeekAnim;
 import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -80,6 +81,10 @@
     @Nullable
     T getCreatedActivity();
 
+    default @Nullable BackgroundBlurController getBackgroundBlurController() {
+        return null;
+    }
+
     default boolean isResumed() {
         BaseDraggingActivity activity = getCreatedActivity();
         return activity != null && activity.hasBeenResumed();
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
index 6210fc2..6520c4f 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
@@ -68,4 +68,26 @@
                 ? Z_BOOST_BASE + target.prefixOrderIndex
                 : target.prefixOrderIndex;
     }
+
+    /**
+     * @return the target with the lowest opaque layer for a certain app animation, or null.
+     */
+    static RemoteAnimationTargetCompat findLowestOpaqueLayerTarget(
+            RemoteAnimationTargetCompat[] appTargets, int mode) {
+        int lowestLayer = Integer.MAX_VALUE;
+        int lowestLayerIndex = -1;
+        for (int i = appTargets.length - 1; i >= 0; i--) {
+            RemoteAnimationTargetCompat target = appTargets[i];
+            if (target.mode == mode && !target.isTranslucent) {
+                int layer = getLayer(target, mode);
+                if (layer < lowestLayer) {
+                    lowestLayer = layer;
+                    lowestLayerIndex = i;
+                }
+            }
+        }
+        return lowestLayerIndex != -1
+                ? appTargets[lowestLayerIndex]
+                : null;
+    }
 }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 20ebc7a..c2c0936 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -78,6 +78,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.OvershootInterpolator;
 import android.widget.Toast;
@@ -125,6 +126,7 @@
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.AllAppsSwipeController;
 import com.android.launcher3.touch.ItemClickHandler;
+import com.android.launcher3.uioverrides.BackgroundBlurController;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
@@ -328,6 +330,9 @@
     private boolean mDeferOverlayCallbacks;
     private final Runnable mDeferredOverlayCallbacks = this::checkIfOverlayStillDeferred;
 
+    private BackgroundBlurController mBackgroundBlurController =
+            new BackgroundBlurController(this);
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         Object traceToken = TraceHelper.INSTANCE.beginSection(ON_CREATE_EVT,
@@ -941,6 +946,7 @@
 
         NotificationListener.removeNotificationsChangedListener();
         getStateManager().moveToRestState();
+        getBackgroundBlurController().setSurfaceToLauncher(null);
 
         // Workaround for b/78520668, explicitly trim memory once UI is hidden
         onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
@@ -968,6 +974,13 @@
         if (!mDeferOverlayCallbacks) {
             mOverlayManager.onActivityStarted(this);
         }
+        mDragLayer.getViewTreeObserver().addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
+            @Override
+            public void onDraw() {
+                getBackgroundBlurController().setSurfaceToLauncher(mDragLayer);
+                mDragLayer.post(() -> mDragLayer.getViewTreeObserver().removeOnDrawListener(this));
+            }
+        });
 
         mAppWidgetHost.setListenIfResumed(true);
         TraceHelper.INSTANCE.endSection(traceToken);
@@ -2710,7 +2723,8 @@
     }
 
     protected StateHandler[] createStateHandlers() {
-        return new StateHandler[] { getAllAppsController(), getWorkspace() };
+        return new StateHandler[] { getAllAppsController(), getWorkspace(),
+                getBackgroundBlurController() };
     }
 
     public TouchController[] createTouchControllers() {
@@ -2739,8 +2753,12 @@
         return Stream.of(APP_INFO, WIDGETS, INSTALL);
     }
 
+    public BackgroundBlurController getBackgroundBlurController() {
+        return mBackgroundBlurController;
+    }
+
     public static Launcher getLauncher(Context context) {
-        return (Launcher) fromContext(context);
+        return fromContext(context);
     }
 
     /**
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 36440c9..62b8927 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -41,6 +41,7 @@
 import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.SPRING_LOADED_STATE_ORDINAL;
 
+import android.content.Context;
 import android.view.View;
 import android.view.animation.Interpolator;
 
@@ -269,6 +270,14 @@
         return 0;
     }
 
+    /**
+     * The amount of blur to apply to the background of either the app or Launcher surface in this
+     * state.
+     */
+    public int getBackgroundBlurRadius(Context context) {
+        return 0;
+    }
+
     public String getDescription(Launcher launcher) {
         return launcher.getWorkspace().getCurrentPageDescription();
     }
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 1310d37..f72e674 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -21,6 +21,7 @@
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
 import static com.android.launcher3.graphics.IconShape.getShape;
 import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
+import static com.android.launcher3.uioverrides.BackgroundBlurController.BACKGROUND_BLUR;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -46,6 +47,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.PropertyResetListener;
 import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.uioverrides.BackgroundBlurController;
 import com.android.launcher3.util.Themes;
 
 import java.util.List;
@@ -220,6 +222,13 @@
         Animator z = getAnimator(mFolder, View.TRANSLATION_Z, -mFolder.getElevation(), 0);
         play(a, z, mIsOpening ? midDuration : 0, midDuration);
 
+        BackgroundBlurController blurController = mLauncher.getBackgroundBlurController();
+        int stateBackgroundBlur = mLauncher.getStateManager().getState()
+                .getBackgroundBlurRadius(mLauncher);
+        int folderBackgroundBlurAdjustment = blurController.getFolderBackgroundBlurAdjustment();
+        play(a, ObjectAnimator.ofInt(blurController, BACKGROUND_BLUR, mIsOpening
+                ? stateBackgroundBlur + folderBackgroundBlurAdjustment
+                : stateBackgroundBlur));
 
         // Store clip variables
         CellLayout cellLayout = mContent.getCurrentCellLayout();
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/BackgroundBlurController.java b/src_ui_overrides/com/android/launcher3/uioverrides/BackgroundBlurController.java
new file mode 100644
index 0000000..232bad3
--- /dev/null
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/BackgroundBlurController.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 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.uioverrides;
+
+
+import android.util.IntProperty;
+import android.view.View;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager;
+import com.android.launcher3.anim.AnimatorSetBuilder;
+
+/**
+ * Controls the blur, for the Launcher surface only.
+ */
+public class BackgroundBlurController implements LauncherStateManager.StateHandler {
+
+    public static final IntProperty<BackgroundBlurController> BACKGROUND_BLUR =
+            new IntProperty<BackgroundBlurController>("backgroundBlur") {
+                @Override
+                public void setValue(BackgroundBlurController blurController, int blurRadius) {}
+
+                @Override
+                public Integer get(BackgroundBlurController blurController) {
+                    return 0;
+                }
+            };
+
+    public BackgroundBlurController(Launcher l) {}
+
+    public int getFolderBackgroundBlurAdjustment() {
+        return 0;
+    }
+
+    public void setSurfaceToLauncher(View v) {}
+
+    @Override
+    public void setState(LauncherState toState) {}
+
+    @Override
+    public void setStateWithAnimation(LauncherState toState, AnimatorSetBuilder builder,
+            LauncherStateManager.AnimationConfig config) {}
+}