blob: 45f47a42c54eeafae33412c0000789af2f27550a [file] [log] [blame]
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.gallery3d.provider;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
import android.provider.MediaStore.Images.ImageColumns;
import android.util.Log;
import com.android.gallery3d.app.GalleryApp;
import com.android.gallery3d.common.AsyncTaskUtil;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.data.DataManager;
import com.android.gallery3d.data.MediaItem;
import com.android.gallery3d.data.MediaObject;
import com.android.gallery3d.data.MtpImage;
import com.android.gallery3d.data.Path;
import com.android.gallery3d.picasasource.PicasaSource;
import com.android.gallery3d.util.GalleryUtils;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
public class GalleryProvider extends ContentProvider {
private static final String TAG = "GalleryProvider";
public static final String AUTHORITY = "com.android.gallery3d.provider";
public static final Uri BASE_URI = Uri.parse("content://" + AUTHORITY);
public static interface PicasaColumns {
public static final String USER_ACCOUNT = "user_account";
public static final String PICASA_ID = "picasa_id";
}
private static final String[] SUPPORTED_PICASA_COLUMNS = {
PicasaColumns.USER_ACCOUNT,
PicasaColumns.PICASA_ID,
ImageColumns.DISPLAY_NAME,
ImageColumns.SIZE,
ImageColumns.MIME_TYPE,
ImageColumns.DATE_TAKEN,
ImageColumns.LATITUDE,
ImageColumns.LONGITUDE,
ImageColumns.ORIENTATION};
private DataManager mDataManager;
private static Uri sBaseUri;
public static String getAuthority(Context context) {
return context.getPackageName() + ".provider";
}
public static Uri getUriFor(Context context, Path path) {
if (sBaseUri == null) {
sBaseUri = Uri.parse("content://" + context.getPackageName() + ".provider");
}
return sBaseUri.buildUpon()
.appendEncodedPath(path.toString().substring(1)) // ignore the leading '/'
.build();
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException();
}
// TODO: consider concurrent access
@Override
public String getType(Uri uri) {
long token = Binder.clearCallingIdentity();
try {
Path path = Path.fromString(uri.getPath());
MediaItem item = (MediaItem) mDataManager.getMediaObject(path);
return item != null ? item.getMimeType() : null;
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public Uri insert(Uri uri, ContentValues values) {
throw new UnsupportedOperationException();
}
@Override
public boolean onCreate() {
GalleryApp app = (GalleryApp) getContext().getApplicationContext();
mDataManager = app.getDataManager();
return true;
}
// TODO: consider concurrent access
@Override
public Cursor query(Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder) {
long token = Binder.clearCallingIdentity();
try {
Path path = Path.fromString(uri.getPath());
MediaObject object = mDataManager.getMediaObject(path);
if (object == null) {
Log.w(TAG, "cannot find: " + uri);
return null;
}
if (PicasaSource.isPicasaImage(object)) {
return queryPicasaItem(object,
projection, selection, selectionArgs, sortOrder);
} else if (object instanceof MtpImage) {
return queryMtpItem((MtpImage) object,
projection, selection, selectionArgs, sortOrder);
} else {
return null;
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
private Cursor queryMtpItem(MtpImage image, String[] projection,
String selection, String[] selectionArgs, String sortOrder) {
Object[] columnValues = new Object[projection.length];
for (int i = 0, n = projection.length; i < n; ++i) {
String column = projection[i];
if (ImageColumns.DISPLAY_NAME.equals(column)) {
columnValues[i] = image.getName();
} else if (ImageColumns.SIZE.equals(column)){
columnValues[i] = image.getSize();
} else if (ImageColumns.MIME_TYPE.equals(column)) {
columnValues[i] = image.getMimeType();
} else if (ImageColumns.DATE_TAKEN.equals(column)) {
columnValues[i] = image.getDateInMs();
} else {
Log.w(TAG, "unsupported column: " + column);
}
}
MatrixCursor cursor = new MatrixCursor(projection);
cursor.addRow(columnValues);
return cursor;
}
private Cursor queryPicasaItem(MediaObject image, String[] projection,
String selection, String[] selectionArgs, String sortOrder) {
if (projection == null) projection = SUPPORTED_PICASA_COLUMNS;
Object[] columnValues = new Object[projection.length];
double latitude = PicasaSource.getLatitude(image);
double longitude = PicasaSource.getLongitude(image);
boolean isValidLatlong = GalleryUtils.isValidLocation(latitude, longitude);
for (int i = 0, n = projection.length; i < n; ++i) {
String column = projection[i];
if (PicasaColumns.USER_ACCOUNT.equals(column)) {
columnValues[i] = PicasaSource.getUserAccount(getContext(), image);
} else if (PicasaColumns.PICASA_ID.equals(column)) {
columnValues[i] = PicasaSource.getPicasaId(image);
} else if (ImageColumns.DISPLAY_NAME.equals(column)) {
columnValues[i] = PicasaSource.getImageTitle(image);
} else if (ImageColumns.SIZE.equals(column)){
columnValues[i] = PicasaSource.getImageSize(image);
} else if (ImageColumns.MIME_TYPE.equals(column)) {
columnValues[i] = PicasaSource.getContentType(image);
} else if (ImageColumns.DATE_TAKEN.equals(column)) {
columnValues[i] = PicasaSource.getDateTaken(image);
} else if (ImageColumns.LATITUDE.equals(column)) {
columnValues[i] = isValidLatlong ? latitude : null;
} else if (ImageColumns.LONGITUDE.equals(column)) {
columnValues[i] = isValidLatlong ? longitude : null;
} else if (ImageColumns.ORIENTATION.equals(column)) {
columnValues[i] = PicasaSource.getRotation(image);
} else {
Log.w(TAG, "unsupported column: " + column);
}
}
MatrixCursor cursor = new MatrixCursor(projection);
cursor.addRow(columnValues);
return cursor;
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
long token = Binder.clearCallingIdentity();
try {
if (mode.contains("w")) {
throw new FileNotFoundException("cannot open file for write");
}
Path path = Path.fromString(uri.getPath());
MediaObject object = mDataManager.getMediaObject(path);
if (object == null) {
throw new FileNotFoundException(uri.toString());
}
if (PicasaSource.isPicasaImage(object)) {
return PicasaSource.openFile(getContext(), object, mode);
} else if (object instanceof MtpImage) {
return openPipeHelper(null,
new MtpPipeDataWriter((MtpImage) object));
} else {
throw new FileNotFoundException("unspported type: " + object);
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException();
}
private static interface PipeDataWriter<T> {
void writeDataToPipe(ParcelFileDescriptor output, T args);
}
// Modified from ContentProvider.openPipeHelper. We are target at API LEVEL 10.
// But openPipeHelper is available in API LEVEL 11.
private static <T> ParcelFileDescriptor openPipeHelper(
final T args, final PipeDataWriter<T> func) throws FileNotFoundException {
try {
final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
AsyncTask<Object, Object, Object> task = new AsyncTask<Object, Object, Object>() {
@Override
protected Object doInBackground(Object... params) {
try {
func.writeDataToPipe(pipe[1], args);
return null;
} finally {
Utils.closeSilently(pipe[1]);
}
}
};
AsyncTaskUtil.executeInParallel(task, (Object[]) null);
return pipe[0];
} catch (IOException e) {
throw new FileNotFoundException("failure making pipe");
}
}
private final class MtpPipeDataWriter implements PipeDataWriter<Object> {
private final MtpImage mImage;
private MtpPipeDataWriter(MtpImage image) {
mImage = image;
}
@Override
public void writeDataToPipe(ParcelFileDescriptor output, Object args) {
OutputStream os = null;
try {
os = new ParcelFileDescriptor.AutoCloseOutputStream(output);
os.write(mImage.getImageData());
} catch (IOException e) {
Log.w(TAG, "fail to download: " + mImage.toString(), e);
} finally {
Utils.closeSilently(os);
}
}
}
}