1#![allow(clippy::manual_async_fn)]
2
3use std::future::Future;
4use std::io::Read;
5use std::path::{Path, PathBuf};
6
7use crate::error::PlatformError;
8use crate::traits::app_runtime::{AnimationType, AppRuntime, LxAppOpenMode, OpenUrlRequest};
9use crate::traits::device::{Device, DeviceHardware};
10use crate::traits::file::{
11 ChooseDirectoryRequest, ChooseFileRequest, FileDialogResult, FileService, OpenFileRequest,
12 RevealInFileManagerRequest,
13};
14use crate::traits::keyboard::AppKeyboard;
15use crate::traits::location::{Location, LocationRequestConfig};
16use crate::traits::media_interaction::{
17 ChooseMediaRequest, MediaInteraction, PreviewMediaRequest, SaveMediaRequest, ScanCodeRequest,
18};
19use crate::traits::media_runtime::{
20 CompressImageRequest, CompressVideoRequest, CompressedVideo, ExtractVideoThumbnailRequest,
21 ImageInfo, MediaRuntime, VideoInfo, VideoThumbnail,
22};
23use crate::traits::mouse::AppMouse;
24use crate::traits::network::Network;
25use crate::traits::pull_to_refresh::PullToRefresh;
26use crate::traits::screenshot::AppScreenshot;
27use crate::traits::secure_store::SecureStore;
28use crate::traits::share::{ShareRequest, ShareResult, ShareService};
29use crate::traits::stream_decoder::{VideoStreamDecoderHandle, VideoStreamDecoderManager};
30use crate::traits::ui::{ModalOptions, SurfacePresenter, ToastOptions, UIUpdate, UserFeedback};
31use crate::traits::update::UpdateService;
32use crate::traits::video_player::{VideoPlayerHandle, VideoPlayerManager};
33use crate::traits::wifi::Wifi;
34use crate::{AssetFileEntry, DeviceInfo, ScreenInfo};
35
36#[derive(Debug, Clone)]
37pub struct Platform {
38 data_dir: PathBuf,
39 cache_dir: PathBuf,
40 locale: String,
41}
42
43impl Default for Platform {
44 fn default() -> Self {
45 let base = std::env::temp_dir().join("lingxia");
46 Self {
47 data_dir: base.join("data"),
48 cache_dir: base.join("cache"),
49 locale: "en-US".to_string(),
50 }
51 }
52}
53
54impl Platform {
55 pub fn new(
56 data_dir: impl Into<PathBuf>,
57 cache_dir: impl Into<PathBuf>,
58 locale: impl Into<String>,
59 ) -> Result<Self, PlatformError> {
60 Ok(Self {
61 data_dir: data_dir.into(),
62 cache_dir: cache_dir.into(),
63 locale: locale.into(),
64 })
65 }
66}
67
68fn not_supported<T>(name: &str) -> Result<T, PlatformError> {
69 Err(PlatformError::NotSupported(format!(
70 "{name} is not supported on this platform"
71 )))
72}
73
74impl Device for Platform {
75 fn device_info(&self) -> DeviceInfo {
76 DeviceInfo {
77 brand: "unsupported".to_string(),
78 model: std::env::consts::OS.to_string(),
79 market_name: std::env::consts::OS.to_string(),
80 os_name: std::env::consts::OS.to_string(),
81 os_version: String::new(),
82 }
83 }
84
85 fn screen_info(&self) -> ScreenInfo {
86 ScreenInfo {
87 width: 0.0,
88 height: 0.0,
89 scale: 1.0,
90 }
91 }
92
93 fn vibrate(&self, _long: bool) -> Result<(), PlatformError> {
94 not_supported("vibrate")
95 }
96
97 fn make_phone_call(&self, _phone_number: &str) -> Result<(), PlatformError> {
98 not_supported("make_phone_call")
99 }
100}
101
102impl DeviceHardware for Platform {}
103impl SecureStore for Platform {}
104impl Network for Platform {}
105impl SurfacePresenter for Platform {}
106impl UpdateService for Platform {}
107impl Wifi for Platform {}
108impl AppScreenshot for Platform {}
109impl AppMouse for Platform {}
110impl AppKeyboard for Platform {}
111
112impl FileService for Platform {
113 fn review_file(
114 &self,
115 _request: OpenFileRequest,
116 ) -> impl Future<Output = Result<(), PlatformError>> + Send {
117 async { not_supported("review_file") }
118 }
119
120 fn open_external(
121 &self,
122 _request: OpenFileRequest,
123 ) -> impl Future<Output = Result<(), PlatformError>> + Send {
124 async { not_supported("open_external") }
125 }
126
127 fn reveal_in_file_manager(
128 &self,
129 _request: RevealInFileManagerRequest,
130 ) -> impl Future<Output = Result<(), PlatformError>> + Send {
131 async { not_supported("reveal_in_file_manager") }
132 }
133
134 fn choose_file(
135 &self,
136 _request: ChooseFileRequest,
137 ) -> impl Future<Output = Result<FileDialogResult, PlatformError>> + Send {
138 async { not_supported("choose_file") }
139 }
140
141 fn choose_directory(
142 &self,
143 _request: ChooseDirectoryRequest,
144 ) -> impl Future<Output = Result<FileDialogResult, PlatformError>> + Send {
145 async { not_supported("choose_directory") }
146 }
147}
148
149impl Location for Platform {
150 fn is_location_enabled(&self) -> Result<bool, PlatformError> {
151 not_supported("is_location_enabled")
152 }
153
154 fn request_location(
155 &self,
156 _config: LocationRequestConfig,
157 ) -> impl Future<Output = Result<String, PlatformError>> + Send {
158 async { not_supported("request_location") }
159 }
160}
161
162impl MediaInteraction for Platform {
163 fn preview_media(&self, _request: PreviewMediaRequest) -> Result<(), PlatformError> {
164 not_supported("preview_media")
165 }
166
167 fn cancel_preview(&self, _callback_id: u64) -> Result<(), PlatformError> {
168 not_supported("cancel_preview")
169 }
170
171 fn choose_media(
172 &self,
173 _request: ChooseMediaRequest,
174 ) -> impl Future<Output = Result<String, PlatformError>> + Send {
175 async { not_supported("choose_media") }
176 }
177
178 fn scan_code(
179 &self,
180 _request: ScanCodeRequest,
181 ) -> impl Future<Output = Result<String, PlatformError>> + Send {
182 async { not_supported("scan_code") }
183 }
184
185 fn save_image_to_photos_album(
186 &self,
187 _request: SaveMediaRequest,
188 ) -> impl Future<Output = Result<(), PlatformError>> + Send {
189 async { not_supported("save_image_to_photos_album") }
190 }
191
192 fn save_video_to_photos_album(
193 &self,
194 _request: SaveMediaRequest,
195 ) -> impl Future<Output = Result<(), PlatformError>> + Send {
196 async { not_supported("save_video_to_photos_album") }
197 }
198}
199
200impl MediaRuntime for Platform {
201 fn copy_album_media_to_file(
202 &self,
203 _uri: &str,
204 _dest_path: &Path,
205 _kind: crate::traits::media_interaction::MediaKind,
206 ) -> Result<(), PlatformError> {
207 not_supported("copy_album_media_to_file")
208 }
209
210 fn get_image_info(&self, _uri: &str) -> Result<ImageInfo, PlatformError> {
211 not_supported("get_image_info")
212 }
213
214 fn compress_image(&self, _request: &CompressImageRequest) -> Result<PathBuf, PlatformError> {
215 not_supported("compress_image")
216 }
217
218 fn compress_video(&self, _request: &CompressVideoRequest) -> Result<(), PlatformError> {
219 not_supported("compress_video")
220 }
221
222 fn cancel_compress_video(&self, _callback_id: u64) -> Result<(), PlatformError> {
223 not_supported("cancel_compress_video")
224 }
225
226 fn get_video_info(&self, _uri: &str) -> Result<VideoInfo, PlatformError> {
227 not_supported("get_video_info")
228 }
229
230 fn extract_video_thumbnail(
231 &self,
232 _request: &ExtractVideoThumbnailRequest,
233 ) -> Result<VideoThumbnail, PlatformError> {
234 not_supported("extract_video_thumbnail")
235 }
236}
237
238impl ShareService for Platform {
239 fn share(
240 &self,
241 _request: ShareRequest,
242 ) -> impl Future<Output = Result<ShareResult, PlatformError>> + Send {
243 async { not_supported("share") }
244 }
245}
246
247impl UIUpdate for Platform {
248 fn update_navbar_ui(&self, _appid: String) -> Result<(), PlatformError> {
249 not_supported("update_navbar_ui")
250 }
251
252 fn update_tabbar_ui(&self, _appid: String) -> Result<(), PlatformError> {
253 not_supported("update_tabbar_ui")
254 }
255}
256
257impl UserFeedback for Platform {
258 fn show_toast(&self, _options: ToastOptions) -> Result<(), PlatformError> {
259 not_supported("show_toast")
260 }
261
262 fn hide_toast(&self) -> Result<(), PlatformError> {
263 not_supported("hide_toast")
264 }
265
266 fn show_modal(
267 &self,
268 _options: ModalOptions,
269 ) -> impl Future<Output = Result<String, PlatformError>> + Send {
270 async { not_supported("show_modal") }
271 }
272
273 fn show_action_sheet(
274 &self,
275 _options: Vec<String>,
276 _cancel_text: String,
277 _item_color: String,
278 ) -> impl Future<Output = Result<String, PlatformError>> + Send {
279 async { not_supported("show_action_sheet") }
280 }
281}
282
283impl PullToRefresh for Platform {
284 fn start_pull_down_refresh(&self, _app_id: &str, _path: &str) -> Result<(), PlatformError> {
285 not_supported("start_pull_down_refresh")
286 }
287
288 fn stop_pull_down_refresh(&self, _app_id: &str, _path: &str) -> Result<(), PlatformError> {
289 not_supported("stop_pull_down_refresh")
290 }
291}
292
293impl VideoPlayerManager for Platform {
294 fn bind_player(
295 &self,
296 _component_id: &str,
297 ) -> Result<Box<dyn VideoPlayerHandle>, PlatformError> {
298 not_supported("bind_player")
299 }
300}
301
302impl VideoStreamDecoderManager for Platform {
303 fn create_stream_decoder(
304 &self,
305 _component_id: &str,
306 ) -> Result<Box<dyn VideoStreamDecoderHandle>, PlatformError> {
307 not_supported("create_stream_decoder")
308 }
309}
310
311impl AppRuntime for Platform {
312 fn read_asset<'a>(&'a self, _path: &str) -> Result<Box<dyn Read + 'a>, PlatformError> {
313 not_supported("read_asset")
314 }
315
316 fn asset_dir_iter<'a>(
317 &'a self,
318 _asset_dir: &str,
319 ) -> Box<dyn Iterator<Item = Result<AssetFileEntry<'a>, PlatformError>> + 'a> {
320 Box::new(std::iter::once(not_supported("asset_dir_iter")))
321 }
322
323 fn app_data_dir(&self) -> PathBuf {
324 self.data_dir.clone()
325 }
326
327 fn app_cache_dir(&self) -> PathBuf {
328 self.cache_dir.clone()
329 }
330
331 fn get_app_identifier(&self) -> Result<String, PlatformError> {
332 not_supported("get_app_identifier")
333 }
334
335 fn get_system_locale(&self) -> &str {
336 &self.locale
337 }
338
339 fn show_lxapp(
340 &self,
341 _appid: String,
342 _path: String,
343 _session_id: u64,
344 _open_mode: LxAppOpenMode,
345 _panel_id: String,
346 ) -> Result<(), PlatformError> {
347 not_supported("show_lxapp")
348 }
349
350 fn hide_lxapp(&self, _appid: String, _session_id: u64) -> Result<(), PlatformError> {
351 not_supported("hide_lxapp")
352 }
353
354 fn exit(&self) -> Result<(), PlatformError> {
355 not_supported("exit")
356 }
357
358 fn navigate(
359 &self,
360 _appid: String,
361 _path: String,
362 _animation_type: AnimationType,
363 ) -> Result<(), PlatformError> {
364 not_supported("navigate")
365 }
366
367 fn open_url(&self, _req: OpenUrlRequest) -> Result<(), PlatformError> {
368 not_supported("open_url")
369 }
370
371 fn get_capsule_rect(&self) -> impl Future<Output = Result<String, PlatformError>> + Send {
372 async { not_supported("get_capsule_rect") }
373 }
374}