OLD | NEW |
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.Activity; | 7 import android.app.Activity; |
8 import android.app.Dialog; | |
9 import android.content.DialogInterface; | 8 import android.content.DialogInterface; |
10 import android.content.res.Resources; | 9 import android.content.res.Resources; |
11 import android.graphics.Bitmap; | 10 import android.graphics.Bitmap; |
12 import android.graphics.Canvas; | 11 import android.graphics.Canvas; |
13 import android.graphics.Shader; | 12 import android.graphics.Shader; |
14 import android.graphics.drawable.BitmapDrawable; | 13 import android.graphics.drawable.BitmapDrawable; |
15 import android.graphics.drawable.Drawable; | 14 import android.graphics.drawable.Drawable; |
16 import android.support.design.widget.TabLayout; | 15 import android.support.design.widget.TabLayout; |
17 import android.support.v4.view.ViewPager; | |
18 import android.support.v7.app.AlertDialog; | 16 import android.support.v7.app.AlertDialog; |
19 import android.text.TextUtils; | 17 import android.text.TextUtils; |
20 import android.util.Pair; | 18 import android.util.Pair; |
21 import android.view.LayoutInflater; | 19 import android.view.LayoutInflater; |
22 import android.view.View; | 20 import android.view.View; |
23 import android.view.ViewGroup; | 21 import android.view.ViewGroup; |
24 import android.widget.AdapterView; | 22 import android.widget.AdapterView; |
25 import android.widget.BaseAdapter; | 23 import android.widget.BaseAdapter; |
26 import android.widget.ImageView; | 24 import android.widget.ImageView; |
27 import android.widget.ListView; | 25 import android.widget.ListView; |
28 import android.widget.TextView; | 26 import android.widget.TextView; |
29 | 27 |
30 import org.chromium.base.ApiCompatibilityUtils; | 28 import org.chromium.base.ApiCompatibilityUtils; |
31 import org.chromium.base.Callback; | 29 import org.chromium.base.Callback; |
32 import org.chromium.base.VisibleForTesting; | 30 import org.chromium.base.VisibleForTesting; |
33 import org.chromium.chrome.R; | 31 import org.chromium.chrome.R; |
| 32 import org.chromium.chrome.browser.widget.ContextMenuDialog; |
| 33 import org.chromium.content.browser.RenderCoordinates; |
34 | 34 |
35 import java.util.ArrayList; | 35 import java.util.ArrayList; |
36 import java.util.List; | 36 import java.util.List; |
37 | 37 |
38 /** | 38 /** |
39 * A custom dialog that separates each group into separate tabs. It uses a dialo
g instead. | 39 * A custom dialog that separates each group into separate tabs. It uses a dialo
g instead. |
40 */ | 40 */ |
41 public class TabularContextMenuUi implements ContextMenuUi, AdapterView.OnItemCl
ickListener { | 41 public class TabularContextMenuUi implements ContextMenuUi, AdapterView.OnItemCl
ickListener { |
42 private Dialog mDialog; | 42 private ContextMenuDialog mContextMenuDialog; |
43 private Callback<Integer> mCallback; | 43 private Callback<Integer> mCallback; |
44 private int mMenuItemHeight; | 44 private int mMenuItemHeight; |
45 private ImageView mHeaderImageView; | 45 private ImageView mHeaderImageView; |
46 private Runnable mOnShareItemClicked; | 46 private Runnable mOnShareItemClicked; |
| 47 private View mPagerView; |
| 48 private RenderCoordinates mRenderCoordinates; |
47 | 49 |
48 public TabularContextMenuUi(Runnable onShareItemClicked) { | 50 public TabularContextMenuUi(Runnable onShareItemClicked) { |
49 mOnShareItemClicked = onShareItemClicked; | 51 mOnShareItemClicked = onShareItemClicked; |
50 } | 52 } |
51 | 53 |
52 @Override | 54 @Override |
53 public void displayMenu(Activity activity, ContextMenuParams params, | 55 public void displayMenu(final Activity activity, ContextMenuParams params, |
54 List<Pair<Integer, List<ContextMenuItem>>> items, Callback<Integer>
onItemClicked, | 56 List<Pair<Integer, List<ContextMenuItem>>> items, Callback<Integer>
onItemClicked, |
55 final Runnable onMenuShown, final Runnable onMenuClosed) { | 57 final Runnable onMenuShown, final Runnable onMenuClosed) { |
56 mCallback = onItemClicked; | 58 mCallback = onItemClicked; |
57 mDialog = createDialog(activity, params, items); | |
58 | 59 |
59 mDialog.getWindow().setBackgroundDrawable(ApiCompatibilityUtils.getDrawa
ble( | 60 float density = Resources.getSystem().getDisplayMetrics().density; |
60 activity.getResources(), R.drawable.white_with_rounded_corners))
; | 61 final float touchPointXPx = params.getTriggeringTouchXDp() * density; |
| 62 final float touchPointYPx = params.getTriggeringTouchYDp() * density; |
61 | 63 |
62 mDialog.setOnShowListener(new DialogInterface.OnShowListener() { | 64 mContextMenuDialog = |
| 65 createContextMenuDialog(activity, params, items, touchPointXPx,
touchPointYPx); |
| 66 |
| 67 mContextMenuDialog.setOnShowListener(new DialogInterface.OnShowListener(
) { |
63 @Override | 68 @Override |
64 public void onShow(DialogInterface dialogInterface) { | 69 public void onShow(DialogInterface dialogInterface) { |
65 onMenuShown.run(); | 70 onMenuShown.run(); |
66 } | 71 } |
67 }); | 72 }); |
68 | 73 |
69 mDialog.setOnDismissListener(new DialogInterface.OnDismissListener() { | 74 mContextMenuDialog.setOnDismissListener(new DialogInterface.OnDismissLis
tener() { |
70 @Override | 75 @Override |
71 public void onDismiss(DialogInterface dialogInterface) { | 76 public void onDismiss(DialogInterface dialogInterface) { |
72 onMenuClosed.run(); | 77 onMenuClosed.run(); |
73 } | 78 } |
74 }); | 79 }); |
75 | 80 |
76 mDialog.show(); | 81 mContextMenuDialog.show(); |
77 } | 82 } |
78 | 83 |
79 /** | 84 /** |
80 * Returns the fully complete dialog based off the params and the itemGroups
. | 85 * Returns the fully complete dialog based off the params and the itemGroups
. |
| 86 * |
81 * @param activity Used to inflate the dialog. | 87 * @param activity Used to inflate the dialog. |
82 * @param params Used to get the header title. | 88 * @param params Used to get the header title. |
83 * @param itemGroups If there is more than one group it will create a paged
view. | 89 * @param itemGroups If there is more than one group it will create a paged
view. |
| 90 * @param touchPointYPx The x-coordinate of the touch that triggered the con
text menu. |
| 91 * @param touchPointXPx The y-coordinate of the touch that triggered the con
text menu. |
84 * @return Returns a final dialog that does not have a background can be dis
played using | 92 * @return Returns a final dialog that does not have a background can be dis
played using |
85 * {@link AlertDialog#show()}. | 93 * {@link AlertDialog#show()}. |
86 */ | 94 */ |
87 private Dialog createDialog(Activity activity, ContextMenuParams params, | 95 private ContextMenuDialog createContextMenuDialog(Activity activity, Context
MenuParams params, |
88 List<Pair<Integer, List<ContextMenuItem>>> itemGroups) { | 96 List<Pair<Integer, List<ContextMenuItem>>> itemGroups, float touchPo
intXPx, |
89 Dialog dialog = new Dialog(activity); | 97 float touchPointYPx) { |
90 dialog.setContentView(createPagerView(activity, params, itemGroups)); | 98 View view = LayoutInflater.from(activity).inflate(R.layout.tabular_conte
xt_menu, null); |
| 99 |
| 100 mPagerView = initPagerView(activity, params, itemGroups, |
| 101 (TabularContextMenuViewPager) view.findViewById(R.id.custom_page
r)); |
| 102 |
| 103 final ContextMenuDialog dialog = new ContextMenuDialog(activity, R.style
.DialogWhenLarge, |
| 104 touchPointXPx, touchPointYPx, mPagerView, mRenderCoordinates); |
| 105 dialog.setContentView(view); |
| 106 |
91 return dialog; | 107 return dialog; |
92 } | 108 } |
93 | 109 |
94 /** | 110 /** |
95 * Creates a ViewPageAdapter based off the given list of views. | 111 * Creates a ViewPageAdapter based off the given list of views. |
96 * @param activity Used to inflate the new ViewPager | 112 * |
| 113 * @param activity Used to inflate the new ViewPager. |
97 * @param params Used to get the header text. | 114 * @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 | 115 * @param itemGroups The list of views to put into the ViewPager. The string
is the title of the |
99 * tab | 116 * tab. |
| 117 * @param viewPager The {@link TabularContextMenuViewPager} to initialize. |
100 * @return Returns a complete tabular context menu view. | 118 * @return Returns a complete tabular context menu view. |
101 */ | 119 */ |
102 @VisibleForTesting | 120 @VisibleForTesting |
103 View createPagerView(Activity activity, ContextMenuParams params, | 121 View initPagerView(Activity activity, ContextMenuParams params, |
104 List<Pair<Integer, List<ContextMenuItem>>> itemGroups) { | 122 List<Pair<Integer, List<ContextMenuItem>>> itemGroups, |
105 View view = LayoutInflater.from(activity).inflate(R.layout.tabular_conte
xt_menu, null); | 123 TabularContextMenuViewPager viewPager) { |
106 | |
107 List<Pair<String, ViewGroup>> viewGroups = new ArrayList<>(); | 124 List<Pair<String, ViewGroup>> viewGroups = new ArrayList<>(); |
108 int maxCount = 0; | 125 int maxCount = 0; |
109 for (int i = 0; i < itemGroups.size(); i++) { | 126 for (int i = 0; i < itemGroups.size(); i++) { |
110 Pair<Integer, List<ContextMenuItem>> itemGroup = itemGroups.get(i); | 127 Pair<Integer, List<ContextMenuItem>> itemGroup = itemGroups.get(i); |
111 maxCount = Math.max(maxCount, itemGroup.second.size()); | 128 maxCount = Math.max(maxCount, itemGroup.second.size()); |
112 } | 129 } |
113 for (int i = 0; i < itemGroups.size(); i++) { | 130 for (int i = 0; i < itemGroups.size(); i++) { |
114 Pair<Integer, List<ContextMenuItem>> itemGroup = itemGroups.get(i); | 131 Pair<Integer, List<ContextMenuItem>> itemGroup = itemGroups.get(i); |
115 // TODO(tedchoc): Pass the ContextMenuGroup identifier to determine
if it's an image. | 132 // TODO(tedchoc): Pass the ContextMenuGroup identifier to determine
if it's an image. |
116 boolean isImageTab = itemGroup.first == R.string.contextmenu_image_t
itle; | 133 boolean isImageTab = itemGroup.first == R.string.contextmenu_image_t
itle; |
117 viewGroups.add(new Pair<>(activity.getString(itemGroup.first), | 134 viewGroups.add(new Pair<>(activity.getString(itemGroup.first), |
118 createContextMenuPageUi( | 135 createContextMenuPageUi( |
119 activity, params, itemGroup.second, isImageTab, maxC
ount))); | 136 activity, params, itemGroup.second, isImageTab, maxC
ount))); |
120 } | 137 } |
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 | 138 |
128 TabularContextMenuViewPager pager = | 139 viewPager.setAdapter(new TabularContextMenuPagerAdapter(viewGroups)); |
129 (TabularContextMenuViewPager) view.findViewById(R.id.custom_page
r); | 140 TabLayout tabLayout = (TabLayout) viewPager.findViewById(R.id.tab_layout
); |
130 pager.setAdapter(new TabularContextMenuPagerAdapter(viewGroups)); | |
131 | |
132 TabLayout tabLayout = (TabLayout) view.findViewById(R.id.tab_layout); | |
133 if (itemGroups.size() <= 1) { | 141 if (itemGroups.size() <= 1) { |
134 tabLayout.setVisibility(View.GONE); | 142 tabLayout.setVisibility(View.GONE); |
135 } else { | 143 } else { |
136 tabLayout.setupWithViewPager((ViewPager) view.findViewById(R.id.cust
om_pager)); | 144 tabLayout.setBackgroundResource(R.drawable.grey_with_top_rounded_cor
ners); |
| 145 tabLayout.setupWithViewPager(viewPager); |
137 } | 146 } |
138 | 147 |
139 return view; | 148 return viewPager; |
140 } | 149 } |
141 | 150 |
142 /** | 151 /** |
143 * Creates the view of a context menu. Based off the Context Type, it'll adj
ust the list of | 152 * 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. | 153 * items and display only the ones that'll be on that specific group. |
| 154 * |
145 * @param activity Used to get the resources of an item. | 155 * @param activity Used to get the resources of an item. |
146 * @param params used to create the header text. | 156 * @param params used to create the header text. |
147 * @param items A set of Items to display in a context menu. Filtered based
off the type. | 157 * @param items A set of Items to display in a context menu. Filtered based
off the type. |
148 * @param isImage Whether or not the view should have an image layout or not
. | 158 * @param isImage Whether or not the view should have an image layout or not
. |
149 * @param maxCount The maximum amount of {@link ContextMenuItem}s that could
exist in this view | 159 * @param maxCount The maximum amount of {@link ContextMenuItem}s that could
exist in this view |
150 * or any other views calculated in the context menu. Used t
o estimate the size | 160 * or any other views calculated in the context menu. Used t
o estimate the size |
151 * of the list. | 161 * of the list. |
152 * @return Returns a filled LinearLayout with all the context menu items. | 162 * @return Returns a filled LinearLayout with all the context menu items. |
153 */ | 163 */ |
154 @VisibleForTesting | 164 @VisibleForTesting |
(...skipping 10 matching lines...) Expand all Loading... |
165 baseLayout.findViewById(R.id.context_header_layout).setVisibility(Vi
ew.VISIBLE); | 175 baseLayout.findViewById(R.id.context_header_layout).setVisibility(Vi
ew.VISIBLE); |
166 baseLayout.findViewById(R.id.context_divider).setVisibility(View.VIS
IBLE); | 176 baseLayout.findViewById(R.id.context_divider).setVisibility(View.VIS
IBLE); |
167 displayImageHeader(baseLayout, params, activity.getResources()); | 177 displayImageHeader(baseLayout, params, activity.getResources()); |
168 } | 178 } |
169 | 179 |
170 // Set the list adapter and get the height to display it appropriately i
n a dialog. | 180 // Set the list adapter and get the height to display it appropriately i
n a dialog. |
171 Runnable onDirectShare = new Runnable() { | 181 Runnable onDirectShare = new Runnable() { |
172 @Override | 182 @Override |
173 public void run() { | 183 public void run() { |
174 mOnShareItemClicked.run(); | 184 mOnShareItemClicked.run(); |
175 mDialog.dismiss(); | 185 mContextMenuDialog.dismiss(); |
176 } | 186 } |
177 }; | 187 }; |
178 TabularContextMenuListAdapter listAdapter = | 188 TabularContextMenuListAdapter listAdapter = |
179 new TabularContextMenuListAdapter(items, activity, onDirectShare
); | 189 new TabularContextMenuListAdapter(items, activity, onDirectShare
); |
180 ViewGroup.LayoutParams layoutParams = listView.getLayoutParams(); | 190 ViewGroup.LayoutParams layoutParams = listView.getLayoutParams(); |
181 layoutParams.height = measureApproximateListViewHeight(listView, listAda
pter, maxCount); | 191 layoutParams.height = measureApproximateListViewHeight(listView, listAda
pter, maxCount); |
182 listView.setLayoutParams(layoutParams); | 192 listView.setLayoutParams(layoutParams); |
183 listView.setAdapter(listAdapter); | 193 listView.setAdapter(listAdapter); |
184 listView.setOnItemClickListener(this); | 194 listView.setOnItemClickListener(this); |
185 | 195 |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
238 BitmapDrawable bm = new BitmapDrawable(resources, bitmap); | 248 BitmapDrawable bm = new BitmapDrawable(resources, bitmap); |
239 bm.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); | 249 bm.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); |
240 imageView.setVisibility(View.VISIBLE); | 250 imageView.setVisibility(View.VISIBLE); |
241 imageView.setBackground(bm); | 251 imageView.setBackground(bm); |
242 } | 252 } |
243 | 253 |
244 /** | 254 /** |
245 * To save time measuring the height, this method gets an item if the height
has not been | 255 * To save time measuring the height, this method gets an item if the height
has not been |
246 * previous measured and multiplies it by count of the total amount of items
. It is fine if the | 256 * previous measured and multiplies it by count of the total amount of items
. It is fine if the |
247 * height too small as the ListView will scroll through the other values. | 257 * height too small as the ListView will scroll through the other values. |
| 258 * |
248 * @param listView The ListView to measure the surrounding padding. | 259 * @param listView The ListView to measure the surrounding padding. |
249 * @param listAdapter The adapter which contains the items within the list. | 260 * @param listAdapter The adapter which contains the items within the list. |
250 * @return Returns the combined height of the padding of the ListView and th
e approximate height | 261 * @return Returns the combined height of the padding of the ListView and th
e approximate height |
251 * of the ListView based off the an item. | 262 * of the ListView based off the an item. |
252 */ | 263 */ |
253 private int measureApproximateListViewHeight( | 264 private int measureApproximateListViewHeight( |
254 ListView listView, BaseAdapter listAdapter, int maxCount) { | 265 ListView listView, BaseAdapter listAdapter, int maxCount) { |
255 int totalHeight = listView.getPaddingTop() + listView.getPaddingBottom()
; | 266 int totalHeight = listView.getPaddingTop() + listView.getPaddingBottom()
; |
256 if (mMenuItemHeight == 0 && !listAdapter.isEmpty()) { | 267 if (mMenuItemHeight == 0 && !listAdapter.isEmpty()) { |
257 View view = listAdapter.getView(0, null, listView); | 268 View view = listAdapter.getView(0, null, listView); |
258 view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UN
SPECIFIED), | 269 view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UN
SPECIFIED), |
259 View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECI
FIED)); | 270 View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECI
FIED)); |
260 mMenuItemHeight = view.getMeasuredHeight(); | 271 mMenuItemHeight = view.getMeasuredHeight(); |
261 } | 272 } |
262 return totalHeight + mMenuItemHeight * maxCount; | 273 return totalHeight + mMenuItemHeight * maxCount; |
263 } | 274 } |
264 | 275 |
265 /** | 276 /** |
266 * When an thumbnail is retrieved for the header of an image, this will set
the header to | 277 * When an thumbnail is retrieved for the header of an image, this will set
the header to that |
267 * that particular bitmap. | 278 * particular bitmap. |
268 */ | 279 */ |
269 public void onImageThumbnailRetrieved(Bitmap bitmap) { | 280 public void onImageThumbnailRetrieved(Bitmap bitmap) { |
270 if (mHeaderImageView != null) { | 281 if (mHeaderImageView != null) { |
271 mHeaderImageView.setImageBitmap(bitmap); | 282 mHeaderImageView.setImageBitmap(bitmap); |
272 } | 283 } |
273 } | 284 } |
274 | 285 |
275 @Override | 286 @Override |
276 public void onItemClick(AdapterView<?> adapterView, View view, int position,
long id) { | 287 public void onItemClick(AdapterView<?> adapterView, View view, int position,
long id) { |
277 mDialog.dismiss(); | 288 mContextMenuDialog.dismiss(); |
278 mCallback.onResult((int) id); | 289 mCallback.onResult((int) id); |
279 } | 290 } |
| 291 |
| 292 /** |
| 293 * Gives this class access to the render coordinates to allow access to the
total size of the |
| 294 * toolbar and tab strip. |
| 295 */ |
| 296 public void setRenderCoordinates(RenderCoordinates renderCoordinates) { |
| 297 mRenderCoordinates = renderCoordinates; |
| 298 } |
280 } | 299 } |
OLD | NEW |