Calculating widget minSpans and spans statically/independent of orientation
 > Filtering the widget list and excluding widgets which dont fit the grid
 > setting minSpans for the widget item when binding.

Bug: 22541314
Bug: 22559137
Change-Id: Ieda48b56c95bee0c7ec71dd691af7e23e2d43db6
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index ea7c221..e6bf525 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -75,8 +75,8 @@
         mResizeMode = info.resizeMode;
         mDragLayer = dragLayer;
 
-        mMinHSpan = info.getMinSpanX(mLauncher);
-        mMinVSpan = info.getMinSpanY(mLauncher);
+        mMinHSpan = info.minSpanX;
+        mMinVSpan = info.minSpanY;
 
         setBackgroundResource(R.drawable.widget_resize_shadow);
         setForeground(getResources().getDrawable(R.drawable.widget_resize_frame));
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index b875d22..84e2d49 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -54,7 +54,6 @@
 import com.android.launcher3.accessibility.FolderAccessibilityHelper;
 import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
 import com.android.launcher3.util.Thunk;
-import com.android.launcher3.widget.PendingAddWidgetInfo;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -2686,65 +2685,6 @@
         resultRect.set(x, y, x + width, y + height);
     }
 
-    /**
-     * Computes the required horizontal and vertical cell spans to always
-     * fit the given rectangle.
-     *
-     * @param width Width in pixels
-     * @param height Height in pixels
-     * @param result An array of length 2 in which to store the result (may be null).
-     */
-    public static int[] rectToCell(Launcher launcher, int width, int height, int[] result) {
-        return rectToCell(launcher.getDeviceProfile(), launcher, width, height, result);
-    }
-
-    public static int[] rectToCell(DeviceProfile grid, Context context, int width, int height,
-            int[] result) {
-        Rect padding = grid.getWorkspacePadding(Utilities.isRtl(context.getResources()));
-
-        // Always assume we're working with the smallest span to make sure we
-        // reserve enough space in both orientations.
-        int parentWidth = DeviceProfile.calculateCellWidth(grid.widthPx
-                - padding.left - padding.right, (int) grid.inv.numColumns);
-        int parentHeight = DeviceProfile.calculateCellHeight(grid.heightPx
-                - padding.top - padding.bottom, (int) grid.inv.numRows);
-        int smallerSize = Math.min(parentWidth, parentHeight);
-
-        // Always round up to next largest cell
-        int spanX = (int) Math.ceil(width / (float) smallerSize);
-        int spanY = (int) Math.ceil(height / (float) smallerSize);
-
-        if (result == null) {
-            return new int[] { spanX, spanY };
-        }
-        result[0] = spanX;
-        result[1] = spanY;
-        return result;
-    }
-
-    /**
-     * Calculate the grid spans needed to fit given item
-     */
-    public void calculateSpans(ItemInfo info) {
-        final int minWidth;
-        final int minHeight;
-
-        if (info instanceof LauncherAppWidgetInfo) {
-            minWidth = ((LauncherAppWidgetInfo) info).minWidth;
-            minHeight = ((LauncherAppWidgetInfo) info).minHeight;
-        } else if (info instanceof PendingAddWidgetInfo) {
-            minWidth = ((PendingAddWidgetInfo) info).minWidth;
-            minHeight = ((PendingAddWidgetInfo) info).minHeight;
-        } else {
-            // It's not a widget, so it must be 1x1
-            info.spanX = info.spanY = 1;
-            return;
-        }
-        int[] spans = rectToCell(mLauncher, minWidth, minHeight, null);
-        info.spanX = spans[0];
-        info.spanY = spans[1];
-    }
-
     private void clearOccupiedCells() {
         for (int x = 0; x < mCountX; x++) {
             for (int y = 0; y < mCountY; y++) {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 6648b6e..a6c7289 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1553,23 +1553,6 @@
         }
     }
 
-    private int[] getSpanForWidget(ComponentName component, int minWidth, int minHeight) {
-        Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(this, component, null);
-        // We want to account for the extra amount of padding that we are adding to the widget
-        // to ensure that it gets the full amount of space that it has requested
-        int requiredWidth = minWidth + padding.left + padding.right;
-        int requiredHeight = minHeight + padding.top + padding.bottom;
-        return CellLayout.rectToCell(this, requiredWidth, requiredHeight, null);
-    }
-
-    public int[] getSpanForWidget(AppWidgetProviderInfo info) {
-        return getSpanForWidget(info.provider, info.minWidth, info.minHeight);
-    }
-
-    public int[] getMinSpanForWidget(AppWidgetProviderInfo info) {
-        return getSpanForWidget(info.provider, info.minResizeWidth, info.minResizeHeight);
-    }
-
     /**
      * Add a widget to the workspace.
      *
@@ -2218,7 +2201,7 @@
         mPendingAddInfo.screenId = -1;
         mPendingAddInfo.cellX = mPendingAddInfo.cellY = -1;
         mPendingAddInfo.spanX = mPendingAddInfo.spanY = -1;
-        mPendingAddInfo.minSpanX = mPendingAddInfo.minSpanY = -1;
+        mPendingAddInfo.minSpanX = mPendingAddInfo.minSpanY = 1;
         mPendingAddInfo.dropPos = null;
     }
 
@@ -3957,6 +3940,8 @@
             }
 
             item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
+            item.minSpanX = appWidgetInfo.minSpanX;
+            item.minSpanY = appWidgetInfo.minSpanY;
         } else {
             appWidgetInfo = null;
             PendingAppWidgetHostView view = new PendingAppWidgetHostView(this, item,
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java
index aad18b5..882f7e2 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java
@@ -68,10 +68,6 @@
 
     ComponentName providerName;
 
-    // TODO: Are these necessary here?
-    int minWidth = -1;
-    int minHeight = -1;
-
     /**
      * Indicates the restore status of the widget.
      */
diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
index 9ba7853..71a08a8 100644
--- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
@@ -1,11 +1,13 @@
 package com.android.launcher3;
 
 import android.annotation.TargetApi;
+import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.graphics.Point;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Parcel;
@@ -20,10 +22,10 @@
 
     public boolean isCustomWidget = false;
 
-    private int mSpanX = -1;
-    private int mSpanY = -1;
-    private int mMinSpanX = -1;
-    private int mMinSpanY = -1;
+    public int spanX;
+    public int spanY;
+    public int minSpanX;
+    public int minSpanY;
 
     public static LauncherAppWidgetProviderInfo fromProviderInfo(Context context,
             AppWidgetProviderInfo info) {
@@ -42,6 +44,7 @@
 
     public LauncherAppWidgetProviderInfo(Parcel in) {
         super(in);
+        initSpans();
     }
 
     public LauncherAppWidgetProviderInfo(Context context, CustomAppWidget widget) {
@@ -53,6 +56,41 @@
         previewImage = widget.getPreviewImage();
         initialLayout = widget.getWidgetLayout();
         resizeMode = widget.getResizeMode();
+        initSpans();
+    }
+
+    private void initSpans() {
+        LauncherAppState app = LauncherAppState.getInstance();
+        InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
+
+        // We only care out the cell size, which is independent of the the layout direction.
+        Rect paddingLand = idp.landscapeProfile.getWorkspacePadding(false /* isLayoutRtl */);
+        Rect paddingPort = idp.portraitProfile.getWorkspacePadding(false /* isLayoutRtl */);
+
+        // Always assume we're working with the smallest span to make sure we
+        // reserve enough space in both orientations.
+        float smallestCellWidth = DeviceProfile.calculateCellWidth(Math.min(
+                idp.landscapeProfile.widthPx - paddingLand.left - paddingLand.right,
+                idp.portraitProfile.widthPx - paddingPort.left - paddingPort.right),
+                idp.numColumns);
+        float smallestCellHeight = DeviceProfile.calculateCellWidth(Math.min(
+                idp.landscapeProfile.heightPx - paddingLand.top - paddingLand.bottom,
+                idp.portraitProfile.heightPx - paddingPort.top - paddingPort.bottom),
+                idp.numRows);
+
+        // We want to account for the extra amount of padding that we are adding to the widget
+        // to ensure that it gets the full amount of space that it has requested.
+        Rect widgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(
+                app.getContext(), provider, null);
+        spanX = Math.max(1, (int) Math.ceil(
+                        (minWidth + widgetPadding.left + widgetPadding.right) / smallestCellWidth));
+        spanY = Math.max(1, (int) Math.ceil(
+                (minHeight + widgetPadding.top + widgetPadding.bottom) / smallestCellHeight));
+
+        minSpanX = Math.max(1, (int) Math.ceil(
+                (minResizeWidth + widgetPadding.left + widgetPadding.right) / smallestCellWidth));
+        minSpanY = Math.max(1, (int) Math.ceil(
+                (minResizeHeight + widgetPadding.top + widgetPadding.bottom) / smallestCellHeight));
     }
 
     @TargetApi(Build.VERSION_CODES.LOLLIPOP)
@@ -80,46 +118,9 @@
                 provider.toString(), provider.getPackageName(), provider.getShortClassName(), getLabel(pm));
     }
 
