From d140eba7c9849372a6673b0d37bfe83948c4e530 Mon Sep 17 00:00:00 2001 From: Randommist <70176501+Randommist@users.noreply.github.com> Date: Mon, 3 Jul 2023 04:49:41 +0300 Subject: [PATCH] Added example for android (#368) --- Cargo.lock | 29 +++++ examples/minimal-winit-android/Cargo.toml | 27 +++++ examples/minimal-winit-android/README.md | 18 +++ examples/minimal-winit-android/src/lib.rs | 130 ++++++++++++++++++++++ img/minimal-winit-android.png | Bin 0 -> 12143 bytes 5 files changed, 204 insertions(+) create mode 100644 examples/minimal-winit-android/Cargo.toml create mode 100644 examples/minimal-winit-android/README.md create mode 100644 examples/minimal-winit-android/src/lib.rs create mode 100644 img/minimal-winit-android.png diff --git a/Cargo.lock b/Cargo.lock index ba470f1..7fbd4b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -88,6 +88,24 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" +[[package]] +name = "android_log-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e" + +[[package]] +name = "android_logger" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8619b80c242aa7bd638b5c7ddd952addeecb71f69c75e33f1d47b2804f8f883a" +dependencies = [ + "android_log-sys", + "env_logger", + "log", + "once_cell", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -1921,6 +1939,17 @@ dependencies = [ "winit_input_helper 0.14.1", ] +[[package]] +name = "minimal-winit-android" +version = "0.1.0" +dependencies = [ + "android_logger", + "env_logger", + "log", + "pixels", + "winit 0.28.6", +] + [[package]] name = "miniz_oxide" version = "0.6.2" diff --git a/examples/minimal-winit-android/Cargo.toml b/examples/minimal-winit-android/Cargo.toml new file mode 100644 index 0000000..39dddec --- /dev/null +++ b/examples/minimal-winit-android/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "minimal-winit-android" +version = "0.1.0" +authors = ["Randommist "] +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +log = "0.4.19" +pixels = { path = "../.." } +winit = { version = "0.28.6", features = ["android-native-activity"] } + +[target.'cfg(not(target_os = "android"))'.dependencies] +env_logger = "0.10.0" + +[target.'cfg(target_os = "android")'.dependencies] +android_logger = "0.11.0" + +[lib] +crate_type = ["cdylib"] + + +[package.metadata.android.signing.release] +path = "./path/to/KeyStoreFile.jks" +keystore_password = "password" diff --git a/examples/minimal-winit-android/README.md b/examples/minimal-winit-android/README.md new file mode 100644 index 0000000..75f7928 --- /dev/null +++ b/examples/minimal-winit-android/README.md @@ -0,0 +1,18 @@ +# Hello Android + +![Hello Android](../../img/minimal-winit-android.png) + +Minimal example to run on android using `winit` with `android-native-activity` feature + +## Running +``` +export ANDROID_HOME="path/to/sdk" +export ANDROID_NDK_HOME="path/to/ndk" + +rustup target add aarch64-linux-android +cargo install cargo-apk +``` +Connect your Android device via USB cable to your computer in debug mode and run the following command +``` +cargo apk run +``` diff --git a/examples/minimal-winit-android/src/lib.rs b/examples/minimal-winit-android/src/lib.rs new file mode 100644 index 0000000..8284928 --- /dev/null +++ b/examples/minimal-winit-android/src/lib.rs @@ -0,0 +1,130 @@ +#[cfg(target_os = "android")] +use winit::platform::android::activity::AndroidApp; + +use pixels::{Pixels, SurfaceTexture}; +use winit::event::Event; +use winit::event_loop::{ControlFlow, EventLoop, EventLoopBuilder}; +use winit::window::Window; + +const WIDTH: u32 = 320; +const HEIGHT: u32 = 240; +const BOX_SIZE: i16 = 64; + +/// Representation of the application state. In this example, a box will bounce around the screen. +struct World { + box_x: i16, + box_y: i16, + velocity_x: i16, + velocity_y: i16, +} + +fn _main(event_loop: EventLoop<()>) { + let mut window: Option = None; + let mut pixels: Option = None; + + let mut world = World::new(); + + event_loop.run(move |event, event_loop, control_flow| { + *control_flow = ControlFlow::Wait; + match event { + Event::Resumed => { + let _window = Window::new(event_loop).unwrap(); + let _pixels = { + let window_size = _window.inner_size(); + let surface_texture = + SurfaceTexture::new(window_size.width, window_size.height, &_window); + Pixels::new(WIDTH, HEIGHT, surface_texture).unwrap() + }; + _window.request_redraw(); + window = Some(_window); + pixels = Some(_pixels); + } + Event::Suspended => { + pixels = None; + window = None; + } + Event::RedrawRequested(_) => { + if let (Some(pixels), Some(window)) = (&mut pixels, &window) { + world.draw(pixels.frame_mut()); + pixels.render().unwrap(); + window.request_redraw(); + } + } + _ => {} + } + if window.is_some() { + world.update(); + } + }); +} + +impl World { + /// Create a new `World` instance that can draw a moving box. + fn new() -> Self { + Self { + box_x: 24, + box_y: 16, + velocity_x: 1, + velocity_y: 1, + } + } + + /// Update the `World` internal state; bounce the box around the screen. + fn update(&mut self) { + if self.box_x <= 0 || self.box_x + BOX_SIZE > WIDTH as i16 { + self.velocity_x *= -1; + } + if self.box_y <= 0 || self.box_y + BOX_SIZE > HEIGHT as i16 { + self.velocity_y *= -1; + } + + self.box_x += self.velocity_x; + self.box_y += self.velocity_y; + } + + /// Draw the `World` state to the frame buffer. + /// + /// Assumes the default texture format: `wgpu::TextureFormat::Rgba8UnormSrgb` + fn draw(&self, frame: &mut [u8]) { + for (i, pixel) in frame.chunks_exact_mut(4).enumerate() { + let x = (i % WIDTH as usize) as i16; + let y = (i / WIDTH as usize) as i16; + + let inside_the_box = x >= self.box_x + && x < self.box_x + BOX_SIZE + && y >= self.box_y + && y < self.box_y + BOX_SIZE; + + let rgba = if inside_the_box { + [0x5e, 0x48, 0xe8, 0xff] + } else { + [0x48, 0xb2, 0xe8, 0xff] + }; + + pixel.copy_from_slice(&rgba); + } + } +} + +#[allow(dead_code)] +#[cfg(target_os = "android")] +#[no_mangle] +fn android_main(app: AndroidApp) { + use winit::platform::android::EventLoopBuilderExtAndroid; + android_logger::init_once(android_logger::Config::default().with_min_level(log::Level::Info)); + let event_loop = EventLoopBuilder::new().with_android_app(app).build(); + log::info!("Hello from android!"); + _main(event_loop); +} + +#[allow(dead_code)] +#[cfg(not(target_os = "android"))] +fn main() { + env_logger::builder() + .filter_level(log::LevelFilter::Info) // Default Log Level + .parse_default_env() + .init(); + let event_loop = EventLoopBuilder::new().build(); + log::info!("Hello from desktop!"); + _main(event_loop); +} diff --git a/img/minimal-winit-android.png b/img/minimal-winit-android.png new file mode 100644 index 0000000000000000000000000000000000000000..e682aa328e60ef25cd2043fd74a6ad1b24af5928 GIT binary patch literal 12143 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 Rst;LLI-+(s;gJ4~{{jjt>*xRg literal 0 HcmV?d00001