Merge "Move default pip position to right above the shelf (Pt. Launcher)" into ub-launcher3-master
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index c3ddfd6..fde22eb 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -34,7 +34,7 @@
     <permission
         android:name="com.android.launcher3.permission.READ_SETTINGS"
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
-        android:protectionLevel="normal"
+        android:protectionLevel="signatureOrSystem"
         android:label="@string/permlab_read_settings"
         android:description="@string/permdesc_read_settings"/>
     <permission
diff --git a/quickstep/res/drawable-hdpi/recents_horizontal_scrim_left.png b/quickstep/res/drawable-hdpi/recents_horizontal_scrim_left.png
deleted file mode 100644
index d4f995d..0000000
--- a/quickstep/res/drawable-hdpi/recents_horizontal_scrim_left.png
+++ /dev/null
Binary files differ
diff --git a/quickstep/res/drawable-hdpi/recents_horizontal_scrim_right.png b/quickstep/res/drawable-hdpi/recents_horizontal_scrim_right.png
deleted file mode 100644
index 9013c1a..0000000
--- a/quickstep/res/drawable-hdpi/recents_horizontal_scrim_right.png
+++ /dev/null
Binary files differ
diff --git a/quickstep/res/drawable-mdpi/recents_horizontal_scrim_left.png b/quickstep/res/drawable-mdpi/recents_horizontal_scrim_left.png
deleted file mode 100644
index 6e924ec..0000000
--- a/quickstep/res/drawable-mdpi/recents_horizontal_scrim_left.png
+++ /dev/null
Binary files differ
diff --git a/quickstep/res/drawable-mdpi/recents_horizontal_scrim_right.png b/quickstep/res/drawable-mdpi/recents_horizontal_scrim_right.png
deleted file mode 100644
index 33d0edd..0000000
--- a/quickstep/res/drawable-mdpi/recents_horizontal_scrim_right.png
+++ /dev/null
Binary files differ
diff --git a/quickstep/res/drawable-xhdpi/recents_horizontal_scrim_left.png b/quickstep/res/drawable-xhdpi/recents_horizontal_scrim_left.png
deleted file mode 100644
index 20c85e6..0000000
--- a/quickstep/res/drawable-xhdpi/recents_horizontal_scrim_left.png
+++ /dev/null
Binary files differ
diff --git a/quickstep/res/drawable-xhdpi/recents_horizontal_scrim_right.png b/quickstep/res/drawable-xhdpi/recents_horizontal_scrim_right.png
deleted file mode 100644
index 58a1ca0..0000000
--- a/quickstep/res/drawable-xhdpi/recents_horizontal_scrim_right.png
+++ /dev/null
Binary files differ
diff --git a/quickstep/res/drawable-xxhdpi/recents_horizontal_scrim_left.png b/quickstep/res/drawable-xxhdpi/recents_horizontal_scrim_left.png
deleted file mode 100644
index 9d3dc31..0000000
--- a/quickstep/res/drawable-xxhdpi/recents_horizontal_scrim_left.png
+++ /dev/null
Binary files differ
diff --git a/quickstep/res/drawable-xxhdpi/recents_horizontal_scrim_right.png b/quickstep/res/drawable-xxhdpi/recents_horizontal_scrim_right.png
deleted file mode 100644
index 7fb248b..0000000
--- a/quickstep/res/drawable-xxhdpi/recents_horizontal_scrim_right.png
+++ /dev/null
Binary files differ
diff --git a/quickstep/res/drawable-xxxhdpi/recents_horizontal_scrim_left.png b/quickstep/res/drawable-xxxhdpi/recents_horizontal_scrim_left.png
deleted file mode 100644
index 49ec7aa..0000000
--- a/quickstep/res/drawable-xxxhdpi/recents_horizontal_scrim_left.png
+++ /dev/null
Binary files differ
diff --git a/quickstep/res/drawable-xxxhdpi/recents_horizontal_scrim_right.png b/quickstep/res/drawable-xxxhdpi/recents_horizontal_scrim_right.png
deleted file mode 100644
index a8922e2..0000000
--- a/quickstep/res/drawable-xxxhdpi/recents_horizontal_scrim_right.png
+++ /dev/null
Binary files differ
diff --git a/quickstep/res/drawable/ic_empty_recents.xml b/quickstep/res/drawable/ic_empty_recents.xml
new file mode 100644
index 0000000..5183733
--- /dev/null
+++ b/quickstep/res/drawable/ic_empty_recents.xml
@@ -0,0 +1,30 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="92dp"
+    android:height="80dp"
+    android:viewportWidth="92.0"
+    android:viewportHeight="80.0"
+    android:tint="?android:attr/textColorPrimary">
+    <path
+        android:fillColor="#AAFFFFFF"
+        android:pathData="M18,6H2C0.9,6,0,6.9,0,8v64c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V8C20,6.9,19.1,6,18,6z M16,69.25
+        c0,0.41-0.34,0.75-0.75,0.75H4.75C4.34,70,4,69.66,4,69.25v-58.5C4,10.34,4.34,10,4.75,10h10.5c0.41,0,0.75,0.34,0.75,0.75V69.25
+        z M90,6H74c-1.1,0-2,0.9-2,2v64c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V8C92,6.9,91.1,6,90,6z M88,69.25c0,0.41-0.34,0.75-0.75,0.75
+        h-10.5C76.34,70,76,69.66,76,69.25v-58.5c0-0.41,0.34-0.75,0.75-0.75h10.5c0.41,0,0.75,0.34,0.75,0.75V69.25z M64,0H28
+        c-2.21,0-4,1.79-4,4v72c0,2.21,1.79,4,4,4h36c2.21,0,4-1.79,4-4V4C68,1.79,66.21,0,64,0z M64,75c0,0.55-0.45,1-1,1H29
+        c-0.55,0-1-0.45-1-1V5c0-0.55,0.45-1,1-1h34c0.55,0,1,0.45,1,1V75z"/>
+</vector>
diff --git a/quickstep/res/layout/drag_handle_indicator.xml b/quickstep/res/layout/drag_handle_indicator.xml
new file mode 100644
index 0000000..9ee05d5
--- /dev/null
+++ b/quickstep/res/layout/drag_handle_indicator.xml
@@ -0,0 +1,23 @@
+<?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.QuickstepDragIndicator
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/drag_indicator"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:contentDescription="@string/accessibility_desc_recent_apps"
+    android:scaleType="centerInside"
+    android:tint="?attr/workspaceTextColor" />
diff --git a/quickstep/res/layout/overview_panel.xml b/quickstep/res/layout/overview_panel.xml
index e2f9ba8..89e0571 100644
--- a/quickstep/res/layout/overview_panel.xml
+++ b/quickstep/res/layout/overview_panel.xml
@@ -21,7 +21,6 @@
     android:layout_height="match_parent"
     android:clipChildren="false"
     android:clipToPadding="false"
-    android:alpha="0.0"
     android:visibility="invisible"
     android:focusableInTouchMode="true" >
 
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index e61e359..e354193 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -22,7 +22,6 @@
     <dimen name="task_corner_radius">2dp</dimen>
     <dimen name="task_fade_length">20dp</dimen>
     <dimen name="recents_page_spacing">10dp</dimen>
-    <dimen name="recents_page_fade_length">100dp</dimen>
 
     <!-- The speed in dp/s at which the user needs to be scrolling in recents such that we start
              loading full resolution screenshots. -->
@@ -37,4 +36,8 @@
     <dimen name="recents_adjacent_trans_x">140dp</dimen>
     <dimen name="recents_adjacent_trans_y">80dp</dimen>
     <fraction name="recents_adjacent_scale">150%</fraction>
+
+    <dimen name="recents_empty_message_text_size">16sp</dimen>
+    <dimen name="recents_empty_message_text_padding">16dp</dimen>
+
 </resources>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index ec8eb52..bd37a06 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -29,4 +29,10 @@
 
     <!-- Text that shows above the navigation bar after launching a few apps -->
     <string name="recents_swipe_up_onboarding">Swipe up from the bottom to switch apps</string>
+
+    <!-- Content description for the recent apps panel (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_desc_recent_apps">Overview</string>
+
+    <!-- Recents: The empty recents string. [CHAR LIMIT=NONE] -->
+    <string name="recents_empty_message">No recent items</string>
 </resources>
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
index abbf45e..4b2763b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -110,11 +110,11 @@
     @Override
     public int getVisibleElements(Launcher launcher) {
         if (launcher.getDeviceProfile().isVerticalBarLayout()) {
-            // TODO: Remove hotseat from overview
-            return HOTSEAT_ICONS;
+            return NONE;
         } else {
-            return launcher.getAppsView().getFloatingHeaderView().hasVisibleContent()
-                    ? HOTSEAT_EXTRA | ALL_APPS_HEADER_EXTRA : HOTSEAT_ICONS | HOTSEAT_EXTRA;
+            return HOTSEAT_SEARCH_BOX | DRAG_HANDLE_INDICATOR |
+                    (launcher.getAppsView().getFloatingHeaderView().hasVisibleContent()
+                            ? ALL_APPS_HEADER_EXTRA : HOTSEAT_ICONS);
         }
     }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index b993c3c..9f7fef3 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -16,12 +16,11 @@
 package com.android.launcher3.uioverrides;
 
 import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.anim.AlphaUpdateListener.updateVisibility;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATION;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
 import static com.android.quickstep.views.LauncherRecentsView.TRANSLATION_X_FACTOR;
 import static com.android.quickstep.views.LauncherRecentsView.TRANSLATION_Y_FACTOR;
+import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
 
 import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
@@ -49,12 +48,12 @@
 
     @Override
     public void setState(LauncherState state) {
-        mRecentsView.setAlpha(state.overviewUi ? 1 : 0);
-        updateVisibility(mRecentsView, isAccessibilityEnabled(mLauncher));
+        mRecentsView.setContentAlpha(state.overviewUi ? 1 : 0);
         float[] translationFactor = state.getOverviewTranslationFactor(mLauncher);
         mRecentsView.setTranslationXFactor(translationFactor[0]);
         mRecentsView.setTranslationYFactor(translationFactor[1]);
         if (state.overviewUi) {
+            mRecentsView.updateEmptyMessage();
             mRecentsView.resetTaskVisuals();
         }
     }
@@ -83,7 +82,7 @@
         setter.setFloat(mRecentsView, TRANSLATION_Y_FACTOR,
                 translationFactor[1],
                 builder.getInterpolator(ANIM_OVERVIEW_TRANSLATION, LINEAR));
-        setter.setViewAlpha(mRecentsView, toState.overviewUi ? 1 : 0, LINEAR);
+        setter.setFloat(mRecentsView, CONTENT_ALPHA, toState.overviewUi ? 1 : 0, LINEAR);
 
         if (toState.overviewUi) {
             ValueAnimator updateAnim = ValueAnimator.ofFloat(0, 1);
@@ -93,6 +92,7 @@
             });
             updateAnim.setDuration(config.duration);
             builder.play(updateAnim);
+            mRecentsView.updateEmptyMessage();
         }
     }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index 80d63ae..e6d06da 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -22,14 +22,10 @@
 
 import android.content.Context;
 import android.content.SharedPreferences;
-import android.view.View;
-import android.view.View.AccessibilityDelegate;
 
 import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Hotseat;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherStateManager.StateHandler;
-import com.android.launcher3.R;
 import com.android.launcher3.util.TouchController;
 import com.android.quickstep.OverviewInteractionState;
 import com.android.quickstep.RecentsModel;
@@ -61,10 +57,6 @@
         }
     }
 
-    public static AccessibilityDelegate newPageIndicatorAccessibilityDelegate() {
-        return null;
-    }
-
     public static StateHandler[] getStateHandler(Launcher launcher) {
         return new StateHandler[] {
                 launcher.getAllAppsController(), launcher.getWorkspace(),
@@ -109,11 +101,4 @@
             model.onTrimMemory(level);
         }
     }
-
-    public static View[] getHotseatExtraContent(Hotseat hotseat) {
-        return new View[] {
-                hotseat.findViewById(R.id.drag_indicator),
-                hotseat.findViewById(R.id.search_container_hotseat),
-        };
-    }
 }
diff --git a/quickstep/src/com/android/quickstep/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/FallbackRecentsView.java
index 032d753..251e958 100644
--- a/quickstep/src/com/android/quickstep/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/FallbackRecentsView.java
@@ -16,8 +16,10 @@
 package com.android.quickstep;
 
 import android.content.Context;
+import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.view.View;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
@@ -32,6 +34,7 @@
     public FallbackRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         setOverviewStateEnabled(true);
+        updateEmptyMessage();
     }
 
     @Override
@@ -40,6 +43,18 @@
     }
 
     @Override
