Merge "Various icon size changes" into ub-launcher3-burnaby
diff --git a/res/layout/apps_grid_row_icon_view.xml b/res/layout/apps_grid_row_icon_view.xml
index 286deb4..acb3da3 100644
--- a/res/layout/apps_grid_row_icon_view.xml
+++ b/res/layout/apps_grid_row_icon_view.xml
@@ -21,8 +21,8 @@
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_gravity="left|center_vertical"
-    android:paddingTop="8dp"
-    android:paddingBottom="8dp"
+    android:paddingTop="@dimen/apps_icon_top_bottom_padding"
+    android:paddingBottom="@dimen/apps_icon_top_bottom_padding"
     android:focusable="true"
     android:background="@drawable/focusable_view_bg"
     launcher:deferShadowGeneration="true"
diff --git a/res/layout/apps_list_view.xml b/res/layout/apps_list_view.xml
index a726cd8..ddcb639 100644
--- a/res/layout/apps_list_view.xml
+++ b/res/layout/apps_list_view.xml
@@ -25,7 +25,7 @@
     <FrameLayout
         android:id="@+id/header"
         android:layout_width="match_parent"
-        android:layout_height="52dp"
+        android:layout_height="@dimen/apps_search_bar_height"
         android:orientation="horizontal"
         android:background="@drawable/apps_search_bg">
         <LinearLayout
@@ -39,6 +39,7 @@
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_gravity="start|center_vertical"
+                android:layout_marginStart="4dp"
                 android:paddingTop="12dp"
                 android:paddingBottom="12dp"
                 android:contentDescription="@string/all_apps_button_label"
@@ -67,6 +68,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="end|center_vertical"
+            android:layout_marginEnd="6dp"
             android:paddingTop="12dp"
             android:paddingBottom="12dp"
             android:contentDescription="@string/apps_view_search_bar_hint"
diff --git a/res/layout/widget_cell.xml b/res/layout/widget_cell.xml
index ab23b84..196dfca 100644
--- a/res/layout/widget_cell.xml
+++ b/res/layout/widget_cell.xml
@@ -19,10 +19,11 @@
     android:layout_width="@dimen/widget_preview_container_width"
     android:layout_height="@dimen/widget_cell_height"
     android:layout_weight="1"
-    android:layout_marginRight="@dimen/widget_row_divider"
+    android:layout_marginEnd="@dimen/widget_row_divider"
     android:orientation="vertical"
     android:background="@color/widgets_cell_color"
-    android:focusable="true">
+    android:focusable="true"
+    android:gravity="center_horizontal">
 
     <LinearLayout
         android:layout_width="wrap_content"
@@ -43,7 +44,6 @@
             android:singleLine="true"
             android:ellipsize="end"
             android:fadingEdge="horizontal"
-
             android:textColor="@color/widgets_view_item_text_color"
             android:textSize="16sp"
             android:textAlignment="viewStart"
diff --git a/res/layout/widgets_list_row_view.xml b/res/layout/widgets_list_row_view.xml
index 12f6401..ea95d24 100644
--- a/res/layout/widgets_list_row_view.xml
+++ b/res/layout/widgets_list_row_view.xml
@@ -55,17 +55,18 @@
     </LinearLayout>
 
     <!--  Widget list -->
-    <RelativeLayout
+    <com.android.launcher3.widget.WidgetRowView
+        android:id="@+id/widget_row"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
+        android:layout_height="@dimen/widget_cell_height"
         android:layout_gravity="end"
+        android:layout_marginLeft="@dimen/widget_row_padding"
         android:background="@color/widgets_cell_color"
         >
         <HorizontalScrollView
             android:id="@+id/widgets_scroll_container"
             android:layout_width="match_parent"
-            android:layout_height="@dimen/widget_cell_height"
-            android:layout_marginLeft="@dimen/widget_row_padding"
+            android:layout_height="match_parent"
             android:scrollbars="none" >
             <LinearLayout
                 android:id="@+id/widgets_cell_list"
@@ -74,5 +75,5 @@
                 android:orientation="horizontal"
                 android:background="@color/widget_text_panel"/>
         </HorizontalScrollView>
-    </RelativeLayout>
+    </com.android.launcher3.widget.WidgetRowView>
 </LinearLayout>
diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml
index 9ecd07dd..b184642 100644
--- a/res/values-sw600dp/dimens.xml
+++ b/res/values-sw600dp/dimens.xml
@@ -22,6 +22,7 @@
     <dimen name="apps_grid_view_start_margin">64dp</dimen>
     <dimen name="apps_view_section_text_size">26sp</dimen>
     <dimen name="apps_view_row_height">76dp</dimen>
+    <dimen name="apps_icon_top_bottom_padding">12dp</dimen>
 
 <!-- AppsCustomize -->
     <dimen name="widget_preview_label_margin_top">8dp</dimen>
diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml
index 68fc1ec..cec6b7d 100644
--- a/res/values-sw720dp/dimens.xml
+++ b/res/values-sw720dp/dimens.xml
@@ -16,6 +16,8 @@
 
 <resources>
     <dimen name="app_icon_size">72dp</dimen>
+    <dimen name="apps_search_bar_height">56dp</dimen>
+    <dimen name="apps_icon_top_bottom_padding">16dp</dimen>
 
 <!-- QSB -->
     <dimen name="toolbar_button_vertical_padding">8dip</dimen>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 4d33b88..5447371 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -59,6 +59,8 @@
     <dimen name="apps_view_fast_scroll_scrubber_touch_inset">-16dp</dimen>
     <dimen name="apps_view_fast_scroll_popup_size">64dp</dimen>
     <dimen name="apps_view_fast_scroll_text_size">40dp</dimen>
+    <dimen name="apps_search_bar_height">52dp</dimen>
+    <dimen name="apps_icon_top_bottom_padding">8dp</dimen>
 
     <!-- Note: This needs to match the fixed insets for the search box. -->
     <dimen name="container_fixed_bounds_inset">8dp</dimen>
@@ -86,7 +88,7 @@
 
 <!-- Widget tray -->
     <dimen name="widget_container_inset">8dp</dimen>
-    <dimen name="widget_preview_size">120dp</dimen>
+    <dimen name="widget_preview_size">130dp</dimen>
     <dimen name="widget_preview_padding_top">8dp</dimen>
     <dimen name="widget_preview_label_vertical_padding">8dp</dimen>
     <dimen name="widget_preview_label_horizontal_padding">8dp</dimen>
@@ -98,10 +100,10 @@
     <dimen name="widget_section_icon_horizontal_padding">16dp</dimen>
 
     <!-- Equation: widget_preview_size + 2 * widget_preview_padding_horizontal -->
-    <dimen name="widget_preview_container_width">136dp</dimen>
+    <dimen name="widget_preview_container_width">146dp</dimen>
     <dimen name="widget_cell_height">150dp</dimen>
     <dimen name="widget_row_padding">8dp</dimen>
-    <dimen name="widget_row_divider">1dp</dimen>
+    <dimen name="widget_row_divider">2dp</dimen>
 
     <!-- Padding applied to shortcut previews -->
     <dimen name="shortcut_preview_padding_left">0dp</dimen>
diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java
index f075e41..62cb237 100644
--- a/src/com/android/launcher3/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/AlphabeticalAppsList.java
@@ -130,17 +130,21 @@
         public AppInfo appInfo = null;
         // The index of this app not including sections
         public int appIndex = -1;
+        // Whether or not this is a predicted app
+        public boolean isPredictedApp;
 
         public static AdapterItem asSectionBreak(int pos, SectionInfo section) {
             AdapterItem item = new AdapterItem();
             item.position = pos;
             item.isSectionHeader = true;
             item.sectionInfo = section;
+            section.sectionBreakItem = item;
             return item;
         }
 
         public static AdapterItem asApp(int pos, SectionInfo section, String sectionName,
-                                        int sectionAppIndex, AppInfo appInfo, int appIndex) {
+                                        int sectionAppIndex, AppInfo appInfo, int appIndex,
+                                        boolean isPredictedApp) {
             AdapterItem item = new AdapterItem();
             item.position = pos;
             item.isSectionHeader = false;
@@ -149,6 +153,7 @@
             item.sectionAppIndex = sectionAppIndex;
             item.appInfo = appInfo;
             item.appIndex = appIndex;
+            item.isPredictedApp = isPredictedApp;
             return item;
         }
     }
@@ -168,6 +173,7 @@
     private List<AdapterItem> mSectionedFilteredApps = new ArrayList<>();
     private List<SectionInfo> mSections = new ArrayList<>();
     private List<FastScrollSectionInfo> mFastScrollerSections = new ArrayList<>();
+    private List<ComponentName> mPredictedApps = new ArrayList<>();
     private RecyclerView.Adapter mAdapter;
     private Filter mFilter;
     private AlphabeticIndexCompat mIndexer;
