How to make links in a webview to open in the default browser?


#1

I am using a WebView in my code and it displays some HTML email message, the message might contain some links in it. If I click on the link, the WebView navigates to that link. I want to prevent this behavior and want to enforce all links in the WebView to open in the default browser. I want to do this for Android and iOS.
I found some sample on developers.android and it suggests to make use of shouldOverrideUrlLoading() method.
https://developer.android.com/guide/webapps/webview.html
But I am not sure how to implement the same functionality in NativeScript and how something similar can be done in iOS.
Any help will be appreciated.


WebView links in new window
#2

Here’s a Gist that I put up. It’ll help you
@ssiddh


#3

Thanks buddy. You saved my day.


#4

Hello,
I tried to implement this snippet, but I have got an error showing up :
error TS2339: Property 'extend' does not exist on type 'typeof WebViewClient'
When I check with the IDE autocompletion, webkit property is not even referenced on the android object.

Do you know what could be the cause ?


#5

Have you installed tns-platform-declarations


#6

Yes I have got the tns-platform-declarations directory in my node_modules, and I have set

/// <reference path="./node_modules/tns-platform-declarations/ios.d.ts" />
/// <reference path="./node_modules/tns-platform-declarations/android.d.ts" />

in my references.d.ts file


#7

I found a dirty hack to get this working, but that didn’t solve the real problem.
I disabled the noEmitOnError property of my tsconfig, but that doesn’t tell me why typescript cannot find the references …


#8

Once I had similar issue, if you are using VS Code you might want to restart the VS Code Editor for it to recognize changes in references.d.ts


#9

I am using JetBrains PHP Storm, and I even restarted the computer, but the problem is still here, as if it was a TS compiler config issue


#10

That’s a real problem when building with webpack, compiler doesn’t find the extend property on WebViewClient, which made it impossible to build my app optimized with webpack …


#11

Hi @multishiv19 I have tried this as well but does not work for me. App is crashing when I tap on URL in WebView text. Are there any modules I have to include before this? Thanks


#12

May I know what do you see in the console when it crashes?


#13

Hi sorry for delay with reply.
Here is what I get in app console.

An uncaught Exception occurred on "main" thread.
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.Boolean.booleanValue()' on a null object reference
   at android.webkit.WebViewClient_page_107_38_.shouldOverrideUrlLoading(Unknown Source:29)
   at yk.b(PG:89)
   at org.chromium.android_webview.AwContentsClientBridge.shouldOverrideUrlLoading(PG:172)
   at org.chromium.base.SystemMessageHandler.nativeDoRunLoopOnce(Native Method)
   at org.chromium.base.SystemMessageHandler.handleMessage(PG:9)
   at android.os.Handler.dispatchMessage(Handler.java:105)
   at android.os.Looper.loop(Looper.java:164)
   at android.app.ActivityThread.main(ActivityThread.java:6798)
   at java.lang.reflect.Method.invoke(Native Method)
   at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)

And here is code sample I’m using:

exports.webViewLoaded = function(args) {
    var webview = args.object;      
    var TNSWebViewClient =
        android.webkit.WebViewClient.extend({               
            shouldOverrideUrlLoading: function(view, url) {
                // if (url != null && url.startsWith("http://")) {
                    console.log(url);
                    // use openUrl form utils module to open the page in a browser
                     utilityModule.openUrl("https://www.youtube.com/user/cmcosmeticmarketbih");
            }
        });
      if (1 === 1) {
        webview.android.getSettings().setDisplayZoomControls(false);
        webview.android.getSettings().setBuiltInZoomControls(false);
        webview.android.setWebViewClient(new TNSWebViewClient());
      } 
}

#14

This was the problem when chrome browser got updated to version 67. Which version of NS are you using? You need to update to N 4.1.


#15

See the example carefully, you have to return a boolean value from shouldOverrideUrlLoading function.


#16

Hi @nmongiya I am using CLI version 4.1.0, with SideKick and all latest updates since friday.

Here is what I tested last and no error are displayed but it does not get correct URL property.

Code:

exports.webViewLoaded = function(args) {
    var webview = args.object;
    var TNSWebViewClient =
        android.webkit.WebViewClient.extend({
            shouldOverrideUrlLoading: function(view, url) {
                console.log('Show url parameters: '+url);
                // if (url != null && url.startsWith("http://")) {
                    console.log(url);
                    // use openUrl form utils module to open the page in a browser
                    return true;
                // } else {
                //     return false;
                // }
            }

        });
    // if (isAndroid) {
      webview.android.getSettings().setDisplayZoomControls(false);
      webview.android.getSettings().setBuiltInZoomControls(false);
      webview.android.setWebViewClient(new TNSWebViewClient());
    // } 
}

As you can see, I had to comment few lines as it would not work at all if I do not do so. Because url.startsWith is not a function error shows up. Ok, I return true anyway. But thing is that console.log shows Show url parameters: vS@5dd8496 in console. It’s not URL formatted string. It looks encoded somehow. I have set two different URL-s and I’m getting different encoded string when clicked (taped).
That is why I can not pass it to openUrl, it’s not valid url.

In searching around, I came with this solution but it’s not perfect as it loads content and open browser in the same time.

// handling WebView load finish event
function onWebViewLoaded(webargs) {
const page = webargs.object.page;
const vm = page.bindingContext;
const webview = webargs.object;
vm.set("result", "WebView is still loading...");

webview.on(webViewModule.WebView.loadFinishedEvent, (args) => {
    let message = "Loading initiated, please wait....";
    if (!args.error) {
        message = `WebView is loaded: ${args.url}`;
    }
    else {
        message = `Error while loading: ${args.url} : ${args.error}`;
    }
    // vm.set("result", message);
    console.log(`WebView console log messages - ${message}`);
    if (args.url.indexOf('http://') === 0) {
      
        // Do something depending on the coordinates in the URL
        utilityModule.openUrl(args.url);
    }
});
}

This does open URL in browser but first loads url in webview. First solution looks more clean and I love it, but none of those two does not work as expected :confused:


#17

You are just logging the URL, you have to open it when you return true.

To avoid invalid urls, you have to check for the condition given in the example - see whether it starts with http.


#18

Thanks for fast reply. Yes I do pass url to utilityModule.openUrl but URL is not valid: vS@9f8a7b1
This does not looks as URL to me. utilityModule expects http:// , and all my URL-s are encoded (or what else I have no idea) to some vS@ … strings. That is what I get as URL.


#19

PS: When I use if (url != null && url.startsWith("http://")) I’m getting error TypeError: url.startsWith is not a function
I guess this is used to “filter” content and search for http:// in response. This does not work nider:

if ((url != null) && (url.indexOf('http://') === 0))

#20

Can you share the playground example so I can take a quick look?