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/search_controller.h"
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/metrics/histogram.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/ui/app_list/search/app_search_provider.h"
18 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
19 #include "chrome/browser/ui/app_list/search/history.h"
20 #include "chrome/browser/ui/app_list/search/history_factory.h"
21 #include "chrome/browser/ui/app_list/search/omnibox_provider.h"
22 #include "chrome/browser/ui/app_list/search/people/people_provider.h"
23 #include "chrome/browser/ui/app_list/search/search_provider.h"
24 #include "chrome/browser/ui/app_list/search/webstore/webstore_provider.h"
25 #include "chrome/browser/ui/app_list/start_page_service.h"
26 #include "chrome/common/chrome_switches.h"
27 #include "content/public/browser/user_metrics.h"
28 #include "grit/generated_resources.h"
29 #include "grit/theme_resources.h"
30 #include "ui/app_list/search_box_model.h"
31 #include "ui/app_list/speech_ui_model.h"
32 #include "ui/base/l10n/l10n_util.h"
33 #include "ui/base/resource/resource_bundle.h"
36 const char kAppListSearchResultOpenTypeHistogram
[] =
37 "Apps.AppListSearchResultOpenType";
42 SearchController::SearchController(Profile
* profile
,
43 SearchBoxModel
* search_box
,
44 AppListModel::SearchResults
* results
,
45 SpeechUIModel
* speech_ui
,
46 AppListControllerDelegate
* list_controller
)
48 search_box_(search_box
),
49 speech_ui_(speech_ui
),
50 list_controller_(list_controller
),
51 dispatching_query_(false),
52 mixer_(new Mixer(results
)),
53 history_(HistoryFactory::GetForBrowserContext(profile
)) {
54 speech_ui_
->AddObserver(this);
58 SearchController::~SearchController() {
59 speech_ui_
->RemoveObserver(this);
62 void SearchController::Init() {
63 ui::ResourceBundle
& bundle
= ui::ResourceBundle::GetSharedInstance();
64 search_box_
->SetIcon(*bundle
.GetImageSkiaNamed(IDR_OMNIBOX_SEARCH
));
65 StartPageService
* service
= StartPageService::Get(profile_
);
66 if (service
&& service
->GetSpeechRecognitionContents()) {
67 search_box_
->SetSpeechRecognitionButton(
68 scoped_ptr
<SearchBoxModel::SpeechButtonProperty
>(
69 new SearchBoxModel::SpeechButtonProperty(
70 *bundle
.GetImageSkiaNamed(IDR_OMNIBOX_MIC_SEARCH
),
71 l10n_util::GetStringUTF16(
72 IDS_APP_LIST_HOTWORD_LISTENING
),
73 *bundle
.GetImageSkiaNamed(IDR_APP_LIST_MIC_HOTWORD_OFF
),
74 l10n_util::GetStringUTF16(
75 IDS_APP_LIST_START_SPEECH_RECOGNITION
))));
77 OnSpeechRecognitionStateChanged(speech_ui_
->state());
81 AddProvider(Mixer::MAIN_GROUP
, scoped_ptr
<SearchProvider
>(
82 new AppSearchProvider(profile_
, list_controller_
)).Pass());
83 AddProvider(Mixer::OMNIBOX_GROUP
, scoped_ptr
<SearchProvider
>(
84 new OmniboxProvider(profile_
)).Pass());
85 AddProvider(Mixer::WEBSTORE_GROUP
, scoped_ptr
<SearchProvider
>(
86 new WebstoreProvider(profile_
, list_controller_
)).Pass());
87 if (!CommandLine::ForCurrentProcess()->HasSwitch(
88 switches::kDisablePeopleSearch
)) {
89 AddProvider(Mixer::PEOPLE_GROUP
, scoped_ptr
<SearchProvider
>(
90 new PeopleProvider(profile_
)).Pass());
94 void SearchController::Start() {
98 TrimWhitespace(search_box_
->text(), TRIM_ALL
, &query
);
100 dispatching_query_
= true;
101 for (Providers::iterator it
= providers_
.begin();
102 it
!= providers_
.end();
106 dispatching_query_
= false;
110 // Maximum time (in milliseconds) to wait to the search providers to finish.
111 const int kStopTimeMS
= 1500;
112 stop_timer_
.Start(FROM_HERE
,
113 base::TimeDelta::FromMilliseconds(kStopTimeMS
),
114 base::Bind(&SearchController::Stop
,
115 base::Unretained(this)));
118 void SearchController::Stop() {
121 for (Providers::iterator it
= providers_
.begin();
122 it
!= providers_
.end();
128 void SearchController::OpenResult(SearchResult
* result
, int event_flags
) {
129 // Count AppList.Search here because it is composed of search + action.
130 content::RecordAction(base::UserMetricsAction("AppList_Search"));
132 ChromeSearchResult
* chrome_result
=
133 static_cast<app_list::ChromeSearchResult
*>(result
);
134 UMA_HISTOGRAM_ENUMERATION(kAppListSearchResultOpenTypeHistogram
,
135 chrome_result
->GetType(),
136 SEARCH_RESULT_TYPE_BOUNDARY
);
137 chrome_result
->Open(event_flags
);
139 if (history_
&& history_
->IsReady()) {
140 history_
->AddLaunchEvent(base::UTF16ToUTF8(search_box_
->text()),
141 chrome_result
->id());
145 void SearchController::InvokeResultAction(SearchResult
* result
,
148 // TODO(xiyuan): Hook up with user learning.
149 static_cast<app_list::ChromeSearchResult
*>(result
)->InvokeAction(
150 action_index
, event_flags
);
153 void SearchController::AddProvider(Mixer::GroupId group
,
154 scoped_ptr
<SearchProvider
> provider
) {
155 provider
->set_result_changed_callback(base::Bind(
156 &SearchController::OnResultsChanged
,
157 base::Unretained(this)));
158 mixer_
->AddProviderToGroup(group
, provider
.get());
159 providers_
.push_back(provider
.release()); // Takes ownership.
162 void SearchController::OnResultsChanged() {
163 if (dispatching_query_
)
166 KnownResults known_results
;
167 if (history_
&& history_
->IsReady()) {
168 history_
->GetKnownResults(base::UTF16ToUTF8(search_box_
->text()))
169 ->swap(known_results
);
172 mixer_
->MixAndPublish(known_results
);
175 void SearchController::OnSpeechRecognitionStateChanged(
176 SpeechRecognitionState new_state
) {
177 search_box_
->SetHintText(l10n_util::GetStringUTF16(
178 (new_state
== SPEECH_RECOGNITION_HOTWORD_LISTENING
) ?
179 IDS_SEARCH_BOX_HOTWORD_HINT
: IDS_SEARCH_BOX_HINT
));
182 } // namespace app_list