diff options
author | dzhioev@chromium.org <dzhioev@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-13 05:41:09 +0000 |
---|---|---|
committer | dzhioev@chromium.org <dzhioev@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-13 05:41:09 +0000 |
commit | d4839996076e54913112005350125d159f04898e (patch) | |
tree | 072b98029a76020a21bd6b6fb7a2002c433c7004 | |
parent | 913d2062c2ee21100a2b03bd72f36c50c3b09cbe (diff) | |
download | chromium_src-d4839996076e54913112005350125d159f04898e.zip chromium_src-d4839996076e54913112005350125d159f04898e.tar.gz chromium_src-d4839996076e54913112005350125d159f04898e.tar.bz2 |
Implemented sync of user account images in ChromeOS.
Currently not works for custom images, only for default and G+ picture.
Sync can be disabled by --disable-user-image-sync or by flag.
BUG=130302
TBR=darin,mnissler
Review URL: https://chromiumcodereview.appspot.com/22295011
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@217210 0039d316-1c4b-4281-b951-d872f2087c98
24 files changed, 573 insertions, 36 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index b705b3f..9c7a809 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -6443,6 +6443,12 @@ Keep your key file in a safe place. You will need it to create new versions of y <message name="IDS_FLAGS_ENABLE_DEVICE_MOTION_DESCRIPTION" desc="Description for the flag to enable device motion."> Enables device motion DOM events in JavaScript. </message> + <message name="IDS_FLAGS_DISABLE_USER_IMAGE_SYNC_NAME" desc="Name of the flag to disable sync of user account image."> + Disable avatar sync. + </message> + <message name="IDS_FLAGS_DISABLE_USER_IMAGE_SYNC_DESCRIPTION" desc="Description for the flag to disable sync of user account image."> + Disables sync of user account image between different ChromeOS devices. + </message> <!-- Crashes --> <message name="IDS_CRASHES_TITLE" desc="Title for the chrome://crashes page."> diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index f4ff5a2..0e93c9e 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc @@ -1610,6 +1610,15 @@ const Experiment kExperiments[] = { SINGLE_VALUE_TYPE(switches::kDisableAppList) }, #endif +#if defined(OS_CHROMEOS) + { + "disable-user-image-sync", + IDS_FLAGS_DISABLE_USER_IMAGE_SYNC_NAME, + IDS_FLAGS_DISABLE_USER_IMAGE_SYNC_DESCRIPTION, + kOsCrOS, + SINGLE_VALUE_TYPE(chromeos::switches::kDisableUserImageSync) + }, +#endif }; const Experiment* experiments = kExperiments; diff --git a/chrome/browser/automation/testing_automation_provider_chromeos.cc b/chrome/browser/automation/testing_automation_provider_chromeos.cc index 598da8d..f1b91d5 100644 --- a/chrome/browser/automation/testing_automation_provider_chromeos.cc +++ b/chrome/browser/automation/testing_automation_provider_chromeos.cc @@ -338,12 +338,12 @@ void TestingAutomationProvider::PickUserImage(DictionaryValue* args, WizardControllerObserver* observer = new WizardControllerObserver(wizard_controller, this, reply_message); if (image_type == "profile") { - image_screen->OnImageSelected("", image_type); + image_screen->OnImageSelected("", image_type, true); image_screen->OnImageAccepted(); } else if (image_type.empty() && image_number >= 0 && image_number < chromeos::kDefaultImagesCount) { image_screen->OnImageSelected( - chromeos::GetDefaultImageUrl(image_number), image_type); + chromeos::GetDefaultImageUrl(image_number), image_type, true); image_screen->OnImageAccepted(); } else { AutomationJSONReply(this, reply_message).SendError( diff --git a/chrome/browser/chromeos/login/screens/user_image_screen.cc b/chrome/browser/chromeos/login/screens/user_image_screen.cc index 9542be3..a245be3 100644 --- a/chrome/browser/chromeos/login/screens/user_image_screen.cc +++ b/chrome/browser/chromeos/login/screens/user_image_screen.cc @@ -6,6 +6,7 @@ #include "base/compiler_specific.h" #include "base/metrics/histogram.h" +#include "base/timer/timer.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/chromeos/accessibility/accessibility_manager.h" #include "chrome/browser/chromeos/camera_detector.h" @@ -36,6 +37,10 @@ namespace { // Time histogram suffix for profile image download. const char kProfileDownloadReason[] = "OOBE"; +// Maximum ammount of time to wait for the user image to sync. +// The screen is shown iff sync failed or time limit exceeded. +const int kSyncTimeoutSeconds = 10; + } // namespace UserImageScreen::UserImageScreen(ScreenObserver* screen_observer, @@ -47,9 +52,13 @@ UserImageScreen::UserImageScreen(ScreenObserver* screen_observer, selected_image_(User::kInvalidImageIndex), profile_picture_enabled_(false), profile_picture_data_url_(content::kAboutBlankURL), - profile_picture_absent_(false) { + profile_picture_absent_(false), + is_screen_ready_(false), + user_has_selected_image_(false) { actor_->SetDelegate(this); SetProfilePictureEnabled(true); + registrar_.Add(this, chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED, + content::NotificationService::AllSources()); } UserImageScreen::~UserImageScreen() { @@ -59,17 +68,10 @@ UserImageScreen::~UserImageScreen() { image_decoder_->set_delegate(NULL); } -void UserImageScreen::CheckCameraPresence() { - CameraDetector::StartPresenceCheck( - base::Bind(&UserImageScreen::OnCameraPresenceCheckDone, - weak_factory_.GetWeakPtr())); -} - -void UserImageScreen::OnCameraPresenceCheckDone() { - if (actor_) { - actor_->SetCameraPresent( - CameraDetector::camera_presence() == CameraDetector::kCameraPresent); - } +void UserImageScreen::OnScreenReady() { + is_screen_ready_ = true; + if (actor_ && !IsWaitingForSync()) + actor_->HideCurtain(); } void UserImageScreen::OnPhotoTaken(const std::string& raw_data) { @@ -84,6 +86,20 @@ void UserImageScreen::OnPhotoTaken(const std::string& raw_data) { image_decoder_->Start(task_runner); } +void UserImageScreen::CheckCameraPresence() { + CameraDetector::StartPresenceCheck( + base::Bind(&UserImageScreen::OnCameraPresenceCheckDone, + weak_factory_.GetWeakPtr())); +} + +void UserImageScreen::OnCameraPresenceCheckDone() { + if (actor_) { + actor_->SetCameraPresent( + CameraDetector::camera_presence() == CameraDetector::kCameraPresent); + } +} + + void UserImageScreen::OnImageDecoded(const ImageDecoder* decoder, const SkBitmap& decoded_image) { DCHECK_EQ(image_decoder_.get(), decoder); @@ -96,8 +112,38 @@ void UserImageScreen::OnDecodeImageFailed(const ImageDecoder* decoder) { NOTREACHED() << "Failed to decode PNG image from WebUI"; } +void UserImageScreen::OnInitialSync(bool local_image_updated) { + DCHECK(sync_timer_.get()); + sync_timer_->Stop(); + sync_timer_.reset(); + UserManager::Get()->GetUserImageManager()->GetSyncObserver()-> + RemoveObserver(this); + if (!local_image_updated) { + if (is_screen_ready_ && actor_) + actor_->HideCurtain(); + return; + } + get_screen_observer()->OnExit(ScreenObserver::USER_IMAGE_SELECTED); +} + +void UserImageScreen::OnSyncTimeout() { + sync_timer_.reset(); + UserManager::Get()->GetUserImageManager()->GetSyncObserver()-> + RemoveObserver(this); + if (is_screen_ready_ && actor_) + actor_->HideCurtain(); +} + +bool UserImageScreen::IsWaitingForSync() const { + return sync_timer_.get() && sync_timer_->IsRunning(); +} + void UserImageScreen::OnImageSelected(const std::string& image_type, - const std::string& image_url) { + const std::string& image_url, + bool is_user_selection) { + if (is_user_selection) { + user_has_selected_image_ = true; + } if (image_url.empty()) return; int user_image_index = User::kInvalidImageIndex; @@ -139,9 +185,11 @@ void UserImageScreen::OnImageAccepted() { uma_index = GetDefaultImageHistogramValue(selected_image_); break; } - UMA_HISTOGRAM_ENUMERATION("UserImage.FirstTimeChoice", - uma_index, - kHistogramImagesCount); + if (user_has_selected_image_) { + UMA_HISTOGRAM_ENUMERATION("UserImage.FirstTimeChoice", + uma_index, + kHistogramImagesCount); + } get_screen_observer()->OnExit(ScreenObserver::USER_IMAGE_SELECTED); } @@ -186,7 +234,23 @@ const User* UserImageScreen::GetUser() { void UserImageScreen::Show() { if (!actor_) return; - + if (GetUser()->CanSyncImage()) { + if (UserImageSyncObserver* sync_observer = + UserManager::Get()->GetUserImageManager()->GetSyncObserver()) { + // We have synced image already. + if (sync_observer->is_synced()) { + get_screen_observer()->OnExit(ScreenObserver::USER_IMAGE_SELECTED); + return; + } + sync_observer->AddObserver(this); + sync_timer_.reset(new base::Timer( + FROM_HERE, + base::TimeDelta::FromSeconds(kSyncTimeoutSeconds), + base::Bind(&UserImageScreen::OnSyncTimeout, base::Unretained(this)), + false)); + sync_timer_->Reset(); + } + } actor_->Show(); actor_->SetProfilePictureEnabled(profile_picture_enabled_); @@ -237,6 +301,11 @@ void UserImageScreen::Observe(int type, actor_->OnProfileImageAbsent(); break; } + case chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED: { + if (actor_) + actor_->SelectImage(GetUser()->image_index()); + break; + } default: NOTREACHED(); } diff --git a/chrome/browser/chromeos/login/screens/user_image_screen.h b/chrome/browser/chromeos/login/screens/user_image_screen.h index 28c61df..21780cc 100644 --- a/chrome/browser/chromeos/login/screens/user_image_screen.h +++ b/chrome/browser/chromeos/login/screens/user_image_screen.h @@ -10,16 +10,22 @@ #include "chrome/browser/chromeos/login/screens/user_image_screen_actor.h" #include "chrome/browser/chromeos/login/screens/wizard_screen.h" #include "chrome/browser/chromeos/login/user.h" +#include "chrome/browser/chromeos/login/user_image_sync_observer.h" #include "chrome/browser/image_decoder.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" +namespace base { +class Timer; +}; + namespace chromeos { class UserImageScreen: public WizardScreen, public UserImageScreenActor::Delegate, public ImageDecoder::Delegate, - public content::NotificationObserver { + public content::NotificationObserver, + public UserImageSyncObserver::Observer { public: UserImageScreen(ScreenObserver* screen_observer, UserImageScreenActor* actor); @@ -37,10 +43,12 @@ class UserImageScreen: public WizardScreen, virtual std::string GetName() const OVERRIDE; // UserImageScreenActor::Delegate implementation: - virtual void CheckCameraPresence() OVERRIDE; + virtual void OnScreenReady() OVERRIDE; virtual void OnPhotoTaken(const std::string& raw_data) OVERRIDE; + virtual void CheckCameraPresence() OVERRIDE; virtual void OnImageSelected(const std::string& image_url, - const std::string& image_type) OVERRIDE; + const std::string& image_type, + bool is_user_selection) OVERRIDE; virtual void OnImageAccepted() OVERRIDE; virtual void OnActorDestroyed(UserImageScreenActor* actor) OVERRIDE; @@ -53,12 +61,22 @@ class UserImageScreen: public WizardScreen, const content::NotificationSource& source, const content::NotificationDetails& details) OVERRIDE; - // Overriden from ImageDecoder::Delegate: + // ImageDecoder::Delegate implementation: virtual void OnImageDecoded(const ImageDecoder* decoder, const SkBitmap& decoded_image) OVERRIDE; virtual void OnDecodeImageFailed(const ImageDecoder* decoder) OVERRIDE; + // UserImageSyncObserver::Observer implementation: + virtual void OnInitialSync(bool local_image_updated) OVERRIDE; + + bool user_selected_image() const { return user_has_selected_image_; } + private: + // Called when whaiting for sync timed out. + void OnSyncTimeout(); + + bool IsWaitingForSync() const; + const User* GetUser(); // Called when the camera presence check has been completed. @@ -95,6 +113,15 @@ class UserImageScreen: public WizardScreen, std::string user_id_; + // Timer used for waiting for user image sync. + scoped_ptr<base::Timer> sync_timer_; + + // If screen ready to be shown. + bool is_screen_ready_; + + // True if user has explicitly selected some image. + bool user_has_selected_image_; + DISALLOW_COPY_AND_ASSIGN(UserImageScreen); }; diff --git a/chrome/browser/chromeos/login/screens/user_image_screen_actor.h b/chrome/browser/chromeos/login/screens/user_image_screen_actor.h index 8f99e98..5e5d073 100644 --- a/chrome/browser/chromeos/login/screens/user_image_screen_actor.h +++ b/chrome/browser/chromeos/login/screens/user_image_screen_actor.h @@ -23,13 +23,17 @@ class UserImageScreenActor { public: virtual ~Delegate() {} + // Called when UI ready to be shown. + virtual void OnScreenReady() = 0; // Called when user accepts photo as login user image. virtual void OnPhotoTaken(const std::string& raw_data) = 0; // Called to check camera presence. virtual void CheckCameraPresence() = 0; - // Called when user selects some image + // Called when some image was selected. |is_user_selection| indicates if + // it was user selection or image was selected programmatically. virtual void OnImageSelected(const std::string& image_url, - const std::string& image_type) = 0; + const std::string& image_type, + bool is_user_selection) = 0; // Called when user accepts currently selected image virtual void OnImageAccepted() = 0; @@ -70,6 +74,9 @@ class UserImageScreenActor { // Sends result of camera check virtual void SetCameraPresent(bool enabled) = 0; + + // Hides curtain with spinner. + virtual void HideCurtain() = 0; }; } // namespace chromeos diff --git a/chrome/browser/chromeos/login/user.cc b/chrome/browser/chromeos/login/user.cc index 5e47a5a..ffe6c47 100644 --- a/chrome/browser/chromeos/login/user.cc +++ b/chrome/browser/chromeos/login/user.cc @@ -34,6 +34,7 @@ class RegularUser : public User { // Overridden from User: virtual UserType GetType() const OVERRIDE; + virtual bool CanSyncImage() const OVERRIDE; virtual bool can_lock() const OVERRIDE; private: @@ -151,6 +152,10 @@ bool User::HasDefaultImage() const { return image_index_ >= 0 && image_index_ < kDefaultImagesCount; } +bool User::CanSyncImage() const { + return false; +} + std::string User::display_email() const { return display_email_; } @@ -238,6 +243,10 @@ User::UserType RegularUser::GetType() const { return USER_TYPE_REGULAR; } +bool RegularUser::CanSyncImage() const { + return true; +} + bool RegularUser::can_lock() const { return true; } diff --git a/chrome/browser/chromeos/login/user.h b/chrome/browser/chromeos/login/user.h index 35911bb..4465aab 100644 --- a/chrome/browser/chromeos/login/user.h +++ b/chrome/browser/chromeos/login/user.h @@ -112,6 +112,9 @@ class User { // Whether the user has a default image. bool HasDefaultImage() const; + // True if user image can be synced. + virtual bool CanSyncImage() const; + int image_index() const { return image_index_; } bool has_raw_image() const { return user_image_.has_raw_image(); } // Returns raw representation of static user image. diff --git a/chrome/browser/chromeos/login/user_image_manager.h b/chrome/browser/chromeos/login/user_image_manager.h index eeec8a0..383fafe 100644 --- a/chrome/browser/chromeos/login/user_image_manager.h +++ b/chrome/browser/chromeos/login/user_image_manager.h @@ -22,6 +22,7 @@ class ImageSkia; namespace chromeos { class UserImage; +class UserImageSyncObserver; // Base class that provides a mechanism for updating user images. class UserImageManager { @@ -74,6 +75,13 @@ class UserImageManager { // Returns the result of the last successful profile image download, if any. // Otherwise, returns an empty bitmap. virtual const gfx::ImageSkia& DownloadedProfileImage() const = 0; + + // Returns sync observer attached to current user. Returns NULL if current + // user can't sync images or user is not logged in. + virtual UserImageSyncObserver* GetSyncObserver() const = 0; + + // Unregisters preference observers before browser process shutdown. + virtual void Shutdown() = 0; }; } // namespace chromeos diff --git a/chrome/browser/chromeos/login/user_image_manager_impl.cc b/chrome/browser/chromeos/login/user_image_manager_impl.cc index f66f0f0..ec9f7d6 100644 --- a/chrome/browser/chromeos/login/user_image_manager_impl.cc +++ b/chrome/browser/chromeos/login/user_image_manager_impl.cc @@ -5,6 +5,7 @@ #include "chrome/browser/chromeos/login/user_image_manager_impl.h" #include "base/bind.h" +#include "base/command_line.h" #include "base/debug/trace_event.h" #include "base/file_util.h" #include "base/files/file_path.h" @@ -22,11 +23,14 @@ #include "chrome/browser/chromeos/login/default_user_images.h" #include "chrome/browser/chromeos/login/helper.h" #include "chrome/browser/chromeos/login/user_image.h" +#include "chrome/browser/chromeos/login/user_image_sync_observer.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/prefs/scoped_user_pref_update.h" #include "chrome/browser/profiles/profile_downloader.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_switches.h" +#include "chromeos/chromeos_switches.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_service.h" #include "content/public/common/url_constants.h" @@ -266,12 +270,11 @@ void UserImageManagerImpl::LoadUserImages(const UserList& users) { void UserImageManagerImpl::UserLoggedIn(const std::string& email, bool user_is_new, bool user_is_local) { + User* user = UserManager::Get()->GetLoggedInUser(); if (user_is_new) { if (!user_is_local) SetInitialUserImage(email); } else { - User* user = UserManager::Get()->GetLoggedInUser(); - if (!user_is_local) { // If current user image is profile image, it needs to be refreshed. bool download_profile_image = @@ -334,6 +337,14 @@ void UserImageManagerImpl::UserLoggedIn(const std::string& email, FROM_HERE, base::TimeDelta::FromSeconds(kProfileRefreshIntervalSec), this, &UserImageManagerImpl::DownloadProfileDataScheduled); } + CommandLine* command_line = CommandLine::ForCurrentProcess(); + if (user->CanSyncImage() && + !command_line->HasSwitch(chromeos::switches::kDisableUserImageSync)) { + if (user_image_sync_observer_.get() && + !command_line->HasSwitch(::switches::kMultiProfiles)) + NOTREACHED() << "User logged in second time."; + user_image_sync_observer_.reset(new UserImageSyncObserver(user)); + } } void UserImageManagerImpl::SaveUserDefaultImageIndex( @@ -401,6 +412,14 @@ void UserImageManagerImpl::DownloadProfileImage(const std::string& reason) { DownloadProfileData(reason, true); } +UserImageSyncObserver* UserImageManagerImpl::GetSyncObserver() const { + return user_image_sync_observer_.get(); +} + +void UserImageManagerImpl::Shutdown() { + user_image_sync_observer_.reset(); +} + const gfx::ImageSkia& UserImageManagerImpl::DownloadedProfileImage() const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); return downloaded_profile_image_; diff --git a/chrome/browser/chromeos/login/user_image_manager_impl.h b/chrome/browser/chromeos/login/user_image_manager_impl.h index ef6da72..3b09c47 100644 --- a/chrome/browser/chromeos/login/user_image_manager_impl.h +++ b/chrome/browser/chromeos/login/user_image_manager_impl.h @@ -26,6 +26,10 @@ class FilePath; } namespace chromeos { +class UserImageSyncObserver; +} + +namespace chromeos { class UserImageManagerImpl : public UserImageManager, public ProfileDownloaderDelegate { @@ -49,6 +53,8 @@ class UserImageManagerImpl : public UserImageManager, virtual void DeleteUserImage(const std::string& username) OVERRIDE; virtual void DownloadProfileImage(const std::string& reason) OVERRIDE; virtual const gfx::ImageSkia& DownloadedProfileImage() const OVERRIDE; + virtual UserImageSyncObserver* GetSyncObserver() const OVERRIDE; + virtual void Shutdown() OVERRIDE; private: friend class UserImageManagerTest; @@ -175,6 +181,9 @@ class UserImageManagerImpl : public UserImageManager, // If |true|, current user image should be migrated right after it is loaded. bool migrate_current_user_on_load_; + // Sync observer attached to current user. + scoped_ptr<UserImageSyncObserver> user_image_sync_observer_; + DISALLOW_COPY_AND_ASSIGN(UserImageManagerImpl); }; diff --git a/chrome/browser/chromeos/login/user_image_sync_observer.cc b/chrome/browser/chromeos/login/user_image_sync_observer.cc new file mode 100644 index 0000000..a20f6e9 --- /dev/null +++ b/chrome/browser/chromeos/login/user_image_sync_observer.cc @@ -0,0 +1,210 @@ +// Copyright 2013 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/login/user_image_sync_observer.h" + +#include "base/bind.h" +#include "base/prefs/pref_change_registrar.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/chromeos/login/default_user_images.h" +#include "chrome/browser/chromeos/login/screens/user_image_screen.h" +#include "chrome/browser/chromeos/login/user.h" +#include "chrome/browser/chromeos/login/user_image_manager.h" +#include "chrome/browser/chromeos/login/wizard_controller.h" +#include "chrome/browser/chromeos/profiles/profile_helper.h" +#include "chrome/browser/prefs/pref_service_syncable.h" +#include "chrome/browser/prefs/scoped_user_pref_update.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_registrar.h" +#include "content/public/browser/notification_service.h" + +namespace chromeos { +namespace { + +// A dictionary containing info about user image. +const char kUserImageInfo[] = "user_image_info"; +// Path to value with image index. +const char kImageIndex[] = "image_index"; + +bool IsIndexSupported(int index) { + return (index >= kFirstDefaultImageIndex && index < kDefaultImagesCount) || + (index == User::kProfileImageIndex); +} + +Profile* GetUserProfile() { + ProfileManager* profile_manager = g_browser_process->profile_manager(); + if (!profile_manager) + return NULL; + base::FilePath profile_path = profile_manager->GetInitialProfileDir(); + return profile_manager->GetProfileByPath(profile_path); +} + +} // anonymous namespace + +UserImageSyncObserver::Observer::~Observer() {} + +UserImageSyncObserver::UserImageSyncObserver(const User* user) + : user_(user), + prefs_(NULL), + is_synced_(false), + local_image_changed_(false) { + notification_registrar_.reset(new content::NotificationRegistrar); + notification_registrar_->Add(this, + chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED, + content::NotificationService::AllSources()); + Profile* profile = GetUserProfile(); + if (profile) { + OnProfileGained(profile); + } else { + notification_registrar_->Add(this, + chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED, + content::NotificationService::AllSources()); + } +} + +UserImageSyncObserver::~UserImageSyncObserver() { + if (!is_synced_ && prefs_) + prefs_->RemoveObserver(this); + if (pref_change_registrar_) + pref_change_registrar_->RemoveAll(); +} + +// static +void UserImageSyncObserver::RegisterProfilePrefs( + user_prefs::PrefRegistrySyncable* registry_) { + registry_->RegisterDictionaryPref( + kUserImageInfo, + user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF); +} + +void UserImageSyncObserver::AddObserver(Observer* observer) { + observer_list_.AddObserver(observer); +} + +void UserImageSyncObserver::RemoveObserver(Observer* observer) { + observer_list_.RemoveObserver(observer); +} + +void UserImageSyncObserver::OnProfileGained(Profile* profile) { + prefs_ = PrefServiceSyncable::FromProfile(profile); + pref_change_registrar_.reset(new PrefChangeRegistrar); + pref_change_registrar_->Init(prefs_); + pref_change_registrar_->Add(kUserImageInfo, + base::Bind(&UserImageSyncObserver::OnPreferenceChanged, + base::Unretained(this))); + is_synced_ = prefs_->IsPrioritySyncing(); + if (!is_synced_) { + prefs_->AddObserver(this); + } else { + OnInitialSync(); + } +} + +void UserImageSyncObserver::OnInitialSync() { + int synced_index; + bool local_image_updated = false; + if (!GetSyncedImageIndex(&synced_index) || local_image_changed_) { + UpdateSyncedImageFromLocal(); + } else if (IsIndexSupported(synced_index) && CanUpdateLocalImageNow()) { + UpdateLocalImageFromSynced(); + local_image_updated = true; + } + FOR_EACH_OBSERVER(UserImageSyncObserver::Observer, observer_list_, + OnInitialSync(local_image_updated)); +} + +void UserImageSyncObserver::OnPreferenceChanged(const std::string& pref_name) { + // OnPreferenceChanged can be called before OnIsSyncingChanged. + if (!is_synced_) { + is_synced_ = true; + prefs_->RemoveObserver(this); + OnInitialSync(); + } else if (CanUpdateLocalImageNow()) { + UpdateLocalImageFromSynced(); + } +} + +void UserImageSyncObserver::Observe( + int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + if (type == chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED) { + Profile* profile = content::Details<Profile>(details).ptr(); + if (GetUserProfile() != profile) + return; + notification_registrar_->Remove( + this, + chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED, + content::NotificationService::AllSources()); + OnProfileGained(profile); + } else if (type == chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED) { + if (is_synced_) + UpdateSyncedImageFromLocal(); + else + local_image_changed_ = true; + } else { + NOTREACHED(); + } +} + +void UserImageSyncObserver::OnIsSyncingChanged() { + is_synced_ = prefs_->IsPrioritySyncing(); + if (is_synced_) { + prefs_->RemoveObserver(this); + OnInitialSync(); + } +} + +void UserImageSyncObserver::UpdateSyncedImageFromLocal() { + int local_index = user_->image_index(); + if (!IsIndexSupported(local_index)) { + local_index = User::kInvalidImageIndex; + } + int synced_index; + if (GetSyncedImageIndex(&synced_index) && (synced_index == local_index)) + return; + DictionaryPrefUpdate update(prefs_, kUserImageInfo); + DictionaryValue* dict = update.Get(); + dict->SetInteger(kImageIndex, local_index); + LOG(INFO) << "Saved avatar index " << local_index << " to sync."; +} + +void UserImageSyncObserver::UpdateLocalImageFromSynced() { + int synced_index; + GetSyncedImageIndex(&synced_index); + int local_index = user_->image_index(); + if ((synced_index == local_index) || !IsIndexSupported(synced_index)) + return; + UserImageManager* image_manager = UserManager::Get()->GetUserImageManager(); + if (synced_index == User::kProfileImageIndex) { + image_manager->SaveUserImageFromProfileImage(user_->email()); + } else { + image_manager->SaveUserDefaultImageIndex(user_->email(), synced_index); + } + LOG(INFO) << "Loaded avatar index " << synced_index << " from sync."; +} + +bool UserImageSyncObserver::GetSyncedImageIndex(int* index) { + *index = User::kInvalidImageIndex; + const DictionaryValue* dict = prefs_->GetDictionary(kUserImageInfo); + return dict && dict->GetInteger(kImageIndex, index); +} + +bool UserImageSyncObserver::CanUpdateLocalImageNow() { + if (WizardController* wizard_controller = + WizardController::default_controller()) { + UserImageScreen* screen = wizard_controller->GetUserImageScreen(); + if (wizard_controller->current_screen() == screen) { + if (screen->user_selected_image()) + return false; + } + } + return true; +} + +} // namespace chromeos + diff --git a/chrome/browser/chromeos/login/user_image_sync_observer.h b/chrome/browser/chromeos/login/user_image_sync_observer.h new file mode 100644 index 0000000..16a9eb7 --- /dev/null +++ b/chrome/browser/chromeos/login/user_image_sync_observer.h @@ -0,0 +1,104 @@ +// Copyright 2013 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. + +#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_USER_IMAGE_SYNC_OBSERVER_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_USER_IMAGE_SYNC_OBSERVER_H_ + +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "base/observer_list.h" +#include "chrome/browser/chromeos/login/user_manager.h" +#include "chrome/browser/prefs/pref_service_syncable_observer.h" +#include "content/public/browser/notification_observer.h" + +class PrefChangeRegistrar; +class PrefServiceSyncable; +class Profile; +namespace chromeos { +class User; +} +namespace content { +class NotificationRegistrar; +} +namespace user_prefs { +class PrefRegistrySyncable; +} + +namespace chromeos { + +// This class is responsible for keeping local user image synced with +// image saved in syncable preference. +class UserImageSyncObserver: public PrefServiceSyncableObserver, + public content::NotificationObserver { + public: + class Observer { + public: + // Called right after image info synced (i.e. |is_synced| became |true|). + // |local_image_updated| indicates if we desided to update local image in + // result of sync. + virtual void OnInitialSync(bool local_image_updated) = 0; + virtual ~Observer(); + }; + + public: + explicit UserImageSyncObserver(const User* user); + virtual ~UserImageSyncObserver(); + + // Register syncable preference for profile. + static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); + + // Returns |true| if sync was initialized and prefs have actual state. + bool is_synced() const { return is_synced_; } + + // Adds |observer| into observers list. + void AddObserver(Observer* observer); + // Removes |observer| from observers list. + void RemoveObserver(Observer* observer); + + private: + // PrefServiceSyncableObserver implementation. + virtual void OnIsSyncingChanged() OVERRIDE; + + // content::NotificationObserver implementation. + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE; + + // Called after user profile was loaded. + void OnProfileGained(Profile* profile); + + // Called when sync servise started it's work and we are able to sync needed + // preferences. + void OnInitialSync(); + + // Called when preference |pref_name| was changed.j + void OnPreferenceChanged(const std::string& pref_name); + + // Saves local image preferences to sync. + void UpdateSyncedImageFromLocal(); + + // Saves sync preferences to local state and updates user image. + void UpdateLocalImageFromSynced(); + + // Gets synced image index. Returns false if user has no needed preferences. + bool GetSyncedImageIndex(int* result); + + // If it is allowed to change user image now. + bool CanUpdateLocalImageNow(); + + const User* user_; + scoped_ptr<PrefChangeRegistrar> pref_change_registrar_; + scoped_ptr<content::NotificationRegistrar> notification_registrar_; + PrefServiceSyncable* prefs_; + bool is_synced_; + // Indicates if local user image changed during initialization. + bool local_image_changed_; + ObserverList<Observer> observer_list_; +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_USER_IMAGE_SYNC_OBSERVER_H_ + diff --git a/chrome/browser/chromeos/login/user_manager_impl.cc b/chrome/browser/chromeos/login/user_manager_impl.cc index cdcf4bb..90ed473 100644 --- a/chrome/browser/chromeos/login/user_manager_impl.cc +++ b/chrome/browser/chromeos/login/user_manager_impl.cc @@ -269,6 +269,7 @@ void UserManagerImpl::Shutdown() { if (observed_sync_service_) observed_sync_service_->RemoveObserver(this); + user_image_manager_->Shutdown(); } UserImageManager* UserManagerImpl::GetUserImageManager() { diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index 8e3fcb3..12365c3 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc @@ -131,6 +131,7 @@ #include "chrome/browser/chromeos/login/login_utils.h" #include "chrome/browser/chromeos/login/startup_utils.h" #include "chrome/browser/chromeos/login/user_image_manager.h" +#include "chrome/browser/chromeos/login/user_image_sync_observer.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/chromeos/login/wallpaper_manager.h" #include "chrome/browser/chromeos/net/proxy_config_handler.h" @@ -387,6 +388,7 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) { extensions::EnterprisePlatformKeysPrivateChallengeUserKeyFunction:: RegisterProfilePrefs(registry); FlagsUI::RegisterProfilePrefs(registry); + chromeos::UserImageSyncObserver::RegisterProfilePrefs(registry); #endif #if defined(OS_WIN) diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_user_image.css b/chrome/browser/resources/chromeos/login/oobe_screen_user_image.css index 94578bd..9c69a8f 100644 --- a/chrome/browser/resources/chromeos/login/oobe_screen_user_image.css +++ b/chrome/browser/resources/chromeos/login/oobe_screen_user_image.css @@ -9,6 +9,11 @@ width: 702px; } +#user-image.loading { + min-height: 609px; /* Should be the same as #gaia-signin height. */ + width: 722px; /* Should be the same as #gaia-signin width. */ +} + #user-image-screen-curtain { -webkit-margin-start: 8px; } diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_user_image.js b/chrome/browser/resources/chromeos/login/oobe_screen_user_image.js index 2785605..be2f1a8 100644 --- a/chrome/browser/resources/chromeos/login/oobe_screen_user_image.js +++ b/chrome/browser/resources/chromeos/login/oobe_screen_user_image.js @@ -199,7 +199,8 @@ cr.define('login', function() { } else { $('ok-button').disabled = false; chrome.send('selectImage', - [imageGrid.selectedItemUrl, imageGrid.selectionType]); + [imageGrid.selectedItemUrl, imageGrid.selectionType, + !imageGrid.inProgramSelection]); } // Start/stop camera on (de)selection. if (!imageGrid.inProgramSelection && @@ -384,7 +385,7 @@ cr.define('login', function() { item.author = data.author || ''; item.website = data.website || ''; } - this.classList.remove('loading'); + chrome.send('screenReady'); }, /** @@ -399,6 +400,15 @@ cr.define('login', function() { }, /** + * Hides curtain with spinner. + * @private + */ + hideCurtain_: function() { + this.classList.remove('loading'); + Oobe.getInstance().updateScreenSize(this); + }, + + /** * Updates the image preview caption. * @private */ @@ -432,6 +442,7 @@ cr.define('login', function() { 'setProfilePictureEnabled', 'setProfileImage', 'setSelectedImage', + 'hideCurtain' ].forEach(function(name) { UserImageScreen[name] = function(value) { $('user-image')[name + '_'](value); diff --git a/chrome/browser/resources/chromeos/login/screen_gaia_signin.css b/chrome/browser/resources/chromeos/login/screen_gaia_signin.css index af26cc3..c5891a4 100644 --- a/chrome/browser/resources/chromeos/login/screen_gaia_signin.css +++ b/chrome/browser/resources/chromeos/login/screen_gaia_signin.css @@ -4,9 +4,9 @@ */ #gaia-signin { - height: 609px; + height: 609px; /* Should be the same as #user-image.loading min-heigth. */ padding: 70px 17px 69px; /* Screen has no controls. */ - width: 722px; + width: 722px; /* Should be the same as #user-image.loading width. */ } #gaia-signin.no-right-panel { @@ -118,4 +118,5 @@ #createManagedUserLinkPlaceholder, #createManagedUserNoManagerText { margin-top: 10px; -}
\ No newline at end of file +} + diff --git a/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc index 44cb0181..75b3152 100644 --- a/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc +++ b/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc @@ -30,7 +30,8 @@ namespace chromeos { UserImageScreenHandler::UserImageScreenHandler() : BaseScreenHandler(kJsScreenPath), screen_(NULL), - show_on_init_(false) { + show_on_init_(false), + is_ready_(false) { } UserImageScreenHandler::~UserImageScreenHandler() { @@ -62,6 +63,8 @@ void UserImageScreenHandler::Show() { if (!screen_) return; screen_->CheckCameraPresence(); + if (is_ready_) + screen_->OnScreenReady(); } void UserImageScreenHandler::Hide() { @@ -94,6 +97,7 @@ void UserImageScreenHandler::DeclareLocalizedValues( void UserImageScreenHandler::RegisterMessages() { AddCallback("getImages", &UserImageScreenHandler::HandleGetImages); + AddCallback("screenReady", &UserImageScreenHandler::HandleScreenReady); AddCallback("photoTaken", &UserImageScreenHandler::HandlePhotoTaken); AddCallback("selectImage", &UserImageScreenHandler::HandleSelectImage); AddCallback("checkCameraPresence", @@ -146,6 +150,12 @@ void UserImageScreenHandler::HandleGetImages() { OnProfileImageAbsent(); } +void UserImageScreenHandler::HandleScreenReady() { + is_ready_ = true; + if (screen_) + screen_->OnScreenReady(); +} + void UserImageScreenHandler::HandlePhotoTaken(const std::string& image_url) { std::string mime_type, charset, raw_data; if (!net::DataURL::Parse(GURL(image_url), &mime_type, &charset, &raw_data)) @@ -163,9 +173,10 @@ void UserImageScreenHandler::HandleCheckCameraPresence() { } void UserImageScreenHandler::HandleSelectImage(const std::string& image_url, - const std::string& image_type) { + const std::string& image_type, + bool is_user_selection) { if (screen_) - screen_->OnImageSelected(image_type, image_url); + screen_->OnImageSelected(image_type, image_url, is_user_selection); } void UserImageScreenHandler::HandleImageAccepted() { @@ -185,6 +196,10 @@ void UserImageScreenHandler::SetCameraPresent(bool present) { CallJS("setCameraPresent", present); } +void UserImageScreenHandler::HideCurtain() { + CallJS("hideCurtain"); +} + void UserImageScreenHandler::SetProfilePictureEnabled(bool enabled) { CallJS("setProfilePictureEnabled", enabled); } diff --git a/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.h index 9ac7c64..8a0959c9 100644 --- a/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.h +++ b/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.h @@ -47,11 +47,16 @@ class UserImageScreenHandler : public UserImageScreenActor, virtual void SetCameraPresent(bool enabled) OVERRIDE; + virtual void HideCurtain() OVERRIDE; + private: // Sends image data to the page. void HandleGetImages(); + // Screen ready to be shown. + void HandleScreenReady(); + // Handles photo taken with WebRTC UI. void HandlePhotoTaken(const std::string& image_url); @@ -60,7 +65,8 @@ class UserImageScreenHandler : public UserImageScreenActor, // Handles clicking on default user image. void HandleSelectImage(const std::string& image_url, - const std::string& image_type); + const std::string& image_type, + bool is_user_selection); // Called when user accept the image closing the screen. void HandleImageAccepted(); @@ -73,6 +79,9 @@ class UserImageScreenHandler : public UserImageScreenActor, // Keeps whether screen should be shown right after initialization. bool show_on_init_; + // Keeps whether screen has loaded all default images and redy to be shown. + bool is_ready_; + base::Time screen_show_time_; DISALLOW_COPY_AND_ASSIGN(UserImageScreenHandler); diff --git a/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc b/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc index e75e1e9..537feab 100644 --- a/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc +++ b/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc @@ -79,6 +79,8 @@ ChangePictureOptionsHandler::ChangePictureOptionsHandler() content::NotificationService::AllSources()); registrar_.Add(this, chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED, content::NotificationService::AllSources()); + registrar_.Add(this, chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED, + content::NotificationService::AllSources()); } ChangePictureOptionsHandler::~ChangePictureOptionsHandler() { @@ -411,6 +413,11 @@ void ChangePictureOptionsHandler::Observe( // User profile image has been updated. SendProfileImage(*content::Details<const gfx::ImageSkia>(details).ptr(), false); + } else if (type == chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED) { + // Not initialized yet. + if (previous_image_index_ == User::kInvalidImageIndex) + return; + SendSelectedImage(); } } diff --git a/chrome/chrome_browser_chromeos.gypi b/chrome/chrome_browser_chromeos.gypi index 51c3e7b..0a282de 100644 --- a/chrome/chrome_browser_chromeos.gypi +++ b/chrome/chrome_browser_chromeos.gypi @@ -572,6 +572,8 @@ 'browser/chromeos/login/user_image_manager.cc', 'browser/chromeos/login/user_image_manager_impl.cc', 'browser/chromeos/login/user_image_manager_impl.h', + 'browser/chromeos/login/user_image_sync_observer.cc', + 'browser/chromeos/login/user_image_sync_observer.h', 'browser/chromeos/login/user_flow.cc', 'browser/chromeos/login/user_flow.h', 'browser/chromeos/login/user_manager.cc', diff --git a/chromeos/chromeos_switches.cc b/chromeos/chromeos_switches.cc index 63945db..c4bec2a 100644 --- a/chromeos/chromeos_switches.cc +++ b/chromeos/chromeos_switches.cc @@ -195,5 +195,8 @@ const char kStubCrosSettings[] = "stub-cros-settings"; const char kUseNewNetworkConfigurationHandlers[] = "use-new-network-configuration-handlers"; +// Disables user image sync. +const char kDisableUserImageSync[] = "disable-user-image-sync"; + } // namespace switches } // namespace chromeos diff --git a/chromeos/chromeos_switches.h b/chromeos/chromeos_switches.h index 4d4b57f..1e99663 100644 --- a/chromeos/chromeos_switches.h +++ b/chromeos/chromeos_switches.h @@ -72,6 +72,7 @@ CHROMEOS_EXPORT extern const char kSkipHWIDCheck[]; CHROMEOS_EXPORT extern const char kSmsTestMessages[]; CHROMEOS_EXPORT extern const char kStubCrosSettings[]; CHROMEOS_EXPORT extern const char kUseNewNetworkConfigurationHandlers[]; +CHROMEOS_EXPORT extern const char kDisableUserImageSync[]; } // namespace switches } // namespace chromeos |