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

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

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

Powered by Google App Engine
This is Rietveld 408576698