-    public int getSpanX(Launcher launcher) {
-        lazyLoadSpans(launcher);
-        return mSpanX;
-    }
-
-    public int getSpanY(Launcher launcher) {
-        lazyLoadSpans(launcher);
-        return mSpanY;
-    }
-
-    public int getMinSpanX(Launcher launcher) {
-        lazyLoadSpans(launcher);
-        return mMinSpanX;
-    }
-
-    public int getMinSpanY(Launcher launcher) {
-        lazyLoadSpans(launcher);
-        return mMinSpanY;
-    }
-
-    private void lazyLoadSpans(Launcher launcher) {
-        if (mSpanX < 0 || mSpanY < 0 || mMinSpanX < 0 || mMinSpanY < 0) {
-            int[] minResizeSpan = launcher.getMinSpanForWidget(this);
-            int[] span = launcher.getSpanForWidget(this);
-
-            mSpanX = span[0];
-            mSpanY = span[1];
-            mMinSpanX = minResizeSpan[0];
-            mMinSpanY = minResizeSpan[1];
-        }
-    }
-
     public Point getMinSpans(InvariantDeviceProfile idp, Context context) {
-        // Calculate the spans corresponding to any one of the orientations as it should not change
-        // based on orientation.
-        // TODO: Use the max of both profiles
-        int[] minSpans = CellLayout.rectToCell(
-                idp.portraitProfile, context, minResizeWidth, minResizeHeight, null);
         return new Point(
-                (resizeMode & RESIZE_HORIZONTAL) != 0 ? minSpans[0] : -1,
-                        (resizeMode & RESIZE_VERTICAL) != 0 ? minSpans[1] : -1);
+                (resizeMode & RESIZE_HORIZONTAL) != 0 ? minSpanX : -1,
+                        (resizeMode & RESIZE_VERTICAL) != 0 ? minSpanY : -1);
     }
  }
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 2a8053d..1fb795a 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -361,8 +361,8 @@
         }
 
         final boolean widgetPreviewExists = (drawable != null);
