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