Refactoring CellLayout into three classes

- splitting the rendering of children from the CellLayout to enhance performance, gives ~4 fps boost while scrolling on pages full of icons, and no change on pages full of widgets
- this will allow us to add hardware layer support trivially, which will increase performance while scrolling ~6-10 fps
- separated logic for caching celllayouts to bitmaps into a separate class

Change-Id: Ib6abeb19126e1504997b43c2f44af2a2fb3cd39f
diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java
index 49ae652..9c629ee 100644
--- a/src/com/android/launcher2/CellLayout.java
+++ b/src/com/android/launcher2/CellLayout.java
@@ -25,7 +25,6 @@
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.app.WallpaperManager;
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -37,8 +36,6 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
-import android.graphics.Bitmap.Config;
-import android.graphics.PorterDuff.Mode;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -53,7 +50,7 @@
 
 import java.util.Arrays;
 
-public class CellLayout extends ViewGroup implements VisibilityChangedListener {
+public class CellLayout extends ViewGroup {
     static final String TAG = "CellLayout";
 
     private int mCellWidth;
@@ -99,18 +96,6 @@
     private float mGlowBackgroundScale;
     private float mGlowBackgroundAlpha;
 
-    private Bitmap mCache;
-    private Canvas mCacheCanvas;
-    private Rect mCacheRect;
-    private Paint mCachePaint;
-
-    private boolean mIsCacheEnabled = true;
-    private boolean mDisableCacheUpdates = false;
-    private boolean mForceCacheUpdate = false;
-    private boolean mIsCacheDirty = true;
-    private float mBitmapCacheScale;
-    private float mMaxScaleForUsingBitmapCache;
-
     private boolean mAcceptsDrops = false;
     // If we're actively dragging something over this screen, mIsDragOverlapping is true
     private boolean mIsDragOverlapping = false;
@@ -136,11 +121,10 @@
     // When a drag operation is in progress, holds the nearest cell to the touch point
     private final int[] mDragCell = new int[2];
 
-    private final WallpaperManager mWallpaperManager;
-
     private boolean mDragging = false;
 
     private TimeInterpolator mEaseOutInterpolator;
+    private CellLayoutChildren mChildren;
 
     public CellLayout(Context context) {
         this(context, null);
@@ -181,8 +165,6 @@
 
         setAlwaysDrawnWithCacheEnabled(false);
 
-        mWallpaperManager = WallpaperManager.getInstance(context);
-
         final Resources res = getResources();
 
         if (LauncherApplication.isScreenXLarge()) {
@@ -279,15 +261,13 @@
 
         mBackgroundRect = new Rect();
         mGlowBackgroundRect = new Rect();
-        mCacheRect = new Rect();
         setHoverScale(1.0f);
         setHoverAlpha(1.0f);
 
-        mBitmapCacheScale =
-            res.getInteger(R.integer.config_workspaceScreenBitmapCacheScale) / 100.0f;
-        mMaxScaleForUsingBitmapCache =
-            res.getInteger(R.integer.config_maxScaleForUsingWorkspaceScreenBitmapCache) / 100.0f;
-        mCacheCanvas = new Canvas();
+        mChildren = new CellLayoutChildren(context);
+        mChildren.setCellDimensions(
+                mCellWidth, mCellHeight, mLeftPadding, mTopPadding, mWidthGap, mHeightGap);
+        addView(mChildren);
     }
 
     public void setIsDefaultDropTarget(boolean isDefaultDropTarget) {
@@ -376,100 +356,12 @@
         }
     }
 
-    public void drawChildren(Canvas canvas) {
-        super.dispatchDraw(canvas);
-    }
-
-    private void invalidateIfNeeded() {
-        if (mIsCacheDirty) {
-            // Force a redraw to update the cache if it's dirty
-            invalidate();
-        }
-    }
-
-    public void enableCache() {
-        mIsCacheEnabled = true;
-        invalidateIfNeeded();
-    }
-
-    public void disableCache() {
-        mIsCacheEnabled = false;
-    }
-
     public void disableCacheUpdates() {
-        mDisableCacheUpdates = true;
-        // Force just one update before we enter a period of no cache updates
-        mForceCacheUpdate = true;
+        mChildren.disableCacheUpdates();
     }
 
     public void enableCacheUpdates() {
-        mDisableCacheUpdates = false;
-        invalidateIfNeeded();
-    }
-
-    private void invalidateCache() {
-        mIsCacheDirty = true;
-        invalidate();
-    }
-
-    public void receiveVisibilityChangedMessage(View v) {
-        invalidateCache();
-    }
-
-    public void updateCache() {
-        mCacheCanvas.drawColor(0, Mode.CLEAR);
-
-        float alpha = getAlpha();
-        setAlpha(1.0f);
-        drawChildren(mCacheCanvas);
-        setAlpha(alpha);
-
-        mIsCacheDirty = false;
-    }
-
-    public void dispatchDraw(Canvas canvas) {
-        final int count = getChildCount();
-
-        if (!mIsCacheDirty) {
-            // Check if one of the children (an icon or widget) is dirty
-            for (int i = 0; i < count; i++) {
-                final View child = getChildAt(i);
-                if (child.isDirty()) {
-                    mIsCacheDirty = true;
-                    break;
-                }
-            }
-        }
-
-        boolean useBitmapCache = mIsCacheEnabled && getScaleX() < mMaxScaleForUsingBitmapCache;
-        if (mForceCacheUpdate ||
-                (useBitmapCache && !mDisableCacheUpdates)) {
-            // Sometimes we force a cache update-- this is used to make sure the cache will look as
-            // up-to-date as possible right when we disable cache updates
-            if (mIsCacheDirty) {
-                updateCache();
-            }
-            mForceCacheUpdate = false;
-        }
-
-        if (useBitmapCache) {
-            mCachePaint.setAlpha((int)(255*getAlpha()));
-            canvas.drawBitmap(mCache, mCacheRect, mBackgroundRect, mCachePaint);
-        } else {
-            super.dispatchDraw(canvas);
-        }
-    }
-
-    private void prepareCacheBitmap() {
-        if (mCache == null) {
-            mCache = Bitmap.createBitmap((int) (getWidth() * mBitmapCacheScale),
-                    (int) (getHeight() * mBitmapCacheScale), Config.ARGB_8888);
-
-            mCachePaint = new Paint();
-            mCachePaint.setFilterBitmap(true);
-            mCacheCanvas.setBitmap(mCache);
-            mCacheCanvas.scale(mBitmapCacheScale, mBitmapCacheScale);
-        }
+        mChildren.enableCacheUpdates();
     }
 
     @Override
@@ -610,15 +502,8 @@
 
             child.setId(childId);
 
-            addView(child, index, lp);
-            child.setAlpha(getAlpha());
-            if (child instanceof VisibilityChangedBroadcaster) {
-                VisibilityChangedBroadcaster v = (VisibilityChangedBroadcaster) child;
-                v.setVisibilityChangedListener(this);
-            }
+            mChildren.addView(child, index, lp);
 
-            // invalidate the cache to have it reflect the new item
-            invalidateCache();
             if (markCells) markCellsAsOccupiedForView(child);
 
             return true;
@@ -639,70 +524,56 @@
 
     @Override
     public void removeAllViews() {
-        super.removeAllViews();
         clearOccupiedCells();
-        invalidateCache();
+        mChildren.removeAllViews();
     }
 
     @Override
     public void removeAllViewsInLayout() {
-        super.removeAllViewsInLayout();
         clearOccupiedCells();
-        invalidateCache();
+        mChildren.removeAllViewsInLayout();
     }
 
     public void removeViewWithoutMarkingCells(View view) {
-        super.removeView(view);
-        invalidateCache();
+        mChildren.removeViewWithoutMarkingCells(view);
     }
 
     @Override
     public void removeView(View view) {
         markCellsAsUnoccupiedForView(view);
-        super.removeView(view);
-        invalidateCache();
+        mChildren.removeView(view);
     }
 
     @Override
     public void removeViewAt(int index) {
-        markCellsAsUnoccupiedForView(getChildAt(index));
-        super.removeViewAt(index);
-        invalidateCache();
+        markCellsAsUnoccupiedForView(mChildren.getChildAt(index));
+        mChildren.removeViewAt(index);
     }
 
     @Override
     public void removeViewInLayout(View view) {
         markCellsAsUnoccupiedForView(view);
-        super.removeViewInLayout(view);
-        invalidateCache();
+        mChildren.removeViewInLayout(view);
     }
 
     @Override
     public void removeViews(int start, int count) {
         for (int i = start; i < start + count; i++) {
-            markCellsAsUnoccupiedForView(getChildAt(i));
+            markCellsAsUnoccupiedForView(mChildren.getChildAt(i));
         }
-        super.removeViews(start, count);
-        invalidateCache();
+        mChildren.removeViews(start, count);
     }
 
     @Override
     public void removeViewsInLayout(int start, int count) {
         for (int i = start; i < start + count; i++) {
-            markCellsAsUnoccupiedForView(getChildAt(i));
+            markCellsAsUnoccupiedForView(mChildren.getChildAt(i));
         }
-        super.removeViewsInLayout(start, count);
-        invalidateCache();
+        mChildren.removeViewsInLayout(start, count);
     }
 
-    @Override
-    public void requestChildFocus(View child, View focused) {
-        super.requestChildFocus(child, focused);
-        if (child != null) {
-            Rect r = new Rect();
-            child.getDrawingRect(r);
-            requestRectangleOnScreen(r);
-        }
+    public void drawChildren(Canvas canvas) {
+        mChildren.draw(canvas);
     }
 
     @Override
@@ -716,11 +587,11 @@
         final Rect frame = mRect;
         final int x = touchX + mScrollX;
         final int y = touchY + mScrollY;
-        final int count = getChildCount();
+        final int count = mChildren.getChildCount();
 
         boolean found = false;
         for (int i = count - 1; i >= 0; i--) {
-            final View child = getChildAt(i);
+            final View child = mChildren.getChildAt(i);
 
             if ((child.getVisibility()) == VISIBLE || child.getAnimation() != null) {
                 child.getHitRect(frame);
@@ -753,7 +624,6 @@
         setTag(cellInfo);
     }
 
-
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         if (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev)) {
@@ -886,68 +756,35 @@
             mWidthGap = mHeightGap = minGap;
         }
 
-        int count = getChildCount();
-
-        for (int i = 0; i < count; i++) {
-            View child = getChildAt(i);
-            LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap,
-                    mLeftPadding, mTopPadding);
-
-            int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
-            int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height,
-                    MeasureSpec.EXACTLY);
-
-            child.measure(childWidthMeasureSpec, childheightMeasureSpec);
-        }
+        // Initial values correspond to widthSpecMode == MeasureSpec.EXACTLY
+        int newWidth = widthSpecSize;
+        int newHeight = heightSpecSize;
         if (widthSpecMode == MeasureSpec.AT_MOST) {
-            int newWidth = mLeftPadding + mRightPadding + (mCountX * cellWidth) +
+            newWidth = mLeftPadding + mRightPadding + (mCountX * cellWidth) +
                 ((mCountX - 1) * mWidthGap);
-            int newHeight = mTopPadding + mBottomPadding + (mCountY * cellHeight) +
+            newHeight = mTopPadding + mBottomPadding + (mCountY * cellHeight) +
                 ((mCountY - 1) * mHeightGap);
             setMeasuredDimension(newWidth, newHeight);
-        } else if (widthSpecMode == MeasureSpec.EXACTLY) {
-            setMeasuredDimension(widthSpecSize, heightSpecSize);
         }
+
+        int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            View child = getChildAt(i);
+            int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.EXACTLY);
+            int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(newHeight,
+                    MeasureSpec.EXACTLY);
+            child.measure(childWidthMeasureSpec, childheightMeasureSpec);
+        }
+        setMeasuredDimension(newWidth, newHeight);
     }
 
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         int count = getChildCount();
-
         for (int i = 0; i < count; i++) {
-            final View child = getChildAt(i);
-            if (child.getVisibility() != GONE) {
-
-                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
-
-                int childLeft = lp.x;
-                int childTop = lp.y;
-                child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
-
-                if (lp.dropped) {
-                    lp.dropped = false;
-
-                    final int[] cellXY = mTmpCellXY;
-                    getLocationOnScreen(cellXY);
-                    mWallpaperManager.sendWallpaperCommand(getWindowToken(),
-                            WallpaperManager.COMMAND_DROP,
-                            cellXY[0] + childLeft + lp.width / 2,
-                            cellXY[1] + childTop + lp.height / 2, 0, null);
-
-                    if (lp.animateDrop) {
-                        lp.animateDrop = false;
-
-                        // This call does not result in a requestLayout(), but at one point did.
-                        // We need to be cautious about any method calls within the layout pass
-                        // to insure we don't leave the view tree in a bad state.
-                        ((Workspace) mParent).animateViewIntoPosition(child);
-                    }
-                }
-            }
+            View child = getChildAt(i);
+            child.layout(0, 0, r - l, b - t);
         }
