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