[Tut]Providing data in a ContentProvider

Tutorials with advanced 'difficulty' and more Lines of Code.

[Tut]Providing data in a ContentProvider

Postby MrSnowflake » Fri Dec 19, 2008 3:20 pm

[marq=left]openFileDescriptor() support for ContenProvider[/marq]

Hi guys,

today, I'm going to demonstrate how to make a ContentProvider which supports ContentResolver.openFileDescriptor(Uri, String) and both ContentResolver.openOutputStream(Uri) and ContentResolver.openInputStream(Uri). I'm not going to demonstrate how to make a basic ContentProvider as the Notepad example in the Sample dir of the SDK already does this.

I assume you have a basic content provider working, so retrieving, adding, deleting and updating data in the ContentProvider works. The thing we are going to do now is implement the ContentProvider.openFile(Uri, String), the default implementation is throwing a FileNotFoundExceptio("No files supported by provider at "+ uri) and thus we can't use it.

So start with putting this in your ContentProvider:
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. @Override
  2.  
  3. public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
  4.  
  5.   throw new IllegalArgumentException();
  6.  
  7. }
  8.  
  9.  
Parsed in 0.031 seconds, using GeSHi 1.0.8.4


Step 2 is making sure the user is querying for a specific item, and not the list. Using the NotePadProvider this would make our openFile() method look like:
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. @Override
  2.  
  3. public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
  4.  
  5.   if (sUriMatcher.match(uri) != NOTE_ID)
  6.  
  7.     throw new IllegalArgumentException("openFile not supported for directories");;
  8.  
  9.  
  10.  
  11.   throw new IllegalArgumentException();
  12.  
  13. }
  14.  
  15.  
Parsed in 0.031 seconds, using GeSHi 1.0.8.4
So now an exception will be thrown when you are trying to openFile() the notes directory, instead of a single note.

To be able to support files we are making use of the openFileHelper() provided in the ContentProvider. This makes it very easy to implement fileOpen(), but we need to adjust the database for this. We need to add a field name _data with type TEXT to our table. In case of the NotePadProvider this will make our NotePadProvider.DatabaseHelper.onCreate() look like this:
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. @Override
  2.  
  3. public void onCreate(SQLiteDatabase db) {
  4.  
  5.   db.execSQL("CREATE TABLE " + NOTES_TABLE_NAME + " ("
  6.  
  7.                     + Notes._ID + " INTEGER PRIMARY KEY,"
  8.  
  9.                     + "_data TEXT,"
  10.  
  11.                     + Notes.TITLE + " TEXT,"
  12.  
  13.                     + Notes.NOTE + " TEXT,"
  14.  
  15.                     + Notes.CREATED_DATE + " INTEGER,"
  16.  
  17.                     + Notes.MODIFIED_DATE + " INTEGER"
  18.  
  19.                     + ");");
  20.  
  21. }
Parsed in 0.037 seconds, using GeSHi 1.0.8.4
Ok I know, _data should be in fact a field of Notes, but for demoing this is a lot more clear (and easy for me).
Do NOT for get to ++ the DATABASE_VERSION, because only then the new table will be created.

Now our table is ready for file support, but now we need to fill the _data field with the filename of our file. We will do this in NotePadProvider.insert(). There is a section which inserts data into the database and then checks if the insert succeeded.
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.  
  2. SQLiteDatabase db = mOpenHelper.getWritableDatabase();
  3.  
  4. long rowId = db.insert(NOTES_TABLE_NAME, Notes.NOTE, values);
  5.  
  6. if (rowId > 0) {
  7.  
  8.     Uri noteUri = ContentUris.withAppendedId(NotePad.Notes.CONTENT_URI, rowId);
  9.  
  10.     getContext().getContentResolver().notifyChange(noteUri, null);
  11.  
  12.     return noteUri;
  13.  
  14. }
Parsed in 0.037 seconds, using GeSHi 1.0.8.4
This is where we are going to update this row and add some data to _data:
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.  
  2. SQLiteDatabase db = mOpenHelper.getWritableDatabase();
  3.  
  4. long rowId = db.insert(NOTES_TABLE_NAME, Notes.NOTE, values);
  5.  
  6. if (rowId > 0) {
  7.  
  8.     Uri noteUri = ContentUris.withAppendedId(NotePad.Notes.CONTENT_URI, rowId);
  9.  
  10.     getContext().getContentResolver().notifyChange(noteUri, null);
  11.  
  12.  
  13.  
  14.     values.put("_data", "/data/data/my.package/file"+rowId+".extension");
  15.  
  16.     rowId = db.update(MESSAGE_TABLE_NAME, values, "_id=?", new String[]{""+rowId});
  17.  
  18.     if (rowId > 0)
  19.  
  20.       return messageUri;
  21.  
  22.     else
  23.  
  24.       return null;
  25.  
  26.  
  27.  
  28.     return noteUri;
  29.  
  30. }
Parsed in 0.038 seconds, using GeSHi 1.0.8.4
This will update the just added row to have _data contain a generic name for the file. (You could also do this immediately but now we can use the _id of the newly added row.)

Only 1 thing is left: Updating NotePadProvider.openFile() to really return a ParcelFileDescriptor. And this is pretty easy:
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. @Override
  2.  
  3. public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
  4.  
  5.   if (sUriMatcher.match(uri) != NOTE_ID)
  6.  
  7.     throw new IllegalArgumentException("openFile not supported for directories");;
  8.  
  9.  
  10.  
  11.   try{
  12.  
  13.     return this.openFileHelper(uri, mode);
  14.  
  15.    catch (FileNotFoundException e) {
  16.  
  17.     Log.i(TAG, "File not found");
  18.  
  19.     throw new FileNotFoundException();
  20.  
  21.   }
  22.  
  23.  
  24.  
  25.   throw new IllegalArgumentException();
  26.  
  27. }
  28.  
  29.  
Parsed in 0.037 seconds, using GeSHi 1.0.8.4
When you have done all this, your ContentProvider is ready to serve files.

Usage:
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.  
  2. Uri note = ContentUris.withAppendedId(Notes.CONTENT_URI, 1);
  3.  
  4. ParcelFileDescriptor file = getContentResolver().openFileDescriptor(note, "r");
  5.  
  6.  
Parsed in 0.036 seconds, using GeSHi 1.0.8.4
File will now refer to the file described in _data of the first entry in our NotePadProver. The "r" means the file will be read-only (like in C), there is also a "rw", which is, of course, read-write.

If I missed something, or something is not clear, or just wrong, let me know.
User avatar
MrSnowflake
Moderator
Moderator
 
Posts: 1439
Joined: Sat Feb 16, 2008 3:11 pm
Location: Flanders, Belgium

Top

Return to Advanced Tutorials

Who is online

Users browsing this forum: No registered users and 2 guests