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