Merge "Initial changes to break out AllApps into its own view." into ub-launcher3-burnaby
diff --git a/res/drawable/apps_list_bg.xml b/res/drawable/apps_list_bg.xml
new file mode 100644
index 0000000..61f1c08
--- /dev/null
+++ b/res/drawable/apps_list_bg.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2015 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <solid android:color="#ffffff" />
+    <corners android:radius="3dp" />
+</shape>
\ No newline at end of file
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index 6f95bd5..b13984a 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -62,6 +62,12 @@
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:visibility="invisible" />
+
+        <include layout="@layout/apps_view"
+            android:id="@+id/apps_view"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:visibility="invisible" />
     </com.android.launcher3.DragLayer>
 
     <ViewStub
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index af30a32..3cb338e 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -71,6 +71,12 @@
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:visibility="invisible" />
+
+        <include layout="@layout/apps_view"
+            android:id="@+id/apps_view"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:visibility="invisible" />
     </com.android.launcher3.DragLayer>
 
     <ViewStub
diff --git a/res/layout-sw600dp/apps_view.xml b/res/layout-sw600dp/apps_view.xml
new file mode 100644
index 0000000..1f773b3
--- /dev/null
+++ b/res/layout-sw600dp/apps_view.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.launcher3.AppsContainerView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/apps_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:padding="8dp"
+    android:background="#22000000"
+    android:descendantFocusability="afterDescendants">
+    <include
+        layout="@layout/apps_list_reveal_view"
+        android:layout_width="420dp"
+        android:layout_height="match_parent"
+        android:layout_gravity="center" />
+    <include
+        layout="@layout/apps_list_view"
+        android:layout_width="420dp"
+        android:layout_height="match_parent"
+        android:layout_gravity="center" />
+</com.android.launcher3.AppsContainerView>
\ No newline at end of file
diff --git a/res/layout-sw720dp/launcher.xml b/res/layout-sw720dp/launcher.xml
index 960ccf3..a3d502c 100644
--- a/res/layout-sw720dp/launcher.xml
+++ b/res/layout-sw720dp/launcher.xml
@@ -71,6 +71,12 @@
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:visibility="invisible" />
+
+        <include layout="@layout/apps_view"
+            android:id="@+id/apps_view"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:visibility="invisible" />
     </com.android.launcher3.DragLayer>
 
     <ViewStub
diff --git a/res/layout/apps_grid_row_icon_view.xml b/res/layout/apps_grid_row_icon_view.xml
new file mode 100644
index 0000000..11c8eeb
--- /dev/null
+++ b/res/layout/apps_grid_row_icon_view.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<com.android.launcher3.BubbleTextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    style="@style/WorkspaceIcon.AppsCustomize"
+    android:id="@+id/application_icon"
+    android:focusable="true"
+    android:background="@drawable/focusable_view_bg" />
diff --git a/res/layout/apps_grid_row_view.xml b/res/layout/apps_grid_row_view.xml
new file mode 100644
index 0000000..bce43bc
--- /dev/null
+++ b/res/layout/apps_grid_row_view.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/apps_view_row_height"
+    android:paddingTop="12dp"
+    android:paddingBottom="12dp"
+    android:orientation="horizontal"
+    android:focusable="true"
+    android:background="@drawable/focusable_view_bg"
+    android:descendantFocusability="afterDescendants">
+    <TextView
+        android:id="@+id/section"
+        android:layout_width="48dp"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:paddingRight="8dp"
+        android:paddingBottom="12dp"
+        android:gravity="right"
+        android:textColor="#1ca195"
+        android:textSize="16sp"
+        android:textAllCaps="true"
+        android:focusable="false" />
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/apps_list_reveal_view.xml b/res/layout/apps_list_reveal_view.xml
new file mode 100644
index 0000000..4a26787
--- /dev/null
+++ b/res/layout/apps_list_reveal_view.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/all_apps_transition_overlay"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_gravity="center"
+    android:elevation="15dp"
+    android:visibility="invisible"
+    android:background="@drawable/apps_list_bg"
+    android:focusable="false" />
\ No newline at end of file
diff --git a/res/layout/apps_list_row_icon_view.xml b/res/layout/apps_list_row_icon_view.xml
new file mode 100644
index 0000000..607af9b
--- /dev/null
+++ b/res/layout/apps_list_row_icon_view.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<com.android.launcher3.BubbleTextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto"
+    style="@style/WorkspaceIcon.AppsCustomize"
+    android:id="@+id/application_icon"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:focusable="true"
+    android:background="@drawable/focusable_view_bg"
+    launcher:iconPaddingOverride="24dp"
+    launcher:textSizeOverride="16dp"
+    launcher:layoutHorizontal="true" />
diff --git a/res/layout/apps_list_row_view.xml b/res/layout/apps_list_row_view.xml
new file mode 100644
index 0000000..c4dcd00
--- /dev/null
+++ b/res/layout/apps_list_row_view.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/apps_view_row_height"
+    android:orientation="horizontal"
+    android:focusable="true"
+    android:background="@drawable/focusable_view_bg"
+    android:descendantFocusability="afterDescendants">
+    <TextView
+        android:id="@+id/section"
+        android:layout_width="64dp"
+        android:layout_height="match_parent"
+        android:paddingLeft="16dp"
+        android:gravity="left|center_vertical"
+        android:textColor="#009688"
+        android:textSize="24sp"
+        android:textAllCaps="true"
+        android:focusable="false" />
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/apps_list_view.xml b/res/layout/apps_list_view.xml
new file mode 100644
index 0000000..b1b0f31
--- /dev/null
+++ b/res/layout/apps_list_view.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+<ListView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/apps_list"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_gravity="center"
+    android:paddingTop="12dp"
+    android:paddingBottom="12dp"
+    android:clipToPadding="false"
+    android:scrollbars="vertical"
+    android:elevation="15dp"
+    android:background="@drawable/apps_list_bg"
+    android:visibility="gone"
+    android:focusable="true"
+    android:descendantFocusability="afterDescendants" />
\ No newline at end of file
diff --git a/res/layout/apps_view.xml b/res/layout/apps_view.xml
new file mode 100644
index 0000000..19ad3d2
--- /dev/null
+++ b/res/layout/apps_view.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.launcher3.AppsContainerView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/apps_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:padding="8dp"
+    android:background="#22000000"
+    android:descendantFocusability="afterDescendants">
+    <include
+        layout="@layout/apps_list_reveal_view" />
+    <include
+        layout="@layout/apps_list_view" />
+</com.android.launcher3.AppsContainerView>
\ No newline at end of file
diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml
index 28679be..f7ad0c4 100644
--- a/res/values-sw600dp/dimens.xml
+++ b/res/values-sw600dp/dimens.xml
@@ -17,6 +17,9 @@
 <resources>
     <dimen name="app_icon_size">64dp</dimen>
 
+<!-- Apps view -->
+    <dimen name="apps_view_row_height">76dp</dimen>
+
 <!-- AppsCustomize -->
     <dimen name="apps_customize_tab_bar_height">60dp</dimen>
     <dimen name="apps_customize_tab_bar_margin_top">8dp</dimen>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 3331cde..4e7c592 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -18,6 +18,14 @@
 
 <resources>
 
+    <!-- BubbleTextView specific attributes. -->
+    <declare-styleable name="BubbleTextView">
+        <attr name="layoutHorizontal" format="boolean" />
+        <attr name="iconSizeOverride" format="dimension" />
+        <attr name="iconPaddingOverride" format="dimension" />
+        <attr name="textSizeOverride" format="dimension" />
+    </declare-styleable>
+
     <!-- Page Indicator specific attributes. -->
     <declare-styleable name="PageIndicator">
         <attr name="windowSize" format="integer"  />
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index d6fc508..013bd92 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -46,6 +46,9 @@
     <dimen name="toolbar_button_vertical_padding">4dip</dimen>
     <dimen name="toolbar_button_horizontal_padding">12dip</dimen>
 
+<!-- Apps view -->
+    <dimen name="apps_view_row_height">64dp</dimen>
+
 <!-- AllApps/Customize/AppsCustomize -->
     <!-- The height of the tab bar - if this changes, we should update the
          external icon width/height above to compensate -->
diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java
new file mode 100644
index 0000000..cabacec
--- /dev/null
+++ b/src/com/android/launcher3/AppsContainerView.java
@@ -0,0 +1,609 @@
+package com.android.launcher3;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.SectionIndexer;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * Represents a row in the apps list view.
+ */
+class AppsRow {
+    int sectionId;
+    String sectionDescription;
+    List<AppInfo> apps;
+
+    public AppsRow(int sId, String sc, List<AppInfo> ai) {
+        sectionId = sId;
+        sectionDescription = sc;
+        apps = ai;
+    }
+
+    public AppsRow(int sId, List<AppInfo> ai) {
+        sectionId = sId;
+        apps = ai;
+    }
+}
+
+/**
+ * An interface to an algorithm that generates app rows.
+ */
+interface AppRowAlgorithm {
+    public List<AppsRow> computeAppRows(List<AppInfo> sortedApps, int appsPerRow);
+    public int getIconViewLayoutId();
+    public int getRowViewLayoutId();
+    public void bindRowViewIconToInfo(BubbleTextView icon, AppInfo info);
+}
+
+/**
+ * Computes the rows in the apps list view.
+ */
+class SectionedAppsAlgorithm implements AppRowAlgorithm {
+
+    @Override
+    public List<AppsRow> computeAppRows(List<AppInfo> sortedApps, int appsPerRow) {
+        List<AppsRow> rows = new ArrayList<>();
+        LinkedHashMap<String, List<AppInfo>> sections = computeSectionedApps(sortedApps);
+        int sectionId = 0;
+        for (Map.Entry<String, List<AppInfo>> sectionEntry : sections.entrySet()) {
+            String section = sectionEntry.getKey();
+            List<AppInfo> apps = sectionEntry.getValue();
+            int numRows = (int) Math.ceil((float) apps.size() / appsPerRow);
+            for (int i = 0; i < numRows; i++) {
+                List<AppInfo> appsInRow = new ArrayList<>();
+                int offset = i * appsPerRow;
+                for (int j = 0; j < appsPerRow; j++) {
+                    if (offset + j < apps.size()) {
+                        appsInRow.add(apps.get(offset + j));
+                    }
+                }
+                if (i == 0) {
+                    rows.add(new AppsRow(sectionId, section, appsInRow));
+                } else {
+                    rows.add(new AppsRow(sectionId, appsInRow));
+                }
+            }
+            sectionId++;
+        }
+        return rows;
+    }
+
+    @Override
+    public int getIconViewLayoutId() {
+        return R.layout.apps_grid_row_icon_view;
+    }
+
+    @Override
+    public int getRowViewLayoutId() {
+        return R.layout.apps_grid_row_view;
+    }
+
+    private LinkedHashMap<String, List<AppInfo>> computeSectionedApps(List<AppInfo> sortedApps) {
+        LinkedHashMap<String, List<AppInfo>> sections = new LinkedHashMap<>();
+        for (AppInfo info : sortedApps) {
+            String section = getSection(info);
+            List<AppInfo> sectionApps = sections.get(section);
+            if (sectionApps == null) {
+                sectionApps = new ArrayList<>();
+                sections.put(section, sectionApps);
+            }
+            sectionApps.add(info);
+        }
+        return sections;
+    }
+
+    @Override
+    public void bindRowViewIconToInfo(BubbleTextView icon, AppInfo info) {
+        icon.applyFromApplicationInfo(info);
+    }
+
+    private String getSection(AppInfo app) {
+        return app.title.toString().substring(0, 1).toLowerCase();
+    }
+}
+
+/**
+ * Computes the rows in the apps grid view.
+ */
+class ListedAppsAlgorithm implements AppRowAlgorithm {
+
+    @Override
+    public List<AppsRow> computeAppRows(List<AppInfo> sortedApps, int appsPerRow) {
+        List<AppsRow> rows = new ArrayList<>();
+        int sectionId = -1;
+        String prevSection = "";
+        for (AppInfo info : sortedApps) {
+            List<AppInfo> appsInRow = new ArrayList<>();
+            appsInRow.add(info);
+            String section = getSection(info);
+            if (!prevSection.equals(section)) {
+                prevSection = section;
+                sectionId++;
+                rows.add(new AppsRow(sectionId, section, appsInRow));
+            } else {
+                rows.add(new AppsRow(sectionId, appsInRow));
+            }
+        }
+        return rows;
+    }
+
+    @Override
+    public int getIconViewLayoutId() {
+        return R.layout.apps_list_row_icon_view;
+    }
+
+    @Override
+    public int getRowViewLayoutId() {
+        return R.layout.apps_list_row_view;
+    }
+
+    @Override
+    public void bindRowViewIconToInfo(BubbleTextView icon, AppInfo info) {
+        icon.applyFromApplicationInfo(info);
+    }
+
+    private String getSection(AppInfo app) {
+        return app.title.toString().substring(0, 1).toLowerCase();
+    }
+}
+
+/**
+ * The adapter of all the apps
+ */
+class AppsListAdapter extends BaseAdapter implements SectionIndexer {
+
+    private LayoutInflater mLayoutInflater;
+    private List<AppsRow> mAppRows = new ArrayList<>();
+    private View.OnTouchListener mTouchListener;
+    private View.OnClickListener mIconClickListener;
+    private View.OnLongClickListener mIconLongClickListener;
+    private AppRowAlgorithm mRowAlgorithm;
+    private int mAppsPerRow;
+
+    public AppsListAdapter(Context context, View.OnTouchListener touchListener,
+            View.OnClickListener iconClickListener, View.OnLongClickListener iconLongClickListener) {
+        mLayoutInflater = LayoutInflater.from(context);
+        mTouchListener = touchListener;
+        mIconClickListener = iconClickListener;
+        mIconLongClickListener = iconLongClickListener;
+    }
+
+    void setApps(List<AppsRow> apps, int appsPerRow, AppRowAlgorithm algo) {
+        mAppsPerRow = appsPerRow;
+        mRowAlgorithm = algo;
+        mAppRows.clear();
+        mAppRows.addAll(apps);
+        notifyDataSetChanged();
+    }
+
+    @Override
+    public int getCount() {
+        return mAppRows.size();
+    }
+
+    @Override
+    public Object getItem(int position) {
+        return mAppRows.get(position);
+    }
+
+    @Override
+    public long getItemId(int position) {
+        return position;
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        AppsRow info = mAppRows.get(position);
+        ViewGroup row = (ViewGroup) convertView;
+        if (row == null) {
+            // Inflate the row and all the icon children necessary
+            row = (ViewGroup) mLayoutInflater.inflate(mRowAlgorithm.getRowViewLayoutId(),
+                    parent, false);
+            for (int i = 0; i < mAppsPerRow; i++) {
+                BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
+                        mRowAlgorithm.getIconViewLayoutId(), row, false);
+                LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(0,
+                        ViewGroup.LayoutParams.WRAP_CONTENT, 1);
+                lp.gravity = Gravity.CENTER_VERTICAL;
+                icon.setLayoutParams(lp);
+                icon.setOnTouchListener(mTouchListener);
+                icon.setOnClickListener(mIconClickListener);
+                icon.setOnLongClickListener(mIconLongClickListener);
+                icon.setFocusable(true);
+                row.addView(icon);
+            }
+        }
+        // Bind the section header
+        TextView tv = (TextView) row.findViewById(R.id.section);
+        if (info.sectionDescription != null) {
+            tv.setText(info.sectionDescription);
+            tv.setVisibility(View.VISIBLE);
+        } else {
+            tv.setVisibility(View.INVISIBLE);
+        }
+        // Bind the icons
+        for (int i = 0; i < mAppsPerRow; i++) {
+            BubbleTextView icon = (BubbleTextView) row.getChildAt(i + 1);
+            if (i < info.apps.size()) {
+                mRowAlgorithm.bindRowViewIconToInfo(icon, info.apps.get(i));
+                icon.setVisibility(View.VISIBLE);
+            } else {
+                icon.setVisibility(View.INVISIBLE);
+            }
+        }
+        return row;
+    }
+
+    @Override
+    public Object[] getSections() {
+        ArrayList<Object> sections = new ArrayList<>();
+        int prevSectionId = -1;
+        for (AppsRow row : mAppRows) {
+            if (row.sectionId != prevSectionId) {
+                sections.add(row.sectionDescription.toUpperCase());
+                prevSectionId = row.sectionId;
+            }
+        }
+        return sections.toArray();
+    }
+
+    @Override
+    public int getPositionForSection(int sectionIndex) {
+        for (int i = 0; i < mAppRows.size(); i++) {
+            AppsRow row = mAppRows.get(i);
+            if (row.sectionId == sectionIndex) {
+                return i;
+            }
+        }
+        return 0;
+    }
+
+    @Override
+    public int getSectionForPosition(int position) {
+        return mAppRows.get(position).sectionId;
+    }
+}
+
+/**
+ * The alphabetically sorted list of applications.
+ */
+class AlphabeticalAppList {
+
+    /**
+     * Callbacks for when this list is modified.
+     */
+    public interface Callbacks {
+        public void onAppsUpdated();
+    }
+
+    private List<AppInfo> mApps;
+    private Callbacks mCb;
+
+    public AlphabeticalAppList(Callbacks cb) {
+        mCb = cb;
+    }
+
+    /**
+     * Returns the list of applications.
+     */
+    public List<AppInfo> getApps() {
+        return mApps;
+    }
+
+    /**
+     * Sets the current set of apps.
+     */
+    public void setApps(List<AppInfo> apps) {
+        Collections.sort(apps, LauncherModel.getAppNameComparator());
+        mApps = apps;
+        mCb.onAppsUpdated();
+    }
+
+    /**
+     * Adds new apps to the list.
+     */
+    public void addApps(List<AppInfo> apps) {
+        // We add it in place, in alphabetical order
+        Comparator<AppInfo> appNameComparator = LauncherModel.getAppNameComparator();
+        for (AppInfo info : apps) {
+            // This call will return the exact index of where the item is if >= 0, or the index
+            // where it should be inserted if < 0.
+            int index = Collections.binarySearch(mApps, info, appNameComparator);
+            if (index < 0) {
+                mApps.add(-(index + 1), info);
+            }
+        }
+        mCb.onAppsUpdated();
+    }
+
+    /**
+     * Updates existing apps in the list
+     */
+    public void updateApps(List<AppInfo> apps) {
+        Comparator<AppInfo> appNameComparator = LauncherModel.getAppNameComparator();
+        for (AppInfo info : apps) {
+            int index = mApps.indexOf(info);
+            if (index != -1) {
+                mApps.set(index, info);
+            } else {
+                index = Collections.binarySearch(mApps, info, appNameComparator);
+                if (index < 0) {
+                    mApps.add(-(index + 1), info);
+                }
+            }
+        }
+        mCb.onAppsUpdated();
+    }
+
+    /**
+     * Removes some apps from the list.
+     */
+    public void removeApps(List<AppInfo> apps) {
+        for (AppInfo info : apps) {
+            int removeIndex = findAppByComponent(mApps, info);
+            if (removeIndex != -1) {
+                mApps.remove(removeIndex);
+            }
+        }
+        mCb.onAppsUpdated();
+    }
+
+    /**
+     * Finds the index of an app given a target AppInfo.
+     */
+    private int findAppByComponent(List<AppInfo> apps, AppInfo targetInfo) {
+        ComponentName targetComponent = targetInfo.intent.getComponent();
+        int length = apps.size();
+        for (int i = 0; i < length; ++i) {
+            AppInfo info = apps.get(i);
+            if (info.user.equals(info.user)
+                    && info.intent.getComponent().equals(targetComponent)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+}
+
+/**
+ * The all apps list view container.
+ */
+public class AppsContainerView extends FrameLayout implements DragSource, View.OnTouchListener,
+        View.OnLongClickListener, Insettable, AlphabeticalAppList.Callbacks {
+
+    static final int GRID_LAYOUT = 0;
+    static final int LIST_LAYOUT = 1;
+    static final int USE_LAYOUT = LIST_LAYOUT;
+
+    private Launcher mLauncher;
+    private AppRowAlgorithm mAppRowsAlgorithm;
+    private AppsListAdapter mAdapter;
+    private AlphabeticalAppList mApps;
+    private ListView mList;
+    private int mAppsRowSize;
+    private Point mLastTouchDownPos = new Point();
+    private Rect mPadding = new Rect();
+
+    public AppsContainerView(Context context) {
+        this(context, null);
+    }
+
+    public AppsContainerView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public AppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public AppsContainerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        LauncherAppState app = LauncherAppState.getInstance();
+        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+
+        mLauncher = (Launcher) context;
+        if (USE_LAYOUT == GRID_LAYOUT) {
+            mAppRowsAlgorithm = new SectionedAppsAlgorithm();
+            mAppsRowSize = grid.allAppsRowsSize;
+        } else if (USE_LAYOUT == LIST_LAYOUT) {
+            mAppRowsAlgorithm = new ListedAppsAlgorithm();
+            mAppsRowSize = 1;
+        }
+        mAdapter = new AppsListAdapter(context, this, mLauncher, this);
+        mApps = new AlphabeticalAppList(this);
+    }
+
+    /**
+     * Sets the current set of apps.
+     */
+    public void setApps(List<AppInfo> apps) {
+        mApps.setApps(apps);
+    }
+
+    /**
+     * Adds new apps to the list.
+     */
+    public void addApps(List<AppInfo> apps) {
+        mApps.addApps(apps);
+    }
+
+    /**
+     * Updates existing apps in the list
+     */
+    public void updateApps(List<AppInfo> apps) {
+        mApps.updateApps(apps);
+    }
+
+    /**
+     * Removes some apps from the list.
+     */
+    public void removeApps(List<AppInfo> apps) {
+        mApps.removeApps(apps);
+    }
+
+    /**
+     * Scrolls this list view to the top.
+     */
+    public void scrollToTop() {
+        mList.scrollTo(0, 0);
+    }
+
+    /**
+     * Returns the content view used for the launcher transitions.
+     */
+    public View getContentView() {
+        return findViewById(R.id.apps_list);
+    }
+
+    /**
+     * Returns the reveal view used for the launcher transitions.
+     */
+    public View getRevealView() {
+        return findViewById(R.id.all_apps_transition_overlay);
+    }
+
+    @Override
+    public void onAppsUpdated() {
+        List<AppsRow> rows = mAppRowsAlgorithm.computeAppRows(mApps.getApps(), mAppsRowSize);
+        mAdapter.setApps(rows, mAppsRowSize, mAppRowsAlgorithm);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        mList = (ListView) findViewById(R.id.apps_list);
+        mList.setFastScrollEnabled(true);
+        mList.setFastScrollAlwaysVisible(true);
+        mList.setItemsCanFocus(true);
+        mList.setAdapter(mAdapter);
+        mPadding.set(getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom());
+    }
+
+    @Override
+    public void setInsets(Rect insets) {
+        setPadding(mPadding.left + insets.left, mPadding.top + insets.top,
+                mPadding.right + insets.right, mPadding.bottom + insets.bottom);
+    }
+
+    @Override
+    public boolean onTouch(View v, MotionEvent event) {
+        if (event.getAction() == MotionEvent.ACTION_DOWN ||
+                event.getAction() == MotionEvent.ACTION_MOVE) {
+            mLastTouchDownPos.set((int) event.getX(), (int) event.getY());
+        }
+        return false;
+    }
+
+    @Override
+    public boolean onLongClick(View v) {
+        // Return early if this is not initiated from a touch
+        if (!v.isInTouchMode()) return false;
+        // When we have exited all apps or are in transition, disregard long clicks
+        if (!mLauncher.isAppsViewVisible() ||
+                mLauncher.getWorkspace().isSwitchingState()) return false;
+        // Return if global dragging is not enabled
+        if (!mLauncher.isDraggingEnabled()) return false;
+
+        // Start the drag
+        mLauncher.getWorkspace().beginDragShared(v, mLastTouchDownPos, this, false);
+
+        // We delay entering spring-loaded mode slightly to make sure the UI
+        // thready is free of any work.
+        postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                // We don't enter spring-loaded mode if the drag has been cancelled
+                if (mLauncher.getDragController().isDragging()) {
+                    // Go into spring loaded mode (must happen before we startDrag())
+                    mLauncher.enterSpringLoadedDragMode();
+                }
+            }
+        }, 150);
+
+        return false;
+    }
+
+    @Override
+    public boolean supportsFlingToDelete() {
+        return true;
+    }
+
+    @Override
+    public boolean supportsAppInfoDropTarget() {
+        return true;
+    }
+
+    @Override
+    public boolean supportsDeleteDropTarget() {
+        return true;
+    }
+
+    @Override
+    public float getIntrinsicIconScaleFactor() {
+        LauncherAppState app = LauncherAppState.getInstance();
+        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        return (float) grid.allAppsIconSizePx / grid.iconSizePx;
+    }
+
+    @Override
+    public void onFlingToDeleteCompleted() {
+        // We just dismiss the drag when we fling, so cleanup here
+        mLauncher.exitSpringLoadedDragModeDelayed(true,
+                Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
+        mLauncher.unlockScreenOrientation(false);
+    }
+
+    @Override
+    public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete, boolean success) {
+        if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
+                !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) {
+            // Exit spring loaded mode if we have not successfully dropped or have not handled the
+            // drop in Workspace
+            mLauncher.exitSpringLoadedDragModeDelayed(true,
+                    Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
+        }
+        mLauncher.unlockScreenOrientation(false);
+
+        // Display an error message if the drag failed due to there not being enough space on the
+        // target layout we were dropping on.
+        if (!success) {
+            boolean showOutOfSpaceMessage = false;
+            if (target instanceof Workspace) {
+                int currentScreen = mLauncher.getCurrentWorkspaceScreen();
+                Workspace workspace = (Workspace) target;
+                CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen);
+                ItemInfo itemInfo = (ItemInfo) d.dragInfo;
+                if (layout != null) {
+                    layout.calculateSpans(itemInfo);
+                    showOutOfSpaceMessage =
+                            !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY);
+                }
+            }
+            if (showOutOfSpaceMessage) {
+                mLauncher.showOutOfSpaceMessage(false);
+            }
+
+            d.deferDragViewCleanupPostAnimation = false;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java
index 9f8d499..bf36812 100644
--- a/src/com/android/launcher3/AppsCustomizePagedView.java
+++ b/src/com/android/launcher3/AppsCustomizePagedView.java
@@ -149,10 +149,9 @@
      * The different content types that this paged view can show.
      */
     public enum ContentType {
-        Applications,
         Widgets
     }
-    private ContentType mContentType = ContentType.Applications;
+    private ContentType mContentType = ContentType.Widgets;
 
     // Refs
     private Launcher mLauncher;
@@ -164,7 +163,6 @@
     private int mSaveInstanceStateItemIndex = -1;
 
     // Content
-    private ArrayList<AppInfo> mApps;
     private ArrayList<Object> mWidgets;
 
     // Caching
@@ -174,9 +172,7 @@
     private int mContentWidth, mContentHeight;
     private int mWidgetCountX, mWidgetCountY;
     private PagedViewCellLayout mWidgetSpacingLayout;
-    private int mNumAppsPages;
     private int mNumWidgetPages;
-    private Rect mAllAppsPadding = new Rect();
 
     // Previews & outlines
     ArrayList<AppsCustomizeAsyncTask> mRunningTasks;
@@ -214,10 +210,9 @@
         super(context, attrs);
         mLayoutInflater = LayoutInflater.from(context);
         mPackageManager = context.getPackageManager();
-        mApps = new ArrayList<AppInfo>();
-        mWidgets = new ArrayList<Object>();
+        mWidgets = new ArrayList<>();
         mIconCache = (LauncherAppState.getInstance()).getIconCache();
-        mRunningTasks = new ArrayList<AppsCustomizeAsyncTask>();
+        mRunningTasks = new ArrayList<>();
 
         // Save the default widget preview background
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppsCustomizePagedView, 0, 0);
@@ -256,10 +251,6 @@
                 grid.edgeMarginPx, 2 * grid.edgeMarginPx);
     }
 
-    void setAllAppsPadding(Rect r) {
-        mAllAppsPadding.set(r);
-    }
-
     void setWidgetsPageIndicatorPadding(int pageIndicatorHeight) {
         setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), pageIndicatorHeight);
     }
