Fix touch interceptions.
We were unfortunately passing through all touches and never intercepting events from the message ScrollView. This was desirable when the drag was over the message content, but in the surrounding chrome it would lead to pressed states while dragging, sometimes accidentally causing text selection. This change makes that logic special cased to WebViews only. Bug: 5361173 Change-Id: Icf535c015cec4a79a5ad7eba3d6c5aa7bd572a8a
This commit is contained in:
parent
1f6769facc
commit
98c968d178
@ -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 @@ public class NonLockingScrollView extends ScrollView {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user