// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/chromeos/accessibility/magnification_manager.h" #include <limits> #include "ash/magnifier/magnification_controller.h" #include "ash/magnifier/partial_magnification_controller.h" #include "ash/session_state_delegate.h" #include "ash/shell.h" #include "ash/shell_delegate.h" #include "ash/system/tray/system_tray_notifier.h" #include "base/memory/scoped_ptr.h" #include "base/memory/singleton.h" #include "base/prefs/pref_member.h" #include "base/prefs/pref_service.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/chromeos/accessibility/accessibility_manager.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/common/pref_names.h" #include "content/public/browser/notification_details.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_source.h" namespace chromeos { namespace { static MagnificationManager* g_magnification_manager = NULL; } class MagnificationManagerImpl : public MagnificationManager, public content::NotificationObserver, public ash::SessionStateObserver { public: MagnificationManagerImpl() : first_time_update_(true), profile_(NULL), magnifier_enabled_pref_handler_(prefs::kScreenMagnifierEnabled), magnifier_type_pref_handler_(prefs::kScreenMagnifierType), magnifier_scale_pref_handler_(prefs::kScreenMagnifierScale), type_(ash::kDefaultMagnifierType), enabled_(false) { registrar_.Add(this, chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE, content::NotificationService::AllSources()); registrar_.Add(this, chrome::NOTIFICATION_SESSION_STARTED, content::NotificationService::AllSources()); registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED, content::NotificationService::AllSources()); } virtual ~MagnificationManagerImpl() { CHECK(this == g_magnification_manager); } // MagnificationManager implimentation: virtual bool IsMagnifierEnabled() const OVERRIDE { return enabled_; } virtual ash::MagnifierType GetMagnifierType() const OVERRIDE { return type_; } virtual void SetMagnifierEnabled(bool enabled) OVERRIDE { if (!profile_) return; PrefService* prefs = profile_->GetPrefs(); prefs->SetBoolean(prefs::kScreenMagnifierEnabled, enabled); prefs->CommitPendingWrite(); } virtual void SetMagnifierType(ash::MagnifierType type) OVERRIDE { if (!profile_) return; PrefService* prefs = profile_->GetPrefs(); prefs->SetInteger(prefs::kScreenMagnifierType, type); prefs->CommitPendingWrite(); } virtual void SaveScreenMagnifierScale(double scale) OVERRIDE { if (!profile_) return; profile_->GetPrefs()->SetDouble(prefs::kScreenMagnifierScale, scale); } virtual double GetSavedScreenMagnifierScale() const OVERRIDE { if (!profile_) return std::numeric_limits<double>::min(); return profile_->GetPrefs()->GetDouble(prefs::kScreenMagnifierScale); } virtual void SetProfileForTest(Profile* profile) OVERRIDE { SetProfile(profile); } // SessionStateObserver overrides: virtual void ActiveUserChanged(const std::string& user_id) OVERRIDE { SetProfile(ProfileManager::GetActiveUserProfile()); } private: void SetProfile(Profile* profile) { pref_change_registrar_.reset(); if (profile) { // TODO(yoshiki): Move following code to PrefHandler. pref_change_registrar_.reset(new PrefChangeRegistrar); pref_change_registrar_->Init(profile->GetPrefs()); pref_change_registrar_->Add( prefs::kScreenMagnifierEnabled, base::Bind(&MagnificationManagerImpl::UpdateMagnifierFromPrefs, base::Unretained(this))); pref_change_registrar_->Add( prefs::kScreenMagnifierType, base::Bind(&MagnificationManagerImpl::UpdateMagnifierFromPrefs, base::Unretained(this))); } magnifier_enabled_pref_handler_.HandleProfileChanged(profile_, profile); magnifier_type_pref_handler_.HandleProfileChanged(profile_, profile); magnifier_scale_pref_handler_.HandleProfileChanged(profile_, profile); profile_ = profile; UpdateMagnifierFromPrefs(); } virtual void SetMagnifierEnabledInternal(bool enabled) { // This method may be invoked even when the other magnifier settings (e.g. // type or scale) are changed, so we need to call magnification controller // even if |enabled| is unchanged. Only if |enabled| is false and the // magnifier is already disabled, we are sure that we don't need to reflect // the new settings right now because the magnifier keeps disabled. if (!enabled && !enabled_) return; enabled_ = enabled; if (type_ == ash::MAGNIFIER_FULL) { ash::Shell::GetInstance()->magnification_controller()->SetEnabled( enabled_); } else { ash::Shell::GetInstance()->partial_magnification_controller()->SetEnabled( enabled_); } } virtual void SetMagnifierTypeInternal(ash::MagnifierType type) { if (type_ == type) return; type_ = ash::MAGNIFIER_FULL; // (leave out for full magnifier) } void UpdateMagnifierFromPrefs() { if (!profile_) return; const bool enabled = profile_->GetPrefs()->GetBoolean(prefs::kScreenMagnifierEnabled); const int type_integer = profile_->GetPrefs()->GetInteger(prefs::kScreenMagnifierType); ash::MagnifierType type = ash::kDefaultMagnifierType; if (type_integer > 0 && type_integer <= ash::kMaxMagnifierType) { type = static_cast<ash::MagnifierType>(type_integer); } else if (type_integer == 0) { // Type 0 is used to disable the screen magnifier through policy. As the // magnifier type is irrelevant in this case, it is OK to just fall back // to the default. } else { NOTREACHED(); } if (!enabled) { SetMagnifierEnabledInternal(enabled); SetMagnifierTypeInternal(type); } else { SetMagnifierTypeInternal(type); SetMagnifierEnabledInternal(enabled); } AccessibilityStatusEventDetails details( enabled_, type_, ash::A11Y_NOTIFICATION_NONE); content::NotificationService::current()->Notify( chrome::NOTIFICATION_CROS_ACCESSIBILITY_TOGGLE_SCREEN_MAGNIFIER, content::NotificationService::AllSources(), content::Details<AccessibilityStatusEventDetails>(&details)); } // content::NotificationObserver implementation: virtual void Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) OVERRIDE { switch (type) { case chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE: { // Update |profile_| when entering the login screen. Profile* profile = ProfileManager::GetDefaultProfile(); if (ProfileHelper::IsSigninProfile(profile)) SetProfile(profile); break; } case chrome::NOTIFICATION_SESSION_STARTED: // Update |profile_| when entering a session. SetProfile(ProfileManager::GetDefaultProfile()); // Add a session state observer to be able to monitor session changes. if (!session_state_observer_.get() && ash::Shell::HasInstance()) session_state_observer_.reset( new ash::ScopedSessionStateObserver(this)); break; case chrome::NOTIFICATION_PROFILE_DESTROYED: { // Update |profile_| when exiting a session or shutting down. Profile* profile = content::Source<Profile>(source).ptr(); if (profile_ == profile) SetProfile(NULL); break; } } } bool first_time_update_; Profile* profile_; AccessibilityManager::PrefHandler magnifier_enabled_pref_handler_; AccessibilityManager::PrefHandler magnifier_type_pref_handler_; AccessibilityManager::PrefHandler magnifier_scale_pref_handler_; ash::MagnifierType type_; bool enabled_; content::NotificationRegistrar registrar_; scoped_ptr<PrefChangeRegistrar> pref_change_registrar_; scoped_ptr<ash::ScopedSessionStateObserver> session_state_observer_; DISALLOW_COPY_AND_ASSIGN(MagnificationManagerImpl); }; // static void MagnificationManager::Initialize() { CHECK(g_magnification_manager == NULL); g_magnification_manager = new MagnificationManagerImpl(); } // static void MagnificationManager::Shutdown() { CHECK(g_magnification_manager); delete g_magnification_manager; g_magnification_manager = NULL; } // static MagnificationManager* MagnificationManager::Get() { return g_magnification_manager; } } // namespace chromeos