Skip to content

Commit

Permalink
Experimental Android support for eframe
Browse files Browse the repository at this point in the history
  • Loading branch information
parasyte committed Oct 28, 2024
1 parent 4e101d2 commit 5ca2b43
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 0 deletions.
38 changes: 38 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,23 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"

[[package]]
name = "android_log-sys"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ecc8056bf6ab9892dcd53216c83d1597487d7dacac16c8df6b877d127df9937"

[[package]]
name = "android_logger"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05b07e8e73d720a1f2e4b6014766e6039fd2e96a4fa44e2a78d0e1fa2ff49826"
dependencies = [
"android_log-sys",
"env_filter",
"log",
]

[[package]]
name = "android_system_properties"
version = "0.1.5"
Expand Down Expand Up @@ -1477,6 +1494,16 @@ dependencies = [
"syn 2.0.48",
]

[[package]]
name = "env_filter"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab"
dependencies = [
"log",
"regex",
]

[[package]]
name = "env_logger"
version = "0.10.0"
Expand Down Expand Up @@ -2091,6 +2118,17 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"

[[package]]
name = "hello_android"
version = "0.1.0"
dependencies = [
"android_logger",
"eframe",
"egui_extras",
"log",
"winit",
]

[[package]]
name = "hello_world"
version = "0.1.0"
Expand Down
16 changes: 16 additions & 0 deletions crates/eframe/src/epi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,16 @@ pub struct NativeOptions {
///
/// Defaults to true.
pub dithering: bool,

/// Android application for `winit`'s event loop.
///
/// This value is required on Android to correctly create the event loop. See
/// [`EventLoopBuilder::build`] and [`with_android_app`] for details.
///
/// [`EventLoopBuilder::build`]: winit::event_loop::EventLoopBuilder::build
/// [`with_android_app`]: winit::platform::android::EventLoopBuilderExtAndroid::with_android_app
#[cfg(target_os = "android")]
pub android_app: Option<winit::platform::android::activity::AndroidApp>,
}

#[cfg(not(target_arch = "wasm32"))]
Expand All @@ -383,6 +393,9 @@ impl Clone for NativeOptions {

persistence_path: self.persistence_path.clone(),

#[cfg(target_os = "android")]
android_app: self.android_app.clone(),

..*self
}
}
Expand Down Expand Up @@ -424,6 +437,9 @@ impl Default for NativeOptions {
persistence_path: None,

dithering: true,

#[cfg(target_os = "android")]
android_app: None,
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions crates/eframe/src/native/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,20 @@ use crate::{

// ----------------------------------------------------------------------------
fn create_event_loop(native_options: &mut epi::NativeOptions) -> Result<EventLoop<UserEvent>> {
#[cfg(target_os = "android")]
use winit::platform::android::EventLoopBuilderExtAndroid as _;

crate::profile_function!();
let mut builder = winit::event_loop::EventLoop::with_user_event();

#[cfg(target_os = "android")]
let mut builder =
builder.with_android_app(native_options.android_app.take().ok_or_else(|| {
crate::Error::AppCreation(Box::from(
"`NativeOptions` is missing required `android_app`",
))
})?);

if let Some(hook) = std::mem::take(&mut native_options.event_loop_builder) {
hook(&mut builder);
}
Expand Down
32 changes: 32 additions & 0 deletions examples/hello_android/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[package]
name = "hello_android"
version = "0.1.0"
authors = ["Emil Ernerfeldt <[email protected]>"]
license = "MIT OR Apache-2.0"
edition = "2021"
rust-version = "1.76"
publish = false

# `unsafe_code` is required for `#[no_mangle]`, disable workspace lints to workaround lint error.
# [lints]
# workspace = true

[lib]
crate-type = ["cdylib"]


[dependencies]
eframe = { workspace = true, features = [
"default",
"android-native-activity",
] }

# For image support:
egui_extras = { workspace = true, features = ["default", "image"] }

log = { workspace = true }
winit = { workspace = true }
android_logger = "0.14"

[package.metadata.android]
build_targets = [ "armv7-linux-androideabi", "aarch64-linux-android" ]
20 changes: 20 additions & 0 deletions examples/hello_android/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Hello world example for Android.

Use `cargo-apk` to build and run. Requires a patch to workaround [an upstream bug](https://github.com/rust-mobile/cargo-subcommand/issues/29).

One-time setup:

```sh
cargo install \
--git https://github.com/parasyte/cargo-apk.git \
--rev 282639508eeed7d73f2e1eaeea042da2716436d5 \
cargo-apk
```

Build and run:

```sh
cargo apk run -p hello_android
```

![](screenshot.png)
Binary file added examples/hello_android/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
65 changes: 65 additions & 0 deletions examples/hello_android/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#![cfg(target_os = "android")]
#![allow(rustdoc::missing_crate_level_docs)] // it's an example

use android_logger::Config;
use eframe::egui;
use log::LevelFilter;
use winit::platform::android::activity::AndroidApp;

#[no_mangle]
fn android_main(app: AndroidApp) {
// Log to android output
android_logger::init_once(Config::default().with_max_level(LevelFilter::Info));

let options = eframe::NativeOptions {
android_app: Some(app),
..Default::default()
};
eframe::run_native(
"My egui App",
options,
Box::new(|cc| {
// This gives us image support:
egui_extras::install_image_loaders(&cc.egui_ctx);

Ok(Box::<MyApp>::default())
}),
)
.unwrap()
}

struct MyApp {
name: String,
age: u32,
}

impl Default for MyApp {
fn default() -> Self {
Self {
name: "Arthur".to_owned(),
age: 42,
}
}
}

impl eframe::App for MyApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading("My egui Application");
ui.horizontal(|ui| {
let name_label = ui.label("Your name: ");
ui.text_edit_singleline(&mut self.name)
.labelled_by(name_label.id);
});
ui.add(egui::Slider::new(&mut self.age, 0..=120).text("age"));
if ui.button("Increment").clicked() {
self.age += 1;
}
ui.label(format!("Hello '{}', age {}", self.name, self.age));

ui.image(egui::include_image!(
"../../../crates/egui/assets/ferris.png"
));
});
}
}

0 comments on commit 5ca2b43

Please sign in to comment.