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);
+ }
}