blob: b0ae940b80db96865bfe445220ab6c8a71f7aad6 [file] [log] [blame]
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) {
}
}
}