Skip to content

Commit

Permalink
Migrate VC toasts to anchored nudges
Browse files Browse the repository at this point in the history
Update the SpeakOnMuteDetected and VideoConferenceTrayUseWhileDisabled
VC toasts to anchored nudges so they can be anchored to the appropriate
icon button - either the microphone or camera button in the VC tray.

Bug: b:272363803, b:280309896
Change-Id: Ifd216ef031e94c21bb107e3d3e8ab2a54999da1c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4545492
Reviewed-by: Alex Newcomer <newcomer@chromium.org>
Reviewed-by: James Cook <jamescook@chromium.org>
Commit-Queue: Kevin Radtke <kradtke@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1146628}
  • Loading branch information
Kevin Radtke authored and Chromium LUCI CQ committed May 19, 2023
1 parent 4adcb29 commit a0682c3
Show file tree
Hide file tree
Showing 8 changed files with 226 additions and 129 deletions.
13 changes: 9 additions & 4 deletions ash/constants/notifier_catalogs.h
Original file line number Diff line number Diff line change
Expand Up @@ -253,17 +253,22 @@ enum class ToastCatalogName {
kEcheTrayTabletModeNotSupported = 38,
kNotificationCenterTrayNoNotifications = 39,
kCopyToClipboardAction = 40,
kVideoConferenceTraySpeakOnMuteDetected = 41,
// [Deprecated] kVideoConferenceTraySpeakOnMuteDetected = 41,
kCopyGifToClipboardAction = 42,
kVideoConferenceTrayUseWhileDisabled = 43,
kMaxValue = kVideoConferenceTrayUseWhileDisabled,
// [Deprecated] kVideoConferenceTrayUseWhileDisabled = 43,
kMaxValue = kCopyGifToClipboardAction
};

// A living catalog that registers anchored nudges.
// Current values should not be renumbered or removed.
// TODO(b/280309972): Add metrics for AnchoredNudge catalog.
// To deprecate comment out the entry.
enum class AnchoredNudgeCatalogName { kTest = 0, kMaxValue = kTest };
enum class AnchoredNudgeCatalogName {
kTest = 0,
kVideoConferenceTraySpeakOnMuteDetected = 1,
kVideoConferenceTrayUseWhileDisabled = 2,
kMaxValue = kVideoConferenceTrayUseWhileDisabled
};

} // namespace ash

