Merge "Fix bug where add to folder fails even when folder creation animation runs." into ub-launcher3-edmonton-polish
diff --git a/quickstep/res/layout/fallback_recents_activity.xml b/quickstep/res/layout/fallback_recents_activity.xml
index 84e13ad..ef272ed 100644
--- a/quickstep/res/layout/fallback_recents_activity.xml
+++ b/quickstep/res/layout/fallback_recents_activity.xml
@@ -20,25 +20,12 @@
     android:layout_height="match_parent"
     android:fitsSystemWindows="true">
 
-    <com.android.quickstep.views.RecentsViewContainer
-        android:id="@+id/overview_panel_container"
+    <com.android.quickstep.fallback.FallbackRecentsView
+        android:id="@id/overview_panel"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:clipChildren="false"
-    >
-        <include layout="@layout/overview_clear_all_button"/>
-
-        <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:outlineProvider="none"
-            android:focusableInTouchMode="true"
-            android:theme="@style/HomeScreenElementTheme"
-        >
-
-        </com.android.quickstep.fallback.FallbackRecentsView>
-    </com.android.quickstep.views.RecentsViewContainer>
+        android:clipToPadding="false"
+        android:outlineProvider="none"
+        android:theme="@style/HomeScreenElementTheme" />
 </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
index 25615e0..ea7a494 100644
--- a/quickstep/res/layout/overview_clear_all_button.xml
+++ b/quickstep/res/layout/overview_clear_all_button.xml
@@ -1,15 +1,26 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2018 The Android Open Source Project
 
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
 <com.android.quickstep.views.ClearAllButton
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/clear_all_button"
     style="@android:style/Widget.DeviceDefault.Button.Borderless"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:layout_gravity="start|top"
     android:text="@string/recents_clear_all"
     android:textColor="?attr/workspaceTextColor"
-    android:visibility="invisible"
     android:textSize="14sp"
-    android:importantForAccessibility="no"
-/>
\ No newline at end of file
+    android:translationY="@dimen/task_thumbnail_half_top_margin"
+    />
\ No newline at end of file
diff --git a/quickstep/res/layout/overview_panel.xml b/quickstep/res/layout/overview_panel.xml
index 840b040..7f1425b 100644
--- a/quickstep/res/layout/overview_panel.xml
+++ b/quickstep/res/layout/overview_panel.xml
@@ -14,26 +14,12 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.quickstep.views.RecentsViewContainer
+<com.android.quickstep.views.LauncherRecentsView
     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:visibility="invisible"
->
-    <include layout="@layout/overview_clear_all_button"/>
-
-    <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:outlineProvider="none"
-        android:focusableInTouchMode="true"
-        android:accessibilityPaneTitle="@string/accessibility_recent_apps"
-        android:theme="@style/HomeScreenElementTheme"
-    >
-
-    </com.android.quickstep.views.LauncherRecentsView>
-</com.android.quickstep.views.RecentsViewContainer>
\ No newline at end of file
+    android:clipToPadding="false"
+    android:accessibilityPaneTitle="@string/accessibility_recent_apps"
+    android:visibility="invisible" />
\ No newline at end of file
diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml
index f163872..36d327d 100644
--- a/quickstep/res/layout/task.xml
+++ b/quickstep/res/layout/task.xml
@@ -17,8 +17,9 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:focusable="false"
-    android:elevation="4dp">
+    android:defaultFocusHighlightEnabled="false"
+    android:elevation="4dp"
+    android:focusable="true">
 
     <com.android.quickstep.views.TaskThumbnailView
         android:id="@+id/snapshot"
@@ -30,7 +31,7 @@
         android:id="@+id/icon"
         android:layout_width="@dimen/task_thumbnail_icon_size"
         android:layout_height="@dimen/task_thumbnail_icon_size"
-        android:importantForAccessibility="no"
+        android:layout_gravity="top|center_horizontal"
         android:focusable="false"
-        android:layout_gravity="top|center_horizontal" />
+        android:importantForAccessibility="no" />
 </com.android.quickstep.views.TaskView>
\ No newline at end of file
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index ea7d21b..afaad38 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -17,6 +17,7 @@
 <resources>
 
     <dimen name="task_thumbnail_top_margin">24dp</dimen>
+    <dimen name="task_thumbnail_half_top_margin">12dp</dimen>
     <dimen name="task_thumbnail_icon_size">48dp</dimen>
     <dimen name="task_corner_radius">2dp</dimen>
     <dimen name="recents_page_spacing">10dp</dimen>
@@ -50,9 +51,6 @@
        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>
-
     <dimen name="shelf_surface_radius">16dp</dimen>
     <!-- same as vertical_drag_handle_size -->
     <dimen name="shelf_surface_offset">24dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 72d6260..13530b2 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -92,8 +92,16 @@
         implements OnDeviceProfileChangeListener {
 
     private static final String TAG = "LauncherTransition";
+
+    /** Duration of status bar animations. */
     public static final int STATUS_BAR_TRANSITION_DURATION = 120;
 
+    /**
+     * Since our animations decelerate heavily when finishing, we want to start status bar animations
+     * x ms before the ending.
+     */
+    public static final int STATUS_BAR_TRANSITION_PRE_DELAY = 96;
+
     private static final String CONTROL_REMOTE_APP_TRANSITION_PERMISSION =
             "android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS";
 
@@ -210,9 +218,14 @@
                 }
             };
 
-            int duration = findTaskViewToLaunch(launcher, v, null) != null
-                    ? RECENTS_LAUNCH_DURATION : APP_LAUNCH_DURATION;
-            int statusBarTransitionDelay = duration - STATUS_BAR_TRANSITION_DURATION;
+            boolean fromRecents = mLauncher.getStateManager().getState().overviewUi
+                    && findTaskViewToLaunch(launcher, v, null) != null;
+            int duration = fromRecents
+                    ? RECENTS_LAUNCH_DURATION
+                    : APP_LAUNCH_DURATION;
+
+            int statusBarTransitionDelay = duration - STATUS_BAR_TRANSITION_DURATION
+                    - STATUS_BAR_TRANSITION_PRE_DELAY;
             return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat(
                     runner, duration, statusBarTransitionDelay));
         }
@@ -369,8 +382,9 @@
             launcherAnimator.play(ObjectAnimator.ofFloat(allAppsController, ALL_APPS_PROGRESS,
                     allAppsController.getProgress(), ALL_APPS_PROGRESS_OFF_SCREEN));
 
-            View overview = mLauncher.getOverviewPanelContainer();
-            ObjectAnimator alpha = ObjectAnimator.ofFloat(overview, View.ALPHA, alphas);
+            RecentsView overview = mLauncher.getOverviewPanel();
+            ObjectAnimator alpha = ObjectAnimator.ofFloat(overview,
+                    RecentsView.CONTENT_ALPHA, alphas);
             alpha.setDuration(217);
             alpha.setInterpolator(LINEAR);
             launcherAnimator.play(alpha);
@@ -380,14 +394,7 @@
             transY.setDuration(350);
             launcherAnimator.play(transY);
 
-            overview.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-
-            endListener = () -> {
-                overview.setLayerType(View.LAYER_TYPE_NONE, null);
-                overview.setAlpha(1f);
-                overview.setTranslationY(0f);
-                mLauncher.getStateManager().reapplyState();
-            };
+            endListener = mLauncher.getStateManager()::reapplyState;
         } else {
             mDragLayerAlpha.setValue(alphas[0]);
             ObjectAnimator alpha =
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
index 9169ffb..1e10319 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -25,6 +25,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.R;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.allapps.DiscoveryBounce;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -118,6 +119,11 @@
                 / launcher.getAllAppsController().getShiftRange());
     }
 
