tray_accessibility.cc revision 1e9bf3e0803691d0a228da41fc608347b6db4340
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/shell.h"
9#include "ash/system/tray/hover_highlight_view.h"
10#include "ash/system/tray/system_tray.h"
11#include "ash/system/tray/system_tray_delegate.h"
12#include "ash/system/tray/system_tray_notifier.h"
13#include "ash/system/tray/tray_constants.h"
14#include "ash/system/tray/tray_details_view.h"
15#include "ash/system/tray/tray_item_more.h"
16#include "ash/system/tray/tray_notification_view.h"
17#include "ash/system/tray/tray_popup_label_button.h"
18#include "grit/ash_resources.h"
19#include "grit/ash_strings.h"
20#include "ui/base/l10n/l10n_util.h"
21#include "ui/base/resource/resource_bundle.h"
22#include "ui/gfx/image/image.h"
23#include "ui/views/controls/image_view.h"
24#include "ui/views/controls/label.h"
25#include "ui/views/layout/box_layout.h"
26#include "ui/views/widget/widget.h"
27
28namespace ash {
29namespace internal {
30
31namespace {
32
33enum 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};
41
42uint32 GetAccessibilityState() {
43  AccessibilityDelegate* delegate =
44      Shell::GetInstance()->accessibility_delegate();
45  uint32 state = A11Y_NONE;
46  if (delegate->IsSpokenFeedbackEnabled())
47    state |= A11Y_SPOKEN_FEEDBACK;
48  if (delegate->IsHighContrastEnabled())
49    state |= A11Y_HIGH_CONTRAST;
50  if (delegate->IsMagnifierEnabled())
51    state |= A11Y_SCREEN_MAGNIFIER;
52  if (delegate->IsLargeCursorEnabled())
53    state |= A11Y_LARGE_CURSOR;
54  if (delegate->IsAutoclickEnabled())
55    state |= A11Y_AUTOCLICK;
56  return state;
57}
58
59user::LoginStatus GetCurrentLoginStatus() {
60  return Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus();
61}
62
63}  // namespace
64
65namespace tray {
66
67class DefaultAccessibilityView : public TrayItemMore {
68 public:
69  explicit DefaultAccessibilityView(SystemTrayItem* owner)
70      : TrayItemMore(owner, true) {
71    ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
72    SetImage(bundle.GetImageNamed(IDR_AURA_UBER_TRAY_ACCESSIBILITY_DARK).
73                    ToImageSkia());
74    base::string16 label = bundle.GetLocalizedString(
75        IDS_ASH_STATUS_TRAY_ACCESSIBILITY);
76    SetLabel(label);
77    SetAccessibleName(label);
78  }
79
80  virtual ~DefaultAccessibilityView() {
81  }
82
83 private:
84  DISALLOW_COPY_AND_ASSIGN(DefaultAccessibilityView);
85};
86
87class AccessibilityPopupView : public TrayNotificationView {
88 public:
89  AccessibilityPopupView(SystemTrayItem* owner)
90      : TrayNotificationView(owner, IDR_AURA_UBER_TRAY_ACCESSIBILITY_DARK) {
91    InitView(GetLabel());
92  }
93
94 private:
95  views::Label* GetLabel() {
96    views::Label* label = new views::Label(
97        l10n_util::GetStringUTF16(
98            IDS_ASH_STATUS_TRAY_SPOKEN_FEEDBACK_ENABLED_BUBBLE));
99    label->SetMultiLine(true);
100    label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
101    return label;
102  }
103
104  DISALLOW_COPY_AND_ASSIGN(AccessibilityPopupView);
105};
106
107////////////////////////////////////////////////////////////////////////////////
108// ash::internal::tray::AccessibilityDetailedView
109
110AccessibilityDetailedView::AccessibilityDetailedView(
111    SystemTrayItem* owner, user::LoginStatus login) :
112        TrayDetailsView(owner),
113        spoken_feedback_view_(NULL),
114        high_contrast_view_(NULL),
115        screen_magnifier_view_(NULL),
116        large_cursor_view_(NULL),
117        help_view_(NULL),
118        settings_view_(NULL),
119        autoclick_view_(NULL),
120        spoken_feedback_enabled_(false),
121        high_contrast_enabled_(false),
122        screen_magnifier_enabled_(false),
123        large_cursor_enabled_(false),
124        autoclick_enabled_(false),
125        login_(login) {
126
127  Reset();
128
129  AppendAccessibilityList();
130  AppendHelpEntries();
131  CreateSpecialRow(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_TITLE, this);
132
133  Layout();
134}
135
136void AccessibilityDetailedView::AppendAccessibilityList() {
137  CreateScrollableList();
138  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
139
140  AccessibilityDelegate* delegate =
141      Shell::GetInstance()->accessibility_delegate();
142  spoken_feedback_enabled_ = delegate->IsSpokenFeedbackEnabled();
143  spoken_feedback_view_ = AddScrollListItem(
144      bundle.GetLocalizedString(
145          IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SPOKEN_FEEDBACK),
146      spoken_feedback_enabled_ ? gfx::Font::BOLD : gfx::Font::NORMAL,
147      spoken_feedback_enabled_);
148
149  // Large Cursor item is shown only in Login screen.
150  if (login_ == user::LOGGED_IN_NONE) {
151    large_cursor_enabled_ = delegate->IsLargeCursorEnabled();
152    large_cursor_view_ = AddScrollListItem(
153        bundle.GetLocalizedString(
154            IDS_ASH_STATUS_TRAY_ACCESSIBILITY_LARGE_CURSOR),
155        large_cursor_enabled_ ? gfx::Font::BOLD : gfx::Font::NORMAL,
156        large_cursor_enabled_);
157  }
158
159  high_contrast_enabled_ = delegate->IsHighContrastEnabled();
160  high_contrast_view_ = AddScrollListItem(
161      bundle.GetLocalizedString(
162          IDS_ASH_STATUS_TRAY_ACCESSIBILITY_HIGH_CONTRAST_MODE),
163      high_contrast_enabled_ ? gfx::Font::BOLD : gfx::Font::NORMAL,
164      high_contrast_enabled_);
165  screen_magnifier_enabled_ = delegate->IsMagnifierEnabled();
166  screen_magnifier_view_ = AddScrollListItem(
167      bundle.GetLocalizedString(
168          IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SCREEN_MAGNIFIER),
169      screen_magnifier_enabled_ ? gfx::Font::BOLD : gfx::Font::NORMAL,
170      screen_magnifier_enabled_);
171
172  // Don't show autoclick option at login screen.
173  if (login_ != user::LOGGED_IN_NONE) {
174    autoclick_enabled_ = delegate->IsAutoclickEnabled();
175    autoclick_view_ = AddScrollListItem(
176        bundle.GetLocalizedString(
177            IDS_ASH_STATUS_TRAY_ACCESSIBILITY_AUTOCLICK),
178        autoclick_enabled_ ? gfx::Font::BOLD : gfx::Font::NORMAL,
179        autoclick_enabled_);
180  }
181}
182
183void AccessibilityDetailedView::AppendHelpEntries() {
184  // Currently the help page requires a browser window.
185  // TODO(yoshiki): show this even on login/lock screen. crbug.com/158286
186  if (login_ == user::LOGGED_IN_NONE ||
187      login_ == user::LOGGED_IN_LOCKED)
188    return;
189
190  views::View* bottom_row = new View();
191  views::BoxLayout* layout = new
192      views::BoxLayout(views::BoxLayout::kHorizontal,
193                       kTrayMenuBottomRowPadding,
194                       kTrayMenuBottomRowPadding,
195                       kTrayMenuBottomRowPaddingBetweenItems);
196  layout->set_spread_blank_space(true);
197  bottom_row->SetLayoutManager(layout);
198
199  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
200
201  TrayPopupLabelButton* help = new TrayPopupLabelButton(
202      this,
203      bundle.GetLocalizedString(
204          IDS_ASH_STATUS_TRAY_ACCESSIBILITY_LEARN_MORE));
205  bottom_row->AddChildView(help);
206  help_view_ = help;
207
208  TrayPopupLabelButton* settings = new TrayPopupLabelButton(
209      this,
210      bundle.GetLocalizedString(
211          IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SETTINGS));
212  bottom_row->AddChildView(settings);
213  settings_view_ = settings;
214
215  AddChildView(bottom_row);
216}
217
218HoverHighlightView* AccessibilityDetailedView::AddScrollListItem(
219    const base::string16& text,
220    gfx::Font::FontStyle style,
221    bool checked) {
222  HoverHighlightView* container = new HoverHighlightView(this);
223  container->AddCheckableLabel(text, style, checked);
224  scroll_content()->AddChildView(container);
225  return container;
226}
227
228void AccessibilityDetailedView::OnViewClicked(views::View* sender) {
229  AccessibilityDelegate* delegate =
230      Shell::GetInstance()->accessibility_delegate();
231  if (sender == footer()->content()) {
232    owner()->system_tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
233  } else if (sender == spoken_feedback_view_) {
234    delegate->ToggleSpokenFeedback(ash::A11Y_NOTIFICATION_NONE);
235  } else if (sender == high_contrast_view_) {
236    delegate->ToggleHighContrast();
237  } else if (sender == screen_magnifier_view_) {
238    delegate->SetMagnifierEnabled(!delegate->IsMagnifierEnabled());
239  } else if (large_cursor_view_ && sender == large_cursor_view_) {
240    delegate->SetLargeCursorEnabled(!delegate->IsLargeCursorEnabled());
241  } else if (autoclick_view_ && sender == autoclick_view_) {
242    delegate->SetAutoclickEnabled(!delegate->IsAutoclickEnabled());
243  }
244}
245
246void AccessibilityDetailedView::ButtonPressed(views::Button* sender,
247                                              const ui::Event& event) {
248  SystemTrayDelegate* tray_delegate =
249      Shell::GetInstance()->system_tray_delegate();
250  if (sender == help_view_)
251    tray_delegate->ShowAccessibilityHelp();
252  else if (sender == settings_view_)
253    tray_delegate->ShowAccessibilitySettings();
254}
255
256}  // namespace tray
257
258////////////////////////////////////////////////////////////////////////////////
259// ash::internal::TrayAccessibility
260
261TrayAccessibility::TrayAccessibility(SystemTray* system_tray)
262    : TrayImageItem(system_tray, IDR_AURA_UBER_TRAY_ACCESSIBILITY),
263      default_(NULL),
264      detailed_popup_(NULL),
265      detailed_menu_(NULL),
266      request_popup_view_(false),
267      tray_icon_visible_(false),
268      login_(GetCurrentLoginStatus()),
269      previous_accessibility_state_(GetAccessibilityState()),
270      show_a11y_menu_on_lock_screen_(true) {
271  DCHECK(Shell::GetInstance()->delegate());
272  DCHECK(system_tray);
273  Shell::GetInstance()->system_tray_notifier()->AddAccessibilityObserver(this);
274}
275
276TrayAccessibility::~TrayAccessibility() {
277  Shell::GetInstance()->system_tray_notifier()->
278      RemoveAccessibilityObserver(this);
279}
280
281void TrayAccessibility::SetTrayIconVisible(bool visible) {
282  if (tray_view())
283    tray_view()->SetVisible(visible);
284  tray_icon_visible_ = visible;
285}
286
287tray::AccessibilityDetailedView* TrayAccessibility::CreateDetailedMenu() {
288  return new tray::AccessibilityDetailedView(this, login_);
289}
290
291bool TrayAccessibility::GetInitialVisibility() {
292  // Shows accessibility icon if any accessibility feature is enabled.
293  // Otherwise, doen't show it.
294  return GetAccessibilityState() != A11Y_NONE;
295}
296
297views::View* TrayAccessibility::CreateDefaultView(user::LoginStatus status) {
298  CHECK(default_ == NULL);
299
300  // Shows accessibility menu if:
301  // - on login screen (not logged in);
302  // - "Enable accessibility menu" on chrome://settings is checked;
303  // - or any of accessibility features is enabled
304  // Otherwise, not shows it.
305  AccessibilityDelegate* delegate =
306      Shell::GetInstance()->accessibility_delegate();
307  if (login_ != user::LOGGED_IN_NONE &&
308      !delegate->ShouldAlwaysShowAccessibilityMenu() &&
309      // On login screen, keeps the initial visivility of the menu.
310      (status != user::LOGGED_IN_LOCKED || !show_a11y_menu_on_lock_screen_) &&
311      GetAccessibilityState() == A11Y_NONE)
312    return NULL;
313
314  CHECK(default_ == NULL);
315  default_ = new tray::DefaultAccessibilityView(this);
316
317  return default_;
318}
319
320views::View* TrayAccessibility::CreateDetailedView(user::LoginStatus status) {
321  CHECK(detailed_popup_ == NULL);
322  CHECK(detailed_menu_ == NULL);
323
324  if (request_popup_view_) {
325    detailed_popup_ = new tray::AccessibilityPopupView(this);
326    request_popup_view_ = false;
327    return detailed_popup_;
328  } else {
329    detailed_menu_ = CreateDetailedMenu();
330    return detailed_menu_;
331  }
332}
333
334void TrayAccessibility::DestroyDefaultView() {
335  default_ = NULL;
336}
337
338void TrayAccessibility::DestroyDetailedView() {
339  detailed_popup_ = NULL;
340  detailed_menu_ = NULL;
341}
342
343void TrayAccessibility::UpdateAfterLoginStatusChange(user::LoginStatus status) {
344  // Stores the a11y feature status on just entering the lock screen.
345  if (login_ != user::LOGGED_IN_LOCKED && status == user::LOGGED_IN_LOCKED)
346    show_a11y_menu_on_lock_screen_ = (GetAccessibilityState() != A11Y_NONE);
347
348  login_ = status;
349  SetTrayIconVisible(GetInitialVisibility());
350}
351
352void TrayAccessibility::OnAccessibilityModeChanged(
353    AccessibilityNotificationVisibility notify) {
354  SetTrayIconVisible(GetInitialVisibility());
355
356  uint32 accessibility_state = GetAccessibilityState();
357  if ((notify == ash::A11Y_NOTIFICATION_SHOW) &&
358      !(previous_accessibility_state_ & A11Y_SPOKEN_FEEDBACK) &&
359      (accessibility_state & A11Y_SPOKEN_FEEDBACK)) {
360    // Shows popup if |notify| is true and the spoken feedback is being enabled.
361    request_popup_view_ = true;
362    PopupDetailedView(kTrayPopupAutoCloseDelayForTextInSeconds, false);
363  } else {
364    if (detailed_popup_)
365      detailed_popup_->GetWidget()->Close();
366    if (detailed_menu_)
367      detailed_menu_->GetWidget()->Close();
368  }
369
370  previous_accessibility_state_ = accessibility_state;
371}
372
373}  // namespace internal
374}  // namespace ash
375