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_util.h"
9 #include "chrome/browser/extensions/launch_util.h"
10 #include "chrome/browser/profiles/profile.h"
11 #include "chrome/browser/ui/app_list/app_context_menu.h"
12 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
13 #include "chrome/browser/ui/app_list/app_list_service.h"
14 #include "chrome/browser/ui/extensions/extension_enable_flow.h"
15 #include "chrome/browser/ui/host_desktop.h"
16 #include "chrome/common/extensions/extension_constants.h"
17 #include "chrome/common/extensions/extension_metrics.h"
18 #include "content/public/browser/user_metrics.h"
19 #include "extensions/browser/app_sorting.h"
20 #include "extensions/browser/extension_prefs.h"
21 #include "extensions/browser/extension_registry.h"
22 #include "extensions/common/extension.h"
23 #include "extensions/common/extension_icon_set.h"
24 #include "extensions/common/manifest_handlers/icons_handler.h"
25 #include "extensions/common/manifest_url_handlers.h"
26 #include "grit/theme_resources.h"
27 #include "sync/api/string_ordinal.h"
28 #include "ui/base/resource/resource_bundle.h"
29 #include "ui/gfx/canvas.h"
30 #include "ui/gfx/color_utils.h"
31 #include "ui/gfx/geometry/rect.h"
32 #include "ui/gfx/image/canvas_image_source.h"
33 #include "ui/gfx/image/image_skia_operations.h"
35 using extensions::Extension
;
39 // Overlays a shortcut icon over the bottom left corner of a given image.
40 class ShortcutOverlayImageSource
: public gfx::CanvasImageSource
{
42 explicit ShortcutOverlayImageSource(const gfx::ImageSkia
& icon
)
43 : gfx::CanvasImageSource(icon
.size(), false),
46 ~ShortcutOverlayImageSource() override
{}
49 // gfx::CanvasImageSource overrides:
50 void Draw(gfx::Canvas
* canvas
) override
{
51 canvas
->DrawImageInt(icon_
, 0, 0);
53 // Draw the overlay in the bottom left corner of the icon.
54 const gfx::ImageSkia
& overlay
= *ui::ResourceBundle::GetSharedInstance().
55 GetImageSkiaNamed(IDR_APP_LIST_TAB_OVERLAY
);
56 canvas
->DrawImageInt(overlay
, 0, icon_
.height() - overlay
.height());
61 DISALLOW_COPY_AND_ASSIGN(ShortcutOverlayImageSource
);
64 // Rounds the corners of a given image.
65 class RoundedCornersImageSource
: public gfx::CanvasImageSource
{
67 explicit RoundedCornersImageSource(const gfx::ImageSkia
& icon
)
68 : gfx::CanvasImageSource(icon
.size(), false),
71 ~RoundedCornersImageSource() override
{}
74 // gfx::CanvasImageSource overrides:
75 void Draw(gfx::Canvas
* canvas
) override
{
76 // The radius used to round the app icon.
77 const size_t kRoundingRadius
= 2;
79 canvas
->DrawImageInt(icon_
, 0, 0);
81 scoped_ptr
<gfx::Canvas
> masking_canvas(
82 new gfx::Canvas(gfx::Size(icon_
.width(), icon_
.height()), 1.0f
, false));
83 DCHECK(masking_canvas
);
86 opaque_paint
.setColor(SK_ColorWHITE
);
87 opaque_paint
.setFlags(SkPaint::kAntiAlias_Flag
);
88 masking_canvas
->DrawRoundRect(
89 gfx::Rect(icon_
.width(), icon_
.height()),
90 kRoundingRadius
, opaque_paint
);
92 SkPaint masking_paint
;
93 masking_paint
.setXfermodeMode(SkXfermode::kDstIn_Mode
);
95 gfx::ImageSkia(masking_canvas
->ExtractImageRep()), 0, 0, masking_paint
);
100 DISALLOW_COPY_AND_ASSIGN(RoundedCornersImageSource
);
103 extensions::AppSorting
* GetAppSorting(Profile
* profile
) {
104 return extensions::ExtensionPrefs::Get(profile
)->app_sorting();
107 gfx::ImageSkia
CreateDisabledIcon(const gfx::ImageSkia
& icon
) {
108 const color_utils::HSL shift
= {-1, 0, 0.6};
109 return gfx::ImageSkiaOperations::CreateHSLShiftedImage(icon
, shift
);
114 ExtensionAppItem::ExtensionAppItem(
116 const app_list::AppListSyncableService::SyncItem
* sync_item
,
117 const std::string
& extension_id
,
118 const std::string
& extension_name
,
119 const gfx::ImageSkia
& installing_icon
,
120 bool is_platform_app
)
121 : app_list::AppListItem(extension_id
),
123 extension_id_(extension_id
),
124 extension_enable_flow_controller_(NULL
),
125 extension_name_(extension_name
),
126 installing_icon_(CreateDisabledIcon(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 #if defined(OS_CHROMEOS)
148 // The overlay is disabled completely in ChromeOS.
152 extensions::LaunchType launch_type
=
154 ? extensions::GetLaunchType(extensions::ExtensionPrefs::Get(profile_
),
156 : extensions::LAUNCH_TYPE_WINDOW
;
158 // The overlay icon is disabled for hosted apps in windowed mode with
159 // bookmark apps enabled.
160 return !is_platform_app_
&& extension_id_
!= extension_misc::kChromeAppId
&&
161 (!extensions::util::IsNewBookmarkAppsEnabled() ||
162 launch_type
!= extensions::LAUNCH_TYPE_WINDOW
);
165 void ExtensionAppItem::Reload() {
166 const Extension
* extension
= GetExtension();
167 bool is_installing
= !extension
;
168 SetIsInstalling(is_installing
);
170 SetName(extension_name_
);
174 SetNameAndShortName(extension
->name(), extension
->short_name());
175 LoadImage(extension
);
178 void ExtensionAppItem::UpdateIcon() {
179 gfx::ImageSkia icon
= installing_icon_
;
181 // Use the app icon if the app exists. Turn the image greyscale if the app is
183 if (GetExtension() && icon_
) {
184 icon
= icon_
->image_skia();
185 const bool enabled
= extensions::util::IsAppLaunchable(extension_id_
,
188 icon
= CreateDisabledIcon(icon
);
190 if (GetExtension()->from_bookmark())
191 icon
= gfx::ImageSkia(new RoundedCornersImageSource(icon
), icon
.size());
193 // Paint the shortcut overlay if necessary.
194 has_overlay_
= NeedsOverlay();
196 icon
= gfx::ImageSkia(new ShortcutOverlayImageSource(icon
), icon
.size());
201 void ExtensionAppItem::Move(const ExtensionAppItem
* prev
,
202 const ExtensionAppItem
* next
) {
204 return; // No reordering necessary
206 extensions::ExtensionPrefs
* prefs
= extensions::ExtensionPrefs::Get(profile_
);
207 extensions::AppSorting
* sorting
= GetAppSorting(profile_
);
209 syncer::StringOrdinal page
;
210 std::string prev_id
, next_id
;
212 next_id
= next
->extension_id();
213 page
= sorting
->GetPageOrdinal(next_id
);
215 prev_id
= prev
->extension_id();
216 page
= sorting
->GetPageOrdinal(prev_id
);
218 prev_id
= prev
->extension_id();
219 page
= sorting
->GetPageOrdinal(prev_id
);
220 // Only set |next_id| if on the same page, otherwise just insert after prev.
221 if (page
.Equals(sorting
->GetPageOrdinal(next
->extension_id())))
222 next_id
= next
->extension_id();
224 prefs
->SetAppDraggedByUser(extension_id_
);
225 sorting
->SetPageOrdinal(extension_id_
, page
);
226 sorting
->OnExtensionMoved(extension_id_
, prev_id
, next_id
);
227 UpdatePositionFromExtensionOrdering();
230 const Extension
* ExtensionAppItem::GetExtension() const {
231 const extensions::ExtensionRegistry
* registry
=
232 extensions::ExtensionRegistry::Get(profile_
);
233 const Extension
* extension
= registry
->GetInstalledExtension(
238 void ExtensionAppItem::LoadImage(const Extension
* extension
) {
239 icon_
.reset(new extensions::IconImage(
242 extensions::IconsInfo::GetIcons(extension
),
243 extension_misc::EXTENSION_ICON_MEDIUM
,
244 extensions::util::GetDefaultAppIcon(),
249 bool ExtensionAppItem::RunExtensionEnableFlow() {
250 if (extensions::util::IsAppLaunchableWithoutEnabling(extension_id_
, profile_
))
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());
265 void ExtensionAppItem::Launch(int event_flags
) {
266 // |extension| could be NULL when it is being unloaded for updating.
267 const Extension
* extension
= GetExtension();
271 // Don't auto-enable apps that cannot be launched.
272 if (!extensions::util::IsAppLaunchable(extension_id_
, profile_
))
275 if (RunExtensionEnableFlow())
278 GetController()->LaunchApp(profile_
,
280 AppListControllerDelegate::LAUNCH_FROM_APP_LIST
,
284 void ExtensionAppItem::OnExtensionIconImageChanged(
285 extensions::IconImage
* image
) {
286 DCHECK(icon_
.get() == image
);
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.
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();
311 // Don't auto-enable apps that cannot be launched.
312 if (!extensions::util::IsAppLaunchable(extension_id_
, profile_
))
315 if (RunExtensionEnableFlow())
318 content::RecordAction(base::UserMetricsAction("AppList_ClickOnApp"));
319 extensions::RecordAppListMainLaunch(extension
);
320 GetController()->ActivateApp(profile_
,
322 AppListControllerDelegate::LAUNCH_FROM_APP_LIST
,
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_
);
331 context_menu_
->set_is_in_folder(true);
332 return context_menu_
->GetMenuModel();
335 void ExtensionAppItem::OnExtensionPreferenceChanged() {
336 if (has_overlay_
!= NeedsOverlay())
341 const char ExtensionAppItem::kItemType
[] = "ExtensionAppItem";
343 const char* ExtensionAppItem::GetItemType() const {
344 return ExtensionAppItem::kItemType
;
347 void ExtensionAppItem::ExecuteLaunchCommand(int 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();