Skip to content

Commit

Permalink
Experimentally render to an Android surface
Browse files Browse the repository at this point in the history
  • Loading branch information
maxammann committed May 13, 2022
1 parent c3edea2 commit a889831
Show file tree
Hide file tree
Showing 10 changed files with 221 additions and 11 deletions.
6 changes: 5 additions & 1 deletion android/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@ maplibre = { path = "../maplibre" }
maplibre-winit = { path = "../maplibre-winit", version = "0.0.1" }
env_logger = "0.9"
log = "0.4.16"
ndk-glue = "0.5.0" # version is required by winit
#ndk-glue = "0.5.0" # version is required by winit
ndk-glue = { git = "https://github.com/rust-windowing/android-ndk-rs.git", branch = "native-window-jni-surface" }
ndk = { git = "https://github.com/rust-windowing/android-ndk-rs.git", branch = "native-window-jni-surface" }
jni = "0.19.0"
raw-window-handle = "0.4"
libc = "0.2"

[lib]
#name = "maplibre_android" Currently not supported: https://github.com/rust-windowing/android-ndk-rs/issues/136
Expand Down
17 changes: 17 additions & 0 deletions android/gradle/.idea/deploymentTargetDropDown.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions android/gradle/.idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions android/gradle/demo/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,13 @@
<!-- Tell NativeActivity the name of our .so -->
<meta-data android:name="android.app.lib_name"
android:value="maplibre_android"/>
</activity>
<activity android:name=".MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".MainActivity" android:exported="true">

</activity>
</application>

</manifest>
32 changes: 30 additions & 2 deletions android/gradle/demo/src/main/java/com/example/demo/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,15 +1,43 @@
package com.example.demo

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.SurfaceHolder
import android.view.SurfaceView
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import org.maplibre_rs.MapLibreRs

// Currently not used. Instead the NativeActivity referenced in AndroidManifest.xml is used.

