1 // Copyright 2013 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/search/app_result.h"
7 #include "base/time/time.h"
8 #include "chrome/browser/extensions/extension_util.h"
9 #include "chrome/browser/profiles/profile.h"
10 #include "chrome/browser/ui/app_list/app_context_menu.h"
11 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
12 #include "chrome/browser/ui/app_list/search/search_util.h"
13 #include "chrome/browser/ui/extensions/extension_enable_flow.h"
14 #include "chrome/common/extensions/extension_metrics.h"
15 #include "content/public/browser/user_metrics.h"
16 #include "extensions/browser/extension_registry.h"
17 #include "extensions/browser/extension_system_provider.h"
18 #include "extensions/browser/extensions_browser_client.h"
19 #include "extensions/common/constants.h"
20 #include "extensions/common/extension.h"
21 #include "extensions/common/extension_icon_set.h"
22 #include "extensions/common/manifest_handlers/icons_handler.h"
23 #include "ui/app_list/app_list_switches.h"
24 #include "ui/gfx/color_utils.h"
25 #include "ui/gfx/image/image_skia_operations.h"
29 AppResult::AppResult(Profile
* profile
,
30 const std::string
& app_id
,
31 AppListControllerDelegate
* controller
,
32 bool is_recommendation
)
35 controller_(controller
),
36 extension_registry_(NULL
) {
37 set_id(extensions::Extension::GetBaseURLFromExtensionId(app_id_
).spec());
38 if (app_list::switches::IsExperimentalAppListEnabled())
39 set_display_type(is_recommendation
? DISPLAY_RECOMMENDATION
: DISPLAY_TILE
);
41 const extensions::Extension
* extension
=
42 extensions::ExtensionRegistry::Get(profile_
)->GetInstalledExtension(
46 is_platform_app_
= extension
->is_platform_app();
49 new extensions::IconImage(profile_
,
51 extensions::IconsInfo::GetIcons(extension
),
52 GetPreferredIconDimension(),
53 extensions::util::GetDefaultAppIcon(),
57 StartObservingExtensionRegistry();
60 AppResult::~AppResult() {
61 StopObservingExtensionRegistry();
64 void AppResult::UpdateFromLastLaunched(const base::Time
& current_time
,
65 const base::Time
& last_launched
) {
66 base::TimeDelta delta
= current_time
- last_launched
;
67 // |current_time| can be before |last_launched| in weird cases such as users
68 // playing with their clocks. Handle this gracefully.
69 if (current_time
< last_launched
) {
74 const int kSecondsInWeek
= 60 * 60 * 24 * 7;
76 // Set the relevance to a value between 0 and 1. This function decays as the
77 // time delta increases and reaches a value of 0.5 at 1 week.
78 set_relevance(1 / (1 + delta
.InSecondsF() / kSecondsInWeek
));
81 void AppResult::Open(int event_flags
) {
82 RecordHistogram(APP_SEARCH_RESULT
);
83 const extensions::Extension
* extension
=
84 extensions::ExtensionRegistry::Get(profile_
)->GetInstalledExtension(
89 // Don't auto-enable apps that cannot be launched.
90 if (!extensions::util::IsAppLaunchable(app_id_
, profile_
))
93 // Check if enable flow is already running or should be started
94 if (RunExtensionEnableFlow())
97 if (display_type() != DISPLAY_RECOMMENDATION
) {
98 extensions::RecordAppListSearchLaunch(extension
);
99 content::RecordAction(
100 base::UserMetricsAction("AppList_ClickOnAppFromSearch"));
103 controller_
->ActivateApp(
106 AppListControllerDelegate::LAUNCH_FROM_APP_LIST_SEARCH
,
110 scoped_ptr
<SearchResult
> AppResult::Duplicate() const {
111 scoped_ptr
<SearchResult
> copy(
112 new AppResult(profile_
, app_id_
, controller_
,
113 display_type() == DISPLAY_RECOMMENDATION
));
114 copy
->set_title(title());
115 copy
->set_title_tags(title_tags());
116 copy
->set_relevance(relevance());
121 ui::MenuModel
* AppResult::GetContextMenuModel() {
122 if (!context_menu_
) {
123 context_menu_
.reset(new AppContextMenu(
124 this, profile_
, app_id_
, controller_
));
125 context_menu_
->set_is_platform_app(is_platform_app_
);
126 context_menu_
->set_is_search_result(true);
129 return context_menu_
->GetMenuModel();
132 void AppResult::StartObservingExtensionRegistry() {
133 DCHECK(!extension_registry_
);
135 extension_registry_
= extensions::ExtensionRegistry::Get(profile_
);
136 extension_registry_
->AddObserver(this);
139 void AppResult::StopObservingExtensionRegistry() {
140 if (extension_registry_
)
141 extension_registry_
->RemoveObserver(this);
142 extension_registry_
= NULL
;
145 bool AppResult::RunExtensionEnableFlow() {
146 if (extensions::util::IsAppLaunchableWithoutEnabling(app_id_
, profile_
))
149 if (!extension_enable_flow_
) {
150 controller_
->OnShowChildDialog();
152 extension_enable_flow_
.reset(new ExtensionEnableFlow(
153 profile_
, app_id_
, this));
154 extension_enable_flow_
->StartForNativeWindow(
155 controller_
->GetAppListWindow());
160 void AppResult::UpdateIcon() {
161 gfx::ImageSkia icon
= icon_
->image_skia();
163 if (!extensions::util::IsAppLaunchable(app_id_
, profile_
)) {
164 const color_utils::HSL shift
= {-1, 0, 0.6};
165 icon
= gfx::ImageSkiaOperations::CreateHSLShiftedImage(icon
, shift
);
171 void AppResult::OnExtensionIconImageChanged(extensions::IconImage
* image
) {
172 DCHECK_EQ(icon_
.get(), image
);
176 void AppResult::ExecuteLaunchCommand(int event_flags
) {
180 void AppResult::ExtensionEnableFlowFinished() {
181 extension_enable_flow_
.reset();
182 controller_
->OnCloseChildDialog();
184 // Automatically open app after enabling.
188 void AppResult::ExtensionEnableFlowAborted(bool user_initiated
) {
189 extension_enable_flow_
.reset();
190 controller_
->OnCloseChildDialog();
193 void AppResult::OnExtensionLoaded(content::BrowserContext
* browser_context
,
194 const extensions::Extension
* extension
) {
198 void AppResult::OnShutdown(extensions::ExtensionRegistry
* registry
) {
199 DCHECK_EQ(extension_registry_
, registry
);
200 StopObservingExtensionRegistry();
203 } // namespace app_list