input_pipeline/light_sensor/
led_watcher.rs1use async_utils::hanging_get::client::HangingGetStream;
6use fidl_fuchsia_settings::{LightGroup as LightGroupFidl, LightProxy, LightValue};
7use fidl_fuchsia_ui_brightness::ControlProxy;
8use fuchsia_async as fasync;
9use fuchsia_inspect::Property;
10use futures::StreamExt;
11#[cfg(test)]
12use futures::channel::mpsc;
13use futures::channel::oneshot;
14use sorted_vec_map::SortedVecMap;
15use std::cell::RefCell;
16use std::rc::Rc;
17
18#[derive(Debug, Clone)]
19pub struct LightGroup {
20 name: String,
22 brightness: Option<f32>,
24}
25
26impl LightGroup {
27 #[cfg(test)]
28 pub(crate) fn new(name: String, brightness: Option<f32>) -> Self {
29 Self { name, brightness }
30 }
31
32 pub(crate) fn name(&self) -> &String {
33 &self.name
34 }
35
36 pub(crate) fn brightness(&self) -> Option<f32> {
37 self.brightness
38 }
39
40 #[cfg(test)]
41 pub(crate) fn set_brightness_for_test(&mut self, brightness: Option<f32>) {
42 self.brightness = brightness;
43 }
44
45 fn map_from_light_groups(
46 light_groups: Vec<LightGroupFidl>,
47 ) -> SortedVecMap<String, LightGroup> {
48 light_groups
49 .into_iter()
50 .filter_map(|light_group| {
51 let enabled = light_group.enabled;
52 let lights = light_group.lights;
53 light_group.name.and_then(|name| {
54 if enabled.unwrap_or(false) {
55 lights
56 .as_ref()
57 .and_then(|lights| lights.get(0))
58 .and_then(|light| light.value.as_ref())
59 .map(|value| match value {
60 LightValue::On(true) => Some(1.0),
61 LightValue::On(false) => Some(0.0),
62 LightValue::Brightness(b) => Some(*b as f32),
63 LightValue::Color(_) => None,
64 })
65 .map(|brightness| (name.clone(), LightGroup { name, brightness }))
66 } else {
67 Some((name.clone(), LightGroup { name, brightness: None }))
68 }
69 })
70 })
71 .collect()
72 }
73}
74
75pub struct LedWatcher {
76 backlight_brightness: Rc<RefCell<f32>>,
77 light_groups: Rc<RefCell<SortedVecMap<String, LightGroup>>>,
78 #[cfg(test)]
79 update: Option<RefCell<mpsc::Sender<Update>>>,
80}
81
82#[cfg(test)]
83enum Update {
84 Brightness,
85 LightGroups,
86}
87
88impl LedWatcher {
89 pub(crate) fn new(light_groups: Vec<LightGroupFidl>) -> Self {
93 Self {
94 backlight_brightness: Rc::new(RefCell::new(0.0)),
95 light_groups: Rc::new(RefCell::new(LightGroup::map_from_light_groups(light_groups))),
96 #[cfg(test)]
97 update: None,
98 }
99 }
100
101 #[cfg(test)]
102 fn new_with_sender(light_groups: Vec<LightGroupFidl>, update: mpsc::Sender<Update>) -> Self {
103 Self {
104 backlight_brightness: Rc::new(RefCell::new(0.0)),
105 light_groups: Rc::new(RefCell::new(LightGroup::map_from_light_groups(light_groups))),
106 update: Some(RefCell::new(update)),
107 }
108 }
109
110 pub(crate) fn handle_light_groups_and_brightness_watch(
114 self,
115 light_proxy: LightProxy,
116 brightness_proxy: ControlProxy,
117 mut cancelation_rx: oneshot::Receiver<()>,
118 light_proxy_receives_initial_response: fuchsia_inspect::BoolProperty,
119 brightness_proxy_receives_initial_response: fuchsia_inspect::BoolProperty,
120 ) -> (LedWatcherHandle, fasync::Task<()>) {
121 let light_groups = Rc::clone(&self.light_groups);
122 let backlight_brightness = Rc::clone(&self.backlight_brightness);
123 let task = fasync::Task::local(async move {
124 let mut light_groups_stream =
125 HangingGetStream::new(light_proxy, LightProxy::watch_light_groups);
126 let mut brightness_stream =
127 HangingGetStream::new(brightness_proxy, ControlProxy::watch_current_brightness);
128 let mut light_group_fut = light_groups_stream.select_next_some();
129 let mut brightness_fut = brightness_stream.select_next_some();
130 loop {
131 futures::select! {
132 watch_result = light_group_fut => {
133 light_proxy_receives_initial_response.set(true);
134 match watch_result {
135 Ok(light_groups) => self.update_light_groups(light_groups),
136 Err(e) => log::warn!("Failed to get light group update: {:?}", e),
137 }
138 light_group_fut = light_groups_stream.select_next_some()
139 }
140 watch_result = brightness_fut => {
141 brightness_proxy_receives_initial_response.set(true);
142 match watch_result {
143 Ok(brightness) => self.update_brightness(brightness),
144 Err(e) => log::warn!("Failed to get brightness update: {:?}", e),
145 }
146 brightness_fut = brightness_stream.select_next_some();
147 }
148 _ = cancelation_rx => break,
149 complete => break,
150 }
151 }
152 });
153
154 (LedWatcherHandle { light_groups, backlight_brightness }, task)
155 }
156
157 fn update_light_groups(&self, light_groups: Vec<LightGroupFidl>) {
158 *self.light_groups.borrow_mut() = LightGroup::map_from_light_groups(light_groups);
159 #[cfg(test)]
160 if let Some(sender) = &self.update {
161 sender.borrow_mut().try_send(Update::LightGroups).expect("Failed to send update");
162 }
163 }
164
165 fn update_brightness(&self, brightness: f32) {
166 *self.backlight_brightness.borrow_mut() = brightness;
167 #[cfg(test)]
168 if let Some(sender) = &self.update {
169 sender.borrow_mut().try_send(Update::Brightness).expect("Failed to send update");
170 }
171 }
172}
173
174#[derive(Clone, Debug)]
175pub struct LedWatcherHandle {
176 light_groups: Rc<RefCell<SortedVecMap<String, LightGroup>>>,
177 backlight_brightness: Rc<RefCell<f32>>,
178}
179
180impl LedState for LedWatcherHandle {
181 fn light_groups(&self) -> SortedVecMap<String, LightGroup> {
182 Clone::clone(&*self.light_groups.borrow())
183 }
184
185 fn backlight_brightness(&self) -> f32 {
186 *self.backlight_brightness.borrow()
187 }
188}
189
190pub trait LedState {
191 fn light_groups(&self) -> SortedVecMap<String, LightGroup>;
192 fn backlight_brightness(&self) -> f32;
193}
194
195pub struct CancelableTask {
196 inner: fasync::Task<()>,
197 cancelation_tx: oneshot::Sender<()>,
198}
199
200impl CancelableTask {
201 pub(crate) fn new(cancelation_tx: oneshot::Sender<()>, task: fasync::Task<()>) -> Self {
202 Self { cancelation_tx, inner: task }
203 }
204
205 pub fn detach(self) {
208 self.inner.detach();
209 }
210
211 pub async fn cancel(self) {
213 let _ = self.cancelation_tx.send(());
216 self.inner.await
217 }
218}
219
220#[cfg(test)]
221mod led_watcher_tests;