Merge "Moving prediction icons back into recycler view." into ub-launcher3-burnaby
diff --git a/res/layout/all_apps_container.xml b/res/layout/all_apps_container.xml
index 0221a56..626edaf 100644
--- a/res/layout/all_apps_container.xml
+++ b/res/layout/all_apps_container.xml
@@ -35,16 +35,4 @@
         android:focusable="true"
         android:descendantFocusability="afterDescendants" />
 
-    <LinearLayout
-        android:id="@+id/prediction_bar"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingTop="@dimen/all_apps_prediction_bar_top_padding"
-        android:paddingBottom="@dimen/all_apps_prediction_bar_bottom_padding"
-        android:orientation="horizontal"
-        android:focusable="true"
-        android:descendantFocusability="afterDescendants"
-        android:visibility="invisible" >
-    </LinearLayout>
-
 </com.android.launcher3.allapps.AllAppsRecyclerViewContainerView>
\ No newline at end of file
diff --git a/res/layout/all_apps_prediction_bar_icon.xml b/res/layout/all_apps_prediction_bar_icon.xml
index 5f63f6b..341d8ef 100644
--- a/res/layout/all_apps_prediction_bar_icon.xml
+++ b/res/layout/all_apps_prediction_bar_icon.xml
@@ -18,10 +18,11 @@
     xmlns:launcher="http://schemas.android.com/apk/res-auto"
     style="@style/Icon.AllApps"
     android:id="@+id/icon"
-    android:layout_width="0dp"
+    android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_gravity="center"
-    android:layout_weight="1"
+    android:paddingTop="@dimen/all_apps_prediction_icon_top_padding"
+    android:paddingBottom="@dimen/all_apps_prediction_icon_bottom_padding"
     android:focusable="true"
     android:background="@drawable/focusable_view_bg"
     launcher:iconDisplay="all_apps" />
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 122b831..589d696 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -70,9 +70,9 @@
     <dimen name="all_apps_search_bar_prediction_bar_padding">8dp</dimen>
     <dimen name="all_apps_icon_top_bottom_padding">8dp</dimen>
     <dimen name="all_apps_icon_width_gap">24dp</dimen>
-    <!-- The top padding should account for the general all_apps_list_top_bottom_padding -->
-    <dimen name="all_apps_prediction_bar_top_padding">0dp</dimen>
-    <dimen name="all_apps_prediction_bar_bottom_padding">16dp</dimen>
+    <!-- The top padding should account for the existing all_apps_list_top_bottom_padding -->
+    <dimen name="all_apps_prediction_icon_top_padding">8dp</dimen>
+    <dimen name="all_apps_prediction_icon_bottom_padding">18dp</dimen>
     <dimen name="all_apps_list_top_bottom_padding">8dp</dimen>
 
 <!-- Widget tray -->
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 0651fb0..47bbf15 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -75,7 +75,11 @@
     public boolean continueMerging(AlphabeticalAppsList.SectionInfo section,
            AlphabeticalAppsList.SectionInfo withSection,
            int sectionAppCount, int numAppsPerRow, int mergeCount) {
-        // Merge EVERYTHING
+        // Don't merge the predicted apps
+        if (section.firstAppItem.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE) {
+            return false;
+        }
+        // Otherwise, merge every other section
         return true;
     }
 }
