EMMA Coverage Report (generated Fri Aug 23 16:39:17 PDT 2013)
[all classes][org.chromium.content.browser]

COVERAGE SUMMARY FOR SOURCE FILE [LongPressDetector.java]

nameclass, %method, %block, %line, %
LongPressDetector.java100% (2/2)100% (15/15)95%  (270/285)97%  (72.5/75)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class LongPressDetector$LongPressHandler100% (1/1)100% (2/2)54%  (14/26)88%  (7/8)
handleMessage (Message): void 100% (1/1)40%  (8/20)80%  (4/5)
LongPressDetector$LongPressHandler (LongPressDetector): void 100% (1/1)100% (6/6)100% (3/3)
     
class LongPressDetector100% (1/1)100% (13/13)99%  (256/259)98%  (65.5/67)
sendLongPressGestureForTest (): void 100% (1/1)86%  (6/7)92%  (2.8/3)
cancelLongPressIfNeeded (Iterator): void 100% (1/1)96%  (25/26)90%  (9/10)
startLongPressTimerIfNeeded (MotionEvent): void 100% (1/1)97%  (29/30)96%  (5.8/6)
<static initializer> 100% (1/1)100% (5/5)100% (2/2)
LongPressDetector (Context, LongPressDetector$LongPressDelegate): void 100% (1/1)100% (23/23)100% (7/7)
access$000 (LongPressDetector): void 100% (1/1)100% (3/3)100% (1/1)
cancelLongPress (): void 100% (1/1)100% (17/17)100% (6/6)
cancelLongPressIfNeeded (MotionEvent): void 100% (1/1)100% (73/73)100% (14/14)
confirmOfferMoveEventToJavaScript (MotionEvent): boolean 100% (1/1)100% (33/33)100% (6/6)
dispatchLongPress (): void 100% (1/1)100% (15/15)100% (5/5)
hasPendingMessage (): boolean 100% (1/1)100% (7/7)100% (1/1)
isInLongPress (): boolean 100% (1/1)100% (3/3)100% (1/1)
onOfferTouchEventToJavaScript (MotionEvent): void 100% (1/1)100% (17/17)100% (5/5)

