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. 4 5 #include "chrome/browser/ui/app_list/search/search_controller.h" 6 7 #include <algorithm> 8 #include <vector> 9 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/app_list_controller_delegate.h" 18 #include "chrome/browser/ui/app_list/search/app_search_provider.h" 19 #include "chrome/browser/ui/app_list/search/chrome_search_result.h" 20 #include "chrome/browser/ui/app_list/search/history.h" 21 #include "chrome/browser/ui/app_list/search/history_factory.h" 22 #include "chrome/browser/ui/app_list/search/omnibox_provider.h" 23 #include "chrome/browser/ui/app_list/search/people/people_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 "chrome/grit/generated_resources.h" 28 #include "content/public/browser/user_metrics.h" 29 #include "grit/components_scaled_resources.h" 30 #include "grit/theme_resources.h" 31 #include "ui/app_list/search_box_model.h" 32 #include "ui/app_list/speech_ui_model.h" 33 #include "ui/base/l10n/l10n_util.h" 34 #include "ui/base/resource/resource_bundle.h" 35 36 namespace { 37 const char kAppListSearchResultOpenTypeHistogram[] = 38 "Apps.AppListSearchResultOpenType"; 39 40 // Maximum time (in milliseconds) to wait to the search providers to finish. 41 const int kStopTimeMS = 1500; 42 } 43 44 namespace app_list { 45 46 SearchController::SearchController(Profile* profile, 47 SearchBoxModel* search_box, 48 AppListModel::SearchResults* results, 49 SpeechUIModel* speech_ui, 50 AppListControllerDelegate* list_controller) 51 : profile_(profile), 52 search_box_(search_box), 53 speech_ui_(speech_ui), 54 list_controller_(list_controller), 55 dispatching_query_(false), 56 mixer_(new Mixer(results)), 57 history_(HistoryFactory::GetForBrowserContext(profile)) { 58 speech_ui_->AddObserver(this); 59 Init(); 60 } 61 62 SearchController::~SearchController() { 63 speech_ui_->RemoveObserver(this); 64 } 65 66 void SearchController::Init() { 67 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 68 search_box_->SetIcon(*bundle.GetImageSkiaNamed(IDR_OMNIBOX_SEARCH)); 69 StartPageService* service = StartPageService::Get(profile_); 70 if (service && service->GetSpeechRecognitionContents()) { 71 search_box_->SetSpeechRecognitionButton( 72 scoped_ptr<SearchBoxModel::SpeechButtonProperty>( 73 new SearchBoxModel::SpeechButtonProperty( 74 *bundle.GetImageSkiaNamed(IDR_OMNIBOX_MIC_SEARCH), 75 l10n_util::GetStringUTF16( 76 IDS_APP_LIST_HOTWORD_LISTENING), 77 *bundle.GetImageSkiaNamed(IDR_APP_LIST_MIC_HOTWORD_OFF), 78 l10n_util::GetStringUTF16( 79 IDS_APP_LIST_START_SPEECH_RECOGNITION)))); 80 } 81 OnSpeechRecognitionStateChanged(speech_ui_->state()); 82 83 mixer_->Init(); 84 85 AddProvider(Mixer::MAIN_GROUP, scoped_ptr<SearchProvider>( 86 new AppSearchProvider(profile_, list_controller_)).Pass()); 87 AddProvider(Mixer::OMNIBOX_GROUP, scoped_ptr<SearchProvider>( 88 new OmniboxProvider(profile_)).Pass()); 89 AddProvider(Mixer::WEBSTORE_GROUP, scoped_ptr<SearchProvider>( 90 new WebstoreProvider(profile_, list_controller_)).Pass()); 91 if (!CommandLine::ForCurrentProcess()->HasSwitch( 92 switches::kDisablePeopleSearch)) { 93 AddProvider(Mixer::PEOPLE_GROUP, scoped_ptr<SearchProvider>( 94 new PeopleProvider(profile_)).Pass()); 95 } 96 } 97 98 void SearchController::Start() { 99 Stop(); 100 101 list_controller_->OnSearchStarted(); 102 103 base::string16 query; 104 base::TrimWhitespace(search_box_->text(), base::TRIM_ALL, &query); 105 106 dispatching_query_ = true; 107 for (Providers::iterator it = providers_.begin(); 108 it != providers_.end(); 109 ++it) { 110 (*it)->Start(query); 111 } 112 dispatching_query_ = false; 113 114 OnResultsChanged(); 115 116 stop_timer_.Start(FROM_HERE, 117 base::TimeDelta::FromMilliseconds(kStopTimeMS), 118 base::Bind(&SearchController::Stop, 119 base::Unretained(this))); 120 } 121 122 void SearchController::Stop() { 123 stop_timer_.Stop(); 124 125 for (Providers::iterator it = providers_.begin(); 126 it != providers_.end(); 127 ++it) { 128 (*it)->Stop(); 129 } 130 } 131 132 void SearchController::OpenResult(SearchResult* result, int event_flags) { 133 // Count AppList.Search here because it is composed of search + action. 134 content::RecordAction(base::UserMetricsAction("AppList_Search")); 135 136 ChromeSearchResult* chrome_result = 137 static_cast<app_list::ChromeSearchResult*>(result); 138 UMA_HISTOGRAM_ENUMERATION(kAppListSearchResultOpenTypeHistogram, 139 chrome_result->GetType(), 140 SEARCH_RESULT_TYPE_BOUNDARY); 141 chrome_result->Open(event_flags); 142 143 if (history_ && history_->IsReady()) { 144 history_->AddLaunchEvent(base::UTF16ToUTF8(search_box_->text()), 145 chrome_result->id()); 146 } 147 } 148 149 void SearchController::InvokeResultAction(SearchResult* result, 150 int action_index, 151 int event_flags) { 152 // TODO(xiyuan): Hook up with user learning. 153 static_cast<app_list::ChromeSearchResult*>(result)->InvokeAction( 154 action_index, event_flags); 155 } 156 157 void SearchController::AddProvider(Mixer::GroupId group, 158 scoped_ptr<SearchProvider> provider) { 159 provider->set_result_changed_callback(base::Bind( 160 &SearchController::OnResultsChanged, 161 base::Unretained(this))); 162 mixer_->AddProviderToGroup(group, provider.get()); 163 providers_.push_back(provider.release()); // Takes ownership. 164 } 165 166 void SearchController::OnResultsChanged() { 167 if (dispatching_query_) 168 return; 169 170 KnownResults known_results; 171 if (history_ && history_->IsReady()) { 172 history_->GetKnownResults(base::UTF16ToUTF8(search_box_->text())) 173 ->swap(known_results); 174 } 175 176 mixer_->MixAndPublish(known_results); 177 } 178 179 void SearchController::OnSpeechRecognitionStateChanged( 180 SpeechRecognitionState new_state) { 181 search_box_->SetHintText(l10n_util::GetStringUTF16( 182 (new_state == SPEECH_RECOGNITION_HOTWORD_LISTENING) ? 183 IDS_SEARCH_BOX_HOTWORD_HINT : IDS_SEARCH_BOX_HINT)); 184 } 185 186 } // namespace app_list 187