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

COVERAGE SUMMARY FOR SOURCE FILE [HandleView.java]

nameclass, %method, %block, %line, %
HandleView.java100% (1/1)83%  (19/23)77%  (595/775)74%  (124/168)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class HandleView100% (1/1)83%  (19/23)77%  (595/775)74%  (124/168)
beginFadeIn (): void 0%   (0/1)0%   (0/14)0%   (0/5)
getDrawable (): Drawable 0%   (0/1)0%   (0/3)0%   (0/1)
isShowing (): boolean 0%   (0/1)0%   (0/4)0%   (0/1)
showPastePopupWindow (): void 0%   (0/1)0%   (0/26)0%   (0/6)
updateAlpha (): void 100% (1/1)21%  (6/28)20%  (1/5)
moveTo (int, int): void 100% (1/1)53%  (76/144)58%  (18/31)
onTouchEvent (MotionEvent): boolean 100% (1/1)71%  (71/100)70%  (19/27)
hide (): void 100% (1/1)77%  (10/13)80%  (4/5)
show (): void 100% (1/1)88%  (44/50)75%  (9/12)
isPositionVisible (): boolean 100% (1/1)94%  (83/88)86%  (13/15)
<static initializer> 100% (1/1)100% (16/16)100% (1/1)
HandleView (CursorController, int, View): void 100% (1/1)100% (87/87)100% (19/19)
getAdjustedPositionX (): int 100% (1/1)100% (8/8)100% (1/1)
getAdjustedPositionY (): int 100% (1/1)100% (8/8)100% (1/1)
getLineAdjustedPositionY (): int 100% (1/1)100% (12/12)100% (1/1)
getPositionX (): int 100% (1/1)100% (3/3)100% (1/1)
getPositionY (): int 100% (1/1)100% (3/3)100% (1/1)
isDragging (): boolean 100% (1/1)100% (3/3)100% (1/1)
onDraw (Canvas): void 100% (1/1)100% (22/22)100% (4/4)
onMeasure (int, int): void 100% (1/1)100% (9/9)100% (2/2)
positionAt (int, int): void 100% (1/1)100% (15/15)100% (2/2)
setOrientation (int): void 100% (1/1)100% (90/90)100% (22/22)
updatePosition (float, float): void 100% (1/1)100% (29/29)100% (4/4)

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.input;
6 
7import android.content.Context;
8import android.content.res.TypedArray;
9import android.graphics.Canvas;
10import android.graphics.Rect;
11import android.graphics.drawable.Drawable;
12import android.os.SystemClock;
13import android.util.TypedValue;
14import android.view.Gravity;
15import android.view.LayoutInflater;
16import android.view.MotionEvent;
17import android.view.View;
18import android.view.ViewConfiguration;
19import android.view.ViewGroup;
20import android.view.ViewParent;
21import android.view.WindowManager;
22import android.view.View.OnClickListener;
23import android.view.ViewGroup.LayoutParams;
24import android.widget.PopupWindow;
25import android.widget.TextView;
26 
27/**
28 * View that displays a selection or insertion handle for text editing.
29 */
30public class HandleView extends View {
31    private static final float FADE_DURATION = 200.f;
32 
33    private Drawable mDrawable;
34    private final PopupWindow mContainer;
35    private int mPositionX;
36    private int mPositionY;
37    private final CursorController mController;
38    private boolean mIsDragging;
39    private float mTouchToWindowOffsetX;
40    private float mTouchToWindowOffsetY;
41    private float mHotspotX;
42    private float mHotspotY;
43    private int mLineOffsetY;
44    private int mLastParentX;
45    private int mLastParentY;
46    private float mDownPositionX, mDownPositionY;
47    private int mContainerPositionX, mContainerPositionY;
48    private long mTouchTimer;
49    private boolean mIsInsertionHandle = false;
50    private float mAlpha;
51    private long mFadeStartTime;
52 
53    private View mParent;
54    private InsertionHandleController.PastePopupMenu mPastePopupWindow;
55 
56    private final int mTextSelectHandleLeftRes;
57    private final int mTextSelectHandleRightRes;
58    private final int mTextSelectHandleRes;
59 
60    private Drawable mSelectHandleLeft;
61    private Drawable mSelectHandleRight;
62    private Drawable mSelectHandleCenter;
63 
64    private final int[] mTempCoords = new int[2];
65    private final Rect mTempRect = new Rect();
66 
67    static final int LEFT = 0;
68    static final int CENTER = 1;
69    static final int RIGHT = 2;
70 
71    // Number of dips to subtract from the handle's y position to give a suitable
72    // y coordinate for the corresponding text position. This is to compensate for the fact
73    // that the handle position is at the base of the line of text.
74    private static final float LINE_OFFSET_Y_DIP = 5.0f;
75 
76    private static final int[] TEXT_VIEW_HANDLE_ATTRS = {
77        android.R.attr.textSelectHandleLeft,
78        android.R.attr.textSelectHandle,
79        android.R.attr.textSelectHandleRight,
80    };
81 
82    HandleView(CursorController controller, int pos, View parent) {
83        super(parent.getContext());
84        Context context = parent.getContext();
85        mParent = parent;
86        mController = controller;
87        mContainer = new PopupWindow(context, null, android.R.attr.textSelectHandleWindowStyle);
88        mContainer.setSplitTouchEnabled(true);
89        mContainer.setClippingEnabled(false);
90 
91        TypedArray a = context.obtainStyledAttributes(TEXT_VIEW_HANDLE_ATTRS);
92        mTextSelectHandleLeftRes = a.getResourceId(a.getIndex(LEFT), 0);
93        mTextSelectHandleRes = a.getResourceId(a.getIndex(CENTER), 0);
94        mTextSelectHandleRightRes = a.getResourceId(a.getIndex(RIGHT), 0);
95        a.recycle();
96 
97        setOrientation(pos);
98 
99        // Convert line offset dips to pixels.
100        mLineOffsetY = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
101                LINE_OFFSET_Y_DIP, context.getResources().getDisplayMetrics());
102 
103        mAlpha = 1.f;
104    }
105 
106    void setOrientation(int pos) {
107        int handleWidth;
108        switch (pos) {
109        case LEFT: {
110            if (mSelectHandleLeft == null) {
111                mSelectHandleLeft = getContext().getResources().getDrawable(
112                        mTextSelectHandleLeftRes);
113            }
114            mDrawable = mSelectHandleLeft;
115            handleWidth = mDrawable.getIntrinsicWidth();
116            mHotspotX = (handleWidth * 3) / 4f;
117            break;
118        }
119 
120        case RIGHT: {
121            if (mSelectHandleRight == null) {
122                mSelectHandleRight = getContext().getResources().getDrawable(
123                        mTextSelectHandleRightRes);
124            }
125            mDrawable = mSelectHandleRight;
126            handleWidth = mDrawable.getIntrinsicWidth();
127            mHotspotX = handleWidth / 4f;
128            break;
129        }
130 
131        case CENTER:
132        default: {
133            if (mSelectHandleCenter == null) {
134                mSelectHandleCenter = getContext().getResources().getDrawable(
135                        mTextSelectHandleRes);
136            }
137            mDrawable = mSelectHandleCenter;
138            handleWidth = mDrawable.getIntrinsicWidth();
139            mHotspotX = handleWidth / 2f;
140            mIsInsertionHandle = true;
141            break;
142        }
143        }
144 
145        mHotspotY = 0;
146        invalidate();
147    }
148 
149    @Override
150    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
151        setMeasuredDimension(mDrawable.getIntrinsicWidth(),
152                mDrawable.getIntrinsicHeight());
153    }
154 
155    void show() {
156        if (!isPositionVisible()) {
157            hide();
158            return;
159        }
160        mContainer.setContentView(this);
161        final int[] coords = mTempCoords;
162        mParent.getLocationInWindow(coords);
163        mContainerPositionX = coords[0] + mPositionX;
164        mContainerPositionY = coords[1] + mPositionY;
165        mContainer.showAtLocation(mParent, 0, mContainerPositionX, mContainerPositionY);
166 
167        // Hide paste view when handle is moved on screen.
168        if (mPastePopupWindow != null) {
169            mPastePopupWindow.hide();
170        }
171    }
172 
173    void hide() {
174        mIsDragging = false;
175        mContainer.dismiss();
176        if (mPastePopupWindow != null) {
177            mPastePopupWindow.hide();
178        }
179    }
180 
181    boolean isShowing() {
182        return mContainer.isShowing();
183    }
184 
185    private boolean isPositionVisible() {
186        // Always show a dragging handle.
187        if (mIsDragging) {
188            return true;
189        }
190 
191        final Rect clip = mTempRect;
192        clip.left = 0;
193        clip.top = 0;
194        clip.right = mParent.getWidth();
195        clip.bottom = mParent.getHeight();
196 
197        final ViewParent parent = mParent.getParent();
198        if (parent == null || !parent.getChildVisibleRect(mParent, clip, null)) {
199            return false;
200        }
201 
202        final int[] coords = mTempCoords;
203        mParent.getLocationInWindow(coords);
204        final int posX = coords[0] + mPositionX + (int) mHotspotX;
205        final int posY = coords[1] + mPositionY + (int) mHotspotY;
206 
207        return posX >= clip.left && posX <= clip.right &&
208                posY >= clip.top && posY <= clip.bottom;
209    }
210 
211    // x and y are in physical pixels.
212    void moveTo(int x, int y) {
213        mPositionX = x;
214        mPositionY = y;
215        if (isPositionVisible()) {
216            int[] coords = null;
217            if (mContainer.isShowing()) {
218                coords = mTempCoords;
219                mParent.getLocationInWindow(coords);
220                final int containerPositionX = coords[0] + mPositionX;
221                final int containerPositionY = coords[1] + mPositionY;
222 
223                if (containerPositionX != mContainerPositionX ||
224                    containerPositionY != mContainerPositionY) {
225                    mContainerPositionX = containerPositionX;
226                    mContainerPositionY = containerPositionY;
227 
228                    mContainer.update(mContainerPositionX, mContainerPositionY,
229                            getRight() - getLeft(), getBottom() - getTop());
230 
231                    // Hide paste popup window as soon as a scroll occurs.
232                    if (mPastePopupWindow != null) {
233                        mPastePopupWindow.hide();
234                    }
235                }
236            } else {
237                show();
238            }
239 
240            if (mIsDragging) {
241                if (coords == null) {
242                    coords = mTempCoords;
243                    mParent.getLocationInWindow(coords);
244                }
245                if (coords[0] != mLastParentX || coords[1] != mLastParentY) {
246                    mTouchToWindowOffsetX += coords[0] - mLastParentX;
247                    mTouchToWindowOffsetY += coords[1] - mLastParentY;
248                    mLastParentX = coords[0];
249                    mLastParentY = coords[1];
250                }
251                // Hide paste popup window as soon as the handle is dragged.
252                if (mPastePopupWindow != null) {
253                    mPastePopupWindow.hide();
254                }
255            }
256        } else {
257            hide();
258        }
259    }
260 
261    @Override
262    protected void onDraw(Canvas c) {
263        updateAlpha();
264        mDrawable.setBounds(0, 0, getRight() - getLeft(), getBottom() - getTop());
265        mDrawable.draw(c);
266    }
267 
268    @Override
269    public boolean onTouchEvent(MotionEvent ev) {
270        switch (ev.getActionMasked()) {
271            case MotionEvent.ACTION_DOWN: {
272                mDownPositionX = ev.getRawX();
273                mDownPositionY = ev.getRawY();
274                mTouchToWindowOffsetX = mDownPositionX - mPositionX;
275                mTouchToWindowOffsetY = mDownPositionY - mPositionY;
276                final int[] coords = mTempCoords;
277                mParent.getLocationInWindow(coords);
278                mLastParentX = coords[0];
279                mLastParentY = coords[1];
280                mIsDragging = true;
281                mController.beforeStartUpdatingPosition(this);
282                mTouchTimer = SystemClock.uptimeMillis();
283                break;
284            }
285 
286            case MotionEvent.ACTION_MOVE: {
287                updatePosition(ev.getRawX(), ev.getRawY());
288                break;
289            }
290 
291            case MotionEvent.ACTION_UP:
292                if (mIsInsertionHandle) {
293                    long delay = SystemClock.uptimeMillis() - mTouchTimer;
294                    if (delay < ViewConfiguration.getTapTimeout()) {
295                        if (mPastePopupWindow != null && mPastePopupWindow.isShowing()) {
296                            // Tapping on the handle dismisses the displayed paste view,
297                            mPastePopupWindow.hide();
298                        } else {
299                            showPastePopupWindow();
300                        }
301                    }
302                }
303                mIsDragging = false;
304                break;
305 
306            case MotionEvent.ACTION_CANCEL:
307                mIsDragging = false;
308                break;
309 
310            default:
311                return false;
312        }
313        return true;
314    }
315 
316    boolean isDragging() {
317        return mIsDragging;
318    }
319 
320    /**
321     * @return Returns the x position of the handle
322     */
323    int getPositionX() {
324        return mPositionX;
325    }
326 
327    /**
328     * @return Returns the y position of the handle
329     */
330    int getPositionY() {
331        return mPositionY;
332    }
333 
334    private void updatePosition(float rawX, float rawY) {
335        final float newPosX = rawX - mTouchToWindowOffsetX + mHotspotX;
336        final float newPosY = rawY - mTouchToWindowOffsetY + mHotspotY - mLineOffsetY;
337 
338        mController.updatePosition(this, Math.round(newPosX), Math.round(newPosY));
339    }
340 
341    // x and y are in physical pixels.
342    void positionAt(int x, int y) {
343        moveTo((int)(x - mHotspotX), (int)(y - mHotspotY));
344    }
345 
346    // Returns the x coordinate of the position that the handle appears to be pointing to.
347    int getAdjustedPositionX() {
348        return (int) (mPositionX + mHotspotX);
349    }
350 
351    // Returns the y coordinate of the position that the handle appears to be pointing to.
352    int getAdjustedPositionY() {
353        return (int) (mPositionY + mHotspotY);
354    }
355 
356    // Returns a suitable y coordinate for the text position corresponding to the handle.
357    // As the handle points to a position on the base of the line of text, this method
358    // returns a coordinate a small number of pixels higher (i.e. a slightly smaller number)
359    // than getAdjustedPositionY.
360    int getLineAdjustedPositionY() {
361        return (int) (mPositionY + mHotspotY - mLineOffsetY);
362    }
363 
364    Drawable getDrawable() {
365        return mDrawable;
366    }
367 
368    private void updateAlpha() {
369        if (mAlpha == 1.f) return;
370        mAlpha = Math.min(1.f, (System.currentTimeMillis() - mFadeStartTime) / FADE_DURATION);
371        mDrawable.setAlpha((int) (255 * mAlpha));
372        invalidate();
373    }
374 
375    /**
376     * If the handle is not visible, sets its visibility to View.VISIBLE and begins fading it in.
377     */
378    void beginFadeIn() {
379        if (getVisibility() == VISIBLE) return;
380        mAlpha = 0.f;
381        mFadeStartTime = System.currentTimeMillis();
382        setVisibility(VISIBLE);
383    }
384 
385    void showPastePopupWindow() {
386        InsertionHandleController ihc = (InsertionHandleController) mController;
387        if (mIsInsertionHandle && ihc.canPaste()) {
388            if (mPastePopupWindow == null) {
389                // Lazy initialization: create when actually shown only.
390                mPastePopupWindow = ihc.new PastePopupMenu();
391            }
392            mPastePopupWindow.show();
393        }
394    }
395}

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