Commit 5589d019 authored by Sophey Dong's avatar Sophey Dong Committed by Commit Bot

[SharingHub] Add icons to preview.

Also make some UI adjustments to match specs.
Screenshots: http://dr/corp/drive/folders/1ga0FKZujvJKfSy3SG7zQ5ZwYxOSYZBNx

Bug: 1120093
Change-Id: Ic464538f1e99654ae5018bd0d51c8025fb1fd69a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2417463
Commit-Queue: Sophey Dong <sophey@chromium.org>
Reviewed-by: default avatarTanya Gupta <tgupta@chromium.org>
Reviewed-by: default avatarKyle Milka <kmilka@chromium.org>
Reviewed-by: default avatarMatthew Jones <mdjones@chromium.org>
Cr-Commit-Position: refs/heads/master@{#809551}
parent edebee9e
......@@ -28,13 +28,13 @@
<org.chromium.components.browser_ui.widget.RoundedCornerImageView
android:id="@+id/image_preview"
android:layout_height="@dimen/sharing_hub_preview_monogram_size"
android:layout_width="@dimen/sharing_hub_preview_monogram_size"
android:scaleType="fitCenter"
app:cornerRadiusBottomStart="@dimen/default_rounded_corner_radius"
app:cornerRadiusBottomEnd="@dimen/default_rounded_corner_radius"
app:cornerRadiusTopStart="@dimen/default_rounded_corner_radius"
app:cornerRadiusTopEnd="@dimen/default_rounded_corner_radius"
android:layout_height="@dimen/sharing_hub_preview_icon_size"
android:layout_width="@dimen/sharing_hub_preview_icon_size"
android:background="@drawable/preview_icon_border_background"
app:cornerRadiusBottomStart="@dimen/sharing_hub_preview_icon_rounded_corner_radius"
app:cornerRadiusBottomEnd="@dimen/sharing_hub_preview_icon_rounded_corner_radius"
app:cornerRadiusTopStart="@dimen/sharing_hub_preview_icon_rounded_corner_radius"
app:cornerRadiusTopEnd="@dimen/sharing_hub_preview_icon_rounded_corner_radius"
tools:ignore="ContentDescription"/>
<TextView
......
......@@ -594,6 +594,8 @@
<dimen name="overflow_menu_update_padding">12dp</dimen>
<!-- Sharing Hub dimensions -->
<dimen name="sharing_hub_preview_monogram_text_size">24dp</dimen>
<dimen name="sharing_hub_preview_monogram_size">48dp</dimen>
<dimen name="sharing_hub_preview_icon_padding">12dp</dimen>
<dimen name="sharing_hub_preview_icon_rounded_corner_radius">4dp</dimen>
<dimen name="sharing_hub_preview_icon_size">48dp</dimen>
<dimen name="sharing_hub_preview_inner_icon_size">24dp</dimen>
</resources>
......@@ -9,10 +9,14 @@ android_resources("java_resources") {
"java/res/drawable/camera_img.xml",
"java/res/drawable/delete.xml",
"java/res/drawable/edit.xml",
"java/res/drawable/generic_favicon.xml",
"java/res/drawable/generic_file.xml",
"java/res/drawable/link.xml",
"java/res/drawable/preview_icon_border_background.xml",
"java/res/drawable/qrcode_background.xml",
"java/res/drawable/save.xml",
"java/res/drawable/share.xml",
"java/res/drawable/text.xml",
"java/res/layout/qrcode_camera_error_layout.xml",
"java/res/layout/qrcode_dialog.xml",
"java/res/layout/qrcode_open_settings_layout.xml",
......
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2020 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. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<group>
<clip-path
android:pathData="M12,2C17.52,2 22,6.48 22,12C22,17.52 17.52,22 12,22C6.48,22 2,17.52 2,12C2,6.48 6.48,2 12,2ZM12,4C7.5846,4 4,7.5846 4,12L4,12L8.3997,12C11.807,12.0217 13.3215,13.7307 12.9433,17.1271L12.9433,17.1271L9.4878,17.1271L9.4878,19.597C10.278,19.8585 11.1226,20 12,20C16.4154,20 20,16.4154 20,12C20,11.837 19.9951,11.6751 19.9855,11.5144C19.3285,12.5048 18.3333,13 17,13C14.8626,13 13.7939,12.0836 13.7939,10.2507L13.7939,10.2507L10.0457,10.2507C9.7719,7.5224 10.7285,6.1583 12.9156,6.1583C12.9156,5.1831 13.243,4.5615 13.7273,4.1873C13.1709,4.0646 12.593,4 12,4Z"/>
<path
android:pathData="M0,0h24v24h-24z"
android:strokeWidth="1"
android:fillColor="@color/modern_grey_700"
android:fillType="evenOdd"/>
</group>
</vector>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2020 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. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<group>
<clip-path
android:pathData="M14,2L6,2C4.9,2 4.01,2.9 4.01,4L4,20C4,21.1 4.89,22 5.99,22L18,22C19.1,22 20,21.1 20,20L20,8L14,2ZM6,20L6,4L13,4L13,9L18,9L18,20L6,20Z"/>
<path
android:pathData="M0,0h24v24h-24z"
android:strokeWidth="1"
android:fillColor="@color/modern_grey_700"
android:fillType="evenOdd"/>
</group>
</vector>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2020 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. -->
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="@color/modern_white" />
<stroke android:width="1dp" android:color="@color/modern_grey_200"/>
<corners android:radius="@dimen/sharing_hub_preview_icon_rounded_corner_radius" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2020 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. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M2,0L22.5714,0L22.5714,6.8571L20.8571,6.8571C20.4457,4.9714 20.6514,3.4629 20,2.88C19.3657,2.3143 17.7714,1.7143 15.7143,1.7143L14,1.7143L14,20.5714C14,21.9429 14.2057,22.3714 14.6343,22.6971C15.0629,23.0229 15.9886,23.2457 17.4286,23.3486L17.4286,24L7.1086,24L7.1086,23.3486C8.6,23.2286 9.5429,22.9886 9.9029,22.5943C10.28,22.2171 10.5714,22.0971 10.5714,20.5714L10.5714,1.7143L8.8571,1.7143C6.9029,1.7143 5.1714,2.3143 4.4686,2.88C3.7486,3.4457 4.0743,4.9543 3.7143,6.8571L2,6.8571L2,0Z"
android:strokeWidth="1"
android:fillColor="@color/modern_grey_800"
android:fillType="nonZero"/>
</vector>
......@@ -5,8 +5,8 @@
package org.chromium.chrome.browser.share.share_sheet;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.text.TextUtils;
import android.view.LayoutInflater;
......@@ -15,11 +15,13 @@ import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.annotation.ColorInt;
import androidx.annotation.Nullable;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
......@@ -32,7 +34,7 @@ import org.chromium.chrome.browser.ui.favicon.IconType;
import org.chromium.chrome.browser.ui.favicon.LargeIconBridge;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
import org.chromium.components.browser_ui.share.ShareParams;
import org.chromium.components.browser_ui.widget.RoundedIconGenerator;
import org.chromium.components.browser_ui.widget.RoundedCornerImageView;
import org.chromium.components.url_formatter.UrlFormatter;
import org.chromium.ui.modelutil.LayoutViewBuilder;
import org.chromium.ui.modelutil.MVCListAdapter.ListItem;
......@@ -89,10 +91,12 @@ class ShareSheetBottomSheetContent implements BottomSheetContent, OnItemClickLis
* @param firstPartyModels The PropertyModels used to build the top row.
* @param thirdPartyModels The PropertyModels used to build the bottom row.
* @param contentTypes The {@link Set} of {@link ContentType}s to build the preview.
* @param fileContentType The MIME type of the file(s) being shared.
*/
void createRecyclerViews(List<PropertyModel> firstPartyModels,
List<PropertyModel> thirdPartyModels, Set<Integer> contentTypes) {
createPreview(contentTypes);
List<PropertyModel> thirdPartyModels, Set<Integer> contentTypes,
String fileContentType) {
createPreview(contentTypes, fileContentType);
createFirstPartyRecyclerViews(firstPartyModels);
RecyclerView thirdParty = this.getContentView().findViewById(R.id.share_sheet_other_apps);
......@@ -142,7 +146,7 @@ class ShareSheetBottomSheetContent implements BottomSheetContent, OnItemClickLis
}
}
private void createPreview(Set<Integer> contentTypes) {
private void createPreview(Set<Integer> contentTypes, String fileContentType) {
// Default preview is to show title + url.
String title = mParams.getTitle();
String subtitle =
......@@ -157,15 +161,18 @@ class ShareSheetBottomSheetContent implements BottomSheetContent, OnItemClickLis
if (contentTypes.contains(ContentType.IMAGE)) {
setImageForPreviewFromUri(mParams.getFileUris().get(0));
if (TextUtils.isEmpty(subtitle)) {
subtitle = mContext.getResources().getString(
R.string.sharing_hub_image_preview_subtitle);
subtitle = getFileType(fileContentType);
}
} else if (contentTypes.contains(ContentType.OTHER_FILE_TYPE)) {
// TODO(1120093): Set file icon.
setDefaultIconForPreview(
AppCompatResources.getDrawable(mContext, R.drawable.generic_file));
if (TextUtils.isEmpty(subtitle)) {
subtitle = getFileType(fileContentType);
}
} else if (contentTypes.size() == 1
&& (contentTypes.contains(ContentType.HIGHLIGHTED_TEXT)
|| contentTypes.contains(ContentType.TEXT))) {
// TODO(1120093): Set text monogram icon.
setDefaultIconForPreview(AppCompatResources.getDrawable(mContext, R.drawable.text));
title = "";
subtitle = mParams.getText();
setSubtitleMaxLines(2);
......@@ -186,8 +193,14 @@ class ShareSheetBottomSheetContent implements BottomSheetContent, OnItemClickLis
private void setImageForPreviewFromUri(Uri imageUri) {
try {
setImagePreview(
ApiCompatibilityUtils.getBitmapByUri(mContext.getContentResolver(), imageUri));
Bitmap bitmap =
ApiCompatibilityUtils.getBitmapByUri(mContext.getContentResolver(), imageUri);
RoundedCornerImageView imageView =
this.getContentView().findViewById(R.id.image_preview);
imageView.setImageBitmap(bitmap);
imageView.setRoundedFillColor(ApiCompatibilityUtils.getColor(
mContext.getResources(), R.color.default_icon_color));
imageView.setScaleType(ScaleType.FIT_CENTER);
} catch (IOException e) {
// If no image preview available, don't show a preview.
}
......@@ -215,9 +228,17 @@ class ShareSheetBottomSheetContent implements BottomSheetContent, OnItemClickLis
subtitleView.setMaxLines(maxLines);
}
private void setImagePreview(Bitmap icon) {
private void setDefaultIconForPreview(Drawable drawable) {
ImageView imageView = this.getContentView().findViewById(R.id.image_preview);
imageView.setImageBitmap(icon);
imageView.setImageDrawable(drawable);
centerIcon(imageView);
}
private void centerIcon(ImageView imageView) {
imageView.setScaleType(ScaleType.FIT_XY);
int padding = mContext.getResources().getDimensionPixelSize(
R.dimen.sharing_hub_preview_icon_padding);
imageView.setPadding(padding, padding, padding, padding);
}
/**
......@@ -238,32 +259,43 @@ class ShareSheetBottomSheetContent implements BottomSheetContent, OnItemClickLis
*/
private void onFaviconAvailable(@Nullable Bitmap icon, @ColorInt int fallbackColor,
boolean isColorDefault, @IconType int iconType) {
// If we didn't get a favicon, generate a monogram instead
if (icon == null) {
RoundedIconGenerator iconGenerator = createRoundedIconGenerator(fallbackColor);
icon = iconGenerator.generateIconForUrl(mUrl);
// generateIconForUrl might return null if the URL is empty or the domain cannot be
// resolved. See https://crbug.com/987101
// TODO(1120093): Handle the case where generating an icon fails.
// If we didn't get a favicon, use the generic favicon instead.
if (icon == null) {
return;
setDefaultIconForPreview(
AppCompatResources.getDrawable(mContext, R.drawable.generic_favicon));
} else {
int size = mContext.getResources().getDimensionPixelSize(
R.dimen.sharing_hub_preview_inner_icon_size);
Bitmap scaledIcon = Bitmap.createScaledBitmap(icon, size, size, true);
ImageView imageView = this.getContentView().findViewById(R.id.image_preview);
imageView.setImageBitmap(scaledIcon);
centerIcon(imageView);
}
}
int size = mContext.getResources().getDimensionPixelSize(
R.dimen.sharing_hub_preview_monogram_size);
setImagePreview(Bitmap.createScaledBitmap(icon, size, size, true));
private String getFileType(String mimeType) {
if (!mimeType.contains("/")) {
return "";
}
String supertype = mimeType.split("/", 2)[0];
// Accepted MIME types are drawn from
// //chrome/browser/webshare/share_service_impl.cc
switch (supertype) {
case "audio":
return mContext.getResources().getString(
R.string.sharing_hub_audio_preview_subtitle);
case "image":
return mContext.getResources().getString(
R.string.sharing_hub_image_preview_subtitle);
case "text":
return mContext.getResources().getString(
R.string.sharing_hub_text_preview_subtitle);
case "video":
return mContext.getResources().getString(
R.string.sharing_hub_video_preview_subtitle);
default:
return "";
}
private RoundedIconGenerator createRoundedIconGenerator(@ColorInt int iconColor) {
Resources resources = mContext.getResources();
int iconSize = resources.getDimensionPixelSize(R.dimen.sharing_hub_preview_monogram_size);
int cornerRadius = iconSize / 2;
int textSize =
resources.getDimensionPixelSize(R.dimen.sharing_hub_preview_monogram_text_size);
return new RoundedIconGenerator(iconSize, iconSize, cornerRadius, iconColor, textSize);
}
/**
......
......@@ -137,7 +137,8 @@ public class ShareSheetCoordinator implements ActivityStateObserver, ChromeOptio
List<PropertyModel> thirdPartyApps = createThirdPartyPropertyModels(
mActivity, params, mContentTypes, chromeShareExtras.saveLastUsed());
mBottomSheet.createRecyclerViews(firstPartyApps, thirdPartyApps, mContentTypes);
mBottomSheet.createRecyclerViews(
firstPartyApps, thirdPartyApps, mContentTypes, params.getFileContentType());
boolean shown = mBottomSheetController.requestShowContent(mBottomSheet, true);
if (shown) {
......
......@@ -81,29 +81,53 @@ public final class ShareSheetBottomSheetContentTest {
@Test
@MediumTest
public void createRecyclerViews_imageOnlyShare() {
String fileContentType = "image/jpeg";
ShareSheetBottomSheetContent shareSheetBottomSheetContent =
new ShareSheetBottomSheetContent(mActivity, new MockLargeIconBridge(), null,
new ShareParams.Builder(/*window=*/null, /*title=*/"", /*url=*/"")
.setFileUris(new ArrayList<>(ImmutableList.of(sImageUri)))
.setFileContentType(fileContentType)
.build());
shareSheetBottomSheetContent.createRecyclerViews(
ImmutableList.of(), ImmutableList.of(), ImmutableSet.of(ContentType.IMAGE));
shareSheetBottomSheetContent.createRecyclerViews(ImmutableList.of(), ImmutableList.of(),
ImmutableSet.of(ContentType.IMAGE), fileContentType);
TextView titleView =
shareSheetBottomSheetContent.getContentView().findViewById(R.id.title_preview);
TextView subtitleView =
shareSheetBottomSheetContent.getContentView().findViewById(R.id.subtitle_preview);
assertEquals("", titleView.getText());
assertEquals("image", subtitleView.getText());
}
@Test
@MediumTest
public void createRecyclerViews_fileShare() {
String fileContentType = "video/mp4";
ShareSheetBottomSheetContent shareSheetBottomSheetContent =
new ShareSheetBottomSheetContent(mActivity, new MockLargeIconBridge(), null,
new ShareParams.Builder(/*window=*/null, /*title=*/"", /*url=*/"")
.setFileUris(new ArrayList<>(
ImmutableList.of(Uri.parse("content://TestVideo.mp4"))))
.setFileContentType(fileContentType)
.build());
shareSheetBottomSheetContent.createRecyclerViews(ImmutableList.of(), ImmutableList.of(),
ImmutableSet.of(ContentType.IMAGE), fileContentType);
TextView titleView =
shareSheetBottomSheetContent.getContentView().findViewById(R.id.title_preview);
TextView subtitleView =
shareSheetBottomSheetContent.getContentView().findViewById(R.id.subtitle_preview);
assertEquals("", titleView.getText());
assertEquals(mActivity.getString(R.string.sharing_hub_image_preview_subtitle),
subtitleView.getText());
assertEquals("video", subtitleView.getText());
}
@Test
@MediumTest
public void createRecyclerViews_highlightedTextShare() {
mShareSheetBottomSheetContent.createRecyclerViews(ImmutableList.of(), ImmutableList.of(),
ImmutableSet.of(ContentType.HIGHLIGHTED_TEXT));
ImmutableSet.of(ContentType.HIGHLIGHTED_TEXT), "");
TextView titleView =
mShareSheetBottomSheetContent.getContentView().findViewById(R.id.title_preview);
......@@ -117,7 +141,7 @@ public final class ShareSheetBottomSheetContentTest {
@MediumTest
public void createRecyclerViews_textOnlyShare() {
mShareSheetBottomSheetContent.createRecyclerViews(
ImmutableList.of(), ImmutableList.of(), ImmutableSet.of(ContentType.TEXT));
ImmutableList.of(), ImmutableList.of(), ImmutableSet.of(ContentType.TEXT), "");
TextView titleView =
mShareSheetBottomSheetContent.getContentView().findViewById(R.id.title_preview);
......@@ -131,14 +155,14 @@ public final class ShareSheetBottomSheetContentTest {
@MediumTest
public void createRecyclerViews_producesCorrectFavicon() {
mShareSheetBottomSheetContent.createRecyclerViews(ImmutableList.of(), ImmutableList.of(),
ImmutableSet.of(ContentType.LINK_PAGE_VISIBLE));
ImmutableSet.of(ContentType.LINK_PAGE_VISIBLE), "");
ImageView imageView =
mShareSheetBottomSheetContent.getContentView().findViewById(R.id.image_preview);
assertNotNull(imageView.getDrawable());
Bitmap bitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
int size = mActivity.getResources().getDimensionPixelSize(
R.dimen.sharing_hub_preview_monogram_size);
R.dimen.sharing_hub_preview_inner_icon_size);
assertEquals(size, bitmap.getWidth());
assertEquals(size, bitmap.getHeight());
assertEquals(sConfig, bitmap.getConfig());
......@@ -148,7 +172,7 @@ public final class ShareSheetBottomSheetContentTest {
@MediumTest
public void createRecyclerViews_tabShare() {
mShareSheetBottomSheetContent.createRecyclerViews(ImmutableList.of(), ImmutableList.of(),
ImmutableSet.of(ContentType.LINK_PAGE_VISIBLE));
ImmutableSet.of(ContentType.LINK_PAGE_VISIBLE), "");
TextView titleView =
mShareSheetBottomSheetContent.getContentView().findViewById(R.id.title_preview);
......@@ -165,7 +189,7 @@ public final class ShareSheetBottomSheetContentTest {
@MediumTest
public void createRecyclerViews_webShareTextAndUrl() {
mShareSheetBottomSheetContent.createRecyclerViews(ImmutableList.of(), ImmutableList.of(),
ImmutableSet.of(ContentType.LINK_PAGE_NOT_VISIBLE, ContentType.TEXT));
ImmutableSet.of(ContentType.LINK_PAGE_NOT_VISIBLE, ContentType.TEXT), "");
TextView titleView =
mShareSheetBottomSheetContent.getContentView().findViewById(R.id.title_preview);
......@@ -186,7 +210,7 @@ public final class ShareSheetBottomSheetContentTest {
new ShareParams.Builder(/*window=*/null, /*title=*/"", sUrl).build());
shareSheetBottomSheetContent.createRecyclerViews(ImmutableList.of(), ImmutableList.of(),
ImmutableSet.of(ContentType.LINK_PAGE_NOT_VISIBLE));
ImmutableSet.of(ContentType.LINK_PAGE_NOT_VISIBLE), "");
TextView titleView =
shareSheetBottomSheetContent.getContentView().findViewById(R.id.title_preview);
......
......@@ -3784,9 +3784,18 @@ In Incognito, your activity might still be visible to websites that you visit, y
<message name="IDS_SHARING_HUB_OPEN_SETTINGS_LABEL" desc="Label for the open settings button.">
Open Settings
</message>
<message name="IDS_SHARING_HUB_AUDIO_PREVIEW_SUBTITLE" desc="Subtitle shown in the preview of Sharing Hub audio file shares.">
audio
</message>
<message name="IDS_SHARING_HUB_IMAGE_PREVIEW_SUBTITLE" desc="Subtitle shown in the preview of Sharing Hub image shares.">
image
</message>
<message name="IDS_SHARING_HUB_TEXT_PREVIEW_SUBTITLE" desc="Subtitle shown in the preview of Sharing Hub text file shares.">
text
</message>
<message name="IDS_SHARING_HUB_VIDEO_PREVIEW_SUBTITLE" desc="Subtitle shown in the preview of Sharing Hub video file shares.">
video
</message>
<!-- ClickToCall -->
<message name="IDS_CLICK_TO_CALL_NOTIFICATION_TEXT" desc="Text displayed in a click to call notification to call on a number.">
......
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