diff options
author | calamity <calamity@chromium.org> | 2015-04-07 01:33:05 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-04-07 08:33:36 +0000 |
commit | 509ce7f973d5755f1b59657325e03f31e6308af0 (patch) | |
tree | 69e43f7680122a36c6134e614a61412fd0a1ee5c | |
parent | 3a672fe050438bc787af3f9bb652048291558c6c (diff) | |
download | chromium_src-509ce7f973d5755f1b59657325e03f31e6308af0.zip chromium_src-509ce7f973d5755f1b59657325e03f31e6308af0.tar.gz chromium_src-509ce7f973d5755f1b59657325e03f31e6308af0.tar.bz2 |
Notify hotwording extension of microphone state change.
This CL notifies the hotwording extension of a microphone state change
by adding a hotwordPrivate.onMicrophoneStateChanged event which triggers
an update of the hotwording state.
It also moves the media device observation from HotwordServiceFactory to
HotwordService.
BUG=461787
TBR=estade@chromium.org
Review URL: https://codereview.chromium.org/1047973003
Cr-Commit-Position: refs/heads/master@{#324024}
9 files changed, 95 insertions, 76 deletions
diff --git a/chrome/browser/extensions/api/hotword_private/hotword_private_api.cc b/chrome/browser/extensions/api/hotword_private/hotword_private_api.cc index d38013a..7ea2b0b 100644 --- a/chrome/browser/extensions/api/hotword_private/hotword_private_api.cc +++ b/chrome/browser/extensions/api/hotword_private/hotword_private_api.cc @@ -106,11 +106,21 @@ void HotwordPrivateEventService::OnSpeakerModelExists() { SignalEvent(api::hotword_private::OnSpeakerModelExists::kEventName); } +void HotwordPrivateEventService::OnMicrophoneStateChanged(bool enabled) { + SignalEvent(api::hotword_private::OnMicrophoneStateChanged::kEventName, + api::hotword_private::OnMicrophoneStateChanged::Create(enabled)); +} + void HotwordPrivateEventService::SignalEvent(const std::string& event_name) { + SignalEvent(event_name, make_scoped_ptr(new base::ListValue())); +} + +void HotwordPrivateEventService::SignalEvent(const std::string& event_name, + scoped_ptr<base::ListValue> args) { EventRouter* router = EventRouter::Get(profile_); if (!router || !router->HasEventListener(event_name)) return; - scoped_ptr<base::ListValue> args(new base::ListValue()); + scoped_ptr<Event> event(new Event(event_name, args.Pass())); router->BroadcastEvent(event.Pass()); } diff --git a/chrome/browser/extensions/api/hotword_private/hotword_private_api.h b/chrome/browser/extensions/api/hotword_private/hotword_private_api.h index 1272ded..d25b5b7 100644 --- a/chrome/browser/extensions/api/hotword_private/hotword_private_api.h +++ b/chrome/browser/extensions/api/hotword_private/hotword_private_api.h @@ -44,10 +44,14 @@ class HotwordPrivateEventService : public BrowserContextKeyedAPI { void OnSpeakerModelExists(); + void OnMicrophoneStateChanged(bool enabled); + private: friend class BrowserContextKeyedAPIFactory<HotwordPrivateEventService>; void SignalEvent(const std::string& event_name); + void SignalEvent(const std::string& event_name, + scoped_ptr<base::ListValue> args); Profile* profile_; PrefChangeRegistrar pref_change_registrar_; diff --git a/chrome/browser/resources/hotword/page_audio_manager.js b/chrome/browser/resources/hotword/page_audio_manager.js index a845828..067ff4e 100644 --- a/chrome/browser/resources/hotword/page_audio_manager.js +++ b/chrome/browser/resources/hotword/page_audio_manager.js @@ -35,6 +35,8 @@ cr.define('hotword', function() { this.tabCreatedListener_ = this.handleCreatedTab_.bind(this); this.tabUpdatedListener_ = this.handleUpdatedTab_.bind(this); this.tabActivatedListener_ = this.handleActivatedTab_.bind(this); + this.microphoneStateChangedListener_ = + this.handleMicrophoneStateChanged_.bind(this); this.windowFocusChangedListener_ = this.handleChangedWindow_.bind(this); this.messageListener_ = this.handleMessageFromPage_.bind(this); @@ -237,6 +239,19 @@ cr.define('hotword', function() { this.updateTabState_(); }, + /** + * Handles the microphone state changing. + * @param {boolean} enabled Whether the microphone is now enabled. + * @private + */ + handleMicrophoneStateChanged_: function(enabled) { + if (enabled) { + this.updateTabState_(); + return; + } + + this.stopHotwording_(); + }, /** * Handles a change in Chrome windows. @@ -492,6 +507,8 @@ cr.define('hotword', function() { chrome.tabs.onActivated.addListener(this.tabActivatedListener_); chrome.windows.onFocusChanged.addListener( this.windowFocusChangedListener_); + chrome.hotwordPrivate.onMicrophoneStateChanged.addListener( + this.microphoneStateChangedListener_); if (chrome.runtime.onMessage.hasListener(this.messageListener_)) return; chrome.runtime.onMessageExternal.addListener( @@ -509,6 +526,8 @@ cr.define('hotword', function() { chrome.tabs.onActivated.removeListener(this.tabActivatedListener_); chrome.windows.onFocusChanged.removeListener( this.windowFocusChangedListener_); + chrome.hotwordPrivate.onMicrophoneStateChanged.removeListener( + this.microphoneStateChangedListener_); // Don't remove the Message listener, as we want them listening all // the time, }, diff --git a/chrome/browser/search/hotword_service.cc b/chrome/browser/search/hotword_service.cc index 6ee699f..db9d85f 100644 --- a/chrome/browser/search/hotword_service.cc +++ b/chrome/browser/search/hotword_service.cc @@ -323,6 +323,8 @@ void HotwordService::HotwordWebstoreInstaller::Shutdown() { HotwordService::HotwordService(Profile* profile) : profile_(profile), extension_registry_observer_(this), + microphone_available_(false), + audio_device_state_updated_(false), client_(NULL), error_message_(0), reinstall_pending_(false), @@ -386,6 +388,13 @@ HotwordService::HotwordService(Profile* profile) session_observer_.get()); } #endif + + // Register with the device observer list to update the microphone + // availability. + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::Bind(&HotwordService::InitializeMicrophoneObserver, + base::Unretained(this))); } HotwordService::~HotwordService() { @@ -456,6 +465,10 @@ std::string HotwordService::ReinstalledExtensionId() { return extension_misc::kHotwordSharedModuleId; } +void HotwordService::InitializeMicrophoneObserver() { + MediaCaptureDevicesDispatcher::GetInstance()->AddObserver(this); +} + void HotwordService::InstalledFromWebstoreCallback( int num_tries, bool success, @@ -605,19 +618,16 @@ bool HotwordService::IsServiceAvailable() { RecordErrorMetrics(error_message_); - // Determine if the proper audio capabilities exist. - // The first time this is called, it probably won't return in time, but that's - // why it won't be included in the error calculation (i.e., the call to - // IsAudioDeviceStateUpdated()). However, this use case is rare and typically - // the devices will be initialized by the time a user goes to settings. - bool audio_device_state_updated = - HotwordServiceFactory::IsAudioDeviceStateUpdated(); + // Determine if the proper audio capabilities exist. The first time this is + // called, it probably won't return in time, but that's why it won't be + // included in the error calculation. However, this use case is rare and + // typically the devices will be initialized by the time a user goes to + // settings. HotwordServiceFactory::GetInstance()->UpdateMicrophoneState(); - if (audio_device_state_updated) { + if (audio_device_state_updated_) { bool audio_capture_allowed = profile_->GetPrefs()->GetBoolean(prefs::kAudioCaptureAllowed); - if (!audio_capture_allowed || - !HotwordServiceFactory::IsMicrophoneAvailable()) + if (!audio_capture_allowed || !microphone_available_) error_message_ = IDS_HOTWORD_MICROPHONE_ERROR_MESSAGE; } @@ -772,6 +782,17 @@ void HotwordService::DisableHotwordPreferences() { } } +void HotwordService::OnUpdateAudioDevices( + const content::MediaStreamDevices& devices) { + bool microphone_was_available = microphone_available_; + microphone_available_ = !devices.empty(); + audio_device_state_updated_ = true; + HotwordPrivateEventService* event_service = + BrowserContextKeyedAPIFactory<HotwordPrivateEventService>::Get(profile_); + if (event_service && microphone_was_available != microphone_available_) + event_service->OnMicrophoneStateChanged(microphone_available_); +} + void HotwordService::OnHotwordAlwaysOnSearchEnabledChanged( const std::string& pref_name) { // If the pref for always on has been changed in some way, that means that diff --git a/chrome/browser/search/hotword_service.h b/chrome/browser/search/hotword_service.h index 9b22c84..2d2394a 100644 --- a/chrome/browser/search/hotword_service.h +++ b/chrome/browser/search/hotword_service.h @@ -12,6 +12,7 @@ #include "base/prefs/pref_change_registrar.h" #include "base/scoped_observer.h" #include "chrome/browser/extensions/webstore_startup_installer.h" +#include "chrome/browser/media/media_capture_devices_dispatcher.h" #include "chrome/common/extensions/webstore_install_result.h" #include "components/keyed_service/core/keyed_service.h" #include "content/public/browser/notification_observer.h" @@ -38,7 +39,8 @@ extern const char kHotwordTrainingEnabled[]; // Provides an interface for the Hotword component that does voice triggered // search. -class HotwordService : public extensions::ExtensionRegistryObserver, +class HotwordService : public MediaCaptureDevicesDispatcher::Observer, + public extensions::ExtensionRegistryObserver, public KeyedService { public: // A simple subclass to allow for aborting an install during shutdown. @@ -125,6 +127,8 @@ class HotwordService : public extensions::ExtensionRegistryObserver, // no error. int error_message() { return error_message_; } + bool microphone_available() { return microphone_available_; } + // These methods are for launching, and getting and setting the launch mode of // the Hotword Audio Verification App. // @@ -174,6 +178,10 @@ class HotwordService : public extensions::ExtensionRegistryObserver, // Turn off the currently enabled version of hotwording if one exists. void DisableHotwordPreferences(); + // Overridden from MediaCaptureDevicesDispatcher::Observer + void OnUpdateAudioDevices( + const content::MediaStreamDevices& devices) override; + protected: // Used in test subclasses. scoped_refptr<HotwordWebstoreInstaller> installer_; @@ -181,6 +189,10 @@ class HotwordService : public extensions::ExtensionRegistryObserver, private: class HotwordUserSessionStateObserver; + // Must be called from the UI thread since the instance of + // MediaCaptureDevicesDispatcher can only be accessed on the UI thread. + void InitializeMicrophoneObserver(); + // Callback for webstore extension installer. void InstalledFromWebstoreCallback( int num_tries, @@ -207,6 +219,13 @@ class HotwordService : public extensions::ExtensionRegistryObserver, scoped_ptr<HotwordAudioHistoryHandler> audio_history_handler_; + bool microphone_available_; + + // Indicates if the check for audio devices has been run such that it can be + // included in the error checking. Audio checking is not done immediately + // upon start up because of the negative impact on performance. + bool audio_device_state_updated_; + HotwordClient* client_; int error_message_; bool reinstall_pending_; diff --git a/chrome/browser/search/hotword_service_factory.cc b/chrome/browser/search/hotword_service_factory.cc index 4baf2be..1aa72ac 100644 --- a/chrome/browser/search/hotword_service_factory.cc +++ b/chrome/browser/search/hotword_service_factory.cc @@ -59,45 +59,16 @@ int HotwordServiceFactory::GetCurrentError(BrowserContext* context) { return hotword_service->error_message(); } -// static -bool HotwordServiceFactory::IsMicrophoneAvailable() { - return GetInstance()->microphone_available(); -} - -// static -bool HotwordServiceFactory::IsAudioDeviceStateUpdated() { - return GetInstance()->audio_device_state_updated(); -} - HotwordServiceFactory::HotwordServiceFactory() : BrowserContextKeyedServiceFactory( "HotwordService", - BrowserContextDependencyManager::GetInstance()), - microphone_available_(false), - audio_device_state_updated_(false) { + BrowserContextDependencyManager::GetInstance()) { // No dependencies. - - // Register with the device observer list to update the microphone - // availability. - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&HotwordServiceFactory::InitializeMicrophoneObserver, - base::Unretained(this))); } HotwordServiceFactory::~HotwordServiceFactory() { } -void HotwordServiceFactory::InitializeMicrophoneObserver() { - MediaCaptureDevicesDispatcher::GetInstance()->AddObserver(this); -} - -void HotwordServiceFactory::OnUpdateAudioDevices( - const content::MediaStreamDevices& devices) { - microphone_available_ = !devices.empty(); - audio_device_state_updated_ = true; -} - void HotwordServiceFactory::UpdateMicrophoneState() { // In order to trigger the monitor, just call getAudioCaptureDevices. content::MediaStreamDevices devices = diff --git a/chrome/browser/search/hotword_service_factory.h b/chrome/browser/search/hotword_service_factory.h index 08d325f..3489296 100644 --- a/chrome/browser/search/hotword_service_factory.h +++ b/chrome/browser/search/hotword_service_factory.h @@ -6,15 +6,13 @@ #define CHROME_BROWSER_SEARCH_HOTWORD_SERVICE_FACTORY_H_ #include "base/memory/singleton.h" -#include "chrome/browser/media/media_capture_devices_dispatcher.h" #include "components/keyed_service/content/browser_context_keyed_service_factory.h" class HotwordService; class Profile; // Singleton that owns all HotwordServices and associates them with Profiles. -class HotwordServiceFactory : public MediaCaptureDevicesDispatcher::Observer, - public BrowserContextKeyedServiceFactory { +class HotwordServiceFactory : public BrowserContextKeyedServiceFactory { public: // Returns the HotwordService for |context|. static HotwordService* GetForProfile(content::BrowserContext* context); @@ -34,19 +32,6 @@ class HotwordServiceFactory : public MediaCaptureDevicesDispatcher::Observer, // A value of 0 indicates no error. static int GetCurrentError(content::BrowserContext* context); - // Returns the current known state of the microphone. Since this state - // is browser (not profile) specific, it resides in the factory. - static bool IsMicrophoneAvailable(); - - // Returns whether the state of the audio devices has been updated. - // Essentially it indicates the validity of the return value from - // IsMicrophoneAvailable(). - static bool IsAudioDeviceStateUpdated(); - - // Overridden from MediaCaptureDevicesDispatcher::Observer - void OnUpdateAudioDevices( - const content::MediaStreamDevices& devices) override; - // This will kick off the monitor that calls OnUpdateAudioDevices when the // number of audio devices changes (or is initialized). It needs to be a // separate function so it can be called after the service is initialized @@ -67,21 +52,6 @@ class HotwordServiceFactory : public MediaCaptureDevicesDispatcher::Observer, KeyedService* BuildServiceInstanceFor( content::BrowserContext* context) const override; - // Must be called from the UI thread since the instance of - // MediaCaptureDevicesDispatcher can only be accessed on the UI thread. - void InitializeMicrophoneObserver(); - - bool microphone_available() { return microphone_available_; } - - bool microphone_available_; - - // Indicates if the check for audio devices has been run such that it can be - // included in the error checking. Audio checking is not done immediately - // upon start up because of the negative impact on performance. - bool audio_device_state_updated_; - - bool audio_device_state_updated() { return audio_device_state_updated_; } - DISALLOW_COPY_AND_ASSIGN(HotwordServiceFactory); }; diff --git a/chrome/browser/ui/webui/voice_search_ui.cc b/chrome/browser/ui/webui/voice_search_ui.cc index 6731445..b676d3b 100644 --- a/chrome/browser/ui/webui/voice_search_ui.cc +++ b/chrome/browser/ui/webui/voice_search_ui.cc @@ -270,9 +270,11 @@ class VoiceSearchDomHandler : public WebUIMessageHandler { AddPair(list, "NaCl Enabled", nacl_enabled); - AddPair(list, - "Microphone", - HotwordServiceFactory::IsMicrophoneAvailable() ? "Yes" : "No"); + HotwordService* hotword_service = + HotwordServiceFactory::GetForProfile(profile_); + AddPair(list, "Microphone", + hotword_service && hotword_service->microphone_available() ? "Yes" + : "No"); std::string audio_capture = "No"; if (profile_->GetPrefs()->GetBoolean(prefs::kAudioCaptureAllowed)) diff --git a/chrome/common/extensions/api/hotword_private.idl b/chrome/common/extensions/api/hotword_private.idl index 012ad64..a7ffeb1 100644 --- a/chrome/common/extensions/api/hotword_private.idl +++ b/chrome/common/extensions/api/hotword_private.idl @@ -182,5 +182,8 @@ namespace hotwordPrivate { // Fired when the browser wants to find out whether the speaker model // exists. static void onSpeakerModelExists(); + + // Fired when the microphone state changes. + static void onMicrophoneStateChanged(boolean enabled); }; }; |