blob: 929fb8be76715081039efa00ebf78846471938de [file] [log] [blame]
/*
* Copyright (C) 2010 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.data;
import android.graphics.Bitmap;
import android.util.Log;
import com.android.gallery3d.app.GalleryContext;
import com.android.gallery3d.data.BlobCache.LookupRequest;
import com.android.gallery3d.picasa.PhotoEntry;
import com.android.gallery3d.util.ComboFuture;
import com.android.gallery3d.util.Future;
import com.android.gallery3d.util.FutureListener;
import com.android.gallery3d.util.Utils;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.util.Arrays;
// PicasaImage is an image in the Picasa account.
public class PicasaImage extends MediaItem {
private static final String TAG = "PicasaImage";
private static final int SUPPORTED_OPERATIONS = 0;
private final PicasaTask[] mTasks = new PicasaTask[MediaItem.TYPE_COUNT];
private final GalleryContext mContext;
private final PhotoEntry mData;
private final BlobCache mPicasaCache;
private final long mUniqueId;
public PicasaImage(GalleryContext context, PhotoEntry entry) {
mContext = context;
mData = entry;
mPicasaCache = mContext.getDataManager().getPicasaCache();
mUniqueId = DataManager.makeId(
DataManager.ID_PICASA_IMAGE, (int) entry.id);
}
public long getUniqueId() {
return mUniqueId;
}
public synchronized Future<Bitmap>
requestImage(int type, FutureListener<? super Bitmap> listener) {
if (mTasks[type] != null) {
// TODO: enable the check when cancelling is done
// throw new IllegalStateException();
} else {
URL photoUrl = getPhotoUrl(type);
if (mPicasaCache != null) {
// Try to get the image from cache.
LookupRequest request = new LookupRequest();
request.key = Utils.crc64Long(photoUrl.toString());
boolean isCached = false;
try {
isCached = mPicasaCache.lookup(request);
} catch (IOException e) {
Log.w(TAG, "IOException in getting an image from " +
"PicasaCache", e);
}
if (isCached) {
byte[] uri = Utils.getBytesInUtf8(photoUrl.toString());
if (isSameUri(uri, request.buffer)) {
Log.i(TAG, "Get Image from Cache (type, url): " + type +
" " + photoUrl.toString());
DecodeService service = mContext.getDecodeService();
return service.requestDecode(request.buffer, uri.length,
request.length - uri.length, null, listener);
}
}
}
// Get the image from Picasaweb instead.
mTasks[type] = new PicasaTask(type, photoUrl, listener);
}
return mTasks[type];
}
private boolean isSameUri(byte[] uri, byte[] buffer) {
int uriLength = uri.length;
if (uriLength > buffer.length) {
return false;
}
for (int i = 0; i < uriLength; ++i) {
if (uri[i] != buffer[i]) {
return false;
}
}
return true;
}
private URL getPhotoUrl(int type) {
URL url = null;
try {
switch (type) {
case MediaItem.TYPE_MICROTHUMBNAIL:
url = new URL(mData.thumbnailUrl);
break;
case MediaItem.TYPE_THUMBNAIL:
url = new URL(mData.screennailUrl);
break;
case MediaItem.TYPE_FULL_IMAGE:
url = new URL(mData.contentUrl);
break;
default:
throw new AssertionError();
}
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
return url;
}
private class PicasaTask extends ComboFuture<Bitmap> {
private final URL mUrl;
private final int mType;
public PicasaTask(
int type, URL url, FutureListener<? super Bitmap> listener) {
super(listener);
mType = type;
mUrl = url;
execute();
}
@Override
protected Future<?> executeNextTask(int step, Future<?> current)
throws Exception {
switch (step) {
case 0: {
DownloadService service = mContext.getDownloadService();
return service.requestDownload(mUrl, this);
}
case 1: {
byte[] downloadedImage = (byte[]) current.get();
// Insert the downloaded image to the cache if the image is
// not a full size one.
if ((mPicasaCache != null)
&& (mType != MediaItem.TYPE_FULL_IMAGE)) {
long cacheKey = Utils.crc64Long(mUrl.toString());
byte[] uri = Utils.getBytesInUtf8(mUrl.toString());
ByteBuffer buffer = ByteBuffer.allocate(
downloadedImage.length + uri.length);
buffer.put(uri);
buffer.put(downloadedImage);
mPicasaCache.insert(cacheKey, buffer.array());
}
// Decode the downloaded image.
DecodeService service = mContext.getDecodeService();
return service.requestDecode(downloadedImage, null, this);
}
case 2: {
synchronized (PicasaImage.this) {
mTasks[mType] = null;
}
break;
}
}
return null;
}
}
@Override
public int getSupportedOperations() {
return SUPPORTED_OPERATIONS;
}
@Override
public boolean supportOperation(int operation) {
return (operation & SUPPORTED_OPERATIONS) != 0;
}
@Override
public void delete() {
if (!supportOperation(MediaOperation.DELETE)) return;
Log.v(TAG, "Delete PicasaImage: ");
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
}
}
}