Merge changes from topic "am-3e098e52-a5e6-4050-ac05-724b2c56ff1d" into ub-launcher3-master

* changes:
  [automerger] Linking swipe animate to stateTransition to that it gets properly reset afterwards am: cc35bb88da
  Linking swipe animate to stateTransition to that it gets properly reset afterwards
diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar
index 8e2f2f1..19a9b17 100644
--- a/quickstep/libs/sysui_shared.jar
+++ b/quickstep/libs/sysui_shared.jar
Binary files differ
diff --git a/quickstep/res/layout/fallback_recents_activity.xml b/quickstep/res/layout/fallback_recents_activity.xml
index 22f8b55..7ecab32 100644
--- a/quickstep/res/layout/fallback_recents_activity.xml
+++ b/quickstep/res/layout/fallback_recents_activity.xml
@@ -20,13 +20,23 @@
     android:layout_height="match_parent"
     android:fitsSystemWindows="true">
 
-    <com.android.quickstep.fallback.FallbackRecentsView
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@+id/overview_panel"
+    <com.android.quickstep.views.RecentsViewContainer
+        android:id="@+id/overview_panel_container"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:clipChildren="false"
-        android:clipToPadding="false"
-        android:theme="@style/HomeScreenElementTheme" />
+    >
+        <include layout="@layout/overview_clear_all_button"/>
 
-</com.android.quickstep.fallback.RecentsRootView>
\ No newline at end of file
+        <com.android.quickstep.fallback.FallbackRecentsView
+            android:id="@id/overview_panel"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:clipChildren="false"
+            android:clipToPadding="false"
+            android:focusableInTouchMode="true"
+            android:theme="@style/HomeScreenElementTheme"
+        >
+
+        </com.android.quickstep.fallback.FallbackRecentsView>
+    </com.android.quickstep.views.RecentsViewContainer>
+</com.android.quickstep.fallback.RecentsRootView>
diff --git a/quickstep/res/layout/overview_clear_all_button.xml b/quickstep/res/layout/overview_clear_all_button.xml
new file mode 100644
index 0000000..79d8a61
--- /dev/null
+++ b/quickstep/res/layout/overview_clear_all_button.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<TextView
+    xmlns:launcher="http://schemas.android.com/apk/res-auto"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/clear_all_button"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_gravity="end|top"
+    android:fontFamily="sans-serif-medium"
+    android:text="@string/recents_clear_all"
+    android:textColor="?attr/workspaceTextColor"
+    android:background="?android:attr/selectableItemBackground"
+    launcher:layout_ignoreInsets="true"
+    android:textSize="14sp"
+/>
\ No newline at end of file
diff --git a/quickstep/res/layout/overview_panel.xml b/quickstep/res/layout/overview_panel.xml
index 89e0571..6102c38 100644
--- a/quickstep/res/layout/overview_panel.xml
+++ b/quickstep/res/layout/overview_panel.xml
@@ -14,14 +14,23 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.quickstep.views.LauncherRecentsView
+<com.android.quickstep.views.RecentsViewContainer
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:theme="@style/HomeScreenElementTheme"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:clipChildren="false"
-    android:clipToPadding="false"
     android:visibility="invisible"
-    android:focusableInTouchMode="true" >
+>
+    <include layout="@layout/overview_clear_all_button"/>
 
-</com.android.quickstep.views.LauncherRecentsView>
\ No newline at end of file
+    <com.android.quickstep.views.LauncherRecentsView
+        android:id="@id/overview_panel"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:clipChildren="false"
+        android:clipToPadding="false"
+        android:focusableInTouchMode="true"
+        android:theme="@style/HomeScreenElementTheme"
+    >
+
+    </com.android.quickstep.views.LauncherRecentsView>
+</com.android.quickstep.views.RecentsViewContainer>
\ No newline at end of file
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index c741913..0199cd9 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -45,4 +45,7 @@
     <!-- Copied from framework resource:
        docked_stack_divider_thickness - 2 * docked_stack_divider_insets -->
     <dimen name="multi_window_task_divider_size">10dp</dimen>
+
+    <!-- Width of the space behind the last task in Overview. In the center of it, there is "Clear all" button. -->
+    <dimen name="clear_all_container_width">168dp</dimen>
 </resources>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index bafa294..34cc0b7 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -35,4 +35,7 @@
 
     <!-- Content description for the recent apps's accessibility option that closes it. [CHAR LIMIT=NONE] -->
     <string name="accessibility_close_task">Close</string>
