search_controller.cc revision f2477e01787aa58f445919b809d89e252beef54f
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/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/base/l10n/l10n_util.h" 32#include "ui/base/resource/resource_bundle.h" 33 34namespace { 35 const char kAppListSearchResultOpenTypeHistogram[] = 36 "Apps.AppListSearchResultOpenType"; 37} 38 39namespace app_list { 40 41SearchController::SearchController(Profile* profile, 42 SearchBoxModel* search_box, 43 AppListModel::SearchResults* results, 44 AppListControllerDelegate* list_controller) 45 : profile_(profile), 46 search_box_(search_box), 47 list_controller_(list_controller), 48 dispatching_query_(false), 49 mixer_(new Mixer(results)), 50 history_(HistoryFactory::GetForBrowserContext(profile)) { 51 Init(); 52} 53 54SearchController::~SearchController() {} 55 56void SearchController::Init() { 57 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 58 search_box_->SetHintText( 59 l10n_util::GetStringUTF16(IDS_SEARCH_BOX_HINT)); 60 search_box_->SetIcon(*bundle.GetImageSkiaNamed(IDR_OMNIBOX_SEARCH)); 61 if (StartPageService::Get(profile_)) { 62 search_box_->SetSpeechRecognitionButton( 63 scoped_ptr<SearchBoxModel::ToggleButtonProperty>( 64 new SearchBoxModel::ToggleButtonProperty( 65 // Right now provides the same image for both state. 66 // TODO(mukai, jennschen): Replace them by the real images. 67 *bundle.GetImageSkiaNamed(IDR_OMNIBOX_MIC_SEARCH), 68 *bundle.GetImageSkiaNamed(IDR_OMNIBOX_MIC_SEARCH), 69 l10n_util::GetStringUTF16( 70 IDS_APP_LIST_START_SPEECH_RECOGNITION), 71 l10n_util::GetStringUTF16( 72 IDS_APP_LIST_STOP_SPEECH_RECOGNITION)))); 73 } 74 75 mixer_->Init(); 76 77 AddProvider(Mixer::MAIN_GROUP, scoped_ptr<SearchProvider>( 78 new AppSearchProvider(profile_, list_controller_)).Pass()); 79 AddProvider(Mixer::OMNIBOX_GROUP, scoped_ptr<SearchProvider>( 80 new OmniboxProvider(profile_)).Pass()); 81 AddProvider(Mixer::WEBSTORE_GROUP, scoped_ptr<SearchProvider>( 82 new WebstoreProvider(profile_, list_controller_)).Pass()); 83 if (!CommandLine::ForCurrentProcess()->HasSwitch( 84 switches::kDisablePeopleSearch)) { 85 AddProvider(Mixer::PEOPLE_GROUP, scoped_ptr<SearchProvider>( 86 new PeopleProvider(profile_)).Pass()); 87 } 88} 89 90void SearchController::Start() { 91 Stop(); 92 93 string16 query; 94 TrimWhitespace(search_box_->text(), TRIM_ALL, &query); 95 96 dispatching_query_ = true; 97 for (Providers::iterator it = providers_.begin(); 98 it != providers_.end(); 99 ++it) { 100 (*it)->Start(query); 101 } 102 dispatching_query_ = false; 103 104 OnResultsChanged(); 105 106 // Maximum time (in milliseconds) to wait to the search providers to finish. 107 const int kStopTimeMS = 1500; 108 stop_timer_.Start(FROM_HERE, 109 base::TimeDelta::FromMilliseconds(kStopTimeMS), 110 base::Bind(&SearchController::Stop, 111 base::Unretained(this))); 112} 113 114void SearchController::Stop() { 115 stop_timer_.Stop(); 116 117 for (Providers::iterator it = providers_.begin(); 118 it != providers_.end(); 119 ++it) { 120 (*it)->Stop(); 121 } 122} 123 124void SearchController::OpenResult(SearchResult* result, int event_flags) { 125 // Count AppList.Search here because it is composed of search + action. 126 content::RecordAction(content::UserMetricsAction("AppList_Search")); 127 128 ChromeSearchResult* chrome_result = 129 static_cast<app_list::ChromeSearchResult*>(result); 130 UMA_HISTOGRAM_ENUMERATION(kAppListSearchResultOpenTypeHistogram, 131 chrome_result->GetType(), 132 SEARCH_RESULT_TYPE_BOUNDARY); 133 chrome_result->Open(event_flags); 134 135 if (history_ && history_->IsReady()) { 136 history_->AddLaunchEvent(UTF16ToUTF8(search_box_->text()), 137 chrome_result->id()); 138 } 139} 140 141void SearchController::InvokeResultAction(SearchResult* result, 142 int action_index, 143 int event_flags) { 144 // TODO(xiyuan): Hook up with user learning. 145 static_cast<app_list::ChromeSearchResult*>(result)->InvokeAction( 146 action_index, event_flags); 147} 148 149void SearchController::AddProvider(Mixer::GroupId group, 150 scoped_ptr<SearchProvider> provider) { 151 provider->set_result_changed_callback(base::Bind( 152 &SearchController::OnResultsChanged, 153 base::Unretained(this))); 154 mixer_->AddProviderToGroup(group, provider.get()); 155 providers_.push_back(provider.release()); // Takes ownership. 156} 157 158void SearchController::OnResultsChanged() { 159 if (dispatching_query_) 160 return; 161 162 KnownResults known_results; 163 if (history_ && history_->IsReady()) { 164 history_->GetKnownResults(UTF16ToUTF8(search_box_->text())) 165 ->swap(known_results); 166 } 167 168 mixer_->MixAndPublish(known_results); 169} 170 171} // namespace app_list 172