Index: chrome/android/java/src/org/chromium/chrome/browser/contextmenu/CustomContextMenuDialog.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/CustomContextMenuDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/CustomContextMenuDialog.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c1be9fc8c85f26f5bfebcca62a8098ade7ad75b1 |
--- /dev/null |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/CustomContextMenuDialog.java |
@@ -0,0 +1,162 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
Ted C
2017/03/21 16:29:39
2017
David Trainor- moved to gerrit
2017/03/21 16:30:02
2017
JJ
2017/03/22 23:35:32
Done.
|
+// 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.contextmenu; |
+ |
+import android.app.Activity; |
+import android.support.design.widget.TabLayout; |
+import android.support.v4.view.ViewPager; |
+import android.support.v7.app.AlertDialog; |
+import android.util.Pair; |
+import android.view.LayoutInflater; |
+import android.view.View; |
+import android.view.ViewGroup; |
+import android.widget.AdapterView; |
+import android.widget.BaseAdapter; |
+import android.widget.ListView; |
+import android.widget.TextView; |
+ |
+import org.chromium.base.ApiCompatibilityUtils; |
+import org.chromium.base.Callback; |
+import org.chromium.base.VisibleForTesting; |
+import org.chromium.chrome.R; |
+ |
+import java.util.ArrayList; |
+import java.util.List; |
+ |
+/** |
+ * A custom dialog that separates each group into separate tabs. It uses a dialog instead. |
+ */ |
+public class CustomContextMenuDialog implements ContextMenuUi, AdapterView.OnItemClickListener { |
Ted C
2017/03/21 16:29:39
If the other name is PlatformContextMenuUi, I thin
JJ
2017/03/22 23:35:32
I've noted this and will change it shortly. I have
|
+ private AlertDialog mDialog; |
David Trainor- moved to gerrit
2017/03/21 16:30:02
final?
JJ
2017/03/22 23:35:32
Can't be done since we don't call this in the cons
David Trainor- moved to gerrit
2017/03/23 04:58:47
Makes sense I misread "displayMenu" for the constr
|
+ private Callback<Integer> mCallback; |
David Trainor- moved to gerrit
2017/03/21 16:30:02
final?
JJ
2017/03/22 23:35:32
Can't be done since we don't call this in the cons
|
+ |
+ @Override |
+ public void displayMenu(Activity activity, ContextMenuParams params, |
+ List<Pair<Integer, List<ContextMenuItem>>> items, Callback<Integer> onItemClicked) { |
+ mCallback = onItemClicked; |
+ mDialog = createDialog(activity, params, items); |
+ mDialog.getWindow().setBackgroundDrawable(ApiCompatibilityUtils.getDrawable( |
+ activity.getResources(), R.drawable.context_menu_bg)); |
+ 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 pagged view. |
+ * @return Returns a final dialog that does not have a background can be displayed using |
+ * {@link AlertDialog#show()}. |
+ */ |
+ private AlertDialog createDialog(Activity activity, ContextMenuParams params, |
David Trainor- moved to gerrit
2017/03/21 16:30:02
Should we have both createDialog and createFinalVi
JJ
2017/03/22 23:35:32
Yeah the view is used mostly for tests (that aren'
David Trainor- moved to gerrit
2017/03/23 04:58:47
I guess meant should we just roll createDialog int
|
+ List<Pair<Integer, List<ContextMenuItem>>> itemGroups) { |
+ AlertDialog.Builder builder = new AlertDialog.Builder(activity); |
+ builder.setView(createFinalView(activity, itemGroups, params)); |
+ return builder.create(); |
+ } |
+ |
+ /** |
+ * This creates the view hosted within the dialog. Mainly used for testing. |
David Trainor- moved to gerrit
2017/03/21 16:30:02
Maybe use "//" or turn it into a more real javadoc
JJ
2017/03/22 23:35:32
Done.
|
+ */ |
+ @VisibleForTesting |
+ View createFinalView(Activity activity, List<Pair<Integer, List<ContextMenuItem>>> itemGroups, |
+ ContextMenuParams params) { |
+ return (itemGroups.size() == 1) |
+ ? createSingleContextView(activity, itemGroups.get(0).second, params) |
+ : createPagerView(itemGroups, activity, params); |
+ } |
+ |
+ /** |
+ * 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 items A set of Items to display in a context menu. Filtered based off the type. |
+ * @param params used to create the header text. |
+ * @return Returns a filled LinearLayout based off custom_context_menu.xml. |
+ */ |
+ private ViewGroup createSingleContextView( |
+ Activity activity, List<ContextMenuItem> items, ContextMenuParams params) { |
+ ViewGroup baseLayout = (ViewGroup) LayoutInflater.from(activity).inflate( |
+ R.layout.custom_context_menu, null); |
+ ListView listView = (ListView) baseLayout.findViewById(R.id.selectable_items); |
+ TextView headerText = (TextView) baseLayout.findViewById(R.id.context_header_text); |
+ headerText.setVisibility(View.VISIBLE); |
+ headerText.setText(ChromeContextMenuPopulator.createHeaderText(params)); |
+ |
+ // Set the list adapter and get the height to display it appropriately in a dialog. |
+ List<ContextMenuItem> itemsType = new ArrayList<>(items); |
+ CustomContextMenuListAdapter listAdapter = |
+ new CustomContextMenuListAdapter(itemsType, activity); |
+ ViewGroup.LayoutParams layoutParams = listView.getLayoutParams(); |
+ layoutParams.height = measureApproximateListViewHeight(listView, listAdapter); |
+ listView.setLayoutParams(layoutParams); |
+ listView.requestLayout(); |
Ted C
2017/03/21 16:29:40
setLayoutParams does a requestlayout for you
David Trainor- moved to gerrit
2017/03/21 16:30:02
I think setLayoutParams does this?
JJ
2017/03/22 23:35:32
Thanks!
|
+ listView.setAdapter(listAdapter); |
+ listView.setOnItemClickListener(this); |
+ baseLayout.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), |
David Trainor- moved to gerrit
2017/03/21 16:30:02
Could you explain why we need to measure baseLayou
JJ
2017/03/22 23:35:32
Oh we don't. Sorry, old experimentation got the be
|
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); |
+ |
+ return baseLayout; |
+ } |
+ |
+ /** |
+ * To save time measuring the height, this method gets the first item and multiplies it by |
Ted C
2017/03/21 16:29:39
I wouldn't call out the first item in this comment
JJ
2017/03/22 23:35:32
Done.
|
+ * count. 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 |
+ * of the ListView based off the first item. |
+ */ |
+ private int measureApproximateListViewHeight(ListView listView, BaseAdapter listAdapter) { |
+ int height = 0; |
+ int totalHeight = listView.getPaddingTop() + listView.getPaddingBottom(); |
+ if (!listAdapter.isEmpty()) { |
+ View view = listAdapter.getView(0, null, listView); |
+ view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), |
Ted C
2017/03/21 16:29:39
I don't "think" this does what you expect. By spe
JJ
2017/03/22 23:35:32
bugh. Thank you. Measurements are still a little f
David Trainor- moved to gerrit
2017/03/23 04:58:47
Does this still need to change?
JJ
2017/03/23 18:57:46
aha! Sorry I thought I redid this edit before send
Ted C
2017/03/24 04:32:53
Sorry, but I don't understand this comment.
You a
JJ
2017/03/25 01:40:25
I think I've repeated this in a few other lines bu
|
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); |
+ height = view.getMeasuredHeight(); |
Ted C
2017/03/21 16:29:40
I don't think you need a height variable. I think
JJ
2017/03/22 23:35:32
Done.
|
+ } |
+ totalHeight += (height * listAdapter.getCount()); |
+ return totalHeight; |
+ } |
+ |
+ /** |
+ * Creates a ViewPageAdapter based off the given list of views. |
+ * @param itemGroups The list of views to put into the ViewPager. The string is the title of the |
+ * tab |
+ * @param activity Used to inflate the new ViewPager |
+ * @param params Used to get the header text. |
+ * @return Returns a complete context menu based of image_link_context_menu.xml |
+ */ |
+ private View createPagerView(List<Pair<Integer, List<ContextMenuItem>>> itemGroups, |
+ Activity activity, ContextMenuParams params) { |
+ LayoutInflater inflater = LayoutInflater.from(activity); |
+ View view = inflater.inflate(R.layout.multi_type_context_menu, null); |
David Trainor- moved to gerrit
2017/03/21 16:30:02
LayoutInflater.from(activity).inflate(...) like wh
JJ
2017/03/22 23:35:32
Done.
|
+ |
+ List<Pair<String, ViewGroup>> viewGroups = new ArrayList<>(); |
+ for (Pair<Integer, List<ContextMenuItem>> itemGroup : itemGroups) { |
JJ
2017/03/17 20:58:20
If you're wondering why we switch from an Integer
|
+ viewGroups.add(new Pair<>(activity.getString(itemGroup.first), |
+ createSingleContextView(activity, itemGroup.second, params))); |
+ } |
+ |
+ CustomContextMenuViewPager pager = |
+ (CustomContextMenuViewPager) view.findViewById(R.id.custom_pager); |
+ pager.setAdapter(new CustomContextMenuPagerAdapter(viewGroups)); |
+ |
+ TabLayout tabLayout = (TabLayout) view.findViewById(R.id.tab_layout); |
Ted C
2017/03/21 16:29:39
what happens if you set the visibility of the tabl
JJ
2017/03/22 23:35:32
O: It does! Wow I can't believe I didn't try that.
|
+ tabLayout.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), |
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); |
+ tabLayout.setupWithViewPager((ViewPager) view.findViewById(R.id.custom_pager)); |
+ |
+ return view; |
+ } |
+ |
+ @Override |
+ public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { |
+ mDialog.dismiss(); |
+ mCallback.onResult((int) id); |
+ } |
+} |