Improve docs

This removes the indirection of the `StateSaver` and `StateLoader`
type aliases (which resulted in no documentation for these interfaces)
and we now simply re-export the types from the backend implementation.
This commit is contained in:
Robert Bragg
2022-11-10 22:23:41 +00:00
parent 4669508823
commit aa5fcc53bb
6 changed files with 107 additions and 19 deletions
+1
View File
@@ -64,6 +64,7 @@ fn android_main(app: AndroidApp) {
app.input_events(|event| {
info!("Input Event: {event:?}");
InputStatus::Unhandled
});
});
}
+7
View File
@@ -6,6 +6,13 @@ use ndk::configuration::{
ScreenSize, Touchscreen, UiModeNight, UiModeType,
};
/// A (cheaply clonable) reference to this application's [`ndk::configuration::Configuration`]
///
/// This provides a thread-safe way to access the latest configuration state for
/// an application without deeply copying the large [`ndk::configuration::Configuration`] struct.
///
/// If the application is notified of configuration changes then those changes
/// will become visible via pre-existing configuration references.
#[derive(Clone)]
pub struct ConfigurationRef {
config: Arc<RwLock<Configuration>>,
@@ -655,9 +655,6 @@ pub unsafe extern "C" fn _rust_glue_entry(app: *mut ffi::android_app) {
// code to look up non-standard Java classes.
android_main(app);
// Since this is a newly spawned thread then the JVM hasn't been attached
// to the thread yet. Attach before calling the applications main function
// so they can safely make JNI calls
if let Some(detach_current_thread) = (*(*jvm)).DetachCurrentThread {
detach_current_thread(jvm);
}
+82 -10
View File
@@ -1,3 +1,62 @@
//! A glue layer for building standalone, Rust applications on Android
//!
//! This crate provides a "glue" layer for building native Rust
//! applications on Android, supporting multiple [`Activity`] base classes.
//! It's comparable to [`android_native_app_glue.c`][ndk_concepts]
//! for C/C++ applications.
//!
//! Currently the crate supports two `Activity` base classes:
//! 1. [`NativeActivity`] - Built in to Android, this doesn't require compiling any Java or Kotlin code.
//! 2. [`GameActivity`] - From the Android Game Development Kit, it has more
//! sophisticated input handling support than `NativeActivity`. `GameActivity`
//! is also based on the `AndroidAppCompat` class which can help with supporting
//! a wider range of devices.
//!
//! Standalone applications based on this crate need to be built as `cdylib` libraries, like:
//! ```
//! [lib]
//! crate_type=["cdylib"]
//! ```
//!
//! and implement a `#[no_mangle]` `android_main` entry point like this:
//! ```rust
//! #[no_mangle]
//! fn android_main(app: AndroidApp) {
//!
//! }
//! ```
//!
//! Once your application's `Activity` class has loaded and it calls `onCreate` then
//! `android-activity` will spawn a dedicated thread to run your `android_main` function,
//! separate from the Java thread that created the corresponding `Activity`.
//!
//! [`AndroidApp`] provides an interface to query state for the application as
//! well as monitor events, such as lifecycle and input events, that are
//! marshalled between the Java thread that owns the `Activity` and the native
//! thread that runs the `android_main()` code.
//!
//! # Main Thread Initialization
//!
//! Before `android_main()` is called, the following application state
//! is also initialized:
//!
//! 1. An I/O thread is spawned that will handle redirecting standard input
//! and output to the Android log, visible via `logcat`.
//! 2. A `JavaVM` and `Activity` instance will be associated with the [`ndk_context`] crate
//! so that other, independent, Rust crates are able to find a JavaVM
//! for making JNI calls.
//! 3. The `JavaVM` will be attached to the native thread
//! 4. A [Looper] is attached to the Rust native thread.
//!
//!
//! These are undone after `android_main()` returns
//!
//! [`Activity`]: https://developer.android.com/reference/android/app/Activity
//! [`NativeActivity`]: https://developer.android.com/reference/android/app/NativeActivity
//! [ndk_concepts]: https://developer.android.com/ndk/guides/concepts#naa
//! [`GameActivity`]: https://developer.android.com/games/agdk/integrate-game-activity
//! [Looper]: https://developer.android.com/reference/android/os/Looper
use std::hash::Hash;
use std::sync::Arc;
use std::sync::RwLock;
@@ -54,13 +113,7 @@ pub use config::ConfigurationRef;
mod util;
// Note: unlike in ndk-glue this has signed components (consistent
// with Android's ARect) which generally allows for representing
// rectangles with a negative/off-screen origin. Even though this
// is currently just used to represent the content rect (that probably
// wouldn't have any negative components) we keep the generality
// since this is a primitive type that could potentially be used
// for more things in the future.
/// A rectangle with integer edge coordinates. Used to represent window insets, for example.
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Rect {
pub left: i32,
@@ -103,9 +156,11 @@ impl From<ndk_sys::ARect> for Rect {
}
}
pub type StateSaver<'a> = activity_impl::StateSaver<'a>;
pub type StateLoader<'a> = activity_impl::StateLoader<'a>;
pub use activity_impl::StateSaver;
pub use activity_impl::StateLoader;
/// An application event delivered during [`AndroidApp::poll_events`]
#[non_exhaustive]
#[derive(Debug)]
pub enum MainEvent<'a> {
@@ -197,6 +252,7 @@ pub enum MainEvent<'a> {
InsetsChanged {},
}
/// An event delivered during [`AndroidApp::poll_events`]
#[derive(Debug)]
#[non_exhaustive]
pub enum PollEvent<'a> {
@@ -205,6 +261,9 @@ pub enum PollEvent<'a> {
Main(MainEvent<'a>),
}
/// Indicates whether an application has handled or ignored an event
///
/// If an event is not handled by an application then some default handling may happen.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InputStatus {
Handled,
@@ -380,6 +439,13 @@ bitflags! {
}
}
/// The top-level state and interface for a native Rust application
///
/// `AndroidApp` provides an interface to query state for the application as
/// well as monitor events, such as lifecycle and input events, that are
/// marshalled between the Java thread that owns the `Activity` and the native
/// thread that runs the `android_main()` code.
///
#[derive(Debug, Clone)]
pub struct AndroidApp {
pub(crate) inner: Arc<RwLock<AndroidAppInner>>,
@@ -398,6 +464,7 @@ impl Hash for AndroidApp {
}
}
impl AndroidApp {
/// Queries the current [`NativeWindow`] for the application.
///
@@ -408,7 +475,7 @@ impl AndroidApp {
self.inner.read().unwrap().native_window()
}
/// Polls for any events associated with this AndroidApp and processes those events
/// Polls for any events associated with this [AndroidApp] and processes those events
/// (such as lifecycle events) via the given `callback`.
///
/// It's important to use this API for polling, and not call [`ALooper_pollAll`] directly since
@@ -420,6 +487,11 @@ impl AndroidApp {
/// main thread. The [`MainEvent::SaveState`] event is also synchronized with the
/// Java main thread.
///
/// # Panics
///
/// This must only be called from your `android_main()` thread and it may panic if called
/// from another thread.
///
/// [`ALooper_pollAll`]: ndk::looper::ThreadLooper::poll_all
pub fn poll_events<F>(&self, timeout: Option<Duration>, callback: F)
where
@@ -816,9 +816,6 @@ extern "C" fn ANativeActivity_onCreate(
// code to look up non-standard Java classes.
android_main(app);
// Since this is a newly spawned thread then the JVM hasn't been attached
// to the thread yet. Attach before calling the applications main function
// so they can safely make JNI calls
if let Some(detach_current_thread) = (*(*jvm)).DetachCurrentThread {
detach_current_thread(jvm);
}
+17 -3
View File
@@ -42,30 +42,37 @@ pub const LOOPER_ID_MAIN: libc::c_int = 1;
pub const LOOPER_ID_INPUT: libc::c_int = 2;
//pub const LOOPER_ID_USER: ::std::os::raw::c_uint = 3;
// The only time it's safe to update the saved_state pointer is
// while handling a SaveState event, so this API is only exposed for those
// events
/// An interface for saving application state during [MainEvent::SaveState] events
///
/// This interface is only available temporarily while handling a [MainEvent::SaveState] event.
#[derive(Debug)]
pub struct StateSaver<'a> {
app: &'a AndroidAppInner,
}
impl<'a> StateSaver<'a> {
/// Stores the given `state` such that it will be available to load the next
/// time that the application resumes.
pub fn store(&self, state: &'a [u8]) {
self.app.native_activity.set_saved_state(state);
}
}
/// An interface for loading application state during [MainEvent::Resume] events
///
/// This interface is only available temporarily while handling a [MainEvent::Resume] event.
#[derive(Debug)]
pub struct StateLoader<'a> {
app: &'a AndroidAppInner,
}
impl<'a> StateLoader<'a> {
/// Returns whatever state was saved during the last [MainEvent::SaveState] event or `None`
pub fn load(&self) -> Option<Vec<u8>> {
self.app.native_activity.saved_state()
}
}
/// A means to wake up the main thread while it is blocked waiting for I/O
#[derive(Clone)]
pub struct AndroidAppWaker {
// The looper pointer is owned by the android_app and effectively
@@ -77,6 +84,13 @@ unsafe impl Send for AndroidAppWaker {}
unsafe impl Sync for AndroidAppWaker {}
impl AndroidAppWaker {
/// Interrupts the main thread if it is blocked within [`AndroidApp::poll_events()`]
///
/// If [`AndroidApp::poll_events()`] is interrupted it will invoke the poll
/// callback with a [PollEvent::Wake][wake_event] event.
///
/// [wake_event]: crate::PollEvent::Wake
pub fn wake(&self) {
unsafe {
ALooper_wake(self.looper.as_ptr());