gui_server_api/
navigation.rs

1// SPDX-FileCopyrightText: 2025 Foundation Devices, Inc. <hello@foundation.xyz>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4use hex_literal::hex;
5use server::{CheckedPermissions, MessageAllowed};
6use xous::AppId;
7
8use crate::error::NavigationError;
9use crate::msg::{
10    FinishResponse, GetPendingNavRequest, NavigateTo, NavigationCancel, NavigationResult, RunApp,
11    RunAppResponse, ShowModal,
12};
13use crate::navigation::securitykeys::{SecurityKeysNavRequest, UserPresenceOptions, UserPresenceResult};
14use crate::{GuiApi, GuiApiLight, GuiServerError, ModalStyle};
15
16pub mod alerts;
17pub mod filepicker;
18pub mod lockscreen;
19pub mod qrscanner;
20pub mod securitykeys;
21
22pub const QR_SCANNER_APP_ID: AppId = AppId(hex!("6775692d6170702d6578616d706c652e"));
23pub const FILE_BROWSER_APP_ID: AppId = AppId(hex!("46696c652042726f7773657200000000"));
24pub const SECURITY_KEYS_APP_ID: AppId = AppId(hex!("5365637572697479204b657973000000"));
25pub const LOCK_SCREEN_APP_ID: AppId = AppId(hex!("0a000000000000000000000000000000"));
26pub const ONBOARDING_APP_ID: AppId = AppId(hex!("dac5321775d449c11bc9c90f38067f8f"));
27pub const ALERTS_APP_ID: AppId = AppId(hex!("32defc0867555fe8002759667000b22a"));
28pub const BITCOIN_APP_ID: AppId = AppId(hex!("426974636f696e2057616c6c65740000"));
29pub const SETTINGS_APP_ID: AppId = AppId(hex!("c192b79230473875f159d4423d74d00f"));
30
31impl<P: CheckedPermissions> GuiApiLight<P> {
32    /// Shows a modal of the app, giving it a navigation object
33    pub fn show_modal(
34        &self,
35        app_id: AppId,
36        modal_style: ModalStyle,
37        args: &[u8],
38    ) -> Result<NavigationResult, GuiServerError>
39    where
40        P: MessageAllowed<ShowModal>,
41    {
42        let nav_req = ShowModal { modal_style, app_id, args: args.to_vec() };
43        let response = self.conn.try_send_blocking_archive(nav_req)?;
44        Ok(response.or_else(|_| Err(NavigationError::RequestBufferTooSmall)))
45    }
46
47    // Switch to the app, giving it a navigation object
48    pub fn navigate_to(&self, app_id: AppId, args: &[u8]) -> Result<NavigationResult, GuiServerError>
49    where
50        P: MessageAllowed<NavigateTo>,
51    {
52        let nav_req = NavigateTo { app_id, args: args.to_vec() };
53        let response = self.conn.try_send_blocking_archive(nav_req)?;
54        Ok(response.or_else(|_| Err(NavigationError::RequestBufferTooSmall)))
55    }
56
57    pub fn run_app(&self, app_id: AppId) -> Result<RunAppResponse, GuiServerError>
58    where
59        P: MessageAllowed<RunApp>,
60    {
61        Ok(self.conn.try_send_blocking_archive(RunApp { app_id })?)
62    }
63
64    pub fn check_user_presence(
65        &self,
66        options: UserPresenceOptions,
67    ) -> Result<Option<UserPresenceResult>, GuiServerError>
68    where
69        P: MessageAllowed<NavigateTo>,
70    {
71        let request = SecurityKeysNavRequest::UserPresence(options);
72        let res = self.navigate_to(SECURITY_KEYS_APP_ID, &request.serialize())?;
73
74        match res {
75            Ok(response) => Ok(UserPresenceResult::from_slice(response.as_slice())),
76            Err(NavigationError::CanceledBySystem) | Err(NavigationError::CanceledByUser) => {
77                Ok(Some(UserPresenceResult::new_cancelled()))
78            }
79            Err(e) => Err(GuiServerError::Navigation(e)),
80        }
81    }
82
83    pub fn invoke_alert(&self, alert: alerts::InvokeAlert) -> Result<alerts::AlertResult, GuiServerError>
84    where
85        P: MessageAllowed<ShowModal>,
86    {
87        let res = self.show_modal(ALERTS_APP_ID, ModalStyle::SlideUpFullscreen, &alert.serialize())?;
88
89        match res {
90            Ok(response) => Ok(alerts::AlertResult::from_slice(response.as_slice())
91                .ok_or(GuiServerError::Navigation(NavigationError::InternalError))?),
92            Err(NavigationError::CanceledBySystem) | Err(NavigationError::CanceledByUser) => {
93                Ok(alerts::AlertResult::Canceled)
94            }
95            Err(e) => Err(GuiServerError::Navigation(e)),
96        }
97    }
98}
99
100impl<P: CheckedPermissions> GuiApi<P> {
101    pub fn navigate_finish(&self, response: Vec<u8>) -> Result<(), GuiServerError>
102    where
103        P: MessageAllowed<FinishResponse>,
104    {
105        let response = FinishResponse { response };
106        Ok(self.conn.try_send_blocking_archive(response)?)
107    }
108
109    pub fn navigate_cancel(&self) -> Result<(), GuiServerError>
110    where
111        P: MessageAllowed<NavigationCancel>,
112    {
113        self.conn.try_send_scalar(NavigationCancel)?;
114        Ok(())
115    }
116
117    pub fn navigate_pending(&self) -> Result<Option<Vec<u8>>, GuiServerError>
118    where
119        P: MessageAllowed<GetPendingNavRequest>,
120    {
121        Ok(self.conn.try_send_blocking_archive(GetPendingNavRequest)?)
122    }
123}