+
+    <!-- Recents: Title of a button that clears the task list, i.e. closes all tasks. [CHAR LIMIT=30] -->
+    <string name="recents_clear_all">Clear all</string>
 </resources>
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java
index 3622fc4..a7cf545 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java
@@ -12,6 +12,7 @@
 import com.android.launcher3.touch.AbstractStateChangeTouchController;
 import com.android.launcher3.touch.SwipeDetector;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
+import com.android.quickstep.RecentsModel;
 import com.android.quickstep.util.SysuiEventLogger;
 
 /**
@@ -19,6 +20,8 @@
  */
 public class LandscapeEdgeSwipeController extends AbstractStateChangeTouchController {
 
+    private static final String TAG = "LandscapeEdgeSwipeCtrl";
+
     public LandscapeEdgeSwipeController(Launcher l) {
         super(l, SwipeDetector.HORIZONTAL);
     }
@@ -69,6 +72,7 @@
     protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
         super.onSwipeInteractionCompleted(targetState, logAction);
         if (mFromState == NORMAL && targetState == OVERVIEW) {
+            RecentsModel.getInstance(mLauncher).onOverviewShown(true, TAG);
             SysuiEventLogger.writeDummyRecentsTransition(0);
         }
     }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
index 9f21a95..2e95c04 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
@@ -39,6 +39,7 @@
 import com.android.launcher3.touch.SwipeDetector;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.quickstep.RecentsModel;
 import com.android.quickstep.TouchInteractionService;
 import com.android.quickstep.util.SysuiEventLogger;
 import com.android.quickstep.views.RecentsView;
@@ -49,6 +50,8 @@
  */
 public class PortraitStatesTouchController extends AbstractStateChangeTouchController {
 
+    private static final String TAG = "PortraitStatesTouchCtrl";
+
     private static final float TOTAL_DISTANCE_MULTIPLIER = 3f;
     private static final float LINEAR_SCALE_LIMIT = 1 / TOTAL_DISTANCE_MULTIPLIER;
 
@@ -284,6 +287,7 @@
     protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
         super.onSwipeInteractionCompleted(targetState, logAction);
         if (mFromState == NORMAL && targetState == OVERVIEW) {
+            RecentsModel.getInstance(mLauncher).onOverviewShown(true, TAG);
             SysuiEventLogger.writeDummyRecentsTransition(0);
         }
     }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 124ec20..49d4931 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -20,7 +20,7 @@
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.quickstep.views.LauncherRecentsView.TRANSLATION_Y_FACTOR;
 import static com.android.quickstep.views.RecentsView.ADJACENT_SCALE;
-import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
+import static com.android.quickstep.views.RecentsViewContainer.CONTENT_ALPHA;
 
 import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
@@ -33,21 +33,24 @@
 import com.android.launcher3.anim.AnimatorSetBuilder;
 import com.android.launcher3.anim.PropertySetter;
 import com.android.quickstep.views.LauncherRecentsView;
