live_caption_controller.cc 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. // Copyright (c) 2020 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. #include "components/live_caption/live_caption_controller.h"
  5. #include <memory>
  6. #include "base/bind.h"
  7. #include "base/command_line.h"
  8. #include "base/metrics/histogram_functions.h"
  9. #include "build/build_config.h"
  10. #include "components/live_caption/caption_bubble_context.h"
  11. #include "components/live_caption/caption_bubble_controller.h"
  12. #include "components/live_caption/caption_util.h"
  13. #include "components/live_caption/pref_names.h"
  14. #include "components/live_caption/views/caption_bubble.h"
  15. #include "components/pref_registry/pref_registry_syncable.h"
  16. #include "components/prefs/pref_change_registrar.h"
  17. #include "components/soda/constants.h"
  18. #include "components/soda/soda_installer.h"
  19. #include "components/sync_preferences/pref_service_syncable.h"
  20. #include "media/base/media_switches.h"
  21. #include "ui/native_theme/native_theme.h"
  22. namespace {
  23. const char* const kCaptionStylePrefsToObserve[] = {
  24. prefs::kAccessibilityCaptionsTextSize,
  25. prefs::kAccessibilityCaptionsTextFont,
  26. prefs::kAccessibilityCaptionsTextColor,
  27. prefs::kAccessibilityCaptionsTextOpacity,
  28. prefs::kAccessibilityCaptionsBackgroundColor,
  29. prefs::kAccessibilityCaptionsTextShadow,
  30. prefs::kAccessibilityCaptionsBackgroundOpacity};
  31. } // namespace
  32. namespace captions {
  33. LiveCaptionController::LiveCaptionController(
  34. PrefService* profile_prefs,
  35. PrefService* global_prefs,
  36. content::BrowserContext* browser_context)
  37. : profile_prefs_(profile_prefs),
  38. global_prefs_(global_prefs),
  39. browser_context_(browser_context) {}
  40. LiveCaptionController::~LiveCaptionController() {
  41. if (enabled_) {
  42. enabled_ = false;
  43. StopLiveCaption();
  44. }
  45. }
  46. // static
  47. void LiveCaptionController::RegisterProfilePrefs(
  48. user_prefs::PrefRegistrySyncable* registry) {
  49. registry->RegisterBooleanPref(
  50. prefs::kLiveCaptionBubbleExpanded, false,
  51. user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
  52. registry->RegisterBooleanPref(
  53. prefs::kLiveCaptionBubblePinned, false,
  54. user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
  55. registry->RegisterBooleanPref(
  56. prefs::kLiveCaptionEnabled, false,
  57. user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
  58. // Initially default the language to en-US.
  59. registry->RegisterStringPref(prefs::kLiveCaptionLanguageCode,
  60. speech::kUsEnglishLocale,
  61. user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
  62. registry->RegisterListPref(
  63. prefs::kLiveCaptionMediaFoundationRendererErrorSilenced,
  64. user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
  65. }
  66. void LiveCaptionController::Init() {
  67. base::UmaHistogramBoolean("Accessibility.LiveCaption.FeatureEnabled",
  68. IsLiveCaptionFeatureSupported());
  69. // Hidden behind a feature flag.
  70. if (!IsLiveCaptionFeatureSupported())
  71. return;
  72. pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
  73. pref_change_registrar_->Init(profile_prefs_);
  74. auto* command_line = base::CommandLine::ForCurrentProcess();
  75. if (command_line &&
  76. command_line->HasSwitch(switches::kEnableLiveCaptionPrefForTesting)) {
  77. profile_prefs_->SetBoolean(prefs::kLiveCaptionEnabled, true);
  78. }
  79. pref_change_registrar_->Add(
  80. prefs::kLiveCaptionEnabled,
  81. base::BindRepeating(&LiveCaptionController::OnLiveCaptionEnabledChanged,
  82. base::Unretained(this)));
  83. pref_change_registrar_->Add(
  84. prefs::kLiveCaptionLanguageCode,
  85. base::BindRepeating(&LiveCaptionController::OnLiveCaptionLanguageChanged,
  86. base::Unretained(this)));
  87. enabled_ = IsLiveCaptionEnabled();
  88. base::UmaHistogramBoolean("Accessibility.LiveCaption", enabled_);
  89. if (enabled_) {
  90. StartLiveCaption();
  91. } else {
  92. StopLiveCaption();
  93. }
  94. }
  95. void LiveCaptionController::OnLiveCaptionEnabledChanged() {
  96. bool enabled = IsLiveCaptionEnabled();
  97. if (enabled == enabled_)
  98. return;
  99. enabled_ = enabled;
  100. if (enabled) {
  101. StartLiveCaption();
  102. } else {
  103. StopLiveCaption();
  104. speech::SodaInstaller::GetInstance()->SetUninstallTimer(profile_prefs_,
  105. global_prefs_);
  106. }
  107. }
  108. void LiveCaptionController::OnLiveCaptionLanguageChanged() {
  109. if (enabled_)
  110. speech::SodaInstaller::GetInstance()->InstallLanguage(
  111. prefs::GetLiveCaptionLanguageCode(profile_prefs_), global_prefs_);
  112. }
  113. bool LiveCaptionController::IsLiveCaptionEnabled() {
  114. return profile_prefs_->GetBoolean(prefs::kLiveCaptionEnabled);
  115. }
  116. void LiveCaptionController::StartLiveCaption() {
  117. DCHECK(enabled_);
  118. // The SodaInstaller determines whether SODA is already on the device and
  119. // whether or not to download. Once SODA is on the device and ready, the
  120. // SODAInstaller calls OnSodaInstalled on its observers. The UI is created at
  121. // that time.
  122. if (speech::SodaInstaller::GetInstance()->IsSodaInstalled(
  123. speech::GetLanguageCode(
  124. prefs::GetLiveCaptionLanguageCode(profile_prefs_)))) {
  125. CreateUI();
  126. } else {
  127. speech::SodaInstaller::GetInstance()->AddObserver(this);
  128. speech::SodaInstaller::GetInstance()->Init(profile_prefs_, global_prefs_);
  129. }
  130. }
  131. void LiveCaptionController::StopLiveCaption() {
  132. DCHECK(!enabled_);
  133. speech::SodaInstaller::GetInstance()->RemoveObserver(this);
  134. DestroyUI();
  135. }
  136. void LiveCaptionController::OnSodaInstalled(
  137. speech::LanguageCode language_code) {
  138. if (!prefs::IsLanguageCodeForLiveCaption(language_code, profile_prefs_))
  139. return;
  140. // Live Caption should always be enabled when this is called. If Live Caption
  141. // has been disabled, then this should not be observing the SodaInstaller
  142. // anymore.
  143. DCHECK(enabled_);
  144. speech::SodaInstaller::GetInstance()->RemoveObserver(this);
  145. CreateUI();
  146. }
  147. void LiveCaptionController::OnSodaInstallError(
  148. speech::LanguageCode language_code,
  149. speech::SodaInstaller::ErrorCode error_code) {
  150. // Check that language code matches the selected language for Live Caption or
  151. // is LanguageCode::kNone (signifying the SODA binary failed).
  152. if (!prefs::IsLanguageCodeForLiveCaption(language_code, profile_prefs_) &&
  153. language_code != speech::LanguageCode::kNone) {
  154. return;
  155. }
  156. if (!base::FeatureList::IsEnabled(media::kLiveCaptionMultiLanguage)) {
  157. profile_prefs_->SetBoolean(prefs::kLiveCaptionEnabled, false);
  158. }
  159. }
  160. void LiveCaptionController::CreateUI() {
  161. if (is_ui_constructed_)
  162. return;
  163. is_ui_constructed_ = true;
  164. caption_bubble_controller_ = CaptionBubbleController::Create(profile_prefs_);
  165. caption_bubble_controller_->UpdateCaptionStyle(caption_style_);
  166. // Observe native theme changes for caption style updates.
  167. ui::NativeTheme::GetInstanceForWeb()->AddObserver(this);
  168. // Observe caption style prefs.
  169. for (const char* const pref_name : kCaptionStylePrefsToObserve) {
  170. DCHECK(!pref_change_registrar_->IsObserved(pref_name));
  171. pref_change_registrar_->Add(
  172. pref_name,
  173. base::BindRepeating(&LiveCaptionController::OnCaptionStyleUpdated,
  174. base::Unretained(this)));
  175. }
  176. OnCaptionStyleUpdated();
  177. }
  178. void LiveCaptionController::DestroyUI() {
  179. if (!is_ui_constructed_)
  180. return;
  181. is_ui_constructed_ = false;
  182. caption_bubble_controller_.reset(nullptr);
  183. // Remove native theme observer.
  184. ui::NativeTheme::GetInstanceForWeb()->RemoveObserver(this);
  185. // Remove prefs to observe.
  186. for (const char* const pref_name : kCaptionStylePrefsToObserve) {
  187. DCHECK(pref_change_registrar_->IsObserved(pref_name));
  188. pref_change_registrar_->Remove(pref_name);
  189. }
  190. }
  191. bool LiveCaptionController::DispatchTranscription(
  192. CaptionBubbleContext* caption_bubble_context,
  193. const media::SpeechRecognitionResult& result) {
  194. if (!caption_bubble_controller_)
  195. return false;
  196. return caption_bubble_controller_->OnTranscription(caption_bubble_context,
  197. result);
  198. }
  199. void LiveCaptionController::OnError(
  200. CaptionBubbleContext* caption_bubble_context,
  201. CaptionBubbleErrorType error_type,
  202. OnErrorClickedCallback error_clicked_callback,
  203. OnDoNotShowAgainClickedCallback error_silenced_callback) {
  204. if (!caption_bubble_controller_)
  205. CreateUI();
  206. caption_bubble_controller_->OnError(caption_bubble_context, error_type,
  207. std::move(error_clicked_callback),
  208. std::move(error_silenced_callback));
  209. }
  210. void LiveCaptionController::OnAudioStreamEnd(
  211. CaptionBubbleContext* caption_bubble_context) {
  212. if (!caption_bubble_controller_)
  213. return;
  214. caption_bubble_controller_->OnAudioStreamEnd(caption_bubble_context);
  215. }
  216. void LiveCaptionController::OnLanguageIdentificationEvent(
  217. const media::mojom::LanguageIdentificationEventPtr& event) {
  218. // TODO(crbug.com/1175357): Implement the UI for language identification.
  219. }
  220. #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS)
  221. void LiveCaptionController::OnToggleFullscreen(
  222. CaptionBubbleContext* caption_bubble_context) {
  223. if (!enabled_)
  224. return;
  225. // The easiest way to move the Live Caption UI to the right workspace is to
  226. // simply destroy and recreate the UI. The UI will automatically be created
  227. // in the workspace of the browser window that is transmitting captions.
  228. DestroyUI();
  229. CreateUI();
  230. }
  231. #endif
  232. void LiveCaptionController::OnCaptionStyleUpdated() {
  233. // Metrics are recorded when passing the caption prefs to the browser, so do
  234. // not duplicate them here.
  235. caption_style_ = GetCaptionStyleFromUserSettings(profile_prefs_,
  236. false /* record_metrics */);
  237. caption_bubble_controller_->UpdateCaptionStyle(caption_style_);
  238. }
  239. } // namespace captions