package com.seafile.seadroid2.gallery;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.MediaStore;
import android.provider.MediaStore.Images;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.seafile.seadroid2.util.Utils;
/**
* ImageManager is used to retrieve and store images
* in the media content provider.
*/
public class ImageManager {
private static final String TAG = "ImageManager";
private static final Uri STORAGE_URI = Images.Media.EXTERNAL_CONTENT_URI;
private static final Uri VIDEO_STORAGE_URI = Uri.parse("content://media/external/video/media");
// ImageListParam specifies all the parameters we need to create an image
// list (we also need a ContentResolver).
public static class ImageListParam implements Parcelable {
public DataLocation mLocation;
public int mInclusion;
public int mSort;
public String mBucketId;
// This is only used if we are creating a single image list.
public Uri mSingleImageUri;
// This is only used if we are creating an empty image list.
public boolean mIsEmptyImageList;
public ImageListParam() {}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mLocation.ordinal());
out.writeInt(mInclusion);
out.writeInt(mSort);
out.writeString(mBucketId);
out.writeParcelable(mSingleImageUri, flags);
out.writeInt(mIsEmptyImageList ? 1 : 0);
}
private ImageListParam(Parcel in) {
mLocation = DataLocation.values()[in.readInt()];
mInclusion = in.readInt();
mSort = in.readInt();
mBucketId = in.readString();
mSingleImageUri = in.readParcelable(null);
mIsEmptyImageList = (in.readInt() != 0);
}
public String toString() {
return String.format("ImageListParam{loc=%s,inc=%d,sort=%d," +
"bucket=%s,empty=%b,single=%s}", mLocation, mInclusion,
mSort, mBucketId, mIsEmptyImageList, mSingleImageUri);
}
public static final Parcelable.Creator CREATOR
= new Parcelable.Creator() {
public ImageListParam createFromParcel(Parcel in) {
return new ImageListParam(in);
}
public ImageListParam[] newArray(int size) {
return new ImageListParam[size];
}
};
public int describeContents() {
return 0;
}
}
// Location
public static enum DataLocation { NONE, INTERNAL, EXTERNAL, ALL }
// Inclusion
public static final int INCLUDE_IMAGES = (1 << 0);
public static final int INCLUDE_DRM_IMAGES = (1 << 1);
public static final int INCLUDE_VIDEOS = (1 << 2);
// Sort
public static final int SORT_ASCENDING = 1;
public static final int SORT_DESCENDING = 2;
private static final String CAMERA_IMAGE_BUCKET_NAME =
Environment.getExternalStorageDirectory().toString()
+ "/DCIM";
public static final String CAMERA_IMAGE_BUCKET_ID =
getBucketId(CAMERA_IMAGE_BUCKET_NAME);
/**
* Matches code in MediaProvider.computeBucketValues. Should be a common
* function.
*/
public static String getBucketId(String path) {
return String.valueOf(path.toLowerCase().hashCode());
}
private static List<String> allBucketIds;
public static List<String> getAutoScannedPathList() {
String[] paths = {
"/DCIM",
"/DCIM/Camera",
"/DCIM/100MEDIA",
// Many Samsung phones mount the external sd card to /sdcard/external_sd
"/external_sd/DCIM",
"/external_sd/DCIM/Camera",
"/external_sd/DCIM/100MEDIA"
};
List<String> pathList = Lists.newArrayList();
for (String path : paths) {
String fullPath = Utils.pathJoin(Environment.getExternalStorageDirectory().getAbsolutePath(), path);
pathList.add(fullPath);
}
return pathList;
}
public static List<String> getAllBucketIds() {
if (allBucketIds == null) {
String[] paths = {
"/DCIM",
"/DCIM/Camera",
"/DCIM/100MEDIA",
// Many Samsung phones mount the external sd card to /sdcard/external_sd
"/external_sd/DCIM",
"/external_sd/DCIM/Camera",
"/external_sd/DCIM/100MEDIA"
};
List<String> ids = Lists.newArrayList();
for (String path : paths) {
String fullPath = Environment.getExternalStorageDirectory().toString() + path;
ids.add(getBucketId(fullPath));
}
allBucketIds = ImmutableList.copyOf(ids);
}
return allBucketIds;
}
/**
* @return true if the mimetype is an image mimetype.
*/
public static boolean isImageMimeType(String mimeType) {
return mimeType.startsWith("image/");
}
/**
* @return true if the mimetype is a video mimetype.
*/
/* This is commented out because isVideo is not calling this now.
public static boolean isVideoMimeType(String mimeType) {
return mimeType.startsWith("video/");
}
*/
/**
* @return true if the image is an image.
*/
public static boolean isImage(IImage image) {
return isImageMimeType(image.getMimeType());
}
/**
* @return true if the image is a video.
*/
public static boolean isVideo(IImage image) {
// This is the right implementation, but we use instanceof for speed.
//return isVideoMimeType(image.getMimeType());
return (image instanceof VideoObject);
}
// This is the factory function to create an image list.
public static IImageList makeImageList(ContentResolver cr,
ImageListParam param) {
DataLocation location = param.mLocation;
int inclusion = param.mInclusion;
int sort = param.mSort;
String bucketId = param.mBucketId;
Uri singleImageUri = param.mSingleImageUri;
boolean isEmptyImageList = param.mIsEmptyImageList;
if (isEmptyImageList || cr == null) {
return new EmptyImageList();
}
if (singleImageUri != null) {
return new SingleImageList(cr, singleImageUri);
}
// false ==> don't require write access
boolean haveSdCard = hasStorage(false);
// use this code to merge videos and stills into the same list
ArrayList<BaseImageList> l = Lists.newArrayList();
if (haveSdCard && location != DataLocation.INTERNAL) {
if ((inclusion & INCLUDE_IMAGES) != 0) {
l.add(new ImageList(cr, STORAGE_URI, sort, bucketId));
}
if ((inclusion & INCLUDE_VIDEOS) != 0) {
l.add(new VideoList(cr, VIDEO_STORAGE_URI, sort, bucketId));
}
}
if (location == DataLocation.INTERNAL || location == DataLocation.ALL) {
if ((inclusion & INCLUDE_IMAGES) != 0) {
l.add(new ImageList(cr,
Images.Media.INTERNAL_CONTENT_URI, sort, bucketId));
}
}
// Optimization: If some of the lists are empty, remove them.
// If there is only one remaining list, return it directly.
Iterator<BaseImageList> iter = l.iterator();
while (iter.hasNext()) {
BaseImageList sublist = iter.next();
if (sublist.isEmpty()) {
sublist.close();
iter.remove();
}
}
if (l.size() == 1) {
BaseImageList list = l.get(0);
return list;
}
ImageListUber uber = new ImageListUber(
l.toArray(new IImageList[l.size()]), sort);
return uber;
}
private static class EmptyImageList implements IImageList {
public void close() {
}
public HashMap<String, String> getBucketIds() {
return Maps.newHashMap();
}
public int getCount() {
return 0;
}
public boolean isEmpty() {
return true;
}
public IImage getImageAt(int i) {
return null;
}
public IImage getImageForUri(Uri uri) {
return null;
}
public boolean removeImage(IImage image) {
return false;
}
public boolean removeImageAt(int i) {
return false;
}
public int getImageIndex(IImage image) {
throw new UnsupportedOperationException();
}
}
public static ImageListParam getImageListParam(DataLocation location,
int inclusion, int sort, String bucketId) {
ImageListParam param = new ImageListParam();
param.mLocation = location;
param.mInclusion = inclusion;
param.mSort = sort;
param.mBucketId = bucketId;
return param;
}
public static ImageListParam getSingleImageListParam(Uri uri) {
ImageListParam param = new ImageListParam();
param.mSingleImageUri = uri;
return param;
}
public static ImageListParam getEmptyImageListParam() {
ImageListParam param = new ImageListParam();
param.mIsEmptyImageList = true;
return param;
}
public static IImageList makeEmptyImageList() {
return makeImageList(null, getEmptyImageListParam());
}
private static boolean checkFsWritable() {
// Create a temporary file to see whether a volume is really writeable.
// It's important not to put it in the root directory which may have a
// limit on the number of files.
String directoryName =
Environment.getExternalStorageDirectory().toString() + "/DCIM";
File directory = new File(directoryName);
if (!directory.isDirectory()) {
if (!directory.mkdirs()) {
return false;
}
}
File f = new File(directoryName, ".probe");
try {
// Remove stale file if any
if (f.exists()) {
f.delete();
}
if (!f.createNewFile()) {
return false;
}
f.delete();
return true;
} catch (IOException ex) {
return false;
}
}
public static boolean hasStorage(boolean requireWriteAccess) {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
if (requireWriteAccess) {
boolean writable = checkFsWritable();
return writable;
} else {
return true;
}
} else if (!requireWriteAccess
&& Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
return true;
}
return false;
}
private static Cursor query(ContentResolver resolver, Uri uri,
String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
try {
if (resolver == null) {
return null;
}
return resolver.query(
uri, projection, selection, selectionArgs, sortOrder);
} catch (UnsupportedOperationException ex) {
return null;
}
}
public static boolean isMediaScannerScanning(ContentResolver cr) {
boolean result = false;
Cursor cursor = query(cr, MediaStore.getMediaScannerUri(),
new String [] {MediaStore.MEDIA_SCANNER_VOLUME},
null, null, null);
if (cursor != null) {
if (cursor.getCount() == 1) {
cursor.moveToFirst();
result = "external".equals(cursor.getString(0));
}
cursor.close();
}
return result;
}
}