PrivacyIndicators: Configure notification message and icon color
Privacy indicators notification should show app name in the message. Thus, we modified AppAccessNotifier and PrivacyIndicatorController to make this happen. We also add a new color to AshColorProvider that will be used in the notification as well as the status area indicator. Bug: 1344681 Change-Id: I0a49e9959130987d42c06e229600d61881e7a26f Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3814159 Reviewed-by: Peter Kasting <pkasting@chromium.org> Reviewed-by: James Cook <jamescook@chromium.org> Commit-Queue: Andre Le <leandre@chromium.org> Reviewed-by: Ahmed Mehfooz <amehfooz@chromium.org> Reviewed-by: Sean Kau <skau@chromium.org> Cr-Commit-Position: refs/heads/main@{#1034242}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
05af1b42b6
commit
e71d527f1c
@ -3918,6 +3918,18 @@ Here are some things you can try to get started.
|
||||
<message name="IDS_PRIVACY_NOTIFICATION_TITLE_MIC" desc="Title for a notification shown when an app is using the microphone.">
|
||||
Microphone in use
|
||||
</message>
|
||||
<message name="IDS_PRIVACY_NOTIFICATION_MESSAGE_DEFAULT_APP_NAME" desc="A default value for app name in the message label for a notification shown when an app is using the camera/microphone. This is used when no app name is detected and we need to use a default value">
|
||||
An app
|
||||
</message>
|
||||
<message name="IDS_PRIVACY_NOTIFICATION_MESSAGE_CAMERA_AND_MIC" desc="Message label for a notification shown when an app is using the camera and the microphone.">
|
||||
<ph name="APP_NAME">$1<ex>Google meet</ex></ph> is currently using your camera and microphone
|
||||
</message>
|
||||
<message name="IDS_PRIVACY_NOTIFICATION_MESSAGE_CAMERA" desc="Message label for a notification shown when an app is using the camera.">
|
||||
<ph name="APP_NAME">$1<ex>Google meet</ex></ph> is currently using your camera
|
||||
</message>
|
||||
<message name="IDS_PRIVACY_NOTIFICATION_MESSAGE_MIC" desc="Message label for a notification shown when an app is using the microphone.">
|
||||
<ph name="APP_NAME">$1<ex>Google meet</ex></ph> is currently using your microphone
|
||||
</message>
|
||||
|
||||
<!-- Strings for microphone mute switch notification -->
|
||||
<message name="IDS_MICROPHONE_MUTED_NOTIFICATION_TITLE" desc="Title for a notification shown to the users when an app tries to use the microphone while the microphone is muted.i Similar to IDS_MICROPHONE_MUTED_NOTIFICATION_TITLE_WITH_APP_NAME, except this message contains a generic app name string. Used when the name of the app that's using the microphone cannot be determined.">
|
||||
|
@ -0,0 +1 @@
|
||||
bfce600ac28b2dc9009fe5a7ea5e8e2251f8b782
|
@ -0,0 +1 @@
|
||||
f36a9f365ff1aee48b5123904b9b651a05e443b4
|
@ -0,0 +1 @@
|
||||
f14a36a2a977cb81ad6cf1e76f5687f4fc498187
|
@ -0,0 +1 @@
|
||||
75cce1456a87ff61ca9c45f9eaff537bc02337f0
|
@ -379,6 +379,10 @@ void AddCrosStylesColorMixer(ui::ColorProvider* provider,
|
||||
void AddAshColorMixer(ui::ColorProvider* provider,
|
||||
const ui::ColorProviderManager::Key& key) {
|
||||
ui::ColorMixer& mixer = provider->AddMixer();
|
||||
bool dark_mode =
|
||||
features::IsDarkLightModeEnabled()
|
||||
? key.color_mode == ui::ColorProviderManager::ColorMode::kDark
|
||||
: DarkLightModeControllerImpl::Get()->IsDarkModeEnabled();
|
||||
|
||||
AddShieldAndBaseColors(mixer, key);
|
||||
AddControlsColors(mixer, key);
|
||||
@ -391,6 +395,16 @@ void AddAshColorMixer(ui::ColorProvider* provider,
|
||||
mixer[ui::kColorAshActionLabelFocusRingHover] =
|
||||
ui::SetAlpha(cros_tokens::kColorPrimaryDark, 0x60);
|
||||
|
||||
// TODO(crbug/1350671): Use cros token for
|
||||
// kPrivacyIndicatorsBackgroundColor instead of constant values.
|
||||
if (dark_mode) {
|
||||
mixer[ui::kColorAshPrivacyIndicatorsBackground] = {
|
||||
SkColorSetRGB(0x37, 0xBE, 0x5F)};
|
||||
} else {
|
||||
mixer[ui::kColorAshPrivacyIndicatorsBackground] = {
|
||||
SkColorSetRGB(0x14, 0x6C, 0x2E)};
|
||||
}
|
||||
|
||||
mixer[ui::kColorAshAppListFocusRingNoKeyboard] = {SK_AlphaTRANSPARENT};
|
||||
mixer[ui::kColorAshAppListSeparatorLight] = {
|
||||
ui::kColorAshSystemUIMenuSeparator};
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/base/metadata/metadata_impl_macros.h"
|
||||
#include "ui/color/color_id.h"
|
||||
#include "ui/color/color_provider.h"
|
||||
#include "ui/compositor/animation_throughput_reporter.h"
|
||||
#include "ui/compositor/layer.h"
|
||||
#include "ui/compositor/layer_animator.h"
|
||||
@ -1485,10 +1486,21 @@ SkColor AshNotificationView::CalculateIconAndButtonsColor(
|
||||
if (!notification)
|
||||
return default_color;
|
||||
|
||||
auto color_id = notification->accent_color_id();
|
||||
absl::optional<SkColor> accent_color = notification->accent_color();
|
||||
if (!accent_color.has_value())
|
||||
|
||||
if ((!color_id || !GetWidget()) && !accent_color.has_value())
|
||||
return default_color;
|
||||
|
||||
SkColor fg_color;
|
||||
// ColorProvider needs widget to be created.
|
||||
if (color_id && GetWidget()) {
|
||||
fg_color = GetColorProvider()->GetColor(color_id.value());
|
||||
} else {
|
||||
fg_color = accent_color.value();
|
||||
}
|
||||
|
||||
// TODO(crbug/1351205): move color calculation logic to color mixer.
|
||||
// TODO(crbug/1294459): re-evaluate contrast, maybe increase or use fixed HSL
|
||||
float minContrastRatio =
|
||||
DarkLightModeControllerImpl::Get()->IsDarkModeEnabled()
|
||||
@ -1499,7 +1511,7 @@ SkColor AshNotificationView::CalculateIconAndButtonsColor(
|
||||
SkColor bg_color = AshColorProvider::Get()->GetBaseLayerColor(
|
||||
AshColorProvider::BaseLayerType::kOpaque);
|
||||
return color_utils::BlendForMinContrast(
|
||||
*accent_color, bg_color,
|
||||
fg_color, bg_color,
|
||||
/*high_contrast_foreground=*/absl::nullopt, minContrastRatio)
|
||||
.color;
|
||||
}
|
||||
|
@ -21,9 +21,11 @@ const char kPrivacyIndicatorsNotificationIdPrefix[] = "privacy-indicators";
|
||||
const char kPrivacyIndicatorsNotifierId[] = "ash.privacy-indicators";
|
||||
} // namespace
|
||||
|
||||
void ModifyPrivacyIndicatorsNotification(const std::string& app_id,
|
||||
bool camera_is_used,
|
||||
bool microphone_is_used) {
|
||||
void ModifyPrivacyIndicatorsNotification(
|
||||
const std::string& app_id,
|
||||
absl::optional<std::u16string> app_name,
|
||||
bool camera_is_used,
|
||||
bool microphone_is_used) {
|
||||
auto* message_center = message_center::MessageCenter::Get();
|
||||
std::string id = kPrivacyIndicatorsNotificationIdPrefix + app_id;
|
||||
bool notification_exist = message_center->FindVisibleNotificationById(id);
|
||||
@ -34,14 +36,23 @@ void ModifyPrivacyIndicatorsNotification(const std::string& app_id,
|
||||
return;
|
||||
}
|
||||
|
||||
std::u16string app_name_str = app_name.value_or(l10n_util::GetStringUTF16(
|
||||
IDS_PRIVACY_NOTIFICATION_MESSAGE_DEFAULT_APP_NAME));
|
||||
std::u16string title;
|
||||
std::u16string message;
|
||||
if (camera_is_used && microphone_is_used) {
|
||||
title = l10n_util::GetStringUTF16(
|
||||
IDS_PRIVACY_NOTIFICATION_TITLE_CAMERA_AND_MIC);
|
||||
message = l10n_util::GetStringFUTF16(
|
||||
IDS_PRIVACY_NOTIFICATION_MESSAGE_CAMERA_AND_MIC, app_name_str);
|
||||
} else if (camera_is_used) {
|
||||
title = l10n_util::GetStringUTF16(IDS_PRIVACY_NOTIFICATION_TITLE_CAMERA);
|
||||
message = l10n_util::GetStringFUTF16(
|
||||
IDS_PRIVACY_NOTIFICATION_MESSAGE_CAMERA, app_name_str);
|
||||
} else {
|
||||
title = l10n_util::GetStringUTF16(IDS_PRIVACY_NOTIFICATION_TITLE_MIC);
|
||||
message = l10n_util::GetStringFUTF16(IDS_PRIVACY_NOTIFICATION_MESSAGE_MIC,
|
||||
app_name_str);
|
||||
}
|
||||
|
||||
message_center::RichNotificationData optional_fields;
|
||||
@ -51,7 +62,7 @@ void ModifyPrivacyIndicatorsNotification(const std::string& app_id,
|
||||
|
||||
auto notification = CreateSystemNotification(
|
||||
message_center::NotificationType::NOTIFICATION_TYPE_SIMPLE, id, title,
|
||||
std::u16string(),
|
||||
message,
|
||||
/*display_source=*/std::u16string(),
|
||||
/*origin_url=*/GURL(),
|
||||
message_center::NotifierId(message_center::NotifierType::SYSTEM_COMPONENT,
|
||||
@ -61,6 +72,8 @@ void ModifyPrivacyIndicatorsNotification(const std::string& app_id,
|
||||
/*delegate=*/nullptr, kImeMenuMicrophoneIcon,
|
||||
message_center::SystemNotificationWarningLevel::NORMAL);
|
||||
|
||||
notification->set_accent_color_id(ui::kColorAshPrivacyIndicatorsBackground);
|
||||
|
||||
if (notification_exist) {
|
||||
message_center->UpdateNotification(id, std::move(notification));
|
||||
return;
|
||||
|
@ -8,14 +8,17 @@
|
||||
#include <string>
|
||||
|
||||
#include "ash/ash_export.h"
|
||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
|
||||
namespace ash {
|
||||
|
||||
// Add, update, or remove the privacy notification associated with the given
|
||||
// `app_id`.
|
||||
void ASH_EXPORT ModifyPrivacyIndicatorsNotification(const std::string& app_id,
|
||||
bool camera_is_used,
|
||||
bool microphone_is_used);
|
||||
void ASH_EXPORT
|
||||
ModifyPrivacyIndicatorsNotification(const std::string& app_id,
|
||||
absl::optional<std::u16string> app_name,
|
||||
bool camera_is_used,
|
||||
bool microphone_is_used);
|
||||
|
||||
} // namespace ash
|
||||
|
||||
|
@ -96,7 +96,7 @@ absl::optional<std::u16string> AppAccessNotifier::GetAppAccessingMicrophone() {
|
||||
// in that case instead of using DCHECK().
|
||||
if (!reg_cache || !cap_cache)
|
||||
return absl::nullopt;
|
||||
return GetAppAccessingMicrophone(cap_cache, reg_cache);
|
||||
return GetMostRecentAppAccessingMicrophone(cap_cache, reg_cache);
|
||||
}
|
||||
|
||||
void AppAccessNotifier::OnCapabilityAccessUpdate(
|
||||
@ -108,8 +108,11 @@ void AppAccessNotifier::OnCapabilityAccessUpdate(
|
||||
bool camera_is_used = update.Camera() == apps::mojom::OptionalBool::kTrue;
|
||||
|
||||
if (ash::features::IsPrivacyIndicatorsEnabled()) {
|
||||
ash::ModifyPrivacyIndicatorsNotification(update.AppId(), camera_is_used,
|
||||
microphone_is_used);
|
||||
auto app_id = update.AppId();
|
||||
ash::ModifyPrivacyIndicatorsNotification(
|
||||
app_id,
|
||||
GetAppShortNameFromAppId(app_id, GetActiveUserAppRegistryCache()),
|
||||
camera_is_used, microphone_is_used);
|
||||
}
|
||||
|
||||
if (microphone_is_used) {
|
||||
@ -147,6 +150,21 @@ void AppAccessNotifier::ActiveUserChanged(user_manager::User* active_user) {
|
||||
CheckActiveUserChanged();
|
||||
}
|
||||
|
||||
// static
|
||||
absl::optional<std::u16string> AppAccessNotifier::GetAppShortNameFromAppId(
|
||||
std::string app_id,
|
||||
apps::AppRegistryCache* registry_cache) {
|
||||
absl::optional<std::u16string> name;
|
||||
if (!registry_cache)
|
||||
return name;
|
||||
|
||||
registry_cache->ForEachApp([&app_id, &name](const apps::AppUpdate& update) {
|
||||
if (update.AppId() == app_id)
|
||||
name = base::UTF8ToUTF16(update.ShortName());
|
||||
});
|
||||
return name;
|
||||
}
|
||||
|
||||
AccountId AppAccessNotifier::GetActiveUserAccountId() {
|
||||
auto* manager = user_manager::UserManager::Get();
|
||||
const user_manager::User* active_user = manager->GetActiveUser();
|
||||
@ -178,7 +196,8 @@ AppAccessNotifier::GetActiveUserAppCapabilityAccessCache() {
|
||||
return GetAppCapabilityAccessCache(GetActiveUserAccountId());
|
||||
}
|
||||
|
||||
absl::optional<std::u16string> AppAccessNotifier::GetAppAccessingMicrophone(
|
||||
absl::optional<std::u16string>
|
||||
AppAccessNotifier::GetMostRecentAppAccessingMicrophone(
|
||||
apps::AppCapabilityAccessCache* capability_cache,
|
||||
apps::AppRegistryCache* registry_cache) {
|
||||
if (mic_using_app_ids[active_user_account_id_].empty())
|
||||
|
@ -44,12 +44,6 @@ class AppAccessNotifier
|
||||
// ash::MicrophoneMuteNotificationDelegate
|
||||
absl::optional<std::u16string> GetAppAccessingMicrophone() override;
|
||||
|
||||
// Returns the "short name" of the registered app to most recently attempt to
|
||||
// access the microphone, or an empty (optional) string if none exists.
|
||||
absl::optional<std::u16string> GetAppAccessingMicrophone(
|
||||
apps::AppCapabilityAccessCache* capability_cache,
|
||||
apps::AppRegistryCache* registry_cache);
|
||||
|
||||
// apps::AppCapabilityAccessCache::Observer
|
||||
void OnCapabilityAccessUpdate(
|
||||
const apps::CapabilityAccessUpdate& update) override;
|
||||
@ -62,6 +56,11 @@ class AppAccessNotifier
|
||||
// user_manager::UserManager::UserSessionStateObserver
|
||||
void ActiveUserChanged(user_manager::User* active_user) override;
|
||||
|
||||
// Get the app short name of the app with `app_id`.
|
||||
static absl::optional<std::u16string> GetAppShortNameFromAppId(
|
||||
std::string app_id,
|
||||
apps::AppRegistryCache* registry_cache);
|
||||
|
||||
protected:
|
||||
// Returns the active user's account ID if we have an active user, an empty
|
||||
// account ID otherwise.
|
||||
@ -73,10 +72,19 @@ class AppAccessNotifier
|
||||
void CheckActiveUserChanged();
|
||||
|
||||
private:
|
||||
friend class AppAccessNotifierTest;
|
||||
|
||||
// Returns the AppCapabilityAccessCache associated with the active user's
|
||||
// account ID.
|
||||
apps::AppCapabilityAccessCache* GetActiveUserAppCapabilityAccessCache();
|
||||
|
||||
// Returns the "short name" of the registered app to most recently attempt to
|
||||
// access the microphone, or an empty (optional) string if none exists. Used
|
||||
// for the microphone mute notification.
|
||||
absl::optional<std::u16string> GetMostRecentAppAccessingMicrophone(
|
||||
apps::AppCapabilityAccessCache* capability_cache,
|
||||
apps::AppRegistryCache* registry_cache);
|
||||
|
||||
// List of IDs of apps that have attempted to use the microphone, in order of
|
||||
// most-recently-launched.
|
||||
using MruAppIdList = std::list<std::string>;
|
||||
|
@ -110,8 +110,8 @@ class AppAccessNotifierTest : public testing::Test,
|
||||
account_id_primary_user_)
|
||||
? ®istry_cache_primary_user_
|
||||
: ®istry_cache_secondary_user_;
|
||||
return microphone_mute_notification_delegate_->GetAppAccessingMicrophone(
|
||||
cap_cache, reg_cache);
|
||||
return microphone_mute_notification_delegate_
|
||||
->GetMostRecentAppAccessingMicrophone(cap_cache, reg_cache);
|
||||
}
|
||||
|
||||
static apps::AppPtr MakeApp(const std::string app_id, const char* name) {
|
||||
@ -395,3 +395,13 @@ TEST_P(AppAccessNotifierTest, AppAccessNotification) {
|
||||
EXPECT_TRUE(message_center::MessageCenter::Get()->FindNotificationById(
|
||||
kPrivacyIndicatorsNotificationIdPrefix + id1));
|
||||
}
|
||||
|
||||
TEST_P(AppAccessNotifierTest, GetShortNameFromAppId) {
|
||||
// Test that GetAppShortNameFromAppId works properly.
|
||||
const std::string id = "test_app_id";
|
||||
LaunchAppUsingCameraOrMicrophone(id, "test_app_name", /*use_camera=*/false,
|
||||
/*use_microphone=*/true);
|
||||
EXPECT_EQ(AppAccessNotifier::GetAppShortNameFromAppId(
|
||||
id, ®istry_cache_primary_user_),
|
||||
u"test_app_name");
|
||||
}
|
||||
|
@ -210,6 +210,8 @@
|
||||
\
|
||||
E_CPONLY(kColorAshOnboardingFocusRing) \
|
||||
\
|
||||
E_CPONLY(kColorAshPrivacyIndicatorsBackground) \
|
||||
\
|
||||
/* TODO(crbug/1319917): Remove these when dark light mode is launched. */ \
|
||||
E_CPONLY(kColorAshSystemUILightBorderColor1) \
|
||||
E_CPONLY(kColorAshSystemUILightBorderColor2) \
|
||||
|
@ -185,6 +185,12 @@ class MESSAGE_CENTER_PUBLIC_EXPORT RichNotificationData {
|
||||
// SystemNotificationWarningLevel should be used.
|
||||
absl::optional<SkColor> accent_color;
|
||||
|
||||
// Similar to `accent_color`, but store a ColorId instead of SkColor so that
|
||||
// the notification view can use this id to correctly handle theme change. In
|
||||
// CrOS notification, if `accent_color_id` is provided, `accent_color` will
|
||||
// not be used.
|
||||
absl::optional<ui::ColorId> accent_color_id;
|
||||
|
||||
// Controls whether a settings button should appear on the notification. See
|
||||
// enum definition. TODO(estade): turn this into a boolean. See
|
||||
// crbug.com/780342
|
||||
@ -441,6 +447,13 @@ class MESSAGE_CENTER_PUBLIC_EXPORT Notification {
|
||||
optional_fields_.accent_color = accent_color;
|
||||
}
|
||||
|
||||
absl::optional<ui::ColorId> accent_color_id() const {
|
||||
return optional_fields_.accent_color_id;
|
||||
}
|
||||
void set_accent_color_id(ui::ColorId accent_color_id) {
|
||||
optional_fields_.accent_color_id = accent_color_id;
|
||||
}
|
||||
|
||||
bool should_show_settings_button() const {
|
||||
return optional_fields_.settings_button_handler !=
|
||||
SettingsButtonHandler::NONE;
|
||||
|
Reference in New Issue
Block a user