+    public void onViewAdded(View child) {
+        super.onViewAdded(child);
+        updateEmptyMessage();
+    }
+
+    @Override
+    public void onViewRemoved(View child) {
+        super.onViewRemoved(child);
+        updateEmptyMessage();
+    }
+
+    @Override
     public void setInsets(Rect insets) {
         mInsets.set(insets);
         DeviceProfile dp = mActivity.getDeviceProfile();
@@ -65,4 +80,10 @@
                 grid.widthPx - targetPadding.right - insets.right,
                 grid.heightPx - targetPadding.bottom - insets.bottom);
     }
+
+    @Override
+    public void draw(Canvas canvas) {
+        maybeDrawEmptyMessage(canvas);
+        super.draw(canvas);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/QuickScrubController.java b/quickstep/src/com/android/quickstep/QuickScrubController.java
index 4f379e6..d868d12 100644
--- a/quickstep/src/com/android/quickstep/QuickScrubController.java
+++ b/quickstep/src/com/android/quickstep/QuickScrubController.java
@@ -140,7 +140,7 @@
             int duration = Math.abs(pageToGoTo - mRecentsView.getNextPage())
                             * QUICKSCRUB_SNAP_DURATION_PER_PAGE;
             mRecentsView.snapToPage(pageToGoTo, duration);
-            mRecentsView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP,
+            mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
                     HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
         }
     }
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 392b73f..1e43202 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -30,6 +30,7 @@
 import android.support.annotation.WorkerThread;
 import android.util.LruCache;
 import android.util.SparseArray;
+import android.view.accessibility.AccessibilityManager;
 
 import com.android.launcher3.MainThreadExecutor;
 import com.android.launcher3.R;
@@ -85,6 +86,7 @@
     private ISystemUiProxy mSystemUiProxy;
     private boolean mClearAssistCacheOnStackChange = true;
     private final boolean mPreloadTasksInBackground;
+    private final AccessibilityManager mAccessibilityManager;
 
     private RecentsModel(Context context) {
         mContext = context;
@@ -111,6 +113,7 @@
 
         mTaskChangeId = 1;
         loadTasks(-1, null);
+        mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
     }
 
     public RecentsTaskLoader getRecentsTaskLoader() {
@@ -140,7 +143,7 @@
             // Preload the plan
             RecentsTaskLoadPlan loadPlan = new RecentsTaskLoadPlan(mContext);
             PreloadOptions opts = new PreloadOptions();
-            opts.loadTitles = false;
+            opts.loadTitles = mAccessibilityManager.isEnabled();
             loadPlan.preloadPlan(opts, mRecentsTaskLoader, taskId, UserHandle.myUserId());
             // Set the load plan on UI thread
             mMainThreadExecutor.execute(() -> {
@@ -187,7 +190,7 @@
         launchOpts.onlyLoadPausedActivities = true;
         launchOpts.loadThumbnails = true;
         PreloadOptions preloadOpts = new PreloadOptions();
-        preloadOpts.loadTitles = false;
+        preloadOpts.loadTitles = mAccessibilityManager.isEnabled();
         plan.preloadPlan(preloadOpts, mRecentsTaskLoader, -1, userId);
         mRecentsTaskLoader.loadTasks(plan, launchOpts);
     }
diff --git a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
index 08be0c8..2ebf252 100644
--- a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
+++ b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
@@ -16,8 +16,6 @@
 
 package com.android.quickstep;
 
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
-
 import android.content.ComponentName;
 import android.content.Intent;
 import android.graphics.Bitmap;
@@ -37,7 +35,6 @@
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.util.InstantAppResolver;
 import com.android.quickstep.views.RecentsView;
@@ -62,7 +59,6 @@
 public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut {
 
     private static final String TAG = "TaskSystemShortcut";
-    private static final int DISMISS_TASK_DURATION = 300;
 
     protected T mSystemShortcut;
 
@@ -206,14 +202,7 @@
             mRecentsView.removeIgnoreResetTask(mTaskView);
 
             // Start animating in the side pages once launcher has been resized
-            PendingAnimation pendingAnim = mRecentsView.createTaskDismissAnimation(mTaskView,
-                    false, false, DISMISS_TASK_DURATION);
-            AnimatorPlaybackController controller = AnimatorPlaybackController.wrap(
-                    pendingAnim.anim, DISMISS_TASK_DURATION);
-            controller.dispatchOnStart();
-            controller.setEndAction(() -> pendingAnim.finish(true));
-            controller.getAnimationPlayer().setInterpolator(FAST_OUT_SLOW_IN);
-            controller.start();
+            mRecentsView.dismissTask(mTaskView, false, false);
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 130d34c..55ed9a9 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -19,21 +19,13 @@
 
 import android.annotation.TargetApi;
 import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
 import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
-import android.graphics.Shader;
-import android.graphics.Shader.TileMode;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
+import android.view.View;
+import android.view.ViewDebug;
 import android.widget.FrameLayout;
 
 import com.android.launcher3.DeviceProfile;
@@ -75,14 +67,11 @@
                 }
             };
 
-    private Bitmap mScrim;
-    private Paint mFadePaint;
-    private Shader mFadeShader;
-    private Matrix mFadeMatrix;
-    private boolean mScrimOnLeft;
-
+    @ViewDebug.ExportedProperty(category = "launcher")
     private float mTranslationXFactor;
+    @ViewDebug.ExportedProperty(category = "launcher")
     private float mTranslationYFactor;
+
     private Rect mPagePadding = new Rect();
 
     public LauncherRecentsView(Context context) {
@@ -95,6 +84,7 @@
 
     public LauncherRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
+        setContentAlpha(0);
     }
 
     @Override
@@ -109,60 +99,6 @@
         setPadding(padding.left, padding.top, padding.right, 0);
         mPagePadding.set(padding);
         mPagePadding.top += getResources().getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
-
-        if (dp.isVerticalBarLayout()) {
-            boolean wasScrimOnLeft = mScrimOnLeft;
-            mScrimOnLeft = dp.isSeascape();
-
-            if (mScrim == null || wasScrimOnLeft != mScrimOnLeft) {
-                Drawable scrim = getContext().getDrawable(mScrimOnLeft
-                        ? R.drawable.recents_horizontal_scrim_left
-                        : R.drawable.recents_horizontal_scrim_right);
-                if (scrim instanceof BitmapDrawable) {
-                    mScrim = ((BitmapDrawable) scrim).getBitmap();
-                    mFadePaint = new Paint();
-                    mFadePaint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
-                    mFadeShader = new BitmapShader(mScrim, TileMode.CLAMP, TileMode.REPEAT);
-                    mFadeMatrix = new Matrix();
-                } else {
-                    mScrim = null;
-                }
-            }
-        } else {
-            mScrim = null;
-            mFadePaint = null;
-            mFadeShader = null;
-            mFadeMatrix = null;
-        }
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        if (mScrim == null) {
-            super.draw(canvas);
-            return;
-        }
-
-        final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
-
-        int length = mScrim.getWidth();
-        int height = getHeight();
-        int saveCount = canvas.getSaveCount();
-
-        int scrimLeft;
-        if (mScrimOnLeft) {
-            scrimLeft = getScrollX();
-        } else {
-            scrimLeft = getScrollX() + getWidth() - length;
-        }
-        canvas.saveLayer(scrimLeft, 0, scrimLeft + length, height, null, flags);
-        super.draw(canvas);
-
-        mFadeMatrix.setTranslate(scrimLeft, 0);
-        mFadeShader.setLocalMatrix(mFadeMatrix);
-        mFadePaint.setShader(mFadeShader);
-        canvas.drawRect(scrimLeft, 0, scrimLeft + length, height, mFadePaint);
-        canvas.restoreToCount(saveCount);
     }
 
     @Override
@@ -173,19 +109,12 @@
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
-
-        int width = right - left;
-        setTranslationX(mTranslationXFactor * (mIsRtl ? -width : width));
         setTranslationYFactor(mTranslationYFactor);
     }
 
     public void setTranslationXFactor(float translationFactor) {
         mTranslationXFactor = translationFactor;
-        setTranslationX(translationFactor * (mIsRtl ? -getWidth() : getWidth()));
-    }
-
-    public float getTranslationXFactor() {
-        return mTranslationXFactor;
+        invalidate();
     }
 
     public void setTranslationYFactor(float translationFactor) {
@@ -193,7 +122,24 @@
         setTranslationY(mTranslationYFactor * (mPagePadding.bottom - mPagePadding.top));
     }
 
-    public float getTranslationYFactor() {
-        return mTranslationYFactor;
+    @Override
+    public void draw(Canvas canvas) {
+        maybeDrawEmptyMessage(canvas);
+        int count = canvas.save();
+        canvas.translate(mTranslationXFactor * (mIsRtl ? -getWidth() : getWidth()), 0);
+        super.draw(canvas);
+        canvas.restoreToCount(count);
+    }
+
+    @Override
+    public void onViewAdded(View child) {
+        super.onViewAdded(child);
+        updateEmptyMessage();
+    }
+
+    @Override
+    protected void onTaskStackUpdated() {
+        // Lazily update the empty message only when the task stack is reapplied
+        updateEmptyMessage();
     }
 }