1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4 
5package org.chromium.content.browser;
6 
7import android.content.Context;
8import android.os.Handler;
9import android.os.Message;
10import android.view.MotionEvent;
11import android.view.ViewConfiguration;
12 
13import java.util.Iterator;
14 
15/**
16 * This class controls long press timers and is owned by a ContentViewGestureHandler.
17 *
18 * For instance, we may receive a DOWN then UP, so we may need to cancel the
19 * timer before the UP completes its roundtrip from WebKit.
20 */
21class LongPressDetector {
22    private MotionEvent mCurrentDownEvent;
23    private final LongPressDelegate mLongPressDelegate;
24    private final Handler mLongPressHandler;
25    private final int mTouchSlopSquare;
26    private boolean mInLongPress;
27 
28    // The following are used when touch events are offered to native, and not for
29    // anything relating to the GestureDetector.
30    // True iff a touch_move has exceeded the touch slop distance.
31    private boolean mMoveConfirmed;
32    // Coordinates of the start of a touch event (i.e. the touch_down).
33    private int mTouchInitialX;
34    private int mTouchInitialY;
35 
36    private static final int LONG_PRESS = 2;
37 
38    private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
39    private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
40 
41    LongPressDetector(Context context, LongPressDelegate delegate) {
42        mLongPressDelegate = delegate;
43        mLongPressHandler = new LongPressHandler();
44        final ViewConfiguration configuration = ViewConfiguration.get(context);
45        int touchSlop = configuration.getScaledTouchSlop();
46        mTouchSlopSquare = touchSlop * touchSlop;
47    }
48 
49    private class LongPressHandler extends Handler {
50        LongPressHandler() {
51            super();
52        }
53 
54        @Override
55        public void handleMessage(Message msg) {
56            switch (msg.what) {
57            case LONG_PRESS:
58                dispatchLongPress();
59                break;
60            default:
61                throw new RuntimeException("Unknown message " + msg); //never
62            }
63        }
64    }
65 
66    /**
67     * This is an interface to execute the LongPress when it receives the onLongPress message.
68     */
69    interface LongPressDelegate {
70        /**
71         * @param event The event will be recycled after this call has returned.
72         */
73        public void onLongPress(MotionEvent event);
74    }
75 
76    /**
77     * Initiates a LONG_PRESS gesture timer if needed.
78     */
79    void startLongPressTimerIfNeeded(MotionEvent ev) {
80        if (ev.getAction() != MotionEvent.ACTION_DOWN) return;
81 
82        // If there is a current down, we do not expect another down event before
83        // receiving an up event
84        if (mCurrentDownEvent != null) return;
85 
86        mCurrentDownEvent = MotionEvent.obtain(ev);
87        mLongPressHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()
88                + TAP_TIMEOUT + LONGPRESS_TIMEOUT);
89        mInLongPress = false;
90    }
91 
92    // Cancel LONG_PRESS timers.
93    void cancelLongPressIfNeeded(MotionEvent ev) {
94        if (!hasPendingMessage() ||
95            mCurrentDownEvent == null || ev.getDownTime() != mCurrentDownEvent.getDownTime()) {
96            return;
97        }
98        final int action = ev.getAction();
99        final float y = ev.getY();
100        final float x = ev.getX();
101        switch (action & MotionEvent.ACTION_MASK) {
102            case MotionEvent.ACTION_MOVE:
103                final int deltaX = (int) (x - mCurrentDownEvent.getX());
104                final int deltaY = (int) (y - mCurrentDownEvent.getY());
105                int distance = (deltaX * deltaX) + (deltaY * deltaY);
106                if (distance > mTouchSlopSquare) {
107                    cancelLongPress();
108                }
109                break;
110            case MotionEvent.ACTION_UP:
111            case MotionEvent.ACTION_CANCEL:
112                if (mCurrentDownEvent.getDownTime() + TAP_TIMEOUT + LONGPRESS_TIMEOUT >
113                    ev.getEventTime()) {
114                    cancelLongPress();
115                }
116                break;
117            default:
118                break;
119        }
120    }
121 
122    // Given a stream of pending events, cancel the LONG_PRESS timer if appropriate.
123    void cancelLongPressIfNeeded(Iterator<MotionEvent> pendingEvents) {
124        if (mCurrentDownEvent == null)
125            return;
126        long currentDownTime = mCurrentDownEvent.getDownTime();
127        while (pendingEvents.hasNext()) {
128            MotionEvent pending = pendingEvents.next();
129            if (pending.getDownTime() != currentDownTime) {
130                break;
131            }
132            cancelLongPressIfNeeded(pending);
133        }
134    }
135 
136    void cancelLongPress() {
137        mInLongPress = false;
138        if (hasPendingMessage()) {
139            mLongPressHandler.removeMessages(LONG_PRESS);
140            mCurrentDownEvent.recycle();
141            mCurrentDownEvent = null;
142        }
143    }
144 
145    // Used this to check if a onSingleTapUp is part of a long press event.
146    boolean isInLongPress() {
147        return mInLongPress;
148    }
149 
150    private void dispatchLongPress() {
151        mInLongPress = true;
152        mLongPressDelegate.onLongPress(mCurrentDownEvent);
153        mCurrentDownEvent.recycle();
154        mCurrentDownEvent = null;
155    }
156 
157    boolean hasPendingMessage() {
158        return mCurrentDownEvent != null;
159    }
160 
161    void onOfferTouchEventToJavaScript(MotionEvent event) {
162        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
163            mMoveConfirmed = false;
164            mTouchInitialX = Math.round(event.getX());
165            mTouchInitialY = Math.round(event.getY());
166        }
167    }
168 
169    boolean confirmOfferMoveEventToJavaScript(MotionEvent event) {
170        if (!mMoveConfirmed) {
171            int deltaX = Math.round(event.getX()) - mTouchInitialX;
172            int deltaY = Math.round(event.getY()) - mTouchInitialY;
173            if (deltaX * deltaX + deltaY * deltaY >= mTouchSlopSquare) {
174                mMoveConfirmed = true;
175            }
176        }
177        return mMoveConfirmed;
178    }
179 
180    /**
181     * This is for testing only.
182     * Sends a LongPress gesture. This should always be called after a down event.
183     */
184    void sendLongPressGestureForTest() {
185        if (mCurrentDownEvent == null) return;
186        dispatchLongPress();
187    }
188}

[all classes][org.chromium.content.browser]
EMMA 2.0.5312 (C) Vladimir Roubtsov