diff --git a/android-activity/src/lib.rs b/android-activity/src/lib.rs index e8984fe..f4c3a4a 100644 --- a/android-activity/src/lib.rs +++ b/android-activity/src/lib.rs @@ -549,6 +549,23 @@ impl AndroidApp { self.inner.read().unwrap().native_window() } + /// Returns a pointer to the current looper associated with the main thread + /// + /// This is appropriate for posting callbacks that need to run on the UI thread + /// Wrap in an [`ndk::looper::ForeignLooper `] + /// ```ignore + /// # use ndk; + /// # let app: AndroidApp = todo!(); + /// let looper = unsafe { + /// let non_null_looper = ptr::NonNull::new(_app.main_looper_as_ptr()).unwrap(); + /// ndk::looper::ForeignLooper::from_ptr(non_null_looper) + /// }; + /// looper.add_fd_with_callback(todo!(), ndk::looper::FdEvent::INPUT, todo!()).unwrap(); + /// ``` + pub fn main_looper_as_ptr(&self) -> *mut ndk_sys::ALooper { + self.inner.read().unwrap().main_looper() + } + /// Returns a pointer to the Java Virtual Machine, for making JNI calls /// /// This returns a pointer to the Java Virtual Machine which can be used diff --git a/android-activity/src/native_activity/glue.rs b/android-activity/src/native_activity/glue.rs index dab3e29..585fa6f 100644 --- a/android-activity/src/native_activity/glue.rs +++ b/android-activity/src/native_activity/glue.rs @@ -848,6 +848,7 @@ extern "C" fn ANativeActivity_onCreate( let rust_glue = jvm_glue.clone(); // Let us Send the NativeActivity pointer to the Rust main() thread without a wrapper type let activity_ptr: libc::intptr_t = activity as _; + let main_looper_ptr: libc::intptr_t = unsafe { ndk_sys::ALooper_forThread() } as _; // Note: we drop the thread handle which will detach the thread std::thread::spawn(move || { @@ -867,7 +868,7 @@ extern "C" fn ANativeActivity_onCreate( jvm }); - let app = AndroidApp::new(rust_glue.clone(), jvm.clone()); + let app = AndroidApp::new(rust_glue.clone(), jvm.clone(), main_looper_ptr as *mut _); rust_glue.notify_main_thread_running(); diff --git a/android-activity/src/native_activity/mod.rs b/android-activity/src/native_activity/mod.rs index 88a9fe8..c50b954 100644 --- a/android-activity/src/native_activity/mod.rs +++ b/android-activity/src/native_activity/mod.rs @@ -86,7 +86,11 @@ impl AndroidAppWaker { } impl AndroidApp { - pub(crate) fn new(native_activity: NativeActivityGlue, jvm: CloneJavaVM) -> Self { + pub(crate) fn new( + native_activity: NativeActivityGlue, + jvm: CloneJavaVM, + main_looper_ptr: *mut ndk_sys::ALooper + ) -> Self { let mut env = jvm.get_env().unwrap(); // We attach to the thread before creating the AndroidApp let key_map_binding = match KeyCharacterMapBinding::new(&mut env) { @@ -103,6 +107,9 @@ impl AndroidApp { looper: Looper { ptr: ptr::null_mut(), }, + main_looper: Looper { + ptr: main_looper_ptr, + }, key_map_binding: Arc::new(key_map_binding), key_maps: Mutex::new(HashMap::new()), input_receiver: Mutex::new(None), @@ -147,6 +154,9 @@ pub(crate) struct AndroidAppInner { pub(crate) native_activity: NativeActivityGlue, looper: Looper, + /// Looper associated with the activy's main thread, sometimes called the UI thread. + main_looper: Looper, + /// Shared JNI bindings for the `KeyCharacterMap` class key_map_binding: Arc, @@ -179,6 +189,10 @@ impl AndroidAppInner { self.looper.ptr } + pub fn main_looper(&self) -> *mut ndk_sys::ALooper { + self.main_looper.ptr + } + pub fn native_window(&self) -> Option { self.native_activity.mutex.lock().unwrap().window.clone() }