Explorations in dense grids for all apps.

- Adds sticky section headers
- Removing AppsListAdapter
- Adding search bar field
- Subtitle filtering

Bug: 20222023

Change-Id: I1eaef701b5d68f475615f09d86561eacc91c937f
diff --git a/res/drawable-hdpi/ic_arrow_back_grey.png b/res/drawable-hdpi/ic_arrow_back_grey.png
new file mode 100755
index 0000000..ccd3900
--- /dev/null
+++ b/res/drawable-hdpi/ic_arrow_back_grey.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_search_grey.png b/res/drawable-hdpi/ic_search_grey.png
new file mode 100755
index 0000000..f4c5e27
--- /dev/null
+++ b/res/drawable-hdpi/ic_search_grey.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_arrow_back_grey.png b/res/drawable-mdpi/ic_arrow_back_grey.png
new file mode 100755
index 0000000..11996ef
--- /dev/null
+++ b/res/drawable-mdpi/ic_arrow_back_grey.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_search_grey.png b/res/drawable-mdpi/ic_search_grey.png
new file mode 100755
index 0000000..e83891c
--- /dev/null
+++ b/res/drawable-mdpi/ic_search_grey.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_arrow_back_grey.png b/res/drawable-xhdpi/ic_arrow_back_grey.png
new file mode 100755
index 0000000..79b9b48
--- /dev/null
+++ b/res/drawable-xhdpi/ic_arrow_back_grey.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_search_grey.png b/res/drawable-xhdpi/ic_search_grey.png
new file mode 100755
index 0000000..bd5fdf4
--- /dev/null
+++ b/res/drawable-xhdpi/ic_search_grey.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_arrow_back_grey.png b/res/drawable-xxhdpi/ic_arrow_back_grey.png
new file mode 100755
index 0000000..8e42e09
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_arrow_back_grey.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_search_grey.png b/res/drawable-xxhdpi/ic_search_grey.png
new file mode 100755
index 0000000..1d5c913
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_search_grey.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_arrow_back_grey.png b/res/drawable-xxxhdpi/ic_arrow_back_grey.png
new file mode 100755
index 0000000..854a9bd
--- /dev/null
+++ b/res/drawable-xxxhdpi/ic_arrow_back_grey.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_search_grey.png b/res/drawable-xxxhdpi/ic_search_grey.png
new file mode 100755
index 0000000..28519fd
--- /dev/null
+++ b/res/drawable-xxxhdpi/ic_search_grey.png
Binary files differ
diff --git a/res/layout/apps_list_view.xml b/res/layout/apps_list_view.xml
index dfb7b58..e29cac5 100644
--- a/res/layout/apps_list_view.xml
+++ b/res/layout/apps_list_view.xml
@@ -22,22 +22,55 @@
     android:elevation="15dp"
     android:visibility="gone"
     android:focusableInTouchMode="true">
-    <EditText
-        android:id="@+id/app_search_box"
+    <FrameLayout
+        android:id="@+id/header"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:padding="16dp"
-        android:hint="@string/apps_view_search_bar_hint"
-        android:maxLines="1"
-        android:singleLine="true"
-        android:scrollHorizontally="true"
-        android:gravity="fill_horizontal"
-        android:textSize="16sp"
-        android:textColor="#4c4c4c"
-        android:textColorHint="#9c9c9c"
-        android:imeOptions="actionDone|flagNoExtractUi"
-        android:background="@drawable/apps_search_bg"
-        android:elevation="4dp" />
+        android:layout_height="52dp"
+        android:orientation="horizontal"
+        android:background="@drawable/apps_search_bg">
+        <LinearLayout
+            android:id="@+id/app_search_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:visibility="invisible">
+            <ImageView
+                android:id="@+id/dismiss_search_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="start|center_vertical"
+                android:paddingTop="12dp"
+                android:paddingBottom="12dp"
+                android:contentDescription="@string/all_apps_button_label"
+                android:src="@drawable/ic_arrow_back_grey" />
+            <EditText
+                android:id="@+id/app_search_box"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:paddingTop="16dp"
+                android:paddingBottom="16dp"
+                android:paddingLeft="8dp"
+                android:hint="@string/apps_view_search_bar_hint"
+                android:maxLines="1"
+                android:singleLine="true"
+                android:scrollHorizontally="true"
+                android:gravity="fill_horizontal"
+                android:textSize="16sp"
+                android:textColor="#4c4c4c"
+                android:textColorHint="#9c9c9c"
+                android:imeOptions="actionDone|flagNoExtractUi"
+                android:background="@android:color/transparent" />
+        </LinearLayout>
+        <ImageView
+            android:id="@+id/search_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="end|center_vertical"
+            android:paddingTop="12dp"
+            android:paddingBottom="12dp"
+            android:contentDescription="@string/apps_view_search_bar_hint"
+            android:src="@drawable/ic_search_grey" />
+    </FrameLayout>
     <com.android.launcher3.AppsContainerRecyclerView
         android:id="@+id/apps_list_view"
         android:layout_width="match_parent"
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index a57ae89..4fbe87e 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -53,6 +53,7 @@
     <!-- Note: This needs to match the fixed insets for the search box -->
     <dimen name="apps_container_fixed_bounds_inset">8dp</dimen>
     <dimen name="apps_grid_view_start_margin">52dp</dimen>
+    <dimen name="apps_grid_section_y_offset">8dp</dimen>
     <dimen name="apps_view_row_height">64dp</dimen>
     <dimen name="apps_view_section_text_size">24sp</dimen>
     <dimen name="apps_view_fast_scroll_bar_width">6dp</dimen>
diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java
index c7ee2e9..477c00f 100644
--- a/src/com/android/launcher3/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/AlphabeticalAppsList.java
@@ -86,6 +86,8 @@
         public String sectionName;
         // The number of applications in this section
         public int numAppsInSection;
+        // The section AdapterItem for this section
+        public AdapterItem sectionItem;
         // The first app AdapterItem for this section
         public AdapterItem firstAppItem;
 
@@ -137,6 +139,9 @@
         public boolean retainApp(AppInfo info, String sectionName);
     }
 
+    // The maximum number of rows allowed in a merged section before we stop merging
+    private static final int MAX_ROWS_IN_MERGED_SECTION = Integer.MAX_VALUE;
+
     private List<AppInfo> mApps = new ArrayList<>();
     private List<AppInfo> mFilteredApps = new ArrayList<>();
     private List<AdapterItem> mSectionedFilteredApps = new ArrayList<>();
