diff --git a/ash/BUILD.gn b/ash/BUILD.gn index 27958011c2c87..d4733546b066d 100644 --- a/ash/BUILD.gn +++ b/ash/BUILD.gn @@ -1618,8 +1618,8 @@ component("ash") { "system/privacy/privacy_indicators_tray_item_view.cc", "system/privacy/privacy_indicators_tray_item_view.h", "system/privacy/screen_capture_observer.h", - "system/privacy/screen_security_notification_controller.cc", - "system/privacy/screen_security_notification_controller.h", + "system/privacy/screen_security_controller.cc", + "system/privacy/screen_security_controller.h", "system/privacy/screen_share_observer.h", "system/privacy/screen_switch_check_controller.cc", "system/privacy/screen_switch_check_controller.h", @@ -2968,7 +2968,7 @@ test("ash_unittests") { "system/power/video_activity_notifier_unittest.cc", "system/privacy/privacy_indicators_controller_unittest.cc", "system/privacy/privacy_indicators_tray_item_view_unittest.cc", - "system/privacy/screen_security_notification_controller_unittest.cc", + "system/privacy/screen_security_controller_unittest.cc", "system/privacy_hub/camera_privacy_switch_controller_unittest.cc", "system/privacy_hub/microphone_privacy_switch_controller_unittest.cc", "system/progress_indicator/progress_indicator_animation_registry_unittest.cc", diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd index 6ae71477eaa60..07cc74f21f6d0 100644 --- a/ash/ash_strings.grd +++ b/ash/ash_strings.grd @@ -3988,6 +3988,10 @@ Here are some things you can try to get started. <message name="IDS_PRIVACY_NOTIFICATION_BUTTON_APP_SETTINGS" desc="Title for the launch app button of the notification shown when an app is using the camera/microphone."> App settings </message> + <message name="IDS_PRIVACY_INDICATORS_VIEW_TOOLTIP" desc="Tooltip for the privacy indicators view in the status area."> + <ph name="CAMERA_AND_MICROPHONE_ACCESS_STATUS">$1<ex>You're sharing your screen</ex></ph>, + <ph name="SCREEN_SHARE_STATUS">$2<ex>Camera in use</ex></ph> + </message> <!-- Strings for camera privacy hub switch notifications --> <message name="IDS_PRIVACY_HUB_WANT_TO_TURN_OFF_CAMERA_NOTIFICATION_TITLE" desc="Title for a notification shown to the users when they disable camera via the hardware switch."> diff --git a/ash/ash_strings_grd/IDS_PRIVACY_INDICATORS_VIEW_TOOLTIP.png.sha1 b/ash/ash_strings_grd/IDS_PRIVACY_INDICATORS_VIEW_TOOLTIP.png.sha1 new file mode 100644 index 0000000000000..55bb2d89adf50 --- /dev/null +++ b/ash/ash_strings_grd/IDS_PRIVACY_INDICATORS_VIEW_TOOLTIP.png.sha1 @@ -0,0 +1 @@ +6bd63e24804bc33446d2edcdda57a66c5a89a096 \ No newline at end of file diff --git a/ash/system/privacy/privacy_indicators_controller.cc b/ash/system/privacy/privacy_indicators_controller.cc index f39756c8fbaee..1e8c09780759f 100644 --- a/ash/system/privacy/privacy_indicators_controller.cc +++ b/ash/system/privacy/privacy_indicators_controller.cc @@ -7,11 +7,13 @@ #include <string> #include "ash/constants/ash_constants.h" +#include "ash/constants/ash_features.h" #include "ash/public/cpp/notification_utils.h" #include "ash/resources/vector_icons/vector_icons.h" #include "ash/root_window_controller.h" #include "ash/shell.h" #include "ash/strings/grit/ash_strings.h" +#include "ash/system/privacy/privacy_indicators_tray_item_view.h" #include "ash/system/status_area_widget.h" #include "ash/system/unified/unified_system_tray.h" #include "ui/base/l10n/l10n_util.h" @@ -154,4 +156,21 @@ void UpdatePrivacyIndicatorsView(bool is_camera_used, bool is_microphone_used) { } } +void UpdatePrivacyIndicatorsScreenShareStatus(bool is_screen_sharing) { + if (!features::IsPrivacyIndicatorsEnabled()) + return; + + DCHECK(ash::Shell::HasInstance()); + for (auto* root_window_controller : + ash::Shell::Get()->GetAllRootWindowControllers()) { + DCHECK(root_window_controller); + DCHECK(root_window_controller->GetStatusAreaWidget()); + + root_window_controller->GetStatusAreaWidget() + ->unified_system_tray() + ->privacy_indicators_view() + ->UpdateScreenShareStatus(is_screen_sharing); + } +} + } // namespace ash \ No newline at end of file diff --git a/ash/system/privacy/privacy_indicators_controller.h b/ash/system/privacy/privacy_indicators_controller.h index e9232959e78b3..b2d268f382f95 100644 --- a/ash/system/privacy/privacy_indicators_controller.h +++ b/ash/system/privacy/privacy_indicators_controller.h @@ -69,6 +69,11 @@ void ASH_EXPORT ModifyPrivacyIndicatorsNotification( void ASH_EXPORT UpdatePrivacyIndicatorsView(bool is_camera_used, bool is_microphone_used); +// Update `PrivacyIndicatorsTrayItemView` screen share status across all status +// area widgets. +void ASH_EXPORT +UpdatePrivacyIndicatorsScreenShareStatus(bool is_screen_sharing); + } // namespace ash #endif // ASH_SYSTEM_PRIVACY_PRIVACY_INDICATORS_CONTROLLER_H_ diff --git a/ash/system/privacy/privacy_indicators_tray_item_view.cc b/ash/system/privacy/privacy_indicators_tray_item_view.cc index 70a692cc2098c..c3825f18d30a8 100644 --- a/ash/system/privacy/privacy_indicators_tray_item_view.cc +++ b/ash/system/privacy/privacy_indicators_tray_item_view.cc @@ -14,7 +14,6 @@ #include "ash/strings/grit/ash_strings.h" #include "ash/style/ash_color_provider.h" #include "ash/system/tray/tray_item_view.h" -#include "base/bind.h" #include "base/metrics/histogram_functions.h" #include "base/time/time.h" #include "ui/base/l10n/l10n_util.h" @@ -41,6 +40,7 @@ const int kPrivacyIndicatorsViewSpacing = 2; const int kPrivacyIndicatorsIconSize = 16; const int kPrivacyIndicatorsViewExpandedShorterSideSize = 24; const int kPrivacyIndicatorsViewExpandedLongerSideSize = 50; +const int kPrivacyIndicatorsViewExpandedWithScreenShareSize = 68; const int kPrivacyIndicatorsViewSize = 8; constexpr auto kDwellInExpandDuration = base::Milliseconds(1000); @@ -108,19 +108,22 @@ PrivacyIndicatorsTrayItemView::PrivacyIndicatorsTrayItemView(Shelf* shelf) layer()->SetRoundedCornerRadius( gfx::RoundedCornersF{kPrivacyIndicatorsViewExpandedShorterSideSize / 2}); - auto camera_icon = std::make_unique<views::ImageView>(); - camera_icon->SetPaintToLayer(); - camera_icon->layer()->SetFillsBoundsOpaquely(false); - camera_icon_ = container_view->AddChildView(std::move(camera_icon)); + auto add_icon_to_container = [&container_view]() { + auto icon = std::make_unique<views::ImageView>(); + icon->SetPaintToLayer(); + icon->layer()->SetFillsBoundsOpaquely(false); + icon->SetVisible(false); + return container_view->AddChildView(std::move(icon)); + }; - auto microphone_icon = std::make_unique<views::ImageView>(); - microphone_icon->SetPaintToLayer(); - microphone_icon->layer()->SetFillsBoundsOpaquely(false); - microphone_icon_ = container_view->AddChildView(std::move(microphone_icon)); + camera_icon_ = add_icon_to_container(); + microphone_icon_ = add_icon_to_container(); + screen_share_icon_ = add_icon_to_container(); AddChildView(std::move(container_view)); UpdateIcons(); + TooltipTextChanged(); } PrivacyIndicatorsTrayItemView::~PrivacyIndicatorsTrayItemView() = default; @@ -134,13 +137,23 @@ void PrivacyIndicatorsTrayItemView::Update(bool camera_is_used, camera_is_used_ = camera_is_used; microphone_is_used_ = microphone_is_used; - SetVisible(camera_is_used_ || microphone_is_used_); + SetVisible(camera_is_used_ || microphone_is_used_ || is_screen_sharing_); if (!GetVisible()) return; camera_icon_->SetVisible(camera_is_used); microphone_icon_->SetVisible(microphone_is_used); + TooltipTextChanged(); +} +void PrivacyIndicatorsTrayItemView::UpdateScreenShareStatus( + bool is_screen_sharing) { + if (is_screen_sharing_ == is_screen_sharing) + return; + is_screen_sharing_ = is_screen_sharing; + + SetVisible(camera_is_used_ || microphone_is_used_ || is_screen_sharing_); + screen_share_icon_->SetVisible(is_screen_sharing_); TooltipTextChanged(); } @@ -153,18 +166,32 @@ void PrivacyIndicatorsTrayItemView::UpdateAlignmentForShelf(Shelf* shelf) { std::u16string PrivacyIndicatorsTrayItemView::GetTooltipText( const gfx::Point& point) const { + auto cam_and_mic_status = std::u16string(); if (camera_is_used_ && microphone_is_used_) { - return l10n_util::GetStringUTF16( + cam_and_mic_status = l10n_util::GetStringUTF16( IDS_PRIVACY_NOTIFICATION_TITLE_CAMERA_AND_MIC); + } else if (camera_is_used_) { + cam_and_mic_status = + l10n_util::GetStringUTF16(IDS_PRIVACY_NOTIFICATION_TITLE_CAMERA); + } else if (microphone_is_used_) { + cam_and_mic_status = + l10n_util::GetStringUTF16(IDS_PRIVACY_NOTIFICATION_TITLE_MIC); } - if (camera_is_used_) - return l10n_util::GetStringUTF16(IDS_PRIVACY_NOTIFICATION_TITLE_CAMERA); + auto screen_share_status = + is_screen_sharing_ + ? l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_SCREEN_SHARE_TITLE) + : std::u16string(); - if (microphone_is_used_) - return l10n_util::GetStringUTF16(IDS_PRIVACY_NOTIFICATION_TITLE_MIC); + if (cam_and_mic_status.empty()) + return screen_share_status; - return std::u16string(); + if (screen_share_status.empty()) + return cam_and_mic_status; + + return l10n_util::GetStringFUTF16(IDS_PRIVACY_INDICATORS_VIEW_TOOLTIP, + {cam_and_mic_status, screen_share_status}, + /*offsets=*/nullptr); } void PrivacyIndicatorsTrayItemView::PerformVisibilityAnimation(bool visible) { @@ -197,13 +224,13 @@ gfx::Size PrivacyIndicatorsTrayItemView::CalculatePreferredSize() const { case AnimationState::kExpand: shorter_side = kPrivacyIndicatorsViewExpandedShorterSideSize; longer_side = - kPrivacyIndicatorsViewExpandedLongerSideSize * + GetLongerSideLengthInExpandedMode() * gfx::Tween::CalculateValue(gfx::Tween::ACCEL_20_DECEL_100, expand_animation_->GetCurrentValue()); break; case AnimationState::kDwellInExpand: shorter_side = kPrivacyIndicatorsViewExpandedShorterSideSize; - longer_side = kPrivacyIndicatorsViewExpandedLongerSideSize; + longer_side = GetLongerSideLengthInExpandedMode(); break; case AnimationState::kOnlyLongerSideShrink: shorter_side = kPrivacyIndicatorsViewExpandedShorterSideSize; @@ -309,6 +336,9 @@ void PrivacyIndicatorsTrayItemView::UpdateIcons() { microphone_icon_->SetImage( gfx::CreateVectorIcon(kPrivacyIndicatorsMicrophoneIcon, kPrivacyIndicatorsIconSize, icon_color)); + screen_share_icon_->SetImage( + gfx::CreateVectorIcon(kPrivacyIndicatorsScreenShareIcon, + kPrivacyIndicatorsIconSize, icon_color)); } void PrivacyIndicatorsTrayItemView::UpdateBoundsInset() { @@ -337,7 +367,7 @@ int PrivacyIndicatorsTrayItemView::CalculateSizeDuringShrinkAnimation( double animation_value = gfx::Tween::CalculateValue( gfx::Tween::ACCEL_20_DECEL_100, animation->GetCurrentValue()); int begin_size = for_longer_side - ? kPrivacyIndicatorsViewExpandedLongerSideSize + ? GetLongerSideLengthInExpandedMode() : kPrivacyIndicatorsViewExpandedShorterSideSize; // The size shrink from `begin_size` to kPrivacyIndicatorsViewSize when @@ -346,6 +376,13 @@ int PrivacyIndicatorsTrayItemView::CalculateSizeDuringShrinkAnimation( (begin_size - kPrivacyIndicatorsViewSize) * animation_value; } +int PrivacyIndicatorsTrayItemView::GetLongerSideLengthInExpandedMode() const { + // If all three icons are visible, the view should be longer. + return camera_is_used_ && microphone_is_used_ && is_screen_sharing_ + ? kPrivacyIndicatorsViewExpandedWithScreenShareSize + : kPrivacyIndicatorsViewExpandedLongerSideSize; +} + void PrivacyIndicatorsTrayItemView::EndAllAnimations() { shorter_side_shrink_animation_->End(); longer_side_shrink_animation_->End(); diff --git a/ash/system/privacy/privacy_indicators_tray_item_view.h b/ash/system/privacy/privacy_indicators_tray_item_view.h index 6e34b7035c1c6..6c75c99e349dc 100644 --- a/ash/system/privacy/privacy_indicators_tray_item_view.h +++ b/ash/system/privacy/privacy_indicators_tray_item_view.h @@ -58,6 +58,9 @@ class ASH_EXPORT PrivacyIndicatorsTrayItemView : public TrayItemView { // Update the view according to the state of camara/microphone access. void Update(bool camera_is_used, bool microphone_is_used); + // Update the view according to the state of screen sharing. + void UpdateScreenShareStatus(bool is_screen_sharing); + // Update the view according to the shelf alignment. void UpdateAlignmentForShelf(Shelf* shelf); @@ -90,6 +93,9 @@ class ASH_EXPORT PrivacyIndicatorsTrayItemView : public TrayItemView { // shorter side. int CalculateSizeDuringShrinkAnimation(bool for_longer_side) const; + // Calculate the length of the longer size, based on `is_screen_sharing_`. + int GetLongerSideLengthInExpandedMode() const; + // End all 3 animations contained in this class. void EndAllAnimations(); @@ -98,9 +104,11 @@ class ASH_EXPORT PrivacyIndicatorsTrayItemView : public TrayItemView { // Owned by the views hierarchy. views::ImageView* camera_icon_ = nullptr; views::ImageView* microphone_icon_ = nullptr; + views::ImageView* screen_share_icon_ = nullptr; bool camera_is_used_ = false; bool microphone_is_used_ = false; + bool is_screen_sharing_ = false; // Keep track the current animation state during the multi-part animation. AnimationState animation_state_ = kIdle; diff --git a/ash/system/privacy/privacy_indicators_tray_item_view_unittest.cc b/ash/system/privacy/privacy_indicators_tray_item_view_unittest.cc index 74649f4f2a9bd..280514cd13590 100644 --- a/ash/system/privacy/privacy_indicators_tray_item_view_unittest.cc +++ b/ash/system/privacy/privacy_indicators_tray_item_view_unittest.cc @@ -40,6 +40,21 @@ int GetExpectedSizeInShrinkAnimation(bool for_longer_side, double progress) { (begin_size - kPrivacyIndicatorsViewSize) * animation_value; } +// Get the expected tooltip text, given the string for camera/mic access and +// screen share. +std::u16string GetExpectedTooltipText(std::u16string cam_mic_status, + std::u16string screen_share_status) { + if (cam_mic_status.empty()) + return screen_share_status; + + if (screen_share_status.empty()) + return cam_mic_status; + + return l10n_util::GetStringFUTF16(IDS_PRIVACY_INDICATORS_VIEW_TOOLTIP, + {cam_mic_status, screen_share_status}, + /*offsets=*/nullptr); +} + } // namespace namespace ash { @@ -94,6 +109,9 @@ class PrivacyIndicatorsTrayItemViewTest : public AshTestBase { views::ImageView* microphone_icon() { return privacy_indicators_view_->microphone_icon_; } + views::ImageView* screen_share_icon() { + return privacy_indicators_view_->screen_share_icon_; + } gfx::LinearAnimation* expand_animation() { return privacy_indicators_view_->expand_animation_.get(); @@ -143,28 +161,78 @@ TEST_F(PrivacyIndicatorsTrayItemViewTest, IconsVisibility) { EXPECT_FALSE(privacy_indicators_view()->GetVisible()); } +TEST_F(PrivacyIndicatorsTrayItemViewTest, ScreenShareIconsVisibility) { + EXPECT_FALSE(privacy_indicators_view()->GetVisible()); + + privacy_indicators_view()->UpdateScreenShareStatus( + /*is_screen_sharing=*/true); + EXPECT_TRUE(privacy_indicators_view()->GetVisible()); + EXPECT_TRUE(screen_share_icon()->GetVisible()); + EXPECT_FALSE(camera_icon()->GetVisible()); + EXPECT_FALSE(microphone_icon()->GetVisible()); + + privacy_indicators_view()->UpdateScreenShareStatus( + /*is_screen_sharing=*/false); + EXPECT_FALSE(privacy_indicators_view()->GetVisible()); + + // Test screen share showing up with other icons. + privacy_indicators_view()->Update(/*camera_is_used=*/false, + /*microphone_is_used=*/true); + privacy_indicators_view()->UpdateScreenShareStatus( + /*is_screen_sharing=*/true); + EXPECT_TRUE(privacy_indicators_view()->GetVisible()); + EXPECT_FALSE(camera_icon()->GetVisible()); + EXPECT_TRUE(microphone_icon()->GetVisible()); + EXPECT_TRUE(screen_share_icon()->GetVisible()); + + privacy_indicators_view()->UpdateScreenShareStatus( + /*is_screen_sharing=*/false); + EXPECT_TRUE(privacy_indicators_view()->GetVisible()); + EXPECT_FALSE(camera_icon()->GetVisible()); + EXPECT_TRUE(microphone_icon()->GetVisible()); + EXPECT_FALSE(screen_share_icon()->GetVisible()); +} + TEST_F(PrivacyIndicatorsTrayItemViewTest, TooltipText) { - EXPECT_EQ(std::u16string(), GetTooltipText()); + EXPECT_EQ(GetExpectedTooltipText(/*cam_mic_status=*/std::u16string(), + /*screen_share_status=*/std::u16string()), + GetTooltipText()); privacy_indicators_view()->Update(/*camera_is_used=*/true, /*microphone_is_used=*/false); - EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PRIVACY_NOTIFICATION_TITLE_CAMERA), + EXPECT_EQ(GetExpectedTooltipText(/*cam_mic_status=*/l10n_util::GetStringUTF16( + IDS_PRIVACY_NOTIFICATION_TITLE_CAMERA), + /*screen_share_status=*/std::u16string()), GetTooltipText()); privacy_indicators_view()->Update(/*camera_is_used=*/false, /*microphone_is_used=*/true); - EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PRIVACY_NOTIFICATION_TITLE_MIC), + EXPECT_EQ(GetExpectedTooltipText(/*cam_mic_status=*/l10n_util::GetStringUTF16( + IDS_PRIVACY_NOTIFICATION_TITLE_MIC), + /*screen_share_status=*/std::u16string()), GetTooltipText()); privacy_indicators_view()->Update(/*camera_is_used=*/true, /*microphone_is_used=*/true); EXPECT_EQ( - l10n_util::GetStringUTF16(IDS_PRIVACY_NOTIFICATION_TITLE_CAMERA_AND_MIC), + GetExpectedTooltipText(/*cam_mic_status=*/l10n_util::GetStringUTF16( + IDS_PRIVACY_NOTIFICATION_TITLE_CAMERA_AND_MIC), + /*screen_share_status=*/std::u16string()), GetTooltipText()); privacy_indicators_view()->Update(/*camera_is_used=*/false, /*microphone_is_used=*/false); - EXPECT_EQ(std::u16string(), GetTooltipText()); + EXPECT_EQ(GetExpectedTooltipText(/*cam_mic_status=*/std::u16string(), + /*screen_share_status=*/std::u16string()), + GetTooltipText()); + + privacy_indicators_view()->UpdateScreenShareStatus( + /*is_screen_sharing=*/true); + EXPECT_EQ(GetExpectedTooltipText( + /*cam_mic_status=*/std::u16string(), + /*screen_share_status=*/l10n_util::GetStringUTF16( + IDS_ASH_STATUS_TRAY_SCREEN_SHARE_TITLE)), + GetTooltipText()); } TEST_F(PrivacyIndicatorsTrayItemViewTest, ShelfAlignmentChanged) { @@ -322,4 +390,36 @@ TEST_F(PrivacyIndicatorsTrayItemViewTest, SideShelfVisibilityAnimation) { privacy_indicators_view()->GetPreferredSize().height()); } +TEST_F(PrivacyIndicatorsTrayItemViewTest, StateChangeDuringAnimation) { + SetViewVisibleWithAnimation(); + double progress = 0.5; + + // Firstly, expand animation will be performed. + AnimateToValue(expand_animation(), progress); + + // Update state in mid animation, shouldn't crash anything. + privacy_indicators_view()->Update(/*camera_is_used=*/true, + /*microphone_is_used=*/false); + + expand_animation()->End(); + + // After that shrink animations will be started. + longer_side_shrink_animation()->Start(); + AnimateToValue(longer_side_shrink_animation(), progress); + + // Update the state again, no crash expected. + privacy_indicators_view()->UpdateScreenShareStatus( + /*is_screen_sharing=*/true); + + shorter_side_shrink_animation()->Start(); + AnimateToValue(shorter_side_shrink_animation(), progress); + + // The view should become invisible immediately after setting these states. + privacy_indicators_view()->Update(/*camera_is_used=*/false, + /*microphone_is_used=*/false); + privacy_indicators_view()->UpdateScreenShareStatus( + /*is_screen_sharing=*/false); + EXPECT_FALSE(privacy_indicators_view()->GetVisible()); +} + } // namespace ash diff --git a/ash/system/privacy/screen_security_notification_controller.cc b/ash/system/privacy/screen_security_controller.cc similarity index 86% rename from ash/system/privacy/screen_security_notification_controller.cc rename to ash/system/privacy/screen_security_controller.cc index 14d51c3ac6159..95a1a6e2ec484 100644 --- a/ash/system/privacy/screen_security_notification_controller.cc +++ b/ash/system/privacy/screen_security_controller.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ash/system/privacy/screen_security_notification_controller.h" +#include "ash/system/privacy/screen_security_controller.h" #include "ash/constants/ash_constants.h" #include "ash/constants/ash_features.h" @@ -11,6 +11,7 @@ #include "ash/resources/vector_icons/vector_icons.h" #include "ash/shell.h" #include "ash/strings/grit/ash_strings.h" +#include "ash/system/privacy/privacy_indicators_controller.h" #include "ash/system/tray/system_tray_notifier.h" #include "base/bind.h" #include "base/metrics/user_metrics.h" @@ -32,21 +33,20 @@ const char kScreenShareNotificationId[] = "chrome://screen/share"; const char kNotifierScreenCapture[] = "ash.screen-capture"; const char kNotifierScreenShare[] = "ash.screen-share"; -ScreenSecurityNotificationController::ScreenSecurityNotificationController() { +ScreenSecurityController::ScreenSecurityController() { Shell::Get()->AddShellObserver(this); Shell::Get()->system_tray_notifier()->AddScreenCaptureObserver(this); Shell::Get()->system_tray_notifier()->AddScreenShareObserver(this); } -ScreenSecurityNotificationController::~ScreenSecurityNotificationController() { +ScreenSecurityController::~ScreenSecurityController() { Shell::Get()->system_tray_notifier()->RemoveScreenShareObserver(this); Shell::Get()->system_tray_notifier()->RemoveScreenCaptureObserver(this); Shell::Get()->RemoveShellObserver(this); } -void ScreenSecurityNotificationController::CreateNotification( - const std::u16string& message, - bool is_capture) { +void ScreenSecurityController::CreateNotification(const std::u16string& message, + bool is_capture) { message_center::RichNotificationData data; data.buttons.push_back(message_center::ButtonInfo(l10n_util::GetStringUTF16( is_capture ? IDS_ASH_STATUS_TRAY_SCREEN_CAPTURE_STOP @@ -62,7 +62,7 @@ void ScreenSecurityNotificationController::CreateNotification( auto delegate = base::MakeRefCounted<message_center::HandleNotificationClickDelegate>( base::BindRepeating( - [](base::WeakPtr<ScreenSecurityNotificationController> controller, + [](base::WeakPtr<ScreenSecurityController> controller, bool is_capture, absl::optional<int> button_index) { if (!button_index) return; @@ -125,7 +125,7 @@ void ScreenSecurityNotificationController::CreateNotification( std::move(notification)); } -void ScreenSecurityNotificationController::StopAllSessions(bool is_capture) { +void ScreenSecurityController::StopAllSessions(bool is_capture) { message_center::MessageCenter::Get()->RemoveNotification( is_capture ? kScreenCaptureNotificationId : kScreenShareNotificationId, false /* by_user */); @@ -141,12 +141,12 @@ void ScreenSecurityNotificationController::StopAllSessions(bool is_capture) { change_source_callback_.Reset(); } -void ScreenSecurityNotificationController::ChangeSource() { +void ScreenSecurityController::ChangeSource() { if (change_source_callback_ && capture_stop_callbacks_.size() == 1) change_source_callback_.Run(); } -void ScreenSecurityNotificationController::OnScreenCaptureStart( +void ScreenSecurityController::OnScreenCaptureStart( const base::RepeatingClosure& stop_callback, const base::RepeatingClosure& source_callback, const std::u16string& screen_capture_status) { @@ -166,11 +166,11 @@ void ScreenSecurityNotificationController::OnScreenCaptureStart( CreateNotification(screen_capture_status, true /* is_capture */); } -void ScreenSecurityNotificationController::OnScreenCaptureStop() { +void ScreenSecurityController::OnScreenCaptureStop() { StopAllSessions(true /* is_capture */); } -void ScreenSecurityNotificationController::OnScreenShareStart( +void ScreenSecurityController::OnScreenShareStart( const base::RepeatingClosure& stop_callback, const std::u16string& helper_name) { share_stop_callbacks_.emplace_back(std::move(stop_callback)); @@ -185,14 +185,19 @@ void ScreenSecurityNotificationController::OnScreenShareStart( } CreateNotification(help_label_text, false /* is_capture */); + + if (features::IsPrivacyIndicatorsEnabled()) + UpdatePrivacyIndicatorsScreenShareStatus(/*is_screen_sharing=*/true); } -void ScreenSecurityNotificationController::OnScreenShareStop() { +void ScreenSecurityController::OnScreenShareStop() { StopAllSessions(false /* is_capture */); + + if (features::IsPrivacyIndicatorsEnabled()) + UpdatePrivacyIndicatorsScreenShareStatus(/*is_screen_sharing=*/false); } -void ScreenSecurityNotificationController::OnCastingSessionStartedOrStopped( - bool started) { +void ScreenSecurityController::OnCastingSessionStartedOrStopped(bool started) { is_casting_ = started; } diff --git a/ash/system/privacy/screen_security_notification_controller.h b/ash/system/privacy/screen_security_controller.h similarity index 73% rename from ash/system/privacy/screen_security_notification_controller.h rename to ash/system/privacy/screen_security_controller.h index be39b5c0c41b9..883039516bafd 100644 --- a/ash/system/privacy/screen_security_notification_controller.h +++ b/ash/system/privacy/screen_security_controller.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef ASH_SYSTEM_PRIVACY_SCREEN_SECURITY_NOTIFICATION_CONTROLLER_H_ -#define ASH_SYSTEM_PRIVACY_SCREEN_SECURITY_NOTIFICATION_CONTROLLER_H_ +#ifndef ASH_SYSTEM_PRIVACY_SCREEN_SECURITY_CONTROLLER_H_ +#define ASH_SYSTEM_PRIVACY_SCREEN_SECURITY_CONTROLLER_H_ #include <string> #include <vector> @@ -21,19 +21,16 @@ extern ASH_EXPORT const char kNotifierScreenCapture[]; extern ASH_EXPORT const char kNotifierScreenShare[]; // Controller class to manage screen security notifications. -class ASH_EXPORT ScreenSecurityNotificationController - : public ScreenCaptureObserver, - public ScreenShareObserver, - public ShellObserver { +class ASH_EXPORT ScreenSecurityController : public ScreenCaptureObserver, + public ScreenShareObserver, + public ShellObserver { public: - ScreenSecurityNotificationController(); + ScreenSecurityController(); - ScreenSecurityNotificationController( - const ScreenSecurityNotificationController&) = delete; - ScreenSecurityNotificationController& operator=( - const ScreenSecurityNotificationController&) = delete; + ScreenSecurityController(const ScreenSecurityController&) = delete; + ScreenSecurityController& operator=(const ScreenSecurityController&) = delete; - ~ScreenSecurityNotificationController() override; + ~ScreenSecurityController() override; private: void CreateNotification(const std::u16string& message, bool is_capture); @@ -69,10 +66,9 @@ class ASH_EXPORT ScreenSecurityNotificationController std::vector<base::OnceClosure> share_stop_callbacks_; base::RepeatingClosure change_source_callback_; - base::WeakPtrFactory<ScreenSecurityNotificationController> weak_ptr_factory_{ - this}; + base::WeakPtrFactory<ScreenSecurityController> weak_ptr_factory_{this}; }; } // namespace ash -#endif // ASH_SYSTEM_PRIVACY_SCREEN_SECURITY_NOTIFICATION_CONTROLLER_H_ +#endif // ASH_SYSTEM_PRIVACY_SCREEN_SECURITY_CONTROLLER_H_ diff --git a/ash/system/privacy/screen_security_controller_unittest.cc b/ash/system/privacy/screen_security_controller_unittest.cc new file mode 100644 index 0000000000000..473335829b6a8 --- /dev/null +++ b/ash/system/privacy/screen_security_controller_unittest.cc @@ -0,0 +1,150 @@ +// 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 "ash/system/privacy/screen_security_controller.h" + +#include "ash/constants/ash_constants.h" +#include "ash/constants/ash_features.h" +#include "ash/root_window_controller.h" +#include "ash/shell.h" +#include "ash/system/privacy/privacy_indicators_tray_item_view.h" +#include "ash/system/status_area_widget.h" +#include "ash/system/tray/system_tray_notifier.h" +#include "ash/system/unified/unified_system_tray.h" +#include "ash/test/ash_test_base.h" +#include "base/callback.h" +#include "base/callback_helpers.h" +#include "base/test/scoped_feature_list.h" +#include "ui/color/color_id.h" +#include "ui/message_center/message_center.h" + +namespace ash { + +namespace { + +message_center::Notification* FindNotification(const std::string& id) { + return message_center::MessageCenter::Get()->FindVisibleNotificationById(id); +} + +// Check the visibility of privacy indicators in all displays. +void ExpectPrivacyIndicatorsVisible(bool visible) { + for (ash::RootWindowController* root_window_controller : + ash::Shell::Get()->GetAllRootWindowControllers()) { + EXPECT_EQ(root_window_controller->GetStatusAreaWidget() + ->unified_system_tray() + ->privacy_indicators_view() + ->GetVisible(), + visible); + } +} + +} // namespace + +class ScreenSecurityControllerTest : public AshTestBase, + public testing::WithParamInterface<bool> { + public: + ScreenSecurityControllerTest() = default; + ScreenSecurityControllerTest(const ScreenSecurityControllerTest&) = delete; + ScreenSecurityControllerTest& operator=(const ScreenSecurityControllerTest&) = + delete; + ~ScreenSecurityControllerTest() override = default; + + // AppAccessNotifierBaseTest: + void SetUp() override { + scoped_feature_list_.InitWithFeatureState( + features::kPrivacyIndicators, IsPrivacyIndicatorsFeatureEnabled()); + AshTestBase::SetUp(); + } + + bool IsPrivacyIndicatorsFeatureEnabled() const { return GetParam(); } + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +INSTANTIATE_TEST_SUITE_P( + All, + ScreenSecurityControllerTest, + /*IsPrivacyIndicatorsFeatureEnabled()=*/::testing::Bool()); + +TEST_P(ScreenSecurityControllerTest, ShowScreenCaptureNotification) { + Shell::Get()->system_tray_notifier()->NotifyScreenCaptureStart( + base::DoNothing(), base::RepeatingClosure(), std::u16string()); + EXPECT_TRUE(FindNotification(kScreenCaptureNotificationId)); + + Shell::Get()->system_tray_notifier()->NotifyScreenCaptureStop(); + EXPECT_FALSE(FindNotification(kScreenCaptureNotificationId)); +} + +TEST_P(ScreenSecurityControllerTest, ShowScreenShareNotification) { + Shell::Get()->system_tray_notifier()->NotifyScreenShareStart( + base::DoNothing(), std::u16string()); + EXPECT_TRUE(FindNotification(kScreenShareNotificationId)); + + Shell::Get()->system_tray_notifier()->NotifyScreenShareStop(); + EXPECT_FALSE(FindNotification(kScreenShareNotificationId)); +} + +TEST_P(ScreenSecurityControllerTest, + DoNotShowScreenCaptureNotificationWhenCasting) { + Shell::Get()->OnCastingSessionStartedOrStopped(true /* started */); + Shell::Get()->system_tray_notifier()->NotifyScreenCaptureStart( + base::DoNothing(), base::RepeatingClosure(), std::u16string()); + EXPECT_FALSE(FindNotification(kScreenCaptureNotificationId)); + + Shell::Get()->system_tray_notifier()->NotifyScreenCaptureStop(); + Shell::Get()->OnCastingSessionStartedOrStopped(false /* started */); + EXPECT_FALSE(FindNotification(kScreenCaptureNotificationId)); +} + +class PrivacyIndicatorsScreenSecurityTest : public AshTestBase { + public: + PrivacyIndicatorsScreenSecurityTest() = default; + PrivacyIndicatorsScreenSecurityTest( + const PrivacyIndicatorsScreenSecurityTest&) = delete; + PrivacyIndicatorsScreenSecurityTest& operator=( + const PrivacyIndicatorsScreenSecurityTest&) = delete; + ~PrivacyIndicatorsScreenSecurityTest() override = default; + + // AshTestBase: + void SetUp() override { + scoped_feature_list_.InitAndEnableFeature(features::kPrivacyIndicators); + + AshTestBase::SetUp(); + } + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +TEST_F(PrivacyIndicatorsScreenSecurityTest, ScreenShareNotification) { + Shell::Get()->system_tray_notifier()->NotifyScreenShareStart( + base::DoNothing(), std::u16string()); + + auto* notification = FindNotification(kScreenShareNotificationId); + EXPECT_TRUE(notification); + + // Notification should have the correct notifier id so that it will be grouped + // with other privacy indicators notification. + EXPECT_EQ(kPrivacyIndicatorsNotifierId, notification->notifier_id().id); + + EXPECT_EQ(ui::kColorAshPrivacyIndicatorsBackground, + notification->accent_color_id()); +} + +TEST_F(PrivacyIndicatorsScreenSecurityTest, TrayItemIndicator) { + // Make sure the indicator shows up on multiple displays. + UpdateDisplay("400x300,400x300,400x300,400x300"); + + ExpectPrivacyIndicatorsVisible(/*visible=*/false); + + Shell::Get()->system_tray_notifier()->NotifyScreenShareStart( + base::DoNothing(), std::u16string()); + ExpectPrivacyIndicatorsVisible(/*visible=*/true); + + Shell::Get()->system_tray_notifier()->NotifyScreenShareStop(); + ExpectPrivacyIndicatorsVisible(/*visible=*/false); +} + +} // namespace ash diff --git a/ash/system/privacy/screen_security_notification_controller_unittest.cc b/ash/system/privacy/screen_security_notification_controller_unittest.cc deleted file mode 100644 index 44038507b82e0..0000000000000 --- a/ash/system/privacy/screen_security_notification_controller_unittest.cc +++ /dev/null @@ -1,54 +0,0 @@ -// 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 "ash/system/privacy/screen_security_notification_controller.h" - -#include "ash/shell.h" -#include "ash/system/tray/system_tray_notifier.h" -#include "ash/test/ash_test_base.h" -#include "base/callback.h" -#include "base/callback_helpers.h" -#include "ui/message_center/message_center.h" - -namespace ash { - -using ScreenSecurityNotificationControllerTest = AshTestBase; - -namespace { - -message_center::Notification* FindNotification(const std::string& id) { - return message_center::MessageCenter::Get()->FindVisibleNotificationById(id); -} - -} // namespace - -TEST_F(ScreenSecurityNotificationControllerTest, - ShowScreenCaptureNotification) { - Shell::Get()->system_tray_notifier()->NotifyScreenCaptureStart( - base::DoNothing(), base::RepeatingClosure(), std::u16string()); - EXPECT_TRUE(FindNotification(kScreenCaptureNotificationId)); - Shell::Get()->system_tray_notifier()->NotifyScreenCaptureStop(); - EXPECT_FALSE(FindNotification(kScreenCaptureNotificationId)); -} - -TEST_F(ScreenSecurityNotificationControllerTest, ShowScreenShareNotification) { - Shell::Get()->system_tray_notifier()->NotifyScreenShareStart( - base::DoNothing(), std::u16string()); - EXPECT_TRUE(FindNotification(kScreenShareNotificationId)); - Shell::Get()->system_tray_notifier()->NotifyScreenShareStop(); - EXPECT_FALSE(FindNotification(kScreenShareNotificationId)); -} - -TEST_F(ScreenSecurityNotificationControllerTest, - DoNotShowScreenCaptureNotificationWhenCasting) { - Shell::Get()->OnCastingSessionStartedOrStopped(true /* started */); - Shell::Get()->system_tray_notifier()->NotifyScreenCaptureStart( - base::DoNothing(), base::RepeatingClosure(), std::u16string()); - EXPECT_FALSE(FindNotification(kScreenCaptureNotificationId)); - Shell::Get()->system_tray_notifier()->NotifyScreenCaptureStop(); - Shell::Get()->OnCastingSessionStartedOrStopped(false /* started */); - EXPECT_FALSE(FindNotification(kScreenCaptureNotificationId)); -} - -} // namespace ash diff --git a/ash/system/system_notification_controller.cc b/ash/system/system_notification_controller.cc index 4c932f392befb..90edb90852871 100644 --- a/ash/system/system_notification_controller.cc +++ b/ash/system/system_notification_controller.cc @@ -14,7 +14,7 @@ #include "ash/system/network/managed_sim_lock_notifier.h" #include "ash/system/network/wifi_toggle_notification_controller.h" #include "ash/system/power/power_notification_controller.h" -#include "ash/system/privacy/screen_security_notification_controller.h" +#include "ash/system/privacy/screen_security_controller.h" #include "ash/system/session/session_limit_notification_controller.h" #include "ash/system/tracing_notification_controller.h" #include "ash/system/update/update_notification_controller.h" @@ -31,8 +31,7 @@ SystemNotificationController::SystemNotificationController() std::make_unique<GestureEducationNotificationController>()), power_(std::make_unique<PowerNotificationController>( message_center::MessageCenter::Get())), - screen_security_( - std::make_unique<ScreenSecurityNotificationController>()), + screen_security_(std::make_unique<ScreenSecurityController>()), session_limit_(std::make_unique<SessionLimitNotificationController>()), tracing_(std::make_unique<TracingNotificationController>()), update_(std::make_unique<UpdateNotificationController>()), diff --git a/ash/system/system_notification_controller.h b/ash/system/system_notification_controller.h index 4e77459b90483..8bba5894eec49 100644 --- a/ash/system/system_notification_controller.h +++ b/ash/system/system_notification_controller.h @@ -18,7 +18,7 @@ class CellularSetupNotifier; class ManagedSimLockNotifier; class MicrophoneMuteNotificationController; class PowerNotificationController; -class ScreenSecurityNotificationController; +class ScreenSecurityController; class SessionLimitNotificationController; class TracingNotificationController; class UpdateNotificationController; @@ -50,7 +50,7 @@ class SystemNotificationController { std::unique_ptr<ManagedSimLockNotifier> managed_sim_lock_notifier_; std::unique_ptr<MicrophoneMuteNotificationController> microphone_mute_; const std::unique_ptr<PowerNotificationController> power_; - const std::unique_ptr<ScreenSecurityNotificationController> screen_security_; + const std::unique_ptr<ScreenSecurityController> screen_security_; const std::unique_ptr<SessionLimitNotificationController> session_limit_; const std::unique_ptr<TracingNotificationController> tracing_; const std::unique_ptr<UpdateNotificationController> update_;