user_image_screen.cc revision 3551c9c881056c480085172ff9840cab31610854
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 "base/compiler_specific.h"
8#include "base/metrics/histogram.h"
9#include "base/timer/timer.h"
10#include "chrome/browser/chrome_notification_types.h"
11#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
12#include "chrome/browser/chromeos/camera_detector.h"
13#include "chrome/browser/chromeos/login/default_user_images.h"
14#include "chrome/browser/chromeos/login/login_utils.h"
15#include "chrome/browser/chromeos/login/screens/screen_observer.h"
16#include "chrome/browser/chromeos/login/user_image.h"
17#include "chrome/browser/chromeos/login/user_image_manager.h"
18#include "chrome/browser/chromeos/login/user_manager.h"
19#include "chrome/browser/chromeos/login/wizard_controller.h"
20#include "chrome/common/url_constants.h"
21#include "content/public/browser/browser_thread.h"
22#include "content/public/browser/notification_service.h"
23#include "grit/generated_resources.h"
24#include "grit/theme_resources.h"
25#include "third_party/skia/include/core/SkBitmap.h"
26#include "ui/base/l10n/l10n_util.h"
27#include "ui/base/resource/resource_bundle.h"
28#include "ui/gfx/image/image_skia.h"
29#include "ui/webui/web_ui_util.h"
30
31using content::BrowserThread;
32
33namespace chromeos {
34
35namespace {
36
37// Time histogram suffix for profile image download.
38const char kProfileDownloadReason[] = "OOBE";
39
40// Maximum ammount of time to wait for the user image to sync.
41// The screen is shown iff sync failed or time limit exceeded.
42const int kSyncTimeoutSeconds = 10;
43
44}  // namespace
45
46UserImageScreen::UserImageScreen(ScreenObserver* screen_observer,
47                                 UserImageScreenActor* actor)
48    : WizardScreen(screen_observer),
49      actor_(actor),
50      weak_factory_(this),
51      accept_photo_after_decoding_(false),
52      selected_image_(User::kInvalidImageIndex),
53      profile_picture_enabled_(false),
54      profile_picture_data_url_(content::kAboutBlankURL),
55      profile_picture_absent_(false),
56      is_screen_ready_(false),
57      user_has_selected_image_(false) {
58  actor_->SetDelegate(this);
59  SetProfilePictureEnabled(true);
60  registrar_.Add(this, chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED,
61      content::NotificationService::AllSources());
62}
63
64UserImageScreen::~UserImageScreen() {
65  if (actor_)
66    actor_->SetDelegate(NULL);
67  if (image_decoder_.get())
68    image_decoder_->set_delegate(NULL);
69}
70
71void UserImageScreen::OnScreenReady() {
72  is_screen_ready_ = true;
73  if (actor_ && !IsWaitingForSync())
74    actor_->HideCurtain();
75}
76
77void UserImageScreen::OnPhotoTaken(const std::string& raw_data) {
78  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
79  user_photo_ = gfx::ImageSkia();
80  if (image_decoder_.get())
81    image_decoder_->set_delegate(NULL);
82  image_decoder_ = new ImageDecoder(this, raw_data,
83                                    ImageDecoder::DEFAULT_CODEC);
84  scoped_refptr<base::MessageLoopProxy> task_runner =
85      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
86  image_decoder_->Start(task_runner);
87}
88
89void UserImageScreen::CheckCameraPresence() {
90  CameraDetector::StartPresenceCheck(
91      base::Bind(&UserImageScreen::OnCameraPresenceCheckDone,
92                 weak_factory_.GetWeakPtr()));
93}
94
95void UserImageScreen::OnCameraPresenceCheckDone() {
96  if (actor_) {
97    actor_->SetCameraPresent(
98        CameraDetector::camera_presence() == CameraDetector::kCameraPresent);
99  }
100}
101
102
103void UserImageScreen::OnImageDecoded(const ImageDecoder* decoder,
104                                     const SkBitmap& decoded_image) {
105  DCHECK_EQ(image_decoder_.get(), decoder);
106  user_photo_ = gfx::ImageSkia::CreateFrom1xBitmap(decoded_image);
107  if (accept_photo_after_decoding_)
108    OnImageAccepted();
109}
110
111void UserImageScreen::OnDecodeImageFailed(const ImageDecoder* decoder) {
112  NOTREACHED() << "Failed to decode PNG image from WebUI";
113}
114
115void UserImageScreen::OnInitialSync(bool local_image_updated) {
116  DCHECK(sync_timer_.get());
117  sync_timer_->Stop();
118  sync_timer_.reset();
119  UserManager::Get()->GetUserImageManager()->GetSyncObserver()->
120      RemoveObserver(this);
121  if (!local_image_updated) {
122    if (is_screen_ready_ && actor_)
123      actor_->HideCurtain();
124    return;
125  }
126  get_screen_observer()->OnExit(ScreenObserver::USER_IMAGE_SELECTED);
127}
128
129void UserImageScreen::OnSyncTimeout() {
130  sync_timer_.reset();
131  UserManager::Get()->GetUserImageManager()->GetSyncObserver()->
132      RemoveObserver(this);
133  if (is_screen_ready_ && actor_)
134    actor_->HideCurtain();
135}
136
137bool UserImageScreen::IsWaitingForSync() const {
138  return sync_timer_.get() && sync_timer_->IsRunning();
139}
140
141void UserImageScreen::OnImageSelected(const std::string& image_type,
142                                      const std::string& image_url,
143                                      bool is_user_selection) {
144  if (is_user_selection) {
145    user_has_selected_image_ = true;
146  }
147  if (image_url.empty())
148    return;
149  int user_image_index = User::kInvalidImageIndex;
150  if (image_type == "default" &&
151      IsDefaultImageUrl(image_url, &user_image_index)) {
152    selected_image_ = user_image_index;
153  } else if (image_type == "camera") {
154    selected_image_ = User::kExternalImageIndex;
155  } else if (image_type == "profile") {
156    selected_image_ = User::kProfileImageIndex;
157  } else {
158    NOTREACHED() << "Unexpected image type: " << image_type;
159  }
160}
161
162void UserImageScreen::OnImageAccepted() {
163  UserManager* user_manager = UserManager::Get();
164  UserImageManager* image_manager = user_manager->GetUserImageManager();
165  std::string user_id = GetUser()->email();
166  int uma_index = 0;
167  switch (selected_image_) {
168    case User::kExternalImageIndex:
169      // Photo decoding may not have been finished yet.
170      if (user_photo_.isNull()) {
171        accept_photo_after_decoding_ = true;
172        return;
173      }
174      image_manager->
175          SaveUserImage(user_id, UserImage::CreateAndEncode(user_photo_));
176      uma_index = kHistogramImageFromCamera;
177      break;
178    case User::kProfileImageIndex:
179      image_manager->SaveUserImageFromProfileImage(user_id);
180      uma_index = kHistogramImageFromProfile;
181      break;
182    default:
183      DCHECK(selected_image_ >= 0 && selected_image_ < kDefaultImagesCount);
184      image_manager->SaveUserDefaultImageIndex(user_id, selected_image_);
185      uma_index = GetDefaultImageHistogramValue(selected_image_);
186      break;
187  }
188  if (user_has_selected_image_) {
189    UMA_HISTOGRAM_ENUMERATION("UserImage.FirstTimeChoice",
190                              uma_index,
191                              kHistogramImagesCount);
192  }
193  get_screen_observer()->OnExit(ScreenObserver::USER_IMAGE_SELECTED);
194}
195
196
197void UserImageScreen::SetProfilePictureEnabled(bool profile_picture_enabled) {
198  if (profile_picture_enabled_ == profile_picture_enabled)
199    return;
200  profile_picture_enabled_ = profile_picture_enabled;
201  if (profile_picture_enabled) {
202    registrar_.Add(this, chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED,
203        content::NotificationService::AllSources());
204    registrar_.Add(this, chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED,
205        content::NotificationService::AllSources());
206  } else {
207    registrar_.Remove(this, chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED,
208        content::NotificationService::AllSources());
209    registrar_.Remove(this, chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED,
210        content::NotificationService::AllSources());
211  }
212  if (actor_)
213    actor_->SetProfilePictureEnabled(profile_picture_enabled);
214}
215
216void UserImageScreen::SetUserID(const std::string& user_id) {
217  DCHECK(!user_id.empty());
218  user_id_ = user_id;
219}
220
221void UserImageScreen::PrepareToShow() {
222  if (actor_)
223    actor_->PrepareToShow();
224}
225
226const User* UserImageScreen::GetUser() {
227  if (user_id_.empty())
228    return UserManager::Get()->GetLoggedInUser();
229  const User* user = UserManager::Get()->FindUser(user_id_);
230  DCHECK(user);
231  return user;
232}
233
234void UserImageScreen::Show() {
235  if (!actor_)
236    return;
237  if (GetUser()->CanSyncImage()) {
238    if (UserImageSyncObserver* sync_observer =
239          UserManager::Get()->GetUserImageManager()->GetSyncObserver()) {
240      // We have synced image already.
241      if (sync_observer->is_synced()) {
242        get_screen_observer()->OnExit(ScreenObserver::USER_IMAGE_SELECTED);
243        return;
244      }
245      sync_observer->AddObserver(this);
246      sync_timer_.reset(new base::Timer(
247            FROM_HERE,
248            base::TimeDelta::FromSeconds(kSyncTimeoutSeconds),
249            base::Bind(&UserImageScreen::OnSyncTimeout, base::Unretained(this)),
250            false));
251      sync_timer_->Reset();
252    }
253  }
254  actor_->Show();
255  actor_->SetProfilePictureEnabled(profile_picture_enabled_);
256
257  selected_image_ = GetUser()->image_index();
258  actor_->SelectImage(selected_image_);
259
260  if (profile_picture_enabled_) {
261    // Start fetching the profile image.
262    UserManager::Get()->GetUserImageManager()->
263        DownloadProfileImage(kProfileDownloadReason);
264  }
265
266  AccessibilityManager::Get()->MaybeSpeak(
267      l10n_util::GetStringUTF8(IDS_OPTIONS_CHANGE_PICTURE_DIALOG_TEXT));
268}
269
270void UserImageScreen::Hide() {
271  if (actor_)
272    actor_->Hide();
273}
274
275std::string UserImageScreen::GetName() const {
276  return WizardController::kUserImageScreenName;
277}
278
279void UserImageScreen::OnActorDestroyed(UserImageScreenActor* actor) {
280  if (actor_ == actor)
281    actor_ = NULL;
282}
283
284void UserImageScreen::Observe(int type,
285                              const content::NotificationSource& source,
286                              const content::NotificationDetails& details) {
287  DCHECK(profile_picture_enabled_);
288  switch (type) {
289    case chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED: {
290      // We've got a new profile image.
291      profile_picture_data_url_ = webui::GetBitmapDataUrl(
292          *content::Details<const gfx::ImageSkia>(details).ptr()->bitmap());
293      if (actor_)
294        actor_->SendProfileImage(profile_picture_data_url_);
295      break;
296    }
297    case chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED: {
298      // User has a default profile image or fetching profile image has failed.
299      profile_picture_absent_ = true;
300      if (actor_)
301        actor_->OnProfileImageAbsent();
302      break;
303    }
304    case chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED: {
305      if (actor_)
306        actor_->SelectImage(GetUser()->image_index());
307      break;
308    }
309    default:
310      NOTREACHED();
311  }
312}
313
314bool UserImageScreen::profile_picture_absent() {
315  return profile_picture_absent_;
316}
317
318int UserImageScreen::selected_image() {
319  return selected_image_;
320}
321
322std::string UserImageScreen::profile_picture_data_url() {
323  return profile_picture_data_url_;
324}
325
326}  // namespace chromeos
327