Merge "Removing unnecessary object creation and double loop during getDescendantCoordRelativeToAncestor" into ub-launcher3-calgary
diff --git a/build.gradle b/build.gradle
index 899767f..f98021c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -18,7 +18,7 @@
     defaultConfig {
         applicationId "com.android.launcher3"
         minSdkVersion 21
-        targetSdkVersion 23
+        targetSdkVersion 'N'
         versionCode 1
         versionName "1.0"
 
diff --git a/res/drawable-ldrtl/container_fastscroll_popup_bg.xml b/res/drawable-ldrtl/container_fastscroll_popup_bg.xml
index d790968..2bbf5cd 100644
--- a/res/drawable-ldrtl/container_fastscroll_popup_bg.xml
+++ b/res/drawable-ldrtl/container_fastscroll_popup_bg.xml
@@ -16,7 +16,7 @@
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
-    <solid android:color="@color/container_fastscroll_thumb_active_color" />
+    <solid android:color="?android:attr/colorAccent" />
     <size
         android:width="64dp"
         android:height="64dp" />
diff --git a/res/drawable/container_fastscroll_popup_bg.xml b/res/drawable/container_fastscroll_popup_bg.xml
index 2ef07ab..3dc7680 100644
--- a/res/drawable/container_fastscroll_popup_bg.xml
+++ b/res/drawable/container_fastscroll_popup_bg.xml
@@ -16,7 +16,7 @@
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
-    <solid android:color="@color/container_fastscroll_thumb_active_color" />
+    <solid android:color="?android:attr/colorAccent" />
     <size
         android:width="64dp"
         android:height="64dp" />
diff --git a/res/drawable/ic_allapps_caret.xml b/res/drawable/ic_allapps_caret.xml
new file mode 100644
index 0000000..34d1882
--- /dev/null
+++ b/res/drawable/ic_allapps_caret.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="48.0"
+    android:viewportWidth="48.0"
+    android:width="24dp" >
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M14.83,30.83L24,21.66l9.17,9.17L36,28 24,16 12,28z"/>
+</vector>
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index d193e2f..27ff789 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -36,7 +36,8 @@
             android:id="@+id/workspace"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:layout_gravity="center" />
+            android:layout_gravity="center"
+            launcher:pageIndicator="@id/page_indicator" />
 
         <!-- DO NOT CHANGE THE ID -->
         <include layout="@layout/hotseat"
@@ -53,6 +54,12 @@
             android:id="@+id/overview_panel"
             android:visibility="gone" />
 
+        <com.android.launcher3.pageindicators.PageIndicatorCaretLandscape
+            android:id="@+id/page_indicator"
+            android:layout_width="48dp"
+            android:layout_height="@dimen/dynamic_grid_page_indicator_height"
+            android:layout_gravity="bottom|left"/>
+
         <include layout="@layout/widgets_view"
             android:id="@+id/widgets_view"
             android:layout_width="match_parent"
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index 527ed54..6b5bf63 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -52,7 +52,7 @@
 
         <!-- Keep these behind the workspace so that they are not visible when
              we go into AllApps -->
-        <com.android.launcher3.pageindicators.PageIndicatorLine
+        <com.android.launcher3.pageindicators.PageIndicatorLineCaret
             android:id="@+id/page_indicator"
             android:layout_width="match_parent"
             android:layout_height="@dimen/dynamic_grid_page_indicator_height" />
@@ -71,7 +71,8 @@
             android:id="@+id/apps_view"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:visibility="invisible" />
+            android:visibility="invisible"
+            launcher:layout_ignoreInsets="true" />
     </com.android.launcher3.dragndrop.DragLayer>
 
 </com.android.launcher3.LauncherRootView>
diff --git a/res/layout-sw720dp/launcher.xml b/res/layout-sw720dp/launcher.xml
index 184e688..33ad323 100644
--- a/res/layout-sw720dp/launcher.xml
+++ b/res/layout-sw720dp/launcher.xml
@@ -55,7 +55,7 @@
 
         <!-- Keep these behind the workspace so that they are not visible when
              we go into AllApps -->
-        <com.android.launcher3.pageindicators.PageIndicatorLine
+        <com.android.launcher3.pageindicators.PageIndicatorLineCaret
             android:id="@+id/page_indicator"
             android:layout_width="match_parent"
             android:layout_height="@dimen/dynamic_grid_page_indicator_height" />
diff --git a/res/layout/all_apps_search_market.xml b/res/layout/all_apps_search_market.xml
index 4bdca99..741c96a 100644
--- a/res/layout/all_apps_search_market.xml
+++ b/res/layout/all_apps_search_market.xml
@@ -23,7 +23,7 @@
     android:paddingRight="16dp"
     android:fontFamily="sans-serif-medium"
     android:textSize="14sp"
-    android:textColor="@color/launcher_accent_color"
+    android:textColor="?android:attr/colorAccent"
     android:text="@string/all_apps_search_market_message"
     android:textAllCaps="true"
     android:focusable="true"
diff --git a/res/layout/widget_cell.xml b/res/layout/widget_cell.xml
index 7fefeba..15f369f 100644
--- a/res/layout/widget_cell.xml
+++ b/res/layout/widget_cell.xml
@@ -21,7 +21,7 @@
     android:layout_weight="1"
     android:orientation="vertical"
     android:focusable="true"
-    android:background="@color/widgets_cell_color"
+    android:background="?android:attr/colorPrimary"
     android:gravity="center_horizontal">
 
     <LinearLayout
diff --git a/res/layout/widgets_list_row_view.xml b/res/layout/widgets_list_row_view.xml
index b7f76a6..c0219b9 100644
--- a/res/layout/widgets_list_row_view.xml
+++ b/res/layout/widgets_list_row_view.xml
@@ -19,7 +19,7 @@
     android:id="@+id/widgets_cell_list_container"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:background="@color/widgets_cell_color"
+    android:background="?android:attr/colorPrimary"
     android:orientation="vertical"
     android:focusable="true"
     android:descendantFocusability="afterDescendants">
diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml
index 0503466..e9bbd37 100644
--- a/res/layout/widgets_view.xml
+++ b/res/layout/widgets_view.xml
@@ -25,7 +25,8 @@
     android:paddingTop="@dimen/container_bounds_inset"
     android:paddingBottom="@dimen/container_bounds_inset"
     android:descendantFocusability="afterDescendants"
-    launcher:revealBackground="@drawable/quantum_panel_shape_dark">
+    launcher:revealBackground="@drawable/quantum_panel_shape_dark"
+    android:theme="@android:style/Theme.DeviceDefault.Settings">
 
     <View
         android:id="@+id/reveal_view"
diff --git a/res/values-v21/styles.xml b/res/values-v21/styles.xml
index aa5d0a6..8d3de01 100644
--- a/res/values-v21/styles.xml
+++ b/res/values-v21/styles.xml
@@ -24,8 +24,5 @@
         <item name="android:windowDrawsSystemBarBackgrounds">true</item>
         <item name="android:statusBarColor">#00000000</item>
         <item name="android:navigationBarColor">#00000000</item>
-        <item name="android:colorControlActivated">@color/launcher_accent_color</item>
-        <item name="android:colorAccent">@color/launcher_accent_color</item>
-        <item name="android:colorPrimary">@color/launcher_accent_color</item>
     </style>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 1329535..44e77e2 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -22,7 +22,6 @@
          over the delete target or the info target -->
     <color name="delete_target_hover_tint">#FFC1C1C1</color>
     <color name="uninstall_target_hover_tint">#FFF0592B</color>
-    <color name="info_target_hover_tint">#FF009688</color>
     <color name="cling_scrim_background">#80000000</color>
 
     <color name="focused_background">#80c6c5c5</color>
@@ -38,20 +37,11 @@
     <color name="quantum_panel_bg_color_dark">#FF374248</color>
 
     <color name="outline_color">#FFFFFFFF</color>
-    <color name="launcher_accent_color">#ff009688</color>
 
     <color name="spring_loaded_panel_color">#40FFFFFF</color>
     <color name="spring_loaded_highlighted_panel_border_color">#FFF</color>
 
-    <!-- Containers -->
-    <color name="container_fastscroll_thumb_inactive_color">#009688</color>
-    <color name="container_fastscroll_thumb_active_color">#009688</color>
-
-    <!-- All Apps -->
-    <color name="all_apps_grid_section_text_color">#009688</color>
-
     <!-- Widgets view -->
     <color name="widgets_view_section_text_color">#FFFFFF</color>
     <color name="widgets_view_item_text_color">#C4C4C4</color>
-    <color name="widgets_cell_color">#263238</color>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 64868f2..02c6c70 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -17,7 +17,9 @@
 <resources>
 <!-- Dynamic Grid -->
     <dimen name="dynamic_grid_edge_margin">6dp</dimen>
-    <dimen name="dynamic_grid_page_indicator_height">1dp</dimen>
+    <dimen name="dynamic_grid_page_indicator_height">24dp</dimen>
+    <dimen name="dynamic_grid_page_indicator_line_height">1dp</dimen>
+    <dimen name="dynamic_grid_page_indicator_extra_touch_height">12dp</dimen>
     <dimen name="dynamic_grid_icon_drawable_padding">4dp</dimen>
     <dimen name="dynamic_grid_workspace_page_spacing">8dp</dimen>
     <dimen name="dynamic_grid_overview_min_icon_zone_height">80dp</dimen>
@@ -86,6 +88,7 @@
     <dimen name="all_apps_search_bar_bg_overflow">-6dp</dimen>
     <dimen name="all_apps_search_bar_divider_width">1dp</dimen>
 
+    <dimen name="all_apps_bezel_swipe_height">24dp</dimen>
 <!-- Widget tray -->
     <dimen name="widget_preview_label_vertical_padding">8dp</dimen>
     <dimen name="widget_preview_label_horizontal_padding">8dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 9f011e5..4e5fcff 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -38,7 +38,7 @@
 
     <!-- Widgets -->
     <!-- Message to tell the user to press and hold on a widget to add it [CHAR_LIMIT=50] -->
-    <string name="long_press_widget_to_add">Tap &amp; hold to pick up a widget.</string>
+    <string name="long_press_widget_to_add">Touch &amp; hold to pick up a widget.</string>
     <!-- Accessibility spoken hint message in widget picker, which allows user to add a widget. Custom action is the label for additional accessibility actions available in this mode [CHAR_LIMIT=100] -->
     <string name="long_accessible_way_to_add">Double-tap &amp; hold to pick up a widget or use custom actions.</string>
     <!-- The format string for the dimensions of a widget in the drawer -->
@@ -135,7 +135,7 @@
     <!-- The title text for workspace longpress action [CHAR_LIMIT=40] -->
     <string name="workspace_cling_longpress_title">Wallpapers, widgets, &amp; settings</string>
     <!-- The description of how to use the workspace [CHAR_LIMIT=70] -->
-    <string name="workspace_cling_longpress_description">Tap &amp; hold background to customize</string>
+    <string name="workspace_cling_longpress_description">Touch &amp; hold background to customize</string>
     <!-- The description of the button to dismiss the cling [CHAR_LIMIT=30] -->
     <string name="workspace_cling_longpress_dismiss">GOT IT</string>
 
@@ -162,6 +162,8 @@
     <string name="settings_button_text">Settings</string>
     <!-- Message shown when a feature is disabled by the administrator -->
     <string name="msg_disabled_by_admin">Disabled by your admin</string>
+    <!-- Text for custom accessibility action to go to the overview mode, where users can look and change the overall UI of the launcher. -->
+    <string name="accessibility_action_overview">Overview</string>
 
     <!-- Strings for settings -->
     <!-- Title for Allow Rotation setting. [CHAR LIMIT=50] -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 1b7072d..0bfd0a0 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -31,7 +31,7 @@
     <style name="Theme" parent="@style/LauncherTheme"></style>
 
     <!-- Overscroll effect -->
-    <style name="CustomOverscroll.Light" parent="@android:style/Theme.DeviceDefault">
+    <style name="CustomOverscroll.Light" parent="@android:style/Theme.DeviceDefault.Light">
         <item name="android:colorEdgeEffect">@color/folder_edge_effect_color</item>
     </style>
 
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index f3a2bdc..0d5043b 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -37,6 +37,7 @@
 
 import com.android.launcher3.LauncherProvider.SqlArguments;
 import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.Thunk;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -157,7 +158,7 @@
     protected final Resources mSourceRes;
     protected final int mLayoutId;
 
-    private final int mHotseatAllAppsRank;
+    private final InvariantDeviceProfile mIdp;
     private final int mRowCount;
     private final int mColumnCount;
 
@@ -181,10 +182,9 @@
         mSourceRes = res;
         mLayoutId = layoutId;
 
-        InvariantDeviceProfile idp = LauncherAppState.getInstance().getInvariantDeviceProfile();
-        mHotseatAllAppsRank = idp.hotseatAllAppsRank;
-        mRowCount = idp.numRows;
-        mColumnCount = idp.numColumns;
+        mIdp = LauncherAppState.getInstance().getInvariantDeviceProfile();
+        mRowCount = mIdp.numRows;
+        mColumnCount = mIdp.numColumns;
     }
 
     /**
@@ -231,7 +231,8 @@
             out[0] = Favorites.CONTAINER_HOTSEAT;
             // Hack: hotseat items are stored using screen ids
             long rank = Long.parseLong(getAttributeValue(parser, ATTR_RANK));
-            out[1] = (rank < mHotseatAllAppsRank) ? rank : (rank + 1);
+            out[1] = (FeatureFlags.NO_ALL_APPS_ICON || rank < mIdp.getAllAppsButtonRank())
+                    ? rank : (rank + 1);
         } else {
             out[0] = Favorites.CONTAINER_DESKTOP;
             out[1] = Long.parseLong(getAttributeValue(parser, ATTR_SCREEN));
diff --git a/src/com/android/launcher3/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java
index a9ef43d..84bd88d 100644
--- a/src/com/android/launcher3/BaseContainerView.java
+++ b/src/com/android/launcher3/BaseContainerView.java
@@ -50,8 +50,10 @@
     public BaseContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
-        int width = ((Launcher) context).getDeviceProfile().availableWidthPx;
-        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && (this instanceof AllAppsContainerView)) {
+        Launcher launcher = Launcher.getLauncher(context);
+        int width = launcher.getDeviceProfile().availableWidthPx;
+        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP &&
+                this instanceof AllAppsContainerView && !launcher.getDeviceProfile().isLandscape) {
             mHorizontalPadding = 0;
         } else {
             mHorizontalPadding = DeviceProfile.getContainerPadding(context, width);
@@ -87,4 +89,4 @@
     public final View getRevealView() {
         return mRevealView;
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
index fd0045e..4ab0ea3 100644
--- a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
+++ b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
@@ -81,9 +81,7 @@
         mTrackPaint = new Paint();
         mTrackPaint.setColor(rv.getFastScrollerTrackColor(Color.BLACK));
         mTrackPaint.setAlpha(MAX_TRACK_ALPHA);
-        mThumbInactiveColor = rv.getFastScrollerThumbInactiveColor(
-                res.getColor(R.color.container_fastscroll_thumb_inactive_color));
-        mThumbActiveColor = res.getColor(R.color.container_fastscroll_thumb_active_color);
+        mThumbActiveColor = mThumbInactiveColor = Utilities.getColorAccent(rv.getContext());
         mThumbPaint = new Paint();
         mThumbPaint.setAntiAlias(true);
         mThumbPaint.setColor(mThumbInactiveColor);
diff --git a/src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java b/src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java
index ebaba18..baf96fe 100644
--- a/src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java
+++ b/src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java
@@ -53,7 +53,7 @@
         mRes = res;
         mRv = rv;
         mBgOriginalSize = res.getDimensionPixelSize(R.dimen.container_fastscroll_popup_size);
-        mBg = res.getDrawable(R.drawable.container_fastscroll_popup_bg);
+        mBg = rv.getContext().getDrawable(R.drawable.container_fastscroll_popup_bg);
         mBg.setBounds(0, 0, mBgOriginalSize, mBgOriginalSize);
         mTextPaint = new Paint();
         mTextPaint.setColor(Color.WHITE);
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 5d0f783..1762ca4 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -103,7 +103,7 @@
 
     public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
-        mLauncher = (Launcher) context;
+        mLauncher = Launcher.getLauncher(context);
         DeviceProfile grid = mLauncher.getDeviceProfile();
 
         TypedArray a = context.obtainStyledAttributes(attrs,
@@ -143,7 +143,7 @@
             setShadowLayer(SHADOW_LARGE_RADIUS, 0.0f, SHADOW_Y_OFFSET, SHADOW_LARGE_COLOUR);
         }
 
-        setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
+        setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
     }
 
     public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache) {
@@ -158,7 +158,7 @@
         if (info.isDisabled()) {
             iconDrawable.setState(FastBitmapDrawable.State.DISABLED);
         }
-        setIcon(iconDrawable, mIconSize);
+        setIcon(iconDrawable);
         if (info.contentDescription != null) {
             setContentDescription(info.contentDescription);
         }
@@ -175,7 +175,7 @@
         if (info.isDisabled()) {
             iconDrawable.setState(FastBitmapDrawable.State.DISABLED);
         }
-        setIcon(iconDrawable, mIconSize);
+        setIcon(iconDrawable);
         setText(info.title);
         if (info.contentDescription != null) {
             setContentDescription(info.contentDescription);
@@ -188,7 +188,7 @@
     }
 
     public void applyFromPackageItemInfo(PackageItemInfo info) {
-        setIcon(mLauncher.createIconDrawable(info.iconBitmap), mIconSize);
+        setIcon(mLauncher.createIconDrawable(info.iconBitmap));
         setText(info.title);
         if (info.contentDescription != null) {
             setContentDescription(info.contentDescription);
@@ -205,7 +205,7 @@
      */
     public void applyDummyInfo() {
         ColorDrawable d = new ColorDrawable();
-        setIcon(mLauncher.resizeIconDrawable(d), mIconSize);
+        setIcon(mLauncher.resizeIconDrawable(d));
         setText("");
     }
 
@@ -477,7 +477,7 @@
                     preloadDrawable = (PreloadIconDrawable) mIcon;
                 } else {
                     preloadDrawable = new PreloadIconDrawable(mIcon, getPreloaderTheme());
-                    setIcon(preloadDrawable, mIconSize);
+                    setIcon(preloadDrawable);
                 }
 
                 preloadDrawable.setLevel(progressLevel);
@@ -506,10 +506,10 @@
      * Sets the icon for this view based on the layout direction.
      */
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
-    private Drawable setIcon(Drawable icon, int iconSize) {
+    public void setIcon(Drawable icon) {
         mIcon = icon;
-        if (iconSize != -1) {
-            mIcon.setBounds(0, 0, iconSize, iconSize);
+        if (mIconSize != -1) {
+            mIcon.setBounds(0, 0, mIconSize, mIconSize);
         }
         if (mLayoutHorizontal) {
             if (Utilities.ATLEAST_JB_MR1) {
@@ -520,7 +520,6 @@
         } else {
             setCompoundDrawables(null, mIcon, null, null);
         }
-        return icon;
     }
 
     @Override
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 61ac713..5a4ed2f 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -312,8 +312,7 @@
 
     @Override
     public void onClick(View v) {
-        LauncherAppState.getInstance().getAccessibilityDelegate()
-            .handleAccessibleDrop(this, null, null);
+        mLauncher.getAccessibilityDelegate().handleAccessibleDrop(this, null, null);
     }
 
     public int getTextColor() {
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 4a550ed..86f22d5 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -457,17 +457,14 @@
         // Layout the page indicators
         View pageIndicator = launcher.findViewById(R.id.page_indicator);
         if (pageIndicator != null) {
-            if (hasVerticalBarLayout) {
-                // Hide the page indicators when we have vertical search/hotseat
-                pageIndicator.setVisibility(View.GONE);
-            } else {
+            lp = (FrameLayout.LayoutParams) pageIndicator.getLayoutParams();
+            if (!hasVerticalBarLayout) {
                 // Put the page indicators above the hotseat
-                lp = (FrameLayout.LayoutParams) pageIndicator.getLayoutParams();
                 lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
                 lp.width = LayoutParams.WRAP_CONTENT;
                 lp.bottomMargin = hotseatBarHeightPx;
-                pageIndicator.setLayoutParams(lp);
             }
+            pageIndicator.setLayoutParams(lp);
         }
 
         // Layout the Overview Mode
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index 0b9e4ac..c73ceea 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -239,14 +239,12 @@
 
         if (keyCode == KeyEvent.KEYCODE_DPAD_UP &&
                 !profile.isVerticalBarLayout()) {
-            matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout,
-                    true /* hotseat horizontal */, profile.inv.hotseatAllAppsRank);
+            matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout, profile);
             iconIndex += iconParent.getChildCount();
             parent = iconParent;
         } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT &&
                 profile.isVerticalBarLayout()) {
-            matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout,
-                    false /* hotseat horizontal */, profile.inv.hotseatAllAppsRank);
+            matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout, profile);
             iconIndex += iconParent.getChildCount();
             parent = iconParent;
         } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT &&
@@ -372,12 +370,10 @@
         // to take a user to the hotseat. For other dpad navigation, do not use the matrix extended
         // with the hotseat.
         if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN && !profile.isVerticalBarLayout()) {
-            matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout,
-                    true /* horizontal */, profile.inv.hotseatAllAppsRank);
+            matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout, profile);
         } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT &&
                 profile.isVerticalBarLayout()) {
-            matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout,
-                    false /* horizontal */, profile.inv.hotseatAllAppsRank);
+            matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout, profile);
         } else if (isUninstallKeyChord(e)) {
             matrix = FocusLogic.createSparseMatrix(iconLayout);
             if (UninstallDropTarget.supportsDrop(launcher, itemInfo)) {
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 7e1ecf5..5245509 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -31,6 +31,7 @@
 import android.widget.FrameLayout;
 import android.widget.TextView;
 
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dynamicui.ExtractedColors;
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -44,9 +45,6 @@
     private Launcher mLauncher;
 
     @ViewDebug.ExportedProperty(category = "launcher")
-    private int mAllAppsButtonRank;
-
-    @ViewDebug.ExportedProperty(category = "launcher")
     private final boolean mHasVerticalHotseat;
 
     @ViewDebug.ExportedProperty(category = "launcher")
@@ -106,16 +104,10 @@
         return mHasVerticalHotseat ? (mContent.getCountY() - (rank + 1)) : 0;
     }
 
-    public boolean isAllAppsButtonRank(int rank) {
-        return rank == mAllAppsButtonRank;
-    }
-
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
         DeviceProfile grid = mLauncher.getDeviceProfile();
-
-        mAllAppsButtonRank = grid.inv.hotseatAllAppsRank;
         mContent = (CellLayout) findViewById(R.id.layout);
         if (grid.isLandscape && !grid.isLargeTablet) {
             mContent.setGridSize(1, (int) grid.inv.numHotseatIcons);
@@ -130,38 +122,41 @@
     void resetLayout() {
         mContent.removeAllViewsInLayout();
 
-        // Add the Apps button
-        Context context = getContext();
+        if (!FeatureFlags.NO_ALL_APPS_ICON) {
+            // Add the Apps button
+            Context context = getContext();
+            int allAppsButtonRank = mLauncher.getDeviceProfile().inv.getAllAppsButtonRank();
 
-        LayoutInflater inflater = LayoutInflater.from(context);
-        TextView allAppsButton = (TextView)
-                inflater.inflate(R.layout.all_apps_button, mContent, false);
-        Drawable d = context.getResources().getDrawable(R.drawable.all_apps_button_icon);
+            LayoutInflater inflater = LayoutInflater.from(context);
+            TextView allAppsButton = (TextView)
+                    inflater.inflate(R.layout.all_apps_button, mContent, false);
+            Drawable d = context.getResources().getDrawable(R.drawable.all_apps_button_icon);
 
-        mLauncher.resizeIconDrawable(d);
-        int scaleDownPx = getResources().getDimensionPixelSize(R.dimen.all_apps_button_scale_down);
-        Rect bounds = d.getBounds();
-        d.setBounds(bounds.left, bounds.top + scaleDownPx / 2, bounds.right - scaleDownPx,
-                bounds.bottom - scaleDownPx / 2);
-        allAppsButton.setCompoundDrawables(null, d, null, null);
+            mLauncher.resizeIconDrawable(d);
+            int scaleDownPx = getResources().getDimensionPixelSize(R.dimen.all_apps_button_scale_down);
+            Rect bounds = d.getBounds();
+            d.setBounds(bounds.left, bounds.top + scaleDownPx / 2, bounds.right - scaleDownPx,
+                    bounds.bottom - scaleDownPx / 2);
+            allAppsButton.setCompoundDrawables(null, d, null, null);
 
-        allAppsButton.setContentDescription(context.getString(R.string.all_apps_button_label));
-        allAppsButton.setOnKeyListener(new HotseatIconKeyEventListener());
-        if (mLauncher != null) {
-            mLauncher.setAllAppsButton(allAppsButton);
-            allAppsButton.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener());
-            allAppsButton.setOnClickListener(mLauncher);
-            allAppsButton.setOnLongClickListener(mLauncher);
-            allAppsButton.setOnFocusChangeListener(mLauncher.mFocusHandler);
+            allAppsButton.setContentDescription(context.getString(R.string.all_apps_button_label));
+            allAppsButton.setOnKeyListener(new HotseatIconKeyEventListener());
+            if (mLauncher != null) {
+                mLauncher.setAllAppsButton(allAppsButton);
+                allAppsButton.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener());
+                allAppsButton.setOnClickListener(mLauncher);
+                allAppsButton.setOnLongClickListener(mLauncher);
+                allAppsButton.setOnFocusChangeListener(mLauncher.mFocusHandler);
+            }
+
+            // Note: We do this to ensure that the hotseat is always laid out in the orientation of
+            // the hotseat in order regardless of which orientation they were added
+            int x = getCellXFromOrder(allAppsButtonRank);
+            int y = getCellYFromOrder(allAppsButtonRank);
+            CellLayout.LayoutParams lp = new CellLayout.LayoutParams(x, y, 1, 1);
+            lp.canReorder = false;
+            mContent.addViewToCellLayout(allAppsButton, -1, allAppsButton.getId(), lp, true);
         }
-
-        // Note: We do this to ensure that the hotseat is always laid out in the orientation of
-        // the hotseat in order regardless of which orientation they were added
-        int x = getCellXFromOrder(mAllAppsButtonRank);
-        int y = getCellYFromOrder(mAllAppsButtonRank);
-        CellLayout.LayoutParams lp = new CellLayout.LayoutParams(x,y,1,1);
-        lp.canReorder = false;
-        mContent.addViewToCellLayout(allAppsButton, -1, allAppsButton.getId(), lp, true);
     }
 
     @Override
@@ -223,4 +218,13 @@
             mBackgroundColor = color;
         }
     }
