Dynamically determine # of rows/cols in All Apps.

Change-Id: Ia8c1b3915325823f7617862e0e4e0db865ea0d5d
diff --git a/res/layout-large/all_apps_tabbed.xml b/res/layout-large/all_apps_tabbed.xml
index b00b3c3..1a20440 100644
--- a/res/layout-large/all_apps_tabbed.xml
+++ b/res/layout-large/all_apps_tabbed.xml
@@ -101,8 +101,6 @@
                 android:id="@+id/all_apps_paged_view"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                launcher:cellCountX="@integer/all_apps_view_cellCountX"
-                launcher:cellCountY="@integer/all_apps_view_cellCountY"
                 launcher:pageLayoutWidthGap="@dimen/all_apps_view_pageLayoutWidthGap"
                 launcher:pageLayoutHeightGap="@dimen/all_apps_view_pageLayoutHeightGap"
                 launcher:pageLayoutPaddingTop="@dimen/all_apps_view_pageLayoutPaddingTop"
diff --git a/res/values-large-land/dimens.xml b/res/values-large-land/dimens.xml
index 196cdb0..0f7e0ed 100644
--- a/res/values-large-land/dimens.xml
+++ b/res/values-large-land/dimens.xml
@@ -22,8 +22,6 @@
     <dimen name="customization_drawer_height">480dp</dimen>
     <dimen name="customization_drawer_content_height">420dp</dimen>
 
-    <integer name="all_apps_view_cellCountX">7</integer>
-    <integer name="all_apps_view_cellCountY">5</integer>
     <dimen name="all_apps_view_pageLayoutWidthGap">36dp</dimen>
     <dimen name="all_apps_view_pageLayoutHeightGap">6dp</dimen>
     <dimen name="all_apps_view_pageLayoutPaddingTop">20dp</dimen>
diff --git a/res/values-large-port/dimens.xml b/res/values-large-port/dimens.xml
index 47cac78..2ea9e38 100644
--- a/res/values-large-port/dimens.xml
+++ b/res/values-large-port/dimens.xml
@@ -22,8 +22,6 @@
     <dimen name="customization_drawer_height">800dp</dimen>
     <dimen name="customization_drawer_content_height">420dp</dimen>
 
-    <integer name="all_apps_view_cellCountX">5</integer>
-    <integer name="all_apps_view_cellCountY">7</integer>
     <dimen name="all_apps_view_pageLayoutWidthGap">36dp</dimen>
     <dimen name="all_apps_view_pageLayoutHeightGap">36dp</dimen>
     <dimen name="all_apps_view_pageLayoutPaddingTop">25dp</dimen>
diff --git a/res/values-large/config.xml b/res/values-large/config.xml
index 99ee1ec..8b77696 100644
--- a/res/values-large/config.xml
+++ b/res/values-large/config.xml
@@ -21,6 +21,9 @@
          Should be an even number, for pixel alignment. -->
     <integer name="config_dragViewExtraPixels">0</integer>
 
+    <!-- When shrinking the workspace, this is the percentage of its original size. -->
+    <integer name="config_workspaceShrinkPercent">17</integer>
+
     <!-- When items are dropped on the mini screens in customize mode, we have a bounce animation
          of the bright green hover outline, and then fade out the outline at the end. These are
          the values used in that animation -->
diff --git a/src/com/android/launcher2/AllAppsPagedView.java b/src/com/android/launcher2/AllAppsPagedView.java
index 59ba57b..d308209 100644
--- a/src/com/android/launcher2/AllAppsPagedView.java
+++ b/src/com/android/launcher2/AllAppsPagedView.java
@@ -66,11 +66,12 @@
     private final LayoutInflater mInflater;
     private boolean mAllowHardwareLayerCreation;
 
-    private boolean mFirstMeasure = true;
-
     private int mPageContentWidth;
     private boolean mHasMadeSuccessfulDrop;
 
+    private int mLastMeasureWidth = -1;
+    private int mLastMeasureHeight = -1;
+
     public AllAppsPagedView(Context context) {
         this(context, null);
     }
@@ -82,8 +83,6 @@
     public AllAppsPagedView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PagedView, defStyle, 0);
-        mCellCountX = a.getInt(R.styleable.PagedView_cellCountX, 6);
-        mCellCountY = a.getInt(R.styleable.PagedView_cellCountY, 4);
         mInflater = LayoutInflater.from(context);
         mApps = new ArrayList<ApplicationInfo>();
         mFilteredApps = new ArrayList<ApplicationInfo>();
@@ -103,27 +102,76 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        final int width = MeasureSpec.getSize(widthMeasureSpec);
+        final int height = MeasureSpec.getSize(heightMeasureSpec);
 
