| package com.cooliris.media; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import android.content.ContentResolver; |
| import android.content.ContentUris; |
| import android.content.Context; |
| import android.database.Cursor; |
| import android.media.ExifInterface; |
| import android.net.Uri; |
| import android.os.Environment; |
| import android.provider.MediaStore; |
| import android.provider.MediaStore.Images; |
| import android.provider.MediaStore.Video; |
| import android.util.Log; |
| |
| import com.cooliris.cache.CacheService; |
| |
| // TODO: Merge SingleDataSource and LocalDataSource into one type if possible |
| |
| public class SingleDataSource implements DataSource { |
| private static final String TAG = "SingleDataSource"; |
| public final String mUri; |
| public final String mBucketId; |
| public boolean mDone; |
| public final boolean mSlideshow; |
| public final boolean mSingleUri; |
| public final boolean mAllItems; |
| public final DiskCache mDiskCache; |
| private Context mContext; |
| |
| public SingleDataSource(final Context context, final String uri, final boolean slideshow) { |
| this.mUri = uri; |
| mContext = context; |
| String bucketId = Uri.parse(uri).getQueryParameter("bucketId"); |
| if (bucketId != null && bucketId.length() > 0) { |
| mBucketId = bucketId; |
| } else { |
| mBucketId = null; |
| } |
| if (mBucketId == null) { |
| if (uri.equals(MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString())) { |
| mAllItems = true; |
| } else { |
| mAllItems = false; |
| } |
| } else { |
| mAllItems = false; |
| } |
| this.mSlideshow = slideshow; |
| mSingleUri = isSingleImageMode(uri) && mBucketId == null; |
| mDone = false; |
| mDiskCache = mUri.startsWith(MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString()) || mUri.startsWith("file://") ? LocalDataSource.sThumbnailCache |
| : null; |
| } |
| |
| public void shutdown() { |
| |
| } |
| |
| public static boolean isSingleImageMode(String uriString) { |
| return !uriString.equals(MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString()) |
| && !uriString.equals(MediaStore.Images.Media.INTERNAL_CONTENT_URI.toString()); |
| } |
| |
| public DiskCache getThumbnailCache() { |
| return mDiskCache; |
| } |
| |
| public void loadItemsForSet(MediaFeed feed, MediaSet parentSet, int rangeStart, int rangeEnd) { |
| if (parentSet.mNumItemsLoaded > 0 && mDone) { |
| return; |
| } |
| if (mSingleUri && !mDone) { |
| MediaItem item = new MediaItem(); |
| item.mId = 0; |
| item.mFilePath = ""; |
| item.setMediaType((isImage(mUri)) ? MediaItem.MEDIA_TYPE_IMAGE : MediaItem.MEDIA_TYPE_VIDEO); |
| if (mUri.startsWith(MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString())) { |
| MediaItem newItem = LocalDataSource.createMediaItemFromUri(mContext, Uri.parse(mUri)); |
| if (newItem != null) { |
| item = newItem; |
| String fileUri = new File(item.mFilePath).toURI().toString(); |
| parentSet.mName = Utils.getBucketNameFromUri(Uri.parse(fileUri)); |
| parentSet.mId = parseBucketIdFromFileUri(fileUri); |
| parentSet.generateTitle(true); |
| } |
| } else if (mUri.startsWith("file://")) { |
| MediaItem newItem = null; |
| int numRetries = 3; |
| do { |
| newItem = LocalDataSource.createMediaItemFromFileUri(mContext, mUri); |
| if (newItem == null) { |
| --numRetries; |
| try { |
| Thread.sleep(300); |
| } catch (InterruptedException e) { |
| ; |
| } |
| } |
| } while (newItem == null && numRetries >= 0); |
| if (newItem != null) { |
| item = newItem; |
| } else { |
| item.mContentUri = mUri; |
| item.mThumbnailUri = mUri; |
| item.mScreennailUri = mUri; |
| feed.setSingleImageMode(true); |
| } |
| } else { |
| item.mContentUri = mUri; |
| item.mThumbnailUri = mUri; |
| item.mScreennailUri = mUri; |
| feed.setSingleImageMode(true); |
| } |
| if (item != null) { |
| feed.addItemToMediaSet(item, parentSet); |
| // Parse EXIF orientation if a local file. |
| if (mUri.startsWith("file://")) { |
| try { |
| ExifInterface exif = new ExifInterface(Uri.parse(mUri).getPath()); |
| item.mRotation = Shared.exifOrientationToDegrees(exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, |
| ExifInterface.ORIENTATION_NORMAL)); |
| } catch (IOException e) { |
| Log.i(TAG, "Error reading Exif information, probably not a jpeg."); |
| } |
| } |
| // Try and get the date taken for this item. |
| long dateTaken = CacheService.fetchDateTaken(item); |
| if (dateTaken != -1L) { |
| item.mDateTakenInMs = dateTaken; |
| } |
| CacheService.loadMediaItemsIntoMediaFeed(feed, parentSet, rangeStart, rangeEnd, true, false); |
| ArrayList<MediaItem> items = parentSet.getItems(); |
| int numItems = items.size(); |
| for (int i = 1; i < numItems; ++i) { |
| MediaItem thisItem = items.get(i); |
| String filePath = Uri.fromFile(new File(thisItem.mFilePath)).toString(); |
| if (item.mId == thisItem.mId || ((item.mContentUri != null && thisItem.mContentUri != null) && (item.mContentUri.equals(thisItem.mContentUri) |
| || item.mContentUri.equals(filePath)))) { |
| items.remove(thisItem); |
| --parentSet.mNumItemsLoaded; |
| break; |
| } |
| } |
| } |
| parentSet.updateNumExpectedItems(); |
| parentSet.generateTitle(true); |
| } else if (mUri.equals(MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString())) { |
| final Uri uriImages = Images.Media.EXTERNAL_CONTENT_URI; |
| final ContentResolver cr = mContext.getContentResolver(); |
| String where = null; |
| Cursor cursor = cr.query(uriImages, CacheService.PROJECTION_IMAGES, where, null, null); |
| if (cursor != null && cursor.moveToFirst()) { |
| parentSet.setNumExpectedItems(cursor.getCount()); |
| do { |
| if (Thread.interrupted()) { |
| return; |
| } |
| final MediaItem item = new MediaItem(); |
| CacheService.populateMediaItemFromCursor(item, cr, cursor, CacheService.BASE_CONTENT_STRING_IMAGES); |
| feed.addItemToMediaSet(item, parentSet); |
| } while (cursor.moveToNext()); |
| if (cursor != null) { |
| cursor.close(); |
| cursor = null; |
| } |
| parentSet.updateNumExpectedItems(); |
| parentSet.generateTitle(true); |
| } |
| } else { |
| CacheService.loadMediaItemsIntoMediaFeed(feed, parentSet, rangeStart, rangeEnd, true, true); |
| } |
| mDone = true; |
| } |
| |
| public static long parseBucketIdFromFileUri(String uriString) { |
| // This is a local folder. |
| final Uri uri = Uri.parse(uriString); |
| final List<String> paths = uri.getPathSegments(); |
| final int numPaths = paths.size() - 1; |
| StringBuffer pathBuilder = new StringBuffer(Environment.getExternalStorageDirectory().toString()); |
| if (numPaths > 1) |
| pathBuilder.append("/"); |
| for (int i = 0; i < numPaths; ++i) { |
| String path = paths.get(i); |
| if (!"file".equals(path) && !"sdcard".equals(path)) { |
| pathBuilder.append(path); |
| if (i != numPaths - 1) { |
| pathBuilder.append("/"); |
| } |
| } |
| } |
| return LocalDataSource.getBucketId(pathBuilder.toString()); |
| } |
| |
| private static boolean isImage(String uriString) { |
| return !uriString.startsWith(MediaStore.Video.Media.EXTERNAL_CONTENT_URI.toString()); |
| } |
| |
| private static long parseIdFromContentUri(String uri) { |
| try { |
| long id = ContentUris.parseId(Uri.parse(uri)); |
| return id; |
| } catch (Exception e) { |
| return 0; |
| } |
| } |
| |
| public void loadMediaSets(MediaFeed feed) { |
| MediaSet set = null; // Dummy set. |
| boolean loadOtherSets = true; |
| if (mSingleUri) { |
| String name = Utils.getBucketNameFromUri(Uri.parse(mUri)); |
| long id = getBucketId(mUri); |
| set = feed.addMediaSet(id, this); |
| set.mName = name; |
| set.setNumExpectedItems(2); |
| set.generateTitle(true); |
| set.mPicasaAlbumId = Shared.INVALID; |
| if (this.getThumbnailCache() != LocalDataSource.sThumbnailCache) { |
| loadOtherSets = false; |
| } |
| } else if (mBucketId == null) { |
| // All the buckets. |
| set = feed.addMediaSet(0, this); // Create dummy set. |
| set.mName = Utils.getBucketNameFromUri(Uri.parse(mUri)); |
| set.mId = LocalDataSource.getBucketId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString() + "/" + set.mName); |
| set.setNumExpectedItems(1); |
| set.generateTitle(true); |
| set.mPicasaAlbumId = Shared.INVALID; |
| } else { |
| CacheService.loadMediaSet(feed, this, Long.parseLong(mBucketId)); |
| ArrayList<MediaSet> sets = feed.getMediaSets(); |
| if (sets.size() > 0) |
| set = sets.get(0); |
| } |
| // We also load the other MediaSets |
| if (!mAllItems && set != null && loadOtherSets) { |
| if (!CacheService.isPresentInCache(set.mId)) { |
| CacheService.markDirty(mContext); |
| } |
| CacheService.loadMediaSets(feed, this, true, false); |
| } |
| } |
| |
| private long getBucketId(String uriString) { |
| if (uriString.startsWith("content://.")) { |
| return parseIdFromContentUri(uriString); |
| } else { |
| return parseBucketIdFromFileUri(uriString); |
| } |
| } |
| |
| public boolean performOperation(int operation, ArrayList<MediaBucket> mediaBuckets, Object data) { |
| int numBuckets = mediaBuckets.size(); |
| ContentResolver cr = mContext.getContentResolver(); |
| switch (operation) { |
| case MediaFeed.OPERATION_DELETE: |
| // TODO: Refactor this against LocalDataSource.performOperation. |
| 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) { |
| // TODO bulk delete |
| // 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(); |
| for (int j = 0; j < numItems; ++j) { |
| MediaItem item = items.get(j); |
| cr.delete(Uri.parse(item.mContentUri), null, null); |
| } |
| 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) { |
| try { |
| int currentOrientation = (int) item.mRotation; |
| angleToRotate += currentOrientation; |
| float rotation = Shared.normalizePositive(angleToRotate); |
| |
| // 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(); |
| } |
| |
| // Update the object representation of the item. |
| item.mRotation = rotation; |
| } catch (Exception e) { |
| } |
| } |
| |
| } |