blob: a7a49958cc39d2129f4c556c8205947f386021f5 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/login/screens/user_image_screen.h"
#include <string>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/compiler_specific.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/metrics/histogram.h"
#include "base/timer/timer.h"
#include "base/values.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
#include "chrome/browser/chromeos/camera_detector.h"
#include "chrome/browser/chromeos/login/default_user_images.h"
#include "chrome/browser/chromeos/login/login_utils.h"
#include "chrome/browser/chromeos/login/screens/screen_observer.h"
#include "chrome/browser/chromeos/login/user_image.h"
#include "chrome/browser/chromeos/login/user_image_manager.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/chromeos/login/wizard_controller.h"
#include "chrome/browser/policy/profile_policy_connector.h"
#include "chrome/browser/policy/profile_policy_connector_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/url_constants.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/core/common/policy_namespace.h"
#include "components/policy/core/common/policy_service.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "policy/policy_constants.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/webui/web_ui_util.h"
#include "ui/gfx/image/image_skia.h"
using content::BrowserThread;
namespace chromeos {
namespace {
// Time histogram suffix for profile image download.
const char kProfileDownloadReason[] = "OOBE";
// Maximum ammount of time to wait for the user image to sync.
// The screen is shown iff sync failed or time limit exceeded.
const int kSyncTimeoutSeconds = 10;
} // namespace
UserImageScreen::UserImageScreen(ScreenObserver* screen_observer,
UserImageScreenActor* actor)
: WizardScreen(screen_observer),
actor_(actor),
weak_factory_(this),
accept_photo_after_decoding_(false),
selected_image_(User::kInvalidImageIndex),
profile_picture_enabled_(false),
profile_picture_data_url_(content::kAboutBlankURL),
profile_picture_absent_(false),
is_screen_ready_(false),
user_has_selected_image_(false) {
actor_->SetDelegate(this);
SetProfilePictureEnabled(true);
notification_registrar_.Add(this,
chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED,
content::NotificationService::AllSources());
}
UserImageScreen::~UserImageScreen() {
if (actor_)
actor_->SetDelegate(NULL);
if (image_decoder_.get())
image_decoder_->set_delegate(NULL);
}
void UserImageScreen::OnScreenReady() {
is_screen_ready_ = true;
if (!IsWaitingForSync())
HideCurtain();
}
void UserImageScreen::OnPhotoTaken(const std::string& raw_data) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
user_photo_ = gfx::ImageSkia();
if (image_decoder_.get())
image_decoder_->set_delegate(NULL);
image_decoder_ = new ImageDecoder(this, raw_data,
ImageDecoder::DEFAULT_CODEC);
scoped_refptr<base::MessageLoopProxy> task_runner =
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
image_decoder_->Start(task_runner);
}
void UserImageScreen::CheckCameraPresence() {
CameraDetector::StartPresenceCheck(
base::Bind(&UserImageScreen::OnCameraPresenceCheckDone,
weak_factory_.GetWeakPtr()));
}
void UserImageScreen::OnCameraPresenceCheckDone() {
if (actor_) {
actor_->SetCameraPresent(
CameraDetector::camera_presence() == CameraDetector::kCameraPresent);
}
}
void UserImageScreen::HideCurtain() {
if (actor_)
actor_->HideCurtain();
}
void UserImageScreen::OnImageDecoded(const ImageDecoder* decoder,
const SkBitmap& decoded_image) {
DCHECK_EQ(image_decoder_.get(), decoder);
user_photo_ = gfx::ImageSkia::CreateFrom1xBitmap(decoded_image);
if (accept_photo_after_decoding_)
OnImageAccepted();
}
void UserImageScreen::OnDecodeImageFailed(const ImageDecoder* decoder) {
NOTREACHED() << "Failed to decode PNG image from WebUI";
}
void UserImageScreen::OnInitialSync(bool local_image_updated) {
DCHECK(sync_timer_);
if (!local_image_updated) {
sync_timer_.reset();
UserManager::Get()->GetUserImageManager()->GetSyncObserver()->
RemoveObserver(this);
if (is_screen_ready_)
HideCurtain();
return;
}
ExitScreen();
}
void UserImageScreen::OnSyncTimeout() {
sync_timer_.reset();
UserManager::Get()->GetUserImageManager()->GetSyncObserver()->
RemoveObserver(this);
if (is_screen_ready_)
HideCurtain();
}
bool UserImageScreen::IsWaitingForSync() const {
return sync_timer_.get() && sync_timer_->IsRunning();
}
void UserImageScreen::OnUserImagePolicyChanged(const base::Value* previous,
const base::Value* current) {
if (current) {
base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE,
policy_registrar_.release());
ExitScreen();
}
}
void UserImageScreen::OnImageSelected(const std::string& image_type,
const std::string& image_url,
bool is_user_selection) {
if (is_user_selection) {
user_has_selected_image_ = true;
}
if (image_url.empty())
return;
int user_image_index = User::kInvalidImageIndex;
if (image_type == "default" &&
IsDefaultImageUrl(image_url, &user_image_index)) {
selected_image_ = user_image_index;
} else if (image_type == "camera") {
selected_image_ = User::kExternalImageIndex;
} else if (image_type == "profile") {
selected_image_ = User::kProfileImageIndex;
} else {
NOTREACHED() << "Unexpected image type: " << image_type;
}
}
void UserImageScreen::OnImageAccepted() {
UserManager* user_manager = UserManager::Get();
UserImageManager* image_manager = user_manager->GetUserImageManager();
std::string user_id = GetUser()->email();
int uma_index = 0;
switch (selected_image_) {
case User::kExternalImageIndex:
// Photo decoding may not have been finished yet.
if (user_photo_.isNull()) {
accept_photo_after_decoding_ = true;
return;
}
image_manager->
SaveUserImage(user_id, UserImage::CreateAndEncode(user_photo_));
uma_index = kHistogramImageFromCamera;
break;
case User::kProfileImageIndex:
image_manager->SaveUserImageFromProfileImage(user_id);
uma_index = kHistogramImageFromProfile;
break;
default:
DCHECK(selected_image_ >= 0 && selected_image_ < kDefaultImagesCount);
image_manager->SaveUserDefaultImageIndex(user_id, selected_image_);
uma_index = GetDefaultImageHistogramValue(selected_image_);
break;
}
if (user_has_selected_image_) {
UMA_HISTOGRAM_ENUMERATION("UserImage.FirstTimeChoice",
uma_index,
kHistogramImagesCount);
}
ExitScreen();
}
void UserImageScreen::SetProfilePictureEnabled(bool profile_picture_enabled) {
if (profile_picture_enabled_ == profile_picture_enabled)
return;
profile_picture_enabled_ = profile_picture_enabled;
if (profile_picture_enabled) {
notification_registrar_.Add(this,
chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED,
content::NotificationService::AllSources());
notification_registrar_.Add(
this,
chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED,
content::NotificationService::AllSources());
} else {
notification_registrar_.Remove(this,
chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED,
content::NotificationService::AllSources());
notification_registrar_.Remove(
this,
chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED,
content::NotificationService::AllSources());
}
if (actor_)
actor_->SetProfilePictureEnabled(profile_picture_enabled);
}
void UserImageScreen::SetUserID(const std::string& user_id) {
DCHECK(!user_id.empty());
user_id_ = user_id;
}
void UserImageScreen::PrepareToShow() {
if (actor_)
actor_->PrepareToShow();
}
const User* UserImageScreen::GetUser() {
if (user_id_.empty())
return UserManager::Get()->GetLoggedInUser();
const User* user = UserManager::Get()->FindUser(user_id_);
DCHECK(user);
return user;
}
void UserImageScreen::Show() {
if (!actor_)
return;
DCHECK(!policy_registrar_);
Profile* profile = UserManager::Get()->GetProfileByUser(GetUser());
if (profile) {
policy::PolicyService* policy_service =
policy::ProfilePolicyConnectorFactory::GetForProfile(profile)->
policy_service();
if (policy_service->GetPolicies(
policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME,
std::string()))
.Get(policy::key::kUserAvatarImage)) {
// If the user image is managed by policy, skip the screen because the
// user is not allowed to override a policy-set image.
ExitScreen();
return;
}
// Listen for policy changes. If at any point, the user image becomes
// managed by policy, the screen will close.
policy_registrar_.reset(new policy::PolicyChangeRegistrar(
policy_service,
policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, std::string())));
policy_registrar_->Observe(
policy::key::kUserAvatarImage,
base::Bind(&UserImageScreen::OnUserImagePolicyChanged,
base::Unretained(this)));
} else {
NOTREACHED();
}
if (GetUser()->CanSyncImage()) {
if (UserImageSyncObserver* sync_observer =
UserManager::Get()->GetUserImageManager()->GetSyncObserver()) {
// We have synced image already.
if (sync_observer->is_synced()) {
ExitScreen();
return;
}
sync_observer->AddObserver(this);
sync_timer_.reset(new base::Timer(
FROM_HERE,
base::TimeDelta::FromSeconds(kSyncTimeoutSeconds),
base::Bind(&UserImageScreen::OnSyncTimeout, base::Unretained(this)),
false));
sync_timer_->Reset();
}
}
actor_->Show();
actor_->SetProfilePictureEnabled(profile_picture_enabled_);
selected_image_ = GetUser()->image_index();
actor_->SelectImage(selected_image_);
if (profile_picture_enabled_) {
// Start fetching the profile image.
UserManager::Get()->GetUserImageManager()->
DownloadProfileImage(kProfileDownloadReason);
}
}
void UserImageScreen::Hide() {
if (actor_)
actor_->Hide();
}
std::string UserImageScreen::GetName() const {
return WizardController::kUserImageScreenName;
}
void UserImageScreen::OnActorDestroyed(UserImageScreenActor* actor) {
if (actor_ == actor)
actor_ = NULL;
}
void UserImageScreen::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
DCHECK(profile_picture_enabled_);
switch (type) {
case chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED: {
// We've got a new profile image.
profile_picture_data_url_ = webui::GetBitmapDataUrl(
*content::Details<const gfx::ImageSkia>(details).ptr()->bitmap());
if (actor_)
actor_->SendProfileImage(profile_picture_data_url_);
break;
}
case chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED: {
// User has a default profile image or fetching profile image has failed.
profile_picture_absent_ = true;
if (actor_)
actor_->OnProfileImageAbsent();
break;
}
case chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED: {
if (actor_)
actor_->SelectImage(GetUser()->image_index());
break;
}
default:
NOTREACHED();
}
}
bool UserImageScreen::profile_picture_absent() {
return profile_picture_absent_;
}
int UserImageScreen::selected_image() {
return selected_image_;
}
std::string UserImageScreen::profile_picture_data_url() {
return profile_picture_data_url_;
}
void UserImageScreen::ExitScreen() {
policy_registrar_.reset();
sync_timer_.reset();
UserImageSyncObserver* sync_observer =
UserManager::Get()->GetUserImageManager()->GetSyncObserver();
if (sync_observer)
sync_observer->RemoveObserver(this);
get_screen_observer()->OnExit(ScreenObserver::USER_IMAGE_SELECTED);
}
} // namespace chromeos