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

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

Issue 2887913004: [Night Light] CL4: Automatic schedule backend. (Closed)
Patch Set: Skip test on mash_unittests 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"
jungshik at Google 2017/05/26 10:03:48 You're not supposed to use a non-public/internal A
afakhry 2017/05/26 21:55:56 Yes, gn gen --check catches this. Please let's pus
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(
mcasas 2017/05/25 16:52:27 This will also enable Wifi-based location via [1],
afakhry 2017/05/25 17:49:59 I believe privacy will have an issue with sending
mcasas 2017/05/26 15:39:17 I'm afraid |enable_high_accuracy| doesn't do that,
afakhry 2017/05/26 21:55:56 Since now we know we can't send wifi data without
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 // Note that the icu calendar return milliseconds since epoch, and
74 // base::Time::FromDoubleT() expects seconds.
75 return base::Time::FromDoubleT(astro.getSunRiseSet(sunrise) / 1000.0);
jungshik at Google 2017/05/26 10:03:48 According to the comment in the source file, this
afakhry 2017/05/26 21:55:56 Thanks for catching this. Done. Please take a look
76 }
77
78 void OnGeoPosition(const device::Geoposition& pos) { position_ = pos; }
79
80 using LocationSubscription = device::GeolocationProvider::Subscription;
81 std::unique_ptr<LocationSubscription> geolocation_subscription_;
82
83 device::Geoposition position_;
84
85 DISALLOW_COPY_AND_ASSIGN(NightLightControllerDelegateImpl);
86 };
28 87
29 // Applies the given |layer_temperature| to all the layers of the root windows 88 // Applies the given |layer_temperature| to all the layers of the root windows
30 // with the given |animation_duration|. 89 // with the given |animation_duration|.
31 // |layer_temperature| is the ui::Layer floating-point value in the range of 90 // |layer_temperature| is the ui::Layer floating-point value in the range of
32 // 0.0f (least warm) to 1.0f (most warm). 91 // 0.0f (least warm) to 1.0f (most warm).
33 void ApplyColorTemperatureToLayers(float layer_temperature, 92 void ApplyColorTemperatureToLayers(float layer_temperature,
34 base::TimeDelta animation_duration) { 93 base::TimeDelta animation_duration) {
35 for (aura::Window* root_window : Shell::GetAllRootWindows()) { 94 for (aura::Window* root_window : Shell::GetAllRootWindows()) {
36 ui::Layer* layer = root_window->layer(); 95 ui::Layer* layer = root_window->layer();
37 96
(...skipping 16 matching lines...) Expand all
54 113
55 NightLightController::~NightLightController() { 114 NightLightController::~NightLightController() {
56 session_controller_->RemoveObserver(this); 115 session_controller_->RemoveObserver(this);
57 } 116 }
58 117
59 // static 118 // static
60 void NightLightController::RegisterPrefs(PrefRegistrySimple* registry) { 119 void NightLightController::RegisterPrefs(PrefRegistrySimple* registry) {
61 registry->RegisterBooleanPref(prefs::kNightLightEnabled, false); 120 registry->RegisterBooleanPref(prefs::kNightLightEnabled, false);
62 registry->RegisterDoublePref(prefs::kNightLightTemperature, 121 registry->RegisterDoublePref(prefs::kNightLightTemperature,
63 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);
64 } 129 }
65 130
66 void NightLightController::AddObserver(Observer* observer) { 131 void NightLightController::AddObserver(Observer* observer) {
67 observers_.AddObserver(observer); 132 observers_.AddObserver(observer);
68 } 133 }
69 134
70 void NightLightController::RemoveObserver(Observer* observer) { 135 void NightLightController::RemoveObserver(Observer* observer) {
71 observers_.RemoveObserver(observer); 136 observers_.RemoveObserver(observer);
72 } 137 }
73 138
74 bool NightLightController::GetEnabled() const { 139 bool NightLightController::GetEnabled() const {
75 return active_user_pref_service_ && 140 return active_user_pref_service_ &&
76 active_user_pref_service_->GetBoolean(prefs::kNightLightEnabled); 141 active_user_pref_service_->GetBoolean(prefs::kNightLightEnabled);
77 } 142 }
78 143
79 float NightLightController::GetColorTemperature() const { 144 float NightLightController::GetColorTemperature() const {
80 if (active_user_pref_service_) 145 if (active_user_pref_service_)
81 return active_user_pref_service_->GetDouble(prefs::kNightLightTemperature); 146 return active_user_pref_service_->GetDouble(prefs::kNightLightTemperature);
82 147
83 return kDefaultColorTemperature; 148 return kDefaultColorTemperature;
84 } 149 }
85 150
86 void NightLightController::SetEnabled(bool enabled) { 151 NightLightController::ScheduleType NightLightController::GetScheduleType()
87 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;
88 active_user_pref_service_->SetBoolean(prefs::kNightLightEnabled, enabled); 183 active_user_pref_service_->SetBoolean(prefs::kNightLightEnabled, enabled);
184 }
89 } 185 }
90 186
91 void NightLightController::SetColorTemperature(float temperature) { 187 void NightLightController::SetColorTemperature(float temperature) {
92 DCHECK_GE(temperature, 0.0f); 188 DCHECK_GE(temperature, 0.0f);
93 DCHECK_LE(temperature, 1.0f); 189 DCHECK_LE(temperature, 1.0f);
94 if (active_user_pref_service_) { 190 if (active_user_pref_service_) {
95 active_user_pref_service_->SetDouble(prefs::kNightLightTemperature, 191 active_user_pref_service_->SetDouble(prefs::kNightLightTemperature,
96 temperature); 192 temperature);
97 } 193 }
98 } 194 }
99 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
100 void NightLightController::Toggle() { 219 void NightLightController::Toggle() {
101 SetEnabled(!GetEnabled()); 220 SetEnabled(!GetEnabled(), AnimationDurationType::kShort);
102 } 221 }
103 222
104 void NightLightController::OnActiveUserSessionChanged( 223 void NightLightController::OnActiveUserSessionChanged(
105 const AccountId& account_id) { 224 const AccountId& account_id) {
106 // Initial login and user switching in multi profiles. 225 // Initial login and user switching in multi profiles.
107 pref_change_registrar_.reset(); 226 pref_change_registrar_.reset();
108 active_user_pref_service_ = Shell::Get()->GetActiveUserPrefService(); 227 active_user_pref_service_ = Shell::Get()->GetActiveUserPrefService();
228 delegate_ = base::MakeUnique<NightLightControllerDelegateImpl>();
109 InitFromUserPrefs(); 229 InitFromUserPrefs();
110 } 230 }
111 231
112 void NightLightController::Refresh() { 232 void NightLightController::SetDelegateForTesting(
233 std::unique_ptr<Delegate> delegate) {
234 delegate_ = std::move(delegate);
235 }
236
237 void NightLightController::RefreshLayersTemperature() {
113 // TODO(afakhry): Add here refreshing of start and end times, when you 238 // TODO(afakhry): Add here refreshing of start and end times, when you
114 // implement the automatic schedule settings. 239 // implement the automatic schedule settings.
115 ApplyColorTemperatureToLayers( 240 ApplyColorTemperatureToLayers(
116 GetEnabled() ? GetColorTemperature() : 0.0f, 241 GetEnabled() ? GetColorTemperature() : 0.0f,
117 base::TimeDelta::FromSeconds(kManualToggleAnimationDurationSec)); 242 base::TimeDelta::FromSeconds(animation_type_ ==
243 AnimationDurationType::kShort
244 ? kManualAnimationDurationSec
245 : kAutomaticAnimationDurationSec));
246
247 // Reset the animation type back to manual to consume any automatically set
248 // animations.
249 last_animation_type_ = animation_type_;
250 animation_type_ = AnimationDurationType::kShort;
118 } 251 }
119 252
120 void NightLightController::StartWatchingPrefsChanges() { 253 void NightLightController::StartWatchingPrefsChanges() {
121 DCHECK(active_user_pref_service_); 254 DCHECK(active_user_pref_service_);
122 255
123 pref_change_registrar_ = base::MakeUnique<PrefChangeRegistrar>(); 256 pref_change_registrar_ = base::MakeUnique<PrefChangeRegistrar>();
124 pref_change_registrar_->Init(active_user_pref_service_); 257 pref_change_registrar_->Init(active_user_pref_service_);
125 pref_change_registrar_->Add( 258 pref_change_registrar_->Add(
126 prefs::kNightLightEnabled, 259 prefs::kNightLightEnabled,
127 base::Bind(&NightLightController::OnEnabledPrefChanged, 260 base::Bind(&NightLightController::OnEnabledPrefChanged,
128 base::Unretained(this))); 261 base::Unretained(this)));
129 pref_change_registrar_->Add( 262 pref_change_registrar_->Add(
130 prefs::kNightLightTemperature, 263 prefs::kNightLightTemperature,
131 base::Bind(&NightLightController::OnColorTemperaturePrefChanged, 264 base::Bind(&NightLightController::OnColorTemperaturePrefChanged,
132 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)));
133 } 278 }
134 279
135 void NightLightController::InitFromUserPrefs() { 280 void NightLightController::InitFromUserPrefs() {
136 if (!active_user_pref_service_) { 281 if (!active_user_pref_service_) {
137 // The pref_service can be null in ash_unittests. 282 // The pref_service can be null in ash_unittests.
138 return; 283 return;
139 } 284 }
140 285
141 StartWatchingPrefsChanges(); 286 StartWatchingPrefsChanges();
142 Refresh(); 287 Refresh(true /* did_schedule_change */);
143 NotifyStatusChanged(); 288 NotifyStatusChanged();
144 } 289 }
145 290
146 void NightLightController::NotifyStatusChanged() { 291 void NightLightController::NotifyStatusChanged() {
147 for (auto& observer : observers_) 292 for (auto& observer : observers_)
148 observer.OnNightLightEnabledChanged(GetEnabled()); 293 observer.OnNightLightEnabledChanged(GetEnabled());
149 } 294 }
150 295
151 void NightLightController::OnEnabledPrefChanged() { 296 void NightLightController::OnEnabledPrefChanged() {
152 DCHECK(active_user_pref_service_); 297 DCHECK(active_user_pref_service_);
153 Refresh(); 298 Refresh(false /* did_schedule_change */);
154 NotifyStatusChanged(); 299 NotifyStatusChanged();
155 } 300 }
156 301
157 void NightLightController::OnColorTemperaturePrefChanged() { 302 void NightLightController::OnColorTemperaturePrefChanged() {
158 DCHECK(active_user_pref_service_); 303 DCHECK(active_user_pref_service_);
159 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, AnimationDurationType::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(), AnimationDurationType::kLong));
160 } 433 }
161 434
162 } // 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