+import com.android.quickstep.views.RecentsViewContainer;
 
 @TargetApi(Build.VERSION_CODES.O)
 public class RecentsViewStateController implements StateHandler {
 
     private final Launcher mLauncher;
     private final LauncherRecentsView mRecentsView;
+    private final RecentsViewContainer mRecentsViewContainer;
 
     public RecentsViewStateController(Launcher launcher) {
         mLauncher = launcher;
         mRecentsView = launcher.getOverviewPanel();
+        mRecentsViewContainer = launcher.getOverviewPanelContainer();
     }
 
     @Override
     public void setState(LauncherState state) {
-        mRecentsView.setContentAlpha(state.overviewUi ? 1 : 0);
+        mRecentsViewContainer.setContentAlpha(state.overviewUi ? 1 : 0);
         float[] scaleTranslationYFactor = state.getOverviewScaleAndTranslationYFactor(mLauncher);
         mRecentsView.setAdjacentScale(scaleTranslationYFactor[0]);
         mRecentsView.setTranslationYFactor(scaleTranslationYFactor[1]);
@@ -66,7 +69,7 @@
                 builder.getInterpolator(ANIM_OVERVIEW_TRANSLATION, LINEAR));
         setter.setFloat(mRecentsView, TRANSLATION_Y_FACTOR, scaleTranslationYFactor[1],
                 builder.getInterpolator(ANIM_OVERVIEW_TRANSLATION, LINEAR));
-        setter.setFloat(mRecentsView, CONTENT_ALPHA, toState.overviewUi ? 1 : 0,
+        setter.setFloat(mRecentsViewContainer, CONTENT_ALPHA, toState.overviewUi ? 1 : 0,
                 AGGRESSIVE_EASE_IN_OUT);
 
         if (!toState.overviewUi) {
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 4652f2d..70b0355 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -26,8 +26,10 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Looper;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.support.annotation.WorkerThread;
+import android.util.Log;
 import android.util.LruCache;
 import android.util.SparseArray;
 import android.view.accessibility.AccessibilityManager;
@@ -234,6 +236,19 @@
         mRecentsTaskLoader.onTrimMemory(level);
     }
 
+    public void onOverviewShown(boolean fromHome, String tag) {
+        if (mSystemUiProxy == null) {
+            return;
+        }
+        try {
+            mSystemUiProxy.onOverviewShown(fromHome);
+        } catch (RemoteException e) {
+            Log.w(tag,
+                    "Failed to notify SysUI of overview shown from " + (fromHome ? "home" : "app")
+                            + ": ", e);
+        }
+    }
+
     @WorkerThread
     public void preloadAssistData(int taskId, Bundle data) {
         mMainThreadExecutor.execute(() -> {
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 4f0187c..57f46b8 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -39,13 +39,12 @@
 import android.view.Choreographer;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
-import android.view.View;
 import android.view.ViewConfiguration;
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.R;
 import com.android.launcher3.util.TraceHelper;
+import com.android.launcher3.views.BaseDragLayer;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.ISystemUiProxy;
@@ -248,7 +247,7 @@
 
         private final ActivityControlHelper<T> mActivityHelper;
         private final T mActivity;
-        private final View mTarget;
+        private final BaseDragLayer mTarget;
         private final int[] mLocationOnScreen = new int[2];
         private final PointF mDownPos = new PointF();
         private final int mTouchSlop;
@@ -293,7 +292,6 @@
                     case ACTION_MOVE: {
                         float displacement = ev.getY() - mDownPos.y;
                         if (Math.abs(displacement) >= mTouchSlop) {
-                            mTrackingStarted = true;
                             mTarget.getLocationOnScreen(mLocationOnScreen);
 
                             // Send a down event only when mTouchSlop is crossed.
@@ -301,6 +299,7 @@
                             down.setAction(ACTION_DOWN);
                             sendEvent(down);
                             down.recycle();
+                            mTrackingStarted = true;
                         }
                     }
                 }
@@ -319,7 +318,10 @@
             int flags = ev.getEdgeFlags();
             ev.setEdgeFlags(flags | EDGE_NAV_BAR);
             ev.offsetLocation(-mLocationOnScreen[0], -mLocationOnScreen[1]);
-            mTarget.dispatchTouchEvent(ev);
+            if (!mTrackingStarted) {
+                mTarget.onInterceptTouchEvent(ev);
+            }
+            mTarget.onTouchEvent(ev);
             ev.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
             ev.setEdgeFlags(flags);
         }
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index d456367..0944a7a 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -18,7 +18,9 @@
 import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
 import static com.android.launcher3.Utilities.postAsyncCallback;
 import static com.android.launcher3.anim.Interpolators.ACCEL_2;
+import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
 import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_START_DURATION;
 import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
 import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
@@ -55,7 +57,6 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -129,10 +130,12 @@
             "STATE_QUICK_SCRUB_END"
     };
 
-    private static final long MAX_SWIPE_DURATION = 200;
+    private static final long MAX_SWIPE_DURATION = 350;
     private static final long MIN_SWIPE_DURATION = 80;
 
     private static final float MIN_PROGRESS_FOR_OVERVIEW = 0.5f;
+    private static final float SWIPE_DURATION_MULTIPLIER =
+            Math.min(1 / MIN_PROGRESS_FOR_OVERVIEW, 1 / (1 - MIN_PROGRESS_FOR_OVERVIEW));
 
     private final ClipAnimationHelper mClipAnimationHelper = new ClipAnimationHelper();
 
@@ -400,7 +403,7 @@
         setStateOnUiThread(STATE_QUICK_SCRUB_START);
 
         // Start the window animation without waiting for launcher.
-        animateToProgress(1f, QUICK_SCRUB_START_DURATION);
+        animateToProgress(1f, QUICK_SCRUB_START_DURATION, TOUCH_RESPONSE_INTERPOLATOR);
     }
 
     @WorkerThread
@@ -558,12 +561,15 @@
     public void onGestureEnded(float endVelocity) {
         Resources res = mContext.getResources();
         float flingThreshold = res.getDimension(R.dimen.quickstep_fling_threshold_velocity);
-        boolean isFling = Math.abs(endVelocity) > flingThreshold;
+        boolean isFling = mGestureStarted && Math.abs(endVelocity) > flingThreshold;
 
         long duration = MAX_SWIPE_DURATION;
         final float endShift;
         if (!isFling) {
-            endShift = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW ? 1 : 0;
+            endShift = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW && mGestureStarted ? 1 : 0;
+            long expectedDuration = Math.abs(Math.round((endShift - mCurrentShift.value)
+                    * MAX_SWIPE_DURATION * SWIPE_DURATION_MULTIPLIER));
+            duration = Math.min(MAX_SWIPE_DURATION, expectedDuration);
             mLogAction = Touch.SWIPE;
         } else {
             endShift = endVelocity < 0 ? 1 : 0;
@@ -573,13 +579,14 @@
 
                 // we want the page's snap velocity to approximately match the velocity at
                 // which the user flings, so we scale the duration by a value near to the
-                // derivative of the scroll interpolator at zero, ie. 5.
-                duration = 5 * Math.round(1000 * Math.abs(distanceToTravel / endVelocity));
+                // derivative of the scroll interpolator at zero, ie. 2.
+                long baseDuration = Math.round(1000 * Math.abs(distanceToTravel / endVelocity));
+                duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration);
             }
             mLogAction = Touch.FLING;
         }
 
