blob: a1d9d0e49d7ce4d2cd2fc7d06c807ee4731cab92 [file] [log] [blame]
// Copyright (c) 2020 The Chromium 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 "chrome/browser/ui/views/accessibility/caption_bubble_controller_views.h"
#include <memory>
#include <string>
#include "base/bind.h"
#include "chrome/browser/accessibility/caption_controller.h"
#include "chrome/browser/accessibility/caption_controller_factory.h"
#include "chrome/browser/accessibility/caption_host_impl.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "components/live_caption/views/caption_bubble.h"
#include "components/live_caption/views/caption_bubble_model.h"
#include "content/public/browser/web_contents.h"
#include "ui/views/widget/widget.h"
namespace captions {
// Static
std::unique_ptr<CaptionBubbleController> CaptionBubbleController::Create() {
return std::make_unique<CaptionBubbleControllerViews>();
}
CaptionBubbleControllerViews::CaptionBubbleControllerViews() {
caption_bubble_ = new CaptionBubble(
base::BindOnce(&CaptionBubbleControllerViews::OnCaptionBubbleDestroyed,
base::Unretained(this)),
/* hide_on_inactivity= */ true);
caption_widget_ =
views::BubbleDialogDelegateView::CreateBubble(caption_bubble_);
}
CaptionBubbleControllerViews::~CaptionBubbleControllerViews() {
if (caption_widget_)
caption_widget_->CloseNow();
}
void CaptionBubbleControllerViews::OnCaptionBubbleDestroyed() {
caption_bubble_ = nullptr;
caption_widget_ = nullptr;
}
bool CaptionBubbleControllerViews::OnTranscription(
CaptionHostImpl* caption_host_impl,
const chrome::mojom::TranscriptionResultPtr& transcription_result) {
if (!caption_bubble_)
return false;
SetActiveModel(caption_host_impl);
if (active_model_->IsClosed())
return false;
// If the caption bubble has no activity and it receives a final
// transcription, don't set text. The speech service sends a final
// transcription after several seconds of no audio. This prevents the bubble
// reappearing with a final transcription after it had disappeared due to no
// activity.
if (!caption_bubble_->HasActivity() && transcription_result->is_final)
return true;
active_model_->SetPartialText(transcription_result->transcription);
if (transcription_result->is_final)
active_model_->CommitPartialText();
return true;
}
void CaptionBubbleControllerViews::OnError(CaptionHostImpl* caption_host_impl) {
if (!caption_bubble_)
return;
SetActiveModel(caption_host_impl);
if (active_model_->IsClosed())
return;
active_model_->OnError();
}
void CaptionBubbleControllerViews::OnAudioStreamEnd(
CaptionHostImpl* caption_host_impl) {
if (!caption_bubble_)
return;
CaptionBubbleModel* caption_bubble_model =
caption_bubble_models_[caption_host_impl].get();
if (active_model_ == caption_bubble_model) {
active_model_ = nullptr;
caption_bubble_->SetModel(nullptr);
}
caption_bubble_models_.erase(caption_host_impl);
}
void CaptionBubbleControllerViews::UpdateCaptionStyle(
base::Optional<ui::CaptionStyle> caption_style) {
caption_bubble_->UpdateCaptionStyle(caption_style);
}
void CaptionBubbleControllerViews::SetActiveModel(
CaptionHostImpl* caption_host_impl) {
if (!caption_bubble_models_.count(caption_host_impl)) {
content::WebContents* web_contents = caption_host_impl->GetWebContents();
views::Widget* context_widget =
web_contents ? views::Widget::GetTopLevelWidgetForNativeView(
web_contents->GetNativeView())
: nullptr;
base::Optional<gfx::Rect> context_bounds = base::nullopt;
if (context_widget)
context_bounds = context_widget->GetClientAreaBoundsInScreen();
caption_bubble_models_.emplace(
caption_host_impl,
std::make_unique<CaptionBubbleModel>(
context_bounds,
base::BindRepeating(&CaptionBubbleControllerViews::ActivateContext,
base::Unretained(this), web_contents)));
}
CaptionBubbleModel* caption_bubble_model =
caption_bubble_models_[caption_host_impl].get();
if (active_model_ != caption_bubble_model) {
active_model_ = caption_bubble_model;
caption_bubble_->SetModel(active_model_);
}
}
void CaptionBubbleControllerViews::ActivateContext(
content::WebContents* web_contents) {
if (!web_contents)
return;
// Activate the web contents and the browser window that the web contents is
// in. Order matters: web contents needs to be active in order for the widget
// getter to work.
Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
TabStripModel* tab_strip_model = browser->tab_strip_model();
int index = tab_strip_model->GetIndexOfWebContents(web_contents);
tab_strip_model->ActivateTabAt(index);
views::Widget* context_widget = views::Widget::GetTopLevelWidgetForNativeView(
web_contents->GetNativeView());
if (context_widget)
context_widget->Activate();
}
bool CaptionBubbleControllerViews::IsWidgetVisibleForTesting() {
return caption_widget_ && caption_widget_->IsVisible();
}
std::string CaptionBubbleControllerViews::GetBubbleLabelTextForTesting() {
return caption_bubble_
? base::UTF16ToUTF8(
caption_bubble_->GetLabelForTesting()->GetText()) // IN-TEST
: "";
}
} // namespace captions