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(¶ms); 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(¶ms); 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