Home | History | Annotate | Download | only in views
      1 // Copyright (c) 2013 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/conflicting_module_view_win.h"
      6 
      7 #include "base/metrics/histogram.h"
      8 #include "chrome/browser/chrome_notification_types.h"
      9 #include "chrome/browser/enumerate_modules_model_win.h"
     10 #include "chrome/browser/profiles/profile.h"
     11 #include "chrome/browser/ui/browser.h"
     12 #include "chrome/common/pref_names.h"
     13 #include "chrome/grit/chromium_strings.h"
     14 #include "chrome/grit/generated_resources.h"
     15 #include "chrome/grit/locale_settings.h"
     16 #include "content/public/browser/notification_service.h"
     17 #include "content/public/browser/user_metrics.h"
     18 #include "grit/theme_resources.h"
     19 #include "ui/accessibility/ax_view_state.h"
     20 #include "ui/base/l10n/l10n_util.h"
     21 #include "ui/base/resource/resource_bundle.h"
     22 #include "ui/views/controls/button/label_button.h"
     23 #include "ui/views/controls/image_view.h"
     24 #include "ui/views/controls/label.h"
     25 #include "ui/views/layout/grid_layout.h"
     26 #include "ui/views/layout/layout_constants.h"
     27 #include "ui/views/widget/widget.h"
     28 
     29 using base::UserMetricsAction;
     30 
     31 namespace {
     32 
     33 // Layout constants.
     34 const int kInsetBottomRight = 13;
     35 const int kInsetLeft = 14;
     36 const int kInsetTop = 9;
     37 const int kHeadlineMessagePadding = 4;
     38 const int kMessageBubblePadding = 11;
     39 
     40 // How often to show this bubble.
     41 const int kShowConflictingModuleBubbleMax = 3;
     42 
     43 }  // namespace
     44 
     45 ////////////////////////////////////////////////////////////////////////////////
     46 // ConflictingModuleView
     47 
     48 ConflictingModuleView::ConflictingModuleView(
     49     views::View* anchor_view,
     50     Browser* browser,
     51     const GURL& help_center_url)
     52     : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT),
     53       browser_(browser),
     54       explanation_(NULL),
     55       learn_more_button_(NULL),
     56       not_now_button_(NULL),
     57       help_center_url_(help_center_url) {
     58   set_close_on_deactivate(false);
     59   set_close_on_esc(true);
     60 
     61   // Compensate for built-in vertical padding in the anchor view's image.
     62   set_anchor_view_insets(gfx::Insets(5, 0, 5, 0));
     63 
     64   registrar_.Add(this, chrome::NOTIFICATION_MODULE_INCOMPATIBILITY_BADGE_CHANGE,
     65                  content::NotificationService::AllSources());
     66 }
     67 
     68 // static
     69 void ConflictingModuleView::MaybeShow(Browser* browser,
     70                                       views::View* anchor_view) {
     71   static bool done_checking = false;
     72   if (done_checking)
     73     return;  // Only show the bubble once per launch.
     74 
     75   EnumerateModulesModel* model = EnumerateModulesModel::GetInstance();
     76   GURL url = model->GetFirstNotableConflict();
     77   if (!url.is_valid()) {
     78     done_checking = true;
     79     return;
     80   }
     81 
     82   // A pref that counts how often the Sideload Wipeout bubble has been shown.
     83   IntegerPrefMember bubble_shown;
     84   bubble_shown.Init(prefs::kModuleConflictBubbleShown,
     85                     browser->profile()->GetPrefs());
     86   if (bubble_shown.GetValue() >= kShowConflictingModuleBubbleMax) {
     87     done_checking = true;
     88     return;
     89   }
     90 
     91   // |anchor_view| must be in a widget (the browser's widget). If not, |browser|
     92   // may be destroyed before us, and we'll crash trying to access |browser|
     93   // later on. We can't DCHECK |browser|'s widget here as we may be called from
     94   // creation of BrowserWindow, which means browser->window() may return NULL.
     95   DCHECK(anchor_view);
     96   DCHECK(anchor_view->GetWidget());
     97 
     98   ConflictingModuleView* bubble_delegate =
     99       new ConflictingModuleView(anchor_view, browser, url);
    100   views::BubbleDelegateView::CreateBubble(bubble_delegate);
    101   bubble_delegate->ShowBubble();
    102 
    103   done_checking = true;
    104 }
    105 
    106 ////////////////////////////////////////////////////////////////////////////////
    107 // ConflictingModuleView - private.
    108 
    109 ConflictingModuleView::~ConflictingModuleView() {
    110 }
    111 
    112 void ConflictingModuleView::ShowBubble() {
    113   GetWidget()->Show();
    114 
    115   IntegerPrefMember bubble_shown;
    116   bubble_shown.Init(
    117       prefs::kModuleConflictBubbleShown,
    118       browser_->profile()->GetPrefs());
    119   bubble_shown.SetValue(bubble_shown.GetValue() + 1);
    120 }
    121 
    122 void ConflictingModuleView::DismissBubble() {
    123   GetWidget()->Close();
    124 
    125   content::RecordAction(
    126       UserMetricsAction("ConflictingModuleNotificationDismissed"));
    127 }
    128 
    129 void ConflictingModuleView::Init() {
    130   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    131 
    132   views::GridLayout* layout = views::GridLayout::CreatePanel(this);
    133   layout->SetInsets(kInsetTop, kInsetLeft,
    134                     kInsetBottomRight, kInsetBottomRight);
    135   SetLayoutManager(layout);
    136 
    137   views::ImageView* icon = new views::ImageView();
    138   icon->SetImage(rb.GetNativeImageNamed(IDR_INPUT_ALERT_MENU).ToImageSkia());
    139   gfx::Size icon_size = icon->GetPreferredSize();
    140 
    141   const int text_column_set_id = 0;
    142   views::ColumnSet* upper_columns = layout->AddColumnSet(text_column_set_id);
    143   upper_columns->AddColumn(
    144       views::GridLayout::LEADING, views::GridLayout::LEADING,
    145       0, views::GridLayout::FIXED, icon_size.width(), icon_size.height());
    146   upper_columns->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing);
    147   upper_columns->AddColumn(
    148       views::GridLayout::LEADING, views::GridLayout::LEADING,
    149       0, views::GridLayout::USE_PREF, 0, 0);
    150 
    151   layout->StartRowWithPadding(
    152       0, text_column_set_id, 0, kHeadlineMessagePadding);
    153   layout->AddView(icon);
    154   explanation_ = new views::Label();
    155   explanation_->SetMultiLine(true);
    156   explanation_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    157   explanation_->SetText(l10n_util::GetStringUTF16(
    158       IDS_OPTIONS_CONFLICTING_MODULE));
    159   explanation_->SizeToFit(views::Widget::GetLocalizedContentsWidth(
    160       IDS_CONFLICTING_MODULE_BUBBLE_WIDTH_CHARS));
    161   layout->AddView(explanation_);
    162 
    163   const int action_row_column_set_id = 1;
    164   views::ColumnSet* bottom_columns =
    165       layout->AddColumnSet(action_row_column_set_id);
    166   bottom_columns->AddPaddingColumn(1, 0);
    167   bottom_columns->AddColumn(views::GridLayout::TRAILING,
    168       views::GridLayout::CENTER, 0, views::GridLayout::USE_PREF, 0, 0);
    169   bottom_columns->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
    170   bottom_columns->AddColumn(views::GridLayout::TRAILING,
    171       views::GridLayout::CENTER, 0, views::GridLayout::USE_PREF, 0, 0);
    172   layout->AddPaddingRow(0, 7);
    173 
    174   layout->StartRowWithPadding(0, action_row_column_set_id,
    175                               0, kMessageBubblePadding);
    176   learn_more_button_ = new views::LabelButton(this,
    177       l10n_util::GetStringUTF16(IDS_CONFLICTS_LEARN_MORE));
    178   learn_more_button_->SetStyle(views::Button::STYLE_BUTTON);
    179   layout->AddView(learn_more_button_);
    180   not_now_button_ = new views::LabelButton(this,
    181       l10n_util::GetStringUTF16(IDS_CONFLICTS_NOT_NOW));
    182   not_now_button_->SetStyle(views::Button::STYLE_BUTTON);
    183   layout->AddView(not_now_button_);
    184 
    185   content::RecordAction(
    186       UserMetricsAction("ConflictingModuleNotificationShown"));
    187 
    188   UMA_HISTOGRAM_ENUMERATION("ConflictingModule.UserSelection",
    189       EnumerateModulesModel::ACTION_BUBBLE_SHOWN,
    190       EnumerateModulesModel::ACTION_BOUNDARY);
    191 }
    192 
    193 void ConflictingModuleView::ButtonPressed(views::Button* sender,
    194                                           const ui::Event& event) {
    195   if (sender == learn_more_button_) {
    196     browser_->OpenURL(
    197         content::OpenURLParams(help_center_url_,
    198                                content::Referrer(),
    199                                NEW_FOREGROUND_TAB,
    200                                ui::PAGE_TRANSITION_LINK,
    201                                false));
    202 
    203     EnumerateModulesModel* model = EnumerateModulesModel::GetInstance();
    204     model->AcknowledgeConflictNotification();
    205     DismissBubble();
    206   } else if (sender == not_now_button_) {
    207     DismissBubble();
    208   }
    209 }
    210 
    211 void ConflictingModuleView::GetAccessibleState(
    212     ui::AXViewState* state) {
    213   state->role = ui::AX_ROLE_ALERT;
    214 }
    215 
    216 void ConflictingModuleView::ViewHierarchyChanged(
    217   const ViewHierarchyChangedDetails& details) {
    218   if (details.is_add && details.child == this)
    219     NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true);
    220 }
    221 
    222 void ConflictingModuleView::Observe(
    223     int type,
    224     const content::NotificationSource& source,
    225     const content::NotificationDetails& details) {
    226   DCHECK(type == chrome::NOTIFICATION_MODULE_INCOMPATIBILITY_BADGE_CHANGE);
    227   EnumerateModulesModel* model = EnumerateModulesModel::GetInstance();
    228   if (!model->ShouldShowConflictWarning())
    229     GetWidget()->Close();
    230 }
    231