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