tray_accessibility.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
11d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert// Copyright (c) 2012 The Chromium Authors. All rights reserved. 21d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert// Use of this source code is governed by a BSD-style license that can be 31d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert// found in the LICENSE file. 41d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 51d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "ash/system/tray_accessibility.h" 61d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 71d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "ash/accessibility_delegate.h" 81d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "ash/shell.h" 91d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "ash/system/tray/hover_highlight_view.h" 101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "ash/system/tray/system_tray.h" 111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "ash/system/tray/system_tray_delegate.h" 121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "ash/system/tray/system_tray_notifier.h" 131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "ash/system/tray/tray_constants.h" 141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "ash/system/tray/tray_details_view.h" 151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "ash/system/tray/tray_item_more.h" 161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "ash/system/tray/tray_notification_view.h" 171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "ash/system/tray/tray_popup_label_button.h" 181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "grit/ash_resources.h" 191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "grit/ash_strings.h" 201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "ui/base/l10n/l10n_util.h" 211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "ui/base/resource/resource_bundle.h" 221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "ui/gfx/image/image.h" 231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "ui/views/controls/image_view.h" 241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "ui/views/controls/label.h" 251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "ui/views/layout/box_layout.h" 261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "ui/views/widget/widget.h" 271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertnamespace ash { 291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertnamespace internal { 301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertnamespace { 321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertenum AccessibilityState { 341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert A11Y_NONE = 0, 351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 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 TransitionToDefaultView(); 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