settings/accessibility/
accessibility_controller.rs

1// Copyright 2019 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 super::accessibility_fidl_handler::Publisher;
6use crate::accessibility::types::AccessibilityInfo;
7use anyhow::{Context, 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::utils::Merge;
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 AccessibilityInfo {
20    type Loader = NoneT;
21    const KEY: &'static str = "accessibility_info";
22}
23
24#[derive(thiserror::Error, Debug)]
25pub enum AccessibilityError {
26    #[error("Invalid argument: arg: {0:?}, value: {1:?}")]
27    InvalidArgument(&'static str, String),
28    #[error("Write failed for Display: {0:?}")]
29    WriteFailure(Error),
30}
31
32impl From<&AccessibilityError> for ResponseType {
33    fn from(error: &AccessibilityError) -> Self {
34        match error {
35            AccessibilityError::InvalidArgument(..) => ResponseType::InvalidArgument,
36            AccessibilityError::WriteFailure(..) => ResponseType::StorageFailure,
37        }
38    }
39}
40
41pub(crate) enum Request {
42    Set(AccessibilityInfo, Sender<Result<(), AccessibilityError>>),
43}
44
45impl StorageAccess for AccessibilityController {
46    type Storage = DeviceStorage;
47    type Data = AccessibilityInfo;
48    const STORAGE_KEY: &'static str = AccessibilityInfo::KEY;
49}
50
51pub(crate) struct AccessibilityController {
52    store: Rc<DeviceStorage>,
53    publisher: Option<Publisher>,
54    setting_value_publisher: SettingValuePublisher<AccessibilityInfo>,
55}
56
57impl AccessibilityController {
58    pub(super) async fn new<F>(
59        storage_factory: Rc<F>,
60        setting_value_publisher: SettingValuePublisher<AccessibilityInfo>,
61    ) -> Self
62    where
63        F: StorageFactory<Storage = DeviceStorage>,
64    {
65        AccessibilityController {
66            store: storage_factory.get_store().await,
67            publisher: None,
68            setting_value_publisher,
69        }
70    }
71
72    pub(super) fn register_publisher(&mut self, publisher: Publisher) {
73        self.publisher = Some(publisher);
74    }
75
76    fn publish(&self, info: AccessibilityInfo) {
77        let _ = self.setting_value_publisher.publish(&info);
78        if let Some(publisher) = self.publisher.as_ref() {
79            publisher.set(info);
80        }
81    }
82
83    pub(super) async fn handle(
84        self,
85        mut request_rx: UnboundedReceiver<Request>,
86    ) -> fasync::Task<()> {
87        fasync::Task::local(async move {
88            while let Some(request) = request_rx.next().await {
89                let Request::Set(info, tx) = request;
90                let res = self.set(info).await.map(|info| {
91                    if let Some(info) = info {
92                        self.publish(info);
93                    }
94                });
95                let _ = tx.send(res);
96            }
97        })
98    }
99
100    async fn set(
101        &self,
102        info: AccessibilityInfo,
103    ) -> Result<Option<AccessibilityInfo>, AccessibilityError> {
104        let original_info = self.store.get::<AccessibilityInfo>().await;
105        assert!(original_info.is_finite());
106        // Validate accessibility info contains valid float numbers.
107        if !info.is_finite() {
108            return Err(AccessibilityError::InvalidArgument("accessibility", format!("{info:?}")));
109        }
110
111        let info = original_info.merge(info);
112        self.store
113            .write(&info)
114            .await
115            .map(|state| (UpdateState::Updated == state).then_some(info))
116            .context("updating accessibility info")
117            .map_err(AccessibilityError::WriteFailure)
118    }
119
120    pub(super) async fn restore(&self) -> AccessibilityInfo {
121        self.store.get::<AccessibilityInfo>().await
122    }
123}