@@ -103,6 +107,11 @@
     public boolean continueMerging(AlphabeticalAppsList.SectionInfo section,
            AlphabeticalAppsList.SectionInfo withSection,
            int sectionAppCount, int numAppsPerRow, int mergeCount) {
+        // Don't merge the predicted apps
+        if (section.firstAppItem.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE) {
+            return false;
+        }
+
         // Continue merging if the number of hanging apps on the final row is less than some
         // fixed number (ragged), the merged rows has yet to exceed some minimum row count,
         // and while the number of merged sections is less than some fixed number of merges
@@ -127,17 +136,14 @@
  * The all apps view container.
  */
 public class AllAppsContainerView extends BaseContainerView implements DragSource,
-        LauncherTransitionable, AlphabeticalAppsList.AdapterChangedCallback,
-        AllAppsGridAdapter.PredictionBarSpacerCallbacks, View.OnTouchListener,
-        View.OnLongClickListener, ViewTreeObserver.OnPreDrawListener,
-        AllAppsSearchBarController.Callbacks, Stats.LaunchSourceProvider {
+        LauncherTransitionable, View.OnTouchListener, View.OnLongClickListener,
+        AllAppsSearchBarController.Callbacks {
 
     private static final int MIN_ROWS_IN_MERGED_SECTION_PHONE = 3;
     private static final int MAX_NUM_MERGES_PHONE = 2;
 
     @Thunk Launcher mLauncher;
     @Thunk AlphabeticalAppsList mApps;
-    private LayoutInflater mLayoutInflater;
     private AllAppsGridAdapter mAdapter;
     private RecyclerView.LayoutManager mLayoutManager;
     private RecyclerView.ItemDecoration mItemDecoration;
@@ -146,7 +152,6 @@
     @Thunk View mContainerView;
     @Thunk View mRevealView;
     @Thunk AllAppsRecyclerView mAppsRecyclerView;
-    @Thunk ViewGroup mPredictionBarView;
     @Thunk AllAppsSearchBarController mSearchBarController;
     private ViewGroup mSearchBarContainerView;
     private View mSearchBarView;
@@ -159,18 +164,9 @@
     private final Point mBoundsCheckLastTouchDownPos = new Point(-1, -1);
     // This coordinate is relative to its parent
     private final Point mIconLastTouchPos = new Point();
-    // This coordinate is used to proxy click and long-click events to the prediction bar icons
-    private final Point mPredictionIconTouchDownPos = new Point();
-    // Normal container insets
-    private int mPredictionBarHeight;
-    private int mLastRecyclerViewScrollPos = -1;
-    @Thunk boolean mFocusPredictionBarOnFirstBind;
 
     private SpannableStringBuilder mSearchQueryBuilder = null;
 
-    private CheckLongPressHelper mPredictionIconCheckForLongPress;
-    private View mPredictionIconUnderTouch;
-
     public AllAppsContainerView(Context context) {
         this(context, null);
     }
@@ -184,19 +180,10 @@
         Resources res = context.getResources();
 
         mLauncher = (Launcher) context;
-        mLayoutInflater = LayoutInflater.from(context);
-        DeviceProfile grid = mLauncher.getDeviceProfile();
-        mPredictionBarHeight = (int) (grid.allAppsIconSizePx + grid.iconDrawablePaddingOriginalPx +
-                Utilities.calculateTextHeight(grid.allAppsIconTextSizePx) +
-                2 * res.getDimensionPixelSize(R.dimen.all_apps_icon_top_bottom_padding) +
-                res.getDimensionPixelSize(R.dimen.all_apps_prediction_bar_top_padding) +
-                res.getDimensionPixelSize(R.dimen.all_apps_prediction_bar_bottom_padding));
         mSectionNamesMargin = res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin);
         mApps = new AlphabeticalAppsList(context);
-        mApps.setAdapterChangedCallback(this);
-        mAdapter = new AllAppsGridAdapter(context, mApps, this, this, mLauncher, this);
+        mAdapter = new AllAppsGridAdapter(context, mApps, this, mLauncher, this);
         mAdapter.setEmptySearchText(res.getString(R.string.all_apps_loading_message));
-        mAdapter.setPredictionRowHeight(mPredictionBarHeight);
         mApps.setAdapter(mAdapter);
         mLayoutManager = mAdapter.getLayoutManager();
         mItemDecoration = mAdapter.getItemDecoration();
@@ -310,17 +297,7 @@
             @Override
             public void onFocusChange(View v, boolean hasFocus) {
                 if (hasFocus) {
-                    if (!mApps.getPredictedApps().isEmpty()) {
-                        // If the prediction bar is going to be bound, then defer focusing until
-                        // it is first bound
-                        if (mPredictionBarView.getChildCount() == 0) {
-                            mFocusPredictionBarOnFirstBind = true;
-                        } else {
-                            mPredictionBarView.requestFocus();
-                        }
-                    } else {
-                        mAppsRecyclerView.requestFocus();
-                    }
+                    mAppsRecyclerView.requestFocus();
                 }
             }
         };
@@ -333,7 +310,6 @@
         // Load the all apps recycler view
         mAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.apps_list_view);
         mAppsRecyclerView.setApps(mApps);
-        mAppsRecyclerView.setPredictionBarHeight(mPredictionBarHeight);
         mAppsRecyclerView.setLayoutManager(mLayoutManager);
         mAppsRecyclerView.setAdapter(mAdapter);
         mAppsRecyclerView.setHasFixedSize(true);
@@ -341,11 +317,6 @@
             mAppsRecyclerView.addItemDecoration(mItemDecoration);
         }
 
-        // Fix the prediction bar height
-        mPredictionBarView = (ViewGroup) findViewById(R.id.prediction_bar);
-        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mPredictionBarView.getLayoutParams();
-        lp.height = mPredictionBarHeight;
-
         updateBackgroundAndPaddings();
     }
 
@@ -355,49 +326,6 @@
     }
 
     @Override
