Skip to content

Commit

Permalink
sharing: implement desktop share preview stub
Browse files Browse the repository at this point in the history
This change adds the preview surface to the desktop sharing hub, gated
by the DesktopSharePreview field trial (or UpcomingSharingFeatures, as
usual). Specifically, this change:

1. Adds methods to SharingHubBubbleController to get the needed
   information about the pending share - these are currently stubs
   that operate on the active WebContents only, for test purposes
   (see https://crbug.com/1312524)
2. Adds a new class PreviewView which displays that information
3. Adds code to SharingHubBubbleViewImpl to construct a PreviewView and
   insert it into its layout when the feature is enabled

A couple of design things:
* PreviewView is fully decoupled from both SharingHubBubbleController
  and SharingHubBubbleViewImpl. SharingHubBubbleViewImpl is responsible
  for registering PreviewView for image update callbacks, so PreviewView
  has the ability to hold a provided CallbackListSubscription, but it is
  unaware of the other two involved classes. This should make it quite
  easy to unit test.
* I considered creating a wrapper type to hold the share preview state,
  but ultimately decided against it; it led to a design where
  PreviewView (appeared to) allow updates of all parts of its visuals,
  not just the image, which is more flexibility than I wanted to add.

Next steps:
* Unit tests for PreviewView, once it has more logic in it
* Wiring SharingHubBubbleController to the actual omnibox share state
* Metrics gathering
* Working with UX to reconcile the differences from the mock redlines

Fixed: 1312516
Change-Id: If55a23cfee64dbaa156d6a257d3f0da1a37bcc3d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3571755
Reviewed-by: Kristi Park <kristipark@chromium.org>
Commit-Queue: Elly Fong-Jones <ellyjones@chromium.org>
Cr-Commit-Position: refs/heads/main@{#989609}
  • Loading branch information
Elly Fong-Jones authored and Chromium LUCI CQ committed Apr 6, 2022
1 parent 8b0b695 commit eb519b0
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 2 deletions.
2 changes: 2 additions & 0 deletions chrome/browser/ui/BUILD.gn
Expand Up @@ -4666,6 +4666,8 @@ static_library("ui") {
"views/sharing/sharing_dialog_view.h",
"views/sharing/sharing_icon_view.cc",
"views/sharing/sharing_icon_view.h",
"views/sharing_hub/preview_view.cc",
"views/sharing_hub/preview_view.h",
"views/sharing_hub/screenshot/screenshot_captured_bubble.cc",
"views/sharing_hub/screenshot/screenshot_captured_bubble.h",
"views/sharing_hub/sharing_hub_bubble_action_button.cc",
Expand Down
29 changes: 29 additions & 0 deletions chrome/browser/ui/sharing_hub/sharing_hub_bubble_controller.cc
Expand Up @@ -9,7 +9,9 @@
#include "base/strings/utf_string_conversions.h"
#include "build/chromeos_buildflags.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/favicon/favicon_utils.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/share/share_features.h"
#include "chrome/browser/share/share_metrics.h"
#include "chrome/browser/sharing_hub/sharing_hub_features.h"
#include "chrome/browser/sharing_hub/sharing_hub_model.h"
Expand Down Expand Up @@ -168,6 +170,33 @@ SharingHubBubbleController::GetThirdPartyActions() {
return actions;
}

bool SharingHubBubbleController::ShouldUsePreview() {
return share::GetDesktopSharePreviewVariant() !=
share::DesktopSharePreviewVariant::kDisabled;
}

std::u16string SharingHubBubbleController::GetPreviewTitle() {
// TODO(https://crbug.com/1312524): get passed this state from the omnibox
// instead.
return GetWebContents().GetTitle();
}

GURL SharingHubBubbleController::GetPreviewUrl() {
// TODO(https://crbug.com/1312524): get passed this state from the omnibox
// instead.
return GetWebContents().GetVisibleURL();
}

ui::ImageModel SharingHubBubbleController::GetPreviewImage() {
return ui::ImageModel::FromImage(favicon::GetDefaultFavicon());
}

base::CallbackListSubscription
SharingHubBubbleController::RegisterPreviewImageChangedCallback(
PreviewImageChangedCallback callback) {
return preview_image_changed_callbacks_.Add(callback);
}

void SharingHubBubbleController::OnActionSelected(
int command_id,
bool is_first_party,
Expand Down
15 changes: 15 additions & 0 deletions chrome/browser/ui/sharing_hub/sharing_hub_bubble_controller.h
Expand Up @@ -10,6 +10,7 @@
#include "build/chromeos_buildflags.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
#include "ui/base/models/image_model.h"
#include "ui/views/view_tracker.h"
#include "ui/views/widget/widget.h"

Expand Down Expand Up @@ -44,6 +45,9 @@ class SharingHubBubbleController
: public content::WebContentsObserver,
public content::WebContentsUserData<SharingHubBubbleController> {
public:
using PreviewImageChangedCallback =
base::RepeatingCallback<void(ui::ImageModel)>;

SharingHubBubbleController(const SharingHubBubbleController&) = delete;
SharingHubBubbleController& operator=(const SharingHubBubbleController&) =
delete;
Expand Down Expand Up @@ -72,6 +76,14 @@ class SharingHubBubbleController
// Returns the list of Sharing Hub third party actions.
virtual std::vector<SharingHubAction> GetThirdPartyActions();

virtual bool ShouldUsePreview();
virtual std::u16string GetPreviewTitle();
virtual GURL GetPreviewUrl();
virtual ui::ImageModel GetPreviewImage();

base::CallbackListSubscription RegisterPreviewImageChangedCallback(
PreviewImageChangedCallback callback);

// Handles when the user clicks on a Sharing Hub action. If this is a first
// party action, executes the appropriate browser command. If this is a third
// party action, navigates to an external webpage.
Expand Down Expand Up @@ -114,6 +126,9 @@ class SharingHubBubbleController
// Cached reference to the model.
raw_ptr<SharingHubModel> sharing_hub_model_ = nullptr;

base::RepeatingCallbackList<void(ui::ImageModel)>
preview_image_changed_callbacks_;

WEB_CONTENTS_USER_DATA_KEY_DECL();

#if BUILDFLAG(IS_CHROMEOS_ASH)
Expand Down
72 changes: 72 additions & 0 deletions chrome/browser/ui/views/sharing_hub/preview_view.cc
@@ -0,0 +1,72 @@
// Copyright 2022 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.

#include "chrome/browser/ui/views/sharing_hub/preview_view.h"

#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/sharing_hub/sharing_hub_bubble_controller.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/flex_layout.h"
#include "ui/views/view_class_properties.h"

namespace sharing_hub {

namespace {
constexpr gfx::Size kImageSize{32, 32};
} // namespace

// This view uses two nested FlexLayouts, a horizontal outer one and a vertical
// inner one, to create a composite layout where the icon is aligned with the
// title and URL taken together. The resulting View tree looks like this:
// PreviewView [FlexLayout, horizontal]
// ImageView
// View [FlexLayout, vertical]
// Label (title)
// Label (URL)
PreviewView::PreviewView(std::u16string title, GURL url, ui::ImageModel image) {
auto* layout = SetLayoutManager(std::make_unique<views::FlexLayout>());
layout->SetOrientation(views::LayoutOrientation::kHorizontal)
.SetMainAxisAlignment(views::LayoutAlignment::kStart)
.SetCrossAxisAlignment(views::LayoutAlignment::kCenter)
// TODO(ellyjones): Check these against the redlines in the mocks, and
// once they're finalized, move them to the constant block above. Getting
// things to line up with the mocks may require a bit of a refactor in
// HoverButton itself.
.SetInteriorMargin(gfx::Insets::VH(12, 8))
.SetDefault(views::kMarginsKey, gfx::Insets::VH(0, 8));

image_ = AddChildView(std::make_unique<views::ImageView>(image));
image_->SetPreferredSize(kImageSize);

auto* labels_container = AddChildView(std::make_unique<views::View>());
auto* labels_layout =
labels_container->SetLayoutManager(std::make_unique<views::FlexLayout>());
labels_layout->SetOrientation(views::LayoutOrientation::kVertical)
.SetMainAxisAlignment(views::LayoutAlignment::kCenter)
.SetCrossAxisAlignment(views::LayoutAlignment::kStart);

// TODO(ellyjones): These do not exactly match the redlines, which call for
// 14pt Roboto specifically. We should probably update the redlines to not
// use a hardcoded font, but we could also specify the font more explicitly
// here.
title_ = labels_container->AddChildView(std::make_unique<views::Label>(
title, views::style::CONTEXT_DIALOG_TITLE));
url_ = labels_container->AddChildView(std::make_unique<views::Label>(
base::UTF8ToUTF16(url.spec()), views::style::CONTEXT_DIALOG_TITLE,
views::style::STYLE_HINT));
}

PreviewView::~PreviewView() = default;

void PreviewView::TakeCallbackSubscription(
base::CallbackListSubscription subscription) {
subscription_ = std::move(subscription);
}

void PreviewView::OnImageChanged(ui::ImageModel model) {
image_->SetImage(model);
}

} // namespace sharing_hub
59 changes: 59 additions & 0 deletions chrome/browser/ui/views/sharing_hub/preview_view.h
@@ -0,0 +1,59 @@
// Copyright 2022 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.

#ifndef CHROME_BROWSER_UI_VIEWS_SHARING_HUB_PREVIEW_VIEW_H_
#define CHROME_BROWSER_UI_VIEWS_SHARING_HUB_PREVIEW_VIEW_H_

#include "ui/base/models/image_model.h"
#include "ui/views/view.h"
#include "url/gurl.h"

namespace views {
class ImageView;
class Label;
} // namespace views

namespace sharing_hub {

// A PreviewView shows some information about a pending share so the user can
// tell what they're about to share. In particular, it looks like this:
// +-----------------------------+
// |+------+ Title |
// || Icon | |
// |+------+ URL |
// +-----------------------------+
// The title and URL are fixed at construction time, but the icon may change -
// which image is used depends on the state of the DesktopSharePreview field
// trial.
class PreviewView : public views::View {
public:
// Taking an initial image here, instead of requiring the caller to call
// OnImageChanged() after construction to set the initial image, means that
// this class always has a valid image to display and does not have a
// "half-initialized" state to worry about.
explicit PreviewView(std::u16string title, GURL url, ui::ImageModel image);
~PreviewView() override;

// This seemingly-odd method allows for PreviewView to be uncoupled from the
// class that provides image updates - it receives image updates via a
// callback which is bound by external code. Having PreviewView itself store
// the subscription guarantees that the callback can't be delivered on a
// deleted PreviewView.
void TakeCallbackSubscription(base::CallbackListSubscription subscription);

// Call this method to supply a new ImageModel to use for the preview image.
// Whatever image you supply will be scaled to fit the image slot.
void OnImageChanged(ui::ImageModel model);

private:
base::CallbackListSubscription subscription_;

views::Label* title_ = nullptr;
views::Label* url_ = nullptr;
views::ImageView* image_ = nullptr;
};

} // namespace sharing_hub

#endif // CHROME_BROWSER_UI_VIEWS_SHARING_HUB_PREVIEW_VIEW_H_
Expand Up @@ -8,6 +8,7 @@
#include "chrome/browser/ui/sharing_hub/sharing_hub_bubble_controller.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "chrome/browser/ui/views/chrome_typography.h"
#include "chrome/browser/ui/views/sharing_hub/preview_view.h"
#include "chrome/browser/ui/views/sharing_hub/sharing_hub_bubble_action_button.h"
#include "chrome/grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
Expand Down Expand Up @@ -116,7 +117,17 @@ const views::View* SharingHubBubbleViewImpl::GetButtonContainerForTesting()
void SharingHubBubbleViewImpl::Init() {
const int kPadding = 8;
set_margins(gfx::Insets::TLBR(kPadding, 0, kPadding, 0));
SetLayoutManager(std::make_unique<views::FillLayout>());
auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>());
layout->SetOrientation(views::BoxLayout::Orientation::kVertical);
if (controller_->ShouldUsePreview()) {
auto* preview = AddChildView(std::make_unique<PreviewView>(
controller_->GetPreviewTitle(), controller_->GetPreviewUrl(),
controller_->GetPreviewImage()));
preview->TakeCallbackSubscription(
controller_->RegisterPreviewImageChangedCallback(base::BindRepeating(
&PreviewView::OnImageChanged, base::Unretained(preview))));
AddChildView(std::make_unique<views::Separator>());
}

scroll_view_ = AddChildView(std::make_unique<views::ScrollView>());
scroll_view_->ClipHeightTo(0, kActionButtonHeight * kMaximumButtons);
Expand Down
Expand Up @@ -25,7 +25,8 @@ class SharingHubBubbleActionButton;
struct SharingHubAction;

// View component of the Sharing Hub bubble that allows users to share/save the
// current page.
// current page. The sharing hub bubble also optionally contains a preview of
// the content being shared.
class SharingHubBubbleViewImpl : public SharingHubBubbleView,
public LocationBarBubbleDelegateView {
public:
Expand Down

0 comments on commit eb519b0

Please sign in to comment.