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/metrics/histogram.h"
15 #include "base/thread_task_runner_handle.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/screen_manager.h"
22 #include "chrome/browser/chromeos/login/screens/base_screen_delegate.h"
23 #include "chrome/browser/chromeos/login/screens/user_image_view.h"
24 #include "chrome/browser/chromeos/login/users/avatar/user_image_manager.h"
25 #include "chrome/browser/chromeos/login/users/chrome_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 "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"
45 using content::BrowserThread
;
51 // Time histogram suffix for profile image download.
52 const char kProfileDownloadReason
[] = "OOBE";
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.
56 const int kSyncTimeoutSeconds
= 10;
61 UserImageScreen
* UserImageScreen::Get(ScreenManager
* manager
) {
62 return static_cast<UserImageScreen
*>(
63 manager
->GetScreen(WizardController::kUserImageScreenName
));
66 UserImageScreen::UserImageScreen(BaseScreenDelegate
* base_screen_delegate
,
68 : UserImageModel(base_screen_delegate
),
70 accept_photo_after_decoding_(false),
71 selected_image_(user_manager::User::USER_IMAGE_INVALID
),
72 is_screen_ready_(false),
73 user_has_selected_image_(false) {
76 notification_registrar_
.Add(this,
77 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED
,
78 content::NotificationService::AllSources());
79 notification_registrar_
.Add(this,
80 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED
,
81 content::NotificationService::AllSources());
82 notification_registrar_
.Add(this,
83 chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED
,
84 content::NotificationService::AllSources());
85 GetContextEditor().SetString(kContextKeyProfilePictureDataURL
, std::string());
88 UserImageScreen::~UserImageScreen() {
89 CameraPresenceNotifier::GetInstance()->RemoveObserver(this);
94 void UserImageScreen::OnScreenReady() {
95 is_screen_ready_
= true;
96 if (!IsWaitingForSync())
100 void UserImageScreen::OnPhotoTaken(const std::string
& raw_data
) {
101 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
102 user_photo_
= gfx::ImageSkia();
103 ImageDecoder::Cancel(this);
104 ImageDecoder::Start(this, raw_data
);
107 void UserImageScreen::OnCameraPresenceCheckDone(bool is_camera_present
) {
108 GetContextEditor().SetBoolean(kContextKeyIsCameraPresent
, is_camera_present
);
111 void UserImageScreen::HideCurtain() {
112 // Skip user image selection for ephemeral users.
113 if (user_manager::UserManager::Get()->IsUserNonCryptohomeDataEphemeral(
114 GetUser()->GetUserID())) {
118 view_
->HideCurtain();
121 void UserImageScreen::OnImageDecoded(const SkBitmap
& decoded_image
) {
122 user_photo_
= gfx::ImageSkia::CreateFrom1xBitmap(decoded_image
);
123 if (accept_photo_after_decoding_
)
127 void UserImageScreen::OnDecodeImageFailed() {
128 NOTREACHED() << "Failed to decode PNG image from WebUI";
131 void UserImageScreen::OnInitialSync(bool local_image_updated
) {
133 ReportSyncResult(SyncResult::SUCCEEDED
);
134 if (!local_image_updated
) {
136 GetSyncObserver()->RemoveObserver(this);
137 if (is_screen_ready_
)
144 void UserImageScreen::OnSyncTimeout() {
145 ReportSyncResult(SyncResult::TIMED_OUT
);
147 GetSyncObserver()->RemoveObserver(this);
148 if (is_screen_ready_
)
152 bool UserImageScreen::IsWaitingForSync() const {
153 return sync_timer_
.get() && sync_timer_
->IsRunning();
156 void UserImageScreen::OnUserImagePolicyChanged(const base::Value
* previous
,
157 const base::Value
* current
) {
159 base::ThreadTaskRunnerHandle::Get()->DeleteSoon(
160 FROM_HERE
, policy_registrar_
.release());
165 void UserImageScreen::OnImageSelected(const std::string
& image_type
,
166 const std::string
& image_url
,
167 bool is_user_selection
) {
168 if (is_user_selection
) {
169 user_has_selected_image_
= true;
171 if (image_url
.empty())
173 int user_image_index
= user_manager::User::USER_IMAGE_INVALID
;
174 if (image_type
== "default" &&
175 user_manager::IsDefaultImageUrl(image_url
, &user_image_index
)) {
176 selected_image_
= user_image_index
;
177 } else if (image_type
== "camera") {
178 selected_image_
= user_manager::User::USER_IMAGE_EXTERNAL
;
179 } else if (image_type
== "profile") {
180 selected_image_
= user_manager::User::USER_IMAGE_PROFILE
;
182 NOTREACHED() << "Unexpected image type: " << image_type
;
186 void UserImageScreen::OnImageAccepted() {
187 UserImageManager
* image_manager
= GetUserImageManager();
189 switch (selected_image_
) {
190 case user_manager::User::USER_IMAGE_EXTERNAL
:
191 // Photo decoding may not have been finished yet.
192 if (user_photo_
.isNull()) {
193 accept_photo_after_decoding_
= true;
196 image_manager
->SaveUserImage(
197 user_manager::UserImage::CreateAndEncode(user_photo_
));
198 uma_index
= user_manager::kHistogramImageFromCamera
;
200 case user_manager::User::USER_IMAGE_PROFILE
:
201 image_manager
->SaveUserImageFromProfileImage();
202 uma_index
= user_manager::kHistogramImageFromProfile
;
205 DCHECK(selected_image_
>= 0 &&
206 selected_image_
< user_manager::kDefaultImagesCount
);
207 image_manager
->SaveUserDefaultImageIndex(selected_image_
);
208 uma_index
= user_manager::GetDefaultImageHistogramValue(selected_image_
);
211 if (user_has_selected_image_
) {
212 UMA_HISTOGRAM_ENUMERATION("UserImage.FirstTimeChoice",
214 user_manager::kHistogramImagesCount
);
220 void UserImageScreen::PrepareToShow() {
222 view_
->PrepareToShow();
225 const user_manager::User
* UserImageScreen::GetUser() {
226 return user_manager::UserManager::Get()->GetLoggedInUser();
229 UserImageManager
* UserImageScreen::GetUserImageManager() {
230 return ChromeUserManager::Get()->GetUserImageManager(GetUser()->email());
233 UserImageSyncObserver
* UserImageScreen::GetSyncObserver() {
234 return GetUserImageManager()->GetSyncObserver();
237 void UserImageScreen::Show() {
241 DCHECK(!policy_registrar_
);
242 if (Profile
* profile
= ProfileHelper::Get()->GetProfileByUser(GetUser())) {
243 policy::PolicyService
* policy_service
=
244 policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile
)
246 if (policy_service
->GetPolicies(
247 policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME
,
249 .Get(policy::key::kUserAvatarImage
)) {
250 // If the user image is managed by policy, skip the screen because the
251 // user is not allowed to override a policy-set image.
256 // Listen for policy changes. If at any point, the user image becomes
257 // managed by policy, the screen will close.
258 policy_registrar_
.reset(new policy::PolicyChangeRegistrar(
260 policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME
, std::string())));
261 policy_registrar_
->Observe(
262 policy::key::kUserAvatarImage
,
263 base::Bind(&UserImageScreen::OnUserImagePolicyChanged
,
264 base::Unretained(this)));
269 if (GetUser()->CanSyncImage()) {
270 if (UserImageSyncObserver
* sync_observer
= GetSyncObserver()) {
271 sync_waiting_start_time_
= base::Time::Now();
272 // We have synced image already.
273 if (sync_observer
->is_synced()) {
274 ReportSyncResult(SyncResult::SUCCEEDED
);
278 sync_observer
->AddObserver(this);
279 sync_timer_
.reset(new base::Timer(
281 base::TimeDelta::FromSeconds(kSyncTimeoutSeconds
),
282 base::Bind(&UserImageScreen::OnSyncTimeout
, base::Unretained(this)),
284 sync_timer_
->Reset();
287 CameraPresenceNotifier::GetInstance()->AddObserver(this);
290 selected_image_
= GetUser()->image_index();
291 GetContextEditor().SetString(
292 kContextKeySelectedImageURL
,
293 user_manager::GetDefaultImageUrl(selected_image_
));
295 // Start fetching the profile image.
296 GetUserImageManager()->DownloadProfileImage(kProfileDownloadReason
);
299 void UserImageScreen::Hide() {
300 CameraPresenceNotifier::GetInstance()->RemoveObserver(this);
301 notification_registrar_
.RemoveAll();
302 policy_registrar_
.reset();
304 if (UserImageSyncObserver
* sync_observer
= GetSyncObserver())
305 sync_observer
->RemoveObserver(this);
310 void UserImageScreen::OnViewDestroyed(UserImageView
* view
) {
315 void UserImageScreen::Observe(int type
,
316 const content::NotificationSource
& source
,
317 const content::NotificationDetails
& details
) {
319 case chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED
: {
320 // We've got a new profile image.
321 GetContextEditor().SetString(
322 kContextKeyProfilePictureDataURL
,
323 webui::GetBitmapDataUrl(
324 *content::Details
<const gfx::ImageSkia
>(details
)
329 case chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED
: {
330 // User has a default profile image or fetching profile image has failed.
331 GetContextEditor().SetString(kContextKeyProfilePictureDataURL
,
335 case chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED
: {
336 GetContextEditor().SetString(
337 kContextKeySelectedImageURL
,
338 user_manager::GetDefaultImageUrl(GetUser()->image_index()));
346 void UserImageScreen::ExitScreen() {
347 policy_registrar_
.reset();
349 if (UserImageSyncObserver
* sync_observer
= GetSyncObserver())
350 sync_observer
->RemoveObserver(this);
351 Finish(BaseScreenDelegate::USER_IMAGE_SELECTED
);
354 void UserImageScreen::ReportSyncResult(SyncResult timed_out
) const {
355 base::TimeDelta duration
= base::Time::Now() - sync_waiting_start_time_
;
356 UMA_HISTOGRAM_TIMES("Login.NewUserPriorityPrefsSyncTime", duration
);
357 UMA_HISTOGRAM_ENUMERATION("Login.NewUserPriorityPrefsSyncResult",
358 static_cast<int>(timed_out
),
359 static_cast<int>(SyncResult::COUNT
));
362 } // namespace chromeos