-    public void onBindPredictionBar() {
-        updatePredictionBarVisibility();
-
-        List<AppInfo> predictedApps = mApps.getPredictedApps();
-
-        // Remove extra prediction icons
-        while (mPredictionBarView.getChildCount() > mNumPredictedAppsPerRow) {
-            mPredictionBarView.removeViewAt(mPredictionBarView.getChildCount() - 1);
-        }
-
-        int childCount = mPredictionBarView.getChildCount();
-        for (int i = 0; i < mNumPredictedAppsPerRow; i++) {
-            BubbleTextView icon;
-            if (i < childCount) {
-                // If a child at that index exists, then get that child
-                icon = (BubbleTextView) mPredictionBarView.getChildAt(i);
-            } else {
-                // Otherwise, inflate a new icon
-                icon = (BubbleTextView) mLayoutInflater.inflate(
-                        R.layout.all_apps_prediction_bar_icon, mPredictionBarView, false);
-                icon.setFocusable(true);
-                icon.setLongPressTimeout(ViewConfiguration.get(getContext()).getLongPressTimeout());
-                mPredictionBarView.addView(icon);
-            }
-
-            // Either apply the app info to the child, or hide the view
-            if (i < predictedApps.size()) {
-                if (icon.getVisibility() != View.VISIBLE) {
-                    icon.setVisibility(View.VISIBLE);
-                }
-                icon.applyFromApplicationInfo(predictedApps.get(i));
-            } else {
-                icon.setVisibility(View.INVISIBLE);
-            }
-        }
-
-        if (mFocusPredictionBarOnFirstBind) {
-            mFocusPredictionBarOnFirstBind = false;
-            mPredictionBarView.requestFocus();
-        }
-    }
-
-    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         // Update the number of items in the grid before we measure the view
         int availableWidth = !mContentBounds.isEmpty() ? mContentBounds.width() :
@@ -476,22 +404,6 @@
             lp.rightMargin = (getMeasuredWidth() - searchBarBounds.right) - backgroundPadding.right;
             mSearchBarContainerView.requestLayout();
         }
-
-        // Update the prediction bar insets as well
-        mPredictionBarView = (ViewGroup) findViewById(R.id.prediction_bar);
-        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mPredictionBarView.getLayoutParams();
-        lp.leftMargin = padding.left + mAppsRecyclerView.getMaxScrollbarWidth();
-        lp.rightMargin = padding.right + mAppsRecyclerView.getMaxScrollbarWidth();
-        mPredictionBarView.requestLayout();
-    }
-
-    @Override
-    public boolean onPreDraw() {
-        if (mNumAppsPerRow > 0) {
-            // Update the position of the prediction bar to match the scroll of the all apps list
-            synchronizeToRecyclerViewScrollPosition(mAppsRecyclerView.getScrollPosition());
-        }
-        return true;
     }
 
     @Override
@@ -621,17 +533,8 @@
     }
 
     @Override
-    public void onAdapterItemsChanged() {
-        updatePredictionBarVisibility();
-    }
-
-    @Override
     public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) {
-        // Register for a pre-draw listener to synchronize the recycler view scroll to other views
-        // in this container
-        if (!toWorkspace) {
-            getViewTreeObserver().addOnPreDrawListener(this);
-        }
+        // Do nothing
     }
 
     @Override
@@ -647,38 +550,12 @@
     @Override
     public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
         if (toWorkspace) {
-            getViewTreeObserver().removeOnPreDrawListener(this);
-            mLastRecyclerViewScrollPos = -1;
-
             // Reset the search bar after transitioning home
             mSearchBarController.reset();
         }
     }
 
     /**
-     * Updates the container when the recycler view is scrolled.
-     */
-    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
-    private void synchronizeToRecyclerViewScrollPosition(int scrollY) {
-        if (mLastRecyclerViewScrollPos != scrollY) {
-            mLastRecyclerViewScrollPos = scrollY;
-
-            // Scroll the prediction bar with the contents of the recycler view
-            mPredictionBarView.setTranslationY(-scrollY + mAppsRecyclerView.getPaddingTop());
-        }
-    }
-
-    @Override
-    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
-        // If we were waiting for long-click, cancel the request once a child has started handling
-        // the scrolling
-        if (mPredictionIconCheckForLongPress != null) {
-            mPredictionIconCheckForLongPress.cancelLongPress();
-        }
-        super.requestDisallowInterceptTouchEvent(disallowIntercept);
-    }
-
-    /**
      * Handles the touch events to dismiss all apps when clicking outside the bounds of the
      * recycler view.
      */
@@ -689,19 +566,6 @@
 
         switch (ev.getAction()) {
             case MotionEvent.ACTION_DOWN:
-                // We workaround the fact that the recycler view needs the touches for the scroll
-                // and we want to intercept it for clicks in the prediction bar by handling clicks
-                // and long clicks in the prediction bar ourselves.
-                if (mPredictionBarView != null && mPredictionBarView.getVisibility() == View.VISIBLE) {
-                    mPredictionIconTouchDownPos.set(x, y);
-                    mPredictionIconUnderTouch = findPredictedAppAtCoordinate(x, y);
-                    if (mPredictionIconUnderTouch != null) {
-                        mPredictionIconCheckForLongPress =
-                                new CheckLongPressHelper(mPredictionIconUnderTouch, this);
-                        mPredictionIconCheckForLongPress.postCheckForLongPress();
-                    }
-                }
-
                 if (!mContentBounds.isEmpty()) {
                     // Outset the fixed bounds and check if the touch is outside all apps
                     Rect tmpRect = new Rect(mContentBounds);
@@ -719,19 +583,6 @@
                     }
                 }
                 break;
