Add ENABLE_MEDIA_ROUTER define to builds other than Android and iOS.
[chromium-blink-merge.git] / chrome / browser / ui / app_list / extension_app_item.cc
bloba15893f12b5b20a5ba07af72b0f4a4b4502e78db
1 // Copyright (c) 2012 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.
5 #include "chrome/browser/ui/app_list/extension_app_item.h"
7 #include "base/prefs/pref_service.h"
8 #include "chrome/browser/extensions/extension_service.h"
9 #include "chrome/browser/extensions/extension_util.h"
10 #include "chrome/browser/extensions/launch_util.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/ui/app_list/app_context_menu.h"
13 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
14 #include "chrome/browser/ui/app_list/app_list_service.h"
15 #include "chrome/browser/ui/extensions/extension_enable_flow.h"
16 #include "chrome/browser/ui/host_desktop.h"
17 #include "chrome/common/extensions/extension_constants.h"
18 #include "chrome/common/extensions/extension_metrics.h"
19 #include "content/public/browser/user_metrics.h"
20 #include "extensions/browser/app_sorting.h"
21 #include "extensions/browser/extension_prefs.h"
22 #include "extensions/browser/extension_system.h"
23 #include "extensions/common/extension.h"
24 #include "extensions/common/extension_icon_set.h"
25 #include "extensions/common/manifest_handlers/icons_handler.h"
26 #include "extensions/common/manifest_url_handlers.h"
27 #include "grit/theme_resources.h"
28 #include "sync/api/string_ordinal.h"
29 #include "ui/base/resource/resource_bundle.h"
30 #include "ui/gfx/canvas.h"
31 #include "ui/gfx/color_utils.h"
32 #include "ui/gfx/geometry/rect.h"
33 #include "ui/gfx/image/canvas_image_source.h"
34 #include "ui/gfx/image/image_skia_operations.h"
36 using extensions::Extension;
38 namespace {
40 // Overlays a shortcut icon over the bottom left corner of a given image.
41 class ShortcutOverlayImageSource : public gfx::CanvasImageSource {
42 public:
43 explicit ShortcutOverlayImageSource(const gfx::ImageSkia& icon)
44 : gfx::CanvasImageSource(icon.size(), false),
45 icon_(icon) {
47 ~ShortcutOverlayImageSource() override {}
49 private:
50 // gfx::CanvasImageSource overrides:
51 void Draw(gfx::Canvas* canvas) override {
52 canvas->DrawImageInt(icon_, 0, 0);
54 // Draw the overlay in the bottom left corner of the icon.
55 const gfx::ImageSkia& overlay = *ui::ResourceBundle::GetSharedInstance().
56 GetImageSkiaNamed(IDR_APP_LIST_TAB_OVERLAY);
57 canvas->DrawImageInt(overlay, 0, icon_.height() - overlay.height());
60 gfx::ImageSkia icon_;
62 DISALLOW_COPY_AND_ASSIGN(ShortcutOverlayImageSource);
65 // Rounds the corners of a given image.
66 class RoundedCornersImageSource : public gfx::CanvasImageSource {
67 public:
68 explicit RoundedCornersImageSource(const gfx::ImageSkia& icon)
69 : gfx::CanvasImageSource(icon.size(), false),
70 icon_(icon) {
72 ~RoundedCornersImageSource() override {}
74 private:
75 // gfx::CanvasImageSource overrides:
76 void Draw(gfx::Canvas* canvas) override {
77 // The radius used to round the app icon.
78 const size_t kRoundingRadius = 2;
80 canvas->DrawImageInt(icon_, 0, 0);
82 scoped_ptr<gfx::Canvas> masking_canvas(
83 new gfx::Canvas(gfx::Size(icon_.width(), icon_.height()), 1.0f, false));
84 DCHECK(masking_canvas);
86 SkPaint opaque_paint;
87 opaque_paint.setColor(SK_ColorWHITE);
88 opaque_paint.setFlags(SkPaint::kAntiAlias_Flag);
89 masking_canvas->DrawRoundRect(
90 gfx::Rect(icon_.width(), icon_.height()),
91 kRoundingRadius, opaque_paint);
93 SkPaint masking_paint;
94 masking_paint.setXfermodeMode(SkXfermode::kDstIn_Mode);
95 canvas->DrawImageInt(
96 gfx::ImageSkia(masking_canvas->ExtractImageRep()), 0, 0, masking_paint);
99 gfx::ImageSkia icon_;
101 DISALLOW_COPY_AND_ASSIGN(RoundedCornersImageSource);
104 extensions::AppSorting* GetAppSorting(Profile* profile) {
105 return extensions::ExtensionPrefs::Get(profile)->app_sorting();
108 gfx::ImageSkia CreateDisabledIcon(const gfx::ImageSkia& icon) {
109 const color_utils::HSL shift = {-1, 0, 0.6};
110 return gfx::ImageSkiaOperations::CreateHSLShiftedImage(icon, shift);
113 } // namespace
115 ExtensionAppItem::ExtensionAppItem(
116 Profile* profile,
117 const app_list::AppListSyncableService::SyncItem* sync_item,
118 const std::string& extension_id,
119 const std::string& extension_name,
120 const gfx::ImageSkia& installing_icon,
121 bool is_platform_app)
122 : app_list::AppListItem(extension_id),
123 profile_(profile),
124 extension_id_(extension_id),
125 extension_enable_flow_controller_(NULL),
126 extension_name_(extension_name),
127 installing_icon_(CreateDisabledIcon(installing_icon)),
128 is_platform_app_(is_platform_app),
129 has_overlay_(false) {
130 Reload();
131 if (sync_item && sync_item->item_ordinal.IsValid()) {
132 // An existing synced position exists, use that.
133 set_position(sync_item->item_ordinal);
134 // Only set the name from the sync item if it is empty.
135 if (name().empty())
136 SetName(sync_item->item_name);
137 return;
139 GetAppSorting(profile_)->EnsureValidOrdinals(extension_id_,
140 syncer::StringOrdinal());
141 UpdatePositionFromExtensionOrdering();
144 ExtensionAppItem::~ExtensionAppItem() {
147 bool ExtensionAppItem::NeedsOverlay() const {
148 #if defined(OS_CHROMEOS)
149 // The overlay is disabled completely in ChromeOS.
150 return false;
151 #endif
153 extensions::LaunchType launch_type =
154 GetExtension()
155 ? extensions::GetLaunchType(extensions::ExtensionPrefs::Get(profile_),
156 GetExtension())
157 : extensions::LAUNCH_TYPE_WINDOW;
159 // The overlay icon is disabled for hosted apps in windowed mode with
160 // bookmark apps enabled.
161 return !is_platform_app_ && extension_id_ != extension_misc::kChromeAppId &&
162 (!extensions::util::IsNewBookmarkAppsEnabled() ||
163 launch_type != extensions::LAUNCH_TYPE_WINDOW);
166 void ExtensionAppItem::Reload() {
167 const Extension* extension = GetExtension();
168 bool is_installing = !extension;
169 SetIsInstalling(is_installing);
170 if (is_installing) {
171 SetName(extension_name_);
172 UpdateIcon();
173 return;
175 SetNameAndShortName(extension->name(), extension->short_name());
176 LoadImage(extension);
179 void ExtensionAppItem::UpdateIcon() {
180 gfx::ImageSkia icon = installing_icon_;
182 // Use the app icon if the app exists. Turn the image greyscale if the app is
183 // not launchable.
184 if (GetExtension() && icon_) {
185 icon = icon_->image_skia();
186 const bool enabled = extensions::util::IsAppLaunchable(extension_id_,
187 profile_);
188 if (!enabled)
189 icon = CreateDisabledIcon(icon);
191 if (GetExtension()->from_bookmark())
192 icon = gfx::ImageSkia(new RoundedCornersImageSource(icon), icon.size());
194 // Paint the shortcut overlay if necessary.
195 has_overlay_ = NeedsOverlay();
196 if (has_overlay_)
197 icon = gfx::ImageSkia(new ShortcutOverlayImageSource(icon), icon.size());
199 SetIcon(icon, true);
202 void ExtensionAppItem::Move(const ExtensionAppItem* prev,
203 const ExtensionAppItem* next) {
204 if (!prev && !next)
205 return; // No reordering necessary
207 extensions::ExtensionPrefs* prefs = extensions::ExtensionPrefs::Get(profile_);
208 extensions::AppSorting* sorting = GetAppSorting(profile_);
210 syncer::StringOrdinal page;
211 std::string prev_id, next_id;
212 if (!prev) {
213 next_id = next->extension_id();
214 page = sorting->GetPageOrdinal(next_id);
215 } else if (!next) {
216 prev_id = prev->extension_id();
217 page = sorting->GetPageOrdinal(prev_id);
218 } else {
219 prev_id = prev->extension_id();
220 page = sorting->GetPageOrdinal(prev_id);
221 // Only set |next_id| if on the same page, otherwise just insert after prev.
222 if (page.Equals(sorting->GetPageOrdinal(next->extension_id())))
223 next_id = next->extension_id();
225 prefs->SetAppDraggedByUser(extension_id_);
226 sorting->SetPageOrdinal(extension_id_, page);
227 sorting->OnExtensionMoved(extension_id_, prev_id, next_id);
228 UpdatePositionFromExtensionOrdering();
231 const Extension* ExtensionAppItem::GetExtension() const {
232 const ExtensionService* service =
233 extensions::ExtensionSystem::Get(profile_)->extension_service();
234 const Extension* extension = service->GetInstalledExtension(extension_id_);
235 return extension;
238 void ExtensionAppItem::LoadImage(const Extension* extension) {
239 icon_.reset(new extensions::IconImage(
240 profile_,
241 extension,
242 extensions::IconsInfo::GetIcons(extension),
243 extension_misc::EXTENSION_ICON_MEDIUM,
244 extensions::util::GetDefaultAppIcon(),
245 this));
246 UpdateIcon();
249 bool ExtensionAppItem::RunExtensionEnableFlow() {
250 if (extensions::util::IsAppLaunchableWithoutEnabling(extension_id_, profile_))
251 return false;
253 if (!extension_enable_flow_) {
254 extension_enable_flow_controller_ = GetController();
255 extension_enable_flow_controller_->OnShowChildDialog();
257 extension_enable_flow_.reset(new ExtensionEnableFlow(
258 profile_, extension_id_, this));
259 extension_enable_flow_->StartForNativeWindow(
260 extension_enable_flow_controller_->GetAppListWindow());
262 return true;
265 void ExtensionAppItem::Launch(int event_flags) {
266 // |extension| could be NULL when it is being unloaded for updating.
267 const Extension* extension = GetExtension();
268 if (!extension)
269 return;
271 // Don't auto-enable apps that cannot be launched.
272 if (!extensions::util::IsAppLaunchable(extension_id_, profile_))
273 return;
275 if (RunExtensionEnableFlow())
276 return;
278 GetController()->LaunchApp(profile_,
279 extension,
280 AppListControllerDelegate::LAUNCH_FROM_APP_LIST,
281 event_flags);
284 void ExtensionAppItem::OnExtensionIconImageChanged(
285 extensions::IconImage* image) {
286 DCHECK(icon_.get() == image);
287 UpdateIcon();
290 void ExtensionAppItem::ExtensionEnableFlowFinished() {
291 extension_enable_flow_.reset();
292 extension_enable_flow_controller_->OnCloseChildDialog();
293 extension_enable_flow_controller_ = NULL;
295 // Automatically launch app after enabling.
296 Launch(ui::EF_NONE);
299 void ExtensionAppItem::ExtensionEnableFlowAborted(bool user_initiated) {
300 extension_enable_flow_.reset();
301 extension_enable_flow_controller_->OnCloseChildDialog();
302 extension_enable_flow_controller_ = NULL;
305 void ExtensionAppItem::Activate(int event_flags) {
306 // |extension| could be NULL when it is being unloaded for updating.
307 const Extension* extension = GetExtension();
308 if (!extension)
309 return;
311 // Don't auto-enable apps that cannot be launched.
312 if (!extensions::util::IsAppLaunchable(extension_id_, profile_))
313 return;
315 if (RunExtensionEnableFlow())
316 return;
318 content::RecordAction(base::UserMetricsAction("AppList_ClickOnApp"));
319 extensions::RecordAppListMainLaunch(extension);
320 GetController()->ActivateApp(profile_,
321 extension,
322 AppListControllerDelegate::LAUNCH_FROM_APP_LIST,
323 event_flags);
326 ui::MenuModel* ExtensionAppItem::GetContextMenuModel() {
327 context_menu_.reset(new app_list::AppContextMenu(
328 this, profile_, extension_id_, GetController()));
329 context_menu_->set_is_platform_app(is_platform_app_);
330 if (IsInFolder())
331 context_menu_->set_is_in_folder(true);
332 return context_menu_->GetMenuModel();
335 void ExtensionAppItem::OnExtensionPreferenceChanged() {
336 if (has_overlay_ != NeedsOverlay())
337 UpdateIcon();
340 // static
341 const char ExtensionAppItem::kItemType[] = "ExtensionAppItem";
343 const char* ExtensionAppItem::GetItemType() const {
344 return ExtensionAppItem::kItemType;
347 void ExtensionAppItem::ExecuteLaunchCommand(int event_flags) {
348 Launch(event_flags);
351 void ExtensionAppItem::UpdatePositionFromExtensionOrdering() {
352 const syncer::StringOrdinal& page =
353 GetAppSorting(profile_)->GetPageOrdinal(extension_id_);
354 const syncer::StringOrdinal& launch =
355 GetAppSorting(profile_)->GetAppLaunchOrdinal(extension_id_);
356 set_position(syncer::StringOrdinal(
357 page.ToInternalValue() + launch.ToInternalValue()));
360 AppListControllerDelegate* ExtensionAppItem::GetController() {
361 return AppListService::Get(chrome::GetActiveDesktop())->
362 GetControllerDelegate();