| 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..ba82c24abd058a9d08c9cde0ce3f9bdc7a12024e
|
| --- /dev/null
|
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/ContextMenuDialog.java
|
| @@ -0,0 +1,171 @@
|
| +// 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.chrome.browser.contextmenu.TabularContextMenuViewPager;
|
| +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 long ENTER_ANIMATION_DURATION_MS = 250;
|
| + // Exit animation duration should be set to 60% of the enter animation duration.
|
| + private static final long EXIT_ANIMATION_DURATION_MS = 150;
|
| +
|
| + private final Activity mActivity;
|
| + private final View mContentView;
|
| + private final float mTouchPointXPx;
|
| + private final float mTouchPointYPx;
|
| + private final RenderCoordinates mRenderCoordinates;
|
| +
|
| + private float mContextMenuSourceXPx;
|
| + private float mContextMenuSourceYPx;
|
| + private int mContextMenuFirstLocationYPx;
|
| +
|
| + /**
|
| + * Creates an instance of the ContextMenuDialog.
|
| + * @param ownerActivity The activity in which the dialog should run
|
| + * @param theme A style resource describing the theme to use for the window, or {@code 0} to use
|
| + * the default dialog theme
|
| + * @param touchPointXPx The x-coordinate of the touch that triggered the context menu.
|
| + * @param touchPointYPx The y-coordinate of the touch that triggered the context menu.
|
| + * @param contentView The The {@link TabularContextMenuViewPager} to display on the dialog.
|
| + * @param renderCoordinates The render coordinates to get the y offset of the window.
|
| + */
|
| + public ContextMenuDialog(Activity ownerActivity, int theme, float touchPointXPx,
|
| + float touchPointYPx, View contentView, RenderCoordinates renderCoordinates) {
|
| + super(ownerActivity, theme);
|
| + mActivity = ownerActivity;
|
| + mTouchPointXPx = touchPointXPx;
|
| + mTouchPointYPx = touchPointYPx;
|
| + mContentView = contentView;
|
| + mRenderCoordinates = renderCoordinates;
|
| + }
|
| +
|
| + @Override
|
| + public void show() {
|
| + Window dialogWindow = getWindow();
|
| + dialogWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
| + dialogWindow.setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
| +
|
| + mContentView.setVisibility(View.INVISIBLE);
|
| + 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).getMeasuredHeight() == 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();
|
| + mContentView.removeOnLayoutChangeListener(this);
|
| + }
|
| + });
|
| + super.show();
|
| + }
|
| +
|
| + private void startEnterAnimation() {
|
| + Rect rectangle = new Rect();
|
| + Window window = mActivity.getWindow();
|
| + window.getDecorView().getWindowVisibleDisplayFrame(rectangle);
|
| +
|
| + float xOffsetPx = rectangle.left;
|
| + float yOffsetPx = rectangle.top + mRenderCoordinates.getContentOffsetYPix();
|
| +
|
| + int[] currentLocationOnScreenPx = new int[2];
|
| + mContentView.getLocationOnScreen(currentLocationOnScreenPx);
|
| +
|
| + mContextMenuFirstLocationYPx = currentLocationOnScreenPx[1];
|
| +
|
| + mContextMenuSourceXPx = mTouchPointXPx - currentLocationOnScreenPx[0] + xOffsetPx;
|
| + mContextMenuSourceYPx = mTouchPointYPx - currentLocationOnScreenPx[1] + yOffsetPx;
|
| +
|
| + Animation animation = getScaleAnimation(true, mContextMenuSourceXPx, mContextMenuSourceYPx);
|
| + mContentView.startAnimation(animation);
|
| + }
|
| +
|
| + @Override
|
| + public void dismiss() {
|
| + int[] contextMenuFinalLocationPx = new int[2];
|
| + mContentView.getLocationOnScreen(contextMenuFinalLocationPx);
|
| + // Recalculate mContextMenuDestinationY because the context menu's final location may not be
|
| + // the same as its first location if it changed in height.
|
| + float contextMenuDestinationYPx = mContextMenuSourceYPx
|
| + + (mContextMenuFirstLocationYPx - contextMenuFinalLocationPx[1]);
|
| +
|
| + Animation exitAnimation =
|
| + getScaleAnimation(false, mContextMenuSourceXPx, contextMenuDestinationYPx);
|
| + 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) {
|
| + dismiss();
|
| + 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_MS : EXIT_ANIMATION_DURATION_MS;
|
| +
|
| + animation.setDuration(duration);
|
| + animation.setInterpolator(new LinearOutSlowInInterpolator());
|
| + return animation;
|
| + }
|
| +}
|
|
|