Commit 89afddf6 authored by Kyle Milka's avatar Kyle Milka Committed by Commit Bot

[SharingHub] Show a preview of the content to be shared

When content is shared in the Sharing Hub v1 include a preview
showing the page favicon, title, and url.

http://screen/704072c1-8ebf-4764-8660-0aa67c95dfa4.png

Bug: 1120093

Change-Id: Iea5b67f063fcf663917badd84bdeacb5942959dd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2367442
Commit-Queue: Kyle Milka <kmilka@chromium.org>
Reviewed-by: default avatarMark Pearson <mpearson@chromium.org>
Reviewed-by: default avatarMatthew Jones <mdjones@chromium.org>
Reviewed-by: default avatarTanya Gupta <tgupta@chromium.org>
Cr-Commit-Position: refs/heads/master@{#805474}
parent 994fc06d
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
<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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:minHeight="@dimen/min_touch_target_size" android:minHeight="@dimen/min_touch_target_size"
...@@ -19,14 +21,73 @@ ...@@ -19,14 +21,73 @@
android:background="@color/default_bg_color_blue" android:background="@color/default_bg_color_blue"
android:textAppearance="@style/TextAppearance.TextSmall.Primary.Inverse" android:textAppearance="@style/TextAppearance.TextSmall.Primary.Inverse"
android:visibility="gone"/> android:visibility="gone"/>
<RelativeLayout
android:id="@+id/preview_header"
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="20dp"
android:paddingStart="16dp"
android:paddingTop="24dp">
<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"
tools:ignore="ContentDescription"/>
<TextView
android:id="@+id/title_preview"
android:ellipsize="end"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toEndOf="@id/image_preview"
android:maxLines="1"
android:minHeight="18dp"
android:paddingEnd="16dp"
android:paddingStart="12dp"
android:gravity="center_vertical"
android:textAlignment="viewStart"
android:textAppearance="@style/TextAppearance.TextMediumThick.Primary"/>
<TextView
android:id="@+id/url_preview"
android:ellipsize="end"
android:layout_below="@id/title_preview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toEndOf="@id/image_preview"
android:maxLines="1"
android:minHeight="18dp"
android:paddingEnd="16dp"
android:paddingStart="12dp"
android:paddingTop="4dp"
android:gravity="center_vertical"
android:textAlignment="viewStart"
android:textAppearance="@style/TextAppearance.TextMedium.Secondary"/>
</RelativeLayout>
<View
android:id="@+id/preview_divider"
android:background="@color/divider_line_bg_color"
android:layout_height="1dp"
android:layout_width="match_parent"/>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/share_sheet_chrome_apps" android:id="@+id/share_sheet_other_apps"
android:clipToPadding="false" android:clipToPadding="false"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="113dp" android:layout_height="113dp"
android:minHeight="@dimen/min_touch_target_size" android:minHeight="@dimen/min_touch_target_size"
android:paddingEnd="16dp" android:orientation="horizontal"
android:visibility="gone" /> android:paddingEnd="16dp" />
<View <View
android:id="@+id/share_sheet_divider" android:id="@+id/share_sheet_divider"
android:background="@color/divider_line_bg_color" android:background="@color/divider_line_bg_color"
...@@ -35,12 +96,13 @@ ...@@ -35,12 +96,13 @@
android:layout_marginRight="16dp" android:layout_marginRight="16dp"
android:layout_width="match_parent" android:layout_width="match_parent"
android:visibility="gone" /> android:visibility="gone" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/share_sheet_other_apps" android:id="@+id/share_sheet_chrome_apps"
android:clipToPadding="false" android:clipToPadding="false"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="113dp" android:layout_height="113dp"
android:minHeight="@dimen/min_touch_target_size" android:minHeight="@dimen/min_touch_target_size"
android:orientation="horizontal" android:paddingEnd="16dp"
android:paddingEnd="16dp" /> android:visibility="gone" />
</LinearLayout> </LinearLayout>
...@@ -602,4 +602,7 @@ ...@@ -602,4 +602,7 @@
<dimen name="overflow_menu_update_min_height">40sp</dimen> <dimen name="overflow_menu_update_min_height">40sp</dimen>
<dimen name="overflow_menu_update_padding">12dp</dimen> <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>
</resources> </resources>
...@@ -21,11 +21,13 @@ import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher; ...@@ -21,11 +21,13 @@ import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
import org.chromium.chrome.browser.offlinepages.OfflinePageUtils; import org.chromium.chrome.browser.offlinepages.OfflinePageUtils;
import org.chromium.chrome.browser.printing.PrintShareActivity; import org.chromium.chrome.browser.printing.PrintShareActivity;
import org.chromium.chrome.browser.printing.TabPrinter; import org.chromium.chrome.browser.printing.TabPrinter;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.send_tab_to_self.SendTabToSelfShareActivity; import org.chromium.chrome.browser.send_tab_to_self.SendTabToSelfShareActivity;
import org.chromium.chrome.browser.share.share_sheet.ShareSheetCoordinator; import org.chromium.chrome.browser.share.share_sheet.ShareSheetCoordinator;
import org.chromium.chrome.browser.share.share_sheet.ShareSheetPropertyModelBuilder; import org.chromium.chrome.browser.share.share_sheet.ShareSheetPropertyModelBuilder;
import org.chromium.chrome.browser.tab.SadTab; import org.chromium.chrome.browser.tab.SadTab;
import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.ui.favicon.LargeIconBridge;
import org.chromium.chrome.browser.util.ChromeFileProvider; import org.chromium.chrome.browser.util.ChromeFileProvider;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController; import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
import org.chromium.components.browser_ui.share.ShareImageFileUtils; import org.chromium.components.browser_ui.share.ShareImageFileUtils;
...@@ -300,11 +302,11 @@ public class ShareDelegateImpl implements ShareDelegate { ...@@ -300,11 +302,11 @@ public class ShareDelegateImpl implements ShareDelegate {
} else if (sharingHubEnabled && !chromeShareExtras.sharingTabGroup()) { } else if (sharingHubEnabled && !chromeShareExtras.sharingTabGroup()) {
// TODO(crbug.com/1085078): Sharing hub is suppressed for tab group sharing. // TODO(crbug.com/1085078): Sharing hub is suppressed for tab group sharing.
// Re-enable it when tab group sharing is supported by sharing hub. // Re-enable it when tab group sharing is supported by sharing hub.
ShareSheetCoordinator coordinator = ShareSheetCoordinator coordinator = new ShareSheetCoordinator(controller,
new ShareSheetCoordinator(controller, lifecycleDispatcher, tabProvider, lifecycleDispatcher, tabProvider,
new ShareSheetPropertyModelBuilder(controller, new ShareSheetPropertyModelBuilder(controller,
ContextUtils.getApplicationContext().getPackageManager()), ContextUtils.getApplicationContext().getPackageManager()),
printCallback); printCallback, new LargeIconBridge(Profile.getLastUsedRegularProfile()));
// TODO(crbug/1009124): open custom share sheet. // TODO(crbug/1009124): open custom share sheet.
coordinator.showShareSheet(params, chromeShareExtras, shareStartTime); coordinator.showShareSheet(params, chromeShareExtras, shareStartTime);
} else { } else {
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package org.chromium.chrome.browser.share.share_sheet; package org.chromium.chrome.browser.share.share_sheet;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
...@@ -19,6 +20,7 @@ import androidx.recyclerview.widget.RecyclerView; ...@@ -19,6 +20,7 @@ import androidx.recyclerview.widget.RecyclerView;
import org.chromium.base.metrics.RecordUserAction; import org.chromium.base.metrics.RecordUserAction;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent; import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
import org.chromium.components.browser_ui.share.ShareParams;
import org.chromium.ui.modelutil.LayoutViewBuilder; import org.chromium.ui.modelutil.LayoutViewBuilder;
import org.chromium.ui.modelutil.MVCListAdapter.ListItem; import org.chromium.ui.modelutil.MVCListAdapter.ListItem;
import org.chromium.ui.modelutil.MVCListAdapter.ModelList; import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
...@@ -37,6 +39,7 @@ class ShareSheetBottomSheetContent implements BottomSheetContent, OnItemClickLis ...@@ -37,6 +39,7 @@ class ShareSheetBottomSheetContent implements BottomSheetContent, OnItemClickLis
private final ShareSheetCoordinator mShareSheetCoordinator; private final ShareSheetCoordinator mShareSheetCoordinator;
private ViewGroup mToolbarView; private ViewGroup mToolbarView;
private ViewGroup mContentView; private ViewGroup mContentView;
private ShareParams mParams;
/** /**
* Creates a ShareSheetBottomSheetContent (custom share sheet) opened from the given activity. * Creates a ShareSheetBottomSheetContent (custom share sheet) opened from the given activity.
...@@ -44,9 +47,11 @@ class ShareSheetBottomSheetContent implements BottomSheetContent, OnItemClickLis ...@@ -44,9 +47,11 @@ class ShareSheetBottomSheetContent implements BottomSheetContent, OnItemClickLis
* @param context The context the share sheet was launched from. * @param context The context the share sheet was launched from.
* @param shareSheetCoordinator The Cooredinator that instatiated this BottomSheetContent. * @param shareSheetCoordinator The Cooredinator that instatiated this BottomSheetContent.
*/ */
ShareSheetBottomSheetContent(Context context, ShareSheetCoordinator shareSheetCoordinator) { ShareSheetBottomSheetContent(
Context context, ShareSheetCoordinator shareSheetCoordinator, ShareParams params) {
mContext = context; mContext = context;
mShareSheetCoordinator = shareSheetCoordinator; mShareSheetCoordinator = shareSheetCoordinator;
mParams = params;
createContentView(); createContentView();
} }
...@@ -59,37 +64,47 @@ class ShareSheetBottomSheetContent implements BottomSheetContent, OnItemClickLis ...@@ -59,37 +64,47 @@ class ShareSheetBottomSheetContent implements BottomSheetContent, OnItemClickLis
* Creates a new share sheet view with two rows based on the provided PropertyModels. * Creates a new share sheet view with two rows based on the provided PropertyModels.
* *
* @param activity The activity the share sheet belongs to. * @param activity The activity the share sheet belongs to.
* @param topRowModels The PropertyModels used to build the top row. * @param firstPartyModels The PropertyModels used to build the top row.
* @param bottomRowModels The PropertyModels used to build the bottom row. * @param thirdPartyModels The PropertyModels used to build the bottom row.
* @param message The message to show on top of the share sheet. * @param message The message to show on top of the share sheet.
*/ */
void createRecyclerViews( void createRecyclerViews(List<PropertyModel> firstPartyModels,
List<PropertyModel> topRowModels, List<PropertyModel> bottomRowModels, String message) { List<PropertyModel> thirdPartyModels, String message) {
// A success/failure message can be shown for features such as LinkToText.
if (!message.isEmpty()) { if (!message.isEmpty()) {
TextView messageView = this.getContentView().findViewById(R.id.message); TextView messageView = this.getContentView().findViewById(R.id.message);
messageView.setVisibility(View.VISIBLE); messageView.setVisibility(View.VISIBLE);
messageView.setText(message); messageView.setText(message);
View preview = this.getContentView().findViewById(R.id.preview_header);
preview.setVisibility(View.GONE);
}
// If there's no message to be shown, show a preview of the content to be shared.
else {
TextView titleView = this.getContentView().findViewById(R.id.title_preview);
titleView.setText(mParams.getTitle());
TextView urlView = this.getContentView().findViewById(R.id.url_preview);
urlView.setText(mParams.getUrl());
} }
createChromeFeatureRecyclerViews(topRowModels); createFirstPartyRecyclerViews(firstPartyModels);
RecyclerView bottomRow = this.getContentView().findViewById(R.id.share_sheet_other_apps); RecyclerView thirdParty = this.getContentView().findViewById(R.id.share_sheet_other_apps);
populateView( populateView(
bottomRowModels, this.getContentView().findViewById(R.id.share_sheet_other_apps)); thirdPartyModels, this.getContentView().findViewById(R.id.share_sheet_other_apps));
bottomRow.addOnScrollListener( thirdParty.addOnScrollListener(
new ScrollEventReporter("SharingHubAndroid.BottomRowScrolled")); new ScrollEventReporter("SharingHubAndroid.ThirdPartyAppsScrolled"));
} }
void createChromeFeatureRecyclerViews(List<PropertyModel> chromeFeatureModels) { void createFirstPartyRecyclerViews(List<PropertyModel> firstPartyModels) {
RecyclerView chromeFeatureRow = RecyclerView firstPartyRow =
this.getContentView().findViewById(R.id.share_sheet_chrome_apps); this.getContentView().findViewById(R.id.share_sheet_chrome_apps);
if (chromeFeatureModels != null && chromeFeatureModels.size() > 0) { if (firstPartyModels != null && firstPartyModels.size() > 0) {
View divider = this.getContentView().findViewById(R.id.share_sheet_divider); View divider = this.getContentView().findViewById(R.id.share_sheet_divider);
divider.setVisibility(View.VISIBLE); divider.setVisibility(View.VISIBLE);
chromeFeatureRow.setVisibility(View.VISIBLE); firstPartyRow.setVisibility(View.VISIBLE);
populateView(chromeFeatureModels, chromeFeatureRow); populateView(firstPartyModels, firstPartyRow);
chromeFeatureRow.addOnScrollListener( firstPartyRow.addOnScrollListener(
new ScrollEventReporter("SharingHubAndroid.TopRowScrolled")); new ScrollEventReporter("SharingHubAndroid.FirstPartyAppsScrolled"));
} }
} }
...@@ -120,6 +135,11 @@ class ShareSheetBottomSheetContent implements BottomSheetContent, OnItemClickLis ...@@ -120,6 +135,11 @@ class ShareSheetBottomSheetContent implements BottomSheetContent, OnItemClickLis
} }
} }
void setFaviconForPreview(Bitmap icon) {
ImageView imageView = this.getContentView().findViewById(R.id.image_preview);
imageView.setImageBitmap(icon);
}
/** /**
* One-shot reporter that records the first time the user scrolls a {@link RecyclerView}. * One-shot reporter that records the first time the user scrolls a {@link RecyclerView}.
*/ */
...@@ -146,11 +166,11 @@ class ShareSheetBottomSheetContent implements BottomSheetContent, OnItemClickLis ...@@ -146,11 +166,11 @@ class ShareSheetBottomSheetContent implements BottomSheetContent, OnItemClickLis
return mContentView; return mContentView;
} }
protected View getTopRowView() { protected View getFirstPartyView() {
return mContentView.findViewById(R.id.share_sheet_chrome_apps); return mContentView.findViewById(R.id.share_sheet_chrome_apps);
} }
protected View getBottomRowView() { protected View getThirdPartyView() {
return mContentView.findViewById(R.id.share_sheet_other_apps); return mContentView.findViewById(R.id.share_sheet_other_apps);
} }
......
...@@ -6,8 +6,12 @@ package org.chromium.chrome.browser.share.share_sheet; ...@@ -6,8 +6,12 @@ package org.chromium.chrome.browser.share.share_sheet;
import android.app.Activity; import android.app.Activity;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.view.View; import android.view.View;
import androidx.annotation.ColorInt;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.appcompat.content.res.AppCompatResources; import androidx.appcompat.content.res.AppCompatResources;
...@@ -22,11 +26,14 @@ import org.chromium.chrome.browser.multiwindow.MultiWindowUtils; ...@@ -22,11 +26,14 @@ import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
import org.chromium.chrome.browser.share.ChromeShareExtras; import org.chromium.chrome.browser.share.ChromeShareExtras;
import org.chromium.chrome.browser.share.ShareHelper; import org.chromium.chrome.browser.share.ShareHelper;
import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.Tab;
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.bottomsheet.BottomSheetContent;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController; import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetObserver; import org.chromium.components.browser_ui.bottomsheet.BottomSheetObserver;
import org.chromium.components.browser_ui.bottomsheet.EmptyBottomSheetObserver; import org.chromium.components.browser_ui.bottomsheet.EmptyBottomSheetObserver;
import org.chromium.components.browser_ui.share.ShareParams; import org.chromium.components.browser_ui.share.ShareParams;
import org.chromium.components.browser_ui.widget.RoundedIconGenerator;
import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.base.WindowAndroid;
import org.chromium.ui.base.WindowAndroid.ActivityStateObserver; import org.chromium.ui.base.WindowAndroid.ActivityStateObserver;
import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModel;
...@@ -56,6 +63,10 @@ public class ShareSheetCoordinator implements ActivityStateObserver, ChromeOptio ...@@ -56,6 +63,10 @@ public class ShareSheetCoordinator implements ActivityStateObserver, ChromeOptio
private ChromeProvidedSharingOptionsProvider mChromeProvidedSharingOptionsProvider; private ChromeProvidedSharingOptionsProvider mChromeProvidedSharingOptionsProvider;
private ShareSheetBottomSheetContent mBottomSheet; private ShareSheetBottomSheetContent mBottomSheet;
private WindowAndroid mWindowAndroid; private WindowAndroid mWindowAndroid;
private final BottomSheetObserver mBottomSheetObserver;
private final LargeIconBridge mIconBridge;
private String mUrl;
private Bitmap mIconForPreview;
/** /**
* Constructs a new ShareSheetCoordinator. * Constructs a new ShareSheetCoordinator.
...@@ -69,14 +80,15 @@ public class ShareSheetCoordinator implements ActivityStateObserver, ChromeOptio ...@@ -69,14 +80,15 @@ public class ShareSheetCoordinator implements ActivityStateObserver, ChromeOptio
// TODO(crbug/1022172): Should be package-protected once modularization is complete. // TODO(crbug/1022172): Should be package-protected once modularization is complete.
public ShareSheetCoordinator(BottomSheetController controller, public ShareSheetCoordinator(BottomSheetController controller,
ActivityLifecycleDispatcher lifecycleDispatcher, Supplier<Tab> tabProvider, ActivityLifecycleDispatcher lifecycleDispatcher, Supplier<Tab> tabProvider,
ShareSheetPropertyModelBuilder modelBuilder, Callback<Tab> printTab) { ShareSheetPropertyModelBuilder modelBuilder, Callback<Tab> printTab,
LargeIconBridge iconBridge) {
mBottomSheetController = controller; mBottomSheetController = controller;
mLifecycleDispatcher = lifecycleDispatcher; mLifecycleDispatcher = lifecycleDispatcher;
mLifecycleDispatcher.register(this); mLifecycleDispatcher.register(this);
mTabProvider = tabProvider; mTabProvider = tabProvider;
mPropertyModelBuilder = modelBuilder; mPropertyModelBuilder = modelBuilder;
mPrintTabCallback = printTab; mPrintTabCallback = printTab;
BottomSheetObserver bottomSheetObserver = new EmptyBottomSheetObserver() { mBottomSheetObserver = new EmptyBottomSheetObserver() {
@Override @Override
public void onSheetContentChanged(BottomSheetContent bottomSheet) { public void onSheetContentChanged(BottomSheetContent bottomSheet) {
super.onSheetContentChanged(bottomSheet); super.onSheetContentChanged(bottomSheet);
...@@ -89,7 +101,8 @@ public class ShareSheetCoordinator implements ActivityStateObserver, ChromeOptio ...@@ -89,7 +101,8 @@ public class ShareSheetCoordinator implements ActivityStateObserver, ChromeOptio
} }
} }
}; };
mBottomSheetController.addObserver(bottomSheetObserver); mBottomSheetController.addObserver(mBottomSheetObserver);
mIconBridge = iconBridge;
} }
protected void destroy() { protected void destroy() {
...@@ -122,16 +135,17 @@ public class ShareSheetCoordinator implements ActivityStateObserver, ChromeOptio ...@@ -122,16 +135,17 @@ public class ShareSheetCoordinator implements ActivityStateObserver, ChromeOptio
} }
} }
mBottomSheet = new ShareSheetBottomSheetContent(mActivity, this); mBottomSheet = new ShareSheetBottomSheetContent(mActivity, this, params);
fetchFavicon(mActivity, params.getUrl());
mShareStartTime = shareStartTime; mShareStartTime = shareStartTime;
mContentTypes = ShareSheetPropertyModelBuilder.getContentTypes(params, chromeShareExtras); mContentTypes = ShareSheetPropertyModelBuilder.getContentTypes(params, chromeShareExtras);
List<PropertyModel> chromeFeatures = List<PropertyModel> firstPartyApps =
createTopRowPropertyModels(mActivity, params, chromeShareExtras, mContentTypes); createFirstPartyPropertyModels(mActivity, params, chromeShareExtras, mContentTypes);
List<PropertyModel> thirdPartyApps = createBottomRowPropertyModels( List<PropertyModel> thirdPartyApps = createThirdPartyPropertyModels(
mActivity, params, mContentTypes, chromeShareExtras.saveLastUsed()); mActivity, params, mContentTypes, chromeShareExtras.saveLastUsed());
mBottomSheet.createRecyclerViews(chromeFeatures, thirdPartyApps, message); mBottomSheet.createRecyclerViews(firstPartyApps, thirdPartyApps, message);
boolean shown = mBottomSheetController.requestShowContent(mBottomSheet, true); boolean shown = mBottomSheetController.requestShowContent(mBottomSheet, true);
if (shown) { if (shown) {
...@@ -157,7 +171,7 @@ public class ShareSheetCoordinator implements ActivityStateObserver, ChromeOptio ...@@ -157,7 +171,7 @@ public class ShareSheetCoordinator implements ActivityStateObserver, ChromeOptio
showShareSheetWithMessage(message, params, chromeShareExtras, shareStartTime); showShareSheetWithMessage(message, params, chromeShareExtras, shareStartTime);
} }
List<PropertyModel> createTopRowPropertyModels(Activity activity, ShareParams shareParams, List<PropertyModel> createFirstPartyPropertyModels(Activity activity, ShareParams shareParams,
ChromeShareExtras chromeShareExtras, Set<Integer> contentTypes) { ChromeShareExtras chromeShareExtras, Set<Integer> contentTypes) {
if (mExcludeFirstParty) { if (mExcludeFirstParty) {
return new ArrayList<>(); return new ArrayList<>();
...@@ -172,7 +186,7 @@ public class ShareSheetCoordinator implements ActivityStateObserver, ChromeOptio ...@@ -172,7 +186,7 @@ public class ShareSheetCoordinator implements ActivityStateObserver, ChromeOptio
} }
@VisibleForTesting @VisibleForTesting
List<PropertyModel> createBottomRowPropertyModels(Activity activity, ShareParams params, List<PropertyModel> createThirdPartyPropertyModels(Activity activity, ShareParams params,
Set<Integer> contentTypes, boolean saveLastUsed) { Set<Integer> contentTypes, boolean saveLastUsed) {
if (params == null) return null; if (params == null) return null;
List<PropertyModel> models = mPropertyModelBuilder.selectThirdPartyApps(mBottomSheet, List<PropertyModel> models = mPropertyModelBuilder.selectThirdPartyApps(mBottomSheet,
...@@ -220,7 +234,7 @@ public class ShareSheetCoordinator implements ActivityStateObserver, ChromeOptio ...@@ -220,7 +234,7 @@ public class ShareSheetCoordinator implements ActivityStateObserver, ChromeOptio
} }
mIsMultiWindow = isMultiWindow; mIsMultiWindow = isMultiWindow;
mBottomSheet.createChromeFeatureRecyclerViews( mBottomSheet.createFirstPartyRecyclerViews(
mChromeProvidedSharingOptionsProvider.getPropertyModels( mChromeProvidedSharingOptionsProvider.getPropertyModels(
mContentTypes, mIsMultiWindow)); mContentTypes, mIsMultiWindow));
mBottomSheetController.requestShowContent(mBottomSheet, /*animate=*/false); mBottomSheetController.requestShowContent(mBottomSheet, /*animate=*/false);
...@@ -233,9 +247,64 @@ public class ShareSheetCoordinator implements ActivityStateObserver, ChromeOptio ...@@ -233,9 +247,64 @@ public class ShareSheetCoordinator implements ActivityStateObserver, ChromeOptio
if ((oldRight - oldLeft) == (right - left)) { if ((oldRight - oldLeft) == (right - left)) {
return; return;
} }
mBottomSheet.getTopRowView().invalidate(); mBottomSheet.getFirstPartyView().invalidate();
mBottomSheet.getTopRowView().requestLayout(); mBottomSheet.getFirstPartyView().requestLayout();
mBottomSheet.getBottomRowView().invalidate(); mBottomSheet.getThirdPartyView().invalidate();
mBottomSheet.getBottomRowView().requestLayout(); mBottomSheet.getThirdPartyView().requestLayout();
}
/** Fetches the favicon for the given url. **/
void fetchFavicon(Activity activity, String url) {
if (!url.isEmpty()) {
// Update mActivity so it's non-null in onFaviconAvailable in tests.
mActivity = activity;
mUrl = url;
mIconBridge.getLargeIconForStringUrl(url,
activity.getResources().getDimensionPixelSize(R.dimen.default_favicon_min_size),
this::onFaviconAvailable);
}
}
/**
* Passed as the callback to {@link LargeIconBridge#getLargeIconForStringUrl}
* by showShareSheetWithMessage.
*/
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 (icon == null) {
return;
}
}
int size = mActivity.getResources().getDimensionPixelSize(
R.dimen.sharing_hub_preview_monogram_size);
mIconForPreview = Bitmap.createScaledBitmap(icon, size, size, true);
if (mBottomSheet != null) {
mBottomSheet.setFaviconForPreview(mIconForPreview);
}
}
private RoundedIconGenerator createRoundedIconGenerator(@ColorInt int iconColor) {
Resources resources = mActivity.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);
}
@VisibleForTesting
Bitmap getIconForPreview() {
return mIconForPreview;
} }
} }
...@@ -306,14 +306,15 @@ public class ChromeProvidedSharingOptionsProviderTest { ...@@ -306,14 +306,15 @@ public class ChromeProvidedSharingOptionsProviderTest {
private void setUpChromeProvidedSharingOptionsProviderTest(boolean printingEnabled) { private void setUpChromeProvidedSharingOptionsProviderTest(boolean printingEnabled) {
Mockito.when(mPrefService.getBoolean(anyString())).thenReturn(printingEnabled); Mockito.when(mPrefService.getBoolean(anyString())).thenReturn(printingEnabled);
mChromeProvidedSharingOptionsProvider = ShareParams shareParams = new ShareParams.Builder(null, /*title=*/"", /*url=*/"").build();
new ChromeProvidedSharingOptionsProvider(mActivity, mTabProvider, mChromeProvidedSharingOptionsProvider = new ChromeProvidedSharingOptionsProvider(mActivity,
/*bottomSheetController=*/null, mTabProvider,
new ShareSheetBottomSheetContent(mActivity, mShareSheetCoordinator), /*bottomSheetController=*/null,
new ShareParams.Builder(null, "", "").build(), new ShareSheetBottomSheetContent(mActivity, mShareSheetCoordinator, shareParams),
new ChromeShareExtras.Builder().build(), new ShareParams.Builder(null, "", "").build(),
/*TabPrinterDelegate=*/null, new ChromeShareExtras.Builder().build(),
/*shareStartTime=*/0, mShareSheetCoordinator); /*TabPrinterDelegate=*/null,
/*shareStartTime=*/0, mShareSheetCoordinator);
} }
private void assertCorrectModelsAreInTheRightOrder( private void assertCorrectModelsAreInTheRightOrder(
......
...@@ -12,6 +12,7 @@ import static org.mockito.Matchers.any; ...@@ -12,6 +12,7 @@ import static org.mockito.Matchers.any;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.app.Activity; import android.app.Activity;
import android.graphics.Bitmap;
import android.support.test.rule.ActivityTestRule; import android.support.test.rule.ActivityTestRule;
import androidx.test.filters.MediumTest; import androidx.test.filters.MediumTest;
...@@ -28,6 +29,8 @@ import org.chromium.base.test.util.CommandLineFlags; ...@@ -28,6 +29,8 @@ import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.flags.ChromeSwitches; import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher; import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
import org.chromium.chrome.browser.ui.favicon.IconType;
import org.chromium.chrome.browser.ui.favicon.LargeIconBridge;
import org.chromium.chrome.test.ChromeBrowserTestRule; import org.chromium.chrome.test.ChromeBrowserTestRule;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.util.browser.Features; import org.chromium.chrome.test.util.browser.Features;
...@@ -65,9 +68,14 @@ public final class ShareSheetCoordinatorTest { ...@@ -65,9 +68,14 @@ public final class ShareSheetCoordinatorTest {
@Mock @Mock
private ShareSheetPropertyModelBuilder mPropertyModelBuilder; private ShareSheetPropertyModelBuilder mPropertyModelBuilder;
@Mock
private ShareParams mParams;
private Activity mActivity; private Activity mActivity;
private ShareSheetCoordinator mShareSheetCoordinator; private ShareSheetCoordinator mShareSheetCoordinator;
private static Bitmap.Config sConfig = Bitmap.Config.ALPHA_8;
@Before @Before
public void setUp() { public void setUp() {
mActivity = mActivityTestRule.getActivity(); mActivity = mActivityTestRule.getActivity();
...@@ -90,8 +98,8 @@ public final class ShareSheetCoordinatorTest { ...@@ -90,8 +98,8 @@ public final class ShareSheetCoordinatorTest {
any(), anySet(), any(), anyBoolean(), any(), anyLong())) any(), anySet(), any(), anyBoolean(), any(), anyLong()))
.thenReturn(thirdPartyPropertyModels); .thenReturn(thirdPartyPropertyModels);
mShareSheetCoordinator = new ShareSheetCoordinator( mShareSheetCoordinator = new ShareSheetCoordinator(mController, mLifecycleDispatcher, null,
mController, mLifecycleDispatcher, null, mPropertyModelBuilder, null); mPropertyModelBuilder, null, new MockLargeIconBridge());
} }
@Test @Test
...@@ -99,18 +107,17 @@ public final class ShareSheetCoordinatorTest { ...@@ -99,18 +107,17 @@ public final class ShareSheetCoordinatorTest {
public void disableFirstPartyFeatures() { public void disableFirstPartyFeatures() {
mShareSheetCoordinator.disableFirstPartyFeaturesForTesting(); mShareSheetCoordinator.disableFirstPartyFeaturesForTesting();
List<PropertyModel> propertyModels = mShareSheetCoordinator.createTopRowPropertyModels( List<PropertyModel> propertyModels = mShareSheetCoordinator.createFirstPartyPropertyModels(
mActivity, /*shareParams=*/null, /*chromeShareExtras=*/null, mActivity, mParams, /*chromeShareExtras=*/null,
ShareSheetPropertyModelBuilder.ALL_CONTENT_TYPES); ShareSheetPropertyModelBuilder.ALL_CONTENT_TYPES);
assertEquals("Property model list should be empty.", 0, propertyModels.size()); assertEquals("Property model list should be empty.", 0, propertyModels.size());
} }
@Test @Test
@MediumTest @MediumTest
public void testCreateBottomRowPropertyModels() { public void testCreateThirdPartyPropertyModels() {
List<PropertyModel> propertyModels = mShareSheetCoordinator.createBottomRowPropertyModels( List<PropertyModel> propertyModels = mShareSheetCoordinator.createThirdPartyPropertyModels(
mActivity, new ShareParams.Builder(null, "", "").build(), mActivity, mParams, ShareSheetPropertyModelBuilder.ALL_CONTENT_TYPES,
ShareSheetPropertyModelBuilder.ALL_CONTENT_TYPES,
/*saveLastUsed=*/false); /*saveLastUsed=*/false);
assertEquals("Incorrect number of property models.", 3, propertyModels.size()); assertEquals("Incorrect number of property models.", 3, propertyModels.size());
...@@ -122,4 +129,28 @@ public final class ShareSheetCoordinatorTest { ...@@ -122,4 +129,28 @@ public final class ShareSheetCoordinatorTest {
mActivity.getResources().getString(R.string.sharing_more_icon_label), mActivity.getResources().getString(R.string.sharing_more_icon_label),
propertyModels.get(2).get(ShareSheetItemViewProperties.LABEL)); propertyModels.get(2).get(ShareSheetItemViewProperties.LABEL));
} }
@Test
@MediumTest
public void testFetchFavicon() {
Activity activity = mActivityTestRule.getActivity();
mShareSheetCoordinator.fetchFavicon(activity, "https://www.example.com");
Bitmap bitmap = mShareSheetCoordinator.getIconForPreview();
int size = activity.getResources().getDimensionPixelSize(
R.dimen.sharing_hub_preview_monogram_size);
assertEquals(size, bitmap.getWidth());
assertEquals(size, bitmap.getHeight());
assertEquals(sConfig, bitmap.getConfig());
}
private static class MockLargeIconBridge extends LargeIconBridge {
@Override
public boolean getLargeIconForStringUrl(String pageUrl, int desiredSizePx,
final LargeIconBridge.LargeIconCallback callback) {
callback.onLargeIconAvailable(
Bitmap.createBitmap(48, 84, sConfig), 0, false, IconType.INVALID);
return true;
}
}
} }
...@@ -20827,6 +20827,9 @@ should be able to be added at any place in this file. ...@@ -20827,6 +20827,9 @@ should be able to be added at any place in this file.
</action> </action>
<action name="SharingHubAndroid.BottomRowScrolled"> <action name="SharingHubAndroid.BottomRowScrolled">
<obsolete>
Renamed to SharingHubAndroid.ThirdPartyAppsScrolled 09/2020.
</obsolete>
<owner>kmilka@chromium.org</owner> <owner>kmilka@chromium.org</owner>
<owner>src/components/send_tab_to_self/OWNERS</owner> <owner>src/components/send_tab_to_self/OWNERS</owner>
<description> <description>
...@@ -20859,6 +20862,15 @@ should be able to be added at any place in this file. ...@@ -20859,6 +20862,15 @@ should be able to be added at any place in this file.
</description> </description>
</action> </action>
<action name="SharingHubAndroid.FirstPartyAppsScrolled">
<owner>kmilka@chromium.org</owner>
<owner>src/components/send_tab_to_self/OWNERS</owner>
<description>
The chrome app row was horizontally scrolled. Recorded only once per open
share sheet.
</description>
</action>
<action name="SharingHubAndroid.LinkToTextSelected"> <action name="SharingHubAndroid.LinkToTextSelected">
<owner>gayane@chromium.org</owner> <owner>gayane@chromium.org</owner>
<owner>src/components/send_tab_to_self/OWNERS</owner> <owner>src/components/send_tab_to_self/OWNERS</owner>
...@@ -20937,7 +20949,19 @@ should be able to be added at any place in this file. ...@@ -20937,7 +20949,19 @@ should be able to be added at any place in this file.
</description> </description>
</action> </action>
<action name="SharingHubAndroid.ThirdPartyAppsScrolled">
<owner>kmilka@chromium.org</owner>
<owner>src/components/send_tab_to_self/OWNERS</owner>
<description>
The third party app row was horizontally scrolled. Recorded only once per
open share sheet.
</description>
</action>
<action name="SharingHubAndroid.TopRowScrolled"> <action name="SharingHubAndroid.TopRowScrolled">
<obsolete>
Renamed to SharingHubAndroid.FirstPartyAppsScrolled 09/2020.
</obsolete>
<owner>kmilka@chromium.org</owner> <owner>kmilka@chromium.org</owner>
<owner>src/components/send_tab_to_self/OWNERS</owner> <owner>src/components/send_tab_to_self/OWNERS</owner>
<description> <description>
......
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