Revert "ash: night_light: Generalize Night Light scheduler and geoposition"
This reverts commit b08c9a689c
.
Reason for revert: Will redesign
Original change's description:
> ash: night_light: Generalize Night Light scheduler and geoposition
>
> - Migrate and generalize automatic Night Light scheduler
> (custom and sunset/sunrise timer) and time zone delegate to its
> own file `time_scheduler_controller`.
> - Move SimpleGeoPosition and TimeOfDay from ash/system/night_light
> to ash/system/time.
> - Modify unittests to support new format.
>
> Bug: 1225865
> Test: Manual and `ash_unittests --gtest_filter=*NightLight*`.
> Change-Id: I5f2fec6e9b22cc2776ab57db90e30f3bdf20ae0c
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3017139
> Commit-Queue: Cattalyya Nuengsigkapian <cattalyya@chromium.org>
> Reviewed-by: Kyle Horimoto <khorimoto@chromium.org>
> Reviewed-by: Xiaoqian Dai <xdai@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#901294}
Bug: 1225865
Change-Id: I11884d886c7c8da36af1df0c1e03ef04481fa2c4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3044603
Commit-Queue: Cattalyya Nuengsigkapian <cattalyya@chromium.org>
Reviewed-by: Ahmed Fakhry <afakhry@chromium.org>
Cr-Commit-Position: refs/heads/master@{#904984}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
91df5cbcca
commit
96d1ca3253
@ -1164,6 +1164,8 @@ component("ash") {
|
||||
"system/night_light/night_light_controller_impl.h",
|
||||
"system/night_light/night_light_feature_pod_controller.cc",
|
||||
"system/night_light/night_light_feature_pod_controller.h",
|
||||
"system/night_light/time_of_day.cc",
|
||||
"system/night_light/time_of_day.h",
|
||||
"system/overview/overview_button_tray.cc",
|
||||
"system/overview/overview_button_tray.h",
|
||||
"system/palette/common_palette_tool.cc",
|
||||
@ -1319,10 +1321,6 @@ component("ash") {
|
||||
"system/supervised/supervised_icon_string.h",
|
||||
"system/system_notification_controller.cc",
|
||||
"system/system_notification_controller.h",
|
||||
"system/time/time_of_day.cc",
|
||||
"system/time/time_of_day.h",
|
||||
"system/time/time_scheduler_controller.cc",
|
||||
"system/time/time_scheduler_controller.h",
|
||||
"system/time/time_tray_item_view.cc",
|
||||
"system/time/time_tray_item_view.h",
|
||||
"system/time/time_view.cc",
|
||||
@ -2301,6 +2299,7 @@ test("ash_unittests") {
|
||||
"system/network/vpn_list_unittest.cc",
|
||||
"system/network/wifi_toggle_notification_controller_unittest.cc",
|
||||
"system/night_light/night_light_controller_unittest.cc",
|
||||
"system/night_light/time_of_day_unittest.cc",
|
||||
"system/overview/overview_button_tray_unittest.cc",
|
||||
"system/palette/mock_palette_tool_delegate.cc",
|
||||
"system/palette/mock_palette_tool_delegate.h",
|
||||
@ -2336,7 +2335,6 @@ test("ash_unittests") {
|
||||
"system/session/logout_confirmation_controller_unittest.cc",
|
||||
"system/session/session_limit_notification_controller_unittest.cc",
|
||||
"system/status_area_widget_unittest.cc",
|
||||
"system/time/time_of_day_unittest.cc",
|
||||
"system/time/time_tray_item_view_unittest.cc",
|
||||
"system/time/time_view_unittest.cc",
|
||||
"system/toast/toast_manager_unittest.cc",
|
||||
|
@ -31,7 +31,7 @@
|
||||
#include "ash/style/ash_color_provider.h"
|
||||
#include "ash/system/model/clock_model.h"
|
||||
#include "ash/system/model/system_tray_model.h"
|
||||
#include "ash/system/time/time_of_day.h"
|
||||
#include "ash/system/night_light/time_of_day.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/i18n/time_formatting.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
|
@ -280,7 +280,6 @@ component("cpp") {
|
||||
"shell_window_ids.h",
|
||||
"shutdown_controller.cc",
|
||||
"shutdown_controller.h",
|
||||
"simple_geo_position.h",
|
||||
"style/color_mode_observer.h",
|
||||
"style/color_provider.cc",
|
||||
"style/color_provider.h",
|
||||
|
@ -6,7 +6,6 @@
|
||||
#define ASH_PUBLIC_CPP_NIGHT_LIGHT_CONTROLLER_H_
|
||||
|
||||
#include "ash/public/cpp/ash_public_export.h"
|
||||
#include "ash/public/cpp/simple_geo_position.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/observer_list.h"
|
||||
|
||||
@ -32,6 +31,22 @@ class ASH_PUBLIC_EXPORT NightLightController {
|
||||
kMaxValue = kCustom,
|
||||
};
|
||||
|
||||
// Represents a geolocation position fix. It's "simple" because it doesn't
|
||||
// expose all the parameters of the position interface as defined by the
|
||||
// Geolocation API Specification:
|
||||
// https://dev.w3.org/geo/api/spec-source.html#position_interface
|
||||
// The NightLightController is only interested in valid latitude and
|
||||
// longitude. It also doesn't require any specific accuracy. The more accurate
|
||||
// the positions, the more accurate sunset and sunrise times calculations.
|
||||
// However, an IP-based geoposition is considered good enough.
|
||||
struct SimpleGeoposition {
|
||||
bool operator==(const SimpleGeoposition& other) const {
|
||||
return latitude == other.latitude && longitude == other.longitude;
|
||||
}
|
||||
double latitude;
|
||||
double longitude;
|
||||
};
|
||||
|
||||
class Observer {
|
||||
public:
|
||||
// Notifies observers with the new schedule type whenever it changes.
|
||||
|
@ -1,27 +0,0 @@
|
||||
// Copyright 2021 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ASH_PUBLIC_CPP_SIMPLE_GEO_POSITION_H_
|
||||
#define ASH_PUBLIC_CPP_SIMPLE_GEO_POSITION_H_
|
||||
|
||||
namespace ash {
|
||||
// Represents a geolocation position fix. It's "simple" because it doesn't
|
||||
// expose all the parameters of the position interface as defined by the
|
||||
// Geolocation API Specification:
|
||||
// https://dev.w3.org/geo/api/spec-source.html#position_interface
|
||||
// The NightLightController is only interested in valid latitude and
|
||||
// longitude. It also doesn't require any specific accuracy. The more accurate
|
||||
// the positions, the more accurate sunset and sunrise times calculations.
|
||||
// However, an IP-based geoposition is considered good enough.
|
||||
struct SimpleGeoposition {
|
||||
bool operator==(const SimpleGeoposition& other) const {
|
||||
return latitude == other.latitude && longitude == other.longitude;
|
||||
}
|
||||
double latitude;
|
||||
double longitude;
|
||||
};
|
||||
|
||||
} // namespace ash
|
||||
|
||||
#endif // ASH_PUBLIC_CPP_SIMPLE_GEO_POSITION_H_
|
@ -6,12 +6,10 @@
|
||||
|
||||
#include "ash/display/screen_orientation_controller.h"
|
||||
#include "ash/display/screen_orientation_controller_test_api.h"
|
||||
#include "ash/public/cpp/simple_geo_position.h"
|
||||
#include "ash/shell.h"
|
||||
#include "ash/system/machine_learning/user_settings_event.pb.h"
|
||||
#include "ash/system/night_light/night_light_controller_impl.h"
|
||||
#include "ash/system/power/power_status.h"
|
||||
#include "ash/system/time/time_scheduler_controller.h"
|
||||
#include "ash/test/ash_test_base.h"
|
||||
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
|
||||
#include "base/test/simple_test_clock.h"
|
||||
@ -67,13 +65,12 @@ NetworkStatePropertiesPtr CreateCellularNetwork(int signal_strength) {
|
||||
return network;
|
||||
}
|
||||
|
||||
class FakeTimeSchedulerDelegate : public TimeSchedulerController::Delegate {
|
||||
class FakeNightLightDelegate : public NightLightControllerImpl::Delegate {
|
||||
public:
|
||||
FakeTimeSchedulerDelegate() = default;
|
||||
~FakeTimeSchedulerDelegate() override = default;
|
||||
FakeTimeSchedulerDelegate(const FakeTimeSchedulerDelegate&) = delete;
|
||||
FakeTimeSchedulerDelegate& operator=(const FakeTimeSchedulerDelegate&) =
|
||||
delete;
|
||||
FakeNightLightDelegate() = default;
|
||||
~FakeNightLightDelegate() override = default;
|
||||
FakeNightLightDelegate(const FakeNightLightDelegate&) = delete;
|
||||
FakeNightLightDelegate& operator=(const FakeNightLightDelegate&) = delete;
|
||||
|
||||
void SetFakeNow(TimeOfDay time) { fake_now_ = time.ToTimeToday(); }
|
||||
void SetFakeSunset(TimeOfDay time) { fake_sunset_ = time.ToTimeToday(); }
|
||||
@ -83,7 +80,8 @@ class FakeTimeSchedulerDelegate : public TimeSchedulerController::Delegate {
|
||||
base::Time GetNow() const override { return fake_now_; }
|
||||
base::Time GetSunsetTime() const override { return fake_sunset_; }
|
||||
base::Time GetSunriseTime() const override { return fake_sunrise_; }
|
||||
bool SetGeoposition(const SimpleGeoposition& position) override {
|
||||
bool SetGeoposition(
|
||||
const NightLightController::SimpleGeoposition& position) override {
|
||||
return false;
|
||||
}
|
||||
bool HasGeoposition() const override { return false; }
|
||||
@ -262,7 +260,7 @@ TEST_F(UserSettingsEventLoggerTest, TestLogNightLightEvent) {
|
||||
}
|
||||
|
||||
TEST_F(UserSettingsEventLoggerTest, TestNightLightSunsetFeature) {
|
||||
auto night_light_delegate = std::make_unique<FakeTimeSchedulerDelegate>();
|
||||
auto night_light_delegate = std::make_unique<FakeNightLightDelegate>();
|
||||
auto* night_light = night_light_delegate.get();
|
||||
Shell::Get()->night_light_controller()->SetDelegateForTesting(
|
||||
std::move(night_light_delegate));
|
||||
|
@ -18,8 +18,8 @@
|
||||
#include "ash/shell.h"
|
||||
#include "ash/strings/grit/ash_strings.h"
|
||||
#include "ash/system/model/system_tray_model.h"
|
||||
#include "ash/system/time/time_scheduler_controller.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/i18n/time_formatting.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/metrics/histogram_macros.h"
|
||||
#include "base/numerics/ranges.h"
|
||||
@ -76,6 +76,12 @@ constexpr char kAutoNightLightNotificationShownHistogram[] =
|
||||
constexpr char kNotifierId[] = "ash.night_light_controller_impl";
|
||||
constexpr char kNotificationId[] = "ash.auto_night_light_notify";
|
||||
|
||||
// Default start time at 6:00 PM as an offset from 00:00.
|
||||
constexpr int kDefaultStartTimeOffsetMinutes = 18 * 60;
|
||||
|
||||
// Default end time at 6:00 AM as an offset from 00:00.
|
||||
constexpr int kDefaultEndTimeOffsetMinutes = 6 * 60;
|
||||
|
||||
constexpr float kDefaultColorTemperature = 0.5f;
|
||||
|
||||
// The duration of the temperature change animation for
|
||||
@ -97,6 +103,62 @@ constexpr float kMinColorTemperatureInKelvin = 4500;
|
||||
constexpr float kNeutralColorTemperatureInKelvin = 6500;
|
||||
constexpr float kMaxColorTemperatureInKelvin = 7500;
|
||||
|
||||
class NightLightControllerDelegateImpl
|
||||
: public NightLightControllerImpl::Delegate {
|
||||
public:
|
||||
NightLightControllerDelegateImpl() = default;
|
||||
NightLightControllerDelegateImpl(const NightLightControllerDelegateImpl&) =
|
||||
delete;
|
||||
NightLightControllerDelegateImpl& operator=(
|
||||
const NightLightControllerDelegateImpl&) = delete;
|
||||
~NightLightControllerDelegateImpl() override = default;
|
||||
|
||||
// ash::NightLightControllerImpl::Delegate:
|
||||
base::Time GetNow() const override { return base::Time::Now(); }
|
||||
base::Time GetSunsetTime() const override { return GetSunRiseSet(false); }
|
||||
base::Time GetSunriseTime() const override { return GetSunRiseSet(true); }
|
||||
bool SetGeoposition(
|
||||
const NightLightController::SimpleGeoposition& position) override {
|
||||
if (geoposition_ && *geoposition_ == position)
|
||||
return false;
|
||||
|
||||
geoposition_ =
|
||||
std::make_unique<NightLightController::SimpleGeoposition>(position);
|
||||
return true;
|
||||
}
|
||||
bool HasGeoposition() const override { return !!geoposition_; }
|
||||
|
||||
private:
|
||||
// Note that the below computation is intentionally performed every time
|
||||
// GetSunsetTime() or GetSunriseTime() is called rather than once whenever we
|
||||
// receive a geoposition (which happens at least once a day). This increases
|
||||
// the chances of getting accurate values, especially around DST changes.
|
||||
base::Time GetSunRiseSet(bool sunrise) const {
|
||||
if (!HasGeoposition()) {
|
||||
LOG(ERROR) << "Invalid geoposition. Using default time for "
|
||||
<< (sunrise ? "sunrise." : "sunset.");
|
||||
return sunrise ? TimeOfDay(kDefaultEndTimeOffsetMinutes).ToTimeToday()
|
||||
: TimeOfDay(kDefaultStartTimeOffsetMinutes).ToTimeToday();
|
||||
}
|
||||
|
||||
icu::CalendarAstronomer astro(geoposition_->longitude,
|
||||
geoposition_->latitude);
|
||||
// For sunset and sunrise times calculations to be correct, the time of the
|
||||
// icu::CalendarAstronomer object should be set to a time near local noon.
|
||||
// This avoids having the computation flopping over into an adjacent day.
|
||||
// See the documentation of icu::CalendarAstronomer::getSunRiseSet().
|
||||
// Note that the icu calendar works with milliseconds since epoch, and
|
||||
// base::Time::FromDoubleT() / ToDoubleT() work with seconds since epoch.
|
||||
const double midday_today_sec =
|
||||
TimeOfDay(12 * 60).ToTimeToday().ToDoubleT();
|
||||
astro.setTime(midday_today_sec * 1000.0);
|
||||
const double sun_rise_set_ms = astro.getSunRiseSet(sunrise);
|
||||
return base::Time::FromDoubleT(sun_rise_set_ms / 1000.0);
|
||||
}
|
||||
|
||||
std::unique_ptr<NightLightController::SimpleGeoposition> geoposition_;
|
||||
};
|
||||
|
||||
// Returns the color temperature range bucket in which |temperature| resides.
|
||||
// The range buckets are:
|
||||
// 0 => Range [0 : 20) (least warm).
|
||||
@ -276,6 +338,9 @@ class ColorTemperatureAnimation : public gfx::LinearAnimation,
|
||||
: gfx::LinearAnimation(kManualAnimationDuration,
|
||||
kNightLightAnimationFrameRate,
|
||||
this) {}
|
||||
ColorTemperatureAnimation(const ColorTemperatureAnimation&) = delete;
|
||||
ColorTemperatureAnimation& operator=(const ColorTemperatureAnimation&) =
|
||||
delete;
|
||||
~ColorTemperatureAnimation() override = default;
|
||||
|
||||
float target_temperature() const { return target_temperature_; }
|
||||
@ -332,21 +397,12 @@ class ColorTemperatureAnimation : public gfx::LinearAnimation,
|
||||
float current_temperature_ = 0.0f;
|
||||
float target_temperature_ = 0.0f;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ColorTemperatureAnimation);
|
||||
};
|
||||
|
||||
NightLightControllerImpl::NightLightControllerImpl()
|
||||
: temperature_animation_(std::make_unique<ColorTemperatureAnimation>()),
|
||||
: delegate_(std::make_unique<NightLightControllerDelegateImpl>()),
|
||||
temperature_animation_(std::make_unique<ColorTemperatureAnimation>()),
|
||||
ambient_temperature_(kNeutralColorTemperatureInKelvin),
|
||||
time_scheduler_controller_(std::make_unique<TimeSchedulerController>(
|
||||
prefs::kNightLightEnabled,
|
||||
prefs::kNightLightCachedLatitude,
|
||||
prefs::kNightLightCachedLongitude,
|
||||
base::BindRepeating(&NightLightControllerImpl::SetEnabled,
|
||||
base::Unretained(this)),
|
||||
base::BindRepeating(
|
||||
&NightLightControllerImpl::RefreshDisplaysTemperature,
|
||||
base::Unretained(this)))),
|
||||
weak_ptr_factory_(this) {
|
||||
Shell::Get()->session_controller()->AddObserver(this);
|
||||
Shell::Get()->window_tree_host_manager()->AddObserver(this);
|
||||
@ -372,12 +428,10 @@ void NightLightControllerImpl::RegisterProfilePrefs(
|
||||
: ScheduleType::kNone;
|
||||
registry->RegisterIntegerPref(prefs::kNightLightScheduleType,
|
||||
static_cast<int>(default_schedule_type));
|
||||
registry->RegisterIntegerPref(
|
||||
prefs::kNightLightCustomStartTime,
|
||||
TimeSchedulerController::kDefaultStartTimeOffsetMinutes);
|
||||
registry->RegisterIntegerPref(
|
||||
prefs::kNightLightCustomEndTime,
|
||||
TimeSchedulerController::kDefaultEndTimeOffsetMinutes);
|
||||
registry->RegisterIntegerPref(prefs::kNightLightCustomStartTime,
|
||||
kDefaultStartTimeOffsetMinutes);
|
||||
registry->RegisterIntegerPref(prefs::kNightLightCustomEndTime,
|
||||
kDefaultEndTimeOffsetMinutes);
|
||||
registry->RegisterBooleanPref(prefs::kAmbientColorEnabled, true);
|
||||
registry->RegisterBooleanPref(prefs::kAutoNightLightNotificationDismissed,
|
||||
false);
|
||||
@ -480,10 +534,6 @@ NightLightControllerImpl::ColorScalesFromRemappedTemperatureInKevin(
|
||||
return {red, green, blue};
|
||||
}
|
||||
|
||||
bool NightLightControllerImpl::is_current_geoposition_from_cache() const {
|
||||
return time_scheduler_controller_->is_current_geoposition_from_cache();
|
||||
}
|
||||
|
||||
float NightLightControllerImpl::GetColorTemperature() const {
|
||||
if (active_user_pref_service_)
|
||||
return active_user_pref_service_->GetDouble(prefs::kNightLightTemperature);
|
||||
@ -513,7 +563,7 @@ TimeOfDay NightLightControllerImpl::GetCustomStartTime() const {
|
||||
prefs::kNightLightCustomStartTime));
|
||||
}
|
||||
|
||||
return TimeOfDay(TimeSchedulerController::kDefaultStartTimeOffsetMinutes);
|
||||
return TimeOfDay(kDefaultStartTimeOffsetMinutes);
|
||||
}
|
||||
|
||||
TimeOfDay NightLightControllerImpl::GetCustomEndTime() const {
|
||||
@ -522,7 +572,7 @@ TimeOfDay NightLightControllerImpl::GetCustomEndTime() const {
|
||||
active_user_pref_service_->GetInteger(prefs::kNightLightCustomEndTime));
|
||||
}
|
||||
|
||||
return TimeOfDay(TimeSchedulerController::kDefaultEndTimeOffsetMinutes);
|
||||
return TimeOfDay(kDefaultEndTimeOffsetMinutes);
|
||||
}
|
||||
|
||||
void NightLightControllerImpl::SetAmbientColorEnabled(bool enabled) {
|
||||
@ -537,19 +587,14 @@ bool NightLightControllerImpl::GetAmbientColorEnabled() const {
|
||||
|
||||
bool NightLightControllerImpl::IsNowWithinSunsetSunrise() const {
|
||||
// The times below are all on the same calendar day.
|
||||
auto* delegate = time_scheduler_controller_->delegate();
|
||||
const base::Time now = delegate->GetNow();
|
||||
return now < delegate->GetSunriseTime() || now > delegate->GetSunsetTime();
|
||||
const base::Time now = delegate_->GetNow();
|
||||
return now < delegate_->GetSunriseTime() || now > delegate_->GetSunsetTime();
|
||||
}
|
||||
|
||||
void NightLightControllerImpl::SetEnabled(
|
||||
bool enabled,
|
||||
TimeSchedulerController::TimeSetterSource source) {
|
||||
void NightLightControllerImpl::SetEnabled(bool enabled,
|
||||
AnimationDuration animation_type) {
|
||||
if (active_user_pref_service_) {
|
||||
animation_duration_ =
|
||||
source == TimeSchedulerController::TimeSetterSource::kAutomatic
|
||||
? AnimationDuration::kLong
|
||||
: AnimationDuration::kShort;
|
||||
animation_duration_ = animation_type;
|
||||
active_user_pref_service_->SetBoolean(prefs::kNightLightEnabled, enabled);
|
||||
}
|
||||
}
|
||||
@ -587,7 +632,7 @@ void NightLightControllerImpl::SetCustomEndTime(TimeOfDay end_time) {
|
||||
}
|
||||
|
||||
void NightLightControllerImpl::Toggle() {
|
||||
SetEnabled(!GetEnabled(), TimeSchedulerController::TimeSetterSource::kUser);
|
||||
SetEnabled(!GetEnabled(), AnimationDuration::kShort);
|
||||
}
|
||||
|
||||
void NightLightControllerImpl::OnDisplayConfigurationChanged() {
|
||||
@ -629,13 +674,14 @@ void NightLightControllerImpl::OnActiveUserPrefServiceChanged(
|
||||
void NightLightControllerImpl::SetCurrentGeoposition(
|
||||
const SimpleGeoposition& position) {
|
||||
VLOG(1) << "Received new geoposition.";
|
||||
time_scheduler_controller_->StoreCachedGeoposition(position);
|
||||
|
||||
auto* delegate = time_scheduler_controller_->delegate();
|
||||
const base::Time previous_sunset = delegate->GetSunsetTime();
|
||||
const base::Time previous_sunrise = delegate->GetSunriseTime();
|
||||
is_current_geoposition_from_cache_ = false;
|
||||
StoreCachedGeoposition(position);
|
||||
|
||||
if (!delegate->SetGeoposition(position)) {
|
||||
const base::Time previous_sunset = delegate_->GetSunsetTime();
|
||||
const base::Time previous_sunrise = delegate_->GetSunriseTime();
|
||||
|
||||
if (!delegate_->SetGeoposition(position)) {
|
||||
VLOG(1) << "Not refreshing since geoposition hasn't changed";
|
||||
return;
|
||||
}
|
||||
@ -652,9 +698,9 @@ void NightLightControllerImpl::SetCurrentGeoposition(
|
||||
// toggles should be ignored.
|
||||
constexpr base::TimeDelta kOneHourDuration = base::TimeDelta::FromHours(1);
|
||||
const bool keep_manual_toggles_during_schedules =
|
||||
(delegate->GetSunsetTime() - previous_sunset).magnitude() <
|
||||
(delegate_->GetSunsetTime() - previous_sunset).magnitude() <
|
||||
kOneHourDuration &&
|
||||
(delegate->GetSunriseTime() - previous_sunrise).magnitude() <
|
||||
(delegate_->GetSunriseTime() - previous_sunrise).magnitude() <
|
||||
kOneHourDuration;
|
||||
|
||||
Refresh(/*did_schedule_change=*/true, keep_manual_toggles_during_schedules);
|
||||
@ -729,21 +775,8 @@ void NightLightControllerImpl::AmbientColorChanged(
|
||||
}
|
||||
|
||||
void NightLightControllerImpl::SetDelegateForTesting(
|
||||
std::unique_ptr<TimeSchedulerController::Delegate> delegate) {
|
||||
time_scheduler_controller_->SetDelegateForTesting( // IN-TEST
|
||||
std::move(delegate));
|
||||
}
|
||||
|
||||
bool NightLightControllerImpl::GetIsTimerRunningForTesting() {
|
||||
return time_scheduler_controller_->timer()->IsRunning();
|
||||
}
|
||||
|
||||
base::TimeDelta NightLightControllerImpl::GetCurrentTimerDelayForTesting() {
|
||||
return time_scheduler_controller_->timer()->GetCurrentDelay();
|
||||
}
|
||||
|
||||
void NightLightControllerImpl::FireTimerForTesting() {
|
||||
return time_scheduler_controller_->timer()->FireNow();
|
||||
std::unique_ptr<Delegate> delegate) {
|
||||
delegate_ = std::move(delegate);
|
||||
}
|
||||
|
||||
message_center::Notification*
|
||||
@ -752,6 +785,26 @@ NightLightControllerImpl::GetAutoNightLightNotificationForTesting() const {
|
||||
kNotificationId);
|
||||
}
|
||||
|
||||
bool NightLightControllerImpl::MaybeRestoreSchedule() {
|
||||
DCHECK(active_user_pref_service_);
|
||||
DCHECK_NE(GetScheduleType(), ScheduleType::kNone);
|
||||
|
||||
auto iter = per_user_schedule_target_state_.find(active_user_pref_service_);
|
||||
if (iter == per_user_schedule_target_state_.end())
|
||||
return false;
|
||||
|
||||
ScheduleTargetState& target_state = iter->second;
|
||||
// It may be that the device was suspended for a very long time that the
|
||||
// target time is no longer valid.
|
||||
if (target_state.target_time <= delegate_->GetNow())
|
||||
return false;
|
||||
|
||||
VLOG(1) << "Restoring a previous schedule.";
|
||||
DCHECK_NE(GetEnabled(), target_state.target_status);
|
||||
ScheduleNextToggle(target_state.target_time - delegate_->GetNow());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NightLightControllerImpl::UserHasEverChangedSchedule() const {
|
||||
return active_user_pref_service_ &&
|
||||
active_user_pref_service_->HasPrefPath(prefs::kNightLightScheduleType);
|
||||
@ -801,8 +854,53 @@ void NightLightControllerImpl::
|
||||
}
|
||||
}
|
||||
|
||||
void NightLightControllerImpl::RefreshDisplaysTemperature() {
|
||||
const float new_temperature = GetEnabled() ? GetColorTemperature() : 0.0f;
|
||||
void NightLightControllerImpl::LoadCachedGeopositionIfNeeded() {
|
||||
DCHECK(active_user_pref_service_);
|
||||
|
||||
// Even if there is a geoposition, but it's coming from a previously cached
|
||||
// value, switching users should load the currently saved values for the
|
||||
// new user. This is to keep users' prefs completely separate. We only ignore
|
||||
// the cached values once we have a valid non-cached geoposition from any
|
||||
// user in the same session.
|
||||
if (delegate_->HasGeoposition() && !is_current_geoposition_from_cache_)
|
||||
return;
|
||||
|
||||
if (!active_user_pref_service_->HasPrefPath(
|
||||
prefs::kNightLightCachedLatitude) ||
|
||||
!active_user_pref_service_->HasPrefPath(
|
||||
prefs::kNightLightCachedLongitude)) {
|
||||
VLOG(1) << "No valid current geoposition and no valid cached geoposition"
|
||||
" are available. Will use default times for sunset / sunrise.";
|
||||
return;
|
||||
}
|
||||
|
||||
VLOG(1) << "Temporarily using a previously cached geoposition.";
|
||||
delegate_->SetGeoposition(SimpleGeoposition{
|
||||
active_user_pref_service_->GetDouble(prefs::kNightLightCachedLatitude),
|
||||
active_user_pref_service_->GetDouble(prefs::kNightLightCachedLongitude)});
|
||||
is_current_geoposition_from_cache_ = true;
|
||||
}
|
||||
|
||||
void NightLightControllerImpl::StoreCachedGeoposition(
|
||||
const SimpleGeoposition& position) {
|
||||
const SessionControllerImpl* session_controller =
|
||||
Shell::Get()->session_controller();
|
||||
for (const auto& user_session : session_controller->GetUserSessions()) {
|
||||
PrefService* pref_service = session_controller->GetUserPrefServiceForUser(
|
||||
user_session->user_info.account_id);
|
||||
if (!pref_service)
|
||||
continue;
|
||||
|
||||
pref_service->SetDouble(prefs::kNightLightCachedLatitude,
|
||||
position.latitude);
|
||||
pref_service->SetDouble(prefs::kNightLightCachedLongitude,
|
||||
position.longitude);
|
||||
}
|
||||
}
|
||||
|
||||
void NightLightControllerImpl::RefreshDisplaysTemperature(
|
||||
float color_temperature) {
|
||||
const float new_temperature = GetEnabled() ? color_temperature : 0.0f;
|
||||
temperature_animation_->AnimateToNewValue(
|
||||
new_temperature, animation_duration_ == AnimationDuration::kShort
|
||||
? kManualAnimationDuration
|
||||
@ -871,8 +969,7 @@ void NightLightControllerImpl::StartWatchingPrefsChanges() {
|
||||
|
||||
void NightLightControllerImpl::InitFromUserPrefs() {
|
||||
StartWatchingPrefsChanges();
|
||||
time_scheduler_controller_->LoadCachedGeopositionIfNeeded(
|
||||
active_user_pref_service_);
|
||||
LoadCachedGeopositionIfNeeded();
|
||||
if (GetAmbientColorEnabled())
|
||||
UpdateAmbientRgbScalingFactors();
|
||||
Refresh(/*did_schedule_change=*/true,
|
||||
@ -935,7 +1032,7 @@ void NightLightControllerImpl::OnColorTemperaturePrefChanged() {
|
||||
UMA_HISTOGRAM_EXACT_LINEAR(
|
||||
"Ash.NightLight.Temperature", GetTemperatureRange(color_temperature),
|
||||
5 /* number of buckets defined in GetTemperatureRange() */);
|
||||
RefreshDisplaysTemperature();
|
||||
RefreshDisplaysTemperature(color_temperature);
|
||||
}
|
||||
|
||||
void NightLightControllerImpl::OnScheduleTypePrefChanged() {
|
||||
@ -959,16 +1056,16 @@ void NightLightControllerImpl::Refresh(
|
||||
bool keep_manual_toggles_during_schedules) {
|
||||
switch (GetScheduleType()) {
|
||||
case ScheduleType::kNone:
|
||||
time_scheduler_controller_->StopTimer();
|
||||
RefreshDisplaysTemperature();
|
||||
timer_.Stop();
|
||||
RefreshDisplaysTemperature(GetColorTemperature());
|
||||
return;
|
||||
case ScheduleType::kSunsetToSunrise: {
|
||||
auto* delegate = time_scheduler_controller_->delegate();
|
||||
RefreshScheduleTimer(delegate->GetSunsetTime(),
|
||||
delegate->GetSunriseTime(), did_schedule_change,
|
||||
|
||||
case ScheduleType::kSunsetToSunrise:
|
||||
RefreshScheduleTimer(delegate_->GetSunsetTime(),
|
||||
delegate_->GetSunriseTime(), did_schedule_change,
|
||||
keep_manual_toggles_during_schedules);
|
||||
return;
|
||||
}
|
||||
|
||||
case ScheduleType::kCustom:
|
||||
RefreshScheduleTimer(
|
||||
GetCustomStartTime().ToTimeToday(), GetCustomEndTime().ToTimeToday(),
|
||||
@ -984,13 +1081,139 @@ void NightLightControllerImpl::RefreshScheduleTimer(
|
||||
bool keep_manual_toggles_during_schedules) {
|
||||
if (GetScheduleType() == ScheduleType::kNone) {
|
||||
NOTREACHED();
|
||||
time_scheduler_controller_->StopTimer();
|
||||
timer_.Stop();
|
||||
return;
|
||||
}
|
||||
|
||||
time_scheduler_controller_->RefreshScheduleTimer(
|
||||
active_user_pref_service_, start_time, end_time, did_schedule_change,
|
||||
keep_manual_toggles_during_schedules);
|
||||
if (keep_manual_toggles_during_schedules && MaybeRestoreSchedule()) {
|
||||
RefreshDisplaysTemperature(GetColorTemperature());
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: Users can set any weird combinations.
|
||||
const base::Time now = delegate_->GetNow();
|
||||
if (end_time <= start_time) {
|
||||
// Example:
|
||||
// Start: 9:00 PM, End: 6:00 AM.
|
||||
//
|
||||
// 6:00 21:00
|
||||
// <----- + ------------------ + ----->
|
||||
// | |
|
||||
// end start
|
||||
//
|
||||
// Note that the above times are times of day (today). It is important to
|
||||
// know where "now" is with respect to these times to decide how to adjust
|
||||
// them.
|
||||
if (end_time >= now) {
|
||||
// If the end time (today) is greater than the time now, this means "now"
|
||||
// is within the NightLight schedule, and the start time is actually
|
||||
// yesterday. The above timeline is interpreted as:
|
||||
//
|
||||
// 21:00 (-1day) 6:00
|
||||
// <----- + ----------- + ------ + ----->
|
||||
// | | |
|
||||
// start now end
|
||||
//
|
||||
start_time -= base::TimeDelta::FromDays(1);
|
||||
} else {
|
||||
// Two possibilities here:
|
||||
// - Either "now" is greater than the end time, but less than start time.
|
||||
// This means NightLight is outside the schedule, waiting for the next
|
||||
// start time. The end time is actually a day later.
|
||||
// - Or "now" is greater than both the start and end times. This means
|
||||
// NightLight is within the schedule, waiting to turn off at the next
|
||||
// end time, which is also a day later.
|
||||
end_time += base::TimeDelta::FromDays(1);
|
||||
}
|
||||
}
|
||||
|
||||
DCHECK_GE(end_time, start_time);
|
||||
|
||||
// The target status that we need to set NightLight to now if a change of
|
||||
// status is needed immediately.
|
||||
bool enable_now = false;
|
||||
|
||||
// Where are we now with respect to the start and end times?
|
||||
if (now < start_time) {
|
||||
// Example:
|
||||
// Start: 6:00 PM today, End: 6:00 AM tomorrow, Now: 4:00 PM.
|
||||
//
|
||||
// <----- + ----------- + ----------- + ----->
|
||||
// | | |
|
||||
// now start end
|
||||
//
|
||||
// In this case, we need to disable NightLight immediately if it's enabled.
|
||||
enable_now = false;
|
||||
} else if (now >= start_time && now < end_time) {
|
||||
// Example:
|
||||
// Start: 6:00 PM today, End: 6:00 AM tomorrow, Now: 11:00 PM.
|
||||
//
|
||||
// <----- + ----------- + ----------- + ----->
|
||||
// | | |
|
||||
// start now end
|
||||
//
|
||||
// Start NightLight right away. Our future start time is a day later than
|
||||
// its current value.
|
||||
enable_now = true;
|
||||
start_time += base::TimeDelta::FromDays(1);
|
||||
} else { // now >= end_time.
|
||||
// Example:
|
||||
// Start: 6:00 PM today, End: 10:00 PM today, Now: 11:00 PM.
|
||||
//
|
||||
// <----- + ----------- + ----------- + ----->
|
||||
// | | |
|
||||
// start end now
|
||||
//
|
||||
// In this case, our future start and end times are a day later from their
|
||||
// current values. NightLight needs to be ended immediately if it's already
|
||||
// enabled.
|
||||
enable_now = false;
|
||||
start_time += base::TimeDelta::FromDays(1);
|
||||
end_time += base::TimeDelta::FromDays(1);
|
||||
}
|
||||
|
||||
// After the above processing, the start and end time are all in the future.
|
||||
DCHECK_GE(start_time, now);
|
||||
DCHECK_GE(end_time, now);
|
||||
|
||||
if (did_schedule_change && enable_now != GetEnabled()) {
|
||||
// If the change in the schedule introduces a change in the status, then
|
||||
// calling SetEnabled() is all we need, since it will trigger a change in
|
||||
// the user prefs to which we will respond by calling Refresh(). This will
|
||||
// end up in this function again, adjusting all the needed schedules.
|
||||
SetEnabled(enable_now, AnimationDuration::kShort);
|
||||
return;
|
||||
}
|
||||
|
||||
// We reach here in one of the following conditions:
|
||||
// 1) If schedule changes don't result in changes in the status, we need to
|
||||
// explicitly update the timer to re-schedule the next toggle to account for
|
||||
// any changes.
|
||||
// 2) The user has just manually toggled the status of NightLight either from
|
||||
// the System Menu or System Settings. In this case, we respect the user
|
||||
// wish and maintain the current status that they desire, but we schedule the
|
||||
// status to be toggled according to the time that corresponds with the
|
||||
// opposite status of the current one.
|
||||
ScheduleNextToggle(GetEnabled() ? end_time - now : start_time - now);
|
||||
RefreshDisplaysTemperature(GetColorTemperature());
|
||||
}
|
||||
|
||||
void NightLightControllerImpl::ScheduleNextToggle(base::TimeDelta delay) {
|
||||
DCHECK(active_user_pref_service_);
|
||||
|
||||
const bool new_status = !GetEnabled();
|
||||
const base::Time target_time = delegate_->GetNow() + delay;
|
||||
|
||||
per_user_schedule_target_state_[active_user_pref_service_] =
|
||||
ScheduleTargetState{target_time, new_status};
|
||||
|
||||
VLOG(1) << "Setting Night Light to toggle to "
|
||||
<< (new_status ? "enabled" : "disabled") << " at "
|
||||
<< base::TimeFormatTimeOfDay(target_time);
|
||||
timer_.Start(FROM_HERE, delay,
|
||||
base::BindOnce(&NightLightControllerImpl::SetEnabled,
|
||||
base::Unretained(this), new_status,
|
||||
AnimationDuration::kLong));
|
||||
}
|
||||
|
||||
} // namespace ash
|
||||
|
@ -11,9 +11,7 @@
|
||||
#include "ash/display/window_tree_host_manager.h"
|
||||
#include "ash/public/cpp/night_light_controller.h"
|
||||
#include "ash/public/cpp/session/session_observer.h"
|
||||
#include "ash/public/cpp/simple_geo_position.h"
|
||||
#include "ash/system/time/time_of_day.h"
|
||||
#include "ash/system/time/time_scheduler_controller.h"
|
||||
#include "ash/system/night_light/time_of_day.h"
|
||||
#include "base/containers/flat_map.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/time/time.h"
|
||||
@ -135,8 +133,10 @@ class ASH_EXPORT NightLightControllerImpl
|
||||
AnimationDuration last_animation_duration() const {
|
||||
return last_animation_duration_;
|
||||
}
|
||||
|
||||
bool is_current_geoposition_from_cache() const;
|
||||
base::OneShotTimer* timer() { return &timer_; }
|
||||
bool is_current_geoposition_from_cache() const {
|
||||
return is_current_geoposition_from_cache_;
|
||||
}
|
||||
float ambient_temperature() const { return ambient_temperature_; }
|
||||
const gfx::Vector3dF& ambient_rgb_scaling_factors() const {
|
||||
return ambient_rgb_scaling_factors_;
|
||||
@ -156,8 +156,7 @@ class ASH_EXPORT NightLightControllerImpl
|
||||
void UpdateAmbientRgbScalingFactors();
|
||||
|
||||
// Set the desired NightLight settings in the current active user prefs.
|
||||
void SetEnabled(bool enabled,
|
||||
TimeSchedulerController::TimeSetterSource source);
|
||||
void SetEnabled(bool enabled, AnimationDuration animation_type);
|
||||
void SetColorTemperature(float temperature);
|
||||
void SetScheduleType(ScheduleType type);
|
||||
void SetCustomStartTime(TimeOfDay start_time);
|
||||
@ -191,12 +190,7 @@ class ASH_EXPORT NightLightControllerImpl
|
||||
void Click(const absl::optional<int>& button_index,
|
||||
const absl::optional<std::u16string>& reply) override;
|
||||
|
||||
void SetDelegateForTesting(
|
||||
std::unique_ptr<TimeSchedulerController::Delegate> delegate);
|
||||
|
||||
bool GetIsTimerRunningForTesting();
|
||||
base::TimeDelta GetCurrentTimerDelayForTesting();
|
||||
void FireTimerForTesting();
|
||||
void SetDelegateForTesting(std::unique_ptr<Delegate> delegate);
|
||||
|
||||
// Returns the Auto Night Light notification if any is currently shown, or
|
||||
// nullptr.
|
||||
@ -222,9 +216,19 @@ class ASH_EXPORT NightLightControllerImpl
|
||||
// Disables showing the Auto Night Light from now on.
|
||||
void DisableShowingFutureAutoNightLightNotification();
|
||||
|
||||
// Refreshes the displays color transforms based on
|
||||
// |prefs::kNightLightTemperature|, or 0 if NightLight is turned off.
|
||||
void RefreshDisplaysTemperature();
|
||||
// Called only when the active user changes in order to see if we need to use
|
||||
// a previously cached geoposition value from the active user's prefs.
|
||||
void LoadCachedGeopositionIfNeeded();
|
||||
|
||||
// Called whenever we receive a new geoposition update to cache it in all
|
||||
// logged-in users' prefs so that it can be used later in the event of not
|
||||
// being able to retrieve a valid geoposition.
|
||||
void StoreCachedGeoposition(const SimpleGeoposition& position);
|
||||
|
||||
// Refreshes the displays color transforms based on the given
|
||||
// |color_temperature|, which will be overridden to a value of 0 if NightLight
|
||||
// is turned off.
|
||||
void RefreshDisplaysTemperature(float color_temperature);
|
||||
|
||||
// Reapplys the current color temperature on the displays without starting a
|
||||
// new animation or overriding an on-going one towards the same target
|
||||
@ -278,6 +282,13 @@ class ASH_EXPORT NightLightControllerImpl
|
||||
bool did_schedule_change,
|
||||
bool keep_manual_toggles_during_schedules);
|
||||
|
||||
// Schedule the upcoming next toggle of NightLight mode. This is used for the
|
||||
// automatic status changes of NightLight which always use an
|
||||
// AnimationDurationType::kLong.
|
||||
void ScheduleNextToggle(base::TimeDelta delay);
|
||||
|
||||
std::unique_ptr<Delegate> delegate_;
|
||||
|
||||
// The pref service of the currently active user. Can be null in
|
||||
// ash_unittests.
|
||||
PrefService* active_user_pref_service_ = nullptr;
|
||||
@ -289,6 +300,22 @@ class ASH_EXPORT NightLightControllerImpl
|
||||
|
||||
std::unique_ptr<ColorTemperatureAnimation> temperature_animation_;
|
||||
|
||||
// Tracks the upcoming NightLight state changes per each user due to automatic
|
||||
// schedules. This can be used to restore a manually toggled status while the
|
||||
// schedule is being used. See MaybeRestoreSchedule().
|
||||
struct ScheduleTargetState {
|
||||
// The time at which NightLight will switch to |target_status| defined
|
||||
// below.
|
||||
base::Time target_time;
|
||||
bool target_status;
|
||||
};
|
||||
base::flat_map<PrefService*, ScheduleTargetState>
|
||||
per_user_schedule_target_state_;
|
||||
|
||||
// The timer that schedules the start and end of NightLight when the schedule
|
||||
// type is either kSunsetToSunrise or kCustom.
|
||||
base::OneShotTimer timer_;
|
||||
|
||||
// True only until Night Light is initialized from the very first user
|
||||
// session. After that, it is set to false.
|
||||
bool is_first_user_init_ = true;
|
||||
@ -317,10 +344,6 @@ class ASH_EXPORT NightLightControllerImpl
|
||||
// Valid only if ambient color is enabled.
|
||||
gfx::Vector3dF ambient_rgb_scaling_factors_ = {1.f, 1.f, 1.f};
|
||||
|
||||
// |time_scheduler_controller_| helps schedule a timer to automatically turn
|
||||
// on and off the night light mode from start and end time in user's prefs.
|
||||
std::unique_ptr<TimeSchedulerController> time_scheduler_controller_;
|
||||
|
||||
base::WeakPtrFactory<NightLightControllerImpl> weak_ptr_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(NightLightControllerImpl);
|
||||
|
@ -12,14 +12,12 @@
|
||||
#include "ash/display/cursor_window_controller.h"
|
||||
#include "ash/display/window_tree_host_manager.h"
|
||||
#include "ash/public/cpp/session/session_types.h"
|
||||
#include "ash/public/cpp/simple_geo_position.h"
|
||||
#include "ash/root_window_controller.h"
|
||||
#include "ash/session/session_controller_impl.h"
|
||||
#include "ash/session/test_session_controller_client.h"
|
||||
#include "ash/shell.h"
|
||||
#include "ash/system/night_light/night_light_controller_impl.h"
|
||||
#include "ash/system/time/time_of_day.h"
|
||||
#include "ash/system/time/time_scheduler_controller.h"
|
||||
#include "ash/system/night_light/time_of_day.h"
|
||||
#include "ash/test/ash_test_base.h"
|
||||
#include "ash/test/ash_test_helper.h"
|
||||
#include "ash/test_shell_delegate.h"
|
||||
@ -148,7 +146,7 @@ constexpr double kFakePosition2_Longitude = -100.5;
|
||||
constexpr int kFakePosition2_SunsetOffset = 17 * 60;
|
||||
constexpr int kFakePosition2_SunriseOffset = 3 * 60;
|
||||
|
||||
class TestDelegate : public TimeSchedulerController::Delegate {
|
||||
class TestDelegate : public NightLightControllerImpl::Delegate {
|
||||
public:
|
||||
TestDelegate() = default;
|
||||
TestDelegate(const TestDelegate& other) = delete;
|
||||
@ -164,15 +162,17 @@ class TestDelegate : public TimeSchedulerController::Delegate {
|
||||
base::Time GetNow() const override { return fake_now_; }
|
||||
base::Time GetSunsetTime() const override { return fake_sunset_; }
|
||||
base::Time GetSunriseTime() const override { return fake_sunrise_; }
|
||||
bool SetGeoposition(const SimpleGeoposition& position) override {
|
||||
bool SetGeoposition(
|
||||
const NightLightController::SimpleGeoposition& position) override {
|
||||
has_geoposition_ = true;
|
||||
if (position ==
|
||||
SimpleGeoposition{kFakePosition1_Latitude, kFakePosition1_Longitude}) {
|
||||
if (position == NightLightController::SimpleGeoposition{
|
||||
kFakePosition1_Latitude, kFakePosition1_Longitude}) {
|
||||
// Set sunset and sunrise times associated with fake position 1.
|
||||
SetFakeSunset(TimeOfDay(kFakePosition1_SunsetOffset));
|
||||
SetFakeSunrise(TimeOfDay(kFakePosition1_SunriseOffset));
|
||||
} else if (position == SimpleGeoposition{kFakePosition2_Latitude,
|
||||
kFakePosition2_Longitude}) {
|
||||
} else if (position ==
|
||||
NightLightController::SimpleGeoposition{
|
||||
kFakePosition2_Latitude, kFakePosition2_Longitude}) {
|
||||
// Set sunset and sunrise times associated with fake position 2.
|
||||
SetFakeSunset(TimeOfDay(kFakePosition2_SunsetOffset));
|
||||
SetFakeSunrise(TimeOfDay(kFakePosition2_SunriseOffset));
|
||||
@ -234,7 +234,7 @@ class NightLightTest : public NoSessionAshTestBase {
|
||||
|
||||
void SetNightLightEnabled(bool enabled) {
|
||||
GetController()->SetEnabled(
|
||||
enabled, TimeSchedulerController::TimeSetterSource::kUser);
|
||||
enabled, NightLightControllerImpl::AnimationDuration::kShort);
|
||||
}
|
||||
|
||||
void SetAmbientColorPrefEnabled(bool enabled) {
|
||||
@ -461,16 +461,16 @@ TEST_F(NightLightTest, TestScheduleNoneToCustomTransition) {
|
||||
TestCompositorsTemperature(controller->GetColorTemperature());
|
||||
EXPECT_EQ(NightLightControllerImpl::AnimationDuration::kShort,
|
||||
controller->last_animation_duration());
|
||||
EXPECT_TRUE(controller->GetIsTimerRunningForTesting());
|
||||
EXPECT_TRUE(controller->timer()->IsRunning());
|
||||
EXPECT_EQ(base::TimeDelta::FromHours(2),
|
||||
controller->GetCurrentTimerDelayForTesting());
|
||||
controller->timer()->GetCurrentDelay());
|
||||
|
||||
// If the user changes the schedule type to "none", the NightLight status
|
||||
// should not change, but the timer should not be running.
|
||||
controller->SetScheduleType(NightLightController::ScheduleType::kNone);
|
||||
EXPECT_TRUE(controller->GetEnabled());
|
||||
TestCompositorsTemperature(controller->GetColorTemperature());
|
||||
EXPECT_FALSE(controller->GetIsTimerRunningForTesting());
|
||||
EXPECT_FALSE(controller->timer()->IsRunning());
|
||||
}
|
||||
|
||||
// Tests what happens when the time now reaches the end of the NightLight
|
||||
@ -494,16 +494,16 @@ TEST_F(NightLightTest, TestCustomScheduleReachingEndTime) {
|
||||
//
|
||||
// Now is 8:00 PM.
|
||||
delegate()->SetFakeNow(TimeOfDay(20 * 60));
|
||||
controller->FireTimerForTesting();
|
||||
controller->timer()->FireNow();
|
||||
EXPECT_FALSE(controller->GetEnabled());
|
||||
TestCompositorsTemperature(0.0f);
|
||||
EXPECT_EQ(NightLightControllerImpl::AnimationDuration::kLong,
|
||||
controller->last_animation_duration());
|
||||
// The timer should still be running, but now scheduling the start at 3:00 PM
|
||||
// tomorrow which is 19 hours from "now" (8:00 PM).
|
||||
EXPECT_TRUE(controller->GetIsTimerRunningForTesting());
|
||||
EXPECT_TRUE(controller->timer()->IsRunning());
|
||||
EXPECT_EQ(base::TimeDelta::FromHours(19),
|
||||
controller->GetCurrentTimerDelayForTesting());
|
||||
controller->timer()->GetCurrentDelay());
|
||||
}
|
||||
|
||||
// Tests that user toggles from the system menu or system settings override any
|
||||
@ -536,9 +536,9 @@ TEST_F(NightLightTest, TestExplicitUserTogglesWhileScheduleIsActive) {
|
||||
controller->last_animation_duration());
|
||||
// The timer should still be running, but NightLight should automatically
|
||||
// turn off at 8:00 PM tomorrow, which is 21 hours from now (11:00 PM).
|
||||
EXPECT_TRUE(controller->GetIsTimerRunningForTesting());
|
||||
EXPECT_TRUE(controller->timer()->IsRunning());
|
||||
EXPECT_EQ(base::TimeDelta::FromHours(21),
|
||||
controller->GetCurrentTimerDelayForTesting());
|
||||
controller->timer()->GetCurrentDelay());
|
||||
|
||||
// Manually turning it back off should also be respected, and this time the
|
||||
// start is scheduled at 3:00 PM tomorrow after 19 hours from "now" (8:00 PM).
|
||||
@ -547,9 +547,9 @@ TEST_F(NightLightTest, TestExplicitUserTogglesWhileScheduleIsActive) {
|
||||
TestCompositorsTemperature(0.0f);
|
||||
EXPECT_EQ(NightLightControllerImpl::AnimationDuration::kShort,
|
||||
controller->last_animation_duration());
|
||||
EXPECT_TRUE(controller->GetIsTimerRunningForTesting());
|
||||
EXPECT_TRUE(controller->timer()->IsRunning());
|
||||
EXPECT_EQ(base::TimeDelta::FromHours(16),
|
||||
controller->GetCurrentTimerDelayForTesting());
|
||||
controller->timer()->GetCurrentDelay());
|
||||
}
|
||||
|
||||
// Tests that changing the custom start and end times, in such a way that
|
||||
@ -574,27 +574,27 @@ TEST_F(NightLightTest, TestChangingStartTimesThatDontChangeTheStatus) {
|
||||
controller->SetScheduleType(NightLightController::ScheduleType::kCustom);
|
||||
EXPECT_FALSE(controller->GetEnabled());
|
||||
TestCompositorsTemperature(0.0f);
|
||||
EXPECT_TRUE(controller->GetIsTimerRunningForTesting());
|
||||
EXPECT_TRUE(controller->timer()->IsRunning());
|
||||
EXPECT_EQ(base::TimeDelta::FromHours(2),
|
||||
controller->GetCurrentTimerDelayForTesting());
|
||||
controller->timer()->GetCurrentDelay());
|
||||
|
||||
// Change the start time in such a way that doesn't change the status, but
|
||||
// despite that, confirm that schedule has been updated.
|
||||
controller->SetCustomStartTime(TimeOfDay(19 * 60)); // 7:00 PM.
|
||||
EXPECT_FALSE(controller->GetEnabled());
|
||||
TestCompositorsTemperature(0.0f);
|
||||
EXPECT_TRUE(controller->GetIsTimerRunningForTesting());
|
||||
EXPECT_TRUE(controller->timer()->IsRunning());
|
||||
EXPECT_EQ(base::TimeDelta::FromHours(3),
|
||||
controller->GetCurrentTimerDelayForTesting());
|
||||
controller->timer()->GetCurrentDelay());
|
||||
|
||||
// Changing the end time in a similar fashion to the above and expect no
|
||||
// change.
|
||||
controller->SetCustomEndTime(TimeOfDay(23 * 60)); // 11:00 PM.
|
||||
EXPECT_FALSE(controller->GetEnabled());
|
||||
TestCompositorsTemperature(0.0f);
|
||||
EXPECT_TRUE(controller->GetIsTimerRunningForTesting());
|
||||
EXPECT_TRUE(controller->timer()->IsRunning());
|
||||
EXPECT_EQ(base::TimeDelta::FromHours(3),
|
||||
controller->GetCurrentTimerDelayForTesting());
|
||||
controller->timer()->GetCurrentDelay());
|
||||
}
|
||||
|
||||
// Tests the behavior of the sunset to sunrise automatic schedule type.
|
||||
@ -619,33 +619,33 @@ TEST_F(NightLightTest, TestSunsetSunrise) {
|
||||
NightLightController::ScheduleType::kSunsetToSunrise);
|
||||
EXPECT_FALSE(controller->GetEnabled());
|
||||
TestCompositorsTemperature(0.0f);
|
||||
EXPECT_TRUE(controller->GetIsTimerRunningForTesting());
|
||||
EXPECT_TRUE(controller->timer()->IsRunning());
|
||||
EXPECT_EQ(base::TimeDelta::FromHours(4),
|
||||
controller->GetCurrentTimerDelayForTesting());
|
||||
controller->timer()->GetCurrentDelay());
|
||||
|
||||
// Simulate reaching sunset.
|
||||
delegate()->SetFakeNow(TimeOfDay(20 * 60)); // Now is 8:00 PM.
|
||||
controller->FireTimerForTesting();
|
||||
controller->timer()->FireNow();
|
||||
EXPECT_TRUE(controller->GetEnabled());
|
||||
TestCompositorsTemperature(controller->GetColorTemperature());
|
||||
EXPECT_EQ(NightLightControllerImpl::AnimationDuration::kLong,
|
||||
controller->last_animation_duration());
|
||||
// Timer is running scheduling the end at sunrise.
|
||||
EXPECT_TRUE(controller->GetIsTimerRunningForTesting());
|
||||
EXPECT_TRUE(controller->timer()->IsRunning());
|
||||
EXPECT_EQ(base::TimeDelta::FromHours(9),
|
||||
controller->GetCurrentTimerDelayForTesting());
|
||||
controller->timer()->GetCurrentDelay());
|
||||
|
||||
// Simulate reaching sunrise.
|
||||
delegate()->SetFakeNow(TimeOfDay(5 * 60)); // Now is 5:00 AM.
|
||||
controller->FireTimerForTesting();
|
||||
controller->timer()->FireNow();
|
||||
EXPECT_FALSE(controller->GetEnabled());
|
||||
TestCompositorsTemperature(0.0f);
|
||||
EXPECT_EQ(NightLightControllerImpl::AnimationDuration::kLong,
|
||||
controller->last_animation_duration());
|
||||
// Timer is running scheduling the start at the next sunset.
|
||||
EXPECT_TRUE(controller->GetIsTimerRunningForTesting());
|
||||
EXPECT_TRUE(controller->timer()->IsRunning());
|
||||
EXPECT_EQ(base::TimeDelta::FromHours(15),
|
||||
controller->GetCurrentTimerDelayForTesting());
|
||||
controller->timer()->GetCurrentDelay());
|
||||
}
|
||||
|
||||
// Tests the behavior of the sunset to sunrise automatic schedule type when the
|
||||
@ -660,29 +660,29 @@ TEST_F(NightLightTest, TestSunsetSunriseGeoposition) {
|
||||
//
|
||||
NightLightControllerImpl* controller = GetController();
|
||||
delegate()->SetFakeNow(TimeOfDay(16 * 60)); // 4:00PM.
|
||||
controller->SetCurrentGeoposition(
|
||||
SimpleGeoposition{kFakePosition1_Latitude, kFakePosition1_Longitude});
|
||||
controller->SetCurrentGeoposition(NightLightController::SimpleGeoposition{
|
||||
kFakePosition1_Latitude, kFakePosition1_Longitude});
|
||||
|
||||
// Expect that timer is running and the start is scheduled after 4 hours.
|
||||
controller->SetScheduleType(
|
||||
NightLightController::ScheduleType::kSunsetToSunrise);
|
||||
EXPECT_FALSE(controller->GetEnabled());
|
||||
TestCompositorsTemperature(0.0f);
|
||||
EXPECT_TRUE(controller->GetIsTimerRunningForTesting());
|
||||
EXPECT_TRUE(controller->timer()->IsRunning());
|
||||
EXPECT_EQ(base::TimeDelta::FromHours(4),
|
||||
controller->GetCurrentTimerDelayForTesting());
|
||||
controller->timer()->GetCurrentDelay());
|
||||
|
||||
// Simulate reaching sunset.
|
||||
delegate()->SetFakeNow(TimeOfDay(20 * 60)); // Now is 8:00 PM.
|
||||
controller->FireTimerForTesting();
|
||||
controller->timer()->FireNow();
|
||||
EXPECT_TRUE(controller->GetEnabled());
|
||||
TestCompositorsTemperature(controller->GetColorTemperature());
|
||||
EXPECT_EQ(NightLightControllerImpl::AnimationDuration::kLong,
|
||||
controller->last_animation_duration());
|
||||
// Timer is running scheduling the end at sunrise.
|
||||
EXPECT_TRUE(controller->GetIsTimerRunningForTesting());
|
||||
EXPECT_TRUE(controller->timer()->IsRunning());
|
||||
EXPECT_EQ(base::TimeDelta::FromHours(8),
|
||||
controller->GetCurrentTimerDelayForTesting());
|
||||
controller->timer()->GetCurrentDelay());
|
||||
|
||||
// Now simulate user changing position.
|
||||
// Position 2 sunset and sunrise times.
|
||||
@ -692,28 +692,28 @@ TEST_F(NightLightTest, TestSunsetSunriseGeoposition) {
|
||||
// | | |
|
||||
// sunset now sunrise
|
||||
//
|
||||
controller->SetCurrentGeoposition(
|
||||
SimpleGeoposition{kFakePosition2_Latitude, kFakePosition2_Longitude});
|
||||
controller->SetCurrentGeoposition(NightLightController::SimpleGeoposition{
|
||||
kFakePosition2_Latitude, kFakePosition2_Longitude});
|
||||
|
||||
// Expect that the scheduled end delay has been updated, and the status hasn't
|
||||
// changed.
|
||||
EXPECT_TRUE(controller->GetEnabled());
|
||||
TestCompositorsTemperature(controller->GetColorTemperature());
|
||||
EXPECT_TRUE(controller->GetIsTimerRunningForTesting());
|
||||
EXPECT_TRUE(controller->timer()->IsRunning());
|
||||
EXPECT_EQ(base::TimeDelta::FromHours(7),
|
||||
controller->GetCurrentTimerDelayForTesting());
|
||||
controller->timer()->GetCurrentDelay());
|
||||
|
||||
// Simulate reaching sunrise.
|
||||
delegate()->SetFakeNow(TimeOfDay(3 * 60)); // Now is 5:00 AM.
|
||||
controller->FireTimerForTesting();
|
||||
controller->timer()->FireNow();
|
||||
EXPECT_FALSE(controller->GetEnabled());
|
||||
TestCompositorsTemperature(0.0f);
|
||||
EXPECT_EQ(NightLightControllerImpl::AnimationDuration::kLong,
|
||||
controller->last_animation_duration());
|
||||
// Timer is running scheduling the start at the next sunset.
|
||||
EXPECT_TRUE(controller->GetIsTimerRunningForTesting());
|
||||
EXPECT_TRUE(controller->timer()->IsRunning());
|
||||
EXPECT_EQ(base::TimeDelta::FromHours(14),
|
||||
controller->GetCurrentTimerDelayForTesting());
|
||||
controller->timer()->GetCurrentDelay());
|
||||
}
|
||||
|
||||
// Tests the behavior when the client sets the geoposition while in custom
|
||||
@ -746,16 +746,16 @@ TEST_F(NightLightTest, DISABLED_TestCustomScheduleGeopositionChanges) {
|
||||
|
||||
int fake_now = 16 * 60;
|
||||
delegate()->SetFakeNow(TimeOfDay(fake_now));
|
||||
controller->SetCurrentGeoposition(
|
||||
SimpleGeoposition{kFakePosition1_Latitude, kFakePosition1_Longitude});
|
||||
controller->SetCurrentGeoposition(NightLightController::SimpleGeoposition{
|
||||
kFakePosition1_Latitude, kFakePosition1_Longitude});
|
||||
|
||||
// Expect that timer is running and is scheduled at next custom start time.
|
||||
controller->SetScheduleType(NightLightController::ScheduleType::kCustom);
|
||||
EXPECT_FALSE(controller->GetEnabled());
|
||||
TestCompositorsTemperature(0.0f);
|
||||
EXPECT_TRUE(controller->GetIsTimerRunningForTesting());
|
||||
EXPECT_TRUE(controller->timer()->IsRunning());
|
||||
EXPECT_EQ(base::TimeDelta::FromMinutes(time_diff(fake_now, kCustom_Start)),
|
||||
controller->GetCurrentTimerDelayForTesting());
|
||||
controller->timer()->GetCurrentDelay());
|
||||
|
||||
// Simulate a timezone change by changing geoposition.
|
||||
// Current time updates to 9PM.
|
||||
@ -766,9 +766,9 @@ TEST_F(NightLightTest, DISABLED_TestCustomScheduleGeopositionChanges) {
|
||||
//
|
||||
fake_now = 21 * 60;
|
||||
delegate()->SetFakeNow(TimeOfDay(fake_now));
|
||||
controller->FireTimerForTesting();
|
||||
controller->SetCurrentGeoposition(
|
||||
SimpleGeoposition{kFakePosition2_Latitude, kFakePosition2_Longitude});
|
||||
controller->timer()->FireNow();
|
||||
controller->SetCurrentGeoposition(NightLightController::SimpleGeoposition{
|
||||
kFakePosition2_Latitude, kFakePosition2_Longitude});
|
||||
|
||||
// Expect the controller to enter night light mode and the scheduled end
|
||||
// delay has been updated.
|
||||
@ -776,26 +776,26 @@ TEST_F(NightLightTest, DISABLED_TestCustomScheduleGeopositionChanges) {
|
||||
TestCompositorsTemperature(controller->GetColorTemperature());
|
||||
EXPECT_EQ(NightLightControllerImpl::AnimationDuration::kShort,
|
||||
controller->last_animation_duration());
|
||||
EXPECT_TRUE(controller->GetIsTimerRunningForTesting());
|
||||
EXPECT_TRUE(controller->timer()->IsRunning());
|
||||
EXPECT_EQ(base::TimeDelta::FromMinutes(time_diff(fake_now, kCustom_End)),
|
||||
controller->GetCurrentTimerDelayForTesting());
|
||||
controller->timer()->GetCurrentDelay());
|
||||
|
||||
// Simulate user changing position back to location 1 and current time goes
|
||||
// back to 4PM.
|
||||
fake_now = 16 * 60;
|
||||
delegate()->SetFakeNow(TimeOfDay(fake_now));
|
||||
controller->FireTimerForTesting();
|
||||
controller->timer()->FireNow();
|
||||
|
||||
controller->SetCurrentGeoposition(
|
||||
SimpleGeoposition{kFakePosition1_Latitude, kFakePosition1_Longitude});
|
||||
controller->SetCurrentGeoposition(NightLightController::SimpleGeoposition{
|
||||
kFakePosition1_Latitude, kFakePosition1_Longitude});
|
||||
EXPECT_FALSE(controller->GetEnabled());
|
||||
TestCompositorsTemperature(0.0f);
|
||||
EXPECT_EQ(NightLightControllerImpl::AnimationDuration::kShort,
|
||||
controller->last_animation_duration());
|
||||
// Timer is running and is scheduled at next custom start time.
|
||||
EXPECT_TRUE(controller->GetIsTimerRunningForTesting());
|
||||
EXPECT_TRUE(controller->timer()->IsRunning());
|
||||
EXPECT_EQ(base::TimeDelta::FromMinutes(time_diff(fake_now, kCustom_Start)),
|
||||
controller->GetCurrentTimerDelayForTesting());
|
||||
controller->timer()->GetCurrentDelay());
|
||||
}
|
||||
|
||||
// Tests the behavior when there is no valid geoposition for example due to lack
|
||||
@ -848,8 +848,8 @@ TEST_F(NightLightTest, AbsentValidGeoposition) {
|
||||
EXPECT_EQ(delegate()->GetSunriseTime(), kSunrise1.ToTimeToday());
|
||||
|
||||
// Now simulate receiving a geoposition update of fake geoposition 2.
|
||||
controller->SetCurrentGeoposition(
|
||||
SimpleGeoposition{kFakePosition2_Latitude, kFakePosition2_Longitude});
|
||||
controller->SetCurrentGeoposition(NightLightController::SimpleGeoposition{
|
||||
kFakePosition2_Latitude, kFakePosition2_Longitude});
|
||||
EXPECT_TRUE(delegate()->HasGeoposition());
|
||||
EXPECT_FALSE(controller->is_current_geoposition_from_cache());
|
||||
EXPECT_EQ(delegate()->GetSunsetTime(), kSunset2.ToTimeToday());
|
||||
@ -877,8 +877,8 @@ TEST_F(NightLightTest, AbsentValidGeoposition) {
|
||||
user2_pref_service()->ClearPref(prefs::kNightLightCachedLongitude);
|
||||
|
||||
// Now simulate receiving a geoposition update of fake geoposition 1.
|
||||
controller->SetCurrentGeoposition(
|
||||
SimpleGeoposition{kFakePosition1_Latitude, kFakePosition1_Longitude});
|
||||
controller->SetCurrentGeoposition(NightLightController::SimpleGeoposition{
|
||||
kFakePosition1_Latitude, kFakePosition1_Longitude});
|
||||
EXPECT_TRUE(delegate()->HasGeoposition());
|
||||
EXPECT_FALSE(controller->is_current_geoposition_from_cache());
|
||||
EXPECT_EQ(delegate()->GetSunsetTime(), kSunset1.ToTimeToday());
|
||||
@ -914,10 +914,10 @@ TEST_F(NightLightTest, TestCustomScheduleOnResume) {
|
||||
|
||||
EXPECT_FALSE(controller->GetEnabled());
|
||||
TestCompositorsTemperature(0.0f);
|
||||
EXPECT_TRUE(controller->GetIsTimerRunningForTesting());
|
||||
EXPECT_TRUE(controller->timer()->IsRunning());
|
||||
// NightLight should start in 2 hours.
|
||||
EXPECT_EQ(base::TimeDelta::FromHours(2),
|
||||
controller->GetCurrentTimerDelayForTesting());
|
||||
controller->timer()->GetCurrentDelay());
|
||||
|
||||
// Now simulate that the device was suspended for 3 hours, and the time now
|
||||
// is 7:00 PM when the devices was resumed. Expect that NightLight turns on.
|
||||
@ -926,10 +926,10 @@ TEST_F(NightLightTest, TestCustomScheduleOnResume) {
|
||||
|
||||
EXPECT_TRUE(controller->GetEnabled());
|
||||
TestCompositorsTemperature(0.4f);
|
||||
EXPECT_TRUE(controller->GetIsTimerRunningForTesting());
|
||||
EXPECT_TRUE(controller->timer()->IsRunning());
|
||||
// NightLight should end in 3 hours.
|
||||
EXPECT_EQ(base::TimeDelta::FromHours(3),
|
||||
controller->GetCurrentTimerDelayForTesting());
|
||||
controller->timer()->GetCurrentDelay());
|
||||
}
|
||||
|
||||
// The following tests ensure that the NightLight schedule is correctly
|
||||
@ -957,10 +957,10 @@ TEST_F(NightLightTest, TestCustomScheduleInvertedStartAndEndTimesCase1) {
|
||||
|
||||
EXPECT_TRUE(controller->GetEnabled());
|
||||
TestCompositorsTemperature(0.4f);
|
||||
EXPECT_TRUE(controller->GetIsTimerRunningForTesting());
|
||||
EXPECT_TRUE(controller->timer()->IsRunning());
|
||||
// NightLight should end in two hours.
|
||||
EXPECT_EQ(base::TimeDelta::FromHours(2),
|
||||
controller->GetCurrentTimerDelayForTesting());
|
||||
controller->timer()->GetCurrentDelay());
|
||||
}
|
||||
|
||||
// Case 2: "Now" is between "end" and "start".
|
||||
@ -983,10 +983,10 @@ TEST_F(NightLightTest, TestCustomScheduleInvertedStartAndEndTimesCase2) {
|
||||
|
||||
EXPECT_FALSE(controller->GetEnabled());
|
||||
TestCompositorsTemperature(0.0f);
|
||||
EXPECT_TRUE(controller->GetIsTimerRunningForTesting());
|
||||
EXPECT_TRUE(controller->timer()->IsRunning());
|
||||
// NightLight should start in 15 hours.
|
||||
EXPECT_EQ(base::TimeDelta::FromHours(15),
|
||||
controller->GetCurrentTimerDelayForTesting());
|
||||
controller->timer()->GetCurrentDelay());
|
||||
}
|
||||
|
||||
// Case 3: "Now" is greater than both "start" and "end".
|
||||
@ -1009,10 +1009,10 @@ TEST_F(NightLightTest, TestCustomScheduleInvertedStartAndEndTimesCase3) {
|
||||
|
||||
EXPECT_TRUE(controller->GetEnabled());
|
||||
TestCompositorsTemperature(0.4f);
|
||||
EXPECT_TRUE(controller->GetIsTimerRunningForTesting());
|
||||
EXPECT_TRUE(controller->timer()->IsRunning());
|
||||
// NightLight should end in 5 hours.
|
||||
EXPECT_EQ(base::TimeDelta::FromHours(5),
|
||||
controller->GetCurrentTimerDelayForTesting());
|
||||
controller->timer()->GetCurrentDelay());
|
||||
}
|
||||
|
||||
TEST_F(NightLightTest, TestAmbientLightEnabledSetting_FeatureOn) {
|
||||
@ -1149,7 +1149,7 @@ TEST_F(NightLightTest, MultiUserManualStatusToggleWithSchedules) {
|
||||
// expected in NightLight's status.
|
||||
delegate()->SetFakeNow(test_case.fake_now);
|
||||
if (user_1_previous_status != test_case.user_1_expected_status)
|
||||
controller->FireTimerForTesting();
|
||||
controller->timer()->FireNow();
|
||||
user_1_previous_status = test_case.user_1_expected_status;
|
||||
|
||||
// The untoggled states for both users should match the expected ones
|
||||
@ -1205,9 +1205,9 @@ TEST_F(NightLightTest, ManualStatusToggleCanPersistAfterResumeFromSuspend) {
|
||||
// turn back off at 8:00 PM.
|
||||
controller->Toggle();
|
||||
EXPECT_TRUE(controller->GetEnabled());
|
||||
EXPECT_TRUE(controller->GetIsTimerRunningForTesting());
|
||||
EXPECT_TRUE(controller->timer()->IsRunning());
|
||||
EXPECT_EQ(base::TimeDelta::FromHours(9),
|
||||
controller->GetCurrentTimerDelayForTesting());
|
||||
controller->timer()->GetCurrentDelay());
|
||||
|
||||
// Simulate suspend and then resume at 2:00 PM (which is outside the user's
|
||||
// custom schedule). However, the manual toggle to on should be kept.
|
||||
@ -1658,7 +1658,7 @@ TEST_F(AutoNightLightTest, Notification) {
|
||||
|
||||
// Simulate reaching sunset.
|
||||
delegate()->SetFakeNow(TimeOfDay(20 * 60)); // Now is 8:00 PM.
|
||||
controller->FireTimerForTesting();
|
||||
controller->timer()->FireNow();
|
||||
EXPECT_TRUE(controller->GetEnabled());
|
||||
auto* notification = controller->GetAutoNightLightNotificationForTesting();
|
||||
ASSERT_TRUE(notification);
|
||||
@ -1669,13 +1669,13 @@ TEST_F(AutoNightLightTest, Notification) {
|
||||
// dismissed.
|
||||
notification->delegate()->Click(absl::nullopt, absl::nullopt);
|
||||
controller->SetEnabled(false,
|
||||
TimeSchedulerController::TimeSetterSource::kUser);
|
||||
NightLightControllerImpl::AnimationDuration::kShort);
|
||||
EXPECT_FALSE(controller->GetEnabled());
|
||||
EXPECT_FALSE(controller->GetAutoNightLightNotificationForTesting());
|
||||
|
||||
// Simulate reaching next sunset. The notification should no longer show.
|
||||
delegate()->SetFakeNow(TimeOfDay(20 * 60)); // Now is 8:00 PM.
|
||||
controller->FireTimerForTesting();
|
||||
controller->timer()->FireNow();
|
||||
EXPECT_TRUE(controller->GetEnabled());
|
||||
EXPECT_FALSE(controller->GetAutoNightLightNotificationForTesting());
|
||||
}
|
||||
@ -1687,12 +1687,12 @@ TEST_F(AutoNightLightTest, DismissNotificationOnTurningOff) {
|
||||
controller->GetScheduleType());
|
||||
|
||||
// Use a fake geoposition with sunset/sunrise times at 5pm/3am.
|
||||
controller->SetCurrentGeoposition(
|
||||
SimpleGeoposition{kFakePosition2_Latitude, kFakePosition2_Longitude});
|
||||
controller->SetCurrentGeoposition(NightLightController::SimpleGeoposition{
|
||||
kFakePosition2_Latitude, kFakePosition2_Longitude});
|
||||
|
||||
// Simulate reaching sunset.
|
||||
delegate()->SetFakeNow(TimeOfDay(17 * 60)); // Now is 5:00 PM.
|
||||
controller->FireTimerForTesting();
|
||||
controller->timer()->FireNow();
|
||||
EXPECT_TRUE(controller->GetEnabled());
|
||||
auto* notification = controller->GetAutoNightLightNotificationForTesting();
|
||||
ASSERT_TRUE(notification);
|
||||
@ -1702,15 +1702,15 @@ TEST_F(AutoNightLightTest, DismissNotificationOnTurningOff) {
|
||||
// 8pm/4am, so now is before sunset. Night Light should turn off, and the
|
||||
// stale notification from above should be removed. However, its removal
|
||||
// should not affect kAutoNightLightNotificationDismissed.
|
||||
controller->SetCurrentGeoposition(
|
||||
SimpleGeoposition{kFakePosition1_Latitude, kFakePosition1_Longitude});
|
||||
controller->SetCurrentGeoposition(NightLightController::SimpleGeoposition{
|
||||
kFakePosition1_Latitude, kFakePosition1_Longitude});
|
||||
EXPECT_FALSE(controller->GetEnabled());
|
||||
EXPECT_FALSE(controller->GetAutoNightLightNotificationForTesting());
|
||||
|
||||
// Simulate reaching next sunset. The notification should still show, since it
|
||||
// was never dismissed by the user.
|
||||
delegate()->SetFakeNow(TimeOfDay(20 * 60)); // Now is 8:00 PM.
|
||||
controller->FireTimerForTesting();
|
||||
controller->timer()->FireNow();
|
||||
EXPECT_TRUE(controller->GetEnabled());
|
||||
EXPECT_TRUE(controller->GetAutoNightLightNotificationForTesting());
|
||||
}
|
||||
@ -1721,7 +1721,7 @@ TEST_F(AutoNightLightTest, CannotDisableNotificationWhenSessionIsBlocked) {
|
||||
// Simulate reaching sunset.
|
||||
NightLightControllerImpl* controller = GetController();
|
||||
delegate()->SetFakeNow(TimeOfDay(20 * 60)); // Now is 8:00 PM.
|
||||
controller->FireTimerForTesting();
|
||||
controller->timer()->FireNow();
|
||||
EXPECT_TRUE(controller->GetEnabled());
|
||||
auto* notification = controller->GetAutoNightLightNotificationForTesting();
|
||||
ASSERT_TRUE(notification);
|
||||
@ -1741,7 +1741,7 @@ TEST_F(AutoNightLightTest, OverriddenByUser) {
|
||||
|
||||
// Simulate reaching sunset.
|
||||
delegate()->SetFakeNow(TimeOfDay(20 * 60)); // Now is 8:00 PM.
|
||||
controller->FireTimerForTesting();
|
||||
controller->timer()->FireNow();
|
||||
EXPECT_TRUE(controller->GetEnabled());
|
||||
EXPECT_FALSE(controller->GetAutoNightLightNotificationForTesting());
|
||||
}
|
||||
|
@ -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/time/time_of_day.h"
|
||||
#include "ash/system/night_light/time_of_day.h"
|
||||
|
||||
#include "base/i18n/time_formatting.h"
|
||||
#include "base/strings/utf_string_conversions.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_TIME_TIME_OF_DAY_H_
|
||||
#define ASH_SYSTEM_TIME_TIME_OF_DAY_H_
|
||||
#ifndef ASH_SYSTEM_NIGHT_LIGHT_TIME_OF_DAY_H_
|
||||
#define ASH_SYSTEM_NIGHT_LIGHT_TIME_OF_DAY_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
@ -49,4 +49,4 @@ class ASH_EXPORT TimeOfDay {
|
||||
|
||||
} // namespace ash
|
||||
|
||||
#endif // ASH_SYSTEM_TIME_TIME_OF_DAY_H_
|
||||
#endif // ASH_SYSTEM_NIGHT_LIGHT_TIME_OF_DAY_H_
|
@ -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/time/time_of_day.h"
|
||||
#include "ash/system/night_light/time_of_day.h"
|
||||
|
||||
#include "base/i18n/rtl.h"
|
||||
#include "base/test/icu_test_util.h"
|
@ -1,342 +0,0 @@
|
||||
// Copyright 2021 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "ash/system/time/time_scheduler_controller.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
|
||||
#include "ash/constants/ash_pref_names.h"
|
||||
#include "ash/session/session_controller_impl.h"
|
||||
#include "ash/shell.h"
|
||||
#include "ash/strings/grit/ash_strings.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/i18n/time_formatting.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/numerics/ranges.h"
|
||||
#include "base/time/time.h"
|
||||
#include "components/prefs/pref_service.h"
|
||||
#include "third_party/icu/source/i18n/astro.h"
|
||||
#include "ui/aura/env.h"
|
||||
|
||||
namespace ash {
|
||||
|
||||
namespace {
|
||||
|
||||
class TimeSchedulerControllerDelegateImpl
|
||||
: public TimeSchedulerController::Delegate {
|
||||
public:
|
||||
TimeSchedulerControllerDelegateImpl() = default;
|
||||
~TimeSchedulerControllerDelegateImpl() override = default;
|
||||
|
||||
// ash::NightLightControllerImpl::Delegate:
|
||||
base::Time GetNow() const override { return base::Time::Now(); }
|
||||
base::Time GetSunsetTime() const override { return GetSunRiseSet(false); }
|
||||
base::Time GetSunriseTime() const override { return GetSunRiseSet(true); }
|
||||
bool SetGeoposition(const SimpleGeoposition& position) override {
|
||||
if (geoposition_ && *geoposition_ == position)
|
||||
return false;
|
||||
|
||||
geoposition_ = std::make_unique<SimpleGeoposition>(position);
|
||||
return true;
|
||||
}
|
||||
bool HasGeoposition() const override { return !!geoposition_; }
|
||||
|
||||
private:
|
||||
// Note that the below computation is intentionally performed every time
|
||||
// GetSunsetTime() or GetSunriseTime() is called rather than once whenever we
|
||||
// receive a geoposition (which happens at least once a day). This increases
|
||||
// the chances of getting accurate values, especially around DST changes.
|
||||
base::Time GetSunRiseSet(bool sunrise) const {
|
||||
if (!HasGeoposition()) {
|
||||
LOG(ERROR) << "Invalid geoposition. Using default time for "
|
||||
<< (sunrise ? "sunrise." : "sunset.");
|
||||
return sunrise
|
||||
? TimeOfDay(
|
||||
TimeSchedulerController::kDefaultEndTimeOffsetMinutes)
|
||||
.ToTimeToday()
|
||||
: TimeOfDay(
|
||||
TimeSchedulerController::kDefaultStartTimeOffsetMinutes)
|
||||
.ToTimeToday();
|
||||
}
|
||||
|
||||
icu::CalendarAstronomer astro(geoposition_->longitude,
|
||||
geoposition_->latitude);
|
||||
// For sunset and sunrise times calculations to be correct, the time of the
|
||||
// icu::CalendarAstronomer object should be set to a time near local noon.
|
||||
// This avoids having the computation flopping over into an adjacent day.
|
||||
// See the documentation of icu::CalendarAstronomer::getSunRiseSet().
|
||||
// Note that the icu calendar works with milliseconds since epoch, and
|
||||
// base::Time::FromDoubleT() / ToDoubleT() work with seconds since epoch.
|
||||
const double midday_today_sec =
|
||||
TimeOfDay(12 * 60).ToTimeToday().ToDoubleT();
|
||||
astro.setTime(midday_today_sec * 1000.0);
|
||||
const double sun_rise_set_ms = astro.getSunRiseSet(sunrise);
|
||||
return base::Time::FromDoubleT(sun_rise_set_ms / 1000.0);
|
||||
}
|
||||
|
||||
std::unique_ptr<SimpleGeoposition> geoposition_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TimeSchedulerController::TimeSchedulerController() {}
|
||||
|
||||
TimeSchedulerController::TimeSchedulerController(
|
||||
const std::string prefs_path_enabled,
|
||||
const std::string prefs_path_latitude,
|
||||
const std::string prefs_path_longitude,
|
||||
base::RepeatingCallback<void(bool, TimeSetterSource)> set_enabled_callback,
|
||||
base::RepeatingCallback<void()> refresh_state_callback)
|
||||
: prefs_path_enabled_(prefs_path_enabled),
|
||||
prefs_path_latitude_(prefs_path_latitude),
|
||||
prefs_path_longitude_(prefs_path_longitude),
|
||||
set_enabled_callback_(std::move(set_enabled_callback)),
|
||||
refresh_state_callback_(std::move(refresh_state_callback)),
|
||||
delegate_(std::make_unique<TimeSchedulerControllerDelegateImpl>()) {}
|
||||
|
||||
TimeSchedulerController::~TimeSchedulerController() {}
|
||||
|
||||
void TimeSchedulerController::RefreshScheduleTimer(
|
||||
PrefService* active_user_pref_service,
|
||||
base::Time start_time,
|
||||
base::Time end_time,
|
||||
bool did_schedule_change,
|
||||
bool keep_manual_toggles_during_schedules) {
|
||||
bool is_enabled = GetEnabled(active_user_pref_service);
|
||||
if (keep_manual_toggles_during_schedules &&
|
||||
MaybeRestoreSchedule(active_user_pref_service)) {
|
||||
refresh_state_callback_.Run();
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: Users can set any weird combinations.
|
||||
const base::Time now = delegate()->GetNow();
|
||||
if (end_time <= start_time) {
|
||||
// Example:
|
||||
// Start: 9:00 PM, End: 6:00 AM.
|
||||
//
|
||||
// 6:00 21:00
|
||||
// <----- + ------------------ + ----->
|
||||
// | |
|
||||
// end start
|
||||
//
|
||||
// Note that the above times are times of day (today). It is important to
|
||||
// know where "now" is with respect to these times to decide how to adjust
|
||||
// them.
|
||||
if (end_time >= now) {
|
||||
// If the end time (today) is greater than the time now, this means "now"
|
||||
// is within the NightLight schedule, and the start time is actually
|
||||
// yesterday. The above timeline is interpreted as:
|
||||
//
|
||||
// 21:00 (-1day) 6:00
|
||||
// <----- + ----------- + ------ + ----->
|
||||
// | | |
|
||||
// start now end
|
||||
//
|
||||
start_time -= base::TimeDelta::FromDays(1);
|
||||
} else {
|
||||
// Two possibilities here:
|
||||
// - Either "now" is greater than the end time, but less than start time.
|
||||
// This means NightLight is outside the schedule, waiting for the next
|
||||
// start time. The end time is actually a day later.
|
||||
// - Or "now" is greater than both the start and end times. This means
|
||||
// NightLight is within the schedule, waiting to turn off at the next
|
||||
// end time, which is also a day later.
|
||||
end_time += base::TimeDelta::FromDays(1);
|
||||
}
|
||||
}
|
||||
|
||||
DCHECK_GE(end_time, start_time);
|
||||
|
||||
// The target status that we need to set NightLight to now if a change of
|
||||
// status is needed immediately.
|
||||
bool enable_now = false;
|
||||
|
||||
// Where are we now with respect to the start and end times?
|
||||
if (now < start_time) {
|
||||
// Example:
|
||||
// Start: 6:00 PM today, End: 6:00 AM tomorrow, Now: 4:00 PM.
|
||||
//
|
||||
// <----- + ----------- + ----------- + ----->
|
||||
// | | |
|
||||
// now start end
|
||||
//
|
||||
// In this case, we need to disable NightLight immediately if it's enabled.
|
||||
enable_now = false;
|
||||
} else if (now >= start_time && now < end_time) {
|
||||
// Example:
|
||||
// Start: 6:00 PM today, End: 6:00 AM tomorrow, Now: 11:00 PM.
|
||||
//
|
||||
// <----- + ----------- + ----------- + ----->
|
||||
// | | |
|
||||
// start now end
|
||||
//
|
||||
// Start NightLight right away. Our future start time is a day later than
|
||||
// its current value.
|
||||
enable_now = true;
|
||||
start_time += base::TimeDelta::FromDays(1);
|
||||
} else { // now >= end_time.
|
||||
// Example:
|
||||
// Start: 6:00 PM today, End: 10:00 PM today, Now: 11:00 PM.
|
||||
//
|
||||
// <----- + ----------- + ----------- + ----->
|
||||
// | | |
|
||||
// start end now
|
||||
//
|
||||
// In this case, our future start and end times are a day later from their
|
||||
// current values. NightLight needs to be ended immediately if it's already
|
||||
// enabled.
|
||||
enable_now = false;
|
||||
start_time += base::TimeDelta::FromDays(1);
|
||||
end_time += base::TimeDelta::FromDays(1);
|
||||
}
|
||||
|
||||
// After the above processing, the start and end time are all in the future.
|
||||
DCHECK_GE(start_time, now);
|
||||
DCHECK_GE(end_time, now);
|
||||
|
||||
if (did_schedule_change && enable_now != is_enabled) {
|
||||
// If the change in the schedule introduces a change in the status, then
|
||||
// calling SetEnabled() is all we need, since it will trigger a change in
|
||||
// the user prefs to which we will respond by calling Refresh(). This will
|
||||
// end up in this function again, adjusting all the needed schedules.
|
||||
set_enabled_callback_.Run(enable_now, TimeSetterSource::kUser);
|
||||
return;
|
||||
}
|
||||
|
||||
// We reach here in one of the following conditions:
|
||||
// 1) If schedule changes don't result in changes in the status, we need to
|
||||
// explicitly update the timer to re-schedule the next toggle to account for
|
||||
// any changes.
|
||||
// 2) The user has just manually toggled the status of NightLight either from
|
||||
// the System Menu or System Settings. In this case, we respect the user
|
||||
// wish and maintain the current status that they desire, but we schedule the
|
||||
// status to be toggled according to the time that corresponds with the
|
||||
// opposite status of the current one.
|
||||
ScheduleNextToggle(active_user_pref_service,
|
||||
is_enabled ? end_time - now : start_time - now,
|
||||
is_enabled);
|
||||
refresh_state_callback_.Run();
|
||||
}
|
||||
|
||||
void TimeSchedulerController::ScheduleNextToggle(
|
||||
PrefService* active_user_pref_service,
|
||||
base::TimeDelta delay,
|
||||
bool old_status) {
|
||||
const bool new_status = !old_status;
|
||||
const base::Time target_time = delegate()->GetNow() + delay;
|
||||
|
||||
per_user_schedule_target_state_[active_user_pref_service] =
|
||||
ScheduleTargetState{target_time, new_status};
|
||||
|
||||
VLOG(1) << "Setting Night Light to toggle to "
|
||||
<< (new_status ? "enabled" : "disabled") << " at "
|
||||
<< base::TimeFormatTimeOfDay(target_time);
|
||||
timer_.Start(FROM_HERE, delay,
|
||||
base::BindRepeating(set_enabled_callback_, new_status,
|
||||
TimeSetterSource::kAutomatic));
|
||||
}
|
||||
|
||||
void TimeSchedulerController::StopTimer() {
|
||||
timer_.Stop();
|
||||
}
|
||||
|
||||
void TimeSchedulerController::LoadCachedGeopositionIfNeeded(
|
||||
PrefService* active_user_pref_service) {
|
||||
DCHECK(active_user_pref_service);
|
||||
|
||||
// Even if there is a geoposition, but it's coming from a previously cached
|
||||
// value, switching users should load the currently saved values for the
|
||||
// new user. This is to keep users' prefs completely separate. We only ignore
|
||||
// the cached values once we have a valid non-cached geoposition from any
|
||||
// user in the same session.
|
||||
if (delegate_->HasGeoposition() && !is_current_geoposition_from_cache_)
|
||||
return;
|
||||
|
||||
if (!active_user_pref_service->HasPrefPath(prefs_path_latitude_) ||
|
||||
!active_user_pref_service->HasPrefPath(prefs_path_longitude_)) {
|
||||
VLOG(1) << "No valid current geoposition and no valid cached geoposition"
|
||||
" are available. Will use default times for sunset / sunrise.";
|
||||
return;
|
||||
}
|
||||
|
||||
VLOG(1) << "Temporarily using a previously cached geoposition.";
|
||||
delegate_->SetGeoposition(SimpleGeoposition{
|
||||
active_user_pref_service->GetDouble(prefs_path_latitude_),
|
||||
active_user_pref_service->GetDouble(prefs_path_longitude_)});
|
||||
is_current_geoposition_from_cache_ = true;
|
||||
}
|
||||
|
||||
void TimeSchedulerController::StoreCachedGeoposition(
|
||||
const SimpleGeoposition& position) {
|
||||
is_current_geoposition_from_cache_ = false;
|
||||
const SessionControllerImpl* session_controller =
|
||||
Shell::Get()->session_controller();
|
||||
for (const auto& user_session : session_controller->GetUserSessions()) {
|
||||
PrefService* pref_service = session_controller->GetUserPrefServiceForUser(
|
||||
user_session->user_info.account_id);
|
||||
if (!pref_service)
|
||||
continue;
|
||||
|
||||
pref_service->SetDouble(prefs_path_latitude_, position.latitude);
|
||||
pref_service->SetDouble(prefs_path_longitude_, position.longitude);
|
||||
}
|
||||
}
|
||||
|
||||
bool TimeSchedulerController::MaybeRestoreSchedule(
|
||||
PrefService* active_user_pref_service) {
|
||||
bool is_enabled = GetEnabled(active_user_pref_service);
|
||||
auto iter = per_user_schedule_target_state_.find(active_user_pref_service);
|
||||
if (iter == per_user_schedule_target_state_.end())
|
||||
return false;
|
||||
|
||||
ScheduleTargetState& target_state = iter->second;
|
||||
// It may be that the device was suspended for a very long time that the
|
||||
// target time is no longer valid.
|
||||
if (target_state.target_time <= delegate_->GetNow())
|
||||
return false;
|
||||
|
||||
VLOG(1) << "Restoring a previous schedule.";
|
||||
DCHECK_NE(is_enabled, target_state.target_status);
|
||||
ScheduleNextToggle(active_user_pref_service,
|
||||
target_state.target_time - delegate_->GetNow(),
|
||||
is_enabled);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TimeSchedulerController::GetEnabled(
|
||||
PrefService* active_user_pref_service) const {
|
||||
return active_user_pref_service &&
|
||||
active_user_pref_service->GetBoolean(prefs_path_enabled_);
|
||||
}
|
||||
|
||||
base::Time TimeSchedulerController::GetSunRiseSet(bool sunrise) const {
|
||||
if (!delegate_->HasGeoposition()) {
|
||||
LOG(ERROR) << "Invalid geoposition. Using default time for "
|
||||
<< (sunrise ? "sunrise." : "sunset.");
|
||||
return sunrise
|
||||
? TimeOfDay(
|
||||
TimeSchedulerController::kDefaultEndTimeOffsetMinutes)
|
||||
.ToTimeToday()
|
||||
: TimeOfDay(
|
||||
TimeSchedulerController::kDefaultStartTimeOffsetMinutes)
|
||||
.ToTimeToday();
|
||||
}
|
||||
|
||||
icu::CalendarAstronomer astro(geoposition_->longitude,
|
||||
geoposition_->latitude);
|
||||
// For sunset and sunrise times calculations to be correct, the time of the
|
||||
// icu::CalendarAstronomer object should be set to a time near local noon.
|
||||
// This avoids having the computation flopping over into an adjacent day.
|
||||
// See the documentation of icu::CalendarAstronomer::getSunRiseSet().
|
||||
// Note that the icu calendar works with milliseconds since epoch, and
|
||||
// base::Time::FromDoubleT() / ToDoubleT() work with seconds since epoch.
|
||||
const double midday_today_sec = TimeOfDay(12 * 60).ToTimeToday().ToDoubleT();
|
||||
astro.setTime(midday_today_sec * 1000.0);
|
||||
const double sun_rise_set_ms = astro.getSunRiseSet(sunrise);
|
||||
return base::Time::FromDoubleT(sun_rise_set_ms / 1000.0);
|
||||
}
|
||||
|
||||
} // namespace ash
|
@ -1,171 +0,0 @@
|
||||
// Copyright 2021 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ASH_SYSTEM_TIME_TIME_SCHEDULER_CONTROLLER_H_
|
||||
#define ASH_SYSTEM_TIME_TIME_SCHEDULER_CONTROLLER_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "ash/ash_export.h"
|
||||
#include "ash/public/cpp/simple_geo_position.h"
|
||||
#include "ash/system/time/time_of_day.h"
|
||||
#include "base/containers/flat_map.h"
|
||||
#include "base/time/time.h"
|
||||
#include "base/timer/timer.h"
|
||||
#include "components/prefs/pref_change_registrar.h"
|
||||
#include "third_party/icu/source/i18n/astro.h"
|
||||
|
||||
class PrefService;
|
||||
|
||||
namespace ash {
|
||||
|
||||
// Schedule a timer to enable and disable a mode specified in a user pref
|
||||
// `prefs_path_enabled`. This class also enforces a delegate which requires
|
||||
// time zone, sunrise, and sunset time.
|
||||
class ASH_EXPORT TimeSchedulerController {
|
||||
public:
|
||||
// This class enables us to inject fake values for "Now" as well as the sunset
|
||||
// and sunrise times, so that we can reliably test the behavior in various
|
||||
// schedule types and times.
|
||||
class Delegate {
|
||||
public:
|
||||
// TimeSchedulerController owns the delegate.
|
||||
virtual ~Delegate() = default;
|
||||
|
||||
// Gets the current time.
|
||||
virtual base::Time GetNow() const = 0;
|
||||
|
||||
// Gets the sunset and sunrise times.
|
||||
virtual base::Time GetSunsetTime() const = 0;
|
||||
virtual base::Time GetSunriseTime() const = 0;
|
||||
|
||||
// Provides the delegate with the geoposition so that it can be used to
|
||||
// calculate sunset and sunrise times.
|
||||
// Returns true if |position| is different than the current known value,
|
||||
// potentially requiring a refresh of the schedule. False otherwise.
|
||||
virtual bool SetGeoposition(const SimpleGeoposition& position) = 0;
|
||||
|
||||
// Returns true if a geoposition value is available.
|
||||
virtual bool HasGeoposition() const = 0;
|
||||
};
|
||||
|
||||
enum class TimeSetterSource {
|
||||
// Short animation (2 seconds) used for manual changes of the status
|
||||
// by the user.
|
||||
kUser,
|
||||
|
||||
// Long animation (20 seconds) used for applying the color temperature
|
||||
// gradually as a result of getting into or out of the automatically
|
||||
// scheduled the mode. This gives the user a smooth transition.
|
||||
kAutomatic,
|
||||
};
|
||||
|
||||
static const int kDefaultStartTimeOffsetMinutes = 18 * 60;
|
||||
static const int kDefaultEndTimeOffsetMinutes = 6 * 60;
|
||||
|
||||
TimeSchedulerController();
|
||||
TimeSchedulerController(
|
||||
const std::string prefs_path_enabled,
|
||||
const std::string prefs_path_latitude,
|
||||
const std::string prefs_path_longitude,
|
||||
base::RepeatingCallback<void(bool, TimeSetterSource)>
|
||||
set_enabled_callback,
|
||||
base::RepeatingCallback<void()> refresh_state_callback);
|
||||
~TimeSchedulerController();
|
||||
|
||||
base::OneShotTimer* timer() { return &timer_; }
|
||||
bool is_current_geoposition_from_cache() const {
|
||||
return is_current_geoposition_from_cache_;
|
||||
}
|
||||
Delegate* delegate() { return delegate_.get(); }
|
||||
|
||||
// Given the desired start and end times that determine the time interval
|
||||
// during which the mode will be ON, depending on the time of "now", it
|
||||
// refreshes the |timer_| to either schedule the future start or end of
|
||||
// the mode, as well as update the current status if needed.
|
||||
// For |did_schedule_change| and |keep_manual_toggles_during_schedules|, see
|
||||
// Refresh() above.
|
||||
// This function should never be called if the schedule type is |kNone|.
|
||||
void RefreshScheduleTimer(PrefService* active_user_pref_service,
|
||||
base::Time start_time,
|
||||
base::Time end_time,
|
||||
bool did_schedule_change,
|
||||
bool keep_manual_toggles_during_schedules);
|
||||
|
||||
// Schedule the upcoming next toggle of the mode. This is used for the
|
||||
// automatic status changes of the mode which always use an
|
||||
// AnimationDurationType::kLong.
|
||||
void ScheduleNextToggle(PrefService* active_user_pref_service,
|
||||
base::TimeDelta delay,
|
||||
bool old_status);
|
||||
|
||||
void StopTimer();
|
||||
|
||||
// Called only when the active user changes in order to see if we need to use
|
||||
// a previously cached geoposition value from the active user's prefs.
|
||||
void LoadCachedGeopositionIfNeeded(PrefService* active_user_pref_service);
|
||||
|
||||
// Called whenever we receive a new geoposition update to cache it in all
|
||||
// logged-in users' prefs so that it can be used later in the event of not
|
||||
// being able to retrieve a valid geoposition.
|
||||
void StoreCachedGeoposition(const SimpleGeoposition& position);
|
||||
|
||||
// Attempts restoring a previously stored schedule for the active user when
|
||||
// the current status is `is_enabled` if possible and returns true if so,
|
||||
// false otherwise.
|
||||
bool MaybeRestoreSchedule(PrefService* active_user_pref_service);
|
||||
|
||||
void SetDelegateForTesting(std::unique_ptr<Delegate> delegate) {
|
||||
delegate_ = std::move(delegate);
|
||||
}
|
||||
|
||||
private:
|
||||
// Return true if user pref `prefs_path_enabled_` indicates the enabled mode.
|
||||
bool GetEnabled(PrefService* active_user_pref_service) const;
|
||||
|
||||
// Note that the below computation is intentionally performed every time
|
||||
// GetSunsetTime() or GetSunriseTime() is called rather than once whenever we
|
||||
// receive a geoposition (which happens at least once a day). This increases
|
||||
// the chances of getting accurate values, especially around DST changes.
|
||||
base::Time GetSunRiseSet(bool sunrise) const;
|
||||
|
||||
// Tracks the upcoming state changes per each user due to automatic
|
||||
// schedules. This can be used to restore a manually toggled status while the
|
||||
// schedule is being used. See MaybeRestoreSchedule().
|
||||
struct ScheduleTargetState {
|
||||
// The time at which the mode will switch to |target_status| defined
|
||||
// below.
|
||||
base::Time target_time;
|
||||
bool target_status;
|
||||
};
|
||||
|
||||
base::flat_map<PrefService*, ScheduleTargetState>
|
||||
per_user_schedule_target_state_;
|
||||
|
||||
// The timer that schedules the start and end of the mode when the schedule
|
||||
// type is either kSunsetToSunrise or kCustom.
|
||||
base::OneShotTimer timer_;
|
||||
|
||||
// True if the current geoposition value used by the Delegate is from a
|
||||
// previously cached value in the user prefs of any of the users in the
|
||||
// current session. It is reset to false once we receive a newly-updated
|
||||
// geoposition from the client.
|
||||
// This is used to treat the current geoposition as temporary until we receive
|
||||
// a valid geoposition update, and also not to let a cached geoposition value
|
||||
// to leak to another user for privacy reasons.
|
||||
bool is_current_geoposition_from_cache_ = false;
|
||||
|
||||
const std::string prefs_path_enabled_;
|
||||
const std::string prefs_path_latitude_;
|
||||
const std::string prefs_path_longitude_;
|
||||
base::RepeatingCallback<void(bool, TimeSetterSource)> set_enabled_callback_;
|
||||
base::RepeatingCallback<void()> refresh_state_callback_;
|
||||
std::unique_ptr<SimpleGeoposition> geoposition_;
|
||||
|
||||
std::unique_ptr<TimeSchedulerController::Delegate> delegate_;
|
||||
};
|
||||
|
||||
} // namespace ash
|
||||
|
||||
#endif // ASH_SYSTEM_TIME_TIME_SCHEDULER_CONTROLLER_H_
|
@ -151,7 +151,7 @@ base::Time NightLightClient::GetNow() const {
|
||||
|
||||
void NightLightClient::SendCurrentGeoposition() {
|
||||
night_light_controller_->SetCurrentGeoposition(
|
||||
ash::SimpleGeoposition{latitude_, longitude_});
|
||||
ash::NightLightController::SimpleGeoposition{latitude_, longitude_});
|
||||
}
|
||||
|
||||
void NightLightClient::ScheduleNextRequest(base::TimeDelta delay) {
|
||||
|
@ -19,7 +19,7 @@
|
||||
namespace {
|
||||
|
||||
using ScheduleType = ash::NightLightController::ScheduleType;
|
||||
using SimpleGeoposition = ash::SimpleGeoposition;
|
||||
using SimpleGeoposition = ash::NightLightController::SimpleGeoposition;
|
||||
|
||||
// Constructs a TimeZone object from the given |timezone_id|.
|
||||
std::unique_ptr<icu::TimeZone> CreateTimezone(const char* timezone_id) {
|
||||
|
Reference in New Issue
Block a user