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