diff options
author | cpu@chromium.org <cpu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-31 20:00:53 +0000 |
---|---|---|
committer | cpu@chromium.org <cpu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-31 20:00:53 +0000 |
commit | d89125b57f35ed83db5be9bad08277e47aa506d2 (patch) | |
tree | a12bd68d32d06fb65907c579d85c2f37637af80d | |
parent | 9514bd914ec605bd0c6eb2f52c0dd46c5566b640 (diff) | |
download | chromium_src-d89125b57f35ed83db5be9bad08277e47aa506d2.zip chromium_src-d89125b57f35ed83db5be9bad08277e47aa506d2.tar.gz chromium_src-d89125b57f35ed83db5be9bad08277e47aa506d2.tar.bz2 |
The new toast experiments.
I've done some generalizing of the code and added the bits to show the new
UI designs. Also fixed some bugs. Gone is the idea of 'compact' toast and now
we just specify the flags we want.
BUG=129499
TEST=see bug
Review URL: https://chromiumcodereview.appspot.com/10821007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@149257 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/first_run/try_chrome_dialog_view.cc | 104 | ||||
-rw-r--r-- | chrome/browser/first_run/try_chrome_dialog_view.h | 33 | ||||
-rw-r--r-- | chrome/common/attrition_experiments.h | 12 | ||||
-rw-r--r-- | chrome/installer/util/browser_distribution.h | 10 | ||||
-rw-r--r-- | chrome/installer/util/google_chrome_distribution.cc | 125 |
5 files changed, 174 insertions, 110 deletions
diff --git a/chrome/browser/first_run/try_chrome_dialog_view.cc b/chrome/browser/first_run/try_chrome_dialog_view.cc index 3d74836..be3c7dd 100644 --- a/chrome/browser/first_run/try_chrome_dialog_view.cc +++ b/chrome/browser/first_run/try_chrome_dialog_view.cc @@ -18,6 +18,7 @@ #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/image/image.h" +#include "ui/views/controls/button/checkbox.h" #include "ui/views/controls/button/image_button.h" #include "ui/views/controls/button/radio_button.h" #include "ui/views/controls/button/text_button.h" @@ -32,6 +33,14 @@ namespace { const wchar_t kHelpCenterUrl[] = L"https://www.google.com/support/chrome/bin/answer.py?answer=150752"; +enum ButtonTags { + BT_NONE, + BT_CLOSE_BUTTON, + BT_OK_BUTTON, + BT_TRY_IT_RADIO, + BT_DONT_BUG_RADIO +}; + } // namespace // static @@ -53,7 +62,8 @@ TryChromeDialogView::TryChromeDialogView(size_t flavor) try_chrome_(NULL), kill_chrome_(NULL), dont_try_chrome_(NULL), - result_(COUNT) { + make_default_(NULL), + result_(COUNT) { } TryChromeDialogView::~TryChromeDialogView() { @@ -90,8 +100,8 @@ TryChromeDialogView::Result TryChromeDialogView::ShowModal( return DIALOG_ERROR; } root_view->SetLayoutManager(layout); - views::ColumnSet* columns; + // First row: [icon][pad][text][button]. columns = layout->AddColumnSet(0); columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::LEADING, 0, @@ -102,37 +112,49 @@ TryChromeDialogView::Result TryChromeDialogView::ShowModal( views::GridLayout::USE_PREF, 0, 0); columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::FILL, 1, views::GridLayout::USE_PREF, 0, 0); - // Second row: [pad][pad][radio 1]. + + // Optional second row: [pad][pad][radio 1]. columns = layout->AddColumnSet(1); columns->AddPaddingColumn(0, icon_size.width()); columns->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing); columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1, views::GridLayout::USE_PREF, 0, 0); + // Third row: [pad][pad][radio 2]. columns = layout->AddColumnSet(2); columns->AddPaddingColumn(0, icon_size.width()); columns->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing); columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1, views::GridLayout::USE_PREF, 0, 0); + // Fourth row: [pad][pad][button][pad][button]. columns = layout->AddColumnSet(3); columns->AddPaddingColumn(0, icon_size.width()); - columns->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing); columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 0, views::GridLayout::USE_PREF, 0, 0); columns->AddPaddingColumn(0, views::kRelatedButtonHSpacing); columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 0, views::GridLayout::USE_PREF, 0, 0); + // Fifth row: [pad][pad][link]. columns = layout->AddColumnSet(4); columns->AddPaddingColumn(0, icon_size.width()); columns->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing); columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1, views::GridLayout::USE_PREF, 0, 0); + // Optional fourth row: [button]. columns = layout->AddColumnSet(5); columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::FILL, 1, views::GridLayout::USE_PREF, 0, 0); + + // Optional fourth row: [pad][pad][checkbox]. + columns = layout->AddColumnSet(6); + columns->AddPaddingColumn(0, icon_size.width()); + columns->AddPaddingColumn(0, + views::kRelatedControlHorizontalSpacing + views::kPanelHorizIndentation); + columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1, + views::GridLayout::USE_PREF, 0, 0); // First row views. layout->StartRow(0, 0); layout->AddView(icon); @@ -171,45 +193,59 @@ TryChromeDialogView::Result TryChromeDialogView::ShowModal( const string16 try_it(l10n_util::GetStringUTF16(IDS_TRY_TOAST_TRY_OPT)); layout->StartRowWithPadding(0, 1, 0, 10); try_chrome_ = new views::RadioButton(try_it, 1); - layout->AddView(try_chrome_); try_chrome_->SetChecked(true); + try_chrome_->set_tag(BT_TRY_IT_RADIO); + try_chrome_->set_listener(this); + layout->AddView(try_chrome_); - // Third row views. - layout->StartRow(0, 2); - if (experiment.compact_bubble) { - // The compact bubble has, as its second radio button, "Don't bug me". + // Decide if the don't bug me is a button or a radio button. + bool dont_bug_me_button = + experiment.flags & BrowserDistribution::kDontBugMeAsButton ? true : false; + + // Optional third and fourth row views. + if (!dont_bug_me_button) { + layout->StartRow(0, 1); const string16 decline(l10n_util::GetStringUTF16(IDS_TRY_TOAST_CANCEL)); dont_try_chrome_ = new views::RadioButton(decline, 1); + dont_try_chrome_->set_tag(BT_DONT_BUG_RADIO); + dont_try_chrome_->set_listener(this); layout->AddView(dont_try_chrome_); - } else { - // The regular bubble has, as its second radio button, "Uninstall Chrome". + } + if (experiment.flags & BrowserDistribution::kUninstall) { + layout->StartRow(0, 2); const string16 kill_it(l10n_util::GetStringUTF16(IDS_UNINSTALL_CHROME)); kill_chrome_ = new views::RadioButton(kill_it, 1); layout->AddView(kill_chrome_); } + if (experiment.flags & BrowserDistribution::kMakeDefault) { + layout->StartRow(0, 6); + const string16 default_text( + l10n_util::GetStringUTF16(IDS_TRY_TOAST_SET_DEFAULT)); + make_default_ = new views::Checkbox(default_text); + gfx::Font font = make_default_->font().DeriveFont(0, gfx::Font::ITALIC); + make_default_->SetFont(font); + make_default_->SetChecked(true); + layout->AddView(make_default_); + } - // Fourth row views. + // Button row, the last or next to last depending on the 'why?' link. const string16 ok_it(l10n_util::GetStringUTF16(IDS_OK)); - const string16 cancel_it(l10n_util::GetStringUTF16(IDS_TRY_TOAST_CANCEL)); - const string16 why_this(l10n_util::GetStringUTF16(IDS_TRY_TOAST_WHY)); views::Button* accept_button = new views::NativeTextButton(this, ok_it); accept_button->set_tag(BT_OK_BUTTON); - // The compact bubble uses a centered button column for buttons, since only - // the OK button appears. - int column_id_buttons = experiment.compact_bubble ? 5 : 3; - layout->StartRowWithPadding(0, column_id_buttons, 0, 10); + layout->StartRowWithPadding(0, dont_bug_me_button ? 3 : 5, 0, 10); layout->AddView(accept_button); - if (!experiment.compact_bubble) { - // The regular bubble needs a "Don't bug me" as a button, since it is not - // one of the options for the radio buttons. We also decided to include the - // "Why am I seeing this?" link for the regular bubble only. + if (dont_bug_me_button) { + // The bubble needs a "Don't bug me" as a button or as a radio button, this + // this the button case. + const string16 cancel_it(l10n_util::GetStringUTF16(IDS_TRY_TOAST_CANCEL)); views::Button* cancel_button = new views::NativeTextButton(this, cancel_it); cancel_button->set_tag(BT_CLOSE_BUTTON); layout->AddView(cancel_button); - - // Fifth row views. + } + if (experiment.flags & BrowserDistribution::kWhyLink) { layout->StartRowWithPadding(0, 4, 0, 10); + const string16 why_this(l10n_util::GetStringUTF16(IDS_TRY_TOAST_WHY)); views::Link* link = new views::Link(why_this); link->set_listener(this); layout->AddView(link); @@ -269,14 +305,26 @@ void TryChromeDialogView::SetToastRegion(HWND window, int w, int h) { void TryChromeDialogView::ButtonPressed(views::Button* sender, const views::Event& event) { - if (sender->tag() == BT_CLOSE_BUTTON) { + if (sender->tag() == BT_DONT_BUG_RADIO) { + if (make_default_) { + make_default_->SetChecked(false); + make_default_->SetState(views::CustomButton::BS_DISABLED); + } + return; + } else if (sender->tag() == BT_TRY_IT_RADIO) { + if (make_default_) { + make_default_->SetChecked(true); + make_default_->SetState(views::CustomButton::BS_NORMAL); + } + return; + } else if (sender->tag() == BT_CLOSE_BUTTON) { // The user pressed cancel or the [x] button. result_ = NOT_NOW; } else if (!try_chrome_) { // We don't have radio buttons, the user pressed ok. result_ = TRY_CHROME; } else { - // The outcome is according to the selected ratio button. + // The outcome is according to the selected radio button. if (try_chrome_->checked()) result_ = TRY_CHROME; else if (dont_try_chrome_ && dont_try_chrome_->checked()) @@ -286,6 +334,10 @@ void TryChromeDialogView::ButtonPressed(views::Button* sender, else NOTREACHED() << "Unknown radio button selected"; } + + if ((result_ == TRY_CHROME) && make_default_->checked()) + result_ = TRY_CHROME_AS_DEFAULT; + popup_->Close(); MessageLoop::current()->Quit(); } diff --git a/chrome/browser/first_run/try_chrome_dialog_view.h b/chrome/browser/first_run/try_chrome_dialog_view.h index 77f90bc..d0a5aa5 100644 --- a/chrome/browser/first_run/try_chrome_dialog_view.h +++ b/chrome/browser/first_run/try_chrome_dialog_view.h @@ -19,6 +19,7 @@ class Rect; namespace views { class RadioButton; +class Checkbox; class Widget; } @@ -27,23 +28,32 @@ class Widget; // resulting actions are up to the caller. One flavor looks like this: // // +-----------------------------------------------+ -// | |icon| You stopped using Google Chrome [x] | -// | |icon| Would you like to: | -// | [o] Give the new version a try | +// | |icon| There is a new, safer version [x] | +// | |icon| of Google Chrome available | +// | [o] Try it out (already installed) | // | [ ] Uninstall Google Chrome | // | [ OK ] [Don't bug me] | -// | | // | _why_am_I_seeing this?_ | // +-----------------------------------------------+ // +// Another flavor looks like: +// +-----------------------------------------------+ +// | |icon| There is a new, safer version [x] | +// | |icon| of Google Chrome available | +// | [o] Try it out (already installed) | +// | [ ] Don't bug me | +// | [ OK ] | +// +-----------------------------------------------+ +// class TryChromeDialogView : public views::ButtonListener, public views::LinkListener { public: enum Result { - TRY_CHROME, // Launch chrome right now. - NOT_NOW, // Don't launch chrome. Exit now. - UNINSTALL_CHROME, // Initiate chrome uninstall and exit. - DIALOG_ERROR, // An error occurred creating the dialog. + TRY_CHROME, // Launch chrome right now. + TRY_CHROME_AS_DEFAULT, // Launch chrome and make it the default. + NOT_NOW, // Don't launch chrome. Exit now. + UNINSTALL_CHROME, // Initiate chrome uninstall and exit. + DIALOG_ERROR, // An error occurred creating the dialog. COUNT }; @@ -59,12 +69,6 @@ class TryChromeDialogView : public views::ButtonListener, static Result Show(size_t flavor, ProcessSingleton* process_singleton); private: - enum ButtonTags { - BT_NONE, - BT_CLOSE_BUTTON, - BT_OK_BUTTON, - }; - explicit TryChromeDialogView(size_t flavor); virtual ~TryChromeDialogView(); @@ -101,6 +105,7 @@ class TryChromeDialogView : public views::ButtonListener, views::RadioButton* try_chrome_; views::RadioButton* kill_chrome_; views::RadioButton* dont_try_chrome_; + views::Checkbox* make_default_; Result result_; DISALLOW_COPY_AND_ASSIGN(TryChromeDialogView); diff --git a/chrome/common/attrition_experiments.h b/chrome/common/attrition_experiments.h index e3dec6b..1404b49 100644 --- a/chrome/common/attrition_experiments.h +++ b/chrome/common/attrition_experiments.h @@ -18,18 +18,8 @@ enum Experiment { kSkype1 = IDS_TRY_TOAST_HEADING_SKYPE, }; -// This is used to match against locale and brands, and represents any -// locale/brand. -const wchar_t kAll[] = L"*"; - // A comma-separated list of brand codes that are associated with Skype. -const wchar_t kSkype[] = L"SKPC,SKPG,SKPH,SKPI,SKPL,SKPM,SKPN"; - -// The brand code for enterprise installations. -const wchar_t kEnterprise[] = L"GGRV"; - -// The brand code for showing more compact bubbles (experimental). -const wchar_t kBrief[] = L"CHMA"; +const wchar_t kSkypeBrandCode[] = L"SKPC,SKPG,SKPH,SKPI,SKPL,SKPM,SKPN"; } // namespace diff --git a/chrome/installer/util/browser_distribution.h b/chrome/installer/util/browser_distribution.h index 4f609c9..96360a9 100644 --- a/chrome/installer/util/browser_distribution.h +++ b/chrome/installer/util/browser_distribution.h @@ -33,6 +33,14 @@ class BrowserDistribution { NUM_TYPES }; + // Flags to control what to show in the UserExperiment dialog. + enum ToastUIflags { + kUninstall = 1, // Uninstall radio button. + kDontBugMeAsButton = 2, // Don't bug me is a button, not a radio button. + kWhyLink = 4, // Has the 'why I am seeing this' link. + kMakeDefault = 8 // Has the 'make it default' checkbox. + }; + // A struct for communicating what a UserExperiment contains. In these // experiments we show toasts to the user if they are inactive for a certain // amount of time. @@ -41,7 +49,7 @@ class BrowserDistribution { // also known as the 'TV' part in 'TV80'. int flavor; // The flavor index for this experiment. int heading; // The heading resource ID to use for this experiment. - bool compact_bubble; // Whether to show the compact heading or not. + int flags; // See ToastUIFlags above. int control_group; // Size of the control group (in percentages). Control // group is the group that qualifies for the // experiment but does not participate. diff --git a/chrome/installer/util/google_chrome_distribution.cc b/chrome/installer/util/google_chrome_distribution.cc index 80c160f..b340873 100644 --- a/chrome/installer/util/google_chrome_distribution.cc +++ b/chrome/installer/util/google_chrome_distribution.cc @@ -60,14 +60,15 @@ const wchar_t kICommandExecuteImplUuid[] = // The following strings are the possible outcomes of the toast experiment // as recorded in the |client| field. -const wchar_t kToastExpControlGroup[] = L"01"; -const wchar_t kToastExpCancelGroup[] = L"02"; -const wchar_t kToastExpUninstallGroup[] = L"04"; -const wchar_t kToastExpTriesOkGroup[] = L"18"; -const wchar_t kToastExpTriesErrorGroup[] = L"28"; -const wchar_t kToastActiveGroup[] = L"40"; -const wchar_t kToastUDDirFailure[] = L"40"; -const wchar_t kToastExpBaseGroup[] = L"80"; +const wchar_t kToastExpControlGroup[] = L"01"; +const wchar_t kToastExpCancelGroup[] = L"02"; +const wchar_t kToastExpUninstallGroup[] = L"04"; +const wchar_t kToastExpTriesOkGroup[] = L"18"; +const wchar_t kToastExpTriesErrorGroup[] = L"28"; +const wchar_t kToastExpTriesOkDefaultGroup[] = L"48"; +const wchar_t kToastActiveGroup[] = L"40"; +const wchar_t kToastUDDirFailure[] = L"40"; +const wchar_t kToastExpBaseGroup[] = L"80"; // Substitute the locale parameter in uninstall URL with whatever // Google Update tells us is the locale. In case we fail to find @@ -612,6 +613,10 @@ void SetClient(const string16& experiment_group, bool last_write) { bool GoogleChromeDistribution::GetExperimentDetails( UserExperiment* experiment, int flavor) { + struct FlavorDetails { + int heading_id; + int flags; + }; // Maximum number of experiment flavors we support. static const int kMax = 4; // This struct determines which experiment flavors we show for each locale and @@ -624,29 +629,37 @@ bool GoogleChromeDistribution::GetExperimentDetails( // The big experiment in Feb 2011 used SJxx SKxx SLxx SMxx. // Note: the plugin infobar experiment uses PIxx codes. using namespace attrition_experiments; + static const struct UserExperimentDetails { const wchar_t* locale; // Locale to show this experiment for (* for all). const wchar_t* brands; // Brand codes show this experiment for (* for all). int control_group; // Size of the control group, in percentages. - const wchar_t prefix1; // The first letter for the experiment code. - const wchar_t prefix2; // The second letter for the experiment code. This - // will be incremented by one for each additional - // experiment flavor beyond the first. - int flavors; // Numbers of flavors for this experiment. Should - // always be positive and never exceed the number - // of headings (below). - int headings[kMax]; // A list of IDs per experiment. 0 == no heading. - } kExperimentFlavors[] = { - // This list should be ordered most-specific rule first (catch-all, like all - // brands or all locales should be last). - - // The experiment with the more compact bubble. This one is a bit special - // because it is split into two: CAxx is regular style bubble and CBxx is - // compact style bubble. See |compact_bubble| below. - {L"en-US", kBrief, 1, L'C', L'A', 2, { kEnUs3, kEnUs3, 0, 0 } }, - - // Catch-all rules. - {kAll, kAll, 1, L'B', L'A', 1, {kEnUs3, 0, 0, 0} }, + const wchar_t* prefix; // The two letter experiment code. The second letter + // will be incremented with the flavor. + FlavorDetails flavors[kMax]; + } kExperiments[] = { + // The first match from top to bottom is used so this list should be ordered + // most-specific rule first. + { L"*", L"CHMA", // All locales, CHMA brand. + 25, // 25 percent control group. + L"ZA", // Experiment is ZAxx, ZBxx, ZCxx, ZDxx etc. + // Three flavors. + { { IDS_TRY_TOAST_HEADING3, kDontBugMeAsButton | kUninstall | kWhyLink }, + { IDS_TRY_TOAST_HEADING3, 0 }, + { IDS_TRY_TOAST_HEADING3, kMakeDefault }, + { 0, 0 }, + } + }, + { L"*", L"GGRV", // All locales, GGRV is enterprise. + 0, // 0 percent control group. + L"EA", // Experiment is EAxx, EBxx, etc. + // No flavors means no experiment. + { { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 } + } + } }; string16 locale; @@ -657,50 +670,36 @@ bool GoogleChromeDistribution::GetExperimentDetails( string16 brand; if (!GoogleUpdateSettings::GetBrand(&brand)) brand = ASCIIToWide(""); // Could still be viable for catch-all rules. - if (brand == kEnterprise) - return false; - for (int i = 0; i < arraysize(kExperimentFlavors); ++i) { - // A maximum of four flavors are supported at the moment. - CHECK_LE(kExperimentFlavors[i].flavors, kMax); - CHECK_GT(kExperimentFlavors[i].flavors, 0); - // Make sure each experiment has valid headings. - for (int f = 0; f < kMax; ++f) { - if (f < kExperimentFlavors[i].flavors) { - CHECK_GT(kExperimentFlavors[i].headings[f], 0); - } else { - CHECK_EQ(kExperimentFlavors[i].headings[f], 0); - } - } - // The prefix has to be a valid two letter combo. - CHECK(kExperimentFlavors[i].prefix1 >= 'A'); - CHECK(kExperimentFlavors[i].prefix2 >= 'A'); - CHECK(kExperimentFlavors[i].prefix2 + - kExperimentFlavors[i].flavors - 1 <= 'Z'); - - if (kExperimentFlavors[i].locale != locale && - kExperimentFlavors[i].locale != ASCIIToWide("*")) + for (int i = 0; i < arraysize(kExperiments); ++i) { + if (kExperiments[i].locale != locale && + kExperiments[i].locale != ASCIIToWide("*")) continue; std::vector<string16> brand_codes; - base::SplitString(kExperimentFlavors[i].brands, L',', &brand_codes); + base::SplitString(kExperiments[i].brands, L',', &brand_codes); if (brand_codes.empty()) return false; for (std::vector<string16>::iterator it = brand_codes.begin(); it != brand_codes.end(); ++it) { if (*it != brand && *it != L"*") continue; - // We have found our match. + const UserExperimentDetails& match = kExperiments[i]; + // Find out how many flavors we have. Zero means no experiment. + int num_flavors = 0; + while (match.flavors[num_flavors].heading_id) { ++num_flavors; } + if (!num_flavors) + return false; + if (flavor < 0) - flavor = base::RandInt(0, kExperimentFlavors[i].flavors - 1); + flavor = base::RandInt(0, num_flavors - 1); experiment->flavor = flavor; - experiment->heading = kExperimentFlavors[i].headings[flavor]; - experiment->control_group = kExperimentFlavors[i].control_group; - experiment->prefix.resize(2); - experiment->prefix[0] = kExperimentFlavors[i].prefix1; - experiment->prefix[1] = kExperimentFlavors[i].prefix2 + flavor; - experiment->compact_bubble = (brand == kBrief) && (flavor == 1); + experiment->heading = match.flavors[flavor].heading_id; + experiment->control_group = match.control_group; + const wchar_t prefix[] = { match.prefix[0], match.prefix[1] + flavor, 0 }; + experiment->prefix = prefix; + experiment->flags = match.flavors[flavor].flags; return true; } } @@ -853,6 +852,16 @@ void GoogleChromeDistribution::InactiveUserToastExperiment(int flavor, outcome = kToastExpTriesErrorGroup; }; + if (outcome == kToastExpTriesOkGroup) { + // User tried chrome, but if it had the default group button it belongs + // to a different outcome group. + UserExperiment experiment; + if (GetExperimentDetails(&experiment, flavor)) { + outcome = experiment.flags & kMakeDefault ? kToastExpTriesOkDefaultGroup : + kToastExpTriesOkGroup; + } + } + // Write to the |client| key for the last time. SetClient(experiment_group + outcome, true); |