class MainActivity : AppCompatActivity() {

var mSurfaceView1: SurfaceView? = null
var mSurfaceHolder1: SurfaceHolder? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
MapLibreRs.android_main()

mSurfaceView1 = findViewById<View>(R.id.surfaceView1) as SurfaceView
mSurfaceHolder1 = mSurfaceView1!!.getHolder()

mSurfaceHolder1!!.addCallback(object : SurfaceHolder.Callback {
override fun surfaceCreated(p0: SurfaceHolder) {
Log.v("TAG", "surfaceCreated")
MapLibreRs.android_main(p0.surface)
}

override fun surfaceChanged(p0: SurfaceHolder, p1: Int, p2: Int, p3: Int) {
Log.v(
"TAG", "surfaceChanged format=" + p1 + ", width=" + p2 + ", height="
+ p3
)
}

override fun surfaceDestroyed(p0: SurfaceHolder) {
Log.v("TAG", "surfaceDestroyed")
}
})
}
}
3 changes: 3 additions & 0 deletions android/gradle/demo/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<SurfaceView
android:layout_width="333dp"
android:layout_height="224dp" android:id="@+id/surfaceView1"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
Expand Down
3 changes: 2 additions & 1 deletion android/gradle/lib/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ android {

cargo {
module = "../../"
targets = ["arm64", "x86_64"]
// targets = ["arm64", "x86_64"]
targets = ["x86_64"]
libname = "maplibre_android"
targetDirectory = "${module}/../target"
profile = "debug"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package org.maplibre_rs;

import android.view.Surface;

public class MapLibreRs {
public static native void android_main();
public static native void android_main(Surface surface);

static {
System.loadLibrary("maplibre_android");
Expand Down
54 changes: 51 additions & 3 deletions android/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
use jni::objects::JClass;
use crate::window::AndroidMapWindowConfig;
use jni::objects::{JClass, JObject};
use jni::JNIEnv;
use log::Level;
use maplibre::platform::http_client::ReqwestHttpClient;
use maplibre::platform::run_multithreaded;
use maplibre::platform::schedule_method::TokioScheduleMethod;
use maplibre::MapBuilder;
use maplibre_winit::winit::{WinitEventLoop, WinitMapWindow, WinitMapWindowConfig, WinitWindow};
use std::ffi::CString;
use ndk::native_window::NativeWindow;
use std::ffi::{CStr, CString};
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::os::unix::io::{FromRawFd, RawFd};
use std::thread;

mod window;

#[cfg(not(target_os = "android"))]
compile_error!("android works only on android.");
Expand All @@ -28,8 +36,48 @@ pub fn android_main() {
}

#[no_mangle]
pub extern "system" fn Java_org_maplibre_1rs_MapLibreRs_android_1main(env: JNIEnv, class: JClass) {
pub extern "system" fn Java_org_maplibre_1rs_MapLibreRs_android_1main(
env: JNIEnv,
class: JClass,
surface: JObject,
) {
env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));

let tag = CString::new("maplibre").unwrap();
let message = CString::new("maplibre WOORKING").unwrap();
ndk_glue::android_log(Level::Warn, &tag, &message);

unsafe {
let mut logpipe: [RawFd; 2] = Default::default();
libc::pipe(logpipe.as_mut_ptr());
libc::dup2(logpipe[1], libc::STDOUT_FILENO);
libc::dup2(logpipe[1], libc::STDERR_FILENO);
thread::spawn(move || {
let tag = CStr::from_bytes_with_nul(b"MapLibreStderr\0").unwrap();
let file = File::from_raw_fd(logpipe[0]);
let mut reader = BufReader::new(file);
let mut buffer = String::new();
loop {
buffer.clear();
if let Ok(len) = reader.read_line(&mut buffer) {
if len == 0 {
break;
} else if let Ok(msg) = CString::new(buffer.clone()) {
ndk_glue::android_log(Level::Info, tag, &msg);
}
}
}
});
}

run_multithreaded(async {
MapBuilder::new()
.with_map_window_config(AndroidMapWindowConfig::new(env, surface))
.with_http_client(ReqwestHttpClient::new(None))
.with_schedule_method(TokioScheduleMethod::new())
.build()
.initialize()
.await
.run()
})
}
107 changes: 107 additions & 0 deletions android/src/window.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use jni::objects::JObject;
use jni::JNIEnv;
use maplibre::error::Error;
use maplibre::io::scheduler::ScheduleMethod;
use maplibre::io::source_client::HTTPClient;
use ndk::native_window::NativeWindow;
use raw_window_handle::{AndroidNdkHandle, RawWindowHandle};
use std::marker::PhantomData;
use std::thread::sleep;
use std::time::Duration;

use maplibre::map_state::MapState;
use maplibre::window::{MapWindow, MapWindowConfig, Runnable, WindowSize};

pub struct AndroidNativeWindow {
window: NativeWindow,
}

pub struct AndroidMapWindowConfig<'a> {
env: JNIEnv<'a>,
surface: JObject<'a>,
}

unsafe impl raw_window_handle::HasRawWindowHandle for AndroidNativeWindow {
fn raw_window_handle(&self) -> RawWindowHandle {
let mut handle = AndroidNdkHandle::empty();
handle.a_native_window = unsafe { self.window.ptr().as_mut() as *mut _ as *mut _ };
RawWindowHandle::AndroidNdk(handle)
}
}

impl<'a> AndroidMapWindowConfig<'a> {
pub fn new(env: JNIEnv<'a>, surface: JObject<'a>) -> Self {
Self { env, surface }
}
}

impl<'a> MapWindowConfig for AndroidMapWindowConfig<'a> {
type MapWindow = AndroidMapWindow<'a>;
}

pub struct AndroidMapWindow<'a> {
window: AndroidNativeWindow,
phantom: PhantomData<&'a u32>,
}

impl AndroidMapWindow<'_> {
pub fn take_event_loop(&mut self) -> Option<()> {
Some(())
}
}

impl<'a, MWC, SM, HC> Runnable<MWC, SM, HC> for AndroidMapWindow<'a>
where
MWC: MapWindowConfig<MapWindow = AndroidMapWindow<'a>>,
SM: ScheduleMethod,
HC: HTTPClient,
{
fn run(mut self, mut map_state: MapState<MWC, SM, HC>, max_frames: Option<u64>) {
for i in 0..100 {
map_state.update_and_redraw();
sleep(Duration::from_millis(16))
}

match map_state.update_and_redraw() {
Ok(_) => {}
Err(Error::Render(e)) => {
eprintln!("{}", e);
}
e => eprintln!("{:?}", e),
};

map_state.recreate_surface(&self);
let size = self.size();
map_state.resize(size.width(), size.height()); // FIXME: Resumed is also called when the app launches for the first time. Instead of first using a "fake" inner_size() in State::new we should initialize with a proper size from the beginning
map_state.resume();
}
}

impl<'a> MapWindow for AndroidMapWindow<'a> {
type EventLoop = ();
type Window = AndroidNativeWindow;
type MapWindowConfig = AndroidMapWindowConfig<'a>;

fn create(map_window_config: &Self::MapWindowConfig) -> Self {
let window = unsafe {
NativeWindow::from_surface(
map_window_config.env.get_native_interface(),
map_window_config.surface.into_inner(),
)
}
.unwrap();

Self {
window: AndroidNativeWindow { window },
phantom: Default::default(),
}
}

fn size(&self) -> WindowSize {
WindowSize::new(100, 100).unwrap()
}

fn inner(&self) -> &Self::Window {
&self.window
}
}

0 comments on commit a889831

Please sign in to comment.