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 "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" 34 35namespace { 36 const char kAppListSearchResultOpenTypeHistogram[] = 37 "Apps.AppListSearchResultOpenType"; 38} 39 40namespace app_list { 41 42SearchController::SearchController(Profile* profile, 43 SearchBoxModel* search_box, 44 AppListModel::SearchResults* results, 45 SpeechUIModel* speech_ui, 46 AppListControllerDelegate* list_controller) 47 : profile_(profile), 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); 55 Init(); 56} 57 58SearchController::~SearchController() { 59 speech_ui_->RemoveObserver(this); 60} 61 62void 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)))); 76 } 77 OnSpeechRecognitionStateChanged(speech_ui_->state()); 78 79 mixer_->Init(); 80 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()); 91 } 92} 93 94void SearchController::Start() { 95 Stop(); 96 97 list_controller_->OnSearchStarted(); 98 99 base::string16 query; 100 base::TrimWhitespace(search_box_->text(), base::TRIM_ALL, &query); 101 102 dispatching_query_ = true; 103 for (Providers::iterator it = providers_.begin(); 104 it != providers_.end(); 105 ++it) { 106 (*it)->Start(query); 107 } 108 dispatching_query_ = false; 109 110 OnResultsChanged(); 111 112 // Maximum time (in milliseconds) to wait to the search providers to finish. 113 const int kStopTimeMS = 1500; 114 stop_timer_.Start(FROM_HERE, 115 base::TimeDelta::FromMilliseconds(kStopTimeMS), 116 base::Bind(&SearchController::Stop, 117 base::Unretained(this))); 118} 119 120void SearchController::Stop() { 121 stop_timer_.Stop(); 122 123 for (Providers::iterator it = providers_.begin(); 124 it != providers_.end(); 125 ++it) { 126 (*it)->Stop(); 127 } 128} 129 130void SearchController::OpenResult(SearchResult* result, int event_flags) { 131 // Count AppList.Search here because it is composed of search + action. 132 content::RecordAction(base::UserMetricsAction("AppList_Search")); 133 134 ChromeSearchResult* chrome_result = 135 static_cast<app_list::ChromeSearchResult*>(result); 136 UMA_HISTOGRAM_ENUMERATION(kAppListSearchResultOpenTypeHistogram, 137 chrome_result->GetType(), 138 SEARCH_RESULT_TYPE_BOUNDARY); 139 chrome_result->Open(event_flags); 140 141 if (history_ && history_->IsReady()) { 142 history_->AddLaunchEvent(base::UTF16ToUTF8(search_box_->text()), 143 chrome_result->id()); 144 } 145} 146 147void SearchController::InvokeResultAction(SearchResult* result, 148 int action_index, 149 int event_flags) { 150 // TODO(xiyuan): Hook up with user learning. 151 static_cast<app_list::ChromeSearchResult*>(result)->InvokeAction( 152 action_index, event_flags); 153} 154 155void SearchController::AddProvider(Mixer::GroupId group, 156 scoped_ptr<SearchProvider> provider) { 157 provider->set_result_changed_callback(base::Bind( 158 &SearchController::OnResultsChanged, 159 base::Unretained(this))); 160 mixer_->AddProviderToGroup(group, provider.get()); 161 providers_.push_back(provider.release()); // Takes ownership. 162} 163 164void SearchController::OnResultsChanged() { 165 if (dispatching_query_) 166 return; 167 168 KnownResults known_results; 169 if (history_ && history_->IsReady()) { 170 history_->GetKnownResults(base::UTF16ToUTF8(search_box_->text())) 171 ->swap(known_results); 172 } 173 174 mixer_->MixAndPublish(known_results); 175} 176 177void SearchController::OnSpeechRecognitionStateChanged( 178 SpeechRecognitionState new_state) { 179 search_box_->SetHintText(l10n_util::GetStringUTF16( 180 (new_state == SPEECH_RECOGNITION_HOTWORD_LISTENING) ? 181 IDS_SEARCH_BOX_HOTWORD_HINT : IDS_SEARCH_BOX_HINT)); 182} 183 184} // namespace app_list 185