Merge "Fix touch interceptions."
diff --git a/src/com/android/email/view/NonLockingScrollView.java b/src/com/android/email/view/NonLockingScrollView.java
index d5098a3..832136c 100644
--- a/src/com/android/email/view/NonLockingScrollView.java
+++ b/src/com/android/email/view/NonLockingScrollView.java
@@ -18,10 +18,16 @@
 package com.android.email.view;
 
 import android.content.Context;
+import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.webkit.WebView;
 import android.widget.ScrollView;
 
+import java.util.ArrayList;
+
 /**
  * A {@link ScrollView} that will never lock scrolling in a particular direction.
  *
@@ -44,31 +50,90 @@
     }
 
     /**
-     * Whether or not this view is in the middle of a drag.
+     * Whether or not the contents of this view is being dragged by one of the children in
+     * {@link #mChildrenNeedingAllTouches}.
      */
-    private boolean mInDrag = false;
+    private boolean mInCustomDrag = false;
+
+    /**
+     * The list of children who should always receive touch events, and not have them intercepted.
+     */
+    private final ArrayList<View> mChildrenNeedingAllTouches = new ArrayList<View>();
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         final int action = ev.getActionMasked();
         final boolean isUp = action == MotionEvent.ACTION_UP;
 
-        if (isUp && mInDrag) {
+        if (isUp && mInCustomDrag) {
             // An up event after a drag should be intercepted so that child views don't handle
             // click events falsely after a drag.
-            mInDrag = false;
+            mInCustomDrag = false;
             onTouchEvent(ev);
             return true;
         }
 
+        if (!mInCustomDrag && !isEventOverChild(ev, mChildrenNeedingAllTouches)) {
+            return super.onInterceptTouchEvent(ev);
+        }
+
         // Note the normal scrollview implementation is to intercept all touch events after it has
         // detected a drag starting. We will handle this ourselves.
-        mInDrag = super.onInterceptTouchEvent(ev);
-        if (mInDrag) {
+        mInCustomDrag = super.onInterceptTouchEvent(ev);
+        if (mInCustomDrag) {
             onTouchEvent(ev);
         }
 
         // Don't intercept events - pass them on to children as normal.
         return false;
     }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        excludeChildrenFromInterceptions(this);
+    }
+
+    /**
+     * Traverses the view tree for {@link WebView}s so they can be excluded from touch
+     * interceptions and receive all events.
+     */
+    private void excludeChildrenFromInterceptions(View node) {
+        // If additional types of children should be excluded (e.g. horizontal scrolling banners),
+        // this needs to be modified accordingly.
+        if (node instanceof WebView) {
+            mChildrenNeedingAllTouches.add(node);
+        } else if (node instanceof ViewGroup) {
+            ViewGroup viewGroup = (ViewGroup) node;
+            final int childCount = viewGroup.getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                final View child = viewGroup.getChildAt(i);
+                excludeChildrenFromInterceptions(child);
+            }
+        }
+    }
+
+    private static final Rect sHitFrame = new Rect();
+    private static boolean isEventOverChild(MotionEvent ev, ArrayList<View> children) {
+        final int actionIndex = ev.getActionIndex();
+        final float x = ev.getX(actionIndex);
+        final float y = ev.getY(actionIndex);
+
+        for (View child : children) {
+            if (!canViewReceivePointerEvents(child)) {
+                continue;
+            }
+            child.getHitRect(sHitFrame);
+
+            // child can receive the motion event.
+            if (sHitFrame.contains((int) x, (int) y)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean canViewReceivePointerEvents(View child) {
+        return child.getVisibility() == VISIBLE || (child.getAnimation() != null);
+    }
 }