Saturday, February 28, 2009

How to display a local file in the Android WebView

For some reason WebView is not displaying local files found on sdcard or in other directories of an Android phone. It was doing so prior to the official 1.0 SDK release, but not anymore.
There was another way to display local files - by overriding WebViewClient.shouldOverrideUrlLoading, but since 1.0 SDK it's gone.

Right now community agrees that a relatively easy way to make WebView display local files is through a custom ContentProvider.

It's not quite obvious what needs to be overridden for this specific task. I couldn't find a complete example online. So I decided to share mine.

There are 2 steps
  1. Create new class that extends ContentProvider and override essentially 1 method openFile. Method onCreate can be empty returning true and the rest can just throw UnsupportedOperationException.
  2. Add a provider entry to AndroidManifest.xml.
Provider implementation

Note, that method constructUri is just a utility method that can be used to construct provider specific URI out of a file path.

package com.tourizo.android.content;

import java.io.*;

import android.content.*;
import android.database.*;
import android.net.*;
import android.os.*;

public class LocalFileContentProvider extends ContentProvider {
private static final String URI_PREFIX = "content://com.tourizo.android.localfile";

public static String constructUri(String url) {
Uri uri = Uri.parse(url);
return uri.isAbsolute() ? url : URI_PREFIX + url;
}

@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
File file = new File(uri.getPath());
ParcelFileDescriptor parcel = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
return parcel;
}

@Override
public boolean onCreate() {
return true;
}

@Override
public int delete(Uri uri, String s, String[] as) {
throw new UnsupportedOperationException("Not supported by this provider");
}

@Override
public String getType(Uri uri) {
throw new UnsupportedOperationException("Not supported by this provider");
}

@Override
public Uri insert(Uri uri, ContentValues contentvalues) {
throw new UnsupportedOperationException("Not supported by this provider");
}

@Override
public Cursor query(Uri uri, String[] as, String s, String[] as1, String s1) {
throw new UnsupportedOperationException("Not supported by this provider");
}

@Override
public int update(Uri uri, ContentValues contentvalues, String s, String[] as) {
throw new UnsupportedOperationException("Not supported by this provider");
}

}



AndroidManifest.xml entry under application tag

        <provider android:name=".content.LocalFileContentProvider"
android:authorities="com.tourizo.android.localfile"
/>


I hope this was helpful.

Happy hacking! :)

2 comments:

  1. Hi, thanks for sharing! I've been searching everywhere for this information. But I think there is one missing step: how exactly do you load the html file into the webview? webview.loadUrl('???:???') How does it refer to the sdcard? And is there an 8kb limit? I want to show some html documentation which is downloaded from the server and stored in the sdcard. It can change over time, so I don't want to embed it as resources. Strange that one needs to 'hack' to display local html files.

    ReplyDelete
  2. Abe, for an example for how to hook it up with WebView, see

    http://www.techjini.com/blog/2009/01/10/android-tip-1-contentprovider-accessing-local-file-system-from-webview-showing-image-in-webview-using-content/

    I think if your HTML data can have a link like

    <img src=content://com.tourizo.android.localfile/foo.jpg>

    ReplyDelete