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 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 40 UserImageSyncObserver::Observer::~Observer() {} 41 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 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 68 void UserImageSyncObserver::RegisterProfilePrefs( 69 user_prefs::PrefRegistrySyncable* registry_) { 70 registry_->RegisterDictionaryPref( 71 kUserImageInfo, 72 user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF); 73 } 74 75 void UserImageSyncObserver::AddObserver(Observer* observer) { 76 observer_list_.AddObserver(observer); 77 } 78 79 void UserImageSyncObserver::RemoveObserver(Observer* observer) { 80 observer_list_.RemoveObserver(observer); 81 } 82 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 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 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 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 144 void UserImageSyncObserver::OnIsSyncingChanged() { 145 is_synced_ = prefs_->IsPrioritySyncing(); 146 if (is_synced_) { 147 prefs_->RemoveObserver(this); 148 OnInitialSync(); 149 } 150 } 151 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 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 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 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