1 // Copyright 2015 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.
5 #include "chrome/browser/download/notification/download_group_notification.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/browser/browser_process.h"
12 #include "chrome/browser/download/download_crx_util.h"
13 #include "chrome/browser/download/download_item_model.h"
14 #include "chrome/browser/notifications/profile_notification.h"
15 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
16 #include "chrome/common/url_constants.h"
17 #include "chrome/grit/chromium_strings.h"
18 #include "chrome/grit/generated_resources.h"
19 #include "content/public/browser/download_item.h"
20 #include "grit/theme_resources.h"
21 #include "ui/base/l10n/l10n_util.h"
22 #include "ui/base/l10n/time_format.h"
23 #include "ui/base/resource/resource_bundle.h"
24 #include "ui/gfx/text_elider.h"
25 #include "ui/message_center/message_center_impl.h"
26 #include "ui/views/controls/label.h"
30 const char kDownloadNotificationNotifierId
[] =
31 "chrome://downloads/notification/id-notifier";
33 const size_t kMaxFilenameWidth
= 160; // in px
35 base::string16
GetStatusString(content::DownloadItem
* download
) {
36 switch (download
->GetState()) {
37 case content::DownloadItem::IN_PROGRESS
:
38 // "Adding to Chrome..."
39 if (download
->AllDataSaved() &&
40 download_crx_util::IsExtensionDownload(*download
)) {
41 return l10n_util::GetStringUTF16(
42 IDS_DOWNLOAD_STATUS_CRX_INSTALL_RUNNING
);
46 if (download
->IsPaused())
47 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_PROGRESS_PAUSED
);
49 // "100/120 MB" or "100 MB"
50 if (download
->GetReceivedBytes() > 0) {
51 DownloadItemModel
model(download
);
52 return model
.GetProgressSizesString();
56 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_STARTING
);
57 case content::DownloadItem::COMPLETE
:
58 // "Removed" or "Completed"
59 if (download
->GetFileExternallyRemoved())
60 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_REMOVED
);
62 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_COMPLETED
);
63 case content::DownloadItem::CANCELLED
:
65 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CANCELLED
);
66 case content::DownloadItem::INTERRUPTED
: {
67 content::DownloadInterruptReason reason
= download
->GetLastReason();
68 if (reason
!= content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED
) {
69 // "Failed - <REASON>"
70 DownloadItemModel
model(download
);
71 base::string16 interrupt_reason
= model
.GetInterruptReasonText();
72 return l10n_util::GetStringFUTF16(
73 IDS_DOWNLOAD_STATUS_INTERRUPTED
, interrupt_reason
);
76 // Same as DownloadItem::CANCELLED.
77 return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CANCELLED
);
79 case content::DownloadItem::MAX_DOWNLOAD_STATE
:
84 return base::string16();
87 } // anonymous namespace
90 base::string16
DownloadGroupNotification::TruncateFileName(
91 const content::DownloadItem
* download
) {
92 return gfx::ElideFilename(download
->GetFileNameToReportUser(),
93 views::Label().font_list(),
97 DownloadGroupNotification::DownloadGroupNotification(
98 Profile
* profile
, DownloadNotificationManagerForProfile
* manager
)
101 ui::ResourceBundle
& bundle
= ui::ResourceBundle::GetSharedInstance();
103 message_center::RichNotificationData data
;
104 // Creates the notification instance. |title| and |body| will be overridden
105 // by UpdateNotificationData() below.
106 notification_
.reset(new Notification(
107 message_center::NOTIFICATION_TYPE_MULTIPLE
,
108 base::string16(), // title
109 base::string16(), // body
110 bundle
.GetImageNamed(IDR_DOWNLOAD_NOTIFICATION_DOWNLOADING
),
111 message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT
,
112 kDownloadNotificationNotifierId
),
113 base::string16(), // display_source
114 GURL(kDownloadNotificationOrigin
), // origin_url
118 notification_
->SetSystemPriority();
119 notification_
->set_never_timeout(false);
121 std::vector
<message_center::ButtonInfo
> notification_actions
;
122 message_center::ButtonInfo button_info
=
123 message_center::ButtonInfo(l10n_util::GetStringUTF16(
124 IDS_DOWNLOAD_LINK_SHOW_ALL_DOWNLOADS
));
125 notification_actions
.push_back(button_info
);
126 notification_
->set_buttons(notification_actions
);
129 DownloadGroupNotification::~DownloadGroupNotification() {}
131 bool DownloadGroupNotification::IsPopup() const {
132 const ProfileID profile_id
=
133 NotificationUIManager::GetProfileID(profile_
);
134 const std::string
& notification_id
= GetNotificationId();
135 const std::string
& raw_notification_id
= g_browser_process
->
136 notification_ui_manager()->FindById(notification_id
, profile_id
)->id();
137 auto popup_notifications
=
138 g_browser_process
->message_center()->GetPopupNotifications();
139 for (auto notification
: popup_notifications
) {
140 if (notification
->id() == raw_notification_id
)
146 void DownloadGroupNotification::OnDownloadUpdated(
147 content::DownloadItem
* download
) {
148 if (items_
.find(download
) != items_
.end()) {
153 void DownloadGroupNotification::OnDownloadAdded(
154 content::DownloadItem
* download
) {
155 if (items_
.find(download
) == items_
.end()) {
156 items_
.insert(download
);
157 int inprogress_download_count
= 0;
158 // If new download is started and there are more than 2 downloads in total,
159 // show the group notification.
160 for (auto it
= items_
.begin(); it
!= items_
.end(); it
++) {
161 if (!(*it
)->IsDone() && ++inprogress_download_count
>= 2) {
169 void DownloadGroupNotification::OnDownloadRemoved(
170 content::DownloadItem
* download
) {
171 // The given |download| may be already free'd.
172 if (items_
.find(download
) != items_
.end()) {
173 items_
.erase(download
);
174 truncated_filename_cache_
.erase(download
);
175 if (items_
.size() <= 1)
180 void DownloadGroupNotification::OnNotificationClose() {
183 // When the notification is closed, removes the finished downloads.
184 auto it
= items_
.begin();
185 while (it
!= items_
.end()) {
186 if ((*it
)->GetState() != content::DownloadItem::IN_PROGRESS
)
187 items_
.erase(*(it
++));
193 void DownloadGroupNotification::OnNotificationClick() {
197 void DownloadGroupNotification::OnNotificationButtonClick(
199 DCHECK_EQ(0, button_index
);
203 void DownloadGroupNotification::Hide() {
208 void DownloadGroupNotification::Show() {
213 void DownloadGroupNotification::Update() {
215 const ProfileID profile_id
=
216 NotificationUIManager::GetProfileID(profile_
);
217 const std::string
& notification_id
= GetNotificationId();
219 g_browser_process
->notification_ui_manager()->
220 CancelById(notification_id
, profile_id
);
223 UpdateNotificationData();
224 g_browser_process
->notification_ui_manager()->
225 Update(*notification_
, profile_
);
227 const std::string notification_id_in_message_center
=
228 ProfileNotification::GetProfileNotificationId(notification_id
,
231 auto message_center
= g_browser_process
->message_center();
232 // TODO(yoshiki): Remove this once in-place updating of notification is
234 InvokeUnsafeForceNotificationFlush(
235 message_center
, notification_id_in_message_center
);
239 UpdateNotificationData();
240 g_browser_process
->notification_ui_manager()->
241 Add(*notification_
, profile_
);
249 void DownloadGroupNotification::UpdateNotificationData() {
250 bool all_finished
= true;
251 std::vector
<message_center::NotificationItem
> subitems
;
252 for (auto download
: items_
) {
253 DownloadItemModel
model(download
);
254 auto it
= truncated_filename_cache_
.find(download
);
255 auto original_filename
= download
->GetFileNameToReportUser();
256 if (it
== truncated_filename_cache_
.end() ||
257 it
->second
.truncated_filename
.empty() ||
258 it
->second
.original_filename
!= original_filename
) {
259 truncated_filename_cache_
[download
].original_filename
= original_filename
;
260 truncated_filename_cache_
[download
].truncated_filename
=
261 TruncateFileName(download
);
264 // TODO(yoshiki): Use emplace_back when C++11 becomes allowed.
265 subitems
.push_back(message_center::NotificationItem(
266 truncated_filename_cache_
[download
].truncated_filename
,
267 GetStatusString(download
)));
269 if (!download
->IsDone())
270 all_finished
= false;
272 notification_
->set_items(subitems
);
274 int title_id
= all_finished
? IDS_DOWNLOAD_STATUS_GROUP_DONE_TITLE
:
275 IDS_DOWNLOAD_STATUS_GROUP_IN_PROGRESS_TITLE
;
276 notification_
->set_title(l10n_util::GetPluralStringFUTF16(
277 title_id
, items_
.size()));
280 std::string
DownloadGroupNotification::GetNotificationId() const {
284 void DownloadGroupNotification::OpenDownloads() {
285 chrome::ScopedTabbedBrowserDisplayer
browser_displayer(
286 profile_
, chrome::GetActiveDesktop());
287 Browser
* browser
= browser_displayer
.browser();
290 browser
->OpenURL(content::OpenURLParams(
291 GURL(chrome::kChromeUIDownloadsURL
), content::Referrer(),
292 NEW_FOREGROUND_TAB
, ui::PAGE_TRANSITION_LINK
,
293 false /* is_renderer_initiated */));