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}