-            case MotionEvent.ACTION_MOVE:
-                if (mPredictionIconUnderTouch != null) {
-                    float dist = (float) Math.hypot(x - mPredictionIconTouchDownPos.x,
-                            y - mPredictionIconTouchDownPos.y);
-                    if (dist > ViewConfiguration.get(getContext()).getScaledTouchSlop()) {
-                        if (mPredictionIconCheckForLongPress != null) {
-                            mPredictionIconCheckForLongPress.cancelLongPress();
-                        }
-                        mPredictionIconCheckForLongPress = null;
-                        mPredictionIconUnderTouch = null;
-                    }
-                }
-                break;
             case MotionEvent.ACTION_UP:
                 if (mBoundsCheckLastTouchDownPos.x > -1) {
                     ViewConfiguration viewConfig = ViewConfiguration.get(getContext());
@@ -745,26 +596,9 @@
                         return true;
                     }
                 }
-
-                // Trigger the click on the prediction bar icon if that's where we touched
-                if (mPredictionIconUnderTouch != null &&
-                        !mPredictionIconCheckForLongPress.hasPerformedLongPress()) {
-                    mLauncher.onClick(mPredictionIconUnderTouch);
-                }
-
                 // Fall through
             case MotionEvent.ACTION_CANCEL:
                 mBoundsCheckLastTouchDownPos.set(-1, -1);
-                mPredictionIconTouchDownPos.set(-1, -1);
-
-                // On touch up/cancel, cancel the long press on the prediction bar icon if it has
-                // not yet been performed
-                if (mPredictionIconCheckForLongPress != null) {
-                    mPredictionIconCheckForLongPress.cancelLongPress();
-                    mPredictionIconCheckForLongPress = null;
-                }
-                mPredictionIconUnderTouch = null;
-
                 break;
         }
         return false;
@@ -792,59 +626,4 @@
         mSearchQueryBuilder.clearSpans();
         Selection.setSelection(mSearchQueryBuilder, 0);
     }
