blob: 0a18a2713dad20dcf907db4683e5b0657876f202 [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.app;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Message;
import com.android.gallery3d.common.BitmapUtils;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.data.MediaItem;
import com.android.gallery3d.data.Path;
import com.android.gallery3d.ui.BitmapScreenNail;
import com.android.gallery3d.ui.PhotoView;
import com.android.gallery3d.ui.ScreenNail;
import com.android.gallery3d.ui.SynchronizedHandler;
import com.android.gallery3d.ui.TileImageViewAdapter;
import com.android.gallery3d.util.Future;
import com.android.gallery3d.util.FutureListener;
import com.android.gallery3d.util.ThreadPool;
public class SinglePhotoDataAdapter extends TileImageViewAdapter
implements PhotoPage.Model {
private static final String TAG = "SinglePhotoDataAdapter";
private static final int SIZE_BACKUP = 1024;
private static final int MSG_UPDATE_IMAGE = 1;
private MediaItem mItem;
private boolean mHasFullImage;
private Future<?> mTask;
private Handler mHandler;
private PhotoView mPhotoView;
private ThreadPool mThreadPool;
private int mLoadingState = LOADING_INIT;
private BitmapScreenNail mBitmapScreenNail;
public SinglePhotoDataAdapter(
AbstractGalleryActivity activity, PhotoView view, MediaItem item) {
mItem = Utils.checkNotNull(item);
mHasFullImage = (item.getSupportedOperations() &
MediaItem.SUPPORT_FULL_IMAGE) != 0;
mPhotoView = Utils.checkNotNull(view);
mHandler = new SynchronizedHandler(activity.getGLRoot()) {
@Override
@SuppressWarnings("unchecked")
public void handleMessage(Message message) {
Utils.assertTrue(message.what == MSG_UPDATE_IMAGE);
if (mHasFullImage) {
onDecodeLargeComplete((ImageBundle) message.obj);
} else {
onDecodeThumbComplete((Future<Bitmap>) message.obj);
}
}
};
mThreadPool = activity.getThreadPool();
}
private static class ImageBundle {
public final BitmapRegionDecoder decoder;
public final Bitmap backupImage;
public ImageBundle(BitmapRegionDecoder decoder, Bitmap backupImage) {
this.decoder = decoder;
this.backupImage = backupImage;
}
}
private FutureListener<BitmapRegionDecoder> mLargeListener =
new FutureListener<BitmapRegionDecoder>() {
@Override
public void onFutureDone(Future<BitmapRegionDecoder> future) {
BitmapRegionDecoder decoder = future.get();
// cannot get large bitmap, then try to get thumb bitmap
if (decoder == null) {
if (mTask != null && !mTask.isCancelled()) {
Log.w(TAG, "fail to get region decoder, try to request thumb image");
mHasFullImage = false;
pause();
resume();
}
return;
}
int width = decoder.getWidth();
int height = decoder.getHeight();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = BitmapUtils.computeSampleSize(
(float) SIZE_BACKUP / Math.max(width, height));
Bitmap bitmap = decoder.decodeRegion(new Rect(0, 0, width, height), options);
mHandler.sendMessage(mHandler.obtainMessage(
MSG_UPDATE_IMAGE, new ImageBundle(decoder, bitmap)));
}
};
private FutureListener<Bitmap> mThumbListener =
new FutureListener<Bitmap>() {
@Override
public void onFutureDone(Future<Bitmap> future) {
mHandler.sendMessage(
mHandler.obtainMessage(MSG_UPDATE_IMAGE, future));
}
};
@Override
public boolean isEmpty() {
return false;
}
private void setScreenNail(Bitmap bitmap, int width, int height) {
mBitmapScreenNail = new BitmapScreenNail(bitmap);
setScreenNail(mBitmapScreenNail, width, height);
}
private void onDecodeLargeComplete(ImageBundle bundle) {
try {
setScreenNail(bundle.backupImage,
bundle.decoder.getWidth(), bundle.decoder.getHeight());
setRegionDecoder(bundle.decoder);
mPhotoView.notifyImageChange(0);
} catch (Throwable t) {
Log.w(TAG, "fail to decode large", t);
}
}
private void onDecodeThumbComplete(Future<Bitmap> future) {
try {
Bitmap backup = future.get();
if (backup == null) {
mLoadingState = LOADING_FAIL;
} else {
mLoadingState = LOADING_COMPLETE;
}
setScreenNail(backup, backup.getWidth(), backup.getHeight());
mPhotoView.notifyImageChange(0);
} catch (Throwable t) {
Log.w(TAG, "fail to decode thumb", t);
}
}
@Override
public void resume() {
if (mTask == null) {
if (mHasFullImage) {
mTask = mThreadPool.submit(
mItem.requestLargeImage(), mLargeListener);
} else {
mTask = mThreadPool.submit(
mItem.requestImage(MediaItem.TYPE_THUMBNAIL),
mThumbListener);
}
}
}
@Override
public void pause() {
Future<?> task = mTask;
if (!task.isDone()) {
task.cancel();
}
mTask = null;
if (mBitmapScreenNail != null) {
mBitmapScreenNail.recycle();
mBitmapScreenNail = null;
}
}
@Override
public void moveTo(int index) {
throw new UnsupportedOperationException();
}
@Override
public void getImageSize(int offset, PhotoView.Size size) {
if (offset == 0) {
size.width = mItem.getWidth();
size.height = mItem.getHeight();
} else {
size.width = 0;
size.height = 0;
}
}
@Override
public int getImageRotation(int offset) {
return (offset == 0) ? mItem.getFullImageRotation() : 0;
}
@Override
public ScreenNail getScreenNail(int offset) {
return (offset == 0) ? getScreenNail() : null;
}
@Override
public void setNeedFullImage(boolean enabled) {
// currently not necessary.
}
@Override
public boolean isCamera(int offset) {
return false;
}
@Override
public boolean isPanorama(int offset) {
return false;
}
@Override
public boolean isStaticCamera(int offset) {
return false;
}
@Override
public boolean isVideo(int offset) {
return mItem.getMediaType() == MediaItem.MEDIA_TYPE_VIDEO;
}
@Override
public boolean isGif(int offset) {
return MediaItem.MIME_TYPE_GIF.equalsIgnoreCase(mItem.getMimeType());
}
@Override
public boolean isDeletable(int offset) {
return (mItem.getSupportedOperations() & MediaItem.SUPPORT_DELETE) != 0;
}
@Override
public MediaItem getMediaItem(int offset) {
return offset == 0 ? mItem : null;
}
@Override
public int getCurrentIndex() {
return 0;
}
@Override
public void setCurrentPhoto(Path path, int indexHint) {
// ignore
}
@Override
public void setFocusHintDirection(int direction) {
// ignore
}
@Override
public void setFocusHintPath(Path path) {
// ignore
}
@Override
public int getLoadingState(int offset) {
return mLoadingState;
}
}