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