Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issue with loading local javascript files inside a webview

I had with an issue that has plagued me for days. It turned out that it was an Android glitch and has been submitted, confirmed, and hopefully will be fixed in a future release. Now I have found a solution that works for me, and will provide it below, however the solution is not perfect as it involves editing the phone gap source. Mainly my question is if someone can find a better solution to this issue.

The Bug:
There is a glitch when you attempt to load a page inside of a WebView on Android 3.0+. The glitch is that if that page references any local javascript files, you cannot append query data to the url. Basically

This works:
<script type="text/javascript" src="StaticJS.js"></script>

This does not work:
<script type="text/javascript" src="StaticJS.js?var=val"></script>

Why the hell would anyone want to do this since the file obviously can't do anything with the query vals? Well for me I have a phonegap application that loads a settings file via JSONP, however if a settings file is not specified it defaults to a local file. So yeah, the file can't process the query data but it would be nice to use the same file format and loading structure.


Solution 1 (Non-PhoneGap)

So there is an easy solution to this if the target android platform is 11(Honeycomb) or higher. (As long as you are careful and do not use any method that do not exists in any lower API levels this code will run on <11 apis, but you will still have to set 11 as your target)

Basically you add a WebViewClient to the WebView that utilizes the shouldInterceptRequest method to intercept the loading of local js files with query data attached.

import java.io.IOException;
import java.io.InputStream;

import android.content.res.AssetManager;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class PatchingWebViewClient extends WebViewClient{

    AssetManager am;

    public PatchingWebViewClient(AssetManager am){
        this.am = am;
    }

    @Override
    public WebResourceResponse shouldInterceptRequest (WebView view, String url){
        if(url.indexOf("file:///android_asset") == 0 && url.contains("?")){
            String filePath = url.substring(22, url.length());
            filePath = filePath.substring(0, filePath.indexOf("?"));
            try {
                InputStream is = am.open(filePath);
                WebResourceResponse wr = new WebResourceResponse("text/javascript", "UTF-8", is);
                return wr;
            } catch (IOException e) {
                return null;
            }
        }else{
            return null;
        }
    }

}

To set the WebViewClient, your code would look something like this:

import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebView;

public class CanWeBreakAWebViewActivity extends Activity {
    WebView mWebView;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mWebView = (WebView) findViewById(R.id.webview);
        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.setWebViewClient(new PatchingWebViewClient(this.getAssets()));
        mWebView.loadUrl("file:///android_asset/index.html");
    }
}

Solution 2 (PhoneGap)

Now for Phonegap I don't have a clean solution. My solution is to go and download the Phonegap source and edit the CordovaWebViewClient by adding the following method:

@Override
public WebResourceResponse shouldInterceptRequest (WebView view, String url){
    if(url.indexOf("file:///android_asset") == 0 && url.contains("?")){
        String filePath = url.substring(22, url.length());
        filePath = filePath.substring(0, filePath.indexOf("?"));
        try {
            InputStream is = ctx.getAssets().open(filePath);
            WebResourceResponse wr = new WebResourceResponse("text/javascript", "Cp1252", is);
            return wr;
        } catch (IOException e) {
            return null;
        }
    }else{
        return null;
    }
}

Solution 3 (Non-Existent)

This solution would hopefully be some easy to include class or tweak to the main activity so that you can use phone gap but could just use a .jar file of the code, making upgrades easier.

like image 474
jtymann Avatar asked Feb 15 '12 22:02

jtymann


1 Answers

Thanks for this post, you clued me in to the latest solution which is simply to use IceCreamCordovaWebViewClient.

@Override
    public void init() {
    super.init(webView, new IceCreamCordovaWebViewClient(this, webView), new CordovaChromeClient(this, webView));
}
like image 97
Clayton Rabenda Avatar answered Oct 08 '22 01:10

Clayton Rabenda