| package com.cooliris.media; |
| |
| import java.io.File; |
| import java.net.URI; |
| import java.util.ArrayList; |
| |
| import android.content.ContentResolver; |
| import android.content.ContentUris; |
| import android.content.ContentValues; |
| import android.content.Context; |
| import android.database.ContentObserver; |
| import android.database.Cursor; |
| import android.media.ExifInterface; |
| import android.net.Uri; |
| import android.os.Environment; |
| import android.os.Handler; |
| import android.provider.MediaStore.Images; |
| import android.provider.MediaStore.Video; |
| import android.util.Log; |
| |
| import com.cooliris.cache.CacheService; |
| |
| public final class LocalDataSource implements DataSource { |
| private static final String TAG = "LocalDataSource"; |
| |
| public static final DiskCache sThumbnailCache = new DiskCache("local-image-thumbs"); |
| public static final DiskCache sThumbnailCacheVideo = new DiskCache("local-video-thumbs"); |
| |
| public static final String CAMERA_STRING = "Camera"; |
| public static final String DOWNLOAD_STRING = "download"; |
| public static final String CAMERA_BUCKET_NAME = Environment.getExternalStorageDirectory().toString() + "/DCIM/" + CAMERA_STRING; |
| public static final String DOWNLOAD_BUCKET_NAME = Environment.getExternalStorageDirectory().toString() + "/" + DOWNLOAD_STRING; |
| public static final int CAMERA_BUCKET_ID = getBucketId(CAMERA_BUCKET_NAME); |
| public static final int DOWNLOAD_BUCKET_ID = getBucketId(DOWNLOAD_BUCKET_NAME); |
| |
| public static boolean sObserverActive = false; |
| private boolean mDisableImages; |
| private boolean mDisableVideos; |
| |
| /** |
| * Matches code in MediaProvider.computeBucketValues. Should be a common |
| * function. |
| */ |
| public static int getBucketId(String path) { |
| return (path.toLowerCase().hashCode()); |
| } |
| |
| private Context mContext; |
| private ContentObserver mObserver; |
| |
| public LocalDataSource(Context context) { |
| mContext = context; |
| } |
| |
| public void setMimeFilter(boolean disableImages, boolean disableVideos) { |
| mDisableImages = disableImages; |
| mDisableVideos = disableVideos; |
| } |
| |
| public void loadMediaSets(final MediaFeed feed) { |
| if (mContext == null) { |
| return; |
| } |
| stopListeners(); |
| CacheService.loadMediaSets(feed, this, !mDisableImages, !mDisableVideos); |
| Handler handler = ((Gallery) mContext).getHandler(); |
| ContentObserver observer = new ContentObserver(handler) { |
| public void onChange(boolean selfChange) { |
| final boolean isPaused = ((Gallery) mContext).isPaused(); |
| if (isPaused) { |
| MediaSet mediaSet = feed.getCurrentSet(); |
| if (mediaSet != null) { |
| CacheService.markDirtyImmediate(mediaSet.mId); |
| refreshUI(feed, mediaSet.mId); |
| } |
| } |
| CacheService.senseDirty(mContext, new CacheService.Observer() { |
| public void onChange(long[] ids) { |
| if (!isPaused) |
| return; |
| if (ids != null) { |
| int numLongs = ids.length; |
| for (int i = 0; i < numLongs; ++i) { |
| refreshUI(feed, ids[i]); |
| } |
| } |
| } |
| }); |
| } |
| }; |
| |
| // Start listening. |
| Uri uriImages = Images.Media.EXTERNAL_CONTENT_URI; |
| Uri uriVideos = Video.Media.EXTERNAL_CONTENT_URI; |
| ContentResolver cr = mContext.getContentResolver(); |
| mObserver = observer; |
| cr.registerContentObserver(uriImages, true, observer); |
| cr.registerContentObserver(uriVideos, true, observer); |
| sObserverActive = true; |
| } |
| |
| public void shutdown() { |
| stopListeners(); |
| } |
| |
| private void stopListeners() { |
| ContentResolver cr = mContext.getContentResolver(); |
| if (mObserver != null) { |
| cr.unregisterContentObserver(mObserver); |
| } |
| sObserverActive = false; |
| } |
| |
| protected void refreshUI(MediaFeed feed, long setIdToUse) { |
| if (setIdToUse == Shared.INVALID) { |
| return; |
| } |
| if (feed.getMediaSet(setIdToUse) == null) { |
| MediaSet mediaSet = feed.addMediaSet(setIdToUse, this); |
| if (setIdToUse == CAMERA_BUCKET_ID) { |
| mediaSet.mName = CAMERA_STRING; |
| } else if (setIdToUse == DOWNLOAD_BUCKET_ID) { |
| mediaSet.mName = DOWNLOAD_STRING; |
| } |
| mediaSet.generateTitle(true); |
| } else { |
| MediaSet mediaSet = feed.replaceMediaSet(setIdToUse, this); |
| Log.i(TAG, "Replacing mediaset " + mediaSet.mName + " id " + setIdToUse + " current Id " + mediaSet.mId); |
| if (setIdToUse == CAMERA_BUCKET_ID) { |
| mediaSet.mName = CAMERA_STRING; |
| } else if (setIdToUse == DOWNLOAD_BUCKET_ID) { |
| mediaSet.mName = DOWNLOAD_STRING; |
| } |
| mediaSet.generateTitle(true); |
| } |
| } |
| |
| public void loadItemsForSet(final MediaFeed feed, final MediaSet parentSet, int rangeStart, int rangeEnd) { |
| // Quick load from the cache. |
| if (mContext == null || parentSet == null) { |
| return; |
| } |
| loadMediaItemsIntoMediaFeed(feed, parentSet, rangeStart, rangeEnd); |
| } |
| |
| private void loadMediaItemsIntoMediaFeed(final MediaFeed mediaFeed, final MediaSet set, int rangeStart, int rangeEnd) { |
| if (rangeEnd - rangeStart < 0) { |
| return; |
| } |
| CacheService.loadMediaItemsIntoMediaFeed(mediaFeed, set, rangeStart, rangeEnd, !mDisableImages, !mDisableVideos); |
| if (set.mId == CAMERA_BUCKET_ID) { |
| mediaFeed.moveSetToFront(set); |
| } |
| } |
| |
| public boolean performOperation(final int operation, final ArrayList<MediaBucket> mediaBuckets, final Object data) { |
| int numBuckets = mediaBuckets.size(); |
| ContentResolver cr = mContext.getContentResolver(); |
| switch (operation) { |
| case MediaFeed.OPERATION_DELETE: |
| for (int i = 0; i < numBuckets; ++i) { |
| MediaBucket bucket = mediaBuckets.get(i); |
| MediaSet set = bucket.mediaSet; |
| ArrayList<MediaItem> items = bucket.mediaItems; |
| if (set != null && items == null) { |
| // Remove the entire bucket. |
| final Uri uriImages = Images.Media.EXTERNAL_CONTENT_URI; |
| final Uri uriVideos = Video.Media.EXTERNAL_CONTENT_URI; |
| final String whereImages = Images.ImageColumns.BUCKET_ID + "=" + Long.toString(set.mId); |
| final String whereVideos = Video.VideoColumns.BUCKET_ID + "=" + Long.toString(set.mId); |
| cr.delete(uriImages, whereImages, null); |
| cr.delete(uriVideos, whereVideos, null); |
| CacheService.markDirty(mContext); |
| } |
| if (set != null && items != null) { |
| // We need to remove these items from the set. |
| int numItems = items.size(); |
| try { |
| for (int j = 0; j < numItems; ++j) { |
| MediaItem item = items.get(j); |
| cr.delete(Uri.parse(item.mContentUri), null, null); |
| } |
| } catch (Exception e) { |
| // If the database operation failed for any reason. |
| ; |
| } |
| set.updateNumExpectedItems(); |
| set.generateTitle(true); |
| CacheService.markDirty(mContext, set.mId); |
| } |
| } |
| break; |
| case MediaFeed.OPERATION_ROTATE: |
| for (int i = 0; i < numBuckets; ++i) { |
| MediaBucket bucket = mediaBuckets.get(i); |
| ArrayList<MediaItem> items = bucket.mediaItems; |
| if (items == null) { |
| continue; |
| } |
| float angleToRotate = ((Float) data).floatValue(); |
| if (angleToRotate == 0) { |
| return true; |
| } |
| int numItems = items.size(); |
| for (int j = 0; j < numItems; ++j) { |
| rotateItem(items.get(j), angleToRotate); |
| } |
| } |
| break; |
| } |
| return true; |
| } |
| |
| private void rotateItem(final MediaItem item, float angleToRotate) { |
| ContentResolver cr = mContext.getContentResolver(); |
| try { |
| int currentOrientation = (int) item.mRotation; |
| angleToRotate += currentOrientation; |
| float rotation = Shared.normalizePositive(angleToRotate); |
| String rotationString = Integer.toString((int) rotation); |
| |
| // Update the database entry. |
| ContentValues values = new ContentValues(); |
| values.put(Images.ImageColumns.ORIENTATION, rotationString); |
| try { |
| cr.update(Uri.parse(item.mContentUri), values, null, null); |
| } catch (Exception e) { |
| // If the database operation fails for any reason. |
| ; |
| } |
| |
| // Update the file EXIF information. |
| Uri uri = Uri.parse(item.mContentUri); |
| String uriScheme = uri.getScheme(); |
| if (uriScheme.equals("file")) { |
| ExifInterface exif = new ExifInterface(uri.getPath()); |
| exif.setAttribute(ExifInterface.TAG_ORIENTATION, Integer.toString(Shared.degreesToExifOrientation(rotation))); |
| exif.saveAttributes(); |
| } |
| |
| // Invalidate the cache entry. |
| CacheService.markDirty(mContext, item.mParentMediaSet.mId); |
| |
| // Update the object representation of the item. |
| item.mRotation = rotation; |
| } catch (Exception e) { |
| // System.out.println("Apparently not a JPEG"); |
| } |
| } |
| |
| public DiskCache getThumbnailCache() { |
| return sThumbnailCache; |
| } |
| |
| public static MediaItem createMediaItemFromUri(Context context, Uri target) { |
| MediaItem item = null; |
| long id = ContentUris.parseId(target); |
| ContentResolver cr = context.getContentResolver(); |
| String whereClause = Images.ImageColumns._ID + "=" + Long.toString(id); |
| try { |
| Cursor cursor = cr.query(Images.Media.EXTERNAL_CONTENT_URI, CacheService.PROJECTION_IMAGES, whereClause, null, null); |
| if (cursor != null) { |
| if (cursor.moveToFirst()) { |
| item = new MediaItem(); |
| CacheService.populateMediaItemFromCursor(item, cr, cursor, Images.Media.EXTERNAL_CONTENT_URI.toString() + "/"); |
| } |
| cursor.close(); |
| cursor = null; |
| } |
| } catch (Exception e) { |
| // If the database operation failed for any reason. |
| ; |
| } |
| return item; |
| } |
| |
| public static MediaItem createMediaItemFromFileUri(Context context, String fileUri) { |
| MediaItem item = null; |
| String filepath = new File(URI.create(fileUri)).toString(); |
| ContentResolver cr = context.getContentResolver(); |
| long bucketId = SingleDataSource.parseBucketIdFromFileUri(fileUri); |
| String whereClause = Images.ImageColumns.BUCKET_ID + "=" + bucketId + " AND " + Images.ImageColumns.DATA + "='" + filepath |
| + "'"; |
| try { |
| Cursor cursor = cr.query(Images.Media.EXTERNAL_CONTENT_URI, CacheService.PROJECTION_IMAGES, whereClause, null, null); |
| if (cursor != null) { |
| if (cursor.moveToFirst()) { |
| item = new MediaItem(); |
| CacheService.populateMediaItemFromCursor(item, cr, cursor, Images.Media.EXTERNAL_CONTENT_URI.toString() + "/"); |
| } |
| cursor.close(); |
| cursor = null; |
| } |
| } catch (Exception e) { |
| // If the database operation failed for any reason. |
| ; |
| } |
| return item; |
| } |
| } |