Chromium Code Reviews| 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..5e1682a1c120349d2d631e9a04f7552a285b36be |
| --- /dev/null |
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/ContextMenuDialog.java |
| @@ -0,0 +1,162 @@ |
| +// 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.Color; |
| +import android.graphics.Rect; |
| +import android.graphics.drawable.ColorDrawable; |
| +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.ViewGroup.LayoutParams; |
| +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 {@link #show()} and {@link #dismiss()}. |
| + */ |
| +public class ContextMenuDialog extends AlwaysDismissedDialog { |
| + private static final int ENTER_ANIMATION_DURATION = 250; |
|
Ted C
2017/05/30 23:04:04
suffix of _MS to indicate milliseconds
nit, I thi
Daniel Park
2017/05/31 18:19:39
Done.
|
| + // Exit animation duration should be set to 60% of the enter animation duration. |
| + private static final int EXIT_ANIMATION_DURATION = 150; |
| + |
| + private final Activity mActivity; |
| + |
| + private View mContentView; |
| + private float mContextMenuSourceX; |
|
Ted C
2017/05/30 23:04:04
same comment about the suffixes of Px or Dp
Daniel Park
2017/05/31 18:19:39
Done.
|
| + private float mContextMenuSourceY; |
| + private float mTouchPointX; |
| + private float mTouchPointY; |
| + private int mContextMenuFirstLocationY; |
| + private RenderCoordinates mRenderCoordinates; |
| + |
| + public ContextMenuDialog(Activity ownerActivity, int theme, float touchPointX, |
|
Ted C
2017/05/30 23:04:04
javadoc
Daniel Park
2017/05/31 18:19:39
Done.
|
| + float touchPointY, View contentView, RenderCoordinates renderCoordinates) { |
| + super(ownerActivity, theme); |
| + mActivity = ownerActivity; |
| + mTouchPointX = touchPointX; |
| + mTouchPointY = touchPointY; |
| + mContentView = contentView; |
| + mRenderCoordinates = renderCoordinates; |
|
Ted C
2017/05/30 23:04:04
looks like many if not all of these could also be
Daniel Park
2017/05/31 18:19:39
Done.
|
| + } |
| + |
| + @Override |
| + public void show() { |
| + Window dialogWindow = this.getWindow(); |
|
Ted C
2017/05/30 23:04:04
no need for this.
Daniel Park
2017/05/31 18:19:39
Done.
|
| + dialogWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); |
| + dialogWindow.setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); |
| + |
| + mContentView.setVisibility(View.INVISIBLE); |
| + mContentView.addOnLayoutChangeListener(new OnLayoutChangeListener() { |
| + |
|
Ted C
2017/05/30 23:04:04
remove blank line
Daniel Park
2017/05/31 18:19:39
Done.
|
| + @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 |
|
Ted C
2017/05/30 23:04:04
I think you can use getMeasuredHeight() == 0 here
Daniel Park
2017/05/31 18:19:39
Done.
|
| + && 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(); |
| + mContentView.removeOnLayoutChangeListener(this); |
| + } |
| + }); |
| + super.show(); |
| + } |
| + |
| + private void startEnterAnimation() { |
| + Rect rectangle = new Rect(); |
| + Window window = mActivity.getWindow(); |
| + window.getDecorView().getWindowVisibleDisplayFrame(rectangle); |
| + |
| + float xOffset = rectangle.left; |
| + float yOffset = rectangle.top + mRenderCoordinates.getContentOffsetYPix(); |
| + |
| + int[] currentLocationOnScreen = new int[2]; |
| + mContentView.getLocationOnScreen(currentLocationOnScreen); |
|
Ted C
2017/05/30 23:04:04
you should try this on a phone on a sight with the
Daniel Park
2017/05/31 18:19:39
Done.
Daniel Park
2017/05/31 18:19:39
the starting points are all correct.
|
| + |
| + mContextMenuFirstLocationY = currentLocationOnScreen[1]; |
| + |
| + mContextMenuSourceX = mTouchPointX - currentLocationOnScreen[0] + xOffset; |
| + mContextMenuSourceY = mTouchPointY - currentLocationOnScreen[1] + yOffset; |
| + |
| + Animation animation = getScaleAnimation(true, mContextMenuSourceX, mContextMenuSourceY); |
| + mContentView.startAnimation(animation); |
| + } |
| + |
| + @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() { |
| + |
|
Ted C
2017/05/30 23:04:05
remove blank line
Daniel Park
2017/05/31 18:19:39
Done.
|
| + @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(); |
|
Ted C
2017/05/30 23:04:04
can drop "this."
Daniel Park
2017/05/31 18:19:39
Done.
|
| + return true; |
| + } |
| + return false; |
| + } |
| + |
| + /** |
| + * @param isEnterAnimation Whether the animation to be returned is for showing the context menu |
| + * as opposed to hiding it. |
| + * @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; |
| + } |
| +} |