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