OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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 | |
5 #include "ash/common/system/web_notification/web_notification_tray.h" | |
6 | |
7 #include "ash/common/material_design/material_design_controller.h" | |
8 #include "ash/common/session/session_state_delegate.h" | |
9 #include "ash/common/shelf/shelf_constants.h" | |
10 #include "ash/common/shelf/wm_shelf.h" | |
11 #include "ash/common/shelf/wm_shelf_util.h" | |
12 #include "ash/common/system/status_area_widget.h" | |
13 #include "ash/common/system/tray/system_tray.h" | |
14 #include "ash/common/system/tray/system_tray_delegate.h" | |
15 #include "ash/common/system/tray/tray_bubble_wrapper.h" | |
16 #include "ash/common/system/tray/tray_constants.h" | |
17 #include "ash/common/system/tray/tray_utils.h" | |
18 #include "ash/common/system/web_notification/ash_popup_alignment_delegate.h" | |
19 #include "ash/common/wm_shell.h" | |
20 #include "ash/common/wm_window.h" | |
21 #include "ash/public/cpp/shell_window_ids.h" | |
22 #include "ash/resources/vector_icons/vector_icons.h" | |
23 #include "ash/root_window_controller.h" | |
24 #include "ash/strings/grit/ash_strings.h" | |
25 #include "base/auto_reset.h" | |
26 #include "base/i18n/number_formatting.h" | |
27 #include "base/i18n/rtl.h" | |
28 #include "base/strings/utf_string_conversions.h" | |
29 #include "base/threading/thread_task_runner_handle.h" | |
30 #include "ui/base/l10n/l10n_util.h" | |
31 #include "ui/display/display.h" | |
32 #include "ui/display/screen.h" | |
33 #include "ui/gfx/paint_vector_icon.h" | |
34 #include "ui/gfx/vector_icons_public.h" | |
35 #include "ui/message_center/message_center_style.h" | |
36 #include "ui/message_center/message_center_tray_delegate.h" | |
37 #include "ui/message_center/views/message_bubble_base.h" | |
38 #include "ui/message_center/views/message_center_bubble.h" | |
39 #include "ui/message_center/views/message_popup_collection.h" | |
40 #include "ui/strings/grit/ui_strings.h" | |
41 #include "ui/views/bubble/tray_bubble_view.h" | |
42 #include "ui/views/controls/image_view.h" | |
43 #include "ui/views/controls/label.h" | |
44 #include "ui/views/controls/menu/menu_runner.h" | |
45 #include "ui/views/layout/fill_layout.h" | |
46 | |
47 namespace message_center { | |
48 | |
49 MessageCenterTrayDelegate* CreateMessageCenterTray() { | |
50 // On non-CrOS, the Tray will not be hosted in ash::Shell. | |
51 NOTREACHED(); | |
52 return nullptr; | |
53 } | |
54 | |
55 } // namespace message_center | |
56 | |
57 namespace ash { | |
58 namespace { | |
59 | |
60 // Menu commands | |
61 constexpr int kToggleQuietMode = 0; | |
62 constexpr int kEnableQuietModeDay = 2; | |
63 | |
64 constexpr int kMaximumSmallIconCount = 3; | |
65 | |
66 constexpr gfx::Size kTrayItemInnerIconSize(16, 16); | |
67 constexpr gfx::Size kTrayItemInnerBellIconSizeNonMd(18, 18); | |
68 constexpr gfx::Size kTrayItemOuterSize(26, 26); | |
69 constexpr int kTrayMainAxisInset = 3; | |
70 constexpr int kTrayCrossAxisInset = 0; | |
71 | |
72 constexpr int kTrayItemAnimationDurationMS = 200; | |
73 | |
74 constexpr size_t kMaximumNotificationNumber = 99; | |
75 | |
76 // Flag to disable animation. Only for testing. | |
77 bool disable_animations_for_test = false; | |
78 } | |
79 | |
80 namespace { | |
81 | |
82 const SkColor kWebNotificationColorNoUnread = | |
83 SkColorSetARGB(128, 255, 255, 255); | |
84 const SkColor kWebNotificationColorWithUnread = SK_ColorWHITE; | |
85 const int kNoUnreadIconSize = 18; | |
86 | |
87 } // namespace | |
88 | |
89 // Class to initialize and manage the WebNotificationBubble and | |
90 // TrayBubbleWrapper instances for a bubble. | |
91 class WebNotificationBubbleWrapper { | |
92 public: | |
93 // Takes ownership of |bubble| and creates |bubble_wrapper_|. | |
94 WebNotificationBubbleWrapper(WebNotificationTray* tray, | |
95 TrayBackgroundView* anchor_tray, | |
96 message_center::MessageBubbleBase* bubble) { | |
97 bubble_.reset(bubble); | |
98 views::TrayBubbleView::AnchorAlignment anchor_alignment = | |
99 tray->GetAnchorAlignment(); | |
100 views::TrayBubbleView::InitParams init_params = | |
101 bubble->GetInitParams(anchor_alignment); | |
102 views::TrayBubbleView* bubble_view = views::TrayBubbleView::Create( | |
103 anchor_tray->GetBubbleAnchor(), tray, &init_params); | |
104 bubble_view->set_anchor_view_insets(anchor_tray->GetBubbleAnchorInsets()); | |
105 bubble_wrapper_.reset(new TrayBubbleWrapper(tray, bubble_view)); | |
106 bubble->InitializeContents(bubble_view); | |
107 } | |
108 | |
109 message_center::MessageBubbleBase* bubble() const { return bubble_.get(); } | |
110 | |
111 // Convenience accessors. | |
112 views::TrayBubbleView* bubble_view() const { return bubble_->bubble_view(); } | |
113 | |
114 private: | |
115 std::unique_ptr<message_center::MessageBubbleBase> bubble_; | |
116 std::unique_ptr<TrayBubbleWrapper> bubble_wrapper_; | |
117 | |
118 DISALLOW_COPY_AND_ASSIGN(WebNotificationBubbleWrapper); | |
119 }; | |
120 | |
121 class WebNotificationItem : public views::View, public gfx::AnimationDelegate { | |
122 public: | |
123 WebNotificationItem(gfx::AnimationContainer* container, | |
124 WebNotificationTray* tray) | |
125 : tray_(tray) { | |
126 SetPaintToLayer(); | |
127 layer()->SetFillsBoundsOpaquely(false); | |
128 views::View::SetVisible(false); | |
129 set_owned_by_client(); | |
130 | |
131 SetLayoutManager(new views::FillLayout); | |
132 | |
133 animation_.reset(new gfx::SlideAnimation(this)); | |
134 animation_->SetContainer(container); | |
135 animation_->SetSlideDuration(kTrayItemAnimationDurationMS); | |
136 animation_->SetTweenType(gfx::Tween::LINEAR); | |
137 } | |
138 | |
139 void SetVisible(bool set_visible) override { | |
140 if (!GetWidget() || disable_animations_for_test) { | |
141 views::View::SetVisible(set_visible); | |
142 return; | |
143 } | |
144 | |
145 if (!set_visible) { | |
146 animation_->Hide(); | |
147 AnimationProgressed(animation_.get()); | |
148 } else { | |
149 animation_->Show(); | |
150 AnimationProgressed(animation_.get()); | |
151 views::View::SetVisible(true); | |
152 } | |
153 } | |
154 | |
155 void HideAndDelete() { | |
156 SetVisible(false); | |
157 | |
158 if (!visible() && !animation_->is_animating()) { | |
159 if (parent()) | |
160 parent()->RemoveChildView(this); | |
161 base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this); | |
162 } else { | |
163 delete_after_animation_ = true; | |
164 } | |
165 } | |
166 | |
167 protected: | |
168 // Overridden from views::View: | |
169 gfx::Size GetPreferredSize() const override { | |
170 if (!animation_.get() || !animation_->is_animating()) | |
171 return kTrayItemOuterSize; | |
172 | |
173 // Animate the width (or height) when this item shows (or hides) so that | |
174 // the icons on the left are shifted with the animation. | |
175 // Note that TrayItemView does the same thing. | |
176 gfx::Size size = kTrayItemOuterSize; | |
177 if (IsHorizontalLayout()) { | |
178 size.set_width(std::max( | |
179 1, gfx::ToRoundedInt(size.width() * animation_->GetCurrentValue()))); | |
180 } else { | |
181 size.set_height(std::max( | |
182 1, gfx::ToRoundedInt(size.height() * animation_->GetCurrentValue()))); | |
183 } | |
184 return size; | |
185 } | |
186 | |
187 int GetHeightForWidth(int width) const override { | |
188 return GetPreferredSize().height(); | |
189 } | |
190 | |
191 bool IsHorizontalLayout() const { | |
192 return IsHorizontalAlignment(tray_->shelf_alignment()); | |
193 } | |
194 | |
195 private: | |
196 // gfx::AnimationDelegate: | |
197 void AnimationProgressed(const gfx::Animation* animation) override { | |
198 gfx::Transform transform; | |
199 if (IsHorizontalLayout()) { | |
200 transform.Translate(0, animation->CurrentValueBetween( | |
201 static_cast<double>(height()) / 2., 0.)); | |
202 } else { | |
203 transform.Translate( | |
204 animation->CurrentValueBetween(static_cast<double>(width() / 2.), 0.), | |
205 0); | |
206 } | |
207 transform.Scale(animation->GetCurrentValue(), animation->GetCurrentValue()); | |
208 layer()->SetTransform(transform); | |
209 PreferredSizeChanged(); | |
210 } | |
211 void AnimationEnded(const gfx::Animation* animation) override { | |
212 if (animation->GetCurrentValue() < 0.1) | |
213 views::View::SetVisible(false); | |
214 | |
215 if (delete_after_animation_) { | |
216 if (parent()) | |
217 parent()->RemoveChildView(this); | |
218 base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this); | |
219 } | |
220 } | |
221 void AnimationCanceled(const gfx::Animation* animation) override { | |
222 AnimationEnded(animation); | |
223 } | |
224 | |
225 std::unique_ptr<gfx::SlideAnimation> animation_; | |
226 bool delete_after_animation_ = false; | |
227 WebNotificationTray* tray_; | |
228 | |
229 DISALLOW_COPY_AND_ASSIGN(WebNotificationItem); | |
230 }; | |
231 | |
232 class WebNotificationImage : public WebNotificationItem { | |
233 public: | |
234 WebNotificationImage(const gfx::ImageSkia& image, | |
235 const gfx::Size& size, | |
236 gfx::AnimationContainer* container, | |
237 WebNotificationTray* tray) | |
238 : WebNotificationItem(container, tray) { | |
239 view_ = new views::ImageView(); | |
240 view_->SetImage(image); | |
241 view_->SetImageSize(size); | |
242 AddChildView(view_); | |
243 } | |
244 | |
245 private: | |
246 views::ImageView* view_; | |
247 | |
248 DISALLOW_COPY_AND_ASSIGN(WebNotificationImage); | |
249 }; | |
250 | |
251 class WebNotificationLabel : public WebNotificationItem { | |
252 public: | |
253 WebNotificationLabel(gfx::AnimationContainer* container, | |
254 WebNotificationTray* tray) | |
255 : WebNotificationItem(container, tray) { | |
256 view_ = new views::Label(); | |
257 SetupLabelForTray(view_); | |
258 AddChildView(view_); | |
259 } | |
260 | |
261 void SetNotificationCount(bool small_icons_exist, size_t notification_count) { | |
262 notification_count = std::min(notification_count, | |
263 kMaximumNotificationNumber); // cap with 99 | |
264 | |
265 // TODO(yoshiki): Use a string for "99" and "+99". | |
266 | |
267 base::string16 str = base::FormatNumber(notification_count); | |
268 if (small_icons_exist) { | |
269 if (!base::i18n::IsRTL()) | |
270 str = base::ASCIIToUTF16("+") + str; | |
271 else | |
272 str = str + base::ASCIIToUTF16("+"); | |
273 } | |
274 | |
275 view_->SetText(str); | |
276 view_->SetEnabledColor(kWebNotificationColorWithUnread); | |
277 SchedulePaint(); | |
278 } | |
279 | |
280 private: | |
281 views::Label* view_; | |
282 | |
283 DISALLOW_COPY_AND_ASSIGN(WebNotificationLabel); | |
284 }; | |
285 | |
286 WebNotificationTray::WebNotificationTray(WmShelf* shelf, | |
287 WmWindow* status_area_window, | |
288 SystemTray* system_tray) | |
289 : TrayBackgroundView(shelf), | |
290 status_area_window_(status_area_window), | |
291 system_tray_(system_tray), | |
292 show_message_center_on_unlock_(false), | |
293 should_update_tray_content_(false), | |
294 should_block_shelf_auto_hide_(false) { | |
295 DCHECK(shelf); | |
296 DCHECK(status_area_window_); | |
297 DCHECK(system_tray_); | |
298 | |
299 if (MaterialDesignController::IsShelfMaterial()) { | |
300 SetInkDropMode(InkDropMode::ON); | |
301 SetContentsBackground(false); | |
302 gfx::ImageSkia bell_image = | |
303 CreateVectorIcon(kShelfNotificationsIcon, kShelfIconColor); | |
304 const gfx::Size bell_icon_size = kTrayItemInnerIconSize; | |
305 bell_icon_.reset(new WebNotificationImage( | |
306 bell_image, bell_icon_size, animation_container_.get(), this)); | |
307 } else { | |
308 SetContentsBackground(true); | |
309 gfx::ImageSkia bell_image = | |
310 CreateVectorIcon(gfx::VectorIconId::NOTIFICATIONS, kNoUnreadIconSize, | |
311 kWebNotificationColorNoUnread); | |
312 const gfx::Size bell_icon_size = kTrayItemInnerBellIconSizeNonMd; | |
313 bell_icon_.reset(new WebNotificationImage( | |
314 bell_image, bell_icon_size, animation_container_.get(), this)); | |
315 } | |
316 tray_container()->AddChildView(bell_icon_.get()); | |
317 | |
318 counter_.reset(new WebNotificationLabel(animation_container_.get(), this)); | |
319 tray_container()->AddChildView(counter_.get()); | |
320 | |
321 message_center_tray_.reset(new message_center::MessageCenterTray( | |
322 this, message_center::MessageCenter::Get())); | |
323 popup_alignment_delegate_.reset(new AshPopupAlignmentDelegate(shelf)); | |
324 popup_collection_.reset(new message_center::MessagePopupCollection( | |
325 message_center(), message_center_tray_.get(), | |
326 popup_alignment_delegate_.get())); | |
327 const display::Display& display = | |
328 status_area_window_->GetDisplayNearestWindow(); | |
329 popup_alignment_delegate_->StartObserving(display::Screen::GetScreen(), | |
330 display); | |
331 OnMessageCenterTrayChanged(); | |
332 | |
333 tray_container()->SetMargin(kTrayMainAxisInset, kTrayCrossAxisInset); | |
334 } | |
335 | |
336 WebNotificationTray::~WebNotificationTray() { | |
337 // Release any child views that might have back pointers before ~View(). | |
338 message_center_bubble_.reset(); | |
339 popup_alignment_delegate_.reset(); | |
340 popup_collection_.reset(); | |
341 } | |
342 | |
343 // static | |
344 void WebNotificationTray::DisableAnimationsForTest(bool disable) { | |
345 disable_animations_for_test = disable; | |
346 } | |
347 | |
348 // Public methods. | |
349 | |
350 bool WebNotificationTray::ShowMessageCenterInternal(bool show_settings) { | |
351 if (!ShouldShowMessageCenter()) | |
352 return false; | |
353 | |
354 should_block_shelf_auto_hide_ = true; | |
355 message_center::MessageCenterBubble* message_center_bubble = | |
356 new message_center::MessageCenterBubble(message_center(), | |
357 message_center_tray_.get()); | |
358 | |
359 // In the horizontal case, message center starts from the top of the shelf. | |
360 // In the vertical case, it starts from the bottom of WebNotificationTray. | |
361 const int max_height = IsHorizontalAlignment(shelf_alignment()) | |
362 ? shelf()->GetIdealBounds().y() | |
363 : GetBoundsInScreen().bottom(); | |
364 message_center_bubble->SetMaxHeight(max_height); | |
365 | |
366 if (show_settings) | |
367 message_center_bubble->SetSettingsVisible(); | |
368 | |
369 // For vertical shelf alignments, anchor to the WebNotificationTray, but for | |
370 // horizontal (i.e. bottom) shelves, anchor to the system tray. | |
371 TrayBackgroundView* anchor_tray = this; | |
372 if (IsHorizontalAlignment(shelf_alignment())) { | |
373 anchor_tray = WmShelf::ForWindow(status_area_window_) | |
374 ->GetStatusAreaWidget() | |
375 ->system_tray(); | |
376 } | |
377 | |
378 message_center_bubble_.reset(new WebNotificationBubbleWrapper( | |
379 this, anchor_tray, message_center_bubble)); | |
380 | |
381 shelf()->UpdateAutoHideState(); | |
382 SetIsActive(true); | |
383 return true; | |
384 } | |
385 | |
386 bool WebNotificationTray::ShowMessageCenter() { | |
387 return ShowMessageCenterInternal(false /* show_settings */); | |
388 } | |
389 | |
390 void WebNotificationTray::HideMessageCenter() { | |
391 if (!message_center_bubble()) | |
392 return; | |
393 SetIsActive(false); | |
394 message_center_bubble_.reset(); | |
395 should_block_shelf_auto_hide_ = false; | |
396 show_message_center_on_unlock_ = false; | |
397 shelf()->UpdateAutoHideState(); | |
398 } | |
399 | |
400 void WebNotificationTray::SetTrayBubbleHeight(int height) { | |
401 popup_alignment_delegate_->SetTrayBubbleHeight(height); | |
402 } | |
403 | |
404 int WebNotificationTray::tray_bubble_height_for_test() const { | |
405 return popup_alignment_delegate_->tray_bubble_height_for_test(); | |
406 } | |
407 | |
408 bool WebNotificationTray::ShowPopups() { | |
409 if (message_center_bubble()) | |
410 return false; | |
411 | |
412 popup_collection_->DoUpdateIfPossible(); | |
413 return true; | |
414 } | |
415 | |
416 void WebNotificationTray::HidePopups() { | |
417 DCHECK(popup_collection_.get()); | |
418 popup_collection_->MarkAllPopupsShown(); | |
419 } | |
420 | |
421 // Private methods. | |
422 | |
423 bool WebNotificationTray::ShouldShowMessageCenter() { | |
424 return WmShell::Get()->system_tray_delegate()->ShouldShowNotificationTray(); | |
425 } | |
426 | |
427 bool WebNotificationTray::ShouldBlockShelfAutoHide() const { | |
428 return should_block_shelf_auto_hide_; | |
429 } | |
430 | |
431 bool WebNotificationTray::IsMessageCenterBubbleVisible() const { | |
432 return (message_center_bubble() && | |
433 message_center_bubble()->bubble()->IsVisible()); | |
434 } | |
435 | |
436 void WebNotificationTray::ShowMessageCenterBubble() { | |
437 if (!IsMessageCenterBubbleVisible()) | |
438 message_center_tray_->ShowMessageCenterBubble(); | |
439 } | |
440 | |
441 void WebNotificationTray::UpdateAfterLoginStatusChange( | |
442 LoginStatus login_status) { | |
443 message_center()->SetLockedState(login_status == LoginStatus::LOCKED); | |
444 OnMessageCenterTrayChanged(); | |
445 } | |
446 | |
447 void WebNotificationTray::SetShelfAlignment(ShelfAlignment alignment) { | |
448 if (alignment == shelf_alignment()) | |
449 return; | |
450 TrayBackgroundView::SetShelfAlignment(alignment); | |
451 // Destroy any existing bubble so that it will be rebuilt correctly. | |
452 message_center_tray_->HideMessageCenterBubble(); | |
453 message_center_tray_->HidePopupBubble(); | |
454 } | |
455 | |
456 void WebNotificationTray::AnchorUpdated() { | |
457 if (message_center_bubble()) { | |
458 message_center_bubble()->bubble_view()->UpdateBubble(); | |
459 UpdateBubbleViewArrow(message_center_bubble()->bubble_view()); | |
460 } | |
461 } | |
462 | |
463 base::string16 WebNotificationTray::GetAccessibleNameForTray() { | |
464 return l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_ACCESSIBLE_NAME); | |
465 } | |
466 | |
467 void WebNotificationTray::HideBubbleWithView( | |
468 const views::TrayBubbleView* bubble_view) { | |
469 if (message_center_bubble() && | |
470 bubble_view == message_center_bubble()->bubble_view()) { | |
471 message_center_tray_->HideMessageCenterBubble(); | |
472 } else if (popup_collection_.get()) { | |
473 message_center_tray_->HidePopupBubble(); | |
474 } | |
475 } | |
476 | |
477 bool WebNotificationTray::PerformAction(const ui::Event& event) { | |
478 if (message_center_bubble()) | |
479 message_center_tray_->HideMessageCenterBubble(); | |
480 else | |
481 message_center_tray_->ShowMessageCenterBubble(); | |
482 return true; | |
483 } | |
484 | |
485 void WebNotificationTray::BubbleViewDestroyed() { | |
486 if (message_center_bubble()) | |
487 message_center_bubble()->bubble()->BubbleViewDestroyed(); | |
488 } | |
489 | |
490 void WebNotificationTray::OnMouseEnteredView() {} | |
491 | |
492 void WebNotificationTray::OnMouseExitedView() {} | |
493 | |
494 base::string16 WebNotificationTray::GetAccessibleNameForBubble() { | |
495 return GetAccessibleNameForTray(); | |
496 } | |
497 | |
498 void WebNotificationTray::OnBeforeBubbleWidgetInit( | |
499 views::Widget* anchor_widget, | |
500 views::Widget* bubble_widget, | |
501 views::Widget::InitParams* params) const { | |
502 // Place the bubble in the same root window as |anchor_widget|. | |
503 WmWindow::Get(anchor_widget->GetNativeWindow()) | |
504 ->GetRootWindowController() | |
505 ->ConfigureWidgetInitParamsForContainer( | |
506 bubble_widget, kShellWindowId_SettingBubbleContainer, params); | |
507 } | |
508 | |
509 void WebNotificationTray::HideBubble(const views::TrayBubbleView* bubble_view) { | |
510 HideBubbleWithView(bubble_view); | |
511 } | |
512 | |
513 bool WebNotificationTray::ShowNotifierSettings() { | |
514 if (message_center_bubble()) { | |
515 static_cast<message_center::MessageCenterBubble*>( | |
516 message_center_bubble()->bubble()) | |
517 ->SetSettingsVisible(); | |
518 return true; | |
519 } | |
520 return ShowMessageCenterInternal(true /* show_settings */); | |
521 } | |
522 | |
523 bool WebNotificationTray::IsContextMenuEnabled() const { | |
524 return IsLoggedIn(); | |
525 } | |
526 | |
527 message_center::MessageCenterTray* WebNotificationTray::GetMessageCenterTray() { | |
528 return message_center_tray_.get(); | |
529 } | |
530 | |
531 bool WebNotificationTray::IsCommandIdChecked(int command_id) const { | |
532 if (command_id != kToggleQuietMode) | |
533 return false; | |
534 return message_center()->IsQuietMode(); | |
535 } | |
536 | |
537 bool WebNotificationTray::IsCommandIdEnabled(int command_id) const { | |
538 return true; | |
539 } | |
540 | |
541 void WebNotificationTray::ExecuteCommand(int command_id, int event_flags) { | |
542 if (command_id == kToggleQuietMode) { | |
543 bool in_quiet_mode = message_center()->IsQuietMode(); | |
544 message_center()->SetQuietMode(!in_quiet_mode); | |
545 return; | |
546 } | |
547 base::TimeDelta expires_in = command_id == kEnableQuietModeDay | |
548 ? base::TimeDelta::FromDays(1) | |
549 : base::TimeDelta::FromHours(1); | |
550 message_center()->EnterQuietModeWithExpire(expires_in); | |
551 } | |
552 | |
553 void WebNotificationTray::OnMessageCenterTrayChanged() { | |
554 // Do not update the tray contents directly. Multiple change events can happen | |
555 // consecutively, and calling Update in the middle of those events will show | |
556 // intermediate unread counts for a moment. | |
557 should_update_tray_content_ = true; | |
558 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
559 FROM_HERE, | |
560 base::Bind(&WebNotificationTray::UpdateTrayContent, AsWeakPtr())); | |
561 } | |
562 | |
563 void WebNotificationTray::UpdateTrayContent() { | |
564 if (!should_update_tray_content_) | |
565 return; | |
566 should_update_tray_content_ = false; | |
567 | |
568 std::unordered_set<std::string> notification_ids; | |
569 for (auto pair : visible_small_icons_) | |
570 notification_ids.insert(pair.first); | |
571 | |
572 // Add small icons (up to kMaximumSmallIconCount = 3). | |
573 message_center::MessageCenter* message_center = | |
574 message_center_tray_->message_center(); | |
575 size_t visible_small_icon_count = 0; | |
576 for (const auto* notification : message_center->GetVisibleNotifications()) { | |
577 gfx::Image image = notification->small_image(); | |
578 if (image.IsEmpty()) | |
579 continue; | |
580 | |
581 if (visible_small_icon_count >= kMaximumSmallIconCount) | |
582 break; | |
583 visible_small_icon_count++; | |
584 | |
585 notification_ids.erase(notification->id()); | |
586 if (visible_small_icons_.count(notification->id()) != 0) | |
587 continue; | |
588 | |
589 auto* item = | |
590 new WebNotificationImage(image.AsImageSkia(), kTrayItemInnerIconSize, | |
591 animation_container_.get(), this); | |
592 visible_small_icons_.insert(std::make_pair(notification->id(), item)); | |
593 | |
594 tray_container()->AddChildViewAt(item, 0); | |
595 item->SetVisible(true); | |
596 } | |
597 | |
598 // Remove unnecessary icons. | |
599 for (const std::string& id : notification_ids) { | |
600 WebNotificationImage* item = visible_small_icons_[id]; | |
601 visible_small_icons_.erase(id); | |
602 item->HideAndDelete(); | |
603 } | |
604 | |
605 // Show or hide the bell icon. | |
606 size_t visible_notification_count = message_center->NotificationCount(); | |
607 bell_icon_->SetVisible(visible_notification_count == 0); | |
608 | |
609 // Show or hide the counter. | |
610 size_t hidden_icon_count = | |
611 visible_notification_count - visible_small_icon_count; | |
612 if (hidden_icon_count != 0) { | |
613 counter_->SetVisible(true); | |
614 counter_->SetNotificationCount( | |
615 (visible_small_icon_count != 0), // small_icons_exist | |
616 hidden_icon_count); | |
617 } else { | |
618 counter_->SetVisible(false); | |
619 } | |
620 | |
621 SetVisible(IsLoggedIn() && ShouldShowMessageCenter()); | |
622 PreferredSizeChanged(); | |
623 Layout(); | |
624 SchedulePaint(); | |
625 if (IsLoggedIn()) | |
626 system_tray_->SetNextFocusableView(this); | |
627 } | |
628 | |
629 void WebNotificationTray::ClickedOutsideBubble() { | |
630 // Only hide the message center | |
631 if (!message_center_bubble()) | |
632 return; | |
633 | |
634 message_center_tray_->HideMessageCenterBubble(); | |
635 } | |
636 | |
637 message_center::MessageCenter* WebNotificationTray::message_center() const { | |
638 return message_center_tray_->message_center(); | |
639 } | |
640 | |
641 bool WebNotificationTray::IsLoggedIn() const { | |
642 WmShell* shell = WmShell::Get(); | |
643 return shell->system_tray_delegate()->GetUserLoginStatus() != | |
644 LoginStatus::NOT_LOGGED_IN && | |
645 !shell->GetSessionStateDelegate()->IsInSecondaryLoginScreen(); | |
646 } | |
647 | |
648 // Methods for testing | |
649 | |
650 bool WebNotificationTray::IsPopupVisible() const { | |
651 return message_center_tray_->popups_visible(); | |
652 } | |
653 | |
654 message_center::MessageCenterBubble* | |
655 WebNotificationTray::GetMessageCenterBubbleForTest() { | |
656 if (!message_center_bubble()) | |
657 return nullptr; | |
658 return static_cast<message_center::MessageCenterBubble*>( | |
659 message_center_bubble()->bubble()); | |
660 } | |
661 | |
662 } // namespace ash | |
OLD | NEW |