1// Copyright (c) 2011 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/take_photo_view.h"
6
7#include "base/utf_string_conversions.h"
8#include "chrome/browser/chromeos/login/helper.h"
9#include "grit/generated_resources.h"
10#include "grit/theme_resources.h"
11#include "skia/ext/image_operations.h"
12#include "ui/base/l10n/l10n_util.h"
13#include "ui/base/resource/resource_bundle.h"
14#include "ui/gfx/canvas.h"
15#include "views/background.h"
16#include "views/border.h"
17#include "views/controls/button/image_button.h"
18#include "views/controls/image_view.h"
19#include "views/controls/label.h"
20#include "views/controls/throbber.h"
21#include "views/layout/grid_layout.h"
22
23namespace {
24
25// Padding between horizontally neighboring elements.
26const int kHorizontalPadding = 10;
27// Padding between vertically neighboring elements.
28const int kVerticalPadding = 10;
29// Color of image view border.
30const SkColor kImageBorderColor = SkColorSetARGB(38, 0, 0, 0);
31
32// IDs of column sets for grid layout manager.
33enum ColumnSets {
34  kTitleRow,    // Column set for screen title.
35  kImageRow,    // Column set for image from camera and snapshot button.
36};
37
38}  // namespace
39
40namespace chromeos {
41
42// Image view that can show center throbber above itself or a message at its
43// bottom.
44class CameraImageView : public views::ImageView {
45 public:
46  CameraImageView()
47      : throbber_(NULL),
48        message_(NULL) {}
49
50  ~CameraImageView() {}
51
52  void Init() {
53    DCHECK(NULL == throbber_);
54    DCHECK(NULL == message_);
55
56    set_border(views::Border::CreateSolidBorder(1, kImageBorderColor));
57    set_background(views::Background::CreateSolidBackground(SK_ColorWHITE));
58
59    throbber_ = CreateDefaultSmoothedThrobber();
60    throbber_->SetVisible(false);
61    AddChildView(throbber_);
62
63    message_ = new views::Label();
64    message_->SetMultiLine(true);
65    message_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
66    message_->SetVisible(false);
67    CorrectLabelFontSize(message_);
68    AddChildView(message_);
69  }
70
71  void SetInitializingState() {
72    ShowThrobber();
73    SetMessage(std::wstring());
74    SetImage(
75        ResourceBundle::GetSharedInstance().GetBitmapNamed(
76            IDR_USER_IMAGE_INITIALIZING));
77  }
78
79  void SetNormalState() {
80    HideThrobber();
81    SetMessage(std::wstring());
82  }
83
84  void SetErrorState() {
85    HideThrobber();
86    SetMessage(UTF16ToWide(l10n_util::GetStringUTF16(IDS_USER_IMAGE_NO_VIDEO)));
87    SetImage(
88        ResourceBundle::GetSharedInstance().GetBitmapNamed(
89            IDR_USER_IMAGE_NO_VIDEO));
90  }
91
92 private:
93  void ShowThrobber() {
94    DCHECK(throbber_);
95    throbber_->SetVisible(true);
96    throbber_->Start();
97  }
98
99  void HideThrobber() {
100    DCHECK(throbber_);
101    throbber_->Stop();
102    throbber_->SetVisible(false);
103  }
104
105  void SetMessage(const std::wstring& message) {
106    DCHECK(message_);
107    message_->SetText(message);
108    message_->SetVisible(!message.empty());
109    Layout();
110  }
111
112  // views::View override:
113  virtual void Layout() {
114    gfx::Size size = GetPreferredSize();
115    if (throbber_->IsVisible()) {
116      gfx::Size throbber_size = throbber_->GetPreferredSize();
117      int throbber_x = (size.width() - throbber_size.width()) / 2;
118      int throbber_y = (size.height() - throbber_size.height()) / 2;
119      throbber_->SetBounds(throbber_x,
120                           throbber_y,
121                           throbber_size.width(),
122                           throbber_size.height());
123    }
124    if (message_->IsVisible()) {
125      message_->SizeToFit(size.width() - kHorizontalPadding * 2);
126      gfx::Size message_size = message_->GetPreferredSize();
127      int message_y = size.height() - kVerticalPadding - message_size.height();
128      message_->SetBounds(kHorizontalPadding,
129                          message_y,
130                          message_size.width(),
131                          message_size.height());
132    }
133  }
134
135  // Throbber centered within the view.
136  views::Throbber* throbber_;
137
138  // Message, multiline, aligned to the bottom of the view.
139  views::Label* message_;
140
141  DISALLOW_COPY_AND_ASSIGN(CameraImageView);
142};
143
144
145TakePhotoView::TakePhotoView(Delegate* delegate)
146    : title_label_(NULL),
147      snapshot_button_(NULL),
148      user_image_(NULL),
149      is_capturing_(false),
150      show_title_(true),
151      delegate_(delegate) {
152}
153
154TakePhotoView::~TakePhotoView() {
155}
156
157void TakePhotoView::Init() {
158  if (show_title_) {
159    title_label_ = new views::Label(
160        UTF16ToWide(l10n_util::GetStringUTF16(IDS_USER_IMAGE_SCREEN_TITLE)));
161    title_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
162    title_label_->SetMultiLine(true);
163    CorrectLabelFontSize(title_label_);
164  }
165
166  user_image_ = new CameraImageView();
167  user_image_->SetImageSize(
168      gfx::Size(login::kUserImageSize, login::kUserImageSize));
169  user_image_->Init();
170
171  snapshot_button_ = new views::ImageButton(this);
172  snapshot_button_->SetFocusable(true);
173  snapshot_button_->SetImage(views::CustomButton::BS_NORMAL,
174                             ResourceBundle::GetSharedInstance().GetBitmapNamed(
175                                 IDR_USER_IMAGE_CAPTURE));
176  snapshot_button_->SetImage(views::CustomButton::BS_DISABLED,
177                             ResourceBundle::GetSharedInstance().GetBitmapNamed(
178                                 IDR_USER_IMAGE_CAPTURE_DISABLED));
179  snapshot_button_->SetAccessibleName(l10n_util::GetStringUTF16(
180      IDS_CHROMEOS_ACC_ACCOUNT_PICTURE));
181
182  InitLayout();
183  // Request focus only after the button is added to views hierarchy.
184  snapshot_button_->RequestFocus();
185}
186
187void TakePhotoView::InitLayout() {
188  views::GridLayout* layout = new views::GridLayout(this);
189  layout->SetInsets(GetInsets());
190  SetLayoutManager(layout);
191
192  // The title is left-top aligned.
193  if (show_title_) {
194    views::ColumnSet* column_set = layout->AddColumnSet(kTitleRow);
195    column_set->AddColumn(views::GridLayout::FILL,
196                          views::GridLayout::LEADING,
197                          1,
198                          views::GridLayout::USE_PREF, 0, 0);
199  }
200
201  // User image and snapshot button are centered horizontally.
202  views::ColumnSet* column_set = layout->AddColumnSet(kImageRow);
203  column_set->AddColumn(views::GridLayout::CENTER,
204                        views::GridLayout::LEADING,
205                        1,
206                        views::GridLayout::USE_PREF, 0, 0);
207
208  // Fill the layout with rows and views now.
209  if (show_title_) {
210    layout->StartRow(0, kTitleRow);
211    layout->AddView(title_label_);
212  }
213  layout->StartRowWithPadding(0, kImageRow, 0, kVerticalPadding);
214  layout->AddView(user_image_);
215  layout->StartRowWithPadding(1, kImageRow, 0, kVerticalPadding);
216  layout->AddView(snapshot_button_);
217}
218
219void TakePhotoView::UpdateVideoFrame(const SkBitmap& frame) {
220  if (!is_capturing_)
221    return;
222
223  if (!snapshot_button_->IsEnabled()) {
224    user_image_->SetNormalState();
225    snapshot_button_->SetEnabled(true);
226    snapshot_button_->RequestFocus();
227  }
228  SkBitmap user_image =
229      skia::ImageOperations::Resize(
230          frame,
231          skia::ImageOperations::RESIZE_BOX,
232          login::kUserImageSize,
233          login::kUserImageSize);
234
235  user_image_->SetImage(&user_image);
236}
237
238void TakePhotoView::ShowCameraInitializing() {
239  is_capturing_ = true;
240  snapshot_button_->SetImage(views::CustomButton::BS_NORMAL,
241                             ResourceBundle::GetSharedInstance().GetBitmapNamed(
242                                 IDR_USER_IMAGE_CAPTURE));
243  snapshot_button_->SetVisible(true);
244  snapshot_button_->SetEnabled(false);
245  user_image_->SetInitializingState();
246}
247
248void TakePhotoView::ShowCameraError() {
249  if (!is_capturing_)
250    return;
251  snapshot_button_->SetEnabled(false);
252  user_image_->SetErrorState();
253}
254
255const SkBitmap& TakePhotoView::GetImage() const {
256  return user_image_->GetImage();
257}
258
259void TakePhotoView::SetImage(SkBitmap* image) {
260  is_capturing_ = false;
261  snapshot_button_->SetVisible(false);
262  user_image_->SetNormalState();
263  user_image_->SetImage(image);
264}
265
266gfx::Size TakePhotoView::GetPreferredSize() {
267  return gfx::Size(width(), height());
268}
269
270void TakePhotoView::ButtonPressed(
271    views::Button* sender, const views::Event& event) {
272  DCHECK(delegate_);
273  DCHECK(sender == snapshot_button_);
274  is_capturing_ = !is_capturing_;
275  if (is_capturing_) {
276    snapshot_button_->SetImage(
277        views::CustomButton::BS_NORMAL,
278        ResourceBundle::GetSharedInstance().GetBitmapNamed(
279            IDR_USER_IMAGE_CAPTURE));
280    delegate_->OnCapturingStarted();
281  } else {
282    snapshot_button_->SetImage(
283        views::CustomButton::BS_NORMAL,
284        ResourceBundle::GetSharedInstance().GetBitmapNamed(
285            IDR_USER_IMAGE_RECYCLE));
286    delegate_->OnCapturingStopped();
287  }
288  snapshot_button_->SchedulePaint();
289}
290
291}  // namespace chromeos
292