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_search_provider.h"
10 #include "base/location.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "base/time/clock.h"
15 #include "chrome/browser/extensions/extension_service.h"
16 #include "chrome/browser/extensions/extension_ui_util.h"
17 #include "chrome/browser/extensions/extension_util.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/ui/app_list/app_list_syncable_service.h"
20 #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
21 #include "chrome/browser/ui/app_list/search/app_result.h"
22 #include "extensions/browser/extension_prefs.h"
23 #include "extensions/browser/extension_registry.h"
24 #include "extensions/browser/extension_system.h"
25 #include "extensions/common/extension.h"
26 #include "extensions/common/extension_set.h"
27 #include "ui/app_list/app_list_item.h"
28 #include "ui/app_list/app_list_model.h"
29 #include "ui/app_list/search/tokenized_string.h"
30 #include "ui/app_list/search/tokenized_string_match.h"
32 using extensions::ExtensionRegistry
;
36 // The size of each step unlaunched apps should increase their relevance by.
37 const double kUnlaunchedAppRelevanceStepSize
= 0.0001;
42 class AppSearchProvider::App
{
44 explicit App(const extensions::Extension
* extension
,
45 const base::Time
& last_launch_time
)
46 : app_id_(extension
->id()),
47 indexed_name_(base::UTF8ToUTF16(extension
->short_name())),
48 last_launch_time_(last_launch_time
) {}
51 const std::string
& app_id() const { return app_id_
; }
52 const TokenizedString
& indexed_name() const { return indexed_name_
; }
53 const base::Time
& last_launch_time() const { return last_launch_time_
; }
56 const std::string app_id_
;
57 TokenizedString indexed_name_
;
58 base::Time last_launch_time_
;
60 DISALLOW_COPY_AND_ASSIGN(App
);
63 AppSearchProvider::AppSearchProvider(Profile
* profile
,
64 AppListControllerDelegate
* list_controller
,
65 scoped_ptr
<base::Clock
> clock
,
66 AppListItemList
* top_level_item_list
)
68 list_controller_(list_controller
),
69 extension_registry_observer_(this),
70 top_level_item_list_(top_level_item_list
),
72 update_results_factory_(this) {
73 extension_registry_observer_
.Add(ExtensionRegistry::Get(profile_
));
77 AppSearchProvider::~AppSearchProvider() {}
79 void AppSearchProvider::Start(bool /*is_voice_query*/,
80 const base::string16
& query
) {
82 const TokenizedString
query_terms(query
);
86 bool show_recommendations
= query
.empty();
87 // Refresh list of apps to ensure we have the latest launch time information.
88 // This will also cause the results to update.
89 if (show_recommendations
)
95 void AppSearchProvider::Stop() {
98 void AppSearchProvider::UpdateResults() {
99 const TokenizedString
query_terms(query_
);
100 bool show_recommendations
= query_
.empty();
103 if (show_recommendations
) {
104 // Build a map of app ids to their position in the app list.
105 std::map
<std::string
, size_t> id_to_app_list_index
;
106 for (size_t i
= 0; i
< top_level_item_list_
->item_count(); ++i
) {
107 id_to_app_list_index
[top_level_item_list_
->item_at(i
)->id()] = i
;
110 for (const App
* app
: apps_
) {
111 scoped_ptr
<AppResult
> result(
112 new AppResult(profile_
, app
->app_id(), list_controller_
, true));
113 result
->set_title(app
->indexed_name().text());
115 // Use the app list order to tiebreak apps that have never been launched.
116 if (app
->last_launch_time().is_null()) {
117 auto it
= id_to_app_list_index
.find(app
->app_id());
118 // If it's in a folder, it won't be in |id_to_app_list_index|. Rank
119 // those as if they are at the end of the list.
120 size_t app_list_index
=
121 it
== id_to_app_list_index
.end() ? apps_
.size() : (*it
).second
;
122 if (app_list_index
> apps_
.size())
123 app_list_index
= apps_
.size();
125 result
->set_relevance(kUnlaunchedAppRelevanceStepSize
*
126 (apps_
.size() - app_list_index
));
128 result
->UpdateFromLastLaunched(clock_
->Now(), app
->last_launch_time());
133 for (const App
* app
: apps_
) {
134 scoped_ptr
<AppResult
> result(
135 new AppResult(profile_
, app
->app_id(), list_controller_
, false));
136 TokenizedStringMatch match
;
137 if (!match
.Calculate(query_terms
, app
->indexed_name()))
140 result
->UpdateFromMatch(app
->indexed_name(), match
);
145 update_results_factory_
.InvalidateWeakPtrs();
148 void AppSearchProvider::AddApps(const extensions::ExtensionSet
& extensions
) {
149 extensions::ExtensionPrefs
* prefs
= extensions::ExtensionPrefs::Get(profile_
);
150 for (extensions::ExtensionSet::const_iterator iter
= extensions
.begin();
151 iter
!= extensions
.end(); ++iter
) {
152 const extensions::Extension
* app
= iter
->get();
154 if (!extensions::ui_util::ShouldDisplayInAppLauncher(app
, profile_
))
157 if (profile_
->IsOffTheRecord() &&
158 !extensions::util::CanLoadInIncognito(app
, profile_
))
161 apps_
.push_back(new App(app
, prefs
->GetLastLaunchTime(app
->id())));
165 void AppSearchProvider::RefreshApps() {
167 ExtensionRegistry
* registry
= ExtensionRegistry::Get(profile_
);
168 AddApps(registry
->enabled_extensions());
169 AddApps(registry
->disabled_extensions());
170 AddApps(registry
->terminated_extensions());
173 void AppSearchProvider::OnExtensionLoaded(
174 content::BrowserContext
* browser_context
,
175 const extensions::Extension
* extension
) {
177 if (!update_results_factory_
.HasWeakPtrs()) {
178 base::ThreadTaskRunnerHandle::Get()->PostTask(
179 FROM_HERE
, base::Bind(&AppSearchProvider::UpdateResults
,
180 update_results_factory_
.GetWeakPtr()));
184 void AppSearchProvider::OnExtensionUninstalled(
185 content::BrowserContext
* browser_context
,
186 const extensions::Extension
* extension
,
187 extensions::UninstallReason reason
) {
189 if (!update_results_factory_
.HasWeakPtrs()) {
190 base::ThreadTaskRunnerHandle::Get()->PostTask(
191 FROM_HERE
, base::Bind(&AppSearchProvider::UpdateResults
,
192 update_results_factory_
.GetWeakPtr()));
196 } // namespace app_list