1use std::io::Read;
2use std::path::{Path, PathBuf};
3
4use crate::AssetFileEntry;
5use crate::error::PlatformError;
6
7use super::PlatformFuture;
8use super::device::{Device, DeviceHardware};
9use super::file::FileService;
10use super::location::Location;
11use super::media_interaction::{MediaInteraction, MediaKind};
12use super::media_runtime::MediaRuntime;
13use super::network::Network;
14use super::secure_store::SecureStore;
15use super::share::ShareService;
16use super::ui::{SurfacePresenter, UIUpdate, UserFeedback};
17use super::update::UpdateService;
18use super::wifi::Wifi;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum AnimationType {
22 None = 0,
23 Forward = 1,
24 Backward = 2,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
28pub enum LxAppOpenMode {
29 #[default]
30 Normal = 0,
31 Panel = 1,
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum OpenUrlTarget {
36 External = 0,
37 SelfTarget = 1,
38 NewBrowserTab = 2,
40 AsideBrowser = 3,
43}
44
45impl OpenUrlTarget {
46 pub fn parse(raw: Option<&str>) -> Self {
47 match raw.map(|v| v.trim().to_ascii_lowercase()) {
48 Some(v) if v == "self" => Self::SelfTarget,
49 Some(v) if v == "new_browser_tab" => Self::NewBrowserTab,
50 Some(v) if v == "aside" => Self::AsideBrowser,
51 Some(v) if v == "external" => Self::External,
52 Some(v) => {
53 log::warn!("Invalid openURL target='{}', fallback to external", v);
54 Self::External
55 }
56 None => Self::External,
57 }
58 }
59}
60
61#[derive(Debug, Clone)]
62pub struct OpenUrlRequest {
63 pub owner_appid: String,
64 pub owner_session_id: u64,
65 pub url: String,
66 pub target: OpenUrlTarget,
67 pub want_tab_id: bool,
71}
72
73#[derive(Debug, Clone, Default, PartialEq, Eq)]
76pub struct OpenUrlResult {
77 pub tab_id: Option<String>,
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq)]
81pub enum BuiltinBrowserPage {
82 Settings = 0,
83 Downloads = 1,
84}
85
86impl From<i32> for AnimationType {
87 fn from(value: i32) -> Self {
88 match value {
89 1 => AnimationType::Forward,
90 2 => AnimationType::Backward,
91 _ => AnimationType::None,
92 }
93 }
94}
95
96pub trait AppRuntime:
97 Send
98 + Sync
99 + MediaInteraction
100 + MediaRuntime
101 + Network
102 + SurfacePresenter
103 + Device
104 + DeviceHardware
105 + SecureStore
106 + ShareService
107 + FileService
108 + Location
109 + UIUpdate
110 + UpdateService
111 + UserFeedback
112 + Wifi
113 + 'static
114{
115 fn read_asset<'a>(&'a self, path: &str) -> Result<Box<dyn Read + 'a>, PlatformError>;
117
118 fn asset_dir_iter<'a>(
120 &'a self,
121 asset_dir: &str,
122 ) -> Box<dyn Iterator<Item = Result<AssetFileEntry<'a>, PlatformError>> + 'a>;
123
124 fn app_data_dir(&self) -> PathBuf;
126
127 fn app_cache_dir(&self) -> PathBuf;
129
130 fn get_app_identifier(&self) -> Result<String, PlatformError>;
132
133 fn copy_album_media_to_file(
135 &self,
136 uri: &str,
137 dest_path: &Path,
138 kind: MediaKind,
139 ) -> Result<(), PlatformError> {
140 MediaRuntime::copy_album_media_to_file(self, uri, dest_path, kind)
141 }
142
143 fn get_system_locale(&self) -> &str;
145
146 fn show_lxapp(
152 &self,
153 appid: String,
154 title: String,
155 path: String,
156 webtag: String,
157 session_id: u64,
158 open_mode: LxAppOpenMode,
159 panel_id: String,
160 ) -> Result<(), PlatformError>;
161
162 fn request_lxapp_main_activation(&self, _appid: &str) {}
167
168 fn hide_lxapp(&self, appid: String, session_id: u64) -> Result<(), PlatformError>;
170
171 fn exit(&self) -> Result<(), PlatformError>;
173
174 fn set_tray_badge(&self, _text: &str) -> Result<(), PlatformError> {
181 Ok(())
182 }
183
184 fn set_tray_icon(&self, _icon: &str) -> Result<(), PlatformError> {
186 Ok(())
187 }
188
189 fn set_shell_sidebar_actions(
192 &self,
193 _items: &[lingxia_shell::ResolvedShellSidebarAction],
194 ) -> Result<(), PlatformError> {
195 Ok(())
196 }
197
198 fn set_shell_pins(&self, _items: &[lingxia_shell::ShellPin]) -> Result<(), PlatformError> {
201 Ok(())
202 }
203
204 fn set_tray_title(&self, _text: &str) -> Result<(), PlatformError> {
206 Ok(())
207 }
208
209 fn set_app_badge(&self, _text: &str) -> Result<(), PlatformError> {
212 Ok(())
213 }
214
215 fn autostart_is_enabled(&self) -> Result<bool, PlatformError> {
219 Err(PlatformError::NotSupported("autostart".to_string()))
220 }
221
222 fn autostart_set_enabled(&self, _enabled: bool) -> Result<(), PlatformError> {
224 Err(PlatformError::NotSupported("autostart".to_string()))
225 }
226
227 fn set_tray_menu(&self, _items_json: &str) -> Result<(), PlatformError> {
231 Ok(())
232 }
233
234 fn set_tray_visible(&self, _visible: bool) -> Result<(), PlatformError> {
236 Ok(())
237 }
238
239 fn set_tray_click_intercept(&self, _intercept: bool) -> Result<(), PlatformError> {
243 Ok(())
244 }
245
246 fn navigate(
252 &self,
253 appid: String,
254 path: String,
255 webtag: String,
256 animation_type: AnimationType,
257 ) -> Result<(), PlatformError>;
258
259 fn open_url(&self, req: OpenUrlRequest) -> Result<OpenUrlResult, PlatformError>;
261
262 fn close_browser_tab(&self, _tab_id: &str) -> Result<(), PlatformError> {
266 Err(PlatformError::NotSupported("browser tab".to_string()))
267 }
268
269 fn activate_browser_tab(&self, _tab_id: String) -> PlatformFuture {
271 Box::pin(async { Err(PlatformError::NotSupported("browser tab".to_string())) })
272 }
273
274 fn open_builtin_browser_page(&self, _page: BuiltinBrowserPage) -> Result<(), PlatformError> {
275 Err(PlatformError::NotSupported(
276 "built-in browser pages".to_string(),
277 ))
278 }
279}
280
281#[cfg(test)]
282mod tests {
283 use super::OpenUrlTarget;
284
285 #[test]
286 fn parse_supports_new_browser_tab() {
287 assert_eq!(
288 OpenUrlTarget::parse(Some("new_browser_tab")),
289 OpenUrlTarget::NewBrowserTab
290 );
291 }
292
293 #[test]
294 fn parse_unknown_falls_back_to_external() {
295 assert_eq!(
296 OpenUrlTarget::parse(Some("foobar")),
297 OpenUrlTarget::External
298 );
299 }
300}