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