Home | History | Annotate | Download | only in app
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.gallery3d.app;
     18 
     19 import android.graphics.Bitmap;
     20 import android.graphics.BitmapFactory;
     21 import android.graphics.BitmapRegionDecoder;
     22 import android.graphics.Rect;
     23 import android.os.Handler;
     24 import android.os.Message;
     25 
     26 import com.android.gallery3d.common.BitmapUtils;
     27 import com.android.gallery3d.common.Utils;
     28 import com.android.gallery3d.data.MediaItem;
     29 import com.android.gallery3d.data.Path;
     30 import com.android.gallery3d.ui.PhotoView;
     31 import com.android.gallery3d.ui.ScreenNail;
     32 import com.android.gallery3d.ui.SynchronizedHandler;
     33 import com.android.gallery3d.ui.TileImageViewAdapter;
     34 import com.android.gallery3d.util.Future;
     35 import com.android.gallery3d.util.FutureListener;
     36 import com.android.gallery3d.util.ThreadPool;
     37 
     38 public class SinglePhotoDataAdapter extends TileImageViewAdapter
     39         implements PhotoPage.Model {
     40 
     41     private static final String TAG = "SinglePhotoDataAdapter";
     42     private static final int SIZE_BACKUP = 1024;
     43     private static final int MSG_UPDATE_IMAGE = 1;
     44 
     45     private MediaItem mItem;
     46     private boolean mHasFullImage;
     47     private Future<?> mTask;
     48     private Handler mHandler;
     49 
     50     private PhotoView mPhotoView;
     51     private ThreadPool mThreadPool;
     52     private int mLoadingState = LOADING_INIT;
     53 
     54     public SinglePhotoDataAdapter(
     55             GalleryActivity activity, PhotoView view, MediaItem item) {
     56         mItem = Utils.checkNotNull(item);
     57         mHasFullImage = (item.getSupportedOperations() &
     58                 MediaItem.SUPPORT_FULL_IMAGE) != 0;
     59         mPhotoView = Utils.checkNotNull(view);
     60         mHandler = new SynchronizedHandler(activity.getGLRoot()) {
     61             @Override
     62             @SuppressWarnings("unchecked")
     63             public void handleMessage(Message message) {
     64                 Utils.assertTrue(message.what == MSG_UPDATE_IMAGE);
     65                 if (mHasFullImage) {
     66                     onDecodeLargeComplete((ImageBundle) message.obj);
     67                 } else {
     68                     onDecodeThumbComplete((Future<Bitmap>) message.obj);
     69                 }
     70             }
     71         };
     72         mThreadPool = activity.getThreadPool();
     73     }
     74 
     75     private static class ImageBundle {
     76         public final BitmapRegionDecoder decoder;
     77         public final Bitmap backupImage;
     78 
     79         public ImageBundle(BitmapRegionDecoder decoder, Bitmap backupImage) {
     80             this.decoder = decoder;
     81             this.backupImage = backupImage;
     82         }
     83     }
     84 
     85     private FutureListener<BitmapRegionDecoder> mLargeListener =
     86             new FutureListener<BitmapRegionDecoder>() {
     87         public void onFutureDone(Future<BitmapRegionDecoder> future) {
     88             BitmapRegionDecoder decoder = future.get();
     89             if (decoder == null) return;
     90             int width = decoder.getWidth();
     91             int height = decoder.getHeight();
     92             BitmapFactory.Options options = new BitmapFactory.Options();
     93             options.inSampleSize = BitmapUtils.computeSampleSize(
     94                     (float) SIZE_BACKUP / Math.max(width, height));
     95             Bitmap bitmap = decoder.decodeRegion(new Rect(0, 0, width, height), options);
     96             mHandler.sendMessage(mHandler.obtainMessage(
     97                     MSG_UPDATE_IMAGE, new ImageBundle(decoder, bitmap)));
     98         }
     99     };
    100 
    101     private FutureListener<Bitmap> mThumbListener =
    102             new FutureListener<Bitmap>() {
    103         public void onFutureDone(Future<Bitmap> future) {
    104             mHandler.sendMessage(
    105                     mHandler.obtainMessage(MSG_UPDATE_IMAGE, future));
    106         }
    107     };
    108 
    109     public boolean isEmpty() {
    110         return false;
    111     }
    112 
    113     private void onDecodeLargeComplete(ImageBundle bundle) {
    114         try {
    115             setScreenNail(bundle.backupImage,
    116                     bundle.decoder.getWidth(), bundle.decoder.getHeight());
    117             setRegionDecoder(bundle.decoder);
    118             mPhotoView.notifyImageChange(0);
    119         } catch (Throwable t) {
    120             Log.w(TAG, "fail to decode large", t);
    121         }
    122     }
    123 
    124     private void onDecodeThumbComplete(Future<Bitmap> future) {
    125         try {
    126             Bitmap backup = future.get();
    127             if (backup == null) {
    128                 mLoadingState = LOADING_FAIL;
    129                 return;
    130             } else {
    131                 mLoadingState = LOADING_COMPLETE;
    132             }
    133             setScreenNail(backup, backup.getWidth(), backup.getHeight());
    134             mPhotoView.notifyImageChange(0);
    135         } catch (Throwable t) {
    136             Log.w(TAG, "fail to decode thumb", t);
    137         }
    138     }
    139 
    140     public void resume() {
    141         if (mTask == null) {
    142             if (mHasFullImage) {
    143                 mTask = mThreadPool.submit(
    144                         mItem.requestLargeImage(), mLargeListener);
    145             } else {
    146                 mTask = mThreadPool.submit(
    147                         mItem.requestImage(MediaItem.TYPE_THUMBNAIL),
    148                         mThumbListener);
    149             }
    150         }
    151     }
    152 
    153     public void pause() {
    154         Future<?> task = mTask;
    155         task.cancel();
    156         task.waitDone();
    157         if (task.get() == null) {
    158             mTask = null;
    159         }
    160     }
    161 
    162     @Override
    163     public void moveTo(int index) {
    164         throw new UnsupportedOperationException();
    165     }
    166 
    167     @Override
    168     public void getImageSize(int offset, PhotoView.Size size) {
    169         if (offset == 0) {
    170             size.width = mItem.getWidth();
    171             size.height = mItem.getHeight();
    172         } else {
    173             size.width = 0;
    174             size.height = 0;
    175         }
    176     }
    177 
    178     @Override
    179     public int getImageRotation(int offset) {
    180         return (offset == 0) ? mItem.getFullImageRotation() : 0;
    181     }
    182 
    183     @Override
    184     public ScreenNail getScreenNail(int offset) {
    185         return (offset == 0) ? getScreenNail() : null;
    186     }
    187 
    188     @Override
    189     public void setNeedFullImage(boolean enabled) {
    190         // currently not necessary.
    191     }
    192 
    193     @Override
    194     public boolean isCamera(int offset) {
    195         return false;
    196     }
    197 
    198     @Override
    199     public boolean isPanorama(int offset) {
    200         return false;
    201     }
    202 
    203     @Override
    204     public boolean isVideo(int offset) {
    205         return mItem.getMediaType() == MediaItem.MEDIA_TYPE_VIDEO;
    206     }
    207 
    208     @Override
    209     public boolean isDeletable(int offset) {
    210         return (mItem.getSupportedOperations() & MediaItem.SUPPORT_DELETE) != 0;
    211     }
    212 
    213     @Override
    214     public MediaItem getMediaItem(int offset) {
    215         return offset == 0 ? mItem : null;
    216     }
    217 
    218     @Override
    219     public int getCurrentIndex() {
    220         return 0;
    221     }
    222 
    223     @Override
    224     public void setCurrentPhoto(Path path, int indexHint) {
    225         // ignore
    226     }
    227 
    228     @Override
    229     public void setFocusHintDirection(int direction) {
    230         // ignore
    231     }
    232 
    233     @Override
    234     public void setFocusHintPath(Path path) {
    235         // ignore
    236     }
    237 
    238     @Override
    239     public int getLoadingState(int offset) {
    240         return mLoadingState;
    241     }
    242 }
    243