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 28 namespace ash { 29 namespace internal { 30 31 namespace { 32 const int kPaddingAroundBottomRow = 5; 33 34 enum 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 42 uint32 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 56 user::LoginStatus GetCurrentLoginStatus() { 57 return Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus(); 58 } 59 60 } // namespace 61 62 namespace tray { 63 64 class 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 84 class 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 107 AccessibilityDetailedView::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 131 void 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 167 void 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 202 HoverHighlightView* 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 212 void 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 228 void 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 243 TrayAccessibility::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 258 TrayAccessibility::~TrayAccessibility() { 259 Shell::GetInstance()->system_tray_notifier()-> 260 RemoveAccessibilityObserver(this); 261 } 262 263 void TrayAccessibility::SetTrayIconVisible(bool visible) { 264 if (tray_view()) 265 tray_view()->SetVisible(visible); 266 tray_icon_visible_ = visible; 267 } 268 269 tray::AccessibilityDetailedView* TrayAccessibility::CreateDetailedMenu() { 270 return new tray::AccessibilityDetailedView(this, login_); 271 } 272 273 bool 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 279 views::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 301 views::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 315 void TrayAccessibility::DestroyDefaultView() { 316 default_ = NULL; 317 } 318 319 void TrayAccessibility::DestroyDetailedView() { 320 detailed_popup_ = NULL; 321 detailed_menu_ = NULL; 322 } 323 324 void 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 333 void 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