-
-    @Override
-    public void fillInLaunchSourceData(Bundle sourceData) {
-        // Since the other cases are caught by the AllAppsRecyclerView LaunchSourceProvider, we just
-        // handle the prediction bar icons here
-        sourceData.putString(Stats.SOURCE_EXTRA_CONTAINER, Stats.CONTAINER_ALL_APPS);
-        sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER,
-                Stats.SUB_CONTAINER_ALL_APPS_PREDICTION);
-    }
-
-    /**
-     * Returns the predicted app in the prediction bar given a set of local coordinates.
-     */
-    private View findPredictedAppAtCoordinate(int x, int y) {
-        Rect hitRect = new Rect();
-
-        // Ensure that are touching in the recycler view
-        int[] coord = {x, y};
-        Utilities.mapCoordInSelfToDescendent(mAppsRecyclerView, this, coord);
-        mAppsRecyclerView.getHitRect(hitRect);
-        if (!hitRect.contains(coord[0], coord[1])) {
-            return null;
-        }
-
-        // Check against the children of the prediction bar
-        coord[0] = x;
-        coord[1] = y;
-        Utilities.mapCoordInSelfToDescendent(mPredictionBarView, this, coord);
-        for (int i = 0; i < mPredictionBarView.getChildCount(); i++) {
-            View child = mPredictionBarView.getChildAt(i);
-            if (child.getVisibility() != View.VISIBLE) {
-                continue;
-            }
-            child.getHitRect(hitRect);
-            if (hitRect.contains(coord[0], coord[1])) {
-                return child;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Updates the visibility of the prediction bar.
-     * @return whether the prediction bar is visible
-     */
-    private boolean updatePredictionBarVisibility() {
-        boolean showPredictionBar = !mApps.getPredictedApps().isEmpty() &&
-                (!mApps.hasFilter() || mSearchBarController.shouldShowPredictionBar());
-        if (showPredictionBar) {
-            mPredictionBarView.setVisibility(View.VISIBLE);
-        } else if (!showPredictionBar) {
-            mPredictionBarView.setVisibility(View.INVISIBLE);
-        }
-        return showPredictionBar;
-    }
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 19e2757..057883c 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -31,7 +31,6 @@
 import android.widget.TextView;
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.Thunk;
@@ -52,17 +51,10 @@
     public static final int SECTION_BREAK_VIEW_TYPE = 0;
     // A normal icon
     public static final int ICON_VIEW_TYPE = 1;
+    // A prediction icon
+    public static final int PREDICTION_ICON_VIEW_TYPE = 2;
     // The message shown when there are no filtered results
-    public static final int EMPTY_SEARCH_VIEW_TYPE = 2;
-    // The spacer used for the prediction bar
-    public static final int PREDICTION_BAR_SPACER_TYPE = 3;
-
-    /**
-     * Callback for when the prediction bar spacer is bound.
-     */
-    public interface PredictionBarSpacerCallbacks {
-        void onBindPredictionBar();
-    }
+    public static final int EMPTY_SEARCH_VIEW_TYPE = 3;
 
     /**
      * ViewHolder for each icon.
@@ -93,11 +85,13 @@
                 return mAppsPerRow;
             }
 
-            if (mApps.getAdapterItems().get(position).viewType != AllAppsGridAdapter.ICON_VIEW_TYPE) {
-                // Both the section breaks and predictive bar span the full width
-                return mAppsPerRow;
-            } else {
-                return 1;
+            switch (mApps.getAdapterItems().get(position).viewType) {
+                case AllAppsGridAdapter.ICON_VIEW_TYPE:
+                case AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE:
+                    return 1;
+                default:
+                    // Section breaks span the full width
+                    return mAppsPerRow;
             }
         }
     }
@@ -141,7 +135,7 @@
 
                 if (shouldDrawItemDivider(holder, items) && !hasDrawnPredictedAppsDivider) {
                     // Draw the divider under the predicted apps
-                    int top = child.getTop() + child.getHeight() - mPredictionBarBottomPadding / 2;
+                    int top = child.getTop() + child.getHeight() + mPredictionBarDividerOffset;
                     c.drawLine(mBackgroundPadding.left, top,
                             parent.getWidth() - mBackgroundPadding.right, top,
                             mPredictedAppsDividerPaint);
@@ -264,7 +258,7 @@
         private boolean shouldDrawItemDivider(ViewHolder holder,
                 List<AlphabeticalAppsList.AdapterItem> items) {
             int pos = holder.getPosition();
-            return items.get(pos).viewType == AllAppsGridAdapter.PREDICTION_BAR_SPACER_TYPE;
+            return items.get(pos).viewType == AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE;
         }
 
         /**
@@ -285,19 +279,16 @@
         }
     }
 
-    private Handler mHandler;
     private LayoutInflater mLayoutInflater;
     @Thunk AlphabeticalAppsList mApps;
     private GridLayoutManager mGridLayoutMgr;
     private GridSpanSizer mGridSizer;
     private GridItemDecoration mItemDecoration;
-    @Thunk PredictionBarSpacerCallbacks mPredictionBarCb;
     private View.OnTouchListener mTouchListener;
     private View.OnClickListener mIconClickListener;
     private View.OnLongClickListener mIconLongClickListener;
     @Thunk final Rect mBackgroundPadding = new Rect();
-    @Thunk int mPredictionBarHeight;
-    @Thunk int mPredictionBarBottomPadding;
+    @Thunk int mPredictionBarDividerOffset;
     @Thunk int mAppsPerRow;
     @Thunk boolean mIsRtl;
     private String mEmptySearchText;
@@ -309,12 +300,10 @@
     @Thunk Paint mPredictedAppsDividerPaint;
 
     public AllAppsGridAdapter(Context context, AlphabeticalAppsList apps,
-            PredictionBarSpacerCallbacks pbCb, View.OnTouchListener touchListener,
-            View.OnClickListener iconClickListener, View.OnLongClickListener iconLongClickListener) {
+            View.OnTouchListener touchListener, View.OnClickListener iconClickListener,
+            View.OnLongClickListener iconLongClickListener) {
         Resources res = context.getResources();
-        mHandler = new Handler();
         mApps = apps;
-        mPredictionBarCb = pbCb;
         mGridSizer = new GridSpanSizer();
         mGridLayoutMgr = new GridLayoutManager(context, 1, GridLayoutManager.VERTICAL, false);
         mGridLayoutMgr.setSpanSizeLookup(mGridSizer);
@@ -336,8 +325,9 @@
         mPredictedAppsDividerPaint.setStrokeWidth(Utilities.pxFromDp(1f, res.getDisplayMetrics()));
         mPredictedAppsDividerPaint.setColor(0x1E000000);
         mPredictedAppsDividerPaint.setAntiAlias(true);
-        mPredictionBarBottomPadding =
-                res.getDimensionPixelSize(R.dimen.all_apps_prediction_bar_bottom_padding);
+        mPredictionBarDividerOffset =
+                (-res.getDimensionPixelSize(R.dimen.all_apps_prediction_icon_bottom_padding) +
+                        res.getDimensionPixelSize(R.dimen.all_apps_icon_top_bottom_padding)) / 2;
     }
 
     /**
@@ -349,13 +339,6 @@
     }
 
     /**
-     * Sets the prediction row height.
-     */
-    public void setPredictionRowHeight(int height) {
-        mPredictionBarHeight = height;
-    }
-
-    /**
      * Sets whether we are in RTL mode.
      */
     public void setRtl(boolean rtl) {
@@ -400,14 +383,7 @@
                         false));
             case SECTION_BREAK_VIEW_TYPE:
                 return new ViewHolder(new View(parent.getContext()));
-            case PREDICTION_BAR_SPACER_TYPE:
-                // Create a view of a specific height to match the floating prediction bar
-                View v = new View(parent.getContext());
-                ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
-                        ViewGroup.LayoutParams.MATCH_PARENT, mPredictionBarHeight);
-                v.setLayoutParams(lp);
-                return new ViewHolder(v);
-            case ICON_VIEW_TYPE:
+            case ICON_VIEW_TYPE: {
                 BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
                         R.layout.all_apps_icon, parent, false);
                 icon.setOnTouchListener(mTouchListener);
@@ -417,6 +393,18 @@
                         .getLongPressTimeout());
                 icon.setFocusable(true);
                 return new ViewHolder(icon);
