blob: ab9f883dde308e040371889cb6ba076a11ae26ce [file] [log] [blame]
// Copyright (c) 2013 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/ui/webui/options/chromeos/change_picture_options_handler.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/metrics/histogram.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/chromeos/camera_detector.h"
#include "chrome/browser/chromeos/login/default_user_images.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/ui/browser_finder.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/chrome_select_file_policy.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/url_constants.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/web_ui.h"
#include "content/public/common/url_constants.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "net/base/data_url.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/views/widget/widget.h"
#include "url/gurl.h"
using content::BrowserThread;
namespace chromeos {
namespace options {
namespace {
// Returns info about extensions for files we support as user images.
ui::SelectFileDialog::FileTypeInfo GetUserImageFileTypeInfo() {
ui::SelectFileDialog::FileTypeInfo file_type_info;
file_type_info.extensions.resize(1);
file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("bmp"));
file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("jpg"));
file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("jpeg"));
file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("png"));
file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("tif"));
file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("tiff"));
file_type_info.extension_description_overrides.resize(1);
file_type_info.extension_description_overrides[0] =
l10n_util::GetStringUTF16(IDS_IMAGE_FILES);
return file_type_info;
}
// Time histogram suffix for profile image download.
const char kProfileDownloadReason[] = "Preferences";
} // namespace
ChangePictureOptionsHandler::ChangePictureOptionsHandler()
: previous_image_url_(content::kAboutBlankURL),
previous_image_index_(User::kInvalidImageIndex),
weak_factory_(this),
was_camera_present_(false) {
registrar_.Add(this, chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED,
content::NotificationService::AllSources());
registrar_.Add(this, chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED,
content::NotificationService::AllSources());
registrar_.Add(this, chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED,
content::NotificationService::AllSources());
}
ChangePictureOptionsHandler::~ChangePictureOptionsHandler() {
if (select_file_dialog_.get())
select_file_dialog_->ListenerDestroyed();
if (image_decoder_.get())
image_decoder_->set_delegate(NULL);
}
void ChangePictureOptionsHandler::GetLocalizedValues(
DictionaryValue* localized_strings) {
DCHECK(localized_strings);
localized_strings->SetString("changePicturePage",
l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_DIALOG_TITLE));
localized_strings->SetString("changePicturePageDescription",
l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_DIALOG_TEXT));
localized_strings->SetString("takePhoto",
l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_TAKE_PHOTO));
localized_strings->SetString("chooseFile",
l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_CHOOSE_FILE));
localized_strings->SetString("profilePhoto",
l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_PROFILE_PHOTO));
localized_strings->SetString("profilePhotoLoading",
l10n_util::GetStringUTF16(
IDS_OPTIONS_CHANGE_PICTURE_PROFILE_LOADING_PHOTO));
localized_strings->SetString("previewAltText",
l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_PREVIEW_ALT));
localized_strings->SetString("authorCredit",
l10n_util::GetStringUTF16(IDS_OPTIONS_SET_WALLPAPER_AUTHOR_TEXT));
localized_strings->SetString("photoFromCamera",
l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_PHOTO_FROM_CAMERA));
localized_strings->SetString("photoCaptureAccessibleText",
l10n_util::GetStringUTF16(IDS_OPTIONS_PHOTO_CAPTURE_ACCESSIBLE_TEXT));
localized_strings->SetString("photoDiscardAccessibleText",
l10n_util::GetStringUTF16(IDS_OPTIONS_PHOTO_DISCARD_ACCESSIBLE_TEXT));
}
void ChangePictureOptionsHandler::RegisterMessages() {
web_ui()->RegisterMessageCallback("chooseFile",
base::Bind(&ChangePictureOptionsHandler::HandleChooseFile,
base::Unretained(this)));
web_ui()->RegisterMessageCallback("photoTaken",
base::Bind(&ChangePictureOptionsHandler::HandlePhotoTaken,
base::Unretained(this)));
web_ui()->RegisterMessageCallback("checkCameraPresence",
base::Bind(&ChangePictureOptionsHandler::HandleCheckCameraPresence,
base::Unretained(this)));
web_ui()->RegisterMessageCallback("onChangePicturePageShown",
base::Bind(&ChangePictureOptionsHandler::HandlePageShown,
base::Unretained(this)));
web_ui()->RegisterMessageCallback("onChangePicturePageInitialized",
base::Bind(&ChangePictureOptionsHandler::HandlePageInitialized,
base::Unretained(this)));
web_ui()->RegisterMessageCallback("selectImage",
base::Bind(&ChangePictureOptionsHandler::HandleSelectImage,
base::Unretained(this)));
}
void ChangePictureOptionsHandler::SendDefaultImages() {
base::ListValue image_urls;
for (int i = kFirstDefaultImageIndex; i < kDefaultImagesCount; ++i) {
scoped_ptr<base::DictionaryValue> image_data(new base::DictionaryValue);
image_data->SetString("url", GetDefaultImageUrl(i));
image_data->SetString(
"author", l10n_util::GetStringUTF16(kDefaultImageAuthorIDs[i]));
image_data->SetString(
"website", l10n_util::GetStringUTF16(kDefaultImageWebsiteIDs[i]));
image_urls.Append(image_data.release());
}
web_ui()->CallJavascriptFunction("ChangePictureOptions.setDefaultImages",
image_urls);
}
void ChangePictureOptionsHandler::HandleChooseFile(const ListValue* args) {
DCHECK(args && args->empty());
select_file_dialog_ = ui::SelectFileDialog::Create(
this, new ChromeSelectFilePolicy(web_ui()->GetWebContents()));
base::FilePath downloads_path;
if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &downloads_path)) {
NOTREACHED();
return;
}
// Static so we initialize it only once.
CR_DEFINE_STATIC_LOCAL(ui::SelectFileDialog::FileTypeInfo, file_type_info,
(GetUserImageFileTypeInfo()));
select_file_dialog_->SelectFile(
ui::SelectFileDialog::SELECT_OPEN_FILE,
l10n_util::GetStringUTF16(IDS_DOWNLOAD_TITLE),
downloads_path,
&file_type_info,
0,
FILE_PATH_LITERAL(""),
GetBrowserWindow(),
NULL);
}
void ChangePictureOptionsHandler::HandlePhotoTaken(
const base::ListValue* args) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
std::string image_url;
if (!args || args->GetSize() != 1 || !args->GetString(0, &image_url))
NOTREACHED();
DCHECK(!image_url.empty());
std::string mime_type, charset, raw_data;
if (!net::DataURL::Parse(GURL(image_url), &mime_type, &charset, &raw_data))
NOTREACHED();
DCHECK_EQ("image/png", mime_type);
user_photo_ = gfx::ImageSkia();
user_photo_data_url_ = image_url;
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 ChangePictureOptionsHandler::HandleCheckCameraPresence(
const base::ListValue* args) {
DCHECK(args->empty());
CheckCameraPresence();
}
void ChangePictureOptionsHandler::HandlePageInitialized(
const base::ListValue* args) {
DCHECK(args && args->empty());
SendDefaultImages();
}
void ChangePictureOptionsHandler::HandlePageShown(const base::ListValue* args) {
DCHECK(args && args->empty());
CheckCameraPresence();
SendSelectedImage();
UpdateProfileImage();
}
void ChangePictureOptionsHandler::SendSelectedImage() {
const User* user = GetUser();
DCHECK(!user->email().empty());
previous_image_index_ = user->image_index();
switch (previous_image_index_) {
case User::kExternalImageIndex: {
// User has image from camera/file, record it and add to the image list.
previous_image_ = user->image();
SendOldImage(webui::GetBitmapDataUrl(*previous_image_.bitmap()));
break;
}
case User::kProfileImageIndex: {
// User has his/her Profile image as the current image.
SendProfileImage(user->image(), true);
break;
}
default: {
DCHECK(previous_image_index_ >= 0 &&
previous_image_index_ < kDefaultImagesCount);
if (previous_image_index_ >= kFirstDefaultImageIndex) {
// User has image from the current set of default images.
base::StringValue image_url(GetDefaultImageUrl(previous_image_index_));
web_ui()->CallJavascriptFunction(
"ChangePictureOptions.setSelectedImage", image_url);
} else {
// User has an old default image, so present it in the same manner as a
// previous image from file.
SendOldImage(GetDefaultImageUrl(previous_image_index_));
}
}
}
}
void ChangePictureOptionsHandler::SendProfileImage(const gfx::ImageSkia& image,
bool should_select) {
base::StringValue data_url(webui::GetBitmapDataUrl(*image.bitmap()));
base::FundamentalValue select(should_select);
web_ui()->CallJavascriptFunction("ChangePictureOptions.setProfileImage",
data_url, select);
}
void ChangePictureOptionsHandler::UpdateProfileImage() {
UserImageManager* user_image_manager =
UserManager::Get()->GetUserImageManager();
// If we have a downloaded profile image and haven't sent it in
// |SendSelectedImage|, send it now (without selecting).
if (previous_image_index_ != User::kProfileImageIndex &&
!user_image_manager->DownloadedProfileImage().isNull())
SendProfileImage(user_image_manager->DownloadedProfileImage(), false);
user_image_manager->DownloadProfileImage(kProfileDownloadReason);
}
void ChangePictureOptionsHandler::SendOldImage(const std::string& image_url) {
previous_image_url_ = image_url;
base::StringValue url(image_url);
web_ui()->CallJavascriptFunction("ChangePictureOptions.setOldImage", url);
}
void ChangePictureOptionsHandler::HandleSelectImage(const ListValue* args) {
std::string image_url;
std::string image_type;
if (!args ||
args->GetSize() != 2 ||
!args->GetString(0, &image_url) ||
!args->GetString(1, &image_type)) {
NOTREACHED();
return;
}
DCHECK(!image_url.empty());
DCHECK(!image_type.empty());
const User* user = GetUser();
UserImageManager* user_image_manager =
UserManager::Get()->GetUserImageManager();
int image_index = User::kInvalidImageIndex;
bool waiting_for_camera_photo = false;
if (image_type == "old") {
// Previous image (from camera or manually uploaded) re-selected.
DCHECK(!previous_image_.isNull());
user_image_manager->SaveUserImage(
user->email(), UserImage::CreateAndEncode(previous_image_));
UMA_HISTOGRAM_ENUMERATION("UserImage.ChangeChoice",
kHistogramImageOld,
kHistogramImagesCount);
VLOG(1) << "Selected old user image";
} else if (image_type == "default" &&
IsDefaultImageUrl(image_url, &image_index)) {
// One of the default user images.
user_image_manager->SaveUserDefaultImageIndex(user->email(), image_index);
UMA_HISTOGRAM_ENUMERATION("UserImage.ChangeChoice",
GetDefaultImageHistogramValue(image_index),
kHistogramImagesCount);
VLOG(1) << "Selected default user image: " << image_index;
} else if (image_type == "camera") {
// Camera image is selected.
if (user_photo_.isNull()) {
DCHECK(image_decoder_.get());
waiting_for_camera_photo = true;
VLOG(1) << "Still waiting for camera image to decode";
} else {
SetImageFromCamera(user_photo_);
}
} else if (image_type == "profile") {
// Profile image selected. Could be previous (old) user image.
user_image_manager->SaveUserImageFromProfileImage(user->email());
if (previous_image_index_ == User::kProfileImageIndex) {
UMA_HISTOGRAM_ENUMERATION("UserImage.ChangeChoice",
kHistogramImageOld,
kHistogramImagesCount);
VLOG(1) << "Selected old (profile) user image";
} else {
UMA_HISTOGRAM_ENUMERATION("UserImage.ChangeChoice",
kHistogramImageFromProfile,
kHistogramImagesCount);
VLOG(1) << "Selected profile image";
}
} else {
NOTREACHED() << "Unexpected image type: " << image_type;
}
// Ignore the result of the previous decoding if it's no longer needed.
if (!waiting_for_camera_photo && image_decoder_.get())
image_decoder_->set_delegate(NULL);
}
void ChangePictureOptionsHandler::FileSelected(const base::FilePath& path,
int index,
void* params) {
UserManager* user_manager = UserManager::Get();
user_manager->GetUserImageManager()->SaveUserImageFromFile(GetUser()->email(),
path);
UMA_HISTOGRAM_ENUMERATION(
"UserImage.ChangeChoice", kHistogramImageFromFile, kHistogramImagesCount);
VLOG(1) << "Selected image from file";
}
void ChangePictureOptionsHandler::SetImageFromCamera(
const gfx::ImageSkia& photo) {
UserManager* user_manager = UserManager::Get();
user_manager->GetUserImageManager()->SaveUserImage(
GetUser()->email(), UserImage::CreateAndEncode(photo));
UMA_HISTOGRAM_ENUMERATION("UserImage.ChangeChoice",
kHistogramImageFromCamera,
kHistogramImagesCount);
VLOG(1) << "Selected camera photo";
}
void ChangePictureOptionsHandler::CheckCameraPresence() {
CameraDetector::StartPresenceCheck(
base::Bind(&ChangePictureOptionsHandler::OnCameraPresenceCheckDone,
weak_factory_.GetWeakPtr()));
}
void ChangePictureOptionsHandler::SetCameraPresent(bool present) {
base::FundamentalValue present_value(present);
web_ui()->CallJavascriptFunction("ChangePictureOptions.setCameraPresent",
present_value);
}
void ChangePictureOptionsHandler::OnCameraPresenceCheckDone() {
bool is_camera_present = CameraDetector::camera_presence() ==
CameraDetector::kCameraPresent;
if (is_camera_present != was_camera_present_) {
SetCameraPresent(is_camera_present);
was_camera_present_ = is_camera_present;
}
}
void ChangePictureOptionsHandler::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
OptionsPageUIHandler::Observe(type, source, details);
if (type == chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED) {
// User profile image has been updated.
SendProfileImage(*content::Details<const gfx::ImageSkia>(details).ptr(),
false);
} else if (type == chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED) {
// Not initialized yet.
if (previous_image_index_ == User::kInvalidImageIndex)
return;
SendSelectedImage();
}
}
gfx::NativeWindow ChangePictureOptionsHandler::GetBrowserWindow() const {
Browser* browser =
chrome::FindBrowserWithWebContents(web_ui()->GetWebContents());
return browser->window()->GetNativeWindow();
}
void ChangePictureOptionsHandler::OnImageDecoded(
const ImageDecoder* decoder,
const SkBitmap& decoded_image) {
DCHECK_EQ(image_decoder_.get(), decoder);
image_decoder_ = NULL;
user_photo_ = gfx::ImageSkia::CreateFrom1xBitmap(decoded_image);
SetImageFromCamera(user_photo_);
}
void ChangePictureOptionsHandler::OnDecodeImageFailed(
const ImageDecoder* decoder) {
NOTREACHED() << "Failed to decode PNG image from WebUI";
}
User* ChangePictureOptionsHandler::GetUser() const {
Profile* profile = Profile::FromWebUI(web_ui());
User* user = UserManager::Get()->GetUserByProfile(profile);
if (!user)
return UserManager::Get()->GetActiveUser();
return user;
}
} // namespace options
} // namespace chromeos