Paul Brower bio photo

Paul Brower

Android development for fun and profit.

Email Twitter Github Stackoverflow Codementor

Find further code and implementation here: https://github.com/browep/LocalFileContentProviderExample

Android Webviews use the powerful WebKit browser to render HTML and run javascript. You can use several methods to get content into WebViews, this tutorial will show you how to use ContentProviders to serve assets to WebViews. This approach allows you to serve any type of file content from anywhere on the filesystem that your application can access including your application file directory, SD card contents, public directories. Let’s create a simple activity that will load some HTML from the assets directory:

package com.github.browep.lfcp;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.webkit.WebChromeClient;
import android.webkit.WebView;

import java.io.*;

public class Main extends Activity {
    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // push some files to the files dir as an example
        try {

            for (String fileName : getAssets().list("to_copy")) {
                File outputFile = new File(getFilesDir().getPath() + "/" + fileName);

                FileOutputStream out = new FileOutputStream(outputFile);
                InputStream in = getAssets().open( "to_copy/" +fileName);
                // Transfer bytes from the input file to the output file
                copy(in,out);
                out.close();
                in.close();

            }
        } catch (Exception e) {
            Log.e("Main", "error with copying files", e);
        }

        setContentView(R.layout.main);
        WebView webView = (WebView) findViewById(R.id.webview);

        // js includes will not happen unless we enable JS
        webView.getSettings().setJavaScriptEnabled(true);

        // this allows for js alert windows
        webView.setWebChromeClient(new WebChromeClient());

        webView.loadUrl("file:///android_asset/main.html");

    }

    private static final int IO_BUFFER_SIZE = 8 * 1024;

    private static void copy(InputStream in, OutputStream out) throws IOException {
        byte[] b = new byte[IO_BUFFER_SIZE];
        int read;
        while ((read = in.read(b)) != -1) {
            out.write(b, 0, read);
        }
    }

}

This activity loads a main.xml file that contains a single webview. It then points that webview to the main.html file in the assets directory. It also copies some static content to the application specific files directory to use as an example.

The html will need a CSS file, an external Javascript file, and an image that are located in the applications files directory. Here’s the simple HTML:

<html>
 <head>

     <link rel="stylesheet" type="text/css" href="content://com.github.browep.lfcp/main.css" />

     <script type="text/javascript" src="content://com.github.browep.lfcp/main.js" > </script>

 </head>
<body>

<h1>Sample HTML</h1>
<img src="content://com.github.browep.lfcp/main.png" >

</body>

</html>

The “content://” protocol on the files indicates to the OS that the files are to be fetched from a content provider, the host on the link is treated as an Android “authority” which will declare in our AndroidManifest.xml using a “provider” element

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.github.browep.lfcp"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:label="@string/app_name" ><activity android:name="Main" android:label="@string/app_name"><intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> </intent-filter>     </activity>

        <provider android:name="com.github.browep.lfcp.LocalFileContentProvider"
                  android:authorities="com.github.browep.lfcp"
                />

    </application>
</manifest>

This tells the OS that your app is the authority for “com.github.browep.lfcp”. Multiple authorities can be delimited by a semi-colon. In the “provider” element we have also added a class name, this will be the class used to serve up the assets.

LocalFileContentProvider:

package com.github.browep.lfcp;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.util.Log;

import java.io.File;
import java.io.FileNotFoundException;

public class LocalFileContentProvider extends ContentProvider {
    @Override
    public ParcelFileDescriptor openFile(Uri uri, String mode) {

        Log.d("LocalFileContentProvider","fetching: " + uri);

        String path = getContext().getFilesDir().getAbsolutePath() + "/" + uri.getPath();
        File file = new File(path);
        ParcelFileDescriptor parcel = null;
        try {
            parcel = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
        } catch (FileNotFoundException e) {
            Log.e("LocalFileContentProvider", "uri " + uri.toString(), e);
        }
        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");
    }
}

Here we have overridden the openFile method. We are using the path to mean the path relative to the app’s file directory. We return a ParcelFileDescriptor object which the WebView will use to get the assets.

Find further code and implementation here: https://github.com/browep/LocalFileContentProviderExample