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