Android How to - Extend DocumentsProvider to provide image








The following code shows how to Extend DocumentsProvider to provide image.

Code revised from
Android Recipes:A Problem-Solution Approach
http://www.apress.com/9781430234135
ISBN13: 978-1-4302-3413-5

Example

Register provider in manifest file

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.java2s.myapplication3.app" >

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="java2s.com"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.java2s.myapplication3.app.MainActivity"
            android:label="java2s.com" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <provider
            android:name="com.java2s.myapplication3.app.MyProvider"
            android:authorities="com.androidrecipes.sharedocuments.images"
            android:grantUriPermissions="true"
            android:exported="true"
            android:permission="android.permission.MANAGE_DOCUMENTS">
            <!-- Unique filter the system will use to find published providers -->
            <intent-filter>
                <action android:name="android.content.action.DOCUMENTS_PROVIDER" />
            </intent-filter>
        </provider>


    </application>

</manifest>

Java code

//from  ww  w.  j  a v a  2s .  com

package com.java2s.myapplication3.app;

import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.graphics.Point;
import android.net.Uri;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
import android.provider.DocumentsProvider;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Set;

public class MyProvider extends DocumentsProvider {
    private static final String TAG = "ImageProvider";

    /* Cached recent selection */
    private static String sLastFilename;
    private static String sLastTitle;

    /* Default projection for a root when none supplied */
    private static final String[] DEFAULT_ROOT_PROJECTION = {
            Root.COLUMN_ROOT_ID, Root.COLUMN_MIME_TYPES,
            Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE,
            Root.COLUMN_SUMMARY, Root.COLUMN_DOCUMENT_ID,
            Root.COLUMN_AVAILABLE_BYTES
    };
    /* Default projection for documents when none supplied */
    private static final String[] DEFAULT_DOCUMENT_PROJECTION = {
            Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE,
            Document.COLUMN_DISPLAY_NAME, Document.COLUMN_LAST_MODIFIED,
            Document.COLUMN_FLAGS, Document.COLUMN_SIZE
    };

    private ArrayMap<String, String> mDocuments;

    @Override
    public boolean onCreate() {
        //Dummy data for our documents
        mDocuments = new ArrayMap<String, String>();
        mDocuments.put("logo1.png", "John Doe");
        mDocuments.put("logo2.png", "Jane Doe");
        mDocuments.put("logo3.png", "Jill Doe");

        //Dump asset images onto internal storage
        writeAssets(mDocuments.keySet());
        return true;
    }

    /*
     * Helper method to stream some dummy files out to the
     * internal storage directory
     */
    private void writeAssets(Set<String> filenames) {
        for(String name : filenames) {
            try {
                Log.d("ImageProvider", "Writing "+name+" to storage");
                InputStream in = getContext().getAssets().open(name);
                FileOutputStream out = getContext().openFileOutput(name, Context.MODE_PRIVATE);

                int size;
                byte[] buffer = new byte[1024];
                while ((size = in.read(buffer, 0, 1024)) >= 0) {
                    out.write(buffer, 0, size);
                }
                out.flush();
                out.close();
            } catch (IOException e) {
                Log.w(TAG, e);
            }
        }
    }

    /* Helper method to construct documentId from a file name */
    private String getDocumentId(String filename) {
        return "root:" + filename;
    }

    /*
     * Helper method to extract file name from a documentId.
     * Returns empty string for the "root" document.
     */
    private String getFilename(String documentId) {
        int split = documentId.indexOf(":");
        if (split < 0) {
            return "";
        }
        return documentId.substring(split+1);
    }

    /*
     * Called by the system to determine how many "providers" are
     * hosted here.  It is most common to only return one, via a
     * Cursor that has only one result row.
     */
    @Override
    public Cursor queryRoots(String[] projection) throws FileNotFoundException {
        if (projection == null) {
            projection = DEFAULT_ROOT_PROJECTION;
        }
        MatrixCursor result = new MatrixCursor(projection);
        //Add the single root for this provider
        MatrixCursor.RowBuilder builder = result.newRow();

        builder.add(Root.COLUMN_ROOT_ID, "root");
        builder.add(Root.COLUMN_TITLE, "Android Recipes");
        builder.add(Root.COLUMN_SUMMARY, "Android Recipes Documents Provider");
        builder.add(Root.COLUMN_ICON, R.drawable.ic_launcher);

        builder.add(Root.COLUMN_DOCUMENT_ID, "root:");

        builder.add(Root.COLUMN_FLAGS,
                //Results will only come from the local file system
                Root.FLAG_LOCAL_ONLY
                        //We support showing recently selected items
                        | Root.FLAG_SUPPORTS_RECENTS);
        builder.add(Root.COLUMN_MIME_TYPES, "image/*");
        builder.add(Root.COLUMN_AVAILABLE_BYTES, 0);

        return result;
    }

    /*
     * Called by the system to determine the child items for a given
     * parent.  Will be called for the root, and for each subdirectory
     * defined within.
     */
    @Override
    public Cursor queryChildDocuments(String parentDocumentId, String[] projection, String sortOrder)
            throws FileNotFoundException {
        if (projection == null) {
            projection = DEFAULT_DOCUMENT_PROJECTION;
        }
        MatrixCursor result = new MatrixCursor(projection);

        try {
            for(String key : mDocuments.keySet()) {
                addImageRow(result, mDocuments.get(key), key);
            }
        } catch (IOException e) {
            return null;
        }

        return result;
    }

