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.
5 #include "ash/system/tray_accessibility.h"
7 #include "ash/accessibility_delegate.h"
8 #include "ash/metrics/user_metrics_recorder.h"
10 #include "ash/system/tray/hover_highlight_view.h"
11 #include "ash/system/tray/system_tray.h"
12 #include "ash/system/tray/system_tray_delegate.h"
13 #include "ash/system/tray/system_tray_notifier.h"
14 #include "ash/system/tray/tray_constants.h"
15 #include "ash/system/tray/tray_details_view.h"
16 #include "ash/system/tray/tray_item_more.h"
17 #include "ash/system/tray/tray_notification_view.h"
18 #include "ash/system/tray/tray_popup_label_button.h"
19 #include "grit/ash_resources.h"
20 #include "grit/ash_strings.h"
21 #include "ui/base/l10n/l10n_util.h"
22 #include "ui/base/resource/resource_bundle.h"
23 #include "ui/gfx/image/image.h"
24 #include "ui/views/controls/image_view.h"
25 #include "ui/views/controls/label.h"
26 #include "ui/views/layout/box_layout.h"
27 #include "ui/views/widget/widget.h"
34 enum AccessibilityState {
36 A11Y_SPOKEN_FEEDBACK = 1 << 0,
37 A11Y_HIGH_CONTRAST = 1 << 1,
38 A11Y_SCREEN_MAGNIFIER = 1 << 2,
39 A11Y_LARGE_CURSOR = 1 << 3,
40 A11Y_AUTOCLICK = 1 << 4,
41 A11Y_VIRTUAL_KEYBOARD = 1 << 5,
44 uint32 GetAccessibilityState() {
45 AccessibilityDelegate* delegate =
46 Shell::GetInstance()->accessibility_delegate();
47 uint32 state = A11Y_NONE;
48 if (delegate->IsSpokenFeedbackEnabled())
49 state |= A11Y_SPOKEN_FEEDBACK;
50 if (delegate->IsHighContrastEnabled())
51 state |= A11Y_HIGH_CONTRAST;
52 if (delegate->IsMagnifierEnabled())
53 state |= A11Y_SCREEN_MAGNIFIER;
54 if (delegate->IsLargeCursorEnabled())
55 state |= A11Y_LARGE_CURSOR;
56 if (delegate->IsAutoclickEnabled())
57 state |= A11Y_AUTOCLICK;
58 if (delegate->IsVirtualKeyboardEnabled())
59 state |= A11Y_VIRTUAL_KEYBOARD;
63 user::LoginStatus GetCurrentLoginStatus() {
64 return Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus();
71 class DefaultAccessibilityView : public TrayItemMore {
73 explicit DefaultAccessibilityView(SystemTrayItem* owner)
74 : TrayItemMore(owner, true) {
75 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
76 SetImage(bundle.GetImageNamed(IDR_AURA_UBER_TRAY_ACCESSIBILITY_DARK).
78 base::string16 label = bundle.GetLocalizedString(
79 IDS_ASH_STATUS_TRAY_ACCESSIBILITY);
81 SetAccessibleName(label);
84 virtual ~DefaultAccessibilityView() {
88 DISALLOW_COPY_AND_ASSIGN(DefaultAccessibilityView);
91 class AccessibilityPopupView : public TrayNotificationView {
93 AccessibilityPopupView(SystemTrayItem* owner)
94 : TrayNotificationView(owner, IDR_AURA_UBER_TRAY_ACCESSIBILITY_DARK) {
99 views::Label* GetLabel() {
100 views::Label* label = new views::Label(
101 l10n_util::GetStringUTF16(
102 IDS_ASH_STATUS_TRAY_SPOKEN_FEEDBACK_ENABLED_BUBBLE));
103 label->SetMultiLine(true);
104 label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
108 DISALLOW_COPY_AND_ASSIGN(AccessibilityPopupView);
111 ////////////////////////////////////////////////////////////////////////////////
112 // ash::internal::tray::AccessibilityDetailedView
114 AccessibilityDetailedView::AccessibilityDetailedView(
115 SystemTrayItem* owner, user::LoginStatus login) :
116 TrayDetailsView(owner),
117 spoken_feedback_view_(NULL),
118 high_contrast_view_(NULL),
119 screen_magnifier_view_(NULL),
120 large_cursor_view_(NULL),
122 settings_view_(NULL),
123 autoclick_view_(NULL),
124 virtual_keyboard_view_(NULL),
125 spoken_feedback_enabled_(false),
126 high_contrast_enabled_(false),
127 screen_magnifier_enabled_(false),
128 large_cursor_enabled_(false),
129 autoclick_enabled_(false),
130 virtual_keyboard_enabled_(false),
135 AppendAccessibilityList();
137 CreateSpecialRow(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_TITLE, this);
142 void AccessibilityDetailedView::AppendAccessibilityList() {
143 CreateScrollableList();
144 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
146 AccessibilityDelegate* delegate =
147 Shell::GetInstance()->accessibility_delegate();
148 spoken_feedback_enabled_ = delegate->IsSpokenFeedbackEnabled();
149 spoken_feedback_view_ = AddScrollListItem(
150 bundle.GetLocalizedString(
151 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SPOKEN_FEEDBACK),
152 spoken_feedback_enabled_ ? gfx::Font::BOLD : gfx::Font::NORMAL,
153 spoken_feedback_enabled_);
155 // Large Cursor item is shown only in Login screen.
156 if (login_ == user::LOGGED_IN_NONE) {
157 large_cursor_enabled_ = delegate->IsLargeCursorEnabled();
158 large_cursor_view_ = AddScrollListItem(
159 bundle.GetLocalizedString(
160 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_LARGE_CURSOR),
161 large_cursor_enabled_ ? gfx::Font::BOLD : gfx::Font::NORMAL,
162 large_cursor_enabled_);
165 high_contrast_enabled_ = delegate->IsHighContrastEnabled();
166 high_contrast_view_ = AddScrollListItem(
167 bundle.GetLocalizedString(
168 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_HIGH_CONTRAST_MODE),
169 high_contrast_enabled_ ? gfx::Font::BOLD : gfx::Font::NORMAL,
170 high_contrast_enabled_);
171 screen_magnifier_enabled_ = delegate->IsMagnifierEnabled();
172 screen_magnifier_view_ = AddScrollListItem(
173 bundle.GetLocalizedString(
174 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SCREEN_MAGNIFIER),
175 screen_magnifier_enabled_ ? gfx::Font::BOLD : gfx::Font::NORMAL,
176 screen_magnifier_enabled_);
178 // Don't show autoclick option at login screen.
179 if (login_ != user::LOGGED_IN_NONE) {
180 autoclick_enabled_ = delegate->IsAutoclickEnabled();
181 autoclick_view_ = AddScrollListItem(
182 bundle.GetLocalizedString(
183 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_AUTOCLICK),
184 autoclick_enabled_ ? gfx::Font::BOLD : gfx::Font::NORMAL,
188 virtual_keyboard_enabled_ = delegate->IsVirtualKeyboardEnabled();
189 virtual_keyboard_view_ = AddScrollListItem(
190 bundle.GetLocalizedString(
191 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_VIRTUAL_KEYBOARD),
192 virtual_keyboard_enabled_ ? gfx::Font::BOLD : gfx::Font::NORMAL,
193 virtual_keyboard_enabled_);
196 void AccessibilityDetailedView::AppendHelpEntries() {
197 // Currently the help page requires a browser window.
198 // TODO(yoshiki): show this even on login/lock screen. crbug.com/158286
199 if (login_ == user::LOGGED_IN_NONE ||
200 login_ == user::LOGGED_IN_LOCKED)
203 views::View* bottom_row = new View();
204 views::BoxLayout* layout = new
205 views::BoxLayout(views::BoxLayout::kHorizontal,
206 kTrayMenuBottomRowPadding,
207 kTrayMenuBottomRowPadding,
208 kTrayMenuBottomRowPaddingBetweenItems);
209 layout->set_spread_blank_space(true);
210 bottom_row->SetLayoutManager(layout);
212 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
214 TrayPopupLabelButton* help = new TrayPopupLabelButton(
216 bundle.GetLocalizedString(
217 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_LEARN_MORE));
218 bottom_row->AddChildView(help);
221 TrayPopupLabelButton* settings = new TrayPopupLabelButton(
223 bundle.GetLocalizedString(
224 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SETTINGS));
225 bottom_row->AddChildView(settings);
226 settings_view_ = settings;
228 AddChildView(bottom_row);
231 HoverHighlightView* AccessibilityDetailedView::AddScrollListItem(
232 const base::string16& text,
233 gfx::Font::FontStyle style,
235 HoverHighlightView* container = new HoverHighlightView(this);
236 container->AddCheckableLabel(text, style, checked);
237 scroll_content()->AddChildView(container);
241 void AccessibilityDetailedView::OnViewClicked(views::View* sender) {
242 AccessibilityDelegate* delegate =
243 Shell::GetInstance()->accessibility_delegate();
244 if (sender == footer()->content()) {
245 TransitionToDefaultView();
246 } else if (sender == spoken_feedback_view_) {
247 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
248 delegate->IsSpokenFeedbackEnabled() ?
249 ash::UMA_STATUS_AREA_DISABLE_SPOKEN_FEEDBACK :
250 ash::UMA_STATUS_AREA_ENABLE_SPOKEN_FEEDBACK);
251 delegate->ToggleSpokenFeedback(ash::A11Y_NOTIFICATION_NONE);
252 } else if (sender == high_contrast_view_) {
253 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
254 delegate->IsHighContrastEnabled() ?
255 ash::UMA_STATUS_AREA_DISABLE_HIGH_CONTRAST :
256 ash::UMA_STATUS_AREA_ENABLE_HIGH_CONTRAST);
257 delegate->ToggleHighContrast();
258 } else if (sender == screen_magnifier_view_) {
259 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
260 delegate->IsMagnifierEnabled() ?
261 ash::UMA_STATUS_AREA_DISABLE_MAGNIFIER :
262 ash::UMA_STATUS_AREA_ENABLE_MAGNIFIER);
263 delegate->SetMagnifierEnabled(!delegate->IsMagnifierEnabled());
264 } else if (large_cursor_view_ && sender == large_cursor_view_) {
265 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
266 delegate->IsLargeCursorEnabled() ?
267 ash::UMA_STATUS_AREA_DISABLE_LARGE_CURSOR :
268 ash::UMA_STATUS_AREA_ENABLE_LARGE_CURSOR);
269 delegate->SetLargeCursorEnabled(!delegate->IsLargeCursorEnabled());
270 } else if (autoclick_view_ && sender == autoclick_view_) {
271 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
272 delegate->IsAutoclickEnabled() ?
273 ash::UMA_STATUS_AREA_DISABLE_AUTO_CLICK :
274 ash::UMA_STATUS_AREA_ENABLE_AUTO_CLICK);
275 delegate->SetAutoclickEnabled(!delegate->IsAutoclickEnabled());
276 } else if (virtual_keyboard_view_ && sender == virtual_keyboard_view_) {
277 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
278 delegate->IsVirtualKeyboardEnabled() ?
279 ash::UMA_STATUS_AREA_DISABLE_VIRTUAL_KEYBOARD :
280 ash::UMA_STATUS_AREA_ENABLE_VIRTUAL_KEYBOARD);
281 delegate->SetVirtualKeyboardEnabled(!delegate->IsVirtualKeyboardEnabled());
285 void AccessibilityDetailedView::ButtonPressed(views::Button* sender,
286 const ui::Event& event) {
287 SystemTrayDelegate* tray_delegate =
288 Shell::GetInstance()->system_tray_delegate();
289 if (sender == help_view_)
290 tray_delegate->ShowAccessibilityHelp();
291 else if (sender == settings_view_)
292 tray_delegate->ShowAccessibilitySettings();
297 ////////////////////////////////////////////////////////////////////////////////
298 // ash::internal::TrayAccessibility
300 TrayAccessibility::TrayAccessibility(SystemTray* system_tray)
301 : TrayImageItem(system_tray, IDR_AURA_UBER_TRAY_ACCESSIBILITY),
303 detailed_popup_(NULL),
304 detailed_menu_(NULL),
305 request_popup_view_(false),
306 tray_icon_visible_(false),
307 login_(GetCurrentLoginStatus()),
308 previous_accessibility_state_(GetAccessibilityState()),
309 show_a11y_menu_on_lock_screen_(true) {
310 DCHECK(Shell::GetInstance()->delegate());
312 Shell::GetInstance()->system_tray_notifier()->AddAccessibilityObserver(this);
315 TrayAccessibility::~TrayAccessibility() {
316 Shell::GetInstance()->system_tray_notifier()->
317 RemoveAccessibilityObserver(this);
320 void TrayAccessibility::SetTrayIconVisible(bool visible) {
322 tray_view()->SetVisible(visible);
323 tray_icon_visible_ = visible;
326 tray::AccessibilityDetailedView* TrayAccessibility::CreateDetailedMenu() {
327 return new tray::AccessibilityDetailedView(this, login_);
330 bool TrayAccessibility::GetInitialVisibility() {
331 // Shows accessibility icon if any accessibility feature is enabled.
332 // Otherwise, doen't show it.
333 return GetAccessibilityState() != A11Y_NONE;
336 views::View* TrayAccessibility::CreateDefaultView(user::LoginStatus status) {
337 CHECK(default_ == NULL);
339 // Shows accessibility menu if:
340 // - on login screen (not logged in);
341 // - "Enable accessibility menu" on chrome://settings is checked;
342 // - or any of accessibility features is enabled
343 // Otherwise, not shows it.
344 AccessibilityDelegate* delegate =
345 Shell::GetInstance()->accessibility_delegate();
346 if (login_ != user::LOGGED_IN_NONE &&
347 !delegate->ShouldShowAccessibilityMenu() &&
348 // On login screen, keeps the initial visibility of the menu.
349 (status != user::LOGGED_IN_LOCKED || !show_a11y_menu_on_lock_screen_))
352 CHECK(default_ == NULL);
353 default_ = new tray::DefaultAccessibilityView(this);
358 views::View* TrayAccessibility::CreateDetailedView(user::LoginStatus status) {
359 CHECK(detailed_popup_ == NULL);
360 CHECK(detailed_menu_ == NULL);
362 if (request_popup_view_) {
363 detailed_popup_ = new tray::AccessibilityPopupView(this);
364 request_popup_view_ = false;
365 return detailed_popup_;
367 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
368 ash::UMA_STATUS_AREA_DETAILED_ACCESSABILITY);
369 detailed_menu_ = CreateDetailedMenu();
370 return detailed_menu_;
374 void TrayAccessibility::DestroyDefaultView() {
378 void TrayAccessibility::DestroyDetailedView() {
379 detailed_popup_ = NULL;
380 detailed_menu_ = NULL;
383 void TrayAccessibility::UpdateAfterLoginStatusChange(user::LoginStatus status) {
384 // Stores the a11y feature status on just entering the lock screen.
385 if (login_ != user::LOGGED_IN_LOCKED && status == user::LOGGED_IN_LOCKED)
386 show_a11y_menu_on_lock_screen_ = (GetAccessibilityState() != A11Y_NONE);
389 SetTrayIconVisible(GetInitialVisibility());
392 void TrayAccessibility::OnAccessibilityModeChanged(
393 AccessibilityNotificationVisibility notify) {
394 SetTrayIconVisible(GetInitialVisibility());
396 uint32 accessibility_state = GetAccessibilityState();
397 if ((notify == ash::A11Y_NOTIFICATION_SHOW) &&
398 !(previous_accessibility_state_ & A11Y_SPOKEN_FEEDBACK) &&
399 (accessibility_state & A11Y_SPOKEN_FEEDBACK)) {
400 // Shows popup if |notify| is true and the spoken feedback is being enabled.
401 request_popup_view_ = true;
402 PopupDetailedView(kTrayPopupAutoCloseDelayForTextInSeconds, false);
405 detailed_popup_->GetWidget()->Close();
407 detailed_menu_->GetWidget()->Close();
410 previous_accessibility_state_ = accessibility_state;
413 } // namespace internal