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

Side by Side 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 unified diff | Download patch
OLDNEW
1 // Copyright 2017 The Chromium Authors. All rights reserved. 1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ash/system/night_light/night_light_controller.h" 5 #include "ash/system/night_light/night_light_controller.h"
6 6
7 #include "ash/ash_switches.h" 7 #include "ash/ash_switches.h"
8 #include "ash/public/cpp/ash_pref_names.h" 8 #include "ash/public/cpp/ash_pref_names.h"
9 #include "ash/session/session_controller.h" 9 #include "ash/session/session_controller.h"
10 #include "ash/shell.h" 10 #include "ash/shell.h"
11 #include "base/command_line.h" 11 #include "base/command_line.h"
12 #include "base/time/time.h" 12 #include "base/time/time.h"
13 #include "components/prefs/pref_registry_simple.h" 13 #include "components/prefs/pref_registry_simple.h"
14 #include "components/prefs/pref_service.h" 14 #include "components/prefs/pref_service.h"
15 #include "third_party/icu/source/i18n/astro.h"
15 #include "ui/compositor/layer.h" 16 #include "ui/compositor/layer.h"
16 #include "ui/compositor/scoped_layer_animation_settings.h" 17 #include "ui/compositor/scoped_layer_animation_settings.h"
17 18
18 namespace ash { 19 namespace ash {
19 20
20 namespace { 21 namespace {
21 22
23 // Default start time at 6:00 PM as an offset from 00:00.
24 const int kDefaultStartTimeOffsetMinutes = 18 * 60;
25
26 // Default end time at 6:00 AM as an offset from 00:00.
27 const int kDefaultEndTimeOffsetMinutes = 6 * 60;
28
22 constexpr float kDefaultColorTemperature = 0.5f; 29 constexpr float kDefaultColorTemperature = 0.5f;
23 30
24 // The duration of the temperature change animation when the change is a result 31 // The duration of the temperature change animation for
25 // of a manual user setting. 32 // AnimationDurationType::kShort.
26 // TODO(afakhry): Add automatic schedule animation duration when you implement 33 constexpr int kManualAnimationDurationSec = 2;
27 // that part. It should be large enough (20 seconds as agreed) to give the user 34
28 // a nice smooth transition. 35 // The duration of the temperature change animation for
29 constexpr int kManualToggleAnimationDurationSec = 2; 36 // AnimationDurationType::kLong.
37 constexpr int kAutomaticAnimationDurationSec = 20;
38
39 class NightLightControllerDelegateImpl : public NightLightController::Delegate {
40 public:
41 NightLightControllerDelegateImpl() {
42 // TODO(afakhry): Implement geolocation position retrieval.
43 }
44 ~NightLightControllerDelegateImpl() override = default;
45
46 // ash::NightLightController::Delegate:
47 base::Time GetNow() const override { return base::Time::Now(); }
48 base::Time GetSunsetTime() const override { return GetSunRiseSet(false); }
49 base::Time GetSunriseTime() const override { return GetSunRiseSet(true); }
50
51 private:
52 base::Time GetSunRiseSet(bool sunrise) const {
53 if (!ValidatePosition()) {
54 return sunrise ? TimeOfDay(kDefaultEndTimeOffsetMinutes).ToTimeToday()
55 : TimeOfDay(kDefaultStartTimeOffsetMinutes).ToTimeToday();
56 }
57
58 icu::CalendarAstronomer astro(longitude_, latitude_);
59 // For sunset and sunrise times calculations to be correct, the time of the
60 // icu::CalendarAstronomer object should be set to a time near local noon.
61 // This avoids having the computation flopping over into an adjacent day.
62 // See the documentation of icu::CalendarAstronomer::getSunRiseSet().
63 // Note that the icu calendar works with milliseconds since epoch, and
64 // base::Time::FromDoubleT() / ToDoubleT() work with seconds since epoch.
65 const double noon_today_sec = TimeOfDay(12 * 60).ToTimeToday().ToDoubleT();
66 astro.setTime(noon_today_sec * 1000.0);
67 const double sun_rise_set_ms = astro.getSunRiseSet(sunrise);
68 return base::Time::FromDoubleT(sun_rise_set_ms / 1000.0);
69 }
70
71 bool ValidatePosition() const {
72 // TODO(afakhry): Replace with geoposition APIs.
73 return false;
74 }
75
76 // Stub values to be replaced by actual geoposition APIs.
77 double latitude_ = 0.0;
78 double longitude_ = 0.0;
79
80 DISALLOW_COPY_AND_ASSIGN(NightLightControllerDelegateImpl);
81 };
30 82
31 // Applies the given |layer_temperature| to all the layers of the root windows 83 // Applies the given |layer_temperature| to all the layers of the root windows
32 // with the given |animation_duration|. 84 // with the given |animation_duration|.
33 // |layer_temperature| is the ui::Layer floating-point value in the range of 85 // |layer_temperature| is the ui::Layer floating-point value in the range of
34 // 0.0f (least warm) to 1.0f (most warm). 86 // 0.0f (least warm) to 1.0f (most warm).
35 void ApplyColorTemperatureToLayers(float layer_temperature, 87 void ApplyColorTemperatureToLayers(float layer_temperature,
36 base::TimeDelta animation_duration) { 88 base::TimeDelta animation_duration) {
37 for (aura::Window* root_window : Shell::GetAllRootWindows()) { 89 for (aura::Window* root_window : Shell::GetAllRootWindows()) {
38 ui::Layer* layer = root_window->layer(); 90 ui::Layer* layer = root_window->layer();
39 91
(...skipping 22 matching lines...) Expand all
62 bool NightLightController::IsFeatureEnabled() { 114 bool NightLightController::IsFeatureEnabled() {
63 return base::CommandLine::ForCurrentProcess()->HasSwitch( 115 return base::CommandLine::ForCurrentProcess()->HasSwitch(
64 ash::switches::kAshEnableNightLight); 116 ash::switches::kAshEnableNightLight);
65 } 117 }
66 118
67 // static 119 // static
68 void NightLightController::RegisterPrefs(PrefRegistrySimple* registry) { 120 void NightLightController::RegisterPrefs(PrefRegistrySimple* registry) {
69 registry->RegisterBooleanPref(prefs::kNightLightEnabled, false); 121 registry->RegisterBooleanPref(prefs::kNightLightEnabled, false);
70 registry->RegisterDoublePref(prefs::kNightLightTemperature, 122 registry->RegisterDoublePref(prefs::kNightLightTemperature,
71 kDefaultColorTemperature); 123 kDefaultColorTemperature);
124 registry->RegisterIntegerPref(prefs::kNightLightScheduleType,
125 static_cast<int>(ScheduleType::kNone));
126 registry->RegisterIntegerPref(prefs::kNightLightCustomStartTime,
127 kDefaultStartTimeOffsetMinutes);
128 registry->RegisterIntegerPref(prefs::kNightLightCustomEndTime,
129 kDefaultEndTimeOffsetMinutes);
72 } 130 }
73 131
74 void NightLightController::AddObserver(Observer* observer) { 132 void NightLightController::AddObserver(Observer* observer) {
75 observers_.AddObserver(observer); 133 observers_.AddObserver(observer);
76 } 134 }
77 135
78 void NightLightController::RemoveObserver(Observer* observer) { 136 void NightLightController::RemoveObserver(Observer* observer) {
79 observers_.RemoveObserver(observer); 137 observers_.RemoveObserver(observer);
80 } 138 }
81 139
82 bool NightLightController::GetEnabled() const { 140 bool NightLightController::GetEnabled() const {
83 return active_user_pref_service_ && 141 return active_user_pref_service_ &&
84 active_user_pref_service_->GetBoolean(prefs::kNightLightEnabled); 142 active_user_pref_service_->GetBoolean(prefs::kNightLightEnabled);
85 } 143 }
86 144
87 float NightLightController::GetColorTemperature() const { 145 float NightLightController::GetColorTemperature() const {
88 if (active_user_pref_service_) 146 if (active_user_pref_service_)
89 return active_user_pref_service_->GetDouble(prefs::kNightLightTemperature); 147 return active_user_pref_service_->GetDouble(prefs::kNightLightTemperature);
90 148
91 return kDefaultColorTemperature; 149 return kDefaultColorTemperature;
92 } 150 }
93 151
94 void NightLightController::SetEnabled(bool enabled) { 152 NightLightController::ScheduleType NightLightController::GetScheduleType()
95 if (active_user_pref_service_) 153 const {
154 if (active_user_pref_service_) {
155 return static_cast<ScheduleType>(
156 active_user_pref_service_->GetInteger(prefs::kNightLightScheduleType));
157 }
158
159 return ScheduleType::kNone;
160 }
161
162 TimeOfDay NightLightController::GetCustomStartTime() const {
163 if (active_user_pref_service_) {
164 return TimeOfDay(active_user_pref_service_->GetInteger(
165 prefs::kNightLightCustomStartTime));
166 }
167
168 return TimeOfDay(kDefaultStartTimeOffsetMinutes);
169 }
170
171 TimeOfDay NightLightController::GetCustomEndTime() const {
172 if (active_user_pref_service_) {
173 return TimeOfDay(
174 active_user_pref_service_->GetInteger(prefs::kNightLightCustomEndTime));
175 }
176
177 return TimeOfDay(kDefaultEndTimeOffsetMinutes);
178 }
179
180 void NightLightController::SetEnabled(bool enabled,
181 AnimationDuration animation_type) {
182 if (active_user_pref_service_) {
183 animation_duration_ = animation_type;
96 active_user_pref_service_->SetBoolean(prefs::kNightLightEnabled, enabled); 184 active_user_pref_service_->SetBoolean(prefs::kNightLightEnabled, enabled);
185 }
97 } 186 }
98 187
99 void NightLightController::SetColorTemperature(float temperature) { 188 void NightLightController::SetColorTemperature(float temperature) {
100 DCHECK_GE(temperature, 0.0f); 189 DCHECK_GE(temperature, 0.0f);
101 DCHECK_LE(temperature, 1.0f); 190 DCHECK_LE(temperature, 1.0f);
102 if (active_user_pref_service_) { 191 if (active_user_pref_service_) {
103 active_user_pref_service_->SetDouble(prefs::kNightLightTemperature, 192 active_user_pref_service_->SetDouble(prefs::kNightLightTemperature,
104 temperature); 193 temperature);
105 } 194 }
106 } 195 }
107 196
197 void NightLightController::SetScheduleType(ScheduleType type) {
198 if (active_user_pref_service_) {
199 active_user_pref_service_->SetInteger(prefs::kNightLightScheduleType,
200 static_cast<int>(type));
201 }
202 }
203
204 void NightLightController::SetCustomStartTime(TimeOfDay start_time) {
205 if (active_user_pref_service_) {
206 active_user_pref_service_->SetInteger(
207 prefs::kNightLightCustomStartTime,
208 start_time.offset_minutes_from_zero_hour());
209 }
210 }
211
212 void NightLightController::SetCustomEndTime(TimeOfDay end_time) {
213 if (active_user_pref_service_) {
214 active_user_pref_service_->SetInteger(
215 prefs::kNightLightCustomEndTime,
216 end_time.offset_minutes_from_zero_hour());
217 }
218 }
219
108 void NightLightController::Toggle() { 220 void NightLightController::Toggle() {
109 SetEnabled(!GetEnabled()); 221 SetEnabled(!GetEnabled(), AnimationDuration::kShort);
110 } 222 }
111 223
112 void NightLightController::OnActiveUserSessionChanged( 224 void NightLightController::OnActiveUserSessionChanged(
113 const AccountId& account_id) { 225 const AccountId& account_id) {
114 // Initial login and user switching in multi profiles. 226 // Initial login and user switching in multi profiles.
115 pref_change_registrar_.reset(); 227 pref_change_registrar_.reset();
116 active_user_pref_service_ = Shell::Get()->GetActiveUserPrefService(); 228 active_user_pref_service_ = Shell::Get()->GetActiveUserPrefService();
229 delegate_ = base::MakeUnique<NightLightControllerDelegateImpl>();
117 InitFromUserPrefs(); 230 InitFromUserPrefs();
118 } 231 }
119 232
120 void NightLightController::Refresh() { 233 void NightLightController::SetDelegateForTesting(
121 // TODO(afakhry): Add here refreshing of start and end times, when you 234 std::unique_ptr<Delegate> delegate) {
122 // implement the automatic schedule settings. 235 delegate_ = std::move(delegate);
236 }
237
238 void NightLightController::RefreshLayersTemperature() {
123 ApplyColorTemperatureToLayers( 239 ApplyColorTemperatureToLayers(
124 GetEnabled() ? GetColorTemperature() : 0.0f, 240 GetEnabled() ? GetColorTemperature() : 0.0f,
125 base::TimeDelta::FromSeconds(kManualToggleAnimationDurationSec)); 241 base::TimeDelta::FromSeconds(animation_duration_ ==
242 AnimationDuration::kShort
243 ? kManualAnimationDurationSec
244 : kAutomaticAnimationDurationSec));
245
246 // Reset the animation type back to manual to consume any automatically set
247 // animations.
248 last_animation_duration_ = animation_duration_;
249 animation_duration_ = AnimationDuration::kShort;
126 Shell::Get()->SetCursorCompositingEnabled(GetEnabled()); 250 Shell::Get()->SetCursorCompositingEnabled(GetEnabled());
127 } 251 }
128 252
129 void NightLightController::StartWatchingPrefsChanges() { 253 void NightLightController::StartWatchingPrefsChanges() {
130 DCHECK(active_user_pref_service_); 254 DCHECK(active_user_pref_service_);
131 255
132 pref_change_registrar_ = base::MakeUnique<PrefChangeRegistrar>(); 256 pref_change_registrar_ = base::MakeUnique<PrefChangeRegistrar>();
133 pref_change_registrar_->Init(active_user_pref_service_); 257 pref_change_registrar_->Init(active_user_pref_service_);
134 pref_change_registrar_->Add( 258 pref_change_registrar_->Add(
135 prefs::kNightLightEnabled, 259 prefs::kNightLightEnabled,
136 base::Bind(&NightLightController::OnEnabledPrefChanged, 260 base::Bind(&NightLightController::OnEnabledPrefChanged,
137 base::Unretained(this))); 261 base::Unretained(this)));
138 pref_change_registrar_->Add( 262 pref_change_registrar_->Add(
139 prefs::kNightLightTemperature, 263 prefs::kNightLightTemperature,
140 base::Bind(&NightLightController::OnColorTemperaturePrefChanged, 264 base::Bind(&NightLightController::OnColorTemperaturePrefChanged,
141 base::Unretained(this))); 265 base::Unretained(this)));
266 pref_change_registrar_->Add(
267 prefs::kNightLightScheduleType,
268 base::Bind(&NightLightController::OnScheduleParamsPrefsChanged,
269 base::Unretained(this)));
270 pref_change_registrar_->Add(
271 prefs::kNightLightCustomStartTime,
272 base::Bind(&NightLightController::OnScheduleParamsPrefsChanged,
273 base::Unretained(this)));
274 pref_change_registrar_->Add(
275 prefs::kNightLightCustomEndTime,
276 base::Bind(&NightLightController::OnScheduleParamsPrefsChanged,
277 base::Unretained(this)));
142 } 278 }
143 279
144 void NightLightController::InitFromUserPrefs() { 280 void NightLightController::InitFromUserPrefs() {
145 if (!active_user_pref_service_) { 281 if (!active_user_pref_service_) {
146 // The pref_service can be null in ash_unittests. 282 // The pref_service can be null in ash_unittests.
147 return; 283 return;
148 } 284 }
149 285
150 StartWatchingPrefsChanges(); 286 StartWatchingPrefsChanges();
151 Refresh(); 287 Refresh(true /* did_schedule_change */);
152 NotifyStatusChanged(); 288 NotifyStatusChanged();
153 } 289 }
154 290
155 void NightLightController::NotifyStatusChanged() { 291 void NightLightController::NotifyStatusChanged() {
156 for (auto& observer : observers_) 292 for (auto& observer : observers_)
157 observer.OnNightLightEnabledChanged(GetEnabled()); 293 observer.OnNightLightEnabledChanged(GetEnabled());
158 } 294 }
159 295
160 void NightLightController::OnEnabledPrefChanged() { 296 void NightLightController::OnEnabledPrefChanged() {
161 DCHECK(active_user_pref_service_); 297 DCHECK(active_user_pref_service_);
162 Refresh(); 298 Refresh(false /* did_schedule_change */);
163 NotifyStatusChanged(); 299 NotifyStatusChanged();
164 } 300 }
165 301
166 void NightLightController::OnColorTemperaturePrefChanged() { 302 void NightLightController::OnColorTemperaturePrefChanged() {
167 DCHECK(active_user_pref_service_); 303 DCHECK(active_user_pref_service_);
168 Refresh(); 304 RefreshLayersTemperature();
305 }
306
307 void NightLightController::OnScheduleParamsPrefsChanged() {
308 DCHECK(active_user_pref_service_);
309 Refresh(true /* did_schedule_change */);
310 }
311
312 void NightLightController::Refresh(bool did_schedule_change) {
313 RefreshLayersTemperature();
314
315 const ScheduleType type = GetScheduleType();
316 switch (type) {
317 case ScheduleType::kNone:
318 timer_.Stop();
319 return;
320
321 case ScheduleType::kSunsetToSunrise:
322 RefreshScheduleTimer(delegate_->GetSunsetTime(),
323 delegate_->GetSunriseTime(), did_schedule_change);
324 return;
325
326 case ScheduleType::kCustom:
327 RefreshScheduleTimer(GetCustomStartTime().ToTimeToday(),
328 GetCustomEndTime().ToTimeToday(),
329 did_schedule_change);
330 return;
331 }
332 }
333
334 void NightLightController::RefreshScheduleTimer(base::Time start_time,
335 base::Time end_time,
336 bool did_schedule_change) {
337 if (GetScheduleType() == ScheduleType::kNone) {
338 NOTREACHED();
339 timer_.Stop();
340 return;
341 }
342
343 // NOTE: Users can set any weird combinations.
344 if (end_time <= start_time) {
345 // Example:
346 // Start: 9:00 PM, End: 6:00 AM.
347 //
348 // 6:00 21:00
349 // <----- + ------------------ + ----->
350 // | |
351 // end start
352 //
353 // From our perspective, the end time is always a day later.
354 end_time += base::TimeDelta::FromDays(1);
355 }
356
357 DCHECK_GE(end_time, start_time);
358
359 // The target status that we need to set NightLight to now if a change of
360 // status is needed immediately.
361 bool enable_now = false;
362
363 // Where are we now with respect to the start and end times?
364 const base::Time now = delegate_->GetNow();
365 if (now < start_time) {
366 // Example:
367 // Start: 6:00 PM today, End: 6:00 AM tomorrow, Now: 4:00 PM.
368 //
369 // <----- + ----------- + ----------- + ----->
370 // | | |
371 // now start end
372 //
373 // In this case, we need to disable NightLight immediately if it's enabled.
374 enable_now = false;
375 } else if (now >= start_time && now < end_time) {
376 // Example:
377 // Start: 6:00 PM today, End: 6:00 AM tomorrow, Now: 11:00 PM.
378 //
379 // <----- + ----------- + ----------- + ----->
380 // | | |
381 // start now end
382 //
383 // Start NightLight right away. Our future start time is a day later than
384 // its current value.
385 enable_now = true;
386 start_time += base::TimeDelta::FromDays(1);
387 } else { // now >= end_time.
388 // Example:
389 // Start: 6:00 PM today, End: 10:00 PM today, Now: 11:00 PM.
390 //
391 // <----- + ----------- + ----------- + ----->
392 // | | |
393 // start end now
394 //
395 // In this case, our future start and end times are a day later from their
396 // current values. NightLight needs to be ended immediately if it's already
397 // enabled.
398 enable_now = false;
399 start_time += base::TimeDelta::FromDays(1);
400 end_time += base::TimeDelta::FromDays(1);
401 }
402
403 // After the above processing, the start and end time are all in the future.
404 DCHECK_GE(start_time, now);
405 DCHECK_GE(end_time, now);
406
407 if (did_schedule_change && enable_now != GetEnabled()) {
408 // If the change in the schedule introduces a change in the status, then
409 // calling SetEnabled() is all we need, since it will trigger a change in
410 // the user prefs to which we will respond by calling Refresh(). This will
411 // end up in this function again, adjusting all the needed schedules.
412 SetEnabled(enable_now, AnimationDuration::kShort);
413 return;
414 }
415
416 // We reach here in one of the following conditions:
417 // 1) If schedule changes don't result in changes in the status, we need to
418 // explicitly update the timer to re-schedule the next toggle to account for
419 // any changes.
420 // 2) The user has just manually toggled the status of NightLight either from
421 // the System Menu or System Settings. In this case, we respect the user
422 // wish and maintain the current status that he desires, but we schedule the
423 // status to be toggled according to the time that corresponds with the
424 // opposite status of the current one.
425 ScheduleNextToggle(GetEnabled() ? end_time - now : start_time - now);
426 }
427
428 void NightLightController::ScheduleNextToggle(base::TimeDelta delay) {
429 timer_.Start(
430 FROM_HERE, delay,
431 base::Bind(&NightLightController::SetEnabled, base::Unretained(this),
432 !GetEnabled(), AnimationDuration::kLong));
169 } 433 }
170 434
171 } // namespace ash 435 } // namespace ash
OLDNEW
« 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