Home | History | Annotate | Download | only in system
      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