Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2207)

Unified Diff: ash/system/night_light/night_light_controller.cc

Issue 2887913004: [Night Light] CL4: Automatic schedule backend. (Closed)
Patch Set: Nits Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ash/system/night_light/night_light_controller.cc
diff --git a/ash/system/night_light/night_light_controller.cc b/ash/system/night_light/night_light_controller.cc
index cdbc238ad74baae8d1e5cd152f2ad9e1df656668..7b82a77da300c6041def2cb7b2f8a92a58ee35f7 100644
--- a/ash/system/night_light/night_light_controller.cc
+++ b/ash/system/night_light/night_light_controller.cc
@@ -12,6 +12,7 @@
#include "base/time/time.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
+#include "third_party/icu/source/i18n/astro.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
@@ -19,14 +20,65 @@ namespace ash {
namespace {
+// Default start time at 6:00 PM as an offset from 00:00.
+const int kDefaultStartTimeOffsetMinutes = 18 * 60;
+
+// Default end time at 6:00 AM as an offset from 00:00.
+const int kDefaultEndTimeOffsetMinutes = 6 * 60;
+
constexpr float kDefaultColorTemperature = 0.5f;
-// The duration of the temperature change animation when the change is a result
-// of a manual user setting.
-// TODO(afakhry): Add automatic schedule animation duration when you implement
-// that part. It should be large enough (20 seconds as agreed) to give the user
-// a nice smooth transition.
-constexpr int kManualToggleAnimationDurationSec = 2;
+// The duration of the temperature change animation for
+// AnimationDurationType::kShort.
+constexpr int kManualAnimationDurationSec = 2;
+
+// The duration of the temperature change animation for
+// AnimationDurationType::kLong.
+constexpr int kAutomaticAnimationDurationSec = 20;
+
+class NightLightControllerDelegateImpl : public NightLightController::Delegate {
+ public:
+ NightLightControllerDelegateImpl() {
+ // TODO(afakhry): Implement geolocation position retrieval.
+ }
+ ~NightLightControllerDelegateImpl() override = default;
+
+ // ash::NightLightController::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); }
+
+ private:
+ base::Time GetSunRiseSet(bool sunrise) const {
+ if (!ValidatePosition()) {
+ return sunrise ? TimeOfDay(kDefaultEndTimeOffsetMinutes).ToTimeToday()
+ : TimeOfDay(kDefaultStartTimeOffsetMinutes).ToTimeToday();
+ }
+
+ icu::CalendarAstronomer astro(longitude_, 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 noon_today_sec = TimeOfDay(12 * 60).ToTimeToday().ToDoubleT();
+ astro.setTime(noon_today_sec * 1000.0);
+ const double sun_rise_set_ms = astro.getSunRiseSet(sunrise);
+ return base::Time::FromDoubleT(sun_rise_set_ms / 1000.0);
+ }
+
+ bool ValidatePosition() const {
+ // TODO(afakhry): Replace with geoposition APIs.
+ return false;
+ }
+
+ // Stub values to be replaced by actual geoposition APIs.
+ double latitude_ = 0.0;
+ double longitude_ = 0.0;
+
+ DISALLOW_COPY_AND_ASSIGN(NightLightControllerDelegateImpl);
+};
// Applies the given |layer_temperature| to all the layers of the root windows
// with the given |animation_duration|.
@@ -69,6 +121,12 @@ void NightLightController::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterBooleanPref(prefs::kNightLightEnabled, false);
registry->RegisterDoublePref(prefs::kNightLightTemperature,
kDefaultColorTemperature);
+ registry->RegisterIntegerPref(prefs::kNightLightScheduleType,
+ static_cast<int>(ScheduleType::kNone));
+ registry->RegisterIntegerPref(prefs::kNightLightCustomStartTime,
+ kDefaultStartTimeOffsetMinutes);
+ registry->RegisterIntegerPref(prefs::kNightLightCustomEndTime,
+ kDefaultEndTimeOffsetMinutes);
}
void NightLightController::AddObserver(Observer* observer) {
@@ -91,9 +149,40 @@ float NightLightController::GetColorTemperature() const {
return kDefaultColorTemperature;
}
-void NightLightController::SetEnabled(bool enabled) {
- if (active_user_pref_service_)
+NightLightController::ScheduleType NightLightController::GetScheduleType()
+ const {
+ if (active_user_pref_service_) {
+ return static_cast<ScheduleType>(
+ active_user_pref_service_->GetInteger(prefs::kNightLightScheduleType));
+ }
+
+ return ScheduleType::kNone;
+}
+
+TimeOfDay NightLightController::GetCustomStartTime() const {
+ if (active_user_pref_service_) {
+ return TimeOfDay(active_user_pref_service_->GetInteger(
+ prefs::kNightLightCustomStartTime));
+ }
+
+ return TimeOfDay(kDefaultStartTimeOffsetMinutes);
+}
+
+TimeOfDay NightLightController::GetCustomEndTime() const {
+ if (active_user_pref_service_) {
+ return TimeOfDay(
+ active_user_pref_service_->GetInteger(prefs::kNightLightCustomEndTime));
+ }
+
+ return TimeOfDay(kDefaultEndTimeOffsetMinutes);
+}
+
+void NightLightController::SetEnabled(bool enabled,
+ AnimationDuration animation_type) {
+ if (active_user_pref_service_) {
+ animation_duration_ = animation_type;
active_user_pref_service_->SetBoolean(prefs::kNightLightEnabled, enabled);
+ }
}
void NightLightController::SetColorTemperature(float temperature) {
@@ -105,8 +194,31 @@ void NightLightController::SetColorTemperature(float temperature) {
}
}
+void NightLightController::SetScheduleType(ScheduleType type) {
+ if (active_user_pref_service_) {
+ active_user_pref_service_->SetInteger(prefs::kNightLightScheduleType,
+ static_cast<int>(type));
+ }
+}
+
+void NightLightController::SetCustomStartTime(TimeOfDay start_time) {
+ if (active_user_pref_service_) {
+ active_user_pref_service_->SetInteger(
+ prefs::kNightLightCustomStartTime,
+ start_time.offset_minutes_from_zero_hour());
+ }
+}
+
+void NightLightController::SetCustomEndTime(TimeOfDay end_time) {
+ if (active_user_pref_service_) {
+ active_user_pref_service_->SetInteger(
+ prefs::kNightLightCustomEndTime,
+ end_time.offset_minutes_from_zero_hour());
+ }
+}
+
void NightLightController::Toggle() {
- SetEnabled(!GetEnabled());
+ SetEnabled(!GetEnabled(), AnimationDuration::kShort);
}
void NightLightController::OnActiveUserSessionChanged(
@@ -114,15 +226,27 @@ void NightLightController::OnActiveUserSessionChanged(
// Initial login and user switching in multi profiles.
pref_change_registrar_.reset();
active_user_pref_service_ = Shell::Get()->GetActiveUserPrefService();
+ delegate_ = base::MakeUnique<NightLightControllerDelegateImpl>();
InitFromUserPrefs();
}
-void NightLightController::Refresh() {
- // TODO(afakhry): Add here refreshing of start and end times, when you
- // implement the automatic schedule settings.
+void NightLightController::SetDelegateForTesting(
+ std::unique_ptr<Delegate> delegate) {
+ delegate_ = std::move(delegate);
+}
+
+void NightLightController::RefreshLayersTemperature() {
ApplyColorTemperatureToLayers(
GetEnabled() ? GetColorTemperature() : 0.0f,
- base::TimeDelta::FromSeconds(kManualToggleAnimationDurationSec));
+ base::TimeDelta::FromSeconds(animation_duration_ ==
+ AnimationDuration::kShort
+ ? kManualAnimationDurationSec
+ : kAutomaticAnimationDurationSec));
+
+ // Reset the animation type back to manual to consume any automatically set
+ // animations.
+ last_animation_duration_ = animation_duration_;
+ animation_duration_ = AnimationDuration::kShort;
Shell::Get()->SetCursorCompositingEnabled(GetEnabled());
}
@@ -139,6 +263,18 @@ void NightLightController::StartWatchingPrefsChanges() {
prefs::kNightLightTemperature,
base::Bind(&NightLightController::OnColorTemperaturePrefChanged,
base::Unretained(this)));
+ pref_change_registrar_->Add(
+ prefs::kNightLightScheduleType,
+ base::Bind(&NightLightController::OnScheduleParamsPrefsChanged,
+ base::Unretained(this)));
+ pref_change_registrar_->Add(
+ prefs::kNightLightCustomStartTime,
+ base::Bind(&NightLightController::OnScheduleParamsPrefsChanged,
+ base::Unretained(this)));
+ pref_change_registrar_->Add(
+ prefs::kNightLightCustomEndTime,
+ base::Bind(&NightLightController::OnScheduleParamsPrefsChanged,
+ base::Unretained(this)));
}
void NightLightController::InitFromUserPrefs() {
@@ -148,7 +284,7 @@ void NightLightController::InitFromUserPrefs() {
}
StartWatchingPrefsChanges();
- Refresh();
+ Refresh(true /* did_schedule_change */);
NotifyStatusChanged();
}
@@ -159,13 +295,141 @@ void NightLightController::NotifyStatusChanged() {
void NightLightController::OnEnabledPrefChanged() {
DCHECK(active_user_pref_service_);
- Refresh();
+ Refresh(false /* did_schedule_change */);
NotifyStatusChanged();
}
void NightLightController::OnColorTemperaturePrefChanged() {
DCHECK(active_user_pref_service_);
- Refresh();
+ RefreshLayersTemperature();
+}
+
+void NightLightController::OnScheduleParamsPrefsChanged() {
+ DCHECK(active_user_pref_service_);
+ Refresh(true /* did_schedule_change */);
+}
+
+void NightLightController::Refresh(bool did_schedule_change) {
+ RefreshLayersTemperature();
+
+ const ScheduleType type = GetScheduleType();
+ switch (type) {
+ case ScheduleType::kNone:
+ timer_.Stop();
+ return;
+
+ case ScheduleType::kSunsetToSunrise:
+ RefreshScheduleTimer(delegate_->GetSunsetTime(),
+ delegate_->GetSunriseTime(), did_schedule_change);
+ return;
+
+ case ScheduleType::kCustom:
+ RefreshScheduleTimer(GetCustomStartTime().ToTimeToday(),
+ GetCustomEndTime().ToTimeToday(),
+ did_schedule_change);
+ return;
+ }
+}
+
+void NightLightController::RefreshScheduleTimer(base::Time start_time,
+ base::Time end_time,
+ bool did_schedule_change) {
+ if (GetScheduleType() == ScheduleType::kNone) {
+ NOTREACHED();
+ timer_.Stop();
+ return;
+ }
+
+ // NOTE: Users can set any weird combinations.
+ if (end_time <= start_time) {
+ // Example:
+ // Start: 9:00 PM, End: 6:00 AM.
+ //
+ // 6:00 21:00
+ // <----- + ------------------ + ----->
+ // | |
+ // end start
+ //
+ // From our perspective, the end time is always 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?
+ const base::Time now = delegate_->GetNow();
+ 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 he desires, 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);
+}
+
+void NightLightController::ScheduleNextToggle(base::TimeDelta delay) {
+ timer_.Start(
+ FROM_HERE, delay,
+ base::Bind(&NightLightController::SetEnabled, base::Unretained(this),
+ !GetEnabled(), AnimationDuration::kLong));
}
} // namespace ash
« no previous file with comments | « ash/system/night_light/night_light_controller.h ('k') | ash/system/night_light/night_light_controller_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698