diff --git a/quickstep/src/com/android/quickstep/views/QuickstepDragIndicator.java b/quickstep/src/com/android/quickstep/views/QuickstepDragIndicator.java
new file mode 100644
index 0000000..82ec84e
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/QuickstepDragIndicator.java
@@ -0,0 +1,78 @@
+/*
+ * 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.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import com.android.launcher3.R;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
+import com.android.launcher3.views.LauncherDragIndicator;
+
+public class QuickstepDragIndicator extends LauncherDragIndicator {
+
+    public QuickstepDragIndicator(Context context) {
+        super(context);
+    }
+
+    public QuickstepDragIndicator(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public QuickstepDragIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    private boolean isInOverview() {
+        return mLauncher.isInState(OVERVIEW);
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        if (isInOverview()) {
+            info.setContentDescription(getContext().getString(R.string.all_apps_button_label));
+        }
+    }
+
+    @Override
+    protected void initCustomActions(AccessibilityNodeInfo info) {
+        if (!isInOverview()) {
+            super.initCustomActions(info);
+        }
+    }
+
+    @Override
+    public void onClick(View view) {
+        if (isInOverview()) {
+            mLauncher.getUserEventDispatcher().logActionOnControl(
+                    Action.Touch.TAP, ControlType.ALL_APPS_BUTTON, ContainerType.TASKSWITCHER);
+            mLauncher.getStateManager().goToState(ALL_APPS);
+            super.onClick(view);
+        } else {
+            mLauncher.getUserEventDispatcher().logActionOnControl(
+                    Action.Touch.TAP, ControlType.ALL_APPS_BUTTON, ContainerType.WORKSPACE);
+            mLauncher.getStateManager().goToState(OVERVIEW);
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index db82286..97f40bc 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -18,6 +18,7 @@
 
 import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.launcher3.anim.Interpolators.ACCEL_2;
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 
 import android.animation.AnimatorSet;
@@ -28,21 +29,31 @@
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.graphics.Canvas;
+import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.text.Layout;
+import android.text.StaticLayout;
+import android.text.TextPaint;
 import android.util.ArraySet;
 import android.util.AttributeSet;
+import android.util.FloatProperty;
 import android.util.SparseBooleanArray;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewDebug;
 
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.PagedView;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.Themes;
 import com.android.quickstep.PendingAnimation;
 import com.android.quickstep.QuickScrubController;
 import com.android.quickstep.RecentsModel;
@@ -64,7 +75,23 @@
 public abstract class RecentsView<T extends BaseActivity>
         extends PagedView implements OnSharedPreferenceChangeListener {
 
+    public static final FloatProperty<RecentsView> CONTENT_ALPHA =
+            new FloatProperty<RecentsView>("contentAlpha") {
+
+
+        @Override
+        public void setValue(RecentsView recentsView, float v) {
+            recentsView.setContentAlpha(v);
+        }
+
+        @Override
+        public Float get(RecentsView recentsView) {
+            return recentsView.mContentAlpha;
+        }
+    };
+
     private static final String PREF_FLIP_RECENTS = "pref_flip_recents";
+    private static final int DISMISS_TASK_DURATION = 300;
 
     private static final Rect sTempStableInsets = new Rect();
 
@@ -106,9 +133,21 @@
 
     private PendingAnimation mPendingAnimation;
 
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private float mContentAlpha = 1;
+
     // Keeps track of task views whose visual state should not be reset
     private ArraySet<TaskView> mIgnoreResetTaskViews = new ArraySet<>();
 
+    // Variables for empty state
+    private final Drawable mEmptyIcon;
+    private final CharSequence mEmptyMessage;
+    private final TextPaint mEmptyMessagePaint;
+    private final Point mLastMeasureSize = new Point();
+    private final int mEmptyMessagePadding;
+    private boolean mShowEmptyMessage;
+    private Layout mEmptyTextLayout;
+
     public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         setPageSpacing(getResources().getDimensionPixelSize(R.dimen.recents_page_spacing));
@@ -122,6 +161,17 @@
         mModel = RecentsModel.getInstance(context);
 
         onSharedPreferenceChanged(Utilities.getPrefs(context), PREF_FLIP_RECENTS);
+
+        mEmptyIcon = context.getDrawable(R.drawable.ic_empty_recents);
+        mEmptyIcon.setCallback(this);
+        mEmptyMessage = context.getText(R.string.recents_empty_message);
+        mEmptyMessagePaint = new TextPaint();
+        mEmptyMessagePaint.setColor(Themes.getAttrColor(context, android.R.attr.textColorPrimary));
+        mEmptyMessagePaint.setTextSize(getResources()
+                .getDimension(R.dimen.recents_empty_message_text_size));
+        mEmptyMessagePadding = getResources()
+                .getDimensionPixelSize(R.dimen.recents_empty_message_text_padding);
+        setWillNotDraw(false);
     }
 
     @Override
@@ -226,6 +276,7 @@
         TaskStack stack = loadPlan != null ? loadPlan.getTaskStack() : null;
         if (stack == null) {
             removeAllViews();
+            onTaskStackUpdated();
             return;
         }
 
@@ -262,8 +313,11 @@
         if (oldChildCount != getChildCount()) {
             mQuickScrubController.snapToNextTaskIfAvailable();
         }
+        onTaskStackUpdated();
     }
 
+    protected void onTaskStackUpdated() { }
+
     public void resetTaskVisuals() {
         for (int i = getChildCount() - 1; i >= 0; i--) {
             TaskView taskView = (TaskView) getChildAt(i);
@@ -302,12 +356,10 @@
 
         float overviewHeight, overviewWidth;
         if (profile.isVerticalBarLayout()) {
-            float scrimLength = context.getResources()
-                    .getDimension(R.dimen.recents_page_fade_length);
             float maxPadding = Math.max(padding.left, padding.right);
 
             // Use the same padding on both sides for symmetry.
-            float availableWidth = taskWidth - 2 * Math.max(maxPadding, scrimLength);
+            float availableWidth = taskWidth - 2 * maxPadding;
             float availableHeight = profile.availableHeightPx - padding.top - padding.bottom
                     - sTempStableInsets.top;
             float scaledRatio = Math.min(availableWidth / taskWidth, availableHeight / taskHeight);
@@ -323,11 +375,31 @@
         padding.bottom = profile.availableHeightPx - padding.top - sTempStableInsets.top
                 - Math.round(overviewHeight);
         padding.left = padding.right = (int) ((profile.availableWidthPx - overviewWidth) / 2);
+
+        // If the height ratio is larger than the width ratio, the screenshot will get cropped
+        // at the bottom when swiping up. In this case, increase the top/bottom padding to make it
+        // the same aspect ratio.
+        Rect pageRect = new Rect();
+        getPageRect(profile, context, pageRect, padding);
+        float widthRatio = (float) pageRect.width() / taskWidth;
+        float heightRatio = (float) pageRect.height() / taskHeight;
+        if (heightRatio > widthRatio) {
+            float additionalVerticalPadding = pageRect.height() - widthRatio * taskHeight;
+            additionalVerticalPadding = Math.round(additionalVerticalPadding);
+            padding.top += additionalVerticalPadding / 2;
+            padding.bottom += additionalVerticalPadding / 2;
+        }
+
         return padding;
     }
 
     public static void getPageRect(DeviceProfile grid, Context context, Rect outRect) {
         Rect targetPadding = getPadding(grid, context);
+        getPageRect(grid, context, outRect, targetPadding);
+    }
+
+    private static void getPageRect(DeviceProfile grid, Context context, Rect outRect,
+            Rect targetPadding) {
         Rect insets = grid.getInsets();
         outRect.set(
                 targetPadding.left + insets.left,
@@ -610,6 +682,9 @@
     }
 
     private void snapToPageRelative(int delta) {
+        if (getPageCount() == 0) {
+            return;
+        }
         snapToPage((getNextPage() + getPageCount() + delta) % getPageCount());
     }
 
@@ -623,6 +698,17 @@
         }
     }
 
+    public void dismissTask(TaskView taskView, boolean animateTaskView, boolean removeTask) {
+        PendingAnimation pendingAnim = createTaskDismissAnimation(taskView, animateTaskView,
+                removeTask, DISMISS_TASK_DURATION);
+        AnimatorPlaybackController controller = AnimatorPlaybackController.wrap(
+                pendingAnim.anim, DISMISS_TASK_DURATION);
+        controller.dispatchOnStart();
+        controller.setEndAction(() -> pendingAnim.finish(true));
+        controller.getAnimationPlayer().setInterpolator(FAST_OUT_SLOW_IN);
+        controller.start();
+    }
+
     @Override
     public boolean dispatchKeyEvent(KeyEvent event) {
         if (event.getAction() == KeyEvent.ACTION_DOWN) {
@@ -636,6 +722,18 @@
                 case KeyEvent.KEYCODE_DPAD_LEFT:
                     snapToPageRelative(mIsRtl ? 1 : -1);
                     return true;
+                case KeyEvent.KEYCODE_DEL:
+                case KeyEvent.KEYCODE_FORWARD_DEL:
+                    dismissTask((TaskView) getChildAt(getNextPage()), true /*animateTaskView*/,
+                            true /*removeTask*/);
+                    return true;
+                case KeyEvent.KEYCODE_NUMPAD_DOT:
+                    if (event.isAltPressed()) {
+                        // Numpad DEL pressed while holding Alt.
+                        dismissTask((TaskView) getChildAt(getNextPage()), true /*animateTaskView*/,
+                                true /*removeTask*/);
+                        return true;
+                    }
             }
         }
         return super.dispatchKeyEvent(event);
@@ -649,4 +747,85 @@
         final TaskView nextTask = (TaskView) getChildAt(getNextPage());
         nextTask.launchTask(true);
     }
