mirror of
				https://github.com/slint-ui/slint.git
				synced 2025-10-31 03:54:25 +00:00 
			
		
		
		
	 6f4f5aeefa
			
		
	
	
		6f4f5aeefa
		
	
	
	
	
		
			
			We can't just do `s/#\[no_mangle]/#[unsafe(no_mangle)]/g` because the version of cbingen we use doesn't understand the unsafe attribute yet. Part of #7998
		
			
				
	
	
		
			189 lines
		
	
	
	
		
			6.3 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			189 lines
		
	
	
	
		
			6.3 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| // Copyright © SixtyFPS GmbH <info@slint.dev>
 | |
| // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
 | |
| 
 | |
| #![doc = include_str!("README.md")]
 | |
| #![doc(html_logo_url = "https://slint.dev/logo/slint-logo-square-light.svg")]
 | |
| #![cfg_attr(not(target_os = "android"), allow(rustdoc::broken_intra_doc_links))]
 | |
| #![cfg(target_os = "android")]
 | |
| 
 | |
| mod androidwindowadapter;
 | |
| mod javahelper;
 | |
| 
 | |
| #[cfg(all(not(feature = "aa-06"), feature = "aa-05"))]
 | |
| pub use android_activity_05 as android_activity;
 | |
| #[cfg(feature = "aa-06")]
 | |
| pub use android_activity_06 as android_activity;
 | |
| 
 | |
| pub use android_activity::AndroidApp;
 | |
| use android_activity::PollEvent;
 | |
| use androidwindowadapter::AndroidWindowAdapter;
 | |
| use core::ops::ControlFlow;
 | |
| use core::time::Duration;
 | |
| use i_slint_core::api::{EventLoopError, PlatformError};
 | |
| use i_slint_core::platform::{Clipboard, WindowAdapter};
 | |
| use i_slint_renderer_skia::SkiaRendererExt;
 | |
| use std::cell::RefCell;
 | |
| use std::rc::{Rc, Weak};
 | |
| use std::sync::{Arc, Mutex};
 | |
| 
 | |
| thread_local! {
 | |
|     static CURRENT_WINDOW: RefCell<Weak<AndroidWindowAdapter>> = RefCell::new(Default::default());
 | |
| }
 | |
| 
 | |
