[M120 Migration][MM] Merge tizen tv audio input device patches 55/309355/7
authorpeng.yin <peng8.yin@samsung.com>
Thu, 1 Sep 2022 01:09:22 +0000 (09:09 +0800)
committerBot Blink <blinkbot@samsung.com>
Sat, 13 Apr 2024 02:49:43 +0000 (02:49 +0000)
[M108 Migration][MM] Support tizen tv audio input device
https://review.tizen.org/gerrit/#/c/292390

fixup! [M108 Migration][VD][MM] Support tizen tv audio input device
https://review.tizen.org/gerrit/#/c/292437/
https://review.tizen.org/gerrit/#/c/296022/

Audio device type SOUND_DEVICE_FORWARDING is not for inputing
https://review.tizen.org/gerrit/#/c/300368/

Change-Id: Ic642d3fd0a11fb924ceabc7d625150812655c474
Signed-off-by: peng.yin <peng8.yin@samsung.com>
27 files changed:
content/browser/media/audio_input_stream_broker.cc
content/browser/media/audio_loopback_stream_broker.cc
content/browser/renderer_host/render_widget_host_view_aura.cc
content/browser/renderer_host/render_widget_host_view_base.cc
content/browser/renderer_host/render_widget_host_view_base.h
content/browser/web_contents/web_contents_impl.cc
content/browser/web_contents/web_contents_impl.h
content/public/browser/audio_stream_broker.cc
content/public/browser/audio_stream_broker.h
content/public/browser/web_contents.h
content/public/browser/web_contents_delegate.h
packaging/chromium-efl.spec
tizen_src/build/BUILD.gn
tizen_src/build/config/compiler/BUILD.gn
tizen_src/chromium_impl/content/browser/browser_efl.gni
tizen_src/chromium_impl/content/browser/renderer_host/microphone_state_updater.cc [new file with mode: 0644]
tizen_src/chromium_impl/content/browser/renderer_host/microphone_state_updater.h [new file with mode: 0644]
tizen_src/chromium_impl/media/audio/tizen/audio_manager_capi.cc
tizen_src/chromium_impl/media/audio/tizen/audio_manager_capi.h
tizen_src/chromium_impl/media/audio/tizen/capi_bt_audio_input_stream.cc [new file with mode: 0644]
tizen_src/chromium_impl/media/audio/tizen/capi_bt_audio_input_stream.h [new file with mode: 0644]
tizen_src/chromium_impl/media/media_efl.gni
tizen_src/ewk/efl_integration/eweb_view.cc
tizen_src/ewk/efl_integration/eweb_view.h
tizen_src/ewk/efl_integration/public/ewk_view.cc
tizen_src/ewk/efl_integration/web_contents_delegate_efl.cc
tizen_src/ewk/efl_integration/web_contents_delegate_efl.h

