OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package org.chromium.chrome.browser.widget; |
| 6 |
| 7 import android.app.Activity; |
| 8 import android.graphics.Color; |
| 9 import android.graphics.Rect; |
| 10 import android.graphics.drawable.ColorDrawable; |
| 11 import android.support.v4.view.animation.LinearOutSlowInInterpolator; |
| 12 import android.view.MotionEvent; |
| 13 import android.view.View; |
| 14 import android.view.View.OnLayoutChangeListener; |
| 15 import android.view.ViewGroup; |
| 16 import android.view.ViewGroup.LayoutParams; |
| 17 import android.view.Window; |
| 18 import android.view.animation.Animation; |
| 19 import android.view.animation.Animation.AnimationListener; |
| 20 import android.view.animation.ScaleAnimation; |
| 21 |
| 22 import org.chromium.chrome.browser.contextmenu.TabularContextMenuViewPager; |
| 23 import org.chromium.content.browser.RenderCoordinates; |
| 24 |
| 25 /** |
| 26 * ContextMenuDialog is a subclass of AlwaysDismissedDialog that ensures that th
e proper scale |
| 27 * animation is played upon calling {@link #show()} and {@link #dismiss()}. |
| 28 */ |
| 29 public class ContextMenuDialog extends AlwaysDismissedDialog { |
| 30 private static final long ENTER_ANIMATION_DURATION_MS = 250; |
| 31 // Exit animation duration should be set to 60% of the enter animation durat
ion. |
| 32 private static final long EXIT_ANIMATION_DURATION_MS = 150; |
| 33 |
| 34 private final Activity mActivity; |
| 35 private final View mContentView; |
| 36 private final float mTouchPointXPx; |
| 37 private final float mTouchPointYPx; |
| 38 private final RenderCoordinates mRenderCoordinates; |
| 39 |
| 40 private float mContextMenuSourceXPx; |
| 41 private float mContextMenuSourceYPx; |
| 42 private int mContextMenuFirstLocationYPx; |
| 43 |
| 44 /** |
| 45 * Creates an instance of the ContextMenuDialog. |
| 46 * @param ownerActivity The activity in which the dialog should run |
| 47 * @param theme A style resource describing the theme to use for the window,
or {@code 0} to use |
| 48 * the default dialog theme |
| 49 * @param touchPointXPx The x-coordinate of the touch that triggered the con
text menu. |
| 50 * @param touchPointYPx The y-coordinate of the touch that triggered the con
text menu. |
| 51 * @param contentView The The {@link TabularContextMenuViewPager} to display
on the dialog. |
| 52 * @param renderCoordinates The render coordinates to get the y offset of th
e window. |
| 53 */ |
| 54 public ContextMenuDialog(Activity ownerActivity, int theme, float touchPoint
XPx, |
| 55 float touchPointYPx, View contentView, RenderCoordinates renderCoord
inates) { |
| 56 super(ownerActivity, theme); |
| 57 mActivity = ownerActivity; |
| 58 mTouchPointXPx = touchPointXPx; |
| 59 mTouchPointYPx = touchPointYPx; |
| 60 mContentView = contentView; |
| 61 mRenderCoordinates = renderCoordinates; |
| 62 } |
| 63 |
| 64 @Override |
| 65 public void show() { |
| 66 Window dialogWindow = getWindow(); |
| 67 dialogWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT))
; |
| 68 dialogWindow.setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PAR
ENT); |
| 69 |
| 70 mContentView.setVisibility(View.INVISIBLE); |
| 71 mContentView.addOnLayoutChangeListener(new OnLayoutChangeListener() { |
| 72 @Override |
| 73 public void onLayoutChange(View v, int left, int top, int right, int
bottom, |
| 74 int oldLeft, int oldTop, int oldRight, int oldBottom) { |
| 75 ViewGroup group = (ViewGroup) v; |
| 76 for (int i = 0; i < group.getChildCount(); i++) { |
| 77 if (group.getChildAt(i).getMeasuredHeight() == 0 |
| 78 && group.getChildAt(i).getVisibility() == View.VISIB
LE) { |
| 79 // Return early because not all the views have been meas
ured, so animation |
| 80 // pivots will be off. |
| 81 return; |
| 82 } |
| 83 } |
| 84 mContentView.setVisibility(View.VISIBLE); |
| 85 startEnterAnimation(); |
| 86 mContentView.removeOnLayoutChangeListener(this); |
| 87 } |
| 88 }); |
| 89 super.show(); |
| 90 } |
| 91 |
| 92 private void startEnterAnimation() { |
| 93 Rect rectangle = new Rect(); |
| 94 Window window = mActivity.getWindow(); |
| 95 window.getDecorView().getWindowVisibleDisplayFrame(rectangle); |
| 96 |
| 97 float xOffsetPx = rectangle.left; |
| 98 float yOffsetPx = rectangle.top + mRenderCoordinates.getContentOffsetYPi
x(); |
| 99 |
| 100 int[] currentLocationOnScreenPx = new int[2]; |
| 101 mContentView.getLocationOnScreen(currentLocationOnScreenPx); |
| 102 |
| 103 mContextMenuFirstLocationYPx = currentLocationOnScreenPx[1]; |
| 104 |
| 105 mContextMenuSourceXPx = mTouchPointXPx - currentLocationOnScreenPx[0] +
xOffsetPx; |
| 106 mContextMenuSourceYPx = mTouchPointYPx - currentLocationOnScreenPx[1] +
yOffsetPx; |
| 107 |
| 108 Animation animation = getScaleAnimation(true, mContextMenuSourceXPx, mCo
ntextMenuSourceYPx); |
| 109 mContentView.startAnimation(animation); |
| 110 } |
| 111 |
| 112 @Override |
| 113 public void dismiss() { |
| 114 int[] contextMenuFinalLocationPx = new int[2]; |
| 115 mContentView.getLocationOnScreen(contextMenuFinalLocationPx); |
| 116 // Recalculate mContextMenuDestinationY because the context menu's final
location may not be |
| 117 // the same as its first location if it changed in height. |
| 118 float contextMenuDestinationYPx = mContextMenuSourceYPx |
| 119 + (mContextMenuFirstLocationYPx - contextMenuFinalLocationPx[1])
; |
| 120 |
| 121 Animation exitAnimation = |
| 122 getScaleAnimation(false, mContextMenuSourceXPx, contextMenuDesti
nationYPx); |
| 123 exitAnimation.setAnimationListener(new AnimationListener() { |
| 124 @Override |
| 125 public void onAnimationStart(Animation animation) {} |
| 126 |
| 127 @Override |
| 128 public void onAnimationRepeat(Animation animation) {} |
| 129 |
| 130 @Override |
| 131 public void onAnimationEnd(Animation animation) { |
| 132 ContextMenuDialog.super.dismiss(); |
| 133 } |
| 134 }); |
| 135 mContentView.startAnimation(exitAnimation); |
| 136 } |
| 137 |
| 138 @Override |
| 139 public boolean onTouchEvent(MotionEvent event) { |
| 140 if (event.getAction() == MotionEvent.ACTION_DOWN) { |
| 141 dismiss(); |
| 142 return true; |
| 143 } |
| 144 return false; |
| 145 } |
| 146 |
| 147 /** |
| 148 * @param isEnterAnimation Whether the animation to be returned is for showi
ng the context menu |
| 149 * as opposed to hiding it. |
| 150 * @param pivotX The X coordinate of the point about which the object is bei
ng scaled, specified |
| 151 * as an absolute number where 0 is the left edge. |
| 152 * @param pivotY The Y coordinate of the point about which the object is bei
ng scaled, specified |
| 153 * as an absolute number where 0 is the top edge. |
| 154 * @return Returns the scale animation for the context menu. |
| 155 */ |
| 156 public Animation getScaleAnimation(boolean isEnterAnimation, float pivotX, f
loat pivotY) { |
| 157 float fromX = isEnterAnimation ? 0f : 1f; |
| 158 float toX = isEnterAnimation ? 1f : 0f; |
| 159 float fromY = fromX; |
| 160 float toY = toX; |
| 161 |
| 162 ScaleAnimation animation = new ScaleAnimation( |
| 163 fromX, toX, fromY, toY, Animation.ABSOLUTE, pivotX, Animation.AB
SOLUTE, pivotY); |
| 164 |
| 165 long duration = isEnterAnimation ? ENTER_ANIMATION_DURATION_MS : EXIT_AN
IMATION_DURATION_MS; |
| 166 |
| 167 animation.setDuration(duration); |
| 168 animation.setInterpolator(new LinearOutSlowInInterpolator()); |
| 169 return animation; |
| 170 } |
| 171 } |
OLD | NEW |