Home | History | Annotate | Download | only in search
      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