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