+            }
+            case PREDICTION_ICON_VIEW_TYPE: {
+                BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
+                        R.layout.all_apps_prediction_bar_icon, parent, false);
+                icon.setOnTouchListener(mTouchListener);
+                icon.setOnClickListener(mIconClickListener);
+                icon.setOnLongClickListener(mIconLongClickListener);
+                icon.setLongPressTimeout(ViewConfiguration.get(parent.getContext())
+                        .getLongPressTimeout());
+                icon.setFocusable(true);
+                return new ViewHolder(icon);
+            }
             default:
                 throw new RuntimeException("Unexpected view type");
         }
@@ -425,21 +413,18 @@
     @Override
     public void onBindViewHolder(ViewHolder holder, int position) {
         switch (holder.getItemViewType()) {
-            case ICON_VIEW_TYPE:
+            case ICON_VIEW_TYPE: {
                 AppInfo info = mApps.getAdapterItems().get(position).appInfo;
                 BubbleTextView icon = (BubbleTextView) holder.mContent;
                 icon.applyFromApplicationInfo(info);
                 break;
-            case PREDICTION_BAR_SPACER_TYPE:
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        if (mPredictionBarCb != null) {
-                            mPredictionBarCb.onBindPredictionBar();
-                        }
-                    }
-                });
+            }
+            case PREDICTION_ICON_VIEW_TYPE: {
+                AppInfo info = mApps.getAdapterItems().get(position).appInfo;
+                BubbleTextView icon = (BubbleTextView) holder.mContent;
+                icon.applyFromApplicationInfo(info);
                 break;
+            }
             case EMPTY_SEARCH_VIEW_TYPE:
                 TextView emptyViewText = (TextView) holder.mContent.findViewById(R.id.empty_text);
                 emptyViewText.setText(mEmptySearchText);
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 988ecdd..730c8d1 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -27,6 +27,7 @@
 import com.android.launcher3.BaseRecyclerViewFastScrollBar;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Stats;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.util.Thunk;
 
 import java.util.List;
@@ -45,7 +46,6 @@
 
     private AlphabeticalAppsList mApps;
     private int mNumAppsPerRow;
-    private int mPredictionBarHeight;
 
     @Thunk BaseRecyclerViewFastScrollBar.FastScrollFocusableView mLastFastScrollFocusedView;
     @Thunk int mPrevFastScrollFocusedPosition;
@@ -89,20 +89,13 @@
 
         RecyclerView.RecycledViewPool pool = getRecycledViewPool();
         int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx);
-        pool.setMaxRecycledViews(AllAppsGridAdapter.PREDICTION_BAR_SPACER_TYPE, 1);
         pool.setMaxRecycledViews(AllAppsGridAdapter.EMPTY_SEARCH_VIEW_TYPE, 1);
         pool.setMaxRecycledViews(AllAppsGridAdapter.ICON_VIEW_TYPE, approxRows * mNumAppsPerRow);
+        pool.setMaxRecycledViews(AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE, mNumAppsPerRow);
         pool.setMaxRecycledViews(AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE, approxRows);
     }
 
     /**
-     * Sets the prediction bar height.
-     */
-    public void setPredictionBarHeight(int height) {
-        mPredictionBarHeight = height;
-    }
-
-    /**
      * Scrolls this recycler view to the top.
      */
     public void scrollToTop() {
@@ -110,21 +103,6 @@
     }
 
     /**
-     * Returns the current scroll position.
-     */
-    public int getScrollPosition() {
-        List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
-        getCurScrollState(mScrollPosState, items);
-        if (mScrollPosState.rowIndex != -1) {
-            int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight;
-            return getPaddingTop() + predictionBarHeight +
-                    (mScrollPosState.rowIndex * mScrollPosState.rowHeight) -
-                            mScrollPosState.rowTopOffset;
-        }
-        return 0;
-    }
-
-    /**
      * We need to override the draw to ensure that we don't draw the overscroll effect beyond the
      * background bounds.
      */
@@ -189,9 +167,7 @@
 
         // Map the touch position back to the scroll of the recycler view
         getCurScrollState(mScrollPosState, mApps.getAdapterItems());
-        int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight;
-        int availableScrollHeight = getAvailableScrollHeight(rowCount, mScrollPosState.rowHeight,
-                predictionBarHeight);
+        int availableScrollHeight = getAvailableScrollHeight(rowCount, mScrollPosState.rowHeight, 0);
         LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager();
         if (mFastScrollMode == FAST_SCROLL_MODE_FREE_SCROLL) {
             layoutManager.scrollToPositionWithOffset(0, (int) -(availableScrollHeight * touchFraction));
@@ -255,8 +231,7 @@
             return;
         }
 
