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 Downloads = 1,
83}
84
85impl From<i32> for AnimationType {
86 fn from(value: i32) -> Self {
87 match value {
88 1 => AnimationType::Forward,
89 2 => AnimationType::Backward,
90 _ => AnimationType::None,
91 }
92 }
93}
94
95pub trait AppRuntime:
96 Send
97 + Sync
98 + MediaInteraction
99 + MediaRuntime
100 + Network
101 + SurfacePresenter
102 + Device
103 + DeviceHardware
104 + SecureStore
105 + ShareService
106 + FileService
107 + Location
108 + UIUpdate
109 + UpdateService
110 + UserFeedback
111 + Wifi
112 + 'static
113{
114 fn read_asset<'a>(&'a self, path: &str) -> Result<Box<dyn Read + 'a>, PlatformError>;
116
117 fn asset_dir_iter<'a>(
119 &'a self,
120 asset_dir: &str,
121 ) -> Box<dyn Iterator<Item = Result<AssetFileEntry<'a>, PlatformError>> + 'a>;
122
123 fn app_data_dir(&self) -> PathBuf;
125
126 fn app_cache_dir(&self) -> PathBuf;
128
129 fn get_app_identifier(&self) -> Result<String, PlatformError>;
131
132 fn copy_album_media_to_file(
134 &self,
135 uri: &str,
136 dest_path: &Path,
137 kind: MediaKind,
138 ) -> Result<(), PlatformError> {
139 MediaRuntime::copy_album_media_to_file(self, uri, dest_path, kind)
140 }
141
142 fn get_system_locale(&self) -> &str;
144
145 fn show_lxapp(
151 &self,
152 appid: String,
153 title: String,
154 path: String,
155 webtag: String,
156 session_id: u64,
157 open_mode: LxAppOpenMode,
158 panel_id: String,
159 ) -> Result<(), PlatformError>;
160
161 fn request_lxapp_main_activation(&self, _appid: &str) {}
166
167 fn hide_lxapp(&self, appid: String, session_id: u64) -> Result<(), PlatformError>;
169
170 fn exit(&self) -> Result<(), PlatformError>;
172
173 fn set_tray_badge(&self, _text: &str) -> Result<(), PlatformError> {
180 Ok(())
181 }
182
183 fn set_tray_icon(&self, _icon: &str) -> Result<(), PlatformError> {
185 Ok(())
186 }
187
188 fn set_shell_sidebar_actions(
191 &self,
192 _items: &[lingxia_shell::ResolvedShellSidebarAction],
193 ) -> Result<(), PlatformError> {
194 Ok(())
195 }
196
197 fn set_shell_pins(&self, _items: &[lingxia_shell::ShellPin]) -> Result<(), PlatformError> {
200 Ok(())
201 }
202
203 fn set_tray_title(&self, _text: &str) -> Result<(), PlatformError> {
205 Ok(())
206 }
207
208 fn set_app_badge(&self, _text: &str) -> Result<(), PlatformError> {
211 Ok(())
212 }
213
214 fn autostart_is_enabled(&self) -> Result<bool, PlatformError> {
218 Err(PlatformError::NotSupported("autostart".to_string()))
219 }
220
221 fn autostart_set_enabled(&self, _enabled: bool) -> Result<(), PlatformError> {
223 Err(PlatformError::NotSupported("autostart".to_string()))
224 }
225
226 fn set_tray_menu(&self, _items_json: &str) -> Result<(), PlatformError> {
230 Ok(())
231 }
232
233 fn set_tray_visible(&self, _visible: bool) -> Result<(), PlatformError> {
235 Ok(())
236 }
237
238 fn set_tray_click_intercept(&self, _intercept: bool) -> Result<(), PlatformError> {
242 Ok(())
243 }
244
245 fn navigate(
251 &self,
252 appid: String,
253 path: String,
254 webtag: String,
255 animation_type: AnimationType,
256 ) -> Result<(), PlatformError>;
257
258 fn open_url(&self, req: OpenUrlRequest) -> Result<OpenUrlResult, PlatformError>;
260
261 fn close_browser_tab(&self, _tab_id: &str) -> Result<(), PlatformError> {
265 Err(PlatformError::NotSupported("browser tab".to_string()))
266 }
267
268 fn activate_browser_tab(&self, _tab_id: String) -> PlatformFuture {
270 Box::pin(async { Err(PlatformError::NotSupported("browser tab".to_string())) })
271 }
272
273 fn open_builtin_browser_page(&self, _page: BuiltinBrowserPage) -> Result<(), PlatformError> {
274 Err(PlatformError::NotSupported(
275 "built-in browser pages".to_string(),
276 ))
277 }
278}
279
280#[cfg(test)]
281mod tests {
282 use super::OpenUrlTarget;
283
284 #[test]
285 fn parse_supports_new_browser_tab() {
286 assert_eq!(
287 OpenUrlTarget::parse(Some("new_browser_tab")),
288 OpenUrlTarget::NewBrowserTab
289 );
290 }
291
292 #[test]
293 fn parse_unknown_falls_back_to_external() {
294 assert_eq!(
295 OpenUrlTarget::parse(Some("foobar")),
296 OpenUrlTarget::External
297 );
298 }
299}