Repository: tauri-apps/wry (language: rust)

Issue:
[feat] support navigation handler on android
**Is your feature request related to a problem? Please describe.**
I would like to be able to apply a navigation handler to android clients.

**Describe the solution you'd like**
Add support for setting the navigation handler on android.

**Describe alternatives you've considered**
None, this is a feature parity request.

**Would you want to assign yourself to implement this feature?**
- [x] Yes
- [ ] No

**Additional context**
This should be pretty straight forward.




Discussion / hints:

Fix patch:
diff --git a/.cargo/android-navigation-handler.md b/.cargo/android-navigation-handler.md
new file mode 100644
index 000000000..67fc28696
--- /dev/null
+++ b/.cargo/android-navigation-handler.md
@@ -0,0 +1,5 @@
+---
+"wry": "patch"
+---
+
+Add support for `WebViewBuilder::with_navigation_handler` on Android.
diff --git a/src/webview/android/binding.rs b/src/webview/android/binding.rs
index 907816972..c474312a8 100644
--- a/src/webview/android/binding.rs
+++ b/src/webview/android/binding.rs
@@ -16,7 +16,7 @@ use tao::platform::android::ndk_glue::jni::{
 
 use super::{
   create_headers_map, ASSET_LOADER_DOMAIN, IPC, REQUEST_HANDLER, TITLE_CHANGE_HANDLER,
-  WITH_ASSET_LOADER,
+  URL_LOADING_OVERRIDE, WITH_ASSET_LOADER,
 };
 
 fn handle_request(env: JNIEnv, request: JObject) -> Result<jobject, JniError> {
@@ -145,6 +145,28 @@ pub unsafe fn handleRequest(env: JNIEnv, _: JClass, request: JObject) -> jobject
   }
 }
 
+#[allow(non_snake_case)]
+pub unsafe fn shouldOverride(env: JNIEnv, _: JClass, url: JString) -> jboolean {
+  match env.get_string(url) {
+    Ok(url) => {
+      let url = url.to_string_lossy().to_string();
+      URL_LOADING_OVERRIDE
+        .get()
+        // We negate the result of the function because the logic for the android
+        // client is different from how the navigation_handler is defined.
+        //
+        // https://developer.android.com/reference/android/webkit/WebViewClient#shouldOverrideUrlLoading(android.webkit.WebView,%20android.webkit.WebResourceRequest)
+        .map(|f| !f.0(url))
+        .unwrap_or(false)
+    }
+    Err(e) => {
+      log::warn!("Failed to parse JString: {}", e);
+      false
+    }
+  }
+  .into()
+}
+
 pub unsafe fn ipc(env: JNIEnv, _: JClass, arg: JString) {
   match env.get_string(arg) {
     Ok(arg) => {
diff --git a/src/webview/android/kotlin/RustWebViewClient.kt b/src/webview/android/kotlin/RustWebViewClient.kt
index e9d1c592d..ef0305050 100644
--- a/src/webview/android/kotlin/RustWebViewClient.kt
+++ b/src/webview/android/kotlin/RustWebViewClient.kt
@@ -25,6 +25,14 @@ class RustWebViewClient(context: Context): WebViewClient() {
         }
     }
 
+    override fun shouldOverrideUrlLoading(
+        view: WebView,
+        request: WebResourceRequest
+    ): Boolean {
+        return shouldOverride(request.url.toString())
+    }
+
+
     companion object {
         init {
             System.loadLibrary("{{library}}")
@@ -34,6 +42,7 @@ class RustWebViewClient(context: Context): WebViewClient() {
     private external fun assetLoaderDomain(): String
     private external fun withAssetLoader(): Boolean
     private external fun handleRequest(request: WebResourceRequest): WebResourceResponse?
+    private external fun shouldOverride(url: String): Boolean
 
     {{class-extension}}
 }
diff --git a/src/webview/android/mod.rs b/src/webview/android/mod.rs
index ed39e5089..eca2e8751 100644
--- a/src/webview/android/mod.rs
+++ b/src/webview/android/mod.rs
@@ -74,6 +74,14 @@ macro_rules! android_binding {
       [],
       jstring
     );
+    android_fn!(
+      $domain,
+      $package,
+      RustWebViewClient,
+      shouldOverride,
+      [JString],
+      jboolean
+    );
     android_fn!($domain, $package, Ipc, ipc, [JString]);
     android_fn!(
       $domain,
@@ -90,6 +98,7 @@ pub static REQUEST_HANDLER: OnceCell<UnsafeRequestHandler> = OnceCell::new();
 pub static TITLE_CHANGE_HANDLER: OnceCell<UnsafeTitleHandler> = OnceCell::new();
 pub static WITH_ASSET_LOADER: OnceCell<bool> = OnceCell::new();
 pub static ASSET_LOADER_DOMAIN: OnceCell<String> = OnceCell::new();
+pub static URL_LOADING_OVERRIDE: OnceCell<UnsafeUrlLoadingOverride> = OnceCell::new();
 
 pub struct UnsafeIpc(Box<dyn Fn(&Window, String)>, Rc<Window>);
 impl UnsafeIpc {
@@ -120,6 +129,15 @@ impl UnsafeTitleHandler {
 unsafe impl Send for UnsafeTitleHandler {}
 unsafe impl Sync for UnsafeTitleHandler {}
 
+pub struct UnsafeUrlLoadingOverride(Box<dyn Fn(String) -> bool>);
+impl UnsafeUrlLoadingOverride {
+  pub fn new(f: Box<dyn Fn(String) -> bool>) -> Self {
+    Self(f)
+  }
+}
+unsafe impl Send for UnsafeUrlLoadingOverride {}
+unsafe impl Sync for UnsafeUrlLoadingOverride {}
+
 pub unsafe fn setup(env: JNIEnv, looper: &ForeignLooper, activity: GlobalRef) {
   // we must create the WebChromeClient here because it calls `registerForActivityResult`,
   // which gives an `LifecycleOwners must call register before they are STARTED.` error when called outside the onCreate hook
@@ -306,6 +324,10 @@ impl InnerWebView {
       TITLE_CHANGE_HANDLER.get_or_init(move || UnsafeTitleHandler::new(i, w));
     }
 
+    if let Some(i) = attributes.navigation_handler {
+      URL_LOADING_OVERRIDE.get_or_init(move || UnsafeUrlLoadingOverride::new(i));
+    }
+
     Ok(Self { window })
   }
 


Test patch: