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