+    @Override
+    public String getDescription(Launcher launcher) {
+        return launcher.getString(R.string.accessibility_desc_recent_apps);
+    }
+
     public static float getDefaultSwipeHeight(Launcher launcher) {
         DeviceProfile dp = launcher.getDeviceProfile();
         return dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
index 717179d..512d19a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
@@ -139,7 +139,7 @@
         cancelPendingAnim();
 
         RecentsView recentsView = mLauncher.getOverviewPanel();
-        TaskView taskView = (TaskView) recentsView.getChildAt(recentsView.getNextPage());
+        TaskView taskView = recentsView.getTaskViewAt(recentsView.getNextPage());
         if (recentsView.shouldSwipeDownLaunchApp() && mFromState == OVERVIEW && mToState == NORMAL
                 && taskView != null) {
             // Reset the state manager, when changing the interaction mode
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index ea27eb2..abd2846 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -25,7 +25,7 @@
 import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_START_INTERPOLATOR;
 import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_TRANSLATION_Y_FACTOR;
 import static com.android.quickstep.views.LauncherRecentsView.TRANSLATION_Y_FACTOR;
-import static com.android.quickstep.views.RecentsViewContainer.CONTENT_ALPHA;
+import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
 
 import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
@@ -40,24 +40,21 @@
 import com.android.launcher3.anim.Interpolators;
 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) {
-        mRecentsViewContainer.setContentAlpha(state.overviewUi ? 1 : 0);
+        mRecentsView.setContentAlpha(state.overviewUi ? 1 : 0);
         float[] scaleTranslationYFactor = state.getOverviewScaleAndTranslationYFactor(mLauncher);
         SCALE_PROPERTY.set(mRecentsView, scaleTranslationYFactor[0]);
         mRecentsView.setTranslationYFactor(scaleTranslationYFactor[1]);
@@ -86,7 +83,7 @@
                 scaleAndTransYInterpolator);
         setter.setFloat(mRecentsView, TRANSLATION_Y_FACTOR, scaleTranslationYFactor[1],
                 scaleAndTransYInterpolator);
-        setter.setFloat(mRecentsViewContainer, CONTENT_ALPHA, toState.overviewUi ? 1 : 0,
+        setter.setFloat(mRecentsView, CONTENT_ALPHA, toState.overviewUi ? 1 : 0,
                 builder.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
 
         if (!toState.overviewUi) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
index a405735..cfd4119 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
@@ -115,8 +115,8 @@
             } else {
                 mTaskBeingDragged = null;
 
-                for (int i = 0; i < mRecentsView.getChildCount(); i++) {
-                    TaskView view = mRecentsView.getPageAt(i);
+                for (int i = 0; i < mRecentsView.getTaskViewCount(); i++) {
+                    TaskView view = mRecentsView.getTaskViewAt(i);
                     if (mRecentsView.isTaskViewVisible(view) && mActivity.getDragLayer()
                             .isEventOverView(view, ev)) {
                         mTaskBeingDragged = view;
@@ -241,16 +241,16 @@
         if (blockedFling) {
             fling = false;
         }
+        float progress = mCurrentAnimation.getProgressFraction();
+        float interpolatedProgress = mCurrentAnimation.getInterpolator().getInterpolation(progress);
         if (fling) {
             logAction = Touch.FLING;
             boolean goingUp = velocity < 0;
             goingToEnd = goingUp == mCurrentAnimationIsGoingUp;
         } else {
             logAction = Touch.SWIPE;
-            goingToEnd = mCurrentAnimation.getProgressFraction() > SUCCESS_TRANSITION_PROGRESS;
+            goingToEnd = interpolatedProgress > SUCCESS_TRANSITION_PROGRESS;
         }
-
-        float progress = mCurrentAnimation.getProgressFraction();
         long animationDuration = SwipeDetector.calculateDuration(
                 velocity, goingToEnd ? (1 - progress) : progress);
         if (blockedFling && !goingToEnd) {
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
index 8e4270e..275075f 100644
--- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java
+++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
@@ -25,6 +25,7 @@
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
 import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
+import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK;
 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_ROTATION;
 
@@ -67,7 +68,6 @@
 import com.android.quickstep.util.TransformedRect;
 import com.android.quickstep.views.LauncherLayoutListener;
 import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.RecentsViewContainer;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
@@ -185,7 +185,7 @@
             if (dp.isVerticalBarLayout()) {
                 Rect targetInsets = dp.getInsets();
                 int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
-                return dp.hotseatBarSizePx + dp.hotseatBarSidePaddingPx + hotseatInset;
+                return dp.hotseatBarSizePx + hotseatInset;
             } else {
                 int shelfHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
                 // Track slightly below the top of the shelf (between top and content).
@@ -293,7 +293,7 @@
          */
         private void playScaleDownAnim(AnimatorSet anim, Launcher launcher) {
             RecentsView recentsView = launcher.getOverviewPanel();
-            TaskView v = recentsView.getPageAt(recentsView.getCurrentPage());
+            TaskView v = recentsView.getTaskViewAt(recentsView.getCurrentPage());
             ClipAnimationHelper clipHelper = new ClipAnimationHelper();
             clipHelper.fromTaskThumbnailView(v.getThumbnail(), (RecentsView) v.getParent(), null);
             if (!clipHelper.getSourceRect().isEmpty() && !clipHelper.getTargetRect().isEmpty()) {
@@ -448,7 +448,7 @@
             if (dp.isVerticalBarLayout()) {
                 Rect targetInsets = dp.getInsets();
                 int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
-                return dp.hotseatBarSizePx + dp.hotseatBarSidePaddingPx + hotseatInset;
+                return dp.hotseatBarSizePx + hotseatInset;
             } else {
                 return dp.heightPx - outRect.rect.bottom;
             }
@@ -466,7 +466,7 @@
                 return (transitionLength, interactionType) -> { };
             }
 
-            RecentsViewContainer rv = activity.getOverviewPanelContainer();
+            RecentsView rv = activity.getOverviewPanel();
             rv.setContentAlpha(0);
 
             return new AnimationFactory() {
@@ -490,8 +490,7 @@
                         return;
                     }
 
-                    ObjectAnimator anim = ObjectAnimator
-                            .ofFloat(rv, RecentsViewContainer.CONTENT_ALPHA, 0, 1);
+                    ObjectAnimator anim = ObjectAnimator.ofFloat(rv, CONTENT_ALPHA, 0, 1);
                     anim.setDuration(transitionLength).setInterpolator(LINEAR);
                     AnimatorSet animatorSet = new AnimatorSet();
                     animatorSet.play(anim);
diff --git a/quickstep/src/com/android/quickstep/QuickScrubController.java b/quickstep/src/com/android/quickstep/QuickScrubController.java
index e0089c6..cbc7a67 100644
--- a/quickstep/src/com/android/quickstep/QuickScrubController.java
+++ b/quickstep/src/com/android/quickstep/QuickScrubController.java
@@ -98,7 +98,7 @@
         }
         int page = mRecentsView.getNextPage();
         Runnable launchTaskRunnable = () -> {
-            TaskView taskView = mRecentsView.getPageAt(page);
+            TaskView taskView = mRecentsView.getTaskViewAt(page);
             if (taskView != null) {
                 mWaitingForTaskLaunch = true;
                 taskView.launchTask(true, (result) -> {
@@ -222,7 +222,7 @@
 
     private void goToPageWithHaptic(int pageToGoTo, int overrideDuration, boolean forceHaptic,
             Interpolator interpolator) {
-        pageToGoTo = Utilities.boundToRange(pageToGoTo, 0, mRecentsView.getPageCount() - 1);
+        pageToGoTo = Utilities.boundToRange(pageToGoTo, 0, mRecentsView.getTaskViewCount() - 1);
         boolean snappingToPage = pageToGoTo != mRecentsView.getNextPage();
         if (snappingToPage) {
             int duration = overrideDuration > -1 ? overrideDuration
@@ -246,7 +246,7 @@
             return;
         }
         if (mQuickScrubSection == QUICK_SCRUB_THRESHOLDS.length
-                && currPage < mRecentsView.getPageCount() - 1) {
+                && currPage < mRecentsView.getTaskViewCount() - 1) {
             goToPageWithHaptic(currPage + 1);
         } else if (mQuickScrubSection == 0 && currPage > 0) {
             goToPageWithHaptic(currPage - 1);
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 3adb290..ed8b4d2 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -20,6 +20,7 @@
 
 import static com.android.launcher3.LauncherAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
 import static com.android.launcher3.LauncherAppTransitionManagerImpl.STATUS_BAR_TRANSITION_DURATION;
+import static com.android.launcher3.LauncherAppTransitionManagerImpl.STATUS_BAR_TRANSITION_PRE_DELAY;
 import static com.android.quickstep.TaskUtils.getRecentsWindowAnimator;
 import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
@@ -52,7 +53,6 @@
 import com.android.quickstep.fallback.FallbackRecentsView;
 import com.android.quickstep.fallback.RecentsRootView;
 import com.android.quickstep.util.ClipAnimationHelper;
-import com.android.quickstep.views.RecentsViewContainer;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.system.ActivityOptionsCompat;
 import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
@@ -70,7 +70,6 @@
     private Handler mUiHandler = new Handler(Looper.getMainLooper());
     private RecentsRootView mRecentsRootView;
     private FallbackRecentsView mFallbackRecentsView;
-    private RecentsViewContainer mOverviewPanelContainer;
 
     private Configuration mOldConfig;
 
@@ -84,7 +83,6 @@
         setContentView(R.layout.fallback_recents_activity);
         mRecentsRootView = findViewById(R.id.drag_layer);
         mFallbackRecentsView = findViewById(R.id.overview_panel);
-        mOverviewPanelContainer = findViewById(R.id.overview_panel_container);
 
         mRecentsRootView.setup();
 
@@ -166,10 +164,6 @@
         return (T) mFallbackRecentsView;
     }
 
-    public RecentsViewContainer getOverviewPanelContainer() {
-        return mOverviewPanelContainer;
-    }
-
     @Override
     public BadgeInfo getBadgeInfoForItem(ItemInfo info) {
         return null;
@@ -193,7 +187,8 @@
         };
         return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat(
                 runner, RECENTS_LAUNCH_DURATION,
-                RECENTS_LAUNCH_DURATION - STATUS_BAR_TRANSITION_DURATION));
+                RECENTS_LAUNCH_DURATION - STATUS_BAR_TRANSITION_DURATION
+                        - STATUS_BAR_TRANSITION_PRE_DELAY));
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
index f82ff8c..c77d0c7 100644
--- a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
+++ b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
@@ -152,8 +152,7 @@
                             }
                         };
 
-                AbstractFloatingView.closeOpenViews(activity, true,
-                        AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
+                dismissTaskMenuView(activity);
 
                 final int navBarPosition = WindowManagerWrapper.getInstance().getNavBarPosition();
                 if (navBarPosition == WindowManagerWrapper.NAV_BAR_POS_INVALID) {
@@ -246,6 +245,7 @@
                     }
                 };
                 taskView.launchTask(true, resultCallback, mHandler);
+                dismissTaskMenuView(activity);
             };
         }
     }
@@ -265,4 +265,9 @@
             return null;
         }
     }
+
+    private static void dismissTaskMenuView(BaseDraggingActivity activity) {
+        AbstractFloatingView.closeOpenViews(activity, true,
+                AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java
index c9ba7dc..f9b5e30 100644
--- a/quickstep/src/com/android/quickstep/TaskUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskUtils.java
@@ -104,8 +104,8 @@
             ComponentName componentName = itemInfo.getTargetComponent();
             int userId = itemInfo.user.getIdentifier();
             if (componentName != null) {
-                for (int i = 0; i < recentsView.getChildCount(); i++) {
-                    TaskView taskView = recentsView.getPageAt(i);
+                for (int i = 0; i < recentsView.getTaskViewCount(); i++) {
+                    TaskView taskView = recentsView.getTaskViewAt(i);
                     if (recentsView.isTaskViewVisible(taskView)) {
                         Task.TaskKey key = taskView.getTask().key;
                         if (componentName.equals(key.getComponent()) && userId == key.userId) {
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index c9f94bd..703ea2e 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -24,6 +24,7 @@
 import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_FROM_APP_START_DURATION;
 import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
 import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
+import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -560,8 +561,7 @@
                             ? mSyncTransactionApplier
                             : null);
 
-            // TODO: This logic is spartanic!
-            boolean passedThreshold = shift > 0.12f;
+            boolean passedThreshold = shift > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD;
             mRecentsAnimationWrapper.setAnimationTargetsBehindSystemBars(!passedThreshold);
             if (mActivityControlHelper.shouldMinimizeSplitScreen()) {
                 mRecentsAnimationWrapper.setSplitScreenMinimizedForTransaction(passedThreshold);
@@ -575,7 +575,7 @@
         final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
         if (passed != mPassedOverviewThreshold) {
             mPassedOverviewThreshold = passed;
-            if (mRecentsView != null) {
+            if (mInteractionType == INTERACTION_NORMAL && mRecentsView != null) {
                 mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
                     HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
             }
diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
index 0025df1..3911931 100644
--- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
@@ -17,34 +17,57 @@
 package com.android.quickstep.views;
 
 import android.content.Context;
-import android.graphics.Rect;
-import android.support.annotation.Nullable;
 import android.util.AttributeSet;
-import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.Button;
 
-public class ClearAllButton extends Button {
-    RecentsView mRecentsView;
+import com.android.launcher3.Utilities;
+import com.android.quickstep.views.RecentsView.PageCallbacks;
+import com.android.quickstep.views.RecentsView.ScrollState;
 
-    public ClearAllButton(Context context, @Nullable AttributeSet attrs) {
+public class ClearAllButton extends Button implements PageCallbacks {
+
+    private float mScrollAlpha = 1;
+    private float mContentAlpha = 1;
+
+    private final boolean mIsRtl;
+
+    private int mScrollOffset;
+
+    public ClearAllButton(Context context, AttributeSet attrs) {
         super(context, attrs);
-    }
-
-    public void setRecentsView(RecentsView recentsView) {
-        mRecentsView = recentsView;
+        mIsRtl = Utilities.isRtl(context.getResources());
     }
 
     @Override
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfo(info);
-        info.setParent(mRecentsView); // Pretend we are a part of the task carousel.
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+
+        RecentsView parent = (RecentsView) getParent();
+        mScrollOffset = mIsRtl ? parent.getPaddingRight() / 2 : - parent.getPaddingLeft() / 2;
     }
 
     @Override
-    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
-        super.onFocusChanged(focused, direction, previouslyFocusedRect);
-        if (focused) {
-            mRecentsView.revealClearAllButton();
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+
+    public void setContentAlpha(float alpha) {
+        if (mContentAlpha != alpha) {
+            mContentAlpha = alpha;
+            setAlpha(mScrollAlpha * mContentAlpha);
         }
     }
+
+    @Override
+    public void onPageScroll(ScrollState scrollState) {
+        float width = getWidth();
+        if (width == 0) {
+            return;
+        }
+
+        float shift = Math.min(scrollState.scrollFromEdge, width);
+        setTranslationX(mIsRtl ? (mScrollOffset - shift) : (mScrollOffset + shift));
+        mScrollAlpha = 1 - shift / width;
+        setAlpha(mScrollAlpha * mContentAlpha);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 1e3db96..f7f496f 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -40,7 +40,6 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.UserHandle;
 import android.support.annotation.Nullable;
@@ -49,6 +48,7 @@
 import android.text.TextPaint;
 import android.util.ArraySet;
 import android.util.AttributeSet;
+import android.util.FloatProperty;
 import android.util.SparseBooleanArray;
 import android.view.HapticFeedbackConstants;
 import android.view.KeyEvent;
@@ -90,7 +90,6 @@
 import com.android.systemui.shared.system.TaskStackChangeListener;
 
 import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Consumer;
 
 /**
@@ -101,11 +100,24 @@
 
     private static final String TAG = RecentsView.class.getSimpleName();
 
+    public static final FloatProperty<RecentsView> CONTENT_ALPHA =
+            new FloatProperty<RecentsView>("contentAlpha") {
+                @Override
+                public void setValue(RecentsView view, float v) {
+                    view.setContentAlpha(v);
+                }
+
+                @Override
+                public Float get(RecentsView view) {
+                    return view.getContentAlpha();
+                }
+            };
+
     private final Rect mTempRect = new Rect();
 
     private static final int DISMISS_TASK_DURATION = 300;
     // The threshold at which we update the SystemUI flags when animating from the task into the app
-    private static final float UPDATE_SYSUI_FLAGS_THRESHOLD = 0.6f;
+    public static final float UPDATE_SYSUI_FLAGS_THRESHOLD = 0.85f;
 
     private static final float[] sTempFloatArray = new float[3];
 
@@ -114,13 +126,12 @@
     private final float mFastFlingVelocity;
     private final RecentsModel mModel;
     private final int mTaskTopMargin;
+    private final ClearAllButton mClearAllButton;
 
     private final ScrollState mScrollState = new ScrollState();
     // Keeps track of the previously known visible tasks for purposes of loading/unloading task data
     private final SparseBooleanArray mHasVisibleTaskData = new SparseBooleanArray();
 
-    private boolean mIsClearAllButtonFullyRevealed;
-
     /**
      * TODO: Call reloadIdNeeded in onTaskStackChanged.
      */
@@ -228,8 +239,6 @@
     // Keeps track of task views whose visual state should not be reset
     private ArraySet<TaskView> mIgnoreResetTaskViews = new ArraySet<>();
 
-    private View mClearAllButton;
-
     // Variables for empty state
     private final Drawable mEmptyIcon;
     private final CharSequence mEmptyMessage;
@@ -250,8 +259,8 @@
     public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         setPageSpacing(getResources().getDimensionPixelSize(R.dimen.recents_page_spacing));
-        enableFreeScroll(true);
-        setClipToOutline(true);
+        setEnableFreeScroll(true);
+        setEnableOverscroll(true);
 
         mFastFlingVelocity = getResources()
                 .getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
@@ -259,6 +268,10 @@
         mQuickScrubController = new QuickScrubController(mActivity, this);
         mModel = RecentsModel.getInstance(context);
 
+        mClearAllButton = (ClearAllButton) LayoutInflater.from(context)
+                .inflate(R.layout.overview_clear_all_button, this, false);
+        mClearAllButton.setOnClickListener(this::dismissAllTasks);
+
         mIsRtl = !Utilities.isRtl(getResources());
         setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
         mTaskTopMargin = getResources()
@@ -275,7 +288,6 @@
                 .getDimensionPixelSize(R.dimen.recents_empty_message_text_padding);
         setWillNotDraw(false);
         updateEmptyMessage();
-        setFocusable(false);
     }
 
     public boolean isRtl() {
@@ -317,14 +329,15 @@
         super.onViewRemoved(child);
 
         // Clear the task data for the removed child if it was visible
-        Task task = ((TaskView) child).getTask();
-        if (mHasVisibleTaskData.get(task.key.id)) {
-            mHasVisibleTaskData.delete(task.key.id);
-            RecentsTaskLoader loader = mModel.getRecentsTaskLoader();
-            loader.unloadTaskData(task);
-            loader.getHighResThumbnailLoader().onTaskInvisible(task);
+        if (child != mClearAllButton) {
+            Task task = ((TaskView) child).getTask();
+            if (mHasVisibleTaskData.get(task.key.id)) {
+                mHasVisibleTaskData.delete(task.key.id);
+                RecentsTaskLoader loader = mModel.getRecentsTaskLoader();
+                loader.unloadTaskData(task);
+                loader.getHighResThumbnailLoader().onTaskInvisible(task);
+            }
         }
-        onChildViewsChanged();
     }
 
     public boolean isTaskViewVisible(TaskView tv) {
@@ -333,7 +346,7 @@
     }
 
     public TaskView getTaskView(int taskId) {
-        for (int i = 0; i < getChildCount(); i++) {
+        for (int i = 0; i < getTaskViewCount(); i++) {
             TaskView tv = (TaskView) getChildAt(i);
             if (tv.getTask().key.id == taskId) {
                 return tv;
@@ -363,75 +376,14 @@
         }
     }
 
-    private int getScrollEnd() {
-        return mIsRtl ? 0 : mMaxScrollX;
-    }
-
-    private float calculateClearAllButtonAlpha() {
-        final int childCount = getChildCount();
-        if (mShowEmptyMessage || childCount == 0 || mPageScrolls == null
-                || childCount != mPageScrolls.length) {
-            return 0;
-        }
-
-        final int scrollEnd = getScrollEnd();
-        final int oldestChildScroll = getScrollForPage(childCount - 1);
-
-        final int clearAllButtonMotionRange = scrollEnd - oldestChildScroll;
-        if (clearAllButtonMotionRange == 0) return 0;
-
-        final float alphaUnbound = ((float) (getScrollX() - oldestChildScroll)) /
-                clearAllButtonMotionRange;
-        if (alphaUnbound > 1) return 0;
-
-        return Math.max(alphaUnbound, 0);
-    }
-
-    private void updateClearAllButtonAlpha() {
-        if (mClearAllButton != null) {
-            final float alpha = calculateClearAllButtonAlpha();
-            final boolean revealed = alpha == 1;
-            if (mIsClearAllButtonFullyRevealed != revealed) {
-                mIsClearAllButtonFullyRevealed = revealed;
-                mClearAllButton.setImportantForAccessibility(revealed ?
-                        IMPORTANT_FOR_ACCESSIBILITY_YES :
-                        IMPORTANT_FOR_ACCESSIBILITY_NO);
-            }
-            mClearAllButton.setAlpha(alpha * mContentAlpha);
-        }
-    }
-
-    @Override
-    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
-        super.onScrollChanged(l, t, oldl, oldt);
-        updateClearAllButtonAlpha();
-    }
-
-    @Override
-    protected void restoreScrollOnLayout() {
-        if (mIsClearAllButtonFullyRevealed) {
-            scrollAndForceFinish(getScrollEnd());
-        } else {
-            super.restoreScrollOnLayout();
-        }
-    }
-
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
-        if (ev.getAction() == MotionEvent.ACTION_DOWN && mTouchState == TOUCH_STATE_REST
-                && mScroller.isFinished() && mIsClearAllButtonFullyRevealed) {
-            mClearAllButton.getHitRect(mTempRect);
-            mTempRect.offset(-getLeft(), -getTop());
-            if (mTempRect.contains((int) ev.getX(), (int) ev.getY())) {
-                // If nothing is in motion, let the Clear All button process the event.
-                return false;
-            }
-        }
-
+        super.onTouchEvent(ev);
         if (ev.getAction() == MotionEvent.ACTION_UP && mShowEmptyMessage) {
             onAllTasksRemoved();
         }
-        return super.onTouchEvent(ev);
+        // Do not let touch escape to siblings below this view.
+        return true;
     }
 
     private void applyLoadPlan(RecentsTaskLoadPlan loadPlan) {
@@ -453,22 +405,30 @@
         final LayoutInflater inflater = LayoutInflater.from(getContext());
         final ArrayList<Task> tasks = new ArrayList<>(stack.getTasks());
 
-        final int requiredChildCount = tasks.size();
-        for (int i = getChildCount(); i < requiredChildCount; i++) {
-            final TaskView taskView = (TaskView) inflater.inflate(R.layout.task, this, false);
-            addView(taskView);
-        }
-        while (getChildCount() > requiredChildCount) {
-            final TaskView taskView = (TaskView) getChildAt(getChildCount() - 1);
-            removeView(taskView);
+        final int requiredTaskCount = tasks.size();
+        if (getTaskViewCount() != requiredTaskCount) {
+            if (oldChildCount > 0) {
+                removeView(mClearAllButton);
+            }
+            for (int i = getChildCount(); i < requiredTaskCount; i++) {
+                final TaskView taskView = (TaskView) inflater.inflate(R.layout.task, this, false);
+                addView(taskView);
+            }
+            while (getChildCount() > requiredTaskCount) {
+                final TaskView taskView = (TaskView) getChildAt(getChildCount() - 1);
+                removeView(taskView);
+            }
+            if (requiredTaskCount > 0) {
+                addView(mClearAllButton);
+            }
         }
 
         // Unload existing visible task data
         unloadVisibleTaskData();
 
         // Rebind and reset all task views
-        for (int i = requiredChildCount - 1; i >= 0; i--) {
-            final int pageIndex = requiredChildCount - i - 1;
+        for (int i = requiredTaskCount - 1; i >= 0; i--) {
+            final int pageIndex = requiredTaskCount - i - 1;
             final Task task = tasks.get(i);
             final TaskView taskView = (TaskView) getChildAt(pageIndex);
             taskView.bind(task);
@@ -481,10 +441,16 @@
         onTaskStackUpdated();
     }
 
+    public int getTaskViewCount() {
+        // Account for the clear all button.
+        int childCount = getChildCount();
+        return childCount == 0 ? 0 : childCount - 1;
+    }
+
     protected void onTaskStackUpdated() { }
 
     public void resetTaskVisuals() {
-        for (int i = getChildCount() - 1; i >= 0; i--) {
+        for (int i = getTaskViewCount() - 1; i >= 0; i--) {
             TaskView taskView = (TaskView) getChildAt(i);
             if (!mIgnoreResetTaskViews.contains(taskView)) {
                 taskView.resetVisualProperties();
@@ -558,10 +524,12 @@
         if (getPageCount() == 0 || getPageAt(0).getMeasuredWidth() == 0) {
             return;
         }
+        int scrollX = getScrollX();
         final int halfPageWidth = getNormalChildWidth() / 2;
-        final int screenCenter = mInsets.left + getPaddingLeft() + getScrollX() + halfPageWidth;
+        final int screenCenter = mInsets.left + getPaddingLeft() + scrollX + halfPageWidth;
         final int halfScreenWidth = getMeasuredWidth() / 2;
         final int pageSpacing = mPageSpacing;
+        mScrollState.scrollFromEdge = mIsRtl ? scrollX : (mMaxScrollX - scrollX);
 
         final int pageCount = getPageCount();
         for (int i = 0; i < pageCount; i++) {
@@ -587,9 +555,9 @@
 
         RecentsTaskLoader loader = mModel.getRecentsTaskLoader();
         int centerPageIndex = getPageNearestToCenterOfScreen();
+        int numChildren = getTaskViewCount();
         int lower = Math.max(0, centerPageIndex - 2);
-        int upper = Math.min(centerPageIndex + 2, getChildCount() - 1);
-        int numChildren = getChildCount();
+        int upper = Math.min(centerPageIndex + 2, numChildren - 1);
 
         // Update the task data for the in/visible children
         for (int i = 0; i < numChildren; i++) {
@@ -667,6 +635,7 @@
             final TaskView taskView = (TaskView) LayoutInflater.from(getContext())
                     .inflate(R.layout.task, this, false);
             addView(taskView);
+            addView(mClearAllButton);
 
             // The temporary running task is only used for the duration between the start of the
             // gesture and the task list is loaded and applied
@@ -713,14 +682,14 @@
         TaskView runningTaskView = getTaskView(mRunningTaskId);
         if (runningTaskView == null) {
             // Launch the first task
-            if (getChildCount() > 0) {
+            if (getTaskViewCount() > 0) {
                 ((TaskView) getChildAt(0)).launchTask(true /* animate */);
             }
         } else {
             // Get the next launch task
             int runningTaskIndex = indexOfChild(runningTaskView);
-            int nextTaskIndex = Math.max(0, Math.min(getChildCount() - 1, runningTaskIndex + 1));
-            if (nextTaskIndex < getChildCount()) {
+            int nextTaskIndex = Math.max(0, Math.min(getTaskViewCount() - 1, runningTaskIndex + 1));
+            if (nextTaskIndex < getTaskViewCount()) {
                 ((TaskView) getChildAt(nextTaskIndex)).launchTask(true /* animate */);
             }
         }
@@ -763,7 +732,7 @@
         /**
          * Updates the page UI based on scroll params.
          */
-        default void onPageScroll(ScrollState scrollState) {};
+        default void onPageScroll(ScrollState scrollState) {}
     }
 
     public static class ScrollState {
@@ -773,6 +742,11 @@
          * of the screen and 1 is the edge of the screen.
          */
         public float linearInterpolation;
+
+        /**
+         * The amount by which all the content is scrolled relative to the end of the list.
+         */
+        public float scrollFromEdge;
     }
 
     public void addIgnoreResetTask(TaskView taskView) {
@@ -809,7 +783,7 @@
         AnimatorSet anim = new AnimatorSet();
         PendingAnimation pendingAnimation = new PendingAnimation(anim);
 
-        int count = getChildCount();
+        int count = getPageCount();
         if (count == 0) {
             return pendingAnimation;
         }
@@ -820,12 +794,10 @@
         int[] newScroll = new int[count];
         getPageScrolls(newScroll, false, (v) -> v.getVisibility() != GONE && v != taskView);
 
+        int taskCount = getTaskViewCount();
         int scrollDiffPerPage = 0;
-        int leftmostPage = mIsRtl ? count -1 : 0;
-        int rightmostPage = mIsRtl ? 0 : count - 1;
         if (count > 1) {
-            int secondRightmostPage = mIsRtl ? 1 : count - 2;
-            scrollDiffPerPage = oldScroll[rightmostPage] - oldScroll[secondRightmostPage];
+            scrollDiffPerPage = Math.abs(oldScroll[1] - oldScroll[0]);
         }
         int draggedIndex = indexOfChild(taskView);
 
@@ -845,7 +817,7 @@
                 // - Dragging an adjacent page on the left side (right side for RTL)
                 int offset = mIsRtl ? scrollDiffPerPage : 0;
                 if (mCurrentPage == draggedIndex) {
-                    int lastPage = mIsRtl ? leftmostPage : rightmostPage;
+                    int lastPage = taskCount - 1;
                     if (mCurrentPage == lastPage) {
                         offset += mIsRtl ? -scrollDiffPerPage : scrollDiffPerPage;
                     }
@@ -883,13 +855,15 @@
                    removeTask(taskView.getTask(), draggedIndex, onEndListener, true);
                }
                int pageToSnapTo = mCurrentPage;
-               if (draggedIndex < pageToSnapTo) {
+               if (draggedIndex < pageToSnapTo || pageToSnapTo == (getTaskViewCount() - 1)) {
                    pageToSnapTo -= 1;
                }
                removeView(taskView);
-               if (getChildCount() == 0) {
+
+               if (getTaskViewCount() == 0) {
+                   removeView(mClearAllButton);
                    onAllTasksRemoved();
-               } else if (!mIsClearAllButtonFullyRevealed) {
+               } else {
                    snapToPageImmediately(pageToSnapTo);
                }
            }
@@ -906,7 +880,7 @@
         AnimatorSet anim = new AnimatorSet();
         PendingAnimation pendingAnimation = new PendingAnimation(anim);
 
-        int count = getChildCount();
+        int count = getTaskViewCount();
         for (int i = 0; i < count; i++) {
             addDismissedTaskAnimations(getChildAt(i), anim, duration);
         }
@@ -914,11 +888,11 @@
         mPendingAnimation = pendingAnimation;
         mPendingAnimation.addEndListener((onEndListener) -> {
             if (onEndListener.isSuccess) {
-                while (getChildCount() != 0) {
-                    TaskView taskView = getPageAt(getChildCount() - 1);
-                    removeTask(taskView.getTask(), -1, onEndListener, false);
-                    removeView(taskView);
+                int taskViewCount = getTaskViewCount();
+                for (int i = 0; i < taskViewCount; i++) {
+                    removeTask(getTaskViewAt(i).getTask(), -1, onEndListener, false);
                 }
+                removeAllViews();
                 onAllTasksRemoved();
             }
             mPendingAnimation = null;
@@ -932,15 +906,16 @@
         set.play(anim);
     }
 
-    private boolean snapToPageRelative(int delta, boolean cycle) {
-        if (getPageCount() == 0) {
+    private boolean snapToPageRelative(int pageCount, int delta, boolean cycle) {
+        if (pageCount == 0) {
             return false;
         }
         final int newPageUnbound = getNextPage() + delta;
-        if (!cycle && (newPageUnbound < 0 || newPageUnbound >= getChildCount())) {
+        if (!cycle && (newPageUnbound < 0 || newPageUnbound >= pageCount)) {
             return false;
         }
-        snapToPage((newPageUnbound + getPageCount()) % getPageCount());
+        snapToPage((newPageUnbound + pageCount) % pageCount);
+        getChildAt(getNextPage()).requestFocus();
         return true;
     }
 
@@ -958,31 +933,37 @@
                 DISMISS_TASK_DURATION));
     }
 
-    public void dismissAllTasks() {
+    @SuppressWarnings("unused")
+    private void dismissAllTasks(View view) {
         runDismissAnimation(createAllTasksDismissAnimation(DISMISS_TASK_DURATION));
     }
 
+    private void dismissCurrentTask() {
+        TaskView taskView = getTaskView(getNextPage());
+        if (taskView != null) {
+            dismissTask(taskView, true /*animateTaskView*/, true /*removeTask*/);
+        }
+    }
+
     @Override
     public boolean dispatchKeyEvent(KeyEvent event) {
         if (event.getAction() == KeyEvent.ACTION_DOWN) {
             switch (event.getKeyCode()) {
                 case KeyEvent.KEYCODE_TAB:
-                    return snapToPageRelative(event.isShiftPressed() ? -1 : 1,
+                    return snapToPageRelative(getTaskViewCount(), event.isShiftPressed() ? -1 : 1,
                             event.isAltPressed() /* cycle */);
                 case KeyEvent.KEYCODE_DPAD_RIGHT:
-                    return snapToPageRelative(mIsRtl ? -1 : 1, false /* cycle */);
+                    return snapToPageRelative(getPageCount(), mIsRtl ? -1 : 1, false /* cycle */);
                 case KeyEvent.KEYCODE_DPAD_LEFT:
-                    return snapToPageRelative(mIsRtl ? 1 : -1, false /* cycle */);
+                    return snapToPageRelative(getPageCount(), mIsRtl ? 1 : -1, false /* cycle */);
                 case KeyEvent.KEYCODE_DEL:
                 case KeyEvent.KEYCODE_FORWARD_DEL:
-                    dismissTask((TaskView) getChildAt(getNextPage()), true /*animateTaskView*/,
-                            true /*removeTask*/);
+                    dismissCurrentTask();
                     return true;
                 case KeyEvent.KEYCODE_NUMPAD_DOT:
                     if (event.isAltPressed()) {
                         // Numpad DEL pressed while holding Alt.
-                        dismissTask((TaskView) getChildAt(getNextPage()), true /*animateTaskView*/,
-                                true /*removeTask*/);
+                        dismissCurrentTask();
                         return true;
                     }
             }
@@ -1013,19 +994,24 @@
     }
 
     public void setContentAlpha(float alpha) {
+        if (alpha == mContentAlpha) {
+            return;
+        }
         alpha = Utilities.boundToRange(alpha, 0, 1);
         mContentAlpha = alpha;
-        for (int i = getChildCount() - 1; i >= 0; i--) {
-            TaskView child = getPageAt(i);
+        for (int i = getTaskViewCount() - 1; i >= 0; i--) {
+            TaskView child = getTaskViewAt(i);
             if (!mRunningTaskTileHidden || child.getTask().key.id != mRunningTaskId) {
                 getChildAt(i).setAlpha(alpha);
             }
         }
+        mClearAllButton.setContentAlpha(mContentAlpha);
 
         int alphaInt = Math.round(alpha * 255);
         mEmptyMessagePaint.setAlpha(alphaInt);
         mEmptyIcon.setAlpha(alphaInt);
-        updateClearAllButtonAlpha();
+
+        setVisibility(alpha > 0 ? VISIBLE : GONE);
     }
 
     private float[] getAdjacentScaleAndTranslation(TaskView currTask,
@@ -1041,12 +1027,11 @@
     public void onViewAdded(View child) {
         super.onViewAdded(child);
         child.setAlpha(mContentAlpha);
-        onChildViewsChanged();
     }
 
-    @Override
-    public TaskView getPageAt(int index) {
-        return (TaskView) getChildAt(index);
+    public TaskView getTaskViewAt(int index) {
+        View child = getChildAt(index);
+        return child == mClearAllButton ? null : (TaskView) child;
     }
 
     public void updateEmptyMessage() {
@@ -1080,7 +1065,6 @@
             mEmptyTextLayout = null;
             mLastMeasureSize.set(getWidth(), getHeight());
         }
-        updateClearAllButtonAlpha();
 
         if (mShowEmptyMessage && hasValidSize && mEmptyTextLayout == null) {
             int availableWidth = mLastMeasureSize.x - mEmptyMessagePadding - mEmptyMessagePadding;
@@ -1138,16 +1122,16 @@
         float toTranslationY = clipAnimationHelper.getSourceRect().centerY()
                 - clipAnimationHelper.getTargetRect().centerY();
         if (launchingCenterTask) {
-            TaskView centerTask = getPageAt(centerTaskIndex);
+            TaskView centerTask = getTaskViewAt(centerTaskIndex);
             if (taskIndex - 1 >= 0) {
-                TaskView adjacentTask = getPageAt(taskIndex - 1);
+                TaskView adjacentTask = getTaskViewAt(taskIndex - 1);
                 float[] scaleAndTranslation = getAdjacentScaleAndTranslation(centerTask,
                         toScale, toTranslationY);
                 scaleAndTranslation[1] = -scaleAndTranslation[1];
                 anim.play(createAnimForChild(adjacentTask, scaleAndTranslation));
             }
-            if (taskIndex + 1 < getPageCount()) {
-                TaskView adjacentTask = getPageAt(taskIndex + 1);
+            if (taskIndex + 1 < getTaskViewCount()) {
+                TaskView adjacentTask = getTaskViewAt(taskIndex + 1);
                 float[] scaleAndTranslation = getAdjacentScaleAndTranslation(centerTask,
                         toScale, toTranslationY);
                 anim.play(createAnimForChild(adjacentTask, scaleAndTranslation));
@@ -1269,67 +1253,9 @@
         return "";
     }
 
-    private int additionalScrollForClearAllButton() {
-        return (int) getResources().getDimension(
-                R.dimen.clear_all_container_width) - getPaddingEnd();
-    }
-
-    @Override
-    protected int computeMaxScrollX() {
-        if (getChildCount() == 0) {
-            return super.computeMaxScrollX();
-        }
-
-        // Allow a clear_all_container_width-sized gap after the last task.
-        return super.computeMaxScrollX() + (mIsRtl ? 0 : additionalScrollForClearAllButton());
-    }
-
-    @Override
-    protected int offsetForPageScrolls() {
-        return mIsRtl ? additionalScrollForClearAllButton() : 0;
-    }
-
-    public void setClearAllButton(View clearAllButton) {
-        mClearAllButton = clearAllButton;
-        updateClearAllButtonAlpha();
-    }
-
-    private void onChildViewsChanged() {
-        final int childCount = getChildCount();
-        mClearAllButton.setVisibility(childCount == 0 ? INVISIBLE : VISIBLE);
-        setFocusable(childCount != 0);
-    }
-
-    public void revealClearAllButton() {
-        setCurrentPage(getChildCount() - 1); // Loads tasks info if needed.
-        scrollTo(mIsRtl ? 0 : computeMaxScrollX(), 0);
-    }
-
-    @Override
-    public boolean performAccessibilityAction(int action, Bundle arguments) {
-        if (getChildCount() > 0) {
-            switch (action) {
-                case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
-                    if (!mIsClearAllButtonFullyRevealed && getCurrentPage() == getPageCount() - 1) {
-                        revealClearAllButton();
-                        return true;
-                    }
-                }
-                case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
-                    if (mIsClearAllButtonFullyRevealed) {
-                        setCurrentPage(getChildCount() - 1);
-                        return true;
-                    }
-                }
-                break;
-            }
-        }
-        return super.performAccessibilityAction(action, arguments);
-    }
-
     @Override
     public void addChildrenForAccessibility(ArrayList<View> outChildren) {
-        outChildren.add(mClearAllButton);
+        // Add children in reverse order
         for (int i = getChildCount() - 1; i >= 0; --i) {
             outChildren.add(getChildAt(i));
         }
@@ -1338,17 +1264,9 @@
     @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
-
-        if (getChildCount() > 0) {
-            info.addAction(mIsClearAllButtonFullyRevealed ?
-                    AccessibilityNodeInfo.ACTION_SCROLL_FORWARD :
-                    AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
-            info.setScrollable(true);
-        }
-
         final AccessibilityNodeInfo.CollectionInfo
                 collectionInfo = AccessibilityNodeInfo.CollectionInfo.obtain(
-                1, getChildCount(), false,
+                1, getTaskViewCount(), false,
                 AccessibilityNodeInfo.CollectionInfo.SELECTION_MODE_NONE);
         info.setCollectionInfo(collectionInfo);
     }
@@ -1357,15 +1275,14 @@
     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
         super.onInitializeAccessibilityEvent(event);
 
-        event.setScrollable(getPageCount() > 0);
+        final int taskViewCount = getTaskViewCount();
+        event.setScrollable(taskViewCount > 0);
 
-        if (!mIsClearAllButtonFullyRevealed
-                && event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
-            final int childCount = getChildCount();
+        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
             final int[] visibleTasks = getVisibleChildrenRange();
-            event.setFromIndex(childCount - visibleTasks[1] - 1);
-            event.setToIndex(childCount - visibleTasks[0] - 1);
-            event.setItemCount(childCount);
+            event.setFromIndex(taskViewCount - visibleTasks[1] - 1);
+            event.setToIndex(taskViewCount - visibleTasks[0] - 1);
+            event.setItemCount(taskViewCount);
         }
     }
 
@@ -1379,8 +1296,4 @@
     protected boolean isPageOrderFlipped() {
         return true;
     }
-
-    public boolean performTaskAccessibilityActionExtra(int action) {
-        return false;
-    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
deleted file mode 100644
index c6cd527..0000000
--- a/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep.views;
-
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.CLEAR_ALL_BUTTON;
-
-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;
-
-import java.util.ArrayList;
-
-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 ClearAllButton 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.mActivity.getUserEventDispatcher()
-                    .logActionOnControl(TAP, CLEAR_ALL_BUTTON);
-            mRecentsView.dismissAllTasks();
-        });
-
-        mRecentsView = findViewById(R.id.overview_panel);
-        mClearAllButton.forceHasOverlappingRendering(false);
-
-        mRecentsView.setClearAllButton(mClearAllButton);
-        mClearAllButton.setRecentsView(mRecentsView);
-
-        if (mRecentsView.isRtl()) {
-            mClearAllButton.setNextFocusRightId(mRecentsView.getId());
-            mRecentsView.setNextFocusLeftId(mClearAllButton.getId());
-        } else {
-            mClearAllButton.setNextFocusLeftId(mRecentsView.getId());
-            mRecentsView.setNextFocusRightId(mClearAllButton.getId());
-        }
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-
-        mRecentsView.getTaskSize(mTempRect);
-
-        mClearAllButton.setTranslationX(
-                (mRecentsView.isRtl() ? 1 : -1) *
-                        (getResources().getDimension(R.dimen.clear_all_container_width)
-                                - mClearAllButton.getMeasuredWidth()) / 2);
-        mClearAllButton.setTranslationY(
-                mTempRect.top + (mTempRect.height() - mClearAllButton.getMeasuredHeight()) / 2
-                        - mClearAllButton.getTop());
-    }
-
-    @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);
-    }
-
-    @Override
-    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
-        if (mRecentsView.getChildCount() > 0) {
-            // Carousel is first in tab order.
-            views.add(mRecentsView);
-            views.add(mClearAllButton);
-        }
-    }
-
-    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
-        return mRecentsView.requestFocus(direction, previouslyFocusedRect) ||
-                super.requestFocus(direction, previouslyFocusedRect);
-    }
-
-    @Override
-    public void addChildrenForAccessibility(ArrayList<View> outChildren) {
-        outChildren.add(mRecentsView);
-    }
-}
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index 39f5323..6eb6854 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -26,6 +26,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
@@ -172,8 +173,9 @@
         Rect insets = mActivity.getDragLayer().getInsets();
         BaseDragLayer.LayoutParams params = (BaseDragLayer.LayoutParams) getLayoutParams();
         params.width = sTempRect.width();
+        params.gravity = Gravity.LEFT;
         setLayoutParams(params);
-        setX(Utilities.isRtl(getResources()) ? -sTempRect.left : sTempRect.left);
+        setX(sTempRect.left - insets.left);
         setY(sTempRect.top + getResources().getDimension(R.dimen.task_thumbnail_top_margin)
                 - insets.top);
     }
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 468efd8..c4ccd96 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -121,6 +121,7 @@
                     TaskUtils.getLaunchComponentKeyForTask(getTask().key));
         });
         setOutlineProvider(new TaskOutlineProvider(getResources()));
+        setDescendantFocusability(FOCUS_BLOCK_DESCENDANTS);
     }
 
     @Override
@@ -347,8 +348,6 @@
             }
         }
 
-        if (getRecentsView().performTaskAccessibilityActionExtra(action)) return true;
-
         return super.performAccessibilityAction(action, arguments);
     }
 
diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml
index da17b2b..17361ac 100644
--- a/res/layout/launcher.xml
+++ b/res/layout/launcher.xml
@@ -39,7 +39,7 @@
             launcher:pageIndicator="@+id/page_indicator" />
 
         <include
-            android:id="@+id/overview_panel_container"
+            android:id="@+id/overview_panel"
             layout="@layout/overview_panel"
             android:visibility="gone" />
 
diff --git a/res/values/config.xml b/res/values/config.xml
index d5bb131..f2d6c21 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -149,4 +149,5 @@
     <integer name="config_recentsMaxThumbnailCacheSize">6</integer>
     <integer name="config_recentsMaxIconCacheSize">12</integer>
 
+    <item name="workspace_page_container" type="id" />
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 0645f41..07bd800 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -122,6 +122,7 @@
         <item name="android:saveEnabled">false</item>
         <item name="android:textColor">@android:color/white</item>
         <item name="android:includeFontPadding">false</item>
+        <item name="android:importantForAccessibility">no</item>
     </style>
 
     <!-- Base theme for BubbleTextView and sub classes -->
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 2f47728..20c4a5f 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -102,7 +102,9 @@
     public int hotseatBarSizePx;
     public final int hotseatBarTopPaddingPx;
     public final int hotseatBarBottomPaddingPx;
-    public final int hotseatBarSidePaddingPx;
+    // Start is the side next to the nav bar, end is the side next to the workspace
+    public final int hotseatBarSidePaddingStartPx;
+    public final int hotseatBarSidePaddingEndPx;
 
     // All apps
     public int allAppsCellHeightPx;
@@ -178,10 +180,14 @@
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding);
         hotseatBarBottomPaddingPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding);
-        hotseatBarSidePaddingPx =
+        hotseatBarSidePaddingEndPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_side_padding);
+        // Add a bit of space between nav bar and hotseat in multi-window vertical bar layout.
+        hotseatBarSidePaddingStartPx = isMultiWindowMode && isVerticalBarLayout()
+                ? edgeMarginPx : 0;
         hotseatBarSizePx = isVerticalBarLayout()
-                ? Utilities.pxFromDp(inv.iconSize, dm)
+                ? Utilities.pxFromDp(inv.iconSize, dm) + hotseatBarSidePaddingStartPx
+                        + hotseatBarSidePaddingEndPx
                 : res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_size)
                         + hotseatBarTopPaddingPx + hotseatBarBottomPaddingPx;
 
@@ -326,7 +332,8 @@
 
         // Hotseat
         if (isVerticalLayout) {
-            hotseatBarSizePx = iconSizePx;
+            hotseatBarSizePx = iconSizePx + hotseatBarSidePaddingStartPx
+                    + hotseatBarSidePaddingEndPx;
         }
         hotseatCellHeightPx = iconSizePx;
 
@@ -425,14 +432,12 @@
         if (isVerticalBarLayout()) {
             padding.top = 0;
             padding.bottom = edgeMarginPx;
-            padding.left = hotseatBarSidePaddingPx;
-            padding.right = hotseatBarSidePaddingPx;
             if (isSeascape()) {
-                padding.left += hotseatBarSizePx;
-                padding.right += verticalDragHandleSizePx;
+                padding.left = hotseatBarSizePx;
+                padding.right = verticalDragHandleSizePx;
             } else {
-                padding.left += verticalDragHandleSizePx;
-                padding.right += hotseatBarSizePx;
+                padding.left = verticalDragHandleSizePx;
+                padding.right = hotseatBarSizePx;
             }
         } else {
             int paddingBottom = hotseatBarSizePx + verticalDragHandleSizePx;
@@ -462,11 +467,11 @@
     public Rect getHotseatLayoutPadding() {
         if (isVerticalBarLayout()) {
             if (isSeascape()) {
-                mHotseatPadding.set(
-                        mInsets.left, mInsets.top, hotseatBarSidePaddingPx, mInsets.bottom);
+                mHotseatPadding.set(mInsets.left + hotseatBarSidePaddingStartPx,
+                        mInsets.top, hotseatBarSidePaddingEndPx, mInsets.bottom);
             } else {
-                mHotseatPadding.set(
-                        hotseatBarSidePaddingPx, mInsets.top, mInsets.right, mInsets.bottom);
+                mHotseatPadding.set(hotseatBarSidePaddingEndPx, mInsets.top,
+                        mInsets.right + hotseatBarSidePaddingStartPx, mInsets.bottom);
             }
         } else {
 
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index ee4b113..6668f2c 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -157,10 +157,10 @@
             lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
             if (grid.isSeascape()) {
                 lp.gravity = Gravity.LEFT;
-                lp.width = grid.hotseatBarSizePx + insets.left + grid.hotseatBarSidePaddingPx;
+                lp.width = grid.hotseatBarSizePx + insets.left;
             } else {
                 lp.gravity = Gravity.RIGHT;
-                lp.width = grid.hotseatBarSizePx + insets.right + grid.hotseatBarSidePaddingPx;
+                lp.width = grid.hotseatBarSizePx + insets.right;
             }
         } else {
             lp.gravity = Gravity.BOTTOM;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 3eaead1..3a8679e 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -214,8 +214,6 @@
     // UI and state for the overview panel
     private View mOverviewPanel;
 
-    private View mOverviewPanelContainer;
-
     @Thunk boolean mWorkspaceLoading = true;
 
     private OnResumeCallback mOnResumeCallback;
@@ -913,7 +911,6 @@
         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);
         mHotseatSearchBox = findViewById(R.id.search_container_hotseat);
 
@@ -1174,10 +1171,6 @@
         return (T) mOverviewPanel;
     }
 
-    public <T extends View> T getOverviewPanelContainer() {
-        return (T) mOverviewPanelContainer;
-    }
-
     public DropTargetBar getDropTargetBar() {
         return mDropTargetBar;
     }
@@ -2335,6 +2328,8 @@
         if (isInState(NORMAL)) {
             shortcutInfos.add(new KeyboardShortcutInfo(getString(R.string.all_apps_button_label),
                     KeyEvent.KEYCODE_A, KeyEvent.META_CTRL_ON));
+            shortcutInfos.add(new KeyboardShortcutInfo(getString(R.string.widget_button_text),
+                    KeyEvent.KEYCODE_W, KeyEvent.META_CTRL_ON));
         }
         final View currentFocus = getCurrentFocus();
         if (currentFocus != null) {
@@ -2383,6 +2378,12 @@
                         return true;
                     }
                     break;
+                case KeyEvent.KEYCODE_W:
+                    if (isInState(NORMAL)) {
+                        OptionsPopupView.openWidgets(this);
+                        return true;
+                    }
+                    break;
             }
         }
         return super.onKeyShortcut(keyCode, event);
diff --git a/src/com/android/launcher3/LauncherScroller.java b/src/com/android/launcher3/LauncherScroller.java
index a9b4955..af87550 100644
--- a/src/com/android/launcher3/LauncherScroller.java
+++ b/src/com/android/launcher3/LauncherScroller.java
@@ -459,13 +459,13 @@
         return Math.log(INFLEXION * Math.abs(velocity) / (mFlingFriction * mPhysicalCoeff));
     }
 
-    private int getSplineFlingDuration(float velocity) {
+    public int getSplineFlingDuration(float velocity) {
         final double l = getSplineDeceleration(velocity);
         final double decelMinusOne = DECELERATION_RATE - 1.0;
         return (int) (1000.0 * Math.exp(l / decelMinusOne));
     }
 
-    private double getSplineFlingDistance(float velocity) {
+    public double getSplineFlingDistance(float velocity) {
         final double l = getSplineDeceleration(velocity);
         final double decelMinusOne = DECELERATION_RATE - 1.0;
         return mFlingFriction * mPhysicalCoeff * Math.exp(DECELERATION_RATE / decelMinusOne * l);
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index de9cd98..0cb6539 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -24,7 +24,6 @@
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.TypedArray;
-import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.provider.Settings;
@@ -64,7 +63,6 @@
     protected static final ComputePageScrollsLogic SIMPLE_SCROLL_LOGIC = (v) -> v.getVisibility() != GONE;
 
     public static final int PAGE_SNAP_ANIMATION_DURATION = 750;
-    public static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950;
 
     // OverScroll constants
     private final static int OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION = 270;
@@ -84,7 +82,6 @@
     public static final int INVALID_RESTORE_PAGE = -1001;
 
     private boolean mFreeScroll = false;
-    private boolean mSettleOnPageInFreeScroll = false;
 
     protected int mFlingThresholdVelocity;
     protected int mMinFlingVelocity;
@@ -142,8 +139,6 @@
     protected T mPageIndicator;
 
     // Convenience/caching
-    private static final Matrix sTmpInvMatrix = new Matrix();
-    private static final float[] sTmpPoint = new float[2];
     private static final Rect sTmpRect = new Rect();
 
     protected final Rect mInsets = new Rect();
@@ -242,12 +237,6 @@
         return index;
     }
 
-    protected void scrollAndForceFinish(int scrollX) {
-        scrollTo(scrollX, 0);
-        mScroller.setFinalX(scrollX);
-        forceFinishScroller(true);
-    }
-
     /**
      * Updates the scroll of the current page immediately to its final scroll position.  We use this
      * in CustomizePagedView to allow tabs to share the same PagedView while resetting the scroll of
@@ -259,7 +248,9 @@
         if (0 <= mCurrentPage && mCurrentPage < getPageCount()) {
             newX = getScrollForPage(mCurrentPage);
         }
-        scrollAndForceFinish(newX);
+        scrollTo(newX, 0);
+        mScroller.setFinalX(newX);
+        forceFinishScroller(true);
     }
 
     private void abortScrollerAnimation(boolean resetNextPage) {
@@ -363,17 +354,6 @@
 
     @Override
     public void scrollTo(int x, int y) {
-        // In free scroll mode, we clamp the scrollX
-        if (mFreeScroll) {
-            // If the scroller is trying to move to a location beyond the maximum allowed
-            // in the free scroll mode, we make sure to end the scroll operation.
-            if (!mScroller.isFinished() && (x > mMaxScrollX || x < 0)) {
-                forceFinishScroller(false);
-            }
-
-            x = Utilities.boundToRange(x, 0, mMaxScrollX);
-        }
-
         mUnboundedScrollX = x;
 
         boolean isXBeforeFirstPage = mIsRtl ? (x > mMaxScrollX) : (x < 0);
@@ -544,10 +524,6 @@
         setMeasuredDimension(widthSize, heightSize);
     }
 
-    protected void restoreScrollOnLayout() {
-        setCurrentPage(getNextPage());
-    }
-
     @SuppressLint("DrawAllocation")
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
@@ -599,7 +575,7 @@
         }
 
         if (mScroller.isFinished() && pageScrollChanged) {
-            restoreScrollOnLayout();
+            setCurrentPage(getNextPage());
         }
     }
 
@@ -620,23 +596,26 @@
                 - mInsets.bottom - getPaddingBottom()) / 2;
 
         final int scrollOffsetLeft = mInsets.left + getPaddingLeft();
+        final int scrollOffsetRight = getWidth() - getPaddingRight() - mInsets.right;
         boolean pageScrollChanged = false;
 
-        for (int i = startIndex, childLeft = scrollOffsetLeft + offsetForPageScrolls();
-                i != endIndex;
-                i += delta) {
+        for (int i = startIndex, childLeft = scrollOffsetLeft; i != endIndex; i += delta) {
             final View child = getPageAt(i);
             if (scrollLogic.shouldIncludeView(child)) {
-                final int childTop = verticalCenter - child.getMeasuredHeight() / 2;
                 final int childWidth = child.getMeasuredWidth();
+                final int childRight = childLeft + childWidth;
 
                 if (layoutChildren) {
                     final int childHeight = child.getMeasuredHeight();
-                    child.layout(childLeft, childTop,
-                            childLeft + child.getMeasuredWidth(), childTop + childHeight);
+                    final int childTop = verticalCenter - childHeight / 2;
+                    child.layout(childLeft, childTop, childRight, childTop + childHeight);
                 }
 
-                final int pageScroll = childLeft - scrollOffsetLeft;
+                // In case the pages are of different width, align the page to left or right edge
+                // based on the orientation.
+                final int pageScroll = mIsRtl
+                        ? (childLeft - scrollOffsetLeft)
+                        : Math.max(0, childRight  - scrollOffsetRight);
                 if (outPageScrolls[i] != pageScroll) {
                     pageScrollChanged = true;
                     outPageScrolls[i] = pageScroll;
@@ -666,10 +645,6 @@
         }
     }
 
-    protected int offsetForPageScrolls() {
-        return 0;
-    }
-
     public void setPageSpacing(int pageSpacing) {
         mPageSpacing = pageSpacing;
         requestLayout();
@@ -747,11 +722,13 @@
         if (direction == View.FOCUS_LEFT) {
             if (getCurrentPage() > 0) {
                 snapToPage(getCurrentPage() - 1);
+                getChildAt(getCurrentPage() - 1).requestFocus(direction);
                 return true;
             }
         } else if (direction == View.FOCUS_RIGHT) {
             if (getCurrentPage() < getPageCount() - 1) {
                 snapToPage(getCurrentPage() + 1);
+                getChildAt(getCurrentPage() + 1).requestFocus(direction);
                 return true;
             }
         }
@@ -1036,13 +1013,7 @@
         dampedOverScroll(amount);
     }
 
-
-    protected void enableFreeScroll(boolean settleOnPageInFreeScroll) {
-        setEnableFreeScroll(true);
-        mSettleOnPageInFreeScroll = settleOnPageInFreeScroll;
-    }
-
-    private void setEnableFreeScroll(boolean freeScroll) {
+    protected void setEnableFreeScroll(boolean freeScroll) {
         boolean wasFreeScroll = mFreeScroll;
         mFreeScroll = freeScroll;
 
@@ -1051,8 +1022,6 @@
         } else if (wasFreeScroll) {
             snapToPage(getNextPage());
         }
-
-        setEnableOverscroll(!freeScroll);
     }
 
     protected void setEnableOverscroll(boolean enable) {
@@ -1165,42 +1134,15 @@
                         snapToDestination();
                     }
                 } else {
-                    if (!mScroller.isFinished()) {
-                        abortScrollerAnimation(true);
+                    int unscaledScrollX = getScrollX() - (int) Math.round(
+                            mScroller.getSplineFlingDistance(velocityX) * Math.signum(velocityX));
+                    int duration = mScroller.getSplineFlingDuration(velocityX);
+                    int finalPage = getPageNearestToCenterOfScreen(unscaledScrollX);
+                    if ((isFling || isSignificantMove) && (finalPage != mCurrentPage)) {
+                        snapToPage(finalPage, duration);
+                    } else {
+                        snapToDestination();
                     }
-
-                    float scaleX = getScaleX();
-                    int vX = (int) (-velocityX * scaleX);
-                    int initialScrollX = (int) (getScrollX() * scaleX);
-
-                    mScroller.setInterpolator(mDefaultInterpolator);
-                    mScroller.fling(initialScrollX,
-                            getScrollY(), vX, 0, Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 0);
-                    int unscaledScrollX = (int) (mScroller.getFinalX() / scaleX);
-                    mNextPage = getPageNearestToCenterOfScreen(unscaledScrollX);
-                    int firstPageScroll = getScrollForPage(!mIsRtl ? 0 : getPageCount() - 1);
-                    int lastPageScroll = getScrollForPage(!mIsRtl ? getPageCount() - 1 : 0);
-                    if (mSettleOnPageInFreeScroll && unscaledScrollX > 0
-                            && unscaledScrollX < mMaxScrollX) {
-                        // If scrolling ends in the half of the added space that is closer to the
-                        // end, settle to the end. Otherwise snap to the nearest page.
-                        // If flinging past one of the ends, don't change the velocity as it will
-                        // get stopped at the end anyway.
-                        final int finalX = unscaledScrollX < firstPageScroll / 2 ?
-                                0 :
-                                unscaledScrollX > (lastPageScroll + mMaxScrollX) / 2 ?
-                                        mMaxScrollX :
-                                        getScrollForPage(mNextPage);
-
-                        mScroller.setFinalX((int) (finalX * getScaleX()));
-                        // Ensure the scroll/snap doesn't happen too fast;
-                        int extraScrollDuration = OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION
-                                - mScroller.getDuration();
-                        if (extraScrollDuration > 0) {
-                            mScroller.extendDuration(extraScrollDuration);
-                        }
-                    }
-                    invalidate();
                 }
                 onScrollInteractionEnd();
             } else if (mTouchState == TOUCH_STATE_PREV_PAGE) {
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index eafdecc..abba9c4 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -547,6 +547,7 @@
         // created CellLayout.
         CellLayout newScreen = (CellLayout) LayoutInflater.from(getContext()).inflate(
                         R.layout.workspace_screen, this, false /* attachToRoot */);
+        newScreen.getShortcutsAndWidgets().setId(R.id.workspace_page_container);
         int paddingLeftRight = mLauncher.getDeviceProfile().cellLayoutPaddingLeftRightPx;
         int paddingBottom = mLauncher.getDeviceProfile().cellLayoutBottomPaddingPx;
         newScreen.setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom);
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 72ba418..8993978 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -490,6 +490,12 @@
         });
     }
 
+    @Override
+    public void getDrawingRect(Rect outRect) {
+        super.getDrawingRect(outRect);
+        outRect.offset(0, (int) getTranslationY());
+    }
+
     public class AdapterHolder {
         public static final int MAIN = 0;
         public static final int WORK = 1;
diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
index 50fb0a5..164728a 100644
--- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java
+++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.anim;
 
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+
 import android.animation.Animator;
 import android.animation.Animator.AnimatorListener;
 import android.animation.AnimatorListenerAdapter;
@@ -72,7 +74,7 @@
         mOnCancelRunnable = onCancelRunnable;
 
         mAnimationPlayer = ValueAnimator.ofFloat(0, 1);
-        mAnimationPlayer.setInterpolator(Interpolators.LINEAR);
+        mAnimationPlayer.setInterpolator(LINEAR);
         mAnimationPlayer.addListener(new OnAnimationEndDispatcher());
         mAnimationPlayer.addUpdateListener(this);
 
@@ -107,6 +109,10 @@
         return mDuration;
     }
 
+    public TimeInterpolator getInterpolator() {
+        return mAnim.getInterpolator() != null ? mAnim.getInterpolator() : LINEAR;
+    }
+
     /**
      * Starts playing the animation forward from current position.
      */
diff --git a/src/com/android/launcher3/notification/NotificationMainView.java b/src/com/android/launcher3/notification/NotificationMainView.java
index 33caded..5c0e259 100644
--- a/src/com/android/launcher3/notification/NotificationMainView.java
+++ b/src/com/android/launcher3/notification/NotificationMainView.java
@@ -116,6 +116,10 @@
      */
     public void applyNotificationInfo(NotificationInfo mainNotification, boolean animate) {
         mNotificationInfo = mainNotification;
+        NotificationListener listener = NotificationListener.getInstanceIfConnected();
+        if (listener != null) {
+            listener.setNotificationsShown(new String[] {mNotificationInfo.notificationKey});
+        }
         CharSequence title = mNotificationInfo.title;
         CharSequence text = mNotificationInfo.text;
         if (!TextUtils.isEmpty(title) && !TextUtils.isEmpty(text)) {
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index 9098777..be666a6 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -266,11 +266,7 @@
         }
 
         // Insets are added later, so subtract them now.
-        if (mIsRtl) {
-            x += insets.right;
-        } else {
-            x -= insets.left;
-        }
+        x -= insets.left;
         y -= insets.top;
 
         mGravity = 0;
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index ff3b425..955177a 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -29,6 +29,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
+import android.os.SystemClock;
 import android.view.HapticFeedbackConstants;
 import android.view.MotionEvent;
 
@@ -88,6 +89,7 @@
     private AnimatorSet mAtomicAnim;
     // True if we want to resume playing atomic components when mAtomicAnim completes.
     private boolean mScheduleResumeAtomicComponent;
+    private AutoPlayAtomicAnimationInfo mAtomicAnimAutoPlayInfo;
 
     private boolean mPassedOverviewAtomicThreshold;
     // mAtomicAnim plays the atomic components of the state animations when we pass the threshold.
@@ -235,12 +237,17 @@
         if (mCurrentAnimation == null) {
             mFromState = mStartState;
             mToState = null;
-            mAtomicComponentsController = null;
+            cancelAnimationControllers();
             reinitCurrentAnimation(false, mDetector.wasInitialTouchPositive());
             mDisplacementShift = 0;
         } else {
             mCurrentAnimation.pause();
             mStartProgress = mCurrentAnimation.getProgressFraction();
+
+            mAtomicAnimAutoPlayInfo = null;
+            if (mAtomicComponentsController != null) {
+                mAtomicComponentsController.pause();
+            }
         }
         mCanBlockFling = mFromState == NORMAL;
         mFlingBlockCheck.unblockFling();
@@ -315,6 +322,7 @@
                         return;
                     }
                     cancelAtomicComponentsController();
+
                     if (mCurrentAnimation != null) {
                         mAtomicComponentsStartProgress = mCurrentAnimation.getProgressFraction();
                         long duration = (long) (getShiftRange() * 2);
@@ -322,6 +330,7 @@
                                 createAtomicAnimForState(mFromState, mToState, duration), duration);
                         mAtomicComponentsController.dispatchOnStart();
                         mAtomicComponentsTargetState = mToState;
+                        maybeAutoPlayAtomicComponentsAnim();
                     }
                 }
             });
@@ -354,6 +363,8 @@
 
         final LauncherState targetState;
         final float progress = mCurrentAnimation.getProgressFraction();
+        final float interpolatedProgress = mCurrentAnimation.getInterpolator()
+                .getInterpolation(progress);
         if (fling) {
             targetState =
                     Float.compare(Math.signum(velocity), Math.signum(mProgressMultiplier)) == 0
@@ -362,7 +373,7 @@
         } else {
             float successProgress = mToState == ALL_APPS
                     ? MIN_PROGRESS_TO_ALL_APPS : SUCCESS_TRANSITION_PROGRESS;
-            targetState = (progress > successProgress) ? mToState : mFromState;
+            targetState = (interpolatedProgress > successProgress) ? mToState : mFromState;
         }
 
         final float endProgress;
@@ -414,16 +425,8 @@
             mLauncher.getAppsView().addSpringFromFlingUpdateListener(anim, velocity);
         }
         anim.start();
-        if (mAtomicAnim == null) {
-            startAtomicComponentsAnim(endProgress, anim.getDuration());
-        } else {
-            mAtomicAnim.addListener(new AnimationSuccessListener() {
-                @Override
-                public void onAnimationSuccess(Animator animator) {
-                    startAtomicComponentsAnim(endProgress, anim.getDuration());
-                }
-            });
-        }
+        mAtomicAnimAutoPlayInfo = new AutoPlayAtomicAnimationInfo(endProgress, anim.getDuration());
+        maybeAutoPlayAtomicComponentsAnim();
     }
 
     /**
@@ -433,18 +436,32 @@
      * the non-atomic components, which only happens if we reinit before the atomic animation
      * finishes.
      */
-    private void startAtomicComponentsAnim(float toProgress, long duration) {
-        if (mAtomicComponentsController != null) {
-            ValueAnimator atomicAnim = mAtomicComponentsController.getAnimationPlayer();
-            atomicAnim.setFloatValues(mAtomicComponentsController.getProgressFraction(), toProgress);
-            atomicAnim.setDuration(duration);
+    private void maybeAutoPlayAtomicComponentsAnim() {
+        if (mAtomicComponentsController == null || mAtomicAnimAutoPlayInfo == null) {
+            return;
+        }
+
+        final AnimatorPlaybackController controller = mAtomicComponentsController;
+        ValueAnimator atomicAnim = controller.getAnimationPlayer();
+        atomicAnim.setFloatValues(controller.getProgressFraction(),
+                mAtomicAnimAutoPlayInfo.toProgress);
+        long duration = mAtomicAnimAutoPlayInfo.endTime - SystemClock.elapsedRealtime();
+        mAtomicAnimAutoPlayInfo = null;
+        if (duration <= 0) {
             atomicAnim.start();
+            atomicAnim.end();
+            mAtomicComponentsController = null;
+        } else {
+            atomicAnim.setDuration(duration);
             atomicAnim.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
-                    mAtomicComponentsController = null;
+                    if (mAtomicComponentsController == controller) {
+                        mAtomicComponentsController = null;
+                    }
                 }
             });
+            atomicAnim.start();
         }
     }
 
@@ -474,6 +491,10 @@
     }
 
     protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
+        if (mAtomicComponentsController != null) {
+            mAtomicComponentsController.getAnimationPlayer().end();
+            mAtomicComponentsController = null;
+        }
         cancelAnimationControllers();
         boolean shouldGoToTargetState = true;
         if (mPendingAnimation != null) {
@@ -521,5 +542,17 @@
             mAtomicComponentsController.getAnimationPlayer().cancel();
             mAtomicComponentsController = null;
         }
+        mAtomicAnimAutoPlayInfo = null;
+    }
+
+    private static class AutoPlayAtomicAnimationInfo {
+
+        public final float toProgress;
+        public final long endTime;
+
+        AutoPlayAtomicAnimationInfo(float toProgress, long duration) {
+            this.toProgress = toProgress;
+            this.endTime = duration + SystemClock.elapsedRealtime();
+        }
     }
 }
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index c17857f..db4c492 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -153,7 +153,10 @@
     }
 
     public static boolean onWidgetsClicked(View view) {
-        Launcher launcher = Launcher.getLauncher(view.getContext());
+        return openWidgets(Launcher.getLauncher(view.getContext()));
+    }
+
+    public static boolean openWidgets(Launcher launcher) {
         if (launcher.getPackageManager().isSafeMode()) {
             Toast.makeText(launcher, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
             return false;
diff --git a/tests/Android.mk b/tests/Android.mk
index e8797a7..f6f02fe 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -22,7 +22,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_FULL_LIBS_MANIFEST_FILES := $(LOCAL_PATH)/AndroidManifest-common.xml
 
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := 28
 LOCAL_MIN_SDK_VERSION := 21
 
 LOCAL_PACKAGE_NAME := Launcher3Tests