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