tray_accessibility.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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/accessibility_delegate.h" 8#include "ash/metrics/user_metrics_recorder.h" 9#include "ash/shell.h" 10#include "ash/system/tray/hover_highlight_view.h" 11#include "ash/system/tray/system_tray.h" 12#include "ash/system/tray/system_tray_delegate.h" 13#include "ash/system/tray/system_tray_notifier.h" 14#include "ash/system/tray/tray_constants.h" 15#include "ash/system/tray/tray_details_view.h" 16#include "ash/system/tray/tray_item_more.h" 17#include "ash/system/tray/tray_notification_view.h" 18#include "ash/system/tray/tray_popup_label_button.h" 19#include "grit/ash_resources.h" 20#include "grit/ash_strings.h" 21#include "ui/base/l10n/l10n_util.h" 22#include "ui/base/resource/resource_bundle.h" 23#include "ui/gfx/image/image.h" 24#include "ui/views/controls/image_view.h" 25#include "ui/views/controls/label.h" 26#include "ui/views/layout/box_layout.h" 27#include "ui/views/widget/widget.h" 28 29namespace ash { 30namespace internal { 31 32namespace { 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 A11Y_AUTOCLICK = 1 << 4, 41 A11Y_VIRTUAL_KEYBOARD = 1 << 5, 42}; 43 44uint32 GetAccessibilityState() { 45 AccessibilityDelegate* delegate = 46 Shell::GetInstance()->accessibility_delegate(); 47 uint32 state = A11Y_NONE; 48 if (delegate->IsSpokenFeedbackEnabled()) 49 state |= A11Y_SPOKEN_FEEDBACK; 50 if (delegate->IsHighContrastEnabled()) 51 state |= A11Y_HIGH_CONTRAST; 52 if (delegate->IsMagnifierEnabled()) 53 state |= A11Y_SCREEN_MAGNIFIER; 54 if (delegate->IsLargeCursorEnabled()) 55 state |= A11Y_LARGE_CURSOR; 56 if (delegate->IsAutoclickEnabled()) 57 state |= A11Y_AUTOCLICK; 58 if (delegate->IsVirtualKeyboardEnabled()) 59 state |= A11Y_VIRTUAL_KEYBOARD; 60 return state; 61} 62 63user::LoginStatus GetCurrentLoginStatus() { 64 return Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus(); 65} 66 67} // namespace 68 69namespace tray { 70 71class DefaultAccessibilityView : public TrayItemMore { 72 public: 73 explicit DefaultAccessibilityView(SystemTrayItem* owner) 74 : TrayItemMore(owner, true) { 75 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 76 SetImage(bundle.GetImageNamed(IDR_AURA_UBER_TRAY_ACCESSIBILITY_DARK). 77 ToImageSkia()); 78 base::string16 label = bundle.GetLocalizedString( 79 IDS_ASH_STATUS_TRAY_ACCESSIBILITY); 80 SetLabel(label); 81 SetAccessibleName(label); 82 } 83 84 virtual ~DefaultAccessibilityView() { 85 } 86 87 private: 88 DISALLOW_COPY_AND_ASSIGN(DefaultAccessibilityView); 89}; 90 91class AccessibilityPopupView : public TrayNotificationView { 92 public: 93 AccessibilityPopupView(SystemTrayItem* owner) 94 : TrayNotificationView(owner, IDR_AURA_UBER_TRAY_ACCESSIBILITY_DARK) { 95 InitView(GetLabel()); 96 } 97 98 private: 99 views::Label* GetLabel() { 100 views::Label* label = new views::Label( 101 l10n_util::GetStringUTF16( 102 IDS_ASH_STATUS_TRAY_SPOKEN_FEEDBACK_ENABLED_BUBBLE)); 103 label->SetMultiLine(true); 104 label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 105 return label; 106 } 107 108 DISALLOW_COPY_AND_ASSIGN(AccessibilityPopupView); 109}; 110 111//////////////////////////////////////////////////////////////////////////////// 112// ash::internal::tray::AccessibilityDetailedView 113 114AccessibilityDetailedView::AccessibilityDetailedView( 115 SystemTrayItem* owner, user::LoginStatus login) : 116 TrayDetailsView(owner), 117 spoken_feedback_view_(NULL), 118 high_contrast_view_(NULL), 119 screen_magnifier_view_(NULL), 120 large_cursor_view_(NULL), 121 help_view_(NULL), 122 settings_view_(NULL), 123 autoclick_view_(NULL), 124 virtual_keyboard_view_(NULL), 125 spoken_feedback_enabled_(false), 126 high_contrast_enabled_(false), 127 screen_magnifier_enabled_(false), 128 large_cursor_enabled_(false), 129 autoclick_enabled_(false), 130 virtual_keyboard_enabled_(false), 131 login_(login) { 132 133 Reset(); 134 135 AppendAccessibilityList(); 136 AppendHelpEntries(); 137 CreateSpecialRow(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_TITLE, this); 138 139 Layout(); 140} 141 142void AccessibilityDetailedView::AppendAccessibilityList() { 143 CreateScrollableList(); 144 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 145 146 AccessibilityDelegate* delegate = 147 Shell::GetInstance()->accessibility_delegate(); 148 spoken_feedback_enabled_ = delegate->IsSpokenFeedbackEnabled(); 149 spoken_feedback_view_ = AddScrollListItem( 150 bundle.GetLocalizedString( 151 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SPOKEN_FEEDBACK), 152 spoken_feedback_enabled_ ? gfx::Font::BOLD : gfx::Font::NORMAL, 153 spoken_feedback_enabled_); 154 155 // Large Cursor item is shown only in Login screen. 156 if (login_ == user::LOGGED_IN_NONE) { 157 large_cursor_enabled_ = delegate->IsLargeCursorEnabled(); 158 large_cursor_view_ = AddScrollListItem( 159 bundle.GetLocalizedString( 160 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_LARGE_CURSOR), 161 large_cursor_enabled_ ? gfx::Font::BOLD : gfx::Font::NORMAL, 162 large_cursor_enabled_); 163 } 164 165 high_contrast_enabled_ = delegate->IsHighContrastEnabled(); 166 high_contrast_view_ = AddScrollListItem( 167 bundle.GetLocalizedString( 168 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_HIGH_CONTRAST_MODE), 169 high_contrast_enabled_ ? gfx::Font::BOLD : gfx::Font::NORMAL, 170 high_contrast_enabled_); 171 screen_magnifier_enabled_ = delegate->IsMagnifierEnabled(); 172 screen_magnifier_view_ = AddScrollListItem( 173 bundle.GetLocalizedString( 174 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SCREEN_MAGNIFIER), 175 screen_magnifier_enabled_ ? gfx::Font::BOLD : gfx::Font::NORMAL, 176 screen_magnifier_enabled_); 177 178 // Don't show autoclick option at login screen. 179 if (login_ != user::LOGGED_IN_NONE) { 180 autoclick_enabled_ = delegate->IsAutoclickEnabled(); 181 autoclick_view_ = AddScrollListItem( 182 bundle.GetLocalizedString( 183 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_AUTOCLICK), 184 autoclick_enabled_ ? gfx::Font::BOLD : gfx::Font::NORMAL, 185 autoclick_enabled_); 186 } 187 188 virtual_keyboard_enabled_ = delegate->IsVirtualKeyboardEnabled(); 189 virtual_keyboard_view_ = AddScrollListItem( 190 bundle.GetLocalizedString( 191 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_VIRTUAL_KEYBOARD), 192 virtual_keyboard_enabled_ ? gfx::Font::BOLD : gfx::Font::NORMAL, 193 virtual_keyboard_enabled_); 194} 195 196void AccessibilityDetailedView::AppendHelpEntries() { 197 // Currently the help page requires a browser window. 198 // TODO(yoshiki): show this even on login/lock screen. crbug.com/158286 199 if (login_ == user::LOGGED_IN_NONE || 200 login_ == user::LOGGED_IN_LOCKED) 201 return; 202 203 views::View* bottom_row = new View(); 204 views::BoxLayout* layout = new 205 views::BoxLayout(views::BoxLayout::kHorizontal, 206 kTrayMenuBottomRowPadding, 207 kTrayMenuBottomRowPadding, 208 kTrayMenuBottomRowPaddingBetweenItems); 209 layout->set_spread_blank_space(true); 210 bottom_row->SetLayoutManager(layout); 211 212 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 213 214 TrayPopupLabelButton* help = new TrayPopupLabelButton( 215 this, 216 bundle.GetLocalizedString( 217 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_LEARN_MORE)); 218 bottom_row->AddChildView(help); 219 help_view_ = help; 220 221 TrayPopupLabelButton* settings = new TrayPopupLabelButton( 222 this, 223 bundle.GetLocalizedString( 224 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SETTINGS)); 225 bottom_row->AddChildView(settings); 226 settings_view_ = settings; 227 228 AddChildView(bottom_row); 229} 230 231HoverHighlightView* AccessibilityDetailedView::AddScrollListItem( 232 const base::string16& text, 233 gfx::Font::FontStyle style, 234 bool checked) { 235 HoverHighlightView* container = new HoverHighlightView(this); 236 container->AddCheckableLabel(text, style, checked); 237 scroll_content()->AddChildView(container); 238 return container; 239} 240 241void AccessibilityDetailedView::OnViewClicked(views::View* sender) { 242 AccessibilityDelegate* delegate = 243 Shell::GetInstance()->accessibility_delegate(); 244 if (sender == footer()->content()) { 245 TransitionToDefaultView(); 246 } else if (sender == spoken_feedback_view_) { 247 Shell::GetInstance()->metrics()->RecordUserMetricsAction( 248 delegate->IsSpokenFeedbackEnabled() ? 249 ash::UMA_STATUS_AREA_DISABLE_SPOKEN_FEEDBACK : 250 ash::UMA_STATUS_AREA_ENABLE_SPOKEN_FEEDBACK); 251 delegate->ToggleSpokenFeedback(ash::A11Y_NOTIFICATION_NONE); 252 } else if (sender == high_contrast_view_) { 253 Shell::GetInstance()->metrics()->RecordUserMetricsAction( 254 delegate->IsHighContrastEnabled() ? 255 ash::UMA_STATUS_AREA_DISABLE_HIGH_CONTRAST : 256 ash::UMA_STATUS_AREA_ENABLE_HIGH_CONTRAST); 257 delegate->ToggleHighContrast(); 258 } else if (sender == screen_magnifier_view_) { 259 Shell::GetInstance()->metrics()->RecordUserMetricsAction( 260 delegate->IsMagnifierEnabled() ? 261 ash::UMA_STATUS_AREA_DISABLE_MAGNIFIER : 262 ash::UMA_STATUS_AREA_ENABLE_MAGNIFIER); 263 delegate->SetMagnifierEnabled(!delegate->IsMagnifierEnabled()); 264 } else if (large_cursor_view_ && sender == large_cursor_view_) { 265 Shell::GetInstance()->metrics()->RecordUserMetricsAction( 266 delegate->IsLargeCursorEnabled() ? 267 ash::UMA_STATUS_AREA_DISABLE_LARGE_CURSOR : 268 ash::UMA_STATUS_AREA_ENABLE_LARGE_CURSOR); 269 delegate->SetLargeCursorEnabled(!delegate->IsLargeCursorEnabled()); 270 } else if (autoclick_view_ && sender == autoclick_view_) { 271 Shell::GetInstance()->metrics()->RecordUserMetricsAction( 272 delegate->IsAutoclickEnabled() ? 273 ash::UMA_STATUS_AREA_DISABLE_AUTO_CLICK : 274 ash::UMA_STATUS_AREA_ENABLE_AUTO_CLICK); 275 delegate->SetAutoclickEnabled(!delegate->IsAutoclickEnabled()); 276 } else if (virtual_keyboard_view_ && sender == virtual_keyboard_view_) { 277 Shell::GetInstance()->metrics()->RecordUserMetricsAction( 278 delegate->IsVirtualKeyboardEnabled() ? 279 ash::UMA_STATUS_AREA_DISABLE_VIRTUAL_KEYBOARD : 280 ash::UMA_STATUS_AREA_ENABLE_VIRTUAL_KEYBOARD); 281 delegate->SetVirtualKeyboardEnabled(!delegate->IsVirtualKeyboardEnabled()); 282 } 283} 284 285void AccessibilityDetailedView::ButtonPressed(views::Button* sender, 286 const ui::Event& event) { 287 SystemTrayDelegate* tray_delegate = 288 Shell::GetInstance()->system_tray_delegate(); 289 if (sender == help_view_) 290 tray_delegate->ShowAccessibilityHelp(); 291 else if (sender == settings_view_) 292 tray_delegate->ShowAccessibilitySettings(); 293} 294 295} // namespace tray 296 297//////////////////////////////////////////////////////////////////////////////// 298// ash::internal::TrayAccessibility 299 300TrayAccessibility::TrayAccessibility(SystemTray* system_tray) 301 : TrayImageItem(system_tray, IDR_AURA_UBER_TRAY_ACCESSIBILITY), 302 default_(NULL), 303 detailed_popup_(NULL), 304 detailed_menu_(NULL), 305 request_popup_view_(false), 306 tray_icon_visible_(false), 307 login_(GetCurrentLoginStatus()), 308 previous_accessibility_state_(GetAccessibilityState()), 309 show_a11y_menu_on_lock_screen_(true) { 310 DCHECK(Shell::GetInstance()->delegate()); 311 DCHECK(system_tray); 312 Shell::GetInstance()->system_tray_notifier()->AddAccessibilityObserver(this); 313} 314 315TrayAccessibility::~TrayAccessibility() { 316 Shell::GetInstance()->system_tray_notifier()-> 317 RemoveAccessibilityObserver(this); 318} 319 320void TrayAccessibility::SetTrayIconVisible(bool visible) { 321 if (tray_view()) 322 tray_view()->SetVisible(visible); 323 tray_icon_visible_ = visible; 324} 325 326tray::AccessibilityDetailedView* TrayAccessibility::CreateDetailedMenu() { 327 return new tray::AccessibilityDetailedView(this, login_); 328} 329 330bool TrayAccessibility::GetInitialVisibility() { 331 // Shows accessibility icon if any accessibility feature is enabled. 332 // Otherwise, doen't show it. 333 return GetAccessibilityState() != A11Y_NONE; 334} 335 336views::View* TrayAccessibility::CreateDefaultView(user::LoginStatus status) { 337 CHECK(default_ == NULL); 338 339 // Shows accessibility menu if: 340 // - on login screen (not logged in); 341 // - "Enable accessibility menu" on chrome://settings is checked; 342 // - or any of accessibility features is enabled 343 // Otherwise, not shows it. 344 AccessibilityDelegate* delegate = 345 Shell::GetInstance()->accessibility_delegate(); 346 if (login_ != user::LOGGED_IN_NONE && 347 !delegate->ShouldShowAccessibilityMenu() && 348 // On login screen, keeps the initial visibility of the menu. 349 (status != user::LOGGED_IN_LOCKED || !show_a11y_menu_on_lock_screen_)) 350 return NULL; 351 352 CHECK(default_ == NULL); 353 default_ = new tray::DefaultAccessibilityView(this); 354 355 return default_; 356} 357 358views::View* TrayAccessibility::CreateDetailedView(user::LoginStatus status) { 359 CHECK(detailed_popup_ == NULL); 360 CHECK(detailed_menu_ == NULL); 361 362 if (request_popup_view_) { 363 detailed_popup_ = new tray::AccessibilityPopupView(this); 364 request_popup_view_ = false; 365 return detailed_popup_; 366 } else { 367 Shell::GetInstance()->metrics()->RecordUserMetricsAction( 368 ash::UMA_STATUS_AREA_DETAILED_ACCESSABILITY); 369 detailed_menu_ = CreateDetailedMenu(); 370 return detailed_menu_; 371 } 372} 373 374void TrayAccessibility::DestroyDefaultView() { 375 default_ = NULL; 376} 377 378void TrayAccessibility::DestroyDetailedView() { 379 detailed_popup_ = NULL; 380 detailed_menu_ = NULL; 381} 382 383void TrayAccessibility::UpdateAfterLoginStatusChange(user::LoginStatus status) { 384 // Stores the a11y feature status on just entering the lock screen. 385 if (login_ != user::LOGGED_IN_LOCKED && status == user::LOGGED_IN_LOCKED) 386 show_a11y_menu_on_lock_screen_ = (GetAccessibilityState() != A11Y_NONE); 387 388 login_ = status; 389 SetTrayIconVisible(GetInitialVisibility()); 390} 391 392void TrayAccessibility::OnAccessibilityModeChanged( 393 AccessibilityNotificationVisibility notify) { 394 SetTrayIconVisible(GetInitialVisibility()); 395 396 uint32 accessibility_state = GetAccessibilityState(); 397 if ((notify == ash::A11Y_NOTIFICATION_SHOW) && 398 !(previous_accessibility_state_ & A11Y_SPOKEN_FEEDBACK) && 399 (accessibility_state & A11Y_SPOKEN_FEEDBACK)) { 400 // Shows popup if |notify| is true and the spoken feedback is being enabled. 401 request_popup_view_ = true; 402 PopupDetailedView(kTrayPopupAutoCloseDelayForTextInSeconds, false); 403 } else { 404 if (detailed_popup_) 405 detailed_popup_->GetWidget()->Close(); 406 if (detailed_menu_) 407 detailed_menu_->GetWidget()->Close(); 408 } 409 410 previous_accessibility_state_ = accessibility_state; 411} 412 413} // namespace internal 414} // namespace ash 415