Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(866)

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuUi.java

Issue 2868403003: added scale animation for context menu (Closed)
Patch Set: switching context menu tab bug fix Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2017 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.chrome.browser.contextmenu; 5 package org.chromium.chrome.browser.contextmenu;
6 6
7 import android.app.ActionBar.LayoutParams;
7 import android.app.Activity; 8 import android.app.Activity;
8 import android.app.Dialog; 9 import android.app.Dialog;
9 import android.content.DialogInterface; 10 import android.content.DialogInterface;
10 import android.content.res.Resources; 11 import android.content.res.Resources;
11 import android.graphics.Bitmap; 12 import android.graphics.Bitmap;
12 import android.graphics.Canvas; 13 import android.graphics.Canvas;
14 import android.graphics.Color;
13 import android.graphics.Shader; 15 import android.graphics.Shader;
14 import android.graphics.drawable.BitmapDrawable; 16 import android.graphics.drawable.BitmapDrawable;
17 import android.graphics.drawable.ColorDrawable;
15 import android.graphics.drawable.Drawable; 18 import android.graphics.drawable.Drawable;
16 import android.support.design.widget.TabLayout; 19 import android.support.design.widget.TabLayout;
17 import android.support.v4.view.ViewPager; 20 import android.support.v4.view.ViewPager;
21 import android.support.v4.view.animation.LinearOutSlowInInterpolator;
18 import android.support.v7.app.AlertDialog; 22 import android.support.v7.app.AlertDialog;
19 import android.text.TextUtils; 23 import android.text.TextUtils;
20 import android.util.Pair; 24 import android.util.Pair;
25 import android.view.Gravity;
21 import android.view.LayoutInflater; 26 import android.view.LayoutInflater;
22 import android.view.View; 27 import android.view.View;
28 import android.view.View.OnClickListener;
29 import android.view.View.OnLayoutChangeListener;
23 import android.view.ViewGroup; 30 import android.view.ViewGroup;
31 import android.view.Window;
32 import android.view.animation.Animation;
33 import android.view.animation.Animation.AnimationListener;
34 import android.view.animation.ScaleAnimation;
24 import android.widget.AdapterView; 35 import android.widget.AdapterView;
25 import android.widget.BaseAdapter; 36 import android.widget.BaseAdapter;
37 import android.widget.FrameLayout;
26 import android.widget.ImageView; 38 import android.widget.ImageView;
27 import android.widget.ListView; 39 import android.widget.ListView;
28 import android.widget.TextView; 40 import android.widget.TextView;
29 41
30 import org.chromium.base.ApiCompatibilityUtils; 42 import org.chromium.base.ApiCompatibilityUtils;
31 import org.chromium.base.Callback; 43 import org.chromium.base.Callback;
32 import org.chromium.base.VisibleForTesting; 44 import org.chromium.base.VisibleForTesting;
33 import org.chromium.chrome.R; 45 import org.chromium.chrome.R;
46 import org.chromium.chrome.browser.widget.AlwaysDismissedDialog;
47 import org.chromium.content.browser.ContentViewCore;
34 48
35 import java.util.ArrayList; 49 import java.util.ArrayList;
36 import java.util.List; 50 import java.util.List;
37 51
38 /** 52 /**
39 * A custom dialog that separates each group into separate tabs. It uses a dialo g instead. 53 * A custom dialog that separates each group into separate tabs. It uses a dialo g instead.
40 */ 54 */
41 public class TabularContextMenuUi implements ContextMenuUi, AdapterView.OnItemCl ickListener { 55 public class TabularContextMenuUi implements ContextMenuUi, AdapterView.OnItemCl ickListener {
42 private Dialog mDialog; 56 private Dialog mDialog;
43 private Callback<Integer> mCallback; 57 private Callback<Integer> mCallback;
44 private int mMenuItemHeight; 58 private int mMenuItemHeight;
45 private ImageView mHeaderImageView; 59 private ImageView mHeaderImageView;
46 private Runnable mOnShareItemClicked; 60 private Runnable mOnShareItemClicked;
61 private View mPagerView;
62
63 private float mContextMenuSourceX;
64 private float mContextMenuSourceY;
65 private int mContextMenuLocationOnScreen;
66 private int mContextMenuHeight = -1;
67 private int mContextMenuWidth;
68 private ContentViewCore mContentViewCore;
47 69
48 public TabularContextMenuUi(Runnable onShareItemClicked) { 70 public TabularContextMenuUi(Runnable onShareItemClicked) {
49 mOnShareItemClicked = onShareItemClicked; 71 mOnShareItemClicked = onShareItemClicked;
50 } 72 }
51 73
52 @Override 74 @Override
53 public void displayMenu(Activity activity, ContextMenuParams params, 75 public void displayMenu(Activity activity, ContextMenuParams params,
54 List<Pair<Integer, List<ContextMenuItem>>> items, Callback<Integer> onItemClicked, 76 List<Pair<Integer, List<ContextMenuItem>>> items, Callback<Integer> onItemClicked,
55 final Runnable onMenuShown, final Runnable onMenuClosed) { 77 final Runnable onMenuShown, final Runnable onMenuClosed) {
56 mCallback = onItemClicked; 78 mCallback = onItemClicked;
57 mDialog = createDialog(activity, params, items); 79 mDialog = createDialog(activity, params, items);
58 80
59 mDialog.getWindow().setBackgroundDrawable(ApiCompatibilityUtils.getDrawa ble(
60 activity.getResources(), R.drawable.white_with_rounded_corners)) ;
61
62 mDialog.setOnShowListener(new DialogInterface.OnShowListener() { 81 mDialog.setOnShowListener(new DialogInterface.OnShowListener() {
63 @Override 82 @Override
64 public void onShow(DialogInterface dialogInterface) { 83 public void onShow(DialogInterface dialogInterface) {
65 onMenuShown.run(); 84 onMenuShown.run();
66 } 85 }
67 }); 86 });
68 87
69 mDialog.setOnDismissListener(new DialogInterface.OnDismissListener() { 88 mDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
70 @Override 89 @Override
71 public void onDismiss(DialogInterface dialogInterface) { 90 public void onDismiss(DialogInterface dialogInterface) {
72 onMenuClosed.run(); 91 onMenuClosed.run();
73 } 92 }
74 }); 93 });
75 94
95 Window dialogWindow = mDialog.getWindow();
96 dialogWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)) ;
97 dialogWindow.setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PAR ENT);
98
99 float density = Resources.getSystem().getDisplayMetrics().density;
100 final float touchPointX = params.getX() * density;
101 final float touchPointY = params.getY() * density;
102
76 mDialog.show(); 103 mDialog.show();
104 mPagerView.setVisibility(View.INVISIBLE);
Theresa 2017/05/19 17:19:30 nit: I would move this visibility change and the l
Daniel Park 2017/05/22 18:12:55 Done.
105 mPagerView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
106
107 @Override
108 public void onLayoutChange(View v, int left, int top, int right, int bottom,
109 int oldLeft, int oldTop, int oldRight, int oldBottom) {
110 if (mContextMenuHeight == -1) {
Theresa 2017/05/19 17:19:30 Rather than relying on mContextMenuHeight == -1, l
Daniel Park 2017/05/22 18:12:55 Done.
111 mContextMenuHeight = v.getHeight();
112 initializeAnimationAndRevealView(touchPointX, touchPointY);
113 }
114 }
115 });
77 } 116 }
78 117
79 /** 118 /**
80 * Returns the fully complete dialog based off the params and the itemGroups . 119 * Returns the fully complete dialog based off the params and the itemGroups .
81 * @param activity Used to inflate the dialog. 120 * @param activity Used to inflate the dialog.
82 * @param params Used to get the header title. 121 * @param params Used to get the header title.
83 * @param itemGroups If there is more than one group it will create a paged view. 122 * @param itemGroups If there is more than one group it will create a paged view.
84 * @return Returns a final dialog that does not have a background can be dis played using 123 * @return Returns a final dialog that does not have a background can be dis played using
85 * {@link AlertDialog#show()}. 124 * {@link AlertDialog#show()}.
86 */ 125 */
126 @SuppressWarnings("deprecation")
Theresa 2017/05/19 17:19:30 What method is deprecated?
Daniel Park 2017/05/22 18:12:55 Done.
87 private Dialog createDialog(Activity activity, ContextMenuParams params, 127 private Dialog createDialog(Activity activity, ContextMenuParams params,
88 List<Pair<Integer, List<ContextMenuItem>>> itemGroups) { 128 List<Pair<Integer, List<ContextMenuItem>>> itemGroups) {
89 Dialog dialog = new Dialog(activity); 129 final Dialog dialog = new AlwaysDismissedDialog(activity, R.style.Dialog WhenLarge);
90 dialog.setContentView(createPagerView(activity, params, itemGroups)); 130
131 dialog.findViewById(android.R.id.content).setOnClickListener(new OnClick Listener() {
132 @Override
133 public void onClick(View v) {
134 int[] outLocation = new int[2];
135 mPagerView.getLocationOnScreen(outLocation);
136 mContextMenuSourceY =
137 mContextMenuSourceY + (mContextMenuLocationOnScreen - ou tLocation[1]);
138 Animation exitAnimation = getScaleAnimation(false);
139 exitAnimation.setAnimationListener(new AnimationListener() {
140
141 @Override
142 public void onAnimationStart(Animation animation) {}
143
144 @Override
145 public void onAnimationRepeat(Animation animation) {}
146
147 @Override
148 public void onAnimationEnd(Animation animation) {
149 dialog.dismiss();
150 }
151 });
152 mPagerView.startAnimation(exitAnimation);
153 mContextMenuHeight = -1;
Theresa 2017/05/19 17:19:30 Each time the context menu is displayed, a new Dia
Daniel Park 2017/05/22 18:12:55 Done.
154 }
155 });
156
157 FrameLayout mFullContainer = new FrameLayout(activity);
158 mFullContainer.setBackgroundColor(ApiCompatibilityUtils.getColor(
159 activity.getResources(), R.color.modal_dialog_scrim_color));
160
161 mContextMenuWidth =
162 (int) Math.min(Resources.getSystem().getDisplayMetrics().widthPi xels * 0.75, 1000);
Theresa 2017/05/19 17:19:30 0.75 should be defined as private static final MAX
Daniel Park 2017/05/22 18:12:55 Done.
163
164 FrameLayout.LayoutParams layoutParams =
165 new FrameLayout.LayoutParams(mContextMenuWidth, LayoutParams.MAT CH_PARENT);
166 layoutParams.gravity = Gravity.CENTER;
167
168 mPagerView = createPagerView(activity, params, itemGroups);
169
170 mFullContainer.addView(mPagerView, layoutParams);
171 dialog.addContentView(mFullContainer,
172 new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_P ARENT));
173
91 return dialog; 174 return dialog;
92 } 175 }
93 176
94 /** 177 /**
95 * Creates a ViewPageAdapter based off the given list of views. 178 * Creates a ViewPageAdapter based off the given list of views.
96 * @param activity Used to inflate the new ViewPager 179 * @param activity Used to inflate the new ViewPager
97 * @param params Used to get the header text. 180 * @param params Used to get the header text.
98 * @param itemGroups The list of views to put into the ViewPager. The string is the title of the 181 * @param itemGroups The list of views to put into the ViewPager. The string is the title of the
99 * tab 182 * tab
100 * @return Returns a complete tabular context menu view. 183 * @return Returns a complete tabular context menu view.
101 */ 184 */
102 @VisibleForTesting 185 @VisibleForTesting
103 View createPagerView(Activity activity, ContextMenuParams params, 186 View createPagerView(Activity activity, ContextMenuParams params,
104 List<Pair<Integer, List<ContextMenuItem>>> itemGroups) { 187 List<Pair<Integer, List<ContextMenuItem>>> itemGroups) {
105 View view = LayoutInflater.from(activity).inflate(R.layout.tabular_conte xt_menu, null); 188 View view = LayoutInflater.from(activity).inflate(R.layout.tabular_conte xt_menu, null);
189 view.findViewById(R.id.custom_pager)
190 .setBackgroundResource(R.drawable.white_with_rounded_corners);
Theresa 2017/05/19 17:19:30 Let's set the background in tabular_context_menu.x
Daniel Park 2017/05/22 18:12:55 Done.
106 191
107 List<Pair<String, ViewGroup>> viewGroups = new ArrayList<>(); 192 List<Pair<String, ViewGroup>> viewGroups = new ArrayList<>();
108 int maxCount = 0; 193 int maxCount = 0;
109 for (int i = 0; i < itemGroups.size(); i++) { 194 for (int i = 0; i < itemGroups.size(); i++) {
110 Pair<Integer, List<ContextMenuItem>> itemGroup = itemGroups.get(i); 195 Pair<Integer, List<ContextMenuItem>> itemGroup = itemGroups.get(i);
111 maxCount = Math.max(maxCount, itemGroup.second.size()); 196 maxCount = Math.max(maxCount, itemGroup.second.size());
112 } 197 }
113 for (int i = 0; i < itemGroups.size(); i++) { 198 for (int i = 0; i < itemGroups.size(); i++) {
114 Pair<Integer, List<ContextMenuItem>> itemGroup = itemGroups.get(i); 199 Pair<Integer, List<ContextMenuItem>> itemGroup = itemGroups.get(i);
115 // TODO(tedchoc): Pass the ContextMenuGroup identifier to determine if it's an image. 200 // TODO(tedchoc): Pass the ContextMenuGroup identifier to determine if it's an image.
116 boolean isImageTab = itemGroup.first == R.string.contextmenu_image_t itle; 201 boolean isImageTab = itemGroup.first == R.string.contextmenu_image_t itle;
117 viewGroups.add(new Pair<>(activity.getString(itemGroup.first), 202 viewGroups.add(new Pair<>(activity.getString(itemGroup.first),
118 createContextMenuPageUi( 203 createContextMenuPageUi(
119 activity, params, itemGroup.second, isImageTab, maxC ount))); 204 activity, params, itemGroup.second, isImageTab, maxC ount)));
120 } 205 }
121 if (itemGroups.size() == 1) {
122 viewGroups.get(0)
123 .second.getChildAt(0)
124 .findViewById(R.id.context_header_layout)
125 .setBackgroundResource(R.color.google_grey_100);
126 }
127 206
128 TabularContextMenuViewPager pager = 207 TabularContextMenuViewPager pager =
129 (TabularContextMenuViewPager) view.findViewById(R.id.custom_page r); 208 (TabularContextMenuViewPager) view.findViewById(R.id.custom_page r);
130 pager.setAdapter(new TabularContextMenuPagerAdapter(viewGroups)); 209 pager.setAdapter(new TabularContextMenuPagerAdapter(viewGroups));
131
132 TabLayout tabLayout = (TabLayout) view.findViewById(R.id.tab_layout); 210 TabLayout tabLayout = (TabLayout) view.findViewById(R.id.tab_layout);
133 if (itemGroups.size() <= 1) { 211 if (itemGroups.size() <= 1) {
134 tabLayout.setVisibility(View.GONE); 212 tabLayout.setVisibility(View.GONE);
135 } else { 213 } else {
214 tabLayout.setBackgroundResource(R.drawable.grey_with_top_rounded_cor ners);
136 tabLayout.setupWithViewPager((ViewPager) view.findViewById(R.id.cust om_pager)); 215 tabLayout.setupWithViewPager((ViewPager) view.findViewById(R.id.cust om_pager));
137 } 216 }
138 217
139 return view; 218 return view;
140 } 219 }
141 220
142 /** 221 /**
143 * Creates the view of a context menu. Based off the Context Type, it'll adj ust the list of 222 * Creates the view of a context menu. Based off the Context Type, it'll adj ust the list of
144 * items and display only the ones that'll be on that specific group. 223 * items and display only the ones that'll be on that specific group.
145 * @param activity Used to get the resources of an item. 224 * @param activity Used to get the resources of an item.
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
256 if (mMenuItemHeight == 0 && !listAdapter.isEmpty()) { 335 if (mMenuItemHeight == 0 && !listAdapter.isEmpty()) {
257 View view = listAdapter.getView(0, null, listView); 336 View view = listAdapter.getView(0, null, listView);
258 view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UN SPECIFIED), 337 view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UN SPECIFIED),
259 View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECI FIED)); 338 View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECI FIED));
260 mMenuItemHeight = view.getMeasuredHeight(); 339 mMenuItemHeight = view.getMeasuredHeight();
261 } 340 }
262 return totalHeight + mMenuItemHeight * maxCount; 341 return totalHeight + mMenuItemHeight * maxCount;
263 } 342 }
264 343
265 /** 344 /**
345 * @param touchPointX The x-coordinate of the pixel where the touch was regi stered
346 * @param touchPointY The y-coordinate of the pixel where the touch was regi stered
347 */
348 private void initializeAnimationAndRevealView(
349 final float touchPointX, final float touchPointY) {
350 mPagerView.setVisibility(View.VISIBLE);
351
352 int[] locationOnScreen = new int[2];
353 mPagerView.getLocationOnScreen(locationOnScreen);
354
355 mContextMenuLocationOnScreen = locationOnScreen[1];
356 mPagerView.startAnimation(
357 initAnimations(touchPointX, touchPointY, locationOnScreen[0], lo cationOnScreen[1]));
358 }
359
360 /**
361 * @param startX The starting x-coordinate of the translation animation
Theresa 2017/05/19 17:19:30 nit: add a period to the end of all param descript
Daniel Park 2017/05/22 18:12:55 Done.
362 * @param startY The starting y-coordinate of the translation animation
363 * @param viewLocationX The x-coordinate of the top left corner of the view relative to the
364 * device
365 * @param viewLocationY The y-coordinate of the top left corner of the view relative to the
366 * device
367 * @return Returns the animation set containing the scale & translation anim ations with the
368 * pivots set relative to the view because 0, 0 is relative to the v iew, not the device
369 * window
370 */
371 protected Animation initAnimations(final float touchPointX, final float touc hPointY,
372 int viewLocationX, int viewLocationY) {
373 float offset = mContentViewCore.getRenderCoordinates().getContentOffsetY Pix();
Theresa 2017/05/19 17:19:30 nit: Add a comment about why this is necessary. I
Daniel Park 2017/05/22 18:12:55 Done.
374 mContextMenuSourceX = touchPointX - viewLocationX;
375 mContextMenuSourceY = touchPointY - viewLocationY + offset;
376 return getScaleAnimation(true);
377 }
378
379 /**
266 * When an thumbnail is retrieved for the header of an image, this will set the header to 380 * When an thumbnail is retrieved for the header of an image, this will set the header to
267 * that particular bitmap. 381 * that particular bitmap.
268 */ 382 */
269 public void onImageThumbnailRetrieved(Bitmap bitmap) { 383 public void onImageThumbnailRetrieved(Bitmap bitmap) {
270 if (mHeaderImageView != null) { 384 if (mHeaderImageView != null) {
271 mHeaderImageView.setImageBitmap(bitmap); 385 mHeaderImageView.setImageBitmap(bitmap);
272 } 386 }
273 } 387 }
274 388
275 @Override 389 @Override
276 public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { 390 public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
277 mDialog.dismiss(); 391 mDialog.dismiss();
278 mCallback.onResult((int) id); 392 mCallback.onResult((int) id);
279 } 393 }
394
395 public void setContentViewCore(ContentViewCore contentViewCore) {
Theresa 2017/05/19 17:19:30 nit: JavaDoc for this public method
Daniel Park 2017/05/22 18:12:55 Done.
396 mContentViewCore = contentViewCore;
397 }
398
399 private Animation getScaleAnimation(boolean comingIn) {
400 float fromX = comingIn ? 0f : 1f;
401 float toX = comingIn ? 1f : 0f;
402 float fromY = fromX;
403 float toY = toX;
404
405 ScaleAnimation scaleAnimation = new ScaleAnimation(fromX, toX, fromY, to Y,
406 Animation.ABSOLUTE, mContextMenuSourceX, Animation.ABSOLUTE, mCo ntextMenuSourceY);
Theresa 2017/05/19 17:19:30 On dismiss, is the context menu disappearing back
Daniel Park 2017/05/22 18:12:55 The updated design asks for it to go back to the t
407
408 long duration = (long) (comingIn ? 250 : (250 * 0.6));
Theresa 2017/05/19 17:19:30 These int's should be defined as "private static f
Daniel Park 2017/05/22 18:12:55 Done.
409 scaleAnimation.setDuration(duration);
410 scaleAnimation.setInterpolator(new LinearOutSlowInInterpolator());
411 return scaleAnimation;
412 }
280 } 413 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698