1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 #include "chrome/browser/chromeos/crostini/crostini_upgrade_available_notification.h"
5
6 #include "ash/public/cpp/notification_utils.h"
7 #include "base/callback.h"
8 #include "base/metrics/histogram_functions.h"
9 #include "chrome/browser/chromeos/crostini/crostini_manager.h"
10 #include "chrome/browser/notifications/notification_display_service.h"
11 #include "chrome/browser/ui/settings_window_manager_chromeos.h"
12 #include "chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_dialog.h"
13 #include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
14 #include "chrome/grit/generated_resources.h"
15 #include "components/vector_icons/vector_icons.h"
16 #include "ui/base/l10n/l10n_util.h"
17
18 namespace crostini {
19
20 const char kNotifierCrostiniUpgradeAvailable[] = "crostini.upgrade_available";
21
22 class CrostiniUpgradeAvailableNotificationDelegate
23 : public message_center::NotificationDelegate {
24 public:
CrostiniUpgradeAvailableNotificationDelegate(Profile * profile,base::WeakPtr<CrostiniUpgradeAvailableNotification> notification,base::OnceClosure closure)25 explicit CrostiniUpgradeAvailableNotificationDelegate(
26 Profile* profile,
27 base::WeakPtr<CrostiniUpgradeAvailableNotification> notification,
28 base::OnceClosure closure)
29 : profile_(profile),
30 notification_(notification),
31 closure_(std::move(closure)) {
32 CrostiniManager::GetForProfile(profile_)->UpgradePromptShown(
33 ContainerId::GetDefault());
34 }
35
Click(const base::Optional<int> & button_index,const base::Optional<base::string16> & reply)36 void Click(const base::Optional<int>& button_index,
37 const base::Optional<base::string16>& reply) override {
38 disposition_ =
39 CrostiniUpgradeAvailableNotificationClosed::kNotificationBody;
40 if (button_index && button_index.value() == 0) {
41 disposition_ = CrostiniUpgradeAvailableNotificationClosed::kUpgradeButton;
42 HandleButtonClick();
43 return;
44 }
45 HandleBodyClick();
46 }
47
HandleButtonClick()48 void HandleButtonClick() {
49 chromeos::CrostiniUpgraderDialog::Show(profile_, base::DoNothing());
50 if (notification_) {
51 notification_->UpgradeDialogShown();
52 }
53 Close(false);
54 }
55
HandleBodyClick()56 void HandleBodyClick() {
57 chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(
58 profile_, chromeos::settings::mojom::kCrostiniDetailsSubpagePath);
59 Close(false);
60 }
61
Close(bool by_user)62 void Close(bool by_user) override {
63 if (by_user) {
64 disposition_ = CrostiniUpgradeAvailableNotificationClosed::kByUser;
65 }
66 // Run the callback now. The notification might hang around after the
67 // closure has been run, so we need to guard it.
68 if (closure_) {
69 std::move(closure_).Run();
70 }
71 base::UmaHistogramEnumeration("Crostini.UpgradeAvailable", disposition_);
72 }
73
74 private:
75 ~CrostiniUpgradeAvailableNotificationDelegate() override = default;
76
77 CrostiniUpgradeAvailableNotificationClosed disposition_ =
78 CrostiniUpgradeAvailableNotificationClosed::kUnknown;
79 Profile* profile_; // Not owned.
80 base::WeakPtr<CrostiniUpgradeAvailableNotification> notification_;
81 base::OnceClosure closure_;
82
83 base::WeakPtrFactory<CrostiniUpgradeAvailableNotificationDelegate>
84 weak_ptr_factory_{this};
85
86 DISALLOW_COPY_AND_ASSIGN(CrostiniUpgradeAvailableNotificationDelegate);
87 };
88
89 std::unique_ptr<CrostiniUpgradeAvailableNotification>
Show(Profile * profile,base::OnceClosure closure)90 CrostiniUpgradeAvailableNotification::Show(Profile* profile,
91 base::OnceClosure closure) {
92 return std::make_unique<CrostiniUpgradeAvailableNotification>(
93 profile, std::move(closure));
94 }
95
CrostiniUpgradeAvailableNotification(Profile * profile,base::OnceClosure closure)96 CrostiniUpgradeAvailableNotification::CrostiniUpgradeAvailableNotification(
97 Profile* profile,
98 base::OnceClosure closure)
99 : profile_(profile) {
100 message_center::RichNotificationData rich_notification_data;
101 rich_notification_data.small_image = gfx::Image(gfx::CreateVectorIcon(
102 vector_icons::kFileDownloadIcon, 64, gfx::kGoogleBlue800));
103 rich_notification_data.accent_color = ash::kSystemNotificationColorNormal;
104 rich_notification_data.buttons.emplace_back(
105 message_center::ButtonInfo(l10n_util::GetStringUTF16(
106 IDS_CROSTINI_UPGRADE_AVAILABLE_NOTIFICATION_UPGRADE)));
107
108 notification_ = std::make_unique<message_center::Notification>(
109 message_center::NOTIFICATION_TYPE_MULTIPLE,
110 kNotifierCrostiniUpgradeAvailable,
111 l10n_util::GetStringUTF16(
112 IDS_CROSTINI_UPGRADE_AVAILABLE_NOTIFICATION_TITLE),
113 l10n_util::GetStringUTF16(
114 IDS_CROSTINI_UPGRADE_AVAILABLE_NOTIFICATION_BODY),
115 gfx::Image(), base::string16(), GURL(),
116 message_center::NotifierId(message_center::NotifierType::SYSTEM_COMPONENT,
117 kNotifierCrostiniUpgradeAvailable),
118 rich_notification_data,
119 base::MakeRefCounted<CrostiniUpgradeAvailableNotificationDelegate>(
120 profile_, weak_ptr_factory_.GetWeakPtr(), std::move(closure)));
121
122 notification_->SetSystemPriority();
123 ForceRedisplay();
124 }
125
126 CrostiniUpgradeAvailableNotification::~CrostiniUpgradeAvailableNotification() =
127 default;
128
UpgradeDialogShown()129 void CrostiniUpgradeAvailableNotification::UpgradeDialogShown() {
130 notification_->set_buttons({});
131 notification_->set_never_timeout(false);
132 notification_->set_pinned(false);
133 ForceRedisplay();
134 }
135
ForceRedisplay()136 void CrostiniUpgradeAvailableNotification::ForceRedisplay() {
137 NotificationDisplayService::GetForProfile(profile_)->Display(
138 NotificationHandler::Type::TRANSIENT, *notification_,
139 /*metadata=*/nullptr);
140 }
141
142 } // namespace crostini
143