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/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 
     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 UserImageScreen::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_(url::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 
     79 UserImageScreen::~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 
     87 void UserImageScreen::OnScreenReady() {
     88   is_screen_ready_ = true;
     89   if (!IsWaitingForSync())
     90     HideCurtain();
     91 }
     92 
     93 void 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 
    105 void UserImageScreen::OnCameraPresenceCheckDone(bool is_camera_present) {
    106   if (actor_)
    107     actor_->SetCameraPresent(is_camera_present);
    108 }
    109 
    110 void UserImageScreen::HideCurtain() {
    111   if (actor_)
    112     actor_->HideCurtain();
    113 }
    114 
    115 void 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 
    123 void UserImageScreen::OnDecodeImageFailed(const ImageDecoder* decoder) {
    124   NOTREACHED() << "Failed to decode PNG image from WebUI";
    125 }
    126 
    127 void 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 
    139 void UserImageScreen::OnSyncTimeout() {
    140   sync_timer_.reset();
    141   GetSyncObserver()->RemoveObserver(this);
    142   if (is_screen_ready_)
    143     HideCurtain();
    144 }
    145 
    146 bool UserImageScreen::IsWaitingForSync() const {
    147   return sync_timer_.get() && sync_timer_->IsRunning();
    148 }
    149 
    150 void 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 
    159 void 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 
    180 void 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 
    212 void 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 
    237 void UserImageScreen::SetUserID(const std::string& user_id) {
    238   DCHECK(!user_id.empty());
    239   user_id_ = user_id;
    240 }
    241 
    242 void UserImageScreen::PrepareToShow() {
    243   if (actor_)
    244     actor_->PrepareToShow();
    245 }
    246 
    247 const User* UserImageScreen::GetUser() {
    248   if (user_id_.empty())
    249     return UserManager::Get()->GetLoggedInUser();
    250   return UserManager::Get()->FindUser(user_id_);
    251 }
    252 
    253 UserImageManager* UserImageScreen::GetUserImageManager() {
    254   return UserManager::Get()->GetUserImageManager(GetUser()->email());
    255 }
    256 
    257 UserImageSyncObserver* UserImageScreen::GetSyncObserver() {
    258   return GetUserImageManager()->GetSyncObserver();
    259 }
    260 
    261 void 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 
    323 void UserImageScreen::Hide() {
    324   CameraPresenceNotifier::GetInstance()->RemoveObserver(this);
    325   if (actor_)
    326     actor_->Hide();
    327 }
    328 
    329 std::string UserImageScreen::GetName() const {
    330   return WizardController::kUserImageScreenName;
    331 }
    332 
    333 void UserImageScreen::OnActorDestroyed(UserImageScreenActor* actor) {
    334   if (actor_ == actor)
    335     actor_ = NULL;
    336 }
    337 
    338 void 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 
    368 bool UserImageScreen::profile_picture_absent() {
    369   return profile_picture_absent_;
    370 }
    371 
    372 int UserImageScreen::selected_image() {
    373   return selected_image_;
    374 }
    375 
    376 std::string UserImageScreen::profile_picture_data_url() {
    377   return profile_picture_data_url_;
    378 }
    379 
    380 void 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