Skip to main content

settings_keyboard/
keyboard_controller.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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        // The US_QWERTY keymap is the default if no settings are ever applied.
27        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
128        // Clean up Autorepeat when delay and period are set to zero.
129        current.autorepeat = keyboard_info
130            .autorepeat
131            .or(current.autorepeat)
132            .filter(|&value| !(value.delay == 0 && value.period == 0));
133
134        self.store
135            .write(&current)
136            .await
137            .map(|state| (UpdateState::Updated == state).then_some(current))
138            .map_err(KeyboardError::WriteFailure)
139    }
140
141    pub(super) async fn restore(&self) -> KeyboardInfo {
142        self.store.get::<KeyboardInfo>().await
143    }
144}
145
146#[cfg(test)]
147mod tests {
148    use super::*;
149    use crate::types::Autorepeat;
150    use futures::channel::mpsc;
151    use settings_test_common::storage::InMemoryStorageFactory;
152
153    #[fuchsia::test(allow_stalls = false)]
154    async fn test_keyboard_storage() {
155        let changed_value = KeyboardInfo {
156            keymap: Some(KeymapId::UsDvorak),
157            autorepeat: Some(Autorepeat { delay: 2, period: 1 }),
158        };
159        let factory = Rc::new(InMemoryStorageFactory::new());
160        let (tx, _) = mpsc::unbounded();
161        let setting_value_publisher = SettingValuePublisher::new(tx);
162
163        factory.initialize::<KeyboardController>().await.expect("can initialize keyboard storage");
164
165        // Create and fetch a store from device storage so we can read stored value for testing.
166        let keyboard_controller =
167            KeyboardController::new(Rc::clone(&factory), setting_value_publisher).await;
168        let store = factory.get_store().await;
169
170        // Set a new value.
171        let result = keyboard_controller
172            .set(changed_value)
173            .await
174            .expect("set successful")
175            .expect("keyboard info changed");
176        assert_eq!(result, changed_value);
177
178        // Verify the value we set is persisted in DeviceStorage.
179        let retrieved_struct = store.get::<KeyboardInfo>().await;
180        assert_eq!(changed_value, retrieved_struct);
181    }
182}