Merge "Removing SecondaryDisplayLauncher library as it is directly included in Launcher" into ub-launcher3-master
diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto
index d08d851..0fe5310 100644
--- a/protos/launcher_log.proto
+++ b/protos/launcher_log.proto
@@ -155,6 +155,7 @@
   OVERVIEW_ACTIONS_SCREENSHOT_BUTTON = 25;
   OVERVIEW_ACTIONS_SELECT_BUTTON = 26;
   SELECT_MODE_CLOSE_BUTTON = 27;
+  SELECT_MODE_ITEM = 28;
 }
 
 enum TipType {
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/appprediction/PredictionRowView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
index 834e6cf..8faec46 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -1,5 +1,5 @@
-/**
- * Copyright (C) 2019 The Android Open Source Project
+/*
+ * Copyright (C) 2012 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.
@@ -16,8 +16,11 @@
 
 package com.android.launcher3.appprediction;
 
+import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
+import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+import static com.android.launcher3.logging.LoggerUtils.newTarget;
 
 import android.annotation.TargetApi;
 import android.content.Context;
@@ -44,6 +47,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
 import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.allapps.AllAppsStore;
@@ -187,7 +191,7 @@
     public int getExpectedHeight() {
         return getVisibility() == GONE ? 0 :
                 Launcher.getLauncher(getContext()).getDeviceProfile().allAppsCellHeightPx
-                + getPaddingTop() + getPaddingBottom();
+                        + getPaddingTop() + getPaddingBottom();
     }
 
     @Override
@@ -282,7 +286,8 @@
         mParent.onHeightUpdated();
     }
 
-    private List<ItemInfoWithIcon> processPredictedAppComponents(List<ComponentKeyMapper> components) {
+    private List<ItemInfoWithIcon> processPredictedAppComponents(
+            List<ComponentKeyMapper> components) {
         if (getAppsStore().getApps().length == 0) {
             // Apps have not been bound yet.
             return Collections.emptyList();
@@ -309,16 +314,26 @@
     }
 
     @Override
-    public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
-            LauncherLogProto.Target targetParent) {
+    public void fillInLogContainerData(ItemInfo childInfo, LauncherLogProto.Target child,
+            ArrayList<LauncherLogProto.Target> parents) {
         for (int i = 0; i < mPredictedApps.size(); i++) {
             ItemInfoWithIcon appInfo = mPredictedApps.get(i);
-            if (appInfo == info) {
-                targetParent.containerType = LauncherLogProto.ContainerType.PREDICTION;
-                target.predictedRank = i;
+            if (appInfo == childInfo) {
+                child.predictedRank = i;
                 break;
             }
         }
+        parents.add(newContainerTarget(LauncherLogProto.ContainerType.PREDICTION));
+
+        // include where the prediction is coming this used to be Launcher#modifyUserEvent
+        LauncherLogProto.Target parent = newTarget(LauncherLogProto.Target.Type.CONTAINER);
+        LauncherState state = mLauncher.getStateManager().getState();
+        if (state == LauncherState.ALL_APPS) {
+            parent.containerType = LauncherLogProto.ContainerType.ALLAPPS;
+        } else if (state == OVERVIEW) {
+            parent.containerType = LauncherLogProto.ContainerType.TASKSWITCHER;
+        }
+        parents.add(parent);
     }
 
     public void setTextAlpha(int textAlpha) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index 0b05427..ff4bb74 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -575,9 +575,9 @@
     }
 
     @Override
-    public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
-            LauncherLogProto.Target targetParent) {
-        mHotseat.fillInLogContainerData(v, info, target, targetParent);
+    public void fillInLogContainerData(ItemInfo childInfo, LauncherLogProto.Target child,
+            ArrayList<LauncherLogProto.Target> parents) {
+        mHotseat.fillInLogContainerData(childInfo, child, parents);
     }
 
     private class PinPrediction extends SystemShortcut<QuickstepLauncher> {
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/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
index e9ce692..9f19bb3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -49,6 +49,7 @@
 import com.android.launcher3.model.PagedViewOrientedState;
 import com.android.launcher3.states.RotationHelper;
 import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.touch.PortraitPagedViewHandler;
 import com.android.launcher3.util.VibratorWrapper;
 import com.android.launcher3.views.FloatingIconView;
 import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
@@ -428,6 +429,9 @@
     }
 
     protected PagedOrientationHandler getOrientationHandler() {
+        if (mOrientedState == null) {
+            return new PortraitPagedViewHandler();
+        }
         return mOrientedState.getOrientationHandler();
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
index 65fba08..ce67457 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -41,6 +41,9 @@
 import com.android.launcher3.R;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.touch.LandscapePagedViewHandler;
+import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.touch.PortraitPagedViewHandler;
 import com.android.launcher3.util.ObjectWrapper;
 import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
 import com.android.quickstep.GestureState.GestureEndTarget;
@@ -470,8 +473,13 @@
         HomeAnimationFactory factory = new HomeAnimationFactory() {
             @Override
             public RectF getWindowTargetRect() {
+                PagedOrientationHandler orientationHandler = mRecentsView != null
+                        ? mRecentsView.getPagedOrientationHandler()
+                        : (mDp.isLandscape
+                                ? new LandscapePagedViewHandler()
+                                : new PortraitPagedViewHandler());
                 return HomeAnimationFactory
-                    .getDefaultWindowTargetRect(mRecentsView.getPagedOrientationHandler(), mDp);
+                    .getDefaultWindowTargetRect(orientationHandler, mDp);
             }
 
             @Override
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/TaskOverlayFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
index eb3b190..33d9d9a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
@@ -31,6 +31,7 @@
 import com.android.launcher3.util.ResourceBasedOverride;
 import com.android.quickstep.views.TaskThumbnailView;
 import com.android.quickstep.views.TaskView;
+import com.android.systemui.plugins.OverscrollPlugin;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 
@@ -67,6 +68,13 @@
     public static final MainThreadInitializedObject<TaskOverlayFactory> INSTANCE =
             forOverride(TaskOverlayFactory.class, R.string.task_overlay_factory_class);
 
+    /**
+     * @return a launcher-provided OverscrollPlugin if available, otherwise null
+     */
+    public OverscrollPlugin getLocalOverscrollPlugin() {
+        return null;
+    }
+
     public TaskOverlay createOverlay(TaskThumbnailView thumbnailView) {
         return new TaskOverlay();
     }
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/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index ffa17ce..4598cdf 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -508,12 +508,25 @@
                 base = new AssistantInputConsumer(this, newGestureState, base, mInputMonitorCompat);
             }
 
