Gracefully handle drawing caches allocation failure.
Bug #3431451

This bug was causing ListView to not render properly when showing an item
larger than the maximum drawing cache size. ListView relies on the drawing
cache to correctly mask all the background pixels. However, if the cache
is not properly created, the background will show through even though
ListView.isOpaque() == true. This change detects this case and falls
back to the default non opaque behavior.

Change-Id: I30a45e7a03fb7ebb2b12f0e85c075c2901954c44
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 378eb21..0df464f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2138,6 +2138,13 @@
 
     private int[] mDrawableState = null;
 
+    /**
+     * Set to true when drawing cache is enabled and cannot be created.
+     * 
+     * @hide
+     */
+    public boolean mCachingFailed;
+
     private Bitmap mDrawingCache;
     private Bitmap mUnscaledDrawingCache;
     private DisplayList mDisplayList;
@@ -8355,6 +8362,7 @@
      * @see #setLayerType(int, android.graphics.Paint)
      */
     public void setDrawingCacheEnabled(boolean enabled) {
+        mCachingFailed = false;
         setFlags(enabled ? DRAWING_CACHE_ENABLED : 0, DRAWING_CACHE_ENABLED);
     }
 
@@ -8597,7 +8605,7 @@
     public void buildDrawingCache() {
         buildDrawingCache(false);
     }
-
+    
     /**
      * <p>Forces the drawing cache to be built if the drawing cache is invalid.</p>
      *
@@ -8624,6 +8632,7 @@
     public void buildDrawingCache(boolean autoScale) {
         if ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || (autoScale ?
                 mDrawingCache == null : mUnscaledDrawingCache == null)) {
+            mCachingFailed = false;
 
             if (ViewDebug.TRACE_HIERARCHY) {
                 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.BUILD_CACHE);
@@ -8649,6 +8658,7 @@
                     (width * height * (opaque && !use32BitCache ? 2 : 4) >
                             ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize())) {
                 destroyDrawingCache();
+                mCachingFailed = true;
                 return;
             }
 
@@ -8701,6 +8711,7 @@
                     } else {
                         mUnscaledDrawingCache = null;
                     }
+                    mCachingFailed = true;
                     return;
                 }
 
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 0444496..cc4e89c 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -152,12 +152,12 @@
      * should be at least equal to the size of the screen in ARGB888 format.
      */
     @Deprecated
-    private static final int MAXIMUM_DRAWING_CACHE_SIZE = 320 * 480 * 4; // HVGA screen, ARGB8888
+    private static final int MAXIMUM_DRAWING_CACHE_SIZE = 480 * 800 * 4; // ARGB8888
 
     /**
      * The coefficient of friction applied to flings/scrolls.
      */
-    private static float SCROLL_FRICTION = 0.015f;
+    private static final float SCROLL_FRICTION = 0.015f;
 
     /**
      * Max distance to overscroll for edge effects
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index ad6b0f6..b5a2558 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -17,10 +17,6 @@
 package android.view;
 
 import android.animation.LayoutTransition;
-import android.view.animation.AlphaAnimation;
-import com.android.internal.R;
-import com.android.internal.util.Predicate;
-
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
@@ -39,10 +35,13 @@
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
 import android.view.animation.LayoutAnimationController;
 import android.view.animation.Transformation;
+import com.android.internal.R;
+import com.android.internal.util.Predicate;
 
 import java.util.ArrayList;
 import java.util.HashSet;
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 3f38f2e..27020c5 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -334,6 +334,7 @@
      * the drawing cache was enabled on the children
      */
     boolean mCachingStarted;
+    boolean mCachingActive;
 
     /**
      * The position of the view that received the down motion event
@@ -4169,7 +4170,7 @@
         if (mScrollingCacheEnabled && !mCachingStarted) {
             setChildrenDrawnWithCacheEnabled(true);
             setChildrenDrawingCacheEnabled(true);
-            mCachingStarted = true;
+            mCachingStarted = mCachingActive = true;
         }
     }
 
@@ -4178,7 +4179,7 @@
             mClearScrollingCache = new Runnable() {
                 public void run() {
                     if (mCachingStarted) {
-                        mCachingStarted = false;
+                        mCachingStarted = mCachingActive = false;
                         setChildrenDrawnWithCacheEnabled(false);
                         if ((mPersistentDrawingCache & PERSISTENT_SCROLLING_CACHE) == 0) {
                             setChildrenDrawingCacheEnabled(false);
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 12a0ebf..2802144 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -3013,12 +3013,9 @@
         return mItemsCanFocus;
     }
 
-    /**
-     * @hide Pending API council approval.
-     */
     @Override
     public boolean isOpaque() {
-        return (mCachingStarted && mIsCacheColorOpaque && mDividerIsOpaque &&
+        return (mCachingActive && mIsCacheColorOpaque && mDividerIsOpaque &&
                 hasOpaqueScrollbars()) || super.isOpaque();
     }
 
@@ -3071,6 +3068,10 @@
 
     @Override
     protected void dispatchDraw(Canvas canvas) {
+        if (mCachingStarted) {
+            mCachingActive = true;
+        }
+
         // Draw the dividers
         final int dividerHeight = mDividerHeight;
         final Drawable overscrollHeader = mOverScrollHeader;
@@ -3164,7 +3165,6 @@
                 }
             } else {
                 int top;
-                int listTop = effectivePaddingTop;
 
                 final int scrollY = mScrollY;
 
@@ -3181,7 +3181,7 @@
                         View child = getChildAt(i);
                         top = child.getTop();
                         // Don't draw dividers next to items that are not enabled
-                        if (top > listTop) {
+                        if (top > effectivePaddingTop) {
                             if ((areAllItemsSelectable ||
                                     (adapter.isEnabled(first + i) && (i == count - 1 ||
                                             adapter.isEnabled(first + i + 1))))) {
@@ -3220,6 +3220,15 @@
         super.dispatchDraw(canvas);
     }
 
+    @Override
+    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+        boolean more = super.drawChild(canvas, child, drawingTime);
+        if (mCachingActive && child.mCachingFailed) {
+            mCachingActive = false;
+        }
+        return more;
+    }
+
     /**
      * Draws a divider for the given child in the given bounds.
      *
@@ -3558,6 +3567,7 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
+        //noinspection SimplifiableIfStatement
         if (mItemsCanFocus && ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {
             // Don't handle edge touches immediately -- they may actually belong to one of our
             // descendants.