index 50fa596..71ed9bc 100644 (file)
@@ -61,7 +61,7 @@ AudioInputStreamBroker::AudioInputStreamBroker(
   renderer_factory_client_.set_disconnect_handler(base::BindOnce(
       &AudioInputStreamBroker::ClientBindingLost, base::Unretained(this)));
 
-  NotifyProcessHostOfStartedStream(render_process_id);
+  NotifyProcessHostOfStartedStream(render_process_id, render_frame_id);
 
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kUseFakeDeviceForMediaStream)) {
@@ -77,7 +77,7 @@ AudioInputStreamBroker::~AudioInputStreamBroker() {
   if (user_input_monitor_)
     user_input_monitor_->DisableKeyPressMonitoring();
 
-  NotifyProcessHostOfStoppedStream(render_process_id());
+  NotifyProcessHostOfStoppedStream(render_process_id(), render_frame_id());
 
   // TODO(https://crbug.com/829317) update tab recording indicator.
 
index c9f683c..812809a 100644 (file)
@@ -48,7 +48,7 @@ AudioLoopbackStreamBroker::AudioLoopbackStreamBroker(
   // Notify the source that we are capturing from it.
   source_->AddLoopbackSink(this);
 
-  NotifyProcessHostOfStartedStream(render_process_id);
+  NotifyProcessHostOfStartedStream(render_process_id, render_frame_id);
 }
 
 AudioLoopbackStreamBroker::~AudioLoopbackStreamBroker() {
@@ -57,7 +57,7 @@ AudioLoopbackStreamBroker::~AudioLoopbackStreamBroker() {
   if (source_)
     source_->RemoveLoopbackSink(this);
 
-  NotifyProcessHostOfStoppedStream(render_process_id());
+  NotifyProcessHostOfStoppedStream(render_process_id(), render_frame_id());
 }
 
 void AudioLoopbackStreamBroker::CreateStream(
index def9115..3dcbb87 100644 (file)
@@ -289,6 +289,11 @@ RenderWidgetHostViewAura::RenderWidgetHostViewAura(
   // a valid FrameSinkId.
   DCHECK(frame_sink_id_.is_valid());
 
+#if BUILDFLAG(IS_TIZEN_TV)
+  mic_updater_ =
+      std::make_unique<MicrophoneStateUpdaterTizenEfl>(&web_contents);
+#endif
+
   CreateDelegatedFrameHostClient();
 
   host()->SetView(this);
@@ -2622,6 +2627,11 @@ RenderWidgetHostViewAura::~RenderWidgetHostViewAura() {
 
   if (text_input_manager_)
     text_input_manager_->RemoveObserver(this);
+
+#if BUILDFLAG(IS_TIZEN_TV)
+  if (mic_updater_)
+    mic_updater_->Release();
+#endif
 }
 
 void RenderWidgetHostViewAura::CreateAuraWindow(aura::client::WindowType type) {
index 94d403b..3804188 100644 (file)
@@ -797,6 +797,18 @@ void RenderWidgetHostViewBase::ImeCompositionRangeChanged(
   }
 }
 
+#if BUILDFLAG(IS_TIZEN_TV)
+void RenderWidgetHostViewBase::OnMediaStreamAdded() {
+  if (mic_updater_)
+    mic_updater_->UpdateMicrophoneState(true);
+}
+
+void RenderWidgetHostViewBase::OnMediaStreamRemoved() {
+  if (mic_updater_)
+    mic_updater_->UpdateMicrophoneState(false);
+}
+#endif  // IS_TIZEN_TV
+
 TextInputManager* RenderWidgetHostViewBase::GetTextInputManager() {
   if (text_input_manager_)
     return text_input_manager_;
index 5076dde..8db3f0c 100644 (file)
 #include "ui/gfx/range/range.h"
 #include "ui/surface/transport_dib.h"
 
+#if BUILDFLAG(IS_TIZEN_TV)
+#include "content/browser/renderer_host/microphone_state_updater.h"
+#endif
+
 namespace blink {
 class WebMouseEvent;
 class WebMouseWheelEvent;
@@ -272,6 +276,12 @@ class CONTENT_EXPORT RenderWidgetHostViewBase : public RenderWidgetHostView {
   ) {
   }
 
+#if BUILDFLAG(IS_TIZEN_TV)
+  // to notify RenderWidgetHostViewAura audio input is on/off
+  void OnMediaStreamAdded();
+  void OnMediaStreamRemoved();
+#endif
+
   // Requests to start stylus writing and returns true if successful.
   virtual bool RequestStartStylusWriting();
 
@@ -753,6 +763,10 @@ class CONTENT_EXPORT RenderWidgetHostViewBase : public RenderWidgetHostView {
   // to all displays.
   gfx::Size system_cursor_size_;
 
+#if BUILDFLAG(IS_TIZEN_TV)
+  std::unique_ptr<MicrophoneStateUpdaterTizenEfl> mic_updater_;
+#endif
+
  private:
   FRIEND_TEST_ALL_PREFIXES(
       BrowserSideFlingBrowserTest,
index fc639cf..0c464a4 100644 (file)
@@ -1545,6 +1545,27 @@ void WebContentsImpl::SetDelegate(WebContentsDelegate* delegate) {
   }
 }
 
+#if BUILDFLAG(IS_TIZEN_TV)
+void WebContentsImpl::ShowMicOpenedNotification(bool show) {
+  if (delegate_)
+    delegate_->ShowMicOpenedNotification(show);
+}
+
+void WebContentsImpl::OnMediaStreamAdded() {
+  RenderWidgetHostViewBase* rwhv =
+      static_cast<RenderWidgetHostViewBase*>(GetRenderWidgetHostView());
+  if (rwhv)
+    rwhv->OnMediaStreamAdded();
+}
+
+void WebContentsImpl::OnMediaStreamRemoved() {
+  RenderWidgetHostViewBase* rwhv =
+      static_cast<RenderWidgetHostViewBase*>(GetRenderWidgetHostView());
+  if (rwhv)
+    rwhv->OnMediaStreamRemoved();
+}
+#endif
+
 const RenderFrameHostImpl* WebContentsImpl::GetPrimaryMainFrame() const {
   return primary_frame_tree_.root()->current_frame_host();
 }
index 3b10b27..d55f6eb 100644 (file)
@@ -367,6 +367,11 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents,
   // WebContents ------------------------------------------------------
   WebContentsDelegate* GetDelegate() override;
   void SetDelegate(WebContentsDelegate* delegate) override;
+#if BUILDFLAG(IS_TIZEN_TV)
+  void ShowMicOpenedNotification(bool show) override;
+  void OnMediaStreamAdded() override;
+  void OnMediaStreamRemoved() override;
+#endif
   NavigationControllerImpl& GetController() override;
   BrowserContext* GetBrowserContext() override;
   base::WeakPtr<WebContents> GetWeakPtr() override;
index 28cad5b..c8345fa 100644 (file)
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
 
+#if BUILDFLAG(IS_TIZEN_TV)
+#include "content/public/browser/web_contents.h"
+#endif
+
 namespace content {
 
 AudioStreamBroker::LoopbackSink::LoopbackSink() = default;
@@ -26,23 +30,37 @@ AudioStreamBroker::AudioStreamBroker(int render_process_id, int render_frame_id)
 AudioStreamBroker::~AudioStreamBroker() = default;
 
 // static
-void AudioStreamBroker::NotifyProcessHostOfStartedStream(
-    int render_process_id) {
+void AudioStreamBroker::NotifyProcessHostOfStartedStream(int render_process_id,
+                                                         int render_frame_id) {
   auto impl = [](int id) {
     if (auto* process_host = RenderProcessHost::FromID(id))
       process_host->OnMediaStreamAdded();
   };
+#if BUILDFLAG(IS_TIZEN_TV)
+  content::WebContents* web_contents =
+      content::WebContents::FromRenderFrameHost(
+          content::RenderFrameHost::FromID(render_process_id, render_frame_id));
+  if (web_contents)
+    web_contents->OnMediaStreamAdded();
+#endif
   GetUIThreadTaskRunner({})->PostTask(FROM_HERE,
                                       base::BindOnce(impl, render_process_id));
 }
 
 // static
-void AudioStreamBroker::NotifyProcessHostOfStoppedStream(
-    int render_process_id) {
+void AudioStreamBroker::NotifyProcessHostOfStoppedStream(int render_process_id,
+                                                         int render_frame_id) {
   auto impl = [](int id) {
     if (auto* process_host = RenderProcessHost::FromID(id))
       process_host->OnMediaStreamRemoved();
   };
+#if BUILDFLAG(IS_TIZEN_TV)
+  content::WebContents* web_contents =
+      content::WebContents::FromRenderFrameHost(
+          content::RenderFrameHost::FromID(render_process_id, render_frame_id));
+  if (web_contents)
+    web_contents->OnMediaStreamRemoved();
+#endif
   GetUIThreadTaskRunner({})->PostTask(FROM_HERE,
                                       base::BindOnce(impl, render_process_id));
 }
index 124b061..8dddbe8 100644 (file)
@@ -74,8 +74,10 @@ class CONTENT_EXPORT AudioStreamBroker {
   // |render_process_id| of a started stream to ensure that the renderer is not
   // backgrounded. Must be paired with a later call to
   // NotifyRenderProcessOfStoppedStream()
-  static void NotifyProcessHostOfStartedStream(int render_process_id);
-  static void NotifyProcessHostOfStoppedStream(int render_process_id);
+  static void NotifyProcessHostOfStartedStream(int render_process_id,
+                                               int render_frame_id);
+  static void NotifyProcessHostOfStoppedStream(int render_process_id,
+                                               int render_frame_id);
 
   int render_process_id() const { return render_process_id_; }
   int render_frame_id() const { return render_frame_id_; }
index 87c9a75..8345d2e 100644 (file)
@@ -375,7 +375,11 @@ class WebContents : public PageNavigator,
   // Gets/Sets the delegate.
   virtual WebContentsDelegate* GetDelegate() = 0;
   virtual void SetDelegate(WebContentsDelegate* delegate) = 0;
-
+#if BUILDFLAG(IS_TIZEN_TV)
+  virtual void ShowMicOpenedNotification(bool show) = 0;
+  virtual void OnMediaStreamAdded() = 0;
+  virtual void OnMediaStreamRemoved() = 0;
+#endif
   // Gets the NavigationController for primary frame tree of this WebContents.
   // See comments on NavigationController for more details.
   virtual NavigationController& GetController() = 0;
index ec8def3..b0b8b63 100644 (file)
@@ -410,7 +410,6 @@ class CONTENT_EXPORT WebContentsDelegate {
 #if BUILDFLAG(IS_TIZEN_TV)
   virtual void DidEdgeScrollBy(const gfx::Point& offset, bool handled) {}
   virtual void MoveFocusToBrowser(int direction) {}
-  virtual void ShowMicOpenedNotification(bool show) {}
 #endif
   virtual void UpdateTooltipUnderCursor(const std::u16string& text) {}
 
@@ -773,6 +772,7 @@ class CONTENT_EXPORT WebContentsDelegate {
   virtual void NotifyPESData(const std::string& buf,
                              unsigned int len,
                              int media_position) {}
+  virtual void ShowMicOpenedNotification(bool show) {}
 #endif
 
 #if BUILDFLAG(IS_ANDROID)
index f2dc9b0..9af8a0d 100644 (file)
@@ -202,6 +202,8 @@ BuildRequires: pkgconfig(capi-media-sound-manager)
 BuildRequires: pkgconfig(capi-stt-wrapper-tv)
 BuildRequires: pkgconfig(capi-system-display-rotator)
 BuildRequires: pkgconfig(capi-appfw-app-manager)
+BuildRequires: pkgconfig(capi-network-bluetooth)
+BuildRequires: pkgconfig(capi-network-bluetooth-tv)
 BuildRequires: pkgconfig(cynara-client)
 BuildRequires: pkgconfig(security-privilege-manager)
 BuildRequires: pkgconfig(drmdecrypt)
index ff72301..9c1e976 100644 (file)
@@ -616,6 +616,32 @@ config("capi-media-audio-io-public") {
   }
 }
 
+config("capi-network-bluetooth") {
+  if (tizen_product_tv) {
+    ldflags = [ "-lcapi-network-bluetooth" ]
+  }
+}
+
+tizen_pkg_config("libcapi-network-bluetooth") {
+  packages = []
+  if (tizen_product_tv) {
+    packages = [ "capi-network-bluetooth" ]
+  }
+}
+
+config("capi-network-bluetooth-tv") {
+  if (tizen_product_tv) {
+    ldflags = [ "-lcapi-network-bluetooth-tv" ]
+  }
+}
+
+tizen_pkg_config("libcapi-network-bluetooth-tv") {
+  packages = []
+  if (tizen_product_tv) {
+    packages = [ "capi-network-bluetooth-tv" ]
+  }
+}
+
 config("capi-media-player") {
   if (is_tizen) {
     #ldflags = [ "-capi-media-player" ]
index 6f3402a..be230c2 100644 (file)
@@ -120,6 +120,7 @@ config("tizen_default_include_dirs") {
     "$deps_include_path/gstreamer-1.0/gst/video",
     "$deps_include_path/libsoup-2.4",
     "$deps_include_path/libsoup-2.4/libsoup",
+    "$deps_include_path/network",
     "$deps_include_path/orc-0.4",
     "$deps_include_path/orc-0.4/orc",
     "$deps_include_path/orc-0.4/orc-test",
index 2d87914..914da39 100644 (file)
@@ -192,3 +192,10 @@ if (tizen_web_speech_recognition) {
     "//tizen_src/chromium_impl/content/browser/speech/tts_platform_impl_tizen.h",
     ]
 }
+
+if (tizen_product_tv) {
+  external_content_browser_efl_sources += [
+    "//tizen_src/chromium_impl/content/browser/renderer_host/microphone_state_updater.cc",
+    "//tizen_src/chromium_impl/content/browser/renderer_host/microphone_state_updater.h",
+  ]
+}
diff --git a/tizen_src/chromium_impl/content/browser/renderer_host/microphone_state_updater.cc b/tizen_src/chromium_impl/content/browser/renderer_host/microphone_state_updater.cc
new file mode 100644 (file)
index 0000000..6246487
--- /dev/null
@@ -0,0 +1,213 @@
+// Copyright 2022 Samsung Electronics Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#include "content/browser/renderer_host/microphone_state_updater.h"
+
+#include "base/logging.h"
+#include "content/browser/web_contents/web_contents_impl_efl.h"
+#include "ecore_x_wayland_wrapper.h"
+#include "tizen_src/ewk/efl_integration/common/application_type.h"
+#include "tizen_src/ewk/efl_integration/ewk_privilege_checker.h"
+
+namespace content {
+
+constexpr auto kKeyBtVoice = "XF86BTVoice";
+
+// static
+int MicrophoneStateUpdaterTizenEfl::keygrab_set_count_ = 0;
+
+MicrophoneStateUpdaterTizenEfl::MicrophoneStateUpdaterTizenEfl(
+    content::WebContents* web_contents)
+    : web_contents_(web_contents) {
+  LOG(INFO) << __func__ << " this:" << this;
+
+  if (!web_contents_) {
+    LOG(ERROR) << "There is no web contents!";
+    state_ = State::kInvalid;
+    return;
+  }
+
+  if (!content::IsTIZENWRT()) {
+    LOG(ERROR) << "Only Grab BT voice key in WRT APP";
+    state_ = State::kInvalid;
+    return;
+  }
+
+  if (!CheckMicrophonePrivilege()) {
+    LOG(ERROR) << "Do not have micrphone privilege!";
+    state_ = State::kInvalid;
+    return;
+  }
+
+  if (!GetEflWindow()) {
+    LOG(ERROR) << "Get window failed!";
+    state_ = State::kInvalid;
+    return;
+  }
+
+  if (keygrab_set_count_ == 0) {
+    auto ret = ecore_wl2_window_keygrab_set(wl_win_, kKeyBtVoice, 0, 0, 0,
+                                            ECORE_WL2_WINDOW_KEYGRAB_TOPMOST);
+    if (!ret) {
+      LOG(ERROR) << "ecore_wl2_window_keygrab_set failed:" << ret;
+      state_ = State::kInvalid;
+      return;
+    }
+  }
+  keygrab_set_count_++;
+  is_keygrab_set_ = true;
+
+  keydown_hander_ =
+      ecore_event_handler_add(ECORE_EVENT_KEY_DOWN, OnKeyDown, this);
+  keyup_hander_ = ecore_event_handler_add(ECORE_EVENT_KEY_UP, OnKeyUp, this);
+
+  state_ = State::kGranted;
+}
+
+MicrophoneStateUpdaterTizenEfl::~MicrophoneStateUpdaterTizenEfl() {
+  LOG(INFO) << __func__ << " this:" << this;
+}
+
+void MicrophoneStateUpdaterTizenEfl::Release() {
+  LOG(INFO) << __func__ << " this:" << this;
+  state_ = State::kInvalid;
+  if (is_keygrab_set_)
+    keygrab_set_count_--;
+  if (keydown_hander_)
+    ecore_event_handler_del(keydown_hander_);
+  if (keyup_hander_)
+    ecore_event_handler_del(keyup_hander_);
+  if (wl_win_ && keygrab_set_count_ == 0 && is_keygrab_set_)
+    ecore_wl2_window_keygrab_unset(wl_win_, kKeyBtVoice, 0, 0);
+
+  keydown_hander_ = nullptr;
+  keyup_hander_ = nullptr;
+  wl_win_ = nullptr;
+  is_keygrab_set_ = false;
+}
+
+bool MicrophoneStateUpdaterTizenEfl::CheckMicrophonePrivilege() const {
+  bool result = false;
+  result = content::EwkPrivilegeChecker::GetInstance()->CheckPrivilege(
+      "http://developer.samsung.com/privilege/smartcontroller.microphone");
+  LOG(INFO) << "CheckSmartRCMicPrivilege = " << result;
+  return result;
+}
+
+void MicrophoneStateUpdaterTizenEfl::ShowMicOpenedNotification(bool show) {
+  if (!web_contents_) {
+    LOG(ERROR) << "ShowMicOpenedNotification failed!";
+    return;
+  }
+
+  web_contents_->ShowMicOpenedNotification(show);
+}
+
+void MicrophoneStateUpdaterTizenEfl::UpdateMicrophoneState(bool open) {
+  if (state_ == State::kInvalid) {
+    LOG(ERROR) << "Invalid state!";
+    return;
+  }
+
+  ShowMicOpenedNotification(open);
+  state_ = open ? State::kOpened : State::kClosed;
+}
+
+// the key up, key down, and long press logic is copied from m94 class
+// BTMicVoiceKeyGrab
+void MicrophoneStateUpdaterTizenEfl::OnVoiceKeyDown() {
+  voice_key_timer_.Reset();
+  /// 100ms is too short here, most press will be long press
+  voice_key_timer_.Start(FROM_HERE, base::Milliseconds(500), this,
+                         &MicrophoneStateUpdaterTizenEfl::VoiceKeyLongPressed);
+}
+
+void MicrophoneStateUpdaterTizenEfl::OnVoiceKeyUp() {
+  if (voice_key_timer_.IsRunning()) {
+    voice_key_timer_.Stop();
+    ShowMicOpenedNotification(true);
+  }
+}
+
+void MicrophoneStateUpdaterTizenEfl::VoiceKeyLongPressed() {
+  ShowMicOpenedNotification(false);
+}
+
+Evas_Object* MicrophoneStateUpdaterTizenEfl::GetEflNativeView() {
+  if (!web_contents_) {
+    LOG(ERROR) << "GetEflNativeView failed!";
+    return nullptr;
+  }
+
+  return static_cast<Evas_Object*>(
+      static_cast<WebContentsImplEfl*>(web_contents_)->GetEflMainLayout());
+}
+
+bool MicrophoneStateUpdaterTizenEfl::GetEflWindow() {
+  Evas_Object* parent_view = GetEflNativeView();
+  if (!parent_view) {
+    LOG(ERROR) << "GetParentEflWindow failed!";
+    return false;
+  }
+
+  Evas* evas = evas_object_evas_get(parent_view);
+  if (!evas) {
+    LOG(ERROR) << "evas_object_evas_get failed!";
+    return false;
+  }
+
+  const Ecore_Evas* ee = ecore_evas_ecore_evas_get(evas);
+  wl_win_ = ecore_evas_wayland2_window_get(ee);
+  if (!wl_win_) {
+    LOG(ERROR) << "ecore_evas_wayland2_window_get failed!";
+    return false;
+  }
+
+  return true;
+}
+
+Eina_Bool MicrophoneStateUpdaterTizenEfl::OnKeyUp(void* user_data,
+                                                  int type,
+                                                  void* event) {
+  Ecore_Event_Key* ev = static_cast<Ecore_Event_Key*>(event);
+  LOG(INFO) << "OnKeyUp type: " << type << " keyname: " << ev->keyname;
+
+  MicrophoneStateUpdaterTizenEfl* updater =
+      static_cast<MicrophoneStateUpdaterTizenEfl*>(user_data);
+  if (!ev || !updater || updater->GetState() == State::kInvalid) {
+    LOG(ERROR) << "Invalid state!";
+    return ECORE_CALLBACK_PASS_ON;
+  }
+
+  std::string voice_key(kKeyBtVoice);
+  if (!voice_key.compare(ev->keyname)) {
+    updater->OnVoiceKeyUp();
+    return ECORE_CALLBACK_DONE;
+  }
+
+  return ECORE_CALLBACK_PASS_ON;
+}
+
+Eina_Bool MicrophoneStateUpdaterTizenEfl::OnKeyDown(void* user_data,
+                                                    int type,
+                                                    void* event) {
+  Ecore_Event_Key* ev = static_cast<Ecore_Event_Key*>(event);
+  LOG(INFO) << "OnKeyUp type: " << type << " keyname: " << ev->keyname;
+
+  MicrophoneStateUpdaterTizenEfl* updater =
+      static_cast<MicrophoneStateUpdaterTizenEfl*>(user_data);
+  if (!ev || !updater || updater->GetState() == State::kInvalid) {
+    LOG(ERROR) << "Invalid state!";
+    return ECORE_CALLBACK_PASS_ON;
+  }
+
+  std::string voice_key(kKeyBtVoice);
+  if (!voice_key.compare(ev->keyname)) {
+    updater->OnVoiceKeyDown();
+    return ECORE_CALLBACK_DONE;
+  }
+
+  return ECORE_CALLBACK_PASS_ON;
+}
+
+}  // namespace content
diff --git a/tizen_src/chromium_impl/content/browser/renderer_host/microphone_state_updater.h b/tizen_src/chromium_impl/content/browser/renderer_host/microphone_state_updater.h
new file mode 100644 (file)
index 0000000..7458cda
--- /dev/null
@@ -0,0 +1,61 @@
+// Copyright 2022 Samsung Electronics Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MICROPHONE_STATE_UPDATER
+#define MICROPHONE_STATE_UPDATER
+
+#include <Ecore.h>
+#include <Ecore_Evas.h>
+#include <Ecore_Input.h>
+#include <Elementary.h>
+
+#include "base/timer/timer.h"
+
+namespace content {
+
+class WebContents;
+
+class MicrophoneStateUpdaterTizenEfl {
+ public:
+  enum class State { kClosed, kInvalid, kGranted, kOpened };
+
+  explicit MicrophoneStateUpdaterTizenEfl(WebContents* web_contents);
+  virtual ~MicrophoneStateUpdaterTizenEfl();
+
+  MicrophoneStateUpdaterTizenEfl(const MicrophoneStateUpdaterTizenEfl&) =
+      delete;
+  MicrophoneStateUpdaterTizenEfl& operator=(
+      const MicrophoneStateUpdaterTizenEfl&) = delete;
+
+  static Eina_Bool OnKeyUp(void* user_data, int type, void* event);
+  static Eina_Bool OnKeyDown(void* user_data, int type, void* event);
+
+  void UpdateMicrophoneState(bool open);
+  void Release();
+
+ protected:
+  bool CheckMicrophonePrivilege() const;
+  State GetState() { return state_; }
+  // notify only if current state is opened
+  void ShowMicOpenedNotification(bool show);
+  void OnVoiceKeyDown();
+  void OnVoiceKeyUp();
+  void VoiceKeyLongPressed();
+  bool GetEflWindow();
+  Evas_Object* GetEflNativeView();
+
+  base::OneShotTimer voice_key_timer_;
+  Ecore_Event_Handler* keydown_hander_ = nullptr;
+  Ecore_Event_Handler* keyup_hander_ = nullptr;
+  Ecore_Wl2_Window* wl_win_ = nullptr;
+
+  State state_ = State::kInvalid;
+  content::WebContents* web_contents_ = nullptr;
+
+  static int keygrab_set_count_;
+  bool is_keygrab_set_ = false;
+};
+
+}  // namespace content
+#endif
index 08fd7c5..b36c0aa 100644 (file)
 #include "media/base/audio_parameters.h"
 #include "media/base/channel_layout.h"
 #include "media/base/limits.h"
+#if BUILDFLAG(IS_TIZEN_TV)
+#include "media/audio/tizen/capi_bt_audio_input_stream.h"
+#include "tizen_src/ewk/efl_integration/ewk_privilege_checker.h"
+#define UNIQUE_BT_DEVICE_ID "1"
+#endif
 
 namespace media {
 
@@ -49,11 +54,87 @@ bool AudioManagerCapi::HasAudioInputDevices() {
   return !devices.empty();
 }
 
+#if BUILDFLAG(IS_TIZEN_TV)
+bool AudioManagerCapi::CheckSmartRCMicPrivilege() const {
+  bool result = content::EwkPrivilegeChecker::GetInstance()->CheckPrivilege(
+      "http://developer.samsung.com/privilege/smartcontroller.microphone");
+
+  if (!result)
+    LOG(INFO) << "AudioManagerCapi::CheckSmartRCMicPrivilege = " << result;
+  return result;
+}
+
+void AudioManagerCapi::BTHidStateChangedCB(int result,
+                                           bool connected,
+                                           const char* remote_address,
+                                           void* user_data) {
+  LOG(INFO) << "Bluetooth Event " << result << "Received address"
+            << remote_address;
+}
+
+void AudioManagerCapi::BTHidAudioDataReceiveCB(bt_hid_voice_data_s* voice_data,
+                                               void* user_data) {
+  LOG(INFO) << "Bluetooth Event BTHidAudioDataReceiveCB";
+}
+
+bool AudioManagerCapi::GetBluetoothMicNames(
+    media::AudioDeviceNames* device_names) const {
+  int ret = BT_ERROR_NONE;
+
+  ret = bt_product_init();
+  if (ret != BT_ERROR_NONE) {
+    LOG(ERROR) << "Fail to init bt : " << ret;
+    return false;
+  }
+
+  ret = bt_hid_host_initialize(BTHidStateChangedCB, NULL);
+  if (ret != BT_ERROR_NONE) {
+    LOG(ERROR) << "bt_hid_host_initialize fail: " << ret;
+    bt_product_deinit();
+    return false;
+  }
+
+  ret = bt_hid_set_audio_data_receive_cb(BTHidAudioDataReceiveCB, NULL);
+  if (ret != BT_ERROR_NONE) {
+    LOG(ERROR) << "bt_hid_set_audio_data_receive_cb fail: " << ret;
+    bt_hid_host_deinitialize();
+    bt_product_deinit();
+    return false;
+  }
+
+  bool is_connected = false;
+  ret = bt_hid_get_smart_remote_conn_status(&is_connected);
+  if (ret == BT_ERROR_NONE && is_connected) {
+    device_names->push_front(
+        AudioDeviceName("Samsung Bluetooth Microphone", UNIQUE_BT_DEVICE_ID));
+  }
+
+  bt_hid_unset_audio_data_receive_cb();
+  bt_hid_host_deinitialize();
+  bt_product_deinit();
+
+  if (!is_connected) {
+    LOG(INFO) << "Bluetooth mic is not connected.";
+    return false;
+  }
+
+  return true;
+}
+#endif
+
 void AudioManagerCapi::GetAudioDeviceNames(
     bool input,
     media::AudioDeviceNames* device_names) const {
   DCHECK(device_names != NULL);
   DCHECK(device_names->empty());
+#if BUILDFLAG(IS_TIZEN_TV)
+  if (CheckSmartRCMicPrivilege() && GetBluetoothMicNames(device_names)) {
+    // if app has privilege to access microphone of smartRC
+    // and it is connected, usb mic will be hided.
+    LOG(WARNING) << "use SmartRC microphone instead.";
+    return;
+  }
+#endif
   auto mask = input ? SOUND_DEVICE_IO_DIRECTION_IN_MASK
                     : SOUND_DEVICE_IO_DIRECTION_OUT_MASK;
 
@@ -73,6 +154,64 @@ void AudioManagerCapi::GetAudioDeviceNames(
     return;
   }
 
+#if BUILDFLAG(IS_TIZEN_TV)
+  sound_device_h device;
+  AudioDeviceNames device_names_built_in;
+  while (sound_manager_get_next_device(list, &device) ==
+         SOUND_MANAGER_ERROR_NONE) {
+    int id;
+    char* name = nullptr;
+    sound_device_type_e device_type;
+    int get_status = sound_manager_get_device_id(device, &id);
+    if (get_status != SOUND_MANAGER_ERROR_NONE) {
+      LOG(ERROR) << "Failed to get device ID. Err:" << get_status;
+      continue;
+    }
+
+    get_status = sound_manager_get_device_name(device, &name);
+    if (get_status != SOUND_MANAGER_ERROR_NONE) {
+      LOG(ERROR) << "Failed to get device name. Err:" << get_status;
+      continue;
+    }
+
+    get_status = sound_manager_get_device_type(device, &device_type);
+    if (get_status != SOUND_MANAGER_ERROR_NONE) {
+      LOG(ERROR) << "Failed to get device type. Err:" << get_status;
+      continue;
+    }
+
+    if (input && device_type == SOUND_DEVICE_FORWARDING) {
+      LOG(ERROR) << "Device with type SOUND_DEVICE_FORWARDING is audio input "
+                    "device but it doesn't provide microphone data. ignore it:"
+                 << "Device - ID:" << id << ". Name:" << name
+                 << ". type:" << device_type;
+      continue;
+    }
+
+    LOG(INFO) << "Device - ID:" << id << ". Name:" << name
+              << ". type:" << device_type;
+
+    if (device_type == SOUND_DEVICE_BUILTIN_MIC) {
+      device_names_built_in.push_back(
+          AudioDeviceName(name, std::to_string(id)));
+    } else {
+      device_names->push_back(AudioDeviceName(name, std::to_string(id)));
+    }
+  }
+
+  device_names->insert(device_names->end(), device_names_built_in.begin(),
+                       device_names_built_in.end());
+
+  ret = sound_manager_free_device_list(list);
+  if (ret != SOUND_MANAGER_ERROR_NONE)
+    LOG(INFO) << "Failed to free device list. Err:" << ret;
+
+  if (device_names->empty())
+    device_names->push_front(AudioDeviceName::CreateDefault());
+
+  return;
+#endif
+
   device_names->push_front(AudioDeviceName::CreateDefault());
 }
 
@@ -88,6 +227,15 @@ void AudioManagerCapi::GetAudioOutputDeviceNames(
 
 AudioParameters AudioManagerCapi::GetInputStreamParameters(
     const std::string& device_id) {
+#if BUILDFLAG(IS_TIZEN_TV)
+  LOG(INFO) << "GetInputStreamParameters " << device_id;
+  if (CheckSmartRCMicPrivilege() &&
+      device_id.compare(UNIQUE_BT_DEVICE_ID) == 0) {
+    return AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
+                           media::ChannelLayoutConfig::Mono(), kBtSampleRate,
+                           kBtMinReadSize);
+  }
+#endif
   return AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
                          media::ChannelLayoutConfig::Stereo(),
                          kDefaultSampleRate, kDefaultInputBufferSize);
@@ -232,6 +380,12 @@ AudioOutputStream* AudioManagerCapi::MakeOutputStream(
 AudioInputStream* AudioManagerCapi::MakeInputStream(
     const AudioParameters& params,
     const std::string& device_id) {
+#if BUILDFLAG(IS_TIZEN_TV)
+  if (CheckSmartRCMicPrivilege() &&
+      device_id.compare(UNIQUE_BT_DEVICE_ID) == 0) {
+    return new CapiBtAudioInputStream(this, params);
+  }
+#endif
   return new CapiUsbAudioInputStream(this, device_id, params);
 }
 
index dfe2513..edc491b 100644 (file)
 #include "build/build_config.h"
 #include "media/audio/audio_manager_base.h"
 
+#if BUILDFLAG(IS_TIZEN_TV)
+#include <bluetooth_product.h>
+#endif
+
 namespace media {
 
 class MuteableAudioOutputStream;
@@ -45,6 +49,10 @@ class MEDIA_EXPORT AudioManagerCapi : public AudioManagerBase {
   void ReleaseOutputStream(AudioOutputStream* stream) override;
   void ReleaseInputStream(AudioInputStream* stream) override;
   const char* GetName() override;
+#if BUILDFLAG(IS_TIZEN_TV)
+  bool CheckSmartRCMicPrivilege() const;
+#endif
+
   // Implementation of AudioManagerBase.
   AudioOutputStream* MakeLinearOutputStream(
       const AudioParameters& params,
@@ -73,6 +81,15 @@ class MEDIA_EXPORT AudioManagerCapi : public AudioManagerBase {
       const AudioParameters& input_params) override;
 
  private:
+#if BUILDFLAG(IS_TIZEN_TV)
+  static void BTHidStateChangedCB(int result,
+                                  bool connected,
+                                  const char* remote_address,
+                                  void* user_data);
+  static void BTHidAudioDataReceiveCB(bt_hid_voice_data_s* voice_data,
+                                      void* user_data);
+  bool GetBluetoothMicNames(media::AudioDeviceNames* device_names) const;
+#endif
   void GetAudioDeviceNames(bool input,
                            media::AudioDeviceNames* device_names) const;
 
diff --git a/tizen_src/chromium_impl/media/audio/tizen/capi_bt_audio_input_stream.cc b/tizen_src/chromium_impl/media/audio/tizen/capi_bt_audio_input_stream.cc
new file mode 100644 (file)
index 0000000..fbceb5a
--- /dev/null
@@ -0,0 +1,250 @@
+// Copyright (c) 2021 The Samsung Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/tizen/capi_bt_audio_input_stream.h"
+
+#include <audio_io.h>
+#include <sys/types.h>
+
+#include "base/functional/bind.h"
+#include "base/logging.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace media {
+
+#define SMART_CONTROL_EXTEND_CMD 0x03
+#define SMART_CONTROL_START_CMD 0x04
+
+#define KEY_BT_VOICE "XF86BTVoice"
+
+#define BUF_SAVE_MODE 1
+#undef BUF_SAVE_MODE
+
+#ifdef BUF_SAVE_MODE
+
+static FILE* g_dump_file;
+
+void CapiBtAudioInputStream::CreateAudioDumpFile() {
+  int ret = -1;
+  char g_dump_filename[128] = {
+      '\0',
+  };
+  static int count = 1;
+  count++;
+
+  while (1) {
+    snprintf(g_dump_filename, sizeof(g_dump_filename), "/tmp/vc_normal_%d_%d",
+             getpid(), count);
+    ret = access(g_dump_filename, 0);
+
+    if (0 == ret) {
+      if (0 == remove(g_dump_filename)) {
+        break;
+      } else {
+        count++;
+      }
+    } else {
+      break;
+    }
+  }
+
+  /* open test file */
+  g_dump_file = fopen(g_dump_filename, "wb+x");
+  if (!g_dump_file) {
+    LOG(ERROR) << "File not found!";
+  }
+}
+#endif  // BUF_SAVE_MODE
+
+CapiBtAudioInputStream::CapiBtAudioInputStream(AudioManagerCapi* audio_manager,
+                                               const AudioParameters& params)
+    : CapiAudioInputStream(audio_manager, params),
+      send_stop_flag_(false),
+      weak_factory_(this) {}
+
+CapiBtAudioInputStream::~CapiBtAudioInputStream() {
+  LOG(INFO) << "~CapiBtAudioInputStream";
+}
+
+void CapiBtAudioInputStream::BTHidStateChangedCB(int result,
+                                                 bool connected,
+                                                 const char* remote_address,
+                                                 void* user_data) {
+  LOG(INFO) << "Bluetooth Event" << result << "Received address"
+            << remote_address;
+}
+
+void CapiBtAudioInputStream::BTHidAudioDataReceiveCB(
+    bt_hid_voice_data_s* voice_data,
+    void* user_data) {
+  if (voice_data == nullptr || user_data == nullptr) {
+    LOG(ERROR) << "BTHidAudioDataReceiveCB paramerter invalid";
+    return;
+  }
+
+  CapiBtAudioInputStream* mic =
+      reinterpret_cast<CapiBtAudioInputStream*>(user_data);
+  mic->ReadBTAudioData(voice_data);
+}
+
+bool CapiBtAudioInputStream::OpenMic() {
+  int ret = BT_ERROR_NONE;
+  ret = bt_product_init();
+  if (ret != BT_ERROR_NONE) {
+    LOG(ERROR) << "Fail to init bt: " << ret;
+    if (callback_)
+      callback_->OnError();
+    return false;
+  }
+
+  send_stop_flag_ = false;
+  ret = bt_hid_host_initialize(BTHidStateChangedCB, this);
+  if (ret != BT_ERROR_NONE) {
+    LOG(ERROR) << "bt_hid_host_initialize fail: " << ret;
+    if (callback_)
+      callback_->OnError();
+    return false;
+  }
+
+  ret = bt_hid_set_audio_data_receive_cb(BTHidAudioDataReceiveCB, this);
+  if (ret != BT_ERROR_NONE) {
+    LOG(ERROR) << "bt_hid_set_audio_data_receive_cb fail: " << ret;
+    if (callback_)
+      callback_->OnError();
+    return false;
+  }
+
+  bool is_connected = false;
+  ret = bt_hid_get_smart_remote_conn_status(&is_connected);
+  if (ret != BT_ERROR_NONE || !is_connected) {
+    LOG(INFO) << "Bluetooth mic is not connected.";
+    if (callback_)
+      callback_->OnError();
+    return false;
+  }
+
+  state_ = media::kIsOpened;
+
+  return true;
+}
+
+void CapiBtAudioInputStream::StartMic() {
+#ifdef BUF_SAVE_MODE
+  CreateAudioDumpFile();
+#endif  // BUF_SAVE_MODE
+
+  audio_worker_.Start();
+  StartAgc();
+
+  state_ = media::kIsStarted;
+}
+
+void CapiBtAudioInputStream::StopMic() {
+  int ret = -1;
+
+#ifdef BUF_SAVE_MODE
+  if (g_dump_file) {
+    fclose(g_dump_file);
+    g_dump_file = nullptr;
+  }
+#endif  // BUF_SAVE_MODE
+
+  bool stoped = false;
+  ret = bt_hid_rc_stop_sending_voice(nullptr);
+  if (ret == BT_ERROR_NONE) {
+    LOG(INFO) << "Stop bt audio recorder";
+    stoped = true;
+  } else if (ret == BT_ERROR_NOW_IN_PROGRESS) {
+    LOG(INFO) << "Fail bt_hid_rc_stop_sending_voice(), send again in CloseMic";
+    send_stop_flag_ = true;
+    bt_hid_unset_audio_data_receive_cb();
+  }
+
+  if (!stoped) {
+    LOG(ERROR) << "Fail to stop bt audio";
+  }
+}
+
+void CapiBtAudioInputStream::CloseMic(bool success) {
+  state_ = media::kIsClosed;
+  if (send_stop_flag_) {
+    const int ret = bt_hid_rc_stop_sending_voice(nullptr);
+    LOG(INFO) << "bt_hid_rc_stop_sending_voice = " << ret;
+    send_stop_flag_ = false;
+  }
+
+  bt_hid_unset_audio_data_receive_cb();
+  bt_hid_host_deinitialize();
+  bt_product_deinit();
+}
+
+#define SEND_COMMAND_BUFFER_COUNT 250
+#define MAX_BUFFER_COUNT 100000
+void CapiBtAudioInputStream::ReadBTAudioData(bt_hid_voice_data_s* voice_data) {
+  if (state_ != media::kIsStarted) {
+    LOG(INFO) << "Not started yet, but send audio data vi Bluetooth";
+    return;
+  }
+
+  static int buffer_count = 0;
+  if (SEND_COMMAND_BUFFER_COUNT == buffer_count) {
+    const unsigned char input_data[2] = {SMART_CONTROL_EXTEND_CMD, 0x10};
+    if (BT_ERROR_NONE !=
+        bt_hid_send_rc_command(nullptr, input_data, sizeof(input_data))) {
+      LOG(ERROR) << "Fail bt_hid_send_rc_command";
+    } else {
+      LOG(INFO) << "Extend bt audio recorder";
+    }
+  }
+  if (MAX_BUFFER_COUNT == buffer_count) {
+    buffer_count = 0;
+  }
+  buffer_count++;
+
+  int number_of_frames =
+      voice_data->length / params_.GetBytesPerFrame(kDefaultSampleFormat);
+  if (number_of_frames > fifo_.GetUnfilledFrames()) {
+    int increase_blocks_of_buffer =
+        (number_of_frames - fifo_.GetUnfilledFrames()) /
+            params_.frames_per_buffer() +
+        1;
+    fifo_.IncreaseCapacity(increase_blocks_of_buffer);
+  }
+
+  fifo_.Push((const void*)voice_data->audio_buf, number_of_frames,
+             kBitsPerSample / 8);
+
+#ifdef BUF_SAVE_MODE
+  if (g_dump_file) /* write pcm buffer */
+    fwrite(voice_data->audio_buf, 1, voice_data->length, g_dump_file);
+#endif  // BUF_SAVE_MODE
+
+  double normalized_volume = 0.0;
+  GetAgcVolume(&normalized_volume);
+
+  double hardware_delay_seconds =
+      static_cast<double>(params_.GetBufferDuration().InSeconds() * 2) *
+      params_.channels();
+
+  while (fifo_.available_blocks()) {
+    const AudioBus* audio_bus = fifo_.Consume();
+    hardware_delay_seconds +=
+        static_cast<double>(fifo_.GetAvailableFrames()) / params_.sample_rate();
+
+    // To reduce latency run client CB from dedicated thread
+    if (callback_) {
+      // Need to copy data out if it runs in different thread
+      std::unique_ptr<AudioBus> audio_bus_copy =
+          AudioBus::Create(audio_bus->channels(), audio_bus->frames());
+      audio_bus->CopyTo(audio_bus_copy.get());
+      audio_worker_.task_runner()->PostTask(
+          FROM_HERE,
+          base::BindOnce(&CapiBtAudioInputStream::OnAudioIOData,
+                         base::Unretained(this), std::move(audio_bus_copy),
+                         hardware_delay_seconds, normalized_volume));
+    }
+  }
+}
+
+}  // namespace media
diff --git a/tizen_src/chromium_impl/media/audio/tizen/capi_bt_audio_input_stream.h b/tizen_src/chromium_impl/media/audio/tizen/capi_bt_audio_input_stream.h
new file mode 100644 (file)
index 0000000..712fedc
--- /dev/null
@@ -0,0 +1,57 @@
+// Copyright (c) 2021 The Samsung Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_TIZEN_CAPI_BT_AUDIO_INPUT_STREAM_H_
+#define MEDIA_AUDIO_TIZEN_CAPI_BT_AUDIO_INPUT_STREAM_H_
+
+#include <audio_io.h>
+#include <string>
+
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+#include "media/audio/audio_debug_recording_helper.h"
+#include "media/audio/tizen/capi_audio_input.h"
+#include "media/audio/tizen/capi_util.h"
+#include "media/base/audio_block_fifo.h"
+#include "media/base/audio_parameters.h"
+
+#include <bluetooth_product.h>
+
+namespace media {
+
+class AudioManagerCapi;
+
+class CapiBtAudioInputStream final : public CapiAudioInputStream {
+ public:
+  CapiBtAudioInputStream(AudioManagerCapi* audio_manager,
+                         const AudioParameters& params);
+
+  CapiBtAudioInputStream(const CapiBtAudioInputStream&) = delete;
+  CapiBtAudioInputStream& operator=(const CapiBtAudioInputStream&) = delete;
+
+  ~CapiBtAudioInputStream() override;
+
+ protected:
+  bool OpenMic() override;
+  void StartMic() override;
+  void StopMic() override;
+  void CloseMic(bool success) override;
+
+ private:
+  void CreateAudioDumpFile();
+  static void BTHidStateChangedCB(int result,
+                                  bool connected,
+                                  const char* remote_address,
+                                  void* user_data);
+  static void BTHidAudioDataReceiveCB(bt_hid_voice_data_s* voice_data,
+                                      void* user_data);
+  void ReadBTAudioData(bt_hid_voice_data_s* voice_data);
+
+  bool send_stop_flag_;
+  base::WeakPtrFactory<CapiBtAudioInputStream> weak_factory_;
+};
+
+}  // namespace media
+#endif  // MEDIA_AUDIO_TIZEN_CAPI_AUDIO_INPUT_H_
index 5abc8b7..d004498 100644 (file)
@@ -172,4 +172,17 @@ if (tizen_audio_io) {
     "//tizen_src/chromium_impl/media/audio/tizen/capi_util.cc",
     "//tizen_src/chromium_impl/media/audio/tizen/capi_util.h",
   ]
+
+  if (tizen_product_tv) {
+    external_media_efl_audio_io_sources += [
+      "//tizen_src/chromium_impl/media/audio/tizen/capi_bt_audio_input_stream.cc",
+      "//tizen_src/chromium_impl/media/audio/tizen/capi_bt_audio_input_stream.h",
+    ]
+    external_media_efl_audio_io_config += [
+      "//tizen_src/build:capi-network-bluetooth",
+      "//tizen_src/build:libcapi-network-bluetooth",
+      "//tizen_src/build:capi-network-bluetooth-tv",
+      "//tizen_src/build:libcapi-network-bluetooth-tv",
+    ]
+  }
 }
index 70d210d..e49555e 100644 (file)
@@ -2246,6 +2246,25 @@ bool EWebView::InvokeViewUnfocusAllowCallback(Ewk_Unfocus_Direction direction,
   return unfocus_allow_cb_.Run(ewk_view_, direction, callback_result);
 }
 
+#if BUILDFLAG(IS_TIZEN_TV)
+void EWebView::SetViewSmartrcMicNotificationCallback(
+    Ewk_View_SmartRC_Mic_Notification_Callback callback,
+    void* user_data) {
+  smartrc_mic_notification_cb_.Set(callback, user_data);
+}
+
+void EWebView::ShowMicOpenedNotification(bool show) {
+  if (smartrc_mic_notification_cb_.IsCallbackSet()) {
+    smartrc_mic_notification_cb_.Run(ewk_view_, static_cast<Eina_Bool>(show));
+  } else {
+    LOG(INFO)
+        << "call ewk_view_smartrc_show_mic_notification_callback_set to get "
+           "notified when bluetooth mic is going to access user voice. "
+        << show;
+  }
+}
+#endif
+
 void EWebView::StopFinding() {
   web_contents_->StopFinding(content::STOP_FIND_ACTION_CLEAR_SELECTION);
 }
index 5888c68..b7d349d 100644 (file)
@@ -584,6 +584,12 @@ class EWebView {
                                    void* user_data);
   bool InvokeViewUnfocusAllowCallback(Ewk_Unfocus_Direction direction,
                                       Eina_Bool* result);
+#if BUILDFLAG(IS_TIZEN_TV)
+  void SetViewSmartrcMicNotificationCallback(
+      Ewk_View_SmartRC_Mic_Notification_Callback callback,
+      void* user_data);
+  void ShowMicOpenedNotification(bool show);
+#endif
   void DidChangeContentsSize(int width, int height);
   const Eina_Rectangle GetContentsSize() const;
   void GetScrollSize(int* w, int* h);
@@ -990,6 +996,10 @@ class EWebView {
       load_error_page_cb_;
   WebViewCallback<Ewk_View_Unfocus_Allow_Callback, Ewk_Unfocus_Direction>
       unfocus_allow_cb_;
+#if BUILDFLAG(IS_TIZEN_TV)
+  WebViewCallback<Ewk_View_SmartRC_Mic_Notification_Callback, bool>
+      smartrc_mic_notification_cb_;
+#endif
   WebViewCallback<Ewk_View_Notification_Permission_Callback,
                   Ewk_Notification_Permission_Request*>
       notification_permission_callback_;
index d30fc43..e85e612 100644 (file)
@@ -940,6 +940,16 @@ void ewk_view_unfocus_allow_callback_set(Evas_Object* ewkView, Ewk_View_Unfocus_
   impl->SetViewUnfocusAllowCallback(callback, user_data);
 }
 
+void ewk_view_smartrc_show_mic_notification_callback_set(Evas_Object* ewkView, Ewk_View_SmartRC_Mic_Notification_Callback callback, void* user_data)
+{
+#if BUILDFLAG(IS_TIZEN_TV)
+  EWK_VIEW_IMPL_GET_OR_RETURN(ewkView, impl);
+  impl->SetViewSmartrcMicNotificationCallback(callback, user_data);
+#else
+  LOG_EWK_API_MOCKUP("This API is only available in Tizen TV product.");
+#endif
+}
+
 void ewk_view_geolocation_permission_callback_set(Evas_Object* ewk_view, Ewk_View_Geolocation_Permission_Callback callback, void* user_data)
 {
   EWK_VIEW_IMPL_GET_OR_RETURN(ewk_view, impl);
@@ -1685,11 +1695,6 @@ void ewk_view_ime_window_set(Evas_Object* o, void* window)
   LOG_EWK_API_MOCKUP();
 }
 
-void ewk_view_smartrc_show_mic_notification_callback_set(Evas_Object* o, Ewk_View_SmartRC_Mic_Notification_Callback callback, void* user_data)
-{
-  LOG_EWK_API_MOCKUP();
-}
-
 Eina_Bool ewk_view_set_support_canvas_hole(Evas_Object* ewkView, const char* url)
 {
   LOG_EWK_API_MOCKUP();
index 01f2d66..18db86a 100644 (file)
@@ -809,6 +809,10 @@ void WebContentsDelegateEfl::NotifyPESData(const std::string& buf,
                                            int media_position) {
   return web_view_->NotifyPESData(buf, len, media_position);
 }
+
+void WebContentsDelegateEfl::ShowMicOpenedNotification(bool show) {
+  web_view_->ShowMicOpenedNotification(show);
+}
 #endif
 
 void WebContentsDelegateEfl::OnGetMainFrameScrollbarVisible(int callback_id,
index 6803af8..33b7045 100644 (file)
@@ -187,6 +187,7 @@ class WebContentsDelegateEfl : public WebContentsDelegate {
       base::OnceCallback<void(const MediaDeviceEnumeration&)>;
   void GetMediaDeviceList(EnumerationCallback cb);
   void VideoPlayingStatusReceived(bool is_playing, int callback_id) override;
+  void ShowMicOpenedNotification(bool show) override;
 #endif
 
 #if defined(TIZEN_AUTOFILL)