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