Chromium Code Reviews| 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; |
|
cjhopman
2014/07/09 22:29:12
Tap is now detected by native, so touchtimer isn't
jdduke (slow)
2014/07/10 02:08:40
Done.
|
| private boolean mIsInsertionHandle = false; |
|
cjhopman
2014/07/09 22:29:12
This seems unused.
jdduke (slow)
2014/07/10 02:08:40
Done.
|
| @@ -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); |
| } |
| } |