| /* |
| * 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; |
| } |
| } |