@@ -277,22 +268,12 @@
         int i = -1;
         if (getPageCount() > 0) {
             int currentPage = getCurrentPage();
-            if (mContentType == ContentType.Applications) {
-                AppsCustomizeCellLayout layout = (AppsCustomizeCellLayout) getPageAt(currentPage);
-                ShortcutAndWidgetContainer childrenLayout = layout.getShortcutsAndWidgets();
-                int numItemsPerPage = mCellCountX * mCellCountY;
-                int childCount = childrenLayout.getChildCount();
-                if (childCount > 0) {
-                    i = (currentPage * numItemsPerPage) + (childCount / 2);
-                }
-            } else if (mContentType == ContentType.Widgets) {
-                int numApps = mApps.size();
+            if (mContentType == ContentType.Widgets) {
                 PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(currentPage);
                 int numItemsPerPage = mWidgetCountX * mWidgetCountY;
                 int childCount = layout.getChildCount();
                 if (childCount > 0) {
-                    i = numApps +
-                        (currentPage * numItemsPerPage) + (childCount / 2);
+                    i = (currentPage * numItemsPerPage) + (childCount / 2);
                 }
             } else {
                 throw new RuntimeException("Invalid ContentType");
@@ -314,13 +295,8 @@
     int getPageForComponent(int index) {
         if (index < 0) return 0;
 
-        if (index < mApps.size()) {
-            int numItemsPerPage = mCellCountX * mCellCountY;
-            return (index / numItemsPerPage);
-        } else {
-            int numItemsPerPage = mWidgetCountX * mWidgetCountY;
-            return (index - mApps.size()) / numItemsPerPage;
-        }
+        int numItemsPerPage = mWidgetCountX * mWidgetCountY;
+        return index / numItemsPerPage;
     }
 
     /** Restores the page for an item at the specified index */
@@ -332,16 +308,9 @@
     private void updatePageCounts() {
         mNumWidgetPages = (int) Math.ceil(mWidgets.size() /
                 (float) (mWidgetCountX * mWidgetCountY));
-        mNumAppsPages = (int) Math.ceil((float) mApps.size() / (mCellCountX * mCellCountY));
     }
 
     protected void onDataReady(int width, int height) {
-        // Now that the data is ready, we can calculate the content width, the number of cells to
-        // use for each page
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-        mCellCountX = (int) grid.allAppsNumCols;
-        mCellCountY = (int) grid.allAppsNumRows;
         updatePageCounts();
 
         // Force a measure to update recalculate the gaps
@@ -360,7 +329,7 @@
         super.onLayout(changed, l, t, r, b);
 
         if (!isDataReady()) {
-            if ((!mApps.isEmpty()) && !mWidgets.isEmpty()) {
+            if (!mWidgets.isEmpty()) {
                 post(new Runnable() {
                     // This code triggers requestLayout so must be posted outside of the
                     // layout pass.
@@ -438,7 +407,7 @@
     @Override
     public void onClick(View v) {
         // When we have exited all apps or are in transition, disregard clicks
-        if (!mLauncher.isAllAppsVisible()
+        if (!mLauncher.isWidgetsViewVisible()
                 || mLauncher.getWorkspace().isSwitchingState()
                 || !(v instanceof PagedViewWidget)) return;
 
@@ -456,11 +425,6 @@
      */
     @Override
     protected void determineDraggingStart(android.view.MotionEvent ev) {
-        // Disable dragging by pulling an app down for now.
-    }
-
-    private void beginDraggingApplication(View v) {
-        mLauncher.getWorkspace().beginDragShared(v, this);
     }
 
     static Bundle getDefaultOptionsForWidget(Launcher launcher, PendingAddWidgetInfo info) {
@@ -681,12 +645,12 @@
     protected boolean beginDragging(final View v) {
         if (!super.beginDragging(v)) return false;
 
-        if (v instanceof BubbleTextView) {
-            beginDraggingApplication(v);
-        } else if (v instanceof PagedViewWidget) {
+        if (v instanceof PagedViewWidget) {
             if (!beginDraggingWidget(v)) {
                 return false;
             }
+        } else {
+            Log.e(TAG, "Unexpected dragging view: " + v);
         }
 
         // We delay entering spring-loaded mode slightly to make sure the UI
@@ -852,7 +816,7 @@
         // Clean up all the async tasks
         Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator();
         while (iter.hasNext()) {
-            AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next();
+            AppsCustomizeAsyncTask task = iter.next();
             task.cancel(false);
             iter.remove();
             mDirtyPageContent.set(task.page, true);
@@ -886,7 +850,7 @@
         // Update the thread priorities given the direction lookahead
         Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator();
         while (iter.hasNext()) {
-            AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next();
+            AppsCustomizeAsyncTask task = iter.next();
             int pageIndex = task.page;
             if ((mNextPage > mCurrentPage && pageIndex >= mCurrentPage) ||
                 (mNextPage < mCurrentPage && pageIndex <= mCurrentPage)) {
@@ -897,36 +861,6 @@
         }
     }
 
-    /*
-     * Apps PagedView implementation
-     */
-    private void setVisibilityOnChildren(ViewGroup layout, int visibility) {
-        int childCount = layout.getChildCount();
-        for (int i = 0; i < childCount; ++i) {
-            layout.getChildAt(i).setVisibility(visibility);
-        }
-    }
-    private void setupPage(AppsCustomizeCellLayout layout) {
-        layout.setGridSize(mCellCountX, mCellCountY);
-
-        // Note: We force a measure here to get around the fact that when we do layout calculations
-        // immediately after syncing, we don't have a proper width.  That said, we already know the
-        // expected page width, so we can actually optimize by hiding all the TextView-based
-        // children that are expensive to measure, and let that happen naturally later.
-        setVisibilityOnChildren(layout, View.GONE);
-        int widthSpec = MeasureSpec.makeMeasureSpec(mContentWidth, MeasureSpec.AT_MOST);
-        int heightSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.AT_MOST);
-        layout.measure(widthSpec, heightSpec);
-
-        Drawable bg = getContext().getResources().getDrawable(R.drawable.quantum_panel);
-        if (bg != null) {
-            bg.setAlpha(mPageBackgroundsVisible ? 255: 0);
-            layout.setBackground(bg);
-        }
-
-        setVisibilityOnChildren(layout, View.VISIBLE);
-    }
-
     public void setPageBackgroundsVisible(boolean visible) {
         mPageBackgroundsVisible = visible;
         int childCount = getChildCount();
@@ -938,43 +872,6 @@
         }
     }
 
-    public void syncAppsPageItems(int page, boolean immediate) {
-        // ensure that we have the right number of items on the pages
-        final boolean isRtl = isLayoutRtl();
-        int numCells = mCellCountX * mCellCountY;
-        int startIndex = page * numCells;
-        int endIndex = Math.min(startIndex + numCells, mApps.size());
-        AppsCustomizeCellLayout layout = (AppsCustomizeCellLayout) getPageAt(page);
-
-        layout.removeAllViewsOnPage();
-        ArrayList<Object> items = new ArrayList<Object>();
-        ArrayList<Bitmap> images = new ArrayList<Bitmap>();
-        for (int i = startIndex; i < endIndex; ++i) {
-            AppInfo info = mApps.get(i);
-            BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
-                    R.layout.apps_customize_application, layout, false);
-            icon.applyFromApplicationInfo(info);
-            icon.setOnClickListener(mLauncher);
-            icon.setOnLongClickListener(this);
-            icon.setOnTouchListener(this);
-            icon.setOnKeyListener(mKeyListener);
-            icon.setOnFocusChangeListener(layout.mFocusHandlerView);
-
-            int index = i - startIndex;
-            int x = index % mCellCountX;
-            int y = index / mCellCountX;
-            if (isRtl) {
-                x = mCellCountX - x - 1;
-            }
-            layout.addViewToCellLayout(icon, -1, i, new CellLayout.LayoutParams(x,y, 1,1), false);
-
-            items.add(info);
-            images.add(info.iconBitmap);
-        }
-
-        enableHwLayersOnVisiblePages();
-    }
-
     /**
      * A helper to return the priority for loading of the specified widget page.
      */
@@ -991,7 +888,7 @@
         Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator();
         int minPageDiff = Integer.MAX_VALUE;
         while (iter.hasNext()) {
-            AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next();
+            AppsCustomizeAsyncTask task = iter.next();
             minPageDiff = Math.abs(task.page - toPage);
         }
 
@@ -1026,7 +923,7 @@
         // Prune all tasks that are no longer needed
         Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator();
         while (iter.hasNext()) {
-            AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next();
+            AppsCustomizeAsyncTask task = iter.next();
             int taskPage = task.page;
             if (taskPage < getAssociatedLowerPageBound(mCurrentPage) ||
                     taskPage > getAssociatedUpperPageBound(mCurrentPage)) {
@@ -1264,14 +1161,7 @@
         cancelAllTasks();
 
         Context context = getContext();
-        if (mContentType == ContentType.Applications) {
-            for (int i = 0; i < mNumAppsPages; ++i) {
-                AppsCustomizeCellLayout layout = new AppsCustomizeCellLayout(context);
-                setupPage(layout);
-                addView(layout, new PagedView.LayoutParams(LayoutParams.MATCH_PARENT,
-                        LayoutParams.MATCH_PARENT));
-            }
-        } else if (mContentType == ContentType.Widgets) {
+        if (mContentType == ContentType.Widgets) {
             for (int j = 0; j < mNumWidgetPages; ++j) {
                 PagedViewGridLayout layout = new PagedViewGridLayout(context, mWidgetCountX,
                         mWidgetCountY);
@@ -1291,7 +1181,7 @@
         if (mContentType == ContentType.Widgets) {
             syncWidgetPageItems(page, immediate);
         } else {
-            syncAppsPageItems(page, immediate);
+            Log.e(TAG, "Unexpected ContentType");
         }
     }
 
@@ -1383,7 +1273,7 @@
     }
 
     /**
-     * We should call thise method whenever the core data changes (mApps, mWidgets) so that we can
+     * We should call thise method whenever the core data changes (mWidgets) so that we can
      * appropriately determine when to invalidate the PagedView page data.  In cases where the data
      * has yet to be set, we can requestLayout() and wait for onDataReady() to be called in the
      * next onMeasure() pass, which will trigger an invalidatePageData() itself.
@@ -1399,73 +1289,12 @@
         }
     }
 
-    public void setApps(ArrayList<AppInfo> list) {
-        mApps = list;
-        Collections.sort(mApps, LauncherModel.getAppNameComparator());
-        updatePageCountsAndInvalidateData();
-    }
-
-    public ArrayList<AppInfo> getApps() {
-        return mApps;
-    }
-
-    private void addAppsWithoutInvalidate(ArrayList<AppInfo> list) {
-        // We add it in place, in alphabetical order
-        int count = list.size();
-        for (int i = 0; i < count; ++i) {
-            AppInfo info = list.get(i);
-            int index = Collections.binarySearch(mApps, info, LauncherModel.getAppNameComparator());
-            if (index < 0) {
-                mApps.add(-(index + 1), info);
-            }
-        }
-    }
-    public void addApps(ArrayList<AppInfo> list) {
-        addAppsWithoutInvalidate(list);
-        updatePageCountsAndInvalidateData();
-    }
-    private int findAppByComponent(List<AppInfo> list, AppInfo item) {
-        ComponentName removeComponent = item.intent.getComponent();
-        int length = list.size();
-        for (int i = 0; i < length; ++i) {
-            AppInfo info = list.get(i);
-            if (info.user.equals(item.user)
-                    && info.intent.getComponent().equals(removeComponent)) {
-                return i;
-            }
-        }
-        return -1;
-    }
-    private void removeAppsWithoutInvalidate(ArrayList<AppInfo> list) {
-        // loop through all the apps and remove apps that have the same component
-        int length = list.size();
-        for (int i = 0; i < length; ++i) {
-            AppInfo info = list.get(i);
-            int removeIndex = findAppByComponent(mApps, info);
-            if (removeIndex > -1) {
-                mApps.remove(removeIndex);
-            }
-        }
-    }
-    public void removeApps(ArrayList<AppInfo> appInfos) {
-        removeAppsWithoutInvalidate(appInfos);
-        updatePageCountsAndInvalidateData();
-    }
-    public void updateApps(ArrayList<AppInfo> list) {
-        // We remove and re-add the updated applications list because it's properties may have
-        // changed (ie. the title), and this will ensure that the items will be in their proper
-        // place in the list.
-        removeAppsWithoutInvalidate(list);
-        addAppsWithoutInvalidate(list);
-        updatePageCountsAndInvalidateData();
-    }
-
     public void reset() {
         // If we have reset, then we should not continue to restore the previous state
         mSaveInstanceStateItemIndex = -1;
 
-        if (mContentType != ContentType.Applications) {
-            setContentType(ContentType.Applications);
+        if (mContentType != ContentType.Widgets) {
+            setContentType(ContentType.Widgets);
         }
 
         if (mCurrentPage != 0) {
@@ -1479,7 +1308,6 @@
 
     public void dumpState() {
         // TODO: Dump information related to current list of Applications, Widgets, etc.
-        AppInfo.dumpApplicationInfoList(TAG, "mApps", mApps);
         dumpAppWidgetProviderInfoList(TAG, "mWidgets", mWidgets);
     }
 
@@ -1534,10 +1362,7 @@
         int stringId = R.string.default_scroll_format;
         int count = 0;
 
-        if (mContentType == ContentType.Applications) {
-            stringId = R.string.apps_customize_apps_scroll_format;
-            count = mNumAppsPages;
-        } else if (mContentType == ContentType.Widgets) {
+        if (mContentType == ContentType.Widgets) {
             stringId = R.string.apps_customize_widgets_scroll_format;
             count = mNumWidgetPages;
         } else {
diff --git a/src/com/android/launcher3/AppsCustomizeTabHost.java b/src/com/android/launcher3/AppsCustomizeTabHost.java
index a271712..5e2f05c 100644
--- a/src/com/android/launcher3/AppsCustomizeTabHost.java
+++ b/src/com/android/launcher3/AppsCustomizeTabHost.java
@@ -27,7 +27,6 @@
 public class AppsCustomizeTabHost extends FrameLayout implements LauncherTransitionable, Insettable  {
     static final String LOG_TAG = "AppsCustomizeTabHost";
 
-    private static final String APPS_TAB_TAG = "APPS";
     private static final String WIDGETS_TAB_TAG = "WIDGETS";
 
     private AppsCustomizePagedView mPagedView;
@@ -50,10 +49,6 @@
         mPagedView.setContentType(type);
     }
 
-    public void setCurrentTabFromContent(AppsCustomizePagedView.ContentType type) {
-        setContentTypeImmediate(type);
-    }
-
     @Override
     public void setInsets(Rect insets) {
         mInsets.set(insets);
@@ -79,27 +74,38 @@
     }
 
     /**
+     * Returns the content view used for the launcher transitions.
+     */
+    public View getContentView() {
+        return findViewById(R.id.apps_customize_pane_content);
+    }
+
+    /**
+     * Returns the reveal view used for the launcher transitions.
+     */
+    public View getRevealView() {
+        return findViewById(R.id.fake_page);
+    }
+
+    /**
+     * Returns the page indicators view.
+     */
+    public View getPageIndicators() {
+        return findViewById(R.id.apps_customize_page_indicator);
+    }
+
+    /**
      * Returns the content type for the specified tab tag.
      */
     public AppsCustomizePagedView.ContentType getContentTypeForTabTag(String tag) {
-        if (tag.equals(APPS_TAB_TAG)) {
-            return AppsCustomizePagedView.ContentType.Applications;
-        } else if (tag.equals(WIDGETS_TAB_TAG)) {
-            return AppsCustomizePagedView.ContentType.Widgets;
-        }
-        return AppsCustomizePagedView.ContentType.Applications;
+        return AppsCustomizePagedView.ContentType.Widgets;
     }
 
     /**
      * Returns the tab tag for a given content type.
      */
     public String getTabTagForContentType(AppsCustomizePagedView.ContentType type) {
-        if (type == AppsCustomizePagedView.ContentType.Applications) {
-            return APPS_TAB_TAG;
-        } else if (type == AppsCustomizePagedView.ContentType.Widgets) {
-            return WIDGETS_TAB_TAG;
-        }
-        return APPS_TAB_TAG;
+        return WIDGETS_TAB_TAG;
     }
 
     /**
@@ -199,6 +205,7 @@
         ViewGroup parent = (ViewGroup) getParent();
         if (parent == null) return;
 
+        View appsView = ((Launcher) getContext()).getAppsView();
         View overviewPanel = ((Launcher) getContext()).getOverviewPanel();
         final int count = parent.getChildCount();
         if (!isChildrenDrawingOrderEnabled()) {
@@ -207,7 +214,8 @@
                 if (child == this) {
                     break;
                 } else {
-                    if (child.getVisibility() == GONE || child == overviewPanel) {
+                    if (child.getVisibility() == GONE || child == overviewPanel ||
+                            child == appsView) {
                         continue;
                     }
                     child.setVisibility(visibility);
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index f9255e6..5ea84ae 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -28,6 +28,7 @@
 import android.util.AttributeSet;
 import android.util.SparseArray;
 import android.util.TypedValue;
+import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
@@ -49,7 +50,7 @@
     private static final int SHADOW_SMALL_COLOUR = 0xCC000000;
     static final float PADDING_V = 3.0f;
 
-
+    private Drawable mIcon;
     private final Drawable mBackground;
     private final CheckLongPressHelper mLongPressHelper;
     private final HolographicOutlineHelper mOutlineHelper;
@@ -62,9 +63,12 @@
 
     private float mSlop;
 
-    private int mTextColor;
     private final boolean mCustomShadowsEnabled;
-    private boolean mIsTextVisible;
+    private final boolean mLayoutHorizontal;
+    private final int mIconSize;
+    private final int mIconPaddingSize;
+    private final int mTextSize;
+    private int mTextColor;
 
     private boolean mStayPressed;
     private boolean mIgnorePressedStateChange;
@@ -79,10 +83,19 @@
 
     public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
+        LauncherAppState app = LauncherAppState.getInstance();
+        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
 
         TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.BubbleTextView, defStyle, 0);
         mCustomShadowsEnabled = a.getBoolean(R.styleable.BubbleTextView_customShadows, true);
+        mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false);
+        mIconSize = a.getDimensionPixelSize(R.styleable.BubbleTextView_iconSizeOverride,
+                grid.allAppsIconSizePx);
+        mIconPaddingSize = a.getDimensionPixelSize(R.styleable.BubbleTextView_iconPaddingOverride,
+                grid.iconDrawablePaddingPx);
+        mTextSize = a.getDimensionPixelSize(R.styleable.BubbleTextView_textSizeOverride,
+                grid.allAppsIconTextSizePx);
         a.recycle();
 
         if (mCustomShadowsEnabled) {
@@ -92,6 +105,12 @@
         } else {
             mBackground = null;
         }
+
+        // If we are laying out horizontal, then center the text vertically
+        if (mLayoutHorizontal) {
+            setGravity(Gravity.CENTER_VERTICAL);
+        }
+
         mLongPressHelper = new CheckLongPressHelper(this);
 
         mOutlineHelper = HolographicOutlineHelper.obtain(getContext());
@@ -106,9 +125,7 @@
         super.onFinishInflate();
 
         // Ensure we are using the right text size
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-        setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
+        setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
     }
 
     public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache,
@@ -119,16 +136,11 @@
     public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache,
             boolean setDefaultPadding, boolean promiseStateChanged) {
         Bitmap b = info.getIcon(iconCache);
-        LauncherAppState app = LauncherAppState.getInstance();
 
         FastBitmapDrawable iconDrawable = Utilities.createIconDrawable(b);
         iconDrawable.setGhostModeEnabled(info.isDisabled != 0);
 
-        setCompoundDrawables(null, iconDrawable, null, null);
-        if (setDefaultPadding) {
-            DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-            setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
-        }
+        setIcon(iconDrawable, mIconSize, setDefaultPadding ? mIconPaddingSize : -1);
         if (info.contentDescription != null) {
             setContentDescription(info.contentDescription);
         }
@@ -141,13 +153,7 @@
     }
 
     public void applyFromApplicationInfo(AppInfo info) {
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-
-        Drawable topDrawable = Utilities.createIconDrawable(info.iconBitmap);
-        topDrawable.setBounds(0, 0, grid.allAppsIconSizePx, grid.allAppsIconSizePx);
-        setCompoundDrawables(null, topDrawable, null, null);
-        setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
+        setIcon(Utilities.createIconDrawable(info.iconBitmap), mIconSize, mIconPaddingSize);
         setText(info.title);
         if (info.contentDescription != null) {
             setContentDescription(info.contentDescription);
@@ -155,7 +161,6 @@
         setTag(info);
     }
 
-
     @Override
     protected boolean setFrame(int left, int top, int right, int bottom) {
         if (getLeft() != left || getRight() != right || getTop() != top || getBottom() != bottom) {
@@ -186,10 +191,19 @@
         }
     }
 
+    /** Returns the icon for this view. */
+    public Drawable getIcon() {
+        return mIcon;
+    }
+
+    /** Returns whether the layout is horizontal. */
+    public boolean isLayoutHorizontal() {
+        return mLayoutHorizontal;
+    }
+
     private void updateIconState() {
-        Drawable top = getCompoundDrawables()[1];
-        if (top instanceof FastBitmapDrawable) {
-            ((FastBitmapDrawable) top).setPressed(isPressed() || mStayPressed);
+        if (mIcon instanceof FastBitmapDrawable) {
+            ((FastBitmapDrawable) mIcon).setPressed(isPressed() || mStayPressed);
         }
     }
 
@@ -325,10 +339,9 @@
         super.onAttachedToWindow();
 
         if (mBackground != null) mBackground.setCallback(this);
-        Drawable top = getCompoundDrawables()[1];
 
-        if (top instanceof PreloadIconDrawable) {
-            ((PreloadIconDrawable) top).applyPreloaderTheme(getPreloaderTheme());
+        if (mIcon instanceof PreloadIconDrawable) {
+            ((PreloadIconDrawable) mIcon).applyPreloaderTheme(getPreloaderTheme());
         }
         mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
     }
@@ -358,11 +371,6 @@
         } else {
             super.setTextColor(res.getColor(android.R.color.transparent));
         }
-        mIsTextVisible = visible;
-    }
-
-    public boolean isTextVisible() {
-        return mIsTextVisible;
     }
 
     @Override
@@ -385,15 +393,13 @@
                     ((info.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE) ?
                             info.getInstallProgress() : 0)) : 100;
 
-            Drawable[] drawables = getCompoundDrawables();
-            Drawable top = drawables[1];
-            if (top != null) {
+            if (mIcon != null) {
                 final PreloadIconDrawable preloadDrawable;
-                if (top instanceof PreloadIconDrawable) {
-                    preloadDrawable = (PreloadIconDrawable) top;
+                if (mIcon instanceof PreloadIconDrawable) {
+                    preloadDrawable = (PreloadIconDrawable) mIcon;
                 } else {
-                    preloadDrawable = new PreloadIconDrawable(top, getPreloaderTheme());
-                    setCompoundDrawables(drawables[0], preloadDrawable, drawables[2], drawables[3]);
+                    preloadDrawable = new PreloadIconDrawable(mIcon, getPreloaderTheme());
+                    setIcon(preloadDrawable, mIconSize, -1);
                 }
 
                 preloadDrawable.setLevel(progressLevel);
@@ -417,4 +423,23 @@
         }
         return theme;
     }
+
+    /**
+     * Sets the icon for this view based on the layout direction.
+     */
+    private Drawable setIcon(Drawable icon, int iconSize, int drawablePadding) {
+        mIcon = icon;
+        if (iconSize != -1) {
+            mIcon.setBounds(0, 0, iconSize, iconSize);
+        }
+        if (mLayoutHorizontal) {
+            setCompoundDrawablesRelative(mIcon, null, null, null);
+        } else {
+            setCompoundDrawablesRelative(null, mIcon, null, null);
+        }
+        if (drawablePadding != -1) {
+            setCompoundDrawablePadding(drawablePadding);
+        }
+        return icon;
+    }
 }
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 7d02e10..b5bb55c 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -122,6 +122,8 @@
     int hotseatAllAppsRank;
     int allAppsNumRows;
     int allAppsNumCols;
+    // TODO(winsonc): to be used with the grid layout
+    int allAppsRowsSize;
     int searchBarSpaceWidthPx;
     int searchBarSpaceHeightPx;
     int pageIndicatorHeightPx;
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index 87ef0bc..bef1f0d 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -24,6 +24,7 @@
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Build;
@@ -47,7 +48,6 @@
 import android.view.inputmethod.InputMethodManager;
 import android.widget.LinearLayout;
 import android.widget.TextView;
-
 import com.android.launcher3.FolderInfo.FolderListener;
 import com.android.launcher3.Workspace.ItemOperator;
 
@@ -220,7 +220,7 @@
                 return false;
             }
 
-            mLauncher.getWorkspace().beginDragShared(v, this);
+            mLauncher.getWorkspace().beginDragShared(v, new Point(), this, false);
 
             mCurrentDragInfo = item;
             mEmptyCellRank = item.rank;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 984d536..9c4632c 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1,4 +1,3 @@
-
 /*
  * Copyright (C) 2008 The Android Open Source Project
  *
@@ -22,7 +21,6 @@
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
-import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
 import android.app.Activity;
@@ -48,7 +46,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.database.sqlite.SQLiteDatabase;
 import android.graphics.Bitmap;
@@ -82,15 +79,12 @@
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.View.OnLongClickListener;
-import android.view.ViewAnimationUtils;
 import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.view.ViewTreeObserver;
 import android.view.Window;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.Advanceable;
 import android.widget.FrameLayout;
@@ -132,7 +126,8 @@
  */
 public class Launcher extends Activity
         implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
-                   View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener {
+                   View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener,
+                   LauncherStateTransitionAnimation.Callbacks {
     static final String TAG = "Launcher";
     static final boolean LOGD = false;
 
@@ -213,9 +208,10 @@
     public static final String USER_HAS_MIGRATED = "launcher.user_migrated_from_old_data";
 
     /** The different states that Launcher can be in. */
-    private enum State { NONE, WORKSPACE, APPS_CUSTOMIZE, APPS_CUSTOMIZE_SPRING_LOADED };
+    enum State { NONE, WORKSPACE, APPS, APPS_SPRING_LOADED, WIDGETS, WIDGETS_SPRING_LOADED };
     private State mState = State.WORKSPACE;
     private AnimatorSet mStateAnimation;
+    private LauncherStateTransitionAnimation mStateTransitionAnimation;
 
     private boolean mIsSafeModeEnabled;
 
@@ -235,7 +231,6 @@
     private static int NEW_APPS_PAGE_MOVE_DELAY = 500;
     private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
     private static int NEW_APPS_ANIMATION_DELAY = 500;
-    private static final int SINGLE_FRAME_DELAY = 16;
 
     private final BroadcastReceiver mCloseSystemDialogsReceiver
             = new CloseSystemDialogsIntentReceiver();
@@ -267,6 +262,7 @@
     private View mAllAppsButton;
 
     private SearchDropTargetBar mSearchDropTargetBar;
+    private AppsContainerView mAppsView;
     private AppsCustomizeTabHost mAppsCustomizeTabHost;
     private AppsCustomizePagedView mAppsCustomizeContent;
     private boolean mAutoAdvanceRunning = false;
@@ -305,9 +301,6 @@
 
     private View.OnTouchListener mHapticFeedbackTouchListener;
 
-    public static final int BUILD_LAYER = 0;
-    public static final int BUILD_AND_SET_LAYER = 1;
-
     // Related to the auto-advancing of widgets
     private final int ADVANCE_MSG = 1;
     private final int mAdvanceInterval = 20000;
@@ -431,6 +424,7 @@
         mIconCache.flushInvalidIcons(grid);
         mDragController = new DragController(this);
         mInflater = getLayoutInflater();
+        mStateTransitionAnimation = new LauncherStateTransitionAnimation(this, this);
 
         mStats = new Stats(this);
 
@@ -990,10 +984,12 @@
         super.onResume();
 
         // Restore the previous launcher state
-        if (mOnResumeState == State.WORKSPACE) {
+        if (mOnResumeState == State.WORKSPACE || mOnResumeState == State.NONE) {
             showWorkspace(false);
-        } else if (mOnResumeState == State.APPS_CUSTOMIZE) {
-            showAllApps(false, mAppsCustomizeContent.getContentType(), false);
+        } else if (mOnResumeState == State.APPS) {
+            showAppsView(false /* animated */, false /* resetListToTop */);
+        } else if (mOnResumeState == State.WIDGETS) {
+            showWidgetsView(false, false);
         }
         mOnResumeState = State.NONE;
 
@@ -1302,8 +1298,8 @@
         }
 
         State state = intToState(savedState.getInt(RUNTIME_STATE, State.WORKSPACE.ordinal()));
-        if (state == State.APPS_CUSTOMIZE) {
-            mOnResumeState = State.APPS_CUSTOMIZE;
+        if (state == State.APPS || state == State.WIDGETS) {
+            mOnResumeState = state;
         }
 
         int currentScreen = savedState.getInt(RUNTIME_STATE_CURRENT_SCREEN,
@@ -1429,6 +1425,9 @@
         mSearchDropTargetBar = (SearchDropTargetBar)
                 mDragLayer.findViewById(R.id.search_drop_target_bar);
 
+        // Setup Apps
+        mAppsView = (AppsContainerView) findViewById(R.id.apps_view);
+
         // Setup AppsCustomize
         mAppsCustomizeTabHost = (AppsCustomizeTabHost) findViewById(R.id.apps_customize_pane);
         mAppsCustomizeContent = (AppsCustomizePagedView)
@@ -1646,16 +1645,17 @@
             if (Intent.ACTION_SCREEN_OFF.equals(action)) {
                 mUserPresent = false;
                 mDragLayer.clearAllResizeFrames();
-                updateRunning();
+                updateAutoAdvanceState();
 
                 // Reset AllApps to its initial state only if we are not in the middle of
                 // processing a multi-step drop
-                if (mAppsCustomizeTabHost != null && mPendingAddInfo.container == ItemInfo.NO_ID) {
+                if (mAppsView != null && mAppsCustomizeTabHost != null &&
+                        mPendingAddInfo.container == ItemInfo.NO_ID) {
                     showWorkspace(false);
                 }
             } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
                 mUserPresent = true;
-                updateRunning();
+                updateAutoAdvanceState();
             } else if (ENABLE_DEBUG_INTENTS && DebugIntents.DELETE_DATABASE.equals(action)) {
                 mModel.resetLoadedState(false, true);
                 mModel.startLoader(false, PagedView.INVALID_RESTORE_PAGE,
@@ -1723,12 +1723,12 @@
             unregisterReceiver(mReceiver);
             mAttached = false;
         }
-        updateRunning();
+        updateAutoAdvanceState();
     }
 
     public void onWindowVisibilityChanged(int visibility) {
         mVisible = visibility == View.VISIBLE;
-        updateRunning();
+        updateAutoAdvanceState();
         // The following code used to be in onResume, but it turns out onResume is called when
         // you're in All Apps and click home to go to the workspace. onWindowVisibilityChanged
         // is a more appropriate event to handle
@@ -1775,7 +1775,7 @@
         mAutoAdvanceSentTime = System.currentTimeMillis();
     }
 
-    private void updateRunning() {
+    private void updateAutoAdvanceState() {
         boolean autoAdvanceRunning = mVisible && mUserPresent && !mWidgetsToAdvance.isEmpty();
         if (autoAdvanceRunning != mAutoAdvanceRunning) {
             mAutoAdvanceRunning = autoAdvanceRunning;
@@ -1821,14 +1821,14 @@
         if (v instanceof Advanceable) {
             mWidgetsToAdvance.put(hostView, appWidgetInfo);
             ((Advanceable) v).fyiWillBeAdvancedByHostKThx();
-            updateRunning();
+            updateAutoAdvanceState();
         }
     }
 
     void removeWidgetToAutoAdvance(View hostView) {
         if (mWidgetsToAdvance.containsKey(hostView)) {
             mWidgetsToAdvance.remove(hostView);
-            updateRunning();
+            updateAutoAdvanceState();
         }
     }
 
@@ -1842,14 +1842,18 @@
         Toast.makeText(this, getString(strId), Toast.LENGTH_SHORT).show();
     }
 
-    public ArrayList<AppInfo> getAllAppsList() {
-        return mAppsCustomizeContent.getApps();
-    }
-
     public DragLayer getDragLayer() {
         return mDragLayer;
     }
 
+    public AppsContainerView getAppsView() {
+        return mAppsView;
+    }
+
+    public AppsCustomizeTabHost getWidgetsView() {
+        return mAppsCustomizeTabHost;
+    }
+
     public Workspace getWorkspace() {
         return mWorkspace;
     }
@@ -1935,6 +1939,11 @@
                 imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
             }
 
+            // Reset the apps view
+            if (!alreadyOnHome && mAppsView != null) {
+                mAppsView.scrollToTop();
+            }
+
             // Reset the apps customize page
             if (!alreadyOnHome && mAppsCustomizeTabHost != null) {
                 mAppsCustomizeTabHost.reset();
@@ -2452,13 +2461,10 @@
             return;
         }
 
-        if (isAllAppsVisible()) {
-            if (mAppsCustomizeContent.getContentType() ==
-                    AppsCustomizePagedView.ContentType.Applications) {
-                showWorkspace(true);
-            } else {
-                showOverviewMode(true);
-            }
+        if (isAppsViewVisible()) {
+            showWorkspace(true);
+        } else if (isWidgetsViewVisible())  {
+            showOverviewMode(true);
         } else if (mWorkspace.isInOverviewMode()) {
             mWorkspace.exitOverviewMode(true);
         } else if (mWorkspace.getOpenFolder() != null) {
@@ -2589,10 +2595,10 @@
      */
     protected void onClickAllAppsButton(View v) {
         if (LOGD) Log.d(TAG, "onClickAllAppsButton");
-        if (isAllAppsVisible()) {
+        if (isAppsViewVisible()) {
             showWorkspace(true);
         } else {
-            showAllApps(true, AppsCustomizePagedView.ContentType.Applications, false);
+            showAppsView(true /* animated */, false /* resetListToTop */);
         }
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onClickAllAppsButton(v);
@@ -2763,7 +2769,7 @@
         if (mIsSafeModeEnabled) {
             Toast.makeText(this, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
         } else {
-            showAllApps(true, AppsCustomizePagedView.ContentType.Widgets, true);
+            showWidgetsView(true /* animated */, true /* resetPageToZero */);
             if (mLauncherCallbacks != null) {
                 mLauncherCallbacks.onClickAddWidgetButton(view);
             }
@@ -2920,12 +2926,11 @@
                     int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
                     if (v instanceof TextView) {
                         // Launch from center of icon, not entire view
-                        TextView tv = (TextView) v;
-                        Drawable[] drawables = tv.getCompoundDrawables();
-                        if (drawables != null && drawables[1] != null) {
-                            Rect bounds = drawables[1].getBounds();
+                        Drawable icon = Workspace.getTextViewIcon((TextView) v);
+                        if (icon != null) {
+                            Rect bounds = icon.getBounds();
                             left = (width - bounds.width()) / 2;
-                            top = tv.getPaddingTop();
+                            top = v.getPaddingTop();
                             width = bounds.width();
                             height = bounds.height();
                         }
@@ -3219,12 +3224,23 @@
                 return null;
             }
         } else {
-            return (CellLayout) mWorkspace.getScreenWithId(screenId);
+            return mWorkspace.getScreenWithId(screenId);
         }
     }
 
+    /**
+     * For overridden classes.
+     */
     public boolean isAllAppsVisible() {
-        return (mState == State.APPS_CUSTOMIZE) || (mOnResumeState == State.APPS_CUSTOMIZE);
+        return isAppsViewVisible();
+    }
+
+    public boolean isAppsViewVisible() {
+        return (mState == State.APPS) || (mOnResumeState == State.APPS);
+    }
+
+    public boolean isWidgetsViewVisible() {
+        return (mState == State.WIDGETS) || (mOnResumeState == State.WIDGETS);
     }
 
     private void setWorkspaceBackground(boolean workspace) {
@@ -3242,578 +3258,6 @@
         setWorkspaceBackground(visible);
     }
 
-    private void dispatchOnLauncherTransitionPrepare(View v, boolean animated, boolean toWorkspace) {
-        if (v instanceof LauncherTransitionable) {
-            ((LauncherTransitionable) v).onLauncherTransitionPrepare(this, animated, toWorkspace);
-        }
-    }
-
-    private void dispatchOnLauncherTransitionStart(View v, boolean animated, boolean toWorkspace) {
-        if (v instanceof LauncherTransitionable) {
-            ((LauncherTransitionable) v).onLauncherTransitionStart(this, animated, toWorkspace);
-        }
-
-        // Update the workspace transition step as well
-        dispatchOnLauncherTransitionStep(v, 0f);
-    }
-
-    private void dispatchOnLauncherTransitionStep(View v, float t) {
-        if (v instanceof LauncherTransitionable) {
-            ((LauncherTransitionable) v).onLauncherTransitionStep(this, t);
-        }
-    }
-
-    private void dispatchOnLauncherTransitionEnd(View v, boolean animated, boolean toWorkspace) {
-        if (v instanceof LauncherTransitionable) {
-            ((LauncherTransitionable) v).onLauncherTransitionEnd(this, animated, toWorkspace);
-        }
-
-        // Update the workspace transition step as well
-        dispatchOnLauncherTransitionStep(v, 1f);
-    }
-
-    /**
-     * Things to test when changing the following seven functions.
-     *   - Home from workspace
-     *          - from center screen
-     *          - from other screens
-     *   - Home from all apps
-     *          - from center screen
-     *          - from other screens
-     *   - Back from all apps
-     *          - from center screen
-     *          - from other screens
-     *   - Launch app from workspace and quit
-     *          - with back
-     *          - with home
-     *   - Launch app from all apps and quit
-     *          - with back
-     *          - with home
-     *   - Go to a screen that's not the default, then all
-     *     apps, and launch and app, and go back
-     *          - with back
-     *          -with home
-     *   - On workspace, long press power and go back
-     *          - with back
-     *          - with home
-     *   - On all apps, long press power and go back
-     *          - with back
-     *          - with home
-     *   - On workspace, power off
-     *   - On all apps, power off
-     *   - Launch an app and turn off the screen while in that app
-     *          - Go back with home key
-     *          - Go back with back key  TODO: make this not go to workspace
-     *          - From all apps
-     *          - From workspace
-     *   - Enter and exit car mode (becuase it causes an extra configuration changed)
-     *          - From all apps
-     *          - From the center workspace
-     *          - From another workspace
-     */
-
-    /**
-     * Zoom the camera out from the workspace to reveal 'toView'.
-     * Assumes that the view to show is anchored at either the very top or very bottom
-     * of the screen.
-     */
-    private void showAppsCustomizeHelper(final boolean animated, final boolean springLoaded) {
-        AppsCustomizePagedView.ContentType contentType = mAppsCustomizeContent.getContentType();
-        showAppsCustomizeHelper(animated, springLoaded, contentType);
-    }
-
-    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
-    private void showAppsCustomizeHelper(final boolean animated, final boolean springLoaded,
-                                         final AppsCustomizePagedView.ContentType contentType) {
-        if (mStateAnimation != null) {
-            mStateAnimation.setDuration(0);
-            mStateAnimation.cancel();
-            mStateAnimation = null;
-        }
-
-        boolean material = Utilities.isLmpOrAbove();
-
-        final Resources res = getResources();
-
-        final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime);
-        final int itemsAlphaStagger =
-                res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger);
-
-        final View fromView = mWorkspace;
-        final AppsCustomizeTabHost toView = mAppsCustomizeTabHost;
-
-        final HashMap<View, Integer> layerViews = new HashMap<View, Integer>();
-
-        Workspace.State workspaceState = contentType == AppsCustomizePagedView.ContentType.Widgets ?
-                Workspace.State.OVERVIEW_HIDDEN : Workspace.State.NORMAL_HIDDEN;
-        Animator workspaceAnim =
-                mWorkspace.getChangeStateAnimation(workspaceState, animated, layerViews);
-        if (contentType == AppsCustomizePagedView.ContentType.Widgets) {
-            // Set the content type for the all apps/widgets space
-            mAppsCustomizeTabHost.setContentTypeImmediate(contentType);
-        }
-
-        // If for some reason our views aren't initialized, don't animate
-        boolean initialized = getAllAppsButton() != null;
-
-        if (animated && initialized) {
-            mStateAnimation = LauncherAnimUtils.createAnimatorSet();
-            final AppsCustomizePagedView content = (AppsCustomizePagedView)
-                    toView.findViewById(R.id.apps_customize_pane_content);
-
-            final View page = content.getPageAt(content.getCurrentPage());
-            final View revealView = toView.findViewById(R.id.fake_page);
-
-            final boolean isWidgetTray = contentType == AppsCustomizePagedView.ContentType.Widgets;
-            if (isWidgetTray) {
-                revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark));
-            } else {
-                revealView.setBackground(res.getDrawable(R.drawable.quantum_panel));
-            }
-
-            // Hide the real page background, and swap in the fake one
-            content.setPageBackgroundsVisible(false);
-            revealView.setVisibility(View.VISIBLE);
-            // We need to hide this view as the animation start will be posted.
-            revealView.setAlpha(0);
-
-            int width = revealView.getMeasuredWidth();
-            int height = revealView.getMeasuredHeight();
-            float revealRadius = (float) Math.sqrt((width * width) / 4 + (height * height) / 4);
-
-            revealView.setTranslationY(0);
-            revealView.setTranslationX(0);
-
-            // Get the y delta between the center of the page and the center of the all apps button
-            int[] allAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
-                    getAllAppsButton(), null);
-
-            float alpha = 0;
-            float xDrift = 0;
-            float yDrift = 0;
-            if (material) {
-                alpha = isWidgetTray ? 0.3f : 1f;
-                yDrift = isWidgetTray ? height / 2 : allAppsToPanelDelta[1];
-                xDrift = isWidgetTray ? 0 : allAppsToPanelDelta[0];
-            } else {
-                yDrift = 2 * height / 3;
-                xDrift = 0;
-            }
-            final float initAlpha = alpha;
-
-            layerViews.put(revealView, BUILD_AND_SET_LAYER);
-            PropertyValuesHolder panelAlpha = PropertyValuesHolder.ofFloat("alpha", initAlpha, 1f);
-            PropertyValuesHolder panelDriftY =
-                    PropertyValuesHolder.ofFloat("translationY", yDrift, 0);
-            PropertyValuesHolder panelDriftX =
-                    PropertyValuesHolder.ofFloat("translationX", xDrift, 0);
-
-            ObjectAnimator panelAlphaAndDrift = ObjectAnimator.ofPropertyValuesHolder(revealView,
-                    panelAlpha, panelDriftY, panelDriftX);
-
-            panelAlphaAndDrift.setDuration(revealDuration);
-            panelAlphaAndDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
-
-            mStateAnimation.play(panelAlphaAndDrift);
-
-            if (page != null) {
-                page.setVisibility(View.VISIBLE);
-                layerViews.put(page, BUILD_AND_SET_LAYER);
-
-                ObjectAnimator pageDrift = ObjectAnimator.ofFloat(page, "translationY", yDrift, 0);
-                page.setTranslationY(yDrift);
-                pageDrift.setDuration(revealDuration);
-                pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
-                pageDrift.setStartDelay(itemsAlphaStagger);
-                mStateAnimation.play(pageDrift);
-
-                page.setAlpha(0f);
-                ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(page, "alpha", 0f, 1f);
-                itemsAlpha.setDuration(revealDuration);
-                itemsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
-                itemsAlpha.setStartDelay(itemsAlphaStagger);
-                mStateAnimation.play(itemsAlpha);
-            }
-
-            View pageIndicators = toView.findViewById(R.id.apps_customize_page_indicator);
-            pageIndicators.setAlpha(0.01f);
-            ObjectAnimator indicatorsAlpha =
-                    ObjectAnimator.ofFloat(pageIndicators, "alpha", 1f);
-            indicatorsAlpha.setDuration(revealDuration);
-            mStateAnimation.play(indicatorsAlpha);
-
-            if (material) {
-                final View allApps = getAllAppsButton();
-                int allAppsButtonSize = LauncherAppState.getInstance().
-                        getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize;
-                float startRadius = isWidgetTray ? 0 : allAppsButtonSize / 2;
-                Animator reveal = ViewAnimationUtils.createCircularReveal(revealView, width / 2,
-                                height / 2, startRadius, revealRadius);
-                reveal.setDuration(revealDuration);
-                reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
-
-                reveal.addListener(new AnimatorListenerAdapter() {
-                    public void onAnimationStart(Animator animation) {
-                        if (!isWidgetTray) {
-                            allApps.setVisibility(View.INVISIBLE);
-                        }
-                    }
-                    public void onAnimationEnd(Animator animation) {
-                        if (!isWidgetTray) {
-                            allApps.setVisibility(View.VISIBLE);
-                        }
-                    }
-                });
-                mStateAnimation.play(reveal);
-            }
-
-            mStateAnimation.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    dispatchOnLauncherTransitionEnd(fromView, animated, false);
-                    dispatchOnLauncherTransitionEnd(toView, animated, false);
-
-                    revealView.setVisibility(View.INVISIBLE);
-
-                    for (View v : layerViews.keySet()) {
-                        if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
-                            v.setLayerType(View.LAYER_TYPE_NONE, null);
-                        }
-                    }
-                    content.setPageBackgroundsVisible(true);
-
-                    // Hide the search bar
-                    if (mSearchDropTargetBar != null) {
-                        mSearchDropTargetBar.hideSearchBar(false);
-                    }
-
-                    // This can hold unnecessary references to views.
-                    mStateAnimation = null;
-                }
-
-            });
-
-            if (workspaceAnim != null) {
-                mStateAnimation.play(workspaceAnim);
-            }
-
-            dispatchOnLauncherTransitionPrepare(fromView, animated, false);
-            dispatchOnLauncherTransitionPrepare(toView, animated, false);
-            final AnimatorSet stateAnimation = mStateAnimation;
-            final Runnable startAnimRunnable = new Runnable() {
-                public void run() {
-                    // Check that mStateAnimation hasn't changed while
-                    // we waited for a layout/draw pass
-                    if (mStateAnimation != stateAnimation)
-                        return;
-                    dispatchOnLauncherTransitionStart(fromView, animated, false);
-                    dispatchOnLauncherTransitionStart(toView, animated, false);
-
-                    revealView.setAlpha(initAlpha);
-
-                    for (View v : layerViews.keySet()) {
-                        if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
-                            v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-                        }
-                    }
-
-                    if (Utilities.isLmpOrAbove()) {
-                        for (View v : layerViews.keySet()) {
-                            if (Utilities.isViewAttachedToWindow(v)) v.buildLayer();
-                        }
-                    }
-                    mStateAnimation.start();
-                }
-            };
-            toView.bringToFront();
-            toView.setVisibility(View.VISIBLE);
-            toView.post(startAnimRunnable);
-        } else {
-            toView.setTranslationX(0.0f);
-            toView.setTranslationY(0.0f);
-            toView.setScaleX(1.0f);
-            toView.setScaleY(1.0f);
-            toView.setVisibility(View.VISIBLE);
-            toView.bringToFront();
-
-            if (!springLoaded && !LauncherAppState.getInstance().isScreenLarge()) {
-                // Hide the search bar
-                if (mSearchDropTargetBar != null) {
-                    mSearchDropTargetBar.hideSearchBar(false);
-                }
-            }
-            dispatchOnLauncherTransitionPrepare(fromView, animated, false);
-            dispatchOnLauncherTransitionStart(fromView, animated, false);
-            dispatchOnLauncherTransitionEnd(fromView, animated, false);
-            dispatchOnLauncherTransitionPrepare(toView, animated, false);
-            dispatchOnLauncherTransitionStart(toView, animated, false);
-            dispatchOnLauncherTransitionEnd(toView, animated, false);
-        }
-    }
-
-    /**
-     * Zoom the camera back into the workspace, hiding 'fromView'.
-     * This is the opposite of showAppsCustomizeHelper.
-     * @param animated If true, the transition will be animated.
-     */
-    private void hideAppsCustomizeHelper(Workspace.State toState, final boolean animated,
-            final boolean springLoaded, final Runnable onCompleteRunnable) {
-
-        if (mStateAnimation != null) {
-            mStateAnimation.setDuration(0);
-            mStateAnimation.cancel();
-            mStateAnimation = null;
-        }
-
-        boolean material = Utilities.isLmpOrAbove();
-        Resources res = getResources();
-
-        final int revealDuration = res.getInteger(R.integer.config_appsCustomizeConcealTime);
-        final int itemsAlphaStagger =
-                res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger);
-
-        final View fromView = mAppsCustomizeTabHost;
-        final View toView = mWorkspace;
-        Animator workspaceAnim = null;
-        final HashMap<View, Integer> layerViews = new HashMap<View, Integer>();
-
-        if (toState == Workspace.State.NORMAL) {
-            workspaceAnim = mWorkspace.getChangeStateAnimation(
-                    toState, animated, layerViews);
-        } else if (toState == Workspace.State.SPRING_LOADED ||
-                toState == Workspace.State.OVERVIEW) {
-            workspaceAnim = mWorkspace.getChangeStateAnimation(
-                    toState, animated, layerViews);
-        }
-
-        // If for some reason our views aren't initialized, don't animate
-        boolean initialized = getAllAppsButton() != null;
-
-        if (animated && initialized) {
-            mStateAnimation = LauncherAnimUtils.createAnimatorSet();
-            if (workspaceAnim != null) {
-                mStateAnimation.play(workspaceAnim);
-            }
-
-            final AppsCustomizePagedView content = (AppsCustomizePagedView)
-                    fromView.findViewById(R.id.apps_customize_pane_content);
-
-            final View page = content.getPageAt(content.getNextPage());
-
-            // We need to hide side pages of the Apps / Widget tray to avoid some ugly edge cases
-            int count = content.getChildCount();
-            for (int i = 0; i < count; i++) {
-                View child = content.getChildAt(i);
-                if (child != page) {
-                    child.setVisibility(View.INVISIBLE);
-                }
-            }
-            final View revealView = fromView.findViewById(R.id.fake_page);
-
-            // hideAppsCustomizeHelper is called in some cases when it is already hidden
-            // don't perform all these no-op animations. In particularly, this was causing
-            // the all-apps button to pop in and out.
-            if (fromView.getVisibility() == View.VISIBLE) {
-                AppsCustomizePagedView.ContentType contentType = content.getContentType();
-                final boolean isWidgetTray =
-                        contentType == AppsCustomizePagedView.ContentType.Widgets;
-
-                if (isWidgetTray) {
-                    revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark));
-                } else {
-                    revealView.setBackground(res.getDrawable(R.drawable.quantum_panel));
-                }
-
-                int width = revealView.getMeasuredWidth();
-                int height = revealView.getMeasuredHeight();
-                float revealRadius = (float) Math.sqrt((width * width) / 4 + (height * height) / 4);
-
-                // Hide the real page background, and swap in the fake one
-                revealView.setVisibility(View.VISIBLE);
-                content.setPageBackgroundsVisible(false);
-
-                final View allAppsButton = getAllAppsButton();
-                revealView.setTranslationY(0);
-                int[] allAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
-                        allAppsButton, null);
-
-                float xDrift = 0;
-                float yDrift = 0;
-                if (material) {
-                    yDrift = isWidgetTray ? height / 2 : allAppsToPanelDelta[1];
-                    xDrift = isWidgetTray ? 0 : allAppsToPanelDelta[0];
-                } else {
-                    yDrift = 2 * height / 3;
-                    xDrift = 0;
-                }
-
-                layerViews.put(revealView, BUILD_AND_SET_LAYER);
-                TimeInterpolator decelerateInterpolator = material ?
-                        new LogDecelerateInterpolator(100, 0) :
-                        new DecelerateInterpolator(1f);
-
-                // The vertical motion of the apps panel should be delayed by one frame
-                // from the conceal animation in order to give the right feel. We correpsondingly
-                // shorten the duration so that the slide and conceal end at the same time.
-                ObjectAnimator panelDriftY = LauncherAnimUtils.ofFloat(revealView, "translationY",
-                        0, yDrift);
-                panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY);
-                panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
-                panelDriftY.setInterpolator(decelerateInterpolator);
-                mStateAnimation.play(panelDriftY);
-
-                ObjectAnimator panelDriftX = LauncherAnimUtils.ofFloat(revealView, "translationX",
-                        0, xDrift);
-                panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY);
-                panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
-                panelDriftX.setInterpolator(decelerateInterpolator);
-                mStateAnimation.play(panelDriftX);
-
-                if (isWidgetTray || !material) {
-                    float finalAlpha = material ? 0.4f : 0f;
-                    revealView.setAlpha(1f);
-                    ObjectAnimator panelAlpha = LauncherAnimUtils.ofFloat(revealView, "alpha",
-                            1f, finalAlpha);
-                    panelAlpha.setDuration(material ? revealDuration : 150);
-                    panelAlpha.setInterpolator(decelerateInterpolator);
-                    panelAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
-                    mStateAnimation.play(panelAlpha);
-                }
-
-                if (page != null) {
-                    layerViews.put(page, BUILD_AND_SET_LAYER);
-
-                    ObjectAnimator pageDrift = LauncherAnimUtils.ofFloat(page, "translationY",
-                            0, yDrift);
-                    page.setTranslationY(0);
-                    pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY);
-                    pageDrift.setInterpolator(decelerateInterpolator);
-                    pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
-                    mStateAnimation.play(pageDrift);
-
-                    page.setAlpha(1f);
-                    ObjectAnimator itemsAlpha = LauncherAnimUtils.ofFloat(page, "alpha", 1f, 0f);
-                    itemsAlpha.setDuration(100);
-                    itemsAlpha.setInterpolator(decelerateInterpolator);
-                    mStateAnimation.play(itemsAlpha);
-                }
-
-                View pageIndicators = fromView.findViewById(R.id.apps_customize_page_indicator);
-                pageIndicators.setAlpha(1f);
-                ObjectAnimator indicatorsAlpha =
-                        LauncherAnimUtils.ofFloat(pageIndicators, "alpha", 0f);
-                indicatorsAlpha.setDuration(revealDuration);
-                indicatorsAlpha.setInterpolator(new DecelerateInterpolator(1.5f));
-                mStateAnimation.play(indicatorsAlpha);
-
-                width = revealView.getMeasuredWidth();
-
-                if (material) {
-                    if (!isWidgetTray) {
-                        allAppsButton.setVisibility(View.INVISIBLE);
-                    }
-                    int allAppsButtonSize = LauncherAppState.getInstance().
-                            getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize;
-                    float finalRadius = isWidgetTray ? 0 : allAppsButtonSize / 2;
-                    Animator reveal =
-                            LauncherAnimUtils.createCircularReveal(revealView, width / 2,
-                                    height / 2, revealRadius, finalRadius);
-                    reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
-                    reveal.setDuration(revealDuration);
-                    reveal.setStartDelay(itemsAlphaStagger);
-
-                    reveal.addListener(new AnimatorListenerAdapter() {
-                        public void onAnimationEnd(Animator animation) {
-                            revealView.setVisibility(View.INVISIBLE);
-                            if (!isWidgetTray) {
-                                allAppsButton.setVisibility(View.VISIBLE);
-                            }
-                        }
-                    });
-
-                    mStateAnimation.play(reveal);
-                }
-
-                dispatchOnLauncherTransitionPrepare(fromView, animated, true);
-                dispatchOnLauncherTransitionPrepare(toView, animated, true);
-                mAppsCustomizeContent.stopScrolling();
-            }
-
-            mStateAnimation.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    fromView.setVisibility(View.GONE);
-                    dispatchOnLauncherTransitionEnd(fromView, animated, true);
-                    dispatchOnLauncherTransitionEnd(toView, animated, true);
-                    if (onCompleteRunnable != null) {
-                        onCompleteRunnable.run();
-                    }
-
-                    for (View v : layerViews.keySet()) {
-                        if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
-                            v.setLayerType(View.LAYER_TYPE_NONE, null);
-                        }
-                    }
-
-                    content.setPageBackgroundsVisible(true);
-                    // Unhide side pages
-                    int count = content.getChildCount();
-                    for (int i = 0; i < count; i++) {
-                        View child = content.getChildAt(i);
-                        child.setVisibility(View.VISIBLE);
-                    }
-
-                    // Reset page transforms
-                    if (page != null) {
-                        page.setTranslationX(0);
-                        page.setTranslationY(0);
-                        page.setAlpha(1);
-                    }
-                    content.setCurrentPage(content.getNextPage());
-
-                    mAppsCustomizeContent.updateCurrentPageScroll();
-
-                    // This can hold unnecessary references to views.
-                    mStateAnimation = null;
-                }
-            });
-
-            final AnimatorSet stateAnimation = mStateAnimation;
-            final Runnable startAnimRunnable = new Runnable() {
-                public void run() {
-                    // Check that mStateAnimation hasn't changed while
-                    // we waited for a layout/draw pass
-                    if (mStateAnimation != stateAnimation)
-                        return;
-                    dispatchOnLauncherTransitionStart(fromView, animated, false);
-                    dispatchOnLauncherTransitionStart(toView, animated, false);
-
-                    for (View v : layerViews.keySet()) {
-                        if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
-                            v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-                        }
-                    }
-
-                    if (Utilities.isLmpOrAbove()) {
-                        for (View v : layerViews.keySet()) {
-                            if (Utilities.isViewAttachedToWindow(v)) v.buildLayer();
-                        }
-                    }
-                    mStateAnimation.start();
-                }
-            };
-            fromView.post(startAnimRunnable);
-        } else {
-            fromView.setVisibility(View.GONE);
-            dispatchOnLauncherTransitionPrepare(fromView, animated, true);
-            dispatchOnLauncherTransitionStart(fromView, animated, true);
-            dispatchOnLauncherTransitionEnd(fromView, animated, true);
-            dispatchOnLauncherTransitionPrepare(toView, animated, true);
-            dispatchOnLauncherTransitionStart(toView, animated, true);
-            dispatchOnLauncherTransitionEnd(toView, animated, true);
-        }
-    }
-
     @Override
     public void onTrimMemory(int level) {
         super.onTrimMemory(level);
@@ -3829,19 +3273,24 @@
         }
     }
 
