123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905 |
- // 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/phone_hub_notification_controller.h"
- #include "ash/components/multidevice/logging/logging.h"
- #include "ash/components/phonehub/notification.h"
- #include "ash/components/phonehub/notification_interaction_handler.h"
- #include "ash/components/phonehub/phone_hub_manager.h"
- #include "ash/components/phonehub/phone_model.h"
- #include "ash/constants/ash_features.h"
- #include "ash/constants/notifier_catalogs.h"
- #include "ash/public/cpp/notification_utils.h"
- #include "ash/public/cpp/system_tray_client.h"
- #include "ash/resources/vector_icons/vector_icons.h"
- #include "ash/shell.h"
- #include "ash/strings/grit/ash_strings.h"
- #include "ash/system/message_center/ash_notification_view.h"
- #include "ash/system/message_center/message_view_factory.h"
- #include "ash/system/model/system_tray_model.h"
- #include "ash/system/phonehub/phone_hub_metrics.h"
- #include "ash/system/tray/tray_popup_utils.h"
- #include "base/bind.h"
- #include "base/containers/contains.h"
- #include "base/logging.h"
- #include "base/memory/weak_ptr.h"
- #include "base/strings/strcat.h"
- #include "base/strings/string_number_conversions.h"
- #include "base/strings/utf_string_conversions.h"
- #include "base/timer/timer.h"
- #include "ui/base/l10n/l10n_util.h"
- #include "ui/base/resource/resource_bundle.h"
- #include "ui/display/types/display_constants.h"
- #include "ui/gfx/text_elider.h"
- #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/notification_header_view.h"
- #include "ui/message_center/views/notification_view.h"
- #include "ui/views/controls/label.h"
- #include "ui/views/controls/textfield/textfield.h"
- #include "ui/views/view.h"
- namespace ash {
- using phone_hub_metrics::NotificationInteraction;
- using phonehub::proto::CameraRollItemMetadata;
- namespace {
- const char kNotifierId[] = "chrome://phonehub";
- const char kNotifierIdSeparator[] = "-";
- const char kPhoneHubInstantTetherNotificationId[] =
- "chrome://phonehub-instant-tether";
- const char kPhoneHubCameraRollNotificationId[] =
- "chrome://phonehub-camera-roll";
- const char kNotificationCustomViewType[] = "phonehub";
- const char kNotificationCustomCallViewType[] = "phonehub-call";
- const int kReplyButtonIndex = 0;
- const int kNotificationHeaderTextWidth = 180;
- const int kNotificationAppNameMaxWidth = 140;
- // The max age since a notification's creation on the Android for it to get
- // shown in a heads-up pop up. Notifications older than this will only silently
- // get added.
- constexpr base::TimeDelta kMaxRecentNotificationAge = base::Seconds(15);
- // The amount of time the reply button is disabled after sending an inline
- // reply. This is used to make sure that all the replies are received by the
- // phone in a correct order (a reply sent right after another could cause it to
- // be received before the former one).
- constexpr base::TimeDelta kInlineReplyDisableTime = base::Seconds(1);
- class PhoneHubNotificationView : public message_center::NotificationView {
- public:
- explicit PhoneHubNotificationView(
- const message_center::Notification& notification,
- const std::u16string& phone_name)
- : message_center::NotificationView(notification) {
- // Add customized header.
- message_center::NotificationHeaderView* header_row =
- static_cast<message_center::NotificationHeaderView*>(
- GetViewByID(message_center::NotificationView::kHeaderRow));
- views::View* app_name_view =
- GetViewByID(message_center::NotificationView::kAppNameView);
- views::Label* summary_text_view = static_cast<views::Label*>(
- GetViewByID(message_center::NotificationView::kSummaryTextView));
- // The app name should be displayed in full, leaving the rest of the space
- // for device name. App name will only be truncated when it reached it
- // maximum width.
- int app_name_width = std::min(app_name_view->GetPreferredSize().width(),
- kNotificationAppNameMaxWidth);
- int device_name_width = kNotificationHeaderTextWidth - app_name_width;
- header_row->SetSummaryText(
- gfx::ElideText(phone_name, summary_text_view->font_list(),
- device_name_width, gfx::ELIDE_TAIL));
- custom_view_type_ = notification.custom_view_type();
- if (custom_view_type_ == kNotificationCustomCallViewType) {
- // Expand the action buttons row by default for Call Style notification.
- SetManuallyExpandedOrCollapsed(true);
- SetExpanded(true);
- return;
- }
- action_buttons_row_ =
- GetViewByID(message_center::NotificationView::kActionButtonsRow);
- if (!action_buttons_row_->children().empty())
- reply_button_ = static_cast<views::View*>(
- action_buttons_row_->children()[kReplyButtonIndex]);
- inline_reply_ = static_cast<message_center::NotificationInputContainer*>(
- GetViewByID(message_center::NotificationView::kInlineReply));
- }
- ~PhoneHubNotificationView() override = default;
- PhoneHubNotificationView(const PhoneHubNotificationView&) = delete;
- PhoneHubNotificationView& operator=(const PhoneHubNotificationView&) = delete;
- // message_center::NotificationViewBase
- void ActionButtonPressed(size_t index, const ui::Event& event) override {
- if (custom_view_type_ == kNotificationCustomCallViewType) {
- message_center::MessageCenter::Get()->ClickOnNotificationButton(
- notification_id(), static_cast<int>(index));
- } else {
- message_center::NotificationView::ActionButtonPressed(index, event);
- }
- }
- // message_center::NotificationView:
- void OnNotificationInputSubmit(size_t index,
- const std::u16string& text) override {
- message_center::NotificationView::OnNotificationInputSubmit(index, text);
- DCHECK(reply_button_);
- // After sending a reply, take the UI back to action buttons and clear out
- // text input.
- inline_reply_->SetVisible(false);
- action_buttons_row_->SetVisible(true);
- inline_reply_->textfield()->SetText(std::u16string());
- // Briefly disable reply button.
- reply_button_->SetEnabled(false);
- enable_reply_timer_ = std::make_unique<base::OneShotTimer>();
- enable_reply_timer_->Start(
- FROM_HERE, kInlineReplyDisableTime,
- base::BindOnce(&PhoneHubNotificationView::EnableReplyButton,
- base::Unretained(this)));
- }
- void EnableReplyButton() {
- reply_button_->SetEnabled(true);
- enable_reply_timer_.reset();
- }
- private:
- // Owned by view hierarchy.
- views::View* action_buttons_row_ = nullptr;
- views::View* reply_button_ = nullptr;
- message_center::NotificationInputContainer* inline_reply_ = nullptr;
- // Timer that fires to enable reply button after a brief period of time.
- std::unique_ptr<base::OneShotTimer> enable_reply_timer_;
- std::string custom_view_type_;
- };
- class PhoneHubAshNotificationView : public AshNotificationView {
- public:
- explicit PhoneHubAshNotificationView(
- const message_center::Notification& notification,
- bool shown_in_popup,
- const std::u16string& phone_name)
- : AshNotificationView(notification, shown_in_popup) {
- // Add customized header.
- message_center::NotificationHeaderView* header_row =
- static_cast<message_center::NotificationHeaderView*>(
- GetViewByID(message_center::NotificationView::kHeaderRow));
- views::View* app_name_view =
- GetViewByID(message_center::NotificationView::kAppNameView);
- views::Label* summary_text_view = static_cast<views::Label*>(
- GetViewByID(message_center::NotificationView::kSummaryTextView));
- // The app name should be displayed in full, leaving the rest of the space
- // for device name. App name will only be truncated when it reached it
- // maximum width.
- int app_name_width = std::min(app_name_view->GetPreferredSize().width(),
- kNotificationAppNameMaxWidth);
- int device_name_width = kNotificationHeaderTextWidth - app_name_width;
- header_row->SetSummaryText(
- gfx::ElideText(phone_name, summary_text_view->font_list(),
- device_name_width, gfx::ELIDE_TAIL));
- custom_view_type_ = notification.custom_view_type();
- if (custom_view_type_ == kNotificationCustomCallViewType) {
- // Expand the action buttons row by default for Call Style notification.
- SetManuallyExpandedOrCollapsed(true);
- SetExpanded(true);
- return;
- }
- action_buttons_row_ =
- GetViewByID(message_center::NotificationView::kActionButtonsRow);
- if (!action_buttons_row_->children().empty())
- reply_button_ = static_cast<views::View*>(
- action_buttons_row_->children()[kReplyButtonIndex]);
- inline_reply_ = static_cast<message_center::NotificationInputContainer*>(
- GetViewByID(message_center::NotificationView::kInlineReply));
- }
- ~PhoneHubAshNotificationView() override = default;
- PhoneHubAshNotificationView(const PhoneHubAshNotificationView&) = delete;
- PhoneHubAshNotificationView& operator=(const PhoneHubAshNotificationView&) =
- delete;
- // message_center::NotificationViewBase
- void ActionButtonPressed(size_t index, const ui::Event& event) override {
- if (custom_view_type_ == kNotificationCustomCallViewType) {
- message_center::MessageCenter::Get()->ClickOnNotificationButton(
- notification_id(), static_cast<int>(index));
- } else {
- AshNotificationView::ActionButtonPressed(index, event);
- }
- }
- // message_center::NotificationView:
- void OnNotificationInputSubmit(size_t index,
- const std::u16string& text) override {
- AshNotificationView::OnNotificationInputSubmit(index, text);
- DCHECK(reply_button_);
- // After sending a reply, take the UI back to action buttons and clear out
- // text input.
- inline_reply_->SetVisible(false);
- action_buttons_row_->SetVisible(true);
- inline_reply_->textfield()->SetText(std::u16string());
- // Briefly disable reply button.
- reply_button_->SetEnabled(false);
- enable_reply_timer_.Start(
- FROM_HERE, kInlineReplyDisableTime,
- base::BindOnce(&PhoneHubAshNotificationView::EnableReplyButton,
- base::Unretained(this)));
- }
- void EnableReplyButton() {
- reply_button_->SetEnabled(true);
- enable_reply_timer_.AbandonAndStop();
- }
- private:
- // Owned by view hierarchy.
- views::View* action_buttons_row_ = nullptr;
- views::View* reply_button_ = nullptr;
- message_center::NotificationInputContainer* inline_reply_ = nullptr;
- // Timer that fires to enable reply button after a brief period of time.
- base::OneShotTimer enable_reply_timer_;
- std::string custom_view_type_;
- };
- } // namespace
- // Delegate for the displayed ChromeOS notification.
- class PhoneHubNotificationController::NotificationDelegate
- : public message_center::NotificationObserver {
- public:
- NotificationDelegate(PhoneHubNotificationController* controller,
- int64_t phone_hub_id,
- const std::string& cros_id,
- phonehub::Notification::Category category)
- : controller_(controller),
- phone_hub_id_(phone_hub_id),
- cros_id_(cros_id),
- category_(category) {}
- virtual ~NotificationDelegate() { controller_ = nullptr; }
- NotificationDelegate(const NotificationDelegate&) = delete;
- NotificationDelegate& operator=(const NotificationDelegate&) = delete;
- // Returns a scoped_refptr that can be passed in the
- // message_center::Notification constructor.
- scoped_refptr<message_center::NotificationDelegate> AsScopedRefPtr() {
- return base::MakeRefCounted<message_center::ThunkNotificationDelegate>(
- weak_ptr_factory_.GetWeakPtr());
- }
- // Called by the controller to remove the notification from the message
- // center.
- void Remove() {
- removed_by_phone_hub_ = true;
- message_center::MessageCenter::Get()->RemoveNotification(cros_id_,
- /*by_user=*/false);
- }
- // message_center::NotificationObserver:
- void Close(bool by_user) override {
- if (!controller_ || removed_by_phone_hub_)
- return;
- if (category_ == phonehub::Notification::Category::kIncomingCall ||
- category_ == phonehub::Notification::Category::kOngoingCall) {
- // TODO(b/203734343): Wait for UX confirm. Call notification is not
- // dismissible in android phone.
- PA_LOG(INFO)
- << "Can't dismiss an Incoming/Ongoing call notification with id: "
- << phone_hub_id_ << ".";
- return;
- }
- controller_->DismissNotification(phone_hub_id_);
- }
- void Click(const absl::optional<int>& button_index,
- const absl::optional<std::u16string>& reply) override {
- if (!controller_)
- return;
- if (!button_index.has_value()) {
- controller_->HandleNotificationBodyClick(
- phone_hub_id_, controller_->manager_->GetNotification(phone_hub_id_)
- ->app_metadata());
- return;
- }
- if (category_ == phonehub::Notification::Category::kIncomingCall) {
- // TODO(b/199223417): Implement actions.
- switch (*button_index) {
- case BUTTON_ANSWER:
- PA_LOG(INFO) << "answer button clicked";
- break;
- case BUTTON_DECLINE:
- PA_LOG(INFO) << "decline button clicked";
- break;
- }
- } else if (category_ == phonehub::Notification::Category::kOngoingCall) {
- switch (*button_index) {
- case BUTTON_HANGUP:
- PA_LOG(INFO) << "hangup button clicked";
- break;
- }
- } else if (button_index.value() == kReplyButtonIndex && reply.has_value()) {
- controller_->SendInlineReply(phone_hub_id_, reply.value());
- }
- }
- void SettingsClick() override {
- if (controller_)
- controller_->OpenSettings();
- }
- phonehub::Notification::Category Category() { return category_; }
- private:
- // Incoming call buttons that appear in notifications.
- enum IncomingCallButton { BUTTON_DECLINE, BUTTON_ANSWER };
- // Ongoing call buttons that appear in notifications.
- enum OngoingCallButton { BUTTON_HANGUP };
- // The parent controller, which owns this object.
- PhoneHubNotificationController* controller_ = nullptr;
- // The notification ID tracked by PhoneHub.
- const int64_t phone_hub_id_;
- // The notification ID tracked by the CrOS message center.
- const std::string cros_id_;
- // The category of the notification.
- phonehub::Notification::Category category_;
- // Flag set if the notification was removed by PhoneHub so we avoid a cycle.
- bool removed_by_phone_hub_ = false;
- base::WeakPtrFactory<NotificationDelegate> weak_ptr_factory_{this};
- };
- PhoneHubNotificationController::PhoneHubNotificationController() {
- if (!MessageViewFactory::HasCustomNotificationViewFactory(
- kNotificationCustomViewType)) {
- MessageViewFactory::SetCustomNotificationViewFactory(
- kNotificationCustomViewType,
- base::BindRepeating(
- &PhoneHubNotificationController::CreateCustomNotificationView,
- weak_ptr_factory_.GetWeakPtr()));
- }
- if (!MessageViewFactory::HasCustomNotificationViewFactory(
- kNotificationCustomCallViewType)) {
- MessageViewFactory::SetCustomNotificationViewFactory(
- kNotificationCustomCallViewType,
- base::BindRepeating(
- &PhoneHubNotificationController::CreateCustomActionNotificationView,
- weak_ptr_factory_.GetWeakPtr()));
- }
- }
- PhoneHubNotificationController::~PhoneHubNotificationController() {
- if (manager_)
- manager_->RemoveObserver(this);
- if (tether_controller_)
- tether_controller_->RemoveObserver(this);
- if (camera_roll_manager_)
- camera_roll_manager_->RemoveObserver(this);
- }
- void PhoneHubNotificationController::SetManager(
- phonehub::PhoneHubManager* phone_hub_manager) {
- if (manager_)
- manager_->RemoveObserver(this);
- if (phone_hub_manager) {
- manager_ = phone_hub_manager->GetNotificationManager();
- manager_->AddObserver(this);
- } else {
- manager_ = nullptr;
- }
- if (tether_controller_)
- tether_controller_->RemoveObserver(this);
- if (phone_hub_manager) {
- tether_controller_ = phone_hub_manager->GetTetherController();
- tether_controller_->AddObserver(this);
- } else {
- tether_controller_ = nullptr;
- }
- if (camera_roll_manager_)
- camera_roll_manager_->RemoveObserver(this);
- if (phone_hub_manager) {
- camera_roll_manager_ = phone_hub_manager->GetCameraRollManager();
- if (camera_roll_manager_) {
- camera_roll_manager_->AddObserver(this);
- } else {
- camera_roll_manager_ = nullptr;
- }
- } else {
- camera_roll_manager_ = nullptr;
- }
- if (phone_hub_manager)
- phone_model_ = phone_hub_manager->GetPhoneModel();
- else
- phone_model_ = nullptr;
- if (phone_hub_manager) {
- notification_interaction_handler_ =
- phone_hub_manager->GetNotificationInteractionHandler();
- } else {
- notification_interaction_handler_ = nullptr;
- }
- }
- const std::u16string PhoneHubNotificationController::GetPhoneName() const {
- if (!phone_model_)
- return std::u16string();
- return phone_model_->phone_name().value_or(std::u16string());
- }
- void PhoneHubNotificationController::OnNotificationsAdded(
- const base::flat_set<int64_t>& notification_ids) {
- for (int64_t id : notification_ids) {
- SetNotification(manager_->GetNotification(id),
- /*is_update=*/false);
- }
- LogNotificationCount();
- }
- void PhoneHubNotificationController::OnNotificationsUpdated(
- const base::flat_set<int64_t>& notification_ids) {
- for (int64_t id : notification_ids) {
- SetNotification(manager_->GetNotification(id),
- /*is_update=*/true);
- }
- }
- void PhoneHubNotificationController::OnNotificationsRemoved(
- const base::flat_set<int64_t>& notification_ids) {
- for (int64_t id : notification_ids) {
- auto it = notification_map_.find(id);
- if (it == notification_map_.end())
- continue;
- it->second->Remove();
- notification_map_.erase(it);
- }
- LogNotificationCount();
- }
- void PhoneHubNotificationController::OnAttemptConnectionScanFailed() {
- // Add a notification if tether failed.
- scoped_refptr<message_center::NotificationDelegate> delegate =
- base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
- base::BindRepeating([](absl::optional<int> button_index) {
- // When clicked, open Tether Settings page if we can open WebUI
- // settings, otherwise do nothing.
- if (TrayPopupUtils::CanOpenWebUISettings()) {
- Shell::Get()
- ->system_tray_model()
- ->client()
- ->ShowTetherNetworkSettings();
- } else {
- LOG(WARNING) << "Cannot open Tether Settings since it's not "
- "possible to opening WebUI settings";
- }
- }));
- std::unique_ptr<message_center::Notification> notification =
- CreateSystemNotification(
- message_center::NOTIFICATION_TYPE_SIMPLE,
- kPhoneHubInstantTetherNotificationId,
- l10n_util::GetStringUTF16(
- IDS_ASH_PHONE_HUB_NOTIFICATION_HOTSPOT_FAILED_TITLE),
- l10n_util::GetStringUTF16(
- IDS_ASH_PHONE_HUB_NOTIFICATION_HOTSPOT_FAILED_MESSAGE),
- std::u16string() /*display_source */, GURL() /* origin_url */,
- message_center::NotifierId(
- message_center::NotifierType::SYSTEM_COMPONENT,
- kPhoneHubInstantTetherNotificationId,
- NotificationCatalogName::kPhoneHubTetherFailed),
- message_center::RichNotificationData(), std::move(delegate),
- kPhoneHubEnableHotspotIcon,
- message_center::SystemNotificationWarningLevel::NORMAL);
- message_center::MessageCenter::Get()->AddNotification(
- std::move(notification));
- }
- void PhoneHubNotificationController::OnCameraRollDownloadError(
- DownloadErrorType error_type,
- const CameraRollItemMetadata& metadata) {
- std::unique_ptr<message_center::Notification> notification;
- switch (error_type) {
- case DownloadErrorType::kGenericError:
- notification = CreateCameraRollGenericNotification(metadata);
- break;
- case DownloadErrorType::kInsufficientStorage:
- notification = CreateCameraRollStorageNotification(metadata);
- break;
- case DownloadErrorType::kNetworkConnection:
- notification = CreateCameraRollNetworkNotification(metadata);
- break;
- }
- message_center::MessageCenter::Get()->AddNotification(
- std::move(notification));
- }
- std::unique_ptr<message_center::Notification>
- PhoneHubNotificationController::CreateCameraRollGenericNotification(
- const CameraRollItemMetadata& metadata) {
- scoped_refptr<message_center::NotificationDelegate> delegate =
- base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
- base::BindRepeating(
- [](phonehub::CameraRollManager* manager,
- const CameraRollItemMetadata& metadata,
- absl::optional<int> button_index) {
- // When button is clicked, close notification and retry the
- // download
- if (button_index.has_value()) {
- message_center::MessageCenter::Get()->RemoveNotification(
- kPhoneHubCameraRollNotificationId, /*by_user=*/true);
- manager->DownloadItem(metadata);
- }
- },
- camera_roll_manager_, metadata));
- message_center::NotifierId notifier_id(
- message_center::NotifierType::PHONE_HUB,
- kPhoneHubCameraRollNotificationId);
- message_center::RichNotificationData optional_fields;
- message_center::ButtonInfo button;
- button.title = l10n_util::GetStringUTF16(
- IDS_ASH_PHONE_HUB_CAMERA_ROLL_ERROR_GENERIC_ACTION);
- optional_fields.buttons.push_back(button);
- return CreateSystemNotification(
- message_center::NOTIFICATION_TYPE_SIMPLE,
- kPhoneHubCameraRollNotificationId,
- l10n_util::GetStringUTF16(IDS_ASH_PHONE_HUB_CAMERA_ROLL_ERROR_TITLE),
- l10n_util::GetStringFUTF16(
- IDS_ASH_PHONE_HUB_CAMERA_ROLL_ERROR_GENERIC_BODY,
- base::UTF8ToUTF16(metadata.file_name())),
- l10n_util::GetStringUTF16(IDS_ASH_PHONE_HUB_TRAY_ACCESSIBLE_NAME),
- /*origin_url=*/GURL(), notifier_id, optional_fields, std::move(delegate),
- kPhoneHubCameraRollMenuDownloadIcon,
- message_center::SystemNotificationWarningLevel::WARNING);
- }
- std::unique_ptr<message_center::Notification>
- PhoneHubNotificationController::CreateCameraRollStorageNotification(
- const CameraRollItemMetadata& metadata) {
- scoped_refptr<message_center::NotificationDelegate> delegate =
- base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
- base::BindRepeating([](absl::optional<int> button_index) {
- // When button is clicked, close notification and open Storage
- // Management Settings page if we can open WebUI settings.
- if (button_index.has_value()) {
- message_center::MessageCenter::Get()->RemoveNotification(
- kPhoneHubCameraRollNotificationId, /*by_user=*/true);
- if (TrayPopupUtils::CanOpenWebUISettings()) {
- Shell::Get()
- ->system_tray_model()
- ->client()
- ->ShowStorageSettings();
- } else {
- PA_LOG(WARNING)
- << "Cannot open Storage Management Settings since it's not "
- "possible to open WebUI settings";
- }
- }
- }));
- message_center::NotifierId notifier_id(
- message_center::NotifierType::PHONE_HUB,
- kPhoneHubCameraRollNotificationId);
- message_center::RichNotificationData optional_fields;
- message_center::ButtonInfo button;
- button.title = l10n_util::GetStringUTF16(
- IDS_ASH_PHONE_HUB_CAMERA_ROLL_ERROR_STORAGE_ACTION);
- optional_fields.buttons.push_back(button);
- return CreateSystemNotification(
- message_center::NOTIFICATION_TYPE_SIMPLE,
- kPhoneHubCameraRollNotificationId,
- l10n_util::GetStringUTF16(IDS_ASH_PHONE_HUB_CAMERA_ROLL_ERROR_TITLE),
- l10n_util::GetStringFUTF16(
- IDS_ASH_PHONE_HUB_CAMERA_ROLL_ERROR_STORAGE_BODY,
- base::UTF8ToUTF16(metadata.file_name())),
- l10n_util::GetStringUTF16(IDS_ASH_PHONE_HUB_TRAY_ACCESSIBLE_NAME),
- /*origin_url=*/GURL(), notifier_id, optional_fields, std::move(delegate),
- kPhoneHubCameraRollMenuDownloadIcon,
- message_center::SystemNotificationWarningLevel::WARNING);
- }
- std::unique_ptr<message_center::Notification>
- PhoneHubNotificationController::CreateCameraRollNetworkNotification(
- const CameraRollItemMetadata& metadata) {
- scoped_refptr<message_center::NotificationDelegate> delegate =
- base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
- base::BindRepeating([](absl::optional<int> button_index) {
- // When button is clicked, close notification and open Network
- // Settings page if we can open WebUI settings.
- if (button_index.has_value()) {
- message_center::MessageCenter::Get()->RemoveNotification(
- kPhoneHubCameraRollNotificationId, /*by_user=*/true);
- if (TrayPopupUtils::CanOpenWebUISettings()) {
- Shell::Get()->system_tray_model()->client()->ShowSettings(
- display::kInvalidDisplayId);
- } else {
- PA_LOG(WARNING)
- << "Cannot open Settings since it's not possible to open "
- "WebUI settings";
- }
- }
- }));
- message_center::NotifierId notifier_id(
- message_center::NotifierType::PHONE_HUB,
- kPhoneHubCameraRollNotificationId);
- message_center::RichNotificationData optional_fields;
- message_center::ButtonInfo button;
- button.title = l10n_util::GetStringUTF16(
- IDS_ASH_PHONE_HUB_CAMERA_ROLL_ERROR_NETWORK_ACTION);
- optional_fields.buttons.push_back(button);
- return CreateSystemNotification(
- message_center::NOTIFICATION_TYPE_SIMPLE,
- kPhoneHubCameraRollNotificationId,
- l10n_util::GetStringUTF16(IDS_ASH_PHONE_HUB_CAMERA_ROLL_ERROR_TITLE),
- l10n_util::GetStringFUTF16(
- IDS_ASH_PHONE_HUB_CAMERA_ROLL_ERROR_NETWORK_BODY,
- base::UTF8ToUTF16(metadata.file_name())),
- l10n_util::GetStringUTF16(IDS_ASH_PHONE_HUB_TRAY_ACCESSIBLE_NAME),
- /*origin_url=*/GURL(), notifier_id, optional_fields, std::move(delegate),
- kPhoneHubCameraRollMenuDownloadIcon,
- message_center::SystemNotificationWarningLevel::WARNING);
- }
- void PhoneHubNotificationController::OpenSettings() {
- DCHECK(TrayPopupUtils::CanOpenWebUISettings());
- Shell::Get()->system_tray_model()->client()->ShowConnectedDevicesSettings();
- }
- void PhoneHubNotificationController::DismissNotification(
- int64_t notification_id) {
- CHECK(manager_);
- manager_->DismissNotification(notification_id);
- phone_hub_metrics::LogNotificationInteraction(
- NotificationInteraction::kDismiss);
- }
- void PhoneHubNotificationController::HandleNotificationBodyClick(
- int64_t notification_id,
- const phonehub::Notification::AppMetadata& app_metadata) {
- CHECK(manager_);
- if (!notification_interaction_handler_)
- return;
- const phonehub::Notification* notification =
- manager_->GetNotification(notification_id);
- if (!notification)
- return;
- if (notification->interaction_behavior() ==
- phonehub::Notification::InteractionBehavior::kOpenable) {
- notification_interaction_handler_->HandleNotificationClicked(
- notification_id, app_metadata);
- }
- }
- void PhoneHubNotificationController::SendInlineReply(
- int64_t notification_id,
- const std::u16string& inline_reply_text) {
- CHECK(manager_);
- manager_->SendInlineReply(notification_id, inline_reply_text);
- phone_hub_metrics::LogNotificationInteraction(
- NotificationInteraction::kInlineReply);
- }
- void PhoneHubNotificationController::LogNotificationCount() {
- int count = notification_map_.size();
- phone_hub_metrics::LogNotificationCount(count);
- }
- void PhoneHubNotificationController::SetNotification(
- const phonehub::Notification* notification,
- bool is_update) {
- int64_t phone_hub_id = notification->id();
- std::string cros_id = base::StrCat(
- {kNotifierId, kNotifierIdSeparator, base::NumberToString(phone_hub_id)});
- bool notification_already_exists =
- base::Contains(notification_map_, phone_hub_id);
- if (!notification_already_exists) {
- notification_map_[phone_hub_id] = std::make_unique<NotificationDelegate>(
- this, phone_hub_id, cros_id, notification->category());
- }
- NotificationDelegate* delegate = notification_map_[phone_hub_id].get();
- auto cros_notification =
- CreateNotification(notification, cros_id, delegate, is_update);
- if (notification->category() ==
- phonehub::Notification::Category::kIncomingCall ||
- notification->category() ==
- phonehub::Notification::Category::kOngoingCall) {
- cros_notification->set_custom_view_type(kNotificationCustomCallViewType);
- } else {
- cros_notification->set_custom_view_type(kNotificationCustomViewType);
- }
- phone_hub_metrics::LogNotificationMessageLength(
- cros_notification->message().length());
- auto* message_center = message_center::MessageCenter::Get();
- if (notification_already_exists)
- message_center->UpdateNotification(cros_id, std::move(cros_notification));
- else
- message_center->AddNotification(std::move(cros_notification));
- }
- std::unique_ptr<message_center::Notification>
- PhoneHubNotificationController::CreateNotification(
- const phonehub::Notification* notification,
- const std::string& cros_id,
- NotificationDelegate* delegate,
- bool is_update) {
- message_center::NotifierId notifier_id(
- message_center::NotifierType::PHONE_HUB, kNotifierId);
- auto notification_type = message_center::NOTIFICATION_TYPE_CUSTOM;
- std::u16string title = notification->title().value_or(std::u16string());
- std::u16string message =
- notification->text_content().value_or(std::u16string());
- auto app_metadata = notification->app_metadata();
- std::u16string display_source = app_metadata.visible_app_name;
- message_center::RichNotificationData optional_fields;
- optional_fields.small_image = app_metadata.icon;
- optional_fields.timestamp = notification->timestamp();
- optional_fields.accessible_name = l10n_util::GetStringFUTF16(
- IDS_ASH_PHONE_HUB_NOTIFICATION_ACCESSIBLE_NAME, display_source, title,
- message, PhoneHubNotificationController::GetPhoneName());
- if (app_metadata.icon_is_monochrome) {
- optional_fields.accent_color = app_metadata.icon_color;
- if (features::IsNotificationsRefreshEnabled()) {
- optional_fields.ignore_accent_color_for_small_image = true;
- optional_fields.ignore_accent_color_for_text = false;
- optional_fields.small_image_needs_additional_masking = true;
- } else {
- optional_fields.ignore_accent_color_for_small_image = false;
- optional_fields.ignore_accent_color_for_text = true;
- optional_fields.small_image_needs_additional_masking = false;
- }
- } else {
- optional_fields.ignore_accent_color_for_small_image = true;
- optional_fields.ignore_accent_color_for_text = false;
- optional_fields.small_image_needs_additional_masking = false;
- }
- auto shared_image = notification->shared_image();
- if (shared_image.has_value())
- optional_fields.image = shared_image.value();
- const gfx::Image& icon = notification->contact_image().value_or(gfx::Image());
- optional_fields.priority =
- GetSystemPriorityForNotification(notification, is_update);
- // If the notification was updated, set renotify to true so that the
- // notification pops up again and is visible to the user. See
- // https://crbug.com/1159063.
- if (is_update)
- optional_fields.renotify = true;
- switch (notification->category()) {
- case phonehub::Notification::Category::kIncomingCall: {
- message_center::ButtonInfo decline_button;
- decline_button.title = l10n_util::GetStringUTF16(
- IDS_ASH_PHONE_HUB_NOTIFICATION_CALL_DECLINE_BUTTON);
- optional_fields.buttons.push_back(decline_button);
- message_center::ButtonInfo answer_button;
- answer_button.title = l10n_util::GetStringUTF16(
- IDS_ASH_PHONE_HUB_NOTIFICATION_CALL_ANSWER_BUTTON);
- optional_fields.buttons.push_back(answer_button);
- break;
- }
- case phonehub::Notification::Category::kOngoingCall: {
- message_center::ButtonInfo hangup_button;
- hangup_button.title = l10n_util::GetStringUTF16(
- IDS_ASH_PHONE_HUB_NOTIFICATION_CALL_HANGUP_BUTTON);
- optional_fields.buttons.push_back(hangup_button);
- break;
- }
- default: {
- message_center::ButtonInfo reply_button;
- reply_button.title = l10n_util::GetStringUTF16(
- IDS_ASH_PHONE_HUB_NOTIFICATION_INLINE_REPLY_BUTTON);
- // Setting a placeholder is needed to show the input field
- reply_button.placeholder = std::u16string();
- optional_fields.buttons.push_back(reply_button);
- break;
- }
- }
- if (TrayPopupUtils::CanOpenWebUISettings()) {
- optional_fields.settings_button_handler =
- message_center::SettingsButtonHandler::DELEGATE;
- }
- return std::make_unique<message_center::Notification>(
- notification_type, cros_id, title, message,
- ui::ImageModel::FromImage(icon), display_source,
- /*origin_url=*/GURL(), notifier_id, optional_fields,
- delegate->AsScopedRefPtr());
- }
- int PhoneHubNotificationController::GetSystemPriorityForNotification(
- const phonehub::Notification* notification,
- bool is_update) {
- bool is_recent = (notification->timestamp() + kMaxRecentNotificationAge) >
- base::Time::Now();
- // Use MAX_PRIORITY, which causes the notification to be shown in a popup
- // so that users can see new messages come in as they are chatting.
- if (is_recent || is_update)
- return message_center::MAX_PRIORITY;
- // Silently add older notifications that are likely to be stale.
- return message_center::LOW_PRIORITY;
- }
- std::u16string GetPhoneName(base::WeakPtr<ash::PhoneHubNotificationController>
- notification_controller) {
- return (notification_controller) ? notification_controller->GetPhoneName()
- : std::u16string();
- }
- // static
- std::unique_ptr<message_center::MessageView>
- PhoneHubNotificationController::CreateCustomNotificationView(
- base::WeakPtr<PhoneHubNotificationController> notification_controller,
- const message_center::Notification& notification,
- bool shown_in_popup) {
- DCHECK(notification.custom_view_type() == kNotificationCustomViewType);
- if (features::IsNotificationsRefreshEnabled()) {
- return std::make_unique<PhoneHubAshNotificationView>(
- notification, shown_in_popup,
- ash::GetPhoneName(notification_controller));
- }
- return std::make_unique<PhoneHubNotificationView>(
- notification, ash::GetPhoneName(notification_controller));
- }
- // static
- std::unique_ptr<message_center::MessageView>
- PhoneHubNotificationController::CreateCustomActionNotificationView(
- base::WeakPtr<PhoneHubNotificationController> notification_controller,
- const message_center::Notification& notification,
- bool shown_in_popup) {
- DCHECK(notification.custom_view_type() == kNotificationCustomCallViewType);
- if (features::IsNotificationsRefreshEnabled()) {
- return std::make_unique<PhoneHubAshNotificationView>(
- notification, shown_in_popup,
- ash::GetPhoneName(notification_controller));
- }
- return std::make_unique<PhoneHubNotificationView>(
- notification, ash::GetPhoneName(notification_controller));
- }
- } // namespace ash
|