-        final int spanX = info.getSpanX(launcher) < 1 ? 1 : info.getSpanX(launcher);
-        final int spanY = info.getSpanY(launcher) < 1 ? 1 : info.getSpanY(launcher);
+        final int spanX = info.spanX;
+        final int spanY = info.spanY;
 
         int previewWidth;
         int previewHeight;
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 67d5728..010b2cb 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -528,7 +528,6 @@
                 CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen);
                 ItemInfo itemInfo = (ItemInfo) d.dragInfo;
                 if (layout != null) {
-                    layout.calculateSpans(itemInfo);
                     showOutOfSpaceMessage =
                             !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY);
                 }
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index cabff14..eef4f91 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -8,6 +8,9 @@
 
 import com.android.launcher3.AppFilter;
 import com.android.launcher3.IconCache;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.AlphabeticIndexCompat;
@@ -40,7 +43,7 @@
 
     private final AppWidgetManagerCompat mAppWidgetMgr;
     private final WidgetsAndShortcutNameComparator mWidgetAndShortcutNameComparator;
-    private final Comparator mAppNameComparator;
+    private final Comparator<ItemInfo> mAppNameComparator;
     private final IconCache mIconCache;
     private final AppFilter mAppFilter;
     private AlphabeticIndexCompat mIndexer;
@@ -54,6 +57,7 @@
         mIndexer = new AlphabeticIndexCompat(context);
     }
 
