Index: chrome/android/java/src/org/chromium/chrome/browser/widget/ContextMenuDialog.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/ContextMenuDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/ContextMenuDialog.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..37e8a4aa8b2b56afaefeb36ae4796ef5dfb0428a |
--- /dev/null |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/ContextMenuDialog.java |
@@ -0,0 +1,177 @@ |
+// Copyright 2017 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.chrome.browser.widget; |
+ |
+import android.app.Activity; |
+import android.graphics.Rect; |
+import android.support.v4.view.animation.LinearOutSlowInInterpolator; |
+import android.view.MotionEvent; |
+import android.view.View; |
+import android.view.View.OnLayoutChangeListener; |
+import android.view.ViewGroup; |
+import android.view.Window; |
+import android.view.animation.Animation; |
+import android.view.animation.Animation.AnimationListener; |
+import android.view.animation.ScaleAnimation; |
+ |
+import org.chromium.content.browser.RenderCoordinates; |
+ |
+/** |
+ * ContextMenuDialog is a subclass of AlwaysDismissedDialog that ensures that the proper scale |
+ * animation is played upon calling dismiss(). |
+ */ |
+public class ContextMenuDialog extends AlwaysDismissedDialog { |
+ private static final int ENTER_ANIMATION_DURATION = 250; |
+ // Exit animation duration should be set to 60% of the enter animation duration. |
+ private static final int EXIT_ANIMATION_DURATION = 150; |
+ |
+ private View mContentView; |
+ private float mContextMenuSourceX; |
+ private float mContextMenuSourceY; |
+ private int mContextMenuFirstLocationY; |
+ |
+ public ContextMenuDialog(Activity ownerActivity, int theme) { |
+ super(ownerActivity, theme); |
+ } |
+ |
+ /** |
+ * @param contentView The content view to run the animation on. |
+ * @param touchPointX The x-coordinate of the touch that triggered the context menu in pixels. |
+ * @param touchPointY The y-coordinate of the touch that triggered the context menu in pixels. |
+ * @param activity Activity to determine the number of pixels to the left and above of the |
+ * window. |
+ * @param renderCoordinates Render coordinates to determine the y offset taken by non content |
+ * window items. |
+ */ |
+ public void setupEnterAnimationAndShow(final View contentView, final float touchPointX, |
Theresa
2017/05/24 22:50:50
How about an init() methods that takes all of thes
Daniel Park
2017/05/25 17:12:36
Done.
|
+ final float touchPointY, final Activity activity, |
+ final RenderCoordinates renderCoordinates) { |
+ mContentView = contentView; |
+ mContentView.addOnLayoutChangeListener(new OnLayoutChangeListener() { |
+ |
+ @Override |
+ public void onLayoutChange(View v, int left, int top, int right, int bottom, |
+ int oldLeft, int oldTop, int oldRight, int oldBottom) { |
+ ViewGroup group = (ViewGroup) v; |
+ for (int i = 0; i < group.getChildCount(); i++) { |
+ if (group.getChildAt(i).getHeight() == 0 |
+ && group.getChildAt(i).getVisibility() == View.VISIBLE) { |
+ // Return early because not all the views have been measured, so animation |
+ // pivots will be off. |
+ return; |
+ } |
+ } |
+ mContentView.setVisibility(View.VISIBLE); |
+ startEnterAnimation(touchPointX, touchPointY, activity, renderCoordinates); |
+ mContentView.removeOnLayoutChangeListener(this); |
+ } |
+ }); |
+ super.show(); |
+ } |
+ |
+ /** |
+ * @param touchPointX The x-coordinate of the touch that triggered the context menu in pixels. |
+ * @param touchPointY The y-coordinate of the touch that triggered the context menu in pixels. |
+ * @param activity Activity to determine the number of pixels to the left and above of the |
+ * window. |
+ * @param renderCoordinates Render coordinates to determine the y offset taken by non content |
+ * window items. |
+ */ |
+ private void startEnterAnimation(float touchPointX, float touchPointY, Activity activity, |
+ RenderCoordinates renderCoordinates) { |
+ int[] currentLocationOnScreen = new int[2]; |
+ mContentView.getLocationOnScreen(currentLocationOnScreen); |
+ |
+ mContextMenuFirstLocationY = currentLocationOnScreen[1]; |
+ Animation animation = getStartAnimation( |
+ touchPointX, touchPointY, currentLocationOnScreen, activity, renderCoordinates); |
+ mContentView.startAnimation(animation); |
+ } |
+ |
+ /** |
+ * @param touchPointX The x-coordinate of the touch that triggered the context menu in pixels. |
+ * @param touchPointY The y-coordinate of the touch that triggered the context menu in pixels. |
+ * @param currentLocationOnScreen A 2-element array that stores the x,y coordinates of the view |
+ * in its 0th and 1st indices respectively. |
+ * @param activity Activity to determine the number of pixels to the left and above of the |
+ * window. |
+ * @param renderCoordinates Render coordinates to determine the y offset taken by non content |
+ * window items. |
+ */ |
+ private Animation getStartAnimation(float touchPointX, float touchPointY, |
Theresa
2017/05/24 22:50:50
I would combine this with startEnterAnimation(). T
Daniel Park
2017/05/25 17:12:37
Done.
|
+ int[] currentLocationOnScreen, Activity activity, RenderCoordinates renderCoordinates) { |
+ Rect rectangle = new Rect(); |
+ Window window = activity.getWindow(); |
+ window.getDecorView().getWindowVisibleDisplayFrame(rectangle); |
+ |
+ float xOffset = rectangle.left; |
+ float yOffset = rectangle.top + renderCoordinates.getContentOffsetYPix(); |
+ |
+ mContextMenuSourceX = touchPointX - currentLocationOnScreen[0] + xOffset; |
+ mContextMenuSourceY = touchPointY - currentLocationOnScreen[1] + yOffset; |
+ |
+ return getScaleAnimation(true, mContextMenuSourceX, mContextMenuSourceY); |
+ } |
+ |
+ @Override |
+ public void dismiss() { |
+ int[] contextMenuFinalLocation = new int[2]; |
+ mContentView.getLocationOnScreen(contextMenuFinalLocation); |
+ // Recalculate mContextMenuDestinationY because the context menu's final location may not be |
+ // the same as its first location if it changed in height. |
+ float contextMenuDestinationY = |
+ mContextMenuSourceY + (mContextMenuFirstLocationY - contextMenuFinalLocation[1]); |
+ |
+ Animation exitAnimation = |
+ getScaleAnimation(false, mContextMenuSourceX, contextMenuDestinationY); |
+ exitAnimation.setAnimationListener(new AnimationListener() { |
+ |
+ @Override |
+ public void onAnimationStart(Animation animation) {} |
+ |
+ @Override |
+ public void onAnimationRepeat(Animation animation) {} |
+ |
+ @Override |
+ public void onAnimationEnd(Animation animation) { |
+ ContextMenuDialog.super.dismiss(); |
+ } |
+ }); |
+ mContentView.startAnimation(exitAnimation); |
+ } |
+ |
+ @Override |
+ public boolean onTouchEvent(MotionEvent event) { |
+ if (event.getAction() == MotionEvent.ACTION_DOWN) { |
+ this.dismiss(); |
Theresa
2017/05/24 22:50:50
Just to confirm onTouchEvent() doesn't get called
Daniel Park
2017/05/25 17:12:37
from testing, it does get called when an item is c
Daniel Park
2017/05/25 17:12:37
Done.
|
+ } |
+ return true; |
+ } |
+ |
+ /** |
+ * @param isEnterAnimation Whether or not the animation will be for when the context menu enters |
+ * or not. |
Theresa
2017/05/24 22:50:50
nit: "Whether animation is for showing the context
Daniel Park
2017/05/25 17:12:37
Done.
|
+ * @param pivotX The X coordinate of the point about which the object is being scaled, specified |
+ * as an absolute number where 0 is the left edge. |
+ * @param pivotY The Y coordinate of the point about which the object is being scaled, specified |
+ * as an absolute number where 0 is the top edge. |
+ * @return Returns the scale animation for the context menu. |
+ */ |
+ public Animation getScaleAnimation(boolean isEnterAnimation, float pivotX, float pivotY) { |
+ float fromX = isEnterAnimation ? 0f : 1f; |
+ float toX = isEnterAnimation ? 1f : 0f; |
+ float fromY = fromX; |
+ float toY = toX; |
+ |
+ ScaleAnimation animation = new ScaleAnimation( |
+ fromX, toX, fromY, toY, Animation.ABSOLUTE, pivotX, Animation.ABSOLUTE, pivotY); |
+ |
+ long duration = isEnterAnimation ? ENTER_ANIMATION_DURATION : EXIT_ANIMATION_DURATION; |
+ |
+ animation.setDuration(duration); |
+ animation.setInterpolator(new LinearOutSlowInInterpolator()); |
+ return animation; |
+ } |
+} |