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        current.autorepeat = keyboard_info.autorepeat.or(current.autorepeat).and_then(|value| {
128            if value.delay == 0 && value.period == 0 {
129                // Clean up Autorepeat when delay and period are set to zero.
130                None
131            } else {
132                Some(value)
133            }
134        });
135
136        self.store
137            .write(&current)
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        // Create and fetch a store from device storage so we can read stored value for testing.
168        let keyboard_controller =
169            KeyboardController::new(Rc::clone(&factory), setting_value_publisher).await;
170        let store = factory.get_store().await;
171
172        // Set a new value.
173        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        // Verify the value we set is persisted in DeviceStorage.
181        let retrieved_struct = store.get::<KeyboardInfo>().await;
182        assert_eq!(changed_value, retrieved_struct);
183    }
184}