Upstream version 5.34.104.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/shell.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"
28
29 namespace ash {
30 namespace internal {
31
32 namespace {
33
34 enum AccessibilityState {
35   A11Y_NONE             = 0,
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,
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   return state;
61 }
62
63 user::LoginStatus GetCurrentLoginStatus() {
64   return Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus();
65 }
66
67 }  // namespace
68
69 namespace tray {
70
71 class DefaultAccessibilityView : public TrayItemMore {
72  public:
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).
77                     ToImageSkia());
78     base::string16 label = bundle.GetLocalizedString(
79         IDS_ASH_STATUS_TRAY_ACCESSIBILITY);
80     SetLabel(label);
81     SetAccessibleName(label);
82   }
83
84   virtual ~DefaultAccessibilityView() {
85   }
86
87  private:
88   DISALLOW_COPY_AND_ASSIGN(DefaultAccessibilityView);
89 };
90
91 class AccessibilityPopupView : public TrayNotificationView {
92  public:
93   AccessibilityPopupView(SystemTrayItem* owner)
94       : TrayNotificationView(owner, IDR_AURA_UBER_TRAY_ACCESSIBILITY_DARK) {
95     InitView(GetLabel());
96   }
97
98  private:
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);
105     return label;
106   }
107
108   DISALLOW_COPY_AND_ASSIGN(AccessibilityPopupView);
109 };
110
111 ////////////////////////////////////////////////////////////////////////////////
112 // ash::internal::tray::AccessibilityDetailedView
113
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),
121         help_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),
131         login_(login) {
132
133   Reset();
134
135   AppendAccessibilityList();
136   AppendHelpEntries();
137   CreateSpecialRow(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_TITLE, this);
138
139   Layout();
140 }
141
142 void AccessibilityDetailedView::AppendAccessibilityList() {
143   CreateScrollableList();
144   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
145
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_);
154
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_);
163   }
164
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_);
177
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,
185         autoclick_enabled_);
186   }
187
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_);
194 }
195
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)
201     return;
202
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);
211
212   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
213
214   TrayPopupLabelButton* help = new TrayPopupLabelButton(
215       this,
216       bundle.GetLocalizedString(
217           IDS_ASH_STATUS_TRAY_ACCESSIBILITY_LEARN_MORE));
218   bottom_row->AddChildView(help);
219   help_view_ = help;
220
221   TrayPopupLabelButton* settings = new TrayPopupLabelButton(
222       this,
223       bundle.GetLocalizedString(
224           IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SETTINGS));
225   bottom_row->AddChildView(settings);
226   settings_view_ = settings;
227
228   AddChildView(bottom_row);
229 }
230
231 HoverHighlightView* AccessibilityDetailedView::AddScrollListItem(
232     const base::string16& text,
233     gfx::Font::FontStyle style,
234     bool checked) {
235   HoverHighlightView* container = new HoverHighlightView(this);
236   container->AddCheckableLabel(text, style, checked);
237   scroll_content()->AddChildView(container);
238   return container;
239 }
240
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());
282   }
283 }
284
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();
293 }
294
295 }  // namespace tray
296
297 ////////////////////////////////////////////////////////////////////////////////
298 // ash::internal::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_(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());
311   DCHECK(system_tray);
312   Shell::GetInstance()->system_tray_notifier()->AddAccessibilityObserver(this);
313 }
314
315 TrayAccessibility::~TrayAccessibility() {
316   Shell::GetInstance()->system_tray_notifier()->
317       RemoveAccessibilityObserver(this);
318 }
319
320 void TrayAccessibility::SetTrayIconVisible(bool visible) {
321   if (tray_view())
322     tray_view()->SetVisible(visible);
323   tray_icon_visible_ = visible;
324 }
325
326 tray::AccessibilityDetailedView* TrayAccessibility::CreateDetailedMenu() {
327   return new tray::AccessibilityDetailedView(this, login_);
328 }
329
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;
334 }
335
336 views::View* TrayAccessibility::CreateDefaultView(user::LoginStatus status) {
337   CHECK(default_ == NULL);
338
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_))
350     return NULL;
351
352   CHECK(default_ == NULL);
353   default_ = new tray::DefaultAccessibilityView(this);
354
355   return default_;
356 }
357
358 views::View* TrayAccessibility::CreateDetailedView(user::LoginStatus status) {
359   CHECK(detailed_popup_ == NULL);
360   CHECK(detailed_menu_ == NULL);
361
362   if (request_popup_view_) {
363     detailed_popup_ = new tray::AccessibilityPopupView(this);
364     request_popup_view_ = false;
365     return detailed_popup_;
366   } else {
367     Shell::GetInstance()->metrics()->RecordUserMetricsAction(
368         ash::UMA_STATUS_AREA_DETAILED_ACCESSABILITY);
369     detailed_menu_ = CreateDetailedMenu();
370     return detailed_menu_;
371   }
372 }
373
374 void TrayAccessibility::DestroyDefaultView() {
375   default_ = NULL;
376 }
377
378 void TrayAccessibility::DestroyDetailedView() {
379   detailed_popup_ = NULL;
380   detailed_menu_ = NULL;
381 }
382
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);
387
388   login_ = status;
389   SetTrayIconVisible(GetInitialVisibility());
390 }
391
392 void TrayAccessibility::OnAccessibilityModeChanged(
393     AccessibilityNotificationVisibility notify) {
394   SetTrayIconVisible(GetInitialVisibility());
395
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);
403   } else {
404     if (detailed_popup_)
405       detailed_popup_->GetWidget()->Close();
406     if (detailed_menu_)
407       detailed_menu_->GetWidget()->Close();
408   }
409
410   previous_accessibility_state_ = accessibility_state;
411 }
412
413 }  // namespace internal
414 }  // namespace ash