Skip to main content
KeyOS API Reference

settings/
types.rs

1// SPDX-FileCopyrightText: 2024 Foundation Devices, Inc. <hello@foundation.xyz>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4//!
5//! Global settings data types
6//!
7//! Each global setting has a Get/Set/Subscribe method on [`crate::SettingsApi`].
8
9use std::time::Duration;
10
11use num_derive::{FromPrimitive, ToPrimitive};
12use num_traits::{FromPrimitive, ToPrimitive};
13use server::{AsScalar, FromScalar};
14
15use crate::macros::*;
16
17#[derive(
18    Debug, Copy, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, FromPrimitive, ToPrimitive,
19)]
20pub enum BoardRevision {
21    /// Board revision D1.
22    D1,
23
24    /// Board revision D6.
25    D6,
26}
27
28impl FromScalar<1> for BoardRevision {
29    fn from_scalar(value: [u32; 1]) -> Self { Self::from_u32(value[0]).unwrap() }
30}
31
32impl AsScalar<1> for BoardRevision {
33    fn as_scalar(&self) -> [u32; 1] { [self.to_u32().unwrap()] }
34}
35
36create_modules! {
37    SystemTheme,
38    ScreenBrightness,
39    OnboardingStatus,
40    Locale,
41    DeviceName,
42    ShowSecurityWords,
43    AutoLock,
44    EnvoyTimeSync,
45    UseStandardTimeFormat,
46    TimeZone,
47    DebugTouch,
48    AirlockMode,
49    TouchOffset,
50    MagicBackupEnabled,
51    NfcEnabled,
52    BluetoothEnabled,
53    CameraEnabled,
54    UsbEnabled,
55    DeveloperMode,
56}
57
58global_scalar! {
59    /// System-wide theme.
60    ///
61    /// This affects the appearance of all system apps on KeyOS
62    system,
63    #[derive(FromPrimitive, ToPrimitive)]
64    pub enum SystemTheme {
65        Dark,
66        Light,
67    }
68}
69
70impl FromScalar<1> for SystemTheme {
71    fn from_scalar(value: [u32; 1]) -> Self { Self::from_u32(value[0]).unwrap() }
72}
73
74impl AsScalar<1> for SystemTheme {
75    fn as_scalar(&self) -> [u32; 1] { [self.to_u32().unwrap()] }
76}
77
78global_scalar! {
79    /// Screen brightness level (0-100)
80    system,
81    pub struct ScreenBrightness(pub u8);
82}
83
84impl FromScalar<1> for ScreenBrightness {
85    fn from_scalar(value: [u32; 1]) -> Self { Self(value[0] as u8) }
86}
87
88impl AsScalar<1> for ScreenBrightness {
89    fn as_scalar(&self) -> [u32; 1] { [self.0 as u32] }
90}
91
92global_archive! {
93    /// Status of the user's onboarding.
94    encrypted,
95    #[derive(Default)]
96    pub enum OnboardingStatus {
97        #[default]
98        NotComplete,
99        Complete,
100    }
101}
102
103impl OnboardingStatus {
104    pub fn is_complete(&self) -> bool { matches!(self, Self::Complete) }
105}
106
107impl ArchivedOnboardingStatus {
108    pub fn is_complete(&self) -> bool { matches!(self, Self::Complete) }
109}
110
111global_scalar! {
112    /// The user's preferred language.
113    system,
114    #[derive(Default, FromPrimitive, ToPrimitive)]
115    pub enum Locale {
116        #[default]
117        EnglishUS,
118    }
119}
120
121impl FromScalar<1> for Locale {
122    fn from_scalar(value: [u32; 1]) -> Self { Self::from_u32(value[0]).unwrap() }
123}
124
125impl AsScalar<1> for Locale {
126    fn as_scalar(&self) -> [u32; 1] { [self.to_u32().unwrap()] }
127}
128
129impl Locale {
130    pub fn lang(&self) -> &str {
131        match self {
132            Self::EnglishUS => "en",
133        }
134    }
135}
136
137global_archive! {
138    /// The user's device name.
139    system,
140    pub struct DeviceName(pub String);
141}
142
143impl From<&str> for DeviceName {
144    fn from(value: &str) -> Self { Self(String::from(value)) }
145}
146
147impl DeviceName {
148    pub const DEFAULT: &str = "Passport Prime";
149}
150
151global_scalar! {
152    /// Whether to show the security words on the login screen.
153    system,
154    pub struct ShowSecurityWords(pub bool);
155}
156
157impl FromScalar<1> for ShowSecurityWords {
158    fn from_scalar(value: [u32; 1]) -> Self { Self(bool::from_scalar(value)) }
159}
160
161impl AsScalar<1> for ShowSecurityWords {
162    fn as_scalar(&self) -> [u32; 1] { self.0.as_scalar() }
163}
164
165global_scalar! {
166    /// Time to lock the device after inactivity.
167    system,
168    pub struct AutoLock(pub Duration);
169}
170
171impl FromScalar<2> for AutoLock {
172    fn from_scalar(value: [u32; 2]) -> Self {
173        Self(Duration::from_secs(value[0] as u64) + Duration::from_millis(value[1] as u64))
174    }
175}
176
177impl AsScalar<2> for AutoLock {
178    fn as_scalar(&self) -> [u32; 2] { [self.0.as_secs() as u32, self.0.subsec_millis()] }
179}
180
181global_scalar! {
182    /// Whether to set time from envoy via QuantumLink
183    system,
184    pub struct EnvoyTimeSync(pub bool);
185}
186
187impl FromScalar<1> for EnvoyTimeSync {
188    fn from_scalar(value: [u32; 1]) -> Self { Self(bool::from_scalar(value)) }
189}
190
191impl AsScalar<1> for EnvoyTimeSync {
192    fn as_scalar(&self) -> [u32; 1] { self.0.as_scalar() }
193}
194
195global_scalar! {
196    /// Whether to use the standard time format (24-hour).
197    system,
198    pub struct UseStandardTimeFormat(pub bool);
199}
200
201impl FromScalar<1> for UseStandardTimeFormat {
202    fn from_scalar(value: [u32; 1]) -> Self { Self(bool::from_scalar(value)) }
203}
204
205impl AsScalar<1> for UseStandardTimeFormat {
206    fn as_scalar(&self) -> [u32; 1] { self.0.as_scalar() }
207}
208
209global_archive! {
210    /// The user's preferred timezone.
211    system,
212    pub struct TimeZone {
213         pub name: String,
214         pub data: Vec<u8>
215    }
216}
217
218impl Default for TimeZone {
219    fn default() -> Self {
220        Self {
221            name: String::from("America/New_York"),
222            data: include_bytes!("../assets/america_new_york.tzif").to_vec(),
223        }
224    }
225}
226
227impl TimeZone {
228    pub fn now(&self) -> jiff::Zoned { jiff::Timestamp::now().to_zoned(self.timezone()) }
229
230    pub fn timezone(&self) -> jiff::tz::TimeZone {
231        jiff::tz::TimeZone::tzif(&self.name, &self.data).unwrap_or_else(|_| TimeZone::default().timezone())
232    }
233
234    pub fn name(&self) -> &str { &self.name }
235
236    pub fn abbreviation(&self, timestamp: jiff::Timestamp) -> String {
237        let tz = self.timezone();
238        let info = tz.to_offset_info(timestamp);
239        info.abbreviation().into()
240    }
241}
242
243global_scalar! {
244    system,
245    pub struct DebugTouch(pub bool);
246}
247
248impl FromScalar<1> for DebugTouch {
249    fn from_scalar(value: [u32; 1]) -> Self { Self(bool::from_scalar(value)) }
250}
251
252impl AsScalar<1> for DebugTouch {
253    fn as_scalar(&self) -> [u32; 1] { self.0.as_scalar() }
254}
255
256global_scalar! {
257    system,
258    #[derive(FromPrimitive, ToPrimitive)]
259    pub enum AirlockMode {
260        Disabled,
261        ReadOnly,
262        ReadWrite,
263    }
264}
265
266impl FromScalar<1> for AirlockMode {
267    fn from_scalar(value: [u32; 1]) -> Self { Self::from_u32(value[0]).unwrap() }
268}
269
270impl AsScalar<1> for AirlockMode {
271    fn as_scalar(&self) -> [u32; 1] { [self.to_u32().unwrap()] }
272}
273
274global_scalar! {
275    system,
276    pub struct TouchOffset(pub i32);
277}
278
279impl FromScalar<1> for TouchOffset {
280    fn from_scalar(value: [u32; 1]) -> Self { Self(i32::from_scalar(value)) }
281}
282
283impl AsScalar<1> for TouchOffset {
284    fn as_scalar(&self) -> [u32; 1] { self.0.as_scalar() }
285}
286
287global_scalar! {
288    system,
289    pub struct MagicBackupEnabled(pub bool);
290}
291
292global_scalar! {
293    system,
294    pub struct NfcEnabled(pub bool);
295}
296
297global_scalar! {
298    system,
299    pub struct BluetoothEnabled(pub bool);
300}
301
302global_scalar! {
303    system,
304    pub struct CameraEnabled(pub bool);
305}
306
307global_scalar! {
308    system,
309    pub struct UsbEnabled(pub bool);
310}
311
312global_scalar! {
313    encrypted,
314    pub struct DeveloperMode(pub bool);
315}
316
317impl FromScalar<1> for MagicBackupEnabled {
318    fn from_scalar(value: [u32; 1]) -> Self { Self(bool::from_scalar(value)) }
319}
320
321impl AsScalar<1> for MagicBackupEnabled {
322    fn as_scalar(&self) -> [u32; 1] { self.0.as_scalar() }
323}
324
325impl FromScalar<1> for NfcEnabled {
326    fn from_scalar(value: [u32; 1]) -> Self { Self(bool::from_scalar(value)) }
327}
328
329impl AsScalar<1> for NfcEnabled {
330    fn as_scalar(&self) -> [u32; 1] { self.0.as_scalar() }
331}
332
333impl FromScalar<1> for BluetoothEnabled {
334    fn from_scalar(value: [u32; 1]) -> Self { Self(bool::from_scalar(value)) }
335}
336
337impl AsScalar<1> for BluetoothEnabled {
338    fn as_scalar(&self) -> [u32; 1] { self.0.as_scalar() }
339}
340
341impl FromScalar<1> for CameraEnabled {
342    fn from_scalar(value: [u32; 1]) -> Self { Self(bool::from_scalar(value)) }
343}
344
345impl AsScalar<1> for CameraEnabled {
346    fn as_scalar(&self) -> [u32; 1] { self.0.as_scalar() }
347}
348
349impl FromScalar<1> for UsbEnabled {
350    fn from_scalar(value: [u32; 1]) -> Self { Self(bool::from_scalar(value)) }
351}
352
353impl AsScalar<1> for UsbEnabled {
354    fn as_scalar(&self) -> [u32; 1] { self.0.as_scalar() }
355}
356
357impl FromScalar<1> for DeveloperMode {
358    fn from_scalar(value: [u32; 1]) -> Self { Self(bool::from_scalar(value)) }
359}
360
361impl AsScalar<1> for DeveloperMode {
362    fn as_scalar(&self) -> [u32; 1] { self.0.as_scalar() }
363}
364
365#[cfg(test)]
366mod tests {
367    use super::*;
368
369    #[test]
370    fn default_timezone() {
371        let tz = TimeZone::default();
372        assert_eq!(tz.name(), "America/New_York");
373        let timezone = tz.timezone();
374        assert_eq!(timezone.iana_name(), Some("America/New_York"));
375    }
376}