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.
5 #include "chrome/browser/chromeos/login/screens/user_image_screen.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_detector.h"
21 #include "chrome/browser/chromeos/login/default_user_images.h"
22 #include "chrome/browser/chromeos/login/login_utils.h"
23 #include "chrome/browser/chromeos/login/screens/screen_observer.h"
24 #include "chrome/browser/chromeos/login/user_image.h"
25 #include "chrome/browser/chromeos/login/user_image_manager.h"
26 #include "chrome/browser/chromeos/login/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"
46 using content::BrowserThread
;
52 // Time histogram suffix for profile image download.
53 const char kProfileDownloadReason
[] = "OOBE";
55 // Maximum ammount of time to wait for the user image to sync.
56 // The screen is shown iff sync failed or time limit exceeded.
57 const int kSyncTimeoutSeconds
= 10;
61 UserImageScreen::UserImageScreen(ScreenObserver
* screen_observer
,
62 UserImageScreenActor
* actor
)
63 : WizardScreen(screen_observer
),
66 accept_photo_after_decoding_(false),
67 selected_image_(User::kInvalidImageIndex
),
68 profile_picture_enabled_(false),
69 profile_picture_data_url_(content::kAboutBlankURL
),
70 profile_picture_absent_(false),
71 is_screen_ready_(false),
72 user_has_selected_image_(false),
73 was_camera_present_(false) {
74 actor_
->SetDelegate(this);
75 SetProfilePictureEnabled(true);
76 notification_registrar_
.Add(this,
77 chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED
,
78 content::NotificationService::AllSources());
81 UserImageScreen::~UserImageScreen() {
83 actor_
->SetDelegate(NULL
);
84 if (image_decoder_
.get())
85 image_decoder_
->set_delegate(NULL
);
88 void UserImageScreen::OnScreenReady() {
89 is_screen_ready_
= true;
90 if (!IsWaitingForSync())
94 void 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
);
106 void UserImageScreen::CheckCameraPresence() {
107 CameraDetector::StartPresenceCheck(
108 base::Bind(&UserImageScreen::OnCameraPresenceCheckDone
,
109 weak_factory_
.GetWeakPtr()));
112 void UserImageScreen::OnCameraPresenceCheckDone() {
113 bool is_camera_present
= CameraDetector::camera_presence() ==
114 CameraDetector::kCameraPresent
;
116 if (is_camera_present
!= was_camera_present_
) {
117 actor_
->SetCameraPresent(is_camera_present
);
118 was_camera_present_
= is_camera_present
;
123 void UserImageScreen::HideCurtain() {
125 actor_
->HideCurtain();
128 void UserImageScreen::OnImageDecoded(const ImageDecoder
* decoder
,
129 const SkBitmap
& decoded_image
) {
130 DCHECK_EQ(image_decoder_
.get(), decoder
);
131 user_photo_
= gfx::ImageSkia::CreateFrom1xBitmap(decoded_image
);
132 if (accept_photo_after_decoding_
)
136 void UserImageScreen::OnDecodeImageFailed(const ImageDecoder
* decoder
) {
137 NOTREACHED() << "Failed to decode PNG image from WebUI";
140 void UserImageScreen::OnInitialSync(bool local_image_updated
) {
142 if (!local_image_updated
) {
144 GetSyncObserver()->RemoveObserver(this);
145 if (is_screen_ready_
)
152 void UserImageScreen::OnSyncTimeout() {
154 GetSyncObserver()->RemoveObserver(this);
155 if (is_screen_ready_
)
159 bool UserImageScreen::IsWaitingForSync() const {
160 return sync_timer_
.get() && sync_timer_
->IsRunning();
163 void UserImageScreen::OnUserImagePolicyChanged(const base::Value
* previous
,
164 const base::Value
* current
) {
166 base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE
,
167 policy_registrar_
.release());
172 void UserImageScreen::OnImageSelected(const std::string
& image_type
,
173 const std::string
& image_url
,
174 bool is_user_selection
) {
175 if (is_user_selection
) {
176 user_has_selected_image_
= true;
178 if (image_url
.empty())
180 int user_image_index
= User::kInvalidImageIndex
;
181 if (image_type
== "default" &&
182 IsDefaultImageUrl(image_url
, &user_image_index
)) {
183 selected_image_
= user_image_index
;
184 } else if (image_type
== "camera") {
185 selected_image_
= User::kExternalImageIndex
;
186 } else if (image_type
== "profile") {
187 selected_image_
= User::kProfileImageIndex
;
189 NOTREACHED() << "Unexpected image type: " << image_type
;
193 void UserImageScreen::OnImageAccepted() {
194 UserImageManager
* image_manager
= GetUserImageManager();
196 switch (selected_image_
) {
197 case User::kExternalImageIndex
:
198 // Photo decoding may not have been finished yet.
199 if (user_photo_
.isNull()) {
200 accept_photo_after_decoding_
= true;
203 image_manager
->SaveUserImage(UserImage::CreateAndEncode(user_photo_
));
204 uma_index
= kHistogramImageFromCamera
;
206 case User::kProfileImageIndex
:
207 image_manager
->SaveUserImageFromProfileImage();
208 uma_index
= kHistogramImageFromProfile
;
211 DCHECK(selected_image_
>= 0 && selected_image_
< kDefaultImagesCount
);
212 image_manager
->SaveUserDefaultImageIndex(selected_image_
);
213 uma_index
= GetDefaultImageHistogramValue(selected_image_
);
216 if (user_has_selected_image_
) {
217 UMA_HISTOGRAM_ENUMERATION("UserImage.FirstTimeChoice",
219 kHistogramImagesCount
);
225 void UserImageScreen::SetProfilePictureEnabled(bool profile_picture_enabled
) {
226 if (profile_picture_enabled_
== profile_picture_enabled
)
228 profile_picture_enabled_
= profile_picture_enabled
;
229 if (profile_picture_enabled
) {
230 notification_registrar_
.Add(this,
231 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED
,
232 content::NotificationService::AllSources());
233 notification_registrar_
.Add(
235 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED
,
236 content::NotificationService::AllSources());
238 notification_registrar_
.Remove(this,
239 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED
,
240 content::NotificationService::AllSources());
241 notification_registrar_
.Remove(
243 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED
,
244 content::NotificationService::AllSources());
247 actor_
->SetProfilePictureEnabled(profile_picture_enabled
);
250 void UserImageScreen::SetUserID(const std::string
& user_id
) {
251 DCHECK(!user_id
.empty());
255 void UserImageScreen::PrepareToShow() {
257 actor_
->PrepareToShow();
260 const User
* UserImageScreen::GetUser() {
261 if (user_id_
.empty())
262 return UserManager::Get()->GetLoggedInUser();
263 return UserManager::Get()->FindUser(user_id_
);
266 UserImageManager
* UserImageScreen::GetUserImageManager() {
267 return UserManager::Get()->GetUserImageManager(GetUser()->email());
270 UserImageSyncObserver
* UserImageScreen::GetSyncObserver() {
271 return GetUserImageManager()->GetSyncObserver();
274 void UserImageScreen::Show() {
278 DCHECK(!policy_registrar_
);
279 Profile
* profile
= UserManager::Get()->GetProfileByUser(GetUser());
281 policy::PolicyService
* policy_service
=
282 policy::ProfilePolicyConnectorFactory::GetForProfile(profile
)->
284 if (policy_service
->GetPolicies(
285 policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME
,
287 .Get(policy::key::kUserAvatarImage
)) {
288 // If the user image is managed by policy, skip the screen because the
289 // user is not allowed to override a policy-set image.
294 // Listen for policy changes. If at any point, the user image becomes
295 // managed by policy, the screen will close.
296 policy_registrar_
.reset(new policy::PolicyChangeRegistrar(
298 policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME
, std::string())));
299 policy_registrar_
->Observe(
300 policy::key::kUserAvatarImage
,
301 base::Bind(&UserImageScreen::OnUserImagePolicyChanged
,
302 base::Unretained(this)));
307 if (GetUser()->CanSyncImage()) {
308 if (UserImageSyncObserver
* sync_observer
= GetSyncObserver()) {
309 // We have synced image already.
310 if (sync_observer
->is_synced()) {
314 sync_observer
->AddObserver(this);
315 sync_timer_
.reset(new base::Timer(
317 base::TimeDelta::FromSeconds(kSyncTimeoutSeconds
),
318 base::Bind(&UserImageScreen::OnSyncTimeout
, base::Unretained(this)),
320 sync_timer_
->Reset();
324 actor_
->SetProfilePictureEnabled(profile_picture_enabled_
);
326 selected_image_
= GetUser()->image_index();
327 actor_
->SelectImage(selected_image_
);
329 if (profile_picture_enabled_
) {
330 // Start fetching the profile image.
331 GetUserImageManager()->DownloadProfileImage(kProfileDownloadReason
);
335 void UserImageScreen::Hide() {
340 std::string
UserImageScreen::GetName() const {
341 return WizardController::kUserImageScreenName
;
344 void UserImageScreen::OnActorDestroyed(UserImageScreenActor
* actor
) {
349 void UserImageScreen::Observe(int type
,
350 const content::NotificationSource
& source
,
351 const content::NotificationDetails
& details
) {
352 DCHECK(profile_picture_enabled_
);
354 case chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED
: {
355 // We've got a new profile image.
356 profile_picture_data_url_
= webui::GetBitmapDataUrl(
357 *content::Details
<const gfx::ImageSkia
>(details
).ptr()->bitmap());
359 actor_
->SendProfileImage(profile_picture_data_url_
);
362 case chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED
: {
363 // User has a default profile image or fetching profile image has failed.
364 profile_picture_absent_
= true;
366 actor_
->OnProfileImageAbsent();
369 case chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED
: {
371 actor_
->SelectImage(GetUser()->image_index());
379 bool UserImageScreen::profile_picture_absent() {
380 return profile_picture_absent_
;
383 int UserImageScreen::selected_image() {
384 return selected_image_
;
387 std::string
UserImageScreen::profile_picture_data_url() {
388 return profile_picture_data_url_
;
391 void UserImageScreen::ExitScreen() {
392 policy_registrar_
.reset();
394 if (UserImageSyncObserver
* sync_observer
= GetSyncObserver())
395 sync_observer
->RemoveObserver(this);
396 get_screen_observer()->OnExit(ScreenObserver::USER_IMAGE_SELECTED
);
399 } // namespace chromeos