+
+    public void setContentAlpha(float alpha) {
+        if (mContentAlpha == alpha) {
+            return;
+        }
+
+        mContentAlpha = alpha;
+        for (int i = getChildCount() - 1; i >= 0; i--) {
+            getChildAt(i).setAlpha(alpha);
+        }
+
+        int alphaInt = Math.round(alpha * 255);
+        mEmptyMessagePaint.setAlpha(alphaInt);
+        mEmptyIcon.setAlpha(alphaInt);
+
+        setVisibility(alpha > 0 ? VISIBLE : GONE);
+    }
+
+    @Override
+    public void onViewAdded(View child) {
+        super.onViewAdded(child);
+        child.setAlpha(mContentAlpha);
+    }
+
+    public void updateEmptyMessage() {
+        boolean isEmpty = getChildCount() == 0;
+        boolean hasSizeChanged = mLastMeasureSize.x != getWidth()
+                || mLastMeasureSize.y != getHeight();
+        if (isEmpty == mShowEmptyMessage && !hasSizeChanged) {
+            return;
+        }
+        setContentDescription(isEmpty ? mEmptyMessage : "");
+        mShowEmptyMessage = isEmpty;
+        updateEmptyStateUi(hasSizeChanged);
+        invalidate();
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        updateEmptyStateUi(changed);
+    }
+
+    private void updateEmptyStateUi(boolean sizeChanged) {
+        boolean hasValidSize = getWidth() > 0 && getHeight() > 0;
+        if (sizeChanged && hasValidSize) {
+            mEmptyTextLayout = null;
+        }
+
+        if (mShowEmptyMessage && hasValidSize && mEmptyTextLayout == null) {
+            mLastMeasureSize.set(getWidth(), getHeight());
+            int availableWidth = mLastMeasureSize.x - mEmptyMessagePadding - mEmptyMessagePadding;
+            mEmptyTextLayout = StaticLayout.Builder.obtain(mEmptyMessage, 0, mEmptyMessage.length(),
+                    mEmptyMessagePaint, availableWidth)
+                    .setAlignment(Layout.Alignment.ALIGN_CENTER)
+                    .build();
+            int totalHeight = mEmptyTextLayout.getHeight()
+                    + mEmptyMessagePadding + mEmptyIcon.getIntrinsicHeight();
+
+            int top = (mLastMeasureSize.y - totalHeight) / 2;
+            int left = (mLastMeasureSize.x - mEmptyIcon.getIntrinsicWidth()) / 2;
+            mEmptyIcon.setBounds(left, top, left + mEmptyIcon.getIntrinsicWidth(),
+                    top + mEmptyIcon.getIntrinsicHeight());
+        }
+    }
+
+    @Override
+    protected boolean verifyDrawable(Drawable who) {
+        return super.verifyDrawable(who) || (mShowEmptyMessage && who == mEmptyIcon);
+    }
+
+    protected void maybeDrawEmptyMessage(Canvas canvas) {
+        if (mShowEmptyMessage && mEmptyTextLayout != null) {
+            mEmptyIcon.draw(canvas);
+            canvas.save();
+            canvas.translate(mEmptyMessagePadding,
+                    mEmptyIcon.getBounds().bottom + mEmptyMessagePadding);
+            mEmptyTextLayout.draw(canvas);
+            canvas.restore();
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index 87bb53b..4c8d69f 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -36,6 +36,7 @@
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.quickstep.TaskOverlayFactory;
 import com.android.quickstep.TaskOverlayFactory.TaskOverlay;
 import com.android.systemui.shared.recents.model.Task;
@@ -138,6 +139,7 @@
     }
 
     private void updateThumbnailMatrix() {
+        boolean rotate = false;
         if (mBitmapShader != null && mThumbnailData != null) {
             float scale = mThumbnailData.scale;
             float thumbnailWidth = mThumbnailData.thumbnail.getWidth() -
@@ -145,7 +147,8 @@
             float thumbnailHeight = mThumbnailData.thumbnail.getHeight() -
                     (mThumbnailData.insets.top + mThumbnailData.insets.bottom) * scale;
             final float thumbnailScale;
-
+            final DeviceProfile profile = BaseActivity.fromContext(getContext())
+                    .getDeviceProfile();
             if (getMeasuredWidth() == 0) {
                 // If we haven't measured , skip the thumbnail drawing and only draw the background
                 // color
@@ -153,48 +156,81 @@
             } else {
                 final Configuration configuration =
                         getContext().getApplicationContext().getResources().getConfiguration();
-                final DeviceProfile profile = BaseActivity.fromContext(getContext())
-                        .getDeviceProfile();
                 if (configuration.orientation == mThumbnailData.orientation) {
                     // If we are in the same orientation as the screenshot, just scale it to the
                     // width of the task view
                     thumbnailScale = getMeasuredWidth() / thumbnailWidth;
-                } else if (configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
-                    // Scale the landscape thumbnail up to app size, then scale that to the task
-                    // view size to match other portrait screenshots
-                    thumbnailScale = ((float) getMeasuredWidth() / profile.widthPx);
                 } else {
-                    // Otherwise, scale the screenshot to fit 1:1 in the current orientation
-                    thumbnailScale = 1;
+                    if (FeatureFlags.OVERVIEW_USE_SCREENSHOT_ORIENTATION) {
+                        rotate = true;
+                        // Scale the height (will be width after rotation) to the width of this view
+                        thumbnailScale = getMeasuredWidth() / thumbnailHeight;
+                    } else {
+                        if (configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
+                            // Scale the landscape thumbnail up to app size, then scale that to the
+                            // task view size to match other portrait screenshots
+                            thumbnailScale = ((float) getMeasuredWidth() / profile.widthPx);
+                        } else {
+                            // Otherwise, scale the screenshot to fit 1:1 in the current orientation
+                            thumbnailScale = 1;
+                        }
+                    }
                 }
             }
-            mMatrix.setTranslate(-mThumbnailData.insets.left * scale,
-                    -mThumbnailData.insets.top * scale);
+            if (rotate) {
+                int rotationDir = profile.isVerticalBarLayout() && !profile.isSeascape() ? -1 : 1;
+                mMatrix.setRotate(90 * rotationDir);
+                Rect thumbnailInsets  = mThumbnailData.insets;
+                int newLeftInset = rotationDir == 1 ? thumbnailInsets.bottom : thumbnailInsets.top;
+                int newTopInset = rotationDir == 1 ? thumbnailInsets.left : thumbnailInsets.right;
+                mMatrix.postTranslate(-newLeftInset * scale, -newTopInset * scale);
+                if (rotationDir == -1) {
+                    // Crop the right/bottom side of the screenshot rather than left/top
+                    float excessHeight = thumbnailWidth * thumbnailScale - getMeasuredHeight();
+                    mMatrix.postTranslate(0, -excessHeight);
+                }
+                // Move the screenshot to the thumbnail window (rotation moved it out).
+                if (rotationDir == 1) {
+                    mMatrix.postTranslate(mThumbnailData.thumbnail.getHeight(), 0);
+                } else {
+                    mMatrix.postTranslate(0, mThumbnailData.thumbnail.getWidth());
+                }
+            } else {
+                mMatrix.setTranslate(-mThumbnailData.insets.left * scale,
+                        -mThumbnailData.insets.top * scale);
+            }
             mMatrix.postScale(thumbnailScale, thumbnailScale);
             mBitmapShader.setLocalMatrix(mMatrix);
 
-            float bitmapHeight = Math.max(thumbnailHeight * thumbnailScale, 0);
             Shader shader = mBitmapShader;
-            if (bitmapHeight < getMeasuredHeight()) {
-                int color = mPaint.getColor();
-                LinearGradient fade = new LinearGradient(
-                        0, bitmapHeight - mFadeLength, 0, bitmapHeight,
-                        color & 0x00FFFFFF, color, Shader.TileMode.CLAMP);
-                shader = new ComposeShader(fade, shader, Mode.DST_OVER);
-            }
+            if (!FeatureFlags.OVERVIEW_USE_SCREENSHOT_ORIENTATION) {
+                float bitmapHeight = Math.max(thumbnailHeight * thumbnailScale, 0);
+                if (Math.round(bitmapHeight) < getMeasuredHeight()) {
+                    int color = mPaint.getColor();
+                    LinearGradient fade = new LinearGradient(
+                            0, bitmapHeight - mFadeLength, 0, bitmapHeight,
+                            color & 0x00FFFFFF, color, Shader.TileMode.CLAMP);
+                    shader = new ComposeShader(fade, shader, Mode.DST_OVER);
+                }
 
-            float bitmapWidth = Math.max(thumbnailWidth * thumbnailScale, 0);
-            if (bitmapWidth < getMeasuredWidth()) {
-                int color = mPaint.getColor();
-                LinearGradient fade = new LinearGradient(
-                        bitmapWidth - mFadeLength, 0, bitmapWidth, 0,
-                        color & 0x00FFFFFF, color, Shader.TileMode.CLAMP);
-                shader = new ComposeShader(fade, shader, Mode.DST_OVER);
+                float bitmapWidth = Math.max(thumbnailWidth * thumbnailScale, 0);
+                if (Math.round(bitmapWidth) < getMeasuredWidth()) {
+                    int color = mPaint.getColor();
+                    LinearGradient fade = new LinearGradient(
+                            bitmapWidth - mFadeLength, 0, bitmapWidth, 0,
+                            color & 0x00FFFFFF, color, Shader.TileMode.CLAMP);
+                    shader = new ComposeShader(fade, shader, Mode.DST_OVER);
+                }
             }
             mPaint.setShader(shader);
         }
 
-        mOverlay.setTaskInfo(mTask, mThumbnailData, mMatrix);
+        if (rotate) {
+            // The overlay doesn't really work when the screenshot is rotated, so don't add it.
+            mOverlay.reset();
+        } else {
+            mOverlay.setTaskInfo(mTask, mThumbnailData, mMatrix);
+        }
         invalidate();
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 7de1600..9884a48 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -96,6 +96,7 @@
         mTask = task;
         mSnapshotView.bind();
         task.addCallback(this);
+        setContentDescription(task.titleDescription);
     }
 
     public Task getTask() {
@@ -176,6 +177,12 @@
         setScaleY(scale);
     }
 
+    @Override
+    public boolean hasOverlappingRendering() {
+        // TODO: Clip-out the icon region from the thumbnail, since they are overlapping.
+        return false;
+    }
+
     private static final class TaskOutlineProvider extends ViewOutlineProvider {
 
         private final int mMarginTop;
diff --git a/res/layout/all_apps_fast_scroller.xml b/res/layout/all_apps_fast_scroller.xml
index d858d3e..5537bc6 100644
--- a/res/layout/all_apps_fast_scroller.xml
+++ b/res/layout/all_apps_fast_scroller.xml
@@ -21,8 +21,8 @@
         android:id="@+id/fast_scroller_popup"
         style="@style/FastScrollerPopup"
         android:layout_alignParentEnd="true"
-        android:layout_marginEnd="@dimen/fastscroll_popup_margin"
-        android:layout_marginTop="@dimen/all_apps_search_bar_field_height_and_margin" />
+        android:layout_below="@+id/search_container_all_apps"
+        android:layout_marginEnd="@dimen/fastscroll_popup_margin" />
 
     <com.android.launcher3.views.RecyclerViewFastScroller
         android:id="@+id/fast_scroller"
@@ -30,8 +30,8 @@
         android:layout_height="wrap_content"
         android:layout_alignParentBottom="true"
         android:layout_alignParentEnd="true"
+        android:layout_below="@+id/search_container_all_apps"
         android:layout_marginEnd="@dimen/fastscroll_end_margin"
-        android:layout_marginTop="@dimen/all_apps_search_bar_field_height_and_margin"
         launcher:canThumbDetach="true" />
 
 </merge>
\ No newline at end of file
diff --git a/res/layout/all_apps_floating_header.xml b/res/layout/all_apps_floating_header.xml
index f88c600..c4240f8 100644
--- a/res/layout/all_apps_floating_header.xml
+++ b/res/layout/all_apps_floating_header.xml
@@ -18,10 +18,10 @@
     android:id="@+id/all_apps_header"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:layout_marginTop="@dimen/all_apps_search_bar_field_height_and_margin"
+    android:layout_below="@id/search_container_all_apps"
     android:clipToPadding="false"
-    android:orientation="vertical"
-    android:paddingTop="@dimen/all_apps_header_top_padding" >
+    android:paddingTop="@dimen/all_apps_header_top_padding"
+    android:orientation="vertical" >
 
     <com.android.launcher3.allapps.PersonalWorkSlidingTabStrip
         android:id="@+id/tabs"
diff --git a/res/layout/all_apps_rv_layout.xml b/res/layout/all_apps_rv_layout.xml
index 8eba7fe..c353b36 100644
--- a/res/layout/all_apps_rv_layout.xml
+++ b/res/layout/all_apps_rv_layout.xml
@@ -19,7 +19,7 @@
     android:id="@+id/apps_list_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:layout_marginTop="@dimen/all_apps_search_bar_field_height_and_margin"
+    android:layout_below="@id/search_container_all_apps"
     android:clipToPadding="false"
     android:descendantFocusability="afterDescendants"
     android:focusable="true" />
diff --git a/res/layout/all_apps_tabs.xml b/res/layout/all_apps_tabs.xml
index fea2eea..2accd2d 100644
--- a/res/layout/all_apps_tabs.xml
+++ b/res/layout/all_apps_tabs.xml
@@ -20,8 +20,9 @@
     android:id="@+id/all_apps_tabs_view_pager"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:layout_below="@id/search_container_all_apps"
     android:layout_gravity="center_horizontal|top"
-    android:layout_marginTop="@dimen/all_apps_tabs_top_margin"
+    android:layout_marginTop="@dimen/all_apps_header_tab_height"
     android:clipChildren="true"
     android:clipToPadding="false"
     android:descendantFocusability="afterDescendants"
diff --git a/res/layout/drag_handle_indicator.xml b/res/layout/drag_handle_indicator.xml
new file mode 100644
index 0000000..d5a7b8a
--- /dev/null
+++ b/res/layout/drag_handle_indicator.xml
@@ -0,0 +1,23 @@
+<?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.launcher3.views.LauncherDragIndicator
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/drag_indicator"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:contentDescription="@string/all_apps_button_label"
+    android:scaleType="centerInside"
+    android:tint="?attr/workspaceTextColor" />
diff --git a/res/layout/hotseat.xml b/res/layout/hotseat.xml
index 73c0e52..00f0b5f 100644
--- a/res/layout/hotseat.xml
+++ b/res/layout/hotseat.xml
@@ -17,6 +17,7 @@
     android:theme="@style/HomeScreenElementTheme"
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res-auto">
+
     <com.android.launcher3.CellLayout
         android:id="@+id/layout"
         android:layout_width="wrap_content"
diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml
index 4693917..a4acf06 100644
--- a/res/layout/launcher.xml
+++ b/res/layout/launcher.xml
@@ -49,7 +49,8 @@
         <com.android.launcher3.pageindicators.WorkspacePageIndicator
             android:id="@+id/page_indicator"
             android:layout_width="match_parent"
-            android:layout_height="@dimen/dynamic_grid_min_page_indicator_size"
+            android:layout_height="4dp"
+            android:layout_gravity="bottom|center_horizontal"
             android:theme="@style/HomeScreenElementTheme" />
 
         <include
@@ -63,6 +64,9 @@
             android:layout_height="match_parent"
             android:visibility="invisible" />
 
+        <include android:id="@+id/drag_indicator"
+            layout="@layout/drag_handle_indicator" />
+
         <!-- DO NOT CHANGE THE ID -->
         <include
             android:id="@+id/hotseat"
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
index 6cf23ad..7b52529 100644
--- a/res/values-land/dimens.xml
+++ b/res/values-land/dimens.xml
@@ -31,9 +31,6 @@
     <dimen name="dynamic_grid_cell_layout_padding">0dp</dimen>
     <dimen name="dynamic_grid_cell_layout_bottom_padding">5.5dp</dimen>
 
-    <!-- Folders -->
-    <dimen name="folder_preview_padding">2dp</dimen>
-
     <!-- Hotseat -->
     <!-- Will be set to equal the hotseat icon size. -->
     <dimen name="dynamic_grid_hotseat_size">0dp</dimen>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index b7e7ca1..a2f7286 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -95,12 +95,7 @@
 
     <dimen name="all_apps_divider_margin_vertical">8dp</dimen>
 
-    <!-- Derived dimens -->
-    <dimen name="all_apps_search_bar_field_height_and_margin">56dp</dimen>
-    <!-- all_apps_search_bar_field_height_and_margin + all_apps_header_tab_height -->
-    <dimen name="all_apps_tabs_top_margin">106dp</dimen>
-
- <!-- Widget tray -->
+<!-- Widget tray -->
     <dimen name="widget_preview_label_vertical_padding">8dp</dimen>
     <dimen name="widget_preview_label_horizontal_padding">16dp</dimen>
 
@@ -142,8 +137,6 @@
     <dimen name="spring_loaded_panel_border">1dp</dimen>
 
 <!-- Folders -->
-    <!-- The size of the padding on the preview background drawable -->
-    <dimen name="folder_preview_padding">10dp</dimen>
     <dimen name="page_indicator_dot_size">8dp</dimen>
 
     <dimen name="folder_cell_x_padding">9dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 7d5d81c..bcb90e3 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -74,7 +74,7 @@
     <!-- Drag and drop -->
     <!-- Message to tell the user to press and hold on a shortcut to add it [CHAR_LIMIT=50] -->
     <string name="long_press_shortcut_to_add">Touch &amp; hold to pick up a shortcut.</string>
-    <!-- Accessibility spoken hint message in deep shortcut menu, which allows user to add a shortcut. Custom action is the label for additional accessibility actions available in this mode [CHAR_LIMIT=100] -->
+    <!-- Accessibility spoken hint message in deep shortcut menu, which allows user to add a shortcut. Custom action is the label for additional accessibility actions available in this mode [CHAR_LIMIT=200] -->
     <string name="long_accessible_way_to_add_shortcut">Double-tap &amp; hold to pick up a shortcut or use custom actions.</string>
 
     <skip />
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 13971ad..6f35752 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -28,6 +28,7 @@
 
 import com.android.launcher3.CellLayout.ContainerType;
 import com.android.launcher3.badge.BadgeRenderer;
+import com.android.launcher3.graphics.IconNormalizer;
 
 public class DeviceProfile {
 
@@ -81,9 +82,8 @@
     public int workspaceCellPaddingXPx;
 
     // Folder
-    public int folderBackgroundOffset;
     public int folderIconSizePx;
-    public int folderIconPreviewPadding;
+    public int folderIconOffsetYPx;
 
     // Folder cell
     public int folderCellWidthPx;
@@ -117,6 +117,7 @@
     // Insets
     private final Rect mInsets = new Rect();
     public final Rect workspacePadding = new Rect();
+    private final Rect mHotseatPadding = new Rect();
 
     // Icon badges
     public BadgeRenderer mBadgeRenderer;
@@ -339,9 +340,8 @@
         }
 
         // Folder icon
-        folderBackgroundOffset = -iconDrawablePaddingPx;
-        folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset;
-        folderIconPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding);
+        folderIconSizePx = IconNormalizer.getNormalizedCircleSize(iconSizePx);
+        folderIconOffsetYPx = (iconSizePx - folderIconSizePx) / 2;
     }
 
     private void updateAvailableFolderCellDimensions(DisplayMetrics dm, Resources res) {
@@ -456,6 +456,33 @@
         }
     }
 
