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 "chrome/browser/chromeos/accessibility/magnification_manager.h" 6 7 #include <limits> 8 9 #include "ash/magnifier/magnification_controller.h" 10 #include "ash/magnifier/partial_magnification_controller.h" 11 #include "ash/session/session_state_delegate.h" 12 #include "ash/shell.h" 13 #include "ash/shell_delegate.h" 14 #include "ash/system/tray/system_tray_notifier.h" 15 #include "base/memory/scoped_ptr.h" 16 #include "base/memory/singleton.h" 17 #include "base/prefs/pref_member.h" 18 #include "base/prefs/pref_service.h" 19 #include "chrome/browser/chrome_notification_types.h" 20 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h" 21 #include "chrome/browser/chromeos/login/users/user_manager.h" 22 #include "chrome/browser/chromeos/profiles/profile_helper.h" 23 #include "chrome/browser/profiles/profile.h" 24 #include "chrome/browser/profiles/profile_manager.h" 25 #include "chrome/common/pref_names.h" 26 #include "content/public/browser/notification_details.h" 27 #include "content/public/browser/notification_observer.h" 28 #include "content/public/browser/notification_registrar.h" 29 #include "content/public/browser/notification_service.h" 30 #include "content/public/browser/notification_source.h" 31 32 namespace chromeos { 33 34 namespace { 35 static MagnificationManager* g_magnification_manager = NULL; 36 } 37 38 class MagnificationManagerImpl : public MagnificationManager, 39 public content::NotificationObserver, 40 public ash::SessionStateObserver { 41 public: 42 MagnificationManagerImpl() 43 : first_time_update_(true), 44 profile_(NULL), 45 magnifier_enabled_pref_handler_( 46 prefs::kAccessibilityScreenMagnifierEnabled), 47 magnifier_type_pref_handler_(prefs::kAccessibilityScreenMagnifierType), 48 magnifier_scale_pref_handler_( 49 prefs::kAccessibilityScreenMagnifierScale), 50 type_(ash::kDefaultMagnifierType), 51 enabled_(false) { 52 registrar_.Add(this, 53 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE, 54 content::NotificationService::AllSources()); 55 registrar_.Add(this, 56 chrome::NOTIFICATION_SESSION_STARTED, 57 content::NotificationService::AllSources()); 58 registrar_.Add(this, 59 chrome::NOTIFICATION_PROFILE_DESTROYED, 60 content::NotificationService::AllSources()); 61 } 62 63 virtual ~MagnificationManagerImpl() { 64 CHECK(this == g_magnification_manager); 65 } 66 67 // MagnificationManager implimentation: 68 virtual bool IsMagnifierEnabled() const OVERRIDE { 69 return enabled_; 70 } 71 72 virtual ash::MagnifierType GetMagnifierType() const OVERRIDE { 73 return type_; 74 } 75 76 virtual void SetMagnifierEnabled(bool enabled) OVERRIDE { 77 if (!profile_) 78 return; 79 80 PrefService* prefs = profile_->GetPrefs(); 81 prefs->SetBoolean(prefs::kAccessibilityScreenMagnifierEnabled, enabled); 82 prefs->CommitPendingWrite(); 83 } 84 85 virtual void SetMagnifierType(ash::MagnifierType type) OVERRIDE { 86 if (!profile_) 87 return; 88 89 PrefService* prefs = profile_->GetPrefs(); 90 prefs->SetInteger(prefs::kAccessibilityScreenMagnifierType, type); 91 prefs->CommitPendingWrite(); 92 } 93 94 virtual void SaveScreenMagnifierScale(double scale) OVERRIDE { 95 if (!profile_) 96 return; 97 98 profile_->GetPrefs()->SetDouble(prefs::kAccessibilityScreenMagnifierScale, 99 scale); 100 } 101 102 virtual double GetSavedScreenMagnifierScale() const OVERRIDE { 103 if (!profile_) 104 return std::numeric_limits<double>::min(); 105 106 return profile_->GetPrefs()->GetDouble( 107 prefs::kAccessibilityScreenMagnifierScale); 108 } 109 110 virtual void SetProfileForTest(Profile* profile) OVERRIDE { 111 SetProfile(profile); 112 } 113 114 // SessionStateObserver overrides: 115 virtual void ActiveUserChanged(const std::string& user_id) OVERRIDE { 116 SetProfile(ProfileManager::GetActiveUserProfile()); 117 } 118 119 private: 120 void SetProfile(Profile* profile) { 121 pref_change_registrar_.reset(); 122 123 if (profile) { 124 // TODO(yoshiki): Move following code to PrefHandler. 125 pref_change_registrar_.reset(new PrefChangeRegistrar); 126 pref_change_registrar_->Init(profile->GetPrefs()); 127 pref_change_registrar_->Add( 128 prefs::kAccessibilityScreenMagnifierEnabled, 129 base::Bind(&MagnificationManagerImpl::UpdateMagnifierFromPrefs, 130 base::Unretained(this))); 131 pref_change_registrar_->Add( 132 prefs::kAccessibilityScreenMagnifierType, 133 base::Bind(&MagnificationManagerImpl::UpdateMagnifierFromPrefs, 134 base::Unretained(this))); 135 } 136 137 magnifier_enabled_pref_handler_.HandleProfileChanged(profile_, profile); 138 magnifier_type_pref_handler_.HandleProfileChanged(profile_, profile); 139 magnifier_scale_pref_handler_.HandleProfileChanged(profile_, profile); 140 141 profile_ = profile; 142 UpdateMagnifierFromPrefs(); 143 } 144 145 virtual void SetMagnifierEnabledInternal(bool enabled) { 146 // This method may be invoked even when the other magnifier settings (e.g. 147 // type or scale) are changed, so we need to call magnification controller 148 // even if |enabled| is unchanged. Only if |enabled| is false and the 149 // magnifier is already disabled, we are sure that we don't need to reflect 150 // the new settings right now because the magnifier keeps disabled. 151 if (!enabled && !enabled_) 152 return; 153 154 enabled_ = enabled; 155 156 if (type_ == ash::MAGNIFIER_FULL) { 157 ash::Shell::GetInstance()->magnification_controller()->SetEnabled( 158 enabled_); 159 } else { 160 ash::Shell::GetInstance()->partial_magnification_controller()->SetEnabled( 161 enabled_); 162 } 163 } 164 165 virtual void SetMagnifierTypeInternal(ash::MagnifierType type) { 166 if (type_ == type) 167 return; 168 169 type_ = ash::MAGNIFIER_FULL; // (leave out for full magnifier) 170 } 171 172 void UpdateMagnifierFromPrefs() { 173 if (!profile_) 174 return; 175 176 const bool enabled = profile_->GetPrefs()->GetBoolean( 177 prefs::kAccessibilityScreenMagnifierEnabled); 178 const int type_integer = profile_->GetPrefs()->GetInteger( 179 prefs::kAccessibilityScreenMagnifierType); 180 181 ash::MagnifierType type = ash::kDefaultMagnifierType; 182 if (type_integer > 0 && type_integer <= ash::kMaxMagnifierType) { 183 type = static_cast<ash::MagnifierType>(type_integer); 184 } else if (type_integer == 0) { 185 // Type 0 is used to disable the screen magnifier through policy. As the 186 // magnifier type is irrelevant in this case, it is OK to just fall back 187 // to the default. 188 } else { 189 NOTREACHED(); 190 } 191 192 if (!enabled) { 193 SetMagnifierEnabledInternal(enabled); 194 SetMagnifierTypeInternal(type); 195 } else { 196 SetMagnifierTypeInternal(type); 197 SetMagnifierEnabledInternal(enabled); 198 } 199 200 AccessibilityStatusEventDetails details( 201 ACCESSIBILITY_TOGGLE_SCREEN_MAGNIFIER, 202 enabled_, 203 type_, 204 ash::A11Y_NOTIFICATION_NONE); 205 206 #if defined(OS_CHROMEOS) 207 if (AccessibilityManager::Get()) { 208 AccessibilityManager::Get()->NotifyAccessibilityStatusChanged(details); 209 if (ash::Shell::GetInstance()) { 210 ash::Shell::GetInstance()->SetCursorCompositingEnabled( 211 AccessibilityManager::Get()->ShouldEnableCursorCompositing()); 212 } 213 } 214 #endif 215 } 216 217 // content::NotificationObserver implementation: 218 virtual void Observe(int type, 219 const content::NotificationSource& source, 220 const content::NotificationDetails& details) OVERRIDE { 221 switch (type) { 222 case chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE: { 223 // Update |profile_| when entering the login screen. 224 Profile* profile = ProfileManager::GetActiveUserProfile(); 225 if (ProfileHelper::IsSigninProfile(profile)) 226 SetProfile(profile); 227 break; 228 } 229 case chrome::NOTIFICATION_SESSION_STARTED: 230 // Update |profile_| when entering a session. 231 SetProfile(ProfileManager::GetActiveUserProfile()); 232 233 // Add a session state observer to be able to monitor session changes. 234 if (!session_state_observer_.get() && ash::Shell::HasInstance()) 235 session_state_observer_.reset( 236 new ash::ScopedSessionStateObserver(this)); 237 break; 238 case chrome::NOTIFICATION_PROFILE_DESTROYED: { 239 // Update |profile_| when exiting a session or shutting down. 240 Profile* profile = content::Source<Profile>(source).ptr(); 241 if (profile_ == profile) 242 SetProfile(NULL); 243 break; 244 } 245 } 246 } 247 248 bool first_time_update_; 249 Profile* profile_; 250 251 AccessibilityManager::PrefHandler magnifier_enabled_pref_handler_; 252 AccessibilityManager::PrefHandler magnifier_type_pref_handler_; 253 AccessibilityManager::PrefHandler magnifier_scale_pref_handler_; 254 255 ash::MagnifierType type_; 256 bool enabled_; 257 258 content::NotificationRegistrar registrar_; 259 scoped_ptr<PrefChangeRegistrar> pref_change_registrar_; 260 scoped_ptr<ash::ScopedSessionStateObserver> session_state_observer_; 261 262 DISALLOW_COPY_AND_ASSIGN(MagnificationManagerImpl); 263 }; 264 265 // static 266 void MagnificationManager::Initialize() { 267 CHECK(g_magnification_manager == NULL); 268 g_magnification_manager = new MagnificationManagerImpl(); 269 } 270 271 // static 272 void MagnificationManager::Shutdown() { 273 CHECK(g_magnification_manager); 274 delete g_magnification_manager; 275 g_magnification_manager = NULL; 276 } 277 278 // static 279 MagnificationManager* MagnificationManager::Get() { 280 return g_magnification_manager; 281 } 282 283 } // namespace chromeos 284