Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(137)

Side by Side Diff: chrome/browser/ui/views/chrome_to_mobile_bubble_view.cc

Issue 9443007: Add Chrome To Mobile Service and Views Page Action. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Put shared CloudPrint consts/helpers in chrome/common/; use CloudPrintURL. Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 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/views/chrome_to_mobile_bubble_view.h"
6
7 #include "base/bind.h"
8 #include "base/file_util.h"
9 #include "base/string16.h"
10 #include "base/utf_string_conversions.h"
11 #include "base/values.h"
12 #include "chrome/app/chrome_command_ids.h"
13 #include "chrome/browser/browser_process.h"
14 #include "chrome/browser/chrome_to_mobile_service.h"
15 #include "chrome/browser/chrome_to_mobile_service_factory.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/ui/browser.h"
18 #include "chrome/browser/ui/browser_list.h"
19 #include "chrome/browser/ui/views/window.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/browser/web_contents.h"
22 #include "grit/generated_resources.h"
23 #include "grit/theme_resources.h"
24 #include "ui/base/animation/throb_animation.h"
25 #include "ui/base/keycodes/keyboard_codes.h"
26 #include "ui/base/l10n/l10n_util.h"
27 #include "ui/base/resource/resource_bundle.h"
28 #include "ui/base/text/bytes_formatting.h"
29 #include "ui/views/controls/button/checkbox.h"
30 #include "ui/views/controls/button/radio_button.h"
31 #include "ui/views/controls/button/text_button.h"
32 #include "ui/views/controls/label.h"
33 #include "ui/views/events/event.h"
34 #include "ui/views/layout/grid_layout.h"
35 #include "ui/views/layout/layout_constants.h"
36
37 using views::GridLayout;
38
39 namespace {
40
41 // The millisecond interval between refreshes of the "Sending..." progress text.
42 const size_t kProgressThrobIntervalMS = 300;
43
44 // The bubble's margin for the "Sending..." and "Sent" states.
45 const size_t kProgressMargin = 20;
46
47 // The snapshot path constant; combined with a guid to store the MHTML snapshot.
48 const FilePath::CharType kSnapshotPath[] =
49 FILE_PATH_LITERAL("chrome_to_mobile_snapshot_.mhtml");
50
51 void DeleteFilePath(const FilePath& file_path) {
52 bool success = file_util::Delete(file_path, false);
53 DCHECK(success);
54 }
55
56 } // namespace
57
58 // Declared in browser_dialogs.h so callers don't have to depend on our header.
59
60 namespace browser {
61
62 void ShowChromeToMobileBubbleView(views::View* anchor_view, Profile* profile) {
63 ChromeToMobileBubbleView::ShowBubble(anchor_view, profile);
64 }
65
66 void HideChromeToMobileBubbleView() {
67 ChromeToMobileBubbleView::Hide();
68 }
69
70 bool IsChromeToMobileBubbleViewShowing() {
71 return ChromeToMobileBubbleView::IsShowing();
72 }
73
74 } // namespace browser
75
76 // ChromeToMobileBubbleView ----------------------------------------------------
77
78 ChromeToMobileBubbleView* ChromeToMobileBubbleView::bubble_ = NULL;
79
80 // static
81 void ChromeToMobileBubbleView::ShowBubble(views::View* anchor_view,
82 Profile* profile) {
83 if (IsShowing())
84 return;
85
86 bubble_ = new ChromeToMobileBubbleView(anchor_view, profile);
87 browser::CreateViewsBubble(bubble_);
88 bubble_->Show();
89 }
90
91 // static
92 bool ChromeToMobileBubbleView::IsShowing() {
93 return bubble_ != NULL;
94 }
95
96 void ChromeToMobileBubbleView::Hide() {
97 if (IsShowing())
98 bubble_->GetWidget()->Close();
99 }
100
101 ChromeToMobileBubbleView::~ChromeToMobileBubbleView() {}
102
103 views::View* ChromeToMobileBubbleView::GetInitiallyFocusedView() {
104 return send_;
105 }
106
107 gfx::Rect ChromeToMobileBubbleView::GetAnchorRect() {
108 // Compensate for some built-in padding in the arrow image.
109 gfx::Rect rect(BubbleDelegateView::GetAnchorRect());
110 rect.Inset(0, anchor_view() ? 5 : 0);
111 return rect;
112 }
113
114 void ChromeToMobileBubbleView::WindowClosing() {
115 // We have to reset |bubble_| here, not in our destructor, because we'll be
116 // destroyed asynchronously and the shown state will be checked before then.
117 DCHECK(bubble_ == this);
118 bubble_ = NULL;
119
120 if (!snapshot_path_.empty())
121 content::BrowserThread::PostBlockingPoolTask(FROM_HERE,
sky 2012/03/11 21:41:22 I'm not familiar with this, does it black the UI t
msw 2012/03/12 10:17:18 Nope, See the decl; this is the new FILE thread pa
122 base::Bind(&DeleteFilePath, snapshot_path_));
123 }
124
125 bool ChromeToMobileBubbleView::AcceleratorPressed(
126 const ui::Accelerator& accelerator) {
127 if (accelerator.key_code() == ui::VKEY_RETURN &&
128 (send_->HasFocus() || cancel_->HasFocus())) {
129 HandleButtonPressed(send_->HasFocus() ? send_ : cancel_);
130 return true;
131 }
132 return BubbleDelegateView::AcceleratorPressed(accelerator);
133 }
134
135 void ChromeToMobileBubbleView::OnSendComplete(bool success) {
136 progress_animation_->Stop();
137 progress_label_->SetText(l10n_util::GetStringUTF16(success ?
138 IDS_CHROME_TO_MOBILE_BUBBLE_SENT : IDS_CHROME_TO_MOBILE_BUBBLE_ERROR));
139 Layout();
140 }
141
142 void ChromeToMobileBubbleView::AnimationProgressed(
143 const ui::Animation* animation) {
144 if (animation == progress_animation_.get()) {
145 // Show the "Sending" string with 0-3 trailing periods "..."
146 progress_periods_count_ = (progress_periods_count_ + 1) % 4;
147 string16 text = progress_label_->GetText();
148 if (progress_periods_count_ == 0)
149 progress_label_->SetText(text.substr(0, text.length() - 3));
150 else
151 progress_label_->SetText(text.append(ASCIIToUTF16(".")));
sky 2012/03/11 21:41:22 I suspect that adding a '.' to the end of the stri
msw 2012/03/12 10:17:18 I'm now using multiple IDS messages for internatio
152 // Run Layout now but do not resize the bubble when adding/removing periods.
153 Layout();
154 return;
155 }
156 views::BubbleDelegateView::AnimationProgressed(animation);
157 }
158
159 void ChromeToMobileBubbleView::SnapshotGenerated(const FilePath& snapshot_path,
160 int64 snapshot_bytes) {
161 snapshot_path_ = snapshot_path;
162 send_copy_->SetText(
163 l10n_util::GetStringFUTF16(IDS_CHROME_TO_MOBILE_BUBBLE_SEND_COPY,
164 ui::FormatBytes(snapshot_bytes)));
165 send_copy_->SetEnabled(snapshot_bytes > 0);
166 Layout();
167 }
168
169 void ChromeToMobileBubbleView::Init() {
170 GridLayout* layout = new GridLayout(this);
171 SetLayoutManager(layout);
172
173 const size_t single_column_set_id = 0;
174 views::ColumnSet* cs = layout->AddColumnSet(single_column_set_id);
175 cs->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0,
176 GridLayout::USE_PREF, 0, 0);
177 cs->AddPaddingColumn(1, 0);
178
179 const size_t button_column_set_id = 1;
180 cs = layout->AddColumnSet(button_column_set_id);
181 cs->AddPaddingColumn(1, 0);
182 cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0,
183 GridLayout::USE_PREF, 0, 0);
184 // Subtract 2px for the natural button padding and to correspond with row
185 // separation height; like BookmarkBubbleView.
186 cs->AddPaddingColumn(0, views::kRelatedButtonHSpacing - 2);
187 cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0,
188 GridLayout::USE_PREF, 0, 0);
189
190 std::vector<DictionaryValue*> mobiles =
191 ChromeToMobileServiceFactory::GetForProfile(profile_)->mobiles();
192 DCHECK_GT(mobiles.size(), 0U);
193 layout->StartRow(0, single_column_set_id);
194 if (mobiles.size() == 1) {
195 selected_mobile_ = mobiles[0];
196 string16 mobile_name;
197 mobiles[0]->GetString("name", &mobile_name);
198 layout->AddView(new views::Label(
199 l10n_util::GetStringFUTF16(IDS_CHROME_TO_MOBILE_BUBBLE_SINGLE_TITLE,
200 mobile_name)));
201 } else {
202 layout->AddView(new views::Label(
203 l10n_util::GetStringUTF16(IDS_CHROME_TO_MOBILE_BUBBLE_MULTI_TITLE)));
204
205 const size_t radio_column_set_id = 2;
206 cs = layout->AddColumnSet(radio_column_set_id);
207 cs->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing);
208 cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
209 GridLayout::USE_PREF, 0, 0);
210
211 views::RadioButton* radio;
212 layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing);
213 for (std::vector<DictionaryValue*>::const_iterator it = mobiles.begin();
214 it != mobiles.end(); ++it) {
215 string16 name;
216 (*it)->GetString("name", &name);
217 radio = new views::RadioButton(name, 0);
218 radio->set_listener(this);
219 mobile_map_[radio] = *it;
220 layout->StartRow(0, radio_column_set_id);
221 layout->AddView(radio);
222 }
223 mobile_map_.begin()->first->SetChecked(true);
224 selected_mobile_ = mobile_map_.begin()->second;
225 }
226
227 send_copy_ = new views::Checkbox(
228 l10n_util::GetStringFUTF16(IDS_CHROME_TO_MOBILE_BUBBLE_SEND_COPY,
229 l10n_util::GetStringUTF16(
230 IDS_CHROME_TO_MOBILE_BUBBLE_SEND_COPY_GENERATING)));
231 send_copy_->SetEnabled(false);
232 layout->StartRow(0, single_column_set_id);
233 layout->AddView(send_copy_);
234
235 layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing);
236 send_ = new views::NativeTextButton(
237 this, l10n_util::GetStringUTF16(IDS_CHROME_TO_MOBILE_BUBBLE_SEND));
238 send_->SetIsDefault(true);
239 cancel_ = new views::NativeTextButton(
240 this, l10n_util::GetStringUTF16(IDS_CANCEL));
241 layout->StartRow(0, button_column_set_id);
242 layout->AddView(send_);
243 layout->AddView(cancel_);
244
245 AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, 0));
246 }
247
248 ChromeToMobileBubbleView::ChromeToMobileBubbleView(views::View* anchor_view,
249 Profile* profile)
250 : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT),
251 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
252 profile_(profile),
253 snapshot_path_(kSnapshotPath),
254 send_copy_(NULL),
255 send_(NULL),
256 cancel_(NULL),
257 progress_label_(NULL),
258 progress_periods_count_(0) {
259 // Generate the MHTML snapshot immediately to report its size in the bubble.
260 content::WebContents* web_contents =
261 BrowserList::GetLastActiveWithProfile(profile_)->GetSelectedWebContents();
262 web_contents->GenerateMHTML(
263 FilePath(kSnapshotPath).InsertBeforeExtensionASCII(guid::GenerateGUID()),
264 base::Bind(&ChromeToMobileBubbleView::SnapshotGenerated,
265 weak_ptr_factory_.GetWeakPtr()));
266 }
267
268 void ChromeToMobileBubbleView::ButtonPressed(views::Button* sender,
269 const views::Event& event) {
270 HandleButtonPressed(sender);
271 }
272
273 void ChromeToMobileBubbleView::HandleButtonPressed(views::Button* sender) {
274 if (sender == send_) {
275 Send();
276 } else if (sender == cancel_) {
277 GetWidget()->Close();
278 } else {
279 // The sender is a mobile radio button
280 views::RadioButton* radio = static_cast<views::RadioButton*>(sender);
281 DCHECK(mobile_map_.find(radio) != mobile_map_.end());
282 selected_mobile_ = mobile_map_.find(radio)->second;
283 }
284 }
285
286 void ChromeToMobileBubbleView::Send() {
287 string16 mobile_id;
288 selected_mobile_->GetString("id", &mobile_id);
289 content::WebContents* web_contents =
290 BrowserList::GetLastActiveWithProfile(profile_)->GetSelectedWebContents();
291 ChromeToMobileServiceFactory::GetForProfile(profile_)->SendToMobile(
292 mobile_id, web_contents->GetURL(), web_contents->GetTitle(),
293 send_copy_->checked() ? snapshot_path_ : FilePath(),
294 weak_ptr_factory_.GetWeakPtr());
295 // Pass ownership of the temp MHTML file to the service, if sending a copy.
296 if (send_copy_->checked())
297 snapshot_path_.clear();
298
299 // Re-initialize the view's contents to show progress sending the page.
300 RemoveAllChildViews(true);
sky 2012/03/11 21:41:22 Reset the fields that point to views this deletes.
msw 2012/03/12 10:17:18 Done.
301 GridLayout* layout = new GridLayout(this);
302 SetLayoutManager(layout);
303
304 const size_t single_column_set_id = 0;
305 views::ColumnSet* cs = layout->AddColumnSet(single_column_set_id);
306 cs->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0,
307 GridLayout::USE_PREF, 0, 0);
308 set_margin(kProgressMargin);
309
310 // Start the string off with its three periods to properly size the bubble.
311 layout->StartRow(0, single_column_set_id);
312 progress_periods_count_ = 3;
313 progress_label_ = new views::Label(
314 l10n_util::GetStringUTF16(IDS_CHROME_TO_MOBILE_BUBBLE_SENDING).append(
315 ASCIIToUTF16("...")));
316 layout->AddView(progress_label_);
317 SizeToContents();
318
319 progress_animation_.reset(new ui::ThrobAnimation(this));
320 progress_animation_->set_timer_interval(
sky 2012/03/11 21:41:22 I think you want SetDuration and don't add set_tim
msw 2012/03/12 10:17:18 Done. Would a RepeatingTimer be better than an Ani
sky 2012/03/12 15:28:12 Either one works, but we typically use animation f
321 base::TimeDelta::FromMilliseconds(kProgressThrobIntervalMS));
322 progress_animation_->StartThrobbing(-1);
323 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698