Home | History | Annotate | Download | only in app_info_dialog
      1 // Copyright 2014 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/apps/app_info_dialog/app_info_header_panel.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/strings/utf_string_conversions.h"
      9 #include "chrome/browser/extensions/extension_service.h"
     10 #include "chrome/browser/extensions/extension_util.h"
     11 #include "chrome/browser/profiles/profile.h"
     12 #include "chrome/browser/ui/browser_navigator.h"
     13 #include "chrome/common/extensions/extension_constants.h"
     14 #include "chrome/common/extensions/manifest_url_handler.h"
     15 #include "extensions/browser/extension_system.h"
     16 #include "extensions/browser/image_loader.h"
     17 #include "extensions/common/constants.h"
     18 #include "extensions/common/extension.h"
     19 #include "extensions/common/extension_icon_set.h"
     20 #include "extensions/common/extension_resource.h"
     21 #include "extensions/common/manifest_handlers/icons_handler.h"
     22 #include "extensions/common/manifest_handlers/shared_module_info.h"
     23 #include "grit/generated_resources.h"
     24 #include "net/base/url_util.h"
     25 #include "third_party/skia/include/core/SkBitmap.h"
     26 #include "ui/app_list/app_list_constants.h"
     27 #include "ui/base/l10n/l10n_util.h"
     28 #include "ui/base/resource/resource_bundle.h"
     29 #include "ui/gfx/geometry/size.h"
     30 #include "ui/gfx/image/image.h"
     31 #include "ui/gfx/image/image_skia.h"
     32 #include "ui/gfx/image/image_skia_rep.h"
     33 #include "ui/gfx/native_widget_types.h"
     34 #include "ui/gfx/text_constants.h"
     35 #include "ui/views/controls/image_view.h"
     36 #include "ui/views/controls/label.h"
     37 #include "ui/views/controls/link.h"
     38 #include "ui/views/layout/box_layout.h"
     39 #include "ui/views/layout/grid_layout.h"
     40 #include "ui/views/layout/layout_constants.h"
     41 #include "ui/views/view.h"
     42 #include "url/gurl.h"
     43 
     44 namespace {
     45 
     46 // Size of extension icon in top left of dialog.
     47 const int kIconSize = 64;
     48 
     49 // The number of pixels spacing between the app's title and version in the
     50 // dialog, when both are available.
     51 const int kSpacingBetweenAppTitleAndVersion = 4;
     52 
     53 }  // namespace
     54 
     55 AppInfoHeaderPanel::AppInfoHeaderPanel(Profile* profile,
     56                                        const extensions::Extension* app)
     57     : AppInfoPanel(profile, app),
     58       app_icon_(NULL),
     59       app_name_label_(NULL),
     60       app_version_label_(NULL),
     61       view_in_store_link_(NULL),
     62       licenses_link_(NULL),
     63       weak_ptr_factory_(this) {
     64   CreateControls();
     65 
     66   SetLayoutManager(
     67       new views::BoxLayout(views::BoxLayout::kHorizontal,
     68                            views::kButtonHEdgeMargin,
     69                            views::kButtonVEdgeMargin,
     70                            views::kRelatedControlHorizontalSpacing));
     71 
     72   LayoutControls();
     73 }
     74 
     75 AppInfoHeaderPanel::~AppInfoHeaderPanel() {
     76 }
     77 
     78 void AppInfoHeaderPanel::CreateControls() {
     79   app_name_label_ =
     80       new views::Label(base::UTF8ToUTF16(app_->name()),
     81                        ui::ResourceBundle::GetSharedInstance().GetFontList(
     82                            ui::ResourceBundle::MediumFont));
     83   app_name_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
     84 
     85   // The version number doesn't make sense for bookmarked apps.
     86   if (!app_->from_bookmark()) {
     87     app_version_label_ =
     88         new views::Label(base::UTF8ToUTF16(app_->VersionString()),
     89                          ui::ResourceBundle::GetSharedInstance().GetFontList(
     90                              ui::ResourceBundle::MediumFont));
     91     app_version_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
     92     app_version_label_->SetEnabledColor(app_list::kDialogSubtitleColor);
     93   }
     94 
     95   app_icon_ = new views::ImageView();
     96   app_icon_->SetImageSize(gfx::Size(kIconSize, kIconSize));
     97   LoadAppImageAsync();
     98 
     99   if (CanShowAppInWebStore()) {
    100     view_in_store_link_ = new views::Link(
    101         l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_WEB_STORE_LINK));
    102     view_in_store_link_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    103     view_in_store_link_->set_listener(this);
    104   }
    105 
    106   if (CanDisplayLicenses()) {
    107     licenses_link_ = new views::Link(
    108         l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_LICENSES_BUTTON_TEXT));
    109     licenses_link_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    110     licenses_link_->set_listener(this);
    111   }
    112 }
    113 
    114 void AppInfoHeaderPanel::LayoutAppNameAndVersionInto(views::View* parent_view) {
    115   views::View* view = new views::View();
    116   // We need a horizontal BoxLayout here to ensure that the GridLayout does
    117   // not stretch beyond the size of its content.
    118   view->SetLayoutManager(
    119       new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0));
    120 
    121   views::View* container_view = new views::View();
    122   view->AddChildView(container_view);
    123   views::GridLayout* layout = new views::GridLayout(container_view);
    124   container_view->SetLayoutManager(layout);
    125 
    126   static const int kColumnId = 1;
    127   views::ColumnSet* column_set = layout->AddColumnSet(kColumnId);
    128   column_set->AddColumn(views::GridLayout::LEADING,
    129                         views::GridLayout::TRAILING,
    130                         1,  // Stretch the title to as wide as needed
    131                         views::GridLayout::USE_PREF,
    132                         0,
    133                         0);
    134   column_set->AddPaddingColumn(0, kSpacingBetweenAppTitleAndVersion);
    135   column_set->AddColumn(views::GridLayout::LEADING,
    136                         views::GridLayout::TRAILING,
    137                         0,  // Do not stretch the version
    138                         views::GridLayout::USE_PREF,
    139                         0,
    140                         0);
    141 
    142   layout->StartRow(1, kColumnId);
    143   layout->AddView(app_name_label_);
    144   if (app_version_label_)
    145     layout->AddView(app_version_label_);
    146 
    147   parent_view->AddChildView(view);
    148 }
    149 
    150 void AppInfoHeaderPanel::LayoutControls() {
    151   AddChildView(app_icon_);
    152   if (!app_version_label_ && !view_in_store_link_ && !licenses_link_) {
    153     // If there's no version _and_ no links, allow the app's name to take up
    154     // multiple lines.
    155     // TODO(sashab): Limit the number of lines to 2.
    156     app_name_label_->SetMultiLine(true);
    157     AddChildView(app_name_label_);
    158   } else {
    159     // Create a vertical container to store the app's name and info.
    160     views::View* vertical_info_container = new views::View();
    161     views::BoxLayout* vertical_container_layout =
    162         new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0);
    163     vertical_container_layout->set_main_axis_alignment(
    164         views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER);
    165     vertical_info_container->SetLayoutManager(vertical_container_layout);
    166 
    167     if (!view_in_store_link_ && !licenses_link_) {
    168       // If there are no links, display the version on the second line.
    169       vertical_info_container->AddChildView(app_name_label_);
    170       vertical_info_container->AddChildView(app_version_label_);
    171     } else {
    172       LayoutAppNameAndVersionInto(vertical_info_container);
    173       // Create a horizontal container to store the app's links.
    174       views::View* horizontal_links_container = new views::View();
    175       horizontal_links_container->SetLayoutManager(
    176           new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 3));
    177       if (view_in_store_link_)
    178         horizontal_links_container->AddChildView(view_in_store_link_);
    179       if (licenses_link_)
    180         horizontal_links_container->AddChildView(licenses_link_);
    181       vertical_info_container->AddChildView(horizontal_links_container);
    182     }
    183     AddChildView(vertical_info_container);
    184   }
    185 }
    186 void AppInfoHeaderPanel::LinkClicked(views::Link* source, int event_flags) {
    187   if (source == view_in_store_link_) {
    188     ShowAppInWebStore();
    189   } else if (source == licenses_link_) {
    190     DisplayLicenses();
    191   } else {
    192     NOTREACHED();
    193   }
    194 }
    195 
    196 void AppInfoHeaderPanel::LoadAppImageAsync() {
    197   extensions::ExtensionResource image = extensions::IconsInfo::GetIconResource(
    198       app_,
    199       extension_misc::EXTENSION_ICON_LARGE,
    200       ExtensionIconSet::MATCH_BIGGER);
    201   int pixel_size =
    202       static_cast<int>(kIconSize * gfx::ImageSkia::GetMaxSupportedScale());
    203   extensions::ImageLoader::Get(profile_)->LoadImageAsync(
    204       app_,
    205       image,
    206       gfx::Size(pixel_size, pixel_size),
    207       base::Bind(&AppInfoHeaderPanel::OnAppImageLoaded, AsWeakPtr()));
    208 }
    209 
    210 void AppInfoHeaderPanel::OnAppImageLoaded(const gfx::Image& image) {
    211   const SkBitmap* bitmap;
    212   if (image.IsEmpty()) {
    213     bitmap = &extensions::util::GetDefaultAppIcon()
    214                   .GetRepresentation(gfx::ImageSkia::GetMaxSupportedScale())
    215                   .sk_bitmap();
    216   } else {
    217     bitmap = image.ToSkBitmap();
    218   }
    219 
    220   app_icon_->SetImage(gfx::ImageSkia::CreateFrom1xBitmap(*bitmap));
    221 }
    222 
    223 void AppInfoHeaderPanel::ShowAppInWebStore() const {
    224   DCHECK(CanShowAppInWebStore());
    225   const GURL url = extensions::ManifestURL::GetDetailsURL(app_);
    226   DCHECK_NE(url, GURL::EmptyGURL());
    227   chrome::NavigateParams params(
    228       profile_,
    229       net::AppendQueryParameter(url,
    230                                 extension_urls::kWebstoreSourceField,
    231                                 extension_urls::kLaunchSourceAppListInfoDialog),
    232       content::PAGE_TRANSITION_LINK);
    233   chrome::Navigate(&params);
    234 }
    235 
    236 bool AppInfoHeaderPanel::CanShowAppInWebStore() const {
    237   return app_->from_webstore();
    238 }
    239 
    240 void AppInfoHeaderPanel::DisplayLicenses() {
    241   // Find the first shared module for this app, and display it's options page
    242   // as an 'about' link.
    243   // TODO(sashab): Revisit UI layout once shared module usage becomes more
    244   // common.
    245   DCHECK(CanDisplayLicenses());
    246   ExtensionService* service =
    247       extensions::ExtensionSystem::Get(profile_)->extension_service();
    248   DCHECK(service);
    249   const std::vector<extensions::SharedModuleInfo::ImportInfo>& imports =
    250       extensions::SharedModuleInfo::GetImports(app_);
    251   const extensions::Extension* imported_module =
    252       service->GetExtensionById(imports[0].extension_id, true);
    253   DCHECK(imported_module);
    254   GURL about_page = extensions::ManifestURL::GetAboutPage(imported_module);
    255   DCHECK(about_page != GURL::EmptyGURL());
    256   chrome::NavigateParams params(
    257       profile_, about_page, content::PAGE_TRANSITION_LINK);
    258   chrome::Navigate(&params);
    259 }
    260 
    261 bool AppInfoHeaderPanel::CanDisplayLicenses() {
    262   if (!extensions::SharedModuleInfo::ImportsModules(app_))
    263     return false;
    264   ExtensionService* service =
    265       extensions::ExtensionSystem::Get(profile_)->extension_service();
    266   DCHECK(service);
    267   const std::vector<extensions::SharedModuleInfo::ImportInfo>& imports =
    268       extensions::SharedModuleInfo::GetImports(app_);
    269   const extensions::Extension* imported_module =
    270       service->GetExtensionById(imports[0].extension_id, true);
    271   DCHECK(imported_module);
    272   GURL about_page = extensions::ManifestURL::GetAboutPage(imported_module);
    273   if (about_page == GURL::EmptyGURL())
    274     return false;
    275   return true;
    276 }
    277