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/tray_accessibility.h" | |
6 | |
7 #include "ash/common/accessibility_delegate.h" | |
8 #include "ash/common/accessibility_types.h" | |
9 #include "ash/common/session/session_state_delegate.h" | |
10 #include "ash/common/system/tray/hover_highlight_view.h" | |
11 #include "ash/common/system/tray/system_tray.h" | |
12 #include "ash/common/system/tray/system_tray_controller.h" | |
13 #include "ash/common/system/tray/system_tray_delegate.h" | |
14 #include "ash/common/system/tray/system_tray_notifier.h" | |
15 #include "ash/common/system/tray/tray_constants.h" | |
16 #include "ash/common/system/tray/tray_details_view.h" | |
17 #include "ash/common/system/tray/tray_item_more.h" | |
18 #include "ash/common/system/tray/tray_popup_item_style.h" | |
19 #include "ash/common/system/tray/tray_popup_utils.h" | |
20 #include "ash/common/system/tray/tri_view.h" | |
21 #include "ash/common/wm_shell.h" | |
22 #include "ash/resources/grit/ash_resources.h" | |
23 #include "ash/resources/vector_icons/vector_icons.h" | |
24 #include "ash/strings/grit/ash_strings.h" | |
25 #include "base/strings/utf_string_conversions.h" | |
26 #include "ui/base/l10n/l10n_util.h" | |
27 #include "ui/base/resource/resource_bundle.h" | |
28 #include "ui/gfx/color_palette.h" | |
29 #include "ui/gfx/image/image.h" | |
30 #include "ui/gfx/paint_vector_icon.h" | |
31 #include "ui/gfx/vector_icons_public.h" | |
32 #include "ui/views/controls/button/custom_button.h" | |
33 #include "ui/views/controls/image_view.h" | |
34 #include "ui/views/controls/label.h" | |
35 #include "ui/views/layout/box_layout.h" | |
36 #include "ui/views/widget/widget.h" | |
37 | |
38 namespace ash { | |
39 namespace { | |
40 | |
41 enum AccessibilityState { | |
42 A11Y_NONE = 0, | |
43 A11Y_SPOKEN_FEEDBACK = 1 << 0, | |
44 A11Y_HIGH_CONTRAST = 1 << 1, | |
45 A11Y_SCREEN_MAGNIFIER = 1 << 2, | |
46 A11Y_LARGE_CURSOR = 1 << 3, | |
47 A11Y_AUTOCLICK = 1 << 4, | |
48 A11Y_VIRTUAL_KEYBOARD = 1 << 5, | |
49 A11Y_BRAILLE_DISPLAY_CONNECTED = 1 << 6, | |
50 }; | |
51 | |
52 uint32_t GetAccessibilityState() { | |
53 AccessibilityDelegate* delegate = WmShell::Get()->accessibility_delegate(); | |
54 uint32_t state = A11Y_NONE; | |
55 if (delegate->IsSpokenFeedbackEnabled()) | |
56 state |= A11Y_SPOKEN_FEEDBACK; | |
57 if (delegate->IsHighContrastEnabled()) | |
58 state |= A11Y_HIGH_CONTRAST; | |
59 if (delegate->IsMagnifierEnabled()) | |
60 state |= A11Y_SCREEN_MAGNIFIER; | |
61 if (delegate->IsLargeCursorEnabled()) | |
62 state |= A11Y_LARGE_CURSOR; | |
63 if (delegate->IsAutoclickEnabled()) | |
64 state |= A11Y_AUTOCLICK; | |
65 if (delegate->IsVirtualKeyboardEnabled()) | |
66 state |= A11Y_VIRTUAL_KEYBOARD; | |
67 if (delegate->IsBrailleDisplayConnected()) | |
68 state |= A11Y_BRAILLE_DISPLAY_CONNECTED; | |
69 return state; | |
70 } | |
71 | |
72 LoginStatus GetCurrentLoginStatus() { | |
73 return WmShell::Get()->system_tray_delegate()->GetUserLoginStatus(); | |
74 } | |
75 | |
76 } // namespace | |
77 | |
78 namespace tray { | |
79 | |
80 class DefaultAccessibilityView : public TrayItemMore { | |
81 public: | |
82 explicit DefaultAccessibilityView(SystemTrayItem* owner) | |
83 : TrayItemMore(owner) { | |
84 base::string16 label = | |
85 l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBILITY); | |
86 SetLabel(label); | |
87 SetAccessibleName(label); | |
88 set_id(test::kAccessibilityTrayItemViewId); | |
89 } | |
90 | |
91 ~DefaultAccessibilityView() override {} | |
92 | |
93 protected: | |
94 // TrayItemMore: | |
95 void UpdateStyle() override { | |
96 TrayItemMore::UpdateStyle(); | |
97 std::unique_ptr<TrayPopupItemStyle> style = CreateStyle(); | |
98 SetImage(gfx::CreateVectorIcon(kSystemMenuAccessibilityIcon, | |
99 style->GetIconColor())); | |
100 } | |
101 | |
102 private: | |
103 DISALLOW_COPY_AND_ASSIGN(DefaultAccessibilityView); | |
104 }; | |
105 | |
106 //////////////////////////////////////////////////////////////////////////////// | |
107 // ash::tray::AccessibilityPopupView | |
108 | |
109 AccessibilityPopupView::AccessibilityPopupView(uint32_t enabled_state_bits) | |
110 : TrayNotificationView(IDR_AURA_UBER_TRAY_ACCESSIBILITY_DARK), | |
111 label_(CreateLabel(enabled_state_bits)) { | |
112 InitView(label_); | |
113 } | |
114 | |
115 views::Label* AccessibilityPopupView::CreateLabel(uint32_t enabled_state_bits) { | |
116 DCHECK((enabled_state_bits & | |
117 (A11Y_SPOKEN_FEEDBACK | A11Y_BRAILLE_DISPLAY_CONNECTED)) != 0); | |
118 base::string16 text; | |
119 if (enabled_state_bits & A11Y_BRAILLE_DISPLAY_CONNECTED) { | |
120 text.append(l10n_util::GetStringUTF16( | |
121 IDS_ASH_STATUS_TRAY_BRAILLE_DISPLAY_CONNECTED_BUBBLE)); | |
122 } | |
123 if (enabled_state_bits & A11Y_SPOKEN_FEEDBACK) { | |
124 if (!text.empty()) | |
125 text.append(base::ASCIIToUTF16(" ")); | |
126 text.append(l10n_util::GetStringUTF16( | |
127 IDS_ASH_STATUS_TRAY_SPOKEN_FEEDBACK_ENABLED_BUBBLE)); | |
128 } | |
129 views::Label* label = new views::Label(text); | |
130 label->SetMultiLine(true); | |
131 label->SetHorizontalAlignment(gfx::ALIGN_LEFT); | |
132 return label; | |
133 } | |
134 | |
135 //////////////////////////////////////////////////////////////////////////////// | |
136 // ash::tray::AccessibilityDetailedView | |
137 | |
138 AccessibilityDetailedView::AccessibilityDetailedView(SystemTrayItem* owner, | |
139 LoginStatus login) | |
140 : TrayDetailsView(owner), | |
141 spoken_feedback_view_(nullptr), | |
142 high_contrast_view_(nullptr), | |
143 screen_magnifier_view_(nullptr), | |
144 large_cursor_view_(nullptr), | |
145 help_view_(nullptr), | |
146 settings_view_(nullptr), | |
147 autoclick_view_(nullptr), | |
148 virtual_keyboard_view_(nullptr), | |
149 spoken_feedback_enabled_(false), | |
150 high_contrast_enabled_(false), | |
151 screen_magnifier_enabled_(false), | |
152 large_cursor_enabled_(false), | |
153 autoclick_enabled_(false), | |
154 virtual_keyboard_enabled_(false), | |
155 login_(login) { | |
156 Reset(); | |
157 AppendAccessibilityList(); | |
158 CreateTitleRow(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_TITLE); | |
159 Layout(); | |
160 } | |
161 | |
162 void AccessibilityDetailedView::AppendAccessibilityList() { | |
163 CreateScrollableList(); | |
164 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); | |
165 | |
166 AccessibilityDelegate* delegate = WmShell::Get()->accessibility_delegate(); | |
167 spoken_feedback_enabled_ = delegate->IsSpokenFeedbackEnabled(); | |
168 spoken_feedback_view_ = | |
169 AddScrollListItem(bundle.GetLocalizedString( | |
170 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SPOKEN_FEEDBACK), | |
171 spoken_feedback_enabled_, spoken_feedback_enabled_, | |
172 kSystemMenuAccessibilityChromevoxIcon); | |
173 | |
174 // Large Cursor item is shown only in Login screen. | |
175 if (login_ == LoginStatus::NOT_LOGGED_IN) { | |
176 large_cursor_enabled_ = delegate->IsLargeCursorEnabled(); | |
177 large_cursor_view_ = | |
178 AddScrollListItem(bundle.GetLocalizedString( | |
179 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_LARGE_CURSOR), | |
180 large_cursor_enabled_, large_cursor_enabled_, | |
181 kSystemMenuAccessibilityLargeCursorIcon); | |
182 } | |
183 | |
184 high_contrast_enabled_ = delegate->IsHighContrastEnabled(); | |
185 high_contrast_view_ = AddScrollListItem( | |
186 bundle.GetLocalizedString( | |
187 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_HIGH_CONTRAST_MODE), | |
188 high_contrast_enabled_, high_contrast_enabled_, | |
189 kSystemMenuAccessibilityContrastIcon); | |
190 screen_magnifier_enabled_ = delegate->IsMagnifierEnabled(); | |
191 screen_magnifier_view_ = | |
192 AddScrollListItem(bundle.GetLocalizedString( | |
193 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SCREEN_MAGNIFIER), | |
194 screen_magnifier_enabled_, screen_magnifier_enabled_, | |
195 kSystemMenuAccessibilityScreenMagnifierIcon); | |
196 | |
197 // Don't show autoclick option at login screen. | |
198 if (login_ != LoginStatus::NOT_LOGGED_IN) { | |
199 autoclick_enabled_ = delegate->IsAutoclickEnabled(); | |
200 autoclick_view_ = AddScrollListItem( | |
201 bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_AUTOCLICK), | |
202 autoclick_enabled_, autoclick_enabled_, | |
203 kSystemMenuAccessibilityAutoClickIcon); | |
204 } | |
205 | |
206 virtual_keyboard_enabled_ = delegate->IsVirtualKeyboardEnabled(); | |
207 virtual_keyboard_view_ = | |
208 AddScrollListItem(bundle.GetLocalizedString( | |
209 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_VIRTUAL_KEYBOARD), | |
210 virtual_keyboard_enabled_, virtual_keyboard_enabled_, | |
211 kSystemMenuKeyboardIcon); | |
212 } | |
213 | |
214 HoverHighlightView* AccessibilityDetailedView::AddScrollListItem( | |
215 const base::string16& text, | |
216 bool highlight, | |
217 bool checked, | |
218 const gfx::VectorIcon& icon) { | |
219 HoverHighlightView* container = new HoverHighlightView(this); | |
220 gfx::ImageSkia image = CreateVectorIcon(icon, kMenuIconColor); | |
221 const int padding = (kMenuButtonSize - image.width()) / 2; | |
222 container->AddIconAndLabelCustomSize( | |
223 image, text, highlight, image.width() + kMenuSeparatorVerticalPadding * 2, | |
224 padding, padding); | |
225 | |
226 if (checked) { | |
227 gfx::ImageSkia check_mark = | |
228 CreateVectorIcon(gfx::VectorIconId::CHECK_CIRCLE, gfx::kGoogleGreen700); | |
229 container->AddRightIcon(check_mark, check_mark.width()); | |
230 container->SetRightViewVisible(true); | |
231 container->SetAccessiblityState( | |
232 HoverHighlightView::AccessibilityState::CHECKED_CHECKBOX); | |
233 } else { | |
234 container->SetAccessiblityState( | |
235 HoverHighlightView::AccessibilityState::UNCHECKED_CHECKBOX); | |
236 } | |
237 | |
238 scroll_content()->AddChildView(container); | |
239 return container; | |
240 } | |
241 | |
242 void AccessibilityDetailedView::HandleViewClicked(views::View* view) { | |
243 AccessibilityDelegate* delegate = WmShell::Get()->accessibility_delegate(); | |
244 UserMetricsAction user_action; | |
245 if (view == spoken_feedback_view_) { | |
246 user_action = delegate->IsSpokenFeedbackEnabled() | |
247 ? ash::UMA_STATUS_AREA_DISABLE_SPOKEN_FEEDBACK | |
248 : ash::UMA_STATUS_AREA_ENABLE_SPOKEN_FEEDBACK; | |
249 delegate->ToggleSpokenFeedback(A11Y_NOTIFICATION_NONE); | |
250 } else if (view == high_contrast_view_) { | |
251 user_action = delegate->IsHighContrastEnabled() | |
252 ? ash::UMA_STATUS_AREA_DISABLE_HIGH_CONTRAST | |
253 : ash::UMA_STATUS_AREA_ENABLE_HIGH_CONTRAST; | |
254 delegate->ToggleHighContrast(); | |
255 } else if (view == screen_magnifier_view_) { | |
256 user_action = delegate->IsMagnifierEnabled() | |
257 ? ash::UMA_STATUS_AREA_DISABLE_MAGNIFIER | |
258 : ash::UMA_STATUS_AREA_ENABLE_MAGNIFIER; | |
259 delegate->SetMagnifierEnabled(!delegate->IsMagnifierEnabled()); | |
260 } else if (large_cursor_view_ && view == large_cursor_view_) { | |
261 user_action = delegate->IsLargeCursorEnabled() | |
262 ? ash::UMA_STATUS_AREA_DISABLE_LARGE_CURSOR | |
263 : ash::UMA_STATUS_AREA_ENABLE_LARGE_CURSOR; | |
264 delegate->SetLargeCursorEnabled(!delegate->IsLargeCursorEnabled()); | |
265 } else if (autoclick_view_ && view == autoclick_view_) { | |
266 user_action = delegate->IsAutoclickEnabled() | |
267 ? ash::UMA_STATUS_AREA_DISABLE_AUTO_CLICK | |
268 : ash::UMA_STATUS_AREA_ENABLE_AUTO_CLICK; | |
269 delegate->SetAutoclickEnabled(!delegate->IsAutoclickEnabled()); | |
270 } else if (virtual_keyboard_view_ && view == virtual_keyboard_view_) { | |
271 user_action = delegate->IsVirtualKeyboardEnabled() | |
272 ? ash::UMA_STATUS_AREA_DISABLE_VIRTUAL_KEYBOARD | |
273 : ash::UMA_STATUS_AREA_ENABLE_VIRTUAL_KEYBOARD; | |
274 delegate->SetVirtualKeyboardEnabled(!delegate->IsVirtualKeyboardEnabled()); | |
275 } else { | |
276 return; | |
277 } | |
278 WmShell::Get()->RecordUserMetricsAction(user_action); | |
279 } | |
280 | |
281 void AccessibilityDetailedView::HandleButtonPressed(views::Button* sender, | |
282 const ui::Event& event) { | |
283 if (sender == help_view_) | |
284 ShowHelp(); | |
285 else if (sender == settings_view_) | |
286 ShowSettings(); | |
287 } | |
288 | |
289 void AccessibilityDetailedView::CreateExtraTitleRowButtons() { | |
290 DCHECK(!help_view_); | |
291 DCHECK(!settings_view_); | |
292 | |
293 tri_view()->SetContainerVisible(TriView::Container::END, true); | |
294 | |
295 help_view_ = CreateHelpButton(login_); | |
296 settings_view_ = | |
297 CreateSettingsButton(login_, IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SETTINGS); | |
298 tri_view()->AddView(TriView::Container::END, help_view_); | |
299 tri_view()->AddView(TriView::Container::END, settings_view_); | |
300 } | |
301 | |
302 void AccessibilityDetailedView::ShowSettings() { | |
303 if (TrayPopupUtils::CanOpenWebUISettings(login_)) { | |
304 WmShell::Get()->system_tray_controller()->ShowAccessibilitySettings(); | |
305 owner()->system_tray()->CloseSystemBubble(); | |
306 } | |
307 } | |
308 | |
309 void AccessibilityDetailedView::ShowHelp() { | |
310 if (TrayPopupUtils::CanOpenWebUISettings(login_)) { | |
311 WmShell::Get()->system_tray_controller()->ShowAccessibilityHelp(); | |
312 owner()->system_tray()->CloseSystemBubble(); | |
313 } | |
314 } | |
315 | |
316 } // namespace tray | |
317 | |
318 //////////////////////////////////////////////////////////////////////////////// | |
319 // ash::TrayAccessibility | |
320 | |
321 TrayAccessibility::TrayAccessibility(SystemTray* system_tray) | |
322 : TrayImageItem(system_tray, | |
323 kSystemTrayAccessibilityIcon, | |
324 UMA_ACCESSIBILITY), | |
325 default_(NULL), | |
326 detailed_popup_(NULL), | |
327 detailed_menu_(NULL), | |
328 request_popup_view_state_(A11Y_NONE), | |
329 tray_icon_visible_(false), | |
330 login_(GetCurrentLoginStatus()), | |
331 previous_accessibility_state_(GetAccessibilityState()), | |
332 show_a11y_menu_on_lock_screen_(true) { | |
333 DCHECK(system_tray); | |
334 WmShell::Get()->system_tray_notifier()->AddAccessibilityObserver(this); | |
335 } | |
336 | |
337 TrayAccessibility::~TrayAccessibility() { | |
338 WmShell::Get()->system_tray_notifier()->RemoveAccessibilityObserver(this); | |
339 } | |
340 | |
341 void TrayAccessibility::SetTrayIconVisible(bool visible) { | |
342 if (tray_view()) | |
343 tray_view()->SetVisible(visible); | |
344 tray_icon_visible_ = visible; | |
345 } | |
346 | |
347 tray::AccessibilityDetailedView* TrayAccessibility::CreateDetailedMenu() { | |
348 return new tray::AccessibilityDetailedView(this, login_); | |
349 } | |
350 | |
351 bool TrayAccessibility::GetInitialVisibility() { | |
352 // Shows accessibility icon if any accessibility feature is enabled. | |
353 // Otherwise, doen't show it. | |
354 return GetAccessibilityState() != A11Y_NONE; | |
355 } | |
356 | |
357 views::View* TrayAccessibility::CreateDefaultView(LoginStatus status) { | |
358 CHECK(default_ == NULL); | |
359 | |
360 // Shows accessibility menu if: | |
361 // - on login screen (not logged in); | |
362 // - "Enable accessibility menu" on chrome://settings is checked; | |
363 // - or any of accessibility features is enabled | |
364 // Otherwise, not shows it. | |
365 AccessibilityDelegate* delegate = WmShell::Get()->accessibility_delegate(); | |
366 if (login_ != LoginStatus::NOT_LOGGED_IN && | |
367 !delegate->ShouldShowAccessibilityMenu() && | |
368 // On login screen, keeps the initial visibility of the menu. | |
369 (status != LoginStatus::LOCKED || !show_a11y_menu_on_lock_screen_)) | |
370 return NULL; | |
371 | |
372 CHECK(default_ == NULL); | |
373 default_ = new tray::DefaultAccessibilityView(this); | |
374 | |
375 return default_; | |
376 } | |
377 | |
378 views::View* TrayAccessibility::CreateDetailedView(LoginStatus status) { | |
379 CHECK(detailed_popup_ == NULL); | |
380 CHECK(detailed_menu_ == NULL); | |
381 | |
382 if (request_popup_view_state_) { | |
383 detailed_popup_ = | |
384 new tray::AccessibilityPopupView(request_popup_view_state_); | |
385 request_popup_view_state_ = A11Y_NONE; | |
386 return detailed_popup_; | |
387 } else { | |
388 WmShell::Get()->RecordUserMetricsAction( | |
389 ash::UMA_STATUS_AREA_DETAILED_ACCESSABILITY); | |
390 detailed_menu_ = CreateDetailedMenu(); | |
391 return detailed_menu_; | |
392 } | |
393 } | |
394 | |
395 void TrayAccessibility::DestroyDefaultView() { | |
396 default_ = NULL; | |
397 } | |
398 | |
399 void TrayAccessibility::DestroyDetailedView() { | |
400 detailed_popup_ = NULL; | |
401 detailed_menu_ = NULL; | |
402 } | |
403 | |
404 void TrayAccessibility::UpdateAfterLoginStatusChange(LoginStatus status) { | |
405 // Stores the a11y feature status on just entering the lock screen. | |
406 if (login_ != LoginStatus::LOCKED && status == LoginStatus::LOCKED) | |
407 show_a11y_menu_on_lock_screen_ = (GetAccessibilityState() != A11Y_NONE); | |
408 | |
409 login_ = status; | |
410 SetTrayIconVisible(GetInitialVisibility()); | |
411 } | |
412 | |
413 void TrayAccessibility::OnAccessibilityModeChanged( | |
414 AccessibilityNotificationVisibility notify) { | |
415 SetTrayIconVisible(GetInitialVisibility()); | |
416 | |
417 uint32_t accessibility_state = GetAccessibilityState(); | |
418 // We'll get an extra notification if a braille display is connected when | |
419 // spoken feedback wasn't already enabled. This is because the braille | |
420 // connection state is already updated when spoken feedback is enabled so | |
421 // that the notifications can be consolidated into one. Therefore, we | |
422 // return early if there's no change in the state that we keep track of. | |
423 if (accessibility_state == previous_accessibility_state_) | |
424 return; | |
425 // Contains bits for spoken feedback and braille display connected currently | |
426 // being enabled. | |
427 uint32_t being_enabled = | |
428 (accessibility_state & ~previous_accessibility_state_) & | |
429 (A11Y_SPOKEN_FEEDBACK | A11Y_BRAILLE_DISPLAY_CONNECTED); | |
430 if ((notify == A11Y_NOTIFICATION_SHOW) && being_enabled != A11Y_NONE) { | |
431 // Shows popup if |notify| is true and the spoken feedback is being enabled. | |
432 request_popup_view_state_ = being_enabled; | |
433 PopupDetailedView(kTrayPopupAutoCloseDelayForTextInSeconds, false); | |
434 } else { | |
435 if (detailed_popup_) | |
436 detailed_popup_->GetWidget()->Close(); | |
437 if (detailed_menu_) | |
438 detailed_menu_->GetWidget()->Close(); | |
439 } | |
440 | |
441 previous_accessibility_state_ = accessibility_state; | |
442 } | |
443 | |
444 } // namespace ash | |
OLD | NEW |