Skip to content

Commit

Permalink
Android: make slint::android_init public
Browse files Browse the repository at this point in the history
Add a `backend-android-native-acticity` feature that enables the
`slint::android_*` re-exports.
  • Loading branch information
ogoffart committed Feb 15, 2024
1 parent 7980eda commit 12124c6
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 41 deletions.
6 changes: 6 additions & 0 deletions api/rs/slint/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ backend-linuxkms = ["i-slint-backend-selector/backend-linuxkms", "std"]
## windowing system. (Experimental)
backend-linuxkms-noseat = ["i-slint-backend-selector/backend-linuxkms-noseat", "std"]

## Use the backend based on the [android-activity](https://docs.rs/android-activity) crate. (Using it's native activity feature)
backend-android-native-activity = ["i-slint-backend-android-activity/native-activity"]

[dependencies]
i-slint-core = { workspace = true }
slint-macros = { workspace = true }
Expand All @@ -177,6 +180,9 @@ log = { version = "0.4.17", optional = true }
# end even then wouldn't work because it can't load fonts
i-slint-renderer-femtovg = { workspace = true, optional = true }

[target.'cfg(target_os = "android")'.dependencies]
i-slint-backend-android-activity = { workspace = true, optional = true }

[dev-dependencies]
slint-build = { path = "../build" }
i-slint-backend-testing = { path = "../../../internal/backends/testing" }
Expand Down
110 changes: 110 additions & 0 deletions api/rs/slint/android.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial

/// Re-export of the android_activity crate.
#[cfg(all(target_os = "android", feature = "backend-android-native-activity"))]
pub use i_slint_backend_android_activity::android_activity;

#[cfg(not(all(target_os = "android", feature = "backend-android-native-activity")))]
#[doc(hidden)]
mod android_activity {
pub(crate) struct AndroidApp;
pub(crate) struct PollEvent<'a>(&'a ());
}

use crate::platform::SetPlatformError;

/// Initializes the Android backend.
///
/// **Note:** This function is only available on Android with the "backend-android-native-activity" feature
///
/// This is meant to be called from the `android_main` function.
///
/// Slint uses the [android-activity crate](https://github.com/rust-mobile/android-activity) as a backend.
/// For convenience, Slint re-export the content of the [`android_activity`](https://docs.rs/android-activity) under `slint::android_activity`
/// As every application using the android-activity crate, the entry point to your app will be the `android_main` function.
///
/// See also [`android_init_with_event_listener`]
///
/// # Example
///
/// This is a basic example of a rust application.
/// Do not forget the `#[no_mangle]`
///
/// ```rust
/// # #[cfg(target_os = "android")]
/// #[no_mangle]
/// fn android_main(app: slint::android_activity::AndroidApp) {
/// slint::android_init(app).unwrap();
///
/// // ... rest of your code ...
/// slint::slint!{
/// export component MainWindow inherits Window {
/// Text { text: "Hello World"; }
/// }
/// }
/// MainWindow::new().unwrap().run().unwrap();
/// }
/// ```
///
/// That function must be in a `cdylib` library, and you should enable the "backend-android-native-activity"`
/// feature of the slint crate in your Cargo.toml:
///
/// ```toml
/// [lib]
/// crate-type = ["cdylib"]
///
/// [dependencies]
/// slint = { version = "1.5", features = ["backend-android-native-activity"] }
/// ```
///
/// ## Building and Deploying
///
/// To build and deploy your application, we suggest the usage of [cargo-apk](https://github.com/rust-mobile/cargo-apk),
/// a cargo subcommand that allows you to build, sign, and deploy Android APKs made in Rust.
///
/// You can install it and use it with the following command:
///
/// ```sh
/// cargo install cargo-apk
/// cargo apk run --target aarch64-linux-android --lib
/// ```
///
/// Please ensure that you have the Android NDK and SDK installed and properly set up in your development environment for the above command to work as expected.
pub fn android_init(app: android_activity::AndroidApp) -> Result<(), SetPlatformError> {
crate::platform::set_platform(Box::new(i_slint_backend_android_activity::AndroidPlatform::new(
app,
)))
}

/// Similar as [`android_init`], which allow to listen to android-activity's event
///
/// **Note:** This function is only available on Android with the "backend-android-native-activity" feature
///
/// The listener argument is a function that takes a [`android_activity::PollEvent`](https://docs.rs/android-activity/latest/android_activity/enum.PollEvent.html)
///
/// # Example
///
/// ```rust
/// # #[cfg(target_os = "android")]
/// #[no_mangle]
/// fn android_main(app: slint::android_activity::AndroidApp) {
/// slint::android_init_with_event_listener(
/// app,
/// |event| { eprintln!("got event {event:?}") }
/// ).unwrap();
///
/// // ... rest of your application ...
///
/// }
/// ```
///
/// Check out the documentation of [`android_init`] for a more complete example on how to write an android application
pub fn android_init_with_event_listener(
app: android_activity::AndroidApp,
listener: impl Fn(&android_activity::PollEvent<'_>) + 'static,
) -> Result<(), SetPlatformError> {
crate::platform::set_platform(Box::new(
i_slint_backend_android_activity::AndroidPlatform::new_with_event_listener(app, listener),
))
}
5 changes: 5 additions & 0 deletions api/rs/slint/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,11 @@ pub mod platform {
}
}

