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

Side by Side Diff: ash/system/night_light/night_light_controller.cc

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

Powered by Google App Engine
This is Rietveld 408576698