-        prepareCacheBitmap();
-        invalidateCache();
     }
 
     @Override
@@ -955,28 +792,16 @@
         super.onSizeChanged(w, h, oldw, oldh);
         mBackgroundRect.set(0, 0, w, h);
         updateGlowRect();
-        mCacheRect.set(0, 0, (int) (mBitmapCacheScale * w), (int) (mBitmapCacheScale * h));
-        mCache = null;
-        prepareCacheBitmap();
-        invalidateCache();
     }
 
     @Override
     protected void setChildrenDrawingCacheEnabled(boolean enabled) {
-        final int count = getChildCount();
-        for (int i = 0; i < count; i++) {
-            final View view = getChildAt(i);
-            view.setDrawingCacheEnabled(enabled);
-            // Update the drawing caches
-            if (!view.isHardwareAccelerated()) {
-                view.buildDrawingCache(true);
-            }
-        }
+        mChildren.setChildrenDrawingCacheEnabled(enabled);
     }
 
     @Override
     protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
-        super.setChildrenDrawnWithCacheEnabled(enabled);
+        mChildren.setChildrenDrawnWithCacheEnabled(enabled);
     }
 
     public float getBackgroundAlpha() {
@@ -1017,17 +842,7 @@
     }
 
     public View getChildAt(int x, int y) {
-        final int count = getChildCount();
-        for (int i = 0; i < count; i++) {
-            View child = getChildAt(i);
-            LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
-            if ((lp.cellX <= x) && (x < lp.cellX + lp.cellHSpan) &&
-                    (lp.cellY <= y) && (y < lp.cellY + lp.cellHSpan)) {
-                return child;
-            }
-        }
-        return null;
+        return mChildren.getChildAt(x, y);
     }
 
     /**
@@ -1506,13 +1321,13 @@
     }
 
     private void markCellsAsOccupiedForView(View view) {
-        if (view == null || view.getParent() != this) return;
+        if (view == null || view.getParent() != mChildren) return;
         LayoutParams lp = (LayoutParams) view.getLayoutParams();
         markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, true);
     }
 
     private void markCellsAsUnoccupiedForView(View view) {
-        if (view == null || view.getParent() != this) return;
+        if (view == null || view.getParent() != mChildren) return;
         LayoutParams lp = (LayoutParams) view.getLayoutParams();
         markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false);
     }
@@ -1676,14 +1491,3 @@
         }
     }
 }
-
-// Custom interfaces used to listen to "visibility changed" events of *children* of Views. Avoided
-// using "onVisibilityChanged" in the names because there's a method of that name in framework
-// (which can only can be used to listen to ancestors' "visibility changed" events)
-interface VisibilityChangedBroadcaster {
-    public void setVisibilityChangedListener(VisibilityChangedListener listener);
-}
-
-interface VisibilityChangedListener {
-    public void receiveVisibilityChangedMessage(View v);
-}