-        animateToProgress(endShift, duration);
+        animateToProgress(endShift, duration, DEACCEL);
     }
 
     private void doLogGesture(boolean toLauncher) {
@@ -599,10 +606,10 @@
     }
 
     /** Animates to the given progress, where 0 is the current app and 1 is overview. */
-    private void animateToProgress(float progress, long duration) {
+    private void animateToProgress(float progress, long duration, Interpolator interpolator) {
         mIsGoingToHome = Float.compare(progress, 1) == 0;
         ObjectAnimator anim = mCurrentShift.animateToValue(progress).setDuration(duration);
-        anim.setInterpolator(Interpolators.SCROLL);
+        anim.setInterpolator(interpolator);
         anim.addListener(new AnimationSuccessListener() {
             @Override
             public void onAnimationSuccess(Animator animator) {
@@ -691,6 +698,7 @@
             // If we haven't posted the transition end runnable, run it now
             finishTransitionRunnable.run();
         }
+        RecentsModel.getInstance(mContext).onOverviewShown(false, TAG);
         doLogGesture(true /* toLauncher */);
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 597e333..29d999d 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -87,21 +87,10 @@
 public abstract class RecentsView<T extends BaseActivity>
         extends PagedView implements OnSharedPreferenceChangeListener, Insettable {
 
+    public static final boolean DEBUG_SHOW_CLEAR_ALL_BUTTON = false;
+
     private final Rect mTempRect = new Rect();
 
-    public static final FloatProperty<RecentsView> CONTENT_ALPHA =
-            new FloatProperty<RecentsView>("contentAlpha") {
-        @Override
-        public void setValue(RecentsView recentsView, float v) {
-            recentsView.setContentAlpha(v);
-        }
-
-        @Override
-        public Float get(RecentsView recentsView) {
-            return recentsView.mContentAlpha;
-        }
-    };
-
     public static final FloatProperty<RecentsView> ADJACENT_SCALE =
             new FloatProperty<RecentsView>("adjacentScale") {
         @Override
@@ -180,6 +169,8 @@
     // Keeps track of task views whose visual state should not be reset
     private ArraySet<TaskView> mIgnoreResetTaskViews = new ArraySet<>();
 
+    private RecentsViewContainer mContainerView;
+
     // Variables for empty state
     private final Drawable mEmptyIcon;
     private final CharSequence mEmptyMessage;
@@ -320,12 +311,18 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
-        super.onTouchEvent(ev);
+        if (DEBUG_SHOW_CLEAR_ALL_BUTTON && mTouchState == TOUCH_STATE_REST && mScroller.isFinished()
+                && getChildCount() != 0
+                && ev.getX() > getChildAt(getChildCount() - 1).getRight() - getScrollX()) {
+            // If nothing is in motion, allow events to the right of the last task to go to the
+            // Clear All button.
+            return false;
+        }
+
         if (ev.getAction() == MotionEvent.ACTION_UP && mShowEmptyMessage) {
             onAllTasksRemoved();
         }
-        // Do not let touch escape to siblings below this view.
-        return true;
+        return super.onTouchEvent(ev);
     }
 
     private void applyLoadPlan(RecentsTaskLoadPlan loadPlan) {
@@ -424,6 +421,10 @@
 
     protected abstract void getTaskSize(DeviceProfile dp, Rect outRect);
 
+    public void getTaskSize(Rect outRect) {
+        getTaskSize(mActivity.getDeviceProfile(), outRect);
+    }
+
     @Override
     protected boolean computeScrollHelper() {
         boolean scrolling = super.computeScrollHelper();
@@ -844,11 +845,11 @@
         snapToPageRelative(1);
     }
 
-    public void setContentAlpha(float alpha) {
-        if (mContentAlpha == alpha) {
-            return;
-        }
+    public float getContentAlpha() {
+        return mContentAlpha;
+    }
 
+    public void setContentAlpha(float alpha) {
         mContentAlpha = alpha;
         for (int i = getChildCount() - 1; i >= 0; i--) {
             TaskView child = getPageAt(i);
@@ -860,8 +861,6 @@
         int alphaInt = Math.round(alpha * 255);
         mEmptyMessagePaint.setAlpha(alphaInt);
         mEmptyIcon.setAlpha(alphaInt);
-
-        setVisibility(alpha > 0 ? VISIBLE : GONE);
     }
 
     public void setAdjacentScale(float adjacentScale) {
@@ -929,6 +928,9 @@
         mShowEmptyMessage = isEmpty;
         updateEmptyStateUi(hasSizeChanged);
         invalidate();
+        if (mContainerView != null) {
+            mContainerView.onEmptyStateChanged(!DEBUG_SHOW_CLEAR_ALL_BUTTON || mShowEmptyMessage);
+        }
     }
 
     @Override
@@ -1095,4 +1097,30 @@
     protected String getCurrentPageDescription() {
         return "";
     }
+
+    public void dismissAllTasks() {
+        for (int i = 0; i < getChildCount(); ++i) {
+            Task task = getPageAt(i).getTask();
+            if (task != null) {
+                ActivityManagerWrapper.getInstance().removeTask(task.key.id);
+            }
+        }
+        onAllTasksRemoved();
+    }
+
+    @Override
+    protected int computeMaxScrollX() {
+        if (!DEBUG_SHOW_CLEAR_ALL_BUTTON || getChildCount() == 0) {
+            return super.computeMaxScrollX();
+        }
+
+        // Allow a clear_all_container_width-sized gap after the last task.
+        return super.computeMaxScrollX() + (int) getResources().getDimension(
+                R.dimen.clear_all_container_width) - getPaddingEnd();
+    }
+
+    public void setContainerView(RecentsViewContainer containerView) {
+        mContainerView = containerView;
+        mContainerView.onEmptyStateChanged(!DEBUG_SHOW_CLEAR_ALL_BUTTON || mShowEmptyMessage);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
new file mode 100644
index 0000000..ece78c1
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
@@ -0,0 +1,81 @@
+package com.android.quickstep.views;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.android.launcher3.InsettableFrameLayout;
+import com.android.launcher3.R;
+
+public class RecentsViewContainer extends InsettableFrameLayout {
+    public static final FloatProperty<RecentsViewContainer> CONTENT_ALPHA =
+            new FloatProperty<RecentsViewContainer>("contentAlpha") {
+                @Override
+                public void setValue(RecentsViewContainer view, float v) {
+                    view.setContentAlpha(v);
+                }
+
+                @Override
+                public Float get(RecentsViewContainer view) {
+                    return view.mRecentsView.getContentAlpha();
+                }
+            };
+
+    private final Rect mTempRect = new Rect();
+
+    private RecentsView mRecentsView;
+    private View mClearAllButton;
+
+    public RecentsViewContainer(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mClearAllButton = findViewById(R.id.clear_all_button);
+        mClearAllButton.setOnClickListener((v) -> {
+            mRecentsView.dismissAllTasks();
+        });
+
+        mRecentsView = (RecentsView) findViewById(R.id.overview_panel);
+        mRecentsView.setContainerView(this);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        mRecentsView.getTaskSize(mTempRect);
+
+        mClearAllButton.setTranslationX(
+                (mClearAllButton.getMeasuredWidth() - getResources().getDimension(
+                        R.dimen.clear_all_container_width)) / 2);
+        mClearAllButton.setTranslationY(
+                mTempRect.top + (mTempRect.height() - mClearAllButton.getMeasuredHeight()) / 2);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        super.onTouchEvent(ev);
+        // Do not let touch escape to siblings below this view. This prevents scrolling of the
+        // workspace while in Recents.
+        return true;
+    }
+
+    public void setContentAlpha(float alpha) {
+        if (alpha == mRecentsView.getContentAlpha()) {
+            return;
+        }
+        mRecentsView.setContentAlpha(alpha);
+        setVisibility(alpha > 0 ? VISIBLE : GONE);
+    }
+
+    public void onEmptyStateChanged(boolean isEmpty) {
+        mClearAllButton.setVisibility(isEmpty ? GONE : VISIBLE);
+    }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index 6473d16..2f7199b 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -148,20 +148,18 @@
         }
         int width = getMeasuredWidth();
         int height = getMeasuredHeight();
-        if (mClipBottom > 0 && !mTask.isLocked) {
-            canvas.save();
-            canvas.clipRect(0, 0, width, mClipBottom);
 
-            canvas.drawRoundRect(0, 0, width, height, mCornerRadius, mCornerRadius, mPaint);
-            canvas.restore();
-            canvas.save();
-            canvas.clipRect(0, mClipBottom, width, height);
-            canvas.drawRoundRect(0, 0, width, height, mCornerRadius, mCornerRadius,
-                    mBackgroundPaint);
-            canvas.restore();
-        } else {
-            canvas.drawRoundRect(0, 0, width, height, mCornerRadius,
-                    mCornerRadius, mTask.isLocked ? mBackgroundPaint : mPaint);
+        // Always draw the background since the snapshots may be translucent
+        canvas.drawRoundRect(0, 0, width, height, mCornerRadius, mCornerRadius, mBackgroundPaint);
+        if (!mTask.isLocked) {
+            if (mClipBottom > 0) {
+                canvas.save();
+                canvas.clipRect(0, 0, width, mClipBottom);
+                canvas.drawRoundRect(0, 0, width, height, mCornerRadius, mCornerRadius, mPaint);
+                canvas.restore();
+            } else {
+                canvas.drawRoundRect(0, 0, width, height, mCornerRadius, mCornerRadius, mPaint);
+            }
         }
     }
 
diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml
index a4acf06..6556adf 100644
--- a/res/layout/launcher.xml
+++ b/res/layout/launcher.xml
@@ -40,7 +40,7 @@
             launcher:pageIndicator="@+id/page_indicator" />
 
         <include
-            android:id="@+id/overview_panel"
+            android:id="@+id/overview_panel_container"
             layout="@layout/overview_panel"
             android:visibility="gone" />
 
diff --git a/res/values/config.xml b/res/values/config.xml
index a40afe1..9d1bb41 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -147,6 +147,7 @@
     <item type="id" name="search_container_all_apps" />
 
 <!-- Recents -->
+    <item type="id" name="overview_panel"/>
     <integer name="config_recentsMaxThumbnailCacheSize">6</integer>
     <integer name="config_recentsMaxIconCacheSize">12</integer>
 
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index f197921..a41edc0 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -144,7 +144,7 @@
 
     @Override
     protected void onStop() {
-        mActivityFlags &= ~ACTIVITY_STATE_STARTED;
+        mActivityFlags &= ~ACTIVITY_STATE_STARTED & ~ACTIVITY_STATE_USER_ACTIVE;
         mForceInvisible = 0;
         super.onStop();
     }
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 41bfcb7..fb7c0ce 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -65,27 +65,6 @@
 
     private static final int[] STATE_PRESSED = new int[] {android.R.attr.state_pressed};
 
-    private final BaseDraggingActivity mActivity;
-    private Drawable mIcon;
-    private final boolean mCenterVertically;
-
-    private final CheckLongPressHelper mLongPressHelper;
-    private final StylusEventHelper mStylusEventHelper;
-    private final float mSlop;
-
-    private final boolean mLayoutHorizontal;
-    private final int mIconSize;
-    @ViewDebug.ExportedProperty(category = "launcher")
-    private int mTextColor;
-    private boolean mIsIconVisible = true;
-
-    private BadgeInfo mBadgeInfo;
-    private BadgeRenderer mBadgeRenderer;
-    private int mBadgeColor;
-    private float mBadgeScale;
-    private boolean mForceHideBadge;
-    private Point mTempSpaceForBadgeOffset = new Point();
-    private Rect mTempIconBounds = new Rect();
 
     private static final Property<BubbleTextView, Float> BADGE_SCALE_PROPERTY
             = new Property<BubbleTextView, Float>(Float.TYPE, "badgeScale") {
@@ -101,19 +80,45 @@
         }
     };
 
-    public static final Property<BubbleTextView, Integer> TEXT_ALPHA_PROPERTY
-            = new Property<BubbleTextView, Integer>(Integer.class, "textAlpha") {
+    public static final Property<BubbleTextView, Float> TEXT_ALPHA_PROPERTY
+            = new Property<BubbleTextView, Float>(Float.class, "textAlpha") {
         @Override
-        public Integer get(BubbleTextView bubbleTextView) {
-            return bubbleTextView.getTextAlpha();
+        public Float get(BubbleTextView bubbleTextView) {
+            return bubbleTextView.mTextAlpha;
         }
 
         @Override
-        public void set(BubbleTextView bubbleTextView, Integer alpha) {
+        public void set(BubbleTextView bubbleTextView, Float alpha) {
             bubbleTextView.setTextAlpha(alpha);
         }
     };
 
+    private final BaseDraggingActivity mActivity;
+    private Drawable mIcon;
+    private final boolean mCenterVertically;
+
+    private final CheckLongPressHelper mLongPressHelper;
+    private final StylusEventHelper mStylusEventHelper;
+    private final float mSlop;
+
+    private final boolean mLayoutHorizontal;
+    private final int mIconSize;
+
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private boolean mIsIconVisible = true;
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private int mTextColor;
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private float mTextAlpha = 1;
+
+    private BadgeInfo mBadgeInfo;
+    private BadgeRenderer mBadgeRenderer;
+    private int mBadgeColor;
+    private float mBadgeScale;
+    private boolean mForceHideBadge;
+    private Point mTempSpaceForBadgeOffset = new Point();
+    private Rect mTempIconBounds = new Rect();
+
     @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mStayPressed;
     @ViewDebug.ExportedProperty(category = "launcher")
@@ -166,7 +171,7 @@
 
         setEllipsize(TruncateAt.END);
         setAccessibilityDelegate(mActivity.getAccessibilityDelegate());
-
+        setTextAlpha(1f);
     }
 
     @Override
@@ -404,13 +409,17 @@
     @Override
     public void setTextColor(int color) {
         mTextColor = color;
-        super.setTextColor(color);
+        super.setTextColor(getModifiedColor());
     }
 
     @Override
     public void setTextColor(ColorStateList colors) {
         mTextColor = colors.getDefaultColor();
-        super.setTextColor(colors);
+        if (Float.compare(mTextAlpha, 1) == 0) {
+            super.setTextColor(colors);
+        } else {
+            super.setTextColor(getModifiedColor());
+        }
     }
 
     public boolean shouldTextBeVisible() {
@@ -421,19 +430,21 @@
     }
 
     public void setTextVisibility(boolean visible) {
-        if (visible) {
-            super.setTextColor(mTextColor);
-        } else {
-            setTextAlpha(0);
+        setTextAlpha(visible ? 1 : 0);
+    }
+
+    private void setTextAlpha(float alpha) {
+        mTextAlpha = alpha;
+        super.setTextColor(getModifiedColor());
+    }
+
+    private int getModifiedColor() {
+        if (mTextAlpha == 0) {
+            // Special case to prevent text shadows in high contrast mode
+            return Color.TRANSPARENT;
         }
-    }
-
-    public void setTextAlpha(int alpha) {
-        super.setTextColor(ColorUtils.setAlphaComponent(mTextColor, alpha));
-    }
-
-    private int getTextAlpha() {
-        return Color.alpha(getCurrentTextColor());
+        return ColorUtils.setAlphaComponent(
+                mTextColor, Math.round(Color.alpha(mTextColor) * mTextAlpha));
     }
 
     /**
@@ -441,8 +452,8 @@
      * @param fadeIn Whether the text should fade in or fade out.
      */
     public ObjectAnimator createTextAlphaAnimator(boolean fadeIn) {
-        int toAlpha = shouldTextBeVisible() && fadeIn ? Color.alpha(mTextColor) : 0;
-        return ObjectAnimator.ofInt(this, TEXT_ALPHA_PROPERTY, toAlpha);
+        float toAlpha = shouldTextBeVisible() && fadeIn ? 1 : 0;
+        return ObjectAnimator.ofFloat(this, TEXT_ALPHA_PROPERTY, toAlpha);
     }
 
     @Override
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 8840860..ec0a8ff 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -18,6 +18,7 @@
 
 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
+
 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
@@ -205,6 +206,8 @@
     // UI and state for the overview panel
     private View mOverviewPanel;
 
+    private View mOverviewPanelContainer;
+
     @Thunk boolean mWorkspaceLoading = true;
 
     private OnResumeCallback mOnResumeCallback;
@@ -912,6 +915,7 @@
         mWorkspace = mDragLayer.findViewById(R.id.workspace);
         mWorkspace.initParentViews(mDragLayer);
         mOverviewPanel = findViewById(R.id.overview_panel);
+        mOverviewPanelContainer = findViewById(R.id.overview_panel_container);
         mHotseat = findViewById(R.id.hotseat);
         mDragHandleIndicator = findViewById(R.id.drag_indicator);
         mHotseatSearchBox = findViewById(R.id.search_container_hotseat);
@@ -1192,6 +1196,10 @@
         return (T) mOverviewPanel;
     }
 
+    public <T extends View> T getOverviewPanelContainer() {
+        return (T) mOverviewPanelContainer;
+    }
+
     public DropTargetBar getDropTargetBar() {
         return mDropTargetBar;
     }
diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompat.java b/src/com/android/launcher3/compat/WallpaperManagerCompat.java
index 00258c7..6605ace 100644
--- a/src/com/android/launcher3/compat/WallpaperManagerCompat.java
+++ b/src/com/android/launcher3/compat/WallpaperManagerCompat.java
@@ -31,7 +31,7 @@
             if (sInstance == null) {
                 context = context.getApplicationContext();
 
-                if (Utilities.ATLEAST_OREO) {
+                if (Utilities.ATLEAST_OREO_MR1) {
                     try {
                         sInstance = new WallpaperManagerCompatVOMR1(context);
                     } catch (Throwable e) {
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index ec448e9..9ae3775 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.folder;
 
+import static com.android.launcher3.BubbleTextView.TEXT_ALPHA_PROPERTY;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
 
@@ -172,9 +173,8 @@
         AnimatorSet a = LauncherAnimUtils.createAnimatorSet();
 
         // Initialize the Folder items' text.
-        PropertyResetListener colorResetListener = new PropertyResetListener<>(
-                BubbleTextView.TEXT_ALPHA_PROPERTY,
-                Color.alpha(Themes.getAttrColor(mContext, android.R.attr.textColorSecondary)));
+        PropertyResetListener colorResetListener =
+                new PropertyResetListener<>(TEXT_ALPHA_PROPERTY, 1f);
         for (BubbleTextView icon : mFolder.getItemsOnPage(mFolder.mContent.getCurrentPage())) {
             if (mIsOpening) {
                 icon.setTextVisibility(false);
diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java
index 117287b..b527b6a 100644
--- a/src/com/android/launcher3/notification/NotificationListener.java
+++ b/src/com/android/launcher3/notification/NotificationListener.java
@@ -150,22 +150,12 @@
     public void onCreate() {
         super.onCreate();
         sIsCreated = true;
-        mNotificationBadgingObserver = new SettingsObserver.Secure(getContentResolver()) {
-            @Override
-            public void onSettingChanged(boolean isNotificationBadgingEnabled) {
-                if (!isNotificationBadgingEnabled) {
-                    requestUnbind();
-                }
-            }
-        };
-        mNotificationBadgingObserver.register(NOTIFICATION_BADGING);
     }
 
     @Override
     public void onDestroy() {
         super.onDestroy();
         sIsCreated = false;
-        mNotificationBadgingObserver.unregister();
     }
 
     public static @Nullable NotificationListener getInstanceIfConnected() {
@@ -203,6 +193,17 @@
     public void onListenerConnected() {
         super.onListenerConnected();
         sIsConnected = true;
+
+        mNotificationBadgingObserver = new SettingsObserver.Secure(getContentResolver()) {
+            @Override
+            public void onSettingChanged(boolean isNotificationBadgingEnabled) {
+                if (!isNotificationBadgingEnabled) {
+                    requestUnbind();
+                }
+            }
+        };
+        mNotificationBadgingObserver.register(NOTIFICATION_BADGING);
+
         onNotificationFullRefresh();
     }
 
@@ -214,6 +215,7 @@
     public void onListenerDisconnected() {
         super.onListenerDisconnected();
         sIsConnected = false;
+        mNotificationBadgingObserver.unregister();
     }
 
     @Override