+    public Rect getHotseatLayoutPadding() {
+        if (isVerticalBarLayout()) {
+            if (isSeascape()) {
+                mHotseatPadding.set(
+                        mInsets.left, mInsets.top, hotseatBarSidePaddingPx, mInsets.bottom);
+            } else {
+                mHotseatPadding.set(
+                        hotseatBarSidePaddingPx, mInsets.top, mInsets.right, mInsets.bottom);
+            }
+        } else {
+
+            // We want the edges of the hotseat to line up with the edges of the workspace, but the
+            // icons in the hotseat are a different size, and so don't line up perfectly. To account
+            // for this, we pad the left and right of the hotseat with half of the difference of a
+            // workspace cell vs a hotseat cell.
+            float workspaceCellWidth = (float) widthPx / inv.numColumns;
+            float hotseatCellWidth = (float) widthPx / inv.numHotseatIcons;
+            int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2);
+            mHotseatPadding.set(
+                    hotseatAdjustment + workspacePadding.left + cellLayoutPaddingLeftRightPx,
+                    hotseatBarTopPaddingPx,
+                    hotseatAdjustment + workspacePadding.right + cellLayoutPaddingLeftRightPx,
+                    hotseatBarBottomPaddingPx + mInsets.bottom + cellLayoutBottomPaddingPx);
+        }
+        return mHotseatPadding;
+    }
+
     /**
      * @return the bounds for which the open folders should be contained within
      */
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index dec6cb4..d025a9b 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -20,7 +20,6 @@
 import static com.android.launcher3.ButtonDropTarget.TOOLTIP_LEFT;
 import static com.android.launcher3.ButtonDropTarget.TOOLTIP_RIGHT;
 import static com.android.launcher3.anim.AlphaUpdateListener.updateVisibility;
-import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
 
 import android.animation.TimeInterpolator;
 import android.content.Context;
@@ -47,7 +46,7 @@
     protected static final TimeInterpolator DEFAULT_INTERPOLATOR = Interpolators.ACCEL;
 
     private final Runnable mFadeAnimationEndRunnable =
-            () -> updateVisibility(DropTargetBar.this, isAccessibilityEnabled(getContext()));
+            () -> updateVisibility(DropTargetBar.this);
 
     @ViewDebug.ExportedProperty(category = "launcher")
     protected boolean mDeferOnDragEnd;
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 211a756..c6025fe 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -81,12 +81,17 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         mContent = findViewById(R.id.layout);
-
-        resetLayout();
     }
 
