android_activity/
lib.rs

1//! A glue layer for building standalone, Rust applications on Android
2//!
3//! This crate provides a "glue" layer for building native Rust
4//! applications on Android, supporting multiple [`Activity`] base classes.
5//! It's comparable to [`android_native_app_glue.c`][ndk_concepts]
6//! for C/C++ applications.
7//!
8//! Currently the crate supports two `Activity` base classes:
9//! 1. [`NativeActivity`] - Built in to Android, this doesn't require compiling any Java or Kotlin code.
10//! 2. [`GameActivity`] - From the Android Game Development Kit, it has more
11//!    sophisticated input handling support than `NativeActivity`. `GameActivity`
12//!    is also based on the `AndroidAppCompat` class which can help with supporting
13//!    a wider range of devices.
14//!
15//! Standalone applications based on this crate need to be built as `cdylib` libraries, like:
16//! ```
17//! [lib]
18//! crate_type=["cdylib"]
19//! ```
20//!
21//! and implement a `#[no_mangle]` `android_main` entry point like this:
22//! ```rust
23//! #[no_mangle]
24//! fn android_main(app: AndroidApp) {
25//!
26//! }
27//! ```
28//!
29//! Once your application's `Activity` class has loaded and it calls `onCreate` then
30//! `android-activity` will spawn a dedicated thread to run your `android_main` function,
31//! separate from the Java thread that created the corresponding `Activity`.
32//!
33//! [`AndroidApp`] provides an interface to query state for the application as
34//! well as monitor events, such as lifecycle and input events, that are
35//! marshalled between the Java thread that owns the `Activity` and the native
36//! thread that runs the `android_main()` code.
37//!
38//! # Cheaply Clonable [`AndroidApp`]
39//!
40//! [`AndroidApp`] is intended to be something that can be cheaply passed around
41//! by referenced within an application. It is reference counted and can be
42//! cheaply cloned.
43//!
44//! # `Send` and `Sync` [`AndroidApp`]
45//!
46//! Although an [`AndroidApp`] implements `Send` and `Sync` you do need to take
47//! into consideration that some APIs, such as [`AndroidApp::poll_events()`] are
48//! explicitly documented to only be usable from your `android_main()` thread.
49//!
50//! # Main Thread Initialization
51//!
52//! Before `android_main()` is called, the following application state
53//! is also initialized:
54//!
55//! 1. An I/O thread is spawned that will handle redirecting standard input
56//!    and output to the Android log, visible via `logcat`.
57//! 2. A `JavaVM` and `Activity` instance will be associated with the [`ndk_context`] crate
58//!    so that other, independent, Rust crates are able to find a JavaVM
59//!    for making JNI calls.
60//! 3. The `JavaVM` will be attached to the native thread
61//! 4. A [Looper] is attached to the Rust native thread.
62//!
63//!
64//! These are undone after `android_main()` returns
65//!
66//! # Android Extensible Enums
67//!
68//! There are numerous enums in the `android-activity` API which are effectively
69//! bindings to enums declared in the Android SDK which need to be considered
70//! _runtime_ extensible.
71//!
72//! Any enum variants that come from the Android SDK may be extended in future
73//! versions of Android and your code could be exposed to new variants if you
74//! build an application that might be installed on new versions of Android.
75//!
76//! This crate follows a convention of adding a hidden `__Unknown(u32)` variant
77//! to these enum to ensure we can always do lossless conversions between the
78//! integers from the SDK and our corresponding Rust enums. This can be
79//! important in case you need to pass certain variants back to the SDK
80//! regardless of whether you knew about that variants specific semantics at
81//! compile time.
82//!
83//! You should never include this `__Unknown(u32)` variant within any exhaustive
84//! pattern match and should instead treat the enums like `#[non_exhaustive]`
85//! enums that require you to add a catch-all for any `unknown => {}` values.
86//!
87//! Any code that would exhaustively include the `__Unknown(u32)` variant when
88//! pattern matching can not be guaranteed to be forwards compatible with new
89//! releases of `android-activity` which may add new Rust variants to these
90//! enums without requiring a breaking semver bump.
91//!
92//! You can (infallibly) convert these enums to and from primitive `u32` values
93//! using `.into()`:
94//!
95//! For example, here is how you could ensure forwards compatibility with both
96//! compile-time and runtime extensions of a `SomeEnum` enum:
97//!
98//! ```rust
99//! match some_enum {
100//!     SomeEnum::Foo => {},
101//!     SomeEnum::Bar => {},
102//!     unhandled => {
103//!         let sdk_val: u32 = unhandled.into();
104//!         println!("Unhandled enum variant {some_enum:?} has SDK value: {sdk_val}");
105//!     }
106//! }
107//! ```
108//!
109//! [`Activity`]: https://developer.android.com/reference/android/app/Activity
110//! [`NativeActivity`]: https://developer.android.com/reference/android/app/NativeActivity
111//! [ndk_concepts]: https://developer.android.com/ndk/guides/concepts#naa
112//! [`GameActivity`]: https://developer.android.com/games/agdk/integrate-game-activity
113//! [Looper]: https://developer.android.com/reference/android/os/Looper
114
115#![deny(clippy::manual_let_else)]
116
117use std::hash::Hash;
118use std::sync::Arc;
119use std::sync::RwLock;
120use std::time::Duration;
121
122use input::KeyCharacterMap;
123use libc::c_void;
124use ndk::asset::AssetManager;
125use ndk::native_window::NativeWindow;
126
127use bitflags::bitflags;
128
129#[cfg(not(target_os = "android"))]
130compile_error!("android-activity only supports compiling for Android");
131
132#[cfg(all(feature = "game-activity", feature = "native-activity"))]
133compile_error!(
134    r#"The "game-activity" and "native-activity" features cannot be enabled at the same time"#
135);
136#[cfg(all(
137    not(any(feature = "game-activity", feature = "native-activity")),
138    not(doc)
139))]
140compile_error!(
141    r#"Either "game-activity" or "native-activity" must be enabled as features
142
143If you have set one of these features then this error indicates that Cargo is trying to
144link together multiple implementations of android-activity (with incompatible versions)
145which is not supported.
146
147Since android-activity is responsible for the `android_main` entrypoint of your application
148then there can only be a single implementation of android-activity linked with your application.
149
150You can use `cargo tree` (e.g. via `cargo ndk -t arm64-v8a tree`) to identify why multiple
151versions have been resolved.
152
153You may need to add a `[patch]` into your Cargo.toml to ensure a specific version of
154android-activity is used across all of your application's crates."#
155);
156
157#[cfg_attr(any(feature = "native-activity", doc), path = "native_activity/mod.rs")]
158#[cfg_attr(any(feature = "game-activity", doc), path = "game_activity/mod.rs")]
159pub(crate) mod activity_impl;
160
161pub mod error;
162use error::Result;
163
164pub mod input;
165
166mod config;
167pub use config::ConfigurationRef;
168
169mod util;
170
171mod jni_utils;
172
173/// A rectangle with integer edge coordinates. Used to represent window insets, for example.
174#[derive(Clone, Debug, Default, Eq, PartialEq)]
175pub struct Rect {
176    pub left: i32,
177    pub top: i32,
178    pub right: i32,
179    pub bottom: i32,
180}
181
182impl Rect {
183    /// An empty `Rect` with all components set to zero.
184    pub fn empty() -> Self {
185        Self {
186            left: 0,
187            top: 0,
188            right: 0,
189            bottom: 0,
190        }
191    }
192}
193
194impl From<Rect> for ndk_sys::ARect {
195    fn from(rect: Rect) -> Self {
196        Self {
197            left: rect.left,
198            right: rect.right,
199            top: rect.top,
200            bottom: rect.bottom,
201        }
202    }
203}
204
205impl From<ndk_sys::ARect> for Rect {
206    fn from(arect: ndk_sys::ARect) -> Self {
207        Self {
208            left: arect.left,
209            right: arect.right,
210            top: arect.top,
211            bottom: arect.bottom,
212        }
213    }
214}
215
216pub use activity_impl::StateLoader;
217pub use activity_impl::StateSaver;
218
219/// An application event delivered during [`AndroidApp::poll_events`]
220#[non_exhaustive]
221#[derive(Debug)]
222pub enum MainEvent<'a> {
223    /// New input events are available via [`AndroidApp::input_events_iter()`]
224    ///
225    /// _Note: Even if more input is received this event will not be resent
226    /// until [`AndroidApp::input_events_iter()`] has been called, which enables
227    /// applications to batch up input processing without there being lots of
228    /// redundant event loop wake ups._
229    ///
230    /// [`AndroidApp::input_events_iter()`]: AndroidApp::input_events_iter
231    InputAvailable,
232
233    /// Command from main thread: a new [`NativeWindow`] is ready for use.  Upon
234    /// receiving this command, [`AndroidApp::native_window()`] will return the new window
235    #[non_exhaustive]
236    InitWindow {},
237
238    /// Command from main thread: the existing [`NativeWindow`] needs to be
239    /// terminated.  Upon receiving this command, [`AndroidApp::native_window()`] still
240    /// returns the existing window; after returning from the [`AndroidApp::poll_events()`]
241    /// callback then [`AndroidApp::native_window()`] will return `None`.
242    #[non_exhaustive]
243    TerminateWindow {},
244
245    // TODO: include the prev and new size in the event
246    /// Command from main thread: the current [`NativeWindow`] has been resized.
247    /// Please redraw with its new size.
248    #[non_exhaustive]
249    WindowResized {},
250
251    /// Command from main thread: the current [`NativeWindow`] needs to be redrawn.
252    /// You should redraw the window before the [`AndroidApp::poll_events()`]
253    /// callback returns in order to avoid transient drawing glitches.
254    #[non_exhaustive]
255    RedrawNeeded {},
256
257    /// Command from main thread: the content area of the window has changed,
258    /// such as from the soft input window being shown or hidden.  You can
259    /// get the new content rect by calling [`AndroidApp::content_rect()`]
260    #[non_exhaustive]
261    ContentRectChanged {},
262
263    /// Command from main thread: the app's activity window has gained
264    /// input focus.
265    GainedFocus,
266
267    /// Command from main thread: the app's activity window has lost
268    /// input focus.
269    LostFocus,
270
271    /// Command from main thread: the current device configuration has changed.
272    /// You can get a copy of the latest [`ndk::configuration::Configuration`] by calling
273    /// [`AndroidApp::config()`]
274    #[non_exhaustive]
275    ConfigChanged {},
276
277    /// Command from main thread: the system is running low on memory.
278    /// Try to reduce your memory use.
279    LowMemory,
280
281    /// Command from main thread: the app's activity has been started.
282    Start,
283
284    /// Command from main thread: the app's activity has been resumed.
285    #[non_exhaustive]
286    Resume { loader: StateLoader<'a> },
287
288    /// Command from main thread: the app should generate a new saved state
289    /// for itself, to restore from later if needed.  If you have saved state,
290    /// allocate it with malloc and place it in android_app.savedState with
291    /// the size in android_app.savedStateSize.  The will be freed for you
292    /// later.
293    #[non_exhaustive]
294    SaveState { saver: StateSaver<'a> },
295
296    /// Command from main thread: the app's activity has been paused.
297    Pause,
298
299    /// Command from main thread: the app's activity has been stopped.
300    Stop,
301
302    /// Command from main thread: the app's activity is being destroyed,
303    /// and waiting for the app thread to clean up and exit before proceeding.
304    Destroy,
305
306    /// Command from main thread: the app's insets have changed.
307    #[non_exhaustive]
308    InsetsChanged {},
309}
310
311/// An event delivered during [`AndroidApp::poll_events`]
312#[derive(Debug)]
313#[non_exhaustive]
314pub enum PollEvent<'a> {
315    Wake,
316    Timeout,
317    Main(MainEvent<'a>),
318}
319
320/// Indicates whether an application has handled or ignored an event
321///
322/// If an event is not handled by an application then some default handling may happen.
323#[derive(Debug, Clone, Copy, PartialEq, Eq)]
324pub enum InputStatus {
325    Handled,
326    Unhandled,
327}
328
329use activity_impl::AndroidAppInner;
330pub use activity_impl::AndroidAppWaker;
331
332bitflags! {
333    /// Flags for [`AndroidApp::set_window_flags`]
334    /// as per the [android.view.WindowManager.LayoutParams Java API](https://developer.android.com/reference/android/view/WindowManager.LayoutParams)
335    #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
336    pub struct WindowManagerFlags: u32 {
337        /// As long as this window is visible to the user, allow the lock
338        /// screen to activate while the screen is on.  This can be used
339        /// independently, or in combination with
340        /// [`Self::KEEP_SCREEN_ON`] and/or [`Self::SHOW_WHEN_LOCKED`]
341        const ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001;
342
343        /// Everything behind this window will be dimmed. */
344        const DIM_BEHIND = 0x00000002;
345
346        /// Blur everything behind this window.
347        #[deprecated = "Blurring is no longer supported"]
348        const BLUR_BEHIND = 0x00000004;
349
350        /// This window won't ever get key input focus, so the
351        /// user can not send key or other button events to it.  Those will
352        /// instead go to whatever focusable window is behind it.  This flag
353        /// will also enable [`Self::NOT_TOUCH_MODAL`] whether or not
354        /// that is explicitly set.
355        ///
356        /// Setting this flag also implies that the window will not need to
357        /// interact with
358        /// a soft input method, so it will be Z-ordered and positioned
359        /// independently of any active input method (typically this means it
360        /// gets Z-ordered on top of the input method, so it can use the full
361        /// screen for its content and cover the input method if needed.  You
362        /// can use [`Self::ALT_FOCUSABLE_IM`] to modify this
363        /// behavior.
364        const NOT_FOCUSABLE = 0x00000008;
365
366        /// This window can never receive touch events.
367        const NOT_TOUCHABLE = 0x00000010;
368
369        /// Even when this window is focusable (if
370        /// [`Self::NOT_FOCUSABLE`] is not set), allow any pointer
371        /// events outside of the window to be sent to the windows behind it.
372        /// Otherwise it will consume all pointer events itself, regardless of
373        /// whether they are inside of the window.
374        const NOT_TOUCH_MODAL = 0x00000020;
375
376        /// When set, if the device is asleep when the touch
377        /// screen is pressed, you will receive this first touch event.  Usually
378        /// the first touch event is consumed by the system since the user can
379        /// not see what they are pressing on.
380        #[deprecated]
381        const TOUCHABLE_WHEN_WAKING = 0x00000040;
382
383        /// As long as this window is visible to the user, keep
384        /// the device's screen turned on and bright.
385        const KEEP_SCREEN_ON = 0x00000080;
386
387        /// Place the window within the entire screen, ignoring
388        /// decorations around the border (such as the status bar).  The
389        /// window must correctly position its contents to take the screen
390        /// decoration into account.
391        const LAYOUT_IN_SCREEN = 0x00000100;
392
393        /// Allows the window to extend outside of the screen.
394        const LAYOUT_NO_LIMITS = 0x00000200;
395
396        /// Hide all screen decorations (such as the status
397        /// bar) while this window is displayed.  This allows the window to
398        /// use the entire display space for itself -- the status bar will
399        /// be hidden when an app window with this flag set is on the top
400        /// layer. A fullscreen window will ignore a value of
401        /// [`Self::SOFT_INPUT_ADJUST_RESIZE`] the window will stay
402        /// fullscreen and will not resize.
403        const FULLSCREEN = 0x00000400;
404
405        /// Override [`Self::FULLSCREEN`] and force the
406        /// screen decorations (such as the status bar) to be shown.
407        const FORCE_NOT_FULLSCREEN = 0x00000800;
408        /// Turn on dithering when compositing this window to
409        /// the screen.
410        #[deprecated="This flag is no longer used"]
411        const DITHER = 0x00001000;
412
413        /// Treat the content of the window as secure, preventing
414        /// it from appearing in screenshots or from being viewed on non-secure
415        /// displays.
416        const SECURE = 0x00002000;
417
418        /// A special mode where the layout parameters are used
419        /// to perform scaling of the surface when it is composited to the
420        /// screen.
421        const SCALED = 0x00004000;
422
423        /// Intended for windows that will often be used when the user is
424        /// holding the screen against their face, it will aggressively
425        /// filter the event stream to prevent unintended presses in this
426        /// situation that may not be desired for a particular window, when
427        /// such an event stream is detected, the application will receive
428        /// a `AMOTION_EVENT_ACTION_CANCEL` to indicate this so
429        /// applications can handle this accordingly by taking no action on
430        /// the event until the finger is released.
431        const IGNORE_CHEEK_PRESSES = 0x00008000;
432
433        /// A special option only for use in combination with
434        /// [`Self::LAYOUT_IN_SCREEN`].  When requesting layout in
435        /// the screen your window may appear on top of or behind screen decorations
436        /// such as the status bar.  By also including this flag, the window
437        /// manager will report the inset rectangle needed to ensure your
438        /// content is not covered by screen decorations.
439        const LAYOUT_INSET_DECOR = 0x00010000;
440
441        /// Invert the state of [`Self::NOT_FOCUSABLE`] with
442        /// respect to how this window interacts with the current method.
443        /// That is, if [`Self::NOT_FOCUSABLE`] is set and this flag is set,
444        /// then the window will behave as if it needs to interact with the
445        /// input method and thus be placed behind/away from it; if
446        /// [`Self::NOT_FOCUSABLE`] is not set and this flag is set,
447        /// then the window will behave as if it doesn't need to interact
448        /// with the input method and can be placed to use more space and
449        /// cover the input method.
450        const ALT_FOCUSABLE_IM = 0x00020000;
451
452        /// If you have set [`Self::NOT_TOUCH_MODAL`], you
453        /// can set this flag to receive a single special MotionEvent with
454        /// the action
455        /// `AMOTION_EVENT_ACTION_OUTSIDE` for
456        /// touches that occur outside of your window.  Note that you will not
457        /// receive the full down/move/up gesture, only the location of the
458        /// first down as an `AMOTION_EVENT_ACTION_OUTSIDE`.
459        const WATCH_OUTSIDE_TOUCH = 0x00040000;
460
461        /// Special flag to let windows be shown when the screen
462        /// is locked. This will let application windows take precedence over
463        /// key guard or any other lock screens. Can be used with
464        /// [`Self::KEEP_SCREEN_ON`] to turn screen on and display
465        /// windows directly before showing the key guard window.  Can be used with
466        /// [`Self::DISMISS_KEYGUARD`] to automatically fully
467        /// dismiss non-secure key guards.  This flag only applies to the top-most
468        /// full-screen window.
469        const SHOW_WHEN_LOCKED = 0x00080000;
470
471        /// Ask that the system wallpaper be shown behind
472        /// your window.  The window surface must be translucent to be able
473        /// to actually see the wallpaper behind it; this flag just ensures
474        /// that the wallpaper surface will be there if this window actually
475        /// has translucent regions.
476        const SHOW_WALLPAPER = 0x00100000;
477
478        /// When set as a window is being added or made
479        /// visible, once the window has been shown then the system will
480        /// poke the power manager's user activity (as if the user had woken
481        /// up the device) to turn the screen on.
482        const TURN_SCREEN_ON = 0x00200000;
483
484        /// When set the window will cause the key guard to
485        /// be dismissed, only if it is not a secure lock key guard.  Because such
486        /// a key guard is not needed for security, it will never re-appear if
487        /// the user navigates to another window (in contrast to
488        /// [`Self::SHOW_WHEN_LOCKED`], which will only temporarily
489        /// hide both secure and non-secure key guards but ensure they reappear
490        /// when the user moves to another UI that doesn't hide them).
491        /// If the key guard is currently active and is secure (requires an
492        /// unlock pattern) then the user will still need to confirm it before
493        /// seeing this window, unless [`Self::SHOW_WHEN_LOCKED`] has
494        /// also been set.
495        const DISMISS_KEYGUARD = 0x00400000;
496    }
497}
498
499/// The top-level state and interface for a native Rust application
500///
501/// `AndroidApp` provides an interface to query state for the application as
502/// well as monitor events, such as lifecycle and input events, that are
503/// marshalled between the Java thread that owns the `Activity` and the native
504/// thread that runs the `android_main()` code.
505///
506/// # Cheaply Clonable [`AndroidApp`]
507///
508/// [`AndroidApp`] is intended to be something that can be cheaply passed around
509/// by referenced within an application. It is reference counted and can be
510/// cheaply cloned.
511///
512/// # `Send` and `Sync` [`AndroidApp`]
513///
514/// Although an [`AndroidApp`] implements `Send` and `Sync` you do need to take
515/// into consideration that some APIs, such as [`AndroidApp::poll_events()`] are
516/// explicitly documented to only be usable from your `android_main()` thread.
517///
518#[derive(Debug, Clone)]
519pub struct AndroidApp {
520    pub(crate) inner: Arc<RwLock<AndroidAppInner>>,
521}
522
523impl PartialEq for AndroidApp {
524    fn eq(&self, other: &Self) -> bool {
525        Arc::ptr_eq(&self.inner, &other.inner)
526    }
527}
528impl Eq for AndroidApp {}
529
530impl Hash for AndroidApp {
531    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
532        Arc::as_ptr(&self.inner).hash(state);
533    }
534}
535
536impl AndroidApp {
537    /// Queries the current [`NativeWindow`] for the application.
538    ///
539    /// This will only return `Some(window)` between
540    /// [`MainEvent::InitWindow`] and [`MainEvent::TerminateWindow`]
541    /// events.
542    pub fn native_window(&self) -> Option<NativeWindow> {
543        self.inner.read().unwrap().native_window()
544    }
545
546    /// Returns a pointer to the Java Virtual Machine, for making JNI calls
547    ///
548    /// This returns a pointer to the Java Virtual Machine which can be used
549    /// with the [`jni`] crate (or similar crates) to make JNI calls that bridge
550    /// between native Rust code and Java/Kotlin code running within the JVM.
551    ///
552    /// If you use the [`jni`] crate you can wrap this as a [`JavaVM`] via:
553    /// ```ignore
554    /// # use jni::JavaVM;
555    /// # let app: AndroidApp = todo!();
556    /// let vm = unsafe { JavaVM::from_raw(app.vm_as_ptr()) };
557    /// ```
558    ///
559    /// [`jni`]: https://crates.io/crates/jni
560    /// [`JavaVM`]: https://docs.rs/jni/latest/jni/struct.JavaVM.html
561    pub fn vm_as_ptr(&self) -> *mut c_void {
562        self.inner.read().unwrap().vm_as_ptr()
563    }
564
565    /// Returns a JNI object reference for this application's JVM `Activity` as a pointer
566    ///
567    /// If you use the [`jni`] crate you can wrap this as an object reference via:
568    /// ```ignore
569    /// # use jni::objects::JObject;
570    /// # let app: AndroidApp = todo!();
571    /// let activity = unsafe { JObject::from_raw(app.activity_as_ptr()) };
572    /// ```
573    ///
574    /// # JNI Safety
575    ///
576    /// Note that the object reference will be a JNI global reference, not a
577    /// local reference and it should not be deleted. Don't wrap the reference
578    /// in an [`AutoLocal`] which would try to explicitly delete the reference
579    /// when dropped. Similarly, don't wrap the reference as a [`GlobalRef`]
580    /// which would also try to explicitly delete the reference when dropped.
581    ///
582    /// [`jni`]: https://crates.io/crates/jni
583    /// [`AutoLocal`]: https://docs.rs/jni/latest/jni/objects/struct.AutoLocal.html
584    /// [`GlobalRef`]: https://docs.rs/jni/latest/jni/objects/struct.GlobalRef.html
585    pub fn activity_as_ptr(&self) -> *mut c_void {
586        self.inner.read().unwrap().activity_as_ptr()
587    }
588
589    /// Polls for any events associated with this [AndroidApp] and processes those events
590    /// (such as lifecycle events) via the given `callback`.
591    ///
592    /// It's important to use this API for polling, and not call [`ALooper_pollAll`] directly since
593    /// some events require pre- and post-processing either side of the callback. For correct
594    /// behavior events should be handled immediately, before returning from the callback and
595    /// not simply queued for batch processing later. For example the existing [`NativeWindow`]
596    /// is accessible during a [`MainEvent::TerminateWindow`] callback and will be
597    /// set to `None` once the callback returns, and this is also synchronized with the Java
598    /// main thread. The [`MainEvent::SaveState`] event is also synchronized with the
599    /// Java main thread.
600    ///
601    /// # Panics
602    ///
603    /// This must only be called from your `android_main()` thread and it may panic if called
604    /// from another thread.
605    ///
606    /// [`ALooper_pollAll`]: ndk::looper::ThreadLooper::poll_all
607    pub fn poll_events<F>(&self, timeout: Option<Duration>, callback: F)
608    where
609        F: FnMut(PollEvent<'_>),
610    {
611        self.inner.read().unwrap().poll_events(timeout, callback);
612    }
613
614    /// Creates a means to wake up the main loop while it is blocked waiting for
615    /// events within [`AndroidApp::poll_events()`].
616    pub fn create_waker(&self) -> AndroidAppWaker {
617        self.inner.read().unwrap().create_waker()
618    }
619
620    /// Returns a (cheaply clonable) reference to this application's [`ndk::configuration::Configuration`]
621    pub fn config(&self) -> ConfigurationRef {
622        self.inner.read().unwrap().config()
623    }
624
625    /// Queries the current content rectangle of the window; this is the area where the
626    /// window's content should be placed to be seen by the user.
627    pub fn content_rect(&self) -> Rect {
628        self.inner.read().unwrap().content_rect()
629    }
630
631    /// Queries the Asset Manager instance for the application.
632    ///
633    /// Use this to access binary assets bundled inside your application's .apk file.
634    pub fn asset_manager(&self) -> AssetManager {
635        self.inner.read().unwrap().asset_manager()
636    }
637
638    /// Change the window flags of the given activity.
639    ///
640    /// Note that some flags must be set before the window decoration is created,
641    /// see
642    /// `<https://developer.android.com/reference/android/view/Window#setFlags(int,%20int)>`.
643    pub fn set_window_flags(
644        &self,
645        add_flags: WindowManagerFlags,
646        remove_flags: WindowManagerFlags,
647    ) {
648        self.inner
649            .write()
650            .unwrap()
651            .set_window_flags(add_flags, remove_flags);
652    }
653
654    /// Enable additional input axis
655    ///
656    /// To reduce overhead, by default only [`input::Axis::X`] and [`input::Axis::Y`] are enabled
657    /// and other axis should be enabled explicitly.
658    pub fn enable_motion_axis(&self, axis: input::Axis) {
659        self.inner.write().unwrap().enable_motion_axis(axis);
660    }
661
662    /// Disable input axis
663    ///
664    /// To reduce overhead, by default only [`input::Axis::X`] and [`input::Axis::Y`] are enabled
665    /// and other axis should be enabled explicitly.
666    pub fn disable_motion_axis(&self, axis: input::Axis) {
667        self.inner.write().unwrap().disable_motion_axis(axis);
668    }
669
670    /// Explicitly request that the current input method's soft input area be
671    /// shown to the user, if needed.
672    ///
673    /// Call this if the user interacts with your view in such a way that they
674    /// have expressed they would like to start performing input into it.
675    pub fn show_soft_input(&self, show_implicit: bool) {
676        self.inner.read().unwrap().show_soft_input(show_implicit);
677    }
678
679    /// Request to hide the soft input window from the context of the window
680    /// that is currently accepting input.
681    ///
682    /// This should be called as a result of the user doing some action that
683    /// fairly explicitly requests to have the input window hidden.
684    pub fn hide_soft_input(&self, hide_implicit_only: bool) {
685        self.inner
686            .read()
687            .unwrap()
688            .hide_soft_input(hide_implicit_only);
689    }
690
691    /// Fetch the current input text state, as updated by any active IME.
692    pub fn text_input_state(&self) -> input::TextInputState {
693        self.inner.read().unwrap().text_input_state()
694    }
695
696    /// Forward the given input text `state` to any active IME.
697    pub fn set_text_input_state(&self, state: input::TextInputState) {
698        self.inner.read().unwrap().set_text_input_state(state);
699    }
700
701    /// Get an exclusive, lending iterator over buffered input events
702    ///
703    /// Applications are expected to call this in-sync with their rendering or
704    /// in response to a [`MainEvent::InputAvailable`] event being delivered.
705    ///
706    /// _**Note:** your application is will only be delivered a single
707    /// [`MainEvent::InputAvailable`] event between calls to this API._
708    ///
709    /// To reduce overhead, by default, only [`input::Axis::X`] and [`input::Axis::Y`] are enabled
710    /// and other axis should be enabled explicitly via [`Self::enable_motion_axis`].
711    ///
712    /// This isn't the most ergonomic iteration API since we can't return a standard `Iterator`:
713    /// - This API returns a lending iterator may borrow from the internal buffer
714    ///   of pending events without copying them.
715    /// - For each event we want to ensure the application reports whether the
716    ///   event was handled.
717    ///
718    /// # Example
719    /// Code to iterate all pending input events would look something like this:
720    ///
721    /// ```rust
722    /// match app.input_events_iter() {
723    ///     Ok(mut iter) => {
724    ///         loop {
725    ///             let read_input = iter.next(|event| {
726    ///                 let handled = match event {
727    ///                     InputEvent::KeyEvent(key_event) => {
728    ///                         // Snip
729    ///                     }
730    ///                     InputEvent::MotionEvent(motion_event) => {
731    ///                         // Snip
732    ///                     }
733    ///                     event => {
734    ///                         // Snip
735    ///                     }
736    ///                 };
737    ///
738    ///                 handled
739    ///             });
740    ///
741    ///             if !read_input {
742    ///                 break;
743    ///             }
744    ///         }
745    ///     }
746    ///     Err(err) => {
747    ///         log::error!("Failed to get input events iterator: {err:?}");
748    ///     }
749    /// }
750    /// ```
751    ///
752    /// # Panics
753    ///
754    /// This must only be called from your `android_main()` thread and it may panic if called
755    /// from another thread.
756    pub fn input_events_iter(&self) -> Result<input::InputIterator> {
757        let receiver = {
758            let guard = self.inner.read().unwrap();
759            guard.input_events_receiver()?
760        };
761
762        Ok(input::InputIterator {
763            inner: receiver.into(),
764        })
765    }
766
767    /// Lookup the [`KeyCharacterMap`] for the given input `device_id`
768    ///
769    /// Use [`KeyCharacterMap::get`] to map key codes + meta state into unicode characters
770    /// or dead keys that compose with the next key.
771    ///
772    /// # Example
773    ///
774    /// Code to handle unicode character mapping as well as combining dead keys could look some thing like:
775    ///
776    /// ```rust
777    /// let mut combining_accent = None;
778    /// // Snip
779    ///
780    /// let combined_key_char = if let Ok(map) = app.device_key_character_map(device_id) {
781    ///     match map.get(key_event.key_code(), key_event.meta_state()) {
782    ///         Ok(KeyMapChar::Unicode(unicode)) => {
783    ///             let combined_unicode = if let Some(accent) = combining_accent {
784    ///                 match map.get_dead_char(accent, unicode) {
785    ///                     Ok(Some(key)) => {
786    ///                         info!("KeyEvent: Combined '{unicode}' with accent '{accent}' to give '{key}'");
787    ///                         Some(key)
788    ///                     }
789    ///                     Ok(None) => None,
790    ///                     Err(err) => {
791    ///                         log::error!("KeyEvent: Failed to combine 'dead key' accent '{accent}' with '{unicode}': {err:?}");
792    ///                         None
793    ///                     }
794    ///                 }
795    ///             } else {
796    ///                 info!("KeyEvent: Pressed '{unicode}'");
797    ///                 Some(unicode)
798    ///             };
799    ///             combining_accent = None;
800    ///             combined_unicode.map(|unicode| KeyMapChar::Unicode(unicode))
801    ///         }
802    ///         Ok(KeyMapChar::CombiningAccent(accent)) => {
803    ///             info!("KeyEvent: Pressed 'dead key' combining accent '{accent}'");
804    ///             combining_accent = Some(accent);
805    ///             Some(KeyMapChar::CombiningAccent(accent))
806    ///         }
807    ///         Ok(KeyMapChar::None) => {
808    ///             info!("KeyEvent: Pressed non-unicode key");
809    ///             combining_accent = None;
810    ///             None
811    ///         }
812    ///         Err(err) => {
813    ///             log::error!("KeyEvent: Failed to get key map character: {err:?}");
814    ///             combining_accent = None;
815    ///             None
816    ///         }
817    ///     }
818    /// } else {
819    ///     None
820    /// };
821    /// ```
822    ///
823    /// # Errors
824    ///
825    /// Since this API needs to use JNI internally to call into the Android JVM it may return
826    /// a [`error::AppError::JavaError`] in case there is a spurious JNI error or an exception
827    /// is caught.
828    pub fn device_key_character_map(&self, device_id: i32) -> Result<KeyCharacterMap> {
829        Ok(self
830            .inner
831            .read()
832            .unwrap()
833            .device_key_character_map(device_id)?)
834    }
835
836    /// The user-visible SDK version of the framework
837    ///
838    /// Also referred to as [`Build.VERSION_CODES`](https://developer.android.com/reference/android/os/Build.VERSION_CODES)
839    pub fn sdk_version() -> i32 {
840        let mut prop = android_properties::getprop("ro.build.version.sdk");
841        if let Some(val) = prop.value() {
842            val.parse::<i32>()
843                .expect("Failed to parse ro.build.version.sdk property")
844        } else {
845            panic!("Couldn't read ro.build.version.sdk system property");
846        }
847    }
848
849    /// Path to this application's internal data directory
850    pub fn internal_data_path(&self) -> Option<std::path::PathBuf> {
851        self.inner.read().unwrap().internal_data_path()
852    }
853
854    /// Path to this application's external data directory
855    pub fn external_data_path(&self) -> Option<std::path::PathBuf> {
856        self.inner.read().unwrap().external_data_path()
857    }
858
859    /// Path to the directory containing the application's OBB files (if any).
860    pub fn obb_path(&self) -> Option<std::path::PathBuf> {
861        self.inner.read().unwrap().obb_path()
862    }
863}
864
865#[test]
866fn test_app_is_send_sync() {
867    fn needs_send_sync<T: Send + Sync>() {}
868    needs_send_sync::<AndroidApp>();
869}