@@ -145,10 +150,23 @@
     private Filter mFilter;
     private AlphabeticIndexCompat mIndexer;
     private AppNameComparator mAppNameComparator;
+    private int mNumAppsPerRow;
+    // The maximum number of section merges we allow at a given time before we stop merging
+    private int mMaxAllowableMerges = Integer.MAX_VALUE;
 
-    public AlphabeticalAppsList(Context context) {
+    public AlphabeticalAppsList(Context context, int numAppsPerRow) {
         mIndexer = new AlphabeticIndexCompat(context);
         mAppNameComparator = new AppNameComparator(context);
+        setNumAppsPerRow(numAppsPerRow);
+    }
+
+    /**
+     * Sets the number of apps per row.  Used only for AppsContainerView.SECTIONED_GRID_COALESCED.
+     */
+    public void setNumAppsPerRow(int numAppsPerRow) {
+        mNumAppsPerRow = numAppsPerRow;
+        mMaxAllowableMerges = (int) Math.ceil(numAppsPerRow / 2f);
+        onAppsUpdated();
     }
 
     /**
@@ -180,6 +198,13 @@
     }
 
     /**
+     * Returns whether there are is a filter set.
+     */
+    public boolean hasFilter() {
+        return (mFilter != null);
+    }
+
+    /**
      * Returns whether there are no filtered results.
      */
     public boolean hasNoFilteredResults() {
@@ -190,9 +215,11 @@
      * Sets the current filter for this list of apps.
      */
     public void setFilter(Filter f) {
-        mFilter = f;
-        onAppsUpdated();
-        mAdapter.notifyDataSetChanged();
+        if (mFilter != f) {
+            mFilter = f;
+            onAppsUpdated();
+            mAdapter.notifyDataSetChanged();
+        }
     }
 
     /**
@@ -298,9 +325,13 @@
                 lastSectionInfo = new SectionInfo(sectionName);
                 mSections.add(lastSectionInfo);
 
-                // Create a new section item
+                // Create a new section item, this item is used to break the flow of items in the
+                // list
                 AdapterItem sectionItem = AdapterItem.asSection(position++, sectionName);
-                mSectionedFilteredApps.add(sectionItem);
+                if (!AppsContainerView.GRID_HIDE_SECTION_HEADERS && !hasFilter()) {
+                    lastSectionInfo.sectionItem = sectionItem;
+                    mSectionedFilteredApps.add(sectionItem);
+                }
             }
 
             // Create an app item
@@ -312,5 +343,53 @@
             mSectionedFilteredApps.add(appItem);
             mFilteredApps.add(info);
         }
+
+        if (AppsContainerView.GRID_MERGE_SECTIONS && !hasFilter()) {
+            // Go through each section and try and merge some of the sections
+            int minNumAppsPerRow = (int) Math.ceil(mNumAppsPerRow / 2f);
+            int sectionAppCount = 0;
+            for (int i = 0; i < mSections.size(); i++) {
+                SectionInfo section = mSections.get(i);
+                String mergedSectionName = section.sectionName;
+                sectionAppCount = section.numAppsInSection;
+                int mergeCount = 1;
+                // Merge rows if the last app in this section is in a column that is greater than
+                // 0, but less than the min number of apps per row.  In addition, apply the
+                // constraint to stop merging if the number of rows in the section is greater than
+                // some limit, and also if there are no lessons to merge.
+                while (0 < (sectionAppCount % mNumAppsPerRow) &&
+                        (sectionAppCount % mNumAppsPerRow) < minNumAppsPerRow &&
+                        (int) Math.ceil(sectionAppCount / mNumAppsPerRow) < MAX_ROWS_IN_MERGED_SECTION &&
+                        (i + 1) < mSections.size()) {
+                    SectionInfo nextSection = mSections.remove(i + 1);
+                    // Merge the section names
+                    if (AppsContainerView.GRID_MERGE_SECTION_HEADERS) {
+                        mergedSectionName += nextSection.sectionName;
+                    }
+                    // Remove the next section break
+                    mSectionedFilteredApps.remove(nextSection.sectionItem);
+                    if (AppsContainerView.GRID_MERGE_SECTION_HEADERS) {
+                        // Update the section names for the two sections
+                        int pos = mSectionedFilteredApps.indexOf(section.firstAppItem);
+                        for (int j = pos; j < (pos + section.numAppsInSection + nextSection.numAppsInSection); j++) {
+                            AdapterItem item = mSectionedFilteredApps.get(j);
+                            item.sectionName = mergedSectionName;
+                        }
+                    }
+                    // Update the following adapter items of the removed section
+                    int pos = mSectionedFilteredApps.indexOf(nextSection.firstAppItem);
+                    for (int j = pos; j < mSectionedFilteredApps.size(); j++) {
+                        AdapterItem item = mSectionedFilteredApps.get(j);
+                        item.position--;
+                    }
+                    section.numAppsInSection += nextSection.numAppsInSection;
+                    sectionAppCount += nextSection.numAppsInSection;
+                    mergeCount++;
+                    if (mergeCount >= mMaxAllowableMerges) {
+                        break;
+                    }
+                }
+            }
+        }
     }
 }
diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java
index bf478ed..d91bcea 100644
--- a/src/com/android/launcher3/AppsContainerRecyclerView.java
+++ b/src/com/android/launcher3/AppsContainerRecyclerView.java
@@ -66,6 +66,7 @@
     private int mScrollbarWidth;
     private int mScrollbarMinHeight;
     private int mScrollbarInset;
+    private RecyclerView.OnScrollListener mScrollListenerProxy;
 
     public AppsContainerRecyclerView(Context context) {
         this(context, null);
@@ -102,7 +103,7 @@
         mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD;
 
         ScrollListener listener = new ScrollListener();
-        addOnScrollListener(listener);
+        setOnScrollListener(listener);
     }
 
     private class ScrollListener extends RecyclerView.OnScrollListener {
@@ -112,6 +113,7 @@
         @Override
         public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
             mDy = dy;
+            mScrollListenerProxy.onScrolled(recyclerView, dx, dy);
         }
     }
 
@@ -130,6 +132,13 @@
     }
 
     /**
+     * Sets an additional scroll listener, not necessary in master support lib.
+     */
+    public void setOnScrollListenerProxy(RecyclerView.OnScrollListener listener) {
+        mScrollListenerProxy = listener;
+    }
+
+    /**
      * Sets the fast scroller alpha.
      */
     public void setFastScrollerAlpha(float alpha) {
@@ -178,10 +187,6 @@
         handleTouchEvent(ev);
     }
 
-    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
-        // Do nothing
-    }
-
     /**
      * Handles the touch event and determines whether to show the fast scroller (or updates it if
      * it is already showing).
@@ -322,6 +327,7 @@
 
         // Find the position of the first application in the section that contains the row at the
         // current progress
+        List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
         int rowAtProgress = (int) (progress * getNumRows());
         int rowCount = 0;
         AlphabeticalAppsList.SectionInfo lastSectionInfo = null;
@@ -333,7 +339,7 @@
             }
             rowCount += numRowsInSection;
         }
-        int position = mApps.getAdapterItems().indexOf(lastSectionInfo.firstAppItem);
+        int position = items.indexOf(lastSectionInfo.firstAppItem);
 
         // Scroll the position into view, anchored at the top of the screen if possible. We call the
         // scroll method on the LayoutManager directly since it is not exposed by RecyclerView.
@@ -342,15 +348,17 @@
         layoutManager.scrollToPositionWithOffset(position, 0);
 
         // Return the section name of the row
-        return mApps.getAdapterItems().get(position).sectionName;
+        return lastSectionInfo.sectionName;
     }
 
     /**
      * Returns the bounds for the scrollbar.
      */
     private void updateVerticalScrollbarBounds() {
+        List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
+
         // Skip early if there are no items
-        if (mApps.getAdapterItems().isEmpty()) {
+        if (items.isEmpty()) {
             mVerticalScrollbarBounds.setEmpty();
             return;
         }
@@ -369,7 +377,7 @@
             View child = getChildAt(i);
             int position = getChildPosition(child);
             if (position != NO_POSITION) {
-                AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position);
+                AlphabeticalAppsList.AdapterItem item = items.get(position);
                 if (!item.isSectionHeader) {
                     rowIndex = findRowForAppIndex(item.appIndex);
                     rowTopOffset = getLayoutManager().getDecoratedTop(child);
diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java
index c3cf629..9122427 100644
--- a/src/com/android/launcher3/AppsContainerView.java
+++ b/src/com/android/launcher3/AppsContainerView.java
@@ -34,7 +34,6 @@
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 import android.widget.TextView;
-
 import com.android.launcher3.util.Thunk;
 
 import java.util.List;
@@ -45,24 +44,32 @@
  */
 public class AppsContainerView extends FrameLayout implements DragSource, Insettable, TextWatcher,
         TextView.OnEditorActionListener, LauncherTransitionable, View.OnTouchListener,
-        View.OnLongClickListener {
+        View.OnClickListener, View.OnLongClickListener {
+
+    public static final boolean GRID_MERGE_SECTIONS = true;
+    public static final boolean GRID_MERGE_SECTION_HEADERS = false;
+    public static final boolean GRID_HIDE_SECTION_HEADERS = false;
 
     private static final boolean ALLOW_SINGLE_APP_LAUNCH = true;
-
-    private static final int GRID_LAYOUT = 0;
-    private static final int LIST_LAYOUT = 1;
-    private static final int USE_LAYOUT = GRID_LAYOUT;
+    private static final boolean DYNAMIC_HEADER_ELEVATION = false;
+    private static final float HEADER_ELEVATION_DP = 4;
+    private static final int FADE_IN_DURATION = 175;
+    private static final int FADE_OUT_DURATION = 125;
 
     @Thunk Launcher mLauncher;
     @Thunk AlphabeticalAppsList mApps;
-    private RecyclerView.Adapter mAdapter;
+    private AppsGridAdapter mAdapter;
     private RecyclerView.LayoutManager mLayoutManager;
     private RecyclerView.ItemDecoration mItemDecoration;
 
     private LinearLayout mContentView;
     @Thunk AppsContainerRecyclerView mAppsRecyclerView;
-    private EditText mSearchBarView;
-    
+    private View mHeaderView;
+    private View mSearchBarContainerView;
+    private View mSearchButtonView;
+    private View mDismissSearchButtonView;
+    private EditText mSearchBarEditView;
+
     private int mNumAppsPerRow;
     private Point mLastTouchDownPos = new Point(-1, -1);
     private Point mLastTouchPos = new Point();
@@ -73,6 +80,8 @@
     private int mContainerInset;
     // Fixed bounds container insets
     private int mFixedBoundsContainerInset;
+    // RecyclerView scroll position
+    @Thunk int mRecyclerViewScrollY;
 
     public AppsContainerView(Context context) {
         this(context, null);
@@ -93,23 +102,14 @@
         mFixedBoundsContainerInset = context.getResources().getDimensionPixelSize(
                 R.dimen.apps_container_fixed_bounds_inset);
         mLauncher = (Launcher) context;
-        mApps = new AlphabeticalAppsList(context);
-        if (USE_LAYOUT == GRID_LAYOUT) {
-            mNumAppsPerRow = grid.appsViewNumCols;
-            AppsGridAdapter adapter = new AppsGridAdapter(context, mApps, mNumAppsPerRow, this,
-                    mLauncher, this);
-            adapter.setEmptySearchText(res.getString(R.string.loading_apps_message));
-            mLayoutManager = adapter.getLayoutManager(context);
-            mItemDecoration = adapter.getItemDecoration();
-            mAdapter = adapter;
-            mContentMarginStart = adapter.getContentMarginStart();
-        } else if (USE_LAYOUT == LIST_LAYOUT) {
-            mNumAppsPerRow = 1;
-            AppsListAdapter adapter = new AppsListAdapter(context, mApps, this, mLauncher, this);
-            adapter.setEmptySearchText(res.getString(R.string.loading_apps_message));
-            mLayoutManager = adapter.getLayoutManager(context);
-            mAdapter = adapter;
-        }
+        mNumAppsPerRow = grid.appsViewNumCols;
+        mApps = new AlphabeticalAppsList(context, mNumAppsPerRow);
+        mAdapter = new AppsGridAdapter(context, mApps, mNumAppsPerRow, this, mLauncher, this);
+        mAdapter.setEmptySearchText(res.getString(R.string.loading_apps_message));
+        mAdapter.setNumAppsPerRow(mNumAppsPerRow);
+        mLayoutManager = mAdapter.getLayoutManager();
+        mItemDecoration = mAdapter.getItemDecoration();
+        mContentMarginStart = mAdapter.getContentMarginStart();
         mApps.setAdapter(mAdapter);
     }
 
@@ -142,10 +142,10 @@
     }
 
     /**
-     * Hides the search bar
+     * Hides the header bar
      */
-    public void hideSearchBar() {
-        mSearchBarView.setVisibility(View.GONE);
+    public void hideHeaderBar() {
+        mHeaderView.setVisibility(View.GONE);
         updateBackgrounds();
         updatePaddings();
     }
@@ -155,6 +155,7 @@
      */
     public void scrollToTop() {
         mAppsRecyclerView.scrollToPosition(0);
+        mRecyclerViewScrollY = 0;
     }
 
     /**
@@ -175,9 +176,7 @@
     protected void onFinishInflate() {
         boolean isRtl = (getResources().getConfiguration().getLayoutDirection() ==
                 LAYOUT_DIRECTION_RTL);
-        if (USE_LAYOUT == GRID_LAYOUT) {
-            ((AppsGridAdapter) mAdapter).setRtl(isRtl);
-        }
+        mAdapter.setRtl(isRtl);
 
         // Work around the search box getting first focus and showing the cursor by
         // proxying the focus from the content view to the recycler view directly
@@ -190,10 +189,20 @@
                 }
             }
         });
-        mSearchBarView = (EditText) findViewById(R.id.app_search_box);
-        if (mSearchBarView != null) {
-            mSearchBarView.addTextChangedListener(this);
-            mSearchBarView.setOnEditorActionListener(this);
+        mHeaderView = findViewById(R.id.header);
+        mHeaderView.setOnClickListener(this);
+        if (Utilities.isLmpOrAbove() && !DYNAMIC_HEADER_ELEVATION) {
+            mHeaderView.setElevation(DynamicGrid.pxFromDp(HEADER_ELEVATION_DP,
+                getContext().getResources().getDisplayMetrics()));
+        }
+        mSearchButtonView = mHeaderView.findViewById(R.id.search_button);
+        mSearchBarContainerView = findViewById(R.id.app_search_container);
+        mDismissSearchButtonView = mSearchBarContainerView.findViewById(R.id.dismiss_search_button);
+        mDismissSearchButtonView.setOnClickListener(this);
+        mSearchBarEditView = (EditText) findViewById(R.id.app_search_box);
+        if (mSearchBarEditView != null) {
+            mSearchBarEditView.addTextChangedListener(this);
+            mSearchBarEditView.setOnEditorActionListener(this);
         }
         mAppsRecyclerView = (AppsContainerRecyclerView) findViewById(R.id.apps_list_view);
         mAppsRecyclerView.setApps(mApps);
@@ -201,6 +210,18 @@
         mAppsRecyclerView.setLayoutManager(mLayoutManager);
         mAppsRecyclerView.setAdapter(mAdapter);
         mAppsRecyclerView.setHasFixedSize(true);
+        mAppsRecyclerView.setOnScrollListenerProxy(new RecyclerView.OnScrollListener() {
+            @Override
+            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+                // Do nothing
+            }
+
+            @Override
+            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+                mRecyclerViewScrollY += dy;
+                onRecyclerViewScrolled();
+            }
+        });
         if (mItemDecoration != null) {
             mAppsRecyclerView.addItemDecoration(mItemDecoration);
         }
@@ -225,12 +246,15 @@
             if (grid.updateAppsViewNumCols(context.getResources(), fixedBounds.width())) {
                 mNumAppsPerRow = grid.appsViewNumCols;
                 mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow);
-                if (USE_LAYOUT == GRID_LAYOUT) {
-                    ((AppsGridAdapter) mAdapter).setNumAppsPerRow(mNumAppsPerRow);
-                }
+                mAdapter.setNumAppsPerRow(mNumAppsPerRow);
+                mApps.setNumAppsPerRow(mNumAppsPerRow);
             }
 
             mFixedBounds.set(fixedBounds);
+            if (Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION) {
+                mFixedBounds.top = mInsets.top;
+                mFixedBounds.bottom = getMeasuredHeight();
+            }
         }
         // Post the updates since they can trigger a relayout, and this call can be triggered from
         // a layout pass itself.
@@ -265,6 +289,15 @@
     }
 
     @Override
+    public void onClick(View v) {
+        if (v == mHeaderView) {
+            showSearchField();
+        } else if (v == mDismissSearchButtonView) {
+            hideSearchField(true, true);
+        }
+    }
+
+    @Override
     public boolean onLongClick(View v) {
         // Return early if this is not initiated from a touch
         if (!v.isInTouchMode()) return false;
@@ -363,24 +396,27 @@
             mApps.setFilter(null);
         } else {
             String formatStr = getResources().getString(R.string.apps_view_no_search_results);
-            if (USE_LAYOUT == GRID_LAYOUT) {
-                ((AppsGridAdapter) mAdapter).setEmptySearchText(String.format(formatStr,
-                        s.toString()));
-            } else {
-                ((AppsListAdapter) mAdapter).setEmptySearchText(String.format(formatStr,
-                        s.toString()));
-            }
+            mAdapter.setEmptySearchText(String.format(formatStr, s.toString()));
 
             final String filterText = s.toString().toLowerCase().replaceAll("\\s+", "");
             mApps.setFilter(new AlphabeticalAppsList.Filter() {
                 @Override
                 public boolean retainApp(AppInfo info, String sectionName) {
                     String title = info.title.toString();
-                    return sectionName.toLowerCase().contains(filterText) ||
-                            title.toLowerCase().replaceAll("\\s+", "").contains(filterText);
+                    if (sectionName.toLowerCase().contains(filterText)) {
+                        return true;
+                    }
+                    String[] words = title.toLowerCase().split("\\s+");
+                    for (int i = 0; i < words.length; i++) {
+                        if (words[i].startsWith(filterText)) {
+                            return true;
+                        }
+                    }
+                    return false;
                 }
             });
         }
+        scrollToTop();
     }
 
     @Override
@@ -396,9 +432,7 @@
                 AlphabeticalAppsList.AdapterItem item = items.get(i);
                 if (!item.isSectionHeader) {
                     mAppsRecyclerView.getChildAt(i).performClick();
-                    InputMethodManager imm = (InputMethodManager)
-                            getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
-                    imm.hideSoftInputFromWindow(getWindowToken(), 0);
+                    getInputMethodManager().hideSoftInputFromWindow(getWindowToken(), 0);
                     return true;
                 }
             }
@@ -428,10 +462,22 @@
 
     @Override
     public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
-        if (mSearchBarView != null) {
+        if (mSearchBarEditView != null) {
             if (toWorkspace) {
-                // Clear the search bar
-                mSearchBarView.setText("");
+                hideSearchField(false, false);
+            }
+        }
+    }
+
+    /**
+     * Updates the container when the recycler view is scrolled.
+     */
+    private void onRecyclerViewScrolled() {
+        if (DYNAMIC_HEADER_ELEVATION) {
+            int elevation = Math.min(mRecyclerViewScrollY, DynamicGrid.pxFromDp(HEADER_ELEVATION_DP,
+                    getContext().getResources().getDisplayMetrics()));
+            if (Float.compare(mHeaderView.getElevation(), elevation) != 0) {
+                mHeaderView.setElevation(elevation);
             }
         }
     }
@@ -494,8 +540,8 @@
     private void updatePaddings() {
         boolean isRtl = (getResources().getConfiguration().getLayoutDirection() ==
                 LAYOUT_DIRECTION_RTL);
-        boolean hasSearchBar = (mSearchBarView != null) &&
-                (mSearchBarView.getVisibility() == View.VISIBLE);
+        boolean hasSearchBar = (mSearchBarEditView != null) &&
+                (mSearchBarEditView.getVisibility() == View.VISIBLE);
 
         if (mFixedBounds.isEmpty()) {
             // If there are no fixed bounds, then use the default padding and insets
@@ -516,10 +562,10 @@
             mAppsRecyclerView.setPadding(inset + mContentMarginStart, inset, inset, inset);
         }
 
-        // Update the search bar
+        // Update the header
         if (hasSearchBar) {
             LinearLayout.LayoutParams lp =
-                    (LinearLayout.LayoutParams) mSearchBarView.getLayoutParams();
+                    (LinearLayout.LayoutParams) mHeaderView.getLayoutParams();
             lp.leftMargin = lp.rightMargin = inset;
         }
     }
@@ -529,8 +575,8 @@
      */
     private void updateBackgrounds() {
         int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset;
-        boolean hasSearchBar = (mSearchBarView != null) &&
-                (mSearchBarView.getVisibility() == View.VISIBLE);
+        boolean hasSearchBar = (mSearchBarEditView != null) &&
+                (mSearchBarEditView.getVisibility() == View.VISIBLE);
 
         // Update the background of the reveal view and list to be inset with the fixed bound
         // insets instead of the default insets
@@ -542,4 +588,63 @@
                 getContext().getResources().getDrawable(R.drawable.apps_reveal_bg),
                 inset, 0, inset, 0));
     }
+
+    /**
+     * Shows the search field.
+     */
+    private void showSearchField() {
+        // Show the search bar and focus the search
+        mSearchBarContainerView.setVisibility(View.VISIBLE);
+        mSearchBarContainerView.setAlpha(0f);
+        mSearchBarContainerView.animate().alpha(1f).setDuration(FADE_IN_DURATION).withLayer()
+                .withEndAction(new Runnable() {
+                    @Override
+                    public void run() {
+                        mSearchBarEditView.requestFocus();
+                        getInputMethodManager().showSoftInput(mSearchBarEditView,
+                                InputMethodManager.SHOW_IMPLICIT);
+                    }
+                });
+        mSearchButtonView.animate().alpha(0f).setDuration(FADE_OUT_DURATION).withLayer();
+    }
+
+    /**
+     * Hides the search field.
+     */
+    private void hideSearchField(boolean animated, final boolean returnFocusToRecyclerView) {
+        if (animated) {
+            // Hide the search bar and focus the recycler view
+            mSearchBarContainerView.animate().alpha(0f).setDuration(FADE_IN_DURATION).withLayer()
+                    .withEndAction(new Runnable() {
+                        @Override
+                        public void run() {
+                            mSearchBarContainerView.setVisibility(View.INVISIBLE);
+                            mSearchBarEditView.setText("");
+                            mApps.setFilter(null);
+                            if (returnFocusToRecyclerView) {
+                                mAppsRecyclerView.requestFocus();
+                            }
+                            scrollToTop();
+                        }
+                    });
+            mSearchButtonView.animate().alpha(1f).setDuration(FADE_OUT_DURATION).withLayer();
+        } else {
+            mSearchBarContainerView.setVisibility(View.INVISIBLE);
+            mSearchBarEditView.setText("");
+            mApps.setFilter(null);
+            mSearchButtonView.setAlpha(1f);
+            if (returnFocusToRecyclerView) {
+                mAppsRecyclerView.requestFocus();
+            }
+            scrollToTop();
+        }
+        getInputMethodManager().hideSoftInputFromWindow(getWindowToken(), 0);
+    }
+
+    /**
+     * Returns an input method manager.
+     */
+    private InputMethodManager getInputMethodManager() {
+        return (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+    }
 }
diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java
index 5bc3981..62d9129 100644
--- a/src/com/android/launcher3/AppsGridAdapter.java
+++ b/src/com/android/launcher3/AppsGridAdapter.java
@@ -4,16 +4,18 @@
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Paint;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.support.v7.widget.GridLayoutManager;
 import android.support.v7.widget.RecyclerView;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
-
 import com.android.launcher3.util.Thunk;
 
+import java.util.HashMap;
 import java.util.List;
 
 
@@ -23,6 +25,7 @@
 class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
 
     public static final String TAG = "AppsGridAdapter";
+    private static final boolean DEBUG = false;
 
     private static final int SECTION_BREAK_VIEW_TYPE = 0;
     private static final int ICON_VIEW_TYPE = 1;
@@ -48,6 +51,12 @@
      * Helper class to size the grid items.
      */
     public class GridSpanSizer extends GridLayoutManager.SpanSizeLookup {
+
+        public GridSpanSizer() {
+            super();
+            setSpanIndexCacheEnabled(true);
+        }
+
         @Override
         public int getSpanSize(int position) {
             if (mApps.hasNoFilteredResults()) {
@@ -57,7 +66,11 @@
 
             if (mApps.getAdapterItems().get(position).isSectionHeader) {
                 // Section break spans full width
-                return mAppsPerRow;
+                if (AppsContainerView.GRID_HIDE_SECTION_HEADERS) {
+                    return 0;
+                } else {
+                    return mAppsPerRow;
+                }
             } else {
                 return 1;
             }
@@ -69,31 +82,88 @@
      */
     public class GridItemDecoration extends RecyclerView.ItemDecoration {
 
+        private static final boolean FADE_OUT_SECTIONS = false;
+
+        private HashMap<String, Point> mCachedSectionBounds = new HashMap<>();
+        private Rect mTmpBounds = new Rect();
+
         @Override
         public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
+            if (mApps.hasFilter()) {
+                return;
+            }
+
             List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
+            String lastSectionName = null;
+            int appIndexInSection = 0;
+            int lastSectionTop = 0;
+            int lastSectionHeight = 0;
             for (int i = 0; i < parent.getChildCount(); i++) {
                 View child = parent.getChildAt(i);
                 ViewHolder holder = (ViewHolder) parent.getChildViewHolder(child);
-                if (shouldDrawItemSection(holder, child, items)) {
-                    // Draw at the parent
-                    AlphabeticalAppsList.AdapterItem item =
-                            items.get(holder.getPosition());
-                    String section = item.sectionName;
-                    mSectionTextPaint.getTextBounds(section, 0, section.length(),
-                            mTmpBounds);
-                    if (mIsRtl) {
-                        int left = parent.getWidth() - mPaddingStart - mStartMargin;
-                        c.drawText(section, left + (mStartMargin - mTmpBounds.width()) / 2,
-                                child.getTop() + (2 * child.getPaddingTop()) +
-                                        mTmpBounds.height(), mSectionTextPaint);
-                    } else {
-                        int left = mPaddingStart;
-                        c.drawText(section, left + (mStartMargin - mTmpBounds.width()) / 2,
-                            child.getTop() + (2 * child.getPaddingTop()) +
-                                    mTmpBounds.height(), mSectionTextPaint);
+                if (shouldDrawItemSection(holder, child, i, items)) {
+                    int cellTopOffset = (2 * child.getPaddingTop());
+                    int pos = holder.getPosition();
+                    AlphabeticalAppsList.AdapterItem item = items.get(pos);
+                    if (!item.sectionName.equals(lastSectionName)) {
+                        lastSectionName = item.sectionName;
+
+                        // Find the section code points
+                        String sectionBegin = null;
+                        String sectionEnd = null;
+                        int charOffset = 0;
+                        while (charOffset < item.sectionName.length()) {
+                            int codePoint = item.sectionName.codePointAt(charOffset);
+                            int codePointSize = Character.charCount(codePoint);
+                            if (charOffset == 0) {
+                                // The first code point
+                                sectionBegin = item.sectionName.substring(charOffset, charOffset + codePointSize);
+                            } else if ((charOffset + codePointSize) >= item.sectionName.length()) {
+                                // The last code point
+                                sectionEnd = item.sectionName.substring(charOffset, charOffset + codePointSize);
+                            }
+                            charOffset += codePointSize;
+                        }
+
+                        Point sectionBeginBounds = getAndCacheSectionBounds(sectionBegin);
+                        int minTop = cellTopOffset + sectionBeginBounds.y;
+                        int top = child.getTop() + cellTopOffset + sectionBeginBounds.y;
+                        int left = mIsRtl ? parent.getWidth() - mPaddingStart - mStartMargin :
+                                mPaddingStart;
+                        int col = appIndexInSection % mAppsPerRow;
+                        int nextRowPos = Math.min(pos - col + mAppsPerRow, items.size() - 1);
+                        int alpha = 255;
+                        boolean fixedToRow = !items.get(nextRowPos).sectionName.equals(item.sectionName);
+                        if (fixedToRow) {
+                            alpha = Math.min(255, (int) (255 * (Math.max(0, top) / (float) minTop)));
+                        } else {
+                            // If we aren't fixed to the current row, then bound into the viewport
+                            top = Math.max(minTop, top);
+                        }
+                        if (lastSectionHeight > 0 && top <= (lastSectionTop + lastSectionHeight)) {
+                            top += lastSectionTop - top + lastSectionHeight;
+                        }
+                        if (FADE_OUT_SECTIONS) {
+                            mSectionTextPaint.setAlpha(alpha);
+                        }
+                        if (sectionEnd != null) {
+                            Point sectionEndBounds = getAndCacheSectionBounds(sectionEnd);
+                            c.drawText(sectionBegin + "/" + sectionEnd,
+                                    left + (mStartMargin - sectionBeginBounds.x - sectionEndBounds.x) / 2, top,
+                                    mSectionTextPaint);
+                        } else {
+                            c.drawText(sectionBegin, left + (mStartMargin - sectionBeginBounds.x) / 2, top,
+                                    mSectionTextPaint);
+                        }
+                        lastSectionTop = top;
+                        lastSectionHeight = sectionBeginBounds.y + mSectionHeaderOffset;
                     }
                 }
+                if (holder.mIsSectionHeader) {
+                    appIndexInSection = 0;
+                } else {
+                    appIndexInSection++;
+                }
             }
         }
 
@@ -103,7 +173,17 @@
             // Do nothing
         }
 
-        private boolean shouldDrawItemSection(ViewHolder holder, View child,
+        private Point getAndCacheSectionBounds(String sectionName) {
+            Point bounds = mCachedSectionBounds.get(sectionName);
+            if (bounds == null) {
+                mSectionTextPaint.getTextBounds(sectionName, 0, sectionName.length(), mTmpBounds);
+                bounds = new Point(mTmpBounds.width(), mTmpBounds.height());
+                mCachedSectionBounds.put(sectionName, bounds);
+            }
+            return bounds;
+        }
+
+        private boolean shouldDrawItemSection(ViewHolder holder, View child, int childIndex,
                 List<AlphabeticalAppsList.AdapterItem> items) {
             // Ensure item is not already removed
             GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams)
@@ -121,11 +201,19 @@
             }
             // Ensure we have a holder position
             int pos = holder.getPosition();
-            if (pos <= 0 || pos >= items.size()) {
+            if (pos < 0 || pos >= items.size()) {
                 return false;
             }
-            // Only draw the first item in the section (the first one after the section header)
-            return items.get(pos - 1).isSectionHeader && !items.get(pos).isSectionHeader;
+            // Ensure this is not a section header
+            if (items.get(pos).isSectionHeader) {
+                return false;
+            }
+            // Only draw the header for the first item in a section, or whenever the sub-sections
+            // changes (if AppsContainerView.GRID_MERGE_SECTIONS is true, but
+            // AppsContainerView.GRID_MERGE_SECTION_HEADERS is false)
+            return (childIndex == 0) ||
+                    items.get(pos - 1).isSectionHeader && !items.get(pos).isSectionHeader ||
+                    (!items.get(pos - 1).sectionName.equals(items.get(pos).sectionName));
         }
     }
 
@@ -144,8 +232,8 @@
     // Section drawing
     @Thunk int mPaddingStart;
     @Thunk int mStartMargin;
+    @Thunk int mSectionHeaderOffset;
     @Thunk Paint mSectionTextPaint;
-    @Thunk Rect mTmpBounds = new Rect();
 
 
     public AppsGridAdapter(Context context, AlphabeticalAppsList apps, int appsPerRow,
@@ -163,7 +251,10 @@
         mTouchListener = touchListener;
         mIconClickListener = iconClickListener;
         mIconLongClickListener = iconLongClickListener;
-        mStartMargin = res.getDimensionPixelSize(R.dimen.apps_grid_view_start_margin);
+        if (!AppsContainerView.GRID_HIDE_SECTION_HEADERS) {
+            mStartMargin = res.getDimensionPixelSize(R.dimen.apps_grid_view_start_margin);
+            mSectionHeaderOffset = res.getDimensionPixelSize(R.dimen.apps_grid_section_y_offset);
+        }
         mPaddingStart = res.getDimensionPixelSize(R.dimen.apps_container_inset);
         mSectionTextPaint = new Paint();
         mSectionTextPaint.setTextSize(res.getDimensionPixelSize(
@@ -197,7 +288,7 @@
     /**
      * Returns the grid layout manager.
      */
-    public GridLayoutManager getLayoutManager(Context context) {
+    public GridLayoutManager getLayoutManager() {
         return mGridLayoutMgr;
     }
 
@@ -205,7 +296,11 @@
      * Returns the item decoration for the recycler view.
      */
     public RecyclerView.ItemDecoration getItemDecoration() {
-        return mItemDecoration;
+        // We don't draw any headers when we are uncomfortably dense
+        if (!AppsContainerView.GRID_HIDE_SECTION_HEADERS) {
+            return mItemDecoration;
+        }
+        return null;
     }
 
     /**
diff --git a/src/com/android/launcher3/AppsListAdapter.java b/src/com/android/launcher3/AppsListAdapter.java
deleted file mode 100644
index ffd3092..0000000
--- a/src/com/android/launcher3/AppsListAdapter.java
+++ /dev/null
@@ -1,143 +0,0 @@
-package com.android.launcher3;
-
-import android.content.Context;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-/**
- * The linear list view adapter for all the apps.
- */
-class AppsListAdapter extends RecyclerView.Adapter<AppsListAdapter.ViewHolder> {
-
-    /**
-     * ViewHolder for each row.
-     */
-    public static class ViewHolder extends RecyclerView.ViewHolder {
-        public View mContent;
-
-        public ViewHolder(View v) {
-            super(v);
-            mContent = v;
-        }
-    }
-
-    private static final int SECTION_BREAK_VIEW_TYPE = 0;
-    private static final int ICON_VIEW_TYPE = 1;
-    private static final int EMPTY_VIEW_TYPE = 2;
-
-    private LayoutInflater mLayoutInflater;
-    private AlphabeticalAppsList mApps;
-    private View.OnTouchListener mTouchListener;
-    private View.OnClickListener mIconClickListener;
-    private View.OnLongClickListener mIconLongClickListener;
-    private String mEmptySearchText;
-
-    public AppsListAdapter(Context context, AlphabeticalAppsList apps,
-            View.OnTouchListener touchListener, View.OnClickListener iconClickListener,
-            View.OnLongClickListener iconLongClickListener) {
-        mApps = apps;
-        mLayoutInflater = LayoutInflater.from(context);
-        mTouchListener = touchListener;
-        mIconClickListener = iconClickListener;
-        mIconLongClickListener = iconLongClickListener;
-    }
-
-    public RecyclerView.LayoutManager getLayoutManager(Context context) {
-        return new LinearLayoutManager(context);
-    }
-
-    /**
-     * Sets the text to show when there are no apps.
-     */
-    public void setEmptySearchText(String query) {
-        mEmptySearchText = query;
-    }
-
-    @Override
-    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-        switch (viewType) {
-            case EMPTY_VIEW_TYPE:
-                return new ViewHolder(mLayoutInflater.inflate(R.layout.apps_empty_view, parent,
-                        false));
-            case SECTION_BREAK_VIEW_TYPE:
-                return new ViewHolder(new View(parent.getContext()));
-            case ICON_VIEW_TYPE:
-                // Inflate the row and all the icon children necessary
-                ViewGroup row = (ViewGroup) mLayoutInflater.inflate(R.layout.apps_list_row_view,
-                        parent, false);
-                BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
-                        R.layout.apps_list_row_icon_view, 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);
-                return new ViewHolder(row);
-            default:
-                throw new RuntimeException("Unexpected view type");
-        }
-    }
-
-    @Override
-    public void onBindViewHolder(ViewHolder holder, int position) {
-        switch (holder.getItemViewType()) {
-            case ICON_VIEW_TYPE:
-                AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position);
-                ViewGroup content = (ViewGroup) holder.mContent;
-                String sectionDescription = item.sectionName;
-
-                // Bind the section header
-                boolean showSectionHeader = true;
-                if (position > 0) {
-                    AlphabeticalAppsList.AdapterItem prevItem =
-                            mApps.getAdapterItems().get(position - 1);
-                    showSectionHeader = prevItem.isSectionHeader;
-                }
-                TextView tv = (TextView) content.findViewById(R.id.section);
-                if (showSectionHeader) {
-                    tv.setText(sectionDescription);
-                    tv.setVisibility(View.VISIBLE);
-                } else {
-                    tv.setVisibility(View.INVISIBLE);
-                }
-
-                // Bind the icon
-                BubbleTextView icon = (BubbleTextView) content.getChildAt(1);
-                icon.applyFromApplicationInfo(item.appInfo);
-                break;
-            case EMPTY_VIEW_TYPE:
-                TextView emptyViewText = (TextView) holder.mContent.findViewById(R.id.empty_text);
-                emptyViewText.setText(mEmptySearchText);
-                break;
-        }
-    }
-
-    @Override
-    public int getItemCount() {
-        if (mApps.hasNoFilteredResults()) {
-            // For the empty view
-            return 1;
-        }
-        return mApps.getAdapterItems().size();
-    }
-
-    @Override
-    public int getItemViewType(int position) {
-        if (mApps.hasNoFilteredResults()) {
-            return EMPTY_VIEW_TYPE;
-        } else if (mApps.getAdapterItems().get(position).isSectionHeader) {
-            return SECTION_BREAK_VIEW_TYPE;
-        }
-        return ICON_VIEW_TYPE;
-    }
-}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index deb8075..918517e 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -428,6 +428,13 @@
     }
 
     public boolean updateAppsViewNumCols(Resources res, int containerWidth) {
+        if (AppsContainerView.GRID_HIDE_SECTION_HEADERS) {
+            if (appsViewNumCols != allAppsNumCols) {
+                appsViewNumCols = allAppsNumCols;
+                return true;
+            }
+            return false;
+        }
         int appsViewLeftMarginPx =
                 res.getDimensionPixelSize(R.dimen.apps_grid_view_start_margin);
         int availableAppsWidthPx = (containerWidth > 0) ? containerWidth : availableWidthPx;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 339b4e4..c0f09f4 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -135,6 +135,9 @@
     static final String TAG = "Launcher";
     static final boolean LOGD = true;
 
+    // Temporary flag
+    static final boolean DISABLE_ALL_APPS_SEARCH_INTEGRATION = true;
+
     static final boolean PROFILE_STARTUP = false;
     static final boolean DEBUG_WIDGETS = true;
     static final boolean DEBUG_STRICT_MODE = false;
@@ -530,10 +533,12 @@
 
             @Override
             public void dismissAllApps() {
-                // Dismiss All Apps if we aren't already paused/invisible
-                if (!mPaused) {
-                    showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, true,
-                            null /* onCompleteRunnable */, false /* notifyLauncherCallbacks */);
+                if (!DISABLE_ALL_APPS_SEARCH_INTEGRATION) {
+                    // Dismiss All Apps if we aren't already paused/invisible
+                    if (!mPaused) {
+                        showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, true,
+                                null /* onCompleteRunnable */, false /* notifyLauncherCallbacks */);
+                    }
                 }
             }
         });
@@ -1019,7 +1024,7 @@
         mOnResumeState = State.NONE;
 
         // Restore the apps state if we are in all apps
-        if (mState == State.APPS) {
+        if (!Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION && mState == State.APPS) {
             if (mLauncherCallbacks != null) {
                 mLauncherCallbacks.onAllAppsShown();
             }
@@ -1453,8 +1458,8 @@
 
         // Setup Apps
         mAppsView = (AppsContainerView) findViewById(R.id.apps_view);
-        if (mLauncherCallbacks != null && mLauncherCallbacks.overrideAllAppsSearch()) {
-            mAppsView.hideSearchBar();
+        if (isAllAppsSearchOverridden()) {
+            mAppsView.hideHeaderBar();
         }
 
         // Setup AppsCustomize
@@ -2877,15 +2882,22 @@
 
     /** Updates the interaction state. */
     public void updateInteraction(Workspace.State fromState, Workspace.State toState) {
-        // Only update the interacting state if we are transitioning to/from a view without an
+        // Only update the interacting state if we are transitioning to/from a view with an
         // overlay
-        boolean fromStateWithoutOverlay = fromState != Workspace.State.NORMAL &&
-                fromState != Workspace.State.NORMAL_HIDDEN;
-        boolean toStateWithoutOverlay = toState != Workspace.State.NORMAL &&
-                toState != Workspace.State.NORMAL_HIDDEN;
-        if (toStateWithoutOverlay) {
+        boolean fromStateWithOverlay;
+        boolean toStateWithOverlay;
+        if (Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION) {
+            fromStateWithOverlay = fromState != Workspace.State.NORMAL;
+            toStateWithOverlay = toState != Workspace.State.NORMAL;
+        } else {
+            fromStateWithOverlay = fromState != Workspace.State.NORMAL &&
+                    fromState != Workspace.State.NORMAL_HIDDEN;
+            toStateWithOverlay = toState != Workspace.State.NORMAL &&
+                    toState != Workspace.State.NORMAL_HIDDEN;
+        }
+        if (toStateWithOverlay) {
             onInteractionBegin();
-        } else if (fromStateWithoutOverlay) {
+        } else if (fromStateWithOverlay) {
             onInteractionEnd();
         }
     }
@@ -3367,7 +3379,7 @@
                     .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
             if (notifyLauncherCallbacks) {
                 // Dismiss all apps when the workspace is shown
-                if (mLauncherCallbacks != null) {
+                if (!Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION && mLauncherCallbacks != null) {
                     mLauncherCallbacks.onAllAppsHidden();
                 }
             }
@@ -3419,7 +3431,7 @@
 
         if (toState == State.APPS) {
             mStateTransitionAnimation.startAnimationToAllApps(animated);
-            if (mLauncherCallbacks != null) {
+            if (!Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION && mLauncherCallbacks != null) {
                 mLauncherCallbacks.onAllAppsShown();
             }
         } else {
@@ -3472,7 +3484,7 @@
         if (successfulDrop) {
             // We need to trigger all apps hidden to notify search to update itself before the
             // delayed call to showWorkspace below
-            if (mLauncherCallbacks != null) {
+            if (!Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION && mLauncherCallbacks != null) {
                 mLauncherCallbacks.onAllAppsHidden();
             }
         }
@@ -4454,9 +4466,12 @@
 
     /**
      * Returns whether the launcher callbacks overrides search in all apps.
-     * @return
      */
     @Thunk boolean isAllAppsSearchOverridden() {
+        if (DISABLE_ALL_APPS_SEARCH_INTEGRATION) {
+            return false;
+        }
+
         if (mLauncherCallbacks != null) {
             return mLauncherCallbacks.overrideAllAppsSearch();
         }
diff --git a/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java b/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java
index f70f170..80e13bc 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerRecyclerView.java
@@ -51,7 +51,7 @@
         mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD;
 
         ScrollListener listener = new ScrollListener();
-        addOnScrollListener(listener);
+        setOnScrollListener(listener);
     }
 
     private class ScrollListener extends RecyclerView.OnScrollListener {