Skip to content

Commit

Permalink
capture mode: implement customized image capture notification.
Browse files Browse the repository at this point in the history
Bug: 1159111
Change-Id: I5a96480a929a05a7782f51ae6baea8ddc7c9df10
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2632390
Reviewed-by: Evan Stade <estade@chromium.org>
Reviewed-by: Ahmed Fakhry <afakhry@chromium.org>
Commit-Queue: Xiaoqian Dai <xdai@chromium.org>
Cr-Commit-Position: refs/heads/master@{#846957}
  • Loading branch information
Xiaoqian Dai authored and Chromium LUCI CQ committed Jan 26, 2021
1 parent 9dc1d6c commit 0eb6b1e
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 1 deletion.
2 changes: 2 additions & 0 deletions ash/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,8 @@ component("ash") {
"capture_mode/capture_mode_feature_pod_controller.h",
"capture_mode/capture_mode_metrics.cc",
"capture_mode/capture_mode_metrics.h",
"capture_mode/capture_mode_notification_view.cc",
"capture_mode/capture_mode_notification_view.h",
"capture_mode/capture_mode_session.cc",
"capture_mode/capture_mode_session.h",
"capture_mode/capture_mode_settings_entry_view.cc",
Expand Down
14 changes: 13 additions & 1 deletion ash/capture_mode/capture_mode_controller.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <utility>

#include "ash/capture_mode/capture_mode_metrics.h"
#include "ash/capture_mode/capture_mode_notification_view.h"
#include "ash/capture_mode/capture_mode_session.h"
#include "ash/capture_mode/capture_mode_util.h"
#include "ash/capture_mode/video_recording_watcher.h"
Expand Down Expand Up @@ -49,6 +50,7 @@
#include "ui/message_center/message_center.h"
#include "ui/message_center/public/cpp/notification.h"
#include "ui/message_center/public/cpp/notification_delegate.h"
#include "ui/message_center/views/message_view_factory.h"
#include "ui/snapshot/snapshot.h"

namespace ash {
Expand All @@ -66,6 +68,8 @@ constexpr char kScreenCaptureNotificationId[] = "capture_mode_notification";
constexpr char kScreenCaptureStoppedNotificationId[] =
"capture_mode_stopped_notification";
constexpr char kScreenCaptureNotifierId[] = "ash.capture_mode_controller";
constexpr char kScreenCaptureNotificationType[] =
"capture_mode_notification_type";

// The format strings of the file names of captured images.
// TODO(afakhry): Discuss with UX localizing "Screenshot" and "Screen
Expand Down Expand Up @@ -169,7 +173,7 @@ void ShowNotification(
const gfx::VectorIcon& notification_icon = kCaptureModeIcon) {
const auto type = optional_fields.image.IsEmpty()
? message_center::NOTIFICATION_TYPE_SIMPLE
: message_center::NOTIFICATION_TYPE_IMAGE;
: message_center::NOTIFICATION_TYPE_CUSTOM;
std::unique_ptr<message_center::Notification> notification =
CreateSystemNotification(
type, notification_id, l10n_util::GetStringUTF16(title_id),
Expand All @@ -180,6 +184,8 @@ void ShowNotification(
message_center::NotifierType::SYSTEM_COMPONENT,
kScreenCaptureNotifierId),
optional_fields, delegate, notification_icon, warning_level);
if (type == message_center::NOTIFICATION_TYPE_CUSTOM)
notification->set_custom_view_type(kScreenCaptureNotificationType);

// Remove the previous notification before showing the new one if there is
// any.
Expand Down Expand Up @@ -297,6 +303,12 @@ CaptureModeController::CaptureModeController(
&CaptureModeController::RecordAndResetScreenshotsTakenInLastWeek,
weak_ptr_factory_.GetWeakPtr()));

DCHECK(!message_center::MessageViewFactory::HasCustomNotificationViewFactory(
kScreenCaptureNotificationType));
message_center::MessageViewFactory::SetCustomNotificationViewFactory(
kScreenCaptureNotificationType,
base::BindRepeating(&CaptureModeNotificationView::Create));

Shell::Get()->session_controller()->AddObserver(this);
chromeos::PowerManagerClient::Get()->AddObserver(this);
}
Expand Down
125 changes: 125 additions & 0 deletions ash/capture_mode/capture_mode_notification_view.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Copyright 2021 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 "ash/capture_mode/capture_mode_notification_view.h"

#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h"
#include "ash/style/scoped_light_mode_as_default.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/views/background.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/view.h"

namespace ash {

namespace {

// Constants related to the banner view on the image capture notification.
constexpr int kBannerHeightDip = 36;
constexpr int kBannerHorizontalInsetDip = 12;
constexpr int kBannerVerticalInsetDip = 8;
constexpr int kBannerIconTextSpacingDip = 8;
constexpr int kBannerIconSizeDip = 20;

// Creates the banner view that will show on top of the notification image.
std::unique_ptr<views::View> CreateBannerViewImpl() {
std::unique_ptr<views::View> banner_view = std::make_unique<views::View>();

// Use the light mode as default as notification is still using light
// theme as the default theme.
ScopedLightModeAsDefault scoped_light_mode_as_default;

auto* color_provider = AshColorProvider::Get();
const SkColor background_color = color_provider->GetControlsLayerColor(
AshColorProvider::ControlsLayerType::kControlBackgroundColorActive);
// The text and icon are showing on the background with |background_color|
// so its color is same with kButtonLabelColorPrimary although they're
// not theoretically showing on a button.
const SkColor text_icon_color = color_provider->GetContentLayerColor(
AshColorProvider::ContentLayerType::kButtonLabelColorPrimary);
auto layout = std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal,
gfx::Insets(kBannerVerticalInsetDip, kBannerHorizontalInsetDip),
kBannerIconTextSpacingDip);
banner_view->SetLayoutManager(std::move(layout));
banner_view->SetBackground(views::CreateSolidBackground(background_color));

views::ImageView* icon =
banner_view->AddChildView(std::make_unique<views::ImageView>());
icon->SetImage(gfx::CreateVectorIcon(kCaptureModeCopiedToClipboardIcon,
kBannerIconSizeDip, text_icon_color));

views::Label* label = banner_view->AddChildView(
std::make_unique<views::Label>(l10n_util::GetStringUTF16(
IDS_ASH_SCREEN_CAPTURE_SCREENSHOT_COPIED_TO_CLIPBOARD)));
label->SetBackgroundColor(background_color);
label->SetEnabledColor(text_icon_color);

return banner_view;
}

} // namespace

CaptureModeNotificationView::CaptureModeNotificationView(
const message_center::Notification& notification)
: message_center::NotificationViewMD(notification) {
// Create the banner view if notification image is not empty. The banner
// will show on top of the notification image.
if (!notification.image().IsEmpty())
CreateBannerView();

// We need to observe this view as |this| view will be re-used for
// notifications for with/without image scenarios if |this| is not destroyed
// by the user or by the timeout before the next notification shows up.
views::View::AddObserver(this);
}

CaptureModeNotificationView::~CaptureModeNotificationView() = default;

// static
std::unique_ptr<message_center::MessageView>
CaptureModeNotificationView::Create(
const message_center::Notification& notification) {
return std::make_unique<CaptureModeNotificationView>(notification);
}

void CaptureModeNotificationView::Layout() {
message_center::NotificationViewMD::Layout();
if (!banner_view_)
return;

// Calculate the banner view's desired bounds.
gfx::Rect banner_bounds = image_container_view()->GetContentsBounds();
banner_bounds.set_y(banner_bounds.bottom() - kBannerHeightDip);
banner_bounds.set_height(kBannerHeightDip);
banner_view_->SetBoundsRect(banner_bounds);
}

void CaptureModeNotificationView::OnChildViewAdded(views::View* observed_view,
views::View* child) {
if (observed_view == this && child == image_container_view())
CreateBannerView();
}

void CaptureModeNotificationView::OnChildViewRemoved(views::View* observed_view,
views::View* child) {
if (observed_view == this && child == image_container_view())
banner_view_ = nullptr;
}

void CaptureModeNotificationView::OnViewIsDeleting(View* observed_view) {
DCHECK_EQ(observed_view, this);
views::View::RemoveObserver(this);
}

void CaptureModeNotificationView::CreateBannerView() {
DCHECK(image_container_view());
DCHECK(!image_container_view()->children().empty());
DCHECK(!banner_view_);
banner_view_ = image_container_view()->AddChildView(CreateBannerViewImpl());
}

} // namespace ash
53 changes: 53 additions & 0 deletions ash/capture_mode/capture_mode_notification_view.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright 2021 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 ASH_CAPTURE_MODE_CAPTURE_MODE_NOTIFICATION_VIEW_H_
#define ASH_CAPTURE_MODE_CAPTURE_MODE_NOTIFICATION_VIEW_H_

#include "ash/ash_export.h"
#include "ui/message_center/views/notification_view_md.h"
#include "ui/views/view_observer.h"

namespace ash {

// A customized notification view for capture mode that can show a notification
// with a banner on top of the notification image.
class ASH_EXPORT CaptureModeNotificationView
: public message_center::NotificationViewMD,
public views::ViewObserver {
public:
explicit CaptureModeNotificationView(
const message_center::Notification& notification);
CaptureModeNotificationView(const CaptureModeNotificationView&) = delete;
CaptureModeNotificationView& operator=(const CaptureModeNotificationView&) =
delete;
~CaptureModeNotificationView() override;

// Creates the custom capture mode notification for image capture
// notification. There is a banner on top of the image area of the
// notification to indicate the image has been copied to clipboard.
static std::unique_ptr<message_center::MessageView> Create(
const message_center::Notification& notification);

// message_center::NotificationViewMD:
void Layout() override;

// views::ViewObserver:
void OnChildViewAdded(views::View* observed_view,
views::View* child) override;
void OnChildViewRemoved(views::View* observed_view,
views::View* child) override;
void OnViewIsDeleting(View* observed_view) override;

private:
void CreateBannerView();

// The banner view that shows a banner string on top of the captured image.
// Owned by view hierarchy.
views::View* banner_view_ = nullptr;
};

} // namespace ash

#endif // ASH_CAPTURE_MODE_CAPTURE_MODE_NOTIFICATION_VIEW_H_
3 changes: 3 additions & 0 deletions ui/message_center/views/notification_view_md.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,9 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD
void OnNotificationInputSubmit(size_t index,
const base::string16& text) override;

protected:
views::View* image_container_view() { return image_container_view_; }

private:
FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, AppNameExtension);
FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, AppNameSystemNotification);
Expand Down

0 comments on commit 0eb6b1e

Please sign in to comment.