1be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang/* 2be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang * Copyright (C) 2013 Google Inc. 3be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang * Licensed to The Android Open Source Project. 4be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang * 5be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang * Licensed under the Apache License, Version 2.0 (the "License"); 6be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang * you may not use this file except in compliance with the License. 7be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang * You may obtain a copy of the License at 8be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang * 9be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang * http://www.apache.org/licenses/LICENSE-2.0 10be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang * 11be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang * Unless required by applicable law or agreed to in writing, software 12be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang * distributed under the License is distributed on an "AS IS" BASIS, 13be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang * See the License for the specific language governing permissions and 15be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang * limitations under the License. 16be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang */ 17be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang 18be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huangpackage com.android.mail.browse; 19be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang 20be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huangimport android.content.Context; 21be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huangimport android.util.AttributeSet; 22f2dabb90c33740189112757349c49ef9e48ea541Tony Mantlerimport android.view.GestureDetector; 23be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huangimport android.view.MotionEvent; 24d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantlerimport android.view.ScaleGestureDetector; 25f2dabb90c33740189112757349c49ef9e48ea541Tony Mantlerimport android.view.ViewConfiguration; 26be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huangimport android.widget.ScrollView; 27be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang 28be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huangimport com.android.mail.utils.LogUtils; 29be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang 3050554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdonimport java.util.Set; 3150554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdonimport java.util.concurrent.CopyOnWriteArraySet; 3250554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon 33be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang/** 34be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang * A container that tries to play nice with an internally scrollable {@link Touchable} child view. 35be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang * The assumption is that the child view can scroll horizontally, but not vertically, so any 36be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang * touch events on that child view should ALSO be sent here so it can simultaneously vertically 37be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang * scroll (not the standard either/or behavior). 38be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang * <p> 39be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang * Touch events on any other child of this ScrollView are intercepted in the standard fashion. 40be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang */ 41d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantlerpublic class MessageScrollView extends ScrollView implements ScrollNotifier, 42f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler ScaleGestureDetector.OnScaleGestureListener, GestureDetector.OnDoubleTapListener { 43be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang 44be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang /** 45be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang * A View that reports whether onTouchEvent() was recently called. 46be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang */ 47be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang public interface Touchable { 48be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang boolean wasTouched(); 49be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang void clearTouched(); 50f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler boolean zoomIn(); 51f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler boolean zoomOut(); 52be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang } 53be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang 54be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang /** 55be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang * True when performing "special" interception. 56be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang */ 57be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang private boolean mWantToIntercept; 58be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang /** 59be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang * Whether to perform the standard touch interception procedure. This is set to true when we 60be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang * want to intercept a touch stream from any child OTHER than {@link #mTouchableChild}. 61be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang */ 62be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang private boolean mInterceptNormally; 63be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang /** 64be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang * The special child that we want to NOT intercept from in the normal way. Instead, this child 65be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang * will continue to receive the touch event stream (so it can handle the horizontal component) 66be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang * while this parent will additionally handle the events to perform vertical scrolling. 67be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang */ 68be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang private Touchable mTouchableChild; 69be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang 70d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler /** 71d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler * We want to detect the scale gesture so that we don't try to scroll instead, but we don't 72d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler * care about actually interpreting it because the webview does that by itself when it handles 73d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler * the touch events. 74d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler * 75d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler * This might lead to really weird interactions if the two gesture detectors' implementations 76d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler * drift... 77d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler */ 78d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler private ScaleGestureDetector mScaleDetector; 79d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler private boolean mInScaleGesture; 80d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler 81f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler /** 82f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler * We also want to detect double-tap gestures, but in a way that doesn't conflict with 83f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler * tap-tap-drag gestures 84f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler */ 85f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler private GestureDetector mGestureDetector; 86f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler private boolean mDoubleTapOccurred; 87f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler private boolean mZoomedIn; 88f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler 89f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler /** 90f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler * Touch slop used to determine if this double tap is valid for starting a scale or should be 91f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler * ignored. 92f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler */ 93f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler private int mTouchSlopSquared; 94f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler 95f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler /** 96f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler * X and Y coordinates for the current down event. Since mDoubleTapOccurred only contains the 97f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler * information that there was a double tap event, use these to get the secondary tap 98f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler * information to determine if a user has moved beyond touch slop. 99f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler */ 100f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler private float mDownFocusX; 101f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler private float mDownFocusY; 102f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler 10350554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon private final Set<ScrollListener> mScrollListeners = 10450554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon new CopyOnWriteArraySet<ScrollListener>(); 10550554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon 106be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang public static final String LOG_TAG = "MsgScroller"; 107be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang 108be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang public MessageScrollView(Context c) { 109be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang this(c, null); 110be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang } 111be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang 112be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang public MessageScrollView(Context c, AttributeSet attrs) { 113be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang super(c, attrs); 114f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler final int touchSlop = ViewConfiguration.get(c).getScaledTouchSlop(); 115f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler mTouchSlopSquared = touchSlop * touchSlop; 116d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler mScaleDetector = new ScaleGestureDetector(c, this); 117f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler mGestureDetector = new GestureDetector(c, new GestureDetector.SimpleOnGestureListener()); 118f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler mGestureDetector.setOnDoubleTapListener(this); 119be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang } 120be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang 121be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang public void setInnerScrollableView(Touchable child) { 122be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang mTouchableChild = child; 123be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang } 124be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang 125be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang @Override 126be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang public boolean onInterceptTouchEvent(MotionEvent ev) { 127be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang if (mInterceptNormally) { 128be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang LogUtils.d(LOG_TAG, "IN ScrollView.onIntercept, NOW stealing. ev=%s", ev); 129be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang return true; 130be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang } else if (mWantToIntercept) { 131be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang LogUtils.d(LOG_TAG, "IN ScrollView.onIntercept, already stealing. ev=%s", ev); 132be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang return false; 133be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang } 134be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang 135be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang mWantToIntercept = super.onInterceptTouchEvent(ev); 136be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang LogUtils.d(LOG_TAG, "OUT ScrollView.onIntercept, steal=%s ev=%s", mWantToIntercept, ev); 137be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang return false; 138be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang } 139be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang 140be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang @Override 141be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang public boolean dispatchTouchEvent(MotionEvent ev) { 142be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang final int action = ev.getActionMasked(); 143be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang switch (action) { 144be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang case MotionEvent.ACTION_DOWN: 145be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang LogUtils.d(LOG_TAG, "IN ScrollView.dispatchTouch, clearing flags"); 146be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang mWantToIntercept = false; 147be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang mInterceptNormally = false; 148be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang break; 149be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang } 150be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang if (mTouchableChild != null) { 151be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang mTouchableChild.clearTouched(); 152be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang } 153d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler 154d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler mScaleDetector.onTouchEvent(ev); 155f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler mGestureDetector.onTouchEvent(ev); 156d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler 157be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang final boolean handled = super.dispatchTouchEvent(ev); 158be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang LogUtils.d(LOG_TAG, "OUT ScrollView.dispatchTouch, handled=%s ev=%s", handled, ev); 159be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang 160d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler if (mWantToIntercept && !mInScaleGesture) { 161be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang final boolean touchedChild = (mTouchableChild != null && mTouchableChild.wasTouched()); 162be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang if (touchedChild) { 163be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang // also give the event to this scroll view if the WebView got the event 164be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang // and didn't stop any parent interception 165be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang LogUtils.d(LOG_TAG, "IN extra ScrollView.onTouch, ev=%s", ev); 166be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang onTouchEvent(ev); 167be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang } else { 168be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang mInterceptNormally = true; 169be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang mWantToIntercept = false; 170be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang } 171be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang } 172be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang 173be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang return handled; 174be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang } 175be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang 17650554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon @Override 177d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler public boolean onScale(ScaleGestureDetector detector) { 178d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler return true; 179d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler } 180d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler 181d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler @Override 182d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler public boolean onScaleBegin(ScaleGestureDetector detector) { 183d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler LogUtils.d(LOG_TAG, "Begin scale gesture"); 184d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler mInScaleGesture = true; 185d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler return true; 186d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler } 187d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler 188d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler @Override 189d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler public void onScaleEnd(ScaleGestureDetector detector) { 190d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler LogUtils.d(LOG_TAG, "End scale gesture"); 191d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler mInScaleGesture = false; 192d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler } 193d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler 194d4dae4817fdddbd7a43f18ddbef38815d6e0a182Tony Mantler @Override 195f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler public boolean onSingleTapConfirmed(MotionEvent e) { 196f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler return false; 197f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler } 198f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler 199f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler @Override 200f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler public boolean onDoubleTap(MotionEvent e) { 201f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler mDoubleTapOccurred = true; 202f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler return false; 203f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler } 204f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler 205f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler @Override 206f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler public boolean onDoubleTapEvent(MotionEvent e) { 207f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler final int action = e.getAction(); 208f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler boolean handled = false; 209f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler 210f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler switch (action) { 211f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler case MotionEvent.ACTION_DOWN: 212f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler mDownFocusX = e.getX(); 213f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler mDownFocusY = e.getY(); 214f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler break; 215f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler case MotionEvent.ACTION_UP: 216f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler handled = triggerZoom(); 217f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler break; 218f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler case MotionEvent.ACTION_MOVE: 219f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler final int deltaX = (int) (e.getX() - mDownFocusX); 220f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler final int deltaY = (int) (e.getY() - mDownFocusY); 221f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler int distance = (deltaX * deltaX) + (deltaY * deltaY); 222f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler if (distance > mTouchSlopSquared) { 223f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler mDoubleTapOccurred = false; 224f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler } 225f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler break; 226f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler 227f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler } 228f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler return handled; 229f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler } 230f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler 231f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler private boolean triggerZoom() { 232f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler boolean handled = false; 233f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler if (mDoubleTapOccurred) { 2349d3b1dd6e411bba6c415e19d51e32569956a8bb7Tony Mantler if (mZoomedIn) { 2359d3b1dd6e411bba6c415e19d51e32569956a8bb7Tony Mantler mTouchableChild.zoomOut(); 2369d3b1dd6e411bba6c415e19d51e32569956a8bb7Tony Mantler } else { 2379d3b1dd6e411bba6c415e19d51e32569956a8bb7Tony Mantler mTouchableChild.zoomIn(); 238f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler } 2399d3b1dd6e411bba6c415e19d51e32569956a8bb7Tony Mantler mZoomedIn = !mZoomedIn; 2409d3b1dd6e411bba6c415e19d51e32569956a8bb7Tony Mantler LogUtils.d(LogUtils.TAG, "Trigger Zoom!"); 2419d3b1dd6e411bba6c415e19d51e32569956a8bb7Tony Mantler handled = true; 242f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler } 243f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler mDoubleTapOccurred = false; 244f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler return handled; 245f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler } 246f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler 247f2dabb90c33740189112757349c49ef9e48ea541Tony Mantler @Override 24850554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon public void addScrollListener(ScrollListener l) { 24950554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon mScrollListeners.add(l); 25050554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon } 25150554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon 25250554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon @Override 25350554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon public void removeScrollListener(ScrollListener l) { 25450554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon mScrollListeners.remove(l); 25550554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon } 25650554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon 25750554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon @Override 25850554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon protected void onScrollChanged(int l, int t, int oldl, int oldt) { 25950554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon super.onScrollChanged(l, t, oldl, oldt); 26050554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon for (ScrollListener listener : mScrollListeners) { 26150554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon listener.onNotifierScroll(t); 26250554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon } 26350554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon } 26450554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon 26550554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon @Override 26650554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon public int computeVerticalScrollRange() { 26750554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon return super.computeVerticalScrollRange(); 26850554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon } 26950554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon 27050554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon @Override 27150554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon public int computeVerticalScrollOffset() { 27250554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon return super.computeVerticalScrollOffset(); 27350554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon } 27450554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon 27550554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon @Override 27650554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon public int computeVerticalScrollExtent() { 27750554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon return super.computeVerticalScrollExtent(); 27850554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon } 27950554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon 28050554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon @Override 28150554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon public int computeHorizontalScrollRange() { 28250554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon return super.computeHorizontalScrollRange(); 28350554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon } 28450554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon 28550554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon @Override 28650554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon public int computeHorizontalScrollOffset() { 28750554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon return super.computeHorizontalScrollOffset(); 28850554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon } 28950554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon 29050554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon @Override 29150554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon public int computeHorizontalScrollExtent() { 29250554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon return super.computeHorizontalScrollExtent(); 29350554de30bfe85befac6edf5376b600eb3a1f462Martin Hibdon } 294be7739bf0cebc5018c8ffb0c649fc62a74d74e69Andy Huang} 295