slint/android.rs
1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4// cSpell: ignore javac
5//! Android backend.
6//!
7//! **Note:** This module is only available on Android with the "backend-android-activity-06" feature
8//!
9//! Slint uses the [android-activity crate](https://github.com/rust-mobile/android-activity) as a backend.
10//!
11//! For convenience, Slint re-exports the content of the [`android-activity`](https://docs.rs/android-activity) under `slint::android::android_activity`.
12//!
13//! As with every application using the android-activity crate, the entry point to your app will be the `android_main` function.
14//! From that function, you can call [`slint::android::init`](init()) or [`slint::android::init_with_event_listener`](init_with_event_listener)
15//!
16//! # Example
17//!
18//! This is a basic example of an Android application.
19//! Do not forget the `#[unsafe(no_mangle)]`
20//!
21//! ```rust
22//! # #[cfg(target_os = "android")]
23//! #[unsafe(no_mangle)]
24//! fn android_main(app: slint::android::AndroidApp) {
25//! slint::android::init(app).unwrap();
26//!
27//! // ... rest of your code ...
28//! slint::slint!{
29//! export component MainWindow inherits Window {
30//! Text { text: "Hello World"; }
31//! }
32//! }
33//! MainWindow::new().unwrap().run().unwrap();
34//! }
35//! ```
36//!
37//! That function must be in a `cdylib` library, and you should enable the "backend-android-activity-06"
38//! feature of the slint crate in your Cargo.toml:
39//!
40//! ```toml
41//! [lib]
42//! crate-type = ["cdylib"]
43//!
44//! [dependencies]
45//! slint = { version = "1.6", features = ["backend-android-activity-06"] }
46//! ```
47//!
48//! ## Building and Deploying
49//!
50//! Building a Rust application requires the target toolchain to be installed. You can install it via `rustup`. For example, to target AArch64 Android, use the following command:
51//!
52//! ```sh
53//! rustup target add aarch64-linux-android
54//! ```
55//!
56//! Make sure that you have the Android NDK and SDK installed and set up in your development environment.
57//! For detailed instructions on how to set up the Android NDK and SDK, please refer to the [Android Developer's guide](https://developer.android.com/studio/projects/install-ndk).
58//! The following environment variables need to be set:
59//! * `ANDROID_HOME`: The directory in which your Android SDK is located. Usually `$HOME/Android/Sdk`.
60//! * `ANDROID_NDK_ROOT`: The directory in which your Android NDK is located. Usually `$HOME/Android/Sdk/ndk/${NDK_VERSION}`. ${NDK_VERSION} is the version of the NDK you have installed.
61//! * `JAVA_HOME`: The directory in which your Java compiler (`javac`) is located. This variable is optional if a `javac` is found in your `$PATH`.
62//! Otherwise you can set `JAVA_HOME` to the `javac` installation shipped with Android Studio in `android-studio/jbr`.
63//!
64//! Slint doesn't require a specific tool to build, sign, and deploy the APK.
65//! The APK's activity must be `android.app.NativeActivity`, or a subclass of it,
66//! because that's the activity type the "backend-android-activity-06" feature supports.
67//! Tools that can package such an APK include:
68//!
69//! * [cargo-apk2](https://crates.io/crates/cargo-apk2): `cargo apk2 run --target aarch64-linux-android --lib`
70//! * [cargo-apk](https://crates.io/crates/cargo-apk): `cargo apk run --target aarch64-linux-android --lib`
71//! * [cargo-ndk](https://crates.io/crates/cargo-ndk) to build the library, with Gradle to package it
72//! * [xbuild](https://github.com/rust-mobile/xbuild): `x run --device <id>`
73//!
74# for how to set up each of them.")]
75
76/// Re-export of the android-activity crate.
77#[cfg(all(
78 target_os = "android",
79 any(feature = "backend-android-activity-05", feature = "backend-android-activity-06")
80))]
81pub use i_slint_backend_android_activity::android_activity;
82
83#[cfg(not(all(
84 target_os = "android",
85 any(feature = "backend-android-activity-05", feature = "backend-android-activity-06")
86)))]
87/// Re-export of the [android-activity](https://docs.rs/android-activity) crate.
88pub mod android_activity {
89 #[doc(hidden)]
90 pub struct AndroidApp;
91 #[doc(hidden)]
92 pub struct PollEvent<'a>(&'a ());
93}
94
95/// Re-export of AndroidApp from the [android-activity](https://docs.rs/android-activity) crate.
96#[doc(no_inline)]
97pub use android_activity::AndroidApp;
98
99use crate::platform::SetPlatformError;
100
101/// Initializes the Android backend.
102///
103/// **Note:** This function is only available on Android with the "backend-android-activity-06" feature
104///
105/// This function must be called from the `android_main` function before any call to Slint that needs a backend.
106///
107/// See the [module documentation](self) for an example on how to create Android application.
108///
109/// See also [`init_with_event_listener`]
110pub fn init(app: android_activity::AndroidApp) -> Result<(), SetPlatformError> {
111 #[cfg(not(target_os = "android"))]
112 unreachable!();
113 #[cfg(target_os = "android")]
114 {
115 crate::platform::set_platform(alloc::boxed::Box::new(
116 i_slint_backend_android_activity::AndroidPlatform::new(app),
117 ))?;
118 #[cfg(any(feature = "mcp", feature = "system-testing"))]
119 i_slint_backend_selector::init_testing_backends();
120 Ok(())
121 }
122}
123
124/// Similar to [`init()`], which allow to listen to android-activity's event
125///
126/// **Note:** This function is only available on Android with the "backend-android-activity-06" feature
127///
128/// The listener argument is a function that takes a [`android_activity::PollEvent`](https://docs.rs/android-activity/latest/android_activity/enum.PollEvent.html)
129///
130/// # Example
131///
132/// ```rust
133/// # #[cfg(target_os = "android")]
134/// #[unsafe(no_mangle)]
135/// fn android_main(app: slint::android_activity::AndroidApp) {
136/// slint::android::init_with_event_listener(
137/// app,
138/// |event| { eprintln!("got event {event:?}") }
139/// ).unwrap();
140///
141/// // ... rest of your application ...
142///
143/// }
144/// ```
145///
146/// Check out the [module documentation](self) for a more complete example on how to write an android application
147pub fn init_with_event_listener(
148 app: android_activity::AndroidApp,
149 listener: impl Fn(&android_activity::PollEvent<'_>) + 'static,
150) -> Result<(), SetPlatformError> {
151 #[cfg(not(target_os = "android"))]
152 unreachable!();
153 #[cfg(target_os = "android")]
154 {
155 crate::platform::set_platform(alloc::boxed::Box::new(
156 i_slint_backend_android_activity::AndroidPlatform::new_with_event_listener(
157 app, listener,
158 ),
159 ))?;
160 #[cfg(any(feature = "mcp", feature = "system-testing"))]
161 i_slint_backend_selector::init_testing_backends();
162 Ok(())
163 }
164}