Commit 4e887537 authored by Sinan Sahin's avatar Sinan Sahin Committed by Commit Bot

Reland "Progress on the Revamped context menu"

This is a reland of 310bb105

Original change's description:
> Progress on the Revamped context menu
> 
> This CL consists of:
> - Improvements for URL formatting (i18n and emphasizing)
> - Addition of grey background while loading
> - Addition of favicons, monograms, and thumbnails
> - Addition of checkered pattern behind the transparent PNGs
> - Some visual polish and fixes
> 
> Bug: 655359
> Change-Id: I6ebf13b6097780db22c05804f2b9cdf6d1ee8461
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1595114
> Reviewed-by: Theresa <twellington@chromium.org>
> Commit-Queue: Sinan Sahin <sinansahin@google.com>
> Cr-Commit-Position: refs/heads/master@{#659233}

Bug: 655359
Change-Id: Ie3d559eb0c353aef2eecf56cf36bcc29d41ec4ec
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1610854Reviewed-by: default avatarTheresa <twellington@chromium.org>
Commit-Queue: Sinan Sahin <sinansahin@google.com>
Cr-Commit-Position: refs/heads/master@{#659763}
parent 9d9956fa
...@@ -7,25 +7,25 @@ ...@@ -7,25 +7,25 @@
<item> <item>
<shape android:shape="rectangle"> <shape android:shape="rectangle">
<solid android:color="@android:color/white" /> <solid android:color="@android:color/white" />
<size android:height="10dp" android:width="10dp"/> <size android:height="4dp" android:width="4dp"/>
</shape> </shape>
</item> </item>
<item android:top="0dp" android:left="10dp"> <item android:top="0dp" android:left="4dp">
<shape android:shape="rectangle"> <shape android:shape="rectangle">
<solid android:color="@color/modern_grey_200"/> <solid android:color="@color/modern_grey_200"/>
<size android:height="10dp" android:width="10dp"/> <size android:height="4dp" android:width="4dp"/>
</shape> </shape>
</item> </item>
<item android:top="10dp" android:left="0dp"> <item android:top="4dp" android:left="0dp">
<shape android:shape="rectangle"> <shape android:shape="rectangle">
<solid android:color="@color/modern_grey_200"/> <solid android:color="@color/modern_grey_200"/>
<size android:height="10dp" android:width="10dp"/> <size android:height="4dp" android:width="4dp"/>
</shape> </shape>
</item> </item>
<item android:top="10dp" android:left="10dp"> <item android:top="4dp" android:left="4dp">
<shape android:shape="rectangle"> <shape android:shape="rectangle">
<solid android:color="@android:color/white" /> <solid android:color="@android:color/white" />
<size android:height="10dp" android:width="10dp"/> <size android:height="4dp" android:width="4dp"/>
</shape> </shape>
</item> </item>
</layer-list> </layer-list>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2019 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. -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/revamped_context_menu_divider_padding">
<!-- TODO(sinansahin): divider_preference can be renamed to horizontal_divider -->
<include layout="@layout/divider_preference" />
</FrameLayout>
\ No newline at end of file
...@@ -13,7 +13,10 @@ ...@@ -13,7 +13,10 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:layout_margin="24dp" android:layout_marginStart="@dimen/revamped_context_menu_lateral_margin"
android:layout_marginEnd="@dimen/revamped_context_menu_lateral_margin"
android:layout_marginTop="@dimen/revamped_context_menu_vertical_margin"
android:layout_marginBottom="@dimen/revamped_context_menu_vertical_margin"
android:background="@drawable/popup_bg_tinted" android:background="@drawable/popup_bg_tinted"
android:divider="@null" /> android:divider="@null" />
</FrameLayout> </FrameLayout>
\ No newline at end of file
...@@ -5,46 +5,71 @@ ...@@ -5,46 +5,71 @@
<!-- We used nested LinearLayouts here because it was harder to align the text vertically with <!-- We used nested LinearLayouts here because it was harder to align the text vertically with
the center of the image using a single RelativeLayout. A ConstraintLayout could be a better the center of the image using a single RelativeLayout. A ConstraintLayout could be a better
choice here, but it isn't available to us, yet --> choice here, but it isn't available to us, yet. -->
<LinearLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingTop="12dp" android:paddingStart="@dimen/revamped_context_menu_list_lateral_padding"
android:paddingBottom="12dp" android:paddingEnd="@dimen/revamped_context_menu_list_lateral_padding"
android:paddingStart="16dp" android:paddingTop="@dimen/revamped_context_menu_header_vertical_padding"
android:paddingEnd="16dp" > android:paddingBottom="@dimen/revamped_context_menu_header_vertical_padding">
<ImageView <FrameLayout
android:id="@+id/menu_header_image" android:layout_width="wrap_content"
android:layout_width="@dimen/revamped_context_menu_header_image_max_size" android:layout_height="wrap_content"
android:layout_height="@dimen/revamped_context_menu_header_image_max_size" android:layout_marginEnd="16dp">
android:layout_gravity="top|start"
android:src="@drawable/checkerboard_background" <!-- Circle background for when we have a favicon or monogram -->
android:scaleType="fitCenter" <View
android:importantForAccessibility="no" /> android:id="@+id/circle_background"
android:background="@drawable/tile_view_icon_background_modern"
android:layout_width="@dimen/revamped_context_menu_header_circle_bg_diameter"
android:layout_height="@dimen/revamped_context_menu_header_circle_bg_diameter"
android:layout_margin="@dimen/revamped_context_menu_header_circle_bg_margin"
android:visibility="invisible" />
<org.chromium.ui.widget.RoundedCornerImageView
android:id="@+id/menu_header_image"
android:layout_width="@dimen/revamped_context_menu_header_image_max_size"
android:layout_height="@dimen/revamped_context_menu_header_image_max_size"
android:scaleType="centerInside"
android:importantForAccessibility="no"
app:cornerRadiusTopStart="@dimen/default_rounded_corner_radius"
app:cornerRadiusTopEnd="@dimen/default_rounded_corner_radius"
app:cornerRadiusBottomStart="@dimen/default_rounded_corner_radius"
app:cornerRadiusBottomEnd="@dimen/default_rounded_corner_radius"
app:roundedfillColor="@color/thumbnail_placeholder_on_primary_bg" />
</FrameLayout>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:layout_gravity="center_vertical"> android:layout_gravity="center_vertical">
<TextView
<!-- TODO(sinansahin): Sync with UX to decide on what to do with cases like RTL text on LTR
device and LTR text on RTL device -->
<org.chromium.ui.widget.TextViewWithLeading
android:id="@+id/menu_header_title" android:id="@+id/menu_header_title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAlignment="viewStart"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="1" android:maxLines="1"
android:textAppearance="@style/TextAppearance.BlackCaptionDefault"/> android:textAppearance="@style/TextAppearance.BlackCaptionDefault"
<TextView app:leading="@dimen/text_size_small_leading" />
<org.chromium.ui.widget.TextViewWithLeading
android:id="@+id/menu_header_url" android:id="@+id/menu_header_url"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAlignment="viewStart"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="1" android:maxLines="1"
android:textAppearance="@style/TextAppearance.BlackCaption"/> android:textAppearance="@style/TextAppearance.BlackCaption"
app:leading="@dimen/text_size_small_leading" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
\ No newline at end of file
...@@ -10,6 +10,6 @@ ...@@ -10,6 +10,6 @@
android:textAppearance="@style/TextAppearance.BlackTitle1" android:textAppearance="@style/TextAppearance.BlackTitle1"
android:minHeight="48dp" android:minHeight="48dp"
android:gravity="center_vertical" android:gravity="center_vertical"
android:paddingStart="16dp" android:paddingStart="@dimen/revamped_context_menu_list_lateral_padding"
android:paddingEnd="16dp" android:paddingEnd="@dimen/revamped_context_menu_list_lateral_padding"
android:background="?attr/selectableItemBackground"/> android:background="?attr/selectableItemBackground"/>
\ No newline at end of file
...@@ -542,8 +542,16 @@ ...@@ -542,8 +542,16 @@
<dimen name="context_menu_selectable_items_min_size">120dp</dimen> <dimen name="context_menu_selectable_items_min_size">120dp</dimen>
<!-- Revamped Context Menu Dimensions --> <!-- Revamped Context Menu Dimensions -->
<dimen name="revamped_context_menu_header_image_max_size">64dp</dimen> <dimen name="revamped_context_menu_lateral_margin">40dp</dimen>
<dimen name="revamped_context_menu_vertical_margin">20dp</dimen>
<dimen name="revamped_context_menu_list_lateral_padding">16dp</dimen>
<dimen name="revamped_context_menu_header_vertical_padding">16dp</dimen>
<dimen name="revamped_context_menu_divider_padding">8dp</dimen>
<dimen name="revamped_context_menu_header_image_max_size">60dp</dimen>
<dimen name="revamped_context_menu_header_circle_bg_diameter">48dp</dimen>
<dimen name="revamped_context_menu_header_circle_bg_margin">6dp</dimen>
<dimen name="revamped_context_menu_header_monogram_text_size">16dp</dimen>
<dimen name="revamped_context_menu_header_monogram_size">26dp</dimen>
<!-- Reader Mode dimensions --> <!-- Reader Mode dimensions -->
<!-- Padding surrounding the message. --> <!-- Padding surrounding the message. -->
<dimen name="reader_mode_infobar_text_padding">8dp</dimen> <dimen name="reader_mode_infobar_text_padding">8dp</dimen>
......
...@@ -224,21 +224,35 @@ public class ChromeContextMenuPopulator implements ContextMenuPopulator { ...@@ -224,21 +224,35 @@ public class ChromeContextMenuPopulator implements ContextMenuPopulator {
*/ */
public static String createHeaderText(ContextMenuParams params) { public static String createHeaderText(ContextMenuParams params) {
if (!isEmptyUrl(params.getLinkUrl())) { if (!isEmptyUrl(params.getLinkUrl())) {
// The context menu can be created without native library return getUrlText(params);
// being loaded. Only use native URL formatting methods
// if the native libraries have been loaded.
if (BrowserStartupController.get(LibraryProcessType.PROCESS_BROWSER)
.isStartupSuccessfullyCompleted()) {
return UrlFormatter.formatUrlForDisplayOmitHTTPScheme(params.getLinkUrl());
} else {
return params.getLinkUrl();
}
} else if (!TextUtils.isEmpty(params.getTitleText())) { } else if (!TextUtils.isEmpty(params.getTitleText())) {
return params.getTitleText(); return params.getTitleText();
} }
return ""; return "";
} }
/**
* Gets the link of the item or empty text if the Url is empty.
* @return A string with the link or an empty string.
*/
public static String createUrlText(ContextMenuParams params) {
if (!isEmptyUrl(params.getLinkUrl())) {
return getUrlText(params);
}
return "";
}
private static String getUrlText(ContextMenuParams params) {
// The context menu can be created without native library
// being loaded. Only use native URL formatting methods
// if the native libraries have been loaded.
if (BrowserStartupController.get(LibraryProcessType.PROCESS_BROWSER)
.isStartupSuccessfullyCompleted()) {
return UrlFormatter.formatUrlForDisplayOmitHTTPScheme(params.getLinkUrl());
}
return params.getLinkUrl();
}
@Override @Override
public List<Pair<Integer, List<ContextMenuItem>>> buildContextMenu( public List<Pair<Integer, List<ContextMenuItem>>> buildContextMenu(
ContextMenu menu, Context context, ContextMenuParams params) { ContextMenu menu, Context context, ContextMenuParams params) {
......
...@@ -23,6 +23,8 @@ import org.chromium.base.metrics.RecordHistogram; ...@@ -23,6 +23,8 @@ import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.task.PostTask; import org.chromium.base.task.PostTask;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeFeatureList; import org.chromium.chrome.browser.ChromeFeatureList;
import org.chromium.chrome.browser.favicon.LargeIconBridge;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.share.ShareHelper; import org.chromium.chrome.browser.share.ShareHelper;
import org.chromium.chrome.browser.share.ShareParams; import org.chromium.chrome.browser.share.ShareParams;
import org.chromium.content_public.browser.UiThreadTaskTraits; import org.chromium.content_public.browser.UiThreadTaskTraits;
...@@ -138,12 +140,13 @@ public class ContextMenuHelper implements OnCreateContextMenuListener { ...@@ -138,12 +140,13 @@ public class ContextMenuHelper implements OnCreateContextMenuListener {
mOnMenuShown, mOnMenuClosed); mOnMenuShown, mOnMenuClosed);
// TODO(sinansahin): This could be pushed in to the menuController. // TODO(sinansahin): This could be pushed in to the menuController.
if (mCurrentContextMenuParams.isImage()) { if (mCurrentContextMenuParams.isImage()) {
getThumbnail(menuController, new Callback<Bitmap>() { getThumbnail(menuController, menuController::onImageThumbnailRetrieved);
@Override } else {
public void onResult(Bitmap result) { LargeIconBridge iconBridge = new LargeIconBridge(Profile.getLastUsedProfile());
menuController.onImageThumbnailRetrieved(result); iconBridge.getLargeIconForUrl(mCurrentContextMenuParams.getUrl(),
} getActivity().getResources().getDimensionPixelSize(
}); R.dimen.default_favicon_size),
menuController::onFaviconAvailable);
} }
return; return;
} }
......
...@@ -7,16 +7,33 @@ package org.chromium.chrome.browser.contextmenu; ...@@ -7,16 +7,33 @@ package org.chromium.chrome.browser.contextmenu;
import android.app.Activity; import android.app.Activity;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.support.annotation.ColorInt;
import android.support.annotation.IntDef; import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.text.SpannableString;
import android.text.TextUtils;
import android.util.Pair; import android.util.Pair;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.AdapterView; import android.widget.AdapterView;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.Callback; import org.chromium.base.Callback;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeBaseAppCompatActivity;
import org.chromium.chrome.browser.favicon.IconType;
import org.chromium.chrome.browser.night_mode.GlobalNightModeStateProviderHolder;
import org.chromium.chrome.browser.omnibox.OmniboxUrlEmphasizer;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.widget.ContextMenuDialog; import org.chromium.chrome.browser.widget.ContextMenuDialog;
import org.chromium.components.security_state.ConnectionSecurityLevel;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
...@@ -44,13 +61,14 @@ public class RevampedContextMenuController ...@@ -44,13 +61,14 @@ public class RevampedContextMenuController
private RevampedContextMenuListAdapter mListAdapter; private RevampedContextMenuListAdapter mListAdapter;
private Callback<Integer> mItemClickCallback; private Callback<Integer> mItemClickCallback;
private float mTopContentOffsetPx; private float mTopContentOffsetPx;
private String mPlainUrl;
/** /**
* Constructor that also sets the content offset. * Constructor that also sets the content offset.
* *
* @param topContentOffsetPx content offset from the top. * @param topContentOffsetPx content offset from the top.
*/ */
public RevampedContextMenuController(float topContentOffsetPx) { RevampedContextMenuController(float topContentOffsetPx) {
mTopContentOffsetPx = topContentOffsetPx; mTopContentOffsetPx = topContentOffsetPx;
} }
...@@ -59,15 +77,16 @@ public class RevampedContextMenuController ...@@ -59,15 +77,16 @@ public class RevampedContextMenuController
List<Pair<Integer, List<ContextMenuItem>>> items, Callback<Integer> onItemClicked, List<Pair<Integer, List<ContextMenuItem>>> items, Callback<Integer> onItemClicked,
final Runnable onMenuShown, final Runnable onMenuClosed) { final Runnable onMenuShown, final Runnable onMenuClosed) {
mItemClickCallback = onItemClicked; mItemClickCallback = onItemClicked;
float density = Resources.getSystem().getDisplayMetrics().density; final Resources resources = activity.getResources();
final float density = resources.getDisplayMetrics().density;
final float touchPointXPx = params.getTriggeringTouchXDp() * density; final float touchPointXPx = params.getTriggeringTouchXDp() * density;
final float touchPointYPx = params.getTriggeringTouchYDp() * density; final float touchPointYPx = params.getTriggeringTouchYDp() * density;
final View view = final View view =
LayoutInflater.from(activity).inflate(R.layout.revamped_context_menu, null); LayoutInflater.from(activity).inflate(R.layout.revamped_context_menu, null);
mContextMenuDialog = createContextMenuDialog(activity, view, touchPointXPx, touchPointYPx); mContextMenuDialog = createContextMenuDialog(activity, view, touchPointXPx, touchPointYPx);
mContextMenuDialog.setOnShowListener(dialogInterface -> { onMenuShown.run(); }); mContextMenuDialog.setOnShowListener(dialogInterface -> onMenuShown.run());
mContextMenuDialog.setOnDismissListener(dialogInterface -> { onMenuClosed.run(); }); mContextMenuDialog.setOnDismissListener(dialogInterface -> onMenuClosed.run());
// Integer here indicates if the item is the header, a divider, or a row (selectable option) // Integer here indicates if the item is the header, a divider, or a row (selectable option)
List<Pair<Integer, ContextMenuItem>> itemList = new ArrayList<>(); List<Pair<Integer, ContextMenuItem>> itemList = new ArrayList<>();
...@@ -82,9 +101,32 @@ public class RevampedContextMenuController ...@@ -82,9 +101,32 @@ public class RevampedContextMenuController
} }
} }
mListAdapter = new RevampedContextMenuListAdapter(itemList); String headerTitle = params.getTitleText();
mListAdapter.setHeaderTitle(params.getTitleText()); if (TextUtils.isEmpty(headerTitle)) {
mListAdapter.setHeaderUrl(params.getLinkUrl()); headerTitle = params.getLinkText();
}
mPlainUrl = params.getUrl();
CharSequence url = params.getUrl();
if (!TextUtils.isEmpty(url)) {
boolean useDarkColors =
!GlobalNightModeStateProviderHolder.getInstance().isInNightMode();
if (activity instanceof ChromeBaseAppCompatActivity) {
useDarkColors = !((ChromeBaseAppCompatActivity) activity)
.getNightModeStateProvider()
.isInNightMode();
}
SpannableString spannableUrl =
new SpannableString(ChromeContextMenuPopulator.createUrlText(params));
OmniboxUrlEmphasizer.emphasizeUrl(spannableUrl, resources, Profile.getLastUsedProfile(),
ConnectionSecurityLevel.NONE, false, useDarkColors, false);
url = spannableUrl;
}
mListAdapter = new RevampedContextMenuListAdapter(activity, itemList);
mListAdapter.setHeaderTitle(headerTitle);
mListAdapter.setHeaderUrl(url);
RevampedContextMenuListView listView = view.findViewById(R.id.context_menu_list_view); RevampedContextMenuListView listView = view.findViewById(R.id.context_menu_list_view);
listView.setAdapter(mListAdapter); listView.setAdapter(mListAdapter);
listView.setOnItemClickListener(this); listView.setOnItemClickListener(this);
...@@ -113,17 +155,56 @@ public class RevampedContextMenuController ...@@ -113,17 +155,56 @@ public class RevampedContextMenuController
return dialog; return dialog;
} }
/**
* This adds a checkerboard style background to the image.
* It is useful for the transparent PNGs.
* @return The given image with the checkerboard pattern in the background.
*/
static Bitmap getImageWithCheckerBackground(Resources res, Bitmap image) {
// 1. Create a bitmap for the checkerboard pattern.
Drawable drawable = ApiCompatibilityUtils.getDrawable(
res, org.chromium.chrome.R.drawable.checkerboard_background);
Bitmap tileBitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas tileCanvas = new Canvas(tileBitmap);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
drawable.draw(tileCanvas);
// 2. Create a BitmapDrawable using the checkerboard pattern bitmap.
BitmapDrawable bitmapDrawable = new BitmapDrawable(res, tileBitmap);
bitmapDrawable.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
bitmapDrawable.setBounds(0, 0, image.getWidth(), image.getHeight());
// 3. Create a bitmap-backed canvas for the final image.
Bitmap bitmap =
Bitmap.createBitmap(image.getWidth(), image.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
// 4. Paint the checkerboard background into the final canvas
bitmapDrawable.draw(canvas);
// 5. Draw the image on top.
Paint paint = new Paint();
paint.setAntiAlias(true);
canvas.drawBitmap(image, new Matrix(), paint);
return bitmap;
}
/** /**
* This is called when the thumbnail is fetched and ready to display. * This is called when the thumbnail is fetched and ready to display.
* @param thumbnail The bitmap received that will be displayed as the header image. * @param thumbnail The bitmap received that will be displayed as the header image.
*/ */
public void onImageThumbnailRetrieved(Bitmap thumbnail) { void onImageThumbnailRetrieved(Bitmap thumbnail) {
if (thumbnail != null) { mListAdapter.onImageThumbnailRetrieved(thumbnail);
mListAdapter.getHeaderImage().setImageBitmap(thumbnail); }
} else {
// TODO(sinansahin): Handle the failed image loads differently. /**
mListAdapter.getHeaderImage().setImageResource(R.drawable.sad_tab); * See {@link org.chromium.chrome.browser.favicon.LargeIconBridge#getLargeIconForUrl}
} */
void onFaviconAvailable(@Nullable Bitmap icon, @ColorInt int fallbackColor,
boolean isColorDefault, @IconType int iconType) {
mListAdapter.onFaviconAvailable(icon, fallbackColor, mPlainUrl);
} }
@Override @Override
......
...@@ -4,16 +4,22 @@ ...@@ -4,16 +4,22 @@
package org.chromium.chrome.browser.contextmenu; package org.chromium.chrome.browser.contextmenu;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.support.annotation.ColorInt;
import android.support.annotation.Nullable;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Pair; import android.util.Pair;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.BaseAdapter; import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.widget.RoundedIconGenerator;
import org.chromium.ui.widget.RoundedCornerImageView;
import java.util.List; import java.util.List;
...@@ -23,10 +29,17 @@ class RevampedContextMenuListAdapter extends BaseAdapter { ...@@ -23,10 +29,17 @@ class RevampedContextMenuListAdapter extends BaseAdapter {
private final List<Pair<Integer, ContextMenuItem>> mMenuItems; private final List<Pair<Integer, ContextMenuItem>> mMenuItems;
private String mHeaderTitle; private String mHeaderTitle;
private String mHeaderUrl; private CharSequence mHeaderUrl;
private ImageView mHeaderImage; private RoundedCornerImageView mHeaderImage;
private Bitmap mHeaderBitmap;
public RevampedContextMenuListAdapter(List<Pair<Integer, ContextMenuItem>> menuItems) { private boolean mIsHeaderImageThumbnail;
private Context mContext;
public RevampedContextMenuListAdapter(
Context context, List<Pair<Integer, ContextMenuItem>> menuItems) {
mContext = context;
mMenuItems = menuItems; mMenuItems = menuItems;
} }
...@@ -70,6 +83,12 @@ class RevampedContextMenuListAdapter extends BaseAdapter { ...@@ -70,6 +83,12 @@ class RevampedContextMenuListAdapter extends BaseAdapter {
mHeaderImage = convertView.findViewById(R.id.menu_header_image); mHeaderImage = convertView.findViewById(R.id.menu_header_image);
// We should've cached the bitmap if the header wasn't ready when we received a
// thumbnail, favicon, or monogram. We can set the image to the cached bitmap here.
if (mHeaderBitmap != null) {
setHeaderImage(mHeaderBitmap);
}
TextView titleText = convertView.findViewById(R.id.menu_header_title); TextView titleText = convertView.findViewById(R.id.menu_header_title);
titleText.setText(mHeaderTitle); titleText.setText(mHeaderTitle);
...@@ -98,8 +117,7 @@ class RevampedContextMenuListAdapter extends BaseAdapter { ...@@ -98,8 +117,7 @@ class RevampedContextMenuListAdapter extends BaseAdapter {
} }
} else if (getItem(position).first } else if (getItem(position).first
== RevampedContextMenuController.ListItemType.DIVIDER) { == RevampedContextMenuController.ListItemType.DIVIDER) {
// TODO(sinansahin): divider_preference can be renamed to horizontal_divider layout = R.layout.context_menu_divider;
layout = R.layout.divider_preference;
convertView = LayoutInflater.from(parent.getContext()).inflate(layout, null); convertView = LayoutInflater.from(parent.getContext()).inflate(layout, null);
} else { } else {
layout = R.layout.revamped_context_menu_row; layout = R.layout.revamped_context_menu_row;
...@@ -117,12 +135,72 @@ class RevampedContextMenuListAdapter extends BaseAdapter { ...@@ -117,12 +135,72 @@ class RevampedContextMenuListAdapter extends BaseAdapter {
mHeaderTitle = headerTitle; mHeaderTitle = headerTitle;
} }
public void setHeaderUrl(String headerUrl) { public void setHeaderUrl(CharSequence headerUrl) {
mHeaderUrl = headerUrl; mHeaderUrl = headerUrl;
} }
public ImageView getHeaderImage() { /**
return mHeaderImage; * This is called when the thumbnail is fetched and ready to display.
* @param thumbnail The bitmap received that will be displayed as the header image.
*/
void onImageThumbnailRetrieved(Bitmap thumbnail) {
mIsHeaderImageThumbnail = true;
if (thumbnail != null) {
setHeaderImage(RevampedContextMenuController.getImageWithCheckerBackground(
mContext.getResources(), thumbnail));
}
// TODO(sinansahin): Handle the case where the retrieval of the thumbnail fails.
}
void onFaviconAvailable(@Nullable Bitmap icon, @ColorInt int fallbackColor, String iconUrl) {
// If we didn't get a favicon, generate a monogram instead
if (icon == null) {
RoundedIconGenerator iconGenerator = createRoundedIconGenerator(fallbackColor);
icon = iconGenerator.generateIconForUrl(iconUrl);
}
mIsHeaderImageThumbnail = false;
final int size = mContext.getResources().getDimensionPixelSize(
R.dimen.revamped_context_menu_header_monogram_size);
if (icon.getWidth() > size) {
icon = Bitmap.createScaledBitmap(icon, size, size, true);
}
setHeaderImage(icon);
}
private void setHeaderImage(Bitmap bitmap) {
// If the HeaderImage hasn't been inflated by the time we receive the bitmap,
// cache the bitmap received to be set in #getView later.
if (mHeaderImage == null) {
mHeaderBitmap = bitmap;
return;
}
// Clear the loading background color now that we have the real image.
mHeaderImage.setRoundedFillColor(android.R.color.transparent);
if (mIsHeaderImageThumbnail) {
mHeaderImage.setImageBitmap(bitmap);
return;
}
((View) mHeaderImage.getParent())
.findViewById(R.id.circle_background)
.setVisibility(View.VISIBLE);
mHeaderImage.setImageBitmap(bitmap);
}
private RoundedIconGenerator createRoundedIconGenerator(@ColorInt int iconColor) {
final Resources resources = mContext.getResources();
final int iconSize =
resources.getDimensionPixelSize(R.dimen.revamped_context_menu_header_monogram_size);
final int cornerRadius = iconSize / 2;
final int textSize = resources.getDimensionPixelSize(
R.dimen.revamped_context_menu_header_monogram_text_size);
return new RoundedIconGenerator(iconSize, iconSize, cornerRadius, iconColor, textSize);
} }
@Override @Override
......
...@@ -34,7 +34,8 @@ public class RevampedContextMenuListView extends ListView { ...@@ -34,7 +34,8 @@ public class RevampedContextMenuListView extends ListView {
private int calculateWidth() { private int calculateWidth() {
int windowWidthPx = getResources().getDisplayMetrics().widthPixels; int windowWidthPx = getResources().getDisplayMetrics().widthPixels;
int maxWidth = getResources().getDimensionPixelSize(R.dimen.context_menu_max_width); int maxWidth = getResources().getDimensionPixelSize(R.dimen.context_menu_max_width);
int lateralMargin = getResources().getDimensionPixelSize(R.dimen.context_menu_min_padding); int lateralMargin =
getResources().getDimensionPixelSize(R.dimen.revamped_context_menu_lateral_margin);
return Math.min(maxWidth, windowWidthPx - 2 * lateralMargin); return Math.min(maxWidth, windowWidthPx - 2 * lateralMargin);
} }
......
...@@ -18,6 +18,7 @@ import android.graphics.drawable.ColorDrawable; ...@@ -18,6 +18,7 @@ import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.graphics.drawable.shapes.RoundRectShape; import android.graphics.drawable.shapes.RoundRectShape;
import android.graphics.drawable.shapes.Shape; import android.graphics.drawable.shapes.Shape;
import android.support.annotation.ColorRes;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewCompat;
import android.util.AttributeSet; import android.util.AttributeSet;
...@@ -122,6 +123,15 @@ public class RoundedCornerImageView extends ImageView { ...@@ -122,6 +123,15 @@ public class RoundedCornerImageView extends ImageView {
updateApplyShader(); updateApplyShader();
} }
/**
* Set the fill color resource.
* @param id The color resource id.
*/
public void setRoundedFillColor(@ColorRes int id) {
mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mFillPaint.setColor(getContext().getResources().getColor(id));
}
protected void maybeCreateShader() { protected void maybeCreateShader() {
// Only create the shader if we have a rectangle to use as a mask. // Only create the shader if we have a rectangle to use as a mask.
Drawable drawable = getDrawable(); Drawable drawable = getDrawable();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment