From 56e41a58177da67619a63ca7fa90ae45a592a8b7 Mon Sep 17 00:00:00 2001 From: mukai Date: Tue, 2 Dec 2014 17:31:00 -0800 Subject: [PATCH] Updates the mic icon status based on the device's audio state (2nd) The previous CL was reverted due to browser_tests failures. That happened because of a missing null check of the new |audio_status_|. This CL also contains its fix. BUG=383624 TBR=amistry@chromium.org, tapted@chromium.org, oshima@chromium.org TEST=manually Review URL: https://codereview.chromium.org/757433003 Cr-Commit-Position: refs/heads/master@{#306517} --- .../chromeos/chrome_browser_main_chromeos.cc | 5 +- .../ui/app_list/search/search_resource_manager.cc | 30 ++++++---- chrome/browser/ui/app_list/start_page_service.cc | 70 +++++++++++++++++++++- chrome/browser/ui/app_list/start_page_service.h | 10 ++++ ui/app_list/views/search_box_view.cc | 1 + 5 files changed, 100 insertions(+), 16 deletions(-) diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc index 448c8c8d0395..58ae1d8752dd 100644 --- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc +++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc @@ -753,8 +753,6 @@ void ChromeBrowserMainPartsChromeos::PostMainMessageLoopRun() { SystemKeyEventListener::Shutdown(); #endif - CrasAudioHandler::Shutdown(); - // Detach D-Bus clients before DBusThreadManager is shut down. power_button_observer_.reset(); idle_action_warning_observer_.reset(); @@ -804,6 +802,9 @@ void ChromeBrowserMainPartsChromeos::PostMainMessageLoopRun() { // Stops all in-flight OAuth2 token fetchers before the IO thread stops. DeviceOAuth2TokenServiceFactory::Shutdown(); + // Shutdown after PostMainMessageLoopRun() which should destroy all observers. + CrasAudioHandler::Shutdown(); + // Called after // ChromeBrowserMainPartsLinux::PostMainMessageLoopRun() to be // executed after execution of chrome::CloseAsh(), because some diff --git a/chrome/browser/ui/app_list/search/search_resource_manager.cc b/chrome/browser/ui/app_list/search/search_resource_manager.cc index 4c6a1c125527..f83f15bde73c 100644 --- a/chrome/browser/ui/app_list/search/search_resource_manager.cc +++ b/chrome/browser/ui/app_list/search/search_resource_manager.cc @@ -15,6 +15,23 @@ namespace app_list { +namespace { + +scoped_ptr CreateNewProperty( + SpeechRecognitionState state) { + if (state == SPEECH_RECOGNITION_OFF) + return nullptr; + + ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); + return make_scoped_ptr(new SearchBoxModel::SpeechButtonProperty( + *bundle.GetImageSkiaNamed(IDR_OMNIBOX_MIC_SEARCH), + l10n_util::GetStringUTF16(IDS_APP_LIST_HOTWORD_LISTENING), + *bundle.GetImageSkiaNamed(IDR_APP_LIST_MIC_HOTWORD_OFF), + l10n_util::GetStringUTF16(IDS_APP_LIST_START_SPEECH_RECOGNITION))); +} + +} // namespace + SearchResourceManager::SearchResourceManager(Profile* profile, SearchBoxModel* search_box, SpeechUIModel* speech_ui) @@ -24,18 +41,6 @@ SearchResourceManager::SearchResourceManager(Profile* profile, ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); search_box_->SetIcon(*bundle.GetImageSkiaNamed(IDR_OMNIBOX_SEARCH)); - StartPageService* service = StartPageService::Get(profile); - if (service && service->GetSpeechRecognitionContents()) { - search_box_->SetSpeechRecognitionButton( - scoped_ptr( - new SearchBoxModel::SpeechButtonProperty( - *bundle.GetImageSkiaNamed(IDR_OMNIBOX_MIC_SEARCH), - l10n_util::GetStringUTF16( - IDS_APP_LIST_HOTWORD_LISTENING), - *bundle.GetImageSkiaNamed(IDR_APP_LIST_MIC_HOTWORD_OFF), - l10n_util::GetStringUTF16( - IDS_APP_LIST_START_SPEECH_RECOGNITION)))); - } OnSpeechRecognitionStateChanged(speech_ui_->state()); } @@ -48,6 +53,7 @@ void SearchResourceManager::OnSpeechRecognitionStateChanged( search_box_->SetHintText(l10n_util::GetStringUTF16( (new_state == SPEECH_RECOGNITION_HOTWORD_LISTENING) ? IDS_SEARCH_BOX_HOTWORD_HINT : IDS_SEARCH_BOX_HINT)); + search_box_->SetSpeechRecognitionButton(CreateNewProperty(new_state)); } } // namespace app_list diff --git a/chrome/browser/ui/app_list/start_page_service.cc b/chrome/browser/ui/app_list/start_page_service.cc index 2f14a4af0cae..b1164a016122 100644 --- a/chrome/browser/ui/app_list/start_page_service.cc +++ b/chrome/browser/ui/app_list/start_page_service.cc @@ -36,6 +36,10 @@ #include "extensions/common/extension.h" #include "ui/app_list/app_list_switches.h" +#if defined(OS_CHROMEOS) +#include "chromeos/audio/cras_audio_handler.h" +#endif + using base::RecordAction; using base::UserMetricsAction; @@ -48,7 +52,7 @@ bool InSpeechRecognition(SpeechRecognitionState state) { state == SPEECH_RECOGNITION_IN_SPEECH; } -} +} // namespace class StartPageService::ProfileDestroyObserver : public content::NotificationObserver { @@ -102,6 +106,48 @@ class StartPageService::StartPageWebContentsDelegate DISALLOW_COPY_AND_ASSIGN(StartPageWebContentsDelegate); }; +#if defined(OS_CHROMEOS) + +class StartPageService::AudioStatus + : public chromeos::CrasAudioHandler::AudioObserver { + public: + explicit AudioStatus(StartPageService* start_page_service) + : start_page_service_(start_page_service) { + chromeos::CrasAudioHandler::Get()->AddAudioObserver(this); + CheckAndUpdate(); + } + + ~AudioStatus() override { + chromeos::CrasAudioHandler::Get()->RemoveAudioObserver(this); + } + + bool CanListen() { + chromeos::CrasAudioHandler* audio_handler = + chromeos::CrasAudioHandler::Get(); + return (audio_handler->GetPrimaryActiveInputNode() != 0) && + !audio_handler->IsInputMuted(); + } + + private: + void CheckAndUpdate() { + // TODO(mukai): If the system can listen, this should also restart the + // hotword recognition. + start_page_service_->OnSpeechRecognitionStateChanged( + CanListen() ? SPEECH_RECOGNITION_READY : SPEECH_RECOGNITION_OFF); + } + + // chromeos::CrasAudioHandler::AudioObserver: + void OnInputMuteChanged() override { CheckAndUpdate(); } + + void OnActiveInputNodeChanged() override { CheckAndUpdate(); } + + StartPageService* start_page_service_; + + DISALLOW_COPY_AND_ASSIGN(AudioStatus); +}; + +#endif // OS_CHROMEOS + // static StartPageService* StartPageService::Get(Profile* profile) { return StartPageServiceFactory::GetForProfile(profile); @@ -119,8 +165,9 @@ StartPageService::StartPageService(Profile* profile) // If experimental hotwording is enabled, then we're always "ready". // Transitioning into the "hotword recognizing" state is handled by the // hotword extension. - if (HotwordService::IsExperimentalHotwordingEnabled()) + if (HotwordService::IsExperimentalHotwordingEnabled()) { state_ = app_list::SPEECH_RECOGNITION_READY; + } if (app_list::switches::IsExperimentalAppListEnabled()) LoadContents(); @@ -148,6 +195,10 @@ void StartPageService::AppListShown() { "appList.startPage.onAppListShown", base::FundamentalValue(HotwordEnabled())); } + +#if defined(OS_CHROMEOS) + audio_status_.reset(new AudioStatus(this)); +#endif } void StartPageService::AppListHidden() { @@ -162,6 +213,10 @@ void StartPageService::AppListHidden() { speech_recognizer_) { speech_recognizer_->Stop(); } + +#if defined(OS_CHROMEOS) + audio_status_.reset(); +#endif } void StartPageService::ToggleSpeechRecognition() { @@ -251,6 +306,14 @@ void StartPageService::OnSpeechSoundLevelChanged(int16_t level) { void StartPageService::OnSpeechRecognitionStateChanged( SpeechRecognitionState new_state) { +#if defined(OS_CHROMEOS) + // Sometimes this can be called even though there are no audio input devices. + if (audio_status_ && !audio_status_->CanListen()) + new_state = SPEECH_RECOGNITION_OFF; +#endif + + if (state_ == new_state) + return; if (HotwordService::IsExperimentalHotwordingEnabled() && new_state == SPEECH_RECOGNITION_READY && @@ -283,6 +346,9 @@ content::WebContents* StartPageService::GetSpeechContents() { void StartPageService::Shutdown() { UnloadContents(); +#if defined(OS_CHROMEOS) + audio_status_.reset(); +#endif } void StartPageService::WebUILoaded() { diff --git a/chrome/browser/ui/app_list/start_page_service.h b/chrome/browser/ui/app_list/start_page_service.h index 61e226055de4..d33bd531bbd2 100644 --- a/chrome/browser/ui/app_list/start_page_service.h +++ b/chrome/browser/ui/app_list/start_page_service.h @@ -88,6 +88,12 @@ class StartPageService : public KeyedService, // getUserMedia() request from the web contents. class StartPageWebContentsDelegate; +#if defined(OS_CHROMEOS) + // This class observes the change of audio input device availability and + // checks if currently the system has valid audio input. + class AudioStatus; +#endif + void LoadContents(); void UnloadContents(); @@ -109,6 +115,10 @@ class StartPageService : public KeyedService, scoped_ptr speech_recognizer_; +#if defined(OS_CHROMEOS) + scoped_ptr audio_status_; +#endif + base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(StartPageService); diff --git a/ui/app_list/views/search_box_view.cc b/ui/app_list/views/search_box_view.cc index c519fee380ab..9ef86f0dfe21 100644 --- a/ui/app_list/views/search_box_view.cc +++ b/ui/app_list/views/search_box_view.cc @@ -266,6 +266,7 @@ void SearchBoxView::SpeechRecognitionButtonPropChanged() { speech_button_ = NULL; } } + Layout(); } void SearchBoxView::HintTextChanged() { -- 2.11.4.GIT