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