Expand Down
5 changes: 0 additions & 5 deletions ash/system/toast/anchored_nudge.cc
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,6 @@ AnchoredNudge::AnchoredNudge(Delegate* delegate,

AnchoredNudge::~AnchoredNudge() {
hover_observer_.reset();

// Make sure `delegate_` knows that the nudge has been closed, for cases where
// the nudge wasn't closed through the manager (e.g. widget destroyed by
// test).
delegate_->OnNudgeClosed(id_);
}

const std::u16string& AnchoredNudge::GetText() {
Expand Down
3 changes: 0 additions & 3 deletions ash/system/toast/anchored_nudge.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@ class ASH_EXPORT AnchoredNudge : public views::BubbleDialogDelegateView {
public:
virtual ~Delegate() {}

// Called when the nudge is being destroyed.
virtual void OnNudgeClosed(const std::string& id) = 0;

// Called when the mouse hover enters or exits the nudge.
virtual void OnNudgeHoverStateChanged(const std::string& id,
bool is_hovering) = 0;
Expand Down
55 changes: 47 additions & 8 deletions ash/system/toast/anchored_nudge_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,42 @@ class AnchoredNudgeManagerImpl::AnchorViewObserver
raw_ptr<AnchoredNudgeManagerImpl> anchored_nudge_manager_;
};

// A widget observer that is used to clean up the cached objects related to a
// nudge when its widget is destroying.
class AnchoredNudgeManagerImpl::NudgeWidgetObserver
: public views::WidgetObserver {
public:
NudgeWidgetObserver(AnchoredNudge* anchored_nudge,
AnchoredNudgeManagerImpl* anchored_nudge_manager)
: anchored_nudge_(anchored_nudge),
anchored_nudge_manager_(anchored_nudge_manager) {
anchored_nudge->GetWidget()->AddObserver(this);
}

NudgeWidgetObserver(const NudgeWidgetObserver&) = delete;

NudgeWidgetObserver& operator=(const NudgeWidgetObserver&) = delete;

~NudgeWidgetObserver() override {
if (anchored_nudge_ && anchored_nudge_->GetWidget()) {
anchored_nudge_->GetWidget()->RemoveObserver(this);
}
}

// WidgetObserver:
void OnWidgetDestroying(views::Widget* widget) override {
widget->RemoveObserver(this);
anchored_nudge_manager_->HandleNudgeWidgetDestroying(anchored_nudge_->id());
}

private:
// Owned by the views hierarchy.
raw_ptr<AnchoredNudge> anchored_nudge_;

// Owned by `Shell`.
raw_ptr<AnchoredNudgeManagerImpl> anchored_nudge_manager_;
};

AnchoredNudgeManagerImpl::AnchoredNudgeManagerImpl() = default;

AnchoredNudgeManagerImpl::~AnchoredNudgeManagerImpl() {
Expand Down Expand Up @@ -118,6 +154,9 @@ void AnchoredNudgeManagerImpl::Show(const AnchoredNudgeData& nudge_data) {

anchored_nudge_widget->Show();

nudge_widget_observers_[id] =
std::make_unique<NudgeWidgetObserver>(anchored_nudge_ptr, this);

anchor_view_observers_[id] = std::make_unique<AnchorViewObserver>(
anchored_nudge_ptr, anchor_view, this);

Expand All @@ -135,12 +174,8 @@ void AnchoredNudgeManagerImpl::Cancel(const std::string& id) {
return;
}

auto anchored_nudge_ptr = shown_nudges_[id];

dismiss_timers_.erase(id);
anchor_view_observers_.erase(id);
shown_nudges_.erase(id);
anchored_nudge_ptr->GetWidget()->CloseNow();
// Cache cleanup occurs on `HandleNudgeWidgetDestroying()`.
shown_nudges_[id]->GetWidget()->CloseNow();
}

void AnchoredNudgeManagerImpl::CloseAllNudges() {
Expand All @@ -149,8 +184,12 @@ void AnchoredNudgeManagerImpl::CloseAllNudges() {
}
}

void AnchoredNudgeManagerImpl::OnNudgeClosed(const std::string& id) {
Cancel(id);
void AnchoredNudgeManagerImpl::HandleNudgeWidgetDestroying(
const std::string& id) {
dismiss_timers_.erase(id);
anchor_view_observers_.erase(id);
nudge_widget_observers_.erase(id);
shown_nudges_.erase(id);
}

void AnchoredNudgeManagerImpl::OnNudgeHoverStateChanged(const std::string& id,
Expand Down
12 changes: 11 additions & 1 deletion ash/system/toast/anchored_nudge_manager_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@ class ASH_EXPORT AnchoredNudgeManagerImpl : public AnchoredNudgeManager,
// Closes all `shown_nudges_`.
void CloseAllNudges();

// Removes all cached objects (e.g. observers, timers) related to a nudge when
// its widget is destroying.
void HandleNudgeWidgetDestroying(const std::string& id);

// AnchoredNudge::Delegate:
void OnNudgeClosed(const std::string& id) override;
void OnNudgeHoverStateChanged(const std::string& id,
bool is_hovering) override;

Expand All @@ -49,6 +52,7 @@ class ASH_EXPORT AnchoredNudgeManagerImpl : public AnchoredNudgeManager,
private:
friend class AnchoredNudgeManagerImplTest;
class AnchorViewObserver;
class NudgeWidgetObserver;

// Manage the dismiss timer for the nudge with given `id`.
void StartDismissTimer(const std::string& id);
Expand All @@ -65,6 +69,12 @@ class ASH_EXPORT AnchoredNudgeManagerImpl : public AnchoredNudgeManager,
std::map<std::string, std::unique_ptr<AnchorViewObserver>>
anchor_view_observers_;

// Maps an `AnchoredNudge` `id` to an observation of that nudge's widget,
// which is used to clean up the cached objects related to that nudge when its
// widget is destroying.
std::map<std::string, std::unique_ptr<NudgeWidgetObserver>>
nudge_widget_observers_;

// Maps an `AnchoredNudge` `id` to a timer that's used to dismiss the nudge
// after `kAnchoredNudgeDuration` has passed.
std::map<std::string, base::OneShotTimer> dismiss_timers_;
Expand Down
59 changes: 32 additions & 27 deletions ash/system/video_conference/video_conference_tray_controller.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@
#include "ash/constants/ash_pref_names.h"
#include "ash/constants/notifier_catalogs.h"
#include "ash/public/cpp/shelf_types.h"
#include "ash/public/cpp/system/toast_data.h"
#include "ash/public/cpp/system/toast_manager.h"
#include "ash/public/cpp/system/anchored_nudge_data.h"
#include "ash/root_window_controller.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/icon_button.h"
#include "ash/system/status_area_widget.h"
#include "ash/system/toast/anchored_nudge_manager_impl.h"
#include "ash/system/video_conference/video_conference_common.h"
#include "ash/system/video_conference/video_conference_tray.h"
#include "base/check.h"
Expand All @@ -35,13 +35,13 @@
namespace ash {

namespace {
// The ID for the "Speak-on-mute detected" toast.
constexpr char kVideoConferenceTraySpeakOnMuteDetectedId[] =
"video_conference_tray_toast_ids.speak_on_mute_detected";
// The ID for the "Speak-on-mute detected" nudge.
constexpr char kVideoConferenceTraySpeakOnMuteDetectedNudgeId[] =
"video_conference_tray_nudge_ids.speak_on_mute_detected";

// The ID for the "use while disabled" toast.
constexpr char kVideoConferenceTrayUseWhileDisabledToastId[] =
"video_conference_tray_toast_ids.use_while_disable";
// The ID for the "use while disabled" nudge.
constexpr char kVideoConferenceTrayUseWhileDisabledNudgeId[] =
"video_conference_tray_nudge_ids.use_while_disabled";

// The cool down duration for speak-on-mute detection notification in seconds.
constexpr int KSpeakOnMuteNotificationCoolDownDuration = 60;
Expand All @@ -60,6 +60,13 @@ bool IsAnyShelfAutoHidden() {
return false;
}

VideoConferenceTray* GetVcTrayInActiveWindow() {
return RootWindowController::ForWindow(
Shell::Get()->GetRootWindowForNewWindows())
->GetStatusAreaWidget()
->video_conference_tray();
}

} // namespace

VideoConferenceTrayController::VideoConferenceTrayController() {
Expand Down Expand Up @@ -135,7 +142,7 @@ bool VideoConferenceTrayController::IsCapturingMicrophone() const {
void VideoConferenceTrayController::SetCameraMuted(bool muted) {
// If the camera is hardware-muted, do nothing here.
if (camera_muted_by_hardware_switch_) {
// TODO(b/272145024): Display a toast if camera button is clicked during
// TODO(b/272145024): Display a nudge if camera button is clicked during
// hardware-muted.
return;
}
Expand Down Expand Up @@ -277,15 +284,13 @@ void VideoConferenceTrayController::OnSpeakOnMuteDetected() {
if (!last_speak_on_mute_notification_time_.has_value() ||
(current_time - last_speak_on_mute_notification_time_.value())
.InSeconds() >= KSpeakOnMuteNotificationCoolDownDuration) {
ToastData toast_data(
kVideoConferenceTraySpeakOnMuteDetectedId,
ToastCatalogName::kVideoConferenceTraySpeakOnMuteDetected,
AnchoredNudgeData nudge_data(
kVideoConferenceTraySpeakOnMuteDetectedNudgeId,
AnchoredNudgeCatalogName::kVideoConferenceTraySpeakOnMuteDetected,
l10n_util::GetStringUTF16(
IDS_ASH_VIDEO_CONFERENCE_TOAST_SPEAK_ON_MUTE_DETECTED),
ToastData::kDefaultToastDuration,
/*visible_on_lock_screen=*/false);
toast_data.show_on_all_root_windows = true;
ToastManager::Get()->Show(std::move(toast_data));
/*anchor_view=*/GetVcTrayInActiveWindow()->audio_icon());
AnchoredNudgeManager::Get()->Show(nudge_data);

last_speak_on_mute_notification_time_.emplace(current_time);
}
Expand Down Expand Up @@ -375,37 +380,37 @@ void VideoConferenceTrayController::HandleDeviceUsedWhileDisabled(
// TODO(b/273570886): Handle the case when both camera and microphone are
// being used while disabled.
std::u16string device_name;
int toast_text_id;
int text_id;
views::View* anchor_view = nullptr;
switch (device) {
case crosapi::mojom::VideoConferenceMediaDevice::kMicrophone:
device_name =
l10n_util::GetStringUTF16(IDS_ASH_VIDEO_CONFERENCE_MICROPHONE_NAME);
toast_text_id =
text_id =
microphone_muted_by_hardware_switch_
? IDS_ASH_VIDEO_CONFERENCE_TOAST_USE_WHILE_HARDWARE_DISABLED
: IDS_ASH_VIDEO_CONFERENCE_TOAST_USE_WHILE_SOFTWARE_DISABLED;
anchor_view = GetVcTrayInActiveWindow()->audio_icon();
break;
case crosapi::mojom::VideoConferenceMediaDevice::kCamera:
device_name =
l10n_util::GetStringUTF16(IDS_ASH_VIDEO_CONFERENCE_CAMERA_NAME);
toast_text_id =
text_id =
camera_muted_by_hardware_switch_
? IDS_ASH_VIDEO_CONFERENCE_TOAST_USE_WHILE_HARDWARE_DISABLED
: IDS_ASH_VIDEO_CONFERENCE_TOAST_USE_WHILE_SOFTWARE_DISABLED;
anchor_view = GetVcTrayInActiveWindow()->camera_icon();
break;
default:
NOTREACHED();
return;
}

ToastData toast_data(
kVideoConferenceTrayUseWhileDisabledToastId,
ToastCatalogName::kVideoConferenceTrayUseWhileDisabled,
l10n_util::GetStringFUTF16(toast_text_id, app_name, device_name),
ToastData::kDefaultToastDuration,
/*visible_on_lock_screen=*/false);
toast_data.show_on_all_root_windows = true;
ToastManager::Get()->Show(std::move(toast_data));
AnchoredNudgeData nudge_data(
kVideoConferenceTrayUseWhileDisabledNudgeId,
AnchoredNudgeCatalogName::kVideoConferenceTrayUseWhileDisabled,
l10n_util::GetStringFUTF16(text_id, app_name, device_name), anchor_view);
AnchoredNudgeManager::Get()->Show(nudge_data);
}

void VideoConferenceTrayController::UpdateCameraIcons() {
Expand Down

0 comments on commit a0682c3

Please sign in to comment.