user_image_screen.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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_(content::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