-    void resetLayout() {
+    void resetLayout(boolean hasVerticalHotseat) {
         mContent.removeAllViewsInLayout();
+        mHasVerticalHotseat = hasVerticalHotseat;
+        InvariantDeviceProfile idp = mLauncher.getDeviceProfile().inv;
+        if (hasVerticalHotseat) {
+            mContent.setGridSize(1, idp.numHotseatIcons);
+        } else {
+            mContent.setGridSize(idp.numHotseatIcons, 1);
+        }
 
         if (!FeatureFlags.NO_ALL_APPS_ICON) {
             // Add the Apps button
@@ -148,46 +153,24 @@
     public void setInsets(Rect insets) {
         FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
         DeviceProfile grid = mLauncher.getDeviceProfile();
-        mHasVerticalHotseat = mLauncher.getDeviceProfile().isVerticalBarLayout();
 
-        if (mHasVerticalHotseat) {
-            mContent.setGridSize(1, grid.inv.numHotseatIcons);
-
+        if (grid.isVerticalBarLayout()) {
             lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
             if (grid.isSeascape()) {
                 lp.gravity = Gravity.LEFT;
                 lp.width = grid.hotseatBarSizePx + insets.left + grid.hotseatBarSidePaddingPx;
-                getLayout().setPadding(
-                        insets.left, insets.top, grid.hotseatBarSidePaddingPx, insets.bottom);
-
             } else {
                 lp.gravity = Gravity.RIGHT;
                 lp.width = grid.hotseatBarSizePx + insets.right + grid.hotseatBarSidePaddingPx;
-                getLayout().setPadding(
-                        grid.hotseatBarSidePaddingPx, insets.top, insets.right, insets.bottom);
             }
         } else {
-            mContent.setGridSize(grid.inv.numHotseatIcons, 1);
-
             lp.gravity = Gravity.BOTTOM;
             lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
             lp.height = grid.hotseatBarSizePx + insets.bottom;
-
-            // We want the edges of the hotseat to line up with the edges of the workspace, but the
-            // icons in the hotseat are a different size, and so don't line up perfectly. To account for
-            // this, we pad the left and right of the hotseat with half of the difference of a workspace
-            // cell vs a hotseat cell.
-            float workspaceCellWidth = (float) grid.widthPx / grid.inv.numColumns;
-            float hotseatCellWidth = (float) grid.widthPx / grid.inv.numHotseatIcons;
-            int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2);
-            Rect workspacePadding = grid.workspacePadding;
-
-            getLayout().setPadding(
-                    hotseatAdjustment + workspacePadding.left + grid.cellLayoutPaddingLeftRightPx,
-                    grid.hotseatBarTopPaddingPx,
-                    hotseatAdjustment + workspacePadding.right + grid.cellLayoutPaddingLeftRightPx,
-                    grid.hotseatBarBottomPaddingPx + insets.bottom + grid.cellLayoutBottomPaddingPx);
         }
+        Rect padding = grid.getHotseatLayoutPadding();
+        getLayout().setPadding(padding.left, padding.top, padding.right, padding.bottom);
+
         setLayoutParams(lp);
         InsettableFrameLayout.dispatchInsets(this, insets);
     }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 985e096..26e984e 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -197,6 +197,8 @@
     private final int[] mTmpAddItemCellCoordinates = new int[2];
 
     @Thunk Hotseat mHotseat;
+    private View mDragHandleIndicator;
+    @Nullable private View mHotseatSearchBox;
 
     private DropTargetBar mDropTargetBar;
 
@@ -946,6 +948,8 @@
         mWorkspace.initParentViews(mDragLayer);
         mOverviewPanel = findViewById(R.id.overview_panel);
         mHotseat = findViewById(R.id.hotseat);
+        mDragHandleIndicator = findViewById(R.id.drag_indicator);
+        mHotseatSearchBox = findViewById(R.id.search_container_hotseat);
 
         mLauncherView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
@@ -1209,6 +1213,14 @@
         return mHotseat;
     }
 
+    public View getDragHandleIndicator() {
+        return mDragHandleIndicator;
+    }
+
+    public View getHotseatSearchBox() {
+        return mHotseatSearchBox;
+    }
+
     public <T extends View> T getOverviewPanel() {
         return (T) mOverviewPanel;
     }
@@ -1836,7 +1848,7 @@
         mAppWidgetHost.clearViews();
 
         if (mHotseat != null) {
-            mHotseat.resetLayout();
+            mHotseat.resetLayout(mDeviceProfile.isVerticalBarLayout());
         }
         TraceHelper.endSection("startBinding");
     }
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index b1bf6ec..21f9d5a 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -47,10 +47,11 @@
      */
     public static final int NONE = 0;
     public static final int HOTSEAT_ICONS = 1 << 0;
-    public static final int HOTSEAT_EXTRA = 1 << 1; // e.g. a search box
+    public static final int HOTSEAT_SEARCH_BOX = 1 << 1;
     public static final int ALL_APPS_HEADER = 1 << 2;
     public static final int ALL_APPS_HEADER_EXTRA = 1 << 3; // e.g. app predictions
     public static final int ALL_APPS_CONTENT = 1 << 4;
+    public static final int DRAG_HANDLE_INDICATOR = 1 << 5;
 
     protected static final int FLAG_SHOW_SCRIM = 1 << 0;
     protected static final int FLAG_MULTI_PAGE = 1 << 1;
@@ -201,9 +202,9 @@
 
     public int getVisibleElements(Launcher launcher) {
         if (launcher.getDeviceProfile().isVerticalBarLayout()) {
-            return HOTSEAT_ICONS;
+            return HOTSEAT_ICONS | DRAG_HANDLE_INDICATOR;
         }
-        return HOTSEAT_ICONS | HOTSEAT_EXTRA;
+        return HOTSEAT_ICONS | DRAG_HANDLE_INDICATOR | HOTSEAT_SEARCH_BOX;
     }
 
     /**
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 9eff84b..1e761e4 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -187,7 +187,10 @@
         mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * density);
         mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * density);
         mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * density);
-        setWillNotDraw(false);
+
+        if (Utilities.ATLEAST_OREO) {
+            setDefaultFocusHighlightEnabled(false);
+        }
     }
 
     protected void setDefaultInterpolator(Interpolator interpolator) {
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 68ad253..34ae8ea 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -443,12 +443,6 @@
         setWallpaperDimension();
     }
 
-    @Override
-    public void initParentViews(View parent) {
-        super.initParentViews(parent);
-        mPageIndicator.setAccessibilityDelegate(UiFactory.newPageIndicatorAccessibilityDelegate());
-    }
-
     private void setupLayoutTransition() {
         // We want to show layout transitions when pages are deleted, to close the gap.
         mLayoutTransition = new LayoutTransition();
@@ -3351,11 +3345,6 @@
     }
 
     @Override
-    protected String getPageIndicatorDescription() {
-        return getResources().getString(R.string.all_apps_button_label);
-    }
-
-    @Override
     protected String getCurrentPageDescription() {
         int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
         return getPageDescription(page);
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 3a222c2..fa86906 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -18,8 +18,9 @@
 
 import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
-import static com.android.launcher3.LauncherState.HOTSEAT_EXTRA;
+import static com.android.launcher3.LauncherState.DRAG_HANDLE_INDICATOR;
 import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
+import static com.android.launcher3.LauncherState.HOTSEAT_SEARCH_BOX;
 import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
 
 import android.view.View;
@@ -81,16 +82,19 @@
 
         int elements = state.getVisibleElements(mLauncher);
         float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
-        float hotseatExtraAlpha = (elements & HOTSEAT_EXTRA) != 0 ? 1 : 0;
         propertySetter.setViewAlpha(mLauncher.getHotseat().getLayout(), hotseatIconsAlpha,
                 pageAlphaProvider.interpolator);
-        for (View hotseatExtraContent : UiFactory.getHotseatExtraContent(mLauncher.getHotseat())) {
-            propertySetter.setViewAlpha(hotseatExtraContent, hotseatExtraAlpha,
-                    pageAlphaProvider.interpolator);
-        }
         propertySetter.setViewAlpha(mLauncher.getWorkspace().getPageIndicator(),
                 hotseatIconsAlpha, pageAlphaProvider.interpolator);
 
+        propertySetter.setViewAlpha(mLauncher.getHotseatSearchBox(),
+                (elements & HOTSEAT_SEARCH_BOX) != 0 ? 1 : 0,
+                pageAlphaProvider.interpolator);
+
+        propertySetter.setViewAlpha(mLauncher.getDragHandleIndicator(),
+                (elements & DRAG_HANDLE_INDICATOR) != 0 ? 1 : 0,
+                pageAlphaProvider.interpolator);
+
         // Set scrim
         propertySetter.setFloat(ViewScrim.get(mWorkspace), ViewScrim.PROGRESS,
                 state.hasScrim ? 1 : 0, Interpolators.LINEAR);
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 9be123f..8788db4 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -23,7 +23,6 @@
 import com.android.launcher3.LauncherStateManager.AnimationConfig;
 import com.android.launcher3.LauncherStateManager.StateHandler;
 import com.android.launcher3.R;
-import com.android.launcher3.allapps.SearchUiManager.OnScrollRangeChangeListener;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorSetBuilder;
 import com.android.launcher3.anim.PropertySetter;
@@ -39,8 +38,7 @@
  * If release velocity < THRES1, snap according to either top or bottom depending on whether it's
  * closer to top or closer to the page indicator.
  */
-public class AllAppsTransitionController
-        implements OnScrollRangeChangeListener, StateHandler, OnDeviceProfileChangeListener {
+public class AllAppsTransitionController implements StateHandler, OnDeviceProfileChangeListener {
 
     public static final Property<AllAppsTransitionController, Float> ALL_APPS_PROGRESS =
             new Property<AllAppsTransitionController, Float>(Float.class, "allAppsProgress") {
@@ -71,11 +69,11 @@
     private float mShiftRange;      // changes depending on the orientation
     private float mProgress;        // [0, 1], mShiftRange * mProgress = shiftCurrent
 
-    private static final float DEFAULT_SHIFT_RANGE = 10;
+    private float mScrollRangeDelta = 0;
 
     public AllAppsTransitionController(Launcher l) {
         mLauncher = l;
-        mShiftRange = DEFAULT_SHIFT_RANGE;
+        mShiftRange = mLauncher.getDeviceProfile().heightPx;
         mProgress = 1f;
 
         mIsDarkTheme = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark);
@@ -95,6 +93,7 @@
     @Override
     public void onDeviceProfileChanged(DeviceProfile dp) {
         mIsVerticalLayout = dp.isVerticalBarLayout();
+        setScrollRangeDelta(mScrollRangeDelta);
 
         if (mIsVerticalLayout) {
             mAppsView.setAlpha(1);
@@ -122,6 +121,7 @@
         if (!mIsVerticalLayout) {
             mLauncher.getHotseat().setTranslationY(hotseatTranslation);
             mLauncher.getWorkspace().getPageIndicator().setTranslationY(hotseatTranslation);
+            mLauncher.getDragHandleIndicator().setTranslationY(hotseatTranslation);
         }
 
         // Use a light system UI (dark icons) if all apps is behind at least half of the
@@ -204,13 +204,14 @@
 
     public void setupViews(AllAppsContainerView appsView) {
         mAppsView = appsView;
-        mAppsView.getSearchUiManager().addOnScrollRangeChangeListener(this);
     }
 
-    @Override
-    public void onScrollRangeChanged(int scrollRange) {
-        mShiftRange = scrollRange;
-        setProgress(mProgress);
+    /**
+     * Updates the total scroll range but does not update the UI.
+     */
+    public void setScrollRangeDelta(float delta) {
+        mScrollRangeDelta = delta;
+        mShiftRange = mLauncher.getDeviceProfile().heightPx - mScrollRangeDelta;
     }
 
     /**
diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java
index d8568f8..68193f5 100644
--- a/src/com/android/launcher3/allapps/SearchUiManager.java
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -15,8 +15,6 @@
  */
 package com.android.launcher3.allapps;
 
-import android.support.animation.SpringAnimation;
-import android.support.annotation.NonNull;
 import android.view.KeyEvent;
 
 /**
@@ -30,11 +28,6 @@
     void initialize(AllAppsContainerView containerView);
 
     /**
-     * A {@link SpringAnimation} that will be used when the user flings.
-     */
-    @NonNull SpringAnimation getSpringForFling();
-
-    /**
      * Notifies the search manager to close any active search session.
      */
     void resetSearch();
@@ -44,14 +37,4 @@
      * some UI beforehand.
      */
     void preDispatchKeyEvent(KeyEvent keyEvent);
-
-    void addOnScrollRangeChangeListener(OnScrollRangeChangeListener listener);
-
-    /**
-     * Callback for listening to changes in the vertical scroll range when opening all-apps.
-     */
-    interface OnScrollRangeChangeListener {
-
-        void onScrollRangeChanged(int scrollRange);
-    }
 }
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index dd80dac..ad61c55 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -23,10 +23,6 @@
 
 import android.content.Context;
 import android.graphics.Rect;
-import android.support.animation.FloatValueHolder;
-import android.support.animation.SpringAnimation;
-import android.support.animation.SpringForce;
-import android.support.annotation.NonNull;
 import android.text.Selection;
 import android.text.Spannable;
 import android.text.SpannableString;
@@ -39,6 +35,7 @@
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.ExtendedEditText;
+import com.android.launcher3.Insettable;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.AllAppsContainerView;
@@ -55,7 +52,7 @@
  */
 public class AppsSearchContainerLayout extends ExtendedEditText
         implements SearchUiManager, AllAppsSearchBarController.Callbacks,
-        AllAppsStore.OnUpdateListener {
+        AllAppsStore.OnUpdateListener, Insettable {
 
 
     private final Launcher mLauncher;
@@ -64,7 +61,6 @@
 
     private AlphabeticalAppsList mApps;
     private AllAppsContainerView mAppsView;
-    private SpringAnimation mSpring;
 
     public AppsSearchContainerLayout(Context context) {
         this(context, null);
@@ -91,9 +87,6 @@
         spanned.setSpan(new TintedDrawableSpan(getContext(), R.drawable.ic_allapps_search),
                 0, 1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
         setHint(spanned);
-
-        // Note: This spring does nothing.
-        mSpring = new SpringAnimation(new FloatValueHolder()).setSpring(new SpringForce(0));
     }
 
     @Override
@@ -146,11 +139,6 @@
     }
 
     @Override
-    public @NonNull SpringAnimation getSpringForFling() {
-        return mSpring;
-    }
-
-    @Override
     public void onAppsUpdated() {
         mSearchBarController.refreshSearchResult();
     }
@@ -206,22 +194,15 @@
     }
 
     @Override
-    public void addOnScrollRangeChangeListener(final OnScrollRangeChangeListener listener) {
-        mLauncher.getHotseat().addOnLayoutChangeListener(new OnLayoutChangeListener() {
-            @Override
-            public void onLayoutChange(View v, int left, int top, int right, int bottom,
-                    int oldLeft, int oldTop, int oldRight, int oldBottom) {
-                DeviceProfile dp = mLauncher.getDeviceProfile();
-                if (!dp.isVerticalBarLayout()) {
-                    Rect insets = dp.getInsets();
-                    int hotseatBottom = bottom - dp.hotseatBarBottomPaddingPx - insets.bottom;
-                    MarginLayoutParams mlp = ((MarginLayoutParams) getLayoutParams());
-                    int myBot = mlp.topMargin + (int) getTranslationY() + mlp.height;
-                    listener.onScrollRangeChanged(hotseatBottom - myBot);
-                } else {
-                    listener.onScrollRangeChanged(bottom);
-                }
-            }
-        });
+    public void setInsets(Rect insets) {
+        DeviceProfile dp = mLauncher.getDeviceProfile();
+        if (dp.isVerticalBarLayout()) {
+            mLauncher.getAllAppsController().setScrollRangeDelta(0);
+        } else {
+            MarginLayoutParams mlp = ((MarginLayoutParams) getLayoutParams());
+            int myBot = mlp.topMargin + (int) getTranslationY() + mlp.height;
+            mLauncher.getAllAppsController().setScrollRangeDelta(
+                    dp.hotseatBarBottomPaddingPx + myBot);
+        }
     }
 }
diff --git a/src/com/android/launcher3/anim/AlphaUpdateListener.java b/src/com/android/launcher3/anim/AlphaUpdateListener.java
index 04d97a7..a3d02d9 100644
--- a/src/com/android/launcher3/anim/AlphaUpdateListener.java
+++ b/src/com/android/launcher3/anim/AlphaUpdateListener.java
@@ -17,7 +17,6 @@
 package com.android.launcher3.anim;
 
 import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.view.View;
@@ -25,44 +24,24 @@
 /**
  * A convenience class to update a view's visibility state after an alpha animation.
  */
-public class AlphaUpdateListener extends AnimatorListenerAdapter implements AnimatorUpdateListener {
+public class AlphaUpdateListener extends AnimationSuccessListener
+        implements AnimatorUpdateListener {
     private static final float ALPHA_CUTOFF_THRESHOLD = 0.01f;
 
     private View mView;
-    private boolean mAccessibilityEnabled;
-    private boolean mCanceled = false;
 
-    public AlphaUpdateListener(View v, boolean accessibilityEnabled) {
+    public AlphaUpdateListener(View v) {
         mView = v;
-        mAccessibilityEnabled = accessibilityEnabled;
     }
 
     @Override
     public void onAnimationUpdate(ValueAnimator arg0) {
-        updateVisibility(mView, mAccessibilityEnabled);
-    }
-
-    public static void updateVisibility(View view, boolean accessibilityEnabled) {
-        // We want to avoid the extra layout pass by setting the views to GONE unless
-        // accessibility is on, in which case not setting them to GONE causes a glitch.
-        int invisibleState = accessibilityEnabled ? View.GONE : View.INVISIBLE;
-        if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != invisibleState) {
-            view.setVisibility(invisibleState);
-        } else if (view.getAlpha() > ALPHA_CUTOFF_THRESHOLD
-                && view.getVisibility() != View.VISIBLE) {
-            view.setVisibility(View.VISIBLE);
-        }
+        updateVisibility(mView);
     }
 
     @Override
-    public void onAnimationCancel(Animator animation) {
-        mCanceled = true;
-    }
-
-    @Override
-    public void onAnimationEnd(Animator arg0) {
-        if (mCanceled) return;
-        updateVisibility(mView, mAccessibilityEnabled);
+    public void onAnimationSuccess(Animator animator) {
+        updateVisibility(mView);
     }
 
     @Override
@@ -70,4 +49,13 @@
         // We want the views to be visible for animation, so fade-in/out is visible
         mView.setVisibility(View.VISIBLE);
     }
+
+    public static void updateVisibility(View view) {
+        if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != View.INVISIBLE) {
+            view.setVisibility(View.INVISIBLE);
+        } else if (view.getAlpha() > ALPHA_CUTOFF_THRESHOLD
+                && view.getVisibility() != View.VISIBLE) {
+            view.setVisibility(View.VISIBLE);
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/anim/PropertySetter.java b/src/com/android/launcher3/anim/PropertySetter.java
index 1f11f7e..757edff 100644
--- a/src/com/android/launcher3/anim/PropertySetter.java
+++ b/src/com/android/launcher3/anim/PropertySetter.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher3.anim;
 
-import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
-
 import android.animation.Animator;
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
@@ -34,7 +32,7 @@
     public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) {
         if (view != null) {
             view.setAlpha(alpha);
-            AlphaUpdateListener.updateVisibility(view, isAccessibilityEnabled(view.getContext()));
+            AlphaUpdateListener.updateVisibility(view);
         }
     }
 
@@ -64,8 +62,7 @@
                 return;
             }
             ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.ALPHA, alpha);
-            anim.addListener(new AlphaUpdateListener(
-                    view, isAccessibilityEnabled(view.getContext())));
+            anim.addListener(new AlphaUpdateListener(view));
             anim.setDuration(mDuration).setInterpolator(interpolator);
             mStateAnimator.play(anim);
         }
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompat.java b/src/com/android/launcher3/compat/PackageInstallerCompat.java
index 112cca5..3270ba2 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompat.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompat.java
@@ -45,7 +45,7 @@
     /**
      * @return a map of active installs to their progress
      */
-    public abstract HashMap<String, Integer> updateAndGetActiveSessionCache();
+    public abstract HashMap<String, PackageInstaller.SessionInfo> updateAndGetActiveSessionCache();
 
     public abstract void onStop();
 
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
index 1ffd3da..dd17916 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
@@ -59,13 +59,13 @@
     }
 
     @Override
-    public HashMap<String, Integer> updateAndGetActiveSessionCache() {
-        HashMap<String, Integer> activePackages = new HashMap<>();
+    public HashMap<String, SessionInfo> updateAndGetActiveSessionCache() {
+        HashMap<String, SessionInfo> activePackages = new HashMap<>();
         UserHandle user = Process.myUserHandle();
         for (SessionInfo info : getAllVerifiedSessions()) {
             addSessionInfoToCache(info, user);
             if (info.getAppPackageName() != null) {
-                activePackages.put(info.getAppPackageName(), (int) (info.getProgress() * 100));
+                activePackages.put(info.getAppPackageName(), info);
                 mActiveSessions.put(info.getSessionId(), info.getAppPackageName());
             }
         }
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 78ea419..f4c6380 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -51,4 +51,8 @@
 
     // When enabled shows a work profile tab in all apps
     public static final boolean ALL_APPS_TABS_ENABLED = true;
+
+    // When true, overview shows screenshots in the orientation they were taken rather than
+    // trying to make them fit the orientation the device is in.
+    public static final boolean OVERVIEW_USE_SCREENSHOT_ORIENTATION = true;
 }
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index a0912a4..069ec4b 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -129,18 +129,15 @@
             };
 
     public void setup(Launcher launcher, View invalidateDelegate,
-                      int availableSpace, int topPadding) {
+                      int availableSpaceX, int topPadding) {
         mInvalidateDelegate = invalidateDelegate;
         mBgColor = Themes.getAttrColor(launcher, android.R.attr.colorPrimary);
 
         DeviceProfile grid = launcher.getDeviceProfile();
-        final int previewSize = grid.folderIconSizePx;
-        final int previewPadding = grid.folderIconPreviewPadding;
+        previewSize = grid.folderIconSizePx;
 
-        this.previewSize = (previewSize - 2 * previewPadding);
-
-        basePreviewOffsetX = (availableSpace - this.previewSize) / 2;
-        basePreviewOffsetY = previewPadding + grid.folderBackgroundOffset + topPadding;
+        basePreviewOffsetX = (availableSpaceX - previewSize) / 2;
+        basePreviewOffsetY = topPadding + grid.folderIconOffsetYPx;
 
         // Stroke width is 1dp
         mStrokeWidth = launcher.getResources().getDisplayMetrics().density;
diff --git a/src/com/android/launcher3/graphics/IconNormalizer.java b/src/com/android/launcher3/graphics/IconNormalizer.java
index 5d99ba0..680c020 100644
--- a/src/com/android/launcher3/graphics/IconNormalizer.java
+++ b/src/com/android/launcher3/graphics/IconNormalizer.java
@@ -376,4 +376,12 @@
             last = i;
         }
     }
+
+    /**
+     * @return The diameter of the normalized circle that fits inside of the square (size x size).
+     */
+    public static int getNormalizedCircleSize(int size) {
+        float area = size * size * MAX_CIRCLE_AREA_FACTOR;
+        return (int) Math.round(Math.sqrt((4 * area) / Math.PI));
+    }
 }
diff --git a/src/com/android/launcher3/model/FirstScreenBroadcast.java b/src/com/android/launcher3/model/FirstScreenBroadcast.java
new file mode 100644
index 0000000..1149b55
--- /dev/null
+++ b/src/com/android/launcher3/model/FirstScreenBroadcast.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.model;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.util.Log;
+
+import com.android.launcher3.FolderInfo;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.util.MultiHashMap;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Helper class to send broadcasts to package installers that have:
+ * - Items on the first screen
+ * - Items with an active install session
+ *
+ * The packages are broken down by: folder items, workspace items, hotseat items, and widgets.
+ *
+ * Package installers only receive data for items that they are installing.
+ */
+public class FirstScreenBroadcast {
+
+    private static final String TAG = "FirstScreenBroadcast";
+    private static final boolean DEBUG = false;
+
+    private static final String ACTION_FIRST_SCREEN_ACTIVE_INSTALLS
+            = "com.android.launcher3.action.FIRST_SCREEN_ACTIVE_INSTALLS";
+
+    private static final String FOLDER_ITEM_EXTRA = "folderItem";
+    private static final String WORKSPACE_ITEM_EXTRA = "workspaceItem";
+    private static final String HOTSEAT_ITEM_EXTRA = "hotseatItem";
+    private static final String WIDGET_ITEM_EXTRA = "widgetItem";
+
+    private static final String VERIFICATION_TOKEN_EXTRA = "verificationToken";
+
+    private final MultiHashMap<String, String> mPackagesForInstaller;
+
+    public FirstScreenBroadcast(HashMap<String, SessionInfo> sessionInfoForPackage) {
+        mPackagesForInstaller = getPackagesForInstaller(sessionInfoForPackage);
+    }
+
+    /**
+     * @return Map where the key is the package name of the installer, and the value is a list
+     *         of packages with active sessions for that installer.
+     */
+    private MultiHashMap<String, String> getPackagesForInstaller(
+            HashMap<String, SessionInfo> sessionInfoForPackage) {
+        MultiHashMap<String, String> packagesForInstaller = new MultiHashMap<>();
+        for (Map.Entry<String, SessionInfo> entry : sessionInfoForPackage.entrySet()) {
+            packagesForInstaller.addToList(entry.getValue().getInstallerPackageName(),
+                    entry.getKey());
+        }
+        return packagesForInstaller;
+    }
+
+    /**
+     * Sends a broadcast to all package installers that have items with active sessions on the users
+     * first screen.
+     */
+    public void sendBroadcasts(Context context, List<ItemInfo> firstScreenItems) {
+        for (Map.Entry<String, ArrayList<String>> entry : mPackagesForInstaller.entrySet()) {
+            sendBroadcastToInstaller(context, entry.getKey(), entry.getValue(), firstScreenItems);
+        }
+    }
+
+    /**
+     * @param installerPackageName Package name of the package installer.
+     * @param packages List of packages with active sessions for this package installer.
+     * @param firstScreenItems List of items on the first screen.
+     */
+    private void sendBroadcastToInstaller(Context context, String installerPackageName,
+            List<String> packages, List<ItemInfo> firstScreenItems) {
+        Set<String> folderItems = new HashSet<>();
+        Set<String> workspaceItems = new HashSet<>();
+        Set<String> hotseatItems = new HashSet<>();
+        Set<String> widgetItems = new HashSet<>();
+
+        for (ItemInfo info : firstScreenItems) {
+            if (info instanceof FolderInfo) {
+                FolderInfo folderInfo = (FolderInfo) info;
+                String folderItemInfoPackage;
+                for (ItemInfo folderItemInfo : folderInfo.contents) {
+                    folderItemInfoPackage = getPackageName(folderItemInfo);
+                    if (folderItemInfoPackage != null
+                            && packages.contains(folderItemInfoPackage)) {
+                        folderItems.add(folderItemInfoPackage);
+                    }
+                }
+            }
+
+            String packageName = getPackageName(info);
+            if (packageName == null || !packages.contains(packageName)) {
+                continue;
+            }
+            if (info instanceof LauncherAppWidgetInfo) {
+                widgetItems.add(packageName);
+            } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
+                hotseatItems.add(packageName);
+            } else if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+                workspaceItems.add(packageName);
+            }
+        }
+
+        if (DEBUG) {
+            printList(installerPackageName, "Folder item", folderItems);
+            printList(installerPackageName, "Workspace item", workspaceItems);
+            printList(installerPackageName, "Hotseat item", hotseatItems);
+            printList(installerPackageName, "Widget item", widgetItems);
+        }
+
+        context.sendBroadcast(new Intent(ACTION_FIRST_SCREEN_ACTIVE_INSTALLS)
+                .setPackage(installerPackageName)
+                .putStringArrayListExtra(FOLDER_ITEM_EXTRA, new ArrayList<>(folderItems))
+                .putStringArrayListExtra(WORKSPACE_ITEM_EXTRA, new ArrayList<>(workspaceItems))
+                .putStringArrayListExtra(HOTSEAT_ITEM_EXTRA, new ArrayList<>(hotseatItems))
+                .putStringArrayListExtra(WIDGET_ITEM_EXTRA, new ArrayList<>(widgetItems))
+                .putExtra(VERIFICATION_TOKEN_EXTRA, PendingIntent.getActivity(context, 0,
+                        new Intent(), PendingIntent.FLAG_ONE_SHOT)));
+    }
+
+    private static String getPackageName(ItemInfo info) {
+        String packageName = null;
+        if (info instanceof LauncherAppWidgetInfo) {
+            LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
+            if (widgetInfo.providerName != null) {
+                packageName = widgetInfo.providerName.getPackageName();
+            }
+        } else if (info.getTargetComponent() != null){
+            packageName = info.getTargetComponent().getPackageName();
+        }
+        return packageName;
+    }
+
+    private static void printList(String packageInstaller, String label, Set<String> packages) {
+        for (String pkg : packages) {
+            Log.d(TAG, packageInstaller + ":" + label + ":" + pkg);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/model/LoaderResults.java b/src/com/android/launcher3/model/LoaderResults.java
index 5d4a352..0fd9b73 100644
--- a/src/com/android/launcher3/model/LoaderResults.java
+++ b/src/com/android/launcher3/model/LoaderResults.java
@@ -209,7 +209,7 @@
 
     /** Filters the set of items who are directly or indirectly (via another container) on the
      * specified screen. */
-    private <T extends ItemInfo> void filterCurrentWorkspaceItems(long currentScreenId,
+    public static <T extends ItemInfo> void filterCurrentWorkspaceItems(long currentScreenId,
             ArrayList<T> allWorkspaceItems,
             ArrayList<T> currentScreenItems,
             ArrayList<T> otherScreenItems) {
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 9d1ff83..06da843 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -20,6 +20,7 @@
 import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
 import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
+import static com.android.launcher3.model.LoaderResults.filterCurrentWorkspaceItems;
 
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
@@ -29,6 +30,7 @@
 import android.content.IntentFilter;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionInfo;
 import android.graphics.Bitmap;
 import android.os.Handler;
 import android.os.Process;
@@ -92,6 +94,8 @@
     private final AllAppsList mBgAllAppsList;
     private final BgDataModel mBgDataModel;
 
+    private FirstScreenBroadcast mFirstScreenBroadcast;
+
     private final LoaderResults mResults;
 
     private final LauncherAppsCompat mLauncherApps;
@@ -134,6 +138,22 @@
         }
     }
 
+    private void sendFirstScreenActiveInstallsBroadcast() {
+        ArrayList<ItemInfo> firstScreenItems = new ArrayList<>();
+
+        ArrayList<ItemInfo> allItems = new ArrayList<>();
+        synchronized (mBgDataModel) {
+            allItems.addAll(mBgDataModel.workspaceItems);
+            allItems.addAll(mBgDataModel.appWidgets);
+        }
+        long firstScreen = mBgDataModel.workspaceScreens.isEmpty()
+                ? -1 // In this case, we can still look at the items in the hotseat.
+                : mBgDataModel.workspaceScreens.get(0);
+        filterCurrentWorkspaceItems(firstScreen, allItems, firstScreenItems,
+                new ArrayList<>() /* otherScreenItems are ignored */);
+        mFirstScreenBroadcast.sendBroadcasts(mApp.getContext(), firstScreenItems);
+    }
+
     public void run() {
         synchronized (this) {
             // Skip fast if we are already stopped.
@@ -151,6 +171,10 @@
             TraceHelper.partitionSection(TAG, "step 1.2: bind workspace workspace");
             mResults.bindWorkspace();
 
+            // Notify the installer packages of packages with active installs on the first screen.
+            TraceHelper.partitionSection(TAG, "step 1.3: send first screen broadcast");
+            sendFirstScreenActiveInstallsBroadcast();
+
             // Take a break
             TraceHelper.partitionSection(TAG, "step 1 completed, wait for idle");
             waitForIdle();
@@ -242,8 +266,9 @@
         synchronized (mBgDataModel) {
             mBgDataModel.clear();
 
-            final HashMap<String, Integer> installingPkgs =
+            final HashMap<String, SessionInfo> installingPkgs =
                     mPackageInstaller.updateAndGetActiveSessionCache();
+            mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);
             mBgDataModel.workspaceScreens.addAll(LauncherModel.loadWorkspaceScreensDb(context));
 
             Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>();
@@ -511,11 +536,11 @@
                                 }
 
                                 if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) {
-                                    Integer progress = installingPkgs.get(targetPkg);
-                                    if (progress != null) {
-                                        info.setInstallProgress(progress);
-                                    } else {
+                                    SessionInfo si = installingPkgs.get(targetPkg);
+                                    if (si == null) {
                                         info.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
+                                    } else {
+                                        info.setInstallProgress((int) (si.getProgress() * 100));
                                     }
                                 }
 
@@ -605,7 +630,11 @@
                                     appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
                                             component);
                                     appWidgetInfo.restoreStatus = c.restoreFlag;
-                                    Integer installProgress = installingPkgs.get(component.getPackageName());
+                                    SessionInfo si =
+                                            installingPkgs.get(component.getPackageName());
+                                    Integer installProgress = si == null
+                                            ? null
+                                            : (int) (si.getProgress() * 100);
 
                                     if (c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED)) {
                                         // Restore has started once.
diff --git a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
index 94ae39b..4ad7feb 100644
--- a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
@@ -1,9 +1,5 @@
 package com.android.launcher3.pageindicators;
 
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-
-import static com.android.launcher3.LauncherState.ALL_APPS;
-
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
@@ -20,9 +16,7 @@
 import android.util.Property;
 import android.view.Gravity;
 import android.view.View;
-import android.view.View.OnClickListener;
 import android.view.ViewConfiguration;
-import android.view.accessibility.AccessibilityManager;
 import android.widget.FrameLayout;
 
 import com.android.launcher3.DeviceProfile;
@@ -31,18 +25,13 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.dynamicui.WallpaperColorInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
 
 /**
- * A PageIndicator that briefly shows a fraction of a line when moving between pages in
- * portrait mode. In Landscape simply draws the caret drawable bottom-corner aligned in
- * the drag-layer.
+ * A PageIndicator that briefly shows a fraction of a line when moving between pages
  *
  * The fraction is 1 / number of pages and the position is based on the progress of the page scroll.
  */
-public class WorkspacePageIndicator extends View
-        implements Insettable, OnClickListener, PageIndicator {
+public class WorkspacePageIndicator extends View implements Insettable, PageIndicator {
 
     private static final int LINE_ANIMATE_DURATION = ViewConfiguration.getScrollBarFadeDuration();
     private static final int LINE_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay();
@@ -57,7 +46,6 @@
 
     private final Handler mDelayedLineFadeHandler = new Handler(Looper.getMainLooper());
     private final Launcher mLauncher;
-    private final AccessibilityManager mAccessibilityManager;
 
     private boolean mShouldAutoHide = true;
 
@@ -72,8 +60,6 @@
     private Paint mLinePaint;
     private final int mLineHeight;
 
-    private boolean mIsLandscapeUi;
-
     private static final Property<WorkspacePageIndicator, Integer> PAINT_ALPHA
             = new Property<WorkspacePageIndicator, Integer>(Integer.class, "paint_alpha") {
         @Override
@@ -84,7 +70,7 @@
         @Override
         public void set(WorkspacePageIndicator obj, Integer alpha) {
             obj.mLinePaint.setAlpha(alpha);
-            obj.invalidateIfPortrait();
+            obj.invalidate();
         }
     };
 
@@ -98,7 +84,7 @@
         @Override
         public void set(WorkspacePageIndicator obj, Float numPages) {
             obj.mNumPagesFloat = numPages;
-            obj.invalidateIfPortrait();
+            obj.invalidate();
         }
     };
 
@@ -112,7 +98,7 @@
         @Override
         public void set(WorkspacePageIndicator obj, Integer totalScroll) {
             obj.mTotalScroll = totalScroll;
-            obj.invalidateIfPortrait();
+            obj.invalidate();
         }
     };
 
@@ -139,24 +125,23 @@
         boolean darkText = WallpaperColorInfo.getInstance(context).supportsDarkText();
         mActiveAlpha = darkText ? BLACK_ALPHA : WHITE_ALPHA;
         mLinePaint.setColor(darkText ? Color.BLACK : Color.WHITE);
-        mAccessibilityManager = (AccessibilityManager)
-                getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
     }
 
     @Override
     protected void onDraw(Canvas canvas) {
-        if (mTotalScroll == 0 || mNumPagesFloat == 0 || mIsLandscapeUi) {
+        if (mTotalScroll == 0 || mNumPagesFloat == 0) {
             return;
         }
 
         // Compute and draw line rect.
         float progress = Utilities.boundToRange(((float) mCurrentScroll) / mTotalScroll, 0f, 1f);
-        int availableWidth = canvas.getWidth();
+        int availableWidth = getWidth();
         int lineWidth = (int) (availableWidth / mNumPagesFloat);
         int lineLeft = (int) (progress * (availableWidth - lineWidth));
         int lineRight = lineLeft + lineWidth;
-        canvas.drawRoundRect(lineLeft, canvas.getHeight() / 2 - mLineHeight / 2, lineRight,
-                canvas.getHeight() / 2 + mLineHeight / 2, mLineHeight, mLineHeight, mLinePaint);
+
+        canvas.drawRoundRect(lineLeft, getHeight() / 2 - mLineHeight / 2, lineRight,
+                getHeight() / 2 + mLineHeight / 2, mLineHeight, mLineHeight, mLinePaint);
     }
 
     @Override
@@ -172,7 +157,7 @@
         } else if (mTotalScroll != totalScroll) {
             animateToTotalScroll(totalScroll);
         } else {
-            invalidateIfPortrait();
+            invalidate();
         }
 
         if (mShouldAutoHide) {
@@ -191,7 +176,13 @@
     @Override
     public void setMarkersCount(int numMarkers) {
         if (Float.compare(numMarkers, mNumPagesFloat) != 0) {
-            animateToNumPages(numMarkers);
+            setupAndRunAnimation(ObjectAnimator.ofFloat(this, NUM_PAGES, numMarkers),
+                    NUM_PAGES_ANIMATOR_INDEX);
+        } else {
+            if (mAnimators[NUM_PAGES_ANIMATOR_INDEX] != null) {
+                mAnimators[NUM_PAGES_ANIMATOR_INDEX].cancel();
+                mAnimators[NUM_PAGES_ANIMATOR_INDEX] = null;
+            }
         }
     }
 
@@ -219,11 +210,6 @@
                 LINE_ALPHA_ANIMATOR_INDEX);
     }
 
-    private void animateToNumPages(int numPages) {
-        setupAndRunAnimation(ObjectAnimator.ofFloat(this, NUM_PAGES, numPages),
-                NUM_PAGES_ANIMATOR_INDEX);
-    }
-
     private void animateToTotalScroll(int totalScroll) {
         setupAndRunAnimation(ObjectAnimator.ofInt(this, TOTAL_SCROLL, totalScroll),
                 TOTAL_SCROLL_ANIMATOR_INDEX);
@@ -254,54 +240,18 @@
     @Override
     public void setInsets(Rect insets) {
         DeviceProfile grid = mLauncher.getDeviceProfile();
-        mIsLandscapeUi = grid.isVerticalBarLayout();
         FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
 
-        if (mIsLandscapeUi) {
-            if (grid.isSeascape()) {
-                lp.leftMargin = grid.hotseatBarSidePaddingPx;
-                lp.rightMargin = insets.right;
-                lp.gravity =  Gravity.RIGHT | Gravity.BOTTOM;
-            } else {
-                lp.leftMargin = insets.left;
-                lp.rightMargin = grid.hotseatBarSidePaddingPx;
-                lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
-            }
-            lp.bottomMargin = grid.workspacePadding.bottom;
-            lp.width = lp.height = getResources()
-                    .getDimensionPixelSize(R.dimen.dynamic_grid_min_page_indicator_size);
-
-            setBackgroundResource(R.drawable.all_apps_handle_landscape);
-            setOnFocusChangeListener(mLauncher.mFocusHandler);
-            setOnClickListener(this);
-
+        if (grid.isVerticalBarLayout()) {
+            Rect padding = grid.workspacePadding;
+            lp.leftMargin = padding.left + grid.workspaceCellPaddingXPx;
+            lp.rightMargin = padding.right + grid.workspaceCellPaddingXPx;
+            lp.bottomMargin = padding.bottom;
         } else {
             lp.leftMargin = lp.rightMargin = 0;
             lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
-            lp.height = grid.pageIndicatorSizePx;
             lp.bottomMargin = grid.hotseatBarSizePx + insets.bottom;
-            lp.width = MATCH_PARENT;
-
-            setBackgroundResource(0);
-            setOnFocusChangeListener(null);
-            setOnClickListener(mAccessibilityManager.isTouchExplorationEnabled() ? this : null);
         }
-
         setLayoutParams(lp);
     }
-
-    private void invalidateIfPortrait() {
-        if (!mIsLandscapeUi) {
-            invalidate();
-        }
-    }
-
-    @Override
-    public void onClick(View view) {
-        if (!mLauncher.isInState(ALL_APPS)) {
-            mLauncher.getUserEventDispatcher().logActionOnControl(
-                    Action.Touch.TAP, ControlType.ALL_APPS_BUTTON);
-            mLauncher.getStateManager().goToState(ALL_APPS);
-        }
-    }
 }
diff --git a/src/com/android/launcher3/util/PendingRequestArgs.java b/src/com/android/launcher3/util/PendingRequestArgs.java
index dabd40d..b8bcfed 100644
--- a/src/com/android/launcher3/util/PendingRequestArgs.java
+++ b/src/com/android/launcher3/util/PendingRequestArgs.java
@@ -57,7 +57,7 @@
 
         mArg1 = parcel.readInt();
         mObjectType = parcel.readInt();
-        mObject = parcel.readParcelable(null);
+        mObject = parcel.readParcelable(getClass().getClassLoader());
     }
 
     @Override
diff --git a/src/com/android/launcher3/views/LauncherDragIndicator.java b/src/com/android/launcher3/views/LauncherDragIndicator.java
new file mode 100644
index 0000000..f15129c
--- /dev/null
+++ b/src/com/android/launcher3/views/LauncherDragIndicator.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.views;
+
+import static com.android.launcher3.LauncherState.ALL_APPS;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Insettable;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
+
+public class LauncherDragIndicator extends ImageView implements Insettable, OnClickListener {
+
+    private static final int WALLPAPERS = R.string.wallpaper_button_text;
+    private static final int WIDGETS = R.string.widget_button_text;
+    private static final int SETTINGS = R.string.settings_button_text;
+
+    protected final Launcher mLauncher;
+
+    public LauncherDragIndicator(Context context) {
+        this(context, null);
+    }
+
+    public LauncherDragIndicator(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public LauncherDragIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mLauncher = Launcher.getLauncher(context);
+        setOnClickListener(this);
+    }
+
+    @Override
+    public void setInsets(Rect insets) {
+        DeviceProfile grid = mLauncher.getDeviceProfile();
+        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
+
+        if (grid.isVerticalBarLayout()) {
+            if (grid.isSeascape()) {
+                lp.leftMargin = grid.hotseatBarSidePaddingPx;
+                lp.rightMargin = insets.right;
+                lp.gravity =  Gravity.RIGHT | Gravity.BOTTOM;
+            } else {
+                lp.leftMargin = insets.left;
+                lp.rightMargin = grid.hotseatBarSidePaddingPx;
+                lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
+            }
+            lp.bottomMargin = grid.workspacePadding.bottom;
+            setImageResource(R.drawable.all_apps_handle_landscape);
+        } else {
+            lp.leftMargin = lp.rightMargin = 0;
+            lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
+            lp.bottomMargin = getPortraitBottomMargin(grid, insets);
+            setImageResource(R.drawable.ic_drag_indicator);
+        }
+
+        lp.width = lp.height = grid.pageIndicatorSizePx;
+        setLayoutParams(lp);
+    }
+
+    protected int getPortraitBottomMargin(DeviceProfile grid, Rect insets) {
+        return grid.hotseatBarSizePx + insets.bottom - grid.pageIndicatorSizePx;
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        initCustomActions(info);
+    }
+
+    protected void initCustomActions(AccessibilityNodeInfo info) {
+        Context context = getContext();
+        if (Utilities.isWallpaperAllowed(context)) {
+            info.addAction(new AccessibilityAction(WALLPAPERS, context.getText(WALLPAPERS)));
+        }
+        info.addAction(new AccessibilityAction(WIDGETS, context.getText(WIDGETS)));
+        info.addAction(new AccessibilityAction(SETTINGS, context.getText(SETTINGS)));
+    }
+
+    @Override
+    public boolean performAccessibilityAction(int action, Bundle arguments) {
+        Launcher launcher = Launcher.getLauncher(getContext());
+        if (action == WALLPAPERS) {
+            launcher.onClickWallpaperPicker(this);
+            return true;
+        } else if (action == WIDGETS) {
+            return OptionsPopupView.onWidgetsClicked(launcher);
+        } else if (action == SETTINGS) {
+            OptionsPopupView.startSettings(launcher);
+            return true;
+        }
+        return super.performAccessibilityAction(action, arguments);
+    }
+
+    @Override
+    public void onClick(View view) {
+        if (!mLauncher.isInState(ALL_APPS)) {
+            mLauncher.getUserEventDispatcher().logActionOnControl(
+                    Action.Touch.TAP, ControlType.ALL_APPS_BUTTON);
+            mLauncher.getStateManager().goToState(ALL_APPS);
+        }
+    }
+}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewAccessibilityDelegate.java b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewAccessibilityDelegate.java
deleted file mode 100644
index d9ce87c..0000000
--- a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewAccessibilityDelegate.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.uioverrides;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.view.View;
-import android.view.View.AccessibilityDelegate;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.views.OptionsPopupView;
-
-/**
- * Accessibility delegate with actions pointing to various Overview entry points.
- */
-public class OverviewAccessibilityDelegate extends AccessibilityDelegate {
-
-    private static final int WALLPAPERS = R.string.wallpaper_button_text;
-    private static final int WIDGETS = R.string.widget_button_text;
-    private static final int SETTINGS = R.string.settings_button_text;
-
-    @Override
-    public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfo(host, info);
-
-        Context context = host.getContext();
-
-        if (Utilities.isWallpaperAllowed(context)) {
-            info.addAction(new AccessibilityAction(WALLPAPERS, context.getText(WALLPAPERS)));
-        }
-        info.addAction(new AccessibilityAction(WIDGETS, context.getText(WIDGETS)));
-        info.addAction(new AccessibilityAction(SETTINGS, context.getText(SETTINGS)));
-    }
-
-    @Override
-    public boolean performAccessibilityAction(View host, int action, Bundle args) {
-        Launcher launcher = Launcher.getLauncher(host.getContext());
-        if (action == WALLPAPERS) {
-            launcher.onClickWallpaperPicker(host);
-            return true;
-        } else if (action == WIDGETS) {
-            return OptionsPopupView.onWidgetsClicked(launcher);
-        } else if (action == SETTINGS) {
-            OptionsPopupView.startSettings(launcher);
-            return true;
-        }
-        return super.performAccessibilityAction(host, action, args);
-    }
-}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
index 6645e89..be9d5b7 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
@@ -16,10 +16,6 @@
 
 package com.android.launcher3.uioverrides;
 
-import android.view.View;
-import android.view.View.AccessibilityDelegate;
-
-import com.android.launcher3.Hotseat;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherStateManager.StateHandler;
 import com.android.launcher3.util.TouchController;
@@ -31,10 +27,6 @@
                 launcher.getDragController(), new AllAppsSwipeController(launcher)};
     }
 
-    public static AccessibilityDelegate newPageIndicatorAccessibilityDelegate() {
-        return new OverviewAccessibilityDelegate();
-    }
-
     public static StateHandler[] getStateHandler(Launcher launcher) {
         return new StateHandler[] {
                 launcher.getAllAppsController(), launcher.getWorkspace() };
@@ -49,8 +41,4 @@
     public static void onLauncherStateOrResumeChanged(Launcher launcher) { }
 
     public static void onTrimMemory(Launcher launcher, int level) { }
-
-    public static View[] getHotseatExtraContent(Hotseat hotseat) {
-        return new View[0];
-    }
 }