blob: 9eb757bb0b7d66268e9411ee07dd79aaa5a694af [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// 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/media_router/cast_toolbar_button.h"
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "chrome/browser/media/router/discovery/access_code/access_code_cast_feature.h"
#include "chrome/browser/themes/theme_properties.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/color/chrome_color_id.h"
#include "chrome/browser/ui/layout_constants.h"
#include "chrome/browser/ui/media_router/media_router_ui_service.h"
#include "chrome/grit/generated_resources.h"
#include "components/media_router/browser/media_router.h"
#include "components/media_router/browser/media_router_dialog_controller.h"
#include "components/media_router/browser/media_router_factory.h"
#include "components/media_router/browser/media_router_metrics.h"
#include "components/vector_icons/vector_icons.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/models/image_model.h"
#include "ui/base/models/menu_model.h"
#include "ui/base/pointer/touch_ui_controller.h"
#include "ui/base/theme_provider.h"
#include "ui/color/color_id.h"
#include "ui/color/color_provider.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/gfx/vector_icon_types.h"
#include "ui/views/animation/ink_drop.h"
#include "ui/views/controls/button/button_controller.h"
namespace media_router {
namespace {
constexpr char kLoggerComponent[] = "CastToolbarButton";
}
// static
std::unique_ptr<CastToolbarButton> CastToolbarButton::Create(Browser* browser) {
// These objects may be null in tests.
if (!MediaRouterUIService::Get(browser->profile()) ||
!MediaRouterFactory::GetApiForBrowserContext(browser->profile())) {
return nullptr;
}
std::unique_ptr<MediaRouterContextualMenu> context_menu =
MediaRouterContextualMenu::Create(
browser,
MediaRouterUIService::Get(browser->profile())->action_controller());
return std::make_unique<CastToolbarButton>(
browser, MediaRouterFactory::GetApiForBrowserContext(browser->profile()),
std::move(context_menu));
}
CastToolbarButton::CastToolbarButton(
Browser* browser,
MediaRouter* media_router,
std::unique_ptr<MediaRouterContextualMenu> context_menu)
: ToolbarButton(base::BindRepeating(&CastToolbarButton::ButtonPressed,
base::Unretained(this)),
context_menu->CreateMenuModel(),
/** tab_strip_model*/ nullptr,
/** trigger_menu_on_long_press */ false),
IssuesObserver(media_router->GetIssueManager()),
MediaRoutesObserver(media_router),
browser_(browser),
profile_(browser_->profile()),
context_menu_(std::move(context_menu)),
logger_(media_router->GetLogger()) {
button_controller()->set_notify_action(
views::ButtonController::NotifyAction::kOnPress);
SetFlipCanvasOnPaintForRTLUI(false);
SetTooltipText(l10n_util::GetStringUTF16(IDS_MEDIA_ROUTER_ICON_TOOLTIP_TEXT));
IssuesObserver::Init();
DCHECK(GetActionController());
GetActionController()->AddObserver(this);
SetVisible(GetActionController()->ShouldEnableAction());
}
CastToolbarButton::~CastToolbarButton() {
StopObservingMirroringMediaControllerHosts();
if (GetActionController())
GetActionController()->RemoveObserver(this);
}
void CastToolbarButton::ShowIcon() {
SetVisible(true);
PreferredSizeChanged();
}
void CastToolbarButton::HideIcon() {
SetVisible(false);
PreferredSizeChanged();
}
void CastToolbarButton::ActivateIcon() {
views::InkDrop::Get(this)->AnimateToState(views::InkDropState::ACTIVATED,
nullptr);
}
void CastToolbarButton::DeactivateIcon() {
views::InkDrop::Get(this)->AnimateToState(views::InkDropState::DEACTIVATED,
nullptr);
}
void CastToolbarButton::OnIssue(const media_router::Issue& issue) {
current_issue_ = std::make_unique<media_router::IssueInfo>(issue.info());
UpdateIcon();
}
void CastToolbarButton::OnIssuesCleared() {
if (current_issue_)
current_issue_.reset();
UpdateIcon();
}
void CastToolbarButton::OnRoutesUpdated(
const std::vector<media_router::MediaRoute>& routes) {
has_local_route_ =
base::Contains(routes, true, &media_router::MediaRoute::is_local);
StopObservingMirroringMediaControllerHosts();
for (const auto& route : routes) {
const auto& route_id = route.media_route_id();
MirroringMediaControllerHost* mirroring_controller_host =
MediaRouterFactory::GetApiForBrowserContext(profile_)
->GetMirroringMediaControllerHost(route_id);
if (mirroring_controller_host) {
mirroring_controller_host->AddObserver(this);
tracked_mirroring_routes_.emplace_back(route_id);
}
}
UpdateIcon();
}
void CastToolbarButton::OnFreezeInfoChanged() {
UpdateIcon();
}
bool CastToolbarButton::OnMousePressed(const ui::MouseEvent& event) {
if (event.IsRightMouseButton() && GetActionController())
GetActionController()->KeepIconShownOnPressed();
return ToolbarButton::OnMousePressed(event);
}
void CastToolbarButton::OnMouseReleased(const ui::MouseEvent& event) {
ToolbarButton::OnMouseReleased(event);
if (event.IsRightMouseButton() && GetActionController())
GetActionController()->MaybeHideIconOnReleased();
}
void CastToolbarButton::OnGestureEvent(ui::GestureEvent* event) {
switch (event->type()) {
case ui::ET_GESTURE_TAP_DOWN:
GetActionController()->KeepIconShownOnPressed();
break;
case ui::ET_GESTURE_END:
case ui::ET_GESTURE_TAP_CANCEL:
GetActionController()->MaybeHideIconOnReleased();
break;
default:
break;
}
ToolbarButton::OnGestureEvent(event);
}
void CastToolbarButton::OnThemeChanged() {
ToolbarButton::OnThemeChanged();
UpdateIcon();
}
void CastToolbarButton::UpdateIcon() {
if (!GetWidget())
return;
using Severity = media_router::IssueInfo::Severity;
const auto severity =
current_issue_ ? current_issue_->severity : Severity::NOTIFICATION;
bool is_frozen = false;
for (const auto& route_id : tracked_mirroring_routes_) {
MirroringMediaControllerHost* mirroring_controller_host =
MediaRouterFactory::GetApiForBrowserContext(profile_)
->GetMirroringMediaControllerHost(route_id);
if (mirroring_controller_host) {
is_frozen = is_frozen || mirroring_controller_host->IsFrozen();
}
}
const gfx::VectorIcon* new_icon = nullptr;
SkColor icon_color;
const auto* const color_provider = GetColorProvider();
if (severity == Severity::NOTIFICATION && !has_local_route_) {
new_icon = ShouldShowNewIcons()
? &vector_icons::kMediaRouterIdleChromeRefreshIcon
: &vector_icons::kMediaRouterIdleIcon;
icon_color = gfx::kPlaceholderColor;
} else if (severity == Severity::WARNING) {
new_icon = ShouldShowNewIcons()
? &vector_icons::kMediaRouterWarningChromeRefreshIcon
: &vector_icons::kMediaRouterWarningIcon;
icon_color = ShouldShowNewIcons()
? gfx::kPlaceholderColor
: color_provider->GetColor(kColorMediaRouterIconWarning);
} else if (is_frozen && ShouldShowNewIcons()) {
new_icon = &vector_icons::kMediaRouterPausedIcon;
icon_color = gfx::kPlaceholderColor;
} else {
new_icon = ShouldShowNewIcons()
? &vector_icons::kMediaRouterActiveChromeRefreshIcon
: &vector_icons::kMediaRouterActiveIcon;
icon_color = color_provider->GetColor(kColorMediaRouterIconActive);
}
// This function is called when system theme changes. If an idle icon is
// present, its color needs update.
if (icon_color == gfx::kPlaceholderColor) {
for (auto state : kButtonStates) {
SetImageModel(state, ui::ImageModel::FromVectorIcon(
*new_icon, GetForegroundColor(state)));
}
}
if (icon_ == new_icon)
return;
icon_ = new_icon;
LogIconChange(icon_);
if (icon_color != gfx::kPlaceholderColor) {
for (auto state : kButtonStates)
SetImageModel(state, ui::ImageModel::FromVectorIcon(*icon_, icon_color));
}
UpdateLayoutInsetDelta();
}
MediaRouterActionController* CastToolbarButton::GetActionController() const {
return MediaRouterUIService::Get(profile_)->action_controller();
}
void CastToolbarButton::UpdateLayoutInsetDelta() {
// This icon is smaller than the touchable-UI expected 24dp, so we need to pad
// the insets to match.
SetLayoutInsetDelta(
gfx::Insets(ui::TouchUiController::Get()->touch_ui() ? 4 : 0));
}
void CastToolbarButton::ButtonPressed() {
MediaRouterDialogController* dialog_controller =
MediaRouterDialogController::GetOrCreateForWebContents(
browser_->tab_strip_model()->GetActiveWebContents());
if (dialog_controller->IsShowingMediaRouterDialog()) {
dialog_controller->HideMediaRouterDialog();
} else {
dialog_controller->ShowMediaRouterDialog(
MediaRouterDialogActivationLocation::TOOLBAR);
}
}
void CastToolbarButton::LogIconChange(const gfx::VectorIcon* icon) {
if (icon_ == (ShouldShowNewIcons()
? &vector_icons::kMediaRouterIdleChromeRefreshIcon
: &vector_icons::kMediaRouterIdleIcon)) {
logger_->LogInfo(
mojom::LogCategory::kUi, kLoggerComponent,
"Cast toolbar icon indicates no active session nor issues.", "", "",
"");
} else if (icon_ == &vector_icons::kMediaRouterErrorIcon) {
logger_->LogInfo(mojom::LogCategory::kUi, kLoggerComponent,
"Cast toolbar icon shows a fatal issue.", "", "", "");
} else if (icon_ == (ShouldShowNewIcons()
? &vector_icons::kMediaRouterWarningChromeRefreshIcon
: &vector_icons::kMediaRouterWarningIcon)) {
logger_->LogInfo(mojom::LogCategory::kUi, kLoggerComponent,
"Cast toolbar icon shows a warning issue.", "", "", "");
} else if (icon_ == &vector_icons::kMediaRouterPausedIcon) {
logger_->LogInfo(
mojom::LogCategory::kUi, kLoggerComponent,
"Cast toolbar icon indicated there is a paused mirroring session.", "",
"", "");
} else {
CHECK_EQ(icon_, ShouldShowNewIcons()
? &vector_icons::kMediaRouterActiveChromeRefreshIcon
: &vector_icons::kMediaRouterActiveIcon);
logger_->LogInfo(mojom::LogCategory::kUi, kLoggerComponent,
"Cast toolbar icon is blue, indicating an active session.",
"", "", "");
}
}
void CastToolbarButton::StopObservingMirroringMediaControllerHosts() {
for (const auto& route_id : tracked_mirroring_routes_) {
media_router::MirroringMediaControllerHost* mirroring_controller_host =
MediaRouterFactory::GetApiForBrowserContext(profile_)
->GetMirroringMediaControllerHost(route_id);
if (mirroring_controller_host) {
mirroring_controller_host->RemoveObserver(this);
}
}
tracked_mirroring_routes_.clear();
}
bool CastToolbarButton::ShouldShowNewIcons() {
return features::IsChromeRefresh2023() ||
IsAccessCodeCastFreezeUiEnabled(profile_);
}
BEGIN_METADATA(CastToolbarButton, ToolbarButton)
END_METADATA
} // namespace media_router