-        int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight;
-        synchronizeScrollBarThumbOffsetToViewScroll(mScrollPosState, rowCount, predictionBarHeight);
+        synchronizeScrollBarThumbOffsetToViewScroll(mScrollPosState, rowCount, 0);
     }
 
     /**
@@ -293,8 +268,7 @@
 
         // Calculate the full animation from the current scroll position to the final scroll
         // position, and then run the animation for the duration.
-        int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight;
-        int curScrollY = getPaddingTop() + predictionBarHeight +
+        int curScrollY = getPaddingTop() +
                 (scrollPosState.rowIndex * scrollPosState.rowHeight) - scrollPosState.rowTopOffset;
         int newScrollY = getScrollAtPosition(position, scrollPosState.rowHeight);
         int numFrames = mFastScrollFrames.length;
@@ -307,8 +281,7 @@
     }
 
     /**
-     * Returns the current scroll state of the apps rows, not including the prediction
-     * bar.
+     * Returns the current scroll state of the apps rows.
      */
     private void getCurScrollState(ScrollPositionState stateOut,
             List<AlphabeticalAppsList.AdapterItem> items) {
@@ -327,7 +300,8 @@
             int position = getChildPosition(child);
             if (position != NO_POSITION) {
                 AlphabeticalAppsList.AdapterItem item = items.get(position);
-                if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE) {
+                if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE ||
+                        item.viewType == AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) {
                     stateOut.rowIndex = item.rowIndex;
                     stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child);
                     stateOut.rowHeight = child.getHeight();
@@ -342,9 +316,10 @@
      */
     private int getScrollAtPosition(int position, int rowHeight) {
         AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position);
-        if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE) {
-            int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight;
-            return getPaddingTop() + predictionBarHeight + item.rowIndex * rowHeight;
+        if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE ||
+                item.viewType == AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) {
+            int offset = item.rowIndex > 0 ? getPaddingTop() : 0;
+            return offset + item.rowIndex * rowHeight;
         } else {
             return 0;
         }
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
index 1c51ab7..14e2a18 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
@@ -37,7 +37,6 @@
         implements BubbleTextShadowHandler {
 
     private final ClickShadowView mTouchFeedbackView;
-    private View mPredictionBarView;
 
     public AllAppsRecyclerViewContainerView(Context context) {
         this(context, null);
@@ -61,13 +60,6 @@
     }
 
     @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-
-        mPredictionBarView = findViewById(R.id.prediction_bar);
-    }
-
-    @Override
     public void setPressedIcon(BubbleTextView icon, Bitmap background) {
         if (icon == null || background == null) {
             mTouchFeedbackView.setBitmap(null);
@@ -77,33 +69,4 @@
             mTouchFeedbackView.animateShadow();
         }
     }
-
-    /**
-     * This allows us to have custom drawing order, while keeping touch handling in correct z-order.
-     */
-    @Override
-    protected void dispatchDraw(Canvas canvas) {
-        final long drawingTime = getDrawingTime();
-
-        // Draw the click feedback first (since it is always on the bottom)
-        if (mTouchFeedbackView != null && mTouchFeedbackView.getVisibility() == View.VISIBLE) {
-            drawChild(canvas, mTouchFeedbackView, drawingTime);
-        }
-
-        // Then draw the prediction bar, since it needs to be "under" the recycler view to get the
-        // right edge effect to be drawn over it
-        if (mPredictionBarView != null && mPredictionBarView.getVisibility() == View.VISIBLE) {
-            drawChild(canvas, mPredictionBarView, drawingTime);
-        }
-
-        // Draw the remaining views
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View v = getChildAt(i);
-            if (v != mTouchFeedbackView && v != mPredictionBarView &&
-                    v.getVisibility() == View.VISIBLE) {
-                drawChild(canvas, v, drawingTime);
-            }
-        }
-    }
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
index 341539c..2b363c0 100644
--- a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
@@ -72,6 +72,7 @@
      * Returns whether the prediction bar should currently be visible depending on the state of
      * the search bar.
      */