-    protected void showWorkspace(boolean animated) {
-        showWorkspace(animated, null);
+    @Override
+    public void onStateTransitionHideSearchBar() {
+        // Hide the search bar
+        if (mSearchDropTargetBar != null) {
+            mSearchDropTargetBar.hideSearchBar(false /* animated */);
+        }
     }
 
-    protected void showWorkspace() {
-        showWorkspace(true);
+    protected void showWorkspace(boolean animated) {
+        showWorkspace(animated, null);
     }
 
     void showWorkspace(boolean animated, Runnable onCompleteRunnable) {
         if (mState != State.WORKSPACE || mWorkspace.getState() != Workspace.State.NORMAL) {
             boolean wasInSpringLoadedMode = (mState != State.WORKSPACE);
             mWorkspace.setVisibility(View.VISIBLE);
-            hideAppsCustomizeHelper(Workspace.State.NORMAL, animated, false, onCompleteRunnable);
+            mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.NORMAL,
+                    animated, onCompleteRunnable);
 
             // Show the search bar (only animate if we were showing the drop target bar in spring
             // loaded mode)
@@ -3860,7 +3309,7 @@
 
         // Resume the auto-advance of widgets
         mUserPresent = true;
-        updateRunning();
+        updateAutoAdvanceState();
 
         // Send an accessibility event to announce the context change
         getWindow().getDecorView()
@@ -3871,7 +3320,8 @@
 
     void showOverviewMode(boolean animated) {
         mWorkspace.setVisibility(View.VISIBLE);
-        hideAppsCustomizeHelper(Workspace.State.OVERVIEW, animated, false, null);
+        mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.OVERVIEW,
+                animated, null /* onCompleteRunnable */);
         mState = State.WORKSPACE;
         onWorkspaceShown(animated);
     }
@@ -3879,14 +3329,24 @@
     public void onWorkspaceShown(boolean animated) {
     }
 
-    void showAllApps(boolean animated, AppsCustomizePagedView.ContentType contentType,
-                     boolean resetPageToZero) {
-        if (mState != State.WORKSPACE) return;
+    /**
+     * Shows the apps view.
+     */
+    void showAppsView(boolean animated, boolean resetListToTop) {
+        if (resetListToTop) {
+            mAppsView.scrollToTop();
+        }
+        showAppsOrWidgets(animated, State.APPS);
+    }
 
+    /**
+     * Shows the widgets view.
+     */
+    void showWidgetsView(boolean animated, boolean resetPageToZero) {
         if (resetPageToZero) {
             mAppsCustomizeTabHost.reset();
         }
-        showAppsCustomizeHelper(animated, false, contentType);
+        showAppsOrWidgets(animated, State.WIDGETS);
         mAppsCustomizeTabHost.post(new Runnable() {
             @Override
             public void run() {
@@ -3894,13 +3354,27 @@
                 mAppsCustomizeTabHost.requestFocus();
             }
         });
+    }
+
+    /**
+     * Sets up the transition to show the apps/widgets view.
+     */
+    private void showAppsOrWidgets(boolean animated, State toState) {
+        if (mState != State.WORKSPACE) return;
+        if (toState != State.APPS && toState != State.WIDGETS) return;
+
+        if (toState == State.APPS) {
+            mStateTransitionAnimation.startAnimationToAllApps(animated);
+        } else {
+            mStateTransitionAnimation.startAnimationToWidgets(animated);
+        }
 
         // Change the state *after* we've called all the transition code
-        mState = State.APPS_CUSTOMIZE;
+        mState = toState;
 
         // Pause the auto-advance of widgets until we are out of AllApps
         mUserPresent = false;
-        updateRunning();
+        updateAutoAdvanceState();
         closeFolder();
 
         // Send an accessibility event to announce the context change
@@ -3909,15 +3383,19 @@
     }
 
     void enterSpringLoadedDragMode() {
-        if (isAllAppsVisible()) {
-            hideAppsCustomizeHelper(Workspace.State.SPRING_LOADED, true, true, null);
-            mState = State.APPS_CUSTOMIZE_SPRING_LOADED;
+        if (mState == State.WORKSPACE || mState == State.APPS_SPRING_LOADED ||
+                mState == State.WIDGETS_SPRING_LOADED) {
+            return;
         }
+
+        mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.SPRING_LOADED,
+                true /* animated */, null /* onCompleteRunnable */);
+        mState = isAppsViewVisible() ? State.APPS_SPRING_LOADED : State.WIDGETS_SPRING_LOADED;
     }
 
     void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, int delay,
             final Runnable onCompleteRunnable) {
-        if (mState != State.APPS_CUSTOMIZE_SPRING_LOADED) return;
+        if (mState != State.APPS_SPRING_LOADED && mState != State.WIDGETS_SPRING_LOADED) return;
 
         mHandler.postDelayed(new Runnable() {
             @Override
@@ -3936,11 +3414,12 @@
     }
 
     void exitSpringLoadedDragMode() {
-        if (mState == State.APPS_CUSTOMIZE_SPRING_LOADED) {
-            final boolean animated = true;
-            final boolean springLoaded = true;
-            showAppsCustomizeHelper(animated, springLoaded);
-            mState = State.APPS_CUSTOMIZE;
+        if (mState == State.APPS_SPRING_LOADED) {
+            mStateTransitionAnimation.startAnimationToAllApps(true /* animated */);
+            mState = State.APPS;
+        } else if (mState == State.WIDGETS_SPRING_LOADED) {
+            mStateTransitionAnimation.startAnimationToWidgets(true /* animated */);
+            mState = State.WIDGETS;
         }
         // Otherwise, we are not in spring loaded mode, so don't do anything.
     }
@@ -4018,8 +3497,10 @@
         final List<CharSequence> text = event.getText();
         text.clear();
         // Populate event with a fake title based on the current state.
-        if (mState == State.APPS_CUSTOMIZE) {
-            text.add(mAppsCustomizeTabHost.getContentTag());
+        if (mState == State.APPS) {
+            text.add("Apps");
+        } else if (mState == State.WIDGETS) {
+            text.add("Widgets");
         } else {
             text.add(getString(R.string.all_apps_home_button_label));
         }
@@ -4242,8 +3723,8 @@
         // Remove the extra empty screen
         mWorkspace.removeExtraEmptyScreen(false, false);
 
-        if (addedApps != null && mAppsCustomizeContent != null) {
-            mAppsCustomizeContent.addApps(addedApps);
+        if (addedApps != null && mAppsView != null) {
+            mAppsView.addApps(addedApps);
         }
     }
 
@@ -4617,8 +4098,10 @@
      * Implementation of the method from LauncherModel.Callbacks.
      */
     public void bindAllApplications(final ArrayList<AppInfo> apps) {
+        if (mAppsView != null) {
+            mAppsView.setApps(apps);
+        }
         if (mAppsCustomizeContent != null) {
-            mAppsCustomizeContent.setApps(apps);
             mAppsCustomizeContent.onPackagesUpdated(
                     LauncherModel.getSortedWidgetsAndShortcuts(this));
         }
@@ -4642,8 +4125,8 @@
             return;
         }
 
-        if (mAppsCustomizeContent != null) {
-            mAppsCustomizeContent.updateApps(apps);
+        if (mAppsView != null) {
+            mAppsView.updateApps(apps);
         }
     }
 
@@ -4757,8 +4240,8 @@
         }
 
         // Update AllApps
-        if (mAppsCustomizeContent != null) {
-            mAppsCustomizeContent.removeApps(appInfos);
+        if (mAppsView != null) {
+            mAppsView.removeApps(appInfos);
         }
     }
 
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index cb9d12e..3cada6f 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -3308,7 +3308,7 @@
         }
     }
 
-    // Returns a list of ResolveInfos/AppWindowInfos in sorted order
+    // Returns a list of ResolveInfos/AppWidgetInfos in sorted order
     public static ArrayList<Object> getSortedWidgetsAndShortcuts(Context context) {
         PackageManager packageManager = context.getPackageManager();
         final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
new file mode 100644
index 0000000..484ed5c
--- /dev/null
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -0,0 +1,832 @@
+/*
+ * Copyright (C) 2015 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;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.TimeInterpolator;
+import android.content.res.Resources;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewAnimationUtils;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+
+import java.util.HashMap;
+
+/**
+ * TODO: figure out what kind of tests we can write for this
+ *
+ * Things to test when changing the following class.
+ *   - Home from workspace
+ *          - from center screen
+ *          - from other screens
+ *   - Home from all apps
+ *          - from center screen
+ *          - from other screens
+ *   - Back from all apps
+ *          - from center screen
+ *          - from other screens
+ *   - Launch app from workspace and quit
+ *          - with back
+ *          - with home
+ *   - Launch app from all apps and quit
+ *          - with back
+ *          - with home
+ *   - Go to a screen that's not the default, then all
+ *     apps, and launch and app, and go back
+ *          - with back
+ *          -with home
+ *   - On workspace, long press power and go back
+ *          - with back
+ *          - with home
+ *   - On all apps, long press power and go back
+ *          - with back
+ *          - with home
+ *   - On workspace, power off
+ *   - On all apps, power off
+ *   - Launch an app and turn off the screen while in that app
+ *          - Go back with home key
+ *          - Go back with back key  TODO: make this not go to workspace
+ *          - From all apps
+ *          - From workspace
+ *   - Enter and exit car mode (becuase it causes an extra configuration changed)
+ *          - From all apps
+ *          - From the center workspace
+ *          - From another workspace
+ */
+public class LauncherStateTransitionAnimation {
+
+    /**
+     * Callbacks made during the state transition
+     */
+    interface Callbacks {
+        public void onStateTransitionHideSearchBar();
+    }
+
+    /**
+     * Private callbacks made during transition setup.
+     */
+    static abstract class PrivateTransitionCallbacks {
+        void onRevealViewVisible(View revealView, View contentView, View allAppsButtonView) {}
+        void onAnimationComplete(View revealView, View contentView, View allAppsButtonView) {}
+        float getMaterialRevealViewFinalAlpha(View revealView) {
+            return 0;
+        }
+        float getMaterialRevealViewFinalXDrift(View revealView) {
+            return 0;
+        }
+        float getMaterialRevealViewFinalYDrift(View revealView) {
+            return 0;
+        }
+        float getMaterialRevealViewStartFinalRadius() {
+            return 0;
+        }
+        AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(View revealView,
+                View allAppsButtonView) {
+            return null;
+        }
+    }
+
+    public static final String TAG = "LauncherStateTransitionAnimation";
+
+    // Flags to determine how to set the layers on views before the transition animation
+    public static final int BUILD_LAYER = 0;
+    public static final int BUILD_AND_SET_LAYER = 1;
+    public static final int SINGLE_FRAME_DELAY = 16;
+
+    private Launcher mLauncher;
+    private Callbacks mCb;
+    private AnimatorSet mStateAnimation;
+
+    public LauncherStateTransitionAnimation(Launcher l, Callbacks cb) {
+        mLauncher = l;
+        mCb = cb;
+    }
+
+    /**
+     * Starts an animation to the apps view.
+     */
+    public void startAnimationToAllApps(final boolean animated) {
+        final AppsContainerView toView = mLauncher.getAppsView();
+        PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
+            private int[] mAllAppsToPanelDelta;
+
+            @Override
+            public void onRevealViewVisible(View revealView, View contentView,
+                    View allAppsButtonView) {
+                toView.setBackground(null);
+                // Get the y delta between the center of the page and the center of the all apps
+                // button
+                mAllAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
+                        allAppsButtonView, null);
+            }
+            @Override
+            public float getMaterialRevealViewFinalAlpha(View revealView) {
+                return 1f;
+            }
+            @Override
+            public float getMaterialRevealViewFinalXDrift(View revealView) {
+                return mAllAppsToPanelDelta[0];
+            }
+            @Override
+            public float getMaterialRevealViewFinalYDrift(View revealView) {
+                return mAllAppsToPanelDelta[1];
+            }
+            @Override
+            public float getMaterialRevealViewStartFinalRadius() {
+                int allAppsButtonSize = LauncherAppState.getInstance().
+                        getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize;
+                return allAppsButtonSize / 2;
+            }
+            @Override
+            public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
+                    final View revealView, final View allAppsButtonView) {
+                return new AnimatorListenerAdapter() {
+                    public void onAnimationStart(Animator animation) {
+                        allAppsButtonView.setVisibility(View.INVISIBLE);
+                    }
+                    public void onAnimationEnd(Animator animation) {
+                        allAppsButtonView.setVisibility(View.VISIBLE);
+                    }
+                };
+            }
+        };
+        startAnimationToOverlay(Workspace.State.NORMAL_HIDDEN, toView, toView.getContentView(),
+                toView.getRevealView(), null, animated, cb);
+    }
+
+    /**
+     * Starts an animation to the widgets view.
+     */
+    public void startAnimationToWidgets(final boolean animated) {
+        final AppsCustomizeTabHost toView = mLauncher.getWidgetsView();
+        PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
+            @Override
+            public void onRevealViewVisible(View revealView, View contentView,
+                    View allAppsButtonView) {
+                // Hide the real page background, and swap in the fake one
+                ((AppsCustomizePagedView) contentView).setPageBackgroundsVisible(false);
+                revealView.setBackground(mLauncher.getDrawable(R.drawable.quantum_panel_dark));
+            }
+            @Override
+            public void onAnimationComplete(View revealView, View contentView, View allAppsButtonView) {
+                // Show the real page background
+                ((AppsCustomizePagedView) contentView).setPageBackgroundsVisible(true);
+            }
+            @Override
+            public float getMaterialRevealViewFinalAlpha(View revealView) {
+                return 0.3f;
+            }
+            @Override
+            public float getMaterialRevealViewFinalYDrift(View revealView) {
+                return revealView.getMeasuredHeight() / 2;
+            }
+        };
+        startAnimationToOverlay(Workspace.State.OVERVIEW_HIDDEN, toView, toView.getContentView(),
+                toView.getRevealView(), toView.getPageIndicators(), animated, cb);
+    }
+
+    /**
+     * Starts and animation to the workspace from the current overlay view.
+     */
+    public void startAnimationToWorkspace(final Launcher.State fromState,
+              final Workspace.State toWorkspaceState, final boolean animated,
+              final Runnable onCompleteRunnable) {
+        if (toWorkspaceState != Workspace.State.NORMAL &&
+                toWorkspaceState != Workspace.State.SPRING_LOADED &&
+                toWorkspaceState != Workspace.State.OVERVIEW) {
+            Log.e(TAG, "Unexpected call to startAnimationToWorkspace");
+        }
+
+        if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED) {
+            startAnimationToWorkspaceFromAllApps(fromState, toWorkspaceState, animated,
+                    onCompleteRunnable);
+        } else {
+            startAnimationToWorkspaceFromWidgets(fromState, toWorkspaceState, animated,
+                    onCompleteRunnable);
+        }
+    }
+
+    /**
+     * Creates and starts a new animation to a particular overlay view.
+     */
+    private void startAnimationToOverlay(final Workspace.State toWorkspaceState, final View toView,
+             final View contentView, final View revealView, final View pageIndicatorsView,
+             final boolean animated, final PrivateTransitionCallbacks pCb) {
+        final Resources res = mLauncher.getResources();
+        final boolean material = Utilities.isLmpOrAbove();
+        final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime);
+        final int itemsAlphaStagger =
+                res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger);
+
+        final View allAppsButtonView = mLauncher.getAllAppsButton();
+        final View fromView = mLauncher.getWorkspace();
+
+        final HashMap<View, Integer> layerViews = new HashMap<>();
+
+        // If for some reason our views aren't initialized, don't animate
+        boolean initialized = allAppsButtonView != null;
+
+        // Cancel the current animation
+        cancelAnimation();
+
+        // Create the workspace animation.
+        // NOTE: this call apparently also sets the state for the workspace if !animated
+        Animator workspaceAnim = mLauncher.getWorkspace().getChangeStateAnimation(
+                toWorkspaceState, animated, layerViews);
+
+        if (animated && initialized) {
+            mStateAnimation = LauncherAnimUtils.createAnimatorSet();
+
+            // Setup the reveal view animation
+            int width = revealView.getMeasuredWidth();
+            int height = revealView.getMeasuredHeight();
+            float revealRadius = (float) Math.sqrt((width * width) / 4 + (height * height) / 4);
+            revealView.setVisibility(View.VISIBLE);
+            revealView.setAlpha(0f);
+            revealView.setTranslationY(0f);
+            revealView.setTranslationX(0f);
+            pCb.onRevealViewVisible(revealView, contentView, allAppsButtonView);
+
+            // Calculate the final animation values
+            final float revealViewToAlpha;
+            final float revealViewToXDrift;
+            final float revealViewToYDrift;
+            if (material) {
+                revealViewToAlpha = pCb.getMaterialRevealViewFinalAlpha(revealView);
+                revealViewToYDrift = pCb.getMaterialRevealViewFinalYDrift(revealView);
+                revealViewToXDrift = pCb.getMaterialRevealViewFinalXDrift(revealView);
+            } else {
+                revealViewToAlpha = 0f;
+                revealViewToYDrift = 2 * height / 3;
+                revealViewToXDrift = 0;
+            }
+
+            // Create the animators
+            PropertyValuesHolder panelAlpha =
+                    PropertyValuesHolder.ofFloat("alpha", revealViewToAlpha, 1f);
+            PropertyValuesHolder panelDriftY =
+                    PropertyValuesHolder.ofFloat("translationY", revealViewToYDrift, 0);
+            PropertyValuesHolder panelDriftX =
+                    PropertyValuesHolder.ofFloat("translationX", revealViewToXDrift, 0);
+            ObjectAnimator panelAlphaAndDrift = ObjectAnimator.ofPropertyValuesHolder(revealView,
+                    panelAlpha, panelDriftY, panelDriftX);
+            panelAlphaAndDrift.setDuration(revealDuration);
+            panelAlphaAndDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
+
+            // Play the animation
+            layerViews.put(revealView, BUILD_AND_SET_LAYER);
+            mStateAnimation.play(panelAlphaAndDrift);
+
+            // Setup the animation for the page indicators
+            if (pageIndicatorsView != null) {
+                pageIndicatorsView.setAlpha(0.01f);
+                ObjectAnimator indicatorsAlpha =
+                        ObjectAnimator.ofFloat(pageIndicatorsView, "alpha", 1f);
+                indicatorsAlpha.setDuration(revealDuration);
+                mStateAnimation.play(indicatorsAlpha);
+            }
+
+            // Setup the animation for the content view
+            contentView.setVisibility(View.VISIBLE);
+            contentView.setAlpha(0f);
+            contentView.setTranslationY(revealViewToYDrift);
+            layerViews.put(contentView, BUILD_AND_SET_LAYER);
+
+            // Create the individual animators
+            ObjectAnimator pageDrift = ObjectAnimator.ofFloat(contentView, "translationY",
+                    revealViewToYDrift, 0);
+            pageDrift.setDuration(revealDuration);
+            pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
+            pageDrift.setStartDelay(itemsAlphaStagger);
+            mStateAnimation.play(pageDrift);
+
+            ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 0f, 1f);
+            itemsAlpha.setDuration(revealDuration);
+            itemsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
+            itemsAlpha.setStartDelay(itemsAlphaStagger);
+            mStateAnimation.play(itemsAlpha);
+
+            if (material) {
+                // Animate the all apps button
+                float startRadius = pCb.getMaterialRevealViewStartFinalRadius();
+                AnimatorListenerAdapter listener = pCb.getMaterialRevealViewAnimatorListener(
+                        revealView, allAppsButtonView);
+                Animator reveal = ViewAnimationUtils.createCircularReveal(revealView, width / 2,
+                        height / 2, startRadius, revealRadius);
+                reveal.setDuration(revealDuration);
+                reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
+                if (listener != null) {
+                    reveal.addListener(listener);
+                }
+                mStateAnimation.play(reveal);
+            }
+
+            mStateAnimation.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    dispatchOnLauncherTransitionEnd(fromView, animated, false);
+                    dispatchOnLauncherTransitionEnd(toView, animated, false);
+
+                    // Hide the reveal view
+                    revealView.setVisibility(View.INVISIBLE);
+                    pCb.onAnimationComplete(revealView, contentView, allAppsButtonView);
+
+                    // Disable all necessary layers
+                    for (View v : layerViews.keySet()) {
+                        if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
+                            v.setLayerType(View.LAYER_TYPE_NONE, null);
+                        }
+                    }
+
+                    // Hide the search bar
+                    mCb.onStateTransitionHideSearchBar();
+
+                    // This can hold unnecessary references to views.
+                    mStateAnimation = null;
+                }
+
+            });
+
+            // Play the workspace animation
+            if (workspaceAnim != null) {
+                mStateAnimation.play(workspaceAnim);
+            }
+
+            // Dispatch the prepare transition signal
+            dispatchOnLauncherTransitionPrepare(fromView, animated, false);
+            dispatchOnLauncherTransitionPrepare(toView, animated, false);
+
+
+            final AnimatorSet stateAnimation = mStateAnimation;
+            final Runnable startAnimRunnable = new Runnable() {
+                public void run() {
+                    // Check that mStateAnimation hasn't changed while
+                    // we waited for a layout/draw pass
+                    if (mStateAnimation != stateAnimation)
+                        return;
+                    dispatchOnLauncherTransitionStart(fromView, animated, false);
+                    dispatchOnLauncherTransitionStart(toView, animated, false);
+
+                    // Enable all necessary layers
+                    for (View v : layerViews.keySet()) {
+                        if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
+                            v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+                        }
+                        if (Utilities.isViewAttachedToWindow(v)) {
+                            v.buildLayer();
+                        }
+                    }
+
+                    // Focus the new view
+                    toView.requestFocus();
+
+                    mStateAnimation.start();
+                }
+            };
+
+            toView.bringToFront();
+            toView.setVisibility(View.VISIBLE);
+            toView.post(startAnimRunnable);
+        } else {
+            toView.setTranslationX(0.0f);
+            toView.setTranslationY(0.0f);
+            toView.setScaleX(1.0f);
+            toView.setScaleY(1.0f);
+            toView.setVisibility(View.VISIBLE);
+            toView.bringToFront();
+
+            // Show the content view
+            contentView.setVisibility(View.VISIBLE);
+
+            // Hide the search bar
+            mCb.onStateTransitionHideSearchBar();
+
+            dispatchOnLauncherTransitionPrepare(fromView, animated, false);
+            dispatchOnLauncherTransitionStart(fromView, animated, false);
+            dispatchOnLauncherTransitionEnd(fromView, animated, false);
+            dispatchOnLauncherTransitionPrepare(toView, animated, false);
+            dispatchOnLauncherTransitionStart(toView, animated, false);
+            dispatchOnLauncherTransitionEnd(toView, animated, false);
+        }
+    }
+
+    /**
+     * Starts and animation to the workspace from the apps view.
+     */
+    private void startAnimationToWorkspaceFromAllApps(final Launcher.State fromState,
+              final Workspace.State toWorkspaceState, final boolean animated,
+              final Runnable onCompleteRunnable) {
+        AppsContainerView appsView = mLauncher.getAppsView();
+        PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
+            int[] mAllAppsToPanelDelta;
+
+            @Override
+            public void onRevealViewVisible(View revealView, View contentView,
+                                            View allAppsButtonView) {
+                // Get the y delta between the center of the page and the center of the all apps
+                // button
+                mAllAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
+                        allAppsButtonView, null);
+            }
+            @Override
+            public float getMaterialRevealViewFinalXDrift(View revealView) {
+                return mAllAppsToPanelDelta[0];
+            }
+            @Override
+            public float getMaterialRevealViewFinalYDrift(View revealView) {
+                return mAllAppsToPanelDelta[1];
+            }
+            @Override
+            float getMaterialRevealViewFinalAlpha(View revealView) {
+                // No alpha anim from all apps
+                return 1f;
+            }
+            @Override
+            float getMaterialRevealViewStartFinalRadius() {
+                int allAppsButtonSize = LauncherAppState.getInstance().
+                        getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize;
+                return allAppsButtonSize / 2;
+            }
+            @Override
+            public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
+                    final View revealView, final View allAppsButtonView) {
+                return new AnimatorListenerAdapter() {
+                    public void onAnimationStart(Animator animation) {
+                        // We set the alpha instead of visibility to ensure that the focus does not
+                        // get taken from the all apps view
+                        allAppsButtonView.setVisibility(View.VISIBLE);
+                        allAppsButtonView.setAlpha(0f);
+                    }
+                    public void onAnimationEnd(Animator animation) {
+                        // Hide the reveal view
+                        revealView.setVisibility(View.INVISIBLE);
+
+                        // Show the all apps button, and focus it
+                        allAppsButtonView.setAlpha(1f);
+                    }
+                };
+            }
+        };
+        startAnimationToWorkspaceFromOverlay(toWorkspaceState, appsView, appsView.getContentView(),
+                appsView.getRevealView(), null /* pageIndicatorsView */, animated,
+                onCompleteRunnable, cb);
+    }
+
+    /**
+     * Starts and animation to the workspace from the widgets view.
+     */
+    private void startAnimationToWorkspaceFromWidgets(final Launcher.State fromState,
+              final Workspace.State toWorkspaceState, final boolean animated,
+              final Runnable onCompleteRunnable) {
+        AppsCustomizeTabHost widgetsView = mLauncher.getWidgetsView();
+        PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
+            @Override
+            public void onRevealViewVisible(View revealView, View contentView, View allAppsButtonView) {
+                AppsCustomizePagedView pagedView = ((AppsCustomizePagedView) contentView);
+
+                // Hide the real page background, and swap in the fake one
+                pagedView.stopScrolling();
+                pagedView.setPageBackgroundsVisible(false);
+                revealView.setBackground(mLauncher.getDrawable(R.drawable.quantum_panel_dark));
+
+                // Hide the side pages of the Widget tray to avoid some ugly edge cases
+                final View currentPage = pagedView.getPageAt(pagedView.getNextPage());
+                int count = pagedView.getChildCount();
+                for (int i = 0; i < count; i++) {
+                    View child = pagedView.getChildAt(i);
+                    if (child != currentPage) {
+                        child.setVisibility(View.INVISIBLE);
+                    }
+                }
+            }
+            @Override
+            public void onAnimationComplete(View revealView, View contentView, View allAppsButtonView) {
+                AppsCustomizePagedView pagedView = ((AppsCustomizePagedView) contentView);
+
+                // Show the real page background and force-update the page
+                pagedView.setPageBackgroundsVisible(true);
+                pagedView.setCurrentPage(pagedView.getNextPage());
+                pagedView.updateCurrentPageScroll();
+
+                // Unhide the side pages
+                int count = pagedView.getChildCount();
+                for (int i = 0; i < count; i++) {
+                    View child = pagedView.getChildAt(i);
+                    child.setVisibility(View.VISIBLE);
+                }
+            }
+            @Override
+            public float getMaterialRevealViewFinalYDrift(View revealView) {
+                return revealView.getMeasuredHeight() / 2;
+            }
+            @Override
+            float getMaterialRevealViewFinalAlpha(View revealView) {
+                return 0.4f;
+            }
+            @Override
+            public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
+                    final View revealView, final View allAppsButtonView) {
+                return new AnimatorListenerAdapter() {
+                    public void onAnimationEnd(Animator animation) {
+                        // Hide the reveal view
+                        revealView.setVisibility(View.INVISIBLE);
+                    }
+                };
+            }
+        };
+        startAnimationToWorkspaceFromOverlay(toWorkspaceState, widgetsView,
+                widgetsView.getContentView(), widgetsView.getRevealView(),
+                widgetsView.getPageIndicators(), animated, onCompleteRunnable, cb);
+    }
+
+    /**
+     * Creates and starts a new animation to the workspace.
+     */
+    private void startAnimationToWorkspaceFromOverlay(final Workspace.State toWorkspaceState,
+              final View fromView, final View contentView, final View revealView,
+              final View pageIndicatorsView, final boolean animated,
+              final Runnable onCompleteRunnable, final PrivateTransitionCallbacks pCb) {
+        final Resources res = mLauncher.getResources();
+        final boolean material = Utilities.isLmpOrAbove();
+        final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime);
+        final int itemsAlphaStagger =
+                res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger);
+
+        final View allAppsButtonView = mLauncher.getAllAppsButton();
+        final View toView = mLauncher.getWorkspace();
+
+        final HashMap<View, Integer> layerViews = new HashMap<>();
+
+        // If for some reason our views aren't initialized, don't animate
+        boolean initialized = allAppsButtonView != null;
+
+        // Cancel the current animation
+        cancelAnimation();
+
+        // Create the workspace animation.
+        // NOTE: this call apparently also sets the state for the workspace if !animated
+        Animator workspaceAnim = mLauncher.getWorkspace().getChangeStateAnimation(
+                toWorkspaceState, animated, layerViews);
+
+        if (animated && initialized) {
+            mStateAnimation = LauncherAnimUtils.createAnimatorSet();
+
+            // Play the workspace animation
+            if (workspaceAnim != null) {
+                mStateAnimation.play(workspaceAnim);
+            }
+
+            // hideAppsCustomizeHelper is called in some cases when it is already hidden
+            // don't perform all these no-op animations. In particularly, this was causing
+            // the all-apps button to pop in and out.
+            if (fromView.getVisibility() == View.VISIBLE) {
+                int width = revealView.getMeasuredWidth();
+                int height = revealView.getMeasuredHeight();
+                float revealRadius = (float) Math.sqrt((width * width) / 4 + (height * height) / 4);
+                revealView.setVisibility(View.VISIBLE);
+                revealView.setAlpha(1f);
+                revealView.setTranslationY(0);
+                layerViews.put(revealView, BUILD_AND_SET_LAYER);
+                pCb.onRevealViewVisible(revealView, contentView, allAppsButtonView);
+
+                // Calculate the final animation values
+                final float revealViewToXDrift;
+                final float revealViewToYDrift;
+                if (material) {
+                    revealViewToYDrift = pCb.getMaterialRevealViewFinalYDrift(revealView);
+                    revealViewToXDrift = pCb.getMaterialRevealViewFinalXDrift(revealView);
+                } else {
+                    revealViewToYDrift = 2 * height / 3;
+                    revealViewToXDrift = 0;
+                }
+
+                // The vertical motion of the apps panel should be delayed by one frame
+                // from the conceal animation in order to give the right feel. We correspondingly
+                // shorten the duration so that the slide and conceal end at the same time.
+                TimeInterpolator decelerateInterpolator = material ?
+                        new LogDecelerateInterpolator(100, 0) :
+                        new DecelerateInterpolator(1f);
+                ObjectAnimator panelDriftY = LauncherAnimUtils.ofFloat(revealView, "translationY",
+                        0, revealViewToYDrift);
+                panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY);
+                panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
+                panelDriftY.setInterpolator(decelerateInterpolator);
+                mStateAnimation.play(panelDriftY);
+
+                ObjectAnimator panelDriftX = LauncherAnimUtils.ofFloat(revealView, "translationX",
+                        0, revealViewToXDrift);
+                panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY);
+                panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
+                panelDriftX.setInterpolator(decelerateInterpolator);
+                mStateAnimation.play(panelDriftX);
+
+                // Setup animation for the reveal panel alpha
+                final float revealViewToAlpha = !material ? 0f :
+                        pCb.getMaterialRevealViewFinalAlpha(revealView);
+                if (revealViewToAlpha != 1f) {
+                    ObjectAnimator panelAlpha = LauncherAnimUtils.ofFloat(revealView, "alpha",
+                            1f, revealViewToAlpha);
+                    panelAlpha.setDuration(material ? revealDuration : 150);
+                    panelAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
+                    panelAlpha.setInterpolator(decelerateInterpolator);
+                    mStateAnimation.play(panelAlpha);
+                }
+
+                // Setup the animation for the content view
+                layerViews.put(contentView, BUILD_AND_SET_LAYER);
+
+                // Create the individual animators
+                ObjectAnimator pageDrift = LauncherAnimUtils.ofFloat(contentView, "translationY",
+                        0, revealViewToYDrift);
+                contentView.setTranslationY(0);
+                pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY);
+                pageDrift.setInterpolator(decelerateInterpolator);
+                pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
+                mStateAnimation.play(pageDrift);
+
+                contentView.setAlpha(1f);
+                ObjectAnimator itemsAlpha = LauncherAnimUtils.ofFloat(contentView, "alpha", 1f, 0f);
+                itemsAlpha.setDuration(100);
+                itemsAlpha.setInterpolator(decelerateInterpolator);
+                mStateAnimation.play(itemsAlpha);
+
+                // Setup the page indicators animation
+                if (pageIndicatorsView != null) {
+                    pageIndicatorsView.setAlpha(1f);
+                    ObjectAnimator indicatorsAlpha =
+                            LauncherAnimUtils.ofFloat(pageIndicatorsView, "alpha", 0f);
+                    indicatorsAlpha.setDuration(revealDuration);
+                    indicatorsAlpha.setInterpolator(new DecelerateInterpolator(1.5f));
+                    mStateAnimation.play(indicatorsAlpha);
+                }
+
+                if (material) {
+                    // Animate the all apps button
+                    float finalRadius = pCb.getMaterialRevealViewStartFinalRadius();
+                    AnimatorListenerAdapter listener =
+                            pCb.getMaterialRevealViewAnimatorListener(revealView, allAppsButtonView);
+                    Animator reveal =
+                            LauncherAnimUtils.createCircularReveal(revealView, width / 2,
+                                    height / 2, revealRadius, finalRadius);
+                    reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
+                    reveal.setDuration(revealDuration);
+                    reveal.setStartDelay(itemsAlphaStagger);
+                    if (listener != null) {
+                        reveal.addListener(listener);
+                    }
+                    mStateAnimation.play(reveal);
+                }
+
+                dispatchOnLauncherTransitionPrepare(fromView, animated, true);
+                dispatchOnLauncherTransitionPrepare(toView, animated, true);
+            }
+
+            mStateAnimation.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    fromView.setVisibility(View.GONE);
+                    dispatchOnLauncherTransitionEnd(fromView, animated, true);
+                    dispatchOnLauncherTransitionEnd(toView, animated, true);
+
+                    // Run any queued runnables
+                    if (onCompleteRunnable != null) {
+                        onCompleteRunnable.run();
+                    }
+
+                    // Animation complete callback
+                    pCb.onAnimationComplete(revealView, contentView, allAppsButtonView);
+
+                    // Disable all necessary layers
+                    for (View v : layerViews.keySet()) {
+                        if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
+                            v.setLayerType(View.LAYER_TYPE_NONE, null);
+                        }
+                    }
+
+                    // Reset page transforms
+                    if (contentView != null) {
+                        contentView.setTranslationX(0);
+                        contentView.setTranslationY(0);
+                        contentView.setAlpha(1);
+                    }
+
+                    // This can hold unnecessary references to views.
+                    mStateAnimation = null;
+                }
+            });
+
+            final AnimatorSet stateAnimation = mStateAnimation;
+            final Runnable startAnimRunnable = new Runnable() {
+                public void run() {
+                    // Check that mStateAnimation hasn't changed while
+                    // we waited for a layout/draw pass
+                    if (mStateAnimation != stateAnimation)
+                        return;
+                    dispatchOnLauncherTransitionStart(fromView, animated, false);
+                    dispatchOnLauncherTransitionStart(toView, animated, false);
+
+                    // Enable all necessary layers
+                    for (View v : layerViews.keySet()) {
+                        if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
+                            v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+                        }
+                        if (Utilities.isLmpOrAbove()) {
+                            v.buildLayer();
+                        }
+                    }
+                    mStateAnimation.start();
+                }
+            };
+            fromView.post(startAnimRunnable);
+        } else {
+            fromView.setVisibility(View.GONE);
+            dispatchOnLauncherTransitionPrepare(fromView, animated, true);
+            dispatchOnLauncherTransitionStart(fromView, animated, true);
+            dispatchOnLauncherTransitionEnd(fromView, animated, true);
+            dispatchOnLauncherTransitionPrepare(toView, animated, true);
+            dispatchOnLauncherTransitionStart(toView, animated, true);
+            dispatchOnLauncherTransitionEnd(toView, animated, true);
+
+            // Run any queued runnables
+            if (onCompleteRunnable != null) {
+                onCompleteRunnable.run();
+            }
+        }
+    }
+
+
+    /**
+     * Dispatches the prepare-transition event to suitable views.
+     */
+    void dispatchOnLauncherTransitionPrepare(View v, boolean animated, boolean toWorkspace) {
+        if (v instanceof LauncherTransitionable) {
+            ((LauncherTransitionable) v).onLauncherTransitionPrepare(mLauncher, animated,
+                    toWorkspace);
+        }
+    }
+
+    /**
+     * Dispatches the start-transition event to suitable views.
+     */
+    void dispatchOnLauncherTransitionStart(View v, boolean animated, boolean toWorkspace) {
+        if (v instanceof LauncherTransitionable) {
+            ((LauncherTransitionable) v).onLauncherTransitionStart(mLauncher, animated,
+                    toWorkspace);
+        }
+
+        // Update the workspace transition step as well
+        dispatchOnLauncherTransitionStep(v, 0f);
+    }
+
+    /**
+     * Dispatches the step-transition event to suitable views.
+     */
+    void dispatchOnLauncherTransitionStep(View v, float t) {
+        if (v instanceof LauncherTransitionable) {
+            ((LauncherTransitionable) v).onLauncherTransitionStep(mLauncher, t);
+        }
+    }
+
+    /**
+     * Dispatches the end-transition event to suitable views.
+     */
+    void dispatchOnLauncherTransitionEnd(View v, boolean animated, boolean toWorkspace) {
+        if (v instanceof LauncherTransitionable) {
+            ((LauncherTransitionable) v).onLauncherTransitionEnd(mLauncher, animated,
+                    toWorkspace);
+        }
+
+        // Update the workspace transition step as well
+        dispatchOnLauncherTransitionStep(v, 1f);
+    }
+
+    /**
+     * Cancels the current animation.
+     */
+    private void cancelAnimation() {
+        if (mStateAnimation != null) {
+            mStateAnimation.setDuration(0);
+            mStateAnimation.cancel();
+            mStateAnimation = null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/PagedViewWithDraggableItems.java b/src/com/android/launcher3/PagedViewWithDraggableItems.java
index 0e59369..f0743cf 100644
--- a/src/com/android/launcher3/PagedViewWithDraggableItems.java
+++ b/src/com/android/launcher3/PagedViewWithDraggableItems.java
@@ -109,7 +109,7 @@
         // Return early if we are still animating the pages
         if (mNextPage != INVALID_PAGE) return false;
         // When we have exited all apps or are in transition, disregard long clicks
-        if (!mLauncher.isAllAppsVisible() ||
+        if (!mLauncher.isWidgetsViewVisible() ||
                 mLauncher.getWorkspace().isSwitchingState()) return false;
         // Return if global dragging is not enabled
         if (!mLauncher.isDraggingEnabled()) return false;
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index d963f2d..3128140 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -131,8 +131,8 @@
     private final PaintCache mDefaultAppWidgetPreviewPaint = new PaintCache();
     private final BitmapFactoryOptionsCache mCachedBitmapFactoryOptions = new BitmapFactoryOptionsCache();
 
-    private final HashMap<String, WeakReference<Bitmap>> mLoadedPreviews = new HashMap<String, WeakReference<Bitmap>>();
-    private final ArrayList<SoftReference<Bitmap>> mUnusedBitmaps = new ArrayList<SoftReference<Bitmap>>();
+    private final HashMap<String, WeakReference<Bitmap>> mLoadedPreviews = new HashMap<>();
+    private final ArrayList<SoftReference<Bitmap>> mUnusedBitmaps = new ArrayList<>();
 
     private final Context mContext;
     private final int mAppIconSize;
diff --git a/src/com/android/launcher3/WidgetsContainerView.java b/src/com/android/launcher3/WidgetsContainerView.java
new file mode 100644
index 0000000..d0dd733
--- /dev/null
+++ b/src/com/android/launcher3/WidgetsContainerView.java
@@ -0,0 +1,88 @@
+package com.android.launcher3;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+
+class SectionedWidgetsRow {
+    String section;
+    List<List<Object>> widgets;
+
+    public SectionedWidgetsRow(String sc) {
+        section = sc;
+    }
+}
+
+class SectionedWidgetsAlgorithm {
+    public List<SectionedWidgetsRow> computeSectionedWidgetRows(List<Object> sortedWidgets,
+            int widgetsPerRow) {
+        List<SectionedWidgetsRow> rows = new ArrayList<>();
+        LinkedHashMap<String, List<Object>> sections = computeSectionedApps(sortedWidgets);
+        for (Map.Entry<String, List<Object>> sectionEntry : sections.entrySet()) {
+            String section = sectionEntry.getKey();
+            SectionedWidgetsRow row = new SectionedWidgetsRow(section);
+            List<Object> widgets = sectionEntry.getValue();
+            int numRows = (int) Math.ceil((float) widgets.size() / widgetsPerRow);
+            for (int i = 0; i < numRows; i++) {
+                List<Object> widgetsInRow = new ArrayList<>();
+                int offset = i * widgetsPerRow;
+                for (int j = 0; j < widgetsPerRow; j++) {
+                    widgetsInRow.add(widgets.get(offset + j));
+                }
+                row.widgets.add(widgetsInRow);
+            }
+        }
+        return rows;
+    }
+
+    private LinkedHashMap<String, List<Object>> computeSectionedApps(List<Object> sortedWidgets) {
+        LinkedHashMap<String, List<Object>> sections = new LinkedHashMap<>();
+        for (Object info : sortedWidgets) {
+            String section = getSection(info);
+            List<Object> sectionedWidgets = sections.get(section);
+            if (sectionedWidgets == null) {
+                sectionedWidgets = new ArrayList<>();
+                sections.put(section, sectionedWidgets);
+            }
+            sectionedWidgets.add(info);
+        }
+        return sections;
+    }
+
+    private String getSection(Object widgetOrShortcut) {
+        return "UNKNOWN";
+    }
+}
+
+/**
+ * The widgets list view container.
+ */
+public class WidgetsContainerView extends FrameLayout {
+
+
+    public WidgetsContainerView(Context context) {
+        this(context, null);
+    }
+
+    public WidgetsContainerView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+    }
+}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 3b293f9..a59e25e 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1528,7 +1528,7 @@
     @Override
     public void announceForAccessibility(CharSequence text) {
         // Don't announce if apps is on top of us.
-        if (!mLauncher.isAllAppsVisible()) {
+        if (!mLauncher.isAppsViewVisible()) {
             super.announceForAccessibility(text);
         }
     }
@@ -1816,7 +1816,7 @@
 
     @Override
     protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
-        if (!mLauncher.isAllAppsVisible()) {
+        if (!mLauncher.isAppsViewVisible()) {
             final Folder openFolder = getOpenFolder();
             if (openFolder != null) {
                 return openFolder.requestFocus(direction, previouslyFocusedRect);
@@ -1837,7 +1837,7 @@
 
     @Override
     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
-        if (!mLauncher.isAllAppsVisible()) {
+        if (!mLauncher.isAppsViewVisible()) {
             final Folder openFolder = getOpenFolder();
             if (openFolder != null) {
                 openFolder.addFocusables(views, direction);
@@ -2046,8 +2046,7 @@
 
         // If this is a text view, use its drawable instead
         if (v instanceof TextView) {
-            TextView tv = (TextView) v;
-            Drawable d = tv.getCompoundDrawables()[1];
+            Drawable d = getTextViewIcon((TextView) v);
             Rect bounds = getDrawableBounds(d);
             bmpWidth = bounds.width();
             bmpHeight = bounds.height();
@@ -2348,7 +2347,7 @@
                     cl.setShortcutAndWidgetAlpha(mNewAlphas[i]);
                 } else {
                     if (layerViews != null) {
-                        layerViews.put(cl, Launcher.BUILD_LAYER);
+                        layerViews.put(cl, LauncherStateTransitionAnimation.BUILD_LAYER);
                     }
                     if (mOldAlphas[i] != mNewAlphas[i] || currentAlpha != mNewAlphas[i]) {
                         LauncherViewPropertyAnimator alphaAnim =
@@ -2400,8 +2399,8 @@
             if (layerViews != null) {
                 // If layerViews is not null, we add these views, and indicate that
                 // the caller can manage layer state.
-                layerViews.put(hotseat, Launcher.BUILD_AND_SET_LAYER);
-                layerViews.put(overviewPanel, Launcher.BUILD_AND_SET_LAYER);
+                layerViews.put(hotseat, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
+                layerViews.put(overviewPanel, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
             } else {
                 // Otherwise let the animator handle layer management.
                 hotseatAlpha.withLayer();
@@ -2430,7 +2429,7 @@
                 if (layerViews != null) {
                     // If layerViews is not null, we add these views, and indicate that
                     // the caller can manage layer state.
-                    layerViews.put(searchBar, Launcher.BUILD_AND_SET_LAYER);
+                    layerViews.put(searchBar, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
                 } else {
                     // Otherwise let the animator handle layer management.
                     searchBarAlpha.withLayer();
@@ -2584,6 +2583,19 @@
     }
 
     /**
+     * Returns the drawable for the given text view.
+     */
+    public static Drawable getTextViewIcon(TextView tv) {
+        final Drawable[] drawables = tv.getCompoundDrawables();
+        for (int i = 0; i < drawables.length; i++) {
+            if (drawables[i] != null) {
+                return drawables[i];
+            }
+        }
+        return null;
+    }
+
+    /**
      * Draw the View v into the given Canvas.
      *
      * @param v the view to draw
@@ -2598,7 +2610,7 @@
 
         destCanvas.save();
         if (v instanceof TextView) {
-            Drawable d = ((TextView) v).getCompoundDrawables()[1];
+            Drawable d = getTextViewIcon((TextView) v);
             Rect bounds = getDrawableBounds(d);
             clipRect.set(0, 0, bounds.width() + padding, bounds.height() + padding);
             destCanvas.translate(padding / 2 - bounds.left, padding / 2 - bounds.top);
@@ -2635,7 +2647,7 @@
 
         int padding = expectedPadding.get();
         if (v instanceof TextView) {
-            Drawable d = ((TextView) v).getCompoundDrawables()[1];
+            Drawable d = getTextViewIcon((TextView) v);
             Rect bounds = getDrawableBounds(d);
             b = Bitmap.createBitmap(bounds.width() + padding,
                     bounds.height() + padding, Bitmap.Config.ARGB_8888);
@@ -2716,11 +2728,12 @@
         beginDragShared(child, this, accessible);
     }
 
-    public void beginDragShared(View child, DragSource source) {
-        beginDragShared(child, source, false);
+    public void beginDragShared(View child, DragSource source, boolean accessible) {
+        beginDragShared(child, new Point(), source, accessible);
     }
 
-    public void beginDragShared(View child, DragSource source, boolean accessible) {
+    public void beginDragShared(View child, Point relativeTouchPos, DragSource source,
+            boolean accessible) {
         child.clearFocus();
         child.setPressed(false);
 
@@ -2745,11 +2758,23 @@
         Point dragVisualizeOffset = null;
         Rect dragRect = null;
         if (child instanceof BubbleTextView) {
+            BubbleTextView icon = (BubbleTextView) child;
             int iconSize = grid.iconSizePx;
             int top = child.getPaddingTop();
             int left = (bmpWidth - iconSize) / 2;
             int right = left + iconSize;
             int bottom = top + iconSize;
+            if (icon.isLayoutHorizontal()) {
+                // If the layout is horizontal, then if we are just picking up the icon, then just
+                // use the child position since the icon is top-left aligned.  Otherwise, offset
+                // the drag layer position horizontally so that the icon is under the current
+                // touch position.
+                if (icon.getIcon().getBounds().contains(relativeTouchPos.x, relativeTouchPos.y)) {
+                    dragLayerX = Math.round(mTempXY[0]);
+                } else {
+                    dragLayerX = Math.round(mTempXY[0] + relativeTouchPos.x - (bmpWidth / 2));
+                }
+            }
             dragLayerY += top;
             // Note: The drag region is used to calculate drag layer offsets, but the
             // dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
@@ -2831,18 +2856,6 @@
         tmpB.recycle();
     }
 
-    void addApplicationShortcut(ShortcutInfo info, CellLayout target, long container, long screenId,
-            int cellX, int cellY, boolean insertAtFirst, int intersectX, int intersectY) {
-        View view = mLauncher.createShortcut(R.layout.application, target, (ShortcutInfo) info);
-
-        final int[] cellXY = new int[2];
-        target.findCellForSpanThatIntersects(cellXY, 1, 1, intersectX, intersectY);
-        addInScreen(view, container, screenId, cellXY[0], cellXY[1], 1, 1, insertAtFirst);
-
-        LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screenId, cellXY[0],
-                cellXY[1]);
-    }
-
     public boolean transitionStateShouldAllowDrop() {
         return ((!isSwitchingState() || mTransitionProgress > 0.5f) &&
                 (mState == State.NORMAL || mState == State.SPRING_LOADED));
@@ -4769,7 +4782,7 @@
                         updates.contains(info)) {
                     ShortcutInfo si = (ShortcutInfo) info;
                     BubbleTextView shortcut = (BubbleTextView) v;
-                    boolean oldPromiseState = shortcut.getCompoundDrawables()[1]
+                    boolean oldPromiseState = getTextViewIcon(shortcut)
                             instanceof PreloadIconDrawable;
                     shortcut.applyFromShortcutInfo(si, mIconCache, true,
                             si.isPromise() != oldPromiseState);