Home | History | Annotate | Download | only in screens
      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/screen_manager.h"
     23 #include "chrome/browser/chromeos/login/screens/screen_observer.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 "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.h"
     36 #include "components/user_manager/user_image/default_user_images.h"
     37 #include "components/user_manager/user_image/user_image.h"
     38 #include "components/user_manager/user_manager.h"
     39 #include "content/public/browser/browser_thread.h"
     40 #include "content/public/browser/notification_service.h"
     41 #include "policy/policy_constants.h"
     42 #include "third_party/skia/include/core/SkBitmap.h"
     43 #include "ui/base/webui/web_ui_util.h"
     44 #include "ui/gfx/image/image_skia.h"
     45 
     46 using content::BrowserThread;
     47 
     48 namespace chromeos {
     49 
     50 namespace {
     51 
     52 // Time histogram suffix for profile image download.
     53 const 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.
     57 const int kSyncTimeoutSeconds = 10;
     58 
     59 }  // namespace
     60 
     61 // static
     62 UserImageScreen* UserImageScreen::Get(ScreenManager* manager) {
     63   return static_cast<UserImageScreen*>(
     64       manager->GetScreen(WizardController::kUserImageScreenName));
     65 }
     66 
     67 UserImageScreen::UserImageScreen(ScreenObserver* screen_observer,
     68                                  UserImageScreenActor* actor)
     69     : WizardScreen(screen_observer),
     70       actor_(actor),
     71       accept_photo_after_decoding_(false),
     72       selected_image_(user_manager::User::USER_IMAGE_INVALID),
     73       profile_picture_data_url_(url::kAboutBlankURL),
     74       profile_picture_absent_(false),
     75       is_screen_ready_(false),
     76       user_has_selected_image_(false) {
     77   actor_->SetDelegate(this);
     78   notification_registrar_.Add(this,
     79                               chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED,
     80                               content::NotificationService::AllSources());
     81   notification_registrar_.Add(this,
     82                               chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED,
     83                               content::NotificationService::AllSources());
     84   notification_registrar_.Add(this,
     85                               chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED,
     86                               content::NotificationService::AllSources());
     87 }
     88 
     89 UserImageScreen::~UserImageScreen() {
     90   CameraPresenceNotifier::GetInstance()->RemoveObserver(this);
     91   if (actor_)
     92     actor_->SetDelegate(NULL);
     93   if (image_decoder_.get())
     94     image_decoder_->set_delegate(NULL);
     95 }
     96 
     97 void UserImageScreen::OnScreenReady() {
     98   is_screen_ready_ = true;
     99   if (!IsWaitingForSync())
    100     HideCurtain();
    101 }
    102 
    103 void UserImageScreen::OnPhotoTaken(const std::string& raw_data) {
    104   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    105   user_photo_ = gfx::ImageSkia();
    106   if (image_decoder_.get())
    107     image_decoder_->set_delegate(NULL);
    108   image_decoder_ = new ImageDecoder(this, raw_data,
    109                                     ImageDecoder::DEFAULT_CODEC);
    110   scoped_refptr<base::MessageLoopProxy> task_runner =
    111       BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
    112   image_decoder_->Start(task_runner);
    113 }
    114 
    115 void UserImageScreen::OnCameraPresenceCheckDone(bool is_camera_present) {
    116   if (actor_)
    117     actor_->SetCameraPresent(is_camera_present);
    118 }
    119 
    120 void UserImageScreen::HideCurtain() {
    121   if (actor_)
    122     actor_->HideCurtain();
    123 }
    124 
    125 void UserImageScreen::OnImageDecoded(const ImageDecoder* decoder,
    126                                      const SkBitmap& decoded_image) {
    127   DCHECK_EQ(image_decoder_.get(), decoder);
    128   user_photo_ = gfx::ImageSkia::CreateFrom1xBitmap(decoded_image);
    129   if (accept_photo_after_decoding_)
    130     OnImageAccepted();
    131 }
    132 
    133 void UserImageScreen::OnDecodeImageFailed(const ImageDecoder* decoder) {
    134   NOTREACHED() << "Failed to decode PNG image from WebUI";
    135 }
    136 
    137 void UserImageScreen::OnInitialSync(bool local_image_updated) {
    138   DCHECK(sync_timer_);
    139   if (!local_image_updated) {
    140     sync_timer_.reset();
    141     GetSyncObserver()->RemoveObserver(this);
    142     if (is_screen_ready_)
    143       HideCurtain();
    144     return;
    145   }
    146   ExitScreen();
    147 }
    148 
    149 void UserImageScreen::OnSyncTimeout() {
    150   sync_timer_.reset();
    151   GetSyncObserver()->RemoveObserver(this);
    152   if (is_screen_ready_)
    153     HideCurtain();
    154 }
    155 
    156 bool UserImageScreen::IsWaitingForSync() const {
    157   return sync_timer_.get() && sync_timer_->IsRunning();
    158 }
    159 
    160 void UserImageScreen::OnUserImagePolicyChanged(const base::Value* previous,
    161                                                const base::Value* current) {
    162   if (current) {
    163     base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE,
    164                                                   policy_registrar_.release());
    165     ExitScreen();
    166   }
    167 }
    168 
    169 void UserImageScreen::OnImageSelected(const std::string& image_type,
    170                                       const std::string& image_url,
    171                                       bool is_user_selection) {
    172   if (is_user_selection) {
    173     user_has_selected_image_ = true;
    174   }
    175   if (image_url.empty())
    176     return;
    177   int user_image_index = user_manager::User::USER_IMAGE_INVALID;
    178   if (image_type == "default" &&
    179       user_manager::IsDefaultImageUrl(image_url, &user_image_index)) {
    180     selected_image_ = user_image_index;
    181   } else if (image_type == "camera") {
    182     selected_image_ = user_manager::User::USER_IMAGE_EXTERNAL;
    183   } else if (image_type == "profile") {
    184     selected_image_ = user_manager::User::USER_IMAGE_PROFILE;
    185   } else {
    186     NOTREACHED() << "Unexpected image type: " << image_type;
    187   }
    188 }
    189 
    190 void UserImageScreen::OnImageAccepted() {
    191   UserImageManager* image_manager = GetUserImageManager();
    192   int uma_index = 0;
    193   switch (selected_image_) {
    194     case user_manager::User::USER_IMAGE_EXTERNAL:
    195       // Photo decoding may not have been finished yet.
    196       if (user_photo_.isNull()) {
    197         accept_photo_after_decoding_ = true;
    198         return;
    199       }
    200       image_manager->SaveUserImage(
    201           user_manager::UserImage::CreateAndEncode(user_photo_));
    202       uma_index = user_manager::kHistogramImageFromCamera;
    203       break;
    204     case user_manager::User::USER_IMAGE_PROFILE:
    205       image_manager->SaveUserImageFromProfileImage();
    206       uma_index = user_manager::kHistogramImageFromProfile;
    207       break;
    208     default:
    209       DCHECK(selected_image_ >= 0 &&
    210              selected_image_ < user_manager::kDefaultImagesCount);
    211       image_manager->SaveUserDefaultImageIndex(selected_image_);
    212       uma_index = user_manager::GetDefaultImageHistogramValue(selected_image_);
    213       break;
    214   }
    215   if (user_has_selected_image_) {
    216     UMA_HISTOGRAM_ENUMERATION("UserImage.FirstTimeChoice",
    217                               uma_index,
    218                               user_manager::kHistogramImagesCount);
    219   }
    220   ExitScreen();
    221 }
    222 
    223 
    224 void UserImageScreen::PrepareToShow() {
    225   if (actor_)
    226     actor_->PrepareToShow();
    227 }
    228 
    229 const user_manager::User* UserImageScreen::GetUser() {
    230   return user_manager::UserManager::Get()->GetLoggedInUser();
    231 }
    232 
    233 UserImageManager* UserImageScreen::GetUserImageManager() {
    234   return ChromeUserManager::Get()->GetUserImageManager(GetUser()->email());
    235 }
    236 
    237 UserImageSyncObserver* UserImageScreen::GetSyncObserver() {
    238   return GetUserImageManager()->GetSyncObserver();
    239 }
    240 
    241 void UserImageScreen::Show() {
    242   if (!actor_)
    243     return;
    244 
    245   DCHECK(!policy_registrar_);
    246   if (Profile* profile = ProfileHelper::Get()->GetProfileByUser(GetUser())) {
    247     policy::PolicyService* policy_service =
    248         policy::ProfilePolicyConnectorFactory::GetForProfile(profile)->
    249             policy_service();
    250     if (policy_service->GetPolicies(
    251             policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME,
    252                                     std::string()))
    253             .Get(policy::key::kUserAvatarImage)) {
    254       // If the user image is managed by policy, skip the screen because the
    255       // user is not allowed to override a policy-set image.
    256       ExitScreen();
    257       return;
    258     }
    259 
    260     // Listen for policy changes. If at any point, the user image becomes
    261     // managed by policy, the screen will close.
    262     policy_registrar_.reset(new policy::PolicyChangeRegistrar(
    263         policy_service,
    264         policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, std::string())));
    265     policy_registrar_->Observe(
    266         policy::key::kUserAvatarImage,
    267         base::Bind(&UserImageScreen::OnUserImagePolicyChanged,
    268                    base::Unretained(this)));
    269   } else {
    270     NOTREACHED();
    271   }
    272 
    273   if (GetUser()->CanSyncImage()) {
    274     if (UserImageSyncObserver* sync_observer = GetSyncObserver()) {
    275       // We have synced image already.
    276       if (sync_observer->is_synced()) {
    277         ExitScreen();
    278         return;
    279       }
    280       sync_observer->AddObserver(this);
    281       sync_timer_.reset(new base::Timer(
    282             FROM_HERE,
    283             base::TimeDelta::FromSeconds(kSyncTimeoutSeconds),
    284             base::Bind(&UserImageScreen::OnSyncTimeout, base::Unretained(this)),
    285             false));
    286       sync_timer_->Reset();
    287     }
    288   }
    289   CameraPresenceNotifier::GetInstance()->AddObserver(this);
    290   actor_->Show();
    291 
    292   selected_image_ = GetUser()->image_index();
    293   actor_->SelectImage(selected_image_);
    294 
    295   // Start fetching the profile image.
    296   GetUserImageManager()->DownloadProfileImage(kProfileDownloadReason);
    297 }
    298 
    299 void UserImageScreen::Hide() {
    300   CameraPresenceNotifier::GetInstance()->RemoveObserver(this);
    301   notification_registrar_.RemoveAll();
    302   if (actor_)
    303     actor_->Hide();
    304 }
    305 
    306 std::string UserImageScreen::GetName() const {
    307   return WizardController::kUserImageScreenName;
    308 }
    309 
    310 void UserImageScreen::OnActorDestroyed(UserImageScreenActor* actor) {
    311   if (actor_ == actor)
    312     actor_ = NULL;
    313 }
    314 
    315 void UserImageScreen::Observe(int type,
    316                               const content::NotificationSource& source,
    317                               const content::NotificationDetails& details) {
    318   switch (type) {
    319     case chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED: {
    320       // We've got a new profile image.
    321       profile_picture_data_url_ = webui::GetBitmapDataUrl(
    322           *content::Details<const gfx::ImageSkia>(details).ptr()->bitmap());
    323       if (actor_)
    324         actor_->SendProfileImage(profile_picture_data_url_);
    325       break;
    326     }
    327     case chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED: {
    328       // User has a default profile image or fetching profile image has failed.
    329       profile_picture_absent_ = true;
    330       if (actor_)
    331         actor_->OnProfileImageAbsent();
    332       break;
    333     }
    334     case chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED: {
    335       if (actor_)
    336         actor_->SelectImage(GetUser()->image_index());
    337       break;
    338     }
    339     default:
    340       NOTREACHED();
    341   }
    342 }
    343 
    344 bool UserImageScreen::profile_picture_absent() {
    345   return profile_picture_absent_;
    346 }
    347 
    348 int UserImageScreen::selected_image() {
    349   return selected_image_;
    350 }
    351 
    352 std::string UserImageScreen::profile_picture_data_url() {
    353   return profile_picture_data_url_;
    354 }
    355 
    356 void UserImageScreen::ExitScreen() {
    357   policy_registrar_.reset();
    358   sync_timer_.reset();
    359   if (UserImageSyncObserver* sync_observer = GetSyncObserver())
    360     sync_observer->RemoveObserver(this);
    361   get_screen_observer()->OnExit(ScreenObserver::USER_IMAGE_SELECTED);
    362 }
    363 
    364 }  // namespace chromeos
    365