package org.sana.android.db; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import org.sana.android.content.ExifUtil; import org.sana.android.db.SanaDB.ImageSQLFormat; import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.UriMatcher; import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteQueryBuilder; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.ParcelFileDescriptor; import android.text.TextUtils; import android.util.Log; /** * Content provider for image content. * * @author Sana Development Team * */ public class ImageProvider extends ContentProvider { private static final String TAG = "ImageProvider"; public static final String VIEW_PARAMETER = "view"; public static final String THUMBNAIL_VIEW = "thumb"; private static final String IMAGE_TABLE_NAME = "images"; public static final String IMAGE_BUCKET_NAME = "/sdcard/dcim/sana/"; private static final int IMAGES = 1; private static final int IMAGE_ID = 2; private DatabaseHelper mOpenHelper; private static final UriMatcher sUriMatcher; private static HashMap<String,String> sImageProjectionMap; /** {@inheritDoc} */ @Override public boolean onCreate() { Log.i(TAG, "onCreate()"); mOpenHelper = new DatabaseHelper(getContext()); return true; } /** * Return a thumbnail Uri for a given ImageProvider item Uri. The returned * Uri will still be compatible with all ContentProvider methods and refers * to the same item that imageUri refers to. */ public static Uri getThumbUri(Uri imageUri) { Uri.Builder thumbBuilder = imageUri.buildUpon(); thumbBuilder.appendQueryParameter(VIEW_PARAMETER, THUMBNAIL_VIEW); return thumbBuilder.build(); } public static void correctOrientation(Context context, Uri uri){ Log.i(TAG, "correctOrientation()"); List<String> segments = uri.getPathSegments(); String imagePath = buildFilenameFromUri(uri); // Invalid URI if (TextUtils.isEmpty(imagePath)){ Log.w(TAG, "Invalid image uri"); return; } File src = new File(imagePath); if(src.exists()){ Bitmap bitmap = BitmapFactory.decodeFile(imagePath); try { OutputStream os =context.getContentResolver().openOutputStream (uri); bitmap = ExifUtil.rotateBitmap(src, bitmap); boolean saved = bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os); Log.d(TAG, "Compressed rotated bitmap: saved=" + saved); os.close(); bitmap.recycle(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } else { Log.e(TAG, "File not found: " + src); } } private static String basePath() { return "/data/data/org.sana.android/files/"; } private static String buildImageFilenameFromId(String imageId) { return basePath() + imageId +".jpg"; } private static String buildThumbnailFilenameFromId(String imageId) { return basePath() + "thumb_" + imageId + ".jpg"; } private static String buildFilenameFromUri(Uri uri) { List<String> segments = uri.getPathSegments(); // Invalid URI if (segments.size() != 2) return ""; String imageId = segments.get(1); String viewName = uri.getQueryParameter(VIEW_PARAMETER); if (THUMBNAIL_VIEW.equals(viewName)) { return ImageProvider.buildThumbnailFilenameFromId(imageId); } else { // default to image view return buildImageFilenameFromId(imageId); } } private boolean deleteFile(String imageId) { String filename = buildImageFilenameFromId(imageId); File f = new File(filename); boolean result = f.delete(); Log.i(TAG, "Deleting file for id " + imageId + " : " + filename + " " + (result ? "succeeded" : "failed")); filename = buildThumbnailFilenameFromId(imageId); f = new File(filename); boolean thumbResult = f.delete(); Log.i(TAG, "Deleting thumbnail for id " + imageId + " : " + filename + " " + (thumbResult ? "succeeded" : "failed")); return result && thumbResult; } private boolean deleteFile(Uri uri) { List<String> segments = uri.getPathSegments(); // Invalid URI if (segments.size() != 1) return true; String imageId = segments.get(1); return deleteFile(imageId); } /** {@inheritDoc} */ @Override public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { Log.i(TAG, "openFile()"); String filename = buildFilenameFromUri(uri); Log.d(TAG, "...filename='" + filename + "', mode: " + mode); File f = new File(filename); //Hack to get image to write to database int m = ParcelFileDescriptor.MODE_READ_ONLY; if ("w".equals(mode)) { m = ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_CREATE; } else if("rw".equals(mode) || "rwt".equals(mode)) { m = ParcelFileDescriptor.MODE_READ_WRITE; } return ParcelFileDescriptor.open(f,m); } /** {@inheritDoc} */ @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { Log.i(TAG, "query() uri="+uri.toString() + " projection=" + ((projection == null)?"":TextUtils.join(",",projection)) + ", selection="+selection + ", selectionArgs=" + ((selectionArgs == null)?"":TextUtils.join(",",selectionArgs))); SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); qb.setTables(IMAGE_TABLE_NAME); switch(sUriMatcher.match(uri)) { case IMAGES: break; case IMAGE_ID: qb.appendWhere(ImageSQLFormat._ID + "=" + uri.getPathSegments().get(1)); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } String orderBy; if(TextUtils.isEmpty(sortOrder)) { orderBy = ImageSQLFormat.DEFAULT_SORT_ORDER; } else { orderBy = sortOrder; } SQLiteDatabase db = mOpenHelper.getReadableDatabase(); Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy); c.setNotificationUri(getContext().getContentResolver(), uri); return c; } /** {@inheritDoc} */ @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { SQLiteDatabase db = mOpenHelper.getWritableDatabase(); int count = 0; switch(sUriMatcher.match(uri)) { case IMAGES: count = db.update(IMAGE_TABLE_NAME, values, selection, selectionArgs); break; case IMAGE_ID: String procedureId = uri.getPathSegments().get(1); count = db.update(IMAGE_TABLE_NAME, values, ImageSQLFormat._ID + "=" + procedureId + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ")" : ""), selectionArgs); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } getContext().getContentResolver().notifyChange(uri, null); return count; } /** {@inheritDoc} */ @Override public int delete(Uri uri, String selection, String[] selectionArgs) { Log.i(TAG, "delete(): uri="+ uri+", selection="+selection); SQLiteDatabase db = mOpenHelper.getWritableDatabase(); int count; switch (sUriMatcher.match(uri)) { case IMAGES: LinkedList<String> idList = new LinkedList<String>(); Cursor c = query(ImageSQLFormat.CONTENT_URI, new String[] { ImageSQLFormat._ID }, selection, selectionArgs, null); if(c.moveToFirst()) { while(!c.isAfterLast()) { String id = c.getString(c.getColumnIndex( ImageSQLFormat._ID)); idList.add(id); c.moveToNext(); } } c.deactivate(); count = db.delete(IMAGE_TABLE_NAME, selection, selectionArgs); for(String id : idList) { deleteFile(id); } break; case IMAGE_ID: String imageId = uri.getPathSegments().get(1); count = db.delete(IMAGE_TABLE_NAME, ImageSQLFormat._ID + "=" + imageId + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ")" : ""), selectionArgs); deleteFile(uri); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } getContext().getContentResolver().notifyChange(uri, null); return count; } /** {@inheritDoc} */ @Override public Uri insert(Uri uri, ContentValues initialValues) { if (sUriMatcher.match(uri) != IMAGES) { throw new IllegalArgumentException("Unknown URI " + uri); } ContentValues values; if(initialValues != null) { values = new ContentValues(initialValues); } else { values = new ContentValues(); } Long now = Long.valueOf(System.currentTimeMillis()); if(values.containsKey(ImageSQLFormat.CREATED_DATE) == false) { values.put(ImageSQLFormat.CREATED_DATE, now); } if(values.containsKey(ImageSQLFormat.MODIFIED_DATE) == false) { values.put(ImageSQLFormat.MODIFIED_DATE, now); } if(values.containsKey(ImageSQLFormat.ENCOUNTER_ID) == false) { values.put(ImageSQLFormat.ENCOUNTER_ID, ""); } if(values.containsKey(ImageSQLFormat.ELEMENT_ID) == false) { values.put(ImageSQLFormat.ELEMENT_ID, ""); } if(values.containsKey(ImageSQLFormat.FILE_URI) == false) { values.put(ImageSQLFormat.FILE_URI, ""); } if(values.containsKey(ImageSQLFormat.FILE_VALID) == false) { values.put(ImageSQLFormat.FILE_VALID, false); } if(values.containsKey(ImageSQLFormat.FILE_SIZE) == false) { values.put(ImageSQLFormat.FILE_SIZE, 0); } if(values.containsKey(ImageSQLFormat.UPLOAD_PROGRESS) == false) { values.put(ImageSQLFormat.UPLOAD_PROGRESS, 0); } if(values.containsKey(ImageSQLFormat.UPLOADED) == false) { values.put(ImageSQLFormat.UPLOADED, false); } SQLiteDatabase db = mOpenHelper.getWritableDatabase(); long rowId = db.insert(IMAGE_TABLE_NAME, ImageSQLFormat.ENCOUNTER_ID, values); if(rowId > 0) { String filename = rowId + ""; try { getContext().openFileOutput(filename, Context.MODE_PRIVATE).close(); } catch (FileNotFoundException e) { Log.e(TAG, "Couldn't make the file: " + e); } catch (IOException e) { Log.e(TAG, "Couldn't make the file: " + e); } String path = getContext().getFileStreamPath(filename).getAbsolutePath(); Log.i(TAG, "File path is : " + path); Uri noteUri = ContentUris.withAppendedId( ImageSQLFormat.CONTENT_URI, rowId); getContext().getContentResolver().notifyChange(noteUri, null); return noteUri; } throw new SQLException("Failed to insert row into " + uri); } /** {@inheritDoc} */ @Override public String getType(Uri uri) { Log.i(TAG, "getType(uri="+uri.toString()+")"); switch(sUriMatcher.match(uri)) { case IMAGES: return ImageSQLFormat.CONTENT_TYPE; case IMAGE_ID: return ImageSQLFormat.CONTENT_ITEM_TYPE; default: throw new IllegalArgumentException("Unknown URI " + uri); } } /** * Creates the table. * @param db the database to create the table in. */ public static void onCreateDatabase(SQLiteDatabase db) { Log.i(TAG, "Creating Image Table"); db.execSQL("CREATE TABLE " + IMAGE_TABLE_NAME + " (" + ImageSQLFormat._ID + " INTEGER PRIMARY KEY," + ImageSQLFormat.ENCOUNTER_ID + " TEXT," + ImageSQLFormat.ELEMENT_ID + " TEXT," + ImageSQLFormat.FILE_URI + " TEXT," + ImageSQLFormat.FILE_VALID + " INTEGER," + ImageSQLFormat.FILE_SIZE + " INTEGER," + ImageSQLFormat.UPLOAD_PROGRESS + " INTEGER," + ImageSQLFormat.UPLOADED + " INTEGER," + ImageSQLFormat.CREATED_DATE + " INTEGER," + ImageSQLFormat.MODIFIED_DATE + " INTEGER" + ");"); } /** * Updates this providers table * @param db the db to update in * @param oldVersion the current db version * @param newVersion the new db version */ public static void onUpgradeDatabase(SQLiteDatabase db, int oldVersion, int newVersion) { Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion); if (oldVersion == 1 && newVersion == 2) { // Do nothing } } static { sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); sUriMatcher.addURI(SanaDB.IMAGE_AUTHORITY, "images", IMAGES); sUriMatcher.addURI(SanaDB.IMAGE_AUTHORITY, "images/#", IMAGE_ID); sImageProjectionMap = new HashMap<String, String>(); sImageProjectionMap.put(ImageSQLFormat._ID, ImageSQLFormat._ID); sImageProjectionMap.put(ImageSQLFormat.ENCOUNTER_ID, ImageSQLFormat.ENCOUNTER_ID); sImageProjectionMap.put(ImageSQLFormat.ELEMENT_ID, ImageSQLFormat.ELEMENT_ID); sImageProjectionMap.put(ImageSQLFormat.FILE_URI, ImageSQLFormat.FILE_URI); sImageProjectionMap.put(ImageSQLFormat.FILE_VALID, ImageSQLFormat.FILE_VALID); sImageProjectionMap.put(ImageSQLFormat.FILE_SIZE, ImageSQLFormat.FILE_SIZE); sImageProjectionMap.put(ImageSQLFormat.UPLOAD_PROGRESS, ImageSQLFormat.UPLOAD_PROGRESS); sImageProjectionMap.put(ImageSQLFormat.UPLOADED, ImageSQLFormat.UPLOADED); sImageProjectionMap.put(ImageSQLFormat.CREATED_DATE, ImageSQLFormat.CREATED_DATE); sImageProjectionMap.put(ImageSQLFormat.MODIFIED_DATE, ImageSQLFormat.MODIFIED_DATE); } }