Index: chrome/android/java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuUi.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuUi.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuUi.java |
index 09428c07607022ac3b53855c70553f103e64cdfe..7cc1de9c33fa13e7e647de5843ab552cb9a9801e 100644 |
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuUi.java |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuUi.java |
@@ -4,25 +4,38 @@ |
package org.chromium.chrome.browser.contextmenu; |
+import android.app.ActionBar.LayoutParams; |
import android.app.Activity; |
import android.app.Dialog; |
import android.content.DialogInterface; |
import android.content.res.Resources; |
import android.graphics.Bitmap; |
import android.graphics.Canvas; |
+import android.graphics.Color; |
+import android.graphics.Rect; |
import android.graphics.Shader; |
import android.graphics.drawable.BitmapDrawable; |
+import android.graphics.drawable.ColorDrawable; |
import android.graphics.drawable.Drawable; |
import android.support.design.widget.TabLayout; |
import android.support.v4.view.ViewPager; |
+import android.support.v4.view.animation.LinearOutSlowInInterpolator; |
import android.support.v7.app.AlertDialog; |
import android.text.TextUtils; |
import android.util.Pair; |
+import android.view.Gravity; |
import android.view.LayoutInflater; |
import android.view.View; |
+import android.view.View.OnClickListener; |
+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 android.widget.AdapterView; |
import android.widget.BaseAdapter; |
+import android.widget.FrameLayout; |
import android.widget.ImageView; |
import android.widget.ListView; |
import android.widget.TextView; |
@@ -31,6 +44,8 @@ import org.chromium.base.ApiCompatibilityUtils; |
import org.chromium.base.Callback; |
import org.chromium.base.VisibleForTesting; |
import org.chromium.chrome.R; |
+import org.chromium.chrome.browser.widget.AlwaysDismissedDialog; |
+import org.chromium.content.browser.ContentViewCore; |
import java.util.ArrayList; |
import java.util.List; |
@@ -44,21 +59,28 @@ public class TabularContextMenuUi implements ContextMenuUi, AdapterView.OnItemCl |
private int mMenuItemHeight; |
private ImageView mHeaderImageView; |
private Runnable mOnShareItemClicked; |
+ private View mPagerView; |
+ |
+ private float mContextMenuSourceX; |
+ private float mContextMenuSourceY; |
+ private int mContextMenuYLocationOnScreen; |
+ private ContentViewCore mContentViewCore; |
+ |
+ private static final double MAX_PROPORTION = 0.75; |
Theresa
2017/05/23 01:58:08
nit: MAX_WIDTH_PROPORTION
Daniel Park
2017/05/24 16:44:31
Done.
|
+ private static final int ANIMATION_IN_DURATION = 250; |
+ private static final int ANIMATION_OUT_DURATION = (int) (ANIMATION_IN_DURATION * 0.6); |
public TabularContextMenuUi(Runnable onShareItemClicked) { |
mOnShareItemClicked = onShareItemClicked; |
} |
@Override |
- public void displayMenu(Activity activity, ContextMenuParams params, |
+ public void displayMenu(final Activity activity, ContextMenuParams params, |
List<Pair<Integer, List<ContextMenuItem>>> items, Callback<Integer> onItemClicked, |
final Runnable onMenuShown, final Runnable onMenuClosed) { |
mCallback = onItemClicked; |
mDialog = createDialog(activity, params, items); |
- mDialog.getWindow().setBackgroundDrawable(ApiCompatibilityUtils.getDrawable( |
- activity.getResources(), R.drawable.white_with_rounded_corners)); |
- |
mDialog.setOnShowListener(new DialogInterface.OnShowListener() { |
@Override |
public void onShow(DialogInterface dialogInterface) { |
@@ -73,11 +95,38 @@ public class TabularContextMenuUi implements ContextMenuUi, AdapterView.OnItemCl |
} |
}); |
+ Window dialogWindow = mDialog.getWindow(); |
+ dialogWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); |
+ dialogWindow.setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); |
+ |
+ float density = Resources.getSystem().getDisplayMetrics().density; |
+ final float touchPointX = params.getX() * density; |
+ final float touchPointY = params.getY() * density; |
+ |
+ mPagerView.setVisibility(View.INVISIBLE); |
+ mPagerView.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; |
Theresa
2017/05/23 01:58:08
nit: Add a comment about why we're returning early
Daniel Park
2017/05/24 16:44:31
Done.
|
+ } |
+ } |
+ initializeAnimationAndRevealView(touchPointX, touchPointY, activity); |
+ mPagerView.removeOnLayoutChangeListener(this); |
+ } |
+ }); |
+ |
mDialog.show(); |
} |
/** |
* Returns the fully complete dialog based off the params and the itemGroups. |
+ * |
* @param activity Used to inflate the dialog. |
* @param params Used to get the header title. |
* @param itemGroups If there is more than one group it will create a paged view. |
@@ -86,17 +135,64 @@ public class TabularContextMenuUi implements ContextMenuUi, AdapterView.OnItemCl |
*/ |
private Dialog createDialog(Activity activity, ContextMenuParams params, |
List<Pair<Integer, List<ContextMenuItem>>> itemGroups) { |
- Dialog dialog = new Dialog(activity); |
- dialog.setContentView(createPagerView(activity, params, itemGroups)); |
+ final Dialog dialog = new AlwaysDismissedDialog(activity, R.style.DialogWhenLarge); |
+ |
+ dialog.findViewById(android.R.id.content).setOnClickListener(new OnClickListener() { |
Theresa
2017/05/23 01:58:08
The dialog can also be dismissed by a tap on the b
Daniel Park
2017/05/24 16:44:31
Done.
|
+ @Override |
+ public void onClick(View v) { |
+ int[] outLocation = new int[2]; |
+ mPagerView.getLocationOnScreen(outLocation); |
+ mContextMenuSourceY = |
+ mContextMenuSourceY + (mContextMenuYLocationOnScreen - outLocation[1]); |
+ Animation exitAnimation = getScaleAnimation(false); |
+ exitAnimation.setAnimationListener(new AnimationListener() { |
+ |
+ @Override |
+ public void onAnimationStart(Animation animation) {} |
+ |
+ @Override |
+ public void onAnimationRepeat(Animation animation) {} |
+ |
+ @Override |
+ public void onAnimationEnd(Animation animation) { |
+ dialog.dismiss(); |
+ } |
+ }); |
+ mPagerView.startAnimation(exitAnimation); |
+ dialog.findViewById(android.R.id.content).setOnClickListener(null); |
+ } |
+ }); |
+ |
+ Resources resources = activity.getResources(); |
+ |
+ FrameLayout mFullContainer = new FrameLayout(activity); |
+ mFullContainer.setBackgroundColor( |
+ ApiCompatibilityUtils.getColor(resources, R.color.modal_dialog_scrim_color)); |
+ |
+ int contextMenuWidth = |
+ (int) Math.min(resources.getDisplayMetrics().widthPixels * MAX_PROPORTION, |
+ resources.getDimensionPixelSize(R.dimen.context_menu_max_width_in_dp)); |
+ |
+ FrameLayout.LayoutParams layoutParams = |
+ new FrameLayout.LayoutParams(contextMenuWidth, LayoutParams.MATCH_PARENT); |
+ layoutParams.gravity = Gravity.CENTER; |
+ |
+ mPagerView = createPagerView(activity, params, itemGroups); |
+ |
+ mFullContainer.addView(mPagerView, layoutParams); |
+ dialog.addContentView(mFullContainer, |
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); |
+ |
return dialog; |
} |
/** |
* Creates a ViewPageAdapter based off the given list of views. |
- * @param activity Used to inflate the new ViewPager |
+ * |
+ * @param activity Used to inflate the new ViewPager. |
* @param params Used to get the header text. |
* @param itemGroups The list of views to put into the ViewPager. The string is the title of the |
- * tab |
+ * tab. |
* @return Returns a complete tabular context menu view. |
*/ |
@VisibleForTesting |
@@ -118,21 +214,15 @@ public class TabularContextMenuUi implements ContextMenuUi, AdapterView.OnItemCl |
createContextMenuPageUi( |
activity, params, itemGroup.second, isImageTab, maxCount))); |
} |
- if (itemGroups.size() == 1) { |
- viewGroups.get(0) |
- .second.getChildAt(0) |
- .findViewById(R.id.context_header_layout) |
- .setBackgroundResource(R.color.google_grey_100); |
- } |
TabularContextMenuViewPager pager = |
(TabularContextMenuViewPager) view.findViewById(R.id.custom_pager); |
pager.setAdapter(new TabularContextMenuPagerAdapter(viewGroups)); |
- |
TabLayout tabLayout = (TabLayout) view.findViewById(R.id.tab_layout); |
if (itemGroups.size() <= 1) { |
tabLayout.setVisibility(View.GONE); |
} else { |
+ tabLayout.setBackgroundResource(R.drawable.grey_with_top_rounded_corners); |
tabLayout.setupWithViewPager((ViewPager) view.findViewById(R.id.custom_pager)); |
} |
@@ -142,13 +232,14 @@ public class TabularContextMenuUi implements ContextMenuUi, AdapterView.OnItemCl |
/** |
* Creates the view of a context menu. Based off the Context Type, it'll adjust the list of |
* items and display only the ones that'll be on that specific group. |
+ * |
* @param activity Used to get the resources of an item. |
* @param params used to create the header text. |
* @param items A set of Items to display in a context menu. Filtered based off the type. |
* @param isImage Whether or not the view should have an image layout or not. |
* @param maxCount The maximum amount of {@link ContextMenuItem}s that could exist in this view |
- * or any other views calculated in the context menu. Used to estimate the size |
- * of the list. |
+ * or any other views calculated in the context menu. Used to estimate the size of |
+ * the list. |
* @return Returns a filled LinearLayout with all the context menu items. |
*/ |
@VisibleForTesting |
@@ -245,6 +336,7 @@ public class TabularContextMenuUi implements ContextMenuUi, AdapterView.OnItemCl |
* To save time measuring the height, this method gets an item if the height has not been |
* previous measured and multiplies it by count of the total amount of items. It is fine if the |
* height too small as the ListView will scroll through the other values. |
+ * |
* @param listView The ListView to measure the surrounding padding. |
* @param listAdapter The adapter which contains the items within the list. |
* @return Returns the combined height of the padding of the ListView and the approximate height |
@@ -263,8 +355,65 @@ public class TabularContextMenuUi implements ContextMenuUi, AdapterView.OnItemCl |
} |
/** |
- * When an thumbnail is retrieved for the header of an image, this will set the header to |
- * that particular bitmap. |
+ * @param touchPointX The x-coordinate of the pixel where the touch was registered. |
Theresa
2017/05/23 01:58:07
nit: I think this would read better as "The x-coor
Daniel Park
2017/05/24 16:44:32
Done.
|
+ * @param touchPointY The y-coordinate of the pixel where the touch was registered. |
+ * @param activity Activity to get the offsets. |
Theresa
2017/05/23 01:58:08
Elaborate on what "offsets" means.
Daniel Park
2017/05/24 16:44:32
Done.
|
+ */ |
+ private void initializeAnimationAndRevealView( |
+ final float touchPointX, final float touchPointY, Activity activity) { |
+ mPagerView.setVisibility(View.VISIBLE); |
+ |
+ int[] locationOnScreen = new int[2]; |
+ mPagerView.getLocationOnScreen(locationOnScreen); |
+ |
+ mContextMenuYLocationOnScreen = locationOnScreen[1]; |
+ mPagerView.startAnimation(initAnimations( |
+ touchPointX, touchPointY, locationOnScreen[0], locationOnScreen[1], activity)); |
+ mPagerView.getLocationOnScreen(locationOnScreen); |
Theresa
2017/05/23 01:58:08
Why is getLocationOnScreen() called again here?
Daniel Park
2017/05/24 16:44:31
Done.
Daniel Park
2017/05/24 16:44:31
it was for debugging purposes that i forgot to tak
|
+ } |
+ |
+ /** |
+ * @param startX The starting x-coordinate of the translation animation. |
+ * @param startY The starting y-coordinate of the translation animation. |
+ * @param viewLocationX The x-coordinate of the top left corner of the view relative to the |
+ * device. |
+ * @param viewLocationY The y-coordinate of the top left corner of the view relative to the |
+ * device. |
+ * @param activity |
Theresa
2017/05/23 01:58:08
activity needs a description
Daniel Park
2017/05/24 16:44:31
Done.
|
+ * @return Returns the animation set containing the scale & translation animations with the |
+ * pivots set relative to the view because 0, 0 is relative to the view, not the device |
Theresa
2017/05/23 01:58:08
nit: because 0,0.... belongs in an in-line comment
Daniel Park
2017/05/24 16:44:31
Done.
|
+ * window. |
+ */ |
+ protected Animation initAnimations(final float touchPointX, final float touchPointY, |
Theresa
2017/05/23 01:58:07
nit: can this method be private?
Daniel Park
2017/05/24 16:44:32
Done.
|
+ int viewLocationX, int viewLocationY, Activity activity) { |
+ Rect rectangle = new Rect(); |
+ Window window = activity.getWindow(); |
+ window.getDecorView().getWindowVisibleDisplayFrame(rectangle); |
+ |
+ // yOffset represents how many pixels are taken up by anything above the content window |
+ // e.g. toolbar & tab strip. |
+ float yOffset = |
+ rectangle.top + mContentViewCore.getRenderCoordinates().getContentOffsetYPix(); |
+ |
+ // xOffset represents how many pixels are taken up by anything to the left of the content |
+ // window e.g. navigation bar of the Nexus 6P when in landscape mode with usb port to the |
+ // left. |
+ float xOffset = rectangle.left; |
+ |
+ /** |
+ * Touch points & view locations are relative to the content window i.e. 0, 0 represents |
+ * the top left corner of the content window, but animation requires coordinates relative |
+ * to the context menu. These operations are to transform the content window coordinates |
+ * to what they would be if they were relative to the the context menu. |
+ */ |
+ mContextMenuSourceX = touchPointX - viewLocationX + xOffset; |
+ mContextMenuSourceY = touchPointY - viewLocationY + yOffset; |
+ return getScaleAnimation(true); |
+ } |
+ |
+ /** |
+ * When an thumbnail is retrieved for the header of an image, this will set the header to that |
+ * particular bitmap. |
*/ |
public void onImageThumbnailRetrieved(Bitmap bitmap) { |
if (mHeaderImageView != null) { |
@@ -277,4 +426,27 @@ public class TabularContextMenuUi implements ContextMenuUi, AdapterView.OnItemCl |
mDialog.dismiss(); |
mCallback.onResult((int) id); |
} |
+ |
+ /** |
+ * Gives this class access to the content view core to allow access to the total size of the |
+ * toolbar and tab strip. |
+ */ |
+ public void setContentViewCore(ContentViewCore contentViewCore) { |
+ mContentViewCore = contentViewCore; |
+ } |
+ |
+ private Animation getScaleAnimation(boolean comingIn) { |
+ float fromX = comingIn ? 0f : 1f; |
+ float toX = comingIn ? 1f : 0f; |
+ float fromY = fromX; |
+ float toY = toX; |
+ |
+ ScaleAnimation scaleAnimation = new ScaleAnimation(fromX, toX, fromY, toY, |
+ Animation.ABSOLUTE, mContextMenuSourceX, Animation.ABSOLUTE, mContextMenuSourceY); |
+ |
+ long duration = comingIn ? ANIMATION_IN_DURATION : ANIMATION_OUT_DURATION; |
+ scaleAnimation.setDuration(duration); |
+ scaleAnimation.setInterpolator(new LinearOutSlowInInterpolator()); |
+ return scaleAnimation; |
+ } |
} |