| Index: content/public/android/java/src/org/chromium/content/browser/input/PopupTouchHandleDrawable.java
|
| diff --git a/content/public/android/java/src/org/chromium/content/browser/input/HandleView.java b/content/public/android/java/src/org/chromium/content/browser/input/PopupTouchHandleDrawable.java
|
| similarity index 51%
|
| rename from content/public/android/java/src/org/chromium/content/browser/input/HandleView.java
|
| rename to content/public/android/java/src/org/chromium/content/browser/input/PopupTouchHandleDrawable.java
|
| index 2b50079a25361af7172df81a839228ec63f7fdb4..42ae084e46125e2987527e0ca22157a77b69fdb3 100644
|
| --- a/content/public/android/java/src/org/chromium/content/browser/input/HandleView.java
|
| +++ b/content/public/android/java/src/org/chromium/content/browser/input/PopupTouchHandleDrawable.java
|
| @@ -5,19 +5,17 @@
|
| package org.chromium.content.browser.input;
|
|
|
| import android.content.Context;
|
| -import android.content.res.TypedArray;
|
| import android.graphics.Canvas;
|
| import android.graphics.Rect;
|
| import android.graphics.drawable.Drawable;
|
| -import android.os.SystemClock;
|
| -import android.util.TypedValue;
|
| import android.view.MotionEvent;
|
| import android.view.View;
|
| -import android.view.ViewConfiguration;
|
| import android.view.ViewParent;
|
| import android.view.animation.AnimationUtils;
|
| import android.widget.PopupWindow;
|
|
|
| +import org.chromium.base.CalledByNative;
|
| +import org.chromium.base.JNINamespace;
|
| import org.chromium.content.browser.PositionObserver;
|
|
|
| /**
|
| @@ -27,10 +25,12 @@ import org.chromium.content.browser.PositionObserver;
|
| * hierarchy.
|
| *
|
| */
|
| -public class HandleView extends View {
|
| +@JNINamespace("content")
|
| +public class PopupTouchHandleDrawable extends View {
|
| private static final float FADE_DURATION = 200.f;
|
|
|
| private Drawable mDrawable;
|
| + private final PopupTouchHandleDrawableDelegate mDelegate;
|
| private final PopupWindow mContainer;
|
|
|
| // The position of the handle relative to the parent view.
|
| @@ -45,12 +45,10 @@ public class HandleView extends View {
|
| private float mHotspotX;
|
| private float mHotspotY;
|
|
|
| - private final CursorController mController;
|
| private boolean mIsDragging;
|
| private float mTouchToWindowOffsetX;
|
| private float mTouchToWindowOffsetY;
|
|
|
| - private final int mLineOffsetY;
|
| private float mDownPositionX, mDownPositionY;
|
| private long mTouchTimer;
|
| private boolean mIsInsertionHandle = false;
|
| @@ -58,49 +56,39 @@ public class HandleView extends View {
|
| private long mFadeStartTime;
|
|
|
| private final View mParent;
|
| - private InsertionHandleController.PastePopupMenu mPastePopupWindow;
|
|
|
| private final Rect mTempRect = new Rect();
|
| + private final int[] mTempScreenCoords = new int[2];
|
|
|
| static final int LEFT = 0;
|
| static final int CENTER = 1;
|
| static final int RIGHT = 2;
|
| private int mOrientation = -1;
|
|
|
| - /** Defer re-orientation while dragging to prevent confusing handle re-positioning. */
|
| - private int mDeferredOrientation = -1;
|
| -
|
| private final PositionObserver mParentPositionObserver;
|
| private final PositionObserver.Listener mParentPositionListener;
|
|
|
| - // Number of dips to subtract from the handle's y position to give a suitable
|
| - // y coordinate for the corresponding text position. This is to compensate for the fact
|
| - // that the handle position is at the base of the line of text.
|
| - private static final float LINE_OFFSET_Y_DIP = 5.0f;
|
| -
|
| - private static final int[] TEXT_VIEW_HANDLE_ATTRS = {
|
| - android.R.attr.textSelectHandleLeft,
|
| - android.R.attr.textSelectHandle,
|
| - android.R.attr.textSelectHandleRight,
|
| - };
|
| + /**
|
| + * Provides additional interaction behaviors necessary for handle
|
| + * manipulation and interaction.
|
| + */
|
| + public interface PopupTouchHandleDrawableDelegate {
|
| + /**
|
| + * Should route MotionEvents to the appropriate logic layer for
|
| + * performing handle manipulation.
|
| + */
|
| + boolean onTouchHandleEvent(MotionEvent ev);
|
| + }
|
|
|
| - HandleView(CursorController controller, int orientation, View parent,
|
| + public PopupTouchHandleDrawable(PopupTouchHandleDrawableDelegate delegate, View parent,
|
| PositionObserver parentPositionObserver) {
|
| super(parent.getContext());
|
| + mDelegate = delegate;
|
| mParent = parent;
|
| Context context = mParent.getContext();
|
| - mController = controller;
|
| mContainer = new PopupWindow(context, null, android.R.attr.textSelectHandleWindowStyle);
|
| - mContainer.setSplitTouchEnabled(true);
|
| + mContainer.setSplitTouchEnabled(false);
|
| mContainer.setClippingEnabled(false);
|
| - mContainer.setAnimationStyle(0);
|
| -
|
| - setOrientation(orientation);
|
| -
|
| - // Convert line offset dips to pixels.
|
| - mLineOffsetY = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
|
| - LINE_OFFSET_Y_DIP, context.getResources().getDisplayMetrics());
|
| -
|
| mAlpha = 1.f;
|
|
|
| mParentPositionListener = new PositionObserver.Listener() {
|
| @@ -112,45 +100,50 @@ public class HandleView extends View {
|
| mParentPositionObserver = parentPositionObserver;
|
| }
|
|
|
| - void setOrientation(int orientation) {
|
| - assert orientation >= LEFT && orientation <= RIGHT;
|
| -
|
| - if (mIsDragging) {
|
| - mDeferredOrientation = orientation;
|
| - return;
|
| - }
|
| + @Override
|
| + public boolean onTouchEvent(MotionEvent event) {
|
| + // Convert from PopupWindow local coordinates to
|
| + // parent view local coordinates prior to forwarding.
|
| + mParent.getLocationOnScreen(mTempScreenCoords);
|
| + final float offsetX = event.getRawX() - event.getX() - mTempScreenCoords[0];
|
| + final float offsetY = event.getRawY() - event.getY() - mTempScreenCoords[1];
|
| + final MotionEvent offsetEvent = MotionEvent.obtainNoHistory(event);
|
| + offsetEvent.offsetLocation(offsetX, offsetY);
|
| + final boolean handled = mDelegate.onTouchHandleEvent(offsetEvent);
|
| + offsetEvent.recycle();
|
| + return handled;
|
| + }
|
|
|
| + private void setOrientation(int orientation) {
|
| + assert orientation >= LEFT && orientation <= RIGHT;
|
| if (mOrientation == orientation) return;
|
|
|
| final boolean hadValidOrientation = mOrientation != -1;
|
| mOrientation = orientation;
|
| - mDeferredOrientation = -1;
|
| -
|
| - Context context = mParent.getContext();
|
| - TypedArray a = context.getTheme().obtainStyledAttributes(TEXT_VIEW_HANDLE_ATTRS);
|
| - mDrawable = a.getDrawable(orientation);
|
| - a.recycle();
|
|
|
| mIsInsertionHandle = (orientation == CENTER);
|
|
|
| final int oldAdjustedPositionX = getAdjustedPositionX();
|
| final int oldAdjustedPositionY = getAdjustedPositionY();
|
|
|
| - int handleWidth = mDrawable.getIntrinsicWidth();
|
| + Context context = mParent.getContext();
|
| switch (orientation) {
|
| case LEFT: {
|
| - mHotspotX = (handleWidth * 3) / 4f;
|
| + mDrawable = HandleViewResources.getLeftHandleDrawable(context);
|
| + mHotspotX = (mDrawable.getIntrinsicWidth() * 3) / 4f;
|
| break;
|
| }
|
|
|
| case RIGHT: {
|
| - mHotspotX = handleWidth / 4f;
|
| + mDrawable = HandleViewResources.getRightHandleDrawable(context);
|
| + mHotspotX = mDrawable.getIntrinsicWidth() / 4f;
|
| break;
|
| }
|
|
|
| case CENTER:
|
| default: {
|
| - mHotspotX = handleWidth / 2f;
|
| + mDrawable = HandleViewResources.getCenterHandleDrawable(context);
|
| + mHotspotX = mDrawable.getIntrinsicWidth() / 2f;
|
| break;
|
| }
|
| }
|
| @@ -162,16 +155,7 @@ public class HandleView extends View {
|
| invalidate();
|
| }
|
|
|
| - @Override
|
| - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
| - setMeasuredDimension(mDrawable.getIntrinsicWidth(),
|
| - mDrawable.getIntrinsicHeight());
|
| - }
|
| -
|
| private void updateParentPosition(int parentPositionX, int parentPositionY) {
|
| - // Hide paste popup window as soon as a scroll occurs.
|
| - if (mPastePopupWindow != null) mPastePopupWindow.hide();
|
| -
|
| mTouchToWindowOffsetX += parentPositionX - mParentPositionX;
|
| mTouchToWindowOffsetY += parentPositionY - mParentPositionY;
|
| mParentPositionX = parentPositionX;
|
| @@ -188,18 +172,11 @@ public class HandleView extends View {
|
| }
|
|
|
| private void onPositionChanged() {
|
| - // Deferring View invalidation while the handles are hidden prevents
|
| - // scheduling conflicts with the compositor.
|
| - if (getVisibility() != VISIBLE) return;
|
| mContainer.update(getContainerPositionX(), getContainerPositionY(),
|
| getRight() - getLeft(), getBottom() - getTop());
|
| }
|
|
|
| - private void showContainer() {
|
| - mContainer.showAtLocation(mParent, 0, getContainerPositionX(), getContainerPositionY());
|
| - }
|
| -
|
| - void show() {
|
| + private void show() {
|
| // While hidden, the parent position may have become stale. It must be updated before
|
| // checking isPositionVisible().
|
| updateParentPosition(mParentPositionObserver.getPositionX(),
|
| @@ -210,25 +187,16 @@ public class HandleView extends View {
|
| }
|
| mParentPositionObserver.addListener(mParentPositionListener);
|
| mContainer.setContentView(this);
|
| - showContainer();
|
| -
|
| - // Hide paste view when handle is moved on screen.
|
| - if (mPastePopupWindow != null) {
|
| - mPastePopupWindow.hide();
|
| - }
|
| + mContainer.showAtLocation(mParent, 0, getContainerPositionX(), getContainerPositionY());
|
| }
|
|
|
| - void hide() {
|
| + private void hide() {
|
| mIsDragging = false;
|
| - mDeferredOrientation = -1;
|
| mContainer.dismiss();
|
| mParentPositionObserver.removeListener(mParentPositionListener);
|
| - if (mPastePopupWindow != null) {
|
| - mPastePopupWindow.hide();
|
| - }
|
| }
|
|
|
| - boolean isShowing() {
|
| + private boolean isShowing() {
|
| return mContainer.isShowing();
|
| }
|
|
|
| @@ -257,7 +225,7 @@ public class HandleView extends View {
|
| }
|
|
|
| // x and y are in physical pixels.
|
| - void moveTo(int x, int y) {
|
| + private void moveTo(int x, int y) {
|
| int previousPositionX = mPositionX;
|
| int previousPositionY = mPositionY;
|
|
|
| @@ -266,146 +234,48 @@ public class HandleView extends View {
|
| if (isPositionVisible()) {
|
| if (mContainer.isShowing()) {
|
| onPositionChanged();
|
| - // Hide paste popup window as soon as the handle is dragged.
|
| - if (mPastePopupWindow != null &&
|
| - (previousPositionX != mPositionX || previousPositionY != mPositionY)) {
|
| - mPastePopupWindow.hide();
|
| - }
|
| } else {
|
| show();
|
| }
|
| -
|
| - if (mIsDragging) {
|
| - // Hide paste popup window as soon as the handle is dragged.
|
| - if (mPastePopupWindow != null) {
|
| - mPastePopupWindow.hide();
|
| - }
|
| - }
|
| } else {
|
| hide();
|
| }
|
| }
|
|
|
| @Override
|
| + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
| + if (mDrawable == null) return;
|
| + setMeasuredDimension(mDrawable.getIntrinsicWidth(),
|
| + mDrawable.getIntrinsicHeight());
|
| + }
|
| +
|
| + @Override
|
| protected void onDraw(Canvas c) {
|
| + if (mDrawable == null) return;
|
| updateAlpha();
|
| mDrawable.setBounds(0, 0, getRight() - getLeft(), getBottom() - getTop());
|
| mDrawable.draw(c);
|
| }
|
|
|
| - @Override
|
| - public boolean onTouchEvent(MotionEvent ev) {
|
| - switch (ev.getActionMasked()) {
|
| - case MotionEvent.ACTION_DOWN: {
|
| - mDownPositionX = ev.getRawX();
|
| - mDownPositionY = ev.getRawY();
|
| - mTouchToWindowOffsetX = mDownPositionX - mPositionX;
|
| - mTouchToWindowOffsetY = mDownPositionY - mPositionY;
|
| - mIsDragging = true;
|
| - mController.beforeStartUpdatingPosition(this);
|
| - mTouchTimer = SystemClock.uptimeMillis();
|
| - break;
|
| - }
|
| -
|
| - case MotionEvent.ACTION_MOVE: {
|
| - updatePosition(ev.getRawX(), ev.getRawY());
|
| - break;
|
| - }
|
| -
|
| - case MotionEvent.ACTION_UP:
|
| - if (mIsInsertionHandle) {
|
| - long delay = SystemClock.uptimeMillis() - mTouchTimer;
|
| - if (delay < ViewConfiguration.getTapTimeout()) {
|
| - if (mPastePopupWindow != null && mPastePopupWindow.isShowing()) {
|
| - // Tapping on the handle dismisses the displayed paste view,
|
| - mPastePopupWindow.hide();
|
| - } else {
|
| - showPastePopupWindow();
|
| - }
|
| - }
|
| - }
|
| - mIsDragging = false;
|
| - if (mDeferredOrientation != -1) setOrientation(mDeferredOrientation);
|
| - break;
|
| -
|
| - case MotionEvent.ACTION_CANCEL:
|
| - mIsDragging = false;
|
| - if (mDeferredOrientation != -1) setOrientation(mDeferredOrientation);
|
| - break;
|
| -
|
| - default:
|
| - return false;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - boolean isDragging() {
|
| - return mIsDragging;
|
| - }
|
| -
|
| - /**
|
| - * @return Returns the x position of the handle
|
| - */
|
| - int getPositionX() {
|
| - return mPositionX;
|
| - }
|
| -
|
| - /**
|
| - * @return Returns the y position of the handle
|
| - */
|
| - int getPositionY() {
|
| - return mPositionY;
|
| - }
|
| -
|
| - private void updatePosition(float rawX, float rawY) {
|
| - final float newPosX = rawX - mTouchToWindowOffsetX + mHotspotX;
|
| - final float newPosY = rawY - mTouchToWindowOffsetY + mHotspotY - mLineOffsetY;
|
| -
|
| - mController.updatePosition(this, Math.round(newPosX), Math.round(newPosY));
|
| - }
|
| -
|
| // x and y are in physical pixels.
|
| - void positionAt(int x, int y) {
|
| + private void positionAt(int x, int y) {
|
| moveTo(x - Math.round(mHotspotX), y - Math.round(mHotspotY));
|
| }
|
|
|
| // Returns the x coordinate of the position that the handle appears to be pointing to relative
|
| // to the handles "parent" view.
|
| - int getAdjustedPositionX() {
|
| + private int getAdjustedPositionX() {
|
| return mPositionX + Math.round(mHotspotX);
|
| }
|
|
|
| // Returns the y coordinate of the position that the handle appears to be pointing to relative
|
| // to the handles "parent" view.
|
| - int getAdjustedPositionY() {
|
| + private int getAdjustedPositionY() {
|
| return mPositionY + Math.round(mHotspotY);
|
| }
|
|
|
| - // Returns the x coordinate of the postion that the handle appears to be pointing to relative to
|
| - // the root view of the application.
|
| - int getRootViewRelativePositionX() {
|
| - return getContainerPositionX() + Math.round(mHotspotX);
|
| - }
|
| -
|
| - // Returns the y coordinate of the postion that the handle appears to be pointing to relative to
|
| - // the root view of the application.
|
| - int getRootViewRelativePositionY() {
|
| - return getContainerPositionY() + Math.round(mHotspotY);
|
| - }
|
| -
|
| - // Returns a suitable y coordinate for the text position corresponding to the handle.
|
| - // As the handle points to a position on the base of the line of text, this method
|
| - // returns a coordinate a small number of pixels higher (i.e. a slightly smaller number)
|
| - // than getAdjustedPositionY.
|
| - int getLineAdjustedPositionY() {
|
| - return (int) (mPositionY + mHotspotY - mLineOffsetY);
|
| - }
|
| -
|
| - Drawable getDrawable() {
|
| - return mDrawable;
|
| - }
|
| -
|
| private void updateAlpha() {
|
| + if (mDrawable == null) return;
|
| if (mAlpha == 1.f) return;
|
| mAlpha = Math.min(1.f,
|
| (AnimationUtils.currentAnimationTimeMillis() - mFadeStartTime) / FADE_DURATION);
|
| @@ -413,26 +283,52 @@ public class HandleView extends View {
|
| invalidate();
|
| }
|
|
|
| - /**
|
| - * If the handle is not visible, sets its visibility to View.VISIBLE and begins fading it in.
|
| - */
|
| - void beginFadeIn() {
|
| + // If the handle is not visible, sets its visibility to View.VISIBLE and begins fading it in.
|
| + private void beginFadeIn() {
|
| if (getVisibility() == VISIBLE) return;
|
| mAlpha = 0.f;
|
| mFadeStartTime = AnimationUtils.currentAnimationTimeMillis();
|
| setVisibility(VISIBLE);
|
| - // Position updates may have been deferred while the handle was hidden.
|
| - onPositionChanged();
|
| + // Force a position update as such updates may have gone suppressed while invisible.
|
| + if (isShowing()) onPositionChanged();
|
| }
|
|
|
| - void showPastePopupWindow() {
|
| - InsertionHandleController ihc = (InsertionHandleController) mController;
|
| - if (mIsInsertionHandle && ihc.canPaste()) {
|
| - if (mPastePopupWindow == null) {
|
| - // Lazy initialization: create when actually shown only.
|
| - mPastePopupWindow = ihc.new PastePopupMenu();
|
| - }
|
| - mPastePopupWindow.show();
|
| - }
|
| + @CalledByNative
|
| + private void setRightOrientation() {
|
| + setOrientation(RIGHT);
|
| + }
|
| +
|
| + @CalledByNative
|
| + private void setLeftOrientation() {
|
| + setOrientation(LEFT);
|
| + }
|
| +
|
| + @CalledByNative
|
| + private void setCenterOrientation() {
|
| + setOrientation(CENTER);
|
| + }
|
| +
|
| + @CalledByNative
|
| + private void setOpacity(float alpha) {
|
| + mAlpha = alpha;
|
| + }
|
| +
|
| + @CalledByNative
|
| + private void setFocus(float x, float y) {
|
| + positionAt((int) x, (int) y);
|
| + }
|
| +
|
| + @CalledByNative
|
| + private void setVisible(boolean visible) {
|
| + setVisibility(visible ? VISIBLE : INVISIBLE);
|
| + }
|
| +
|
| + @CalledByNative
|
| + private boolean containsPoint(float x, float y) {
|
| + if (mDrawable == null) return false;
|
| + final int width = mDrawable.getIntrinsicWidth();
|
| + final int height = mDrawable.getIntrinsicHeight();
|
| + return x > mPositionX && x < (mPositionX + width)
|
| + && y > mPositionY && y < (mPositionY + height);
|
| }
|
| }
|
|
|