@@ -252,6 +258,17 @@
     }
 
     /**
+     * Sets the current set of predicted apps.  Since this can be called before we get the full set
+     * of applications, we should merge the results only in onAppsUpdated() which is idempotent.
+     */
+    public void setPredictedApps(List<ComponentName> apps) {
+        mPredictedApps.clear();
+        mPredictedApps.addAll(apps);
+        onAppsUpdated();
+        mAdapter.notifyDataSetChanged();
+    }
+
+    /**
      * Sets the current set of apps.
      */
     public void setApps(List<AppInfo> apps) {
@@ -336,7 +353,7 @@
         // Sort the list of apps
         Collections.sort(mApps, mAppNameComparator.getComparator());
 
-        // Recreate the filtered and sectioned apps (for convenience for the grid layout)
+        // Prepare to update the list of sections, filtered apps, etc.
         mFilteredApps.clear();
         mSections.clear();
         mSectionedFilteredApps.clear();
@@ -346,36 +363,62 @@
         FastScrollSectionInfo lastFastScrollerSectionInfo = null;
         int position = 0;
         int appIndex = 0;
-        int numApps = mApps.size();
-        for (AppInfo info : mApps) {
-            String sectionName = mIndexer.computeSectionName(info.title);
+        List<AppInfo> allApps = new ArrayList<>();
+
+        // Add the predicted apps to the combined list
+        int numPredictedApps = 0;
+        if (mPredictedApps != null && !mPredictedApps.isEmpty() && !hasFilter()) {
+            for (ComponentName cn : mPredictedApps) {
+                for (AppInfo info : mApps) {
+                    if (cn.equals(info.componentName)) {
+                        allApps.add(info);
+                        numPredictedApps++;
+                        break;
+                    }
+                }
+                // Stop at the number of predicted apps
+                if (numPredictedApps == mNumAppsPerRow) {
+                    break;
+                }
+            }
+        }
+
+        // Add all the other apps to the combined list
+        allApps.addAll(mApps);
+
+        // Recreate the filtered and sectioned apps (for convenience for the grid layout) from the
+        // combined list
+        int numApps = allApps.size();
+        for (int i = 0; i < numApps; i++) {
+            boolean isPredictedApp = i < numPredictedApps;
+            AppInfo info = allApps.get(i);
+            String sectionName = isPredictedApp ? "" : mIndexer.computeSectionName(info.title);
 
             // Check if we want to retain this app
             if (mFilter != null && !mFilter.retainApp(info, sectionName)) {
                 continue;
             }
 
-            // Create a new section if necessary
-            if (lastSectionInfo == null || !sectionName.equals(lastSectionName)) {
+            // Create a new section if the section names do not match
+            if (lastSectionInfo == null ||
+                    (!isPredictedApp && !sectionName.equals(lastSectionName))) {
                 lastSectionName = sectionName;
                 lastSectionInfo = new SectionInfo();
-                mSections.add(lastSectionInfo);
                 lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName,
                         (float) appIndex / numApps);
+                mSections.add(lastSectionInfo);
                 mFastScrollerSections.add(lastFastScrollerSectionInfo);
 
-                // Create a new section item, this item is used to break the flow of items in the
-                // list
-                AdapterItem sectionItem = AdapterItem.asSectionBreak(position++, lastSectionInfo);
+                // Create a new section item to break the flow of items in the list
                 if (!AppsContainerView.GRID_HIDE_SECTION_HEADERS && !hasFilter()) {
-                    lastSectionInfo.sectionBreakItem = sectionItem;
+                    AdapterItem sectionItem = AdapterItem.asSectionBreak(position++, lastSectionInfo);
                     mSectionedFilteredApps.add(sectionItem);
                 }
             }
 
             // Create an app item
             AdapterItem appItem = AdapterItem.asApp(position++, lastSectionInfo, sectionName,
-                    lastSectionInfo.numApps++, info, appIndex++);
+                    lastSectionInfo.numApps++, info, appIndex++, isPredictedApp);
             if (lastSectionInfo.firstAppItem == null) {
                 lastSectionInfo.firstAppItem = appItem;
                 lastFastScrollerSectionInfo.appItem = appItem;
@@ -384,8 +427,8 @@
             mFilteredApps.add(info);
         }
 
+        // Go through each section and try and merge some of the sections
         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++) {
diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java
index 6556cf9..edb6f0c 100644
--- a/src/com/android/launcher3/AppsContainerRecyclerView.java
+++ b/src/com/android/launcher3/AppsContainerRecyclerView.java
@@ -229,7 +229,7 @@
      * Draws the fast scroller popup.
      */
     private void drawFastScrollerPopup(Canvas canvas) {
-        if (mFastScrollAlpha > 0f) {
+        if (mFastScrollAlpha > 0f && !mFastScrollSectionName.isEmpty()) {
             int x;
             int y;
             boolean isRtl = (getResources().getConfiguration().getLayoutDirection() ==
@@ -381,16 +381,16 @@
     }
 
     /**
-     * Returns the row index for a given position in the list.
+     * Returns the row index for a app index in the list.
      */
-    private int findRowForAppIndex(int position) {
+    private int findRowForAppIndex(int index) {
         List<AlphabeticalAppsList.SectionInfo> sections = mApps.getSections();
         int appIndex = 0;
         int rowCount = 0;
         for (AlphabeticalAppsList.SectionInfo info : sections) {
             int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow);
-            if (appIndex + info.numApps > position) {
-                return rowCount + ((position - appIndex) / mNumAppsPerRow);
+            if (appIndex + info.numApps > index) {
+                return rowCount + ((index - appIndex) / mNumAppsPerRow);
             }
             appIndex += info.numApps;
             rowCount += numRowsInSection;
diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java
index aa6c059..b8d30d0 100644
--- a/src/com/android/launcher3/AppsContainerView.java
+++ b/src/com/android/launcher3/AppsContainerView.java
@@ -15,6 +15,7 @@
  */
 package com.android.launcher3;
 
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Point;
@@ -111,6 +112,13 @@
     }
 
     /**
+     * Sets the current set of predicted apps.
+     */
+    public void setPredictedApps(List<ComponentName> apps) {
+        mApps.setPredictedApps(apps);
+    }
+
+    /**
      * Sets the current set of apps.
      */
     public void setApps(List<AppInfo> apps) {
diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java
index 259740c..9ecb2ee 100644
--- a/src/com/android/launcher3/AppsGridAdapter.java
+++ b/src/com/android/launcher3/AppsGridAdapter.java
@@ -35,13 +35,11 @@
      */
     public static class ViewHolder extends RecyclerView.ViewHolder {
         public View mContent;
-        public boolean mIsSectionHeader;
         public boolean mIsEmptyRow;
 
-        public ViewHolder(View v, boolean isSectionHeader, boolean isEmptyRow) {
+        public ViewHolder(View v, boolean isEmptyRow) {
             super(v);
             mContent = v;
-            mIsSectionHeader = isSectionHeader;
             mIsEmptyRow = isEmptyRow;
         }
     }
@@ -93,13 +91,29 @@
             }
 
             List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
+            boolean hasDrawnPredictedAppDivider = false;
             int childCount = parent.getChildCount();
             int lastSectionTop = 0;
             int lastSectionHeight = 0;
             for (int i = 0; i < childCount; i++) {
                 View child = parent.getChildAt(i);
                 ViewHolder holder = (ViewHolder) parent.getChildViewHolder(child);
-                if (shouldDrawItemSection(holder, child, i, items)) {
+                if (!isValidHolderAndChild(holder, child, items)) {
+                    continue;
+                }
+
+                if (shouldDrawItemDivider(holder, items) && !hasDrawnPredictedAppDivider) {
+                    // Draw the divider under the predicted app
+                    DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().
+                            getDeviceProfile();
+                    int top = child.getTop() + child.getHeight();
+                    int left = parent.getPaddingLeft();
+                    int right = parent.getWidth() - parent.getPaddingRight();
+                    int iconInset = (((right - left) / mAppsPerRow) - grid.allAppsIconSizePx) / 2;
+                    c.drawLine(left + iconInset, top, right - iconInset, top, mPredictedAppsDividerPaint);
+                    hasDrawnPredictedAppDivider = true;
+
+                } else if (shouldDrawItemSection(holder, i, items)) {
                     // At this point, we only draw sections for each section break;
                     int viewTopOffset = (2 * child.getPaddingTop());
                     int pos = holder.getPosition();
@@ -186,9 +200,9 @@
         }
 
         /**
-         * Returns whether to draw the section for the given child.
+         * Returns whether we consider this a valid view holder for us to draw a divider or section for.
          */
-        private boolean shouldDrawItemSection(ViewHolder holder, View child, int childIndex,
+        private boolean isValidHolderAndChild(ViewHolder holder, View child,
                 List<AlphabeticalAppsList.AdapterItem> items) {
             // Ensure item is not already removed
             GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams)
@@ -200,18 +214,44 @@
             if (holder == null) {
                 return false;
             }
+            // Ensure we have a holder position
+            int pos = holder.getPosition();
+            if (pos < 0 || pos >= items.size()) {
+                return false;
+            }
+            return true;
+        }
+
+        /**
+         * Returns whether to draw the divider for a given child.
+         */
+        private boolean shouldDrawItemDivider(ViewHolder holder, List<AlphabeticalAppsList.AdapterItem> items) {
+            int pos = holder.getPosition();
+            return items.get(pos).isPredictedApp;
+        }
+
+        /**
+         * Returns whether to draw the section for the given child.
+         */
+        private boolean shouldDrawItemSection(ViewHolder holder, int childIndex,
+                List<AlphabeticalAppsList.AdapterItem> items) {
+            int pos = holder.getPosition();
+            AlphabeticalAppsList.AdapterItem item = items.get(pos);
+
             // Ensure it's not an empty row
             if (holder.mIsEmptyRow) {
                 return false;
             }
-            // Ensure we have a holder position
-            int pos = holder.getPosition();
-            if (pos <= 0 || pos >= items.size()) {
+            // Ensure this is not a section break
+            if (item.isSectionHeader) {
+                return false;
+            }
+            // Ensure this is not a predicted app
+            if (item.isPredictedApp) {
                 return false;
             }
             // Draw the section header for the first item in each section
-            return (childIndex == 0) ||
-                    (items.get(pos - 1).isSectionHeader && !items.get(pos).isSectionHeader);
+            return (childIndex == 0) || (items.get(pos - 1).isSectionHeader && !item.isSectionHeader);
         }
     }
 
@@ -232,6 +272,7 @@
     @Thunk int mStartMargin;
     @Thunk int mSectionHeaderOffset;
     @Thunk Paint mSectionTextPaint;
+    @Thunk Paint mPredictedAppsDividerPaint;
 
 
     public AppsGridAdapter(Context context, AlphabeticalAppsList apps, int appsPerRow,
@@ -254,11 +295,17 @@
             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(
                 R.dimen.apps_view_section_text_size));
         mSectionTextPaint.setColor(res.getColor(R.color.apps_view_section_text_color));
         mSectionTextPaint.setAntiAlias(true);
+
+        mPredictedAppsDividerPaint = new Paint();
+        mPredictedAppsDividerPaint.setStrokeWidth(DynamicGrid.pxFromDp(1.5f, res.getDisplayMetrics()));
+        mPredictedAppsDividerPaint.setColor(0x10000000);
+        mPredictedAppsDividerPaint.setAntiAlias(true);
     }
 
     /**
@@ -313,10 +360,9 @@
         switch (viewType) {
             case EMPTY_VIEW_TYPE:
                 return new ViewHolder(mLayoutInflater.inflate(R.layout.apps_empty_view, parent,
-                        false), false /* isSectionRow */, true /* isEmptyRow */);
+                        false), true /* isEmptyRow */);
             case SECTION_BREAK_VIEW_TYPE:
-                return new ViewHolder(new View(parent.getContext()), true /* isSectionRow */,
-                        false /* isEmptyRow */);
+                return new ViewHolder(new View(parent.getContext()), false /* isEmptyRow */);
             case ICON_VIEW_TYPE:
                 BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
                         R.layout.apps_grid_row_icon_view, parent, false);
@@ -324,7 +370,7 @@
                 icon.setOnClickListener(mIconClickListener);
                 icon.setOnLongClickListener(mIconLongClickListener);
                 icon.setFocusable(true);
-                return new ViewHolder(icon, false /* isSectionRow */, false /* isEmptyRow */);
+                return new ViewHolder(icon, false /* isEmptyRow */);
             default:
                 throw new RuntimeException("Unexpected view type");
         }
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 918517e..7a6e9a2 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -69,8 +69,10 @@
     String name;
     float minWidthDps;
     float minHeightDps;
-    public float numRows;
-    public float numColumns;
+    public int numRows;
+    public int numColumns;
+    public int numFolderRows;
+    public int numFolderColumns;
     float numHotseatIcons;
     float iconSize;
     private float iconTextSize;
@@ -80,12 +82,11 @@
     int defaultLayoutId;
 
     boolean isLandscape;
-    boolean isTablet;
-    boolean isLargeTablet;
+    public boolean isTablet;
+    public boolean isLargeTablet;
     public boolean isLayoutRtl;
 
     boolean transposeLayoutWithOrientation;
-
     int desiredWorkspaceLeftRightMarginPx;
     public int edgeMarginPx;
     Rect defaultWidgetPadding;
@@ -138,8 +139,9 @@
 
     private ArrayList<DeviceProfileCallbacks> mCallbacks = new ArrayList<DeviceProfileCallbacks>();
 
-    DeviceProfile(String n, float w, float h, float r, float c,
-                  float is, float its, float hs, float his, int dlId) {
+    DeviceProfile(String n, float w, float h,
+            int r, int c, int fr, int fc,
+            float is, float its, float hs, float his, int dlId) {
         // Ensure that we have an odd number of hotseat items (since we need to place all apps)
         if (hs % 2 == 0) {
             throw new RuntimeException("All Device Profiles must have an odd number of hotseat spaces");
@@ -148,8 +150,12 @@
         name = n;
         minWidthDps = w;
         minHeightDps = h;
+
         numRows = r;
         numColumns = c;
+        numFolderRows = fr;
+        numFolderColumns = fc;
+
         iconSize = is;
         iconTextSize = its;
         numHotseatIcons = hs;
@@ -210,6 +216,9 @@
         // Snap to the closest column count
         numColumns = closestProfile.numColumns;
 
+        numFolderRows = closestProfile.numFolderRows;
+        numFolderColumns = closestProfile.numFolderColumns;
+
         // Snap to the closest hotseat size
         numHotseatIcons = closestProfile.numHotseatIcons;
         hotseatAllAppsRank = (int) (numHotseatIcons / 2);
@@ -266,8 +275,8 @@
             DeviceProfile partnerDp = p.getDeviceProfileOverride(dm);
             if (partnerDp != null) {
                 if (partnerDp.numRows > 0 && partnerDp.numColumns > 0) {
-                    numRows = partnerDp.numRows;
-                    numColumns = partnerDp.numColumns;
+                    numRows = numFolderRows = partnerDp.numRows;
+                    numColumns = numFolderColumns = partnerDp.numColumns;
                 }
                 if (partnerDp.allAppsShortEdgeCount > 0 && partnerDp.allAppsLongEdgeCount > 0) {
                     allAppsShortEdgeCount = partnerDp.allAppsShortEdgeCount;
diff --git a/src/com/android/launcher3/DynamicGrid.java b/src/com/android/launcher3/DynamicGrid.java
index 24da97f..d22427f 100644
--- a/src/com/android/launcher3/DynamicGrid.java
+++ b/src/com/android/launcher3/DynamicGrid.java
@@ -59,30 +59,30 @@
         DEFAULT_ICON_SIZE_PX = pxFromDp(DEFAULT_ICON_SIZE_DP, dm);
         // Our phone profiles include the bar sizes in each orientation
         deviceProfiles.add(new DeviceProfile("Super Short Stubby",
-                255, 300,  2, 3,  48, 13, 3, 48, R.xml.default_workspace_4x4));
+                255, 300,  2, 3, 2, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4));
         deviceProfiles.add(new DeviceProfile("Shorter Stubby",
-                255, 400,  3, 3,  48, 13, 3, 48, R.xml.default_workspace_4x4));
+                255, 400,  3, 3, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4));
         deviceProfiles.add(new DeviceProfile("Short Stubby",
-                275, 420,  3, 4,  48, 13, 5, 48, R.xml.default_workspace_4x4));
+                275, 420,  3, 4, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
         deviceProfiles.add(new DeviceProfile("Stubby",
-                255, 450,  3, 4,  48, 13, 5, 48, R.xml.default_workspace_4x4));
+                255, 450,  3, 4, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
         deviceProfiles.add(new DeviceProfile("Nexus S",
-                296, 491.33f,  4, 4,  48, 13, 5, 48, R.xml.default_workspace_4x4));
+                296, 491.33f,  4, 4, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
         deviceProfiles.add(new DeviceProfile("Nexus 4",
-                335, 567,  4, 4,  DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4));
+                335, 567,  4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4));
         deviceProfiles.add(new DeviceProfile("Nexus 5",
-                359, 567,  4, 4,  DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4));
+                359, 567,  4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4));
         deviceProfiles.add(new DeviceProfile("Large Phone",
-                406, 694,  5, 5,  64, 14.4f,  5, 56, R.xml.default_workspace_5x5));
+                406, 694,  5, 5, 4, 4,  64, 14.4f,  5, 56, R.xml.default_workspace_5x5));
         // The tablet profile is odd in that the landscape orientation
         // also includes the nav bar on the side
         deviceProfiles.add(new DeviceProfile("Nexus 7",
-                575, 904,  5, 6,  72, 14.4f,  7, 60, R.xml.default_workspace_5x6));
+                575, 904,  5, 6, 4, 5, 72, 14.4f,  7, 60, R.xml.default_workspace_5x6));
         // Larger tablet profiles always have system bars on the top & bottom
         deviceProfiles.add(new DeviceProfile("Nexus 10",
-                727, 1207,  5, 6,  76, 14.4f,  7, 64, R.xml.default_workspace_5x6));
+                727, 1207,  5, 6, 4, 5, 76, 14.4f,  7, 64, R.xml.default_workspace_5x6));
         deviceProfiles.add(new DeviceProfile("20-inch Tablet",
-                1527, 2527,  7, 7,  100, 20,  7, 72, R.xml.default_workspace_4x4));
+                1527, 2527,  7, 7, 6, 6, 100, 20,  7, 72, R.xml.default_workspace_4x4));
         mMinWidth = dpiFromPx(minWidthPx, dm);
         mMinHeight = dpiFromPx(minHeightPx, dm);
         mProfile = new DeviceProfile(context, deviceProfiles,
diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java
index 6355082..05b2bbf 100644
--- a/src/com/android/launcher3/FolderPagedView.java
+++ b/src/com/android/launcher3/FolderPagedView.java
@@ -51,9 +51,6 @@
 
     private static final int[] sTempPosArray = new int[2];
 
-    // TODO: Remove this restriction
-    private static final int MAX_ITEMS_PER_PAGE = 4;
-
     public final boolean rtlLayout;
 
     private final LayoutInflater mInflater;
@@ -80,13 +77,8 @@
         LauncherAppState app = LauncherAppState.getInstance();
 
         DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-        if (ALLOW_FOLDER_SCROLL) {
-            mMaxCountX = Math.min((int) grid.numColumns, MAX_ITEMS_PER_PAGE);
-            mMaxCountY = Math.min((int) grid.numRows, MAX_ITEMS_PER_PAGE);
-        } else {
-            mMaxCountX = (int) grid.numColumns;
-            mMaxCountY = (int) grid.numRows;
-        }
+        mMaxCountX = (int) grid.numFolderColumns;
+        mMaxCountY = (int) grid.numFolderRows;
 
         mMaxItemsPerPage = mMaxCountX * mMaxCountY;
 
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index b17a92f..16e4ce6 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -64,6 +64,7 @@
 import android.os.Message;
 import android.os.StrictMode;
 import android.os.SystemClock;
+import android.preference.PreferenceManager;
 import android.text.Selection;
 import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
@@ -1020,16 +1021,22 @@
         if (mOnResumeState == State.WORKSPACE) {
             showWorkspace(false);
         } else if (mOnResumeState == State.APPS) {
-            showAppsView(false /* animated */, false /* resetListToTop */);
+            // Don't update the predicted apps if the user is returning to launcher in the apps
+            // view as they may be depending on the UI to be static to switch to another app
+            showAppsView(false /* animated */, false /* resetListToTop */,
+                    false /* updatePredictedApps */);
         } else if (mOnResumeState == State.WIDGETS) {
             showWidgetsView(false, false);
         }
         mOnResumeState = State.NONE;
 
         // Restore the apps state if we are in all apps
-        if (!Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION && mState == State.APPS) {
-            if (mLauncherCallbacks != null) {
-                mLauncherCallbacks.onAllAppsShown();
+        if (!Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION) {
+            // Otherwise, notify the callbacks if we are in all apps mode
+            if (mState == State.APPS) {
+                if (mLauncherCallbacks != null) {
+                    mLauncherCallbacks.onAllAppsShown();
+                }
             }
         }
 
@@ -2621,7 +2628,9 @@
         if (isAppsViewVisible()) {
             showWorkspace(true);
         } else {
-            showAppsView(true /* animated */, false /* resetListToTop */);
+            // Try and refresh the set of predicted apps before we enter launcher
+            showAppsView(true /* animated */, false /* resetListToTop */,
+                    true /* updatePredictedApps */);
         }
     }
 
@@ -3399,10 +3408,13 @@
     /**
      * Shows the apps view.
      */
-    void showAppsView(boolean animated, boolean resetListToTop) {
+    void showAppsView(boolean animated, boolean resetListToTop, boolean updatePredictedApps) {
         if (resetListToTop) {
             mAppsView.scrollToTop();
         }
+        if (updatePredictedApps) {
+            tryAndUpdatePredictedApps();
+        }
         showAppsOrWidgets(animated, State.APPS);
     }
 
@@ -3511,12 +3523,26 @@
 
     void exitSpringLoadedDragMode() {
         if (mState == State.APPS_SPRING_LOADED) {
-            showAppsView(true, false);
+            showAppsView(true /* animated */, false /* resetListToTop */,
+                    false /* updatePredictedApps */);
         } else if (mState == State.WIDGETS_SPRING_LOADED) {
             showWidgetsView(true, false);
         }
     }
 
+    /**
+     * Updates the set of predicted apps if it hasn't been updated since the last time Launcher was
+     * resumed.
+     */
+    private void tryAndUpdatePredictedApps() {
+        if (mLauncherCallbacks != null) {
+            List<ComponentName> apps = mLauncherCallbacks.getPredictedApps();
+            if (!apps.isEmpty()) {
+                mAppsView.setPredictedApps(apps);
+            }
+        }
+    }
+
     void lockAllApps() {
         // TODO
     }
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index 25c86c9..0124d1f 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -11,6 +11,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * LauncherCallbacks is an interface used to extend the Launcher activity. It includes many hooks
@@ -90,6 +91,7 @@
     public boolean overrideWallpaperDimensions();
     public boolean isLauncherPreinstalled();
     public boolean overrideAllAppsSearch();
+    public List<ComponentName> getPredictedApps();
 
     /**
      * Returning true will immediately result in a call to {@link #setLauncherOverlayView(ViewGroup,
diff --git a/src/com/android/launcher3/LauncherExtension.java b/src/com/android/launcher3/LauncherExtension.java
index 8174af0..14ad601 100644
--- a/src/com/android/launcher3/LauncherExtension.java
+++ b/src/com/android/launcher3/LauncherExtension.java
@@ -15,6 +15,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * This class represents a very trivial LauncherExtension. It primarily serves as a simple
@@ -259,6 +260,11 @@
         }
 
         @Override
+        public List<ComponentName> getPredictedApps() {
+            return new ArrayList<>();
+        }
+
+        @Override
         public boolean isLauncherPreinstalled() {
             return false;
         }
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 27b7e6d..8779371 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -49,7 +49,16 @@
     private static final boolean DEBUG = false;
 
     private static final int FADE_IN_DURATION_MS = 90;
-    private int mPresetPreviewSize;
+
+    /** Widget cell width is calculated by multiplying this factor to grid cell width. */
+    private static final float WIDTH_SCALE = 2.8f;
+
+    /** Widget preview width is calculated by multiplying this factor to the widget cell width. */
+    private static final float PREVIEW_SCALE = 0.9f;
+
+    private static int mPresetPreviewSize;
+    private static int mSize;
+    private static int mDividerWidth;
 
     private ImageView mWidgetImage;
     private TextView mWidgetName;
@@ -76,12 +85,22 @@
 
         final Resources r = context.getResources();
         mDimensionsFormatString = r.getString(R.string.widget_dims_format);
-        mPresetPreviewSize = r.getDimensionPixelSize(R.dimen.widget_preview_size);
 
+        setContainerWidth();
         setWillNotDraw(false);
         setClipToPadding(false);
         setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
+    }
 
+    private void setContainerWidth() {
+        // Do nothing if already set
+        if (mSize > 0) {
+            return;
+        }
+        DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
+        mSize = (int) (profile.cellWidthPx * WIDTH_SCALE);
+        mPresetPreviewSize = (int) (profile.cellWidthPx * WIDTH_SCALE * PREVIEW_SCALE);
+        mDividerWidth = getResources().getDimensionPixelSize(R.dimen.widget_row_divider);
     }
 
     @Override
@@ -98,6 +117,12 @@
         mWidgetDims = ((TextView) findViewById(R.id.widget_dims));
     }
 
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(MeasureSpec.makeMeasureSpec(mSize, MeasureSpec.EXACTLY),
+                MeasureSpec.makeMeasureSpec(mSize, MeasureSpec.EXACTLY));
+    }
+
     /**
      * Called to clear the view and free attached resources. (e.g., {@link Bitmap}
      */
