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
23 namespace {
24
25 // Padding between horizontally neighboring elements.
26 const int kHorizontalPadding = 10;
27 // Padding between vertically neighboring elements.
28 const int kVerticalPadding = 10;
29 // Color of image view border.
30 const SkColor kImageBorderColor = SkColorSetARGB(38, 0, 0, 0);
31
32 // IDs of column sets for grid layout manager.
33 enum ColumnSets {
34 kTitleRow, // Column set for screen title.
35 kImageRow, // Column set for image from camera and snapshot button.
36 };
37
38 } // namespace
39
40 namespace chromeos {
41
42 // Image view that can show center throbber above itself or a message at its
43 // bottom.
44 class CameraImageView : public views::ImageView {
45 public:
CameraImageView()46 CameraImageView()
47 : throbber_(NULL),
48 message_(NULL) {}
49
~CameraImageView()50 ~CameraImageView() {}
51
Init()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
SetInitializingState()71 void SetInitializingState() {
72 ShowThrobber();
73 SetMessage(std::wstring());
74 SetImage(
75 ResourceBundle::GetSharedInstance().GetBitmapNamed(
76 IDR_USER_IMAGE_INITIALIZING));
77 }
78
SetNormalState()79 void SetNormalState() {
80 HideThrobber();
81 SetMessage(std::wstring());
82 }
83
SetErrorState()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:
ShowThrobber()93 void ShowThrobber() {
94 DCHECK(throbber_);
95 throbber_->SetVisible(true);
96 throbber_->Start();
97 }
98
HideThrobber()99 void HideThrobber() {
100 DCHECK(throbber_);
101 throbber_->Stop();
102 throbber_->SetVisible(false);
103 }
104
SetMessage(const std::wstring & message)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:
Layout()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
TakePhotoView(Delegate * delegate)145 TakePhotoView::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
~TakePhotoView()154 TakePhotoView::~TakePhotoView() {
155 }
156
Init()157 void 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
InitLayout()187 void 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
UpdateVideoFrame(const SkBitmap & frame)219 void 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
ShowCameraInitializing()238 void 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
ShowCameraError()248 void TakePhotoView::ShowCameraError() {
249 if (!is_capturing_)
250 return;
251 snapshot_button_->SetEnabled(false);
252 user_image_->SetErrorState();
253 }
254
GetImage() const255 const SkBitmap& TakePhotoView::GetImage() const {
256 return user_image_->GetImage();
257 }
258
SetImage(SkBitmap * image)259 void TakePhotoView::SetImage(SkBitmap* image) {
260 is_capturing_ = false;
261 snapshot_button_->SetVisible(false);
262 user_image_->SetNormalState();
263 user_image_->SetImage(image);
264 }
265
GetPreferredSize()266 gfx::Size TakePhotoView::GetPreferredSize() {
267 return gfx::Size(width(), height());
268 }
269
ButtonPressed(views::Button * sender,const views::Event & event)270 void 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