+    @Deprecated
     public abstract boolean shouldShowPredictionBar();
 
     /**
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index ea99872..47241ce 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -110,10 +110,10 @@
             return item;
         }
 
-        public static AdapterItem asPredictionBarSpacer(int pos) {
-            AdapterItem item = new AdapterItem();
-            item.viewType = AllAppsGridAdapter.PREDICTION_BAR_SPACER_TYPE;
-            item.position = pos;
+        public static AdapterItem asPredictedApp(int pos, SectionInfo section, String sectionName,
+                                        int sectionAppIndex, AppInfo appInfo, int appIndex) {
+            AdapterItem item = asApp(pos, section, sectionName, sectionAppIndex, appInfo, appIndex);
+            item.viewType = AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE;
             return item;
         }
 
@@ -132,13 +132,6 @@
     }
 
     /**
-     * Callback to notify when the set of adapter items have changed.
-     */
-    public interface AdapterChangedCallback {
-        void onAdapterItemsChanged();
-    }
-
-    /**
      * Common interface for different merging strategies.
      */
     public interface MergeAlgorithm {
@@ -171,7 +164,6 @@
     private AlphabeticIndexCompat mIndexer;
     private AppNameComparator mAppNameComparator;
     private MergeAlgorithm mMergeAlgorithm;
-    private AdapterChangedCallback mAdapterChangedCallback;
     private int mNumAppsPerRow;
     private int mNumPredictedAppsPerRow;
     private int mNumAppRowsInAdapter;
@@ -183,13 +175,6 @@
     }
 
     /**
-     * Sets the apps updated callback.
-     */
-    public void setAdapterChangedCallback(AdapterChangedCallback cb) {
-        mAdapterChangedCallback = cb;
-    }
-
-    /**
      * Sets the number of apps per row.
      */
     public void setNumAppsPerRow(int numAppsPerRow, int numPredictedAppsPerRow,
@@ -285,13 +270,6 @@
     }
 
     /**
-     * Returns the current set of predicted apps.
-     */
-    public List<AppInfo> getPredictedApps() {
-        return mPredictedApps;
-    }
-
-    /**
      * Sets the current set of apps.
      */
     public void setApps(List<AppInfo> apps) {
@@ -426,13 +404,25 @@
             }
 
             if (!mPredictedApps.isEmpty()) {
-                // Create a new spacer for the prediction bar
-                AdapterItem sectionItem = AdapterItem.asPredictionBarSpacer(position++);
-                mAdapterItems.add(sectionItem);
-                // Add a fastscroller section for the prediction bar
+                // Add a section for the predictions
+                lastSectionInfo = new SectionInfo();
                 lastFastScrollerSectionInfo = new FastScrollSectionInfo("");
-                lastFastScrollerSectionInfo.fastScrollToItem = sectionItem;
+                AdapterItem sectionItem = AdapterItem.asSectionBreak(position++, lastSectionInfo);
+                mSections.add(lastSectionInfo);
                 mFastScrollerSections.add(lastFastScrollerSectionInfo);
+                mAdapterItems.add(sectionItem);
+
+                // Add the predicted app items
+                for (AppInfo info : mPredictedApps) {
+                    AdapterItem appItem = AdapterItem.asPredictedApp(position++, lastSectionInfo,
+                            "", lastSectionInfo.numApps++, info, appIndex++);
+                    if (lastSectionInfo.firstAppItem == null) {
+                        lastSectionInfo.firstAppItem = appItem;
+                        lastFastScrollerSectionInfo.fastScrollToItem = appItem;
+                    }
+                    mAdapterItems.add(appItem);
+                    mFilteredApps.add(info);
+                }
             }
         }
 
@@ -480,7 +470,8 @@
                 item.rowIndex = 0;
                 if (item.viewType == AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE) {
                     numAppsInSection = 0;
-                } else if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE) {
+                } else if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE ||
+                        item.viewType == AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) {
                     if (numAppsInSection % mNumAppsPerRow == 0) {
                         numAppsInRow = 0;
                         rowIndex++;
@@ -493,19 +484,18 @@
             }
             mNumAppRowsInAdapter = rowIndex + 1;
 
-            // Pre-calculate all the fast scroller fractions based on the number of rows, if we have
-            // predicted apps, then we should account for that as a row in the touchFraction
-            float rowFraction = 1f / (mNumAppRowsInAdapter + (mPredictedApps.isEmpty() ? 0 : 1));
-            float initialOffset = mPredictedApps.isEmpty() ? 0 : rowFraction;
+            // Pre-calculate all the fast scroller fractions based on the number of rows
+            float rowFraction = 1f / mNumAppRowsInAdapter;
             for (FastScrollSectionInfo info : mFastScrollerSections) {
                 AdapterItem item = info.fastScrollToItem;
-                if (item.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE) {
+                if (item.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE &&
+                        item.viewType != AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) {
                     info.touchFraction = 0f;
                     continue;
                 }
 
                 float subRowFraction = item.rowAppIndex * (rowFraction / mNumAppsPerRow);
-                info.touchFraction = initialOffset + item.rowIndex * rowFraction + subRowFraction;
+                info.touchFraction = item.rowIndex * rowFraction + subRowFraction;
             }
         }
 
@@ -513,10 +503,6 @@
         if (mAdapter != null) {
             mAdapter.notifyDataSetChanged();
         }
-
-        if (mAdapterChangedCallback != null) {
-            mAdapterChangedCallback.onAdapterItemsChanged();
-        }
     }
 
     private List<AppInfo> getFiltersAppInfos() {
diff --git a/src/com/android/launcher3/allapps/DefaultAppSearchController.java b/src/com/android/launcher3/allapps/DefaultAppSearchController.java
index 20924af..83b9205 100644
--- a/src/com/android/launcher3/allapps/DefaultAppSearchController.java
+++ b/src/com/android/launcher3/allapps/DefaultAppSearchController.java
@@ -125,8 +125,7 @@
 
     @Override
     public boolean shouldShowPredictionBar() {
-        // Keep showing the prediction bar if the input query is empty
-        return mSearchBarEditView.getEditableText().toString().isEmpty();
+        return false;
     }
 
     @Override