winit/platform/android.rs
1//! # Android
2//!
3//! The Android backend builds on (and exposes types from) the [`ndk`](https://docs.rs/ndk/) crate.
4//!
5//! Native Android applications need some form of "glue" crate that is responsible
6//! for defining the main entry point for your Rust application as well as tracking
7//! various life-cycle events and synchronizing with the main JVM thread.
8//!
9//! Winit uses the [android-activity](https://docs.rs/android-activity/) as a
10//! glue crate (prior to `0.28` it used
11//! [ndk-glue](https://github.com/rust-windowing/android-ndk-rs/tree/master/ndk-glue)).
12//!
13//! The version of the glue crate that your application depends on _must_ match the
14//! version that Winit depends on because the glue crate is responsible for your
15//! application's main entry point. If Cargo resolves multiple versions, they will
16//! clash.
17//!
18//! `winit` glue compatibility table:
19//!
20//! | winit | ndk-glue |
21//! | :---: | :--------------------------: |
22//! | 0.30 | `android-activity = "0.6"` |
23//! | 0.29 | `android-activity = "0.5"` |
24//! | 0.28 | `android-activity = "0.4"` |
25//! | 0.27 | `ndk-glue = "0.7"` |
26//! | 0.26 | `ndk-glue = "0.5"` |
27//! | 0.25 | `ndk-glue = "0.3"` |
28//! | 0.24 | `ndk-glue = "0.2"` |
29//!
30//! The recommended way to avoid a conflict with the glue version is to avoid explicitly
31//! depending on the `android-activity` crate, and instead consume the API that
32//! is re-exported by Winit under `winit::platform::android::activity::*`
33//!
34//! Running on an Android device needs a dynamic system library. Add this to Cargo.toml:
35//!
36//! ```toml
37//! [lib]
38//! name = "main"
39//! crate-type = ["cdylib"]
40//! ```
41//!
42//! All Android applications are based on an `Activity` subclass, and the
43//! `android-activity` crate is designed to support different choices for this base
44//! class. Your application _must_ specify the base class it needs via a feature flag:
45//!
46//! | Base Class | Feature Flag | Notes |
47//! | :--------------: | :---------------: | :-----: |
48//! | `NativeActivity` | `android-native-activity` | Built-in to Android - it is possible to use without compiling any Java or Kotlin code. Java or Kotlin code may be needed to subclass `NativeActivity` to access some platform features. It does not derive from the [`AndroidAppCompat`] base class.|
49//! | [`GameActivity`] | `android-game-activity` | Derives from [`AndroidAppCompat`], a defacto standard `Activity` base class that helps support a wider range of Android versions. Requires a build system that can compile Java or Kotlin and fetch Android dependencies from a [Maven repository][agdk_jetpack] (or link with an embedded [release][agdk_releases] of [`GameActivity`]) |
50//!
51//! [`GameActivity`]: https://developer.android.com/games/agdk/game-activity
52//! [`GameTextInput`]: https://developer.android.com/games/agdk/add-support-for-text-input
53//! [`AndroidAppCompat`]: https://developer.android.com/reference/androidx/appcompat/app/AppCompatActivity
54//! [agdk_jetpack]: https://developer.android.com/jetpack/androidx/releases/games
55//! [agdk_releases]: https://developer.android.com/games/agdk/download#agdk-libraries
56//! [Gradle]: https://developer.android.com/studio/build
57//!
58//! For more details, refer to these `android-activity` [example applications](https://github.com/rust-mobile/android-activity/tree/main/examples).
59//!
60//! ## Converting from `ndk-glue` to `android-activity`
61//!
62//! If your application is currently based on `NativeActivity` via the `ndk-glue` crate and building
63//! with `cargo apk`, then the minimal changes would be:
64//! 1. Remove `ndk-glue` from your `Cargo.toml`
65//! 2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.30.9",
66//! features = [ "android-native-activity" ] }`
67//! 3. Add an `android_main` entrypoint (as above), instead of using the '`[ndk_glue::main]` proc
68//! macro from `ndk-macros` (optionally add a dependency on `android_logger` and initialize
69//! logging as above).
70//! 4. Pass a clone of the `AndroidApp` that your application receives to Winit when building your
71//! event loop (as shown above).
72
73use crate::event_loop::{ActiveEventLoop, EventLoop, EventLoopBuilder};
74use crate::window::{Window, WindowAttributes};
75
76use self::activity::{AndroidApp, ConfigurationRef, Rect};
77
78/// Additional methods on [`EventLoop`] that are specific to Android.
79pub trait EventLoopExtAndroid {
80 /// Get the [`AndroidApp`] which was used to create this event loop.
81 fn android_app(&self) -> &AndroidApp;
82}
83
84impl<T> EventLoopExtAndroid for EventLoop<T> {
85 fn android_app(&self) -> &AndroidApp {
86 &self.event_loop.android_app
87 }
88}
89
90/// Additional methods on [`ActiveEventLoop`] that are specific to Android.
91pub trait ActiveEventLoopExtAndroid {
92 /// Get the [`AndroidApp`] which was used to create this event loop.
93 fn android_app(&self) -> &AndroidApp;
94}
95
96/// Additional methods on [`Window`] that are specific to Android.
97pub trait WindowExtAndroid {
98 fn content_rect(&self) -> Rect;
99
100 fn config(&self) -> ConfigurationRef;
101}
102
103impl WindowExtAndroid for Window {
104 fn content_rect(&self) -> Rect {
105 self.window.content_rect()
106 }
107
108 fn config(&self) -> ConfigurationRef {
109 self.window.config()
110 }
111}
112
113impl ActiveEventLoopExtAndroid for ActiveEventLoop {
114 fn android_app(&self) -> &AndroidApp {
115 &self.p.app
116 }
117}
118
119/// Additional methods on [`WindowAttributes`] that are specific to Android.
120pub trait WindowAttributesExtAndroid {}
121
122impl WindowAttributesExtAndroid for WindowAttributes {}
123
124pub trait EventLoopBuilderExtAndroid {
125 /// Associates the [`AndroidApp`] that was passed to `android_main()` with the event loop
126 ///
127 /// This must be called on Android since the [`AndroidApp`] is not global state.
128 fn with_android_app(&mut self, app: AndroidApp) -> &mut Self;
129
130 /// Calling this will mark the volume keys to be manually handled by the application
131 ///
132 /// Default is to let the operating system handle the volume keys
133 fn handle_volume_keys(&mut self) -> &mut Self;
134}
135
136impl<T> EventLoopBuilderExtAndroid for EventLoopBuilder<T> {
137 fn with_android_app(&mut self, app: AndroidApp) -> &mut Self {
138 self.platform_specific.android_app = Some(app);
139 self
140 }
141
142 fn handle_volume_keys(&mut self) -> &mut Self {
143 self.platform_specific.ignore_volume_keys = false;
144 self
145 }
146}
147
148/// Re-export of the `android_activity` API
149///
150/// Winit re-exports the `android_activity` API for convenience so that most
151/// applications can rely on the Winit crate to resolve the required version of
152/// `android_activity` and avoid any chance of a conflict between Winit and the
153/// application crate.
154///
155/// Unlike most libraries there can only be a single implementation
156/// of the `android_activity` glue crate linked with an application because
157/// it is responsible for the application's `android_main()` entry point.
158///
159/// Since Winit depends on a specific version of `android_activity` the simplest
160/// way to avoid creating a conflict is for applications to avoid explicitly
161/// depending on the `android_activity` crate, and instead consume the API that
162/// is re-exported by Winit.
163///
164/// For compatibility applications should then import the [`AndroidApp`] type for
165/// their `android_main(app: AndroidApp)` function like:
166/// ```rust
167/// #[cfg(target_os = "android")]
168/// use winit::platform::android::activity::AndroidApp;
169/// ```
170pub mod activity {
171 // We enable the `"native-activity"` feature just so that we can build the
172 // docs, but it'll be very confusing for users to see the docs with that
173 // feature enabled, so we avoid inlining it so that they're forced to view
174 // it on the crate's own docs.rs page.
175 #[doc(no_inline)]
176 #[cfg(android_platform)]
177 pub use android_activity::*;
178
179 #[cfg(not(android_platform))]
180 #[doc(hidden)]
181 pub struct Rect;
182 #[cfg(not(android_platform))]
183 #[doc(hidden)]
184 pub struct ConfigurationRef;
185 #[cfg(not(android_platform))]
186 #[doc(hidden)]
187 pub struct AndroidApp;
188}