Skip to content

Commit

Permalink
focus-mode: Finalize the DND logic
Browse files Browse the repository at this point in the history
This CL modifies the DND logic according to the latest PRD. Also, we
added two unittests to go over four usecases listed in the PRD.

Bug: b/297980649
Test: Manual & unittest
Change-Id: I954ff0ac2c7194ebb4d2d55bbc26f470e184cafa
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4903001
Reviewed-by: Ahmed Mehfooz <amehfooz@chromium.org>
Commit-Queue: Hongyu Long <hongyulong@chromium.org>
Reviewed-by: Richard Chui <richui@chromium.org>
Reviewed-by: Ahmed Fakhry <afakhry@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1212553}
  • Loading branch information
Hongyu Long authored and Chromium LUCI CQ committed Oct 20, 2023
1 parent c43935e commit de5e705
Show file tree
Hide file tree
Showing 13 changed files with 188 additions and 115 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,10 @@ class MockMessageCenter : public message_center::FakeMessageCenter {
return visible_notifications_;
}

void SetQuietMode(bool in_quiet_mode) override {
void SetQuietMode(
bool in_quiet_mode,
message_center::QuietModeSourceType type =
message_center::QuietModeSourceType::kUserAction) override {
if (in_quiet_mode != in_quiet_mode_) {
in_quiet_mode_ = in_quiet_mode;
for (auto& observer : observer_list())
Expand Down
7 changes: 6 additions & 1 deletion ash/system/do_not_disturb_notification_controller.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,14 @@ const char kDoNotDisturbNotifierId[] =
std::unique_ptr<message_center::Notification> CreateNotification() {
auto* focus_mode_controller =
features::IsFocusModeEnabled() ? FocusModeController::Get() : nullptr;

// `should_show_focus_text` is true only when the notification needs to be
// turned off when the focus session ends.
const bool should_show_focus_text =
focus_mode_controller && focus_mode_controller->in_focus_session() &&
!focus_mode_controller->previous_do_not_disturb_state();
message_center::MessageCenter::Get()
->GetLastQuietModeChangeSourceType() ==
message_center::QuietModeSourceType::kFocusMode;

message_center::RichNotificationData optional_fields;
optional_fields.buttons.emplace_back(
Expand Down
62 changes: 34 additions & 28 deletions ash/system/focus_mode/focus_mode_controller.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ constexpr base::TimeDelta kDefaultSessionDuration = base::Minutes(25);
// duration is extended during a currently active focus session.
constexpr base::TimeDelta kExtendDuration = base::Minutes(10);

bool IsQuietModeOnSetByFocusMode() {
auto* message_center = message_center::MessageCenter::Get();
return message_center->IsQuietMode() &&
message_center->GetLastQuietModeChangeSourceType() ==
message_center::QuietModeSourceType::kFocusMode;
}

} // namespace

FocusModeController::FocusModeController()
Expand Down Expand Up @@ -76,42 +83,44 @@ void FocusModeController::ToggleFocusMode() {
auto* message_center = message_center::MessageCenter::Get();
CHECK(message_center);

in_focus_session_ = !in_focus_session_;
if (in_focus_session_) {
SaveSettingsToUserPrefs();

// Start timer for the specified `session_duration_`. Set `end_time_` before
// `SetQuietMode` called, because we may indirectly use `end_time_` to
// create a notification.
end_time_ = base::Time::Now() + session_duration_;
timer_.Start(FROM_HERE, base::Seconds(1), this,
&FocusModeController::OnTimerTick, base::TimeTicks::Now());

// Only for the case DND is not enabled before starting a session and
// `turn_on_do_not_disturb_` is true, we set `QuietModeSourceType` with
// `kFocusMode` type.
if (!message_center->IsQuietMode() && turn_on_do_not_disturb_) {
message_center->SetQuietMode(
true, message_center::QuietModeSourceType::kFocusMode);
}

SetFocusTrayVisibility(true);
} else {
timer_.Stop();

SetFocusTrayVisibility(false);

if (IsQuietModeOnSetByFocusMode()) {
message_center->SetQuietMode(
false, message_center::QuietModeSourceType::kFocusMode);
}

// Reset the `session_duration_` as it may have been changed during the
// focus session.
session_duration_ = Shell::Get()
->session_controller()
->GetActivePrefService()
->GetTimeDelta(prefs::kFocusModeSessionDuration);
} else {
SaveSettingsToUserPrefs();

// Update DND to the user selected state, and keep track of the state we
// want to revert back to after the Focus Mode session ends.
previous_do_not_disturb_state_ = message_center->IsQuietMode();

// Start timer for the specified `session_duration_`.
end_time_ = base::Time::Now() + session_duration_;
timer_.Start(FROM_HERE, base::Seconds(1), this,
&FocusModeController::OnTimerTick, base::TimeTicks::Now());

SetFocusTrayVisibility(true);
}

in_focus_session_ = !in_focus_session_;

// We'll update the DND notification according to if the DND is turned on in a
// focus session in `DoNotDisturbNotificationController::OnQuietModeChanged`,
// which is triggered by `SetQuietMode`. Thus, we need to call the function
// after `in_focus_session_` is updated.
message_center->SetQuietMode(in_focus_session_
? turn_on_do_not_disturb_
: previous_do_not_disturb_state_);

for (auto& observer : observers_) {
observer.OnFocusModeChanged(in_focus_session_);
}
Expand All @@ -135,11 +144,8 @@ void FocusModeController::ExtendActiveSessionDuration() {
// the next timer tick to update the UI.
OnTimerTick();

// If `previous_do_not_disturb_state_` is true, that means we did not create a
// notification indicating when DND will end. Hence, we don't need to update
// the notification. Otherwise, we will update the notification according to
// the new `end_time_` of the focus session.
if (previous_do_not_disturb_state_) {
// Only update the notification if DND was turned on by the focus mode.
if (!IsQuietModeOnSetByFocusMode()) {
return;
}

Expand Down
7 changes: 0 additions & 7 deletions ash/system/focus_mode/focus_mode_controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,6 @@ class ASH_EXPORT FocusModeController : public SessionObserver {
void set_turn_on_do_not_disturb(bool turn_on) {
turn_on_do_not_disturb_ = turn_on;
}
bool previous_do_not_disturb_state() const {
return previous_do_not_disturb_state_;
}

void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
Expand Down Expand Up @@ -105,10 +102,6 @@ class ASH_EXPORT FocusModeController : public SessionObserver {
// starts. Depends on previous session data (from user prefs) or user input.
bool turn_on_do_not_disturb_ = true;

// When a Focus Mode session starts, the previous DND state is stored so that
// it can be restored when the session ends.
bool previous_do_not_disturb_state_ = false;

base::ObserverList<Observer> observers_;
};

Expand Down
49 changes: 12 additions & 37 deletions ash/system/focus_mode/focus_mode_detailed_view.cc
Original file line number Diff line number Diff line change
Expand Up @@ -283,21 +283,21 @@ FocusModeDetailedView::FocusModeDetailedView(DetailedViewDelegate* delegate)
scene_view_->SetBorderInsets(gfx::Insets::VH(100, 0));
scene_view_->SetProperty(views::kMarginsKey, kContainerMargins);

FocusModeController* focus_mode_controller = FocusModeController::Get();
const bool in_focus_session = focus_mode_controller->in_focus_session();

CreateDoNotDisturbContainer();
do_not_disturb_view_->SetVisible(!in_focus_session);

scroll_content()->SizeToPreferredSize();

FocusModeController* focus_mode_controller = FocusModeController::Get();
if (!focus_mode_controller->in_focus_session()) {
if (!in_focus_session) {
StartClockTimer();
}

focus_mode_controller->AddObserver(this);
message_center::MessageCenter::Get()->AddObserver(this);
}

FocusModeDetailedView::~FocusModeDetailedView() {
message_center::MessageCenter::Get()->RemoveObserver(this);
FocusModeController::Get()->RemoveObserver(this);
}

Expand All @@ -310,16 +310,6 @@ void FocusModeDetailedView::AddedToWidget() {
}
}

void FocusModeDetailedView::OnQuietModeChanged(bool in_quiet_mode) {
// When focus mode is not in a session, the state of the
// `do_not_disturb_toggle_button_` will represent the initial state for the
// next focus session. Once the focus mode session begins, this button should
// be reflective of the actual system do not disturb state.
if (FocusModeController::Get()->in_focus_session()) {
do_not_disturb_toggle_button_->SetIsOn(in_quiet_mode);
}
}

void FocusModeDetailedView::OnFocusModeChanged(bool in_focus_session) {
// TODO(b/302194469): centralize bubble-closing logic.
if (in_focus_session) {
Expand All @@ -340,14 +330,8 @@ void FocusModeDetailedView::OnFocusModeChanged(bool in_focus_session) {

UpdateTimerView(in_focus_session);

if (in_focus_session) {
clock_timer_.Stop();
} else {
StartClockTimer();
}

do_not_disturb_toggle_button_->SetIsOn(
FocusModeController::Get()->turn_on_do_not_disturb());
StartClockTimer();
do_not_disturb_view_->SetVisible(true);
}

void FocusModeDetailedView::OnTimerTick() {
Expand Down Expand Up @@ -539,14 +523,7 @@ void FocusModeDetailedView::CreateDoNotDisturbContainer() {
l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DO_NOT_DISTURB));
auto* controller = FocusModeController::Get();

// The state of the toggle button is used for showing whether the
// do-not-disturb mode is on/off on the device while in a focus session.
// However, if there is no focus session running, it's used for representing
// if the user wants to turn on/off the do not disturb when the next focus
// session is started.
toggle->SetIsOn(controller->in_focus_session()
? message_center::MessageCenter::Get()->IsQuietMode()
: controller->turn_on_do_not_disturb());
toggle->SetIsOn(controller->turn_on_do_not_disturb());
do_not_disturb_toggle_button_ = toggle.get();
toggle_row->AddRightView(toggle.release());

Expand All @@ -556,12 +533,10 @@ void FocusModeDetailedView::CreateDoNotDisturbContainer() {

void FocusModeDetailedView::OnDoNotDisturbToggleClicked() {
auto* controller = FocusModeController::Get();
const bool is_on = do_not_disturb_toggle_button_->GetIsOn();
if (controller->in_focus_session()) {
message_center::MessageCenter::Get()->SetQuietMode(is_on);
} else {
controller->set_turn_on_do_not_disturb(is_on);
}
CHECK(!controller->in_focus_session());

controller->set_turn_on_do_not_disturb(
do_not_disturb_toggle_button_->GetIsOn());
}

void FocusModeDetailedView::OnClockMinutePassed() {
Expand Down
10 changes: 4 additions & 6 deletions ash/system/focus_mode/focus_mode_detailed_view.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#include "ash/system/tray/tray_detailed_view.h"
#include "base/timer/timer.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/message_center/message_center_observer.h"

namespace views {
class BoxLayoutView;
Expand All @@ -28,7 +27,6 @@ class SystemTextfield;
// This view displays the focus panel settings that a user can set.
class ASH_EXPORT FocusModeDetailedView
: public TrayDetailedView,
public message_center::MessageCenterObserver,
public FocusModeController::Observer {
public:
METADATA_HEADER(FocusModeDetailedView);
Expand All @@ -47,9 +45,6 @@ class ASH_EXPORT FocusModeDetailedView
void HandleViewClicked(views::View* view) override {}
void AddedToWidget() override;

// message_center::MessageCenterObserver:
void OnQuietModeChanged(bool in_quiet_mode) override;

// FocusModeController::Observer:
void OnFocusModeChanged(bool in_focus_session) override;
void OnTimerTick() override;
Expand All @@ -65,7 +60,10 @@ class ASH_EXPORT FocusModeDetailedView
// session based on whether focus is in session.
void UpdateTimerView(bool in_focus_session);

// Creates the DND rounded container.
// Creates the DND rounded container. This view will be visible only when
// there is no active focus session. The toggle button in this view will
// represent if we should toggle on the system DND state for a new focus
// session.
void CreateDoNotDisturbContainer();

// Handles clicks on the do not disturb toggle button.
Expand Down

0 comments on commit de5e705

Please sign in to comment.