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