Skip to content

Commit

Permalink
Add initial android port (#510)
Browse files Browse the repository at this point in the history
* Add initial android port

* Add run method

* Add ipc handler

* Cargo fmt

* Update build CI

* Remove used imports
  • Loading branch information
Ngo Iok Ui (Wu Yu Wei) committed Mar 10, 2022
1 parent 93b57e1 commit 6fb9b82
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 13 deletions.
12 changes: 9 additions & 3 deletions .github/workflows/build.yml
Expand Up @@ -13,6 +13,7 @@ jobs:
- { target: x86_64-unknown-linux-gnu, os: ubuntu-latest }
- { target: x86_64-apple-darwin, os: macos-latest }
- { target: aarch64-apple-ios, os: macos-latest }
- { target: aarch64-linux-android, os: ubuntu-latest }

runs-on: ${{ matrix.platform.os }}

Expand All @@ -25,13 +26,13 @@ jobs:
target: ${{ matrix.platform.target }}

- name: install webkit2gtk (ubuntu only)
if: matrix.platform.os == 'ubuntu-latest'
if: contains(matrix.platform.target, 'gnu')
run: |
sudo apt-get update
sudo apt-get install -y webkit2gtk-4.0 libgtksourceview-3.0-dev libappindicator3-dev
- name: install webview2 (windows only)
if: matrix.platform.os == 'windows-latest'
if: contains(matrix.platform.target, 'windows')
shell: pwsh
run: |
Invoke-WebRequest https://go.microsoft.com/fwlink/p/?LinkId=2124703 -OutFile installwebview.exe -UseBasicParsing
Expand Down Expand Up @@ -78,8 +79,13 @@ jobs:

- name: build tests and examples
shell: bash
if: (
!contains(matrix.platform.target, 'android') &&
!contains(matrix.platform.target, 'ios'))
run: cargo test --no-run --verbose --features tray --target ${{ matrix.platform.target }}

- name: run tests
if: (!contains(matrix.platform.target, 'ios'))
if: (
!contains(matrix.platform.target, 'android') &&
!contains(matrix.platform.target, 'ios'))
run: cargo test --verbose --features tray --target ${{ matrix.platform.target }}
3 changes: 3 additions & 0 deletions Cargo.toml
Expand Up @@ -81,3 +81,6 @@ cocoa = "0.24"
core-graphics = "0.22"
objc = "0.2"
objc_id = "0.1"

[target."cfg(target_os = \"android\")".dependencies]
jni = "0.18.0"
23 changes: 23 additions & 0 deletions src/application.rs
Expand Up @@ -9,4 +9,27 @@
//!
//! [tao]: https://crates.io/crates/tao

#[cfg(not(target_os = "android"))]
pub use tao::*;

// TODO Implement actual Window library of Android
#[cfg(target_os = "android")]
pub use tao::{dpi, error};

#[cfg(target_os = "android")]
pub mod window {
use tao::dpi::PhysicalSize;
pub use tao::window::BadIcon;

pub struct Window;

impl Window {
pub fn new() -> Self {
Self
}

pub fn inner_size(&self) -> PhysicalSize<u32> {
todo!()
}
}
}
3 changes: 3 additions & 0 deletions src/lib.rs
Expand Up @@ -180,4 +180,7 @@ pub enum Error {
InvalidMethod(#[from] InvalidMethod),
#[error("Infallible error, something went really wrong: {0}")]
Infallible(#[from] std::convert::Infallible),
#[cfg(target_os = "android")]
#[error("JNI error: {0}")]
JNIError(#[from] jni::errors::Error),
}
96 changes: 96 additions & 0 deletions src/webview/android/mod.rs
@@ -0,0 +1,96 @@
use std::{ffi::c_void, ptr::null_mut, rc::Rc, sync::RwLock};

use crate::{application::window::Window, Result};

use super::{WebContext, WebViewAttributes};

use jni::{
objects::{JClass, JObject},
sys::jobject,
JNIEnv,
};

use once_cell::sync::Lazy;

static IPC: Lazy<RwLock<UnsafeIpc>> = Lazy::new(|| RwLock::new(UnsafeIpc(null_mut())));

pub struct InnerWebView {
pub window: Rc<Window>,
pub attributes: WebViewAttributes,
}

impl InnerWebView {
pub fn new(
window: Rc<Window>,
attributes: WebViewAttributes,
_web_context: Option<&mut WebContext>,
) -> Result<Self> {
Ok(Self { window, attributes })
}

pub fn print(&self) {}

pub fn eval(&self, _js: &str) -> Result<()> {
Ok(())
}

pub fn focus(&self) {}

pub fn devtool(&self) {}

pub fn run(self, env: JNIEnv, _jclass: JClass, jobject: JObject) -> Result<jobject> {
let string_class = env.find_class("java/lang/String")?;
// let client = env.call_method(
// jobject,
// "getWebViewClient",
// "()Landroid/webkit/WebViewClient;",
// &[],
// )?;
let WebViewAttributes {
url,
initialization_scripts,
ipc_handler,
..
} = self.attributes;

// todo
// ipc too?
if let Some(i) = ipc_handler {
let i = UnsafeIpc(Box::into_raw(Box::new(i)) as *mut _);
let mut ipc = IPC.write().unwrap();
*ipc = i;
}

if let Some(u) = url {
let url = env.new_string(u)?;
env.call_method(jobject, "loadUrl", "(Ljava/lang/String;)V", &[url.into()])?;
}

// Return initialization scripts
let len = initialization_scripts.len();
let scripts = env.new_object_array(len as i32, string_class, env.new_string("")?)?;
for (idx, s) in initialization_scripts.into_iter().enumerate() {
env.set_object_array_element(scripts, idx as i32, env.new_string(s)?)?;
}
Ok(scripts)
}

pub fn ipc_handler(window: &Window, arg: String) {
let function = IPC.read().unwrap();
unsafe {
let ipc = function.0;
if !ipc.is_null() {
let ipc = &*(ipc as *mut Box<dyn Fn(&Window, String)>);
ipc(window, arg)
}
}
}
}

pub struct UnsafeIpc(*mut c_void);
unsafe impl Send for UnsafeIpc {}
unsafe impl Sync for UnsafeIpc {}

pub fn platform_webview_version() -> Result<String> {
todo!()
}
41 changes: 33 additions & 8 deletions src/webview/mod.rs
Expand Up @@ -8,6 +8,10 @@ mod web_context;

pub use web_context::WebContext;

#[cfg(target_os = "android")]
pub(crate) mod android;
#[cfg(target_os = "android")]
use android::*;
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
Expand All @@ -33,6 +37,12 @@ pub(crate) mod webview2;
#[cfg(target_os = "windows")]
use self::webview2::*;
use crate::Result;
#[cfg(target_os = "android")]
use jni::{
objects::{JClass, JObject},
sys::jobject,
JNIEnv,
};
#[cfg(target_os = "windows")]
use webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Controller;
#[cfg(target_os = "windows")]
Expand Down Expand Up @@ -324,21 +334,26 @@ pub struct WebView {

// Signal the Window to drop on Linux and Windows. On mac, we need to handle several unsafe code
// blocks and raw pointer properly.
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
impl Drop for WebView {
fn drop(&mut self) {
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
unsafe {
use crate::application::platform::unix::WindowExtUnix;
use gtk::prelude::WidgetExtManual;
self.window().gtk_window().destroy();
}
#[cfg(target_os = "windows")]
}
}

#[cfg(target_os = "windows")]
impl Drop for WebView {
fn drop(&mut self) {
unsafe {
DestroyWindow(HWND(self.window.hwnd() as _));
}
Expand Down Expand Up @@ -412,6 +427,16 @@ impl WebView {
#[cfg(not(target_os = "macos"))]
self.window.inner_size()
}

#[cfg(target_os = "android")]
pub fn run(self, env: JNIEnv, jclass: JClass, jobject: JObject) -> jobject {
self.webview.run(env, jclass, jobject).unwrap()
}

#[cfg(target_os = "android")]
pub fn ipc_handler(window: &Window, arg: String) {
InnerWebView::ipc_handler(window, arg)
}
}

/// An event enumeration sent to [`FileDropHandler`].
Expand Down
4 changes: 2 additions & 2 deletions src/webview/web_context.rs
Expand Up @@ -76,11 +76,11 @@ impl WebContextData {
}
}

#[cfg(target_os = "windows")]
#[cfg(any(target_os = "windows", target_os = "android"))]
#[derive(Debug)]
pub(crate) struct WebContextImpl;

#[cfg(target_os = "windows")]
#[cfg(any(target_os = "windows", target_os = "android"))]
impl WebContextImpl {
fn new(_data: &WebContextData) -> Self {
Self
Expand Down

0 comments on commit 6fb9b82

Please sign in to comment.