190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved. 290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// found in the LICENSE file. 490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/ui/app_list/search/search_controller.h" 690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include <algorithm> 890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include <vector> 990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 1090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "base/bind.h" 1190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "base/command_line.h" 1290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "base/memory/scoped_ptr.h" 13eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/metrics/histogram.h" 147d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "base/strings/string_util.h" 15868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h" 1690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/profiles/profile.h" 17a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch#include "chrome/browser/ui/app_list/app_list_controller_delegate.h" 1890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/ui/app_list/search/app_search_provider.h" 1990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/ui/app_list/search/chrome_search_result.h" 2090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/ui/app_list/search/history.h" 2190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/ui/app_list/search/history_factory.h" 2290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/ui/app_list/search/omnibox_provider.h" 2358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "chrome/browser/ui/app_list/search/people/people_provider.h" 2458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "chrome/browser/ui/app_list/search/webstore/webstore_provider.h" 25f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "chrome/browser/ui/app_list/start_page_service.h" 2658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "chrome/common/chrome_switches.h" 271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "chrome/grit/generated_resources.h" 2890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "content/public/browser/user_metrics.h" 291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "grit/components_scaled_resources.h" 3090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "grit/theme_resources.h" 3190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "ui/app_list/search_box_model.h" 325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/app_list/speech_ui_model.h" 3390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "ui/base/l10n/l10n_util.h" 3490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "ui/base/resource/resource_bundle.h" 3590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 36eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochnamespace { 37eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch const char kAppListSearchResultOpenTypeHistogram[] = 38eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch "Apps.AppListSearchResultOpenType"; 395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // Maximum time (in milliseconds) to wait to the search providers to finish. 415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) const int kStopTimeMS = 1500; 42eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch} 43eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 4490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)namespace app_list { 4590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 4690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)SearchController::SearchController(Profile* profile, 4790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) SearchBoxModel* search_box, 4890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) AppListModel::SearchResults* results, 495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) SpeechUIModel* speech_ui, 5090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) AppListControllerDelegate* list_controller) 5190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) : profile_(profile), 5290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) search_box_(search_box), 535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) speech_ui_(speech_ui), 5490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) list_controller_(list_controller), 5590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) dispatching_query_(false), 5690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) mixer_(new Mixer(results)), 5790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) history_(HistoryFactory::GetForBrowserContext(profile)) { 585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) speech_ui_->AddObserver(this); 5990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) Init(); 6090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)} 6190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SearchController::~SearchController() { 635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) speech_ui_->RemoveObserver(this); 645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 6590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 6690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)void SearchController::Init() { 67f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 68f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) search_box_->SetIcon(*bundle.GetImageSkiaNamed(IDR_OMNIBOX_SEARCH)); 695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) StartPageService* service = StartPageService::Get(profile_); 705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (service && service->GetSpeechRecognitionContents()) { 71f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) search_box_->SetSpeechRecognitionButton( 725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_ptr<SearchBoxModel::SpeechButtonProperty>( 735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) new SearchBoxModel::SpeechButtonProperty( 74f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) *bundle.GetImageSkiaNamed(IDR_OMNIBOX_MIC_SEARCH), 75f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) l10n_util::GetStringUTF16( 765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) IDS_APP_LIST_HOTWORD_LISTENING), 775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) *bundle.GetImageSkiaNamed(IDR_APP_LIST_MIC_HOTWORD_OFF), 785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) l10n_util::GetStringUTF16( 79a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) IDS_APP_LIST_START_SPEECH_RECOGNITION)))); 80f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) OnSpeechRecognitionStateChanged(speech_ui_->state()); 8290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 8390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) mixer_->Init(); 8490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 8590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) AddProvider(Mixer::MAIN_GROUP, scoped_ptr<SearchProvider>( 8690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) new AppSearchProvider(profile_, list_controller_)).Pass()); 8790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) AddProvider(Mixer::OMNIBOX_GROUP, scoped_ptr<SearchProvider>( 8890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) new OmniboxProvider(profile_)).Pass()); 8990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) AddProvider(Mixer::WEBSTORE_GROUP, scoped_ptr<SearchProvider>( 90eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch new WebstoreProvider(profile_, list_controller_)).Pass()); 91f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (!CommandLine::ForCurrentProcess()->HasSwitch( 92f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) switches::kDisablePeopleSearch)) { 9358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) AddProvider(Mixer::PEOPLE_GROUP, scoped_ptr<SearchProvider>( 9458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) new PeopleProvider(profile_)).Pass()); 9558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) } 9690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)} 9790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 9890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)void SearchController::Start() { 9990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) Stop(); 10090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 101a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch list_controller_->OnSearchStarted(); 102a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch 103a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) base::string16 query; 104a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) base::TrimWhitespace(search_box_->text(), base::TRIM_ALL, &query); 10590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 10690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) dispatching_query_ = true; 10790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) for (Providers::iterator it = providers_.begin(); 10890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) it != providers_.end(); 10990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) ++it) { 11090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) (*it)->Start(query); 11190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 11290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) dispatching_query_ = false; 11390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 11490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) OnResultsChanged(); 11590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 11690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) stop_timer_.Start(FROM_HERE, 11790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) base::TimeDelta::FromMilliseconds(kStopTimeMS), 11890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) base::Bind(&SearchController::Stop, 11990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) base::Unretained(this))); 12090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)} 12190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 12290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)void SearchController::Stop() { 12390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) stop_timer_.Stop(); 12490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 12590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) for (Providers::iterator it = providers_.begin(); 12690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) it != providers_.end(); 12790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) ++it) { 12890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) (*it)->Stop(); 12990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 13090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)} 13190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 13290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)void SearchController::OpenResult(SearchResult* result, int event_flags) { 13390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // Count AppList.Search here because it is composed of search + action. 1345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) content::RecordAction(base::UserMetricsAction("AppList_Search")); 13590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 13690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) ChromeSearchResult* chrome_result = 13790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) static_cast<app_list::ChromeSearchResult*>(result); 138eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch UMA_HISTOGRAM_ENUMERATION(kAppListSearchResultOpenTypeHistogram, 139eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch chrome_result->GetType(), 140eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch SEARCH_RESULT_TYPE_BOUNDARY); 14190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) chrome_result->Open(event_flags); 14290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 14390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (history_ && history_->IsReady()) { 1445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) history_->AddLaunchEvent(base::UTF16ToUTF8(search_box_->text()), 14590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) chrome_result->id()); 14690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 14790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)} 14890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 14990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)void SearchController::InvokeResultAction(SearchResult* result, 15090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) int action_index, 15190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) int event_flags) { 15290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // TODO(xiyuan): Hook up with user learning. 15390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) static_cast<app_list::ChromeSearchResult*>(result)->InvokeAction( 15490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) action_index, event_flags); 15590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)} 15690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 15790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)void SearchController::AddProvider(Mixer::GroupId group, 15890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) scoped_ptr<SearchProvider> provider) { 15990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) provider->set_result_changed_callback(base::Bind( 16090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) &SearchController::OnResultsChanged, 16190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) base::Unretained(this))); 16290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) mixer_->AddProviderToGroup(group, provider.get()); 16390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) providers_.push_back(provider.release()); // Takes ownership. 16490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)} 16590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 16690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)void SearchController::OnResultsChanged() { 16790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (dispatching_query_) 16890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) return; 16990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 17090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) KnownResults known_results; 17190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (history_ && history_->IsReady()) { 1725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) history_->GetKnownResults(base::UTF16ToUTF8(search_box_->text())) 17390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) ->swap(known_results); 17490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 17590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 17690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) mixer_->MixAndPublish(known_results); 17790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)} 17890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 1795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void SearchController::OnSpeechRecognitionStateChanged( 1805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) SpeechRecognitionState new_state) { 1815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) search_box_->SetHintText(l10n_util::GetStringUTF16( 1825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) (new_state == SPEECH_RECOGNITION_HOTWORD_LISTENING) ? 1835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) IDS_SEARCH_BOX_HOTWORD_HINT : IDS_SEARCH_BOX_HINT)); 1845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 1855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 18690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)} // namespace app_list 187