app_info_header_panel.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
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/base/l10n/l10n_util.h"
27#include "ui/base/resource/resource_bundle.h"
28#include "ui/gfx/geometry/size.h"
29#include "ui/gfx/image/image.h"
30#include "ui/gfx/image/image_skia.h"
31#include "ui/gfx/image/image_skia_rep.h"
32#include "ui/gfx/native_widget_types.h"
33#include "ui/gfx/text_constants.h"
34#include "ui/views/controls/image_view.h"
35#include "ui/views/controls/label.h"
36#include "ui/views/controls/link.h"
37#include "ui/views/layout/box_layout.h"
38#include "ui/views/layout/grid_layout.h"
39#include "ui/views/layout/layout_constants.h"
40#include "ui/views/view.h"
41#include "url/gurl.h"
42
43namespace {
44
45// Size of extension icon in top left of dialog.
46const int kIconSize = 64;
47
48// The horizontal spacing between the app's links in the header section.
49const int kSpacingBetweenAppLinks = 3;
50
51}  // namespace
52
53AppInfoHeaderPanel::AppInfoHeaderPanel(Profile* profile,
54                                       const extensions::Extension* app)
55    : AppInfoPanel(profile, app),
56      app_icon_(NULL),
57      app_name_label_(NULL),
58      view_in_store_link_(NULL),
59      licenses_link_(NULL),
60      weak_ptr_factory_(this) {
61  CreateControls();
62
63  SetLayoutManager(
64      new views::BoxLayout(views::BoxLayout::kHorizontal,
65                           views::kButtonHEdgeMargin,
66                           views::kButtonVEdgeMargin,
67                           views::kRelatedControlHorizontalSpacing));
68
69  LayoutControls();
70}
71
72AppInfoHeaderPanel::~AppInfoHeaderPanel() {
73}
74
75void AppInfoHeaderPanel::CreateControls() {
76  app_name_label_ =
77      new views::Label(base::UTF8ToUTF16(app_->name()),
78                       ui::ResourceBundle::GetSharedInstance().GetFontList(
79                           ui::ResourceBundle::MediumFont));
80  app_name_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
81
82  app_icon_ = new views::ImageView();
83  app_icon_->SetImageSize(gfx::Size(kIconSize, kIconSize));
84  LoadAppImageAsync();
85
86  if (CanShowAppInWebStore()) {
87    view_in_store_link_ = new views::Link(
88        l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_WEB_STORE_LINK));
89    view_in_store_link_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
90    view_in_store_link_->set_listener(this);
91  }
92
93  if (CanDisplayLicenses()) {
94    licenses_link_ = new views::Link(
95        l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_LICENSES_BUTTON_TEXT));
96    licenses_link_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
97    licenses_link_->set_listener(this);
98  }
99}
100
101void AppInfoHeaderPanel::LayoutControls() {
102  AddChildView(app_icon_);
103  if (!view_in_store_link_ && !licenses_link_) {
104    // If there's no links, allow the app's name to take up multiple lines.
105    // TODO(sashab): Limit the number of lines to 2.
106    app_name_label_->SetMultiLine(true);
107    AddChildView(app_name_label_);
108  } else {
109    // Create a vertical container to store the app's name and links.
110    views::View* vertical_info_container = new views::View();
111    views::BoxLayout* vertical_container_layout =
112        new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0);
113    vertical_container_layout->set_main_axis_alignment(
114        views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER);
115    vertical_info_container->SetLayoutManager(vertical_container_layout);
116
117    vertical_info_container->AddChildView(app_name_label_);
118    // Create a horizontal container to store the app's links.
119    views::View* horizontal_links_container =
120        CreateHorizontalStack(kSpacingBetweenAppLinks);
121    if (view_in_store_link_)
122      horizontal_links_container->AddChildView(view_in_store_link_);
123    if (licenses_link_)
124      horizontal_links_container->AddChildView(licenses_link_);
125    vertical_info_container->AddChildView(horizontal_links_container);
126
127    AddChildView(vertical_info_container);
128  }
129}
130void AppInfoHeaderPanel::LinkClicked(views::Link* source, int event_flags) {
131  if (source == view_in_store_link_) {
132    ShowAppInWebStore();
133  } else if (source == licenses_link_) {
134    DisplayLicenses();
135  } else {
136    NOTREACHED();
137  }
138}
139
140void AppInfoHeaderPanel::LoadAppImageAsync() {
141  extensions::ExtensionResource image = extensions::IconsInfo::GetIconResource(
142      app_,
143      extension_misc::EXTENSION_ICON_LARGE,
144      ExtensionIconSet::MATCH_BIGGER);
145  int pixel_size =
146      static_cast<int>(kIconSize * gfx::ImageSkia::GetMaxSupportedScale());
147  extensions::ImageLoader::Get(profile_)->LoadImageAsync(
148      app_,
149      image,
150      gfx::Size(pixel_size, pixel_size),
151      base::Bind(&AppInfoHeaderPanel::OnAppImageLoaded, AsWeakPtr()));
152}
153
154void AppInfoHeaderPanel::OnAppImageLoaded(const gfx::Image& image) {
155  const SkBitmap* bitmap;
156  if (image.IsEmpty()) {
157    bitmap = &extensions::util::GetDefaultAppIcon()
158                  .GetRepresentation(gfx::ImageSkia::GetMaxSupportedScale())
159                  .sk_bitmap();
160  } else {
161    bitmap = image.ToSkBitmap();
162  }
163
164  app_icon_->SetImage(gfx::ImageSkia::CreateFrom1xBitmap(*bitmap));
165}
166
167void AppInfoHeaderPanel::ShowAppInWebStore() const {
168  DCHECK(CanShowAppInWebStore());
169  const GURL url = extensions::ManifestURL::GetDetailsURL(app_);
170  DCHECK_NE(url, GURL::EmptyGURL());
171  chrome::NavigateParams params(
172      profile_,
173      net::AppendQueryParameter(url,
174                                extension_urls::kWebstoreSourceField,
175                                extension_urls::kLaunchSourceAppListInfoDialog),
176      content::PAGE_TRANSITION_LINK);
177  chrome::Navigate(&params);
178}
179
180bool AppInfoHeaderPanel::CanShowAppInWebStore() const {
181  return app_->from_webstore();
182}
183
184void AppInfoHeaderPanel::DisplayLicenses() {
185  // Find the first shared module for this app, and display it's options page
186  // as an 'about' link.
187  // TODO(sashab): Revisit UI layout once shared module usage becomes more
188  // common.
189  DCHECK(CanDisplayLicenses());
190  ExtensionService* service =
191      extensions::ExtensionSystem::Get(profile_)->extension_service();
192  DCHECK(service);
193  const std::vector<extensions::SharedModuleInfo::ImportInfo>& imports =
194      extensions::SharedModuleInfo::GetImports(app_);
195  const extensions::Extension* imported_module =
196      service->GetExtensionById(imports[0].extension_id, true);
197  DCHECK(imported_module);
198  GURL about_page = extensions::ManifestURL::GetAboutPage(imported_module);
199  DCHECK(about_page != GURL::EmptyGURL());
200  chrome::NavigateParams params(
201      profile_, about_page, content::PAGE_TRANSITION_LINK);
202  chrome::Navigate(&params);
203}
204
205bool AppInfoHeaderPanel::CanDisplayLicenses() {
206  if (!extensions::SharedModuleInfo::ImportsModules(app_))
207    return false;
208  ExtensionService* service =
209      extensions::ExtensionSystem::Get(profile_)->extension_service();
210  DCHECK(service);
211  const std::vector<extensions::SharedModuleInfo::ImportInfo>& imports =
212      extensions::SharedModuleInfo::GetImports(app_);
213  const extensions::Extension* imported_module =
214      service->GetExtensionById(imports[0].extension_id, true);
215  DCHECK(imported_module);
216  GURL about_page = extensions::ManifestURL::GetAboutPage(imported_module);
217  if (about_page == GURL::EmptyGURL())
218    return false;
219  return true;
220}
221