/* * 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) 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 mLargeListener = new FutureListener() { @Override public void onFutureDone(Future future) { BitmapRegionDecoder decoder = future.get(); if (decoder == null) 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 mThumbListener = new FutureListener() { @Override public void onFutureDone(Future 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 future) { try { Bitmap backup = future.get(); if (backup == null) { mLoadingState = LOADING_FAIL; return; } 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; task.cancel(); task.waitDone(); if (task.get() == null) { 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 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; } }