| pub struct AndroidPlatform {
 | |
|     app: AndroidApp,
 | |
|     window: Rc<AndroidWindowAdapter>,
 | |
|     event_listener: Option<Box<dyn Fn(&PollEvent<'_>)>>,
 | |
| }
 | |
| 
 | |
| impl AndroidPlatform {
 | |
|     /// Instantiate a new Android backend given the [`android_activity::AndroidApp`]
 | |
|     ///
 | |
|     /// Pass the returned value to [`slint::platform::set_platform()`](`i_slint_core::platform::set_platform()`)
 | |
|     ///
 | |
|     /// # Example
 | |
|     /// ```
 | |
|     /// #[cfg(target_os = "android")]
 | |
|     /// #[unsafe(no_mangle)]
 | |
|     /// fn android_main(app: i_slint_backend_android_activity::AndroidApp) {
 | |
|     ///     slint::platform::set_platform(Box::new(
 | |
|     ///         i_slint_backend_android_activity::AndroidPlatform::new(app),
 | |
|     ///     ))
 | |
|     ///     .unwrap();
 | |
|     ///     // ... your slint application ...
 | |
|     /// }
 | |
|     /// ```
 | |
|     pub fn new(app: AndroidApp) -> Self {
 | |
|         let window = AndroidWindowAdapter::new(app.clone());
 | |
|         CURRENT_WINDOW.set(Rc::downgrade(&window));
 | |
|         Self { app, window, event_listener: None }
 | |
|     }
 | |
| 
 | |
|     /// Instantiate a new Android backend given the [`android_activity::AndroidApp`]
 | |
|     /// and a function to process the events.
 | |
|     ///
 | |
|     /// This is the same as [`AndroidPlatform::new()`], but it allow you to get notified
 | |
|     /// of events.
 | |
|     ///
 | |
|     /// Pass the returned value to [`slint::platform::set_platform()`](`i_slint_core::platform::set_platform()`)
 | |
|     ///
 | |
|     /// # Example
 | |
|     /// ```
 | |
|     /// #[cfg(target_os = "android")]
 | |
|     /// #[unsafe(no_mangle)]
 | |
|     /// fn android_main(app: i_slint_backend_android_activity::AndroidApp) {
 | |
|     ///     slint::platform::set_platform(Box::new(
 | |
|     ///         i_slint_backend_android_activity::AndroidPlatform::new_with_event_listener(
 | |
|     ///             app,
 | |
|     ///             |event| { eprintln!("got event {event:?}") }
 | |
|     ///         ),
 | |
|     ///     ))
 | |
|     ///     .unwrap();
 | |
|     ///     // ... your slint application ...
 | |
|     /// }
 | |
|     /// ```
 | |
|     pub fn new_with_event_listener(
 | |
|         app: AndroidApp,
 | |
|         listener: impl Fn(&PollEvent<'_>) + 'static,
 | |
|     ) -> Self {
 | |
|         let mut this = Self::new(app);
 | |
|         this.event_listener = Some(Box::new(listener));
 | |
|         this
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl i_slint_core::platform::Platform for AndroidPlatform {
 | |
|     fn create_window_adapter(&self) -> Result<Rc<dyn WindowAdapter>, PlatformError> {
 | |
|         Ok(self.window.clone())
 | |
|     }
 | |
|     fn run_event_loop(&self) -> Result<(), PlatformError> {
 | |
|         loop {
 | |
|             let mut timeout = i_slint_core::platform::duration_until_next_timer_update();
 | |
|             if self.window.window.has_active_animations() {
 | |
|                 // FIXME: we should not hardcode a value here
 | |
|                 let frame_duration = Duration::from_millis(10);
 | |
|                 timeout = Some(match timeout {
 | |
|                     Some(x) => x.min(frame_duration),
 | |
|                     None => frame_duration,
 | |
|                 })
 | |
|             }
 | |
|             let mut r = Ok(ControlFlow::Continue(()));
 | |
|             self.app.poll_events(timeout, |e| {
 | |
|                 i_slint_core::platform::update_timers_and_animations();
 | |
|                 r = self.window.process_event(&e);
 | |
|                 if let Some(event_listener) = &self.event_listener {
 | |
|                     event_listener(&e)
 | |
|                 }
 | |
|             });
 | |
|             if r?.is_break() {
 | |
|                 break;
 | |
|             }
 | |
|             if self.window.pending_redraw.take() {
 | |
|                 self.window.do_render()?;
 | |
|             }
 | |
|         }
 | |
|         Ok(())
 | |
|     }
 | |
| 
 | |
|     fn new_event_loop_proxy(&self) -> Option<Box<dyn i_slint_core::platform::EventLoopProxy>> {
 | |
|         Some(Box::new(AndroidEventLoopProxy {
 | |
|             event_queue: self.window.event_queue.clone(),
 | |
|             waker: self.app.create_waker(),
 | |
|         }))
 | |
|     }
 | |
| 
 | |
|     fn set_clipboard_text(&self, text: &str, clipboard: Clipboard) {
 | |
|         if clipboard == Clipboard::DefaultClipboard {
 | |
|             self.window
 | |
|                 .java_helper
 | |
|                 .set_clipboard(text)
 | |
|                 .unwrap_or_else(|e| javahelper::print_jni_error(&self.app, e));
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     fn clipboard_text(&self, clipboard: Clipboard) -> Option<String> {
 | |
|         if clipboard == Clipboard::DefaultClipboard {
 | |
|             Some(
 | |
|                 self.window
 | |
|                     .java_helper
 | |
|                     .get_clipboard()
 | |
|                     .unwrap_or_else(|e| javahelper::print_jni_error(&self.app, e)),
 | |
|             )
 | |
|         } else {
 | |
|             None
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     fn long_press_interval(&self, _: i_slint_core::InternalToken) -> Duration {
 | |
|         self.window.java_helper.long_press_timeout().unwrap_or(Duration::from_millis(500))
 | |
|     }
 | |
| }
 | |
| 
 | |
| enum Event {
 | |
|     Quit,
 | |
|     Other(Box<dyn FnOnce() + Send + 'static>),
 | |
| }
 | |
| 
 | |
| type EventQueue = Arc<Mutex<Vec<Event>>>;
 | |
| 
 | |
| struct AndroidEventLoopProxy {
 | |
|     event_queue: EventQueue,
 | |
|     waker: android_activity::AndroidAppWaker,
 | |
| }
 | |
| 
 | |
| impl i_slint_core::platform::EventLoopProxy for AndroidEventLoopProxy {
 | |
|     fn quit_event_loop(&self) -> Result<(), EventLoopError> {
 | |
|         self.event_queue.lock().unwrap().push(Event::Quit);
 | |
|         self.waker.wake();
 | |
|         Ok(())
 | |
|     }
 | |
| 
 | |
|     fn invoke_from_event_loop(
 | |
|         &self,
 | |
|         event: Box<dyn FnOnce() + Send>,
 | |
|     ) -> Result<(), EventLoopError> {
 | |
|         self.event_queue.lock().unwrap().push(Event::Other(event));
 | |
|         self.waker.wake();
 | |
|         Ok(())
 | |
|     }
 | |
| }
 |