014c86fa4a826fbd4dec08570ac6bc7c0ed06617
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / webui / options / chromeos / change_picture_options_handler.cc
1 // Copyright (c) 2013 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/ui/webui/options/chromeos/change_picture_options_handler.h"
6
7 #include "ash/audio/sounds.h"
8 #include "base/bind.h"
9 #include "base/bind_helpers.h"
10 #include "base/command_line.h"
11 #include "base/metrics/histogram.h"
12 #include "base/path_service.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/values.h"
16 #include "chrome/browser/chrome_notification_types.h"
17 #include "chrome/browser/chromeos/camera_presence_notifier.h"
18 #include "chrome/browser/chromeos/login/default_user_images.h"
19 #include "chrome/browser/chromeos/login/user_image.h"
20 #include "chrome/browser/chromeos/login/user_image_manager.h"
21 #include "chrome/browser/chromeos/login/user_manager.h"
22 #include "chrome/browser/ui/browser_finder.h"
23 #include "chrome/browser/ui/browser_window.h"
24 #include "chrome/browser/ui/chrome_select_file_policy.h"
25 #include "chrome/common/chrome_paths.h"
26 #include "chrome/common/chrome_switches.h"
27 #include "chrome/common/url_constants.h"
28 #include "chromeos/audio/chromeos_sounds.h"
29 #include "content/public/browser/browser_thread.h"
30 #include "content/public/browser/notification_service.h"
31 #include "content/public/browser/web_ui.h"
32 #include "content/public/common/url_constants.h"
33 #include "grit/browser_resources.h"
34 #include "grit/generated_resources.h"
35 #include "grit/theme_resources.h"
36 #include "net/base/data_url.h"
37 #include "ui/base/l10n/l10n_util.h"
38 #include "ui/base/resource/resource_bundle.h"
39 #include "ui/base/webui/web_ui_util.h"
40 #include "ui/views/widget/widget.h"
41 #include "url/gurl.h"
42
43 using content::BrowserThread;
44
45 namespace chromeos {
46 namespace options {
47
48 namespace {
49
50 // Returns info about extensions for files we support as user images.
51 ui::SelectFileDialog::FileTypeInfo GetUserImageFileTypeInfo() {
52   ui::SelectFileDialog::FileTypeInfo file_type_info;
53   file_type_info.extensions.resize(1);
54
55   file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("bmp"));
56
57   file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("jpg"));
58   file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("jpeg"));
59
60   file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("png"));
61
62   file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("tif"));
63   file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("tiff"));
64
65   file_type_info.extension_description_overrides.resize(1);
66   file_type_info.extension_description_overrides[0] =
67       l10n_util::GetStringUTF16(IDS_IMAGE_FILES);
68
69   return file_type_info;
70 }
71
72 // Time histogram suffix for profile image download.
73 const char kProfileDownloadReason[] = "Preferences";
74
75 }  // namespace
76
77 ChangePictureOptionsHandler::ChangePictureOptionsHandler()
78     : previous_image_url_(content::kAboutBlankURL),
79       previous_image_index_(User::kInvalidImageIndex) {
80   registrar_.Add(this, chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED,
81       content::NotificationService::AllSources());
82   registrar_.Add(this, chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED,
83       content::NotificationService::AllSources());
84   registrar_.Add(this, chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED,
85       content::NotificationService::AllSources());
86
87   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
88   media::SoundsManager* manager = media::SoundsManager::Get();
89   manager->Initialize(SOUND_OBJECT_DELETE,
90                       bundle.GetRawDataResource(IDR_SOUND_OBJECT_DELETE_WAV));
91   manager->Initialize(SOUND_CAMERA_SNAP,
92                       bundle.GetRawDataResource(IDR_SOUND_CAMERA_SNAP_WAV));
93 }
94
95 ChangePictureOptionsHandler::~ChangePictureOptionsHandler() {
96   CameraPresenceNotifier::GetInstance()->RemoveObserver(this);
97   if (select_file_dialog_.get())
98     select_file_dialog_->ListenerDestroyed();
99   if (image_decoder_.get())
100     image_decoder_->set_delegate(NULL);
101 }
102
103 void ChangePictureOptionsHandler::GetLocalizedValues(
104     base::DictionaryValue* localized_strings) {
105   DCHECK(localized_strings);
106   localized_strings->SetString("changePicturePage",
107       l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_DIALOG_TITLE));
108   localized_strings->SetString("changePicturePageDescription",
109       l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_DIALOG_TEXT));
110   localized_strings->SetString("takePhoto",
111       l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_TAKE_PHOTO));
112   localized_strings->SetString("discardPhoto",
113       l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_DISCARD_PHOTO));
114   localized_strings->SetString("flipPhoto",
115       l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_FLIP_PHOTO));
116   localized_strings->SetString("chooseFile",
117       l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_CHOOSE_FILE));
118   localized_strings->SetString("profilePhoto",
119       l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_PROFILE_PHOTO));
120   localized_strings->SetString("profilePhotoLoading",
121       l10n_util::GetStringUTF16(
122           IDS_OPTIONS_CHANGE_PICTURE_PROFILE_LOADING_PHOTO));
123   localized_strings->SetString("previewAltText",
124       l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_PREVIEW_ALT));
125   localized_strings->SetString("authorCredit",
126       l10n_util::GetStringUTF16(IDS_OPTIONS_SET_WALLPAPER_AUTHOR_TEXT));
127   localized_strings->SetString("photoFromCamera",
128       l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_PHOTO_FROM_CAMERA));
129   localized_strings->SetString("photoFlippedAccessibleText",
130       l10n_util::GetStringUTF16(IDS_OPTIONS_PHOTO_FLIP_ACCESSIBLE_TEXT));
131   localized_strings->SetString("photoFlippedBackAccessibleText",
132       l10n_util::GetStringUTF16(IDS_OPTIONS_PHOTO_FLIPBACK_ACCESSIBLE_TEXT));
133   localized_strings->SetString("photoCaptureAccessibleText",
134       l10n_util::GetStringUTF16(IDS_OPTIONS_PHOTO_CAPTURE_ACCESSIBLE_TEXT));
135   localized_strings->SetString("photoDiscardAccessibleText",
136       l10n_util::GetStringUTF16(IDS_OPTIONS_PHOTO_DISCARD_ACCESSIBLE_TEXT));
137 }
138
139 void ChangePictureOptionsHandler::RegisterMessages() {
140   web_ui()->RegisterMessageCallback("chooseFile",
141       base::Bind(&ChangePictureOptionsHandler::HandleChooseFile,
142                  base::Unretained(this)));
143   web_ui()->RegisterMessageCallback("takePhoto",
144       base::Bind(&ChangePictureOptionsHandler::HandleTakePhoto,
145                  base::Unretained(this)));
146   web_ui()->RegisterMessageCallback("photoTaken",
147       base::Bind(&ChangePictureOptionsHandler::HandlePhotoTaken,
148                  base::Unretained(this)));
149   web_ui()->RegisterMessageCallback("discardPhoto",
150       base::Bind(&ChangePictureOptionsHandler::HandleDiscardPhoto,
151                  base::Unretained(this)));
152   web_ui()->RegisterMessageCallback("onChangePicturePageShown",
153       base::Bind(&ChangePictureOptionsHandler::HandlePageShown,
154                  base::Unretained(this)));
155   web_ui()->RegisterMessageCallback("onChangePicturePageHidden",
156       base::Bind(&ChangePictureOptionsHandler::HandlePageHidden,
157                  base::Unretained(this)));
158   web_ui()->RegisterMessageCallback("onChangePicturePageInitialized",
159       base::Bind(&ChangePictureOptionsHandler::HandlePageInitialized,
160                  base::Unretained(this)));
161   web_ui()->RegisterMessageCallback("selectImage",
162       base::Bind(&ChangePictureOptionsHandler::HandleSelectImage,
163                  base::Unretained(this)));
164 }
165
166 void ChangePictureOptionsHandler::SendDefaultImages() {
167   base::ListValue image_urls;
168   for (int i = kFirstDefaultImageIndex; i < kDefaultImagesCount; ++i) {
169     scoped_ptr<base::DictionaryValue> image_data(new base::DictionaryValue);
170     image_data->SetString("url", GetDefaultImageUrl(i));
171     image_data->SetString(
172         "author", l10n_util::GetStringUTF16(kDefaultImageAuthorIDs[i]));
173     image_data->SetString(
174         "website", l10n_util::GetStringUTF16(kDefaultImageWebsiteIDs[i]));
175     image_data->SetString("title", GetDefaultImageDescription(i));
176     image_urls.Append(image_data.release());
177   }
178   web_ui()->CallJavascriptFunction("ChangePictureOptions.setDefaultImages",
179                                    image_urls);
180 }
181
182 void ChangePictureOptionsHandler::HandleChooseFile(
183     const base::ListValue* args) {
184   DCHECK(args && args->empty());
185   select_file_dialog_ = ui::SelectFileDialog::Create(
186       this, new ChromeSelectFilePolicy(web_ui()->GetWebContents()));
187
188   base::FilePath downloads_path;
189   if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &downloads_path)) {
190     NOTREACHED();
191     return;
192   }
193
194   // Static so we initialize it only once.
195   CR_DEFINE_STATIC_LOCAL(ui::SelectFileDialog::FileTypeInfo, file_type_info,
196       (GetUserImageFileTypeInfo()));
197
198   select_file_dialog_->SelectFile(
199       ui::SelectFileDialog::SELECT_OPEN_FILE,
200       l10n_util::GetStringUTF16(IDS_DOWNLOAD_TITLE),
201       downloads_path,
202       &file_type_info,
203       0,
204       FILE_PATH_LITERAL(""),
205       GetBrowserWindow(),
206       NULL);
207 }
208
209 void ChangePictureOptionsHandler::HandleTakePhoto(
210     const base::ListValue* args) {
211   DCHECK(args->empty());
212   ash::PlaySystemSoundIfSpokenFeedback(SOUND_CAMERA_SNAP);
213 }
214
215 void ChangePictureOptionsHandler::HandleDiscardPhoto(
216     const base::ListValue* args) {
217   DCHECK(args->empty());
218   ash::PlaySystemSoundIfSpokenFeedback(SOUND_OBJECT_DELETE);
219 }
220
221 void ChangePictureOptionsHandler::HandlePhotoTaken(
222     const base::ListValue* args) {
223   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
224   std::string image_url;
225   if (!args || args->GetSize() != 1 || !args->GetString(0, &image_url))
226     NOTREACHED();
227   DCHECK(!image_url.empty());
228
229   std::string mime_type, charset, raw_data;
230   if (!net::DataURL::Parse(GURL(image_url), &mime_type, &charset, &raw_data))
231     NOTREACHED();
232   DCHECK_EQ("image/png", mime_type);
233
234   user_photo_ = gfx::ImageSkia();
235   user_photo_data_url_ = image_url;
236
237   if (image_decoder_.get())
238     image_decoder_->set_delegate(NULL);
239   image_decoder_ = new ImageDecoder(this, raw_data,
240                                     ImageDecoder::DEFAULT_CODEC);
241   scoped_refptr<base::MessageLoopProxy> task_runner =
242       BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
243   image_decoder_->Start(task_runner);
244 }
245
246 void ChangePictureOptionsHandler::HandlePageInitialized(
247     const base::ListValue* args) {
248   DCHECK(args && args->empty());
249   SendDefaultImages();
250 }
251
252 void ChangePictureOptionsHandler::HandlePageShown(const base::ListValue* args) {
253   DCHECK(args && args->empty());
254   SendSelectedImage();
255   UpdateProfileImage();
256   CameraPresenceNotifier::GetInstance()->AddObserver(this);
257 }
258
259 void ChangePictureOptionsHandler::HandlePageHidden(
260     const base::ListValue* args) {
261   CameraPresenceNotifier::GetInstance()->RemoveObserver(this);
262 }
263
264 void ChangePictureOptionsHandler::SendSelectedImage() {
265   const User* user = GetUser();
266   DCHECK(!user->email().empty());
267
268   previous_image_index_ = user->image_index();
269   switch (previous_image_index_) {
270     case User::kExternalImageIndex: {
271       // User has image from camera/file, record it and add to the image list.
272       previous_image_ = user->image();
273       SendOldImage(webui::GetBitmapDataUrl(*previous_image_.bitmap()));
274       break;
275     }
276     case User::kProfileImageIndex: {
277       // User has his/her Profile image as the current image.
278       SendProfileImage(user->image(), true);
279       break;
280     }
281     default: {
282       DCHECK(previous_image_index_ >= 0 &&
283              previous_image_index_ < kDefaultImagesCount);
284       if (previous_image_index_ >= kFirstDefaultImageIndex) {
285         // User has image from the current set of default images.
286         base::StringValue image_url(GetDefaultImageUrl(previous_image_index_));
287         web_ui()->CallJavascriptFunction(
288             "ChangePictureOptions.setSelectedImage", image_url);
289       } else {
290         // User has an old default image, so present it in the same manner as a
291         // previous image from file.
292         SendOldImage(GetDefaultImageUrl(previous_image_index_));
293       }
294     }
295   }
296 }
297
298 void ChangePictureOptionsHandler::SendProfileImage(const gfx::ImageSkia& image,
299                                                    bool should_select) {
300   base::StringValue data_url(webui::GetBitmapDataUrl(*image.bitmap()));
301   base::FundamentalValue select(should_select);
302   web_ui()->CallJavascriptFunction("ChangePictureOptions.setProfileImage",
303                                    data_url, select);
304 }
305
306 void ChangePictureOptionsHandler::UpdateProfileImage() {
307   UserImageManager* user_image_manager =
308       UserManager::Get()->GetUserImageManager(GetUser()->email());
309   // If we have a downloaded profile image and haven't sent it in
310   // |SendSelectedImage|, send it now (without selecting).
311   if (previous_image_index_ != User::kProfileImageIndex &&
312       !user_image_manager->DownloadedProfileImage().isNull())
313     SendProfileImage(user_image_manager->DownloadedProfileImage(), false);
314
315   user_image_manager->DownloadProfileImage(kProfileDownloadReason);
316 }
317
318 void ChangePictureOptionsHandler::SendOldImage(const std::string& image_url) {
319   previous_image_url_ = image_url;
320   base::StringValue url(image_url);
321   web_ui()->CallJavascriptFunction("ChangePictureOptions.setOldImage", url);
322 }
323
324 void ChangePictureOptionsHandler::HandleSelectImage(
325     const base::ListValue* args) {
326   std::string image_url;
327   std::string image_type;
328   if (!args ||
329       args->GetSize() != 2 ||
330       !args->GetString(0, &image_url) ||
331       !args->GetString(1, &image_type)) {
332     NOTREACHED();
333     return;
334   }
335   DCHECK(!image_url.empty());
336   DCHECK(!image_type.empty());
337
338   UserImageManager* user_image_manager =
339       UserManager::Get()->GetUserImageManager(GetUser()->email());
340   int image_index = User::kInvalidImageIndex;
341   bool waiting_for_camera_photo = false;
342
343   if (image_type == "old") {
344     // Previous image (from camera or manually uploaded) re-selected.
345     DCHECK(!previous_image_.isNull());
346     user_image_manager->SaveUserImage(
347         UserImage::CreateAndEncode(previous_image_));
348
349     UMA_HISTOGRAM_ENUMERATION("UserImage.ChangeChoice",
350                               kHistogramImageOld,
351                               kHistogramImagesCount);
352     VLOG(1) << "Selected old user image";
353   } else if (image_type == "default" &&
354              IsDefaultImageUrl(image_url, &image_index)) {
355     // One of the default user images.
356     user_image_manager->SaveUserDefaultImageIndex(image_index);
357
358     UMA_HISTOGRAM_ENUMERATION("UserImage.ChangeChoice",
359                               GetDefaultImageHistogramValue(image_index),
360                               kHistogramImagesCount);
361     VLOG(1) << "Selected default user image: " << image_index;
362   } else if (image_type == "camera") {
363     // Camera image is selected.
364     if (user_photo_.isNull()) {
365       DCHECK(image_decoder_.get());
366       waiting_for_camera_photo = true;
367       VLOG(1) << "Still waiting for camera image to decode";
368     } else {
369       SetImageFromCamera(user_photo_);
370     }
371   } else if (image_type == "profile") {
372     // Profile image selected. Could be previous (old) user image.
373     user_image_manager->SaveUserImageFromProfileImage();
374
375     if (previous_image_index_ == User::kProfileImageIndex) {
376       UMA_HISTOGRAM_ENUMERATION("UserImage.ChangeChoice",
377                                 kHistogramImageOld,
378                                 kHistogramImagesCount);
379       VLOG(1) << "Selected old (profile) user image";
380     } else {
381       UMA_HISTOGRAM_ENUMERATION("UserImage.ChangeChoice",
382                                 kHistogramImageFromProfile,
383                                 kHistogramImagesCount);
384       VLOG(1) << "Selected profile image";
385     }
386   } else {
387     NOTREACHED() << "Unexpected image type: " << image_type;
388   }
389
390   // Ignore the result of the previous decoding if it's no longer needed.
391   if (!waiting_for_camera_photo && image_decoder_.get())
392     image_decoder_->set_delegate(NULL);
393 }
394
395 void ChangePictureOptionsHandler::FileSelected(const base::FilePath& path,
396                                                int index,
397                                                void* params) {
398   UserManager* user_manager = UserManager::Get();
399   user_manager->GetUserImageManager(GetUser()->email())->
400       SaveUserImageFromFile(path);
401   UMA_HISTOGRAM_ENUMERATION(
402       "UserImage.ChangeChoice", kHistogramImageFromFile, kHistogramImagesCount);
403   VLOG(1) << "Selected image from file";
404 }
405
406 void ChangePictureOptionsHandler::SetImageFromCamera(
407     const gfx::ImageSkia& photo) {
408   UserManager* user_manager = UserManager::Get();
409   user_manager->GetUserImageManager(GetUser()->email())->SaveUserImage(
410       UserImage::CreateAndEncode(photo));
411   UMA_HISTOGRAM_ENUMERATION("UserImage.ChangeChoice",
412                             kHistogramImageFromCamera,
413                             kHistogramImagesCount);
414   VLOG(1) << "Selected camera photo";
415 }
416
417 void ChangePictureOptionsHandler::SetCameraPresent(bool present) {
418   base::FundamentalValue present_value(present);
419
420   web_ui()->CallJavascriptFunction("ChangePictureOptions.setCameraPresent",
421                                    present_value);
422 }
423
424 void ChangePictureOptionsHandler::OnCameraPresenceCheckDone(
425     bool is_camera_present) {
426   SetCameraPresent(is_camera_present);
427 }
428
429 void ChangePictureOptionsHandler::Observe(
430     int type,
431     const content::NotificationSource& source,
432     const content::NotificationDetails& details) {
433   if (type == chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED) {
434     // User profile image has been updated.
435     SendProfileImage(*content::Details<const gfx::ImageSkia>(details).ptr(),
436                      false);
437   } else if (type == chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED) {
438     // Not initialized yet.
439     if (previous_image_index_ == User::kInvalidImageIndex)
440       return;
441     SendSelectedImage();
442   }
443 }
444
445 gfx::NativeWindow ChangePictureOptionsHandler::GetBrowserWindow() const {
446   Browser* browser =
447       chrome::FindBrowserWithWebContents(web_ui()->GetWebContents());
448   return browser->window()->GetNativeWindow();
449 }
450
451 void ChangePictureOptionsHandler::OnImageDecoded(
452     const ImageDecoder* decoder,
453     const SkBitmap& decoded_image) {
454   DCHECK_EQ(image_decoder_.get(), decoder);
455   image_decoder_ = NULL;
456   user_photo_ = gfx::ImageSkia::CreateFrom1xBitmap(decoded_image);
457   SetImageFromCamera(user_photo_);
458 }
459
460 void ChangePictureOptionsHandler::OnDecodeImageFailed(
461     const ImageDecoder* decoder) {
462   NOTREACHED() << "Failed to decode PNG image from WebUI";
463 }
464
465 User* ChangePictureOptionsHandler::GetUser() const {
466   Profile* profile = Profile::FromWebUI(web_ui());
467   User* user = UserManager::Get()->GetUserByProfile(profile);
468   if (!user)
469     return UserManager::Get()->GetActiveUser();
470   return user;
471 }
472
473 }  // namespace options
474 }  // namespace chromeos