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/browser/ui/webui/ntp/core_app_launcher_handler.h"
18 #include "chrome/common/extensions/extension_constants.h"
19 #include "chrome/common/extensions/manifest_url_handler.h"
20 #include "content/public/browser/user_metrics.h"
21 #include "extensions/browser/app_sorting.h"
22 #include "extensions/browser/extension_prefs.h"
23 #include "extensions/browser/extension_system.h"
24 #include "extensions/common/extension.h"
25 #include "extensions/common/extension_icon_set.h"
26 #include "extensions/common/manifest_handlers/icons_handler.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/image/canvas_image_source.h"
33 #include "ui/gfx/image/image_skia_operations.h"
34 #include "ui/gfx/rect.h"
36 using extensions::Extension
;
40 // Overlays a shortcut icon over the bottom left corner of a given image.
41 class ShortcutOverlayImageSource
: public gfx::CanvasImageSource
{
43 explicit ShortcutOverlayImageSource(const gfx::ImageSkia
& icon
)
44 : gfx::CanvasImageSource(icon
.size(), false),
47 virtual ~ShortcutOverlayImageSource() {}
50 // gfx::CanvasImageSource overrides:
51 virtual 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());
62 DISALLOW_COPY_AND_ASSIGN(ShortcutOverlayImageSource
);
65 // Rounds the corners of a given image.
66 class RoundedCornersImageSource
: public gfx::CanvasImageSource
{
68 explicit RoundedCornersImageSource(const gfx::ImageSkia
& icon
)
69 : gfx::CanvasImageSource(icon
.size(), false),
72 virtual ~RoundedCornersImageSource() {}
75 // gfx::CanvasImageSource overrides:
76 virtual 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
);
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
);
96 gfx::ImageSkia(masking_canvas
->ExtractImageRep()), 0, 0, masking_paint
);
101 DISALLOW_COPY_AND_ASSIGN(RoundedCornersImageSource
);
104 extensions::AppSorting
* GetAppSorting(Profile
* profile
) {
105 return extensions::ExtensionPrefs::Get(profile
)->app_sorting();
108 const color_utils::HSL shift
= {-1, 0, 0.6};
112 ExtensionAppItem::ExtensionAppItem(
114 const app_list::AppListSyncableService::SyncItem
* sync_item
,
115 const std::string
& extension_id
,
116 const std::string
& extension_name
,
117 const gfx::ImageSkia
& installing_icon
,
118 bool is_platform_app
)
119 : app_list::AppListItem(extension_id
),
121 extension_id_(extension_id
),
122 extension_enable_flow_controller_(NULL
),
123 extension_name_(extension_name
),
125 gfx::ImageSkiaOperations::CreateHSLShiftedImage(installing_icon
,
127 is_platform_app_(is_platform_app
),
128 has_overlay_(false) {
130 if (sync_item
&& sync_item
->item_ordinal
.IsValid()) {
131 // An existing synced position exists, use that.
132 set_position(sync_item
->item_ordinal
);
133 // Only set the name from the sync item if it is empty.
135 SetName(sync_item
->item_name
);
138 GetAppSorting(profile_
)->EnsureValidOrdinals(extension_id_
,
139 syncer::StringOrdinal());
140 UpdatePositionFromExtensionOrdering();
143 ExtensionAppItem::~ExtensionAppItem() {
146 bool ExtensionAppItem::NeedsOverlay() const {
147 // The overlay icon is disabled for hosted apps in windowed mode with
148 // streamlined hosted apps.
149 bool streamlined_hosted_apps
=
150 extensions::util::IsStreamlinedHostedAppsEnabled();
151 #if defined(OS_CHROMEOS)
152 if (!streamlined_hosted_apps
)
155 extensions::LaunchType launch_type
=
157 ? extensions::GetLaunchType(extensions::ExtensionPrefs::Get(profile_
),
159 : extensions::LAUNCH_TYPE_WINDOW
;
161 return !is_platform_app_
&& extension_id_
!= extension_misc::kChromeAppId
&&
162 (!streamlined_hosted_apps
||
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
);
171 SetName(extension_name_
);
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
184 if (GetExtension()) {
185 icon
= icon_
->image_skia();
186 const bool enabled
= extensions::util::IsAppLaunchable(extension_id_
,
189 const color_utils::HSL shift
= {-1, 0, 0.6};
190 icon
= gfx::ImageSkiaOperations::CreateHSLShiftedImage(icon
, shift
);
193 if (GetExtension()->from_bookmark())
194 icon
= gfx::ImageSkia(new RoundedCornersImageSource(icon
), icon
.size());
196 // Paint the shortcut overlay if necessary.
197 has_overlay_
= NeedsOverlay();
199 icon
= gfx::ImageSkia(new ShortcutOverlayImageSource(icon
), icon
.size());
204 void ExtensionAppItem::Move(const ExtensionAppItem
* prev
,
205 const ExtensionAppItem
* next
) {
207 return; // No reordering necessary
209 extensions::ExtensionPrefs
* prefs
= extensions::ExtensionPrefs::Get(profile_
);
210 extensions::AppSorting
* sorting
= GetAppSorting(profile_
);
212 syncer::StringOrdinal page
;
213 std::string prev_id
, next_id
;
215 next_id
= next
->extension_id();
216 page
= sorting
->GetPageOrdinal(next_id
);
218 prev_id
= prev
->extension_id();
219 page
= sorting
->GetPageOrdinal(prev_id
);
221 prev_id
= prev
->extension_id();
222 page
= sorting
->GetPageOrdinal(prev_id
);
223 // Only set |next_id| if on the same page, otherwise just insert after prev.
224 if (page
.Equals(sorting
->GetPageOrdinal(next
->extension_id())))
225 next_id
= next
->extension_id();
227 prefs
->SetAppDraggedByUser(extension_id_
);
228 sorting
->SetPageOrdinal(extension_id_
, page
);
229 sorting
->OnExtensionMoved(extension_id_
, prev_id
, next_id
);
230 UpdatePositionFromExtensionOrdering();
233 const Extension
* ExtensionAppItem::GetExtension() const {
234 const ExtensionService
* service
=
235 extensions::ExtensionSystem::Get(profile_
)->extension_service();
236 const Extension
* extension
= service
->GetInstalledExtension(extension_id_
);
240 void ExtensionAppItem::LoadImage(const Extension
* extension
) {
241 icon_
.reset(new extensions::IconImage(
244 extensions::IconsInfo::GetIcons(extension
),
245 extension_misc::EXTENSION_ICON_MEDIUM
,
246 extensions::util::GetDefaultAppIcon(),
251 bool ExtensionAppItem::RunExtensionEnableFlow() {
252 if (extensions::util::IsAppLaunchableWithoutEnabling(extension_id_
, profile_
))
255 if (!extension_enable_flow_
) {
256 extension_enable_flow_controller_
= GetController();
257 extension_enable_flow_controller_
->OnShowChildDialog();
259 extension_enable_flow_
.reset(new ExtensionEnableFlow(
260 profile_
, extension_id_
, this));
261 extension_enable_flow_
->StartForNativeWindow(
262 extension_enable_flow_controller_
->GetAppListWindow());
267 void ExtensionAppItem::Launch(int event_flags
) {
268 // |extension| could be NULL when it is being unloaded for updating.
269 const Extension
* extension
= GetExtension();
273 // Don't auto-enable apps that cannot be launched.
274 if (!extensions::util::IsAppLaunchable(extension_id_
, profile_
))
277 if (RunExtensionEnableFlow())
280 GetController()->LaunchApp(profile_
,
282 AppListControllerDelegate::LAUNCH_FROM_APP_LIST
,
286 void ExtensionAppItem::OnExtensionIconImageChanged(
287 extensions::IconImage
* image
) {
288 DCHECK(icon_
.get() == image
);
292 void ExtensionAppItem::ExtensionEnableFlowFinished() {
293 extension_enable_flow_
.reset();
294 extension_enable_flow_controller_
->OnCloseChildDialog();
295 extension_enable_flow_controller_
= NULL
;
297 // Automatically launch app after enabling.
301 void ExtensionAppItem::ExtensionEnableFlowAborted(bool user_initiated
) {
302 extension_enable_flow_
.reset();
303 extension_enable_flow_controller_
->OnCloseChildDialog();
304 extension_enable_flow_controller_
= NULL
;
307 void ExtensionAppItem::Activate(int event_flags
) {
308 // |extension| could be NULL when it is being unloaded for updating.
309 const Extension
* extension
= GetExtension();
313 // Don't auto-enable apps that cannot be launched.
314 if (!extensions::util::IsAppLaunchable(extension_id_
, profile_
))
317 if (RunExtensionEnableFlow())
320 content::RecordAction(base::UserMetricsAction("AppList_ClickOnApp"));
321 CoreAppLauncherHandler::RecordAppListMainLaunch(extension
);
322 GetController()->ActivateApp(profile_
,
324 AppListControllerDelegate::LAUNCH_FROM_APP_LIST
,
328 ui::MenuModel
* ExtensionAppItem::GetContextMenuModel() {
329 context_menu_
.reset(new app_list::AppContextMenu(
330 this, profile_
, extension_id_
, GetController()));
331 context_menu_
->set_is_platform_app(is_platform_app_
);
333 context_menu_
->set_is_in_folder(true);
334 return context_menu_
->GetMenuModel();
337 void ExtensionAppItem::OnExtensionPreferenceChanged() {
338 if (has_overlay_
!= NeedsOverlay())
343 const char ExtensionAppItem::kItemType
[] = "ExtensionAppItem";
345 const char* ExtensionAppItem::GetItemType() const {
346 return ExtensionAppItem::kItemType
;
349 void ExtensionAppItem::ExecuteLaunchCommand(int event_flags
) {
353 void ExtensionAppItem::UpdatePositionFromExtensionOrdering() {
354 const syncer::StringOrdinal
& page
=
355 GetAppSorting(profile_
)->GetPageOrdinal(extension_id_
);
356 const syncer::StringOrdinal
& launch
=
357 GetAppSorting(profile_
)->GetAppLaunchOrdinal(extension_id_
);
358 set_position(syncer::StringOrdinal(
359 page
.ToInternalValue() + launch
.ToInternalValue()));
362 AppListControllerDelegate
* ExtensionAppItem::GetController() {
363 return AppListService::Get(chrome::GetActiveDesktop())->
364 GetControllerDelegate();