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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/ui/views/chrome_to_mobile_bubble_view.cc
diff --git a/chrome/browser/ui/views/chrome_to_mobile_bubble_view.cc b/chrome/browser/ui/views/chrome_to_mobile_bubble_view.cc
new file mode 100755
index 0000000000000000000000000000000000000000..417cc4959926820c2f87886d68cea59e22518889
--- /dev/null
+++ b/chrome/browser/ui/views/chrome_to_mobile_bubble_view.cc
@@ -0,0 +1,323 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/chrome_to_mobile_bubble_view.h"
+
+#include "base/bind.h"
+#include "base/file_util.h"
+#include "base/string16.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_to_mobile_service.h"
+#include "chrome/browser/chrome_to_mobile_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/views/window.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents.h"
+#include "grit/generated_resources.h"
+#include "grit/theme_resources.h"
+#include "ui/base/animation/throb_animation.h"
+#include "ui/base/keycodes/keyboard_codes.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/text/bytes_formatting.h"
+#include "ui/views/controls/button/checkbox.h"
+#include "ui/views/controls/button/radio_button.h"
+#include "ui/views/controls/button/text_button.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/events/event.h"
+#include "ui/views/layout/grid_layout.h"
+#include "ui/views/layout/layout_constants.h"
+
+using views::GridLayout;
+
+namespace {
+
+// The millisecond interval between refreshes of the "Sending..." progress text.
+const size_t kProgressThrobIntervalMS = 300;
+
+// The bubble's margin for the "Sending..." and "Sent" states.
+const size_t kProgressMargin = 20;
+
+// The snapshot path constant; combined with a guid to store the MHTML snapshot.
+const FilePath::CharType kSnapshotPath[] =
+ FILE_PATH_LITERAL("chrome_to_mobile_snapshot_.mhtml");
+
+void DeleteFilePath(const FilePath& file_path) {
+ bool success = file_util::Delete(file_path, false);
+ DCHECK(success);
+}
+
+} // namespace
+
+// Declared in browser_dialogs.h so callers don't have to depend on our header.
+
+namespace browser {
+
+void ShowChromeToMobileBubbleView(views::View* anchor_view, Profile* profile) {
+ ChromeToMobileBubbleView::ShowBubble(anchor_view, profile);
+}
+
+void HideChromeToMobileBubbleView() {
+ ChromeToMobileBubbleView::Hide();
+}
+
+bool IsChromeToMobileBubbleViewShowing() {
+ return ChromeToMobileBubbleView::IsShowing();
+}
+
+} // namespace browser
+
+// ChromeToMobileBubbleView ----------------------------------------------------
+
+ChromeToMobileBubbleView* ChromeToMobileBubbleView::bubble_ = NULL;
+
+// static
+void ChromeToMobileBubbleView::ShowBubble(views::View* anchor_view,
+ Profile* profile) {
+ if (IsShowing())
+ return;
+
+ bubble_ = new ChromeToMobileBubbleView(anchor_view, profile);
+ browser::CreateViewsBubble(bubble_);
+ bubble_->Show();
+}
+
+// static
+bool ChromeToMobileBubbleView::IsShowing() {
+ return bubble_ != NULL;
+}
+
+void ChromeToMobileBubbleView::Hide() {
+ if (IsShowing())
+ bubble_->GetWidget()->Close();
+}
+
+ChromeToMobileBubbleView::~ChromeToMobileBubbleView() {}
+
+views::View* ChromeToMobileBubbleView::GetInitiallyFocusedView() {
+ return send_;
+}
+
+gfx::Rect ChromeToMobileBubbleView::GetAnchorRect() {
+ // Compensate for some built-in padding in the arrow image.
+ gfx::Rect rect(BubbleDelegateView::GetAnchorRect());
+ rect.Inset(0, anchor_view() ? 5 : 0);
+ return rect;
+}
+
+void ChromeToMobileBubbleView::WindowClosing() {
+ // We have to reset |bubble_| here, not in our destructor, because we'll be
+ // destroyed asynchronously and the shown state will be checked before then.
+ DCHECK(bubble_ == this);
+ bubble_ = NULL;
+
+ if (!snapshot_path_.empty())
+ 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
+ base::Bind(&DeleteFilePath, snapshot_path_));
+}
+
+bool ChromeToMobileBubbleView::AcceleratorPressed(
+ const ui::Accelerator& accelerator) {
+ if (accelerator.key_code() == ui::VKEY_RETURN &&
+ (send_->HasFocus() || cancel_->HasFocus())) {
+ HandleButtonPressed(send_->HasFocus() ? send_ : cancel_);
+ return true;
+ }
+ return BubbleDelegateView::AcceleratorPressed(accelerator);
+}
+
+void ChromeToMobileBubbleView::OnSendComplete(bool success) {
+ progress_animation_->Stop();
+ progress_label_->SetText(l10n_util::GetStringUTF16(success ?
+ IDS_CHROME_TO_MOBILE_BUBBLE_SENT : IDS_CHROME_TO_MOBILE_BUBBLE_ERROR));
+ Layout();
+}
+
+void ChromeToMobileBubbleView::AnimationProgressed(
+ const ui::Animation* animation) {
+ if (animation == progress_animation_.get()) {
+ // Show the "Sending" string with 0-3 trailing periods "..."
+ progress_periods_count_ = (progress_periods_count_ + 1) % 4;
+ string16 text = progress_label_->GetText();
+ if (progress_periods_count_ == 0)
+ progress_label_->SetText(text.substr(0, text.length() - 3));
+ else
+ 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
+ // Run Layout now but do not resize the bubble when adding/removing periods.
+ Layout();
+ return;
+ }
+ views::BubbleDelegateView::AnimationProgressed(animation);
+}
+
+void ChromeToMobileBubbleView::SnapshotGenerated(const FilePath& snapshot_path,
+ int64 snapshot_bytes) {
+ snapshot_path_ = snapshot_path;
+ send_copy_->SetText(
+ l10n_util::GetStringFUTF16(IDS_CHROME_TO_MOBILE_BUBBLE_SEND_COPY,
+ ui::FormatBytes(snapshot_bytes)));
+ send_copy_->SetEnabled(snapshot_bytes > 0);
+ Layout();
+}
+
+void ChromeToMobileBubbleView::Init() {
+ GridLayout* layout = new GridLayout(this);
+ SetLayoutManager(layout);
+
+ const size_t single_column_set_id = 0;
+ views::ColumnSet* cs = layout->AddColumnSet(single_column_set_id);
+ cs->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0,
+ GridLayout::USE_PREF, 0, 0);
+ cs->AddPaddingColumn(1, 0);
+
+ const size_t button_column_set_id = 1;
+ cs = layout->AddColumnSet(button_column_set_id);
+ cs->AddPaddingColumn(1, 0);
+ cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0,
+ GridLayout::USE_PREF, 0, 0);
+ // Subtract 2px for the natural button padding and to correspond with row
+ // separation height; like BookmarkBubbleView.
+ cs->AddPaddingColumn(0, views::kRelatedButtonHSpacing - 2);
+ cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0,
+ GridLayout::USE_PREF, 0, 0);
+
+ std::vector<DictionaryValue*> mobiles =
+ ChromeToMobileServiceFactory::GetForProfile(profile_)->mobiles();
+ DCHECK_GT(mobiles.size(), 0U);
+ layout->StartRow(0, single_column_set_id);
+ if (mobiles.size() == 1) {
+ selected_mobile_ = mobiles[0];
+ string16 mobile_name;
+ mobiles[0]->GetString("name", &mobile_name);
+ layout->AddView(new views::Label(
+ l10n_util::GetStringFUTF16(IDS_CHROME_TO_MOBILE_BUBBLE_SINGLE_TITLE,
+ mobile_name)));
+ } else {
+ layout->AddView(new views::Label(
+ l10n_util::GetStringUTF16(IDS_CHROME_TO_MOBILE_BUBBLE_MULTI_TITLE)));
+
+ const size_t radio_column_set_id = 2;
+ cs = layout->AddColumnSet(radio_column_set_id);
+ cs->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing);
+ cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+
+ views::RadioButton* radio;
+ layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing);
+ for (std::vector<DictionaryValue*>::const_iterator it = mobiles.begin();
+ it != mobiles.end(); ++it) {
+ string16 name;
+ (*it)->GetString("name", &name);
+ radio = new views::RadioButton(name, 0);
+ radio->set_listener(this);
+ mobile_map_[radio] = *it;
+ layout->StartRow(0, radio_column_set_id);
+ layout->AddView(radio);
+ }
+ mobile_map_.begin()->first->SetChecked(true);
+ selected_mobile_ = mobile_map_.begin()->second;
+ }
+
+ send_copy_ = new views::Checkbox(
+ l10n_util::GetStringFUTF16(IDS_CHROME_TO_MOBILE_BUBBLE_SEND_COPY,
+ l10n_util::GetStringUTF16(
+ IDS_CHROME_TO_MOBILE_BUBBLE_SEND_COPY_GENERATING)));
+ send_copy_->SetEnabled(false);
+ layout->StartRow(0, single_column_set_id);
+ layout->AddView(send_copy_);
+
+ layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing);
+ send_ = new views::NativeTextButton(
+ this, l10n_util::GetStringUTF16(IDS_CHROME_TO_MOBILE_BUBBLE_SEND));
+ send_->SetIsDefault(true);
+ cancel_ = new views::NativeTextButton(
+ this, l10n_util::GetStringUTF16(IDS_CANCEL));
+ layout->StartRow(0, button_column_set_id);
+ layout->AddView(send_);
+ layout->AddView(cancel_);
+
+ AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, 0));
+}
+
+ChromeToMobileBubbleView::ChromeToMobileBubbleView(views::View* anchor_view,
+ Profile* profile)
+ : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT),
+ ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
+ profile_(profile),
+ snapshot_path_(kSnapshotPath),
+ send_copy_(NULL),
+ send_(NULL),
+ cancel_(NULL),
+ progress_label_(NULL),
+ progress_periods_count_(0) {
+ // Generate the MHTML snapshot immediately to report its size in the bubble.
+ content::WebContents* web_contents =
+ BrowserList::GetLastActiveWithProfile(profile_)->GetSelectedWebContents();
+ web_contents->GenerateMHTML(
+ FilePath(kSnapshotPath).InsertBeforeExtensionASCII(guid::GenerateGUID()),
+ base::Bind(&ChromeToMobileBubbleView::SnapshotGenerated,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ChromeToMobileBubbleView::ButtonPressed(views::Button* sender,
+ const views::Event& event) {
+ HandleButtonPressed(sender);
+}
+
+void ChromeToMobileBubbleView::HandleButtonPressed(views::Button* sender) {
+ if (sender == send_) {
+ Send();
+ } else if (sender == cancel_) {
+ GetWidget()->Close();
+ } else {
+ // The sender is a mobile radio button
+ views::RadioButton* radio = static_cast<views::RadioButton*>(sender);
+ DCHECK(mobile_map_.find(radio) != mobile_map_.end());
+ selected_mobile_ = mobile_map_.find(radio)->second;
+ }
+}
+
+void ChromeToMobileBubbleView::Send() {
+ string16 mobile_id;
+ selected_mobile_->GetString("id", &mobile_id);
+ content::WebContents* web_contents =
+ BrowserList::GetLastActiveWithProfile(profile_)->GetSelectedWebContents();
+ ChromeToMobileServiceFactory::GetForProfile(profile_)->SendToMobile(
+ mobile_id, web_contents->GetURL(), web_contents->GetTitle(),
+ send_copy_->checked() ? snapshot_path_ : FilePath(),
+ weak_ptr_factory_.GetWeakPtr());
+ // Pass ownership of the temp MHTML file to the service, if sending a copy.
+ if (send_copy_->checked())
+ snapshot_path_.clear();
+
+ // Re-initialize the view's contents to show progress sending the page.
+ 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.
+ GridLayout* layout = new GridLayout(this);
+ SetLayoutManager(layout);
+
+ const size_t single_column_set_id = 0;
+ views::ColumnSet* cs = layout->AddColumnSet(single_column_set_id);
+ cs->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0,
+ GridLayout::USE_PREF, 0, 0);
+ set_margin(kProgressMargin);
+
+ // Start the string off with its three periods to properly size the bubble.
+ layout->StartRow(0, single_column_set_id);
+ progress_periods_count_ = 3;
+ progress_label_ = new views::Label(
+ l10n_util::GetStringUTF16(IDS_CHROME_TO_MOBILE_BUBBLE_SENDING).append(
+ ASCIIToUTF16("...")));
+ layout->AddView(progress_label_);
+ SizeToContents();
+
+ progress_animation_.reset(new ui::ThrobAnimation(this));
+ 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
+ base::TimeDelta::FromMilliseconds(kProgressThrobIntervalMS));
+ progress_animation_->StartThrobbing(-1);
+}

Powered by Google App Engine
This is Rietveld 408576698