1 // Copyright 2014 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/login/users/avatar/user_image_sync_observer.h"
6
7 #include "base/bind.h"
8 #include "base/prefs/pref_change_registrar.h"
9 #include "base/prefs/scoped_user_pref_update.h"
10 #include "chrome/browser/chrome_notification_types.h"
11 #include "chrome/browser/chromeos/login/screens/user_image_screen.h"
12 #include "chrome/browser/chromeos/login/users/avatar/user_image_manager.h"
13 #include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
14 #include "chrome/browser/chromeos/login/wizard_controller.h"
15 #include "chrome/browser/chromeos/profiles/profile_helper.h"
16 #include "chrome/browser/prefs/pref_service_syncable.h"
17 #include "chrome/common/pref_names.h"
18 #include "components/user_manager/user.h"
19 #include "components/user_manager/user_image/default_user_images.h"
20 #include "components/user_manager/user_manager.h"
21 #include "content/public/browser/notification_registrar.h"
22 #include "content/public/browser/notification_service.h"
23
24 namespace chromeos {
25 namespace {
26
27 // A dictionary containing info about user image.
28 const char kUserImageInfo[] = "user_image_info";
29 // Path to value with image index.
30 const char kImageIndex[] = "image_index";
31
IsIndexSupported(int index)32 bool IsIndexSupported(int index) {
33 return (index >= user_manager::kFirstDefaultImageIndex &&
34 index < user_manager::kDefaultImagesCount) ||
35 (index == user_manager::User::USER_IMAGE_PROFILE);
36 }
37
38 } // anonymous namespace
39
~Observer()40 UserImageSyncObserver::Observer::~Observer() {}
41
UserImageSyncObserver(const user_manager::User * user)42 UserImageSyncObserver::UserImageSyncObserver(const user_manager::User* user)
43 : user_(user),
44 prefs_(NULL),
45 is_synced_(false),
46 local_image_changed_(false) {
47 notification_registrar_.reset(new content::NotificationRegistrar);
48 notification_registrar_->Add(this,
49 chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED,
50 content::NotificationService::AllSources());
51 if (Profile* profile = ProfileHelper::Get()->GetProfileByUser(user)) {
52 OnProfileGained(profile);
53 } else {
54 notification_registrar_->Add(this,
55 chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED,
56 content::NotificationService::AllSources());
57 }
58 }
59
~UserImageSyncObserver()60 UserImageSyncObserver::~UserImageSyncObserver() {
61 if (!is_synced_ && prefs_)
62 prefs_->RemoveObserver(this);
63 if (pref_change_registrar_)
64 pref_change_registrar_->RemoveAll();
65 }
66
67 // static
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * registry_)68 void UserImageSyncObserver::RegisterProfilePrefs(
69 user_prefs::PrefRegistrySyncable* registry_) {
70 registry_->RegisterDictionaryPref(
71 kUserImageInfo,
72 user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
73 }
74
AddObserver(Observer * observer)75 void UserImageSyncObserver::AddObserver(Observer* observer) {
76 observer_list_.AddObserver(observer);
77 }
78
RemoveObserver(Observer * observer)79 void UserImageSyncObserver::RemoveObserver(Observer* observer) {
80 observer_list_.RemoveObserver(observer);
81 }
82
OnProfileGained(Profile * profile)83 void UserImageSyncObserver::OnProfileGained(Profile* profile) {
84 prefs_ = PrefServiceSyncable::FromProfile(profile);
85 pref_change_registrar_.reset(new PrefChangeRegistrar);
86 pref_change_registrar_->Init(prefs_);
87 pref_change_registrar_->Add(kUserImageInfo,
88 base::Bind(&UserImageSyncObserver::OnPreferenceChanged,
89 base::Unretained(this)));
90 is_synced_ = prefs_->IsPrioritySyncing();
91 if (!is_synced_) {
92 prefs_->AddObserver(this);
93 } else {
94 OnInitialSync();
95 }
96 }
97
OnInitialSync()98 void UserImageSyncObserver::OnInitialSync() {
99 int synced_index;
100 bool local_image_updated = false;
101 if (!GetSyncedImageIndex(&synced_index) || local_image_changed_) {
102 UpdateSyncedImageFromLocal();
103 } else if (IsIndexSupported(synced_index) && CanUpdateLocalImageNow()) {
104 UpdateLocalImageFromSynced();
105 local_image_updated = true;
106 }
107 FOR_EACH_OBSERVER(UserImageSyncObserver::Observer, observer_list_,
108 OnInitialSync(local_image_updated));
109 }
110
OnPreferenceChanged(const std::string & pref_name)111 void UserImageSyncObserver::OnPreferenceChanged(const std::string& pref_name) {
112 // OnPreferenceChanged can be called before OnIsSyncingChanged.
113 if (!is_synced_) {
114 is_synced_ = true;
115 prefs_->RemoveObserver(this);
116 OnInitialSync();
117 } else if (CanUpdateLocalImageNow()) {
118 UpdateLocalImageFromSynced();
119 }
120 }
121
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)122 void UserImageSyncObserver::Observe(
123 int type,
124 const content::NotificationSource& source,
125 const content::NotificationDetails& details) {
126 if (type == chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED) {
127 if (Profile* profile = ProfileHelper::Get()->GetProfileByUser(user_)) {
128 notification_registrar_->Remove(
129 this,
130 chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED,
131 content::NotificationService::AllSources());
132 OnProfileGained(profile);
133 }
134 } else if (type == chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED) {
135 if (is_synced_)
136 UpdateSyncedImageFromLocal();
137 else
138 local_image_changed_ = true;
139 } else {
140 NOTREACHED();
141 }
142 }
143
OnIsSyncingChanged()144 void UserImageSyncObserver::OnIsSyncingChanged() {
145 is_synced_ = prefs_->IsPrioritySyncing();
146 if (is_synced_) {
147 prefs_->RemoveObserver(this);
148 OnInitialSync();
149 }
150 }
151
UpdateSyncedImageFromLocal()152 void UserImageSyncObserver::UpdateSyncedImageFromLocal() {
153 int local_index = user_->image_index();
154 if (!IsIndexSupported(local_index)) {
155 local_index = user_manager::User::USER_IMAGE_INVALID;
156 }
157 int synced_index;
158 if (GetSyncedImageIndex(&synced_index) && (synced_index == local_index))
159 return;
160 DictionaryPrefUpdate update(prefs_, kUserImageInfo);
161 base::DictionaryValue* dict = update.Get();
162 dict->SetInteger(kImageIndex, local_index);
163 VLOG(1) << "Saved avatar index " << local_index << " to sync.";
164 }
165
UpdateLocalImageFromSynced()166 void UserImageSyncObserver::UpdateLocalImageFromSynced() {
167 int synced_index;
168 GetSyncedImageIndex(&synced_index);
169 int local_index = user_->image_index();
170 if ((synced_index == local_index) || !IsIndexSupported(synced_index))
171 return;
172 UserImageManager* image_manager =
173 ChromeUserManager::Get()->GetUserImageManager(user_->email());
174 if (synced_index == user_manager::User::USER_IMAGE_PROFILE) {
175 image_manager->SaveUserImageFromProfileImage();
176 } else {
177 image_manager->SaveUserDefaultImageIndex(synced_index);
178 }
179 VLOG(1) << "Loaded avatar index " << synced_index << " from sync.";
180 }
181
GetSyncedImageIndex(int * index)182 bool UserImageSyncObserver::GetSyncedImageIndex(int* index) {
183 *index = user_manager::User::USER_IMAGE_INVALID;
184 const base::DictionaryValue* dict = prefs_->GetDictionary(kUserImageInfo);
185 return dict && dict->GetInteger(kImageIndex, index);
186 }
187
CanUpdateLocalImageNow()188 bool UserImageSyncObserver::CanUpdateLocalImageNow() {
189 if (WizardController* wizard_controller =
190 WizardController::default_controller()) {
191 UserImageScreen* screen = UserImageScreen::Get(wizard_controller);
192 if (wizard_controller->current_screen() == screen) {
193 if (screen->user_selected_image())
194 return false;
195 }
196 }
197 return true;
198 }
199
200 } // namespace chromeos
201
202