    /*
     * Return the same information provided via queryChildDocuments(), but
     * just for the single documentId requested.
     */
    @Override
    public Cursor queryDocument(String documentId, String[] projection)
            throws FileNotFoundException {
        if (projection == null) {
            projection = DEFAULT_DOCUMENT_PROJECTION;
        }

        MatrixCursor result = new MatrixCursor(projection);

        try {
            String filename = getFilename(documentId);
            if (TextUtils.isEmpty(filename)) {
                //This is a query for root
                addRootRow(result);
            } else {
                addImageRow(result, mDocuments.get(filename), filename);
            }
        } catch (IOException e) {
            return null;
        }

        return result;
    }

    /*
     * Called to populate any recently used items from this
     * provider in the Recents picker UI.
     */
    @Override
    public Cursor queryRecentDocuments(String rootId, String[] projection)
            throws FileNotFoundException {
        if (projection == null) {
            projection = DEFAULT_DOCUMENT_PROJECTION;
        }

        MatrixCursor result = new MatrixCursor(projection);

        if (sLastFilename != null) {
            try {
                addImageRow(result, sLastTitle, sLastFilename);
            } catch (IOException e) {
                Log.w(TAG, e);
            }
        }
        Log.d(TAG, "Recents: "+result.getCount());
        //We'll return the last selected result to a recents query
        return result;
    }

    /*
     * Helper method to write the root into the supplied
     * Cursor
     */
    private void addRootRow(MatrixCursor cursor) {
        final MatrixCursor.RowBuilder row = cursor.newRow();

        row.add(Document.COLUMN_DOCUMENT_ID, "root:");
        row.add(Document.COLUMN_DISPLAY_NAME, "Root");
        row.add(Document.COLUMN_SIZE, 0);
        row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);

        long installed;
        try {
            installed = getContext().getPackageManager()
                    .getPackageInfo(getContext().getPackageName(), 0)
                    .firstInstallTime;
        } catch (NameNotFoundException e) {
            installed = 0;
        }
        row.add(Document.COLUMN_LAST_MODIFIED, installed);
        row.add(Document.COLUMN_FLAGS, 0);
    }

    /*
     * Helper method to write a specific image file into
     * the supplied Cursor
     */
    private void addImageRow(MatrixCursor cursor, String title, String filename) throws IOException {
        final MatrixCursor.RowBuilder row = cursor.newRow();

        AssetFileDescriptor afd = getContext().getAssets().openFd(filename);

        row.add(Document.COLUMN_DOCUMENT_ID, getDocumentId(filename));
        row.add(Document.COLUMN_DISPLAY_NAME, title);
        row.add(Document.COLUMN_SIZE, afd.getLength());
        row.add(Document.COLUMN_MIME_TYPE, "image/*");

        long installed;
        try {
            installed = getContext().getPackageManager()
                    .getPackageInfo(getContext().getPackageName(), 0)
                    .firstInstallTime;
        } catch (NameNotFoundException e) {
            installed = 0;
        }
        row.add(Document.COLUMN_LAST_MODIFIED, installed);
        row.add(Document.COLUMN_FLAGS, Document.FLAG_SUPPORTS_THUMBNAIL);
    }

    /*
     * Return a reference to an image asset the framework will use
     * in the items list for any document with the FLAG_SUPPORTS_THUMBNAIL
     * flag enabled.  This method is safe to block while downloading content.
     */
    @Override
    public AssetFileDescriptor openDocumentThumbnail(String documentId, Point sizeHint, CancellationSignal signal)
            throws FileNotFoundException {
        //We will load the thumbnail from the version on storage
        String filename = getFilename(documentId);
        //Create a file reference to the image on internal storage
        final File file = new File(getContext().getFilesDir(), filename);
        //Return a file descriptor wrapping the file reference
        final ParcelFileDescriptor pfd =
                ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
        return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH);
    }

    /*
     * Return a file descriptor to the document referenced by the supplied
     * documentId.  The client will use this descriptor to read the contents
     * directly.  This method is safe to block while downloading content.
     */
    @Override
    public ParcelFileDescriptor openDocument(String documentId, String mode, CancellationSignal signal)
            throws FileNotFoundException {
        //We will load the document itself from assets
        try {
            String filename = getFilename(documentId);
            //Return the file descriptor directly from APK assets
            AssetFileDescriptor afd = getContext().getAssets().openFd(filename);

            //Save this as the last selected document
            sLastFilename = filename;
            sLastTitle = mDocuments.get(filename);

            return afd.getParcelFileDescriptor();
        } catch (IOException e) {
            Log.w(TAG, e);
            return null;
        }
    }

    /*
     * This method is invoked when openDocument() receives a file descriptor
     * from assets.  Documents opened from standard files will not invoke
     * this method.
     */
    @Override
    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
        //Last segment of Uri is the documentId
        String filename = getFilename(uri.getLastPathSegment());

        AssetManager manager = getContext().getAssets();
        try {
            //Return the appropriate asset for the requested item
            AssetFileDescriptor afd = manager.openFd(filename);

            //Save this as the last selected document
            sLastFilename = filename;
            sLastTitle = mDocuments.get(filename);

            return afd;
        } catch (IOException e) {
            Log.w(TAG, e);
            return null;
        }
    }
}