+
+    public void setBackgroundTransparent(boolean enable) {
+        // This causes re-layout. Should replace the logic with simply setting the background alpha
+        if (enable) {
+            setBackground(null);
+        } else {
+            setBackground(mBackground);
+        }
+    }
 }
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index 259370c..e136bcd 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -43,7 +43,7 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         // Get the hover color
-        mHoverColor = getResources().getColor(R.color.info_target_hover_tint);
+        mHoverColor = Utilities.getColorAccent(getContext());
 
         setDrawable(R.drawable.ic_info_launcher);
     }
diff --git a/src/com/android/launcher3/InsettableFrameLayout.java b/src/com/android/launcher3/InsettableFrameLayout.java
index 61edc0f..db4d855 100644
--- a/src/com/android/launcher3/InsettableFrameLayout.java
+++ b/src/com/android/launcher3/InsettableFrameLayout.java
@@ -18,6 +18,10 @@
     @ViewDebug.ExportedProperty(category = "launcher")
     protected Rect mInsets = new Rect();
 
+    public Rect getInsets() {
+        return mInsets;
+    }
+
     public InsettableFrameLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
         setOnHierarchyChangeListener(this);
@@ -34,9 +38,6 @@
             lp.rightMargin += (newInsets.right - oldInsets.right);
             lp.bottomMargin += (newInsets.bottom - oldInsets.bottom);
         }
-        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && child instanceof AllAppsContainerView) {
-            lp.setMargins(0, 0, 0, lp.bottomMargin);
-        }
         child.setLayoutParams(lp);
     }
 
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 0742df9..da95d66 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -23,6 +23,8 @@
 import android.view.Display;
 import android.view.WindowManager;
 
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.config.ProviderConfig;
 import com.android.launcher3.util.Thunk;
 
 import java.util.ArrayList;
@@ -77,9 +79,6 @@
     float hotseatIconSize;
     int defaultLayoutId;
 
-    // Derived invariant properties
-    public int hotseatAllAppsRank;
-
     DeviceProfile landscapeProfile;
     DeviceProfile portraitProfile;
 
@@ -141,7 +140,6 @@
         numRows = closestProfile.numRows;
         numColumns = closestProfile.numColumns;
         numHotseatIcons = closestProfile.numHotseatIcons;
-        hotseatAllAppsRank = (int) (numHotseatIcons / 2);
         defaultLayoutId = closestProfile.defaultLayoutId;
         numFolderRows = closestProfile.numFolderRows;
         numFolderColumns = closestProfile.numFolderColumns;
@@ -304,6 +302,17 @@
         return this;
     }
 
+    public int getAllAppsButtonRank() {
+        if (ProviderConfig.IS_DOGFOOD_BUILD && FeatureFlags.NO_ALL_APPS_ICON) {
+            throw new IllegalAccessError("Accessing all apps rank when all-apps is disabled");
+        }
+        return numHotseatIcons / 2;
+    }
+
+    public boolean isAllAppsButtonRank(int rank) {
+        return rank == getAllAppsButtonRank();
+    }
+
     private float weight(float x0, float y0, float x1, float y1, float pow) {
         float d = dist(x0, y0, x1, y1);
         if (Float.compare(d, 0f) == 0) {
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index 286a7f1..f54a2d4 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -32,7 +32,7 @@
     /**
      * Intent extra to store the profile. Format: UserHandle
      */
-    static final String EXTRA_PROFILE = "profile";
+    public static final String EXTRA_PROFILE = "profile";
 
     public static final int NO_ID = -1;
 
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index a504250..d25eca0 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -38,6 +38,7 @@
 import android.content.ComponentName;
 import android.content.ContentValues;
 import android.content.Context;
+import android.content.ContextWrapper;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -63,6 +64,7 @@
 import android.os.Message;
 import android.os.StrictMode;
 import android.os.SystemClock;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.text.Selection;
 import android.text.SpannableStringBuilder;
@@ -92,6 +94,7 @@
 
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.allapps.DefaultAppSearchController;
@@ -113,9 +116,10 @@
 import com.android.launcher3.logging.LoggerUtils;
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.model.WidgetsModel;
-import com.android.launcher3.pageindicators.PageIndicatorLine;
+import com.android.launcher3.pageindicators.PageIndicator;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.TestingUtils;
 import com.android.launcher3.util.Thunk;
@@ -144,11 +148,9 @@
     public static final String TAG = "Launcher";
     static final boolean LOGD = false;
 
-    static final boolean PROFILE_STARTUP = false;
     static final boolean DEBUG_WIDGETS = false;
     static final boolean DEBUG_STRICT_MODE = false;
     static final boolean DEBUG_RESUME_TIME = false;
-    static final boolean DEBUG_LOGGING = false;
 
     private static final int REQUEST_CREATE_SHORTCUT = 1;
     private static final int REQUEST_CREATE_APPWIDGET = 5;
@@ -233,7 +235,6 @@
 
     @Thunk Workspace mWorkspace;
     private View mLauncherView;
-    private PageIndicatorLine mPageIndicator;
     @Thunk DragLayer mDragLayer;
     private DragController mDragController;
 
@@ -286,11 +287,15 @@
     private LauncherModel mModel;
     private IconCache mIconCache;
     private ExtractedColors mExtractedColors;
+    private LauncherAccessibilityDelegate mAccessibilityDelegate;
     @Thunk boolean mUserPresent = true;
     private boolean mVisible = false;
     private boolean mHasFocus = false;
     private boolean mAttached = false;
 
+    /** Maps launcher activity components to their list of shortcut ids. */
+    private MultiHashMap<ComponentKey, String> mDeepShortcutMap = new MultiHashMap<>();
+
     private LauncherClings mClings;
 
     private View.OnTouchListener mHapticFeedbackTouchListener;
@@ -395,6 +400,9 @@
                     .penaltyDeath()
                     .build());
         }
+        if (LauncherAppState.PROFILE_STARTUP) {
+            Trace.beginSection("Launcher-onCreate");
+        }
 
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.preOnCreate();
@@ -414,6 +422,7 @@
         mIsSafeModeEnabled = getPackageManager().isSafeMode();
         mModel = app.setLauncher(this);
         mIconCache = app.getIconCache();
+        mAccessibilityDelegate = new LauncherAccessibilityDelegate(this);
 
         mDragController = new DragController(this);
         mAllAppsController = new AllAppsTransitionController(this);
@@ -429,11 +438,6 @@
         // LauncherModel load.
         mPaused = false;
 
-        if (PROFILE_STARTUP) {
-            android.os.Debug.startMethodTracing(
-                    Environment.getExternalStorageDirectory() + "/launcher");
-        }
-
         setContentView(R.layout.launcher);
 
         setupViews();
@@ -449,8 +453,8 @@
         mSavedState = savedInstanceState;
         restoreState(mSavedState);
 
-        if (PROFILE_STARTUP) {
-            android.os.Debug.stopMethodTracing();
+        if (LauncherAppState.PROFILE_STARTUP) {
+            Trace.endSection();
         }
 
         if (!mRestoring) {
@@ -508,9 +512,7 @@
         if (mExtractedColors != null && Utilities.isNycOrAbove()) {
             mExtractedColors.load(this);
             mHotseat.updateColor(mExtractedColors, !mPaused);
-            if (mPageIndicator != null) {
-                mPageIndicator.updateColor(mExtractedColors);
-            }
+            mWorkspace.getPageIndicator().updateColor(mExtractedColors);
         }
     }
 
@@ -624,29 +626,6 @@
         }
     }
 
-    /**
-     * Logger object is a singleton and does not have to be coupled with the foreground activity.
-     * Since most user event logging is done on the UI, the object is retrieved from the
-     * callback for convenience.
-     */
-    private UserEventDispatcher createUserEventDispatcher() {
-        return new UserEventDispatcher() {
-            @Override
-            public void dispatchUserEvent(LauncherLogProto.LauncherEvent ev, Intent intent) {
-                if (!DEBUG_LOGGING) {
-                    return;
-                }
-                Log.d("UserEvent", String.format(Locale.US,
-                        "action:%s\nchild:%s\nparent:%s\nelapsed container %d ms session %d ms",
-                        LoggerUtils.getActionStr(ev.action),
-                        LoggerUtils.getTargetStr(ev.srcTarget[0]),
-                        LoggerUtils.getTargetStr(ev.srcTarget[1]),
-                        ev.elapsedContainerMillis,
-                        ev.elapsedSessionMillis));
-            }
-        };
-    }
-
     public UserEventDispatcher getUserEventDispatcher() {
         if (mLauncherCallbacks != null) {
             UserEventDispatcher dispatcher = mLauncherCallbacks.getUserEventDispatcher();
@@ -655,8 +634,11 @@
             }
         }
 
+        // Logger object is a singleton and does not have to be coupled with the foreground
+        // activity. Since most user event logging is done on the UI, the object is retrieved
+        // from the callback for convenience.
         if (mUserEventDispatcher == null) {
-            mUserEventDispatcher = createUserEventDispatcher();
+            mUserEventDispatcher = new UserEventDispatcher();
         }
         return mUserEventDispatcher;
     }
@@ -1344,9 +1326,8 @@
         mLauncherView = findViewById(R.id.launcher);
         mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
         mFocusHandler = mDragLayer.getFocusIndicatorHelper();
-
         mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace);
-        mPageIndicator = (PageIndicatorLine) mDragLayer.findViewById(R.id.page_indicator);
+        mWorkspace.initParentViews(mDragLayer);
 
         mLauncherView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
@@ -1392,6 +1373,8 @@
         mDragController.addDropTarget(mWorkspace);
         mDropTargetBar.setup(mDragController);
 
+        mAllAppsController.setupViews(mAppsView, mHotseat, mWorkspace);
+
         if (TestingUtils.MEMORY_DUMP_ENABLED) {
             TestingUtils.addWeightWatcher(this);
         }
@@ -1457,13 +1440,14 @@
 
     /**
      * Sets the all apps button. This method is called from {@link Hotseat}.
+     * TODO: Get rid of this.
      */
     public void setAllAppsButton(View allAppsButton) {
         mAllAppsButton = allAppsButton;
     }
 
-    public View getAllAppsButton() {
-        return mAllAppsButton;
+    public View getStartViewForAllAppsRevealAnimation() {
+        return FeatureFlags.NO_ALL_APPS_ICON ? mWorkspace.getPageIndicator() : mAllAppsButton;
     }
 
     public View getWidgetsButton() {
@@ -1998,6 +1982,10 @@
         }
     }
 
+    public LauncherAccessibilityDelegate getAccessibilityDelegate() {
+        return mAccessibilityDelegate;
+    }
+
     public DragController getDragController() {
         return mDragController;
     }
@@ -2345,7 +2333,7 @@
      * @param itemInfo the {@link ItemInfo} for this view.
      * @param deleteFromDb whether or not to delete this item from the db.
      */
