Polish quick settings Cast tile behavior
When casting: - Toggle the tile - Set the label to "Casting screen", "Casting tab", or "Casting" depending on the source type - Set the sub-label to the cast target name (e.g. "Sony TV") When not casting keep the "Cast screen / Devices available" behavior. https://screenshot.googleplex.com/BLgLAPAUrstvJPi https://screenshot.googleplex.com/wCstR4LW4ixbMJP Bug: b:268575073 Test: added to ash_unittests Change-Id: I0af057f4f29c984cb4225166f5335c73ec69a249 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4492807 Reviewed-by: Jiaming Cheng <jiamingc@chromium.org> Commit-Queue: James Cook <jamescook@chromium.org> Cr-Commit-Position: refs/heads/main@{#1137445}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
6e848fd8ab
commit
58e1429301
@ -498,6 +498,15 @@ Style notes:
|
||||
<message name="IDS_ASH_STATUS_TRAY_CAST_TOOLTIP" desc="The tooltip text used for Cast button in system tray bubble.">
|
||||
Show cast devices
|
||||
</message>
|
||||
<message name="IDS_ASH_STATUS_TRAY_CASTING" desc="The label used in the system tray bubble when casting a generic source to a Chromecast device">
|
||||
Casting
|
||||
</message>
|
||||
<message name="IDS_ASH_STATUS_TRAY_CASTING_SCREEN" desc="The label used in the system tray bubble when casting the screen to a Chromecast device">
|
||||
Casting screen
|
||||
</message>
|
||||
<message name="IDS_ASH_STATUS_TRAY_CASTING_TAB" desc="The label used in the system tray bubble when casting a Chrome tab to a Chromecast device">
|
||||
Casting tab
|
||||
</message>
|
||||
<message name="IDS_ASH_STATUS_TRAY_CAST_NOTIFICATION_TITLE" desc="The notification title to tell the user we are casting to a cast device.">
|
||||
Casting to <ph name="RECEIVER_NAME">$1<ex>Living Room</ex></ph>
|
||||
</message>
|
||||
|
1
ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CASTING.png.sha1
Normal file
1
ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CASTING.png.sha1
Normal file
@ -0,0 +1 @@
|
||||
4b037ebe0357fa9c84e3fdc493e8da76d9fe1641
|
@ -0,0 +1 @@
|
||||
f50d77dea2c23229c50caef5b9b2d839bded3d75
|
@ -0,0 +1 @@
|
||||
a0ffeb67badb487c80d309836d97e430a868b5f1
|
@ -14,6 +14,11 @@ TestCastConfigController::TestCastConfigController() = default;
|
||||
|
||||
TestCastConfigController::~TestCastConfigController() = default;
|
||||
|
||||
void TestCastConfigController::AddSinkAndRoute(
|
||||
const SinkAndRoute& sink_and_route) {
|
||||
sinks_and_routes_.push_back(sink_and_route);
|
||||
}
|
||||
|
||||
void TestCastConfigController::ResetRouteIds() {
|
||||
stop_casting_route_id_.clear();
|
||||
freeze_route_route_id_.clear();
|
||||
|
@ -47,6 +47,9 @@ class TestCastConfigController : public CastConfigController {
|
||||
return unfreeze_route_route_id_;
|
||||
}
|
||||
|
||||
// Adds an entry to `sinks_and_routes_`.
|
||||
void AddSinkAndRoute(const SinkAndRoute& sink_and_route);
|
||||
|
||||
// Resets the captured route IDs.
|
||||
void ResetRouteIds();
|
||||
|
||||
|
@ -20,10 +20,40 @@
|
||||
#include "ash/system/unified/quick_settings_metrics_util.h"
|
||||
#include "ash/system/unified/unified_system_tray_controller.h"
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "components/access_code_cast/common/access_code_cast_metrics.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
|
||||
namespace ash {
|
||||
namespace {
|
||||
|
||||
// Returns the active SinkAndRoute if this machine ("local source") is
|
||||
// casting to it. Otherwise returns an empty SinkAndRoute.
|
||||
SinkAndRoute GetActiveSinkAndRoute() {
|
||||
auto* cast_config = CastConfigController::Get();
|
||||
CHECK(cast_config);
|
||||
for (const auto& sink_and_route : cast_config->GetSinksAndRoutes()) {
|
||||
if (!sink_and_route.route.id.empty() &&
|
||||
sink_and_route.route.is_local_source) {
|
||||
return sink_and_route;
|
||||
}
|
||||
}
|
||||
return SinkAndRoute();
|
||||
}
|
||||
|
||||
// Returns the string to display for the "Casting" feature tile label.
|
||||
std::u16string GetCastingString(const CastRoute& route) {
|
||||
switch (route.content_source) {
|
||||
case ContentSource::kUnknown:
|
||||
return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_CASTING);
|
||||
case ContentSource::kTab:
|
||||
return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_CASTING_TAB);
|
||||
case ContentSource::kDesktop:
|
||||
return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_CASTING_SCREEN);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
CastFeaturePodController::CastFeaturePodController(
|
||||
UnifiedSystemTrayController* tray_controller)
|
||||
@ -105,11 +135,9 @@ std::unique_ptr<FeatureTile> CastFeaturePodController::CreateTile(
|
||||
return tile;
|
||||
}
|
||||
|
||||
tile->SetSubLabel(
|
||||
l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_CAST_DEVICES_AVAILABLE));
|
||||
tile->CreateDecorativeDrillInButton(tooltip);
|
||||
|
||||
UpdateSublabelVisibility();
|
||||
UpdateFeatureTile();
|
||||
|
||||
return tile;
|
||||
}
|
||||
@ -146,10 +174,7 @@ void CastFeaturePodController::OnLabelPressed() {
|
||||
void CastFeaturePodController::OnDevicesUpdated(
|
||||
const std::vector<SinkAndRoute>& devices) {
|
||||
if (features::IsQsRevampEnabled()) {
|
||||
if (tile_->tile_type() == FeatureTile::TileType::kCompact) {
|
||||
return;
|
||||
}
|
||||
UpdateSublabelVisibility();
|
||||
UpdateFeatureTile();
|
||||
} else {
|
||||
Update();
|
||||
}
|
||||
@ -164,14 +189,44 @@ void CastFeaturePodController::Update() {
|
||||
button_->SetVisible(target_visibility);
|
||||
}
|
||||
|
||||
void CastFeaturePodController::UpdateSublabelVisibility() {
|
||||
void CastFeaturePodController::UpdateFeatureTile() {
|
||||
DCHECK(features::IsQsRevampEnabled());
|
||||
DCHECK(tile_);
|
||||
auto* cast_config = CastConfigController::Get();
|
||||
bool devices_available =
|
||||
cast_config && (cast_config->HasSinksAndRoutes() ||
|
||||
cast_config->AccessCodeCastingEnabled());
|
||||
tile_->SetSubLabelVisibility(devices_available);
|
||||
if (!cast_config) {
|
||||
return; // May be null in tests.
|
||||
}
|
||||
bool is_casting = cast_config->HasActiveRoute();
|
||||
tile_->SetToggled(is_casting);
|
||||
if (is_casting) {
|
||||
// TODO(b/268575073): Use a new "cast connected" icon at 20px.
|
||||
tile_->SetVectorIcon(kUnifiedMenuCastIcon);
|
||||
|
||||
// Set the label to "Casting screen" or "Casting tab".
|
||||
SinkAndRoute sink_and_route = GetActiveSinkAndRoute();
|
||||
tile_->SetLabel(GetCastingString(sink_and_route.route));
|
||||
|
||||
if (tile_->tile_type() == FeatureTile::TileType::kPrimary) {
|
||||
// If the sink has a name ("Sony TV") then show it as a sub-label.
|
||||
const std::string& active_sink_name = sink_and_route.sink.name;
|
||||
tile_->SetSubLabel(base::UTF8ToUTF16(active_sink_name));
|
||||
tile_->SetSubLabelVisibility(!active_sink_name.empty());
|
||||
}
|
||||
return;
|
||||
}
|
||||
// TODO(b/268575073): Use a new "cast not connected" icon.
|
||||
tile_->SetVectorIcon(kUnifiedMenuCastIcon);
|
||||
|
||||
// Set the label to "Cast screen".
|
||||
tile_->SetLabel(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_CAST));
|
||||
|
||||
if (tile_->tile_type() == FeatureTile::TileType::kPrimary) {
|
||||
tile_->SetSubLabel(
|
||||
l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_CAST_DEVICES_AVAILABLE));
|
||||
bool devices_available = cast_config->HasSinksAndRoutes() ||
|
||||
cast_config->AccessCodeCastingEnabled();
|
||||
tile_->SetSubLabelVisibility(devices_available);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ash
|
||||
|
@ -47,8 +47,8 @@ class ASH_EXPORT CastFeaturePodController
|
||||
// Updates feature pod button visibility. Used pre-QsRevamp.
|
||||
void Update();
|
||||
|
||||
// Updates tile sublabel visibility. Used post-QsRevamp.
|
||||
void UpdateSublabelVisibility();
|
||||
// Updates the feature tile. Used post-QsRevamp.
|
||||
void UpdateFeatureTile();
|
||||
|
||||
const raw_ptr<UnifiedSystemTrayController,
|
||||
DanglingUntriaged | ExperimentalAsh>
|
||||
|
@ -15,6 +15,14 @@
|
||||
namespace ash {
|
||||
namespace {
|
||||
|
||||
// Returns a SinkAndRoute as if the local machine was casting.
|
||||
SinkAndRoute MakeLocalSinkAndRoute() {
|
||||
SinkAndRoute sink_and_route;
|
||||
sink_and_route.route.id = "route_id";
|
||||
sink_and_route.route.is_local_source = true;
|
||||
return sink_and_route;
|
||||
}
|
||||
|
||||
class CastFeaturePodControllerTest : public AshTestBase {
|
||||
public:
|
||||
CastFeaturePodControllerTest() {
|
||||
@ -39,9 +47,12 @@ class CastFeaturePodControllerTest : public AshTestBase {
|
||||
TEST_F(CastFeaturePodControllerTest, CreateTile) {
|
||||
std::unique_ptr<FeatureTile> tile = controller_->CreateTile();
|
||||
EXPECT_TRUE(tile->GetVisible());
|
||||
EXPECT_FALSE(tile->IsToggled());
|
||||
EXPECT_EQ(tile->label()->GetText(), u"Cast screen");
|
||||
EXPECT_EQ(tile->GetTooltipText(), u"Show cast devices");
|
||||
EXPECT_TRUE(tile->drill_in_button()->GetVisible());
|
||||
EXPECT_EQ(tile->drill_in_button()->GetTooltipText(), u"Show cast devices");
|
||||
EXPECT_FALSE(tile->sub_label()->GetVisible());
|
||||
}
|
||||
|
||||
TEST_F(CastFeaturePodControllerTest, TileNotVisibleWhenNoMediaRouter) {
|
||||
@ -79,5 +90,61 @@ TEST_F(CastFeaturePodControllerTest, SubLabelVisibleOnDevicesUpdated) {
|
||||
EXPECT_TRUE(tile->sub_label()->GetVisible());
|
||||
}
|
||||
|
||||
TEST_F(CastFeaturePodControllerTest, TileStateWhenCastingScreen) {
|
||||
cast_config_.set_has_active_route(true);
|
||||
cast_config_.set_has_sinks_and_routes(true);
|
||||
SinkAndRoute sink_and_route = MakeLocalSinkAndRoute();
|
||||
sink_and_route.sink.name = "Sony TV";
|
||||
sink_and_route.route.content_source = ContentSource::kDesktop;
|
||||
cast_config_.AddSinkAndRoute(sink_and_route);
|
||||
|
||||
std::unique_ptr<FeatureTile> tile = controller_->CreateTile();
|
||||
EXPECT_TRUE(tile->IsToggled());
|
||||
EXPECT_EQ(tile->label()->GetText(), u"Casting screen");
|
||||
EXPECT_TRUE(tile->sub_label()->GetVisible());
|
||||
EXPECT_EQ(tile->sub_label()->GetText(), u"Sony TV");
|
||||
}
|
||||
|
||||
TEST_F(CastFeaturePodControllerTest, TileStateWhenCastingTab) {
|
||||
cast_config_.set_has_active_route(true);
|
||||
cast_config_.set_has_sinks_and_routes(true);
|
||||
SinkAndRoute sink_and_route = MakeLocalSinkAndRoute();
|
||||
sink_and_route.sink.name = "Sony TV";
|
||||
sink_and_route.route.content_source = ContentSource::kTab;
|
||||
cast_config_.AddSinkAndRoute(sink_and_route);
|
||||
|
||||
std::unique_ptr<FeatureTile> tile = controller_->CreateTile();
|
||||
EXPECT_TRUE(tile->IsToggled());
|
||||
EXPECT_EQ(tile->label()->GetText(), u"Casting tab");
|
||||
EXPECT_TRUE(tile->sub_label()->GetVisible());
|
||||
EXPECT_EQ(tile->sub_label()->GetText(), u"Sony TV");
|
||||
}
|
||||
|
||||
TEST_F(CastFeaturePodControllerTest, TileStateWhenCastingUnknownSource) {
|
||||
cast_config_.set_has_active_route(true);
|
||||
cast_config_.set_has_sinks_and_routes(true);
|
||||
SinkAndRoute sink_and_route = MakeLocalSinkAndRoute();
|
||||
sink_and_route.sink.name = "Sony TV";
|
||||
sink_and_route.route.content_source = ContentSource::kUnknown;
|
||||
cast_config_.AddSinkAndRoute(sink_and_route);
|
||||
|
||||
std::unique_ptr<FeatureTile> tile = controller_->CreateTile();
|
||||
EXPECT_TRUE(tile->IsToggled());
|
||||
EXPECT_EQ(tile->label()->GetText(), u"Casting");
|
||||
EXPECT_TRUE(tile->sub_label()->GetVisible());
|
||||
EXPECT_EQ(tile->sub_label()->GetText(), u"Sony TV");
|
||||
}
|
||||
|
||||
TEST_F(CastFeaturePodControllerTest, SubLabelHiddenWhenSinkHasNoName) {
|
||||
cast_config_.set_has_active_route(true);
|
||||
cast_config_.set_has_sinks_and_routes(true);
|
||||
SinkAndRoute sink_and_route = MakeLocalSinkAndRoute();
|
||||
sink_and_route.sink.name = "";
|
||||
cast_config_.AddSinkAndRoute(sink_and_route);
|
||||
|
||||
std::unique_ptr<FeatureTile> tile = controller_->CreateTile();
|
||||
EXPECT_FALSE(tile->sub_label()->GetVisible());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace ash
|
||||
|
Reference in New Issue
Block a user