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