Chromium Code Reviews| 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 |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..7ea01f2950063b914d668e138159069cf1aa44ff |
| --- /dev/null |
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuUi.java |
| @@ -0,0 +1,185 @@ |
| +// Copyright 2017 The Chromium Authors. All rights reserved. |
| +// 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.content.res.Resources; |
| +import android.graphics.Bitmap; |
| +import android.graphics.Canvas; |
| +import android.graphics.Shader; |
| +import android.graphics.drawable.BitmapDrawable; |
| +import android.graphics.drawable.Drawable; |
| +import android.support.design.widget.TabLayout; |
| +import android.support.v4.view.ViewPager; |
| +import android.support.v7.app.AlertDialog; |
| +import android.text.TextUtils; |
| +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.ImageView; |
| +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 TabularContextMenuUi implements ContextMenuUi, AdapterView.OnItemClickListener { |
| + private AlertDialog mDialog; |
| + private Callback<Integer> mCallback; |
| + |
| + @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, |
| + List<Pair<Integer, List<ContextMenuItem>>> itemGroups) { |
| + AlertDialog.Builder builder = new AlertDialog.Builder(activity); |
|
Ted C
2017/03/24 04:32:53
Do we need an alertdialog in this case? Would a p
JJ
2017/03/25 01:40:25
Well what do you know, we can? There was definitel
|
| + builder.setView(createPagerView(activity, params, itemGroups)); |
| + return builder.create(); |
| + } |
| + |
| + /** |
| + * 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. |
| + * @return Returns a filled LinearLayout based off custom_context_menu.xml. |
| + */ |
| + @VisibleForTesting |
| + ViewGroup createSingleContextView( |
| + Activity activity, ContextMenuParams params, List<ContextMenuItem> items) { |
| + ViewGroup baseLayout = (ViewGroup) LayoutInflater.from(activity).inflate( |
| + R.layout.custom_context_menu, null); |
| + ListView listView = (ListView) baseLayout.findViewById(R.id.selectable_items); |
| + displayHeaderIfVisibleItems(params, baseLayout); |
| + |
| + // Set the list adapter and get the height to display it appropriately in a dialog. |
| + List<ContextMenuItem> itemsType = new ArrayList<>(items); |
| + TabularContextMenuListAdapter listAdapter = |
| + new TabularContextMenuListAdapter(itemsType, activity); |
| + ViewGroup.LayoutParams layoutParams = listView.getLayoutParams(); |
| + layoutParams.height = measureApproximateListViewHeight(listView, listAdapter); |
|
Ted C
2017/03/24 04:32:53
what happens if this height is larger than the scr
JJ
2017/03/25 01:40:25
There's a max height that dialogs are willing to g
|
| + listView.setLayoutParams(layoutParams); |
| + listView.setAdapter(listAdapter); |
| + listView.setOnItemClickListener(this); |
| + |
| + return baseLayout; |
| + } |
| + |
| + private void displayHeaderIfVisibleItems(ContextMenuParams params, ViewGroup baseLayout) { |
| + String headerText = ChromeContextMenuPopulator.createHeaderText(params); |
| + TextView headerTextView = (TextView) baseLayout.findViewById(R.id.context_header_text); |
| + if (TextUtils.isEmpty(headerText)) { |
| + headerTextView.setVisibility(View.GONE); |
| + baseLayout.findViewById(R.id.context_header_frame_layout).setVisibility(View.GONE); |
| + baseLayout.findViewById(R.id.context_divider).setVisibility(View.GONE); |
| + return; |
| + } |
| + |
| + headerTextView.setVisibility(View.VISIBLE); |
| + headerTextView.setText(headerText); |
| + } |
| + |
| + private void setBackgroundForImageView(ImageView imageView, Resources resources) { |
|
Ted C
2017/03/24 04:32:53
this seems to be unused. maybe something we shoul
JJ
2017/03/25 01:40:25
Moved to the next CL.
|
| + Bitmap bitmap = null; |
| + Drawable drawable = |
| + ApiCompatibilityUtils.getDrawable(resources, R.drawable.checkerboard_background); |
| + bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), |
| + Bitmap.Config.ARGB_8888); |
| + Canvas canvas = new Canvas(bitmap); |
| + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); |
| + drawable.draw(canvas); |
| + BitmapDrawable bm = new BitmapDrawable(resources, bitmap); |
| + bm.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); |
| + imageView.setVisibility(View.VISIBLE); |
| + imageView.setBackground(bm); |
| + } |
| + |
| + /** |
| + * To save time measuring the height, this method gets an item 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 |
| + * of the ListView based off the an item. |
| + */ |
| + private int measureApproximateListViewHeight(ListView listView, BaseAdapter listAdapter) { |
| + 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), |
| + View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); |
| + totalHeight += (view.getMeasuredHeight() * listAdapter.getCount()); |
| + } |
| + return totalHeight; |
| + } |
| + |
| + /** |
| + * Creates a ViewPageAdapter based off the given list of views. |
| + * @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 |
| + * @return Returns a complete context menu based of image_link_context_menu.xml |
| + */ |
| + @VisibleForTesting |
| + View createPagerView(Activity activity, ContextMenuParams params, |
| + List<Pair<Integer, List<ContextMenuItem>>> itemGroups) { |
| + View view = LayoutInflater.from(activity).inflate(R.layout.multi_type_context_menu, null); |
| + |
| + List<Pair<String, ViewGroup>> viewGroups = new ArrayList<>(); |
| + for (Pair<Integer, List<ContextMenuItem>> itemGroup : itemGroups) { |
| + viewGroups.add(new Pair<>(activity.getString(itemGroup.first), |
| + createSingleContextView(activity, params, itemGroup.second))); |
| + } |
| + |
| + 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); |
| + } |
| + tabLayout.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), |
|
Ted C
2017/03/24 04:32:53
This still seems odd to me. If we were manually s
JJ
2017/03/25 01:40:25
It's not needed. It's a relic. My bad.
|
| + 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); |
| + } |
| +} |