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_prefs.h"
9 #include "chrome/browser/extensions/extension_service.h"
10 #include "chrome/browser/extensions/extension_system.h"
11 #include "chrome/browser/extensions/extension_util.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/ui/app_list/app_context_menu.h"
14 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
15 #include "chrome/browser/ui/app_list/app_list_service.h"
16 #include "chrome/browser/ui/extensions/extension_enable_flow.h"
17 #include "chrome/browser/ui/host_desktop.h"
18 #include "chrome/browser/ui/webui/ntp/core_app_launcher_handler.h"
19 #include "chrome/common/extensions/extension_constants.h"
20 #include "chrome/common/extensions/extension_icon_set.h"
21 #include "chrome/common/extensions/manifest_handlers/icons_handler.h"
22 #include "chrome/common/extensions/manifest_url_handler.h"
23 #include "content/public/browser/user_metrics.h"
24 #include "extensions/browser/app_sorting.h"
25 #include "extensions/common/extension.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/image/canvas_image_source.h"
32 #include "ui/gfx/image/image_skia_operations.h"
34 using extensions::Extension
;
38 // Overlays a shortcut icon over the bottom left corner of a given image.
39 class ShortcutOverlayImageSource
: public gfx::CanvasImageSource
{
41 explicit ShortcutOverlayImageSource(const gfx::ImageSkia
& icon
)
42 : gfx::CanvasImageSource(icon
.size(), false),
45 virtual ~ShortcutOverlayImageSource() {}
48 // gfx::CanvasImageSource overrides:
49 virtual void Draw(gfx::Canvas
* canvas
) OVERRIDE
{
50 canvas
->DrawImageInt(icon_
, 0, 0);
52 // Draw the overlay in the bottom left corner of the icon.
53 const gfx::ImageSkia
& overlay
= *ui::ResourceBundle::GetSharedInstance().
54 GetImageSkiaNamed(IDR_APP_LIST_TAB_OVERLAY
);
55 canvas
->DrawImageInt(overlay
, 0, icon_
.height() - overlay
.height());
60 DISALLOW_COPY_AND_ASSIGN(ShortcutOverlayImageSource
);
63 extensions::AppSorting
* GetAppSorting(Profile
* profile
) {
64 ExtensionService
* service
=
65 extensions::ExtensionSystem::Get(profile
)->extension_service();
66 return service
->extension_prefs()->app_sorting();
69 const color_utils::HSL shift
= {-1, 0, 0.6};
73 ExtensionAppItem::ExtensionAppItem(
75 const app_list::AppListSyncableService::SyncItem
* sync_item
,
76 const std::string
& extension_id
,
77 const std::string
& extension_name
,
78 const gfx::ImageSkia
& installing_icon
,
80 : app_list::AppListItem(extension_id
),
82 extension_id_(extension_id
),
83 extension_enable_flow_controller_(NULL
),
84 extension_name_(extension_name
),
86 gfx::ImageSkiaOperations::CreateHSLShiftedImage(installing_icon
,
88 is_platform_app_(is_platform_app
) {
90 if (sync_item
&& sync_item
->item_ordinal
.IsValid()) {
91 // An existing synced position exists, use that.
92 set_position(sync_item
->item_ordinal
);
95 GetAppSorting(profile_
)->EnsureValidOrdinals(extension_id_
,
96 syncer::StringOrdinal());
97 UpdatePositionFromExtensionOrdering();
100 ExtensionAppItem::~ExtensionAppItem() {
103 bool ExtensionAppItem::HasOverlay() const {
104 #if defined(OS_CHROMEOS)
107 return !is_platform_app_
&& extension_id_
!= extension_misc::kChromeAppId
;
111 void ExtensionAppItem::Reload() {
112 const Extension
* extension
= GetExtension();
113 bool is_installing
= !extension
;
114 SetIsInstalling(is_installing
);
116 SetTitleAndFullName(extension_name_
, extension_name_
);
120 SetTitleAndFullName(extension
->short_name(), extension
->name());
121 LoadImage(extension
);
124 void ExtensionAppItem::UpdateIcon() {
125 if (!GetExtension()) {
126 gfx::ImageSkia icon
= installing_icon_
;
128 icon
= gfx::ImageSkia(new ShortcutOverlayImageSource(icon
), icon
.size());
129 SetIcon(icon
, !HasOverlay());
132 gfx::ImageSkia icon
= icon_
->image_skia();
134 const ExtensionService
* service
=
135 extensions::ExtensionSystem::Get(profile_
)->extension_service();
136 const bool enabled
= extension_util::IsAppLaunchable(extension_id_
, service
);
138 const color_utils::HSL shift
= {-1, 0, 0.6};
139 icon
= gfx::ImageSkiaOperations::CreateHSLShiftedImage(icon
, shift
);
143 icon
= gfx::ImageSkia(new ShortcutOverlayImageSource(icon
), icon
.size());
145 SetIcon(icon
, !HasOverlay());
148 void ExtensionAppItem::Move(const ExtensionAppItem
* prev
,
149 const ExtensionAppItem
* next
) {
151 return; // No reordering necessary
153 ExtensionService
* service
=
154 extensions::ExtensionSystem::Get(profile_
)->extension_service();
155 extensions::AppSorting
* sorting
= service
->extension_prefs()->app_sorting();
157 syncer::StringOrdinal page
;
158 std::string prev_id
, next_id
;
160 next_id
= next
->extension_id();
161 page
= sorting
->GetPageOrdinal(next_id
);
163 prev_id
= prev
->extension_id();
164 page
= sorting
->GetPageOrdinal(prev_id
);
166 prev_id
= prev
->extension_id();
167 page
= sorting
->GetPageOrdinal(prev_id
);
168 // Only set |next_id| if on the same page, otherwise just insert after prev.
169 if (page
.Equals(sorting
->GetPageOrdinal(next
->extension_id())))
170 next_id
= next
->extension_id();
172 service
->extension_prefs()->SetAppDraggedByUser(extension_id_
);
173 sorting
->SetPageOrdinal(extension_id_
, page
);
174 service
->OnExtensionMoved(extension_id_
, prev_id
, next_id
);
175 UpdatePositionFromExtensionOrdering();
178 const Extension
* ExtensionAppItem::GetExtension() const {
179 const ExtensionService
* service
=
180 extensions::ExtensionSystem::Get(profile_
)->extension_service();
181 const Extension
* extension
= service
->GetInstalledExtension(extension_id_
);
185 void ExtensionAppItem::LoadImage(const Extension
* extension
) {
186 icon_
.reset(new extensions::IconImage(
189 extensions::IconsInfo::GetIcons(extension
),
190 extension_misc::EXTENSION_ICON_MEDIUM
,
191 extensions::IconsInfo::GetDefaultAppIcon(),
196 bool ExtensionAppItem::RunExtensionEnableFlow() {
197 const ExtensionService
* service
=
198 extensions::ExtensionSystem::Get(profile_
)->extension_service();
199 if (extension_util::IsAppLaunchableWithoutEnabling(extension_id_
, service
))
202 if (!extension_enable_flow_
) {
203 extension_enable_flow_controller_
= GetController();
204 extension_enable_flow_controller_
->OnShowExtensionPrompt();
206 extension_enable_flow_
.reset(new ExtensionEnableFlow(
207 profile_
, extension_id_
, this));
208 extension_enable_flow_
->StartForNativeWindow(
209 extension_enable_flow_controller_
->GetAppListWindow());
214 void ExtensionAppItem::Launch(int event_flags
) {
215 // |extension| could be NULL when it is being unloaded for updating.
216 const Extension
* extension
= GetExtension();
220 if (RunExtensionEnableFlow())
223 GetController()->LaunchApp(profile_
,
225 AppListControllerDelegate::LAUNCH_FROM_APP_LIST
,
229 void ExtensionAppItem::OnExtensionIconImageChanged(
230 extensions::IconImage
* image
) {
231 DCHECK(icon_
.get() == image
);
235 void ExtensionAppItem::ExtensionEnableFlowFinished() {
236 extension_enable_flow_
.reset();
237 extension_enable_flow_controller_
->OnCloseExtensionPrompt();
238 extension_enable_flow_controller_
= NULL
;
240 // Automatically launch app after enabling.
244 void ExtensionAppItem::ExtensionEnableFlowAborted(bool user_initiated
) {
245 extension_enable_flow_
.reset();
246 extension_enable_flow_controller_
->OnCloseExtensionPrompt();
247 extension_enable_flow_controller_
= NULL
;
250 void ExtensionAppItem::Activate(int event_flags
) {
251 // |extension| could be NULL when it is being unloaded for updating.
252 const Extension
* extension
= GetExtension();
256 if (RunExtensionEnableFlow())
259 content::RecordAction(base::UserMetricsAction("AppList_ClickOnApp"));
260 CoreAppLauncherHandler::RecordAppListMainLaunch(extension
);
261 GetController()->ActivateApp(profile_
,
263 AppListControllerDelegate::LAUNCH_FROM_APP_LIST
,
267 ui::MenuModel
* ExtensionAppItem::GetContextMenuModel() {
268 if (!context_menu_
) {
269 context_menu_
.reset(new app_list::AppContextMenu(
270 this, profile_
, extension_id_
, GetController(),
271 is_platform_app_
, false));
274 return context_menu_
->GetMenuModel();
278 const char ExtensionAppItem::kAppType
[] = "ExtensionAppItem";
280 const char* ExtensionAppItem::GetAppType() const {
281 return ExtensionAppItem::kAppType
;
284 void ExtensionAppItem::ExecuteLaunchCommand(int event_flags
) {
288 void ExtensionAppItem::UpdatePositionFromExtensionOrdering() {
289 const syncer::StringOrdinal
& page
=
290 GetAppSorting(profile_
)->GetPageOrdinal(extension_id_
);
291 const syncer::StringOrdinal
& launch
=
292 GetAppSorting(profile_
)->GetAppLaunchOrdinal(extension_id_
);
293 set_position(syncer::StringOrdinal(
294 page
.ToInternalValue() + launch
.ToInternalValue()));
297 AppListControllerDelegate
* ExtensionAppItem::GetController() {
298 return AppListService::Get(chrome::GetActiveDesktop())->
299 GetControllerDelegate();