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