Navigando in rete si trovano diversi esempi su come creare un proprio Content Provider, purtroppo però tutti partono dal presupposto che i dati da condividere con altre applicazioni si trovino all’interno di un database.

Siccome ho incontrato non poche difficoltà nel cercare di aprire un semplice file pdf con un’applicazione esterna e siccome si trovano poche informazioni in rete pubblico qui il sistema che ho utilizzato per condividere un singolo file tramite Content Provier.

Prima di tutto è necessario dichiarare nell’Android Manifest quanto segue:

<provider android:name=".FileContentProvider" android:authorities="package.name" />

Dove “.FileContentProvider” è il nome del nostro Content Provider.

Di seguito il codice del file FileContentProvider.java

package package.name;
 
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
 
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.res.AssetManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.util.Log;
 
public class FileContentProvider extends ContentProvider {
 
	private Context ctx;
	private ParcelFileDescriptor parcel;
 
	@Override
	public boolean onCreate() {
		ctx = this.getContext();
		return true;
	}

Solite dichiarazioni e una cosa importante nell’OnCreate, è necessario definire il contesto poiché altrimenti non saremo in grado di richiamare l’AssetManager della nostra applicazione.

	@Override
	public ParcelFileDescriptor openFile(Uri uri, String mode) {
		try {
			AssetManager am = ctx.getAssets();
			InputStream is = am.open("file.pdf");
			File dir = new File("data/data/package.name/files/");
			dir.mkdirs();
			File file = new File("data/data/package.name/files/outFile.pdf");
			file.createNewFile();
			OutputStream out=new FileOutputStream(file);
			byte buf[]=new byte[1024];
			int len;
			while((len=is.read(buf))>0)
				out.write(buf,0,len);
			out.close();
			is.close();
			parcel = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE);
		}
		catch (Exception e) {
			Log.e("Exception",e.toString());
		}
 
		return parcel;
	}

Qui ce la parte complicata, i file presenti nella cartella “assets” vengono compressi e inseriti nel package, non sono quindi direttamente accessibili attraverso un path normale, per esempio NON possiamo riferirci ad un file nell’asset con “file:///data/data/package.name/assets/file.pdf” ma dobbiamo utilizzare un AssetManager che definiamo all’inizio del codice.

A questo punto abbiamo il file di origine, dobbiamo copiare il file all’interno di “data/data/package.name/files/”, per fare ciò dobbiamo utilizzare un OutputStream e copiare byte per byte.

Una volta copiato il file possiamo restituirlo come ParcelFileDescriptor.

 
	@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");
	}
 
}

Questi ultimi sono metodi di cui dobbiamo comunque fare l’override ma che non ci servono in quest’occasione.

Ora dalla nostra applicazione possiamo richiedere l’apertura di un file contenuto all’interno del nostro Package attraverso un’altra applicazione:

Intent i = new Intent();
i.setDataAndType(Uri.parse("content://package.name/file.pdf"), "application/pdf");
startActivity(i);

Per fare in modo che l’applicazione esterna utilizzi il nostro Content Provider dobbiamo fornirgli come Uri del file da aprire:

  • “content://” che dice all’applicazione esterna che deve richiamare un content provider
  • “package.name” che dice all’applicazione esterna quale content provider utilizzare
  • “file.pdf” che nell’esempio sopra esposto non serve a niente poichè il nostro content provider restituisce sempre e solo lo stesso file, ma se volessimo utilizzare un content provider per fornire all’esterno del nostro package più di un singolo file sappiate che questo valore viene passato alla variabile “uri” di openFile all’interno di FileContentProvider.