unified_message_center_bubble.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. // Copyright 2019 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 "ash/system/message_center/unified_message_center_bubble.h"
  5. #include <memory>
  6. #include "ash/accessibility/accessibility_controller_impl.h"
  7. #include "ash/bubble/bubble_constants.h"
  8. #include "ash/constants/ash_features.h"
  9. #include "ash/shelf/shelf.h"
  10. #include "ash/shell.h"
  11. #include "ash/strings/grit/ash_strings.h"
  12. #include "ash/style/system_shadow.h"
  13. #include "ash/system/message_center/message_center_style.h"
  14. #include "ash/system/message_center/unified_message_center_view.h"
  15. #include "ash/system/tray/tray_constants.h"
  16. #include "ash/system/tray/tray_event_filter.h"
  17. #include "ash/system/tray/tray_utils.h"
  18. #include "ash/system/unified/unified_system_tray.h"
  19. #include "ash/system/unified/unified_system_tray_bubble.h"
  20. #include "ash/system/unified/unified_system_tray_controller.h"
  21. #include "ash/system/unified/unified_system_tray_view.h"
  22. #include "base/i18n/rtl.h"
  23. #include "ui/base/l10n/l10n_util.h"
  24. #include "ui/compositor/layer.h"
  25. #include "ui/compositor/paint_recorder.h"
  26. #include "ui/views/focus/focus_search.h"
  27. #include "ui/views/widget/widget.h"
  28. namespace ash {
  29. // We need to draw a custom inner border for the message center in a separate
  30. // layer so we can properly clip ARC notifications. Each ARC notification is
  31. // contained in its own Window with its own layer, and the border needs to be
  32. // drawn on top of them all.
  33. class UnifiedMessageCenterBubble::Border : public ui::LayerDelegate {
  34. public:
  35. Border() : layer_(ui::LAYER_TEXTURED) {
  36. layer_.set_delegate(this);
  37. layer_.SetFillsBoundsOpaquely(false);
  38. }
  39. Border(const Border&) = delete;
  40. Border& operator=(const Border&) = delete;
  41. ~Border() override = default;
  42. ui::Layer* layer() { return &layer_; }
  43. private:
  44. // ui::LayerDelegate:
  45. void OnPaintLayer(const ui::PaintContext& context) override {
  46. gfx::Rect bounds = layer()->bounds();
  47. ui::PaintRecorder recorder(context, bounds.size());
  48. gfx::Canvas* canvas = recorder.canvas();
  49. // Draw a solid rounded rect as the inner border.
  50. cc::PaintFlags flags;
  51. flags.setColor(message_center_style::kSeperatorColor);
  52. flags.setStyle(cc::PaintFlags::kStroke_Style);
  53. flags.setStrokeWidth(canvas->image_scale());
  54. flags.setAntiAlias(true);
  55. canvas->DrawRoundRect(bounds, kBubbleCornerRadius, flags);
  56. }
  57. void OnDeviceScaleFactorChanged(float old_device_scale_factor,
  58. float new_device_scale_factor) override {}
  59. ui::Layer layer_;
  60. };
  61. UnifiedMessageCenterBubble::UnifiedMessageCenterBubble(UnifiedSystemTray* tray)
  62. : tray_(tray), border_(std::make_unique<Border>()) {
  63. TrayBubbleView::InitParams init_params;
  64. init_params.delegate = GetWeakPtr();
  65. // Anchor within the overlay container.
  66. init_params.parent_window = tray->GetBubbleWindowContainer();
  67. init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect;
  68. init_params.preferred_width = kTrayMenuWidth;
  69. init_params.has_shadow = false;
  70. init_params.close_on_deactivate = false;
  71. if (features::IsNotificationsRefreshEnabled())
  72. init_params.translucent = true;
  73. bubble_view_ = new TrayBubbleView(init_params);
  74. message_center_view_ =
  75. bubble_view_->AddChildView(std::make_unique<UnifiedMessageCenterView>(
  76. nullptr /* parent */, tray->model(), this));
  77. time_to_click_recorder_ =
  78. std::make_unique<TimeToClickRecorder>(this, message_center_view_);
  79. message_center_view_->AddObserver(this);
  80. }
  81. void UnifiedMessageCenterBubble::ShowBubble() {
  82. bubble_widget_ = views::BubbleDialogDelegateView::CreateBubble(bubble_view_);
  83. bubble_widget_->AddObserver(this);
  84. TrayBackgroundView::InitializeBubbleAnimations(bubble_widget_);
  85. // Stack system tray bubble's window above message center's window, such that
  86. // message center's shadow will not cover on system tray.
  87. tray_->GetBubbleWindowContainer()->StackChildAbove(
  88. tray_->bubble()->GetBubbleWidget()->GetNativeWindow(),
  89. bubble_widget_->GetNativeWindow());
  90. if (!features::IsNotificationsRefreshEnabled()) {
  91. ui::Layer* content_layer = bubble_view_->layer();
  92. float radius = kBubbleCornerRadius;
  93. content_layer->SetRoundedCornerRadius({radius, radius, radius, radius});
  94. content_layer->SetIsFastRoundedCorner(true);
  95. content_layer->Add(border_->layer());
  96. }
  97. if (features::IsSystemTrayShadowEnabled()) {
  98. // Create a shadow for bubble widget.
  99. shadow_ = SystemShadow::CreateShadowOnNinePatchLayerForWindow(
  100. bubble_widget_->GetNativeWindow(), SystemShadow::Type::kElevation12);
  101. shadow_->SetRoundedCornerRadius(kBubbleCornerRadius);
  102. }
  103. bubble_view_->InitializeAndShowBubble();
  104. message_center_view_->Init();
  105. UpdateBubbleState();
  106. tray_->tray_event_filter()->AddBubble(this);
  107. tray_->bubble()->unified_view()->AddObserver(this);
  108. }
  109. UnifiedMessageCenterBubble::~UnifiedMessageCenterBubble() {
  110. if (bubble_widget_) {
  111. tray_->tray_event_filter()->RemoveBubble(this);
  112. tray_->bubble()->unified_view()->RemoveObserver(this);
  113. CHECK(message_center_view_);
  114. message_center_view_->RemoveObserver(this);
  115. bubble_view_->ResetDelegate();
  116. bubble_widget_->RemoveObserver(this);
  117. bubble_widget_->Close();
  118. }
  119. CHECK(!views::WidgetObserver::IsInObserverList());
  120. }
  121. gfx::Rect UnifiedMessageCenterBubble::GetBoundsInScreen() const {
  122. DCHECK(bubble_view_);
  123. return bubble_view_->GetBoundsInScreen();
  124. }
  125. void UnifiedMessageCenterBubble::CollapseMessageCenter() {
  126. if (message_center_view_->collapsed())
  127. return;
  128. message_center_view_->SetCollapsed(true /*animate*/);
  129. }
  130. void UnifiedMessageCenterBubble::ExpandMessageCenter() {
  131. if (!message_center_view_->collapsed())
  132. return;
  133. if (tray_->IsShowingCalendarView())
  134. tray_->bubble()->unified_system_tray_controller()->TransitionToMainView(
  135. /*restore_focus=*/true);
  136. message_center_view_->SetExpanded();
  137. UpdatePosition();
  138. tray_->EnsureQuickSettingsCollapsed(true /*animate*/);
  139. }
  140. void UnifiedMessageCenterBubble::UpdatePosition() {
  141. int available_height = CalculateAvailableHeight();
  142. message_center_view_->SetMaxHeight(available_height);
  143. message_center_view_->SetAvailableHeight(available_height);
  144. if (!tray_->bubble())
  145. return;
  146. // Shelf bubbles need to be offset from the shelf, otherwise they will be
  147. // flush with the shelf. The bounds can't be shifted via insets because this
  148. // enlarges the layer bounds and this can break ARC notification rounded
  149. // corners. Apply the offset to the anchor rect.
  150. gfx::Rect anchor_rect = tray_->shelf()->GetSystemTrayAnchorRect();
  151. gfx::Insets tray_bubble_insets = GetTrayBubbleInsets();
  152. int offset;
  153. switch (tray_->shelf()->alignment()) {
  154. case ShelfAlignment::kLeft:
  155. offset = tray_bubble_insets.left();
  156. break;
  157. case ShelfAlignment::kRight:
  158. offset = -tray_bubble_insets.right();
  159. break;
  160. case ShelfAlignment::kBottom:
  161. case ShelfAlignment::kBottomLocked:
  162. if (base::i18n::IsRTL()) {
  163. offset = tray_bubble_insets.left();
  164. break;
  165. }
  166. offset = -tray_bubble_insets.right();
  167. break;
  168. }
  169. anchor_rect.set_x(anchor_rect.x() + offset);
  170. anchor_rect.set_y(anchor_rect.y() - tray_->bubble()->GetCurrentTrayHeight() -
  171. tray_bubble_insets.bottom() -
  172. kUnifiedMessageCenterBubbleSpacing);
  173. bubble_view_->ChangeAnchorRect(anchor_rect);
  174. if (!features::IsNotificationsRefreshEnabled()) {
  175. bubble_view_->layer()->StackAtTop(border_->layer());
  176. border_->layer()->SetBounds(message_center_view_->GetContentsBounds());
  177. }
  178. if (shadow_) {
  179. // When the last notification is removed, the content bounds of message
  180. // center may become too small such which makes the shadow's bounds smaller
  181. // than its blur region. To avoid this, we hide the shadow when the message
  182. // center has no notifications.
  183. shadow_->GetLayer()->SetVisible(
  184. message_center_view_->message_list_view()->GetTotalNotificationCount());
  185. }
  186. }
  187. void UnifiedMessageCenterBubble::FocusEntered(bool reverse) {
  188. message_center_view_->FocusEntered(reverse);
  189. }
  190. bool UnifiedMessageCenterBubble::FocusOut(bool reverse) {
  191. return tray_->FocusQuickSettings(reverse);
  192. }
  193. void UnifiedMessageCenterBubble::ActivateQuickSettingsBubble() {
  194. tray_->ActivateBubble();
  195. }
  196. bool UnifiedMessageCenterBubble::IsMessageCenterVisible() {
  197. return !!bubble_widget_ && message_center_view_ &&
  198. message_center_view_->GetVisible();
  199. }
  200. bool UnifiedMessageCenterBubble::IsMessageCenterCollapsed() {
  201. return message_center_view_->collapsed();
  202. }
  203. TrayBackgroundView* UnifiedMessageCenterBubble::GetTray() const {
  204. return tray_;
  205. }
  206. TrayBubbleView* UnifiedMessageCenterBubble::GetBubbleView() const {
  207. return bubble_view_;
  208. }
  209. views::Widget* UnifiedMessageCenterBubble::GetBubbleWidget() const {
  210. return bubble_widget_;
  211. }
  212. std::u16string UnifiedMessageCenterBubble::GetAccessibleNameForBubble() {
  213. return l10n_util::GetStringUTF16(IDS_ASH_MESSAGE_CENTER_ACCESSIBLE_NAME);
  214. }
  215. bool UnifiedMessageCenterBubble::ShouldEnableExtraKeyboardAccessibility() {
  216. return Shell::Get()->accessibility_controller()->spoken_feedback().enabled();
  217. }
  218. void UnifiedMessageCenterBubble::OnViewPreferredSizeChanged(
  219. views::View* observed_view) {
  220. UpdatePosition();
  221. bubble_view_->Layout();
  222. }
  223. void UnifiedMessageCenterBubble::OnViewVisibilityChanged(
  224. views::View* observed_view,
  225. views::View* starting_view) {
  226. // Hide the message center widget if the message center is not
  227. // visible. This is to ensure we do not see an empty bubble.
  228. if (observed_view != message_center_view_)
  229. return;
  230. bubble_view_->UpdateBubble();
  231. }
  232. void UnifiedMessageCenterBubble::OnWidgetDestroying(views::Widget* widget) {
  233. CHECK_EQ(bubble_widget_, widget);
  234. tray_->tray_event_filter()->RemoveBubble(this);
  235. tray_->bubble()->unified_view()->RemoveObserver(this);
  236. message_center_view_->RemoveObserver(this);
  237. bubble_widget_->RemoveObserver(this);
  238. bubble_widget_ = nullptr;
  239. shadow_.reset();
  240. bubble_view_->ResetDelegate();
  241. // Close the quick settings bubble as well, which may not automatically happen
  242. // when dismissing the message center bubble by pressing ESC.
  243. tray_->CloseBubble();
  244. }
  245. void UnifiedMessageCenterBubble::OnWidgetActivationChanged(
  246. views::Widget* widget,
  247. bool active) {
  248. if (active)
  249. tray_->bubble()->OnMessageCenterActivated();
  250. }
  251. void UnifiedMessageCenterBubble::OnDisplayConfigurationChanged() {
  252. UpdateBubbleState();
  253. }
  254. void UnifiedMessageCenterBubble::UpdateBubbleState() {
  255. if (CalculateAvailableHeight() < kMessageCenterCollapseThreshold &&
  256. message_center_view_->message_list_view()->GetTotalNotificationCount()) {
  257. if (tray_->IsQuickSettingsExplicitlyExpanded()) {
  258. message_center_view_->SetCollapsed(false /*animate*/);
  259. } else {
  260. message_center_view_->SetExpanded();
  261. tray_->EnsureQuickSettingsCollapsed(false /*animate*/);
  262. }
  263. } else if (message_center_view_->collapsed()) {
  264. message_center_view_->SetExpanded();
  265. }
  266. UpdatePosition();
  267. }
  268. int UnifiedMessageCenterBubble::CalculateAvailableHeight() {
  269. // TODO(crbug/1311738): Temporary fix to prevent crashes in case the quick
  270. // settings bubble is destroyed before the message center bubble. In the long
  271. // term we should remove this code altogether and calculate the max height for
  272. // the message center bubble separately.
  273. if (!tray_->bubble())
  274. return 0;
  275. return tray_->bubble()->CalculateMaxHeight() -
  276. tray_->bubble()->GetCurrentTrayHeight() -
  277. GetBubbleInsetHotseatCompensation() -
  278. kUnifiedMessageCenterBubbleSpacing;
  279. }
  280. void UnifiedMessageCenterBubble::RecordTimeToClick() {
  281. // TODO(tengs): We are currently only using this handler to record the first
  282. // interaction (i.e. whether the message center or quick settings was clicked
  283. // first). Maybe log the time to click if it is useful in the future.
  284. tray_->MaybeRecordFirstInteraction(
  285. UnifiedSystemTray::FirstInteractionType::kMessageCenter);
  286. }
  287. } // namespace ash