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 
5 #include "ash/system/message_center/notification_swipe_control_view.h"
6 
7 #include "ash/system/message_center/message_center_style.h"
8 #include "ash/system/message_center/metrics_utils.h"
9 #include "ui/base/l10n/l10n_util.h"
10 #include "ui/events/event.h"
11 #include "ui/gfx/color_palette.h"
12 #include "ui/gfx/paint_vector_icon.h"
13 #include "ui/message_center/vector_icons.h"
14 #include "ui/message_center/views/message_view.h"
15 #include "ui/message_center/views/notification_background_painter.h"
16 #include "ui/message_center/views/notification_control_buttons_view.h"
17 #include "ui/strings/grit/ui_strings.h"
18 #include "ui/views/background.h"
19 #include "ui/views/layout/box_layout.h"
20 
21 namespace ash {
22 
23 const char NotificationSwipeControlView::kViewClassName[] =
24     "NotificationSwipeControlView";
25 
NotificationSwipeControlView(message_center::MessageView * message_view)26 NotificationSwipeControlView::NotificationSwipeControlView(
27     message_center::MessageView* message_view)
28     : message_view_(message_view) {
29   auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
30       views::BoxLayout::Orientation::kHorizontal,
31       gfx::Insets(message_center_style::kSwipeControlButtonVerticalMargin,
32                   message_center_style::kSwipeControlButtonHorizontalMargin),
33       message_center_style::kSwipeControlButtonHorizontalMargin));
34   layout->set_cross_axis_alignment(
35       views::BoxLayout::CrossAxisAlignment::kStart);
36   layout->set_main_axis_alignment(views::BoxLayout::MainAxisAlignment::kEnd);
37 
38   // Draw on its own layer to round corners
39   SetPaintToLayer();
40   layer()->SetFillsBoundsOpaquely(false);
41 }
42 
43 NotificationSwipeControlView::~NotificationSwipeControlView() = default;
44 
ShowButtons(ButtonPosition button_position,bool show_settings,bool show_snooze)45 void NotificationSwipeControlView::ShowButtons(ButtonPosition button_position,
46                                                bool show_settings,
47                                                bool show_snooze) {
48   views::BoxLayout* layout = static_cast<views::BoxLayout*>(GetLayoutManager());
49   if ((button_position == ButtonPosition::RIGHT) != base::i18n::IsRTL()) {
50     layout->set_main_axis_alignment(views::BoxLayout::MainAxisAlignment::kEnd);
51   } else {
52     layout->set_main_axis_alignment(
53         views::BoxLayout::MainAxisAlignment::kStart);
54   }
55   ShowSettingsButton(show_settings);
56   ShowSnoozeButton(show_snooze);
57   Layout();
58 }
59 
HideButtons()60 void NotificationSwipeControlView::HideButtons() {
61   ShowSettingsButton(false);
62   ShowSnoozeButton(false);
63   Layout();
64 }
65 
UpdateButtonsVisibility()66 void NotificationSwipeControlView::UpdateButtonsVisibility() {
67   float gesture_amount = message_view_->GetSlideAmount();
68   if (gesture_amount == 0) {
69     HideButtons();
70     return;
71   }
72 
73   NotificationSwipeControlView::ButtonPosition button_position =
74       gesture_amount < 0 ? NotificationSwipeControlView::ButtonPosition::RIGHT
75                          : NotificationSwipeControlView::ButtonPosition::LEFT;
76   message_center::NotificationControlButtonsView* buttons =
77       message_view_->GetControlButtonsView();
78   // Ignore when GetControlButtonsView() returns null.
79   if (!buttons)
80     return;
81   bool has_settings_button = buttons->settings_button();
82   bool has_snooze_button = buttons->snooze_button();
83   ShowButtons(button_position, has_settings_button, has_snooze_button);
84 
85   int control_button_count =
86       (has_settings_button ? 1 : 0) + (has_snooze_button ? 1 : 0);
87   int control_button_width =
88       message_center_style::kSwipeControlButtonSize * control_button_count +
89       message_center_style::kSwipeControlButtonHorizontalMargin *
90           (control_button_count ? control_button_count + 1 : 0);
91   message_view_->SetSlideButtonWidth(control_button_width);
92 
93   // Update opacity based on the swipe progress. The swipe controls should
94   // gradually disappear as the user swipes the notification away.
95   float full_opacity_width =
96       message_center_style::kSwipeControlFullOpacityRatio *
97       control_button_width;
98   float fade_out_width = message_view_->width() - full_opacity_width;
99   DCHECK(fade_out_width > 0);
100   float swipe_progress = std::max(
101       0.0f, (fabs(gesture_amount) - full_opacity_width) / fade_out_width);
102   float opacity = std::max(0.0f, 1.0f - swipe_progress);
103 
104   layer()->SetOpacity(opacity);
105 }
106 
UpdateCornerRadius(int top_radius,int bottom_radius)107 void NotificationSwipeControlView::UpdateCornerRadius(int top_radius,
108                                                       int bottom_radius) {
109   SetBackground(views::CreateBackgroundFromPainter(
110       std::make_unique<message_center::NotificationBackgroundPainter>(
111           top_radius, bottom_radius,
112           message_center_style::kSwipeControlBackgroundColor)));
113   SchedulePaint();
114 }
115 
ShowSettingsButton(bool show)116 void NotificationSwipeControlView::ShowSettingsButton(bool show) {
117   if (show && !settings_button_) {
118     settings_button_ = new views::ImageButton(
119         base::BindRepeating(&NotificationSwipeControlView::ButtonPressed,
120                             base::Unretained(this), ButtonId::kSettings));
121     settings_button_->SetImage(
122         views::Button::STATE_NORMAL,
123         gfx::CreateVectorIcon(
124             message_center::kNotificationSettingsButtonIcon,
125             message_center_style::kSwipeControlButtonImageSize,
126             gfx::kChromeIconGrey));
127     settings_button_->SetImageHorizontalAlignment(
128         views::ImageButton::ALIGN_CENTER);
129     settings_button_->SetImageVerticalAlignment(
130         views::ImageButton::ALIGN_MIDDLE);
131     settings_button_->SetPreferredSize(
132         gfx::Size(message_center_style::kSwipeControlButtonSize,
133                   message_center_style::kSwipeControlButtonSize));
134 
135     settings_button_->SetAccessibleName(l10n_util::GetStringUTF16(
136         IDS_MESSAGE_NOTIFICATION_SETTINGS_BUTTON_ACCESSIBLE_NAME));
137     settings_button_->SetTooltipText(l10n_util::GetStringUTF16(
138         IDS_MESSAGE_NOTIFICATION_SETTINGS_BUTTON_ACCESSIBLE_NAME));
139     settings_button_->SetBackground(
140         views::CreateSolidBackground(SK_ColorTRANSPARENT));
141     settings_button_->SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
142 
143     AddChildView(settings_button_);
144     Layout();
145   } else if (!show && settings_button_) {
146     DCHECK(Contains(settings_button_));
147     delete settings_button_;
148     settings_button_ = nullptr;
149   }
150 }
151 
ShowSnoozeButton(bool show)152 void NotificationSwipeControlView::ShowSnoozeButton(bool show) {
153   if (show && !snooze_button_) {
154     snooze_button_ = new views::ImageButton(
155         base::BindRepeating(&NotificationSwipeControlView::ButtonPressed,
156                             base::Unretained(this), ButtonId::kSnooze));
157     snooze_button_->SetImage(
158         views::Button::STATE_NORMAL,
159         gfx::CreateVectorIcon(
160             message_center::kNotificationSnoozeButtonIcon,
161             message_center_style::kSwipeControlButtonImageSize,
162             gfx::kChromeIconGrey));
163     snooze_button_->SetImageHorizontalAlignment(
164         views::ImageButton::ALIGN_CENTER);
165     snooze_button_->SetImageVerticalAlignment(views::ImageButton::ALIGN_MIDDLE);
166     snooze_button_->SetPreferredSize(
167         gfx::Size(message_center_style::kSwipeControlButtonSize,
168                   message_center_style::kSwipeControlButtonSize));
169 
170     snooze_button_->SetAccessibleName(l10n_util::GetStringUTF16(
171         IDS_MESSAGE_NOTIFICATION_SETTINGS_BUTTON_ACCESSIBLE_NAME));
172     snooze_button_->SetTooltipText(l10n_util::GetStringUTF16(
173         IDS_MESSAGE_NOTIFICATION_SETTINGS_BUTTON_ACCESSIBLE_NAME));
174     snooze_button_->SetBackground(
175         views::CreateSolidBackground(SK_ColorTRANSPARENT));
176     snooze_button_->SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
177 
178     AddChildViewAt(snooze_button_, 0);
179     Layout();
180   } else if (!show && snooze_button_) {
181     DCHECK(Contains(snooze_button_));
182     delete snooze_button_;
183     snooze_button_ = nullptr;
184   }
185 }
186 
GetClassName() const187 const char* NotificationSwipeControlView::GetClassName() const {
188   return kViewClassName;
189 }
190 
ButtonPressed(ButtonId button,const ui::Event & event)191 void NotificationSwipeControlView::ButtonPressed(ButtonId button,
192                                                  const ui::Event& event) {
193   auto weak_this = weak_factory_.GetWeakPtr();
194 
195   const std::string notification_id = message_view_->notification_id();
196   if (button == ButtonId::kSettings) {
197     message_view_->OnSettingsButtonPressed(event);
198     metrics_utils::LogSettingsShown(notification_id,
199                                     /*is_slide_controls=*/true,
200                                     /*is_popup=*/false);
201   } else {
202     message_view_->OnSnoozeButtonPressed(event);
203     metrics_utils::LogSnoozed(notification_id,
204                               /*is_slide_controls=*/true,
205                               /*is_popup=*/false);
206   }
207 
208   // Button handlers of |message_view_| may have closed |this|.
209   if (!weak_this)
210     return;
211 
212   HideButtons();
213 
214   // Closing the swipe control is done in these button pressed handlers.
215   // Otherwise, handlers might not work.
216   message_view_->CloseSwipeControl();
217 }
218 
219 }  // namespace ash
220