settings_keyboard/
keyboard_controller.rs1use crate::keyboard_fidl_handler::Publisher;
6use crate::types::{KeyboardInfo, KeymapId};
7use anyhow::Error;
8use fuchsia_async as fasync;
9use futures::StreamExt;
10use futures::channel::mpsc::UnboundedReceiver;
11use futures::channel::oneshot::Sender;
12use settings_common::inspect::event::{ResponseType, SettingValuePublisher};
13use settings_common::trace;
14use settings_storage::UpdateState;
15use settings_storage::device_storage::{DeviceStorage, DeviceStorageCompatible};
16use settings_storage::storage_factory::{NoneT, StorageAccess, StorageFactory};
17use std::rc::Rc;
18
19impl DeviceStorageCompatible for KeyboardInfo {
20 type Loader = NoneT;
21 const KEY: &'static str = "keyboard_info";
22}
23
24impl Default for KeyboardInfo {
25 fn default() -> Self {
26 KeyboardInfo { keymap: Some(KeymapId::UsQwerty), autorepeat: None }
28 }
29}
30
31impl StorageAccess for KeyboardController {
32 type Storage = DeviceStorage;
33 type Data = KeyboardInfo;
34 const STORAGE_KEY: &'static str = KeyboardInfo::KEY;
35}
36
37#[derive(thiserror::Error, Debug)]
38pub(crate) enum KeyboardError {
39 #[error("Invalid argument for keyboard: argument:{0:?} value:{1:?}")]
40 InvalidArgument(&'static str, String),
41 #[error("Write failed for Keyboard: {0:?}")]
42 WriteFailure(Error),
43}
44
45impl From<&KeyboardError> for ResponseType {
46 fn from(error: &KeyboardError) -> Self {
47 match error {
48 KeyboardError::InvalidArgument(..) => ResponseType::InvalidArgument,
49 KeyboardError::WriteFailure(..) => ResponseType::StorageFailure,
50 }
51 }
52}
53
54pub(crate) enum Request {
55 Set(KeyboardInfo, Sender<Result<(), KeyboardError>>),
56}
57
58#[cfg(test)]
59impl std::fmt::Debug for Request {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 let Self::Set(info, _) = self;
62 f.debug_tuple("Set").field(info).finish_non_exhaustive()
63 }
64}
65
66pub struct KeyboardController {
67 store: Rc<DeviceStorage>,
68 publisher: Option<Publisher>,
69 setting_value_publisher: SettingValuePublisher<KeyboardInfo>,
70}
71
72impl KeyboardController {
73 pub(super) async fn new<F>(
74 storage_factory: Rc<F>,
75 setting_value_publisher: SettingValuePublisher<KeyboardInfo>,
76 ) -> Self
77 where
78 F: StorageFactory<Storage = DeviceStorage>,
79 {
80 KeyboardController {
81 store: storage_factory.get_store().await,
82 publisher: None,
83 setting_value_publisher,
84 }
85 }
86
87 pub(super) fn register_publisher(&mut self, publisher: Publisher) {
88 self.publisher = Some(publisher);
89 }
90
91 fn publish(&self, info: KeyboardInfo) {
92 let _ = self.setting_value_publisher.publish(&info);
93 if let Some(publisher) = self.publisher.as_ref() {
94 publisher.set(info);
95 }
96 }
97
98 pub(super) async fn handle(
99 self,
100 mut request_rx: UnboundedReceiver<Request>,
101 ) -> fasync::Task<()> {
102 fasync::Task::local(async move {
103 while let Some(request) = request_rx.next().await {
104 let Request::Set(info, tx) = request;
105 let res = self.set(info).await.map(|info| {
106 if let Some(info) = info {
107 self.publish(info);
108 }
109 });
110 let _ = tx.send(res);
111 }
112 })
113 }
114
115 async fn set(
116 &self,
117 keyboard_info: KeyboardInfo,
118 ) -> Result<Option<KeyboardInfo>, KeyboardError> {
119 let id = fuchsia_trace::Id::new();
120 trace!(id, "set keyboard");
121 let mut current = self.store.get::<KeyboardInfo>().await;
122 if !keyboard_info.is_valid() {
123 return Err(KeyboardError::InvalidArgument("keyboard", format!("{keyboard_info:?}")));
124 }
125
126 current.keymap = keyboard_info.keymap.or(current.keymap);
127 current.autorepeat = keyboard_info.autorepeat.or(current.autorepeat).and_then(|value| {
128 if value.delay == 0 && value.period == 0 {
129 None
131 } else {
132 Some(value)
133 }
134 });
135
136 self.store
137 .write(¤t)
138 .await
139 .map(|state| (UpdateState::Updated == state).then_some(current))
140 .map_err(KeyboardError::WriteFailure)
141 }
142
143 pub(super) async fn restore(&self) -> KeyboardInfo {
144 self.store.get::<KeyboardInfo>().await
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151 use crate::types::Autorepeat;
152 use futures::channel::mpsc;
153 use settings_test_common::storage::InMemoryStorageFactory;
154
155 #[fuchsia::test(allow_stalls = false)]
156 async fn test_keyboard_storage() {
157 let changed_value = KeyboardInfo {
158 keymap: Some(KeymapId::UsDvorak),
159 autorepeat: Some(Autorepeat { delay: 2, period: 1 }),
160 };
161 let factory = Rc::new(InMemoryStorageFactory::new());
162 let (tx, _) = mpsc::unbounded();
163 let setting_value_publisher = SettingValuePublisher::new(tx);
164
165 factory.initialize::<KeyboardController>().await.expect("can initialize keyboard storage");
166
167 let keyboard_controller =
169 KeyboardController::new(Rc::clone(&factory), setting_value_publisher).await;
170 let store = factory.get_store().await;
171
172 let result = keyboard_controller
174 .set(changed_value)
175 .await
176 .expect("set successful")
177 .expect("keyboard info changed");
178 assert_eq!(result, changed_value);
179
180 let retrieved_struct = store.get::<KeyboardInfo>().await;
182 assert_eq!(changed_value, retrieved_struct);
183 }
184}