-    public boolean removeItem(View v, ItemInfo itemInfo, boolean deleteFromDb) {
+    public boolean removeItem(View v, final ItemInfo itemInfo, boolean deleteFromDb) {
         if (itemInfo instanceof ShortcutInfo) {
             // Remove the shortcut from the folder before removing it from launcher
             View folderIcon = mWorkspace.getHomescreenIconByItemId(itemInfo.container);
@@ -2373,7 +2361,6 @@
             if (deleteFromDb) {
                 deleteWidgetInfo(widgetInfo);
             }
-
         } else {
             return false;
         }
@@ -2491,7 +2478,7 @@
             if (v instanceof FolderIcon) {
                 onClickFolderIcon(v);
             }
-        } else if (v == mAllAppsButton) {
+        } else if (v instanceof PageIndicator || (v == mAllAppsButton && mAllAppsButton != null)) {
             onClickAllAppsButton(v);
         } else if (tag instanceof AppInfo) {
             startAppShortcutOrInfoActivity(v);
@@ -2692,7 +2679,7 @@
      * Event handler for the (Add) Widgets button that appears after a long press
      * on the home screen.
      */
-    protected void onClickAddWidgetButton(View view) {
+    public void onClickAddWidgetButton(View view) {
         if (LOGD) Log.d(TAG, "onClickAddWidgetButton");
         if (mIsSafeModeEnabled) {
             Toast.makeText(this, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
@@ -2705,7 +2692,7 @@
      * Event handler for the wallpaper picker button that appears after a long press
      * on the home screen.
      */
-    protected void onClickWallpaperPicker(View v) {
+    public void onClickWallpaperPicker(View v) {
         if (!Utilities.isWallapaperAllowed(this)) {
             Toast.makeText(this, R.string.msg_disabled_by_admin, Toast.LENGTH_SHORT).show();
             return;
@@ -2728,7 +2715,7 @@
      * Event handler for a click on the settings button that appears after a long press
      * on the home screen.
      */
-    private void onClickSettingsButton(View v) {
+    public void onClickSettingsButton(View v) {
         if (LOGD) Log.d(TAG, "onClickSettingsButton");
         startActivity(new Intent(Utilities.ACTION_APPLICATION_PREFERENCES)
                 .setPackage(getPackageName()));
@@ -2810,8 +2797,16 @@
                 // is enabled by default on NYC.
                 StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll()
                         .penaltyLog().build());
-                // Could be launching some bookkeeping activity
-                startActivity(intent, optsBundle);
+
+                if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+                    String id = ((ShortcutInfo) info).getDeepShortcutId();
+                    String packageName = intent.getPackage();
+                    LauncherAppsCompat.getInstance(this).startShortcut(
+                                packageName, id, intent.getSourceBounds(), optsBundle, info.user);
+                } else {
+                    // Could be launching some bookkeeping activity
+                    startActivity(intent, optsBundle);
+                }
             } finally {
                 StrictMode.setVmPolicy(oldPolicy);
             }
@@ -2887,8 +2882,9 @@
                     new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight()));
         }
         try {
-            if (Utilities.ATLEAST_MARSHMALLOW &&
-                    item != null && item.itemType == Favorites.ITEM_TYPE_SHORTCUT) {
+            if (Utilities.ATLEAST_MARSHMALLOW && item != null
+                    && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
+                    || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)) {
                 // Shortcuts need some special checks due to legacy reasons.
                 startShortcutIntentSafely(intent, optsBundle, item);
             } else if (user == null || user.equals(UserHandleCompat.myUserHandle())) {
@@ -3096,7 +3092,7 @@
         if (isWorkspaceLocked()) return false;
         if (mState != State.WORKSPACE) return false;
 
-        if (v == mAllAppsButton) {
+        if (v == mAllAppsButton && mAllAppsButton != null) {
             onLongClickAllAppsButton(v);
             return true;
         }
@@ -3127,7 +3123,6 @@
 
         // The hotseat touch handling does not go through Workspace, and we always allow long press
         // on hotseat items.
-        final boolean inHotseat = isHotseatLayout(v);
         if (!mDragController.isDragging()) {
             if (itemUnderLongClick == null) {
                 // User long pressed on empty space
@@ -3138,13 +3133,12 @@
                 } else {
                     showOverviewMode(true);
                     mHotseat.requestDisallowInterceptTouchEvent(true);
-
                 }
             } else {
-                final boolean isAllAppsButton = inHotseat && isAllAppsButtonRank(
-                        mHotseat.getOrderInHotseat(
-                                longClickCellInfo.cellX,
-                                longClickCellInfo.cellY));
+                final boolean isAllAppsButton =
+                        !FeatureFlags.NO_ALL_APPS_ICON && isHotseatLayout(v) &&
+                                mDeviceProfile.inv.isAllAppsButtonRank(mHotseat.getOrderInHotseat(
+                                        longClickCellInfo.cellX, longClickCellInfo.cellY));
                 if (!(itemUnderLongClick instanceof Folder || isAllAppsButton)) {
                     // User long pressed on an item
                     mWorkspace.startDrag(longClickCellInfo);
@@ -3264,7 +3258,7 @@
     /**
      * Shows the overview button.
      */
-    void showOverviewMode(boolean animated) {
+    public void showOverviewMode(boolean animated) {
         showOverviewMode(animated, false);
     }
 
@@ -3447,7 +3441,7 @@
      * Updates the set of predicted apps if it hasn't been updated since the last time Launcher was
      * resumed.
      */
-    private void tryAndUpdatePredictedApps() {
+    public void tryAndUpdatePredictedApps() {
         if (mLauncherCallbacks != null) {
             List<ComponentKey> apps = mLauncherCallbacks.getPredictedApps();
             if (apps != null) {
@@ -3578,6 +3572,9 @@
      * Implementation of the method from LauncherModel.Callbacks.
      */
     public void startBinding() {
+        if (LauncherAppState.PROFILE_STARTUP) {
+            Trace.beginSection("Starting page bind");
+        }
         setWorkspaceLoading(true);
 
         // Clear the workspace because it's going to be rebound
@@ -3588,15 +3585,22 @@
         if (mHotseat != null) {
             mHotseat.resetLayout();
         }
+        if (LauncherAppState.PROFILE_STARTUP) {
+            Trace.endSection();
+        }
     }
 
     @Override
     public void bindScreens(ArrayList<Long> orderedScreenIds) {
         // Make sure the first screen is always at the start.
-        if (orderedScreenIds.indexOf(Workspace.FIRST_SCREEN_ID) != 0) {
+        if (FeatureFlags.QSB_ON_FIRST_SCREEN &&
+                orderedScreenIds.indexOf(Workspace.FIRST_SCREEN_ID) != 0) {
             orderedScreenIds.remove(Workspace.FIRST_SCREEN_ID);
             orderedScreenIds.add(0, Workspace.FIRST_SCREEN_ID);
             mModel.updateWorkspaceScreenOrder(this, orderedScreenIds);
+        } else if (!FeatureFlags.QSB_ON_FIRST_SCREEN && orderedScreenIds.isEmpty()) {
+            // If there are no screens, we need to have an empty screen
+            mWorkspace.addExtraEmptyScreen();
         }
         bindAddScreens(orderedScreenIds);
 
@@ -3612,7 +3616,7 @@
         int count = orderedScreenIds.size();
         for (int i = 0; i < count; i++) {
             long screenId = orderedScreenIds.get(i);
-            if (screenId != Workspace.FIRST_SCREEN_ID) {
+            if (!FeatureFlags.QSB_ON_FIRST_SCREEN || screenId != Workspace.FIRST_SCREEN_ID) {
                 // No need to bind the first screen, as its always bound.
                 mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(screenId);
             }
@@ -3692,6 +3696,7 @@
             switch (item.itemType) {
                 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+                case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
                     ShortcutInfo info = (ShortcutInfo) item;
                     view = createShortcut(info);
                     break;
@@ -3949,6 +3954,9 @@
         if (waitUntilResume(r)) {
             return;
         }
+        if (LauncherAppState.PROFILE_STARTUP) {
+            Trace.beginSection("Page bind completed");
+        }
         if (mSavedState != null) {
             if (!mWorkspace.hasFocus()) {
                 mWorkspace.getChildAt(mWorkspace.getCurrentPage()).requestFocus();
@@ -3983,13 +3991,9 @@
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.finishBindingItems(false);
         }
-    }
-
-    public boolean isAllAppsButtonRank(int rank) {
-        if (mHotseat != null) {
-            return mHotseat.isAllAppsButtonRank(rank);
+        if (LauncherAppState.PROFILE_STARTUP) {
+            Trace.endSection();
         }
-        return false;
     }
 
     private boolean canRunNewAppsAnimation() {
@@ -4049,6 +4053,16 @@
     }
 
     /**
+     * Copies LauncherModel's map of activities to shortcut ids to Launcher's. This is necessary
+     * because LauncherModel's map is updated in the background, while Launcher runs on the UI.
+     */
+    @Override
+    public void bindDeepShortcutMap(MultiHashMap<ComponentKey, String> deepShortcutMapCopy) {
+        mDeepShortcutMap = deepShortcutMapCopy;
+        if (LOGD) Log.d(TAG, "bindDeepShortcutMap: " + mDeepShortcutMap);
+    }
+
+    /**
      * A package was updated.
      *
      * Implementation of the method from LauncherModel.Callbacks.
@@ -4520,6 +4534,13 @@
         }
     }
 
+    public static Launcher getLauncher(Context context) {
+        if (context instanceof Launcher) {
+            return (Launcher) context;
+        }
+        return ((Launcher) ((ContextWrapper) context).getBaseContext());
+    }
+
     private class RotationPrefChangeHandler implements OnSharedPreferenceChangeListener, Runnable {
 
         @Override
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index ae3abbf..4bc76fb 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -16,20 +16,21 @@
 
 package com.android.launcher3;
 
-import android.app.SearchManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.util.Log;
 
-import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.PackageInstallerCompat;
 import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.ProviderConfig;
 import com.android.launcher3.dynamicui.ExtractionUtils;
-import com.android.launcher3.util.ConfigMonitor;
 import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.shortcuts.ShortcutCache;
+import com.android.launcher3.util.ConfigMonitor;
 import com.android.launcher3.util.TestingUtils;
 import com.android.launcher3.util.Thunk;
 
@@ -37,10 +38,13 @@
 
 public class LauncherAppState {
 
+    public static final boolean PROFILE_STARTUP = ProviderConfig.IS_DOGFOOD_BUILD;
+
     private final AppFilter mAppFilter;
     @Thunk final LauncherModel mModel;
     private final IconCache mIconCache;
     private final WidgetPreviewLoader mWidgetCache;
+    private final DeepShortcutManager mDeepShortcutManager;
 
     @Thunk boolean mWallpaperChangedSinceLastCheck;
 
@@ -51,8 +55,6 @@
 
     private InvariantDeviceProfile mInvariantDeviceProfile;
 
-    private LauncherAccessibilityDelegate mAccessibilityDelegate;
-
     public static LauncherAppState getInstance() {
         if (INSTANCE == null) {
             INSTANCE = new LauncherAppState();
@@ -96,9 +98,10 @@
         mInvariantDeviceProfile = new InvariantDeviceProfile(sContext);
         mIconCache = new IconCache(sContext, mInvariantDeviceProfile);
         mWidgetCache = new WidgetPreviewLoader(sContext, mIconCache);
+        mDeepShortcutManager = new DeepShortcutManager(sContext, new ShortcutCache());
 
         mAppFilter = AppFilter.loadByName(sContext.getString(R.string.app_filter_class));
-        mModel = new LauncherModel(this, mIconCache, mAppFilter);
+        mModel = new LauncherModel(this, mIconCache, mAppFilter, mDeepShortcutManager);
 
         LauncherAppsCompat.getInstance(sContext).addOnAppsChangedCallback(mModel);
 
@@ -154,15 +157,9 @@
     LauncherModel setLauncher(Launcher launcher) {
         sLauncherProvider.get().setLauncherProviderChangeListener(launcher);
         mModel.initialize(launcher);
-        mAccessibilityDelegate = ((launcher != null) && Utilities.ATLEAST_LOLLIPOP) ?
-            new LauncherAccessibilityDelegate(launcher) : null;
         return mModel;
     }
 
-    public LauncherAccessibilityDelegate getAccessibilityDelegate() {
-        return mAccessibilityDelegate;
-    }
-
     public IconCache getIconCache() {
         return mIconCache;
     }
@@ -175,6 +172,10 @@
         return mWidgetCache;
     }
 
+    public DeepShortcutManager getShortcutManager() {
+        return mDeepShortcutManager;
+    }
+
     public boolean hasWallpaperChangedSinceLastCheck() {
         boolean result = mWallpaperChangedSinceLastCheck;
         mWallpaperChangedSinceLastCheck = false;
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index 53d9c04..d371a86 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -63,7 +63,7 @@
         mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
         mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         mDragLayer = ((Launcher) context).getDragLayer();
-        setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
+        setAccessibilityDelegate(((Launcher) context).getAccessibilityDelegate());
 
         setBackgroundResource(R.drawable.widget_internal_focus_bg);
     }
diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java
index 7238f70..e987a9b 100644
--- a/src/com/android/launcher3/LauncherBackupHelper.java
+++ b/src/com/android/launcher3/LauncherBackupHelper.java
@@ -439,7 +439,6 @@
         data.desktopRows = profile.numRows;
         data.desktopCols = profile.numColumns;
         data.hotseatCount = profile.numHotseatIcons;
-        data.allappsRank = profile.hotseatAllAppsRank;
         return data;
     }
 
@@ -561,7 +560,8 @@
 
         // Don't backup apps in other profiles for now.
         String where = "(" + Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_APPLICATION + " OR " +
-                Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_SHORTCUT + ") AND " +
+                Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_SHORTCUT + " OR " +
+                Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_DEEP_SHORTCUT + ") AND " +
                 getUserSelectionArg();
         Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION,
                 where, null, null);
@@ -799,7 +799,8 @@
         return favorite.container == Favorites.CONTAINER_HOTSEAT
                 && favorite.intent != null
                 && (favorite.itemType == Favorites.ITEM_TYPE_APPLICATION
-                || favorite.itemType == Favorites.ITEM_TYPE_SHORTCUT);
+                || favorite.itemType == Favorites.ITEM_TYPE_SHORTCUT
+                || favorite.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT);
     }
 
     /** Serialize a Favorite for persistence, including a checksum wrapper. */
@@ -836,7 +837,8 @@
             if (!TextUtils.isEmpty(appWidgetProvider)) {
                 favorite.appWidgetProvider = appWidgetProvider;
             }
-        } else if (favorite.itemType == Favorites.ITEM_TYPE_SHORTCUT) {
+        } else if (favorite.itemType == Favorites.ITEM_TYPE_SHORTCUT
+                || favorite.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
             String iconPackage = c.getString(ICON_PACKAGE_INDEX);
             String iconResource = c.getString(ICON_RESOURCE_INDEX);
             if (!TextUtils.isEmpty(iconPackage) && !TextUtils.isEmpty(iconResource)) {
@@ -898,7 +900,8 @@
         values.put(Favorites.SPANY, favorite.spanY);
         values.put(Favorites.RANK, favorite.rank);
 
-        if (favorite.itemType == Favorites.ITEM_TYPE_SHORTCUT) {
+        if (favorite.itemType == Favorites.ITEM_TYPE_SHORTCUT
+                || favorite.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
             values.put(Favorites.ICON_PACKAGE, favorite.iconPackage);
             values.put(Favorites.ICON_RESOURCE, favorite.iconResource);
             values.put(Favorites.ICON, favorite.icon);
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 557a91a..8e404a7 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -37,10 +37,12 @@
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.SystemClock;
+import android.os.Trace;
 import android.provider.BaseColumns;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.LongSparseArray;
+import android.util.MutableInt;
 import android.util.Pair;
 
 import com.android.launcher3.compat.AppWidgetManagerCompat;
@@ -50,6 +52,7 @@
 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.config.ProviderConfig;
 import com.android.launcher3.dynamicui.ExtractionUtils;
 import com.android.launcher3.folder.Folder;
@@ -57,6 +60,10 @@
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.GridSizeMigrationTask;
 import com.android.launcher3.model.WidgetsModel;
+import com.android.launcher3.provider.LauncherDbUtils;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.CursorIconInfo;
 import com.android.launcher3.util.FlagOp;
@@ -66,6 +73,7 @@
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.StringFilter;
+import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.ViewOnDrawExecutor;
 
@@ -80,6 +88,7 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.concurrent.Executor;
@@ -116,8 +125,9 @@
     // We start off with everything not loaded.  After that, we assume that
     // our monitoring of the package manager provides all updates and we never
     // need to do a requery.  These are only ever touched from the loader thread.
-    @Thunk boolean mWorkspaceLoaded;
-    @Thunk boolean mAllAppsLoaded;
+    private boolean mWorkspaceLoaded;
+    private boolean mAllAppsLoaded;
+    private boolean mDeepShortcutsLoaded;
 
     /**
      * Set of runnables to be called on the background thread after the workspace binding
@@ -132,6 +142,9 @@
     // Entire list of widgets.
     private final WidgetsModel mBgWidgetsModel;
 
+    // Maps all launcher activities to the id's of their shortcuts (if they have any).
+    private final MultiHashMap<ComponentKey, String> mBgDeepShortcutMap = new MultiHashMap<>();
+
     // The lock that must be acquired before referencing any static bg data structures.  Unlike
     // other locks, this one can generally be held long-term because we never expect any of these
     // static data structures to be referenced outside of the worker thread except on the first
@@ -157,16 +170,21 @@
     // sBgWorkspaceScreens is the ordered set of workspace screens.
     static final ArrayList<Long> sBgWorkspaceScreens = new ArrayList<Long>();
 
+    // sBgPinnedShortcutCounts is the ComponentKey representing a pinned shortcut to the number of
+    // times it is pinned.
+    static final Map<ShortcutKey, MutableInt> sBgPinnedShortcutCounts = new HashMap<>();
+
     // sPendingPackages is a set of packages which could be on sdcard and are not available yet
     static final HashMap<UserHandleCompat, HashSet<String>> sPendingPackages =
             new HashMap<UserHandleCompat, HashSet<String>>();
 
     // </ only access in worker thread >
 
-    @Thunk IconCache mIconCache;
+    private IconCache mIconCache;
+    private DeepShortcutManager mDeepShortcutManager;
 
-    @Thunk final LauncherAppsCompat mLauncherApps;
-    @Thunk final UserManagerCompat mUserManager;
+    private final LauncherAppsCompat mLauncherApps;
+    private final UserManagerCompat mUserManager;
 
     public interface Callbacks {
         public boolean setLoadOnResume();
@@ -194,21 +212,23 @@
         public void bindAppInfosRemoved(ArrayList<AppInfo> appInfos);
         public void notifyWidgetProvidersChanged();
         public void bindWidgetsModel(WidgetsModel model);
-        public boolean isAllAppsButtonRank(int rank);
         public void onPageBoundSynchronously(int page);
         public void executeOnNextDraw(ViewOnDrawExecutor executor);
+        public void bindDeepShortcutMap(MultiHashMap<ComponentKey, String> deepShortcutMap);
     }
 
     public interface ItemInfoFilter {
         public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn);
     }
 
-    LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
+    LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter,
+            DeepShortcutManager deepShortcutManager) {
         Context context = app.getContext();
         mApp = app;
         mBgAllAppsList = new AllAppsList(iconCache, appFilter);
         mBgWidgetsModel = new WidgetsModel(context, iconCache, appFilter);
         mIconCache = iconCache;
+        mDeepShortcutManager = deepShortcutManager;
 
         mLauncherApps = LauncherAppsCompat.getInstance(context);
         mUserManager = UserManagerCompat.getInstance(context);
@@ -677,6 +697,7 @@
                 switch (modelItem.itemType) {
                     case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                     case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+                    case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
                     case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
                         if (!sBgWorkspaceItems.contains(modelItem)) {
                             sBgWorkspaceItems.add(modelItem);
@@ -890,6 +911,7 @@
                             // Fall through
                         case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                         case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+                        case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
                             if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
                                     item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
                                 sBgWorkspaceItems.add(item);
@@ -901,6 +923,14 @@
                                     Log.e(TAG, msg);
                                 }
                             }
+                            if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+                                ShortcutInfo shortcutInfo = (ShortcutInfo) item;
+                                ShortcutKey shortcutToPin = new ShortcutKey(
+                                        shortcutInfo.intent.getPackage(),
+                                        shortcutInfo.user,
+                                        shortcutInfo.getDeepShortcutId());
+                                incrementPinnedShortcutCount(shortcutToPin, true /* shouldPin */);
+                            }
                             break;
                         case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
                             sBgAppWidgets.add((LauncherAppWidgetInfo) item);
@@ -967,6 +997,14 @@
                                 }
                                 sBgWorkspaceItems.remove(item);
                                 break;
+                            case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
+                                ShortcutInfo shortcutInfo = ((ShortcutInfo) item);
+                                ShortcutKey pinnedShortcut = new ShortcutKey(
+                                        shortcutInfo.intent.getPackage(),
+                                        shortcutInfo.user,
+                                        shortcutInfo.getDeepShortcutId());
+                                decrementPinnedShortcutCount(pinnedShortcut);
+                                // Fall through.
                             case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                             case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                                 sBgWorkspaceItems.remove(item);
@@ -984,6 +1022,39 @@
     }
 
     /**
+     * Decrement the count for the given pinned shortcut, unpinning it if the count becomes 0.
+     */
+    private static void decrementPinnedShortcutCount(final ShortcutKey pinnedShortcut) {
+        synchronized (sBgLock) {
+            MutableInt count = sBgPinnedShortcutCounts.get(pinnedShortcut);
+            if (count == null || --count.value == 0) {
+                LauncherAppState.getInstance().getShortcutManager().unpinShortcut(pinnedShortcut);
+            }
+        }
+    }
+
+    /**
+     * Increment the count for the given shortcut, pinning it if the count becomes 1.
+     *
+     * As an optimization, the caller can pass shouldPin == false to avoid
+     * unnecessary RPC's if the shortcut is already pinned.
+     */
+    private static void incrementPinnedShortcutCount(ShortcutKey pinnedShortcut, boolean shouldPin) {
+        synchronized (sBgLock) {
+            MutableInt count = sBgPinnedShortcutCounts.get(pinnedShortcut);
+            if (count == null) {
+                count = new MutableInt(1);
+                sBgPinnedShortcutCounts.put(pinnedShortcut, count);
+            } else {
+                count.value++;
+            }
+            if (shouldPin && count.value == 1) {
+                LauncherAppState.getInstance().getShortcutManager().pinShortcut(pinnedShortcut);
+            }
+        }
+    }
+
+    /**
      * Update the order of the workspace screens in the database. The array list contains
      * a list of screen ids in the order that they should appear.
      */
@@ -1075,28 +1146,28 @@
     @Override
     public void onPackageChanged(String packageName, UserHandleCompat user) {
         int op = PackageUpdatedTask.OP_UPDATE;
-        enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
+        enqueueItemUpdatedTask(new PackageUpdatedTask(op, new String[] { packageName },
                 user));
     }
 
     @Override
     public void onPackageRemoved(String packageName, UserHandleCompat user) {
         int op = PackageUpdatedTask.OP_REMOVE;
-        enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
+        enqueueItemUpdatedTask(new PackageUpdatedTask(op, new String[] { packageName },
                 user));
     }
 
     @Override
     public void onPackageAdded(String packageName, UserHandleCompat user) {
         int op = PackageUpdatedTask.OP_ADD;
-        enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
+        enqueueItemUpdatedTask(new PackageUpdatedTask(op, new String[] { packageName },
                 user));
     }
 
     @Override
     public void onPackagesAvailable(String[] packageNames, UserHandleCompat user,
             boolean replacing) {
-        enqueuePackageUpdated(
+        enqueueItemUpdatedTask(
                 new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE, packageNames, user));
     }
 
@@ -1104,7 +1175,7 @@
     public void onPackagesUnavailable(String[] packageNames, UserHandleCompat user,
             boolean replacing) {
         if (!replacing) {
-            enqueuePackageUpdated(new PackageUpdatedTask(
+            enqueueItemUpdatedTask(new PackageUpdatedTask(
                     PackageUpdatedTask.OP_UNAVAILABLE, packageNames,
                     user));
         }
@@ -1112,18 +1183,24 @@
 
     @Override
     public void onPackagesSuspended(String[] packageNames, UserHandleCompat user) {
-        enqueuePackageUpdated(new PackageUpdatedTask(
+        enqueueItemUpdatedTask(new PackageUpdatedTask(
                 PackageUpdatedTask.OP_SUSPEND, packageNames,
                 user));
     }
 
     @Override
     public void onPackagesUnsuspended(String[] packageNames, UserHandleCompat user) {
-        enqueuePackageUpdated(new PackageUpdatedTask(
+        enqueueItemUpdatedTask(new PackageUpdatedTask(
                 PackageUpdatedTask.OP_UNSUSPEND, packageNames,
                 user));
     }
 
+    @Override
+    public void onShortcutsChanged(String packageName, List<ShortcutInfoCompat> shortcuts,
+            UserHandleCompat user) {
+        enqueueItemUpdatedTask(new ShortcutsChangedTask(packageName, shortcuts, user));
+    }
+
     /**
      * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
      * ACTION_PACKAGE_CHANGED.
@@ -1144,7 +1221,7 @@
                 LauncherAppsCompat.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) {
             UserHandleCompat user = UserHandleCompat.fromIntent(intent);
             if (user != null) {
-                enqueuePackageUpdated(new PackageUpdatedTask(
+                enqueueItemUpdatedTask(new PackageUpdatedTask(
                         PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE,
                         new String[0], user));
             }
@@ -1169,6 +1246,8 @@
             stopLoaderLocked();
             if (resetAllAppsLoaded) mAllAppsLoaded = false;
             if (resetWorkspaceLoaded) mWorkspaceLoaded = false;
+            // Always reset deep shortcuts loaded.
+            mDeepShortcutsLoaded = false;
         }
     }
 
@@ -1246,22 +1325,8 @@
         final Uri screensUri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
 
         // Get screens ordered by rank.
-        final Cursor sc = contentResolver.query(screensUri, null, null, null,
-                LauncherSettings.WorkspaceScreens.SCREEN_RANK);
-        ArrayList<Long> screenIds = new ArrayList<Long>();
-        try {
-            final int idIndex = sc.getColumnIndexOrThrow(LauncherSettings.WorkspaceScreens._ID);
-            while (sc.moveToNext()) {
-                try {
-                    screenIds.add(sc.getLong(idIndex));
-                } catch (Exception e) {
-                    FileLog.d(TAG, "Invalid screen id", e);
-                }
-            }
-        } finally {
-            sc.close();
-        }
-        return screenIds;
+        return LauncherDbUtils.getScreenIdsFromCursor(contentResolver.query(
+                screensUri, null, null, null, LauncherSettings.WorkspaceScreens.SCREEN_RANK));
     }
 
     /**
@@ -1269,6 +1334,7 @@
      *   - workspace icons
      *   - widgets
      *   - all apps icons
+     *   - deep shortcuts within apps
      */
     private class LoaderTask implements Runnable {
         private Context mContext;
@@ -1400,6 +1466,12 @@
                 // second step
                 if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
                 loadAndBindAllApps();
+
+                waitForIdle();
+
+                // third step
+                if (DEBUG_LOADERS) Log.d(TAG, "step 3: loading deep shortcuts");
+                loadAndBindDeepShortcuts();
             }
 
             // Clear out this reference, otherwise we end up holding it until all of the
@@ -1462,8 +1534,8 @@
             long containerIndex = item.screenId;
             if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
                 // Return early if we detect that an item is under the hotseat button
-                if (mCallbacks == null ||
-                        mCallbacks.get().isAllAppsButtonRank((int) item.screenId)) {
+                if (!FeatureFlags.NO_ALL_APPS_ICON &&
+                        profile.isAllAppsButtonRank((int) item.screenId)) {
                     Log.e(TAG, "Error loading shortcut into hotseat " + item
                             + " into position (" + item.screenId + ":" + item.cellX + ","
                             + item.cellY + ") occupied by all apps");
@@ -1522,8 +1594,9 @@
             if (!occupied.containsKey(item.screenId)) {
                 GridOccupancy screen = new GridOccupancy(countX + 1, countY + 1);
                 if (item.screenId == Workspace.FIRST_SCREEN_ID) {
-                    // Mark the first row as occupied in order to account for the QSB.
-                    screen.markCells(0, 0, countX + 1, 1, true);
+                    // Mark the first row as occupied (if the feature is enabled)
+                    // in order to account for the QSB.
+                    screen.markCells(0, 0, countX + 1, 1, FeatureFlags.QSB_ON_FIRST_SCREEN);
                 }
                 occupied.put(item.screenId, screen);
             }
@@ -1550,10 +1623,14 @@
                 sBgFolders.clear();
                 sBgItemsIdMap.clear();
                 sBgWorkspaceScreens.clear();
+                sBgPinnedShortcutCounts.clear();
             }
         }
 
         private void loadWorkspace() {
+            if (LauncherAppState.PROFILE_STARTUP) {
+                Trace.beginSection("Loading Workspace");
+            }
             final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
 
             final Context context = mContext;
@@ -1593,6 +1670,7 @@
 
                 final ArrayList<Long> itemsToRemove = new ArrayList<>();
                 final ArrayList<Long> restoredRows = new ArrayList<>();
+                Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>();
                 final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI;
                 if (DEBUG_LOADERS) Log.d(TAG, "loading model from " + contentUri);
                 final Cursor c = contentResolver.query(contentUri, null, null, null, null);
@@ -1643,6 +1721,13 @@
                         long serialNo = mUserManager.getSerialNumberForUser(user);
                         allUsers.put(serialNo, user);
                         quietMode.put(serialNo, mUserManager.isQuietModeEnabled(user));
+
+                        List<ShortcutInfoCompat> pinnedShortcuts = mDeepShortcutManager
+                                .queryForPinnedShortcuts(null, user);
+                        for (ShortcutInfoCompat shortcut : pinnedShortcuts) {
+                            shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut),
+                                    shortcut);
+                        }
                     }
 
                     ShortcutInfo info;
@@ -1665,6 +1750,7 @@
                             switch (itemType) {
                             case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                             case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+                            case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
                                 id = c.getLong(idIndex);
                                 intentDescription = c.getString(intentIndex);
                                 serialNumber = c.getInt(profileIdIndex);
@@ -1827,7 +1913,37 @@
                                     info = getAppShortcutInfo(intent, user, context, c,
                                             cursorIconInfo.iconIndex, titleIndex,
                                             allowMissingTarget, useLowResIcon);
-                                } else {
+                                } else if (itemType ==
+                                        LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+                                    String shortcutId = intent.getStringExtra(
+                                            ShortcutInfoCompat.EXTRA_SHORTCUT_ID);
+                                    String packageName = intent.getPackage();
+                                    ShortcutKey key = new ShortcutKey(intent.getPackage(),
+                                            user, shortcutId);
+                                    ShortcutInfoCompat pinnedShortcut =
+                                            shortcutKeyToPinnedShortcuts.get(key);
+                                    boolean shouldPin = false; // It's already pinned.
+                                    if (pinnedShortcut == null) {
+                                        // It shouldn't be possible for a shortcut to be on the
+                                        // workspace without being pinned, but if one somehow is,
+                                        // we should pin it now to get back to a good state.
+                                        Log.w(TAG, "Shortcut was on workspace but wasn't pinned");
+                                        // Get full details; incrementing the count will pin it.
+                                        List<ShortcutInfoCompat> fullDetails = mDeepShortcutManager
+                                                .queryForFullDetails(packageName,
+                                                Collections.singletonList(shortcutId), user);
+                                        if (fullDetails == null || fullDetails.isEmpty()) {
+                                            itemsToRemove.add(id);
+                                            continue;
+                                        } else {
+                                            pinnedShortcut = fullDetails.get(0);
+                                            shouldPin = true;
+                                        }
+                                    }
+                                    incrementPinnedShortcutCount(key, shouldPin);
+                                    info = ShortcutInfo.fromDeepShortcutInfo(pinnedShortcut,
+                                            context, launcherApps);
+                                } else { // item type == ITEM_TYPE_SHORTCUT
                                     info = getShortcutInfo(c, context, titleIndex, cursorIconInfo);
 
                                     // Shortcuts are only available on the primary profile
@@ -2106,6 +2222,15 @@
                     }
                 }
 
+                // Unpin shortcuts that don't exist on the workspace.
+                for (ShortcutKey key : shortcutKeyToPinnedShortcuts.keySet()) {
+                    MutableInt numTimesPinned = sBgPinnedShortcutCounts.get(key);
+                    if (numTimesPinned == null || numTimesPinned.value == 0) {
+                        // Shortcut is pinned but doesn't exist on the workspace; unpin it.
+                        mDeepShortcutManager.unpinShortcut(key);
+                    }
+                }
+
                 // Sort all the folder items and make sure the first 3 items are high resolution.
                 for (FolderInfo folder : sBgFolders) {
                     Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
@@ -2169,6 +2294,9 @@
                     }
                 }
             }
+            if (LauncherAppState.PROFILE_STARTUP) {
+                Trace.endSection();
+            }
         }
 
         /**
@@ -2634,6 +2762,27 @@
             }
         }
 
+        private void loadAndBindDeepShortcuts() {
+            if (DEBUG_LOADERS) {
+                Log.d(TAG, "loadAndBindDeepShortcuts mDeepShortcutsLoaded=" + mDeepShortcutsLoaded);
+            }
+            if (!mDeepShortcutsLoaded) {
+                mBgDeepShortcutMap.clear();
+                for (UserHandleCompat user : mUserManager.getUserProfiles()) {
+                    List<ShortcutInfoCompat> shortcuts = mDeepShortcutManager
+                            .queryForAllShortcuts(user);
+                    updateDeepShortcutMap(null, shortcuts);
+                }
+                synchronized (LoaderTask.this) {
+                    if (mStopped) {
+                        return;
+                    }
+                    mDeepShortcutsLoaded = true;
+                }
+            }
+            bindDeepShortcutMapOnMainThread();
+        }
+
         public void dumpState() {
             synchronized (sBgLock) {
                 Log.d(TAG, "mLoaderTask.mContext=" + mContext);
@@ -2644,6 +2793,40 @@
         }
     }
 
+    // Clear all the shortcuts for the given package, and re-add the new shortcuts.
+    private void updateDeepShortcutMap(String packageName, List<ShortcutInfoCompat> shortcuts) {
+        // Remove all keys associated with the given package.
+        if (packageName != null) {
+            Iterator<ComponentKey> keysIter = mBgDeepShortcutMap.keySet().iterator();
+            while (keysIter.hasNext()) {
+                if (keysIter.next().componentName.getPackageName().equals(packageName)) {
+                    keysIter.remove();
+                }
+            }
+        }
+
+        // Now add the new shortcuts to the map.
+        for (ShortcutInfoCompat shortcut : shortcuts) {
+            ComponentKey targetComponent
+                    = new ComponentKey(shortcut.getActivity(), shortcut.getUserHandle());
+            mBgDeepShortcutMap.addToList(targetComponent, shortcut.getId());
+        }
+    }
+
+    private void bindDeepShortcutMapOnMainThread() {
+        final MultiHashMap<ComponentKey, String> shortcutMapCopy = new MultiHashMap<>();
+        shortcutMapCopy.putAll(mBgDeepShortcutMap);
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                Callbacks callbacks = getCallback();
+                if (callbacks != null) {
+                    callbacks.bindDeepShortcutMap(shortcutMapCopy);
+                }
+            }
+        });
+    }
+
     /**
      * Called when the icons for packages have been updated in the icon cache.
      */
@@ -2669,19 +2852,7 @@
             mBgAllAppsList.updateIconsAndLabels(updatedPackages, user, updatedApps);
         }
 
-        if (!updatedShortcuts.isEmpty()) {
-            final UserHandleCompat userFinal = user;
-            mHandler.post(new Runnable() {
-
-                public void run() {
-                    Callbacks cb = getCallback();
-                    if (cb != null && callbacks == cb) {
-                        cb.bindShortcutsChanged(updatedShortcuts,
-                                new ArrayList<ShortcutInfo>(), userFinal);
-                    }
-                }
-            });
-        }
+        bindUpdatedShortcuts(updatedShortcuts, user);
 
         if (!updatedApps.isEmpty()) {
             mHandler.post(new Runnable() {
@@ -2696,7 +2867,25 @@
         }
     }
 
-    void enqueuePackageUpdated(PackageUpdatedTask task) {
+    private void bindUpdatedShortcuts(final ArrayList<ShortcutInfo> updatedShortcuts,
+            UserHandleCompat user) {
+        if (!updatedShortcuts.isEmpty()) {
+            final Callbacks callbacks = getCallback();
+            final UserHandleCompat userFinal = user;
+            mHandler.post(new Runnable() {
+
+                public void run() {
+                    Callbacks cb = getCallback();
+                    if (cb != null && callbacks == cb) {
+                        cb.bindShortcutsChanged(updatedShortcuts,
+                                new ArrayList<ShortcutInfo>(), userFinal);
+                    }
+                }
+            });
+        }
+    }
+
+    void enqueueItemUpdatedTask(Runnable task) {
         sWorker.post(task);
     }
 
@@ -2724,11 +2913,11 @@
                         }
                     }
                     if (!packagesRemoved.isEmpty()) {
-                        enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_REMOVE,
+                        enqueueItemUpdatedTask(new PackageUpdatedTask(PackageUpdatedTask.OP_REMOVE,
                                 packagesRemoved.toArray(new String[packagesRemoved.size()]), user));
                     }
                     if (!packagesUnavailable.isEmpty()) {
-                        enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_UNAVAILABLE,
+                        enqueueItemUpdatedTask(new PackageUpdatedTask(PackageUpdatedTask.OP_UNAVAILABLE,
                                 packagesUnavailable.toArray(new String[packagesUnavailable.size()]), user));
                     }
                 }
@@ -3086,6 +3275,60 @@
         }
     }
 
+    private class ShortcutsChangedTask implements Runnable {
+        private String mPackageName;
+        private List<ShortcutInfoCompat> mShortcuts;
+        private UserHandleCompat mUser;
+
+        public ShortcutsChangedTask(String packageName, List<ShortcutInfoCompat> shortcuts,
+                UserHandleCompat user) {
+            mPackageName = packageName;
+            mShortcuts = shortcuts;
+            mUser = user;
+        }
+
+        @Override
+        public void run() {
+            mDeepShortcutManager.onShortcutsChanged(mShortcuts);
+
+            Map<String, ShortcutInfoCompat> idsToShortcuts = new HashMap<>();
+            for (ShortcutInfoCompat shortcut : mShortcuts) {
+                idsToShortcuts.put(shortcut.getId(), shortcut);
+            }
+
+            // Find ShortcutInfo's that have changed on the workspace.
+            MultiHashMap<String, ShortcutInfo> idsToWorkspaceShortcutInfos = new MultiHashMap<>();
+            for (ItemInfo itemInfo : sBgItemsIdMap) {
+                if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+                    ShortcutInfo si = (ShortcutInfo) itemInfo;
+                    String shortcutId = si.getDeepShortcutId();
+                    if (idsToShortcuts.containsKey(shortcutId)) {
+                        idsToWorkspaceShortcutInfos.addToList(shortcutId, si);
+                    }
+                }
+            }
+
+            // Update the workspace to reflect the changes to updated shortcuts residing on it.
+            List<ShortcutInfoCompat> shortcuts = mDeepShortcutManager.queryForFullDetails(
+                    mPackageName, new ArrayList<>(idsToWorkspaceShortcutInfos.keySet()), mUser);
+            ArrayList<ShortcutInfo> updatedShortcutInfos = new ArrayList<>();
+            Context context = LauncherAppState.getInstance().getContext();
+            for (ShortcutInfoCompat fullDetails : shortcuts) {
+                List<ShortcutInfo> shortcutInfos = idsToWorkspaceShortcutInfos
+                        .get(fullDetails.getId());
+                for (ShortcutInfo shortcutInfo : shortcutInfos) {
+                    shortcutInfo.updateFromDeepShortcutInfo(fullDetails, context, mLauncherApps);
+                    updatedShortcutInfos.add(shortcutInfo);
+                }
+            }
+            bindUpdatedShortcuts(updatedShortcutInfos, mUser);
+
+            // Update the deep shortcut map, in case the list of ids has changed for an activity.
+            updateDeepShortcutMap(mPackageName, mShortcuts);
+            bindDeepShortcutMapOnMainThread();
+        }
+    }
+
     private void bindWidgetsModel(final Callbacks callbacks, final WidgetsModel model) {
         mHandler.post(new Runnable() {
             @Override
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index dfb8ba2..f5b32ed 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -23,7 +23,6 @@
 import android.content.ContentProvider;
 import android.content.ContentProviderOperation;
 import android.content.ContentProviderResult;
-import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
@@ -45,18 +44,20 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.Process;
+import android.os.Trace;
 import android.os.UserManager;
 import android.text.TextUtils;
 import android.util.Log;
-import android.util.SparseArray;
 
 import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.LauncherSettings.WorkspaceScreens;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.config.ProviderConfig;
 import com.android.launcher3.dynamicui.ExtractionUtils;
+import com.android.launcher3.provider.LauncherDbUtils;
 import com.android.launcher3.provider.RestoreDbTask;
 import com.android.launcher3.util.ManagedProfileHeuristic;
 import com.android.launcher3.util.NoLocaleSqliteContext;
@@ -66,13 +67,12 @@
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashSet;
 
 public class LauncherProvider extends ContentProvider {
     private static final String TAG = "LauncherProvider";
     private static final boolean LOGD = false;
 
-    private static final int DATABASE_VERSION = 26;
+    private static final int DATABASE_VERSION = 27;
 
     public static final String AUTHORITY = ProviderConfig.AUTHORITY;
 
@@ -87,6 +87,9 @@
 
     @Override
     public boolean onCreate() {
+        if (ProviderConfig.IS_DOGFOOD_BUILD) {
+            Log.d(TAG, "Launcher process started");
+        }
         mListenerHandler = new Handler(mListenerWrapper);
 
         LauncherAppState.setLauncherProvider(this);
@@ -116,6 +119,9 @@
      */
     protected synchronized void createDbIfNotExists() {
         if (mOpenHelper == null) {
+            if (LauncherAppState.PROFILE_STARTUP) {
+                Trace.beginSection("Opening workspace DB");
+            }
             mOpenHelper = new DatabaseHelper(getContext(), mListenerHandler);
 
             if (RestoreDbTask.isPending(getContext())) {
@@ -126,6 +132,10 @@
                 // executed again.
                 RestoreDbTask.setPending(getContext(), false);
             }
+
+            if (LauncherAppState.PROFILE_STARTUP) {
+                Trace.endSection();
+            }
         }
     }
 
@@ -780,7 +790,13 @@
                     ManagedProfileHeuristic.markExistingUsersForNoFolderCreation(mContext);
                 case 25:
                     convertShortcutsToLauncherActivities(db);
-                case 26: {
+                case 26:
+                    // QSB was moved to the grid. Clear the first row on screen 0.
+                    if (FeatureFlags.QSB_ON_FIRST_SCREEN &&
+                            !LauncherDbUtils.prepareScreenZeroToHostQsb(db)) {
+                        break;
+                    }
+                case 27: {
                     // DB Upgraded successfully
                     return;
                 }
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 52668d7..c213c8d 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -211,6 +211,11 @@
         public static final int ITEM_TYPE_CUSTOM_APPWIDGET = 5;
 
         /**
+         * The gesture is an application created deep shortcut
+         */
+        public static final int ITEM_TYPE_DEEP_SHORTCUT = 6;
+
+        /**
          * The appWidgetId of the widget
          *
          * <P>Type: INTEGER</P>
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index d62c629..e94153d 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -142,7 +142,7 @@
     public void startAnimationToAllApps(final Workspace.State fromWorkspaceState,
             final boolean animated, final boolean startSearchAfterTransition) {
         final AllAppsContainerView toView = mLauncher.getAppsView();
-        final View buttonView = mLauncher.getAllAppsButton();
+        final View buttonView = mLauncher.getStartViewForAllAppsRevealAnimation();
         PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks(1f) {
             @Override
             public float getMaterialRevealViewStartFinalRadius() {
@@ -249,10 +249,9 @@
         cancelAnimation();
 
         final View contentView = toView.getContentView();
-
+        playCommonTransitionAnimations(toWorkspaceState, fromView, toView,
+                animated, initialized, animation, revealDuration, layerViews);
         if (!animated || !initialized) {
-            playCommonTransitionAnimations(toWorkspaceState, fromView, toView,
-                    animated, initialized, animation, revealDuration, layerViews);
 
             toView.setTranslationX(0.0f);
             toView.setTranslationY(0.0f);
@@ -276,9 +275,6 @@
             return null;
         }
         if (animType == CIRCULAR_REVEAL) {
-            playCommonTransitionAnimations(toWorkspaceState, fromView, toView,
-                    animated, initialized, animation, revealDuration, layerViews);
-
             // Setup the reveal view animation
             final View revealView = toView.getRevealView();
 
@@ -424,9 +420,7 @@
                       pCb.onTransitionComplete();
                   }
             });
-            mAllAppsController.animateToAllApps(animation, revealDuration);
-            playCommonTransitionAnimations(toWorkspaceState, fromView, toView,
-                    animated, initialized, animation, revealDuration, layerViews);
+            mAllAppsController.animateToAllApps(animation, revealDuration, false);
 
             dispatchOnLauncherTransitionPrepare(fromView, animated, false);
             dispatchOnLauncherTransitionPrepare(toView, animated, false);
@@ -531,7 +525,7 @@
         };
         // Only animate the search bar if animating to spring loaded mode from all apps
         mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState, toWorkspaceState,
-                mLauncher.getAllAppsButton(), appsView,
+                mLauncher.getStartViewForAllAppsRevealAnimation(), appsView,
                 animated, type, onCompleteRunnable, cb);
     }
 
@@ -675,9 +669,9 @@
 
         boolean multiplePagesVisible = toWorkspaceState.hasMultipleVisiblePages;
 
+        playCommonTransitionAnimations(toWorkspaceState, fromView, toView,
+                animated, initialized, animation, revealDuration, layerViews);
         if (!animated || !initialized) {
-            playCommonTransitionAnimations(toWorkspaceState, fromView, toView,
-                    animated, initialized, animation, revealDuration, layerViews);
             if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
                 mAllAppsController.finishPullDown();
             }
@@ -694,12 +688,9 @@
             if (onCompleteRunnable != null) {
                 onCompleteRunnable.run();
             }
-
             return null;
         }
         if (animType == CIRCULAR_REVEAL) {
-            playCommonTransitionAnimations(toWorkspaceState, fromView, toView,
-                    animated, initialized, animation, revealDuration, layerViews);
             final View revealView = fromView.getRevealView();
             final View contentView = fromView.getContentView();
 
@@ -870,39 +861,31 @@
             return animation;
         } else if (animType == PULLUP) {
             animation.addListener(new AnimatorListenerAdapter() {
+                boolean canceled = false;
                 @Override
-                public void onAnimationEnd(Animator animation) {
-                    dispatchOnLauncherTransitionEnd(fromView, animated, false);
-                    dispatchOnLauncherTransitionEnd(toView, animated, false);
-                    cleanupAnimation();
-                    pCb.onTransitionComplete();
+                public void onAnimationCancel(Animator animation) {
+                    canceled = true;
                 }
 
-            });
-            mAllAppsController.animateToWorkspace(animation, revealDuration);
-            playCommonTransitionAnimations(toWorkspaceState, fromView, toView,
-                    animated, initialized, animation, revealDuration, layerViews);
-
-            // Dispatch the prepare transition signal
-            dispatchOnLauncherTransitionPrepare(fromView, animated, false);
-            dispatchOnLauncherTransitionPrepare(toView, animated, false);
-
-            animation.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
-                    dispatchOnLauncherTransitionEnd(fromView, animated, true);
-                    dispatchOnLauncherTransitionEnd(toView, animated, true);
-
+                    if (canceled) return;
+                    dispatchOnLauncherTransitionEnd(fromView, animated, false);
+                    dispatchOnLauncherTransitionEnd(toView, animated, false);
                     // Run any queued runnables
                     if (onCompleteRunnable != null) {
                         onCompleteRunnable.run();
                     }
-
-                    // This can hold unnecessary references to views.
                     cleanupAnimation();
                     pCb.onTransitionComplete();
                 }
+
             });
+            mAllAppsController.animateToWorkspace(animation, revealDuration, false);
+
+            // Dispatch the prepare transition signal
+            dispatchOnLauncherTransitionPrepare(fromView, animated, false);
+            dispatchOnLauncherTransitionPrepare(toView, animated, false);
 
             final AnimatorSet stateAnimation = animation;
             final Runnable startAnimRunnable = new Runnable() {
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 9266793..5ac3f0b 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -154,7 +154,7 @@
 
     // Page Indicator
     @Thunk int mPageIndicatorViewId;
-    @Thunk PageIndicator mPageIndicator;
+    protected PageIndicator mPageIndicator;
     // The viewport whether the pages are to be contained (the actual view may be larger than the
     // viewport)
     @ViewDebug.ExportedProperty(category = "launcher")
@@ -247,39 +247,14 @@
         mScroller.setInterpolator(mDefaultInterpolator);
     }
 
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        // Hook up the page indicator
-        ViewGroup parent = (ViewGroup) getParent();
-        ViewGroup grandParent = (ViewGroup) parent.getParent();
-        if (mPageIndicator == null && mPageIndicatorViewId > -1) {
-            mPageIndicator = (PageIndicator) grandParent.findViewById(mPageIndicatorViewId);
+    public void initParentViews(View parent) {
+        if (mPageIndicatorViewId > -1) {
+            mPageIndicator = (PageIndicator) parent.findViewById(mPageIndicatorViewId);
             mPageIndicator.setMarkersCount(getChildCount());
-
-            OnClickListener listener = getPageIndicatorClickListener();
-            if (listener != null) {
-                mPageIndicator.setOnClickListener(listener);
-            }
-            mPageIndicator.setContentDescription(getPageIndicatorDescription());
+            mPageIndicator.setContentDescription(getCurrentPageDescription());
         }
     }
 
-    protected String getPageIndicatorDescription() {
-        return getCurrentPageDescription();
-    }
-
-    protected OnClickListener getPageIndicatorClickListener() {
-        return null;
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        // Unhook the page indicator
-        mPageIndicator = null;
-    }
-
     // Convenience methods to map points from self to parent and vice versa
     private float[] mapPointFromViewToParent(View v, float x, float y) {
         sTmpPoint[0] = x;
@@ -467,7 +442,7 @@
     private void updatePageIndicator() {
         // Update the page indicator (when we aren't reordering)
         if (mPageIndicator != null) {
-            mPageIndicator.setContentDescription(getPageIndicatorDescription());
+            mPageIndicator.setContentDescription(getCurrentPageDescription());
             if (!isReordering(false)) {
                 mPageIndicator.setActiveMarker(getNextPage());
             }
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index d051665..63f49e0 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -16,20 +16,23 @@
 
 package com.android.launcher3;
 
+import android.annotation.TargetApi;
 import android.content.ComponentName;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
-import android.util.Log;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.text.TextUtils;
 
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.compat.LauncherActivityInfoCompat;
+import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.folder.FolderIcon;
-
-import java.util.ArrayList;
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 
 /**
  * Represents a launchable icon on the workspaces and in folders.
@@ -274,6 +277,46 @@
         return shortcut;
     }
 
+    /**
+     * Creates a {@link ShortcutInfo} from a {@link ShortcutInfoCompat}. Pardon the overloaded name.
+     */
+    @TargetApi(Build.VERSION_CODES.N)
+    public static ShortcutInfo fromDeepShortcutInfo(ShortcutInfoCompat shortcutInfo,
+            Context context, LauncherAppsCompat launcherApps) {
+        ShortcutInfo si = new ShortcutInfo();
+        si.user = shortcutInfo.getUserHandle();
+        si.itemType = LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
+        si.intent = shortcutInfo.makeIntent(context);
+        si.flags = 0;
+        si.updateFromDeepShortcutInfo(shortcutInfo, context, launcherApps);
+        return si;
+    }
+
+    public void updateFromDeepShortcutInfo(ShortcutInfoCompat shortcutInfo,
+            Context context, LauncherAppsCompat launcherApps) {
+        title = shortcutInfo.getShortLabel();
+
+        CharSequence label = shortcutInfo.getLongLabel();
+        if (TextUtils.isEmpty(label)) {
+            label = shortcutInfo.getShortLabel();
+        }
+        this.contentDescription = UserManagerCompat.getInstance(context)
+                .getBadgedLabelForUser(label, user);
+
+        LauncherAppState launcherAppState = LauncherAppState.getInstance();
+        Drawable unbadgedIcon = launcherApps.getShortcutIconDrawable(shortcutInfo, launcherAppState
+                .getInvariantDeviceProfile().fillResIconDpi);
+        Bitmap icon = unbadgedIcon == null ? null
+                : Utilities.createBadgedIconBitmap(unbadgedIcon, user, context);
+        setIcon(icon != null ? icon : launcherAppState.getIconCache().getDefaultIcon(user));
+    }
+
+    /** Returns the ShortcutInfo id associated with the deep shortcut. */
+    public String getDeepShortcutId() {
+        return itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT ?
+                intent.getStringExtra(ShortcutInfoCompat.EXTRA_SHORTCUT_ID) : null;
+    }
+
     @Override
     public boolean isDisabled() {
         return isDisabled != 0;
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 6bfce19..608a39c 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -30,6 +30,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
@@ -831,4 +832,11 @@
             return getBitmap().getWidth();
         }
     }
+
+    public static int getColorAccent(Context context) {
+        TypedArray ta = context.obtainStyledAttributes(new int[]{android.R.attr.colorAccent});
+        int colorAccent = ta.getColor(0, 0);
+        ta.recycle();
+        return colorAccent;
+    }
 }
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index d9bd782..45e65b5 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -584,7 +584,7 @@
                 // which would gets re-written next time.
                 mVersions = getPackageVersion(mKey.componentName.getPackageName());
 
-                Launcher launcher = (Launcher) mCaller.getContext();
+                Launcher launcher = Launcher.getLauncher(mCaller.getContext());
 
                 // it's not in the db... we need to generate it
                 preview = generatePreview(launcher, mInfo, unusedBitmap, mPreviewWidth, mPreviewHeight);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 85ba57c..416da3a 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -59,8 +59,8 @@
 import com.android.launcher3.Launcher.CustomContentCallbacks;
 import com.android.launcher3.Launcher.LauncherOverlay;
 import com.android.launcher3.UninstallDropTarget.DropTargetSource;
-import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource;
+import com.android.launcher3.accessibility.OverviewAccessibilityDelegate;
 import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate;
 import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
@@ -75,7 +75,7 @@
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.pageindicators.PageIndicatorLine;
+import com.android.launcher3.pageindicators.PageIndicator;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.LongArrayMap;
@@ -199,6 +199,21 @@
         }
     }
 
+    // Direction used for moving the workspace and hotseat UI
+    public enum Direction {
+        X  (TRANSLATION_X),
+        Y  (TRANSLATION_Y);
+
+        private final Property<View, Float> viewProperty;
+
+        Direction(Property<View, Float> viewProperty) {
+            this.viewProperty = viewProperty;
+        }
+    }
+
+    private float[] mPageAlpha = new float[] {1, 1};
+    private float[] mHotseatAlpha = new float[] {1, 1};
+
     @ViewDebug.ExportedProperty(category = "launcher")
     private State mState = State.NORMAL;
     private boolean mIsSwitchingState = false;
@@ -443,6 +458,12 @@
         setEdgeGlowColor(getResources().getColor(R.color.workspace_edge_effect_color));
     }
 
+    @Override
+    public void initParentViews(View parent) {
+        super.initParentViews(parent);
+        mPageIndicator.setAccessibilityDelegate(new OverviewAccessibilityDelegate());
+    }
+
     private int getDefaultPage() {
         return numCustomPages();
     }
@@ -510,6 +531,9 @@
      * @param qsb an exisitng qsb to recycle or null.
      */
     public void bindAndInitFirstWorkspaceScreen(View qsb) {
+        if (!FeatureFlags.QSB_ON_FIRST_SCREEN) {
+            return;
+        }
         // Add the first page
         CellLayout firstPage = insertNewWorkspaceScreen(Workspace.FIRST_SCREEN_ID, 0);
 
@@ -598,9 +622,7 @@
         mScreenOrder.add(insertIndex, screenId);
         addView(newScreen, insertIndex);
 
-        LauncherAccessibilityDelegate delegate =
-                LauncherAppState.getInstance().getAccessibilityDelegate();
-        if (delegate != null && delegate.isInAccessibleDrag()) {
+        if (mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) {
             newScreen.enableAccessibleDrag(true, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
         }
 
@@ -909,13 +931,13 @@
             long id = mWorkspaceScreens.keyAt(i);
             CellLayout cl = mWorkspaceScreens.valueAt(i);
             // FIRST_SCREEN_ID can never be removed.
-            if (id > FIRST_SCREEN_ID && cl.getShortcutsAndWidgets().getChildCount() == 0) {
+            if ((!FeatureFlags.QSB_ON_FIRST_SCREEN || id > FIRST_SCREEN_ID)
+                    && cl.getShortcutsAndWidgets().getChildCount() == 0) {
                 removeScreens.add(id);
             }
         }
 
-        LauncherAccessibilityDelegate delegate =
-                LauncherAppState.getInstance().getAccessibilityDelegate();
+        boolean isInAccessibleDrag = mLauncher.getAccessibilityDelegate().isInAccessibleDrag();
 
         // We enforce at least one page to add new items to. In the case that we remove the last
         // such screen, we convert the last screen to the empty screen
@@ -932,7 +954,7 @@
                     pageShift++;
                 }
 
-                if (delegate != null && delegate.isInAccessibleDrag()) {
+                if (isInAccessibleDrag) {
                     cl.enableAccessibleDrag(false, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
                 }
 
@@ -1387,50 +1409,55 @@
         // TODO(adamcohen): figure out a final effect here. We may need to recommend
         // different effects based on device performance. On at least one relatively high-end
         // device I've tried, translating the launcher causes things to get quite laggy.
-        setWorkspaceTranslation(TRANSLATION_X, transX, alpha);
-        setHotseatTranslation(TRANSLATION_X, transX, alpha);
+        setWorkspaceTranslation(Direction.X, transX, alpha);
+        setHotseatTranslation(Direction.X, transX, alpha);
     }
 
     /**
      * Moves the workspace UI in the provided direction.
-     * @param direction either {@link #TRANSLATION_X} or {@link #TRANSLATION_Y}
-     * @param translation the amound of shift.
+     * @param direction the direction to move the workspace
+     * @param translation the amount of shift.
      * @param alpha the alpha for the workspace page
      */
-    public void setWorkspaceTranslation(
-            Property<View, Float> direction, float translation, float alpha) {
+    public void setWorkspaceTranslation(Direction direction, float translation, float alpha) {
+        Property<View, Float> property = direction.viewProperty;
+        mPageAlpha[direction.ordinal()] = alpha;
+        float finalAlpha = mPageAlpha[0] * mPageAlpha[1];
+
         View currentChild = getChildAt(getCurrentPage());
         if (currentChild != null) {
-            direction.set(currentChild, translation);
-            currentChild.setAlpha(alpha);
+            property.set(currentChild, translation);
+            currentChild.setAlpha(finalAlpha);
         }
 
         // When the animation finishes, reset all pages, just in case we missed a page.
         if (Float.compare(translation, 0) == 0) {
             for (int i = getChildCount() - 1; i >= 0; i--) {
                 View child = getChildAt(i);
-                direction.set(child, translation);
-                child.setAlpha(alpha);
+                property.set(child, translation);
+                child.setAlpha(finalAlpha);
             }
         }
     }
 
     /**
      * Moves the Hotseat UI in the provided direction.
-     * @param direction either {@link #TRANSLATION_X} or {@link #TRANSLATION_Y}
+     * @param direction the direction to move the workspace
      * @param translation the amound of shift.
      * @param alpha the alpha for the hotseat page
      */
-    public void setHotseatTranslation(
-            Property<View, Float> direction, float translation, float alpha) {
-        View pageIndicator = getPageIndicator();
-        if (pageIndicator != null) {
-            direction.set(pageIndicator, translation);
-            pageIndicator.setAlpha(alpha);
+    public void setHotseatTranslation(Direction direction, float translation, float alpha) {
+        Property<View, Float> property = direction.viewProperty;
+        mHotseatAlpha[direction.ordinal()] = alpha;
+        float finalAlpha = mHotseatAlpha[0] * mHotseatAlpha[1];
+
+        if (mPageIndicator != null) {
+            property.set(mPageIndicator, translation);
+            mPageIndicator.setAlpha(alpha);
         }
 
-        direction.set(mLauncher.getHotseat(), translation);
-        mLauncher.getHotseat().setAlpha(alpha);
+        property.set(mLauncher.getHotseat(), translation);
+        mLauncher.getHotseat().setAlpha(finalAlpha);
     }
 
     @Override
@@ -1625,8 +1652,8 @@
             mLauncher.getHotseat().setTranslationX(translationX);
         }
 
-        if (getPageIndicator() != null) {
-            getPageIndicator().setTranslationX(translationX);
+        if (mPageIndicator != null) {
+            mPageIndicator.setTranslationX(translationX);
         }
 
         if (mCustomContentCallbacks != null) {
@@ -1635,21 +1662,6 @@
     }
 
     @Override
-    protected OnClickListener getPageIndicatorClickListener() {
-        AccessibilityManager am = (AccessibilityManager)
-                getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
-        if (!am.isTouchExplorationEnabled()) {
-            return null;
-        }
-        return new OnClickListener() {
-            @Override
-            public void onClick(View arg0) {
-                mLauncher.showOverviewMode(true);
-            }
-        };
-    }
-
-    @Override
     protected void screenScrolled(int screenCenter) {
         updatePageAlphaValues(screenCenter);
         updateStateForCustomContent(screenCenter);
@@ -1670,15 +1682,6 @@
     }
 
     protected void onResume() {
-        if (getPageIndicator() != null) {
-            // In case accessibility state has changed, we need to perform this on every
-            // attach to window
-            OnClickListener listener = getPageIndicatorClickListener();
-            if (listener != null) {
-                getPageIndicator().setOnClickListener(listener);
-            }
-        }
-
         // Update wallpaper dimensions if they were changed since last onResume
         // (we also always set the wallpaper dimensions in the constructor)
         if (LauncherAppState.getInstance().hasWallpaperChangedSinceLastCheck()) {
@@ -2064,10 +2067,13 @@
                     IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
             page.setContentDescription(getPageDescription(pageNo));
 
-            if (mPagesAccessibilityDelegate == null) {
-                mPagesAccessibilityDelegate = new OverviewScreenAccessibilityDelegate(this);
+            // No custom action for the first page.
+            if (!FeatureFlags.QSB_ON_FIRST_SCREEN || pageNo > 0) {
+                if (mPagesAccessibilityDelegate == null) {
+                    mPagesAccessibilityDelegate = new OverviewScreenAccessibilityDelegate(this);
+                }
+                page.setAccessibilityDelegate(mPagesAccessibilityDelegate);
             }
-            page.setAccessibilityDelegate(mPagesAccessibilityDelegate);
         } else {
             int accessible = mState == State.NORMAL ?
                     IMPORTANT_FOR_ACCESSIBILITY_AUTO :
@@ -2096,9 +2102,9 @@
 
     @Override
     public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
-        if (mPageIndicator instanceof PageIndicatorLine) {
+        if (mPageIndicator != null) {
             boolean isNewStateSpringLoaded = mState == State.SPRING_LOADED;
-            ((PageIndicatorLine) mPageIndicator).setShouldAutoHide(!isNewStateSpringLoaded);
+            mPageIndicator.setShouldAutoHide(!isNewStateSpringLoaded);
             if (isNewStateSpringLoaded) {
                 // Show the page indicator at the same time as the rest of the transition.
                 showPageIndicatorAtCurrentScroll();
@@ -2505,9 +2511,9 @@
                 // Don't show the message if we are dropping on the AllApps button and the hotseat
                 // is full
                 boolean isHotseat = mLauncher.isHotseatLayout(dropTargetLayout);
-                if (mTargetCell != null && isHotseat) {
+                if (mTargetCell != null && isHotseat && !FeatureFlags.NO_ALL_APPS_ICON) {
                     Hotseat hotseat = mLauncher.getHotseat();
-                    if (hotseat.isAllAppsButtonRank(
+                    if (mLauncher.getDeviceProfile().inv.isAllAppsButtonRank(
                             hotseat.getOrderInHotseat(mTargetCell[0], mTargetCell[1]))) {
                         return false;
                     }
@@ -2553,7 +2559,8 @@
         boolean aboveShortcut = (dropOverView.getTag() instanceof ShortcutInfo);
         boolean willBecomeShortcut =
                 (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
-                        info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT);
+                        info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
+                        info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT);
 
         return (aboveShortcut && willBecomeShortcut);
     }
@@ -3487,6 +3494,7 @@
             switch (info.itemType) {
             case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
             case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+            case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
                 if (info.container == NO_ID && info instanceof AppInfo) {
                     // Came from all apps -- make a copy
                     info = ((AppInfo) info).makeShortcut();
@@ -4173,7 +4181,7 @@
 
     public interface ItemOperator {
         /**
-         * Process the next itemInfo, possibly with side-effect on {@link ItemOperator#value}.
+         * Process the next itemInfo, possibly with side-effect on the next item.
          *
          * @param info info for the shortcut
          * @param view view for the shortcut
@@ -4357,11 +4365,7 @@
         exitWidgetResizeMode();
     }
 
-    protected String getPageIndicatorDescription() {
-        String settings = getResources().getString(R.string.settings_button_text);
-        return getCurrentPageDescription() + ", " + settings;
-    }
-
+    @Override
     protected String getCurrentPageDescription() {
         if (hasCustomContent() && getNextPage() == 0) {
             return mCustomContentDescription;
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 0f437c1..888cc57 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -30,6 +30,7 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.DecelerateInterpolator;
 
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.util.Thunk;
 
@@ -43,6 +44,7 @@
 
     private View mView;
     private boolean mAccessibilityEnabled;
+    private boolean mCanceled = false;
 
     public AlphaUpdateListener(View v, boolean accessibilityEnabled) {
         mView = v;
@@ -67,7 +69,13 @@
     }
 
     @Override
+    public void onAnimationCancel(Animator animation) {
+        mCanceled = true;
+    }
+
+    @Override
     public void onAnimationEnd(Animator arg0) {
+        if (mCanceled) return;
         updateVisibility(mView, mAccessibilityEnabled);
     }
 
@@ -164,7 +172,7 @@
         workspaceToAllApps = (oldStateIsNormal && stateIsNormalHidden);
         overviewToWorkspace = (oldStateIsOverview && stateIsNormal);
         overviewToAllApps = (oldStateIsOverview && stateIsOverviewHidden);
-        allAppsToWorkspace = (stateIsNormalHidden && stateIsNormal);
+        allAppsToWorkspace = (oldStateIsNormalHidden && stateIsNormal);
     }
 }
 
@@ -322,8 +330,10 @@
             boolean isCurrentPage = (i == toPage);
             float initialAlpha = cl.getShortcutsAndWidgets().getAlpha();
             float finalAlpha;
-            if (states.stateIsNormalHidden || states.stateIsOverviewHidden) {
+            if (states.stateIsOverviewHidden) {
                 finalAlpha = 0f;
+            } else if(states.stateIsNormalHidden) {
+                finalAlpha = FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP  ? 1 : 0;
             } else if (states.stateIsNormal && mWorkspaceFadeInAdjacentScreens) {
                 finalAlpha = (i == toPage || i < customPageCount) ? 1f : 0f;
             } else {
@@ -447,10 +457,16 @@
             mStateAnimator.play(hotseatAlpha);
             mStateAnimator.play(pageIndicatorAlpha);
             mStateAnimator.addListener(new AnimatorListenerAdapter() {
+                boolean canceled = false;
+                @Override
+                public void onAnimationCancel(Animator animation) {
+                    canceled = true;
+                }
+
                 @Override
                 public void onAnimationEnd(Animator animation) {
                     mStateAnimator = null;
-
+                    if (canceled) return;
                     if (accessibilityEnabled && overviewPanel.getVisibility() == View.VISIBLE) {
                         overviewPanel.getChildAt(0).performAccessibilityAction(
                                 AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
@@ -490,7 +506,8 @@
 
         final DragLayer dragLayer = mLauncher.getDragLayer();
         final float startAlpha = dragLayer.getBackgroundAlpha();
-        float finalAlpha = states.stateIsNormal ? 0 : mWorkspaceScrimAlpha;
+        float finalAlpha = states.stateIsNormal || states.stateIsNormalHidden ?
+                0 : mWorkspaceScrimAlpha;
 
         if (finalAlpha != startAlpha) {
             if (animated) {
diff --git a/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java
index 78accf7..4efe445 100644
--- a/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java
@@ -26,7 +26,7 @@
 import android.view.accessibility.AccessibilityEvent;
 
 import com.android.launcher3.CellLayout;
-import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 
 import java.util.List;
@@ -50,7 +50,7 @@
         super(forView);
         mView = forView;
         mContext = mView.getContext();
-        mDelegate = LauncherAppState.getInstance().getAccessibilityDelegate();
+        mDelegate = ((Launcher) mContext).getAccessibilityDelegate();
     }
 
     @Override
diff --git a/src/com/android/launcher3/accessibility/OverviewAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/OverviewAccessibilityDelegate.java
new file mode 100644
index 0000000..64559f2
--- /dev/null
+++ b/src/com/android/launcher3/accessibility/OverviewAccessibilityDelegate.java
@@ -0,0 +1,72 @@
+/*
+ * 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.accessibility;
+
+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.R;
+import com.android.launcher3.Utilities;
+
+/**
+ * Accessibility delegate with actions pointing to various Overview entry points.
+ */
+public class OverviewAccessibilityDelegate extends AccessibilityDelegate {
+
+    private static final int OVERVIEW = R.string.accessibility_action_overview;
+    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();
+        info.addAction(new AccessibilityAction(OVERVIEW, context.getText(OVERVIEW)));
+
+        if (Utilities.isWallapaperAllowed(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) host.getContext();
+        if (action == OVERVIEW) {
+            launcher.showOverviewMode(true);
+            return true;
+        } else if (action == WALLPAPERS) {
+            launcher.onClickWallpaperPicker(host);
+            return true;
+        } else if (action == WIDGETS) {
+            launcher.onClickAddWidgetButton(host);
+            return true;
+        } else if (action == SETTINGS) {
+            launcher.onClickSettingsButton(host);
+            return true;
+        }
+        return super.performAccessibilityAction(host, action, args);
+    }
+}
diff --git a/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java
index c5b52de..5f68f90 100644
--- a/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java
@@ -28,6 +28,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
+import com.android.launcher3.config.FeatureFlags;
 
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
 public class OverviewScreenAccessibilityDelegate extends AccessibilityDelegate {
@@ -88,7 +89,9 @@
         if (index < mWorkspace.getChildCount() - 1) {
             info.addAction(mActions.get(MOVE_FORWARD));
         }
-        if (index > mWorkspace.numCustomPages()) {
+
+        int startIndex = mWorkspace.numCustomPages() + (FeatureFlags.QSB_ON_FIRST_SCREEN ? 1 : 0);
+        if (index > startIndex) {
             info.addAction(mActions.get(MOVE_BACKWARD));
         }
     }
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index a74c4c5..d1da6d9 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -178,14 +178,15 @@
         super(context, attrs, defStyleAttr);
         Resources res = context.getResources();
 
-        mLauncher = (Launcher) context;
+        mLauncher = Launcher.getLauncher(context);
         mSectionNamesMargin = res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin);
         mApps = new AlphabeticalAppsList(context);
         mAdapter = new AllAppsGridAdapter(mLauncher, mApps, this, mLauncher, this);
         mApps.setAdapter(mAdapter);
         mLayoutManager = mAdapter.getLayoutManager();
         mItemDecoration = mAdapter.getItemDecoration();
-        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
+        DeviceProfile grid = mLauncher.getDeviceProfile();
+        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && !grid.isVerticalBarLayout()) {
             mRecyclerViewTopBottomPadding = 0;
             setPadding(0, 0, 0, 0);
         } else {
@@ -350,7 +351,12 @@
         mAppsRecyclerView.setPremeasuredIconHeights(predIcon.getMeasuredHeight(),
                 icon.getMeasuredHeight());
 
-        updatePaddingsAndMargins();
+        // TODO(hyunyoungs): clean up setting the content and the reveal view.
+        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
+            getContentView().setBackground(null);
+            getRevealView().setVisibility(View.VISIBLE);
+            getRevealView().setAlpha(AllAppsTransitionController.ALL_APPS_FINAL_ALPHA);
+        }
     }
 
     @Override
@@ -358,6 +364,7 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        updatePaddingsAndMargins();
         mContentBounds.set(mHorizontalPadding, 0,
                 MeasureSpec.getSize(widthMeasureSpec) - mHorizontalPadding,
                 MeasureSpec.getSize(heightMeasureSpec));
@@ -366,6 +373,7 @@
         int availableWidth = (!mContentBounds.isEmpty() ? mContentBounds.width() :
                 MeasureSpec.getSize(widthMeasureSpec))
                 - 2 * mAppsRecyclerView.getMaxScrollbarWidth();
+        grid.updateAppsViewNumCols(getResources(), availableWidth);
         if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
             if (mNumAppsPerRow != grid.inv.numColumns ||
                     mNumPredictedAppsPerRow != grid.inv.numColumns) {
@@ -378,6 +386,7 @@
                 if (mNumAppsPerRow > 0) {
                     int iconSize = availableWidth / mNumAppsPerRow;
                     int iconSpacing = (iconSize - grid.allAppsIconSizePx) / 2;
+                    mSearchInput.setPaddingRelative(iconSpacing, 0, iconSpacing, 0);
                 }
             }
             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -450,23 +459,26 @@
         lp.leftMargin = bgPadding.left;
         lp.rightMargin = bgPadding.right;
 
+        DeviceProfile grid = mLauncher.getDeviceProfile();
         if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
-            MarginLayoutParams mlp = (MarginLayoutParams) mAppsRecyclerView.getLayoutParams();
+            if (!grid.isVerticalBarLayout()) {
+                MarginLayoutParams mlp = (MarginLayoutParams) mAppsRecyclerView.getLayoutParams();
 
-            int navBarHeight = 84; /* replace with mInset.height() in dragLayer */
-            DeviceProfile grid = mLauncher.getDeviceProfile();
-            int height = navBarHeight + grid.hotseatCellHeightPx;
+                Rect insets = mLauncher.getDragLayer().getInsets();
+                getContentView().setPadding(0,0,0, insets.bottom);
+                int height = insets.top + grid.hotseatCellHeightPx;
 
-            mlp.topMargin = height;
-            mAppsRecyclerView.setLayoutParams(mlp);
+                mlp.topMargin = height;
+                mAppsRecyclerView.setLayoutParams(mlp);
 
-            LinearLayout.LayoutParams llp =
-                    (LinearLayout.LayoutParams) mSearchInput.getLayoutParams();
-            llp.topMargin = navBarHeight;
-            mSearchInput.setLayoutParams(llp);
+                LinearLayout.LayoutParams llp =
+                        (LinearLayout.LayoutParams) mSearchInput.getLayoutParams();
+                llp.topMargin = insets.top;
+                mSearchInput.setLayoutParams(llp);
 
-            lp.height = height;
-            mSearchContainer.setBackground(null);
+                lp.height = height;
+            }
+            mSearchContainer.getBackground().setAlpha(0);
         }
         mSearchContainer.setLayoutParams(lp);
     }
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index c2a319c..ca2556e 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -378,7 +378,7 @@
         mSectionTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
         mSectionTextPaint.setTextSize(res.getDimensionPixelSize(
                 R.dimen.all_apps_grid_section_text_size));
-        mSectionTextPaint.setColor(res.getColor(R.color.all_apps_grid_section_text_color));
+        mSectionTextPaint.setColor(Utilities.getColorAccent(launcher));
 
         mPredictedAppsDividerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
         mPredictedAppsDividerPaint.setStrokeWidth(Utilities.pxFromDp(1f, res.getDisplayMetrics()));
@@ -490,16 +490,14 @@
                 AppInfo info = mApps.getAdapterItems().get(position).appInfo;
                 BubbleTextView icon = (BubbleTextView) holder.mContent;
                 icon.applyFromApplicationInfo(info);
-                icon.setAccessibilityDelegate(
-                        LauncherAppState.getInstance().getAccessibilityDelegate());
+                icon.setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
                 break;
             }
             case PREDICTION_ICON_VIEW_TYPE: {
                 AppInfo info = mApps.getAdapterItems().get(position).appInfo;
                 BubbleTextView icon = (BubbleTextView) holder.mContent;
                 icon.applyFromApplicationInfo(info);
-                icon.setAccessibilityDelegate(
-                        LauncherAppState.getInstance().getAccessibilityDelegate());
+                icon.setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
                 break;
             }
             case EMPTY_SEARCH_VIEW_TYPE:
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 7f047d5..3157c13 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -4,19 +4,22 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
-import android.graphics.drawable.Drawable;
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 
 import com.android.launcher3.CellLayout;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Hotseat;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.PagedView;
+import com.android.launcher3.R;
 import com.android.launcher3.Workspace;
+import com.android.launcher3.Workspace.Direction;
 import com.android.launcher3.util.TouchController;
 
 /**
@@ -34,43 +37,56 @@
     private static final String TAG = "AllAppsTrans";
     private static final boolean DBG = false;
 
-     private final Interpolator mAccelInterpolator = new AccelerateInterpolator(1f);
+    private final Interpolator mAccelInterpolator = new AccelerateInterpolator(2f);
+    private final Interpolator mDecelInterpolator = new DecelerateInterpolator(1f);
 
-    private static final float ANIMATION_DURATION = 2000;
-    private static final float FINAL_ALPHA = .65f;
+    private static final float ANIMATION_DURATION = 1200;
+    public static final float ALL_APPS_FINAL_ALPHA = .9f;
+
+    private static final float PARALLAX_COEFFICIENT = .125f;
 
     private AllAppsContainerView mAppsView;
     private Workspace mWorkspace;
     private Hotseat mHotseat;
-    private Drawable mHotseatBackground;
-    private float mHotseatAlpha;
+    private float mHotseatBackgroundAlpha;
+
+    private float mStatusBarHeight;
 
     private final Launcher mLauncher;
     private final VerticalPullDetector mDetector;
 
-    // Animation in this class is controlled by a single variable {@link mProgressTransY}.
+    // Animation in this class is controlled by a single variable {@link mShiftCurrent}.
     // Visually, it represents top y coordinate of the all apps container. Using the
-    // {@link mTranslation} as the denominator, this fraction value ranges in [0, 1].
-    private float mProgressTransY;   // numerator
-    private float mTranslation = -1; // denominator
+    // {@link mShiftRange} as the denominator, this fraction value ranges in [0, 1].
+    //
+    // When {@link mShiftCurrent} is 0, all apps container is pulled up.
+    // When {@link mShiftCurrent} is {@link mShirtRange}, all apps container is pulled down.
+    private float mShiftStart;      // [0, mShiftRange]
+    private float mShiftCurrent;    // [0, mShiftRange]
+    private float mShiftRange;      // changes depending on the orientation
+
 
     private static final float RECATCH_REJECTION_FRACTION = .0875f;
 
+    private int mBezelSwipeUpHeight;
     private long mAnimationDuration;
-    private float mCurY;
+
 
     private AnimatorSet mCurrentAnimation;
     private boolean mNoIntercept;
 
+    private boolean mLightStatusBar;
+
     public AllAppsTransitionController(Launcher launcher) {
         mLauncher = launcher;
         mDetector = new VerticalPullDetector(launcher);
         mDetector.setListener(this);
+        mBezelSwipeUpHeight = launcher.getResources().getDimensionPixelSize(
+                R.dimen.all_apps_bezel_swipe_height);
     }
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        init();
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
             mNoIntercept = false;
             if (mLauncher.getWorkspace().isInOverviewMode() || mLauncher.isWidgetsViewVisible()) {
@@ -78,98 +94,78 @@
             } else if (mLauncher.isAllAppsVisible() &&
                     !mAppsView.shouldContainerScroll(ev.getX(), ev.getY())) {
                 mNoIntercept = true;
+            } else if (!mLauncher.isAllAppsVisible() && !shouldPossiblyIntercept(ev)) {
+                mNoIntercept = true;
             } else {
-                mDetector.setDetectableScrollConditions(mLauncher.isAllAppsVisible() /* down */,
-                        isInDisallowRecatchTopZone(), isInDisallowRecatchBottomZone());
+                // Now figure out which direction scroll events the controller will start
+                // calling the callbacks.
+                int conditionsToReportScroll = 0;
+
+                if (mDetector.isRestingState()) {
+                    if (mLauncher.isAllAppsVisible()) {
+                        conditionsToReportScroll |= VerticalPullDetector.THRESHOLD_DOWN;
+                    } else {
+                        conditionsToReportScroll |= VerticalPullDetector.THRESHOLD_UP;
+                    }
+                } else {
+                    if (isInDisallowRecatchBottomZone()) {
+                        conditionsToReportScroll |= VerticalPullDetector.THRESHOLD_UP;
+                    } else if (isInDisallowRecatchTopZone()) {
+                        conditionsToReportScroll |= VerticalPullDetector.THRESHOLD_DOWN;
+                    } else {
+                        conditionsToReportScroll |= VerticalPullDetector.THRESHOLD_ONLY;
+                    }
+                }
+                mDetector.setDetectableScrollConditions(conditionsToReportScroll);
             }
         }
         if (mNoIntercept) {
             return false;
         }
         mDetector.onTouchEvent(ev);
+        if (mDetector.isScrollingState() && (isInDisallowRecatchBottomZone() || isInDisallowRecatchTopZone())) {
+            return false;
+        }
         return mDetector.shouldIntercept();
     }
 
+    private boolean shouldPossiblyIntercept(MotionEvent ev) {
+        DeviceProfile grid = mLauncher.getDeviceProfile();
+        if (mDetector.isRestingState()) {
+            if (grid.isVerticalBarLayout()) {
+                if (ev.getY() > mLauncher.getDeviceProfile().heightPx - mBezelSwipeUpHeight) {
+                    return true;
+                }
+            } else {
+                if (mLauncher.getDragLayer().isEventOverHotseat(ev) && !grid.isVerticalBarLayout()) {
+                    return true;
+                }
+            }
+            return false;
+        } else {
+            return true;
+        }
+    }
+
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         return mDetector.onTouchEvent(ev);
     }
 
     private boolean isInDisallowRecatchTopZone() {
-        return mProgressTransY / mTranslation < RECATCH_REJECTION_FRACTION;
+        return mShiftCurrent / mShiftRange < RECATCH_REJECTION_FRACTION;
     }
 
     private boolean isInDisallowRecatchBottomZone() {
-        return mProgressTransY / mTranslation > 1 - RECATCH_REJECTION_FRACTION;
-    }
-
-    private void init() {
-        if (mAppsView != null) {
-            return;
-        }
-        mAppsView = mLauncher.getAppsView();
-        mHotseat = mLauncher.getHotseat();
-        mWorkspace = mLauncher.getWorkspace();
-
-        if (mHotseatBackground == null) {
-            mHotseatBackground = mHotseat.getBackground();
-            mHotseatAlpha = mHotseatBackground.getAlpha() / 255f;
-        }
+        return mShiftCurrent / mShiftRange > 1 - RECATCH_REJECTION_FRACTION;
     }
 
     @Override
     public void onScrollStart(boolean start) {
         cancelAnimation();
         mCurrentAnimation = LauncherAnimUtils.createAnimatorSet();
+        mShiftStart = mAppsView.getTranslationY();
         preparePull(start);
-        mCurY = mAppsView.getTranslationY();
-    }
-
-    /**
-     * @param start {@code true} if start of new drag.
-     */
-    public void preparePull(boolean start) {
-        mHotseat.setVisibility(View.VISIBLE);
-        mHotseat.bringToFront();
-        if (start) {
-            if (!mLauncher.isAllAppsVisible()) {
-                mHotseat.setBackground(null);
-                mAppsView.setVisibility(View.VISIBLE);
-                mAppsView.getContentView().setVisibility(View.VISIBLE);
-                mAppsView.getContentView().setBackground(null);
-                mAppsView.getRevealView().setVisibility(View.VISIBLE);
-                mAppsView.getRevealView().setAlpha(mHotseatAlpha);
-                mAppsView.setSearchBarVisible(false);
-
-                if (mTranslation < 0) {
-                    mTranslation = mHotseat.getTop();
-                    setProgress(mTranslation);
-                }
-            } else {
-                // TODO: get rid of this workaround to override state change by workspace transition
-                mWorkspace.onLauncherTransitionPrepare(mLauncher, false, false);
-                View child = ((CellLayout) mWorkspace.getChildAt(mWorkspace.getNextPage()))
-                        .getShortcutsAndWidgets();
-                child.setVisibility(View.VISIBLE);
-                child.setAlpha(1f);
-
-                mAppsView.setSearchBarVisible(false);
-                setLightStatusBar(false);
-            }
-        }
-    }
-
-    private void setLightStatusBar(boolean enable) {
-        int systemUiFlags = mLauncher.getWindow().getDecorView().getSystemUiVisibility();
-        if (enable) {
-            mLauncher.getWindow().getDecorView().setSystemUiVisibility(systemUiFlags
-                    | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
-
-        } else {
-            mLauncher.getWindow().getDecorView().setSystemUiVisibility(systemUiFlags
-                    & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
-
-        }
     }
 
     @Override
@@ -177,37 +173,12 @@
         if (mAppsView == null) {
             return false;   // early termination.
         }
-        if (0 <= mCurY + displacement && mCurY + displacement < mTranslation) {
-            setProgress(mCurY + displacement);
+        if (0 <= mShiftStart + displacement && mShiftStart + displacement < mShiftRange) {
+            setProgress(mShiftStart + displacement);
         }
         return true;
     }
 
-    /**
-     * @param progress y value of the border between hotseat and all apps
-     */
-    public void setProgress(float progress) {
-        mProgressTransY = progress;
-        float alpha = calcAlphaAllApps(progress);
-        float workspaceHotseatAlpha = 1 - alpha;
-
-        mAppsView.getRevealView().setAlpha(Math.min(FINAL_ALPHA, Math.max(mHotseatAlpha, alpha)));
-        mAppsView.getContentView().setAlpha(alpha);
-        mAppsView.setTranslationY(progress);
-        mWorkspace.setWorkspaceTranslation(View.TRANSLATION_Y, -mTranslation + progress,
-                mAccelInterpolator.getInterpolation(workspaceHotseatAlpha));
-        mWorkspace.setHotseatTranslation(
-                View.TRANSLATION_Y, -mTranslation + progress, workspaceHotseatAlpha);
-    }
-
-    public float getProgress() {
-        return mProgressTransY;
-    }
-
-    private float calcAlphaAllApps(float progress) {
-        return ((mTranslation - progress)/mTranslation);
-    }
-
     @Override
     public void onScrollEnd(float velocity, boolean fling) {
         if (mAppsView == null) {
@@ -217,71 +188,158 @@
         if (fling) {
             if (velocity < 0) {
                 calculateDuration(velocity, mAppsView.getTranslationY());
-                showAppsView(); // Flinging in UP direction
+
+                if (!mLauncher.isAllAppsVisible()) {
+                    mLauncher.showAppsView(true, true, false, false);
+                } else {
+                    animateToAllApps(mCurrentAnimation, mAnimationDuration, true);
+                }
             } else {
-                calculateDuration(velocity, Math.abs(mTranslation - mAppsView.getTranslationY()));
-                showWorkspace(); // Flinging in DOWN direction
+                calculateDuration(velocity, Math.abs(mShiftRange - mAppsView.getTranslationY()));
+                if (mLauncher.isAllAppsVisible()) {
+                    mLauncher.showWorkspace(true);
+                } else {
+                    animateToWorkspace(mCurrentAnimation, mAnimationDuration, true);
+                }
             }
             // snap to top or bottom using the release velocity
         } else {
-            if (mAppsView.getTranslationY() > mTranslation / 2) {
-                calculateDuration(velocity, Math.abs(mTranslation - mAppsView.getTranslationY()));
-                showWorkspace(); // Released in the bottom half
+            if (mAppsView.getTranslationY() > mShiftRange / 2) {
+                calculateDuration(velocity, Math.abs(mShiftRange - mAppsView.getTranslationY()));
+                if (mLauncher.isAllAppsVisible()) {
+                    mLauncher.showWorkspace(true);
+                } else {
+                    animateToWorkspace(mCurrentAnimation, mAnimationDuration, true);
+                }
             } else {
                 calculateDuration(velocity, Math.abs(mAppsView.getTranslationY()));
-                showAppsView(); // Released in the top half
+                if (!mLauncher.isAllAppsVisible()) {
+                    mLauncher.showAppsView(true, true, false, false);
+                } else {
+                    animateToAllApps(mCurrentAnimation, mAnimationDuration, true);
+                }
+
             }
         }
     }
+    /**
+     * @param start {@code true} if start of new drag.
+     */
+    public void preparePull(boolean start) {
+        if (start) {
+            // Initialize values that should not change until #onScrollEnd
+            mStatusBarHeight = mLauncher.getDragLayer().getInsets().top;
+            mHotseat.setVisibility(View.VISIBLE);
+            mHotseat.bringToFront();
+            if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+                mShiftRange = mHotseat.getTop();
+            } else {
+                mShiftRange = mHotseat.getBottom();
+            }
+            if (!mLauncher.isAllAppsVisible()) {
+                mLauncher.tryAndUpdatePredictedApps();
+
+                mHotseatBackgroundAlpha = mHotseat.getBackground().getAlpha() / 255f;
+                mHotseat.setBackgroundTransparent(true /* transparent */);
+                mAppsView.setVisibility(View.VISIBLE);
+                mAppsView.getContentView().setVisibility(View.VISIBLE);
+                mAppsView.getContentView().setBackground(null);
+                mAppsView.getRevealView().setVisibility(View.VISIBLE);
+                mAppsView.getRevealView().setAlpha(mHotseatBackgroundAlpha);
+
+                DeviceProfile grid= mLauncher.getDeviceProfile();
+                if (!grid.isVerticalBarLayout()) {
+                    mShiftRange = mHotseat.getTop();
+                } else {
+                    mShiftRange = mHotseat.getBottom();
+                }
+                mAppsView.getRevealView().setAlpha(mHotseatBackgroundAlpha);
+                setProgress(mShiftRange);
+            } else {
+                // TODO: get rid of this workaround to override state change by workspace transition
+                mWorkspace.onLauncherTransitionPrepare(mLauncher, false, false);
+                View child = ((CellLayout) mWorkspace.getChildAt(mWorkspace.getNextPage()))
+                        .getShortcutsAndWidgets();
+                child.setVisibility(View.VISIBLE);
+                child.setAlpha(1f);
+            }
+        } else {
+            setProgress(mShiftCurrent);
+        }
+    }
+
+    private void updateLightStatusBar(float progress) {
+        boolean enable = (progress < mStatusBarHeight / 2);
+        // Already set correctly
+        if (mLightStatusBar == enable) {
+            return;
+        }
+        int systemUiFlags = mLauncher.getWindow().getDecorView().getSystemUiVisibility();
+        if (enable) {
+            mLauncher.getWindow().getDecorView().setSystemUiVisibility(systemUiFlags
+                    | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+
+        } else {
+            mLauncher.getWindow().getDecorView().setSystemUiVisibility(systemUiFlags
+                    & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+
+        }
+        mLightStatusBar = enable;
+    }
+
+    /**
+     * @param progress y value of the border between hotseat and all apps
+     */
+    public void setProgress(float progress) {
+        updateLightStatusBar(progress);
+        mShiftCurrent = progress;
+        float alpha = calcAlphaAllApps(progress);
+        float workspaceHotseatAlpha = 1 - alpha;
+
+        mAppsView.getRevealView().setAlpha(Math.min(ALL_APPS_FINAL_ALPHA, Math.max(mHotseatBackgroundAlpha,
+                mDecelInterpolator.getInterpolation(alpha))));
+        mAppsView.getContentView().setAlpha(alpha);
+        mAppsView.setTranslationY(progress);
+        mWorkspace.setWorkspaceTranslation(Direction.Y,
+                PARALLAX_COEFFICIENT * (-mShiftRange + progress),
+                mAccelInterpolator.getInterpolation(workspaceHotseatAlpha));
+        if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+            mWorkspace.setHotseatTranslation(Direction.Y, -mShiftRange + progress,
+                    mAccelInterpolator.getInterpolation(workspaceHotseatAlpha));
+        } else {
+            mWorkspace.setHotseatTranslation(Direction.Y,
+                    PARALLAX_COEFFICIENT * (-mShiftRange + progress),
+                    mAccelInterpolator.getInterpolation(workspaceHotseatAlpha));
+        }
+    }
+
+    public float getProgress() {
+        return mShiftCurrent;
+    }
+
+    private float calcAlphaAllApps(float progress) {
+        return ((mShiftRange - progress)/ mShiftRange);
+    }
 
     private void calculateDuration(float velocity, float disp) {
         // TODO: make these values constants after tuning.
-        float velocityDivisor = Math.max(1.5f, Math.abs(0.25f * velocity));
-        float travelDistance = Math.max(0.2f, disp / mTranslation);
+        float velocityDivisor = Math.max(1.5f, Math.abs(0.5f * velocity));
+        float travelDistance = Math.max(0.2f, disp / mShiftRange);
         mAnimationDuration = (long) Math.max(100, ANIMATION_DURATION / velocityDivisor * travelDistance);
         if (DBG) {
             Log.d(TAG, String.format("calculateDuration=%d, v=%f, d=%f", mAnimationDuration, velocity, disp));
         }
     }
 
-    /**
-     * Depending on the current state of the launcher, either just
-     * 1) animate
-     * 2) animate and do all the state updates.
-     */
-    private void showAppsView() {
-        if (mLauncher.isAllAppsVisible()) {
-            animateToAllApps(mCurrentAnimation, mAnimationDuration);
-            mCurrentAnimation.start();
-        } else {
-            mLauncher.showAppsView(true /* animated */, true /* resetListToTop */,
-                    true /* updatePredictedApps */, false /* focusSearchBar */);
-        }
-    }
-
-    /**
-     * Depending on the current state of the launcher, either just
-     * 1) animate
-     * 2) animate and do all the state updates.
-     */
-    private void showWorkspace() {
-        if (mLauncher.isAllAppsVisible()) {
-            mLauncher.showWorkspace(true /* animated */);
-        } else {
-            animateToWorkspace(mCurrentAnimation, mAnimationDuration);
-            mCurrentAnimation.start();
-        }
-    }
-
-    public void animateToAllApps(AnimatorSet animationOut, long duration) {
-        if ((mAppsView = mLauncher.getAppsView()) == null || animationOut == null){
+    public void animateToAllApps(AnimatorSet animationOut, long duration, boolean start) {
+        if (animationOut == null){
             return;
         }
         if (mDetector.isRestingState()) {
             preparePull(true);
             mAnimationDuration = duration;
+            mShiftStart = mAppsView.getTranslationY();
         }
-        mCurY = mAppsView.getTranslationY();
         final float fromAllAppsTop = mAppsView.getTranslationY();
         final float toAllAppsTop = 0;
 
@@ -308,25 +366,22 @@
                 }
             }});
         mCurrentAnimation = animationOut;
+        if (start) {
+            mCurrentAnimation.start();
+        }
     }
 
-    private void finishPullUp() {
-        mAppsView.setSearchBarVisible(true);
-        mHotseat.setVisibility(View.INVISIBLE);
-        setProgress(0f);
-        setLightStatusBar(true);
-    }
-
-    public void animateToWorkspace(AnimatorSet animationOut, long duration) {
-        if ((mAppsView = mLauncher.getAppsView()) == null || animationOut == null){
+    public void animateToWorkspace(AnimatorSet animationOut, long duration, boolean start) {
+        if (animationOut == null){
             return;
         }
         if(mDetector.isRestingState()) {
             preparePull(true);
             mAnimationDuration = duration;
+            mShiftStart = mAppsView.getTranslationY();
         }
         final float fromAllAppsTop = mAppsView.getTranslationY();
-        final float toAllAppsTop = mTranslation;
+        final float toAllAppsTop = mShiftRange;
 
         ObjectAnimator driftAndAlpha = ObjectAnimator.ofFloat(this, "progress",
                 fromAllAppsTop, toAllAppsTop);
@@ -339,6 +394,7 @@
              @Override
              public void onAnimationCancel(Animator animation) {
                  canceled = true;
+                 setProgress(mShiftCurrent);
              }
 
              @Override
@@ -352,18 +408,29 @@
                  }
              }});
         mCurrentAnimation = animationOut;
+        if (start) {
+            mCurrentAnimation.start();
+        }
+    }
+
+    private void finishPullUp() {
+        mHotseat.setVisibility(View.INVISIBLE);
+        setProgress(0f);
     }
 
     public void finishPullDown() {
+        if (mHotseat.getBackground() != null) {
+            return;
+        }
         mAppsView.setVisibility(View.INVISIBLE);
-        mHotseat.setBackground(mHotseatBackground);
+        mHotseat.setBackgroundTransparent(false /* transparent */);
         mHotseat.setVisibility(View.VISIBLE);
-        setProgress(mTranslation);
-        setLightStatusBar(false);
+        setProgress(mShiftRange);
     }
 
     private void cancelAnimation() {
         if (mCurrentAnimation != null) {
+            mCurrentAnimation.setDuration(0);
             mCurrentAnimation.cancel();
             mCurrentAnimation = null;
         }
@@ -372,4 +439,10 @@
     private void cleanUpAnimation() {
         mCurrentAnimation = null;
     }
+
+    public void setupViews(AllAppsContainerView appsView, Hotseat hotseat, Workspace workspace) {
+        mAppsView = appsView;
+        mHotseat = hotseat;
+        mWorkspace = workspace;
+    }
 }
diff --git a/src/com/android/launcher3/allapps/VerticalPullDetector.java b/src/com/android/launcher3/allapps/VerticalPullDetector.java
index 7df63e0..b54cb00 100644
--- a/src/com/android/launcher3/allapps/VerticalPullDetector.java
+++ b/src/com/android/launcher3/allapps/VerticalPullDetector.java
@@ -7,16 +7,22 @@
 
 /**
  * One dimensional scroll gesture detector for all apps container pull up interaction.
+ * Client (e.g., AllAppsTransitionController) of this class can register a listener.
+ *
+ * Features that this gesture detector can support.
  */
 public class VerticalPullDetector {
 
-    private static final String TAG = "ScrollGesture";
     private static final boolean DBG = false;
+    private static final String TAG = "VerticalPullDetector";
 
     private float mTouchSlop;
-    private boolean mScrollDown; // if false, only scroll up will be reported.
-    private boolean mDisallowRecatchFromTop;
-    private boolean mDisallowRecatchFromBottom;
+
+    private int mScrollDirections;
+    public static final int THRESHOLD_UP = 1 << 0;
+    public static final int THRESHOLD_DOWN = 1 << 1;
+    public static final int THRESHOLD_ONLY = THRESHOLD_DOWN | THRESHOLD_UP;
+
 
     /**
      * The minimum release velocity in pixels per millisecond that triggers fling..
@@ -31,23 +37,43 @@
 
     /* Scroll state, this is set to true during dragging and animation. */
     private State mState = State.NONE;
-    enum State {NONE, DRAG, SCROLLING};
+
+    enum State {
+        NONE,
+        CATCH,          // onScrollStart
+        DRAG,           // onScrollStart, onScroll
+        SCROLLING       // onScrollEnd
+    };
+
+    //------------------- State transition diagram -----------------------------------
+    //
+    // NONE -> (mDisplacement > mTouchSlop) -> DRAG
+    // DRAG -> (MotionEvent#ACTION_UP, MotionEvent#ACTION_CANCEL) -> SCROLLING
+    // SCROLLING -> (MotionEvent#ACTION_DOWN) && (mDisplacement > mTouchSlop) -> CATCH
+    // SCROLLING -> (View settled) -> NONE
 
     private void setState(State newState) {
         if (DBG) {
-            Log.d(TAG, mState + "->" + newState);
+            Log.d(TAG, "setState:" + mState + "->" + newState);
         }
         mState = newState;
     }
 
     public boolean shouldIntercept() {
-        return mState == State.DRAG;
+        return mState == State.DRAG || mState == State.SCROLLING || mState == State.CATCH;
     }
 
+    /**
+     * There's no touch and there's no animation.
+     */
     public boolean isRestingState() {
         return mState == State.NONE;
     }
 
+    public boolean isScrollingState() {
+        return mState == State.SCROLLING;
+    }
+
     private float mDownX;
     private float mDownY;
     private float mDownMillis;
@@ -60,8 +86,7 @@
     private float mDisplacementY;
     private float mDisplacementX;
 
-    /* scroll started during previous animation */
-    private boolean mSubtractSlop = true;
+    private float mSubtractDisplacement;
 
     /* Client of this gesture detector can register a callback. */
     Listener mListener;
@@ -80,39 +105,25 @@
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
     }
 
-    public void setDetectableScrollConditions(boolean scrollDown, boolean disallowRecatchFromTop,
-            boolean disallowRecatchFromBottom) {
-        mScrollDown = scrollDown;
-        mDisallowRecatchFromTop = disallowRecatchFromTop;
-        mDisallowRecatchFromBottom = disallowRecatchFromBottom;
+    public void setDetectableScrollConditions(int scrollDirectionFlags) {
+        mScrollDirections = scrollDirectionFlags;
     }
 
     private boolean shouldScrollStart() {
+        // reject cases where the slop condition is not met.
+        if (Math.abs(mDisplacementY) < mTouchSlop) {
+            return false;
+        }
+
+        // reject cases where the angle condition is not met.
         float deltaY = Math.abs(mDisplacementY);
         float deltaX = Math.max(Math.abs(mDisplacementX), 1);
-        if (mScrollDown && mDisplacementY > mTouchSlop) {
-            if (deltaY > deltaX) {
-                return true;
-            }
+        if (deltaX > deltaY) {
+            return false;
         }
-        if (!mScrollDown && mDisplacementY < -mTouchSlop) {
-            if (deltaY > deltaX) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private boolean shouldRecatchScrollStart() {
-        if (!mDisallowRecatchFromBottom && !mDisallowRecatchFromTop) {
-            return true;
-        }
-        if (mDisallowRecatchFromTop && mDisplacementY > mTouchSlop) {
-            mDisallowRecatchFromTop = false;
-            return true;
-        }
-        if (mDisallowRecatchFromBottom && mDisplacementY < -mTouchSlop) {
-            mDisallowRecatchFromBottom = false;
+        // Check if the client is interested in scroll in current direction.
+        if (((mScrollDirections & THRESHOLD_DOWN) > 0 && mDisplacementY > 0) ||
+            ((mScrollDirections & THRESHOLD_UP) > 0 && mDisplacementY < 0)) {
             return true;
         }
         return false;
@@ -126,8 +137,11 @@
                 mDownY = ev.getY();
                 mLastDisplacement = 0;
                 mVelocity = 0;
-                if (mState == State.SCROLLING && shouldRecatchScrollStart()){
+
+                // handle state and listener calls.
+                if (mState == State.SCROLLING && shouldScrollStart()){
                     reportScrollStart(true /* recatch */);
+                    setState(State.CATCH);
                 }
                 break;
             case MotionEvent.ACTION_MOVE:
@@ -135,23 +149,23 @@
                 mDisplacementY = ev.getY() - mDownY;
                 mVelocity = computeVelocity(ev, mVelocity);
 
-                if (mState == State.SCROLLING && shouldRecatchScrollStart()){
+                // handle state and listener calls.
+                if (shouldScrollStart() && mState != State.DRAG) {
+                    if (mState == State.NONE) {
+                        reportScrollStart(false /* recatch */);
+                    }
                     setState(State.DRAG);
-                    reportScrollStart(true /* recatch */);
                 }
-                if (mState == State.NONE && shouldScrollStart()) {
-                    setState(State.DRAG);
-                    reportScrollStart(false /* recatch */);
-                }
-                if (mState == State.DRAG && mListener != null) {
+                if (mState == State.DRAG) {
                     reportScroll();
                 }
                 break;
             case MotionEvent.ACTION_CANCEL:
             case MotionEvent.ACTION_UP:
                 // These are synthetic events and there is no need to update internal values.
-                if (mState == State.DRAG && mListener != null) {
+                if (mState == State.DRAG || mState == State.CATCH) {
                     reportScrollEnd();
+                    setState(State.SCROLLING);
                 }
                 break;
             default:
@@ -173,7 +187,11 @@
 
     private boolean reportScrollStart(boolean recatch) {
         mListener.onScrollStart(!recatch);
-        mSubtractSlop = !recatch;
+        if (mDisplacementY > 0) {
+            mSubtractDisplacement = mTouchSlop;
+        } else {
+            mSubtractDisplacement = -mTouchSlop;
+        }
         if (DBG) {
             Log.d(TAG, "onScrollStart recatch:" + recatch);
         }
@@ -187,15 +205,8 @@
                 Log.d(TAG, String.format("onScroll disp=%.1f, velocity=%.1f",
                         mDisplacementY, mVelocity));
             }
-            float subtractDisplacement = 0f;
-            if (mSubtractSlop) {
-                if (mDisplacementY > 0) {
-                    subtractDisplacement = mTouchSlop;
-                } else {
-                    subtractDisplacement = -mTouchSlop;
-                }
-            }
-            return mListener.onScroll(mDisplacementY - subtractDisplacement, mVelocity);
+
+            return mListener.onScroll(mDisplacementY - mSubtractDisplacement, mVelocity);
         }
         return true;
     }
@@ -206,7 +217,7 @@
                     mDisplacementY, mVelocity));
         }
         mListener.onScrollEnd(mVelocity, Math.abs(mVelocity) > RELEASE_VELOCITY_PX_MS);
-        setState(State.SCROLLING);
+
     }
     /**
      * Computes the damped velocity using the two motion events and the previous velocity.
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java
index 237a9e9..3381064 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompat.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java
@@ -19,10 +19,13 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.LauncherApps;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 
 import com.android.launcher3.Utilities;
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 
 import java.util.List;
 
@@ -45,6 +48,8 @@
         void onPackagesUnavailable(String[] packageNames, UserHandleCompat user, boolean replacing);
         void onPackagesSuspended(String[] packageNames, UserHandleCompat user);
         void onPackagesUnsuspended(String[] packageNames, UserHandleCompat user);
+        void onShortcutsChanged(String packageName, List<ShortcutInfoCompat> shortcuts,
+                UserHandleCompat user);
     }
 
     protected LauncherAppsCompat() {
@@ -56,7 +61,9 @@
     public static LauncherAppsCompat getInstance(Context context) {
         synchronized (sInstanceLock) {
             if (sInstance == null) {
-                if (Utilities.ATLEAST_LOLLIPOP) {
+                if (Utilities.isNycOrAbove()) {
+                    sInstance = new LauncherAppsCompatVNMR1(context.getApplicationContext());
+                } else if (Utilities.ATLEAST_LOLLIPOP) {
                     sInstance = new LauncherAppsCompatVL(context.getApplicationContext());
                 } else {
                     sInstance = new LauncherAppsCompatV16(context.getApplicationContext());
@@ -79,4 +86,11 @@
     public abstract boolean isActivityEnabledForProfile(ComponentName component,
             UserHandleCompat user);
     public abstract boolean isPackageSuspendedForProfile(String packageName, UserHandleCompat user);
+    public abstract List<ShortcutInfoCompat> getShortcuts(LauncherApps.ShortcutQuery q,
+            UserHandleCompat userHandle);
+    public abstract void pinShortcuts(String packageName, List<String> pinnedIds,
+            UserHandleCompat userHandle);
+    public abstract void startShortcut(String packageName, String id, Rect sourceBounds,
+            Bundle startActivityOptions, UserHandleCompat user);
+    public abstract Drawable getShortcutIconDrawable(ShortcutInfoCompat shortcutInfo, int density);
 }
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatV16.java b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
index 4e2fc05..1a144e8 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
@@ -22,15 +22,18 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
+import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.Settings;
 
 import com.android.launcher3.Utilities;
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.Thunk;
 
@@ -130,6 +133,29 @@
         return false;
     }
 
+    @Override
+    public List<ShortcutInfoCompat> getShortcuts(LauncherApps.ShortcutQuery q,
+            UserHandleCompat userHandle) {
+        return null;
+    }
+
+    @Override
+    public void pinShortcuts(String packageName, List<String> pinnedIds,
+            UserHandleCompat userHandle) {
+        // Not supported, so do nothing.
+    }
+
+    @Override
+    public void startShortcut(String packageName, String id, Rect sourceBounds,
+            Bundle startActivityOptions, UserHandleCompat user) {
+        // Not supported, so do nothing.
+    }
+
+    @Override
+    public Drawable getShortcutIconDrawable(ShortcutInfoCompat shortcutInfo, int density) {
+        return null;
+    }
+
     private void unregisterForPackageIntents() {
         mContext.unregisterReceiver(mPackageMonitor);
     }
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
index 7270d02..d97bf2f 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
@@ -22,11 +22,14 @@
 import android.content.Intent;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
 import android.graphics.Rect;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.UserHandle;
 
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -34,7 +37,7 @@
 import java.util.Map;
 
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
-public class LauncherAppsCompatVL extends LauncherAppsCompat {
+public class LauncherAppsCompatVL extends LauncherAppsCompatV16 {
 
     protected LauncherApps mLauncherApps;
 
@@ -42,7 +45,7 @@
             = new HashMap<OnAppsChangedCallbackCompat, WrappedCallback>();
 
     LauncherAppsCompatVL(Context context) {
-        super();
+        super(context);
         mLauncherApps = (LauncherApps) context.getSystemService("launcherapps");
     }
 
@@ -146,6 +149,18 @@
         public void onPackagesUnsuspended(String[] packageNames, UserHandle user) {
             mCallback.onPackagesUnsuspended(packageNames, UserHandleCompat.fromUser(user));
         }
+
+        @Override
+        public void onShortcutsChanged(String packageName, List<ShortcutInfo> shortcuts,
+                UserHandle user) {
+            List<ShortcutInfoCompat> shortcutInfoCompats = new ArrayList<>(shortcuts.size());
+            for (ShortcutInfo shortcutInfo : shortcuts) {
+                shortcutInfoCompats.add(new ShortcutInfoCompat(shortcutInfo));
+            }
+
+            mCallback.onShortcutsChanged(packageName, shortcutInfoCompats,
+                    UserHandleCompat.fromUser(user));
+        }
     }
 }
 
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVNMR1.java b/src/com/android/launcher3/compat/LauncherAppsCompatVNMR1.java
new file mode 100644
index 0000000..0c1db13
--- /dev/null
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVNMR1.java
@@ -0,0 +1,123 @@
+/*
+ * 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.compat;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@TargetApi(Build.VERSION_CODES.N)
+public class LauncherAppsCompatVNMR1 extends LauncherAppsCompatVL {
+
+    LauncherAppsCompatVNMR1(Context context) {
+        super(context);
+    }
+
+    @Override
+    public List<ShortcutInfoCompat> getShortcuts(LauncherApps.ShortcutQuery q,
+            UserHandleCompat userHandle) {
+        List<ShortcutInfo> shortcutInfos = mLauncherApps.getShortcuts(q, userHandle.getUser());
+        if (shortcutInfos == null) {
+            return null;
+        }
+        List<ShortcutInfoCompat> shortcutInfoCompats = new ArrayList<>(shortcutInfos.size());
+        for (ShortcutInfo shortcutInfo : shortcutInfos) {
+            shortcutInfoCompats.add(new ShortcutInfoCompat(shortcutInfo));
+        }
+        return shortcutInfoCompats;
+    }
+
+    @Override
+    public void pinShortcuts(String packageName, List<String> pinnedIds,
+            UserHandleCompat userHandle) {
+        mLauncherApps.pinShortcuts(packageName, pinnedIds, userHandle.getUser());
+    }
+
+    @Override
+    public void startShortcut(String packageName, String id, Rect sourceBounds,
+            Bundle startActivityOptions, UserHandleCompat user) {
+        mLauncherApps.startShortcut(packageName, id, sourceBounds,
+                startActivityOptions, user.getUser());
+    }
+
+    @Override
+    public Drawable getShortcutIconDrawable(ShortcutInfoCompat shortcutInfo, int density) {
+        return mLauncherApps.getShortcutIconDrawable(shortcutInfo.getShortcutInfo(), density);
+    }
+
+    private static class WrappedCallback extends LauncherApps.Callback {
+        private OnAppsChangedCallbackCompat mCallback;
+
+        public WrappedCallback(OnAppsChangedCallbackCompat callback) {
+            mCallback = callback;
+        }
+
+        public void onPackageRemoved(String packageName, UserHandle user) {
+            mCallback.onPackageRemoved(packageName, UserHandleCompat.fromUser(user));
+        }
+
+        public void onPackageAdded(String packageName, UserHandle user) {
+            mCallback.onPackageAdded(packageName, UserHandleCompat.fromUser(user));
+        }
+
+        public void onPackageChanged(String packageName, UserHandle user) {
+            mCallback.onPackageChanged(packageName, UserHandleCompat.fromUser(user));
+        }
+
+        public void onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing) {
+            mCallback.onPackagesAvailable(packageNames, UserHandleCompat.fromUser(user), replacing);
+        }
+
+        public void onPackagesUnavailable(String[] packageNames, UserHandle user,
+                boolean replacing) {
+            mCallback.onPackagesUnavailable(packageNames, UserHandleCompat.fromUser(user),
+                    replacing);
+        }
+
+        public void onPackagesSuspended(String[] packageNames, UserHandle user) {
+            mCallback.onPackagesSuspended(packageNames, UserHandleCompat.fromUser(user));
+        }
+
+        public void onPackagesUnsuspended(String[] packageNames, UserHandle user) {
+            mCallback.onPackagesUnsuspended(packageNames, UserHandleCompat.fromUser(user));
+        }
+
+        @Override
+        public void onShortcutsChanged(String packageName, List<ShortcutInfo> shortcuts,
+                UserHandle user) {
+            List<ShortcutInfoCompat> shortcutInfoCompats = new ArrayList<>(shortcuts.size());
+            for (ShortcutInfo shortcutInfo : shortcuts) {
+                shortcutInfoCompats.add(new ShortcutInfoCompat(shortcutInfo));
+            }
+
+            mCallback.onShortcutsChanged(packageName, shortcutInfoCompats,
+                    UserHandleCompat.fromUser(user));
+        }
+    }
+}
+
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 5d212d8..4966938 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -187,6 +187,11 @@
         removeView(mOverlayView);
     }
 
+    public boolean isEventOverHotseat(MotionEvent ev) {
+        getDescendantRectRelativeToSelf(mLauncher.getHotseat(), mHitRect);
+        return mHitRect.contains((int) ev.getX(), (int) ev.getY());
+    }
+
     private boolean isEventOverFolderTextRegion(Folder folder, MotionEvent ev) {
         getDescendantRectRelativeToSelf(folder.getEditTextRegion(), mHitRect);
         return mHitRect.contains((int) ev.getX(), (int) ev.getY());
@@ -334,9 +339,7 @@
     }
 
     private boolean isInAccessibleDrag() {
-        LauncherAccessibilityDelegate delegate = LauncherAppState
-                .getInstance().getAccessibilityDelegate();
-        return delegate != null && delegate.isInAccessibleDrag();
+        return mLauncher.getAccessibilityDelegate().isInAccessibleDrag();
     }
 
     @Override
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 93238de..2035f99 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -510,7 +510,7 @@
         }
 
         // This is set to true in close(), but isn't reset to false until onDropCompleted(). This
-        // leads to an consistent state if you drag out of the folder and drag back in without
+        // leads to an inconsistent state if you drag out of the folder and drag back in without
         // dropping. One resulting issue is that replaceFolderWithFinalItem() can be called twice.
         mDeleteFolderOnDropCompleted = false;
 
@@ -737,7 +737,8 @@
         final ItemInfo item = d.dragInfo;
         final int itemType = item.itemType;
         return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
-                    itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) &&
+                itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
+                itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) &&
                     !isFull());
     }
 
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 157a970..d08cf54 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -144,8 +144,6 @@
         mPreviewLayoutRule = FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON ?
                 new StackFolderIconLayoutRule() :
                 new ClippedFolderIconLayoutRule();
-
-        setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
     }
 
     public static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group,
@@ -183,6 +181,7 @@
         folder.setFolderIcon(icon);
         folder.bind(folderInfo);
         icon.setFolder(folder);
+        icon.setAccessibilityDelegate(launcher.getAccessibilityDelegate());
 
         folderInfo.addListener(icon);
 
@@ -214,7 +213,8 @@
     private boolean willAcceptItem(ItemInfo item) {
         final int itemType = item.itemType;
         return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
-                itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) &&
+                itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
+                itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) &&
                 !mFolder.isFull() && item != mInfo && !mInfo.opened);
     }
 
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index c56e4e5..c6fc4cb 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -120,6 +120,7 @@
         mFolder = folder;
         mKeyListener = new PagedFolderKeyEventListener(folder);
         mPageIndicator = (PageIndicator) folder.findViewById(R.id.folder_page_indicator);
+        initParentViews(folder);
     }
 
     /**
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index e9a897e..0deee57 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -18,6 +18,7 @@
 
 import android.content.ComponentName;
 import android.content.Intent;
+import android.util.Log;
 import android.view.View;
 import android.view.ViewParent;
 
@@ -28,11 +29,14 @@
 import com.android.launcher3.util.ComponentKey;
 
 import java.util.List;
+import java.util.Locale;
 
 /**
  * Manages the creation of {@link LauncherEvent}.
  */
-public abstract class UserEventDispatcher {
+public class UserEventDispatcher {
+
+    private static final boolean DEBUG_LOGGING = false;
 
     private final static int MAXIMUM_VIEW_HIERARCHY_LEVEL = 5;
     /**
@@ -155,7 +159,17 @@
         mActionDurationMillis = System.currentTimeMillis();
     }
 
-    public abstract void dispatchUserEvent(LauncherEvent ev, Intent intent);
+    public void dispatchUserEvent(LauncherEvent ev, Intent intent) {
+        if (DEBUG_LOGGING) {
+            Log.d("UserEvent", String.format(Locale.US,
+                    "action:%s\nchild:%s\nparent:%s\nelapsed container %d ms session %d ms",
+                    LoggerUtils.getActionStr(ev.action),
+                    LoggerUtils.getTargetStr(ev.srcTarget[0]),
+                    LoggerUtils.getTargetStr(ev.srcTarget[1]),
+                    ev.elapsedContainerMillis,
+                    ev.elapsedSessionMillis));
+        }
+    }
 
     public int getPredictedRank(ComponentKey key) {
         if (mPredictedApps == null) return -1;
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index dd11bde..cb1acaa 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -7,6 +7,7 @@
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.graphics.Point;
 import android.net.Uri;
@@ -26,6 +27,7 @@
 import com.android.launcher3.backup.nano.BackupProtos;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.PackageInstallerCompat;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.LongArrayMap;
 
@@ -47,7 +49,7 @@
     private static final boolean DEBUG = true;
 
     private static final String KEY_MIGRATION_SRC_WORKSPACE_SIZE = "migration_src_workspace_size";
-    private static final String KEY_MIGRATION_SRC_HOTSEAT_SIZE = "migration_src_hotseat_size";
+    private static final String KEY_MIGRATION_SRC_HOTSEAT_COUNT = "migration_src_hotseat_count";
 
     // Set of entries indicating minimum size a widget can be resized to. This is used during
     // restore in case the widget has not been installed yet.
@@ -66,9 +68,9 @@
 
     private final HashMap<String, Point> mWidgetMinSize = new HashMap<>();
     private final ContentValues mTempValues = new ContentValues();
-    private final ArrayList<Long> mEntryToRemove = new ArrayList<>();
+    protected final ArrayList<Long> mEntryToRemove = new ArrayList<>();
     private final ArrayList<ContentProviderOperation> mUpdateOperations = new ArrayList<>();
-    private final ArrayList<DbEntry> mCarryOver = new ArrayList<>();
+    protected final ArrayList<DbEntry> mCarryOver = new ArrayList<>();
     private final HashSet<String> mValidPackages;
 
     private final int mSrcX, mSrcY;
@@ -76,9 +78,7 @@
     private final boolean mShouldRemoveX, mShouldRemoveY;
 
     private final int mSrcHotseatSize;
-    private final int mSrcAllAppsRank;
     private final int mDestHotseatSize;
-    private final int mDestAllAppsRank;
 
     protected GridSizeMigrationTask(Context context, InvariantDeviceProfile idp,
             HashSet<String> validPackages, HashMap<String, Point> widgetMinSize,
@@ -98,22 +98,19 @@
         mShouldRemoveY = mTrgY < mSrcY;
 
         // Non-used variables
-        mSrcHotseatSize = mSrcAllAppsRank = mDestHotseatSize = mDestAllAppsRank = -1;
+        mSrcHotseatSize = mDestHotseatSize = -1;
     }
 
     protected GridSizeMigrationTask(Context context,
             InvariantDeviceProfile idp, HashSet<String> validPackages,
-            int srcHotseatSize, int srcAllAppsRank,
-            int destHotseatSize, int destAllAppsRank) {
+            int srcHotseatSize, int destHotseatSize) {
         mContext = context;
         mIdp = idp;
         mValidPackages = validPackages;
 
         mSrcHotseatSize = srcHotseatSize;
-        mSrcAllAppsRank = srcAllAppsRank;
 
         mDestHotseatSize = destHotseatSize;
-        mDestAllAppsRank = destAllAppsRank;
 
         // Non-used variables
         mSrcX = mSrcY = mTrgX = mTrgY = -1;
@@ -153,7 +150,7 @@
     protected boolean migrateHotseat() throws Exception {
         ArrayList<DbEntry> items = loadHotseatEntries();
 
-        int requiredCount = mDestHotseatSize - 1;
+        int requiredCount = FeatureFlags.NO_ALL_APPS_ICON ? mDestHotseatSize : mDestHotseatSize - 1;
 
         while (items.size() > requiredCount) {
             // Pick the center item by default.
@@ -185,7 +182,7 @@
             }
 
             newScreenId++;
-            if (newScreenId == mDestAllAppsRank) {
+            if (!FeatureFlags.NO_ALL_APPS_ICON && mIdp.isAllAppsButtonRank(newScreenId)) {
                 newScreenId++;
             }
         }
@@ -269,9 +266,10 @@
      *   3) If all those items from the above list can be placed on this screen, place them
      *      (otherwise they are placed on a new screen).
      */
-    private void migrateScreen(long screenId) {
+    protected void migrateScreen(long screenId) {
         // If we are migrating the first screen, do not touch the first row.
-        int startY = screenId == Workspace.FIRST_SCREEN_ID ? 1 : 0;
+        int startY = (FeatureFlags.QSB_ON_FIRST_SCREEN && screenId == Workspace.FIRST_SCREEN_ID)
+                ? 1 : 0;
 
         ArrayList<DbEntry> items = loadWorkspaceEntries(screenId);
 
@@ -366,7 +364,7 @@
     /**
      * Updates an item in the DB.
      */
-    private void update(DbEntry item) {
+    protected void update(DbEntry item) {
         mTempValues.clear();
         item.addToContentValues(mTempValues);
         mUpdateOperations.add(ContentProviderOperation
@@ -643,10 +641,11 @@
                 // calculate weight
                 switch (entry.itemType) {
                     case Favorites.ITEM_TYPE_SHORTCUT:
+                    case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
                     case Favorites.ITEM_TYPE_APPLICATION: {
                         verifyIntent(c.getString(indexIntent));
-                        entry.weight = entry.itemType == Favorites.ITEM_TYPE_SHORTCUT
-                                ? WT_SHORTCUT : WT_APPLICATION;
+                        entry.weight = entry.itemType == Favorites.ITEM_TYPE_APPLICATION ?
+                                WT_APPLICATION : WT_SHORTCUT;
                         break;
                     }
                     case Favorites.ITEM_TYPE_FOLDER: {
@@ -677,8 +676,8 @@
     /**
      * Loads entries for a particular screen id.
      */
-    private ArrayList<DbEntry> loadWorkspaceEntries(long screen) {
-        Cursor c =  mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+    protected ArrayList<DbEntry> loadWorkspaceEntries(long screen) {
+        Cursor c = queryWorkspace(
                 new String[]{
                         Favorites._ID,                  // 0
                         Favorites.ITEM_TYPE,            // 1
@@ -690,7 +689,7 @@
                         Favorites.APPWIDGET_PROVIDER,   // 7
                         Favorites.APPWIDGET_ID},        // 8
                 Favorites.CONTAINER + " = " + Favorites.CONTAINER_DESKTOP
-                        + " AND " + Favorites.SCREEN + " = " + screen, null, null, null);
+                        + " AND " + Favorites.SCREEN + " = " + screen);
 
         final int indexId = c.getColumnIndexOrThrow(Favorites._ID);
         final int indexItemType = c.getColumnIndexOrThrow(Favorites.ITEM_TYPE);
@@ -717,10 +716,11 @@
                 // calculate weight
                 switch (entry.itemType) {
                     case Favorites.ITEM_TYPE_SHORTCUT:
+                    case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
                     case Favorites.ITEM_TYPE_APPLICATION: {
                         verifyIntent(c.getString(indexIntent));
-                        entry.weight = entry.itemType == Favorites.ITEM_TYPE_SHORTCUT
-                            ? WT_SHORTCUT : WT_APPLICATION;
+                        entry.weight = entry.itemType == Favorites.ITEM_TYPE_APPLICATION ?
+                                WT_APPLICATION : WT_SHORTCUT;
                         break;
                     }
                     case Favorites.ITEM_TYPE_APPWIDGET: {
@@ -776,9 +776,9 @@
      * @return the number of valid items in the folder.
      */
     private int getFolderItemsCount(long folderId) {
-        Cursor c =  mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+        Cursor c = queryWorkspace(
                 new String[]{Favorites._ID, Favorites.INTENT},
-                Favorites.CONTAINER + " = " + folderId, null, null, null);
+                Favorites.CONTAINER + " = " + folderId);
 
         int total = 0;
         while (c.moveToNext()) {
@@ -793,6 +793,11 @@
         return total;
     }
 
+    protected Cursor queryWorkspace(String[] columns, String where) {
+        return mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+                columns, where, null, null, null);
+    }
+
     /**
      * Verifies if the intent should be restored.
      */
@@ -815,7 +820,7 @@
         }
     }
 
-    private static class DbEntry extends ItemInfo implements Comparable<DbEntry> {
+    protected static class DbEntry extends ItemInfo implements Comparable<DbEntry> {
 
         public float weight;
 
@@ -886,8 +891,7 @@
         Utilities.getPrefs(context).edit()
                 .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE,
                         getPointString((int) srcProfile.desktopCols, (int) srcProfile.desktopRows))
-                .putString(KEY_MIGRATION_SRC_HOTSEAT_SIZE,
-                        getPointString((int) srcProfile.hotseatCount, srcProfile.allappsRank))
+                .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, (int) srcProfile.hotseatCount)
                 .putStringSet(KEY_MIGRATION_WIDGET_MINSIZE, widgets)
                 .apply();
     }
@@ -901,10 +905,9 @@
         InvariantDeviceProfile idp = LauncherAppState.getInstance().getInvariantDeviceProfile();
 
         String gridSizeString = getPointString(idp.numColumns, idp.numRows);
-        String hotseatSizeString = getPointString(idp.numHotseatIcons, idp.hotseatAllAppsRank);
 
         if (gridSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, "")) &&
-                hotseatSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_HOTSEAT_SIZE, ""))) {
+                idp.numHotseatIcons != prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons)) {
             // Skip if workspace and hotseat sizes have not changed.
             return true;
         }
@@ -913,30 +916,15 @@
         try {
             boolean dbChanged = false;
 
-            // Initialize list of valid packages. This contain all the packages which are already on
-            // the device and packages which are being installed. Any item which doesn't belong to
-            // this set is removed.
-            // Since the loader removes such items anyway, removing these items here doesn't cause
-            // any extra data loss and gives us more free space on the grid for better migration.
-            HashSet validPackages = new HashSet<>();
-            for (PackageInfo info : context.getPackageManager().getInstalledPackages(0)) {
-                validPackages.add(info.packageName);
-            }
-            validPackages.addAll(PackageInstallerCompat.getInstance(context)
-                    .updateAndGetActiveSessionCache().keySet());
-
+            HashSet validPackages = getValidPackages(context);
             // Hotseat
-            Point srcHotseatSize = parsePoint(prefs.getString(
-                    KEY_MIGRATION_SRC_HOTSEAT_SIZE, hotseatSizeString));
-            if (srcHotseatSize.x != idp.numHotseatIcons ||
-                    srcHotseatSize.y != idp.hotseatAllAppsRank) {
+            int srcHotseatCount = prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons);
+            if (srcHotseatCount != idp.numHotseatIcons) {
                 // Migrate hotseat.
 
                 dbChanged = new GridSizeMigrationTask(context,
                         LauncherAppState.getInstance().getInvariantDeviceProfile(),
-                        validPackages,
-                        srcHotseatSize.x, srcHotseatSize.y,
-                        idp.numHotseatIcons, idp.hotseatAllAppsRank).migrateHotseat();
+                        validPackages, srcHotseatCount, idp.numHotseatIcons).migrateHotseat();
             }
 
             // Grid size
@@ -1017,9 +1005,25 @@
             // Save current configuration, so that the migration does not run again.
             prefs.edit()
                     .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString)
-                    .putString(KEY_MIGRATION_SRC_HOTSEAT_SIZE, hotseatSizeString)
+                    .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons)
                     .remove(KEY_MIGRATION_WIDGET_MINSIZE)
                     .apply();
         }
     }
+
+    protected static HashSet<String> getValidPackages(Context context) {
+        // Initialize list of valid packages. This contain all the packages which are already on
+        // the device and packages which are being installed. Any item which doesn't belong to
+        // this set is removed.
+        // Since the loader removes such items anyway, removing these items here doesn't cause
+        // any extra data loss and gives us more free space on the grid for better migration.
+        HashSet validPackages = new HashSet<>();
+        for (PackageInfo info : context.getPackageManager()
+                .getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) {
+            validPackages.add(info.packageName);
+        }
+        validPackages.addAll(PackageInstallerCompat.getInstance(context)
+                .updateAndGetActiveSessionCache().keySet());
+        return validPackages;
+    }
 }
diff --git a/src/com/android/launcher3/pageindicators/PageIndicator.java b/src/com/android/launcher3/pageindicators/PageIndicator.java
index 7c59495..2209bb8 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicator.java
@@ -1,9 +1,26 @@
+/*
+ * 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.pageindicators;
 
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.View;
 
+import com.android.launcher3.dynamicui.ExtractedColors;
+
 /**
  * Base class for a page indicator.
  */
@@ -15,9 +32,9 @@
         super(context, attrs, defStyleAttr);
     }
 
-    public abstract void setScroll(int currentScroll, int totalScroll);
+    public void setScroll(int currentScroll, int totalScroll) {}
 
-    public abstract void setActiveMarker(int activePage);
+    public void setActiveMarker(int activePage) {}
 
     public void addMarker() {
         mNumPages++;
@@ -33,5 +50,9 @@
         onPageCountChanged();
     }
 
-    protected abstract void onPageCountChanged();
+    protected void onPageCountChanged() { }
+
+    public void setShouldAutoHide(boolean shouldAutoHide) {}
+
+    public void updateColor(ExtractedColors extractedColors) {}
 }
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java b/src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java
new file mode 100644
index 0000000..0f9145d
--- /dev/null
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java
@@ -0,0 +1,66 @@
+/*
+ * 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.pageindicators;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.dynamicui.ExtractedColors;
+
+/**
+ * Simply draws the caret drawable in the center. Used for the landscape layout.
+ */
+public class PageIndicatorCaretLandscape extends PageIndicator {
+    // all apps pull up handle drawable.
+    private final Drawable caretDrawable;
+
+    public PageIndicatorCaretLandscape(Context context) {
+        this(context, null);
+    }
+
+    public PageIndicatorCaretLandscape(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public PageIndicatorCaretLandscape(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        Resources res = context.getResources();
+        caretDrawable = res.getDrawable(R.drawable.ic_allapps_caret);
+        Launcher l = (Launcher) context;
+        setOnTouchListener(l.getHapticFeedbackTouchListener());
+        setOnClickListener(l);
+        setOnFocusChangeListener(l.mFocusHandler);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        int size = bottom - top;
+        int l = (right - left) / 2 - size / 2;
+        caretDrawable.setBounds(l, 0, l + size, size);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        caretDrawable.draw(canvas);
+    }
+}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index 99af93b..747c21b 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -123,7 +123,7 @@
         mDotRadius = getResources().getDimension(R.dimen.page_indicator_dot_size) / 2;
         setOutlineProvider(new MyOutlineProver());
 
-        mActiveColor = getResources().getColor(R.color.launcher_accent_color);
+        mActiveColor = Utilities.getColorAccent(context);
         mInActiveColor = getResources().getColor(R.color.page_indicator_dot_color);
 
         mIsRtl = Utilities.isRtl(getResources());
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorLine.java b/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
similarity index 68%
rename from src/com/android/launcher3/pageindicators/PageIndicatorLine.java
rename to src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
index aec708c..2c157b4 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorLine.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
@@ -5,17 +5,24 @@
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.Looper;
 import android.support.v4.graphics.ColorUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Property;
+import android.view.TouchDelegate;
+import android.view.View;
 import android.view.ViewConfiguration;
 
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.dynamicui.ExtractedColors;
 
@@ -24,9 +31,11 @@
  *
  * The fraction is 1 / number of pages and the position is based on the progress of the page scroll.
  */
-public class PageIndicatorLine extends PageIndicator {
+public class PageIndicatorLineCaret extends PageIndicator {
     private static final String TAG = "PageIndicatorLine";
 
+    private static final int[] sTempCoords = new int[2];
+
     private static final int LINE_ANIMATE_DURATION = ViewConfiguration.getScrollBarFadeDuration();
     private static final int LINE_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay();
     public static final int WHITE_ALPHA = (int) (0.70f * 255);
@@ -51,44 +60,50 @@
     private int mCurrentScroll;
     private int mTotalScroll;
     private Paint mLinePaint;
+    private Launcher mLauncher;
+    // all apps pull up handle drawable.
+    private final Drawable caretDrawable;
+    private final int mLineHeight;
+    private final Rect mTouchHitRect = new Rect();
+    private final int mTouchExtensionHeight;
 
-    private static final Property<PageIndicatorLine, Integer> PAINT_ALPHA
-            = new Property<PageIndicatorLine, Integer>(Integer.class, "paint_alpha") {
+    private static final Property<PageIndicatorLineCaret, Integer> PAINT_ALPHA
+            = new Property<PageIndicatorLineCaret, Integer>(Integer.class, "paint_alpha") {
         @Override
-        public Integer get(PageIndicatorLine obj) {
+        public Integer get(PageIndicatorLineCaret obj) {
             return obj.mLinePaint.getAlpha();
         }
 
         @Override
-        public void set(PageIndicatorLine obj, Integer alpha) {
+        public void set(PageIndicatorLineCaret obj, Integer alpha) {
             obj.mLinePaint.setAlpha(alpha);
             obj.invalidate();
         }
     };
 
-    private static final Property<PageIndicatorLine, Float> NUM_PAGES
-            = new Property<PageIndicatorLine, Float>(Float.class, "num_pages") {
+    private static final Property<PageIndicatorLineCaret, Float> NUM_PAGES
+            = new Property<PageIndicatorLineCaret, Float>(Float.class, "num_pages") {
         @Override
-        public Float get(PageIndicatorLine obj) {
+        public Float get(PageIndicatorLineCaret obj) {
             return obj.mNumPagesFloat;
         }
 
         @Override
-        public void set(PageIndicatorLine obj, Float numPages) {
+        public void set(PageIndicatorLineCaret obj, Float numPages) {
             obj.mNumPagesFloat = numPages;
             obj.invalidate();
         }
     };
 
-    private static final Property<PageIndicatorLine, Integer> TOTAL_SCROLL
-            = new Property<PageIndicatorLine, Integer>(Integer.class, "total_scroll") {
+    private static final Property<PageIndicatorLineCaret, Integer> TOTAL_SCROLL
+            = new Property<PageIndicatorLineCaret, Integer>(Integer.class, "total_scroll") {
         @Override
-        public Integer get(PageIndicatorLine obj) {
+        public Integer get(PageIndicatorLineCaret obj) {
             return obj.mTotalScroll;
         }
 
         @Override
-        public void set(PageIndicatorLine obj, Integer totalScroll) {
+        public void set(PageIndicatorLineCaret obj, Integer totalScroll) {
             obj.mTotalScroll = totalScroll;
             obj.invalidate();
         }
@@ -101,22 +116,50 @@
         }
     };
 
-    public PageIndicatorLine(Context context) {
+    public PageIndicatorLineCaret(Context context) {
         this(context, null);
     }
 
-    public PageIndicatorLine(Context context, AttributeSet attrs) {
+    public PageIndicatorLineCaret(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public PageIndicatorLine(Context context, AttributeSet attrs, int defStyle) {
+    public PageIndicatorLineCaret(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         mLinePaint = new Paint();
         mLinePaint.setAlpha(0);
+
+        mLauncher = (Launcher) context;
+        setOnTouchListener(mLauncher.getHapticFeedbackTouchListener());
+        setOnClickListener(mLauncher);
+        setOnFocusChangeListener(mLauncher.mFocusHandler);
+        Resources res = context.getResources();
+        caretDrawable = res.getDrawable(R.drawable.ic_allapps_caret);
+        mLineHeight = res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_line_height);
+        mTouchExtensionHeight = res.getDimensionPixelSize(
+                R.dimen.dynamic_grid_page_indicator_extra_touch_height);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        int size = bottom - top;
+        int l = (right - left) / 2 - size / 2;
+        caretDrawable.setBounds(l, 0, l+ size, size);
+
+        // The touch area is expanded below this view by #mTouchExtensionHeight
+        // which extends to the top of the hotseat.
+        View parent = mLauncher.getDragLayer();
+        sTempCoords[0] = sTempCoords[1] = 0;
+        Utilities.getDescendantCoordRelativeToAncestor(this, parent, sTempCoords, true);
+        mTouchHitRect.set(sTempCoords[0], sTempCoords[1], sTempCoords[0] + this.getWidth(),
+                sTempCoords[1] + getHeight() + mTouchExtensionHeight);
+        parent.setTouchDelegate(new TouchDelegate(mTouchHitRect, this));
     }
 
     @Override
     protected void onDraw(Canvas canvas) {
+        caretDrawable.draw(canvas);
         if (mTotalScroll == 0 || mNumPagesFloat == 0) {
             return;
         }
@@ -127,7 +170,8 @@
         int lineWidth = (int) (availableWidth / mNumPagesFloat);
         int lineLeft = (int) (progress * (availableWidth - lineWidth));
         int lineRight = lineLeft + lineWidth;
-        canvas.drawRect(lineLeft, 0, lineRight, canvas.getHeight(), mLinePaint);
+        canvas.drawRect(lineLeft, canvas.getHeight() + mLineHeight, lineRight, canvas.getHeight(),
+                mLinePaint);
     }
 
     @Override
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.java b/src/com/android/launcher3/provider/LauncherDbUtils.java
new file mode 100644
index 0000000..faa5fad
--- /dev/null
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.java
@@ -0,0 +1,122 @@
+/*
+ * 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.provider;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.util.Log;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.LauncherSettings.WorkspaceScreens;
+import com.android.launcher3.logging.FileLog;
+
+import java.util.ArrayList;
+
+/**
+ * A set of utility methods for Launcher DB used for DB updates and migration.
+ */
+public class LauncherDbUtils {
+
+    private static final String TAG = "LauncherDbUtils";
+
+    /**
+     * Makes the first screen as screen 0 (if screen 0 already exists,
+     * renames it to some other number).
+     * If the first row of screen 0 is non empty, runs a 'lossy' GridMigrationTask to clear
+     * the first row. The items in the first screen are moved and resized but the carry-forward
+     * items are simply deleted.
+     */
+    public static boolean prepareScreenZeroToHostQsb(SQLiteDatabase db) {
+        db.beginTransaction();
+        try {
+            // Get the existing screens
+            ArrayList<Long> screenIds = getScreenIdsFromCursor(db.query(WorkspaceScreens.TABLE_NAME,
+                    null, null, null, null, null, WorkspaceScreens.SCREEN_RANK));
+
+            if (screenIds.isEmpty()) {
+                // No update needed
+                return true;
+            }
+            if (screenIds.get(0) != 0) {
+                // First screen is not 0, we need to rename screens
+                if (screenIds.indexOf(0L) > -1) {
+                    // There is already a screen 0. First rename it to a differen screen.
+                    long newScreenId = 1;
+                    while (screenIds.indexOf(newScreenId) > -1) newScreenId++;
+                    renameScreen(db, 0, newScreenId);
+                }
+
+                // Rename the first screen to 0.
+                renameScreen(db, screenIds.get(0), 0);
+            }
+
+            // Check if the first row is empty
+            try (Cursor c = db.query(Favorites.TABLE_NAME, null,
+                    "container = -100 and screen = 0 and cellY = 0", null, null, null, null)) {
+                if (c.getCount() == 0) {
+                    // First row is empty, no need to migrate.
+                    return true;
+                }
+            }
+
+            LauncherAppState app = LauncherAppState.getInstance();
+            new LossyScreenMigrationTask(app.getContext(), app.getInvariantDeviceProfile(), db)
+                    .migrateScreen0();
+            db.setTransactionSuccessful();
+            return true;
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to update workspace size", e);
+            return false;
+        } finally {
+            db.endTransaction();
+        }
+    }
+
+    private static void renameScreen(SQLiteDatabase db, long oldScreen, long newScreen) {
+        String[] whereParams = new String[] { Long.toString(oldScreen) };
+
+        ContentValues values = new ContentValues();
+        values.put(WorkspaceScreens._ID, newScreen);
+        db.update(WorkspaceScreens.TABLE_NAME, values, "_id = ?", whereParams);
+
+        values.clear();
+        values.put(Favorites.SCREEN, newScreen);
+        db.update(Favorites.TABLE_NAME, values, "container = -100 and screen = ?", whereParams);
+    }
+
+    /**
+     * Parses the cursor containing workspace screens table and returns the list of screen IDs
+     */
+    public static ArrayList<Long> getScreenIdsFromCursor(Cursor sc) {
+        ArrayList<Long> screenIds = new ArrayList<Long>();
+        try {
+            final int idIndex = sc.getColumnIndexOrThrow(WorkspaceScreens._ID);
+            while (sc.moveToNext()) {
+                try {
+                    screenIds.add(sc.getLong(idIndex));
+                } catch (Exception e) {
+                    FileLog.d(TAG, "Invalid screen id", e);
+                }
+            }
+        } finally {
+            sc.close();
+        }
+        return screenIds;
+    }
+}
diff --git a/src/com/android/launcher3/provider/LossyScreenMigrationTask.java b/src/com/android/launcher3/provider/LossyScreenMigrationTask.java
new file mode 100644
index 0000000..bb6ed0d
--- /dev/null
+++ b/src/com/android/launcher3/provider/LossyScreenMigrationTask.java
@@ -0,0 +1,108 @@
+/*
+ * 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.provider;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.graphics.Point;
+import android.util.Log;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.model.GridSizeMigrationTask;
+import com.android.launcher3.util.LongArrayMap;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * An extension of {@link GridSizeMigrationTask} which migrates only one screen and
+ * deletes all carry-forward items.
+ */
+public class LossyScreenMigrationTask extends GridSizeMigrationTask {
+
+    private final SQLiteDatabase mDb;
+
+    private final LongArrayMap<DbEntry> mOriginalItems;
+    private final LongArrayMap<DbEntry> mUpdates;
+
+    protected LossyScreenMigrationTask(
+            Context context, InvariantDeviceProfile idp, SQLiteDatabase db) {
+        // Decrease the rows count by 1
+        super(context, idp, getValidPackages(context), new HashMap<String, Point>(),
+                new Point(idp.numColumns, idp.numRows + 1), new Point(idp.numColumns, idp.numRows));
+
+        mDb = db;
+        mOriginalItems = new LongArrayMap<>();
+        mUpdates = new LongArrayMap<>();
+    }
+
+    @Override
+    protected Cursor queryWorkspace(String[] columns, String where) {
+        return mDb.query(Favorites.TABLE_NAME, columns, where, null, null, null, null);
+    }
+
+    @Override
+    protected void update(DbEntry item) {
+        mUpdates.put(item.id, item.copy());
+    }
+
+    @Override
+    protected ArrayList<DbEntry> loadWorkspaceEntries(long screen) {
+        ArrayList<DbEntry> result = super.loadWorkspaceEntries(screen);
+        for (DbEntry entry : result) {
+            mOriginalItems.put(entry.id, entry.copy());
+
+            // Shift all items by 1 in y direction and mark them for update.
+            entry.cellY++;
+            mUpdates.put(entry.id, entry.copy());
+        }
+
+        return result;
+    }
+
+    public void migrateScreen0() {
+        migrateScreen(Workspace.FIRST_SCREEN_ID);
+
+        ContentValues tempValues = new ContentValues();
+        for (DbEntry update : mUpdates) {
+            DbEntry org = mOriginalItems.get(update.id);
+
+            if (org.cellX != update.cellX || org.cellY != update.cellY
+                    || org.spanX != update.spanX || org.spanY != update.spanY) {
+                tempValues.clear();
+                update.addToContentValues(tempValues);
+                mDb.update(Favorites.TABLE_NAME, tempValues, "_id = ?",
+                        new String[] {Long.toString(update.id)});
+            }
+        }
+
+        // Delete any carry over items as we are only migration a single screen.
+        for (DbEntry entry : mCarryOver) {
+            mEntryToRemove.add(entry.id);
+        }
+
+        if (!mEntryToRemove.isEmpty()) {
+            mDb.delete(Favorites.TABLE_NAME,
+                    Utilities.createDbSelectionQuery(Favorites._ID, mEntryToRemove), null);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutManager.java b/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
new file mode 100644
index 0000000..e2e06af
--- /dev/null
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -0,0 +1,136 @@
+/*
+ * 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.shortcuts;
+
+import android.annotation.TargetApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.LauncherApps.ShortcutQuery;
+import android.os.Build;
+import android.os.Process;
+import android.util.Log;
+
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.UserHandleCompat;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Performs operations related to deep shortcuts, such as querying for them, pinning them, etc.
+ */
+@TargetApi(Build.VERSION_CODES.N)
+public class DeepShortcutManager {
+    private static final int FLAG_GET_ALL = ShortcutQuery.FLAG_GET_DYNAMIC
+            | ShortcutQuery.FLAG_GET_PINNED | ShortcutQuery.FLAG_GET_MANIFEST;
+
+    private final LauncherAppsCompat mLauncherApps;
+
+    public DeepShortcutManager(Context context, ShortcutCache shortcutCache) {
+        mLauncherApps = LauncherAppsCompat.getInstance(context);
+    }
+
+    public void onShortcutsChanged(List<ShortcutInfoCompat> shortcuts) {
+        // mShortcutCache.removeShortcuts(shortcuts);
+    }
+
+    /**
+     * Queries for the shortcuts with the package name and provided ids.
+     *
+     * This method is intended to get the full details for shortcuts when they are added or updated,
+     * because we only get "key" fields in onShortcutsChanged().
+     */
+    public List<ShortcutInfoCompat> queryForFullDetails(String packageName,
+            List<String> shortcutIds, UserHandleCompat user) {
+        return query(FLAG_GET_ALL, packageName, null, shortcutIds, user);
+    }
+
+    /**
+     * Gets all the shortcuts associated with the given package and user.
+     */
+    public List<ShortcutInfoCompat> queryForAllAppShortcuts(ComponentName activity,
+            List<String> ids, UserHandleCompat user) {
+        return query(FLAG_GET_ALL, activity.getPackageName(), activity, ids, user);
+    }
+
+    /**
+     * Removes the given shortcut from the current list of pinned shortcuts.
+     * (Runs on background thread)
+     */
+    public void unpinShortcut(final ShortcutKey key) {
+        String packageName = key.componentName.getPackageName();
+        String id = key.id;
+        UserHandleCompat user = key.user;
+        List<String> pinnedIds = extractIds(queryForPinnedShortcuts(packageName, user));
+        pinnedIds.remove(id);
+        mLauncherApps.pinShortcuts(packageName, pinnedIds, user);
+    }
+
+    /**
+     * Adds the given shortcut to the current list of pinned shortcuts.
+     * (Runs on background thread)
+     */
+    public void pinShortcut(final ShortcutKey key) {
+        String packageName = key.componentName.getPackageName();
+        String id = key.id;
+        UserHandleCompat user = key.user;
+        List<String> pinnedIds = extractIds(queryForPinnedShortcuts(packageName, user));
+        pinnedIds.add(id);
+        mLauncherApps.pinShortcuts(packageName, pinnedIds, user);
+    }
+
+    /**
+     * Returns the id's of pinned shortcuts associated with the given package and user.
+     *
+     * If packageName is null, returns all pinned shortcuts regardless of package.
+     */
+    public List<ShortcutInfoCompat> queryForPinnedShortcuts(String packageName,
+            UserHandleCompat user) {
+        return query(ShortcutQuery.FLAG_GET_PINNED, packageName, null, null, user);
+    }
+
+    public List<ShortcutInfoCompat> queryForAllShortcuts(UserHandleCompat user) {
+        return query(FLAG_GET_ALL, null, null, null, user);
+    }
+
+    private List<String> extractIds(List<ShortcutInfoCompat> shortcuts) {
+        List<String> shortcutIds = new ArrayList<>(shortcuts.size());
+        for (ShortcutInfoCompat shortcut : shortcuts) {
+            shortcutIds.add(shortcut.getId());
+        }
+        return shortcutIds;
+    }
+
+    /**
+     * Query the system server for all the shortcuts matching the given parameters.
+     * If packageName == null, we query for all shortcuts with the passed flags, regardless of app.
+     *
+     * TODO: Use the cache to optimize this so we don't make an RPC every time.
+     */
+    private List<ShortcutInfoCompat> query(int flags, String packageName,
+            ComponentName activity, List<String> shortcutIds, UserHandleCompat user) {
+        ShortcutQuery q = new ShortcutQuery();
+        q.setQueryFlags(flags);
+        if (packageName != null) {
+            q.setPackage(packageName);
+            q.setActivity(activity);
+            q.setShortcutIds(shortcutIds);
+        }
+        return mLauncherApps.getShortcuts(q, user);
+    }
+}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutCache.java b/src/com/android/launcher3/shortcuts/ShortcutCache.java
new file mode 100644
index 0000000..fc118a8
--- /dev/null
+++ b/src/com/android/launcher3/shortcuts/ShortcutCache.java
@@ -0,0 +1,76 @@
+/*
+ * 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.shortcuts;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.os.UserHandle;
+import android.util.LruCache;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Loads {@link ShortcutInfoCompat}s on demand (e.g. when launcher
+ * loads for pinned shortcuts and on long-press for dynamic shortcuts), and caches them
+ * for handful of apps in an LruCache while launcher lives.
+ */
+@TargetApi(Build.VERSION_CODES.N)
+public class ShortcutCache {
+    private static final String TAG = "ShortcutCache";
+    private static final boolean LOGD = false;
+
+    private static final int CACHE_SIZE = 30; // Max number shortcuts we cache.
+
+    private LruCache<ShortcutKey, ShortcutInfoCompat> mCachedShortcuts;
+    // We always keep pinned shortcuts in the cache.
+    private HashMap<ShortcutKey, ShortcutInfoCompat> mPinnedShortcuts;
+
+    public ShortcutCache() {
+        mCachedShortcuts = new LruCache<>(CACHE_SIZE);
+        mPinnedShortcuts = new HashMap<>();
+    }
+
+    /**
+     * Removes shortcuts from the cache when shortcuts change for a given package.
+     *
+     * Returns a map of ids to their evicted shortcuts.
+     *
+     * @see android.content.pm.LauncherApps.Callback#onShortcutsChanged(String, List, UserHandle).
+     */
+    public void removeShortcuts(List<ShortcutInfoCompat> shortcuts) {
+        for (ShortcutInfoCompat shortcut : shortcuts) {
+            ShortcutKey key = ShortcutKey.fromInfo(shortcut);
+            mCachedShortcuts.remove(key);
+        }
+    }
+
+    public ShortcutInfoCompat get(ShortcutKey key) {
+        if (mPinnedShortcuts.containsKey(key)) {
+            return mPinnedShortcuts.get(key);
+        }
+        return mCachedShortcuts.get(key);
+    }
+
+    public void put(ShortcutKey key, ShortcutInfoCompat shortcut) {
+        if (shortcut.isPinned()) {
+            mPinnedShortcuts.put(key, shortcut);
+        } else {
+            mCachedShortcuts.put(key, shortcut);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java b/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
new file mode 100644
index 0000000..8dbeaa7
--- /dev/null
+++ b/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
@@ -0,0 +1,104 @@
+/*
+ * 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.shortcuts;
+
+import android.annotation.TargetApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.os.Build;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.util.ComponentKey;
+
+/**
+ * Wrapper class for {@link android.content.pm.ShortcutInfo}, representing deep shortcuts into apps.
+ *
+ * Not to be confused with {@link com.android.launcher3.ShortcutInfo}.
+ */
+@TargetApi(Build.VERSION_CODES.N)
+public class ShortcutInfoCompat {
+    private static final String INTENT_CATEGORY = "com.android.launcher3.DEEP_SHORTCUT";
+    public static final String EXTRA_SHORTCUT_ID = "shortcut_id";
+
+    private ShortcutInfo mShortcutInfo;
+
+    public ShortcutInfoCompat(ShortcutInfo shortcutInfo) {
+        mShortcutInfo = shortcutInfo;
+    }
+
+    @TargetApi(Build.VERSION_CODES.N)
+    public Intent makeIntent(Context context) {
+        long serialNumber = UserManagerCompat.getInstance(context)
+                .getSerialNumberForUser(getUserHandle());
+        return new Intent(Intent.ACTION_MAIN)
+                .addCategory(INTENT_CATEGORY)
+                .setComponent(getActivity())
+                .setPackage(getPackage())
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
+                .putExtra(ItemInfo.EXTRA_PROFILE, serialNumber)
+                .putExtra(EXTRA_SHORTCUT_ID, getId());
+    }
+
+    public ShortcutInfo getShortcutInfo() {
+        return mShortcutInfo;
+    }
+
+    public String getPackage() {
+        return mShortcutInfo.getPackage();
+    }
+
+    public String getId() {
+        return mShortcutInfo.getId();
+    }
+
+    public CharSequence getShortLabel() {
+        return mShortcutInfo.getShortLabel();
+    }
+
+    public CharSequence getLongLabel() {
+        return mShortcutInfo.getLongLabel();
+    }
+
+    public long getLastChangedTimestamp() {
+        return mShortcutInfo.getLastChangedTimestamp();
+    }
+
+    public ComponentName getActivity() {
+        return mShortcutInfo.getActivity();
+    }
+
+    public UserHandleCompat getUserHandle() {
+        return UserHandleCompat.fromUser(mShortcutInfo.getUserHandle());
+    }
+
+    public boolean hasKeyFieldsOnly() {
+        return mShortcutInfo.hasKeyFieldsOnly();
+    }
+
+    public boolean isPinned() {
+        return mShortcutInfo.isPinned();
+    }
+
+    @Override
+    public String toString() {
+        return mShortcutInfo.toString();
+    }
+}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutKey.java b/src/com/android/launcher3/shortcuts/ShortcutKey.java
new file mode 100644
index 0000000..c9d66eb
--- /dev/null
+++ b/src/com/android/launcher3/shortcuts/ShortcutKey.java
@@ -0,0 +1,30 @@
+package com.android.launcher3.shortcuts;
+
+import android.content.ComponentName;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.util.ComponentKey;
+
+/**
+ * A key that uniquely identifies a shortcut using its package, id, and user handle.
+ */
+public class ShortcutKey extends ComponentKey {
+    final String id;
+
+    public ShortcutKey(String packageName, UserHandleCompat user, String id) {
+        // Use the id as the class name.
+        super(new ComponentName(packageName, id), user);
+        this.id = id;
+    }
+
+    public static ShortcutKey fromInfo(ShortcutInfoCompat shortcutInfo) {
+        return new ShortcutKey(shortcutInfo.getPackage(), shortcutInfo.getUserHandle(),
+                shortcutInfo.getId());
+    }
+
+    @Override
+    public String toString() {
+        return flattenToString(LauncherAppState.getInstance().getContext());
+    }
+}
diff --git a/src/com/android/launcher3/util/CachedPackageTracker.java b/src/com/android/launcher3/util/CachedPackageTracker.java
index d55d573..293714e 100644
--- a/src/com/android/launcher3/util/CachedPackageTracker.java
+++ b/src/com/android/launcher3/util/CachedPackageTracker.java
@@ -170,7 +170,7 @@
      */
     protected abstract void onLauncherPackageRemoved(String packageName, UserHandleCompat user);
 
-    protected static class LauncherActivityInstallInfo
+    public static class LauncherActivityInstallInfo
             implements Comparable<LauncherActivityInstallInfo> {
         public final LauncherActivityInfoCompat info;
         public final long installTime;
diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java
index a5498f7..163c953 100644
--- a/src/com/android/launcher3/util/FocusLogic.java
+++ b/src/com/android/launcher3/util/FocusLogic.java
@@ -22,7 +22,10 @@
 import android.view.ViewGroup;
 
 import com.android.launcher3.CellLayout;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.ShortcutAndWidgetContainer;
+import com.android.launcher3.config.FeatureFlags;
 
 import java.util.Arrays;
 
@@ -190,15 +193,17 @@
      * in portrait orientation. In landscape, [(icon + hotseat) column count x (icon row count)]
      */
     // TODO: get rid of the dynamic matrix creation
-    public static int[][] createSparseMatrixWithHotseat(CellLayout iconLayout,
-            CellLayout hotseatLayout, boolean isHotseatHorizontal, int allappsiconRank) {
+    public static int[][] createSparseMatrixWithHotseat(
+            CellLayout iconLayout, CellLayout hotseatLayout, DeviceProfile dp) {
 
         ViewGroup iconParent = iconLayout.getShortcutsAndWidgets();
         ViewGroup hotseatParent = hotseatLayout.getShortcutsAndWidgets();
 
-        boolean moreIconsInHotseatThanWorkspace = isHotseatHorizontal ?
-                hotseatLayout.getCountX() > iconLayout.getCountX() :
-                hotseatLayout.getCountY() > iconLayout.getCountY();
+        boolean isHotseatHorizontal = !dp.isVerticalBarLayout();
+        boolean moreIconsInHotseatThanWorkspace = !FeatureFlags.NO_ALL_APPS_ICON &&
+                (isHotseatHorizontal
+                        ? hotseatLayout.getCountX() > iconLayout.getCountX()
+                        : hotseatLayout.getCountY() > iconLayout.getCountY());
 
         int m, n;
         if (isHotseatHorizontal) {
@@ -210,6 +215,7 @@
         }
         int[][] matrix = createFullMatrix(m, n);
         if (moreIconsInHotseatThanWorkspace) {
+            int allappsiconRank = dp.inv.getAllAppsButtonRank();
             if (isHotseatHorizontal) {
                 for (int j = 0; j < n; j++) {
                     matrix[allappsiconRank][j] = ALL_APPS_COLUMN;
@@ -229,6 +235,7 @@
             int cx = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellX;
             int cy = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellY;
             if (moreIconsInHotseatThanWorkspace) {
+                int allappsiconRank = dp.inv.getAllAppsButtonRank();
                 if (isHotseatHorizontal && cx >= allappsiconRank) {
                     // Add 1 to account for the All Apps button.
                     cx++;
diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
index df23abe..7dbc0e7a 100644
--- a/src/com/android/launcher3/util/ManagedProfileHeuristic.java
+++ b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
@@ -29,6 +29,7 @@
 import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.LauncherActivityInfoCompat;
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 
@@ -180,6 +181,12 @@
                 saveWorkFolderShortcuts(workFolder.id, 0, workFolderApps);
             }
         }
+
+        @Override
+        public void onShortcutsChanged(String packageName, List<ShortcutInfoCompat> shortcuts,
+                UserHandleCompat user) {
+            // Do nothing
+        }
     }
 
     /**
diff --git a/src/com/android/launcher3/util/MultiHashMap.java b/src/com/android/launcher3/util/MultiHashMap.java
new file mode 100644
index 0000000..f54ab88
--- /dev/null
+++ b/src/com/android/launcher3/util/MultiHashMap.java
@@ -0,0 +1,20 @@
+package com.android.launcher3.util;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * A utility map from keys to an ArrayList of values.
+ */
+public class MultiHashMap<K, V> extends HashMap<K, ArrayList<V>> {
+    public void addToList(K key, V value) {
+        ArrayList<V> list = get(key);
+        if (list == null) {
+            list = new ArrayList<>();
+            list.add(value);
+            put(key, list);
+        } else {
+            list.add(value);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 3ad03ae..97877fd 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -88,13 +88,13 @@
         super(context, attrs, defStyle);
 
         final Resources r = context.getResources();
-        mLauncher = (Launcher) context;
+        mLauncher = Launcher.getLauncher(context);
         mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
 
         setContainerWidth();
         setWillNotDraw(false);
         setClipToPadding(false);
-        setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
+        setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
     }
 
     private void setContainerWidth() {
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index a76f0af..8a58d34 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -82,9 +82,9 @@
 
     public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mLauncher = (Launcher) context;
+        mLauncher = Launcher.getLauncher(context);
         mDragController = mLauncher.getDragController();
-        mAdapter = new WidgetsListAdapter(this, this, mLauncher);
+        mAdapter = new WidgetsListAdapter(this, this, context);
         mIconCache = (LauncherAppState.getInstance()).getIconCache();
         if (LOGD) {
             Log.d(TAG, "WidgetsContainerView constructor");
diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java
index e68450d..6b8ea49 100644
--- a/src/com/android/launcher3/widget/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.widget;
 
 import android.annotation.TargetApi;
+import android.content.Context;
 import android.os.Build;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerView.Adapter;
@@ -27,7 +28,6 @@
 import android.widget.LinearLayout;
 
 import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
@@ -63,13 +63,13 @@
 
     public WidgetsListAdapter(View.OnClickListener iconClickListener,
             View.OnLongClickListener iconLongClickListener,
-            Launcher launcher) {
-        mLayoutInflater = launcher.getLayoutInflater();
+            Context context) {
+        mLayoutInflater = LayoutInflater.from(context);
         mWidgetPreviewLoader = LauncherAppState.getInstance().getWidgetCache();
 
         mIconClickListener = iconClickListener;
         mIconLongClickListener = iconLongClickListener;
-        mIndent = launcher.getResources().getDimensionPixelSize(R.dimen.widget_section_indent);
+        mIndent = context.getResources().getDimensionPixelSize(R.dimen.widget_section_indent);
     }
 
     public void setWidgetsModel(WidgetsModel w) {
diff --git a/src_config/com/android/launcher3/config/FeatureFlags.java b/src_config/com/android/launcher3/config/FeatureFlags.java
index 2b9e6ce..69d8f0f 100644
--- a/src_config/com/android/launcher3/config/FeatureFlags.java
+++ b/src_config/com/android/launcher3/config/FeatureFlags.java
@@ -30,4 +30,9 @@
     public static boolean LAUNCHER3_USE_SYSTEM_DRAG_DRIVER = false;
     public static boolean LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW = false;
     public static boolean LAUNCHER3_ALL_APPS_PULL_UP = true;
+
+    // Feature flag to enable moving the QSB on the 0th screen of the workspace
+    public static final boolean QSB_ON_FIRST_SCREEN = true;
+    // When enabled the all-apps icon is not added to the hotseat.
+    public static final boolean NO_ALL_APPS_ICON = true;
 }
diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
index ebf06ba..c250cb2 100644
--- a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
+++ b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
@@ -10,6 +10,7 @@
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.config.ProviderConfig;
 import com.android.launcher3.util.TestLauncherProvider;
 
@@ -58,10 +59,16 @@
                 addItem(APPLICATION, 4, HOTSEAT, 0, 0),
         };
 
-        new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, 5, 2, 3, 1)
+        mIdp.numHotseatIcons = 3;
+        new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, 5, 3)
                 .migrateHotseat();
-        // First & last items are dropped as they have the least weight.
-        verifyHotseat(hotseatItems[1], -1, hotseatItems[3]);
+        if (FeatureFlags.NO_ALL_APPS_ICON) {
+            // First item is dropped as it has the least weight.
+            verifyHotseat(hotseatItems[1], hotseatItems[3], hotseatItems[4]);
+        } else {
+            // First & last items are dropped as they have the least weight.
+            verifyHotseat(hotseatItems[1], -1, hotseatItems[3]);
+        }
     }
 
     public void testHotseatMigration_shortcuts_dropped() throws Exception {
@@ -73,10 +80,16 @@
                 addItem(10, 4, HOTSEAT, 0, 0),
         };
 
-        new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, 5, 2, 3, 1)
+        mIdp.numHotseatIcons = 3;
+        new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, 5, 3)
                 .migrateHotseat();
-        // First & third items are dropped as they have the least weight.
-        verifyHotseat(hotseatItems[1], -1, hotseatItems[4]);
+        if (FeatureFlags.NO_ALL_APPS_ICON) {
+            // First item is dropped as it has the least weight.
+            verifyHotseat(hotseatItems[1], hotseatItems[3], hotseatItems[4]);
+        } else {
+            // First & third items are dropped as they have the least weight.
+            verifyHotseat(hotseatItems[1], -1, hotseatItems[4]);
+        }
     }
 
     private void verifyHotseat(long... sortedIds) {
diff --git a/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java b/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java
index a59f0ff..e858d17 100644
--- a/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java
+++ b/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java
@@ -28,6 +28,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.ManagedProfileHeuristic;
 
 import java.io.FileInputStream;
@@ -101,8 +102,14 @@
      * Opens all apps and returns the recycler view
      */
     protected UiObject2 openAllApps() {
-        mDevice.wait(Until.findObject(
-                By.desc(mTargetContext.getString(R.string.all_apps_button_label))), DEFAULT_UI_TIMEOUT).click();
+        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
+            // clicking on the page indicator brings up all apps tray on non tablets.
+            findViewById(R.id.page_indicator).click();
+        } else {
+            mDevice.wait(Until.findObject(
+                    By.desc(mTargetContext.getString(R.string.all_apps_button_label))),
+                    DEFAULT_UI_TIMEOUT).click();
+        }
         return findViewById(R.id.apps_list_view);
     }