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_detector.h"
     21 #include "chrome/browser/chromeos/login/default_user_images.h"
     22 #include "chrome/browser/chromeos/login/login_utils.h"
     23 #include "chrome/browser/chromeos/login/screens/screen_observer.h"
     24 #include "chrome/browser/chromeos/login/user_image.h"
     25 #include "chrome/browser/chromeos/login/user_image_manager.h"
     26 #include "chrome/browser/chromeos/login/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 
     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 ammount 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 UserImageScreen::UserImageScreen(ScreenObserver* screen_observer,
     62                                  UserImageScreenActor* actor)
     63     : WizardScreen(screen_observer),
     64       actor_(actor),
     65       weak_factory_(this),
     66       accept_photo_after_decoding_(false),
     67       selected_image_(User::kInvalidImageIndex),
     68       profile_picture_enabled_(false),
     69       profile_picture_data_url_(content::kAboutBlankURL),
     70       profile_picture_absent_(false),
     71       is_screen_ready_(false),
     72       user_has_selected_image_(false),
     73       was_camera_present_(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 
     81 UserImageScreen::~UserImageScreen() {
     82   if (actor_)
     83     actor_->SetDelegate(NULL);
     84   if (image_decoder_.get())
     85     image_decoder_->set_delegate(NULL);
     86 }
     87 
     88 void UserImageScreen::OnScreenReady() {
     89   is_screen_ready_ = true;
     90   if (!IsWaitingForSync())
     91     HideCurtain();
     92 }
     93 
     94 void 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 
    106 void UserImageScreen::CheckCameraPresence() {
    107   CameraDetector::StartPresenceCheck(
    108       base::Bind(&UserImageScreen::OnCameraPresenceCheckDone,
    109                  weak_factory_.GetWeakPtr()));
    110 }
    111 
    112 void UserImageScreen::OnCameraPresenceCheckDone() {
    113   bool is_camera_present = CameraDetector::camera_presence() ==
    114                            CameraDetector::kCameraPresent;
    115   if (actor_) {
    116     if (is_camera_present != was_camera_present_) {
    117       actor_->SetCameraPresent(is_camera_present);
    118       was_camera_present_ = is_camera_present;
    119     }
    120   }
    121 }
    122 
    123 void UserImageScreen::HideCurtain() {
    124   if (actor_)
    125     actor_->HideCurtain();
    126 }
    127 
    128 void UserImageScreen::OnImageDecoded(const ImageDecoder* decoder,
    129                                      const SkBitmap& decoded_image) {
    130   DCHECK_EQ(image_decoder_.get(), decoder);
    131   user_photo_ = gfx::ImageSkia::CreateFrom1xBitmap(decoded_image);
    132   if (accept_photo_after_decoding_)
    133     OnImageAccepted();
    134 }
    135 
    136 void UserImageScreen::OnDecodeImageFailed(const ImageDecoder* decoder) {
    137   NOTREACHED() << "Failed to decode PNG image from WebUI";
    138 }
    139 
    140 void UserImageScreen::OnInitialSync(bool local_image_updated) {
    141   DCHECK(sync_timer_);
    142   if (!local_image_updated) {
    143     sync_timer_.reset();
    144     UserManager::Get()->GetUserImageManager()->GetSyncObserver()->
    145         RemoveObserver(this);
    146     if (is_screen_ready_)
    147       HideCurtain();
    148     return;
    149   }
    150   ExitScreen();
    151 }
    152 
    153 void UserImageScreen::OnSyncTimeout() {
    154   sync_timer_.reset();
    155   UserManager::Get()->GetUserImageManager()->GetSyncObserver()->
    156       RemoveObserver(this);
    157   if (is_screen_ready_)
    158     HideCurtain();
    159 }
    160 
    161 bool UserImageScreen::IsWaitingForSync() const {
    162   return sync_timer_.get() && sync_timer_->IsRunning();
    163 }
    164 
    165 void UserImageScreen::OnUserImagePolicyChanged(const base::Value* previous,
    166                                                const base::Value* current) {
    167   if (current) {
    168     base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE,
    169                                                   policy_registrar_.release());
    170     ExitScreen();
    171   }
    172 }
    173 
    174 void UserImageScreen::OnImageSelected(const std::string& image_type,
    175                                       const std::string& image_url,
    176                                       bool is_user_selection) {
    177   if (is_user_selection) {
    178     user_has_selected_image_ = true;
    179   }
    180   if (image_url.empty())
    181     return;
    182   int user_image_index = User::kInvalidImageIndex;
    183   if (image_type == "default" &&
    184       IsDefaultImageUrl(image_url, &user_image_index)) {
    185     selected_image_ = user_image_index;
    186   } else if (image_type == "camera") {
    187     selected_image_ = User::kExternalImageIndex;
    188   } else if (image_type == "profile") {
    189     selected_image_ = User::kProfileImageIndex;
    190   } else {
    191     NOTREACHED() << "Unexpected image type: " << image_type;
    192   }
    193 }
    194 
    195 void UserImageScreen::OnImageAccepted() {
    196   UserManager* user_manager = UserManager::Get();
    197   UserImageManager* image_manager = user_manager->GetUserImageManager();
    198   std::string user_id = GetUser()->email();
    199   int uma_index = 0;
    200   switch (selected_image_) {
    201     case User::kExternalImageIndex:
    202       // Photo decoding may not have been finished yet.
    203       if (user_photo_.isNull()) {
    204         accept_photo_after_decoding_ = true;
    205         return;
    206       }
    207       image_manager->
    208           SaveUserImage(user_id, UserImage::CreateAndEncode(user_photo_));
    209       uma_index = kHistogramImageFromCamera;
    210       break;
    211     case User::kProfileImageIndex:
    212       image_manager->SaveUserImageFromProfileImage(user_id);
    213       uma_index = kHistogramImageFromProfile;
    214       break;
    215     default:
    216       DCHECK(selected_image_ >= 0 && selected_image_ < kDefaultImagesCount);
    217       image_manager->SaveUserDefaultImageIndex(user_id, selected_image_);
    218       uma_index = GetDefaultImageHistogramValue(selected_image_);
    219       break;
    220   }
    221   if (user_has_selected_image_) {
    222     UMA_HISTOGRAM_ENUMERATION("UserImage.FirstTimeChoice",
    223                               uma_index,
    224                               kHistogramImagesCount);
    225   }
    226   ExitScreen();
    227 }
    228 
    229 
    230 void UserImageScreen::SetProfilePictureEnabled(bool profile_picture_enabled) {
    231   if (profile_picture_enabled_ == profile_picture_enabled)
    232     return;
    233   profile_picture_enabled_ = profile_picture_enabled;
    234   if (profile_picture_enabled) {
    235     notification_registrar_.Add(this,
    236                                 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED,
    237                                 content::NotificationService::AllSources());
    238     notification_registrar_.Add(
    239         this,
    240         chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED,
    241         content::NotificationService::AllSources());
    242   } else {
    243     notification_registrar_.Remove(this,
    244                                    chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED,
    245         content::NotificationService::AllSources());
    246     notification_registrar_.Remove(
    247         this,
    248         chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED,
    249         content::NotificationService::AllSources());
    250   }
    251   if (actor_)
    252     actor_->SetProfilePictureEnabled(profile_picture_enabled);
    253 }
    254 
    255 void UserImageScreen::SetUserID(const std::string& user_id) {
    256   DCHECK(!user_id.empty());
    257   user_id_ = user_id;
    258 }
    259 
    260 void UserImageScreen::PrepareToShow() {
    261   if (actor_)
    262     actor_->PrepareToShow();
    263 }
    264 
    265 const User* UserImageScreen::GetUser() {
    266   if (user_id_.empty())
    267     return UserManager::Get()->GetLoggedInUser();
    268   const User* user = UserManager::Get()->FindUser(user_id_);
    269   DCHECK(user);
    270   return user;
    271 }
    272 
    273 void UserImageScreen::Show() {
    274   if (!actor_)
    275     return;
    276 
    277   DCHECK(!policy_registrar_);
    278   Profile* profile = UserManager::Get()->GetProfileByUser(GetUser());
    279   if (profile) {
    280     policy::PolicyService* policy_service =
    281         policy::ProfilePolicyConnectorFactory::GetForProfile(profile)->
    282             policy_service();
    283     if (policy_service->GetPolicies(
    284             policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME,
    285                                     std::string()))
    286             .Get(policy::key::kUserAvatarImage)) {
    287       // If the user image is managed by policy, skip the screen because the
    288       // user is not allowed to override a policy-set image.
    289       ExitScreen();
    290       return;
    291     }
    292 
    293     // Listen for policy changes. If at any point, the user image becomes
    294     // managed by policy, the screen will close.
    295     policy_registrar_.reset(new policy::PolicyChangeRegistrar(
    296         policy_service,
    297         policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, std::string())));
    298     policy_registrar_->Observe(
    299         policy::key::kUserAvatarImage,
    300         base::Bind(&UserImageScreen::OnUserImagePolicyChanged,
    301                    base::Unretained(this)));
    302   } else {
    303     NOTREACHED();
    304   }
    305 
    306   if (GetUser()->CanSyncImage()) {
    307     if (UserImageSyncObserver* sync_observer =
    308           UserManager::Get()->GetUserImageManager()->GetSyncObserver()) {
    309       // We have synced image already.
    310       if (sync_observer->is_synced()) {
    311         ExitScreen();
    312         return;
    313       }
    314       sync_observer->AddObserver(this);
    315       sync_timer_.reset(new base::Timer(
    316             FROM_HERE,
    317             base::TimeDelta::FromSeconds(kSyncTimeoutSeconds),
    318             base::Bind(&UserImageScreen::OnSyncTimeout, base::Unretained(this)),
    319             false));
    320       sync_timer_->Reset();
    321     }
    322   }
    323   actor_->Show();
    324   actor_->SetProfilePictureEnabled(profile_picture_enabled_);
    325 
    326   selected_image_ = GetUser()->image_index();
    327   actor_->SelectImage(selected_image_);
    328 
    329   if (profile_picture_enabled_) {
    330     // Start fetching the profile image.
    331     UserManager::Get()->GetUserImageManager()->
    332         DownloadProfileImage(kProfileDownloadReason);
    333   }
    334 }
    335 
    336 void UserImageScreen::Hide() {
    337   if (actor_)
    338     actor_->Hide();
    339 }
    340 
    341 std::string UserImageScreen::GetName() const {
    342   return WizardController::kUserImageScreenName;
    343 }
    344 
    345 void UserImageScreen::OnActorDestroyed(UserImageScreenActor* actor) {
    346   if (actor_ == actor)
    347     actor_ = NULL;
    348 }
    349 
    350 void UserImageScreen::Observe(int type,
    351                               const content::NotificationSource& source,
    352                               const content::NotificationDetails& details) {
    353   DCHECK(profile_picture_enabled_);
    354   switch (type) {
    355     case chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED: {
    356       // We've got a new profile image.
    357       profile_picture_data_url_ = webui::GetBitmapDataUrl(
    358           *content::Details<const gfx::ImageSkia>(details).ptr()->bitmap());
    359       if (actor_)
    360         actor_->SendProfileImage(profile_picture_data_url_);
    361       break;
    362     }
    363     case chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED: {
    364       // User has a default profile image or fetching profile image has failed.
    365       profile_picture_absent_ = true;
    366       if (actor_)
    367         actor_->OnProfileImageAbsent();
    368       break;
    369     }
    370     case chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED: {
    371       if (actor_)
    372         actor_->SelectImage(GetUser()->image_index());
    373       break;
    374     }
    375     default:
    376       NOTREACHED();
    377   }
    378 }
    379 
    380 bool UserImageScreen::profile_picture_absent() {
    381   return profile_picture_absent_;
    382 }
    383 
    384 int UserImageScreen::selected_image() {
    385   return selected_image_;
    386 }
    387 
    388 std::string UserImageScreen::profile_picture_data_url() {
    389   return profile_picture_data_url_;
    390 }
    391 
    392 void UserImageScreen::ExitScreen() {
    393   policy_registrar_.reset();
    394   sync_timer_.reset();
    395   UserImageSyncObserver* sync_observer =
    396       UserManager::Get()->GetUserImageManager()->GetSyncObserver();
    397   if (sync_observer)
    398     sync_observer->RemoveObserver(this);
    399   get_screen_observer()->OnExit(ScreenObserver::USER_IMAGE_SELECTED);
    400 }
    401 
    402 }  // namespace chromeos
    403