+    @SuppressWarnings("unchecked")
     private WidgetsModel(WidgetsModel model) {
         mAppWidgetMgr = model.mAppWidgetMgr;
         mPackageItemInfos = (ArrayList<PackageItemInfo>) model.mPackageItemInfos.clone();
@@ -105,6 +109,8 @@
         mPackageItemInfos.clear();
         mWidgetAndShortcutNameComparator.reset();
 
+        InvariantDeviceProfile idp = LauncherAppState.getInstance().getInvariantDeviceProfile();
+
         // add and update.
         for (Object o: rawWidgetsShortcuts) {
             String packageName = "";
@@ -112,9 +118,23 @@
             ComponentName componentName = null;
             if (o instanceof LauncherAppWidgetProviderInfo) {
                 LauncherAppWidgetProviderInfo widgetInfo = (LauncherAppWidgetProviderInfo) o;
-                componentName = widgetInfo.provider;
-                packageName = widgetInfo.provider.getPackageName();
-                userHandle = mAppWidgetMgr.getUser(widgetInfo);
+
+                // Ensure that all widgets we show can be added on a workspace of this size
+                int minSpanX = Math.min(widgetInfo.spanX, widgetInfo.minSpanX);
+                int minSpanY = Math.min(widgetInfo.spanY, widgetInfo.minSpanY);
+                if (minSpanX <= (int) idp.numColumns &&
+                    minSpanY <= (int) idp.numRows) {
+                    componentName = widgetInfo.provider;
+                    packageName = widgetInfo.provider.getPackageName();
+                    userHandle = mAppWidgetMgr.getUser(widgetInfo);
+                } else {
+                    if (DEBUG) {
+                        Log.d(TAG, String.format(
+                                "Widget %s : (%d X %d) can't fit on this device",
+                                widgetInfo.provider, minSpanX, minSpanY));
+                    }
+                    continue;
+                }
             } else if (o instanceof ResolveInfo) {
                 ResolveInfo resolveInfo = (ResolveInfo) o;
                 componentName = new ComponentName(resolveInfo.activityInfo.packageName,
diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
index 758287a..fcb714f 100644
--- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
@@ -31,10 +31,6 @@
  * @see {@link PendingAddItemInfo}
  */
 public class PendingAddWidgetInfo extends PendingAddItemInfo {
-    public int minWidth;
-    public int minHeight;
-    public int minResizeWidth;
-    public int minResizeHeight;
     public int previewImage;
     public int icon;
     public LauncherAppWidgetProviderInfo info;
@@ -50,17 +46,13 @@
         this.info = i;
         user = AppWidgetManagerCompat.getInstance(launcher).getUser(i);
         componentName = i.provider;
-        minWidth = i.minWidth;
-        minHeight = i.minHeight;
-        minResizeWidth = i.minResizeWidth;
-        minResizeHeight = i.minResizeHeight;
         previewImage = i.previewImage;
         icon = i.icon;
 
-        spanX = i.getSpanX(launcher);
-        spanY = i.getSpanY(launcher);
-        minSpanX = i.getMinSpanX(launcher);
-        minSpanY = i.getMinSpanY(launcher);
+        spanX = i.spanX;
+        spanY = i.spanY;
+        minSpanX = i.minSpanX;
+        minSpanY = i.minSpanY;
     }
 
     public boolean isCustomWidget() {
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 7496ea2..94bbd92 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -146,8 +146,8 @@
         mInfo = info;
         // TODO(hyunyoungs): setup a cache for these labels.
         mWidgetName.setText(AppWidgetManagerCompat.getInstance(getContext()).loadLabel(info));
-        int hSpan = Math.min(info.getSpanX(mLauncher), profile.numColumns);
-        int vSpan = Math.min(info.getSpanY(mLauncher), profile.numRows);
+        int hSpan = Math.min(info.spanX, profile.numColumns);
+        int vSpan = Math.min(info.spanY, profile.numRows);
         mWidgetDims.setText(String.format(mDimensionsFormatString, hSpan, vSpan));
         mWidgetPreviewLoader = loader;
     }
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index 5afd7c4..0c6ea31 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -319,7 +319,6 @@
                 CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen);
                 ItemInfo itemInfo = (ItemInfo) d.dragInfo;
                 if (layout != null) {
-                    layout.calculateSpans(itemInfo);
                     showOutOfSpaceMessage =
                             !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY);
                 }