user_image_screen.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
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/login/screens/user_image_screen.h" 6 7#include <string> 8 9#include "base/bind.h" 10#include "base/bind_helpers.h" 11#include "base/compiler_specific.h" 12#include "base/location.h" 13#include "base/logging.h" 14#include "base/message_loop/message_loop_proxy.h" 15#include "base/metrics/histogram.h" 16#include "base/timer/timer.h" 17#include "base/values.h" 18#include "chrome/browser/chrome_notification_types.h" 19#include "chrome/browser/chromeos/accessibility/accessibility_manager.h" 20#include "chrome/browser/chromeos/camera_presence_notifier.h" 21#include "chrome/browser/chromeos/login/login_utils.h" 22#include "chrome/browser/chromeos/login/screens/screen_observer.h" 23#include "chrome/browser/chromeos/login/users/avatar/user_image_manager.h" 24#include "chrome/browser/chromeos/login/users/chrome_user_manager.h" 25#include "chrome/browser/chromeos/login/wizard_controller.h" 26#include "chrome/browser/chromeos/profiles/profile_helper.h" 27#include "chrome/browser/policy/profile_policy_connector.h" 28#include "chrome/browser/policy/profile_policy_connector_factory.h" 29#include "chrome/browser/profiles/profile.h" 30#include "chrome/common/url_constants.h" 31#include "components/policy/core/common/policy_map.h" 32#include "components/policy/core/common/policy_namespace.h" 33#include "components/policy/core/common/policy_service.h" 34#include "components/user_manager/user.h" 35#include "components/user_manager/user_image/default_user_images.h" 36#include "components/user_manager/user_image/user_image.h" 37#include "components/user_manager/user_manager.h" 38#include "content/public/browser/browser_thread.h" 39#include "content/public/browser/notification_service.h" 40#include "policy/policy_constants.h" 41#include "third_party/skia/include/core/SkBitmap.h" 42#include "ui/base/webui/web_ui_util.h" 43#include "ui/gfx/image/image_skia.h" 44 45using content::BrowserThread; 46 47namespace chromeos { 48 49namespace { 50 51// Time histogram suffix for profile image download. 52const char kProfileDownloadReason[] = "OOBE"; 53 54// Maximum amount of time to wait for the user image to sync. 55// The screen is shown iff sync failed or time limit exceeded. 56const int kSyncTimeoutSeconds = 10; 57 58} // namespace 59 60UserImageScreen::UserImageScreen(ScreenObserver* screen_observer, 61 UserImageScreenActor* actor) 62 : WizardScreen(screen_observer), 63 actor_(actor), 64 accept_photo_after_decoding_(false), 65 selected_image_(user_manager::User::USER_IMAGE_INVALID), 66 profile_picture_data_url_(url::kAboutBlankURL), 67 profile_picture_absent_(false), 68 is_screen_ready_(false), 69 user_has_selected_image_(false) { 70 actor_->SetDelegate(this); 71 notification_registrar_.Add(this, 72 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED, 73 content::NotificationService::AllSources()); 74 notification_registrar_.Add(this, 75 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED, 76 content::NotificationService::AllSources()); 77 notification_registrar_.Add(this, 78 chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED, 79 content::NotificationService::AllSources()); 80} 81 82UserImageScreen::~UserImageScreen() { 83 CameraPresenceNotifier::GetInstance()->RemoveObserver(this); 84 if (actor_) 85 actor_->SetDelegate(NULL); 86 if (image_decoder_.get()) 87 image_decoder_->set_delegate(NULL); 88} 89 90void UserImageScreen::OnScreenReady() { 91 is_screen_ready_ = true; 92 if (!IsWaitingForSync()) 93 HideCurtain(); 94} 95 96void UserImageScreen::OnPhotoTaken(const std::string& raw_data) { 97 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 98 user_photo_ = gfx::ImageSkia(); 99 if (image_decoder_.get()) 100 image_decoder_->set_delegate(NULL); 101 image_decoder_ = new ImageDecoder(this, raw_data, 102 ImageDecoder::DEFAULT_CODEC); 103 scoped_refptr<base::MessageLoopProxy> task_runner = 104 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI); 105 image_decoder_->Start(task_runner); 106} 107 108void UserImageScreen::OnCameraPresenceCheckDone(bool is_camera_present) { 109 if (actor_) 110 actor_->SetCameraPresent(is_camera_present); 111} 112 113void UserImageScreen::HideCurtain() { 114 if (actor_) 115 actor_->HideCurtain(); 116} 117 118void UserImageScreen::OnImageDecoded(const ImageDecoder* decoder, 119 const SkBitmap& decoded_image) { 120 DCHECK_EQ(image_decoder_.get(), decoder); 121 user_photo_ = gfx::ImageSkia::CreateFrom1xBitmap(decoded_image); 122 if (accept_photo_after_decoding_) 123 OnImageAccepted(); 124} 125 126void UserImageScreen::OnDecodeImageFailed(const ImageDecoder* decoder) { 127 NOTREACHED() << "Failed to decode PNG image from WebUI"; 128} 129 130void UserImageScreen::OnInitialSync(bool local_image_updated) { 131 DCHECK(sync_timer_); 132 if (!local_image_updated) { 133 sync_timer_.reset(); 134 GetSyncObserver()->RemoveObserver(this); 135 if (is_screen_ready_) 136 HideCurtain(); 137 return; 138 } 139 ExitScreen(); 140} 141 142void UserImageScreen::OnSyncTimeout() { 143 sync_timer_.reset(); 144 GetSyncObserver()->RemoveObserver(this); 145 if (is_screen_ready_) 146 HideCurtain(); 147} 148 149bool UserImageScreen::IsWaitingForSync() const { 150 return sync_timer_.get() && sync_timer_->IsRunning(); 151} 152 153void UserImageScreen::OnUserImagePolicyChanged(const base::Value* previous, 154 const base::Value* current) { 155 if (current) { 156 base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE, 157 policy_registrar_.release()); 158 ExitScreen(); 159 } 160} 161 162void UserImageScreen::OnImageSelected(const std::string& image_type, 163 const std::string& image_url, 164 bool is_user_selection) { 165 if (is_user_selection) { 166 user_has_selected_image_ = true; 167 } 168 if (image_url.empty()) 169 return; 170 int user_image_index = user_manager::User::USER_IMAGE_INVALID; 171 if (image_type == "default" && 172 user_manager::IsDefaultImageUrl(image_url, &user_image_index)) { 173 selected_image_ = user_image_index; 174 } else if (image_type == "camera") { 175 selected_image_ = user_manager::User::USER_IMAGE_EXTERNAL; 176 } else if (image_type == "profile") { 177 selected_image_ = user_manager::User::USER_IMAGE_PROFILE; 178 } else { 179 NOTREACHED() << "Unexpected image type: " << image_type; 180 } 181} 182 183void UserImageScreen::OnImageAccepted() { 184 UserImageManager* image_manager = GetUserImageManager(); 185 int uma_index = 0; 186 switch (selected_image_) { 187 case user_manager::User::USER_IMAGE_EXTERNAL: 188 // Photo decoding may not have been finished yet. 189 if (user_photo_.isNull()) { 190 accept_photo_after_decoding_ = true; 191 return; 192 } 193 image_manager->SaveUserImage( 194 user_manager::UserImage::CreateAndEncode(user_photo_)); 195 uma_index = user_manager::kHistogramImageFromCamera; 196 break; 197 case user_manager::User::USER_IMAGE_PROFILE: 198 image_manager->SaveUserImageFromProfileImage(); 199 uma_index = user_manager::kHistogramImageFromProfile; 200 break; 201 default: 202 DCHECK(selected_image_ >= 0 && 203 selected_image_ < user_manager::kDefaultImagesCount); 204 image_manager->SaveUserDefaultImageIndex(selected_image_); 205 uma_index = user_manager::GetDefaultImageHistogramValue(selected_image_); 206 break; 207 } 208 if (user_has_selected_image_) { 209 UMA_HISTOGRAM_ENUMERATION("UserImage.FirstTimeChoice", 210 uma_index, 211 user_manager::kHistogramImagesCount); 212 } 213 ExitScreen(); 214} 215 216 217void UserImageScreen::PrepareToShow() { 218 if (actor_) 219 actor_->PrepareToShow(); 220} 221 222const user_manager::User* UserImageScreen::GetUser() { 223 return user_manager::UserManager::Get()->GetLoggedInUser(); 224} 225 226UserImageManager* UserImageScreen::GetUserImageManager() { 227 return ChromeUserManager::Get()->GetUserImageManager(GetUser()->email()); 228} 229 230UserImageSyncObserver* UserImageScreen::GetSyncObserver() { 231 return GetUserImageManager()->GetSyncObserver(); 232} 233 234void UserImageScreen::Show() { 235 if (!actor_) 236 return; 237 238 DCHECK(!policy_registrar_); 239 if (Profile* profile = ProfileHelper::Get()->GetProfileByUser(GetUser())) { 240 policy::PolicyService* policy_service = 241 policy::ProfilePolicyConnectorFactory::GetForProfile(profile)-> 242 policy_service(); 243 if (policy_service->GetPolicies( 244 policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, 245 std::string())) 246 .Get(policy::key::kUserAvatarImage)) { 247 // If the user image is managed by policy, skip the screen because the 248 // user is not allowed to override a policy-set image. 249 ExitScreen(); 250 return; 251 } 252 253 // Listen for policy changes. If at any point, the user image becomes 254 // managed by policy, the screen will close. 255 policy_registrar_.reset(new policy::PolicyChangeRegistrar( 256 policy_service, 257 policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, std::string()))); 258 policy_registrar_->Observe( 259 policy::key::kUserAvatarImage, 260 base::Bind(&UserImageScreen::OnUserImagePolicyChanged, 261 base::Unretained(this))); 262 } else { 263 NOTREACHED(); 264 } 265 266 if (GetUser()->CanSyncImage()) { 267 if (UserImageSyncObserver* sync_observer = GetSyncObserver()) { 268 // We have synced image already. 269 if (sync_observer->is_synced()) { 270 ExitScreen(); 271 return; 272 } 273 sync_observer->AddObserver(this); 274 sync_timer_.reset(new base::Timer( 275 FROM_HERE, 276 base::TimeDelta::FromSeconds(kSyncTimeoutSeconds), 277 base::Bind(&UserImageScreen::OnSyncTimeout, base::Unretained(this)), 278 false)); 279 sync_timer_->Reset(); 280 } 281 } 282 CameraPresenceNotifier::GetInstance()->AddObserver(this); 283 actor_->Show(); 284 285 selected_image_ = GetUser()->image_index(); 286 actor_->SelectImage(selected_image_); 287 288 // Start fetching the profile image. 289 GetUserImageManager()->DownloadProfileImage(kProfileDownloadReason); 290} 291 292void UserImageScreen::Hide() { 293 CameraPresenceNotifier::GetInstance()->RemoveObserver(this); 294 notification_registrar_.RemoveAll(); 295 if (actor_) 296 actor_->Hide(); 297} 298 299std::string UserImageScreen::GetName() const { 300 return WizardController::kUserImageScreenName; 301} 302 303void UserImageScreen::OnActorDestroyed(UserImageScreenActor* actor) { 304 if (actor_ == actor) 305 actor_ = NULL; 306} 307 308void UserImageScreen::Observe(int type, 309 const content::NotificationSource& source, 310 const content::NotificationDetails& details) { 311 switch (type) { 312 case chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED: { 313 // We've got a new profile image. 314 profile_picture_data_url_ = webui::GetBitmapDataUrl( 315 *content::Details<const gfx::ImageSkia>(details).ptr()->bitmap()); 316 if (actor_) 317 actor_->SendProfileImage(profile_picture_data_url_); 318 break; 319 } 320 case chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED: { 321 // User has a default profile image or fetching profile image has failed. 322 profile_picture_absent_ = true; 323 if (actor_) 324 actor_->OnProfileImageAbsent(); 325 break; 326 } 327 case chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED: { 328 if (actor_) 329 actor_->SelectImage(GetUser()->image_index()); 330 break; 331 } 332 default: 333 NOTREACHED(); 334 } 335} 336 337bool UserImageScreen::profile_picture_absent() { 338 return profile_picture_absent_; 339} 340 341int UserImageScreen::selected_image() { 342 return selected_image_; 343} 344 345std::string UserImageScreen::profile_picture_data_url() { 346 return profile_picture_data_url_; 347} 348 349void UserImageScreen::ExitScreen() { 350 policy_registrar_.reset(); 351 sync_timer_.reset(); 352 if (UserImageSyncObserver* sync_observer = GetSyncObserver()) 353 sync_observer->RemoveObserver(this); 354 get_screen_observer()->OnExit(ScreenObserver::USER_IMAGE_SELECTED); 355} 356 357} // namespace chromeos 358