-            if (FeatureFlags.ENABLE_QUICK_CAPTURE_GESTURE.get()
-                    && (mOverscrollPlugin != null)
-                    && mOverscrollPlugin.isActive()) {
-                // Put the overscroll gesture as higher priority than the Assistant or base gestures
-                base = new OverscrollInputConsumer(this, newGestureState, base, mInputMonitorCompat,
-                        mOverscrollPlugin);
+            if (FeatureFlags.ENABLE_QUICK_CAPTURE_GESTURE.get()) {
+                OverscrollPlugin plugin = null;
+                if (FeatureFlags.FORCE_LOCAL_OVERSCROLL_PLUGIN.get()) {
+                    TaskOverlayFactory factory =
+                            TaskOverlayFactory.INSTANCE.get(getApplicationContext());
+                    plugin = factory.getLocalOverscrollPlugin();  // may be null
+                }
+
+                // If not local plugin was forced, use the actual overscroll plugin if available.
+                if (plugin == null && mOverscrollPlugin != null && mOverscrollPlugin.isActive()) {
+                    plugin = mOverscrollPlugin;
+                }
+
+                if (plugin != null) {
+                    // Put the overscroll gesture as higher priority than the Assistant or base
+                    // gestures
+                    base = new OverscrollInputConsumer(this, newGestureState, base,
+                        mInputMonitorCompat, plugin);
+                }
             }
 
             if (mDeviceState.isScreenPinningActive()) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/logging/UserEventDispatcherAppPredictionExtension.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/logging/UserEventDispatcherAppPredictionExtension.java
index b251f9e..b9ef57e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/logging/UserEventDispatcherAppPredictionExtension.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/logging/UserEventDispatcherAppPredictionExtension.java
@@ -23,6 +23,8 @@
 import com.android.launcher3.appprediction.PredictionUiStateManager;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 
+import java.util.ArrayList;
+
 /**
  * This class handles AOSP MetricsLogger function calls and logging around
  * quickstep interactions and app launches.
@@ -41,7 +43,7 @@
     @Override
     protected void onFillInLogContainerData(
             @NonNull ItemInfo itemInfo, @NonNull LauncherLogProto.Target target,
-            @NonNull LauncherLogProto.Target targetParent) {
+            @NonNull ArrayList<LauncherLogProto.Target> targets) {
         PredictionUiStateManager.fillInPredictedRank(itemInfo, target);
     }
 }
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 b2d182b..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
@@ -47,6 +47,8 @@
 import com.android.launcher3.anim.Interpolators;
 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;
@@ -86,6 +88,10 @@
         }
     };
 
+    private RotationHelper.ForcedRotationChangedListener mForcedRotationChangedListener =
+            isForcedRotation -> LauncherRecentsView.this
+                    .disableMultipleLayoutRotations(!isForcedRotation);
+
     public LauncherRecentsView(Context context) {
         this(context, null);
     }
@@ -337,6 +343,7 @@
         super.onAttachedToWindow();
         PluginManagerWrapper.INSTANCE.get(getContext()).addPluginListener(
                 mRecentsExtraCardPluginListener, RecentsExtraCard.class);
+        mActivity.getRotationHelper().addForcedRotationCallback(mForcedRotationChangedListener);
     }
 
     @Override
@@ -344,6 +351,7 @@
         super.onDetachedFromWindow();
         PluginManagerWrapper.INSTANCE.get(getContext()).removePluginListener(
                 mRecentsExtraCardPluginListener);
+        mActivity.getRotationHelper().removeForcedRotationCallback(mForcedRotationChangedListener);
     }
 
     @Override
@@ -398,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 9bc95d7..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;
@@ -280,9 +283,6 @@
         }
     };
 
-    private RotationHelper.ForcedRotationChangedListener mForcedRotationChangedListener =
-        isForcedRotation -> RecentsView.this.disableMultipleLayoutRotations(!isForcedRotation);
-
     private final PinnedStackAnimationListener mIPinnedStackAnimationListener =
             new PinnedStackAnimationListener();
 
@@ -482,8 +482,6 @@
         mIPinnedStackAnimationListener.setActivity(mActivity);
         SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(
                 mIPinnedStackAnimationListener);
-        Launcher launcher = Launcher.getLauncher(getContext());
-        launcher.getRotationHelper().addForcedRotationCallback(mForcedRotationChangedListener);
         addActionsView();
     }
 
@@ -499,8 +497,6 @@
         mIdp.removeOnChangeListener(this);
         SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(null);
         mIPinnedStackAnimationListener.setActivity(null);
-        Launcher launcher = Launcher.getLauncher(getContext());
-        launcher.getRotationHelper().removeForcedRotationCallback(mForcedRotationChangedListener);
     }
 
     @Override
@@ -1774,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;
 
@@ -2067,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/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 53859ad..22fe2e1 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -24,6 +24,8 @@
 import static android.stats.launcher.nano.Launcher.LAUNCH_TASK;
 import static android.stats.launcher.nano.Launcher.OVERVIEW;
 
+import static com.android.launcher3.logging.UserEventDispatcher.makeTargetsList;
+
 import android.content.Context;
 import android.content.Intent;
 import android.os.UserHandle;
@@ -47,6 +49,8 @@
 
 import com.google.protobuf.nano.MessageNano;
 
+import java.util.ArrayList;
+
 /**
  * This method calls the StatsLog hidden method until they are made available public.
  *
@@ -61,7 +65,8 @@
     private static final String TAG = "StatsLogCompatManager";
     private static final boolean DEBUG = false;
 
-    public StatsLogCompatManager(Context context) { }
+    public StatsLogCompatManager(Context context) {
+    }
 
     @Override
     public void logAppLaunch(View v, Intent intent, @Nullable UserHandle userHandle) {
@@ -120,14 +125,17 @@
 
             return false;
         }
-        ItemInfo itemInfo = (ItemInfo) v.getTag();
         Target child = new Target();
-        Target parent = new Target();
-        provider.fillInLogContainerData(v, itemInfo, child, parent);
-        extension.srcTarget[0] = new LauncherTarget();
-        extension.srcTarget[1] = new LauncherTarget();
-        copy(child, extension.srcTarget[0]);
-        copy(parent, extension.srcTarget[1]);
+        ArrayList<Target> targets = makeTargetsList(child);
+        targets.add(child);
+        provider.fillInLogContainerData((ItemInfo) v.getTag(), child, targets);
+
+        int maxDepth = Math.min(SUPPORTED_TARGET_DEPTH, targets.size());
+        extension.srcTarget = new LauncherTarget[maxDepth];
+        for (int i = 0; i < maxDepth; i++) {
+            extension.srcTarget[i] = new LauncherTarget();
+            copy(targets.get(i), extension.srcTarget[i]);
+        }
         return true;
     }
 
@@ -238,10 +246,10 @@
 
     @Override
     public void verify() {
-        if(!(StatsLogUtils.LAUNCHER_STATE_ALLAPPS == ALLAPPS &&
-                StatsLogUtils.LAUNCHER_STATE_BACKGROUND == BACKGROUND &&
-                StatsLogUtils.LAUNCHER_STATE_OVERVIEW == OVERVIEW &&
-                StatsLogUtils.LAUNCHER_STATE_HOME == HOME)) {
+        if (!(StatsLogUtils.LAUNCHER_STATE_ALLAPPS == ALLAPPS
+                && StatsLogUtils.LAUNCHER_STATE_BACKGROUND == BACKGROUND
+                && StatsLogUtils.LAUNCHER_STATE_OVERVIEW == OVERVIEW
+                && StatsLogUtils.LAUNCHER_STATE_HOME == HOME)) {
             throw new IllegalStateException(
                     "StatsLogUtil constants doesn't match enums in launcher.proto");
         }
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/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index cd94704..4ac815e 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -49,7 +49,6 @@
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
 
 /**
  * Test rule that allows executing a test with Quickstep on and then Quickstep off.
@@ -186,11 +185,18 @@
                     };
             targetContext.getMainExecutor().execute(() ->
                     SYS_UI_NAVIGATION_MODE.addModeChangeListener(listener));
-            latch.await(60, TimeUnit.SECONDS);
+            // b/139137636
+//            latch.await(60, TimeUnit.SECONDS);
             targetContext.getMainExecutor().execute(() ->
                     SYS_UI_NAVIGATION_MODE.removeModeChangeListener(listener));
-            assertTrue(launcher, "Navigation mode didn't change to " + expectedMode,
-                    currentSysUiNavigationMode() == expectedMode, description);
+
+            Wait.atMost(() -> "Navigation mode didn't change to " + expectedMode,
+                    () -> currentSysUiNavigationMode() == expectedMode, 60000 /* b/148422894 */,
+                    launcher);
+            // b/139137636
+//            assertTrue(launcher, "Navigation mode didn't change to " + expectedMode,
+//                    currentSysUiNavigationMode() == expectedMode, description);
+
         }
 
         Wait.atMost("Couldn't switch to " + overlayPackage,
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 217a41c..814b728 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -40,7 +40,6 @@
 import com.android.launcher3.logging.StatsLogUtils;
 import com.android.launcher3.logging.StatsLogUtils.LogStateProvider;
 import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.logging.UserEventDispatcher.UserEventDelegate;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -52,8 +51,10 @@
 import java.lang.annotation.Retention;
 import java.util.ArrayList;
 
-public abstract class BaseActivity extends Activity
-        implements UserEventDelegate, LogStateProvider, ActivityContext {
+/**
+ * Launcher BaseActivity
+ */
+public abstract class BaseActivity extends Activity implements LogStateProvider, ActivityContext {
 
     private static final String TAG = "BaseActivity";
 
@@ -155,7 +156,7 @@
 
     public final UserEventDispatcher getUserEventDispatcher() {
         if (mUserEventDispatcher == null) {
-            mUserEventDispatcher = UserEventDispatcher.newInstance(this, this);
+            mUserEventDispatcher = UserEventDispatcher.newInstance(this);
         }
         return mUserEventDispatcher;
     }
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index b89e727..76cfe1c 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -16,12 +16,13 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.MotionEvent;
-import android.view.View;
 import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
@@ -32,6 +33,8 @@
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.views.Transposable;
 
+import java.util.ArrayList;
+
 public class Hotseat extends CellLayout implements LogContainerProvider, Insettable, Transposable {
 
     @ViewDebug.ExportedProperty(category = "launcher")
@@ -75,10 +78,12 @@
     }
 
     @Override
-    public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
-        target.gridX = info.cellX;
-        target.gridY = info.cellY;
-        targetParent.containerType = LauncherLogProto.ContainerType.HOTSEAT;
+    public void fillInLogContainerData(ItemInfo childInfo, Target child,
+            ArrayList<Target> parents) {
+        child.rank = childInfo.rank;
+        child.gridX = childInfo.cellX;
+        child.gridY = childInfo.cellY;
+        parents.add(newContainerTarget(LauncherLogProto.ContainerType.HOTSEAT));
     }
 
     @Override
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 20ebc7a..9ff430d 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -30,7 +30,6 @@
 import static com.android.launcher3.Utilities.postAsyncCallback;
 import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_LAUNCHER_LOAD;
 import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-import static com.android.launcher3.logging.LoggerUtils.newTarget;
 import static com.android.launcher3.popup.SystemShortcut.APP_INFO;
 import static com.android.launcher3.popup.SystemShortcut.INSTALL;
 import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
@@ -78,6 +77,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;
@@ -110,7 +110,6 @@
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.logging.StatsLogUtils;
 import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.logging.UserEventDispatcher.UserEventDelegate;
 import com.android.launcher3.model.AppLaunchTracker;
 import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.model.ModelWriter;
@@ -125,8 +124,8 @@
 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;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
@@ -183,8 +182,7 @@
  * Default launcher application.
  */
 public class Launcher extends BaseDraggingActivity implements LauncherExterns,
-        Callbacks, UserEventDelegate,
-        InvariantDeviceProfile.OnIDPChangeListener, PluginListener<OverlayPlugin> {
+        Callbacks, InvariantDeviceProfile.OnIDPChangeListener, PluginListener<OverlayPlugin> {
     public static final String TAG = "Launcher";
 
     public static final ActivityTracker<Launcher> ACTIVITY_TRACKER = new ActivityTracker<>();
@@ -328,6 +326,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 +942,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 +970,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);
@@ -1890,24 +1899,6 @@
     }
 
     @Override
-    public void modifyUserEvent(LauncherLogProto.LauncherEvent event) {
-        if (event.srcTarget != null && event.srcTarget.length > 0 &&
-                event.srcTarget[1].containerType == ContainerType.PREDICTION) {
-            Target[] targets = new Target[3];
-            targets[0] = event.srcTarget[0];
-            targets[1] = event.srcTarget[1];
-            targets[2] = newTarget(Target.Type.CONTAINER);
-            event.srcTarget = targets;
-            LauncherState state = mStateManager.getState();
-            if (state == LauncherState.ALL_APPS) {
-                event.srcTarget[2].containerType = ContainerType.ALLAPPS;
-            } else if (state == OVERVIEW) {
-                event.srcTarget[2].containerType = ContainerType.TASKSWITCHER;
-            }
-        }
-    }
-
-    @Override
     public boolean startActivitySafely(View v, Intent intent, ItemInfo item,
             @Nullable String sourceContainer) {
         if (!hasBeenResumed()) {
@@ -2710,7 +2701,8 @@
     }
 
     protected StateHandler[] createStateHandlers() {
-        return new StateHandler[] { getAllAppsController(), getWorkspace() };
+        return new StateHandler[] { getAllAppsController(), getWorkspace(),
+                getBackgroundBlurController() };
     }
 
     public TouchController[] createTouchControllers() {
@@ -2739,8 +2731,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/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
index 1841134..2430d5e 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -41,6 +41,7 @@
 import com.android.launcher3.util.Themes;
 
 import java.net.URISyntaxException;
+import java.util.ArrayList;
 
 /**
  * Drop target which provides a secondary option for an item.
@@ -322,9 +323,9 @@
         }
 
         @Override
-        public void fillInLogContainerData(View v, ItemInfo info, Target target,
-                Target targetParent) {
-            mOriginal.fillInLogContainerData(v, info, target, targetParent);
+        public void fillInLogContainerData(ItemInfo childInfo, Target child,
+                ArrayList<Target> parents) {
+            mOriginal.fillInLogContainerData(childInfo, child, parents);
         }
 
         @Override
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 590c620..fc1a074 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -24,6 +24,7 @@
 import static com.android.launcher3.LauncherState.SPRING_LOADED;
 import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
 import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_OVERLAY;
+import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -3309,17 +3310,21 @@
     }
 
     @Override
-    public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
-        target.gridX = info.cellX;
-        target.gridY = info.cellY;
-        target.pageIndex = getCurrentPage();
-        targetParent.containerType = ContainerType.WORKSPACE;
-        if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
-            target.rank = info.rank;
-            targetParent.containerType = ContainerType.HOTSEAT;
-        } else if (info.container >= 0) {
-            targetParent.containerType = ContainerType.FOLDER;
+    public void fillInLogContainerData(ItemInfo childInfo, Target child,
+            ArrayList<Target> parents) {
+        if (childInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
+                || childInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
+            getHotseat().fillInLogContainerData(childInfo, child, parents);
+            return;
+        } else if (childInfo.container >= 0) {
+            FolderIcon icon = (FolderIcon) getHomescreenIconByItemId(childInfo.container);
+            icon.getFolder().fillInLogContainerData(childInfo, child, parents);
+            return;
         }
+        child.gridX = childInfo.cellX;
+        child.gridY = childInfo.cellY;
+        child.pageIndex = getCurrentPage();
+        parents.add(newContainerTarget(ContainerType.WORKSPACE));
     }
 
     /**
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index afb7217..a2957bc 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -18,6 +18,8 @@
 import static android.view.View.MeasureSpec.EXACTLY;
 import static android.view.View.MeasureSpec.makeMeasureSpec;
 
+import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Canvas;
@@ -65,6 +67,8 @@
 import com.android.launcher3.views.SpringRelativeLayout;
 import com.android.launcher3.views.WorkFooterContainer;
 
+import java.util.ArrayList;
+
 /**
  * The all apps view container.
  */
@@ -327,12 +331,10 @@
     }
 
     @Override
-    public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
-        if (getApps().hasFilter()) {
-            targetParent.containerType = ContainerType.SEARCHRESULT;
-        } else {
-            targetParent.containerType = ContainerType.ALLAPPS;
-        }
+    public void fillInLogContainerData(ItemInfo childInfo, Target child,
+            ArrayList<Target> parents) {
+        parents.add(newContainerTarget(
+                getApps().hasFilter() ? ContainerType.SEARCHRESULT : ContainerType.ALLAPPS));
     }
 
     @Override
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index b6744cf..c228ddf 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -17,6 +17,8 @@
 
 import static android.view.View.MeasureSpec.UNSPECIFIED;
 
+import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Canvas;
@@ -40,6 +42,7 @@
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.views.RecyclerViewFastScroller;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -145,12 +148,10 @@
     }
 
     @Override
-    public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
-        if (mApps.hasFilter()) {
-            targetParent.containerType = ContainerType.SEARCHRESULT;
-        } else {
-            targetParent.containerType = ContainerType.ALLAPPS;
-        }
+    public void fillInLogContainerData(ItemInfo childInfo, Target child,
+            ArrayList<Target> parents) {
+        parents.add(newContainerTarget(
+                getApps().hasFilter() ? ContainerType.SEARCHRESULT : ContainerType.ALLAPPS));
     }
 
     public void onSearchResultsChanged() {
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index aab6671..ed28df0 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -106,6 +106,10 @@
     public static final BooleanFlag ENABLE_QUICK_CAPTURE_GESTURE = getDebugFlag(
             "ENABLE_QUICK_CAPTURE_GESTURE", true, "Swipe from right to left to quick capture");
 
+    public static final BooleanFlag FORCE_LOCAL_OVERSCROLL_PLUGIN = getDebugFlag(
+            "FORCE_LOCAL_OVERSCROLL_PLUGIN", false,
+            "Use a launcher-provided OverscrollPlugin if available");
+
     public static final BooleanFlag ASSISTANT_GIVES_LAUNCHER_FOCUS = getDebugFlag(
             "ASSISTANT_GIVES_LAUNCHER_FOCUS", false,
             "Allow Launcher to handle nav bar gestures while Assistant is running over it");
diff --git a/src/com/android/launcher3/dragndrop/PinItemDragListener.java b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
index 869dd94..77c6306 100644
--- a/src/com/android/launcher3/dragndrop/PinItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.dragndrop;
 
+import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+
 import android.annotation.TargetApi;
 import android.appwidget.AppWidgetManager;
 import android.content.pm.LauncherApps.PinItemRequest;
@@ -38,6 +40,8 @@
 import com.android.launcher3.widget.PendingItemDragHelper;
 import com.android.launcher3.widget.WidgetAddFlowHandler;
 
+import java.util.ArrayList;
+
 /**
  * {@link DragSource} for handling drop from a different window. This object is initialized
  * in the source window and is passed on to the Launcher activity as an Intent extra.
@@ -103,9 +107,9 @@
     }
 
     @Override
-    public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
-            LauncherLogProto.Target targetParent) {
-        targetParent.containerType = LauncherLogProto.ContainerType.PINITEM;
+    public void fillInLogContainerData(ItemInfo childInfo, LauncherLogProto.Target child,
+            ArrayList<LauncherLogProto.Target> parents) {
+        parents.add(newContainerTarget(LauncherLogProto.ContainerType.PINITEM));
     }
 
     @Override
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 69f93de..2be8ff4 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -27,6 +27,7 @@
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
 import static com.android.launcher3.config.FeatureFlags.ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS;
+import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
 import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_CUSTOM;
 import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_EMPTY;
 import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_FOLDER_LABEL_STATE_UNSPECIFIED;
@@ -89,7 +90,6 @@
 import com.android.launcher3.dragndrop.DragController.DragListener;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.logging.LoggerUtils;
 import com.android.launcher3.pageindicators.PageIndicatorDots;
 import com.android.launcher3.userevent.LauncherLogProto.Action;
 import com.android.launcher3.userevent.LauncherLogProto.ContainerType;
@@ -1459,12 +1459,24 @@
     }
 
     @Override
-    public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
-            LauncherLogProto.Target targetParent) {
-        target.gridX = info.cellX;
-        target.gridY = info.cellY;
-        target.pageIndex = mContent.getCurrentPage();
-        targetParent.containerType = LauncherLogProto.ContainerType.FOLDER;
+    public void fillInLogContainerData(ItemInfo childInfo, LauncherLogProto.Target child,
+            ArrayList<LauncherLogProto.Target> targets) {
+        child.gridX = childInfo.cellX;
+        child.gridY = childInfo.cellY;
+        child.pageIndex = mContent.getCurrentPage();
+
+        LauncherLogProto.Target target = newContainerTarget(LauncherLogProto.ContainerType.FOLDER);
+        target.pageIndex = mInfo.screenId;
+        target.gridX = mInfo.cellX;
+        target.gridY = mInfo.cellY;
+        targets.add(target);
+
+        // continue to parent
+        if (mInfo.container == CONTAINER_HOTSEAT) {
+            mLauncher.getHotseat().fillInLogContainerData(mInfo, target, targets);
+        } else {
+            mLauncher.getWorkspace().fillInLogContainerData(mInfo, target, targets);
+        }
     }
 
     private class OnScrollHintListener implements OnAlarmListener {
@@ -1597,7 +1609,7 @@
                     }
                 } else {
                     mLauncher.getUserEventDispatcher().logActionTapOutside(
-                            LoggerUtils.newContainerTarget(LauncherLogProto.ContainerType.FOLDER));
+                            newContainerTarget(LauncherLogProto.ContainerType.FOLDER));
                     close(true);
                     return true;
                 }
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/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
index b004edf..a9d10d7 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -37,6 +37,7 @@
 
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
+import java.util.ArrayList;
 
 /**
  * Helper methods for logging.
@@ -255,4 +256,13 @@
         event.action = action;
         return event;
     }
+
+    /**
+     * Creates LauncherEvent using Action and ArrayList of Targets
+     */
+    public static LauncherEvent newLauncherEvent(Action action, ArrayList<Target> targets) {
+        Target[] targetsArray = new Target[targets.size()];
+        targets.toArray(targetsArray);
+        return newLauncherEvent(action, targetsArray);
+    }
 }
diff --git a/src/com/android/launcher3/logging/StatsLogUtils.java b/src/com/android/launcher3/logging/StatsLogUtils.java
index b02a050..8449612 100644
--- a/src/com/android/launcher3/logging/StatsLogUtils.java
+++ b/src/com/android/launcher3/logging/StatsLogUtils.java
@@ -5,11 +5,13 @@
 import android.view.View;
 import android.view.ViewParent;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 
-import androidx.annotation.Nullable;
+import java.util.ArrayList;
 
 
 public class StatsLogUtils {
@@ -35,14 +37,9 @@
     public interface LogContainerProvider {
 
         /**
-         * Copies data from the source to the destination proto.
-         *
-         * @param v            source of the data
-         * @param info         source of the data
-         * @param target       dest of the data
-         * @param targetParent dest of the data
+         * Populates parent container targets for an item
          */
-        void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent);
+        void fillInLogContainerData(ItemInfo childInfo, Target child, ArrayList<Target> parents);
     }
 
     /**
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index afa3f6d..513bf62 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -57,6 +57,7 @@
 import com.android.launcher3.util.LogConfig;
 import com.android.launcher3.util.ResourceBasedOverride;
 
+import java.util.ArrayList;
 import java.util.Locale;
 import java.util.UUID;
 
@@ -72,8 +73,10 @@
     private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.USEREVENT);
     private static final String UUID_STORAGE = "uuid";
 
-    public static UserEventDispatcher newInstance(Context context,
-            UserEventDelegate delegate) {
+    /**
+     * A factory method for UserEventDispatcher
+     */
+    public static UserEventDispatcher newInstance(Context context) {
         SharedPreferences sharedPrefs = Utilities.getDevicePrefs(context);
         String uuidStr = sharedPrefs.getString(UUID_STORAGE, null);
         if (uuidStr == null) {
@@ -82,41 +85,31 @@
         }
         UserEventDispatcher ued = Overrides.getObject(UserEventDispatcher.class,
                 context.getApplicationContext(), R.string.user_event_dispatcher_class);
-        ued.mDelegate = delegate;
         ued.mUuidStr = uuidStr;
         ued.mInstantAppResolver = InstantAppResolver.newInstance(context);
         return ued;
     }
 
-    public static UserEventDispatcher newInstance(Context context) {
-        return newInstance(context, null);
-    }
-
-    public interface UserEventDelegate {
-        void modifyUserEvent(LauncherEvent event);
-    }
 
     /**
      * Fills in the container data on the given event if the given view is not null.
      *
      * @return whether container data was added.
      */
-    public boolean fillInLogContainerData(LauncherLogProto.LauncherEvent event, @Nullable View v) {
-        // Fill in grid(x,y), pageIndex of the child and container type of the parent
-        LogContainerProvider provider = StatsLogUtils.getLaunchProviderRecursive(v);
-        if (v == null || !(v.getTag() instanceof ItemInfo) || provider == null) {
+    public boolean fillLogContainer(@Nullable View v, Target child,
+            @Nullable ArrayList<Target> targets) {
+        LogContainerProvider firstParent = StatsLogUtils.getLaunchProviderRecursive(v);
+        if (v == null || !(v.getTag() instanceof ItemInfo) || firstParent == null) {
             return false;
         }
         final ItemInfo itemInfo = (ItemInfo) v.getTag();
-        final Target target = event.srcTarget[0];
-        final Target targetParent = event.srcTarget[1];
-        onFillInLogContainerData(itemInfo, target, targetParent);
-        provider.fillInLogContainerData(v, itemInfo, target, targetParent);
+        firstParent.fillInLogContainerData(itemInfo, child, targets);
         return true;
     }
 
-    protected void onFillInLogContainerData(
-            @NonNull ItemInfo itemInfo, @NonNull Target target, @NonNull Target targetParent) { }
+    protected void onFillInLogContainerData(@NonNull ItemInfo itemInfo, @NonNull Target target,
+            @NonNull ArrayList<Target> targets) {
+    }
 
     private boolean mSessionStarted;
     private long mElapsedContainerMillis;
@@ -125,7 +118,6 @@
     private String mUuidStr;
     protected InstantAppResolver mInstantAppResolver;
     private boolean mAppOrTaskLaunch;
-    private UserEventDelegate mDelegate;
     private boolean mPreviousHomeGesture;
 
     //                      APP_ICON    SHORTCUT    WIDGET
@@ -136,16 +128,15 @@
     // --------------------------------------------------------------
 
     @Deprecated
-    public void logAppLaunch(View v, Intent intent, @Nullable  UserHandle userHandle) {
-        LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.TAP),
-                newItemTarget(v, mInstantAppResolver), newTarget(Target.Type.CONTAINER));
-
-        if (fillInLogContainerData(event, v)) {
-            if (mDelegate != null) {
-                mDelegate.modifyUserEvent(event);
-            }
-            fillIntentInfo(event.srcTarget[0], intent, userHandle);
+    public void logAppLaunch(View v, Intent intent, @Nullable UserHandle userHandle) {
+        Target itemTarget = newItemTarget(v, mInstantAppResolver);
+        Action action = newTouchAction(Action.Touch.TAP);
+        ArrayList<Target> targets = makeTargetsList(itemTarget);
+        if (fillLogContainer(v, itemTarget, targets)) {
+            onFillInLogContainerData((ItemInfo) v.getTag(), itemTarget, targets);
+            fillIntentInfo(itemTarget, intent, userHandle);
         }
+        LauncherEvent event = newLauncherEvent(action,  targets);
         ItemInfo info = (ItemInfo) v.getTag();
         if (info != null && Utilities.IS_DEBUG_DEVICE && FeatureFlags.ENABLE_HYBRID_HOTSEAT.get()) {
             FileLog.d(TAG, "appLaunch: packageName:" + info.getTargetComponent().getPackageName()
@@ -194,8 +185,11 @@
     public void logNotificationLaunch(View v, PendingIntent intent) {
         LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.TAP),
                 newItemTarget(v, mInstantAppResolver), newTarget(Target.Type.CONTAINER));
-        if (fillInLogContainerData(event, v)) {
-            event.srcTarget[0].packageNameHash = (mUuidStr + intent.getCreatorPackage()).hashCode();
+        Target itemTarget = newItemTarget(v, mInstantAppResolver);
+        ArrayList<Target> targets = makeTargetsList(itemTarget);
+
+        if (fillLogContainer(v, itemTarget, targets)) {
+            itemTarget.packageNameHash = (mUuidStr + intent.getCreatorPackage()).hashCode();
         }
         dispatchUserEvent(event, null);
     }
@@ -241,50 +235,45 @@
         LauncherEvent event = newLauncherEvent(newCommandAction(command),
                 newItemTarget(itemView, mInstantAppResolver), newTarget(Target.Type.CONTAINER));
 
-        if (fillInLogContainerData(event, itemView)) {
+        Target itemTarget = newItemTarget(itemView, mInstantAppResolver);
+        ArrayList<Target> targets = makeTargetsList(itemTarget);
+
+        if (fillLogContainer(itemView, itemTarget, targets)) {
             // TODO: Remove the following two lines once fillInLogContainerData can take in a
             // container view.
-            event.srcTarget[0].type = Target.Type.CONTAINER;
-            event.srcTarget[0].containerType = srcContainerType;
+            itemTarget.type = Target.Type.CONTAINER;
+            itemTarget.containerType = srcContainerType;
         }
         dispatchUserEvent(event, null);
     }
 
     public void logActionOnControl(int action, int controlType) {
-        logActionOnControl(action, controlType, null, -1);
+        logActionOnControl(action, controlType, null);
     }
 
     public void logActionOnControl(int action, int controlType, int parentContainerType) {
         logActionOnControl(action, controlType, null, parentContainerType);
     }
 
-    public void logActionOnControl(int action, int controlType, @Nullable View controlInContainer) {
-        logActionOnControl(action, controlType, controlInContainer, -1);
-    }
+    /**
+     * Logs control action with proper parent hierarchy
+     */
+    public void logActionOnControl(int actionType, int controlType,
+            @Nullable View controlInContainer, int... parentTypes) {
+        Target control = newTarget(Target.Type.CONTROL);
+        control.controlType = controlType;
+        Action action = newAction(actionType);
 
-    public void logActionOnControl(int action, int controlType, int parentContainer,
-            int grandParentContainer) {
-        LauncherEvent event = newLauncherEvent(newTouchAction(action),
-                newControlTarget(controlType),
-                newContainerTarget(parentContainer),
-                newContainerTarget(grandParentContainer));
-        dispatchUserEvent(event, null);
-    }
-
-    public void logActionOnControl(int action, int controlType, @Nullable View controlInContainer,
-            int parentContainerType) {
-        final LauncherEvent event = (controlInContainer == null && parentContainerType < 0)
-                ? newLauncherEvent(newTouchAction(action), newTarget(Target.Type.CONTROL))
-                : newLauncherEvent(newTouchAction(action), newTarget(Target.Type.CONTROL),
-                newTarget(Target.Type.CONTAINER));
-        event.srcTarget[0].controlType = controlType;
+        ArrayList<Target> targets = makeTargetsList(control);
         if (controlInContainer != null) {
-            fillInLogContainerData(event, controlInContainer);
+            fillLogContainer(controlInContainer, control, targets);
         }
-        if (parentContainerType >= 0) {
-            event.srcTarget[1].containerType = parentContainerType;
+        for (int parentContainerType : parentTypes) {
+            if (parentContainerType < 0) continue;
+            targets.add(newContainerTarget(parentContainerType));
         }
-        if (action == Action.Touch.DRAGDROP) {
+        LauncherEvent event = newLauncherEvent(action, targets);
+        if (actionType == Action.Touch.DRAGDROP) {
             event.actionDurationMillis = SystemClock.uptimeMillis() - mActionDurationMillis;
         }
         dispatchUserEvent(event, null);
@@ -375,15 +364,16 @@
      * Logs proto lite version of LauncherEvent object to clearcut.
      */
     public void logLauncherEvent(
-                com.android.launcher3.userevent.LauncherLogProto.LauncherEvent launcherEvent) {
+            com.android.launcher3.userevent.LauncherLogProto.LauncherEvent launcherEvent) {
 
         if (mPreviousHomeGesture) {
             mPreviousHomeGesture = false;
         }
         mAppOrTaskLaunch = false;
         launcherEvent.toBuilder()
-            .setElapsedContainerMillis(SystemClock.uptimeMillis() - mElapsedContainerMillis)
-            .setElapsedSessionMillis(SystemClock.uptimeMillis() - mElapsedSessionMillis).build();
+                .setElapsedContainerMillis(SystemClock.uptimeMillis() - mElapsedContainerMillis)
+                .setElapsedSessionMillis(
+                        SystemClock.uptimeMillis() - mElapsedSessionMillis).build();
         if (!IS_VERBOSE) {
             return;
         }
@@ -391,36 +381,35 @@
     }
 
     public void logDeepShortcutsOpen(View icon) {
-        LogContainerProvider provider = StatsLogUtils.getLaunchProviderRecursive(icon);
-        if (icon == null || !(icon.getTag() instanceof ItemInfo || provider == null)) {
-            return;
-        }
         ItemInfo info = (ItemInfo) icon.getTag();
-        LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.LONGPRESS),
-                newItemTarget(info, mInstantAppResolver), newTarget(Target.Type.CONTAINER));
-        provider.fillInLogContainerData(icon, info, event.srcTarget[0], event.srcTarget[1]);
-        dispatchUserEvent(event, null);
-
+        Target child = newItemTarget(info, mInstantAppResolver);
+        ArrayList<Target> targets = makeTargetsList(child);
+        fillLogContainer(icon, child, targets);
+        dispatchUserEvent(newLauncherEvent(newTouchAction(Action.Touch.TAP), targets), null);
         resetElapsedContainerMillis("deep shortcut open");
     }
 
     public void logDragNDrop(DropTarget.DragObject dragObj, View dropTargetAsView) {
-        LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.DRAGDROP),
-                newItemTarget(dragObj.originalDragInfo, mInstantAppResolver),
-                newTarget(Target.Type.CONTAINER));
-        event.destTarget = new Target[]{
-                newItemTarget(dragObj.originalDragInfo, mInstantAppResolver),
-                newDropTarget(dropTargetAsView)
-        };
+        Target srcChild = newItemTarget(dragObj.originalDragInfo, mInstantAppResolver);
+        ArrayList<Target> srcTargets = makeTargetsList(srcChild);
 
-        dragObj.dragSource.fillInLogContainerData(null, dragObj.originalDragInfo,
-                event.srcTarget[0], event.srcTarget[1]);
 
+        Target destChild = newItemTarget(dragObj.originalDragInfo, mInstantAppResolver);
+        ArrayList<Target> destTargets = makeTargetsList(destChild);
+
+        dragObj.dragSource.fillInLogContainerData(dragObj.originalDragInfo, srcChild, srcTargets);
         if (dropTargetAsView instanceof LogContainerProvider) {
-            ((LogContainerProvider) dropTargetAsView).fillInLogContainerData(null,
-                    dragObj.dragInfo, event.destTarget[0], event.destTarget[1]);
-
+            ((LogContainerProvider) dropTargetAsView).fillInLogContainerData(dragObj.dragInfo,
+                    destChild, destTargets);
         }
+        else {
+            destTargets.add(newDropTarget(dropTargetAsView));
+        }
+        LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.DRAGDROP), srcTargets);
+        Target[] destTargetsArray = new Target[destTargets.size()];
+        destTargets.toArray(destTargetsArray);
+        event.destTarget = destTargetsArray;
+
         event.actionDurationMillis = SystemClock.uptimeMillis() - mActionDurationMillis;
         dispatchUserEvent(event, null);
     }
@@ -444,8 +433,6 @@
 
     /**
      * Currently logs following containers: workspace, allapps, widget tray.
-     *
-     * @param reason
      */
     public final void resetElapsedContainerMillis(String reason) {
         mElapsedContainerMillis = SystemClock.uptimeMillis();
@@ -515,4 +502,15 @@
         }
         return result;
     }
+
+    /**
+     * Constructs an ArrayList with targets
+     */
+    public static ArrayList<Target> makeTargetsList(Target... targets) {
+        ArrayList<Target> result = new ArrayList<>();
+        for (Target target : targets) {
+            result.add(target);
+        }
+        return result;
+    }
 }
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 445acca..5af5ebb 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -18,6 +18,7 @@
 
 import static com.android.launcher3.Utilities.squaredHypot;
 import static com.android.launcher3.Utilities.squaredTouchSlop;
+import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
 import static com.android.launcher3.notification.NotificationMainView.NOTIFICATION_ITEM_INFO;
 import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS;
 import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS_IF_NOTIFICATIONS;
@@ -58,7 +59,6 @@
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.dragndrop.DragView;
-import com.android.launcher3.logging.LoggerUtils;
 import com.android.launcher3.notification.NotificationInfo;
 import com.android.launcher3.notification.NotificationItemView;
 import com.android.launcher3.notification.NotificationKeyData;
@@ -172,7 +172,7 @@
             BaseDragLayer dl = getPopupContainer();
             if (!dl.isEventOverView(this, ev)) {
                 mLauncher.getUserEventDispatcher().logActionTapOutside(
-                        LoggerUtils.newContainerTarget(ContainerType.DEEPSHORTCUTS));
+                        newContainerTarget(ContainerType.DEEPSHORTCUTS));
                 close(true);
 
                 // We let touches on the original icon go through so that users can launch
@@ -485,14 +485,15 @@
     }
 
     @Override
-    public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
-        if (info == NOTIFICATION_ITEM_INFO) {
-            target.itemType = ItemType.NOTIFICATION;
+    public void fillInLogContainerData(ItemInfo childInfo, Target child,
+            ArrayList<Target> parents) {
+        if (childInfo == NOTIFICATION_ITEM_INFO) {
+            child.itemType = ItemType.NOTIFICATION;
         } else {
-            target.itemType = ItemType.DEEPSHORTCUT;
-            target.rank = info.rank;
+            child.itemType = ItemType.DEEPSHORTCUT;
+            child.rank = childInfo.rank;
         }
-        targetParent.containerType = ContainerType.DEEPSHORTCUTS;
+        parents.add(newContainerTarget(ContainerType.DEEPSHORTCUTS));
     }
 
     @Override
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index df1a469..73a0615 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -41,6 +41,8 @@
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.AbstractSlideInView;
 
+import java.util.ArrayList;
+
 /**
  * Base class for various widgets popup
  */
@@ -144,9 +146,11 @@
     }
 
     @Override
-    public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
-        targetParent.containerType = ContainerType.WIDGETS;
-        targetParent.cardinality = getElementsRowCount();
+    public void fillInLogContainerData(ItemInfo childInfo, Target child,
+            ArrayList<Target> parents) {
+        Target target = newContainerTarget(ContainerType.WIDGETS);
+        target.cardinality = getElementsRowCount();
+        parents.add(target);
     }
 
     @Override
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) {}
+}
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index c6192bc..3d12248 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -34,6 +34,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.os.Debug;
 import android.os.Process;