#[cfg(any(doc, all(target_os = "android", feature = "backend-android-native-activity")))]
mod android;
#[cfg(any(doc, all(target_os = "android", feature = "backend-android-native-activity")))]
pub use android::*;

/// Helper type that helps checking that the generated code is generated for the right version
#[doc(hidden)]
#[allow(non_camel_case_types)]
Expand Down
20 changes: 11 additions & 9 deletions examples/printerdemo/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,27 @@ path = "main.rs"
name = "printerdemo"

[dependencies]
slint = { path = "../../../api/rs/slint" }
slint = { path = "../../../api/rs/slint", features = ["backend-android-native-activity"] }

chrono = { version = "0.4", default-features = false, features = ["clock", "std"]}

[target.'cfg(target_os = "android")'.dependencies]
i-slint-backend-android-activity = { workspace = true, features = ["native-activity"] }

[build-dependencies]
slint-build = { path = "../../../api/rs/build" }

# Remove the `#wasm#` to uncomment the wasm build.
[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen = { version = "0.2" }
web-sys = { version = "0.3", features=["console"] }
console_error_panic_hook = "0.1.5"

# Remove the `#wasm#` to uncomment the wasm or android build.
# This is commented out by default because we don't want to build it as a library by default
# The CI has a script that does sed "s/#wasm# //" to generate the wasm build.

#wasm# [lib]
#wasm# path = "main.rs"
#wasm# crate-type = ["cdylib"]

#wasm# [target.'cfg(target_arch = "wasm32")'.dependencies]
#wasm# wasm-bindgen = { version = "0.2" }
#wasm# web-sys = { version = "0.3", features=["console"] }
#wasm# console_error_panic_hook = "0.1.5"




5 changes: 1 addition & 4 deletions examples/printerdemo/rust/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,6 @@ pub fn main() {
#[cfg(target_os = "android")]
#[no_mangle]
fn android_main(app: i_slint_backend_android_activity::AndroidApp) {
slint::platform::set_platform(Box::new(
i_slint_backend_android_activity::AndroidPlatform::new(app),
))
.unwrap();
slint::android_init(app).unwrap();
main()
}
5 changes: 1 addition & 4 deletions examples/todo/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,10 @@ path = "main.rs"
name = "todo"

[dependencies]
slint = { path = "../../../api/rs/slint", features = ["serde"] }
slint = { path = "../../../api/rs/slint", features = ["serde", "backend-android-native-activity"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

[target.'cfg(target_os = "android")'.dependencies]
i-slint-backend-android-activity = { workspace = true, features = ["native-activity"] }

[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen = { version = "0.2" }
console_error_panic_hook = "0.1.5"
Expand Down
46 changes: 22 additions & 24 deletions examples/todo/rust/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,30 +106,28 @@ pub fn main() {

#[cfg(target_os = "android")]
#[no_mangle]
fn android_main(app: i_slint_backend_android_activity::AndroidApp) {
use i_slint_backend_android_activity::android_activity::{MainEvent, PollEvent};
slint::platform::set_platform(Box::new(
i_slint_backend_android_activity::AndroidPlatform::new_with_event_listener(app, |event| {
match event {
PollEvent::Main(MainEvent::SaveState { saver, .. }) => {
STATE.with(|state| -> Option<()> {
let todo_state = SerializedState::save(state.borrow().as_ref()?);
saver.store(&serde_json::to_vec(&todo_state).ok()?);
Some(())
});
}
PollEvent::Main(MainEvent::Resume { loader, .. }) => {
STATE.with(|state| -> Option<()> {
let bytes: Vec<u8> = loader.load()?;
let todo_state: SerializedState = serde_json::from_slice(&bytes).ok()?;
todo_state.restore(state.borrow().as_ref()?);
Some(())
});
}
_ => {}
};
}),
))
fn android_main(app: slint::android_activity::AndroidApp) {
use slint::android_activity::{MainEvent, PollEvent};
slint::android_init_with_event_listener(app, |event| {
match event {
PollEvent::Main(MainEvent::SaveState { saver, .. }) => {
STATE.with(|state| -> Option<()> {
let todo_state = SerializedState::save(state.borrow().as_ref()?);
saver.store(&serde_json::to_vec(&todo_state).ok()?);
Some(())
});
}
PollEvent::Main(MainEvent::Resume { loader, .. }) => {
STATE.with(|state| -> Option<()> {
let bytes: Vec<u8> = loader.load()?;
let todo_state: SerializedState = serde_json::from_slice(&bytes).ok()?;
todo_state.restore(state.borrow().as_ref()?);
Some(())
});
}
_ => {}
};
})
.unwrap();
main();
}
Expand Down

0 comments on commit 12124c6

Please sign in to comment.