Commit 93a707e4 authored by amistry's avatar amistry Committed by Commit bot

Open the launcher when hotword is triggered in always-on mode.

BUG=397019

Review URL: https://codereview.chromium.org/631913004

Cr-Commit-Position: refs/heads/master@{#299859}
parent 86206d75
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
#include "chrome/browser/search/hotword_client.h" #include "chrome/browser/search/hotword_client.h"
#include "chrome/browser/search/hotword_service.h" #include "chrome/browser/search/hotword_service.h"
#include "chrome/browser/search/hotword_service_factory.h" #include "chrome/browser/search/hotword_service_factory.h"
#include "chrome/browser/ui/app_list/app_list_service.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_switches.h" #include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h" #include "chrome/common/pref_names.h"
#include "extensions/browser/event_router.h" #include "extensions/browser/event_router.h"
...@@ -157,8 +159,20 @@ bool HotwordPrivateSetHotwordSessionStateFunction::RunSync() { ...@@ -157,8 +159,20 @@ bool HotwordPrivateSetHotwordSessionStateFunction::RunSync() {
bool HotwordPrivateNotifyHotwordRecognitionFunction::RunSync() { bool HotwordPrivateNotifyHotwordRecognitionFunction::RunSync() {
HotwordService* hotword_service = HotwordService* hotword_service =
HotwordServiceFactory::GetForProfile(GetProfile()); HotwordServiceFactory::GetForProfile(GetProfile());
if (hotword_service && hotword_service->client()) if (hotword_service) {
hotword_service->client()->OnHotwordRecognized(); if (hotword_service->client()) {
hotword_service->client()->OnHotwordRecognized();
} else if (HotwordService::IsExperimentalHotwordingEnabled() &&
hotword_service->IsAlwaysOnEnabled()) {
Browser* browser = GetCurrentBrowser();
// If a Browser does not exist, fall back to the universally available,
// but not recommended, way.
AppListService* app_list_service = AppListService::Get(
browser ? browser->host_desktop_type() : chrome::GetActiveDesktop());
CHECK(app_list_service);
app_list_service->ShowForVoiceSearch(GetProfile());
}
}
return true; return true;
} }
......
...@@ -438,6 +438,12 @@ bool HotwordService::IsOptedIntoAudioLogging() { ...@@ -438,6 +438,12 @@ bool HotwordService::IsOptedIntoAudioLogging() {
profile_->GetPrefs()->GetBoolean(prefs::kHotwordAudioLoggingEnabled); profile_->GetPrefs()->GetBoolean(prefs::kHotwordAudioLoggingEnabled);
} }
bool HotwordService::IsAlwaysOnEnabled() {
return
profile_->GetPrefs()->HasPrefPath(prefs::kHotwordAlwaysOnSearchEnabled) &&
profile_->GetPrefs()->GetBoolean(prefs::kHotwordAlwaysOnSearchEnabled);
}
void HotwordService::EnableHotwordExtension( void HotwordService::EnableHotwordExtension(
ExtensionService* extension_service) { ExtensionService* extension_service) {
if (extension_service) if (extension_service)
...@@ -501,6 +507,9 @@ void HotwordService::StopHotwordSession(HotwordClient* client) { ...@@ -501,6 +507,9 @@ void HotwordService::StopHotwordSession(HotwordClient* client) {
if (!IsServiceAvailable()) if (!IsServiceAvailable())
return; return;
// Do nothing if there's no client.
if (!client_)
return;
DCHECK(client_ == client); DCHECK(client_ == client);
client_ = NULL; client_ = NULL;
......
...@@ -72,6 +72,9 @@ class HotwordService : public content::NotificationObserver, ...@@ -72,6 +72,9 @@ class HotwordService : public content::NotificationObserver,
// is opted in, false otherwise.. // is opted in, false otherwise..
bool IsOptedIntoAudioLogging(); bool IsOptedIntoAudioLogging();
// Returns whether always-on hotwording is enabled.
bool IsAlwaysOnEnabled();
// Control the state of the hotword extension. // Control the state of the hotword extension.
void EnableHotwordExtension(ExtensionService* extension_service); void EnableHotwordExtension(ExtensionService* extension_service);
void DisableHotwordExtension(ExtensionService* extension_service); void DisableHotwordExtension(ExtensionService* extension_service);
......
...@@ -77,6 +77,9 @@ class AppListService { ...@@ -77,6 +77,9 @@ class AppListService {
// profile to local prefs as the default app list profile. // profile to local prefs as the default app list profile.
virtual void ShowForProfile(Profile* requested_profile) = 0; virtual void ShowForProfile(Profile* requested_profile) = 0;
// Shows the app list, and jump to voice search. Used by always-on hotwording.
virtual void ShowForVoiceSearch(Profile* profile) = 0;
// Shows the app list, and reveals the page that contains |extension_id|. This // Shows the app list, and reveals the page that contains |extension_id|. This
// should only be called for things that show in the app list, and only when // should only be called for things that show in the app list, and only when
// they begin or complete installing. If |start_discovery_tracking| is set, // they begin or complete installing. If |start_discovery_tracking| is set,
......
...@@ -33,6 +33,7 @@ class AppListServiceDisabled : public AppListService { ...@@ -33,6 +33,7 @@ class AppListServiceDisabled : public AppListService {
virtual void Show() override {} virtual void Show() override {}
virtual void ShowForProfile(Profile* profile) override {} virtual void ShowForProfile(Profile* profile) override {}
virtual void ShowForVoiceSearch(Profile* profile) override {}
virtual void ShowForAppInstall(Profile* profile, virtual void ShowForAppInstall(Profile* profile,
const std::string& extension_id, const std::string& extension_id,
bool start_discovery_tracking) override {} bool start_discovery_tracking) override {}
......
...@@ -331,6 +331,11 @@ void AppListServiceImpl::Show() { ...@@ -331,6 +331,11 @@ void AppListServiceImpl::Show() {
weak_factory_.GetWeakPtr())); weak_factory_.GetWeakPtr()));
} }
void AppListServiceImpl::ShowForVoiceSearch(Profile* profile) {
ShowForProfile(profile);
view_delegate_->ToggleSpeechRecognition();
}
void AppListServiceImpl::ShowForAppInstall(Profile* profile, void AppListServiceImpl::ShowForAppInstall(Profile* profile,
const std::string& extension_id, const std::string& extension_id,
bool start_discovery_tracking) { bool start_discovery_tracking) {
......
...@@ -54,6 +54,7 @@ class AppListServiceImpl : public AppListService, ...@@ -54,6 +54,7 @@ class AppListServiceImpl : public AppListService,
const base::FilePath& user_data_dir) override; const base::FilePath& user_data_dir) override;
virtual void SetProfilePath(const base::FilePath& profile_path) override; virtual void SetProfilePath(const base::FilePath& profile_path) override;
virtual void Show() override; virtual void Show() override;
virtual void ShowForVoiceSearch(Profile* profile) override;
virtual void ShowForAppInstall(Profile* profile, virtual void ShowForAppInstall(Profile* profile,
const std::string& extension_id, const std::string& extension_id,
bool start_discovery_tracking) override; bool start_discovery_tracking) override;
......
...@@ -472,8 +472,22 @@ void AppListViewDelegate::ViewClosing() { ...@@ -472,8 +472,22 @@ void AppListViewDelegate::ViewClosing() {
if (service->HotwordEnabled()) { if (service->HotwordEnabled()) {
HotwordService* hotword_service = HotwordService* hotword_service =
HotwordServiceFactory::GetForProfile(profile_); HotwordServiceFactory::GetForProfile(profile_);
if (hotword_service) if (hotword_service) {
hotword_service->StopHotwordSession(this); hotword_service->StopHotwordSession(this);
// If we're in always-on mode, we always want to restart hotwording
// after closing the launcher window. So, in always-on mode, hotwording
// is stopped, and then started again right away. Note that hotwording
// may already be stopped. The call to StopHotwordSession() above both
// explicitly stops hotwording, if it's running, and clears the
// association between the hotword service and |this|. When starting up
// hotwording, pass nullptr as the client so that hotword triggers cause
// the launcher to open.
// TODO(amistry): This only works on ChromeOS since Chrome hides the
// launcher instead of destroying it. Make this work on Chrome.
if (hotword_service->IsAlwaysOnEnabled())
hotword_service->RequestHotwordSession(nullptr);
}
} }
} }
} }
...@@ -519,6 +533,23 @@ void AppListViewDelegate::ToggleSpeechRecognition() { ...@@ -519,6 +533,23 @@ void AppListViewDelegate::ToggleSpeechRecognition() {
app_list::StartPageService::Get(profile_); app_list::StartPageService::Get(profile_);
if (service) if (service)
service->ToggleSpeechRecognition(); service->ToggleSpeechRecognition();
// With the new hotword extension, stop the hotword session. With the launcher
// and NTP, this is unnecessary since the hotwording is implicitly stopped.
// However, for always on, hotword triggering launches the launcher which
// starts a session and hence starts the hotword detector. This results in the
// hotword detector and the speech-to-text engine running in parallel, which
// will conflict with each other (i.e. saying 'Ok Google' twice in a row
// should cause a search to happen for 'Ok Google', not two hotword triggers).
// To get around this, always stop the session when switching to speech
// recognition.
if (HotwordService::IsExperimentalHotwordingEnabled() &&
service && service->HotwordEnabled()) {
HotwordService* hotword_service =
HotwordServiceFactory::GetForProfile(profile_);
if (hotword_service)
hotword_service->StopHotwordSession(this);
}
} }
void AppListViewDelegate::ShowForProfileByPath( void AppListViewDelegate::ShowForProfileByPath(
...@@ -547,10 +578,12 @@ void AppListViewDelegate::OnSpeechRecognitionStateChanged( ...@@ -547,10 +578,12 @@ void AppListViewDelegate::OnSpeechRecognitionStateChanged(
app_list::StartPageService* service = app_list::StartPageService* service =
app_list::StartPageService::Get(profile_); app_list::StartPageService::Get(profile_);
// With the new hotword extension, we need to re-request hotwording after // With the new hotword extension, we need to re-request hotwording after
// speech recognition has stopped. // speech recognition has stopped. Do not request hotwording after the app
// list has already closed.
if (new_state == app_list::SPEECH_RECOGNITION_READY && if (new_state == app_list::SPEECH_RECOGNITION_READY &&
HotwordService::IsExperimentalHotwordingEnabled() && HotwordService::IsExperimentalHotwordingEnabled() &&
service && service->HotwordEnabled()) { service && service->HotwordEnabled() &&
controller_->GetAppListWindow()) {
HotwordService* hotword_service = HotwordService* hotword_service =
HotwordServiceFactory::GetForProfile(profile_); HotwordServiceFactory::GetForProfile(profile_);
if (hotword_service) { if (hotword_service) {
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <string> #include <string>
#include "base/bind.h"
#include "base/command_line.h" #include "base/command_line.h"
#include "base/memory/singleton.h" #include "base/memory/singleton.h"
#include "base/metrics/user_metrics.h" #include "base/metrics/user_metrics.h"
...@@ -111,7 +112,8 @@ StartPageService::StartPageService(Profile* profile) ...@@ -111,7 +112,8 @@ StartPageService::StartPageService(Profile* profile)
recommended_apps_(new RecommendedApps(profile)), recommended_apps_(new RecommendedApps(profile)),
state_(app_list::SPEECH_RECOGNITION_OFF), state_(app_list::SPEECH_RECOGNITION_OFF),
speech_button_toggled_manually_(false), speech_button_toggled_manually_(false),
speech_result_obtained_(false) { speech_result_obtained_(false),
webui_finished_loading_(false) {
// If experimental hotwording is enabled, then we're always "ready". // If experimental hotwording is enabled, then we're always "ready".
// Transitioning into the "hotword recognizing" state is handled by the // Transitioning into the "hotword recognizing" state is handled by the
// hotword extension. // hotword extension.
...@@ -154,9 +156,16 @@ void StartPageService::AppListHidden() { ...@@ -154,9 +156,16 @@ void StartPageService::AppListHidden() {
} }
void StartPageService::ToggleSpeechRecognition() { void StartPageService::ToggleSpeechRecognition() {
DCHECK(contents_);
speech_button_toggled_manually_ = true; speech_button_toggled_manually_ = true;
contents_->GetWebUI()->CallJavascriptFunction( if (webui_finished_loading_) {
"appList.startPage.toggleSpeechRecognition"); contents_->GetWebUI()->CallJavascriptFunction(
"appList.startPage.toggleSpeechRecognition");
} else {
pending_webui_callbacks_.push_back(
base::Bind(&StartPageService::ToggleSpeechRecognition,
base::Unretained(this)));
}
} }
bool StartPageService::HotwordEnabled() { bool StartPageService::HotwordEnabled() {
...@@ -228,6 +237,18 @@ void StartPageService::Shutdown() { ...@@ -228,6 +237,18 @@ void StartPageService::Shutdown() {
UnloadContents(); UnloadContents();
} }
void StartPageService::WebUILoaded() {
// There's a race condition between the WebUI loading, and calling its JS
// functions. Specifically, calling LoadContents() doesn't mean that the page
// has loaded, but several code paths make this assumption. This function
// allows us to defer calling JS functions until after the page has finished
// loading.
webui_finished_loading_ = true;
for (const auto& cb : pending_webui_callbacks_)
cb.Run();
pending_webui_callbacks_.clear();
}
void StartPageService::LoadContents() { void StartPageService::LoadContents() {
contents_.reset(content::WebContents::Create( contents_.reset(content::WebContents::Create(
content::WebContents::CreateParams(profile_))); content::WebContents::CreateParams(profile_)));
...@@ -250,6 +271,7 @@ void StartPageService::LoadContents() { ...@@ -250,6 +271,7 @@ void StartPageService::LoadContents() {
void StartPageService::UnloadContents() { void StartPageService::UnloadContents() {
contents_.reset(); contents_.reset();
webui_finished_loading_ = false;
} }
} // namespace app_list } // namespace app_list
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <vector> #include <vector>
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/callback.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/observer_list.h" #include "base/observer_list.h"
...@@ -43,6 +44,9 @@ class StartPageService : public KeyedService { ...@@ -43,6 +44,9 @@ class StartPageService : public KeyedService {
void AppListHidden(); void AppListHidden();
void ToggleSpeechRecognition(); void ToggleSpeechRecognition();
// Called when the WebUI has finished loading.
void WebUILoaded();
// Returns true if the hotword is enabled in the app-launcher. // Returns true if the hotword is enabled in the app-launcher.
bool HotwordEnabled(); bool HotwordEnabled();
...@@ -89,6 +93,9 @@ class StartPageService : public KeyedService { ...@@ -89,6 +93,9 @@ class StartPageService : public KeyedService {
bool speech_button_toggled_manually_; bool speech_button_toggled_manually_;
bool speech_result_obtained_; bool speech_result_obtained_;
bool webui_finished_loading_;
std::vector<base::Closure> pending_webui_callbacks_;
DISALLOW_COPY_AND_ASSIGN(StartPageService); DISALLOW_COPY_AND_ASSIGN(StartPageService);
}; };
......
...@@ -160,6 +160,8 @@ void StartPageHandler::HandleInitialize(const base::ListValue* args) { ...@@ -160,6 +160,8 @@ void StartPageHandler::HandleInitialize(const base::ListValue* args) {
if (!service) if (!service)
return; return;
service->WebUILoaded();
recommended_apps_ = service->recommended_apps(); recommended_apps_ = service->recommended_apps();
recommended_apps_->AddObserver(this); recommended_apps_->AddObserver(this);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment