Fix drawing bug: opaque invalidations should not be taken into account when the invalidated view is animating. Also add the ability to disable the auto-fade on the GestureOverlayView.
diff --git a/api/current.xml b/api/current.xml
index 94e2bb1..8140589 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -3518,17 +3518,6 @@
  visibility="public"
 >
 </field>
-<field name="donut_resource_pad33"
- type="int"
- transient="false"
- volatile="false"
- value="16843391"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="donut_resource_pad4"
  type="int"
  transient="false"
@@ -4035,6 +4024,17 @@
  visibility="public"
 >
 </field>
+<field name="fadeEnabled"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843391"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="fadeOffset"
  type="int"
  transient="false"
@@ -46821,6 +46821,17 @@
  visibility="public"
 >
 </method>
+<method name="isFadeEnabled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="isGesturing"
  return="boolean"
  abstract="false"
@@ -46893,6 +46904,19 @@
 <parameter name="enabled" type="boolean">
 </parameter>
 </method>
+<method name="setFadeEnabled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fadeEnabled" type="boolean">
+</parameter>
+</method>
 <method name="setGesture"
  return="void"
  abstract="false"
diff --git a/core/java/android/gesture/GestureOverlayView.java b/core/java/android/gesture/GestureOverlayView.java
index 3476e4d..4cdbd3e 100755
--- a/core/java/android/gesture/GestureOverlayView.java
+++ b/core/java/android/gesture/GestureOverlayView.java
@@ -40,6 +40,7 @@
  * @attr ref android.R.styleable#GestureOverlayView_eventsInterceptionEnabled
  * @attr ref android.R.styleable#GestureOverlayView_fadeDuration
  * @attr ref android.R.styleable#GestureOverlayView_fadeOffset
+ * @attr ref android.R.styleable#GestureOverlayView_fadeEnabled
  * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeWidth
  * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeAngleThreshold
  * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeLengthThreshold
@@ -62,6 +63,7 @@
     private long mFadeOffset = 420;
     private long mFadingStart;
     private boolean mFadingHasStarted;
+    private boolean mFadeEnabled = true;
 
     private int mCurrentColor;
     private int mCertainGestureColor = 0xFFFFFF00;
@@ -146,6 +148,8 @@
                 mGestureStrokeSquarenessTreshold);
         mInterceptEvents = a.getBoolean(R.styleable.GestureOverlayView_eventsInterceptionEnabled,
                 mInterceptEvents);
+        mFadeEnabled = a.getBoolean(R.styleable.GestureOverlayView_fadeEnabled,
+                mFadeEnabled);
 
         a.recycle();
 
@@ -238,6 +242,14 @@
         mInterceptEvents = enabled;
     }
 
+    public boolean isFadeEnabled() {
+        return mFadeEnabled;
+    }
+
+    public void setFadeEnabled(boolean fadeEnabled) {
+        mFadeEnabled = fadeEnabled;
+    }
+
     public Gesture getGesture() {
         return mCurrentGesture;
     }
@@ -329,12 +341,14 @@
     private void clear(boolean animated, boolean fireActionPerformed) {
         setPaintAlpha(255);
         if (animated && mCurrentGesture != null) {
-            mFadingAlpha = 1.0f;
-            mIsFadingOut = true;
-            mFadingHasStarted = false;
-            mFadingOut.fireActionPerformed = fireActionPerformed;
-            removeCallbacks(mFadingOut);
-            mFadingStart = AnimationUtils.currentAnimationTimeMillis() + mFadeOffset;
+            if (mFadeEnabled) {
+                mFadingAlpha = 1.0f;
+                mIsFadingOut = true;
+                mFadingHasStarted = false;
+                mFadingOut.fireActionPerformed = fireActionPerformed;
+                removeCallbacks(mFadingOut);
+                mFadingStart = AnimationUtils.currentAnimationTimeMillis() + mFadeOffset;
+            }
             postDelayed(mFadingOut, mFadeOffset);
         } else {
             mPath.rewind();
@@ -584,6 +598,16 @@
         mIsGesturing = false;
     }
 
+    private void fireOnGesturePerformed() {
+        final ArrayList<OnGesturePerformedListener> actionListeners =
+                mOnGesturePerformedListeners;
+        final int count = actionListeners.size();
+        for (int i = 0; i < count; i++) {
+            actionListeners.get(i).onGesturePerformed(GestureOverlayView.this,
+                    mCurrentGesture);
+        }
+    }    
+
     private class FadeOutRunnable implements Runnable {
         boolean fireActionPerformed;
 
@@ -594,13 +618,7 @@
 
                 if (duration > mFadeDuration) {
                     if (fireActionPerformed) {
-                        final ArrayList<OnGesturePerformedListener> actionListeners =
-                                mOnGesturePerformedListeners;
-                        final int count = actionListeners.size();
-                        for (int i = 0; i < count; i++) {
-                            actionListeners.get(i).onGesturePerformed(GestureOverlayView.this,
-                                    mCurrentGesture);
-                        }
+                        fireOnGesturePerformed();
                     }
 
                     mIsFadingOut = false;
@@ -618,6 +636,14 @@
                 }
 
                 invalidate();
+            } else if (!mFadeEnabled) {
+                fireOnGesturePerformed();
+
+                mIsFadingOut = false;
+                mFadingHasStarted = false;
+                mPath.rewind();
+                mCurrentGesture = null;
+                setPaintAlpha(255);
             }
         }
     }
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index c6f36a0..8b0629c 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2351,7 +2351,8 @@
             final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION;
 
             // Check whether the child that requests the invalidate is fully opaque
-            final boolean isOpaque = child.isOpaque();
+            final boolean isOpaque = child.isOpaque() && !drawAnimation &&
+                    child.getAnimation() != null;
             // Mark the child as dirty, using the appropriate flag
             // Make sure we do not set both flags at the same time
             final int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY;
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index e5fa2c5..b54802b 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2107,6 +2107,8 @@
         <!-- Defines whether the overlay should intercept the motion events when a gesture
              is recognized. -->
         <attr name="eventsInterceptionEnabled" format="boolean" />
+        <!-- Defines whether the gesture will automatically fade out after being recognized. -->
+        <attr name="fadeEnabled" format="boolean" />
     </declare-styleable>
 
     <!-- ======================================= -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e36159d..621270e 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1112,6 +1112,7 @@
   <public type="attr" name="gestureStrokeSquarenessThreshold" />
   <public type="attr" name="gestureStrokeAngleThreshold" />
   <public type="attr" name="eventsInterceptionEnabled" />
+  <public type="attr" name="fadeEnabled" />
 
   <public-padding type="attr" name="donut_resource_pad" end="0x0101029f" />