[FreezeCast] Add freeze button in cast subpage
In the case that a casting session may be frozen, add a button for pausing / resuming, depending on the current state of the session. Since the pause button and stop button cannot fit together on a line with the device name, put them on a separate line below the device. Ensure the stop button will be aligned with the stop buttons of devices that are not freezable. Pause: https://screenshot.googleplex.com/5PsydzGks7oh2SJ.png Resume: https://screenshot.googleplex.com/ALrYGRQBDWpkqnR.png Bug: b:284516619 Change-Id: I250723afd6c3216f0e1a203c68f6aeded62c8bef Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4571349 Reviewed-by: Takumi Fujimoto <takumif@chromium.org> Reviewed-by: Evan Stade <estade@chromium.org> Commit-Queue: Benjamin Zielinski <bzielinski@google.com> Cr-Commit-Position: refs/heads/main@{#1153372}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
c3301a2ffc
commit
2c2f718aea
ash
@@ -606,9 +606,15 @@ Style notes:
|
|||||||
<message name="IDS_ASH_STATUS_TRAY_CAST_PAUSE" desc="Label for a button in the cast notification to pause the current cast mirroring session to a Chromecast device">
|
<message name="IDS_ASH_STATUS_TRAY_CAST_PAUSE" desc="Label for a button in the cast notification to pause the current cast mirroring session to a Chromecast device">
|
||||||
Pause
|
Pause
|
||||||
</message>
|
</message>
|
||||||
|
<message name="IDS_ASH_STATUS_TRAY_CAST_PAUSE_CASTING" desc="Label for a button in the system tray to pause the current cast mirroring session to a Chromecast device">
|
||||||
|
Pause casting
|
||||||
|
</message>
|
||||||
<message name="IDS_ASH_STATUS_TRAY_CAST_RESUME" desc="Label for a button in the cast notification to resume a paused cast mirroring session to a Chromecast device">
|
<message name="IDS_ASH_STATUS_TRAY_CAST_RESUME" desc="Label for a button in the cast notification to resume a paused cast mirroring session to a Chromecast device">
|
||||||
Resume
|
Resume
|
||||||
</message>
|
</message>
|
||||||
|
<message name="IDS_ASH_STATUS_TRAY_CAST_RESUME_CASTING" desc="Label for a button in the system tray to resume a paused cast mirroring session to a Chromecast device">
|
||||||
|
Resume casting
|
||||||
|
</message>
|
||||||
<message name="IDS_ASH_STATUS_TRAY_QUIET_MODE_TOOLTIP" desc="The tooltip text for the status area icon to tell do-not-disturb mode is currently on.">
|
<message name="IDS_ASH_STATUS_TRAY_QUIET_MODE_TOOLTIP" desc="The tooltip text for the status area icon to tell do-not-disturb mode is currently on.">
|
||||||
Do Not Disturb is on
|
Do Not Disturb is on
|
||||||
</message>
|
</message>
|
||||||
|
@@ -0,0 +1 @@
|
|||||||
|
c8647e60121c1b5a16205154492ba70f83d50d29
|
@@ -0,0 +1 @@
|
|||||||
|
12ca3db87ee67fdfa25fd132901a7f491ef74f29
|
@@ -269,6 +269,8 @@ aggregate_vector_icons("ash_vector_icons") {
|
|||||||
"quick_settings_a11y_sticky_keys.icon",
|
"quick_settings_a11y_sticky_keys.icon",
|
||||||
"quick_settings_cast.icon",
|
"quick_settings_cast.icon",
|
||||||
"quick_settings_cast_connected.icon",
|
"quick_settings_cast_connected.icon",
|
||||||
|
"quick_settings_circle_pause.icon",
|
||||||
|
"quick_settings_circle_play.icon",
|
||||||
"quick_settings_circle_stop.icon",
|
"quick_settings_circle_stop.icon",
|
||||||
"quick_settings_left_arrow.icon",
|
"quick_settings_left_arrow.icon",
|
||||||
"quick_settings_managed.icon",
|
"quick_settings_managed.icon",
|
||||||
|
45
ash/resources/vector_icons/quick_settings_circle_pause.icon
Normal file
45
ash/resources/vector_icons/quick_settings_circle_pause.icon
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
// Copyright 2023 The Chromium Authors
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
CANVAS_DIMENSIONS, 20,
|
||||||
|
MOVE_TO, 7.5f, 13,
|
||||||
|
H_LINE_TO, 9,
|
||||||
|
V_LINE_TO, 7,
|
||||||
|
H_LINE_TO, 7.5f,
|
||||||
|
V_LINE_TO, 13,
|
||||||
|
CLOSE,
|
||||||
|
MOVE_TO, 11, 13,
|
||||||
|
H_LINE_TO, 12.5f,
|
||||||
|
V_LINE_TO, 7,
|
||||||
|
H_LINE_TO, 11,
|
||||||
|
V_LINE_TO, 13,
|
||||||
|
CLOSE,
|
||||||
|
MOVE_TO, 10, 18,
|
||||||
|
CUBIC_TO, 8.9f, 18, 7.87f, 17.79f, 6.9f, 17.38f,
|
||||||
|
CUBIC_TO, 5.92f, 16.96f, 5.07f, 16.39f, 4.33f, 15.67f,
|
||||||
|
CUBIC_TO, 3.61f, 14.93f, 3.04f, 14.08f, 2.63f, 13.1f,
|
||||||
|
CUBIC_TO, 2.21f, 12.13f, 2, 11.1f, 2, 10,
|
||||||
|
CUBIC_TO, 2, 8.89f, 2.21f, 7.85f, 2.63f, 6.9f,
|
||||||
|
CUBIC_TO, 3.04f, 5.92f, 3.61f, 5.08f, 4.33f, 4.35f,
|
||||||
|
CUBIC_TO, 5.07f, 3.62f, 5.92f, 3.04f, 6.9f, 2.63f,
|
||||||
|
CUBIC_TO, 7.87f, 2.21f, 8.9f, 2, 10, 2,
|
||||||
|
CUBIC_TO, 11.11f, 2, 12.15f, 2.21f, 13.1f, 2.63f,
|
||||||
|
CUBIC_TO, 14.08f, 3.04f, 14.92f, 3.62f, 15.65f, 4.35f,
|
||||||
|
CUBIC_TO, 16.38f, 5.08f, 16.96f, 5.92f, 17.38f, 6.9f,
|
||||||
|
CUBIC_TO, 17.79f, 7.85f, 18, 8.89f, 18, 10,
|
||||||
|
CUBIC_TO, 18, 11.1f, 17.79f, 12.13f, 17.38f, 13.1f,
|
||||||
|
CUBIC_TO, 16.96f, 14.08f, 16.38f, 14.93f, 15.65f, 15.67f,
|
||||||
|
CUBIC_TO, 14.92f, 16.39f, 14.08f, 16.96f, 13.1f, 17.38f,
|
||||||
|
CUBIC_TO, 12.15f, 17.79f, 11.11f, 18, 10, 18,
|
||||||
|
CLOSE,
|
||||||
|
MOVE_TO, 10, 16.5f,
|
||||||
|
CUBIC_TO, 11.81f, 16.5f, 13.34f, 15.87f, 14.6f, 14.6f,
|
||||||
|
CUBIC_TO, 15.87f, 13.34f, 16.5f, 11.81f, 16.5f, 10,
|
||||||
|
CUBIC_TO, 16.5f, 8.19f, 15.87f, 6.66f, 14.6f, 5.4f,
|
||||||
|
CUBIC_TO, 13.34f, 4.13f, 11.81f, 3.5f, 10, 3.5f,
|
||||||
|
CUBIC_TO, 8.19f, 3.5f, 6.66f, 4.13f, 5.4f, 5.4f,
|
||||||
|
CUBIC_TO, 4.13f, 6.66f, 3.5f, 8.19f, 3.5f, 10,
|
||||||
|
CUBIC_TO, 3.5f, 11.81f, 4.13f, 13.34f, 5.4f, 14.6f,
|
||||||
|
CUBIC_TO, 6.66f, 15.87f, 8.19f, 16.5f, 10, 16.5f,
|
||||||
|
CLOSE
|
38
ash/resources/vector_icons/quick_settings_circle_play.icon
Normal file
38
ash/resources/vector_icons/quick_settings_circle_play.icon
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright 2023 The Chromium Authors
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
CANVAS_DIMENSIONS, 20,
|
||||||
|
MOVE_TO, 8, 13.5f,
|
||||||
|
LINE_TO, 13.5f, 10,
|
||||||
|
LINE_TO, 8, 6.5f,
|
||||||
|
V_LINE_TO, 13.5f,
|
||||||
|
CLOSE,
|
||||||
|
MOVE_TO, 10, 18,
|
||||||
|
CUBIC_TO, 8.9f, 18, 7.87f, 17.79f, 6.9f, 17.38f,
|
||||||
|
CUBIC_TO, 5.92f, 16.96f, 5.07f, 16.39f, 4.33f, 15.67f,
|
||||||
|
CUBIC_TO, 3.61f, 14.93f, 3.04f, 14.08f, 2.63f, 13.1f,
|
||||||
|
CUBIC_TO, 2.21f, 12.13f, 2, 11.1f, 2, 10,
|
||||||
|
CUBIC_TO, 2, 8.89f, 2.21f, 7.85f, 2.63f, 6.9f,
|
||||||
|
CUBIC_TO, 3.04f, 5.92f, 3.61f, 5.08f, 4.33f, 4.35f,
|
||||||
|
CUBIC_TO, 5.07f, 3.62f, 5.92f, 3.04f, 6.9f, 2.63f,
|
||||||
|
CUBIC_TO, 7.87f, 2.21f, 8.9f, 2, 10, 2,
|
||||||
|
CUBIC_TO, 11.11f, 2, 12.15f, 2.21f, 13.1f, 2.63f,
|
||||||
|
CUBIC_TO, 14.08f, 3.04f, 14.92f, 3.62f, 15.65f, 4.35f,
|
||||||
|
CUBIC_TO, 16.38f, 5.08f, 16.96f, 5.92f, 17.38f, 6.9f,
|
||||||
|
CUBIC_TO, 17.79f, 7.85f, 18, 8.89f, 18, 10,
|
||||||
|
CUBIC_TO, 18, 11.1f, 17.79f, 12.13f, 17.38f, 13.1f,
|
||||||
|
CUBIC_TO, 16.96f, 14.08f, 16.38f, 14.93f, 15.65f, 15.67f,
|
||||||
|
CUBIC_TO, 14.92f, 16.39f, 14.08f, 16.96f, 13.1f, 17.38f,
|
||||||
|
CUBIC_TO, 12.15f, 17.79f, 11.11f, 18, 10, 18,
|
||||||
|
CLOSE,
|
||||||
|
MOVE_TO, 10, 16.5f,
|
||||||
|
CUBIC_TO, 11.81f, 16.5f, 13.34f, 15.87f, 14.6f, 14.6f,
|
||||||
|
CUBIC_TO, 15.87f, 13.34f, 16.5f, 11.81f, 16.5f, 10,
|
||||||
|
CUBIC_TO, 16.5f, 8.19f, 15.87f, 6.66f, 14.6f, 5.4f,
|
||||||
|
CUBIC_TO, 13.34f, 4.13f, 11.81f, 3.5f, 10, 3.5f,
|
||||||
|
CUBIC_TO, 8.19f, 3.5f, 6.66f, 4.13f, 5.4f, 5.4f,
|
||||||
|
CUBIC_TO, 4.13f, 6.66f, 3.5f, 8.19f, 3.5f, 10,
|
||||||
|
CUBIC_TO, 3.5f, 11.81f, 4.13f, 13.34f, 5.4f, 14.6f,
|
||||||
|
CUBIC_TO, 6.66f, 15.87f, 8.19f, 16.5f, 10, 16.5f,
|
||||||
|
CLOSE
|
@@ -35,11 +35,15 @@
|
|||||||
#include "ui/views/border.h"
|
#include "ui/views/border.h"
|
||||||
#include "ui/views/controls/scroll_view.h"
|
#include "ui/views/controls/scroll_view.h"
|
||||||
#include "ui/views/layout/box_layout.h"
|
#include "ui/views/layout/box_layout.h"
|
||||||
|
#include "ui/views/view_class_properties.h"
|
||||||
|
|
||||||
namespace ash {
|
namespace ash {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
// Extra spacing to add between cast stop buttons and the edge of the qs tray.
|
||||||
|
constexpr int kStopButtonExtraMargin = 4;
|
||||||
|
|
||||||
// Returns the correct vector icon for |icon_type|. Some types may be different
|
// Returns the correct vector icon for |icon_type|. Some types may be different
|
||||||
// for branded builds.
|
// for branded builds.
|
||||||
const gfx::VectorIcon& SinkIconTypeToIcon(SinkIconType icon_type) {
|
const gfx::VectorIcon& SinkIconTypeToIcon(SinkIconType icon_type) {
|
||||||
@@ -65,6 +69,21 @@ const gfx::VectorIcon& SinkIconTypeToIcon(SinkIconType icon_type) {
|
|||||||
return kSystemMenuCastGenericIcon;
|
return kSystemMenuCastGenericIcon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<views::View> MakeButtonContainer() {
|
||||||
|
std::unique_ptr<views::View> button_container =
|
||||||
|
std::make_unique<views::View>();
|
||||||
|
views::BoxLayout* manager =
|
||||||
|
button_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
|
||||||
|
views::BoxLayout::Orientation::kHorizontal));
|
||||||
|
manager->set_main_axis_alignment(views::BoxLayout::MainAxisAlignment::kEnd);
|
||||||
|
manager->set_between_child_spacing(kTrayPopupLabelRightPadding);
|
||||||
|
button_container->SetProperty(
|
||||||
|
views::kMarginsKey,
|
||||||
|
gfx::Insets::TLBR(0, 0, 0,
|
||||||
|
kStopButtonExtraMargin + kQsExtraMarginsFromRightEdge));
|
||||||
|
return button_container;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
CastDetailedView::CastDetailedView(DetailedViewDelegate* delegate)
|
CastDetailedView::CastDetailedView(DetailedViewDelegate* delegate)
|
||||||
@@ -85,40 +104,18 @@ void CastDetailedView::CreateItems() {
|
|||||||
|
|
||||||
void CastDetailedView::OnDevicesUpdated(
|
void CastDetailedView::OnDevicesUpdated(
|
||||||
const std::vector<SinkAndRoute>& sinks_routes) {
|
const std::vector<SinkAndRoute>& sinks_routes) {
|
||||||
|
sinks_and_routes_.clear();
|
||||||
// Add/update existing.
|
// Add/update existing.
|
||||||
for (const auto& device : sinks_routes)
|
for (const auto& device : sinks_routes)
|
||||||
sinks_and_routes_.insert(std::make_pair(device.sink.id, device));
|
sinks_and_routes_.insert(std::make_pair(device.sink.id, device));
|
||||||
|
|
||||||
// Remove non-existent sinks. Removing an element invalidates all existing
|
|
||||||
// iterators.
|
|
||||||
auto iter = sinks_and_routes_.begin();
|
|
||||||
while (iter != sinks_and_routes_.end()) {
|
|
||||||
bool has_receiver = false;
|
|
||||||
for (auto& receiver : sinks_routes) {
|
|
||||||
if (iter->first == receiver.sink.id)
|
|
||||||
has_receiver = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (has_receiver)
|
|
||||||
++iter;
|
|
||||||
else
|
|
||||||
iter = sinks_and_routes_.erase(iter);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update UI.
|
// Update UI.
|
||||||
UpdateReceiverListFromCachedData();
|
UpdateReceiverListFromCachedData();
|
||||||
Layout();
|
Layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CastDetailedView::UpdateReceiverListFromCachedData() {
|
void CastDetailedView::UpdateReceiverListFromCachedData() {
|
||||||
// Remove all of the existing views.
|
RemoveAllViews();
|
||||||
view_to_sink_map_.clear();
|
|
||||||
scroll_content()->RemoveAllChildViews();
|
|
||||||
add_access_code_device_ = nullptr;
|
|
||||||
if (zero_state_view_) {
|
|
||||||
RemoveChildViewT(zero_state_view_.get());
|
|
||||||
zero_state_view_ = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// QsRevamp places items in a rounded container.
|
// QsRevamp places items in a rounded container.
|
||||||
const bool is_qs_revamp_enabled = features::IsQsRevampEnabled();
|
const bool is_qs_revamp_enabled = features::IsQsRevampEnabled();
|
||||||
@@ -130,18 +127,7 @@ void CastDetailedView::UpdateReceiverListFromCachedData() {
|
|||||||
// Per product requirement, access code receiver should be shown before other
|
// Per product requirement, access code receiver should be shown before other
|
||||||
// receivers.
|
// receivers.
|
||||||
if (CastConfigController::Get()->AccessCodeCastingEnabled()) {
|
if (CastConfigController::Get()->AccessCodeCastingEnabled()) {
|
||||||
add_access_code_device_ = AddScrollListItem(
|
AddAccessCodeCastButton(item_container);
|
||||||
item_container, vector_icons::kKeyboardIcon,
|
|
||||||
l10n_util::GetStringUTF16(
|
|
||||||
IDS_ASH_STATUS_TRAY_CAST_ACCESS_CODE_CAST_CONNECT));
|
|
||||||
if (chromeos::features::IsJellyEnabled()) {
|
|
||||||
// `views::ImageView` does not support changing the color, so set the
|
|
||||||
// image with an updated `ui::ImageModel`.
|
|
||||||
add_access_code_device_->icon()->SetImage(ui::ImageModel::FromVectorIcon(
|
|
||||||
vector_icons::kKeyboardIcon, cros_tokens::kCrosSysPrimary));
|
|
||||||
add_access_code_device_->text_label()->SetEnabledColorId(
|
|
||||||
cros_tokens::kCrosSysPrimary);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a view for each receiver.
|
// Add a view for each receiver.
|
||||||
@@ -153,20 +139,10 @@ void CastDetailedView::UpdateReceiverListFromCachedData() {
|
|||||||
base::UTF8ToUTF16(sink.name));
|
base::UTF8ToUTF16(sink.name));
|
||||||
view_to_sink_map_[container] = sink.id;
|
view_to_sink_map_[container] = sink.id;
|
||||||
|
|
||||||
// Add a stop casting button if this machine ("local source") is casting to
|
// Add receiver action buttons if this machine ("local source") is casting
|
||||||
// the device. See also CastNotificationController::OnDevicesUpdated().
|
// to the device. See also CastNotificationController::OnDevicesUpdated().
|
||||||
if (is_qs_revamp_enabled && !route.id.empty() && route.is_local_source) {
|
if (is_qs_revamp_enabled && !route.id.empty() && route.is_local_source) {
|
||||||
auto button = std::make_unique<PillButton>(
|
AddReceiverActionButtons(sink, route, container, item_container);
|
||||||
base::BindRepeating(&CastDetailedView::StopCasting,
|
|
||||||
base::Unretained(this), route.id),
|
|
||||||
l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_CAST_STOP_CASTING),
|
|
||||||
PillButton::kDefaultWithIconLeading, &kQuickSettingsCircleStopIcon);
|
|
||||||
button->SetBackgroundColorId(cros_tokens::kCrosSysErrorContainer);
|
|
||||||
button->SetIconColorId(cros_tokens::kCrosSysError);
|
|
||||||
button->SetButtonTextColorId(cros_tokens::kCrosSysError);
|
|
||||||
container->AddRightView(
|
|
||||||
button.release(),
|
|
||||||
views::CreateEmptyBorder(gfx::Insets::TLBR(0, 0, 0, 4)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,6 +197,102 @@ void CastDetailedView::StopCasting(const std::string& route_id) {
|
|||||||
CloseBubble(); // Deletes `this`.
|
CloseBubble(); // Deletes `this`.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CastDetailedView::FreezePressed(const std::string& route_id,
|
||||||
|
bool is_frozen) {
|
||||||
|
DCHECK(features::IsQsRevampEnabled());
|
||||||
|
if (is_frozen) {
|
||||||
|
CastConfigController::Get()->UnfreezeRoute(route_id);
|
||||||
|
} else {
|
||||||
|
CastConfigController::Get()->FreezeRoute(route_id);
|
||||||
|
CloseBubble();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CastDetailedView::RemoveAllViews() {
|
||||||
|
view_to_sink_map_.clear();
|
||||||
|
sink_extra_views_map_.clear();
|
||||||
|
scroll_content()->RemoveAllChildViews();
|
||||||
|
add_access_code_device_ = nullptr;
|
||||||
|
if (zero_state_view_) {
|
||||||
|
RemoveChildViewT(zero_state_view_.get());
|
||||||
|
zero_state_view_ = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CastDetailedView::AddAccessCodeCastButton(
|
||||||
|
views::View* receiver_list_view) {
|
||||||
|
add_access_code_device_ =
|
||||||
|
AddScrollListItem(receiver_list_view, vector_icons::kKeyboardIcon,
|
||||||
|
l10n_util::GetStringUTF16(
|
||||||
|
IDS_ASH_STATUS_TRAY_CAST_ACCESS_CODE_CAST_CONNECT));
|
||||||
|
if (chromeos::features::IsJellyEnabled()) {
|
||||||
|
// `views::ImageView` does not support changing the color, so set the
|
||||||
|
// image with an updated `ui::ImageModel`.
|
||||||
|
add_access_code_device_->icon()->SetImage(ui::ImageModel::FromVectorIcon(
|
||||||
|
vector_icons::kKeyboardIcon, cros_tokens::kCrosSysPrimary));
|
||||||
|
add_access_code_device_->text_label()->SetEnabledColorId(
|
||||||
|
cros_tokens::kCrosSysPrimary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CastDetailedView::AddReceiverActionButtons(
|
||||||
|
const CastSink& sink,
|
||||||
|
const CastRoute& route,
|
||||||
|
HoverHighlightView* receiver_view,
|
||||||
|
views::View* receiver_list_view) {
|
||||||
|
std::unique_ptr<PillButton> stop_button = CreateStopButton(route);
|
||||||
|
|
||||||
|
// In the case that we want to show a pause/resume button, then we must
|
||||||
|
// put both buttons on a row below the cast sink.
|
||||||
|
if (route.freeze_info.can_freeze) {
|
||||||
|
std::unique_ptr<PillButton> freeze_button = CreateFreezeButton(route);
|
||||||
|
std::unique_ptr<views::View> button_container = MakeButtonContainer();
|
||||||
|
std::vector<views::View*> extra_views;
|
||||||
|
extra_views.emplace_back(
|
||||||
|
button_container->AddChildView(std::move(freeze_button)));
|
||||||
|
extra_views.emplace_back(
|
||||||
|
button_container->AddChildView(std::move(stop_button)));
|
||||||
|
sink_extra_views_map_[sink.id] = extra_views;
|
||||||
|
|
||||||
|
// Add the button container directly as a new row in the list of cast
|
||||||
|
// devices. Since the associated device was just added, the buttons will
|
||||||
|
// show up correctly below their associated device.
|
||||||
|
receiver_list_view->AddChildView(std::move(button_container));
|
||||||
|
} else {
|
||||||
|
receiver_view->AddRightView(stop_button.release(),
|
||||||
|
views::CreateEmptyBorder(gfx::Insets::TLBR(
|
||||||
|
0, 0, 0, kStopButtonExtraMargin)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<PillButton> CastDetailedView::CreateStopButton(
|
||||||
|
const CastRoute& route) {
|
||||||
|
std::unique_ptr<PillButton> stop_button = std::make_unique<PillButton>(
|
||||||
|
base::BindRepeating(&CastDetailedView::StopCasting,
|
||||||
|
base::Unretained(this), route.id),
|
||||||
|
l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_CAST_STOP_CASTING),
|
||||||
|
PillButton::kDefaultWithIconLeading, &kQuickSettingsCircleStopIcon);
|
||||||
|
stop_button->SetBackgroundColorId(cros_tokens::kCrosSysErrorContainer);
|
||||||
|
stop_button->SetIconColorId(cros_tokens::kCrosSysError);
|
||||||
|
stop_button->SetButtonTextColorId(cros_tokens::kCrosSysError);
|
||||||
|
return stop_button;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<PillButton> CastDetailedView::CreateFreezeButton(
|
||||||
|
const CastRoute& route) {
|
||||||
|
std::unique_ptr<PillButton> freeze_button = std::make_unique<PillButton>(
|
||||||
|
base::BindRepeating(&CastDetailedView::FreezePressed,
|
||||||
|
base::Unretained(this), route.id,
|
||||||
|
route.freeze_info.is_frozen),
|
||||||
|
route.freeze_info.is_frozen
|
||||||
|
? l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_CAST_RESUME_CASTING)
|
||||||
|
: l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_CAST_PAUSE_CASTING),
|
||||||
|
PillButton::kSecondaryWithIconLeading,
|
||||||
|
route.freeze_info.is_frozen ? &kQuickSettingsCirclePlayIcon
|
||||||
|
: &kQuickSettingsCirclePauseIcon);
|
||||||
|
return freeze_button;
|
||||||
|
}
|
||||||
|
|
||||||
BEGIN_METADATA(CastDetailedView, TrayDetailedView)
|
BEGIN_METADATA(CastDetailedView, TrayDetailedView)
|
||||||
END_METADATA
|
END_METADATA
|
||||||
|
|
||||||
|
@@ -22,6 +22,7 @@ class View;
|
|||||||
namespace ash {
|
namespace ash {
|
||||||
|
|
||||||
class HoverHighlightView;
|
class HoverHighlightView;
|
||||||
|
class PillButton;
|
||||||
|
|
||||||
// This view displays a list of cast receivers that can be clicked on and casted
|
// This view displays a list of cast receivers that can be clicked on and casted
|
||||||
// to. It is activated by clicking on the chevron inside of
|
// to. It is activated by clicking on the chevron inside of
|
||||||
@@ -61,12 +62,38 @@ class ASH_EXPORT CastDetailedView : public TrayDetailedView,
|
|||||||
// Stops casting the route identified by `route_id`.
|
// Stops casting the route identified by `route_id`.
|
||||||
void StopCasting(const std::string& route_id);
|
void StopCasting(const std::string& route_id);
|
||||||
|
|
||||||
|
// Pauses or resumes the route given by `route_id`.
|
||||||
|
void FreezePressed(const std::string& route_id, bool is_frozen);
|
||||||
|
|
||||||
|
// Remove all child views from CastDetailedView.
|
||||||
|
void RemoveAllViews();
|
||||||
|
|
||||||
|
// Adds a button which allows for adding a device using an access code.
|
||||||
|
void AddAccessCodeCastButton(views::View* receiver_list_view);
|
||||||
|
|
||||||
|
// Adds buttons associated with a receiver so the user may perform route
|
||||||
|
// actions like stopping or pausing.
|
||||||
|
void AddReceiverActionButtons(const CastSink& sink,
|
||||||
|
const CastRoute& route,
|
||||||
|
HoverHighlightView* receiver_view,
|
||||||
|
views::View* receiver_list_view);
|
||||||
|
|
||||||
|
// Creates a stop button which, when pressed, stops the associated `route`.
|
||||||
|
std::unique_ptr<PillButton> CreateStopButton(const CastRoute& route);
|
||||||
|
|
||||||
|
// Creates a freeze button which, when pressed, pauses / resumes the
|
||||||
|
// associated `route`.
|
||||||
|
std::unique_ptr<PillButton> CreateFreezeButton(const CastRoute& route);
|
||||||
|
|
||||||
// A mapping from the sink id to the receiver/activity data.
|
// A mapping from the sink id to the receiver/activity data.
|
||||||
std::map<std::string, SinkAndRoute> sinks_and_routes_;
|
std::map<std::string, SinkAndRoute> sinks_and_routes_;
|
||||||
|
|
||||||
// A mapping from the view pointer to the associated activity sink id.
|
// A mapping from the view pointer to the associated activity sink id.
|
||||||
std::map<views::View*, std::string> view_to_sink_map_;
|
std::map<views::View*, std::string> view_to_sink_map_;
|
||||||
|
|
||||||
|
// A mapping of sink id to the associated extra views.
|
||||||
|
std::map<std::string, std::vector<views::View*>> sink_extra_views_map_;
|
||||||
|
|
||||||
// Special list item that, if clicked, launches the access code casting dialog
|
// Special list item that, if clicked, launches the access code casting dialog
|
||||||
raw_ptr<HoverHighlightView, ExperimentalAsh> add_access_code_device_ =
|
raw_ptr<HoverHighlightView, ExperimentalAsh> add_access_code_device_ =
|
||||||
nullptr;
|
nullptr;
|
||||||
|
@@ -57,6 +57,10 @@ class CastDetailedViewTest : public AshTestBase {
|
|||||||
return views;
|
return views;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<views::View*> GetExtraViewsForSink(const std::string& sink_id) {
|
||||||
|
return detailed_view_->sink_extra_views_map_[sink_id];
|
||||||
|
}
|
||||||
|
|
||||||
views::View* GetZeroStateView() { return detailed_view_->zero_state_view_; }
|
views::View* GetZeroStateView() { return detailed_view_->zero_state_view_; }
|
||||||
|
|
||||||
// Adds two simulated cast devices.
|
// Adds two simulated cast devices.
|
||||||
@@ -226,4 +230,33 @@ TEST_F(CastDetailedViewTest, NoStopCastingButtonForNonLocalSource) {
|
|||||||
EXPECT_FALSE(row->right_view());
|
EXPECT_FALSE(row->right_view());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(CastDetailedViewTest, FreezeButton) {
|
||||||
|
// Set up a fake sink and route, as if this Chromebook is casting to the
|
||||||
|
// device. And, the route may be frozen.
|
||||||
|
std::vector<SinkAndRoute> devices;
|
||||||
|
SinkAndRoute device;
|
||||||
|
device.sink.id = "fake_sink_id_1";
|
||||||
|
device.sink.name = "Sink Name 1";
|
||||||
|
device.sink.sink_icon_type = SinkIconType::kCast;
|
||||||
|
device.route.id = "fake_route_id_1";
|
||||||
|
device.route.title = "Title 1";
|
||||||
|
// Simulate a local source (this Chromebook).
|
||||||
|
device.route.is_local_source = true;
|
||||||
|
device.route.freeze_info.can_freeze = true;
|
||||||
|
devices.push_back(device);
|
||||||
|
OnDevicesUpdated(devices);
|
||||||
|
|
||||||
|
std::vector<views::View*> views = GetExtraViewsForSink("fake_sink_id_1");
|
||||||
|
ASSERT_EQ(views.size(), 2u);
|
||||||
|
auto* freeze_button = views[0];
|
||||||
|
EXPECT_TRUE(views::IsViewClass<PillButton>(freeze_button));
|
||||||
|
EXPECT_EQ(freeze_button->GetTooltipText(gfx::Point()), u"Pause casting");
|
||||||
|
|
||||||
|
// Clicking on the button pauses casting.
|
||||||
|
LeftClickOn(freeze_button);
|
||||||
|
EXPECT_EQ(cast_config_.freeze_route_count(), 1u);
|
||||||
|
EXPECT_EQ(cast_config_.freeze_route_route_id(), "fake_route_id_1");
|
||||||
|
EXPECT_EQ(delegate_->close_bubble_call_count(), 1u);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ash
|
} // namespace ash
|
||||||
|
Reference in New Issue
Block a user