| 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/PopupTouchHandleDrawable.java b/content/public/android/java/src/org/chromium/content/browser/input/PopupTouchHandleDrawable.java
|
| index 17cc792d8ad475369c5323aa521aed07c03a1425..ffb03af20e7887bdd6079bf3db6c80f70e4f92de 100644
|
| --- a/content/public/android/java/src/org/chromium/content/browser/input/PopupTouchHandleDrawable.java
|
| +++ b/content/public/android/java/src/org/chromium/content/browser/input/PopupTouchHandleDrawable.java
|
| @@ -9,8 +9,10 @@ import android.graphics.Canvas;
|
| import android.graphics.drawable.Drawable;
|
| import android.view.MotionEvent;
|
| import android.view.View;
|
| +import android.view.animation.AnimationUtils;
|
| import android.widget.PopupWindow;
|
|
|
| +import org.chromium.base.ApiCompatibilityUtils;
|
| import org.chromium.base.CalledByNative;
|
| import org.chromium.base.JNINamespace;
|
| import org.chromium.content.browser.PositionObserver;
|
| @@ -60,6 +62,19 @@ public class PopupTouchHandleDrawable extends View {
|
| static final int RIGHT = 2;
|
| private int mOrientation = -1;
|
|
|
| + // Length of the delay before fading in after the last page movement.
|
| + private static final int FADE_IN_DELAY_MS = 300;
|
| + private static final int FADE_IN_DURATION_MS = 200;
|
| + private Runnable mDeferredHandleFadeInRunnable;
|
| + private long mFadeStartTime;
|
| + private boolean mVisible;
|
| + private boolean mTemporarilyHidden;
|
| +
|
| + // Deferred runnable to avoid invalidating outside of frame dispatch,
|
| + // in turn avoiding issues with sync barrier insertion.
|
| + private Runnable mInvalidationRunnable;
|
| + private boolean mHasPendingInvalidate;
|
| +
|
| /**
|
| * Provides additional interaction behaviors necessary for handle
|
| * manipulation and interaction.
|
| @@ -81,6 +96,11 @@ public class PopupTouchHandleDrawable extends View {
|
| * performing handle manipulation.
|
| */
|
| boolean onTouchHandleEvent(MotionEvent ev);
|
| +
|
| + /**
|
| + * @return Whether the associated content is actively scrolling.
|
| + */
|
| + boolean isScrollInProgress();
|
| }
|
|
|
| public PopupTouchHandleDrawable(PopupTouchHandleDrawableDelegate delegate) {
|
| @@ -90,7 +110,9 @@ public class PopupTouchHandleDrawable extends View {
|
| mContainer = new PopupWindow(mContext, null, android.R.attr.textSelectHandleWindowStyle);
|
| mContainer.setSplitTouchEnabled(true);
|
| mContainer.setClippingEnabled(false);
|
| + mContainer.setAnimationStyle(0);
|
| mAlpha = 1.f;
|
| + mVisible = getVisibility() == VISIBLE;
|
| mParentPositionListener = new PositionObserver.Listener() {
|
| @Override
|
| public void onPositionChanged(int x, int y) {
|
| @@ -153,16 +175,16 @@ public class PopupTouchHandleDrawable extends View {
|
| mHotspotY = 0;
|
|
|
| // Force handle repositioning to accommodate the new orientation's hotspot.
|
| - if (hadValidOrientation) positionAt(oldAdjustedPositionX, oldAdjustedPositionY);
|
| + if (hadValidOrientation) setFocus(oldAdjustedPositionX, oldAdjustedPositionY);
|
| mDrawable.setAlpha((int) (255 * mAlpha));
|
| -
|
| - invalidate();
|
| + scheduleInvalidate();
|
| }
|
|
|
| private void updateParentPosition(int parentPositionX, int parentPositionY) {
|
| + if (mParentPositionX == parentPositionX && mParentPositionY == parentPositionY) return;
|
| mParentPositionX = parentPositionX;
|
| mParentPositionY = parentPositionY;
|
| - onPositionChanged();
|
| + temporarilyHide();
|
| }
|
|
|
| private int getContainerPositionX() {
|
| @@ -173,16 +195,77 @@ public class PopupTouchHandleDrawable extends View {
|
| return mParentPositionY + mPositionY;
|
| }
|
|
|
| - private void onPositionChanged() {
|
| + private void updatePosition() {
|
| mContainer.update(getContainerPositionX(), getContainerPositionY(),
|
| getRight() - getLeft(), getBottom() - getTop());
|
| }
|
|
|
| - // x and y are in physical pixels.
|
| - private void moveTo(int x, int y) {
|
| - mPositionX = x;
|
| - mPositionY = y;
|
| - onPositionChanged();
|
| + private void updateVisibility() {
|
| + boolean visible = mVisible && !mTemporarilyHidden;
|
| + setVisibility(visible ? VISIBLE : INVISIBLE);
|
| + }
|
| +
|
| + private void updateAlpha() {
|
| + if (mAlpha == 1.f) return;
|
| + long currentTimeMillis = AnimationUtils.currentAnimationTimeMillis();
|
| + mAlpha = Math.min(1.f, (float) (currentTimeMillis - mFadeStartTime) / FADE_IN_DURATION_MS);
|
| + mDrawable.setAlpha((int) (255 * mAlpha));
|
| + scheduleInvalidate();
|
| + }
|
| +
|
| + private void temporarilyHide() {
|
| + mTemporarilyHidden = true;
|
| + updateVisibility();
|
| + rescheduleFadeIn();
|
| + }
|
| +
|
| + private void doInvalidate() {
|
| + updatePosition();
|
| + updateVisibility();
|
| + invalidate();
|
| + }
|
| +
|
| + private void scheduleInvalidate() {
|
| + if (mInvalidationRunnable == null) {
|
| + mInvalidationRunnable = new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + mHasPendingInvalidate = false;
|
| + doInvalidate();
|
| + }
|
| + };
|
| + }
|
| +
|
| + if (mHasPendingInvalidate) return;
|
| + mHasPendingInvalidate = true;
|
| + ApiCompatibilityUtils.postOnAnimation(this, mInvalidationRunnable);
|
| + }
|
| +
|
| + private void rescheduleFadeIn() {
|
| + if (mDeferredHandleFadeInRunnable == null) {
|
| + mDeferredHandleFadeInRunnable = new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + if (isScrollInProgress()) {
|
| + rescheduleFadeIn();
|
| + return;
|
| + }
|
| + mTemporarilyHidden = false;
|
| + beginFadeIn();
|
| + }
|
| + };
|
| + }
|
| +
|
| + removeCallbacks(mDeferredHandleFadeInRunnable);
|
| + ApiCompatibilityUtils.postOnAnimationDelayed(
|
| + this, mDeferredHandleFadeInRunnable, FADE_IN_DELAY_MS);
|
| + }
|
| +
|
| + private void beginFadeIn() {
|
| + if (getVisibility() == VISIBLE) return;
|
| + mAlpha = 0.f;
|
| + mFadeStartTime = AnimationUtils.currentAnimationTimeMillis();
|
| + doInvalidate();
|
| }
|
|
|
| @Override
|
| @@ -191,22 +274,17 @@ public class PopupTouchHandleDrawable extends View {
|
| setMeasuredDimension(0, 0);
|
| return;
|
| }
|
| - setMeasuredDimension(mDrawable.getIntrinsicWidth(),
|
| - mDrawable.getIntrinsicHeight());
|
| + 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);
|
| }
|
|
|
| - // x and y are in physical pixels.
|
| - 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.
|
| private int getAdjustedPositionX() {
|
| @@ -219,6 +297,16 @@ public class PopupTouchHandleDrawable extends View {
|
| return mPositionY + Math.round(mHotspotY);
|
| }
|
|
|
| + private boolean isScrollInProgress() {
|
| + final PopupTouchHandleDrawableDelegate delegate = mDelegate.get();
|
| + if (delegate == null) {
|
| + hide();
|
| + return false;
|
| + }
|
| +
|
| + return delegate.isScrollInProgress();
|
| + }
|
| +
|
| @CalledByNative
|
| private void show() {
|
| if (mContainer.isShowing()) return;
|
| @@ -244,6 +332,7 @@ public class PopupTouchHandleDrawable extends View {
|
|
|
| @CalledByNative
|
| private void hide() {
|
| + mTemporarilyHidden = false;
|
| mContainer.dismiss();
|
| if (mParentPositionObserver != null) {
|
| mParentPositionObserver.removeListener(mParentPositionListener);
|
| @@ -269,19 +358,30 @@ public class PopupTouchHandleDrawable extends View {
|
|
|
| @CalledByNative
|
| private void setOpacity(float alpha) {
|
| - if (mAlpha == alpha) return;
|
| - mAlpha = alpha;
|
| - if (mDrawable != null) mDrawable.setAlpha((int) (255 * mAlpha));
|
| + // Ignore opacity updates from the caller as they are not compatible
|
| + // with the custom fade animation.
|
| }
|
|
|
| @CalledByNative
|
| - private void setFocus(float x, float y) {
|
| - positionAt((int) x, (int) y);
|
| + private void setFocus(float focusX, float focusY) {
|
| + int x = (int) focusX - Math.round(mHotspotX);
|
| + int y = (int) focusY - Math.round(mHotspotY);
|
| + if (mPositionX == x && mPositionY == y) return;
|
| + mPositionX = x;
|
| + mPositionY = y;
|
| + if (isScrollInProgress()) {
|
| + temporarilyHide();
|
| + } else {
|
| + scheduleInvalidate();
|
| + }
|
| }
|
|
|
| @CalledByNative
|
| private void setVisible(boolean visible) {
|
| - setVisibility(visible ? VISIBLE : INVISIBLE);
|
| + mVisible = visible;
|
| + int visibility = visible ? VISIBLE : INVISIBLE;
|
| + if (getVisibility() == visibility) return;
|
| + scheduleInvalidate();
|
| }
|
|
|
| @CalledByNative
|
|
|