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

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

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

Powered by Google App Engine
This is Rietveld 408576698