-        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
-        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
-
-        if (mFirstMeasure) {
-            mFirstMeasure = false;
-
-            // TODO: actually calculate mCellCountX/mCellCountY as some function of
-            // widthSize and heightSize
-            //mCellCountX = ?;
-            //mCellCountY = ?;
-
-            // Since mCellCountX/mCellCountY changed, we need to update the pages
-            invalidatePageData();
-
+        if (mLastMeasureWidth != width || mLastMeasureHeight != height) {
             // Create a dummy page and set it up to find out the content width (used by our parent)
             PagedViewCellLayout layout = new PagedViewCellLayout(getContext());
             setupPage(layout);
             mPageContentWidth = layout.getContentWidth();
+
+            mCellCountX = determineCellCountX(width, layout);
+            mCellCountY = determineCellCountY(height, layout);
+            mLastMeasureWidth = width;
+            mLastMeasureHeight = height;
         }
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        if (mFirstLayout) {
+            invalidatePageData();
+
+            // invalidatePageData() is what causes the child pages to be created. We need the
+            // children to be measured before layout, so force a new measure here.
+            measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
+                    MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
+        }
+        super.onLayout(changed, left, top, right, bottom);
+    }
+
+    private int determineCellCountX(int availableWidth, PagedViewCellLayout layout) {
+        int cellCountX = 0;
+        final int cellWidth = layout.getCellWidth();
+
+        // Subtract padding for current page and adjacent pages
+        availableWidth -= mPageLayoutPaddingLeft * 2 + mPageLayoutPaddingRight * 2;
+
+        availableWidth -= cellWidth; // Assume at least one column
+        cellCountX = 1 + availableWidth / (cellWidth + mPageLayoutWidthGap);
+        availableWidth = availableWidth % (cellWidth + mPageLayoutWidthGap);
+
+        // Ensures that we show at least 30% of the holo icons on each side
+        final int minLeftoverWidth = (int) (cellWidth * 0.6f);
+
+        // Reserve room for the holo outlines
+        if (cellCountX <= 4) {
+            // When we're really tight on space, just pack the icons a bit closer together
+            final int missingWidth = minLeftoverWidth - availableWidth;
+            if (missingWidth > 0) {
+                mPageLayoutWidthGap -= Math.ceil(missingWidth * 1.0f / (cellCountX - 1));
+            }
+        } else {
+            if (cellCountX >= 8) {
+                // Carve out a few extra columns for very large widths
+                cellCountX = (int) (cellCountX * 0.9f);
+            } else if (availableWidth < minLeftoverWidth) {
+                cellCountX -= 1;
+            }
+        }
+        return cellCountX;
+    }
+
+    private int determineCellCountY(int availableHeight, PagedViewCellLayout layout) {
+        final int cellHeight = layout.getCellHeight();
+        final int screenHeight = mLauncher.getResources().getDisplayMetrics().heightPixels;
+
+        availableHeight -= mPageLayoutPaddingTop + mPageLayoutPaddingBottom;
+        availableHeight -= cellHeight; // Assume at least one row
+        availableHeight -= screenHeight * 0.16f;
+        return (1 + availableHeight / (cellHeight + mPageLayoutHeightGap));
     }
 
     void allowHardwareLayerCreation() {
@@ -504,7 +552,7 @@
 
     @Override
     public void syncPages() {
-        if (mFirstMeasure) {
+        if (mCellCountX <= 0 || mCellCountY <= 0) {
             // We don't know our size yet, which means we haven't calculated cell count x/y;
             // onMeasure will call us once we figure out our size
             return;
diff --git a/src/com/android/launcher2/PagedView.java b/src/com/android/launcher2/PagedView.java
index 1180106..bd41e02 100644
--- a/src/com/android/launcher2/PagedView.java
+++ b/src/com/android/launcher2/PagedView.java
@@ -113,8 +113,8 @@
     protected int mPageLayoutWidthGap;
     protected int mPageLayoutHeightGap;
     protected int mPageLayoutMaxHeight;
-    protected int mCellCountX;
-    protected int mCellCountY;
+    protected int mCellCountX = -1;
+    protected int mCellCountY = -1;
     protected boolean mCenterPagesVertically;
     protected boolean mAllowOverScroll = true;
     protected int mUnboundedScrollX;
@@ -1549,7 +1549,7 @@
      */
     public abstract void syncPageItems(int page);
 
-    public void invalidatePageData() {
+    protected void invalidatePageData() {
         if (mContentIsRefreshable) {
             // Update all the pages
             syncPages();
diff --git a/src/com/android/launcher2/PagedViewCellLayout.java b/src/com/android/launcher2/PagedViewCellLayout.java
index 9022cac..15b155f 100644
--- a/src/com/android/launcher2/PagedViewCellLayout.java
+++ b/src/com/android/launcher2/PagedViewCellLayout.java
@@ -79,6 +79,14 @@
         addView(mHolographicChildren);
     }
 
+    public int getCellWidth() {
+        return mCellWidth;
+    }
+
+    public int getCellHeight() {
+        return mCellHeight;
+    }
+
     public void allowHardwareLayerCreation() {
         // This is called after the first time we launch into All Apps. Before that point,
         // there's no need for hardware layers here since there's a hardware layer set on the
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index bc15b32..3f0c54e 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -80,10 +80,6 @@
     @SuppressWarnings({"UnusedDeclaration"})
     private static final String TAG = "Launcher.Workspace";
 
-    // This is how much the workspace shrinks when we enter all apps or
-    // customization mode
-    private static final float SHRINK_FACTOR = 0.16f;
-
     // How much the screens shrink when we enter spring loaded drag mode
     private static final float SPRING_LOADED_DRAG_SHRINK_FACTOR = 0.7f;
 
@@ -1512,12 +1508,15 @@
         final int screenWidth = getWidth();
         final int screenHeight = getHeight();
 
+        // How much the workspace shrinks when we enter all apps or customization mode
+        final float shrinkFactor = res.getInteger(R.integer.config_workspaceShrinkPercent) / 100.0f;
+
         // Making the assumption that all pages have the same width as the 0th
         final int pageWidth = getChildAt(0).getMeasuredWidth();
         final int pageHeight = getChildAt(0).getMeasuredHeight();
 
-        final int scaledPageWidth = (int) (SHRINK_FACTOR * pageWidth);
-        final int scaledPageHeight = (int) (SHRINK_FACTOR * pageHeight);
+        final int scaledPageWidth = (int) (shrinkFactor * pageWidth);
+        final int scaledPageHeight = (int) (shrinkFactor * pageHeight);
         final float extraScaledSpacing = res.getDimension(R.dimen.smallScreenExtraSpacing);
 
         final int screenCount = getChildCount();
@@ -1568,20 +1567,21 @@
 
         mAnimator = new AnimatorSet();
 
-        final float[] oldXs = new float[getChildCount()];
-        final float[] oldYs = new float[getChildCount()];
-        final float[] oldScaleXs = new float[getChildCount()];
-        final float[] oldScaleYs = new float[getChildCount()];
-        final float[] oldBackgroundAlphas = new float[getChildCount()];
-        final float[] oldAlphas = new float[getChildCount()];
-        final float[] oldRotationYs = new float[getChildCount()];
-        final float[] newXs = new float[getChildCount()];
-        final float[] newYs = new float[getChildCount()];
-        final float[] newScaleXs = new float[getChildCount()];
-        final float[] newScaleYs = new float[getChildCount()];
-        final float[] newBackgroundAlphas = new float[getChildCount()];
-        final float[] newAlphas = new float[getChildCount()];
-        final float[] newRotationYs = new float[getChildCount()];
+        final int childCount = getChildCount();
+        final float[] oldXs = new float[childCount];
+        final float[] oldYs = new float[childCount];
+        final float[] oldScaleXs = new float[childCount];
+        final float[] oldScaleYs = new float[childCount];
+        final float[] oldBackgroundAlphas = new float[childCount];
+        final float[] oldAlphas = new float[childCount];
+        final float[] oldRotationYs = new float[childCount];
+        final float[] newXs = new float[childCount];
+        final float[] newYs = new float[childCount];
+        final float[] newScaleXs = new float[childCount];
+        final float[] newScaleYs = new float[childCount];
+        final float[] newBackgroundAlphas = new float[childCount];
+        final float[] newAlphas = new float[childCount];
+        final float[] newRotationYs = new float[childCount];
 
         for (int i = 0; i < screenCount; i++) {
             final CellLayout cl = (CellLayout) getChildAt(i);
@@ -1606,15 +1606,15 @@
                 oldRotationYs[i] = cl.getRotationY();
                 newXs[i] = x;
                 newYs[i] = y;
-                newScaleXs[i] = SHRINK_FACTOR * rotationScaleX * extraShrinkFactor;
-                newScaleYs[i] = SHRINK_FACTOR * rotationScaleY * extraShrinkFactor;
+                newScaleXs[i] = shrinkFactor * rotationScaleX * extraShrinkFactor;
+                newScaleYs[i] = shrinkFactor * rotationScaleY * extraShrinkFactor;
                 newBackgroundAlphas[i] = finalAlpha;
                 newRotationYs[i] = rotation;
             } else {
                 cl.setX((int)x);
                 cl.setY((int)y);
-                cl.setScaleX(SHRINK_FACTOR * rotationScaleX * extraShrinkFactor);
-                cl.setScaleY(SHRINK_FACTOR * rotationScaleY * extraShrinkFactor);
+                cl.setScaleX(shrinkFactor * rotationScaleX * extraShrinkFactor);
+                cl.setScaleY(shrinkFactor * rotationScaleY * extraShrinkFactor);
                 cl.setBackgroundAlpha(finalAlpha);
                 cl.setAlpha(finalAlpha);
                 cl.setRotationY(rotation);