@@ -108,6 +133,7 @@
         mWidgetImage.setImageDrawable(null);
         mWidgetName.setText(null);
         mWidgetDims.setText(null);
+        setSeparator(true);
 
         if (mActiveRequest != null) {
             mActiveRequest.cleanup();
@@ -228,4 +254,11 @@
         }
         return "";
     }
+
+    public void setSeparator(boolean enable) {
+        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) getLayoutParams();
+        lp.setMarginEnd(enable? mDividerWidth : 0);
+        setLayoutParams(lp);
+        requestLayout();
+    }
 }
diff --git a/src/com/android/launcher3/widget/WidgetRowView.java b/src/com/android/launcher3/widget/WidgetRowView.java
new file mode 100644
index 0000000..05760ae
--- /dev/null
+++ b/src/com/android/launcher3/widget/WidgetRowView.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.widget;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DynamicGrid;
+import com.android.launcher3.LauncherAppState;
+
+/**
+ * Represents the individual cell of the widget inside the widget tray.
+ */
+public class WidgetRowView extends LinearLayout {
+
+    private static final int PRESET_INDENT_SIZE_TABLET = 56;
+
+    /** Widget row width is calculated by multiplying this factor to grid cell width. */
+    private static final float HEIGHT_SCALE = 2.8f;
+
+    static int sIndent = 0;
+    static int sHeight = 0;
+
+    public WidgetRowView(Context context) {
+        this(context, null);
+    }
+
+    public WidgetRowView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public WidgetRowView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        setContainerHeight();
+        setWillNotDraw(false);
+        setClipToPadding(false);
+        setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
+    }
+
+    /**
+     * Sets the widget cell container size based on the physical dimension of the device.
+     */
+    private void setContainerHeight() {
+        // Do nothing if already set
+        if (sHeight > 0) {
+            return;
+        }
+
+        Resources r = getResources();
+        DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
+        if (profile.isLargeTablet || profile.isTablet) {
+            sIndent = DynamicGrid.pxFromDp(PRESET_INDENT_SIZE_TABLET, r.getDisplayMetrics());
+        }
+        sHeight = (int) (profile.cellWidthPx * HEIGHT_SCALE);
+    }
+}
diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java
index a7728a1..8b0a43b 100644
--- a/src/com/android/launcher3/widget/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java
@@ -23,6 +23,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
+import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import com.android.launcher3.IconCache;
@@ -105,6 +106,10 @@
                 // set up touch.
                 widget.setOnClickListener(mIconClickListener);
                 widget.setOnLongClickListener(mIconLongClickListener);
+                // Add a devider if it is not the last item.
+                if (i == diff - 1) {
+                   widget.setSeparator(false);
+                }
                 row.addView(widget);
             }
         } else if (diff < 0) {
@@ -156,6 +161,11 @@
 
         ViewGroup container = (ViewGroup) mLayoutInflater.inflate(
                 R.layout.widgets_list_row_view, parent, false);
+        WidgetRowView row = (WidgetRowView) container.findViewById(R.id.widget_row);
+        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) row.getLayoutParams();
+        lp.setMarginStart(WidgetRowView.sIndent);
+        lp.height = WidgetRowView.sHeight;
+        row.setLayoutParams(lp);
         return new WidgetsRowViewHolder(container);
     }