@@ -70,6 +71,7 @@
 import com.android.launcher3.util.rule.TestStabilityRule;
 
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.rules.RuleChain;
@@ -124,7 +126,7 @@
                                                 getInstrumentation().getTargetContext()
                                                         .getFilesDir().getPath()
                                                         + "/ActivityLeakHeapDump.hprof");
-                                    } catch (IOException e) {
+                                    } catch (Throwable e) {
                                         Log.e(TAG, "dumpHprofData failed", e);
                                     }
                                 }
@@ -213,6 +215,21 @@
 
     @Before
     public void setUp() throws Exception {
+        final String launcherPackageName = mDevice.getLauncherPackageName();
+        try {
+            final Context context = InstrumentationRegistry.getContext();
+            final PackageManager pm = context.getPackageManager();
+            final PackageInfo launcherPackage = pm.getPackageInfo(launcherPackageName, 0);
+
+            if (!launcherPackage.versionName.equals("BuildFromAndroidStudio")) {
+                Assert.assertEquals("Launcher version doesn't match tests version",
+                        pm.getPackageInfo(context.getPackageName(), 0).getLongVersionCode(),
+                        launcherPackage.getLongVersionCode());
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+
         mLauncherPid = 0;
         // Disable app tracker
         AppLaunchTracker.INSTANCE.initializeForTesting(new AppLaunchTracker());
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index 4b1a067..de9757f 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -41,7 +41,6 @@
 import com.android.launcher3.util.rule.ShellCommandRule;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -71,14 +70,12 @@
 
     @Test
     @PortraitLandscape
-    @Ignore // b/148867106
     public void testWidgetConfig() throws Throwable {
         runTest(true);
     }
 
     @Test
     @PortraitLandscape
-    @Ignore // b/148867106
     public void testConfigCancelled() throws Throwable {
         runTest(false);
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 8659aa7..084138c 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -115,7 +115,8 @@
                 int visibleDelta = maxWidth - widget.getVisibleBounds().width();
                 if (visibleDelta > 0) {
                     Rect parentBounds = cell.getVisibleBounds();
-                    mLauncher.linearGesture(parentBounds.centerX() + visibleDelta,
+                    mLauncher.linearGesture(parentBounds.centerX() + visibleDelta
+                                    + mLauncher.getTouchSlop(),
                             parentBounds.centerY(), parentBounds.centerX(),
                             parentBounds.centerY(), 10, true, GestureScope.INSIDE);
                 }