package edu.vanderbilt.cs282.feisele.assignment7; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import android.content.ContentUris; import android.content.ContentValues; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.os.ParcelFileDescriptor; import edu.vanderbilt.cs282.feisele.assignment7.DownloadContentProviderSchema.ImageTable; /** * This content provider holds the images downloaded by the download service. It * does not store its images in the database itself, rather it stores meta-data * about the images in the database and the images are saved as files. These * files are accessed via the openFile() method. This is to accommodate the size * limitation on the cursor fields. * * @author "Fred Eisele" <phreed@gmail.com> * */ public class DownloadContentProvider extends LLContentProvider { static private final Logger logger = LoggerFactory .getLogger("class.provider.download"); private DownloadDatabaseHelper db = null; private File imageDirectory = null; @Override public boolean onCreate() { super.onCreate(); this.db = DownloadDatabaseHelper.newInstance(this.getContext()); this.imageDirectory = this.getContext().getCacheDir(); return true; } @Override public String getType(Uri uri) { super.getType(uri); final int match = DownloadContentProviderSchema.URI_MATCHER.match(uri); switch (match) { case ImageTable.PATH_TOKEN: return ImageTable.CONTENT_TYPE_DIR; case ImageTable.PATH_FOR_ID_TOKEN: return ImageTable.CONTENT_ITEM_TYPE; default: throw new UnsupportedOperationException("URI " + uri + " is not supported."); } } /** * The downloaded image file is large and may not fit in the cursor values. * Therefore the image is placed into a temporary file (see openFile) rather * than in the database. */ @Override public Uri insert(Uri uri, ContentValues values) { logger.debug("insert into=<{}> values=<{}>", uri, values); final SQLiteDatabase db = this.db.getWritableDatabase(); int token = DownloadContentProviderSchema.URI_MATCHER.match(uri); switch (token) { case ImageTable.PATH_TOKEN: { long id = db.insert(ImageTable.NAME, null, values); this.getContext().getContentResolver().notifyChange(uri, null); return ContentUris.withAppendedId(ImageTable.CONTENT_URI, id); } default: { throw new UnsupportedOperationException("URI: " + uri + " not supported."); } } } /** * This method is used to receive images. It is possible to save the images * in the sqlite database but this content provider places them in files. * The problem with this approach is that it requires more effort to delete * the files corresponding to the tuple. */ @Override public ParcelFileDescriptor openFile(Uri uri, String mode) { int imode = 0; try { if (mode.contains("w")) { imode |= ParcelFileDescriptor.MODE_WRITE_ONLY; } if (mode.contains("r")) { imode |= ParcelFileDescriptor.MODE_READ_ONLY; } if (mode.contains("+")) { imode |= ParcelFileDescriptor.MODE_APPEND; } } finally { } int token = DownloadContentProviderSchema.URI_MATCHER.match(uri); switch (token) { case ImageTable.PATH_FOR_ID_TOKEN: { final List<String> segments = uri.getPathSegments(); final File imageFile = new File(this.imageDirectory, segments.get(1)); logger.info("image file mode=<{}> path=<{}>, uri=<{}>", Integer.toHexString(imode), imageFile, uri); try { if (!imageFile.exists()) { imageFile.createNewFile(); } return ParcelFileDescriptor.open(imageFile, imode); } catch (FileNotFoundException ex) { logger.error("could not open file {}", imageFile, ex); } catch (IOException ex) { logger.error("could not create file {}", imageFile, ex); } } break; default: { throw new UnsupportedOperationException("URI: " + uri + " not supported."); } } logger.error("could not open file=<{}>", uri); return null; } /** * Used to obtain the meta data about the images. */ @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { logger.debug("reading from=<{}> where=<{}> args=<{}> columns=<{}>", uri, selection, selectionArgs, projection); final SQLiteDatabase db = this.db.getReadableDatabase(); final int match = DownloadContentProviderSchema.URI_MATCHER.match(uri); switch (match) { case ImageTable.PATH_TOKEN: { /** * retrieve the image meta-data */ final SQLiteQueryBuilder builder = new SQLiteQueryBuilder(); builder.setTables(ImageTable.NAME); return builder.query(db, projection, selection, selectionArgs, null, null, sortOrder); } default: return null; } } /** * Not implemented. */ @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { logger.debug("updating from=<{}> where=<{}> args=<{}> values=<{}>", uri, selection, selectionArgs, values); throw new UnsupportedOperationException("update uri: " + uri + " not supported."); } /** * Used to delete the meta-data. The images are left untouched. */ @Override public int delete(Uri uri, String selection, String[] selectionArgs) { logger.debug("deleting from=<{}> where=<{}> args=<{}>", uri, selection, selectionArgs); final SQLiteDatabase db = this.db.getWritableDatabase(); final int match = DownloadContentProviderSchema.URI_MATCHER.match(uri); switch (match) { case ImageTable.PATH_TOKEN: logger.trace("table=<{}>", ImageTable.NAME); return db.delete(ImageTable.NAME, selection, selectionArgs); default: return super.delete(uri, selection, selectionArgs); } } }