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 applications on
4//! Android, supporting multiple [`Activity`] base classes. It's comparable to
5//! [`android_native_app_glue.c`][ndk_concepts] for C/C++ applications.
6//!
7//! Currently the crate supports two `Activity` base classes:
8//! 1. [`NativeActivity`] - Built in to Android, this doesn't require compiling
9//! any Java or Kotlin code.
10//! 2. [`GameActivity`] - From the Android Game Development Kit, it has more
11//! sophisticated input handling support than `NativeActivity`.
12//! `GameActivity` is also based on the `AndroidAppCompat` class which can
13//! help with supporting a wider range of devices.
14//!
15//! Standalone applications based on this crate need to be built as `cdylib`
16//! libraries, like:
17//! ```toml
18//! [lib]
19//! crate-type=["cdylib"]
20//! ```
21//!
22//! ## Lifecycle of an Activity
23//!
24//! Keep in mind that Android's application programming model is based around
25//! the
26//! [lifecycle](https://developer.android.com/guide/components/activities/activity-lifecycle)
27//! of [`Activity`] and [`Service`] components, and not the lifecycle of the
28//! application process.
29//!
30//! An Android application may have multiple [`Activity`] and [`Service`]
31//! instances created and destroyed over its lifetime, and each of these
32//! [`Activity`] and [`Service`] instances will have their own lifecycles that
33//! are independent from the lifecycle of the application process.
34//!
35//! See the Android SDK [activity lifecycle
36//! documentation](https://developer.android.com/guide/components/activities/activity-lifecycle)
37//! for more details on the [`Activity`] lifecycle.
38//!
39//! Although native applications will typically only have a single instance of
40//! [`NativeActivity`] or [`GameActivity`], it's possible for these activities
41//! to be created and destroyed multiple times within the lifetime of your
42//! application process.
43//!
44//! Although [`NativeActivity`] and [`GameActivity`] were historically designed
45//! for full-screen games and based on the assumption that there would only be a
46//! single instance of these activities, it is good to keep in mind that Android
47//! itself makes no such assumption. It's very common for non-native Android
48//! applications to be tracking multiple `Activity` instances at the same time.
49//!
50//! The `android-activity` crate is designed to be robust to multiple `Activity`
51//! instances being created and destroyed over the lifetime of the application
52//! process.
53//!
54//! ## Entrypoints
55//!
56//! There are currently two supported entrypoints for an `android-activity`
57//! application:
58//!
59//! 1. `android_on_create` **(optional)** - This runs early, on the Java main /
60//! UI thread, during `Activity.onCreate()`. It can be a good place to
61//! initialize logging and JNI bindings.
62//! 2. `android_main` **(required)** - This run a dedicated main loop thread for
63//! handling lifecycle and input events for your `Activity`.
64//!
65//! **Important**: Your `android-activity` entrypoints are tied to the lifecycle
66//! of your native **`Activity`** (i.e. [`NativeActivity`] or [`GameActivity`])
67//! and not the lifecycle of your application process! This means that if your
68//! `Activity` is destroyed and re-created (e.g. depending on how your
69//! application handles configuration changes) then these entrypoints may be
70//! called multiple times, for each `Activity` instance.
71//!
72//! #### Your AndroidManifest `configureChanges` state affects Activity re-creation
73//!
74//! Beware that, by default, certain configuration changes (e.g. device
75//! rotation) will cause the Android system to destroy and re-create your
76//! `Activity`, which will lead to a [`MainEvent::Destroy`] event being sent to
77//! your `android_main()` thread and then `android_main()` will be called again
78//! as a new native `Activity` instance is created.
79//!
80//! Since this can be awkward to handle, it is common practice to set the
81//! `android:configChanges` property to indicate that your application can
82//! handle these changes at runtime via events instead.
83//!
84//! **Example**:
85//!
86//! Here's how you can set `android:configChanges` for your `Activity` in your
87//! AndroidManifest.xml:
88//!
89//! ```xml
90//! <activity
91//! android:name="android.app.NativeActivity"
92//! android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
93//! android:label="NativeActivity Example"
94//! android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
95//! android:exported="true">
96//!
97//! <!-- ... -->
98//! </activity>
99//! ```
100//!
101//! ### onCreate entrypoint: `android_on_create` (optional)
102//!
103//! The `android_on_create` entry point will be called from the Java main
104//! thread, within the `Activity`'s `onCreate` method, before the `android_main`
105//! entry point is called.
106//!
107//! This must be an exported, unmangled, `"Rust"` ABI function with the
108//! signature `fn android_on_create(state: &OnCreateState)`.
109//!
110//! The easiest way to achieve this is with `#[unsafe(no_mangle)]` like this:
111//! ```no_run
112//! #[unsafe(no_mangle)]
113//! fn android_on_create(state: &android_activity::OnCreateState) {
114//! // Initialization code here
115//! }
116//! ```
117//! (Note `extern "Rust"` is the default ABI)
118//!
119//! **I/O redirection**: Before `android_on_create()` is called an I/O thread is
120//! spawned that will handle redirecting standard input and output to the
121//! Android log, visible via `logcat`.
122//!
123//! [`OnCreateState`] provides access to the Java VM and a JNI reference to the
124//! `Activity` instance, as well as any saved state from a previous instance of
125//! the Activity.
126//!
127//! Due to the way JNI class loading works, this can be a convenient place to
128//! initialize JNI bindings because it's called while the `Activity`'s
129//! `onCreate` callback is on the stack, so the default class loader will be
130//! able to find the application's Java classes. See the Android
131//! [JNI tips](https://developer.android.com/ndk/guides/jni-tips#faq:-why-didnt-findclass-find-my-class)
132//! guide for more details on this.
133//!
134//! This can also be a good place to initialize logging, since it's called
135//! first.
136//!
137//! **Important**: This entrypoint must not block for a long time or do heavy
138//! work, since it's running on the Java main thread and will block the
139//! `Activity` from being created until it returns.
140//!
141//! Blocking the Java main thread for too long may cause an "Application Not
142//! Responding" (ANR) dialog to be shown to the user, and cause users to force
143//! close your application.
144//!
145//! **Panic behavior**: If `android_on_create` panics, the application will
146//! abort. This is because the callback runs within a native JNI callback where
147//! unwinding is not permitted. Ensure your initialization code either cannot
148//! panic or uses `catch_unwind` internally if you want to allow partial
149//! initialization failures.
150//!
151//! #### Example:
152//!
153//! ```no_run
154//! # use std::sync::OnceLock;
155//! # use android_activity::OnCreateState;
156//! # use jni::{JavaVM, objects::JObject};
157//! #[unsafe(no_mangle)]
158//! fn android_on_create(state: &OnCreateState) {
159//! static APP_ONCE: OnceLock<()> = OnceLock::new();
160//! APP_ONCE.get_or_init(|| {
161//! // Initialize logging...
162//! //
163//! // Remember, `android_on_create` may be called multiple times but, depending on
164//! // the crate, logger initialization may panic if attempted multiple times.
165//! });
166//! let vm = unsafe { JavaVM::from_raw(state.vm_as_ptr().cast()) };
167//! let activity = state.activity_as_ptr() as jni::sys::jobject;
168//! // Although the thread is implicitly already attached (we are inside an onCreate native method)
169//! // using `vm.attach_current_thread` here will use the existing attachment, give us an `&Env`
170//! // reference and also catch Java exceptions.
171//! if let Err(err) = vm.attach_current_thread(|env| -> jni::errors::Result<()> {
172//! // SAFETY:
173//! // - The `Activity` reference / pointer is at least valid until we return
174//! // - By creating a `Cast` we ensure we can't accidentally delete the reference
175//! let activity = unsafe { env.as_cast_raw::<JObject>(&activity)? };
176//!
177//! // Do something with the activity on the Java main thread...
178//! Ok(())
179//! }) {
180//! eprintln!("Failed to interact with Android SDK on Java main thread: {err:?}");
181//! }
182//! }
183//! ```
184//!
185//! ### Main loop thread entrypoint: `android_main` (required)
186//!
187//! Your application must always define an `android_main` function as an entry
188//! point for running a main loop thread for your Activity.
189//!
190//! This must be an exported, unmangled, `"Rust"` ABI function with the
191//! signature `fn android_main(app: AndroidApp)`.
192//!
193//! The easiest way to achieve this is with `#[unsafe(no_mangle)]` like this:
194//! ```no_run
195//! #[unsafe(no_mangle)]
196//! fn android_main(app: android_activity::AndroidApp) {
197//! // Main loop code here
198//! }
199//! ```
200//! (Note `extern "Rust"` is the default ABI)
201//!
202//! Once your application's `Activity` class has loaded and it calls `onCreate`
203//! then `android-activity` will spawn a dedicated thread to run your
204//! `android_main` function, separate from the Java thread that created the
205//! corresponding `Activity`.
206//!
207//! Before `android_main()` is called:
208//! - A `JavaVM` and
209//! [`android.content.Context`](https://developer.android.com/reference/android/content/Context)
210//! instance will be associated with the [`ndk_context`] crate so that other,
211//! independent, Rust crates are able to find a JavaVM for making JNI calls.
212//! - The `JavaVM` will be attached to the native thread (for JNI)
213//! - A [Looper] is attached to the Rust native thread.
214//!
215//! **Important:** This thread *must* call [`AndroidApp::poll_events()`]
216//! regularly in order to receive lifecycle and input events for the `Activity`.
217//! Some `Activity` lifecycle callbacks on the Java main thread will block until
218//! the next time `poll_events()` is called, so if you don't call
219//! `poll_events()` regularly you may trigger an ANR dialog and cause users to
220//! force close your application.
221//!
222//! **Important**: You should return from `android_main()` as soon as possible
223//! if you receive a [`MainEvent::Destroy`] event from `poll_events()`. Most
224//! [`AndroidApp`] methods will become a no-op after [`MainEvent::Destroy`] is
225//! received, since it no longer has an associated `Activity`.
226//!
227//! **Important**: Do *not* call `std::process::exit()` from your
228//! `android_main()` function since that will subvert the normal lifecycle of
229//! the `Activity` and other components. Keep in mind that code running in
230//! `android_main()` does not logically own the entire process since there may
231//! be other Android components (e.g. Services) running within the process.
232//!
233//! ## AndroidApp: State and Event Loop
234//!
235//! [`AndroidApp`] provides an interface to query state for the application as
236//! well as monitor events, such as lifecycle and input events for the
237//! associated native `Activity` instance.
238//!
239//! ### Cheaply Cloneable [`AndroidApp`]
240//!
241//! [`AndroidApp`] is intended to be something that can be cheaply passed around
242//! within an application. It is reference-counted and can be cheaply cloned.
243//!
244//! ### `Send` and `Sync` [`AndroidApp`] (**but...**)
245//!
246//! Although an [`AndroidApp`] implements `Send` and `Sync` you do need to take
247//! into consideration that some APIs, such as [`AndroidApp::poll_events()`] are
248//! explicitly documented to only be usable from your `android_main()` thread.
249//!
250//! ### No associated Activity after [`MainEvent::Destroy`]
251//!
252//! After you receive a [`MainEvent::Destroy`] event from `poll_events()` then
253//! the [`AndroidApp`] will no longer have an associated `Activity` and most of
254//! its methods will become no-ops. You should return from `android_main()` as
255//! soon as possible after receiving a `Destroy` event since your native
256//! `Activity` no longer exists.
257//!
258//! If a new [`Activity`] instance is created after that then a new
259//! [`AndroidApp`] will be created for that new [`Activity`] instance and sent
260//! to a new call to `android_main()`.
261//!
262//! **Important**: It's not recommended to store an [`AndroidApp`] as global
263//! static state and it should instead be passed around by reference within your
264//! application so it can be reliably dropped when the `Activity` is destroyed
265//! and you return from `android_main()`.
266//!
267//! # Android Extensible Enums
268//!
269//! There are numerous enums in the `android-activity` API which are effectively
270//! bindings to enums declared in the Android SDK which need to be considered
271//! _runtime_ extensible.
272//!
273//! Any enum variants that come from the Android SDK may be extended in future
274//! versions of Android and your code could be exposed to new variants if you
275//! build an application that might be installed on new versions of Android.
276//!
277//! This crate follows a convention of adding a hidden `__Unknown(u32)` variant
278//! to these enums to ensure we can always do lossless conversions between the
279//! integers from the SDK and our corresponding Rust enums. This can be
280//! important in case you need to pass certain variants back to the SDK
281//! regardless of whether you knew about that variants specific semantics at
282//! compile time.
283//!
284//! You should never include this `__Unknown(u32)` variant within any exhaustive
285//! pattern match and should instead treat the enums like `#[non_exhaustive]`
286//! enums that require you to add a catch-all for any `unknown => {}` values.
287//!
288//! Any code that would exhaustively include the `__Unknown(u32)` variant when
289//! pattern matching can not be guaranteed to be forwards compatible with new
290//! releases of `android-activity` which may add new Rust variants to these
291//! enums without requiring a breaking semver bump.
292//!
293//! You can (infallibly) convert these enums to and from primitive `u32` values
294//! using `.into()`:
295//!
296//! For example, here is how you could ensure forwards compatibility with both
297//! compile-time and runtime extensions of a `SomeEnum` enum:
298//!
299//! ```ignore
300//! match some_enum {
301//! SomeEnum::Foo => {},
302//! SomeEnum::Bar => {},
303//! unhandled => {
304//! let sdk_val: u32 = unhandled.into();
305//! println!("Unhandled enum variant {some_enum:?} has SDK value: {sdk_val}");
306//! }
307//! }
308//! ```
309//!
310//! [`Activity`]: https://developer.android.com/reference/android/app/Activity
311//! [`NativeActivity`]:
312//! https://developer.android.com/reference/android/app/NativeActivity
313//! [ndk_concepts]: https://developer.android.com/ndk/guides/concepts#naa
314//! [`GameActivity`]:
315//! https://developer.android.com/games/agdk/integrate-game-activity
316//! [`Service`]: https://developer.android.com/reference/android/app/Service
317//! [Looper]: https://developer.android.com/reference/android/os/Looper
318//! [`Context`]: https://developer.android.com/reference/android/content/Context
319
320#![deny(clippy::manual_let_else)]
321
322use std::ffi::CStr;
323use std::hash::Hash;
324use std::sync::Arc;
325use std::sync::RwLock;
326use std::time::Duration;
327
328use bitflags::bitflags;
329use jni::vm::JavaVM;
330use libc::c_void;
331
332use ndk::asset::AssetManager;
333use ndk::native_window::NativeWindow;
334
335// Since we expose `ndk` types in our public API it's convenient if crates can
336// defer to these re-exported APIs and avoid having to bump explicit
337// dependencies when they pull in new releases of android-activity.
338pub use ndk;
339pub use ndk_sys;
340
341#[cfg(not(target_os = "android"))]
342compile_error!("android-activity only supports compiling for Android");
343
344#[cfg(all(feature = "game-activity", feature = "native-activity"))]
345compile_error!(
346 r#"The "game-activity" and "native-activity" features cannot be enabled at the same time"#
347);
348#[cfg(all(
349 not(any(feature = "game-activity", feature = "native-activity")),
350 not(any(doc, used_on_docsrs)),
351))]
352compile_error!(
353 r#"Either "game-activity" or "native-activity" must be enabled as features
354
355If you have set one of these features then this error indicates that Cargo is trying to
356link together multiple implementations of android-activity (with incompatible versions)
357which is not supported.
358
359Since android-activity is responsible for the `android_main` entrypoint of your application
360then there can only be a single implementation of android-activity linked with your application.
361
362You can use `cargo tree` (e.g. via `cargo ndk -t arm64-v8a tree`) to identify why multiple
363versions have been resolved.
364
365You may need to add a `[patch]` into your Cargo.toml to ensure a specific version of
366android-activity is used across all of your application's crates."#
367);
368
369#[cfg_attr(feature = "native-activity", path = "native_activity/mod.rs")]
370#[cfg_attr(feature = "game-activity", path = "game_activity/mod.rs")]
371#[cfg_attr(
372 all(
373 // No activities enabled.
374 not(any(feature = "native-activity", feature = "game-activity")),
375 // And building docs.
376 any(doc, used_on_docsrs),
377 ),
378 // Fall back to documenting native activity.
379 path = "native_activity/mod.rs"
380)]
381pub(crate) mod activity_impl;
382
383pub mod error;
384use error::Result;
385
386mod init;
387
388pub mod input;
389use input::KeyCharacterMap;
390
391mod config;
392pub use config::ConfigurationRef;
393
394mod util;
395
396mod jni_utils;
397
398mod sdk;
399
400mod waker;
401pub use waker::AndroidAppWaker;
402
403mod main_callbacks;
404
405pub(crate) const ANDROID_ACTIVITY_TAG: &CStr = c"android-activity";
406
407/// A rectangle with integer edge coordinates. Used to represent window insets, for example.
408#[derive(Clone, Debug, Default, Eq, PartialEq)]
409pub struct Rect {
410 pub left: i32,
411 pub top: i32,
412 pub right: i32,
413 pub bottom: i32,
414}
415
416impl Rect {
417 /// An empty `Rect` with all components set to zero.
418 pub fn empty() -> Self {
419 Self {
420 left: 0,
421 top: 0,
422 right: 0,
423 bottom: 0,
424 }
425 }
426}
427
428impl From<Rect> for ndk_sys::ARect {
429 fn from(rect: Rect) -> Self {
430 Self {
431 left: rect.left,
432 right: rect.right,
433 top: rect.top,
434 bottom: rect.bottom,
435 }
436 }
437}
438
439impl From<ndk_sys::ARect> for Rect {
440 fn from(arect: ndk_sys::ARect) -> Self {
441 Self {
442 left: arect.left,
443 right: arect.right,
444 top: arect.top,
445 bottom: arect.bottom,
446 }
447 }
448}
449
450pub use activity_impl::StateLoader;
451pub use activity_impl::StateSaver;
452
453/// An application event delivered during [`AndroidApp::poll_events`]
454#[non_exhaustive]
455#[derive(Debug)]
456pub enum MainEvent<'a> {
457 /// New input events are available via [`AndroidApp::input_events_iter()`]
458 ///
459 /// _Note: Even if more input is received this event will not be resent
460 /// until [`AndroidApp::input_events_iter()`] has been called, which enables
461 /// applications to batch up input processing without there being lots of
462 /// redundant event loop wake ups._
463 ///
464 /// [`AndroidApp::input_events_iter()`]: AndroidApp::input_events_iter
465 InputAvailable,
466
467 /// Command from main thread: a new [`NativeWindow`] is ready for use. Upon
468 /// receiving this command, [`AndroidApp::native_window()`] will return the new window
469 #[non_exhaustive]
470 InitWindow {},
471
472 /// Command from main thread: the existing [`NativeWindow`] needs to be
473 /// terminated. Upon receiving this command, [`AndroidApp::native_window()`] still
474 /// returns the existing window; after returning from the [`AndroidApp::poll_events()`]
475 /// callback then [`AndroidApp::native_window()`] will return `None`.
476 #[non_exhaustive]
477 TerminateWindow {},
478
479 // TODO: include the prev and new size in the event
480 /// Command from main thread: the current [`NativeWindow`] has been resized.
481 /// Please redraw with its new size.
482 #[non_exhaustive]
483 WindowResized {},
484
485 /// Command from main thread: the current [`NativeWindow`] needs to be redrawn.
486 /// You should redraw the window before the [`AndroidApp::poll_events()`]
487 /// callback returns in order to avoid transient drawing glitches.
488 #[non_exhaustive]
489 RedrawNeeded {},
490
491 /// Command from main thread: the content area of the window has changed,
492 /// such as from the soft input window being shown or hidden. You can
493 /// get the new content rect by calling [`AndroidApp::content_rect()`]
494 #[non_exhaustive]
495 ContentRectChanged {},
496
497 /// Command from main thread: the app's activity window has gained
498 /// input focus.
499 GainedFocus,
500
501 /// Command from main thread: the app's activity window has lost
502 /// input focus.
503 LostFocus,
504
505 /// Command from main thread: the current device configuration has changed. Any
506 /// reference gotten via [`AndroidApp::config()`] will automatically contain the latest
507 /// [`ndk::configuration::Configuration`].
508 #[non_exhaustive]
509 ConfigChanged {},
510
511 /// Command from main thread: the system is running low on memory.
512 /// Try to reduce your memory use.
513 LowMemory,
514
515 /// Command from main thread: the app's activity has been started.
516 Start,
517
518 /// Command from main thread: the app's activity has been resumed.
519 #[non_exhaustive]
520 Resume { loader: StateLoader<'a> },
521
522 /// Command from main thread: the app should generate a new saved state
523 /// for itself, to restore from later if needed. If you have saved state,
524 /// allocate it with malloc and place it in android_app.savedState with
525 /// the size in android_app.savedStateSize. The will be freed for you
526 /// later.
527 #[non_exhaustive]
528 SaveState { saver: StateSaver<'a> },
529
530 /// Command from main thread: the app's activity has been paused.
531 Pause,
532
533 /// Command from main thread: the app's activity has been stopped.
534 Stop,
535
536 /// Command from main thread: the app's activity is being destroyed,
537 /// and waiting for the app thread to clean up and exit before proceeding.
538 Destroy,
539
540 /// Command from main thread: the app's insets have changed.
541 #[non_exhaustive]
542 InsetsChanged {},
543}
544
545/// An event delivered during [`AndroidApp::poll_events`]
546#[derive(Debug)]
547#[non_exhaustive]
548pub enum PollEvent<'a> {
549 Wake,
550 Timeout,
551 Main(MainEvent<'a>),
552}
553
554/// Indicates whether an application has handled or ignored an event
555///
556/// If an event is not handled by an application then some default handling may happen.
557#[derive(Debug, Clone, Copy, PartialEq, Eq)]
558pub enum InputStatus {
559 Handled,
560 Unhandled,
561}
562
563use activity_impl::AndroidAppInner;
564
565bitflags! {
566 /// Flags for [`AndroidApp::set_window_flags`]
567 /// as per the [android.view.WindowManager.LayoutParams Java API](https://developer.android.com/reference/android/view/WindowManager.LayoutParams)
568 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
569 pub struct WindowManagerFlags: u32 {
570 /// As long as this window is visible to the user, allow the lock
571 /// screen to activate while the screen is on. This can be used
572 /// independently, or in combination with
573 /// [`Self::KEEP_SCREEN_ON`] and/or [`Self::SHOW_WHEN_LOCKED`]
574 const ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001;
575
576 /// Everything behind this window will be dimmed. */
577 const DIM_BEHIND = 0x00000002;
578
579 /// Blur everything behind this window.
580 #[deprecated = "Blurring is no longer supported"]
581 const BLUR_BEHIND = 0x00000004;
582
583 /// This window won't ever get key input focus, so the
584 /// user can not send key or other button events to it. Those will
585 /// instead go to whatever focusable window is behind it. This flag
586 /// will also enable [`Self::NOT_TOUCH_MODAL`] whether or not
587 /// that is explicitly set.
588 ///
589 /// Setting this flag also implies that the window will not need to
590 /// interact with
591 /// a soft input method, so it will be Z-ordered and positioned
592 /// independently of any active input method (typically this means it
593 /// gets Z-ordered on top of the input method, so it can use the full
594 /// screen for its content and cover the input method if needed. You
595 /// can use [`Self::ALT_FOCUSABLE_IM`] to modify this
596 /// behavior.
597 const NOT_FOCUSABLE = 0x00000008;
598
599 /// This window can never receive touch events.
600 const NOT_TOUCHABLE = 0x00000010;
601
602 /// Even when this window is focusable (if
603 /// [`Self::NOT_FOCUSABLE`] is not set), allow any pointer
604 /// events outside of the window to be sent to the windows behind it.
605 /// Otherwise it will consume all pointer events itself, regardless of
606 /// whether they are inside of the window.
607 const NOT_TOUCH_MODAL = 0x00000020;
608
609 /// When set, if the device is asleep when the touch
610 /// screen is pressed, you will receive this first touch event. Usually
611 /// the first touch event is consumed by the system since the user can
612 /// not see what they are pressing on.
613 #[deprecated]
614 const TOUCHABLE_WHEN_WAKING = 0x00000040;
615
616 /// As long as this window is visible to the user, keep
617 /// the device's screen turned on and bright.
618 const KEEP_SCREEN_ON = 0x00000080;
619
620 /// Place the window within the entire screen, ignoring
621 /// decorations around the border (such as the status bar). The
622 /// window must correctly position its contents to take the screen
623 /// decoration into account.
624 const LAYOUT_IN_SCREEN = 0x00000100;
625
626 /// Allows the window to extend outside of the screen.
627 const LAYOUT_NO_LIMITS = 0x00000200;
628
629 /// Hide all screen decorations (such as the status
630 /// bar) while this window is displayed. This allows the window to
631 /// use the entire display space for itself -- the status bar will
632 /// be hidden when an app window with this flag set is on the top
633 /// layer. A fullscreen window will ignore a value of
634 /// [`Self::SOFT_INPUT_ADJUST_RESIZE`] the window will stay
635 /// fullscreen and will not resize.
636 const FULLSCREEN = 0x00000400;
637
638 /// Override [`Self::FULLSCREEN`] and force the
639 /// screen decorations (such as the status bar) to be shown.
640 const FORCE_NOT_FULLSCREEN = 0x00000800;
641 /// Turn on dithering when compositing this window to
642 /// the screen.
643 #[deprecated="This flag is no longer used"]
644 const DITHER = 0x00001000;
645
646 /// Treat the content of the window as secure, preventing
647 /// it from appearing in screenshots or from being viewed on non-secure
648 /// displays.
649 const SECURE = 0x00002000;
650
651 /// A special mode where the layout parameters are used
652 /// to perform scaling of the surface when it is composited to the
653 /// screen.
654 const SCALED = 0x00004000;
655
656 /// Intended for windows that will often be used when the user is
657 /// holding the screen against their face, it will aggressively
658 /// filter the event stream to prevent unintended presses in this
659 /// situation that may not be desired for a particular window, when
660 /// such an event stream is detected, the application will receive
661 /// a `AMOTION_EVENT_ACTION_CANCEL` to indicate this so
662 /// applications can handle this accordingly by taking no action on
663 /// the event until the finger is released.
664 const IGNORE_CHEEK_PRESSES = 0x00008000;
665
666 /// A special option only for use in combination with
667 /// [`Self::LAYOUT_IN_SCREEN`]. When requesting layout in
668 /// the screen your window may appear on top of or behind screen decorations
669 /// such as the status bar. By also including this flag, the window
670 /// manager will report the inset rectangle needed to ensure your
671 /// content is not covered by screen decorations.
672 const LAYOUT_INSET_DECOR = 0x00010000;
673
674 /// Invert the state of [`Self::NOT_FOCUSABLE`] with
675 /// respect to how this window interacts with the current method.
676 /// That is, if [`Self::NOT_FOCUSABLE`] is set and this flag is set,
677 /// then the window will behave as if it needs to interact with the
678 /// input method and thus be placed behind/away from it; if
679 /// [`Self::NOT_FOCUSABLE`] is not set and this flag is set,
680 /// then the window will behave as if it doesn't need to interact
681 /// with the input method and can be placed to use more space and
682 /// cover the input method.
683 const ALT_FOCUSABLE_IM = 0x00020000;
684
685 /// If you have set [`Self::NOT_TOUCH_MODAL`], you
686 /// can set this flag to receive a single special MotionEvent with
687 /// the action
688 /// `AMOTION_EVENT_ACTION_OUTSIDE` for
689 /// touches that occur outside of your window. Note that you will not
690 /// receive the full down/move/up gesture, only the location of the
691 /// first down as an `AMOTION_EVENT_ACTION_OUTSIDE`.
692 const WATCH_OUTSIDE_TOUCH = 0x00040000;
693
694 /// Special flag to let windows be shown when the screen
695 /// is locked. This will let application windows take precedence over
696 /// key guard or any other lock screens. Can be used with
697 /// [`Self::KEEP_SCREEN_ON`] to turn screen on and display
698 /// windows directly before showing the key guard window. Can be used with
699 /// [`Self::DISMISS_KEYGUARD`] to automatically fully
700 /// dismiss non-secure key guards. This flag only applies to the top-most
701 /// full-screen window.
702 const SHOW_WHEN_LOCKED = 0x00080000;
703
704 /// Ask that the system wallpaper be shown behind
705 /// your window. The window surface must be translucent to be able
706 /// to actually see the wallpaper behind it; this flag just ensures
707 /// that the wallpaper surface will be there if this window actually
708 /// has translucent regions.
709 const SHOW_WALLPAPER = 0x00100000;
710
711 /// When set as a window is being added or made
712 /// visible, once the window has been shown then the system will
713 /// poke the power manager's user activity (as if the user had woken
714 /// up the device) to turn the screen on.
715 const TURN_SCREEN_ON = 0x00200000;
716
717 /// When set the window will cause the key guard to
718 /// be dismissed, only if it is not a secure lock key guard. Because such
719 /// a key guard is not needed for security, it will never re-appear if
720 /// the user navigates to another window (in contrast to
721 /// [`Self::SHOW_WHEN_LOCKED`], which will only temporarily
722 /// hide both secure and non-secure key guards but ensure they reappear
723 /// when the user moves to another UI that doesn't hide them).
724 /// If the key guard is currently active and is secure (requires an
725 /// unlock pattern) then the user will still need to confirm it before
726 /// seeing this window, unless [`Self::SHOW_WHEN_LOCKED`] has
727 /// also been set.
728 const DISMISS_KEYGUARD = 0x00400000;
729 }
730}
731
732/// The top-level state and interface for a native Rust application
733///
734/// `AndroidApp` provides an interface to query state for the application as
735/// well as monitor events, such as lifecycle and input events, that are
736/// marshalled between the Java thread that owns the `Activity` and the native
737/// thread that runs the `android_main()` code.
738///
739/// # Cheaply Clonable [`AndroidApp`]
740///
741/// [`AndroidApp`] is intended to be something that can be cheaply passed around
742/// by referenced within an application. It is reference counted and can be
743/// cheaply cloned.
744///
745/// # `Send` and `Sync` [`AndroidApp`]
746///
747/// Although an [`AndroidApp`] implements `Send` and `Sync` you do need to take
748/// into consideration that some APIs, such as [`AndroidApp::poll_events()`] are
749/// explicitly documented to only be usable from your `android_main()` thread.
750///
751#[derive(Debug, Clone)]
752pub struct AndroidApp {
753 pub(crate) inner: Arc<RwLock<AndroidAppInner>>,
754}
755
756impl PartialEq for AndroidApp {
757 fn eq(&self, other: &Self) -> bool {
758 Arc::ptr_eq(&self.inner, &other.inner)
759 }
760}
761impl Eq for AndroidApp {}
762
763impl Hash for AndroidApp {
764 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
765 Arc::as_ptr(&self.inner).hash(state);
766 }
767}
768
769impl AndroidApp {
770 /// Queries the current [`NativeWindow`] for the application.
771 ///
772 /// This will only return `Some(window)` between
773 /// [`MainEvent::InitWindow`] and [`MainEvent::TerminateWindow`]
774 /// events.
775 pub fn native_window(&self) -> Option<NativeWindow> {
776 self.inner.read().unwrap().native_window()
777 }
778
779 /// Returns a [`ndk::looper::ForeignLooper`] associated with the Java
780 /// main / UI thread.
781 ///
782 /// This can be used to register file descriptors that may wake up the
783 /// Java main / UI thread and optionally run callbacks on that thread.
784 ///
785 /// ```ignore
786 /// # use ndk;
787 /// # let app: AndroidApp = todo!();
788 /// let looper = app.java_main_looper();
789 /// looper.add_fd_with_callback(todo!(), ndk::looper::FdEvent::INPUT, todo!()).unwrap();
790 /// ```
791 pub fn java_main_looper(&self) -> ndk::looper::ForeignLooper {
792 self.inner.read().unwrap().java_main_looper().clone()
793 }
794
795 /// Returns a pointer to the Java Virtual Machine, for making JNI calls
796 ///
797 /// This returns a pointer to the Java Virtual Machine which can be used
798 /// with the [`jni`] crate (or similar crates) to make JNI calls that bridge
799 /// between native Rust code and Java/Kotlin code running within the JVM.
800 ///
801 /// If you use the [`jni`] crate you can could this as a [`JavaVM`] via:
802 /// ```no_run
803 /// # use jni::JavaVM;
804 /// # let app: android_activity::AndroidApp = todo!();
805 /// let vm = unsafe { JavaVM::from_raw(app.vm_as_ptr().cast()) };
806 /// ```
807 ///
808 /// [`jni`]: https://crates.io/crates/jni
809 /// [`JavaVM`]: https://docs.rs/jni/latest/jni/struct.JavaVM.html
810 pub fn vm_as_ptr(&self) -> *mut c_void {
811 JavaVM::singleton().unwrap().get_raw() as _
812 }
813
814 /// Returns an (*unowned*) JNI global object reference for this
815 /// application's JVM `Activity` as a pointer
816 ///
817 /// If you use the [`jni`] crate you can cast this as a `JObject` reference
818 /// via:
819 /// ```no_run
820 /// # use jni::objects::JObject;
821 /// # use jni::refs::Global;
822 /// # fn use_jni(env: &jni::Env, app: &android_activity::AndroidApp) -> jni::errors::Result<()> {
823 /// let raw_activity_global = app.activity_as_ptr() as jni::sys::jobject;
824 /// // SAFETY: The reference / pointer is valid as long as `app` is valid
825 /// let activity = unsafe { env.as_cast_raw::<Global<JObject>>(&raw_activity_global)? };
826 /// # Ok(()) }
827 /// ```
828 ///
829 /// # JNI Safety
830 ///
831 /// Note that the returned reference will be a JNI global reference *that
832 /// you do not own*.
833 /// - Don't wrap the reference as a [`Global`] which would try to delete the
834 /// reference when dropped.
835 /// - Don't wrap the reference in an [`Auto`] which would treat the
836 /// reference like a local reference and try to delete it when dropped.
837 ///
838 /// The reference is only guaranteed to be valid until you drop the
839 /// [`AndroidApp`].
840 ///
841 /// **Warning:** Don't assume the returned reference has a `'static` lifetime
842 /// since it's possible for `android_main()` to run multiple times over the
843 /// lifetime of an application with a new `AndroidApp` instance each time.
844 ///
845 /// [`jni`]: https://crates.io/crates/jni
846 /// [`Auto`]: https://docs.rs/jni/latest/jni/refs/struct.Auto.html
847 /// [`Global`]: https://docs.rs/jni/latest/jni/refs/struct.Global.html
848 pub fn activity_as_ptr(&self) -> *mut c_void {
849 self.inner.read().unwrap().activity_as_ptr()
850 }
851
852 /// Polls for any events associated with this [AndroidApp] and processes
853 /// those events (such as lifecycle events) via the given `callback`.
854 ///
855 /// It's important to use this API for polling, and not call
856 /// [`ALooper_pollAll`] or [`ALooper_pollOnce`] directly since some events
857 /// require pre- and post-processing either side of the callback. For
858 /// correct behavior events should be handled immediately, before returning
859 /// from the callback and not simply queued for batch processing later. For
860 /// example the existing [`NativeWindow`] is accessible during a
861 /// [`MainEvent::TerminateWindow`] callback and will be set to `None` once
862 /// the callback returns, and this is also synchronized with the Java main
863 /// thread. The [`MainEvent::SaveState`] event is also synchronized with the
864 /// Java main thread.
865 ///
866 /// Internally this is based on [`ALooper_pollOnce`] and will only poll
867 /// file descriptors once per invocation.
868 ///
869 /// # Wake Events
870 ///
871 /// Note that although there is an explicit [PollEvent::Wake] that _can_
872 /// indicate that the main loop was explicitly woken up (E.g. via
873 /// [`AndroidAppWaker::wake`]) it's possible that there will be
874 /// more-specific events that will be delivered after a wake up.
875 ///
876 /// In other words you should only expect to explicitly see
877 /// [`PollEvent::Wake`] events after an early wake up if there were no
878 /// other, more-specific, events that could be delivered after the wake up.
879 ///
880 /// Again, said another way - it's possible that _any_ event could
881 /// effectively be delivered after an early wake up so don't assume there is
882 /// a 1:1 relationship between invoking a wake up via
883 /// [`AndroidAppWaker::wake`] and the delivery of [PollEvent::Wake].
884 ///
885 /// # Panics
886 ///
887 /// This must only be called from your `android_main()` thread and it may
888 /// panic if called from another thread.
889 ///
890 /// [`ALooper_pollAll`]: ndk::looper::ThreadLooper::poll_all
891 /// [`ALooper_pollOnce`]: ndk::looper::ThreadLooper::poll_once
892 pub fn poll_events<F>(&self, timeout: Option<Duration>, callback: F)
893 where
894 F: FnMut(PollEvent<'_>),
895 {
896 self.inner.read().unwrap().poll_events(timeout, callback);
897 }
898
899 /// Creates a means to wake up the main loop while it is blocked waiting for
900 /// events within [`AndroidApp::poll_events()`].
901 pub fn create_waker(&self) -> AndroidAppWaker {
902 self.inner.read().unwrap().create_waker()
903 }
904
905 /// Runs the given closure on the Java main / UI thread.
906 ///
907 /// This is useful for performing operations that must be executed on the
908 /// main thread, such as interacting with Android SDK APIs that require
909 /// execution on the main thread.
910 ///
911 /// Any panic within the closure will be caught and logged as an error,
912 /// (assuming your application is built to allow unwinding).
913 ///
914 /// The thread will be attached to the JVM (for using JNI) and any
915 /// un-cleared Java exceptions left over by the callback will be caught,
916 /// cleared and logged as an error.
917 ///
918 /// There is no built-in mechanism to propagate results back to the caller
919 /// but you can use channels or other synchronization primitives that you
920 /// capture.
921 ///
922 /// It's important to avoid blocking the `android_main` thread while waiting
923 /// for any results because this could lead to deadlocks for `Activity`
924 /// callbacks that require a synchronous response for the `android_activity`
925 /// thread.
926 ///
927 /// # Example
928 ///
929 /// This example demonstrates using the `jni` 0.22 API to show a toast
930 /// message from the Java main thread.
931 ///
932 /// ```no_run
933 /// use android_activity::AndroidApp;
934 /// use jni::{objects::JString, refs::Global};
935 ///
936 /// jni::bind_java_type! { Context => "android.content.Context" }
937 /// jni::bind_java_type! {
938 /// Activity => "android.app.Activity",
939 /// type_map {
940 /// Context => "android.content.Context",
941 /// },
942 /// is_instance_of {
943 /// context: Context
944 /// },
945 /// }
946 ///
947 /// jni::bind_java_type! {
948 /// Toast => "android.widget.Toast",
949 /// type_map {
950 /// Context => "android.content.Context",
951 /// },
952 /// methods {
953 /// static fn make_text(context: Context, text: JCharSequence, duration: i32) -> Toast,
954 /// fn show(),
955 /// }
956 /// }
957 ///
958 /// enum ToastDuration {
959 /// Short = 0,
960 /// Long = 1,
961 /// }
962 ///
963 /// fn send_toast(outer_app: &AndroidApp, msg: impl AsRef<str>, duration: ToastDuration) {
964 /// let app = outer_app.clone();
965 /// let msg = msg.as_ref().to_string();
966 /// outer_app.run_on_java_main_thread(Box::new(move || {
967 /// let jvm = unsafe { jni::JavaVM::from_raw(app.vm_as_ptr() as _) };
968 /// // As an micro optimization you could use jvm.with_top_local_frame, since we know
969 /// // we're already attached
970 /// if let Err(err) = jvm.attach_current_thread(|env| -> jni::errors::Result<()> {
971 /// let activity: jni::sys::jobject = app.activity_as_ptr() as _;
972 /// let activity = unsafe { env.as_cast_raw::<Global<Activity>>(&activity)? };
973 /// let message = JString::new(env, &msg)?;
974 /// let toast = Toast::make_text(env, activity.as_ref(), &message, duration as i32)?;
975 /// toast.show(env)?;
976 /// Ok(())
977 /// }) {
978 /// log::error!("Failed to show toast on main thread: {err:?}");
979 /// }
980 /// }));
981 /// }
982 /// ```
983 pub fn run_on_java_main_thread<F>(&self, f: Box<F>)
984 where
985 F: FnOnce() + Send + 'static,
986 {
987 self.inner.read().unwrap().run_on_java_main_thread(f);
988 }
989
990 /// Returns a **reference** to this application's [`ndk::configuration::Configuration`].
991 ///
992 /// # Warning
993 ///
994 /// The value held by this reference **will change** with every [`MainEvent::ConfigChanged`]
995 /// event that is raised. You should **not** [`Clone`] this type to compare it against a
996 /// "new" [`AndroidApp::config()`] when that event is raised, since both point to the same
997 /// internal [`ndk::configuration::Configuration`] and will be identical.
998 pub fn config(&self) -> ConfigurationRef {
999 self.inner.read().unwrap().config()
1000 }
1001
1002 /// Queries the current content rectangle of the window; this is the area where the
1003 /// window's content should be placed to be seen by the user.
1004 pub fn content_rect(&self) -> Rect {
1005 self.inner.read().unwrap().content_rect()
1006 }
1007
1008 /// Returns the `AssetManager` for the application's `Application` context.
1009 ///
1010 /// Use this to access raw files bundled in the application's .apk file.
1011 ///
1012 /// This is an `Application`-scoped asset manager, not an `Activity`-scoped
1013 /// one. In normal usage those behave the same for packaged assets, so this
1014 /// is usually the correct API to use.
1015 ///
1016 /// In uncommon cases, an `Activity` may have a context-specific
1017 /// asset/resource view that differs from the `Application` context. If you
1018 /// specifically need the current `Activity`'s `AssetManager`, obtain the
1019 /// `Activity` via [`AndroidApp::activity_as_ptr`] and call `getAssets()`
1020 /// through JNI.
1021 ///
1022 /// The returned `AssetManager` has a `'static` lifetime and remains valid
1023 /// across `Activity` recreation, including when `android_main()` is
1024 /// re-entered.
1025 ///
1026 /// **Beware**: If you consider accessing the `Activity` context's
1027 /// `AssetManager` through JNI you must keep the `AssetManager` alive via a
1028 /// global reference before accessing the ndk `AAssetManager` and
1029 /// `ndk::asset::AssetManager` does not currently handle this for you.
1030 pub fn asset_manager(&self) -> AssetManager {
1031 self.inner.read().unwrap().asset_manager()
1032 }
1033
1034 /// Change the window flags of the given activity.
1035 ///
1036 /// Note that some flags must be set before the window decoration is created,
1037 /// see
1038 /// `<https://developer.android.com/reference/android/view/Window#setFlags(int,%20int)>`.
1039 pub fn set_window_flags(
1040 &self,
1041 add_flags: WindowManagerFlags,
1042 remove_flags: WindowManagerFlags,
1043 ) {
1044 self.inner
1045 .write()
1046 .unwrap()
1047 .set_window_flags(add_flags, remove_flags);
1048 }
1049
1050 /// Enable additional input axis
1051 ///
1052 /// To reduce overhead, by default only [`input::Axis::X`] and [`input::Axis::Y`] are enabled
1053 /// and other axis should be enabled explicitly.
1054 pub fn enable_motion_axis(&self, axis: input::Axis) {
1055 self.inner.write().unwrap().enable_motion_axis(axis);
1056 }
1057
1058 /// Disable input axis
1059 ///
1060 /// To reduce overhead, by default only [`input::Axis::X`] and [`input::Axis::Y`] are enabled
1061 /// and other axis should be enabled explicitly.
1062 pub fn disable_motion_axis(&self, axis: input::Axis) {
1063 self.inner.write().unwrap().disable_motion_axis(axis);
1064 }
1065
1066 /// Explicitly request that the current input method's soft input area be
1067 /// shown to the user, if needed.
1068 ///
1069 /// Call this if the user interacts with your view in such a way that they
1070 /// have expressed they would like to start performing input into it.
1071 pub fn show_soft_input(&self, show_implicit: bool) {
1072 self.inner.read().unwrap().show_soft_input(show_implicit);
1073 }
1074
1075 /// Request to hide the soft input window from the context of the window
1076 /// that is currently accepting input.
1077 ///
1078 /// This should be called as a result of the user doing some action that
1079 /// fairly explicitly requests to have the input window hidden.
1080 pub fn hide_soft_input(&self, hide_implicit_only: bool) {
1081 self.inner
1082 .read()
1083 .unwrap()
1084 .hide_soft_input(hide_implicit_only);
1085 }
1086
1087 /// Fetch the current input text state, as updated by any active IME.
1088 pub fn text_input_state(&self) -> input::TextInputState {
1089 self.inner.read().unwrap().text_input_state()
1090 }
1091
1092 /// Forward the given input text `state` to any active IME.
1093 pub fn set_text_input_state(&self, state: input::TextInputState) {
1094 self.inner.read().unwrap().set_text_input_state(state);
1095 }
1096
1097 /// Specify the type of text being input, how the IME enter/action key
1098 /// should behave and any additional IME options.
1099 ///
1100 /// Also see the Android SDK documentation for
1101 /// [android.view.inputmethod.EditorInfo](https://developer.android.com/reference/android/view/inputmethod/EditorInfo)
1102 pub fn set_ime_editor_info(
1103 &self,
1104 input_type: input::InputType,
1105 action: input::TextInputAction,
1106 options: input::ImeOptions,
1107 ) {
1108 self.inner
1109 .read()
1110 .unwrap()
1111 .set_ime_editor_info(input_type, action, options);
1112 }
1113
1114 /// Get an exclusive, lending iterator over buffered input events
1115 ///
1116 /// Applications are expected to call this in-sync with their rendering or
1117 /// in response to a [`MainEvent::InputAvailable`] event being delivered.
1118 ///
1119 /// _**Note:** your application is will only be delivered a single
1120 /// [`MainEvent::InputAvailable`] event between calls to this API._
1121 ///
1122 /// To reduce overhead, by default, only [`input::Axis::X`] and [`input::Axis::Y`] are enabled
1123 /// and other axis should be enabled explicitly via [`Self::enable_motion_axis`].
1124 ///
1125 /// This isn't the most ergonomic iteration API since we can't return a standard `Iterator`:
1126 /// - This API returns a lending iterator may borrow from the internal buffer
1127 /// of pending events without copying them.
1128 /// - For each event we want to ensure the application reports whether the
1129 /// event was handled.
1130 ///
1131 /// # Example
1132 /// Code to iterate all pending input events would look something like this:
1133 ///
1134 /// ```no_run
1135 /// # use android_activity::{AndroidApp, InputStatus, input::InputEvent};
1136 /// # let app: AndroidApp = todo!();
1137 /// match app.input_events_iter() {
1138 /// Ok(mut iter) => {
1139 /// loop {
1140 /// let read_input = iter.next(|event| {
1141 /// let handled = match event {
1142 /// InputEvent::KeyEvent(key_event) => {
1143 /// // Snip
1144 /// InputStatus::Handled
1145 /// }
1146 /// InputEvent::MotionEvent(motion_event) => {
1147 /// InputStatus::Unhandled
1148 /// }
1149 /// event => {
1150 /// InputStatus::Unhandled
1151 /// }
1152 /// };
1153 ///
1154 /// handled
1155 /// });
1156 ///
1157 /// if !read_input {
1158 /// break;
1159 /// }
1160 /// }
1161 /// }
1162 /// Err(err) => {
1163 /// log::error!("Failed to get input events iterator: {err:?}");
1164 /// }
1165 /// }
1166 /// ```
1167 ///
1168 /// # Panics
1169 ///
1170 /// This must only be called from your `android_main()` thread and it may panic if called
1171 /// from another thread.
1172 pub fn input_events_iter(&self) -> Result<input::InputIterator<'_>> {
1173 let receiver = {
1174 let guard = self.inner.read().unwrap();
1175 guard.input_events_receiver()?
1176 };
1177
1178 Ok(input::InputIterator {
1179 inner: receiver.into(),
1180 })
1181 }
1182
1183 /// Lookup the [`KeyCharacterMap`] for the given input `device_id`
1184 ///
1185 /// Use [`KeyCharacterMap::get`] to map key codes + meta state into unicode characters
1186 /// or dead keys that compose with the next key.
1187 ///
1188 /// # Example
1189 ///
1190 /// Code to handle unicode character mapping as well as combining dead keys could look some thing like:
1191 ///
1192 /// ```no_run
1193 /// # use android_activity::{AndroidApp, input::{InputEvent, KeyEvent, KeyMapChar}};
1194 /// # let app: AndroidApp = todo!();
1195 /// # let key_event: KeyEvent = todo!();
1196 /// let mut combining_accent = None;
1197 /// // Snip
1198 ///
1199 /// let combined_key_char = if let Ok(map) = app.device_key_character_map(key_event.device_id()) {
1200 /// match map.get(key_event.key_code(), key_event.meta_state()) {
1201 /// Ok(KeyMapChar::Unicode(unicode)) => {
1202 /// let combined_unicode = if let Some(accent) = combining_accent {
1203 /// match map.get_dead_char(accent, unicode) {
1204 /// Ok(Some(key)) => {
1205 /// println!("KeyEvent: Combined '{unicode}' with accent '{accent}' to give '{key}'");
1206 /// Some(key)
1207 /// }
1208 /// Ok(None) => None,
1209 /// Err(err) => {
1210 /// eprintln!("KeyEvent: Failed to combine 'dead key' accent '{accent}' with '{unicode}': {err:?}");
1211 /// None
1212 /// }
1213 /// }
1214 /// } else {
1215 /// println!("KeyEvent: Pressed '{unicode}'");
1216 /// Some(unicode)
1217 /// };
1218 /// combining_accent = None;
1219 /// combined_unicode.map(|unicode| KeyMapChar::Unicode(unicode))
1220 /// }
1221 /// Ok(KeyMapChar::CombiningAccent(accent)) => {
1222 /// println!("KeyEvent: Pressed 'dead key' combining accent '{accent}'");
1223 /// combining_accent = Some(accent);
1224 /// Some(KeyMapChar::CombiningAccent(accent))
1225 /// }
1226 /// Ok(KeyMapChar::None) => {
1227 /// println!("KeyEvent: Pressed non-unicode key");
1228 /// combining_accent = None;
1229 /// None
1230 /// }
1231 /// Err(err) => {
1232 /// eprintln!("KeyEvent: Failed to get key map character: {err:?}");
1233 /// combining_accent = None;
1234 /// None
1235 /// }
1236 /// }
1237 /// } else {
1238 /// None
1239 /// };
1240 /// ```
1241 ///
1242 /// # Errors
1243 ///
1244 /// Since this API needs to use JNI internally to call into the Android JVM it may return
1245 /// a [`error::AppError::JavaError`] in case there is a spurious JNI error or an exception
1246 /// is caught.
1247 ///
1248 /// This API should not be called with a `device_id` of `0`, since that indicates a non-physical
1249 /// device and will result in a [`error::AppError::JavaError`].
1250 pub fn device_key_character_map(&self, device_id: i32) -> Result<KeyCharacterMap> {
1251 Ok(self
1252 .inner
1253 .read()
1254 .unwrap()
1255 .device_key_character_map(device_id)?)
1256 }
1257
1258 /// The user-visible SDK version of the framework
1259 ///
1260 /// Also referred to as [`Build.VERSION_CODES`](https://developer.android.com/reference/android/os/Build.VERSION_CODES)
1261 pub fn sdk_version() -> i32 {
1262 let mut prop = android_properties::getprop("ro.build.version.sdk");
1263 if let Some(val) = prop.value() {
1264 val.parse::<i32>()
1265 .expect("Failed to parse ro.build.version.sdk property")
1266 } else {
1267 panic!("Couldn't read ro.build.version.sdk system property");
1268 }
1269 }
1270
1271 /// Path to this application's internal data directory
1272 pub fn internal_data_path(&self) -> Option<std::path::PathBuf> {
1273 self.inner.read().unwrap().internal_data_path()
1274 }
1275
1276 /// Path to this application's external data directory
1277 pub fn external_data_path(&self) -> Option<std::path::PathBuf> {
1278 self.inner.read().unwrap().external_data_path()
1279 }
1280
1281 /// Path to the directory containing the application's OBB files (if any).
1282 pub fn obb_path(&self) -> Option<std::path::PathBuf> {
1283 self.inner.read().unwrap().obb_path()
1284 }
1285}
1286
1287#[test]
1288fn test_app_is_send_sync() {
1289 fn needs_send_sync<T: Send + Sync>() {}
1290 needs_send_sync::<AndroidApp>();
1291}
1292
1293/// The state passed to the optional `android_on_create` entry point if
1294/// available.
1295///
1296/// This gives access to the Java VM, the Java `Activity` and any saved state
1297/// from a previous instance of the `Activity` that was saved via the
1298/// `onSaveInstanceState` callback.
1299///
1300/// Each time `android_on_create` is called it will receive a new `Activity`
1301/// reference.
1302///
1303/// See the top-level [`android-activity`](crate) documentation for more details
1304/// on `android_on_create`.
1305pub struct OnCreateState<'a> {
1306 jvm: JavaVM,
1307 java_activity: *mut c_void,
1308 saved_state: &'a [u8],
1309}
1310
1311impl<'a> OnCreateState<'a> {
1312 pub(crate) fn new(jvm: JavaVM, java_activity: *mut c_void, saved_state: &'a [u8]) -> Self {
1313 Self {
1314 jvm,
1315 java_activity,
1316 saved_state,
1317 }
1318 }
1319
1320 /// Returns a pointer to the Java Virtual Machine, for making JNI calls
1321 ///
1322 /// If you use the `jni` crate, you can wrap this pointer as a `JavaVM` via:
1323 /// ```no_run
1324 /// # use jni::JavaVM;
1325 /// # let on_create_state: android_activity::OnCreateState = todo!();
1326 /// let vm = unsafe { JavaVM::from_raw(on_create_state.vm_as_ptr().cast()) };
1327 /// ```
1328 pub fn vm_as_ptr(&self) -> *mut c_void {
1329 self.jvm.get_raw().cast()
1330 }
1331
1332 /// Returns an (*unowned*) JNI global object reference for this `Activity`
1333 /// as a pointer
1334 ///
1335 /// If you use the `jni` crate, you can cast this as a `JObject` reference
1336 /// via:
1337 ///
1338 /// ```no_run
1339 /// # use jni::{JavaVM, objects::JObject};
1340 /// # let on_create_state: android_activity::OnCreateState = todo!();
1341 /// let vm = unsafe { JavaVM::from_raw(on_create_state.vm_as_ptr().cast()) };
1342 /// let _res = vm.attach_current_thread(|env| -> jni::errors::Result<()> {
1343 /// let activity = on_create_state.activity_as_ptr() as jni::sys::jobject;
1344 /// // SAFETY: The reference / pointer is valid at least until we return from `android_on_create`
1345 /// let activity = unsafe { env.as_cast_raw::<JObject>(&activity)? };
1346 /// // Do something with `activity` here
1347 /// Ok(())
1348 /// });
1349 /// ```
1350 ///
1351 /// # JNI Safety
1352 ///
1353 /// It is not specified whether this will be a global or local reference and
1354 /// in any case you must treat is as a reference that you do not own and
1355 /// must not attempt to delete it.
1356 /// - Don't wrap the reference as a `Global` which would try to delete the
1357 /// reference when dropped.
1358 /// - Don't wrap the reference in an `Auto` which would treat the reference
1359 /// like a local reference and try to delete it when dropped.
1360 pub fn activity_as_ptr(&self) -> *mut c_void {
1361 self.java_activity
1362 }
1363
1364 /// Returns the saved state of the `Activity` as a byte slice, which may be
1365 /// empty if there is no saved state.
1366 pub fn saved_state(&self) -> &[u8] {
1367 self.saved_state
1368 }
1369}