unified_message_center_view.cc 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. // Copyright 2018 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_view.h"
  5. #include <algorithm>
  6. #include <memory>
  7. #include "ash/constants/ash_features.h"
  8. #include "ash/session/session_controller_impl.h"
  9. #include "ash/shell.h"
  10. #include "ash/system/message_center/ash_message_center_lock_screen_controller.h"
  11. #include "ash/system/message_center/message_center_constants.h"
  12. #include "ash/system/message_center/message_center_scroll_bar.h"
  13. #include "ash/system/message_center/stacked_notification_bar.h"
  14. #include "ash/system/message_center/unified_message_center_bubble.h"
  15. #include "ash/system/message_center/unified_message_list_view.h"
  16. #include "ash/system/tray/tray_constants.h"
  17. #include "ash/system/tray/tray_popup_utils.h"
  18. #include "ash/system/unified/unified_system_tray_model.h"
  19. #include "ash/system/unified/unified_system_tray_view.h"
  20. #include "base/memory/ptr_util.h"
  21. #include "base/memory/scoped_refptr.h"
  22. #include "base/metrics/user_metrics.h"
  23. #include "third_party/abseil-cpp/absl/types/optional.h"
  24. #include "ui/compositor/layer.h"
  25. #include "ui/gfx/animation/linear_animation.h"
  26. #include "ui/message_center/message_center.h"
  27. #include "ui/message_center/views/message_view.h"
  28. #include "ui/views/background.h"
  29. #include "ui/views/controls/button/label_button.h"
  30. #include "ui/views/controls/scroll_view.h"
  31. #include "ui/views/focus/focus_search.h"
  32. #include "ui/views/layout/box_layout.h"
  33. #include "ui/views/layout/fill_layout.h"
  34. #include "ui/views/widget/widget.h"
  35. namespace ash {
  36. namespace {
  37. constexpr base::TimeDelta kHideStackingBarAnimationDuration =
  38. base::Milliseconds(330);
  39. constexpr base::TimeDelta kCollapseAnimationDuration = base::Milliseconds(640);
  40. class ScrollerContentsView : public views::View {
  41. public:
  42. explicit ScrollerContentsView(UnifiedMessageListView* message_list_view) {
  43. auto* contents_layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
  44. views::BoxLayout::Orientation::kVertical));
  45. contents_layout->set_cross_axis_alignment(
  46. views::BoxLayout::CrossAxisAlignment::kStretch);
  47. AddChildView(message_list_view);
  48. }
  49. ScrollerContentsView(const ScrollerContentsView&) = delete;
  50. ScrollerContentsView& operator=(const ScrollerContentsView&) = delete;
  51. ~ScrollerContentsView() override = default;
  52. // views::View:
  53. void ChildPreferredSizeChanged(views::View* view) override {
  54. PreferredSizeChanged();
  55. }
  56. const char* GetClassName() const override { return "ScrollerContentsView"; }
  57. };
  58. } // namespace
  59. UnifiedMessageCenterView::UnifiedMessageCenterView(
  60. UnifiedSystemTrayView* parent,
  61. scoped_refptr<UnifiedSystemTrayModel> model,
  62. UnifiedMessageCenterBubble* bubble)
  63. : parent_(parent),
  64. model_(model),
  65. message_center_bubble_(bubble),
  66. notification_bar_(new StackedNotificationBar(this)),
  67. // TODO(crbug.com/1247455): Determine how to use ScrollWithLayers without
  68. // breaking ARC.
  69. scroller_(new views::ScrollView()),
  70. message_list_view_(new UnifiedMessageListView(this, model)),
  71. last_scroll_position_from_bottom_(0),
  72. is_notifications_refresh_enabled_(
  73. features::IsNotificationsRefreshEnabled()),
  74. animation_(std::make_unique<gfx::LinearAnimation>(this)),
  75. focus_search_(std::make_unique<views::FocusSearch>(this, false, false)) {
  76. if (is_notifications_refresh_enabled_)
  77. scroll_bar_ = new RoundedMessageCenterScrollBar(this);
  78. else
  79. scroll_bar_ = new MessageCenterScrollBar(this);
  80. if (is_notifications_refresh_enabled_) {
  81. SetLayoutManager(std::make_unique<views::BoxLayout>(
  82. views::BoxLayout::Orientation::kVertical,
  83. gfx::Insets(kMessageCenterPadding)));
  84. }
  85. }
  86. UnifiedMessageCenterView::~UnifiedMessageCenterView() {
  87. DCHECK(model_);
  88. model_->set_notification_target_mode(
  89. UnifiedSystemTrayModel::NotificationTargetMode::LAST_NOTIFICATION);
  90. RemovedFromWidget();
  91. }
  92. void UnifiedMessageCenterView::Init() {
  93. message_list_view_->Init();
  94. if (!is_notifications_refresh_enabled_)
  95. AddChildView(notification_bar_);
  96. // Need to set the transparent background explicitly, since ScrollView has
  97. // set the default opaque background color.
  98. // TODO(crbug.com/1247455): Be able to do
  99. // SetContentsLayerType(LAYER_NOT_DRAWN).
  100. scroller_->SetContents(
  101. std::make_unique<ScrollerContentsView>(message_list_view_));
  102. scroller_->SetBackgroundColor(absl::nullopt);
  103. scroller_->SetVerticalScrollBar(base::WrapUnique(scroll_bar_));
  104. scroller_->SetDrawOverflowIndicator(false);
  105. if (is_notifications_refresh_enabled_) {
  106. scroller_->SetPaintToLayer();
  107. scroller_->layer()->SetRoundedCornerRadius(
  108. gfx::RoundedCornersF{kMessageCenterScrollViewCornerRadius});
  109. }
  110. AddChildView(scroller_);
  111. if (is_notifications_refresh_enabled_)
  112. AddChildView(notification_bar_);
  113. notification_bar_->Update(
  114. message_list_view_->GetTotalNotificationCount(),
  115. message_list_view_->GetTotalPinnedNotificationCount(),
  116. GetStackedNotifications());
  117. }
  118. void UnifiedMessageCenterView::SetMaxHeight(int max_height) {
  119. int max_scroller_height = max_height;
  120. if (notification_bar_->GetVisible()) {
  121. max_scroller_height -=
  122. is_notifications_refresh_enabled_
  123. ? notification_bar_->GetPreferredSize().height() +
  124. 2 * kMessageCenterPadding
  125. : kStackedNotificationBarHeight;
  126. }
  127. scroller_->ClipHeightTo(0, max_scroller_height);
  128. }
  129. void UnifiedMessageCenterView::SetAvailableHeight(int available_height) {
  130. available_height_ = available_height;
  131. UpdateVisibility();
  132. }
  133. void UnifiedMessageCenterView::SetExpanded() {
  134. if (!collapsed_)
  135. return;
  136. collapsed_ = false;
  137. notification_bar_->SetExpanded();
  138. scroller_->SetVisible(true);
  139. }
  140. void UnifiedMessageCenterView::SetCollapsed(bool animate) {
  141. if (!GetVisible() || collapsed_)
  142. return;
  143. // Do not collapse the message center if notification bar is not visible.
  144. // i.e. there is only one notification.
  145. if (!notification_bar_->GetVisible())
  146. return;
  147. collapsed_ = true;
  148. if (animate) {
  149. StartCollapseAnimation();
  150. } else {
  151. scroller_->SetVisible(false);
  152. notification_bar_->SetCollapsed();
  153. }
  154. }
  155. void UnifiedMessageCenterView::ClearAllNotifications() {
  156. base::RecordAction(
  157. base::UserMetricsAction("StatusArea_Notifications_StackingBarClearAll"));
  158. message_list_view_->ClearAllWithAnimation();
  159. }
  160. void UnifiedMessageCenterView::ExpandMessageCenter() {
  161. base::RecordAction(
  162. base::UserMetricsAction("StatusArea_Notifications_SeeAllNotifications"));
  163. message_center_bubble_->ExpandMessageCenter();
  164. }
  165. bool UnifiedMessageCenterView::IsNotificationBarVisible() const {
  166. return notification_bar_->GetVisible();
  167. }
  168. bool UnifiedMessageCenterView::IsScrollBarVisible() const {
  169. return scroll_bar_->GetVisible();
  170. }
  171. void UnifiedMessageCenterView::OnNotificationSlidOut() {
  172. if (notification_bar_->GetVisible()) {
  173. notification_bar_->Update(
  174. message_list_view_->GetTotalNotificationCount(),
  175. message_list_view_->GetTotalPinnedNotificationCount(),
  176. GetStackedNotifications());
  177. if (!notification_bar_->GetVisible())
  178. StartHideStackingBarAnimation();
  179. }
  180. if (!message_list_view_->GetTotalNotificationCount()) {
  181. StartCollapseAnimation();
  182. }
  183. }
  184. void UnifiedMessageCenterView::ListPreferredSizeChanged() {
  185. UpdateVisibility();
  186. PreferredSizeChanged();
  187. SetMaxHeight(available_height_);
  188. if (GetWidget() && !GetWidget()->IsClosed())
  189. GetWidget()->SynthesizeMouseMoveEvent();
  190. }
  191. void UnifiedMessageCenterView::ConfigureMessageView(
  192. message_center::MessageView* message_view) {
  193. message_view->set_scroller(scroller_);
  194. }
  195. void UnifiedMessageCenterView::AddedToWidget() {
  196. focus_manager_ = GetFocusManager();
  197. if (focus_manager_)
  198. focus_manager_->AddFocusChangeListener(this);
  199. }
  200. void UnifiedMessageCenterView::RemovedFromWidget() {
  201. if (!focus_manager_)
  202. return;
  203. focus_manager_->RemoveFocusChangeListener(this);
  204. focus_manager_ = nullptr;
  205. }
  206. void UnifiedMessageCenterView::Layout() {
  207. if (is_notifications_refresh_enabled_)
  208. return views::View::Layout();
  209. if (notification_bar_->GetVisible()) {
  210. gfx::Rect counter_bounds(GetContentsBounds());
  211. int notification_bar_expanded_height = kStackedNotificationBarHeight;
  212. int notification_bar_height = collapsed_
  213. ? kStackedNotificationBarCollapsedHeight
  214. : notification_bar_expanded_height;
  215. int notification_bar_offset = 0;
  216. if (animation_state_ ==
  217. UnifiedMessageCenterAnimationState::HIDE_STACKING_BAR)
  218. notification_bar_offset = GetAnimationValue() * notification_bar_height;
  219. counter_bounds.set_height(notification_bar_height);
  220. counter_bounds.set_y(counter_bounds.y() - notification_bar_offset);
  221. notification_bar_->SetBoundsRect(counter_bounds);
  222. gfx::Rect scroller_bounds(GetContentsBounds());
  223. scroller_bounds.Inset(gfx::Insets::TLBR(
  224. notification_bar_height - notification_bar_offset, 0, 0, 0));
  225. scroller_->SetBoundsRect(scroller_bounds);
  226. } else {
  227. scroller_->SetBoundsRect(GetContentsBounds());
  228. }
  229. ScrollToTarget();
  230. }
  231. gfx::Size UnifiedMessageCenterView::CalculatePreferredSize() const {
  232. if (is_notifications_refresh_enabled_)
  233. return views::View::CalculatePreferredSize();
  234. gfx::Size preferred_size = scroller_->GetPreferredSize();
  235. if (notification_bar_->GetVisible()) {
  236. int bar_height = kStackedNotificationBarHeight;
  237. if (animation_state_ ==
  238. UnifiedMessageCenterAnimationState::HIDE_STACKING_BAR)
  239. bar_height -= GetAnimationValue() * bar_height;
  240. preferred_size.set_height(preferred_size.height() + bar_height);
  241. }
  242. if (animation_state_ == UnifiedMessageCenterAnimationState::COLLAPSE) {
  243. int height = preferred_size.height() * (1.0 - GetAnimationValue());
  244. if (collapsed_)
  245. height = std::max(kStackedNotificationBarCollapsedHeight, height);
  246. preferred_size.set_height(height);
  247. } else if (collapsed_) {
  248. preferred_size.set_height(kStackedNotificationBarCollapsedHeight);
  249. }
  250. return preferred_size;
  251. }
  252. const char* UnifiedMessageCenterView::GetClassName() const {
  253. return "UnifiedMessageCenterView";
  254. }
  255. void UnifiedMessageCenterView::OnMessageCenterScrolled() {
  256. last_scroll_position_from_bottom_ =
  257. scroll_bar_->GetMaxPosition() - scroller_->GetVisibleRect().y();
  258. DCHECK(model_);
  259. // Reset the target if user scrolls the list manually.
  260. model_->set_notification_target_mode(
  261. UnifiedSystemTrayModel::NotificationTargetMode::LAST_POSITION);
  262. bool was_count_updated = notification_bar_->Update(
  263. message_list_view_->GetTotalNotificationCount(),
  264. message_list_view_->GetTotalPinnedNotificationCount(),
  265. GetStackedNotifications());
  266. if (was_count_updated) {
  267. const int previous_y = scroller_->y();
  268. // Adjust scroll position when counter visibility is changed so that
  269. // on-screen position of notification list does not change.
  270. scroll_bar_->ScrollByContentsOffset(previous_y - scroller_->y());
  271. }
  272. }
  273. void UnifiedMessageCenterView::OnWillChangeFocus(views::View* before,
  274. views::View* now) {}
  275. void UnifiedMessageCenterView::OnDidChangeFocus(views::View* before,
  276. views::View* now) {
  277. if (message_list_view_->is_deleting_removed_notifications())
  278. return;
  279. OnMessageCenterScrolled();
  280. if (!collapsed()) {
  281. views::View* first_view = GetFirstFocusableChild();
  282. views::View* last_view = GetLastFocusableChild();
  283. // If we are cycling back to the first view from the last view or vice
  284. // verse. Focus out of the message center to the quick settings bubble. The
  285. // direction of the cycle determines where the focus will move to in quick
  286. // settings.
  287. bool focused_out = false;
  288. if (before == last_view && now == first_view)
  289. focused_out = message_center_bubble_->FocusOut(false /* reverse */);
  290. else if (before == first_view && now == last_view)
  291. focused_out = message_center_bubble_->FocusOut(true /* reverse */);
  292. // Clear the focus state completely for the message center.
  293. // We acquire the focus back from the quick settings widget based on the
  294. // cycling direction.
  295. if (focused_out) {
  296. GetFocusManager()->ClearFocus();
  297. GetFocusManager()->SetStoredFocusView(nullptr);
  298. }
  299. }
  300. }
  301. void UnifiedMessageCenterView::AnimationEnded(const gfx::Animation* animation) {
  302. // This is also called from AnimationCanceled().
  303. animation_->SetCurrentValue(1.0);
  304. PreferredSizeChanged();
  305. animation_state_ = UnifiedMessageCenterAnimationState::IDLE;
  306. notification_bar_->SetAnimationState(animation_state_);
  307. UpdateVisibility();
  308. }
  309. void UnifiedMessageCenterView::AnimationProgressed(
  310. const gfx::Animation* animation) {
  311. // Make the scroller containing notifications invisible and change the
  312. // notification bar to it's collapsed state in the middle of the animation to
  313. // the collapsed state.
  314. if (collapsed_ && scroller_->GetVisible() &&
  315. animation_->GetCurrentValue() >= 0.5) {
  316. scroller_->SetVisible(false);
  317. notification_bar_->SetCollapsed();
  318. }
  319. PreferredSizeChanged();
  320. }
  321. void UnifiedMessageCenterView::AnimationCanceled(
  322. const gfx::Animation* animation) {
  323. AnimationEnded(animation);
  324. }
  325. void UnifiedMessageCenterView::StartHideStackingBarAnimation() {
  326. animation_->End();
  327. animation_state_ = UnifiedMessageCenterAnimationState::HIDE_STACKING_BAR;
  328. notification_bar_->SetAnimationState(animation_state_);
  329. animation_->SetDuration(kHideStackingBarAnimationDuration);
  330. animation_->Start();
  331. }
  332. void UnifiedMessageCenterView::StartCollapseAnimation() {
  333. animation_->End();
  334. animation_state_ = UnifiedMessageCenterAnimationState::COLLAPSE;
  335. notification_bar_->SetAnimationState(animation_state_);
  336. animation_->SetDuration(kCollapseAnimationDuration);
  337. animation_->Start();
  338. }
  339. double UnifiedMessageCenterView::GetAnimationValue() const {
  340. return gfx::Tween::CalculateValue(gfx::Tween::FAST_OUT_SLOW_IN,
  341. animation_->GetCurrentValue());
  342. }
  343. void UnifiedMessageCenterView::UpdateVisibility() {
  344. SessionControllerImpl* session_controller =
  345. Shell::Get()->session_controller();
  346. notification_bar_->Update(
  347. message_list_view_->GetTotalNotificationCount(),
  348. message_list_view_->GetTotalPinnedNotificationCount(),
  349. GetStackedNotifications());
  350. SetVisible(
  351. available_height_ >= kUnifiedNotificationMinimumHeight &&
  352. (animation_state_ == UnifiedMessageCenterAnimationState::COLLAPSE ||
  353. message_list_view_->GetPreferredSize().height() > 0) &&
  354. session_controller->ShouldShowNotificationTray() &&
  355. (!session_controller->IsScreenLocked() ||
  356. AshMessageCenterLockScreenController::IsEnabled()));
  357. DCHECK(model_);
  358. if (!GetVisible()) {
  359. // When notification list went invisible, the last notification should be
  360. // targeted next time.
  361. model_->set_notification_target_mode(
  362. UnifiedSystemTrayModel::NotificationTargetMode::LAST_NOTIFICATION);
  363. // Transfer focus to quick settings when going invisible.
  364. auto* widget = GetWidget();
  365. if (widget && widget->IsActive()) {
  366. widget->GetFocusManager()->ClearFocus();
  367. message_center_bubble_->ActivateQuickSettingsBubble();
  368. }
  369. }
  370. }
  371. void UnifiedMessageCenterView::ScrollToTarget() {
  372. // Following logic doesn't work when the view is invisible, because it uses
  373. // the height of |scroller_|.
  374. if (!GetVisible())
  375. return;
  376. DCHECK(model_);
  377. auto target_mode = model_->notification_target_mode();
  378. // Notification views may be deleted during an animation, so wait until it
  379. // finishes before scrolling to a new target (see crbug.com/954001).
  380. if (message_list_view_->IsAnimating())
  381. target_mode = UnifiedSystemTrayModel::NotificationTargetMode::LAST_POSITION;
  382. int position;
  383. switch (target_mode) {
  384. case UnifiedSystemTrayModel::NotificationTargetMode::LAST_POSITION:
  385. // Restore the previous scrolled position with matching the distance from
  386. // the bottom.
  387. position =
  388. scroll_bar_->GetMaxPosition() - last_scroll_position_from_bottom_;
  389. break;
  390. case UnifiedSystemTrayModel::NotificationTargetMode::NOTIFICATION_ID:
  391. [[fallthrough]];
  392. case UnifiedSystemTrayModel::NotificationTargetMode::LAST_NOTIFICATION: {
  393. const gfx::Rect& target_rect =
  394. (model_->notification_target_mode() ==
  395. UnifiedSystemTrayModel::NotificationTargetMode::NOTIFICATION_ID)
  396. ? message_list_view_->GetNotificationBounds(
  397. model_->notification_target_id())
  398. : message_list_view_->GetLastNotificationBounds();
  399. const int last_notification_offset =
  400. target_rect.height() - scroller_->height();
  401. if (last_notification_offset > 0) {
  402. // If the target notification is taller than |scroller_|, we should
  403. // align the top of the notification with the top of |scroller_|.
  404. position = target_rect.y();
  405. } else {
  406. // Otherwise, we align the bottom of the notification with the bottom of
  407. // |scroller_|;
  408. position = target_rect.bottom() - scroller_->height();
  409. }
  410. }
  411. }
  412. scroller_->ScrollToPosition(scroll_bar_, position);
  413. notification_bar_->Update(
  414. message_list_view_->GetTotalNotificationCount(),
  415. message_list_view_->GetTotalPinnedNotificationCount(),
  416. GetStackedNotifications());
  417. last_scroll_position_from_bottom_ =
  418. scroll_bar_->GetMaxPosition() - scroller_->GetVisibleRect().y();
  419. }
  420. std::vector<message_center::Notification*>
  421. UnifiedMessageCenterView::GetStackedNotifications() const {
  422. // CountNotificationsAboveY() only works after SetBoundsRect() is called at
  423. // least once.
  424. if (scroller_->bounds().IsEmpty())
  425. scroller_->SetBoundsRect(GetContentsBounds());
  426. if (collapsed_) {
  427. // When in collapsed state, all notifications are hidden, so all
  428. // notifications are stacked.
  429. return message_list_view_->GetAllNotifications();
  430. }
  431. if (is_notifications_refresh_enabled_) {
  432. const int y_offset = scroller_->GetVisibleRect().bottom() - scroller_->y();
  433. return message_list_view_->GetNotificationsBelowY(y_offset);
  434. }
  435. const int notification_bar_height =
  436. IsNotificationBarVisible() ? kStackedNotificationBarHeight : 0;
  437. const int y_offset = scroller_->GetVisibleRect().y() - scroller_->y() +
  438. notification_bar_height;
  439. return message_list_view_->GetNotificationsAboveY(y_offset);
  440. }
  441. std::vector<std::string>
  442. UnifiedMessageCenterView::GetNonVisibleNotificationIdsInViewHierarchy() const {
  443. // CountNotificationsAboveY() only works after SetBoundsRect() is called at
  444. // least once.
  445. if (scroller_->bounds().IsEmpty())
  446. scroller_->SetBoundsRect(GetContentsBounds());
  447. if (collapsed_) {
  448. // When in collapsed state, all notifications are hidden, so all
  449. // notifications are stacked.
  450. return message_list_view_->GetAllNotificationIds();
  451. }
  452. const int notification_bar_height =
  453. IsNotificationBarVisible() ? kStackedNotificationBarHeight : 0;
  454. const int y_offset_above = scroller_->GetVisibleRect().y() - scroller_->y() +
  455. notification_bar_height;
  456. auto above_id_list =
  457. message_list_view_->GetNotificationIdsAboveY(y_offset_above);
  458. const int y_offset_below =
  459. scroller_->GetVisibleRect().bottom() - scroller_->y();
  460. const auto below_id_list =
  461. message_list_view_->GetNotificationIdsBelowY(y_offset_below);
  462. above_id_list.insert(above_id_list.end(), below_id_list.begin(),
  463. below_id_list.end());
  464. return above_id_list;
  465. }
  466. void UnifiedMessageCenterView::FocusOut(bool reverse) {
  467. if (message_center_bubble_ && message_center_bubble_->FocusOut(reverse)) {
  468. GetFocusManager()->ClearFocus();
  469. GetFocusManager()->SetStoredFocusView(nullptr);
  470. }
  471. }
  472. void UnifiedMessageCenterView::FocusEntered(bool reverse) {
  473. views::View* focus_view =
  474. reverse ? GetLastFocusableChild() : GetFirstFocusableChild();
  475. GetFocusManager()->ClearFocus();
  476. GetFocusManager()->SetFocusedView(focus_view);
  477. }
  478. views::View* UnifiedMessageCenterView::GetFirstFocusableChild() {
  479. views::FocusTraversable* dummy_focus_traversable;
  480. views::View* dummy_focus_traversable_view;
  481. return focus_search_->FindNextFocusableView(
  482. nullptr, views::FocusSearch::SearchDirection::kForwards,
  483. views::FocusSearch::TraversalDirection::kDown,
  484. views::FocusSearch::StartingViewPolicy::kSkipStartingView,
  485. views::FocusSearch::AnchoredDialogPolicy::kCanGoIntoAnchoredDialog,
  486. &dummy_focus_traversable, &dummy_focus_traversable_view);
  487. }
  488. views::View* UnifiedMessageCenterView::GetLastFocusableChild() {
  489. views::FocusTraversable* focus_traversable = nullptr;
  490. views::View* dummy_focus_traversable_view = nullptr;
  491. views::View* last_view = focus_search_->FindNextFocusableView(
  492. nullptr, views::FocusSearch::SearchDirection::kBackwards,
  493. views::FocusSearch::TraversalDirection::kDown,
  494. views::FocusSearch::StartingViewPolicy::kSkipStartingView,
  495. views::FocusSearch::AnchoredDialogPolicy::kCanGoIntoAnchoredDialog,
  496. &focus_traversable, &dummy_focus_traversable_view);
  497. if (last_view || !focus_traversable)
  498. return last_view;
  499. return focus_traversable->GetFocusSearch()->FindNextFocusableView(
  500. nullptr, views::FocusSearch::SearchDirection::kBackwards,
  501. views::FocusSearch::TraversalDirection::kDown,
  502. views::FocusSearch::StartingViewPolicy::kSkipStartingView,
  503. views::FocusSearch::AnchoredDialogPolicy::kCanGoIntoAnchoredDialog,
  504. &focus_traversable, &dummy_focus_traversable_view);
  505. }
  506. } // namespace ash