Commit e95457a8 authored by Meilin Wang's avatar Meilin Wang Committed by Commit Bot

[CrOS PhoneHub] Add notification opt in view.

This CL adds the notification opt in view on the Phone Hub panel to
provide another entry point for user to grant access for notifications
from the phone. This CL also introduces a |PhoneHubViewID| enum for
easy access to main components of the bubble view during unittests.

Screenshot: https://screenshot.googleplex.com/3XBngS9RArKjD44.png

BUG=1106937,1126208

Change-Id: Ie440ebad2080382f48e48778eb94f559474aa5ed
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2424647
Commit-Queue: Meilin Wang <meilinw@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarKyle Horimoto <khorimoto@chromium.org>
Cr-Commit-Position: refs/heads/master@{#810071}
parent a1378b2c
......@@ -1118,6 +1118,8 @@ component("ash") {
"system/phonehub/continue_browsing_chip.h",
"system/phonehub/initial_connecting_view.cc",
"system/phonehub/initial_connecting_view.h",
"system/phonehub/notification_opt_in_view.cc",
"system/phonehub/notification_opt_in_view.h",
"system/phonehub/onboarding_view.cc",
"system/phonehub/onboarding_view.h",
"system/phonehub/phone_hub_interstitial_view.cc",
......@@ -1126,6 +1128,7 @@ component("ash") {
"system/phonehub/phone_hub_notification_controller.h",
"system/phonehub/phone_hub_tray.cc",
"system/phonehub/phone_hub_tray.h",
"system/phonehub/phone_hub_view_ids.h",
"system/phonehub/phone_status_view.cc",
"system/phonehub/phone_status_view.h",
"system/phonehub/quick_actions_view.cc",
......
......@@ -1053,6 +1053,15 @@ This file contains the strings for ash.
<message name="IDS_ASH_PHONE_HUB_NOTIFICATION_INLINE_CANCEL_BUTTON" desc="Label for the cancel button inside a PhoneHub notification.">
Cancel
</message>
<message name="IDS_ASH_PHONE_HUB_NOTIFICATION_OPT_IN_DESCRIPTION" desc="Description for the notification opt in view.">
Turn on Notifications from your Android phone
</message>
<message name="IDS_ASH_PHONE_HUB_NOTIFICATION_OPT_IN_SET_UP_BUTTON" desc="Label for the set up button to start the opt in flow to show notifications from the phone.">
Set up
</message>
<message name="IDS_ASH_PHONE_HUB_NOTIFICATION_OPT_IN_DISMISS_BUTTON" desc="Label for the dismiss button to opt out showing notifications from the phone.">
Dismiss
</message>
<message name="IDS_ASH_STYLUS_TOOLS_CAPTURE_REGION_ACTION" desc="Title of the capture region action in the stylus tools (a pop-up panel next to the status tray). This causes a partial screenshot to be taken (the user selects an area of the screen to take a screenshot of).">
Capture region
......
b4ed88fa864bf36f803e3d3d1d33647c686a9a67
\ No newline at end of file
117eeaa5ed4774aece143c793b6595d3246e649b
\ No newline at end of file
4faf61d74cf5562cd8bb0c1b6364f1f05122632c
\ No newline at end of file
// 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.
#include "ash/system/phonehub/notification_opt_in_view.h"
#include <memory>
#include "ash/public/cpp/new_window_delegate.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h"
#include "ash/system/phonehub/phone_hub_view_ids.h"
#include "ash/system/tray/tray_bubble_view.h"
#include "ash/system/tray/tray_popup_item_style.h"
#include "ash/system/unified/rounded_label_button.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/views/border.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/grid_layout.h"
#include "url/gurl.h"
namespace ash {
namespace {
// Appearance.
// TODO(crbug.com/1126208): update constants to spec.
constexpr int kButtonSpacingDip = 10;
constexpr int kBorderThicknessDip = 1;
constexpr int kBorderCornerRadiusDip = 10;
constexpr gfx::Insets kTextLabelBorderInsets = {10, 0, 0, 0};
constexpr gfx::Insets kButtonContainerBorderInsets = {10, 0, 5, 5};
// Tag value used to uniquely identify the "Dismiss" and "Get started" buttons.
constexpr int kDismissButtonTag = 1;
constexpr int kSetUpButtonTag = 2;
// URL of the multidevice settings page.
// TODO(crbug.com/1126208): update to the direct link to the Phone Hub
// notification set up dialog.
constexpr char kMultideviceSettingsUrl[] =
"chrome://os-settings/multidevice/features";
} // namespace
NotificationOptInView::NotificationOptInView(TrayBubbleView* bubble_view)
: bubble_view_(bubble_view) {
SetID(PhoneHubViewID::kNotificationOptInView);
InitLayout();
}
NotificationOptInView::~NotificationOptInView() = default;
void NotificationOptInView::ButtonPressed(views::Button* sender,
const ui::Event& event) {
switch (sender->tag()) {
case kDismissButtonTag:
// Dismiss this view if user chose to opt out and update the bubble size.
SetVisible(false);
bubble_view_->UpdateBubble();
break;
case kSetUpButtonTag:
// Opens the notification set up dialog in settings to start the opt in
// flow.
NewWindowDelegate::GetInstance()->NewTabWithUrl(
GURL(kMultideviceSettingsUrl), /*from_user_interaction=*/true);
break;
}
}
void NotificationOptInView::InitLayout() {
SetPaintToLayer();
layer()->SetFillsBoundsOpaquely(false);
const SkColor border_color = AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kSeparatorColor);
SetBorder(views::CreateRoundedRectBorder(
kBorderThicknessDip, kBorderCornerRadiusDip, border_color));
views::GridLayout* layout =
SetLayoutManager(std::make_unique<views::GridLayout>());
const int kColumnSetId = 0;
views::ColumnSet* column_set = layout->AddColumnSet(kColumnSetId);
column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER, 1.0,
views::GridLayout::ColumnSize::kUsePreferred, 0, 0);
// Set up layout row for the text label.
layout->StartRow(views::GridLayout::kFixedSize, kColumnSetId);
text_label_ =
layout->AddView(std::make_unique<views::Label>(), 1, 1,
views::GridLayout::CENTER, views::GridLayout::CENTER);
TrayPopupItemStyle body_style(
TrayPopupItemStyle::FontStyle::DETAILED_VIEW_LABEL);
body_style.SetupLabel(text_label_);
text_label_->SetBorder(views::CreateEmptyBorder(kTextLabelBorderInsets));
text_label_->SetText(l10n_util::GetStringUTF16(
IDS_ASH_PHONE_HUB_NOTIFICATION_OPT_IN_DESCRIPTION));
// Set up layout row for the buttons.
layout->StartRow(views::GridLayout::kFixedSize, kColumnSetId);
auto* button_container =
layout->AddView(std::make_unique<views::View>(), 1, 1,
views::GridLayout::TRAILING, views::GridLayout::CENTER);
button_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal, gfx::Insets(),
kButtonSpacingDip));
button_container->SetBorder(
views::CreateEmptyBorder(kButtonContainerBorderInsets));
dismiss_button_ =
button_container->AddChildView(std::make_unique<views::LabelButton>(
this, l10n_util::GetStringUTF16(
IDS_ASH_PHONE_HUB_NOTIFICATION_OPT_IN_DISMISS_BUTTON)));
dismiss_button_->set_tag(kDismissButtonTag);
dismiss_button_->SetEnabledTextColors(
AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kTextColorPrimary));
set_up_button_ =
button_container->AddChildView(std::make_unique<RoundedLabelButton>(
this, l10n_util::GetStringUTF16(
IDS_ASH_PHONE_HUB_NOTIFICATION_OPT_IN_SET_UP_BUTTON)));
set_up_button_->set_tag(kSetUpButtonTag);
}
BEGIN_METADATA(NotificationOptInView, views::View)
END_METADATA
} // namespace ash
// 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.
#ifndef ASH_SYSTEM_PHONEHUB_NOTIFICATION_OPT_IN_VIEW_H_
#define ASH_SYSTEM_PHONEHUB_NOTIFICATION_OPT_IN_VIEW_H_
#include "ash/ash_export.h"
#include "ash/system/unified/rounded_label_button.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/metadata/metadata_header_macros.h"
#include "ui/views/view.h"
namespace views {
class Label;
class LabelButton;
} // namespace views
namespace ash {
class TrayBubbleView;
// An additional entry point shown on the Phone Hub bubble for the user to grant
// access or opt out for notifications from the phone.
class ASH_EXPORT NotificationOptInView : public views::View,
public views::ButtonListener {
public:
METADATA_HEADER(NotificationOptInView);
explicit NotificationOptInView(TrayBubbleView* bubble_view);
NotificationOptInView(const NotificationOptInView&) = delete;
NotificationOptInView& operator=(const NotificationOptInView&) = delete;
~NotificationOptInView() override;
// views::ButtonListener:
void ButtonPressed(views::Button* sender, const ui::Event& event) override;
views::View* set_up_button_for_testing() { return set_up_button_; }
views::View* dismiss_button_for_testing() { return dismiss_button_; }
private:
void InitLayout();
// Main components of this view. Owned by view hierarchy.
views::Label* text_label_ = nullptr;
RoundedLabelButton* set_up_button_ = nullptr;
views::LabelButton* dismiss_button_ = nullptr;
TrayBubbleView* bubble_view_ = nullptr;
};
} // namespace ash
#endif // ASH_SYSTEM_PHONEHUB_NOTIFICATION_OPT_IN_VIEW_H_
......@@ -4,12 +4,15 @@
#include "ash/system/phonehub/phone_hub_tray.h"
#include <memory>
#include "ash/accessibility/accessibility_controller_impl.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h"
#include "ash/system/phonehub/notification_opt_in_view.h"
#include "ash/system/phonehub/phone_status_view.h"
#include "ash/system/phonehub/quick_actions_view.h"
#include "ash/system/phonehub/task_continuation_view.h"
......@@ -21,6 +24,7 @@
#include "ash/system/tray/tray_popup_utils.h"
#include "ash/system/tray/tray_utils.h"
#include "base/bind.h"
#include "chromeos/components/phonehub/notification_access_manager.h"
#include "chromeos/components/phonehub/phone_hub_manager.h"
#include "chromeos/components/phonehub/phone_model.h"
#include "chromeos/constants/chromeos_features.h"
......@@ -66,6 +70,14 @@ class PhoneHubView : public views ::View {
AddSeparator();
// TODO(meilinw): handle the case when the user has dismissed this opt in
// view once, we shouldn't show it again.
if (!phone_hub_manager->GetNotificationAccessManager()
->HasAccessBeenGranted()) {
bubble_view_->AddChildView(
std::make_unique<NotificationOptInView>(bubble_view_));
}
setup_layered_view(
bubble_view_->AddChildView(std::make_unique<QuickActionsView>()));
......
......@@ -4,16 +4,34 @@
#include "ash/system/phonehub/phone_hub_tray.h"
#include "ash/public/cpp/test/test_new_window_delegate.h"
#include "ash/system/phonehub/notification_opt_in_view.h"
#include "ash/system/phonehub/phone_hub_view_ids.h"
#include "ash/system/status_area_widget.h"
#include "ash/system/status_area_widget_test_helper.h"
#include "ash/test/ash_test_base.h"
#include "base/test/scoped_feature_list.h"
#include "chromeos/components/phonehub/fake_notification_access_manager.h"
#include "chromeos/components/phonehub/fake_phone_hub_manager.h"
#include "chromeos/constants/chromeos_features.h"
#include "ui/events/event.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace ash {
namespace {
// A mock implementation of |NewWindowDelegate| for use in tests.
class MockNewWindowDelegate : public testing::NiceMock<TestNewWindowDelegate> {
public:
// TestNewWindowDelegate:
MOCK_METHOD(void,
NewTabWithUrl,
(const GURL& url, bool from_user_interaction),
(override));
};
} // namespace
class PhoneHubTrayTest : public AshTestBase {
public:
PhoneHubTrayTest() = default;
......@@ -36,15 +54,39 @@ class PhoneHubTrayTest : public AshTestBase {
return phone_hub_manager_.fake_feature_status_provider();
}
chromeos::phonehub::FakeNotificationAccessManager*
GetNotificationAccessManager() {
return phone_hub_manager_.fake_notification_access_manager();
}
void ClickTrayButton() {
ui::GestureEvent tap(0, 0, 0, base::TimeTicks(),
ui::GestureEventDetails(ui::ET_GESTURE_TAP));
phone_hub_tray_->PerformAction(tap);
}
// Simulate a mouse click on the given view.
// Waits for the event to be processed.
void ClickOnAndWait(const views::View* view) {
ui::test::EventGenerator* generator = GetEventGenerator();
generator->MoveMouseTo(view->GetBoundsInScreen().CenterPoint());
generator->ClickLeftButton();
base::RunLoop().RunUntilIdle();
}
MockNewWindowDelegate& new_window_delegate() { return new_window_delegate_; }
NotificationOptInView* notification_opt_in_view() {
return static_cast<NotificationOptInView*>(
phone_hub_tray_->GetBubbleView()->GetViewByID(
PhoneHubViewID::kNotificationOptInView));
}
protected:
PhoneHubTray* phone_hub_tray_ = nullptr;
chromeos::phonehub::FakePhoneHubManager phone_hub_manager_;
MockNewWindowDelegate new_window_delegate_;
base::test::ScopedFeatureList feature_list_;
};
......@@ -83,4 +125,45 @@ TEST_F(PhoneHubTrayTest, ClickTrayButton) {
EXPECT_FALSE(phone_hub_tray_->is_active());
}
TEST_F(PhoneHubTrayTest, ShowNotificationOptInViewWhenAccessNotGranted) {
GetNotificationAccessManager()->SetHasAccessBeenGranted(false);
ClickTrayButton();
EXPECT_TRUE(notification_opt_in_view());
EXPECT_TRUE(notification_opt_in_view()->GetVisible());
// Simulate a click on the dimiss button.
ClickOnAndWait(notification_opt_in_view()->dismiss_button_for_testing());
// The view should be dismissed on button clicked.
EXPECT_FALSE(notification_opt_in_view()->GetVisible());
}
TEST_F(PhoneHubTrayTest, HideNotificationOptInViewWhenAccessHasBeenGranted) {
GetNotificationAccessManager()->SetHasAccessBeenGranted(true);
ClickTrayButton();
EXPECT_FALSE(notification_opt_in_view());
}
TEST_F(PhoneHubTrayTest, StartNotificationSetUpFlow) {
GetNotificationAccessManager()->SetHasAccessBeenGranted(false);
ClickTrayButton();
EXPECT_TRUE(notification_opt_in_view());
EXPECT_TRUE(notification_opt_in_view()->GetVisible());
// Clicking on the set up button should open the corresponding settings page
// for the notification set up flow.
EXPECT_CALL(new_window_delegate(), NewTabWithUrl)
.WillOnce([](const GURL& url, bool from_user_interaction) {
EXPECT_EQ(GURL("chrome://os-settings/multidevice/features"), url);
EXPECT_TRUE(from_user_interaction);
});
ClickOnAndWait(notification_opt_in_view()->set_up_button_for_testing());
}
} // namespace ash
// 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.
#ifndef ASH_SYSTEM_PHONEHUB_PHONE_HUB_VIEW_IDS_H_
#define ASH_SYSTEM_PHONEHUB_PHONE_HUB_VIEW_IDS_H_
namespace ash {
// IDs used for the main views that compose the Phone Hub bubble view.
// Use these for easy access to the views during the unittests.
// Note that these IDs are only guaranteed to be unique inside
// the bubble view.
enum PhoneHubViewID {
// We start from 1 because 0 is the default view ID.
kPhoneStatusView = 1,
kNotificationOptInView,
kQuickActionsView,
kTaskContinuationView,
};
} // namespace ash
#endif // ASH_SYSTEM_PHONEHUB_PHONE_HUB_VIEW_IDS_H_
......@@ -9,6 +9,7 @@
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h"
#include "ash/system/phonehub/phone_hub_view_ids.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/tray/tray_popup_item_style.h"
#include "base/i18n/number_formatting.h"
......@@ -57,6 +58,8 @@ PhoneStatusView::PhoneStatusView(chromeos::phonehub::PhoneModel* phone_model)
mobile_provider_label_(new views::Label),
battery_icon_(new views::ImageView),
battery_label_(new views::Label) {
SetID(PhoneHubViewID::kPhoneStatusView);
ConfigureTriViewContainer(TriView::Container::START);
ConfigureTriViewContainer(TriView::Container::CENTER);
ConfigureTriViewContainer(TriView::Container::END);
......
......@@ -7,6 +7,7 @@
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h"
#include "ash/system/phonehub/phone_hub_view_ids.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/unified/feature_pod_button.h"
#include "ui/base/l10n/l10n_util.h"
......@@ -46,6 +47,8 @@ QuickActionsItem::QuickActionsItem(views::ButtonListener* listener,
: icon_button_(new FeaturePodIconButton(listener, true /* is_togglable */)),
label_(new views::Label),
sub_label_(new views::Label) {
SetID(PhoneHubViewID::kQuickActionsView);
auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical, gfx::Insets(),
kUnifiedFeaturePodSpacing));
......
......@@ -7,6 +7,7 @@
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h"
#include "ash/system/phonehub/continue_browsing_chip.h"
#include "ash/system/phonehub/phone_hub_view_ids.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/views/controls/label.h"
......@@ -52,6 +53,8 @@ class HeaderView : public views::View {
TaskContinuationView::TaskContinuationView(
chromeos::phonehub::PhoneModel* phone_model)
: phone_model_(phone_model) {
SetID(PhoneHubViewID::kTaskContinuationView);
phone_model_->AddObserver(this);
auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
......
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