blob: 25b905f7e2686b2044810fb84b97ef86c43294af [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_SYSTEM_NIGHT_LIGHT_NIGHT_LIGHT_CONTROLLER_IMPL_H_
#define ASH_SYSTEM_NIGHT_LIGHT_NIGHT_LIGHT_CONTROLLER_IMPL_H_
#include <memory>
#include "ash/ash_export.h"
#include "ash/display/window_tree_host_manager.h"
#include "ash/public/cpp/night_light_controller.h"
#include "ash/public/cpp/session/session_observer.h"
#include "ash/system/night_light/time_of_day.h"
#include "base/containers/flat_map.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "components/prefs/pref_change_registrar.h"
#include "ui/aura/env_observer.h"
#include "ui/message_center/public/cpp/notification_delegate.h"
class PrefRegistrySimple;
class PrefService;
namespace message_center {
class Notification;
} // namespace message_center
namespace ash {
class ColorTemperatureAnimation;
// Controls the NightLight feature that adjusts the color temperature of the
// screen. It uses the display's hardware CRTC (Cathode Ray Tube Controller)
// color transform matrix (CTM) when possible for efficiency, and can fall back
// to setting a color matrix on the compositor if the display doesn't support
// color transformation.
// For Unified Desktop mode, the color matrix is set on the mirroring actual
// displays' hosts, rather than on the Unified host, so that we can use the
// CRTC matrix if available (the Unified host doesn't correspond to an actual
// display).
class ASH_EXPORT NightLightControllerImpl
: public NightLightController,
public WindowTreeHostManager::Observer,
public aura::EnvObserver,
public SessionObserver,
public chromeos::PowerManagerClient::Observer,
public message_center::NotificationObserver {
public:
enum class AnimationDuration {
// Short animation (2 seconds) used for manual changes of NightLight status
// and temperature by the user.
kShort,
// Long animation (20 seconds) used for applying the color temperature
// gradually as a result of getting into or out of the automatically
// scheduled NightLight mode. This gives the user a smooth transition.
kLong,
};
// This class enables us to inject fake values for "Now" as well as the sunset
// and sunrise times, so that we can reliably test the behavior in various
// schedule types and times.
class Delegate {
public:
// NightLightController owns the delegate.
virtual ~Delegate() = default;
// Gets the current time.
virtual base::Time GetNow() const = 0;
// Gets the sunset and sunrise times.
virtual base::Time GetSunsetTime() const = 0;
virtual base::Time GetSunriseTime() const = 0;
// Provides the delegate with the geoposition so that it can be used to
// calculate sunset and sunrise times.
// Returns true if |position| is different than the current known value,
// potentially requiring a refresh of the schedule. False otherwise.
virtual bool SetGeoposition(const SimpleGeoposition& position) = 0;
// Returns true if a geoposition value is available.
virtual bool HasGeoposition() const = 0;
};
NightLightControllerImpl();
~NightLightControllerImpl() override;
static void RegisterProfilePrefs(PrefRegistrySimple* registry);
// Convenience functions for converting between the color temperature value,
// and the blue and green color scales. Note that the red color scale remains
// unaffected (i.e. its scale remains 1.0f);
// When these color scales are to be applied in the linear color space (i.e.
// after gamma decoding), |temperature| should be the non-linear temperature
// (see GetNonLinearTemperature() below), the blue scale uses the same
// attenuation, while the green scale is attenuated a bit more than it
// normally is when the scales are meant for the compressed gamma space.
static float BlueColorScaleFromTemperature(float temperature);
static float GreenColorScaleFromTemperature(float temperature,
bool in_linear_space);
// When using the CRTC color correction, depending on the hardware, the matrix
// may be applied in the linear gamma space (i.e. after gamma decoding), or in
// the non-linear gamma compressed space (i.e. after degamma encoding). Our
// standard temperature we use here, which the user changes, follow a linear
// slope from 0.0f to 1.0f. This won't give the same linear rate of change in
// colors as the temperature changes in the linear color space. To account for
// this, we want the temperature to follow the same slope as that of the gamma
// factor.
// This function returns the non-linear temperature that corresponds to the
// linear |temperature| value.
static float GetNonLinearTemperature(float temperature);
// When reading ambient color temperature via powerd, it needs to be mapped
// to another temperature before it can be used to determine the RGB scale
// factors (i.e: CTM diagonal). The mapping was computed according to
// internal user studies.
// The returned adjusted temperature is in Kelvin as well.
static float RemapAmbientColorTemperature(float temperature_in_kelvin);
// Given an overall temperature in Kelvin, returns the scale factors for R, G
// and B channel.
// |temperature_in_kelvin| is expected to be a remapped color temperature
// from the sensor using |RemapAmbientColorTemperature|.
static gfx::Vector3dF ColorScalesFromRemappedTemperatureInKevin(
float temperature_in_kelvin);
AnimationDuration animation_duration() const { return animation_duration_; }
AnimationDuration last_animation_duration() const {
return last_animation_duration_;
}
base::OneShotTimer* timer() { return &timer_; }
bool is_current_geoposition_from_cache() const {
return is_current_geoposition_from_cache_;
}
float ambient_temperature() const { return ambient_temperature_; }
const gfx::Vector3dF& ambient_rgb_scaling_factors() const {
return ambient_rgb_scaling_factors_;
}
float GetColorTemperature() const;
ScheduleType GetScheduleType() const;
TimeOfDay GetCustomStartTime() const;
TimeOfDay GetCustomEndTime() const;
bool GetAmbientColorEnabled() const;
// Get whether the current time is after sunset and before sunrise.
bool IsNowWithinSunsetSunrise() const;
// Update |ambient_rgb_scaling_factors_| from the current
// |ambient_temperature_|.
void UpdateAmbientRgbScalingFactors();
// Set the desired NightLight settings in the current active user prefs.
void SetEnabled(bool enabled, AnimationDuration animation_type);
void SetColorTemperature(float temperature);
void SetScheduleType(ScheduleType type);
void SetCustomStartTime(TimeOfDay start_time);
void SetCustomEndTime(TimeOfDay end_time);
void SetAmbientColorEnabled(bool enabled);
// This is always called as a result of a user action and will always use the
// AnimationDurationType::kShort.
void Toggle();
// ash::WindowTreeHostManager::Observer:
void OnDisplayConfigurationChanged() override;
// aura::EnvObserver:
void OnWindowInitialized(aura::Window* window) override {}
void OnHostInitialized(aura::WindowTreeHost* host) override;
// SessionObserver:
void OnActiveUserPrefServiceChanged(PrefService* pref_service) override;
// ash::NightLightController:
void SetCurrentGeoposition(const SimpleGeoposition& position) override;
bool GetEnabled() const override;
// chromeos::PowerManagerClient::Observer:
void SuspendDone(base::TimeDelta sleep_duration) override;
void AmbientColorChanged(const int32_t color_temperature) override;
// message_center::NotificationObserver:
void Close(bool by_user) override;
void Click(const base::Optional<int>& button_index,
const base::Optional<base::string16>& reply) override;
void SetDelegateForTesting(std::unique_ptr<Delegate> delegate);
// Returns the Auto Night Light notification if any is currently shown, or
// nullptr.
message_center::Notification* GetAutoNightLightNotificationForTesting() const;
private:
// Attempts restoring a previously stored schedule for the current user if
// possible and returns true if so, false otherwise.
bool MaybeRestoreSchedule();
// Returns true if the user has ever changed the schedule type, which means we
// must respect the user's choice and let it overwrite Auto Night Light.
bool UserHasEverChangedSchedule() const;
// Returns true if the user has ever dismissed the Auto Night Light
// notification, in which case we never show it again.
bool UserHasEverDismissedAutoNightLightNotification() const;
// Shows the notification informing the user that Night Light has been turned
// on from sunset-to-sunrise as a result of Auto Night Light.
void ShowAutoNightLightNotification();
// Disables showing the Auto Night Light from now on.
void DisableShowingFutureAutoNightLightNotification();
// Called only when the active user changes in order to see if we need to use
// a previously cached geoposition value from the active user's prefs.
void LoadCachedGeopositionIfNeeded();
// Called whenever we receive a new geoposition update to cache it in all
// logged-in users' prefs so that it can be used later in the event of not
// being able to retrieve a valid geoposition.
void StoreCachedGeoposition(const SimpleGeoposition& position);
// Refreshes the displays color transforms based on the given
// |color_temperature|, which will be overridden to a value of 0 if NightLight
// is turned off.
void RefreshDisplaysTemperature(float color_temperature);
// Reapplys the current color temperature on the displays without starting a
// new animation or overriding an on-going one towards the same target
// temperature.
void ReapplyColorTemperatures();
void StartWatchingPrefsChanges();
void InitFromUserPrefs();
void NotifyStatusChanged();
void NotifyClientWithScheduleChange();
// Called when the user pref for the enabled status of NightLight is changed.
void OnEnabledPrefChanged();
// Called when the user pref for the enabled status of Ambient Color is
// changed.
void OnAmbientColorEnabledPrefChanged();
// Called when the user pref for the color temperature is changed.
void OnColorTemperaturePrefChanged();
// Called when the user pref for the schedule type is changed.
void OnScheduleTypePrefChanged();
// Called when either of the custom schedule prefs (custom start or end times)
// are changed.
void OnCustomSchedulePrefsChanged();
// Refreshes the state of NightLight according to the currently set
// parameters. |did_schedule_change| is true when Refresh() is called as a
// result of a change in one of the schedule related prefs, and false
// otherwise.
// If |keep_manual_toggles_during_schedules| is true, refreshing the schedule
// will not override a previous user's decision to toggle the NightLight
// status while the schedule is being used.
void Refresh(bool did_schedule_change,
bool keep_manual_toggles_during_schedules);
// Given the desired start and end times that determine the time interval
// during which NightLight will be ON, depending on the time of "now", it
// refreshes the |timer_| to either schedule the future start or end of
// NightLight mode, as well as update the current status if needed.
// For |did_schedule_change| and |keep_manual_toggles_during_schedules|, see
// Refresh() above.
// This function should never be called if the schedule type is |kNone|.
void RefreshScheduleTimer(base::Time start_time,
base::Time end_time,
bool did_schedule_change,
bool keep_manual_toggles_during_schedules);
// Schedule the upcoming next toggle of NightLight mode. This is used for the
// automatic status changes of NightLight which always use an
// AnimationDurationType::kLong.
void ScheduleNextToggle(base::TimeDelta delay);
std::unique_ptr<Delegate> delegate_;
// The pref service of the currently active user. Can be null in
// ash_unittests.
PrefService* active_user_pref_service_ = nullptr;
// The animation duration of any upcoming future change.
AnimationDuration animation_duration_ = AnimationDuration::kShort;
// The animation duration of the change that was just performed.
AnimationDuration last_animation_duration_ = AnimationDuration::kShort;
std::unique_ptr<ColorTemperatureAnimation> temperature_animation_;
// Tracks the upcoming NightLight state changes per each user due to automatic
// schedules. This can be used to restore a manually toggled status while the
// schedule is being used. See MaybeRestoreSchedule().
struct ScheduleTargetState {
// The time at which NightLight will switch to |target_status| defined
// below.
base::Time target_time;
bool target_status;
};
base::flat_map<PrefService*, ScheduleTargetState>
per_user_schedule_target_state_;
// The timer that schedules the start and end of NightLight when the schedule
// type is either kSunsetToSunrise or kCustom.
base::OneShotTimer timer_;
// True only until Night Light is initialized from the very first user
// session. After that, it is set to false.
bool is_first_user_init_ = true;
// True if the current geoposition value used by the Delegate is from a
// previously cached value in the user prefs of any of the users in the
// current session. It is reset to false once we receive a newly-updated
// geoposition from the client.
// This is used to treat the current geoposition as temporary until we receive
// a valid geoposition update, and also not to let a cached geoposition value
// to leak to another user for privacy reasons.
bool is_current_geoposition_from_cache_ = false;
// The registrar used to watch NightLight prefs changes in the above
// |active_user_pref_service_| from outside ash.
// NOTE: Prefs are how Chrome communicates changes to the NightLight settings
// controlled by this class from the WebUI settings.
std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_;
// Last ambient temperature read from the sensor. It is continuously
// updated for every new value even when GetAmbientColorEnabled() returns
// false.
float ambient_temperature_;
// The ambient color R, G, and B scaling factors.
// Valid only if ambient color is enabled.
gfx::Vector3dF ambient_rgb_scaling_factors_ = {1.f, 1.f, 1.f};
base::WeakPtrFactory<NightLightControllerImpl> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(NightLightControllerImpl);
};
} // namespace ash
#endif // ASH_SYSTEM_NIGHT_LIGHT_NIGHT_LIGHT_CONTROLLER_IMPL_H_