Chromium Code Reviews| Index: chrome/browser/chromeos/login/wallpaper_manager.cc |
| diff --git a/chrome/browser/chromeos/login/wallpaper_manager.cc b/chrome/browser/chromeos/login/wallpaper_manager.cc |
| index c4be12938a6da3683fa966bfd9449187da84ac82..03632e6d6b5d08da01399852cea7f53e05e04ecf 100644 |
| --- a/chrome/browser/chromeos/login/wallpaper_manager.cc |
| +++ b/chrome/browser/chromeos/login/wallpaper_manager.cc |
| @@ -5,9 +5,13 @@ |
| #include "chrome/browser/chromeos/login/wallpaper_manager.h" |
| #include "ash/desktop_background/desktop_background_controller.h" |
| -#include "ash/desktop_background/desktop_background_resources.h" |
| #include "ash/shell.h" |
| +#include "base/command_line.h" |
| #include "base/logging.h" |
| +#include "base/file_path.h" |
| +#include "base/file_util.h" |
| +#include "base/string_number_conversions.h" |
| +#include "base/string_util.h" |
| #include "base/time.h" |
| #include "base/values.h" |
| #include "chrome/browser/browser_process.h" |
| @@ -15,12 +19,43 @@ |
| #include "chrome/browser/chromeos/login/user.h" |
| #include "chrome/browser/chromeos/login/user_manager.h" |
| #include "chrome/browser/chromeos/login/user_manager_impl.h" |
| +#include "chrome/browser/chromeos/login/wizard_controller.h" |
| #include "chrome/browser/prefs/pref_service.h" |
| +#include "chrome/browser/prefs/scoped_user_pref_update.h" |
| +#include "chrome/common/chrome_notification_types.h" |
| +#include "chrome/common/chrome_switches.h" |
| #include "chromeos/dbus/dbus_thread_manager.h" |
| #include "chromeos/dbus/power_manager_client.h" |
| +#include "content/public/browser/browser_thread.h" |
| +#include "content/public/browser/notification_service.h" |
| +#include "skia/ext/image_operations.h" |
| +#include "third_party/zlib/zlib.h" |
| +#include "ui/gfx/codec/png_codec.h" |
| +#include "ui/gfx/skia_util.h" |
| + |
| +using content::BrowserThread; |
| namespace { |
| + |
| const int kWallpaperUpdateIntervalSec = 24 * 60 * 60; |
| + |
| +const char kWallpaperTypeNodeName[] = "type"; |
| +const char kWallpaperIndexNodeName[] = "index"; |
| +const char kWallpaperDateNodeName[] = "date"; |
| + |
| +const int kThumbnailWidth = 128; |
| +const int kThumbnailHeight = 80; |
| + |
| +// Default wallpaper index used in OOBE (first boot). |
| +// Defined here because Chromium default index differs. |
| +// Also see ash::WallpaperInfo kDefaultWallpapers in |
| +// desktop_background_resources.cc |
| +#if defined(GOOGLE_CHROME_BUILD) |
| +const int kDefaultOOBEWallpaperIndex = 16; // IDR_AURA_WALLPAPERS_3_URBAN0 |
| +#else |
| +const int kDefaultOOBEWallpaperIndex = 0; // IDR_AURA_WALLPAPERS_ROMAINGUY_0 |
| +#endif |
| + |
| } // namespace |
| namespace chromeos { |
| @@ -36,9 +71,18 @@ WallpaperManager* WallpaperManager::Get() { |
| return g_wallpaper_manager; |
| } |
| -WallpaperManager::WallpaperManager() : last_selected_user_("") { |
| +WallpaperManager::WallpaperManager() |
| + : ALLOW_THIS_IN_INITIALIZER_LIST(wallpaper_loader_(new WallpaperLoader)), |
| + current_user_wallpaper_type_(User::UNKNOWN), |
| + ALLOW_THIS_IN_INITIALIZER_LIST(current_user_wallpaper_index_( |
| + ash::GetInvalidWallpaperIndex())), |
| + last_selected_user_(""), |
| + ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { |
| system::TimezoneSettings::GetInstance()->AddObserver(this); |
| RestartTimer(); |
| + registrar_.Add(this, |
| + chrome::NOTIFICATION_LOGIN_USER_CHANGED, |
| + content::NotificationService::AllSources()); |
| } |
| void WallpaperManager::AddPowerManagerClientObserver() { |
| @@ -46,6 +90,120 @@ void WallpaperManager::AddPowerManagerClientObserver() { |
| DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this); |
| } |
| +void WallpaperManager::CacheIfCustomWallpaper(const std::string& email) { |
| + User::WallpaperType type; |
| + int index; |
| + base::Time date; |
| + GetUserWallpaperProperties(email, &type, &index, &date); |
| + if (type == User::CUSTOMIZED) { |
| + // Uses WeakPtr here to make the request cancelable. |
| + LoadedCallback loaded_cb = base::Bind(&WallpaperManager::CacheCallback, |
|
Ivan Korotkov
2012/07/27 23:02:02
Here and below: there is no point in storing a cal
bshe
2012/07/31 14:27:54
Done.
|
| + weak_factory_.GetWeakPtr(), email); |
| + LoadCustomWallpaperFromFile(email, loaded_cb); |
| + } |
| +} |
| + |
| +void WallpaperManager::EnsureLoggedInUserWallpaperLoaded() { |
| + User::WallpaperType type; |
| + int index; |
| + base::Time last_modification_date; |
| + GetLoggedInUserWallpaperProperties(&type, &index, &last_modification_date); |
| + |
| + if (type != current_user_wallpaper_type_ || |
| + index != current_user_wallpaper_index_) { |
| + UserSelected(UserManager::Get()->GetLoggedInUser().email()); |
|
Ivan Korotkov
2012/07/27 23:02:02
Please don't make manual call of event handlers (l
bshe
2012/07/31 14:27:54
Done.
|
| + } |
| +} |
| + |
| +void WallpaperManager::FetchCustomWallpaper(const std::string& email) { |
| + User::WallpaperType type; |
| + int index; |
| + base::Time date; |
| + GetUserWallpaperProperties(email, &type, &index, &date); |
| + ash::WallpaperLayout layout = static_cast<ash::WallpaperLayout>(index); |
| + LoadedCallback loaded_cb = base::Bind(&WallpaperManager::FetchCallback, |
| + base::Unretained(this), email, layout); |
| + LoadCustomWallpaperFromFile(email, loaded_cb); |
| +} |
| + |
| +bool WallpaperManager::GetCustomWallpaperFromCache(const std::string& email, |
| + SkBitmap* wallpaper) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + CustomWallpaperMap::iterator it = custom_wallpaper_cache_.find(email); |
|
Ivan Korotkov
2012/07/27 23:02:02
Here and below: const_iterator
bshe
2012/07/31 14:27:54
Done.
|
| + if (it != custom_wallpaper_cache_.end()) { |
| + *wallpaper = (*it).second; |
| + return true; |
| + } else { |
|
Ivan Korotkov
2012/07/27 23:02:02
Chrome's style is "no else after return"
bshe
2012/07/31 14:27:54
Done.
|
| + return false; |
| + } |
| +} |
| + |
| +SkBitmap WallpaperManager::GetCustomWallpaperThumbnail( |
| + const std::string& email) { |
| + CustomWallpaperMap::iterator it = |
| + custom_wallpaper_thumbnail_cache_.find(email); |
| + if (it != custom_wallpaper_cache_.end()) |
| + return (*it).second; |
| + else |
| + return SkBitmap(); |
| +} |
| + |
| +void WallpaperManager::GetLoggedInUserWallpaperProperties( |
| + User::WallpaperType* type, |
| + int* index, |
| + base::Time* last_modification_date) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + |
| + if (UserManager::Get()->IsLoggedInAsStub()) { |
| + *type = current_user_wallpaper_type_ = User::DEFAULT; |
| + *index = current_user_wallpaper_index_ = ash::GetInvalidWallpaperIndex(); |
| + return; |
| + } |
| + |
| + GetUserWallpaperProperties(UserManager::Get()->GetLoggedInUser().email(), |
| + type, index, last_modification_date); |
| +} |
| + |
| +void WallpaperManager::InitializeWallpaper() { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + UserManager* user_manager = UserManager::Get(); |
| + if (!user_manager->IsUserLoggedIn()) { |
| + if (!CommandLine::ForCurrentProcess()->HasSwitch( |
| + switches::kDisableNewOobe)) { |
| + if (!WizardController::IsDeviceRegistered() && |
| + !WizardController::IsZeroDelayEnabled()) { |
| + // TODO(nkostylev): Add switch to disable wallpaper transition on OOBE. |
| + // Should be used on test images so that they are not slowed down. |
| + ash::Shell::GetInstance()->desktop_background_controller()-> |
| + SetDefaultWallpaper(kDefaultOOBEWallpaperIndex); |
| + } else { |
| + bool show_users = true; |
| + bool result = CrosSettings::Get()->GetBoolean( |
| + kAccountsPrefShowUserNamesOnSignIn, &show_users); |
| + DCHECK(result) << "Unable to fetch setting " |
| + << kAccountsPrefShowUserNamesOnSignIn; |
| + if (!show_users) { |
| + ash::Shell::GetInstance()->desktop_background_controller()-> |
| + SetDefaultWallpaper(ash::GetSolidColorIndex()); |
| + } |
| + } |
| + } |
| + return; |
| + } |
| + UserSelected(user_manager->GetLoggedInUser().email()); |
|
Ivan Korotkov
2012/07/27 23:02:02
Same note about calling event handler.
bshe
2012/07/31 14:27:54
Done.
|
| +} |
| + |
| +void WallpaperManager::Observe(int type, |
| + const content::NotificationSource& source, |
| + const content::NotificationDetails& details) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + if (type == chrome::NOTIFICATION_LOGIN_USER_CHANGED) { |
| + // Cancel callback for previous cache requests. |
| + weak_factory_.InvalidateWeakPtrs(); |
| + custom_wallpaper_cache_.clear(); |
| + } |
| +} |
| + |
| void WallpaperManager::RestartTimer() { |
| timer_.Stop(); |
| base::Time midnight = base::Time::Now().LocalMidnight(); |
| @@ -62,6 +220,76 @@ void WallpaperManager::RestartTimer() { |
| } |
| } |
| +void WallpaperManager::SaveUserWallpaperProperties(const std::string& email, |
| + User::WallpaperType type, |
| + int index) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + |
| + current_user_wallpaper_type_ = type; |
| + current_user_wallpaper_index_ = index; |
| + // Ephemeral users can not save data to local state. We just cache the index |
| + // in memory for them. |
| + if (UserManager::Get()->IsCurrentUserEphemeral()) { |
| + return; |
| + } |
| + |
| + PrefService* local_state = g_browser_process->local_state(); |
| + DictionaryPrefUpdate wallpaper_update(local_state, |
| + UserManager::kUserWallpapersProperties); |
| + |
| + base::DictionaryValue* wallpaper_properties = new base::DictionaryValue(); |
| + wallpaper_properties->Set(kWallpaperTypeNodeName, |
| + new base::FundamentalValue(type)); |
| + wallpaper_properties->Set(kWallpaperIndexNodeName, |
| + new base::FundamentalValue(index)); |
| + wallpaper_properties->SetString(kWallpaperDateNodeName, |
| + base::Int64ToString(base::Time::Now().LocalMidnight().ToInternalValue())); |
| + wallpaper_update->SetWithoutPathExpansion(email, wallpaper_properties); |
| +} |
| + |
| +void WallpaperManager::SetUserWallpaperFromFile( |
| + const std::string& username, |
| + const FilePath& path, |
| + ash::WallpaperLayout layout, |
| + base::WeakPtr<WallpaperDelegate> delegate) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + |
| + // TODO(bshe): Refactor GetWallpaperPathForUser to this class. |
| + std::string wallpaper_path = |
| + static_cast<UserManagerImpl*>(UserManager::Get())-> |
| + GetWallpaperPathForUser(username, false).value(); |
| + // need to expose ephemeral user. |
| + bool save_raw_file = true; |
| + |
| + // For wallpapers, save the image without resizing. |
| + wallpaper_loader_->Start( |
| + path.value(), save_raw_file, wallpaper_path, |
| + base::Bind(&WallpaperManager::SetCallback, |
| + base::Unretained(this), username, layout, User::CUSTOMIZED, |
| + delegate)); |
| +} |
| + |
| +void WallpaperManager::SetInitialUserWallpaper(const std::string& username) { |
| + current_user_wallpaper_type_ = User::DEFAULT; |
| + // TODO(bshe): Ideally, wallpaper should start to load as soon as user click |
| + // "Add user". |
| + if (username == kGuestUser) |
| + current_user_wallpaper_index_ = ash::GetGuestWallpaperIndex(); |
| + else |
| + current_user_wallpaper_index_ = ash::GetDefaultWallpaperIndex(); |
| + SaveUserWallpaperProperties(username, |
| + current_user_wallpaper_type_, |
| + current_user_wallpaper_index_); |
| + |
| + // Some browser tests do not have shell instance. And it is not necessary to |
| + // create a wallpaper for these tests. Add HasInstance check to prevent tests |
| + // crash and speed up the tests by avoid loading wallpaper. |
| + if (ash::Shell::HasInstance()) { |
| + ash::Shell::GetInstance()->desktop_background_controller()-> |
| + SetDefaultWallpaper(current_user_wallpaper_index_); |
| + } |
| +} |
| + |
| void WallpaperManager::SetLastSelectedUser( |
| const std::string& last_selected_user) { |
| last_selected_user_ = last_selected_user; |
| @@ -75,6 +303,35 @@ void WallpaperManager::UserDeselected() { |
| } |
| } |
| +void WallpaperManager::UserSelected(const std::string& email) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + if (UserManager::Get()->IsKnownUser(email)) { |
| + User::WallpaperType type; |
| + int index; |
| + base::Time date; |
| + GetUserWallpaperProperties(email, &type, &index, &date); |
| + if (type == User::DAILY && date != base::Time::Now().LocalMidnight()) { |
| + index = ash::GetNextWallpaperIndex(index); |
| + SaveUserWallpaperProperties(email, User::DAILY, index); |
| + } else if (type == User::CUSTOMIZED) { |
| + SkBitmap custom_wallpaper; |
| + if (GetCustomWallpaperFromCache(email, &custom_wallpaper)) { |
| + // In customized mode, we use index pref to save the user selected |
| + // wallpaper layout. See function SaveWallpaperToLocalState(). |
| + ash::WallpaperLayout layout = static_cast<ash::WallpaperLayout>(index); |
| + ash::Shell::GetInstance()->desktop_background_controller()-> |
| + SetCustomWallpaper(custom_wallpaper, layout); |
| + } else { |
| + FetchCustomWallpaper(email); |
| + } |
| + return; |
| + } |
| + ash::Shell::GetInstance()->desktop_background_controller()-> |
| + SetDefaultWallpaper(index); |
| + SetLastSelectedUser(email); |
| + } |
| +} |
| + |
| // WallpaperManager, private: -------------------------------------------------- |
| WallpaperManager::~WallpaperManager() { |
| @@ -96,32 +353,175 @@ void WallpaperManager::BatchUpdateWallpaper() { |
| for (UserList::const_iterator it = users.begin(); |
| it != users.end(); ++it) { |
| std::string email = (*it)->email(); |
| - // TODO(bshe): Move GetUserWallpaperProperties() to this class. |
| - static_cast<UserManagerImpl*>(user_manager)-> |
| - GetUserWallpaperProperties(email, &type, &index, |
| - &last_modification_date); |
| + GetUserWallpaperProperties(email, &type, &index, &last_modification_date); |
| base::Time current_date = base::Time::Now().LocalMidnight(); |
| if (type == User::DAILY && current_date != last_modification_date) { |
| index = ash::GetNextWallpaperIndex(index); |
| - // TODO(bshe): Move SaveUserWallpaperProperties() to this class. |
| - static_cast<UserManagerImpl*>(user_manager)-> |
| - SaveUserWallpaperProperties(email, type, index); |
| + SaveUserWallpaperProperties(email, type, index); |
| } |
| // Force a wallpaper update for logged in / last selected user. |
| // TODO(bshe): Notify lock screen, wallpaper picker UI to update wallpaper |
| // as well. |
| if (user_manager->IsUserLoggedIn() && |
| email == user_manager->GetLoggedInUser().email()) { |
| - user_manager->UserSelected(email); |
| + UserSelected(email); |
| } else if (show_users && |
| email == last_selected_user_) { |
| - user_manager->UserSelected(email); |
| + UserSelected(email); |
| } |
| } |
| } |
| RestartTimer(); |
| } |
| +void WallpaperManager::CacheCallback(const std::string& email, |
| + const SkBitmap& wallpaper) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + |
| + if (custom_wallpaper_cache_.find(email) != custom_wallpaper_cache_.end()) { |
| + LOG(ERROR) << "Try to cache a wallpaper for the same user twice."; |
|
Ivan Korotkov
2012/07/27 23:02:02
No need in braces
Ivan Korotkov
2012/07/27 23:02:02
"Attemped to cache wallpaper for the same user twi
Ivan Korotkov
2012/07/27 23:02:02
If this can happen only due to incorrect programmi
bshe
2012/07/31 14:27:54
Done. Using a DCHECK instead.
On 2012/07/27 23:02
|
| + } |
| + |
| + BrowserThread::PostTask( |
| + BrowserThread::FILE, |
| + FROM_HERE, |
| + base::Bind(&WallpaperManager::CacheThumbnail, |
| + base::Unretained(this), email, wallpaper)); |
| + |
| + custom_wallpaper_cache_.insert(std::make_pair(email, wallpaper)); |
| +} |
| + |
| +void WallpaperManager::CacheThumbnail(const std::string& email, |
| + const SkBitmap& wallpaper) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| + SkBitmap thumbnail = |
| + skia::ImageOperations::Resize(wallpaper, |
| + skia::ImageOperations::RESIZE_LANCZOS3, |
| + kThumbnailWidth, kThumbnailHeight); |
| + CustomWallpaperMap::iterator it = |
|
Ivan Korotkov
2012/07/27 23:02:02
Just custom_wallpaper_thumbnail_cache_[email] = th
bshe
2012/07/31 14:27:54
Done.
|
| + custom_wallpaper_thumbnail_cache_.find(email); |
| + if (it != custom_wallpaper_thumbnail_cache_.end()) |
| + it->second = thumbnail; |
| + else |
| + custom_wallpaper_thumbnail_cache_.insert(std::make_pair(email, thumbnail)); |
| +} |
| + |
| +void WallpaperManager::FetchCallback(const std::string& email, |
| + ash::WallpaperLayout layout, |
| + const SkBitmap& wallpaper) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + |
| + BrowserThread::PostTask( |
| + BrowserThread::FILE, |
| + FROM_HERE, |
| + base::Bind(&WallpaperManager::CacheThumbnail, |
| + base::Unretained(this), email, wallpaper)); |
| + |
| + ash::Shell::GetInstance()->desktop_background_controller()-> |
| + SetCustomWallpaper(wallpaper, layout); |
| +} |
| + |
| +void WallpaperManager::GetUserWallpaperProperties(const std::string& email, |
| + User::WallpaperType* type, |
| + int* index, |
| + base::Time* last_modification_date) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + |
| + // Default to the values cached in memory. |
| + *type = current_user_wallpaper_type_; |
| + *index = current_user_wallpaper_index_; |
| + |
| + // Override with values found in local store, if any. |
| + if (!email.empty()) { |
| + const DictionaryValue* user_wallpapers = g_browser_process->local_state()-> |
| + GetDictionary(UserManager::kUserWallpapersProperties); |
| + base::DictionaryValue* wallpaper_properties; |
| + if (user_wallpapers->GetDictionaryWithoutPathExpansion( |
| + email, |
| + &wallpaper_properties)) { |
| + *type = User::UNKNOWN; |
| + *index = ash::GetInvalidWallpaperIndex(); |
| + wallpaper_properties->GetInteger(kWallpaperTypeNodeName, |
| + reinterpret_cast<int*>(type)); |
| + wallpaper_properties->GetInteger(kWallpaperIndexNodeName, index); |
| + std::string date_string; |
| + int64 val; |
| + if (!(wallpaper_properties->GetString(kWallpaperDateNodeName, |
| + &date_string) && |
| + base::StringToInt64(date_string, &val))) |
| + val = 0; |
| + *last_modification_date = base::Time::FromInternalValue(val); |
| + } |
| + } |
| +} |
| + |
| +void WallpaperManager::GenerateUserWallpaperThumbnail( |
| + const std::string& email, |
| + User::WallpaperType type, |
| + base::WeakPtr<WallpaperDelegate> delegate, |
| + const SkBitmap& wallpaper) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| + SkBitmap thumbnail = |
| + skia::ImageOperations::Resize(wallpaper, |
| + skia::ImageOperations::RESIZE_LANCZOS3, |
| + kThumbnailWidth, kThumbnailHeight); |
| + |
| + CustomWallpaperMap::iterator it = |
|
Ivan Korotkov
2012/07/27 23:02:02
ditto
bshe
2012/07/31 14:27:54
Done.
|
| + custom_wallpaper_thumbnail_cache_.find(email); |
| + if (it != custom_wallpaper_thumbnail_cache_.end()) |
| + it->second = thumbnail; |
| + else |
| + custom_wallpaper_thumbnail_cache_.insert(std::make_pair(email, thumbnail)); |
| + |
| + // Notify thumbnail is ready. |
| + BrowserThread::PostTask( |
| + BrowserThread::UI, |
| + FROM_HERE, |
| + base::Bind(&WallpaperManager::OnThumbnailUpdated, |
| + base::Unretained(this), delegate)); |
| +} |
| + |
| +void WallpaperManager::LoadCustomWallpaperFromFile(const std::string& email, |
| + const LoadedCallback& loaded_cb) { |
| + // TODO(bshe): Refactor GetWallpaperPathForUser to this class. |
|
Ivan Korotkov
2012/07/27 23:02:02
Time to do it maybe? :)
Or put it into UserManager
bshe
2012/07/31 14:27:54
Done.
|
| + std::string wallpaper_path = |
| + static_cast<UserManagerImpl*>(UserManager::Get())-> |
| + GetWallpaperPathForUser(email, false).value(); |
| + // Load user wallpaper asynchronously. |
| + wallpaper_loader_->Start(wallpaper_path, false, std::string(), |
| + base::Bind(loaded_cb)); |
|
Ivan Korotkov
2012/07/27 23:02:02
Should be no need in empty Bind
bshe
2012/07/31 14:27:54
Done.
|
| +} |
| + |
| +void WallpaperManager::OnThumbnailUpdated( |
| + base::WeakPtr<WallpaperDelegate> delegate) { |
| + if (delegate) |
| + delegate->SetCustomWallpaperThumbnail(); |
| +} |
| + |
| +void WallpaperManager::SetCallback(const std::string& username, |
| + ash::WallpaperLayout layout, |
| + User::WallpaperType type, |
| + base::WeakPtr<WallpaperDelegate> delegate, |
| + const SkBitmap& wallpaper) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + |
| + BrowserThread::PostTask( |
| + BrowserThread::FILE, |
| + FROM_HERE, |
| + base::Bind(&WallpaperManager::GenerateUserWallpaperThumbnail, |
| + base::Unretained(this), username, type, delegate, wallpaper)); |
| + |
| + ash::Shell::GetInstance()->desktop_background_controller()-> |
| + SetCustomWallpaper(wallpaper, layout); |
| + |
| + BrowserThread::PostTask( |
| + BrowserThread::UI, |
| + FROM_HERE, |
| + base::Bind(&WallpaperManager::SaveUserWallpaperProperties, |
| + base::Unretained(this), |
| + username, type, layout)); |
| +} |
| + |
| void WallpaperManager::SystemResumed() { |
| BatchUpdateWallpaper(); |
| } |