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.BitmapScreenNail;
     31 import com.android.gallery3d.ui.PhotoView;
     32 import com.android.gallery3d.ui.ScreenNail;
     33 import com.android.gallery3d.ui.SynchronizedHandler;
     34 import com.android.gallery3d.ui.TileImageViewAdapter;
     35 import com.android.gallery3d.util.Future;
     36 import com.android.gallery3d.util.FutureListener;
     37 import com.android.gallery3d.util.ThreadPool;
     38 
     39 public class SinglePhotoDataAdapter extends TileImageViewAdapter
     40         implements PhotoPage.Model {
     41 
     42     private static final String TAG = "SinglePhotoDataAdapter";
     43     private static final int SIZE_BACKUP = 1024;
     44     private static final int MSG_UPDATE_IMAGE = 1;
     45 
     46     private MediaItem mItem;
     47     private boolean mHasFullImage;
     48     private Future<?> mTask;
     49     private Handler mHandler;
     50 
     51     private PhotoView mPhotoView;
     52     private ThreadPool mThreadPool;
     53     private int mLoadingState = LOADING_INIT;
     54     private BitmapScreenNail mBitmapScreenNail;
     55 
     56     public SinglePhotoDataAdapter(
     57             AbstractGalleryActivity activity, PhotoView view, MediaItem item) {
     58         mItem = Utils.checkNotNull(item);
     59         mHasFullImage = (item.getSupportedOperations() &
     60                 MediaItem.SUPPORT_FULL_IMAGE) != 0;
     61         mPhotoView = Utils.checkNotNull(view);
     62         mHandler = new SynchronizedHandler(activity.getGLRoot()) {
     63             @Override
     64             @SuppressWarnings("unchecked")
     65             public void handleMessage(Message message) {
     66                 Utils.assertTrue(message.what == MSG_UPDATE_IMAGE);
     67                 if (mHasFullImage) {
     68                     onDecodeLargeComplete((ImageBundle) message.obj);
     69                 } else {
     70                     onDecodeThumbComplete((Future<Bitmap>) message.obj);
     71                 }
     72             }
     73         };
     74         mThreadPool = activity.getThreadPool();
     75     }
     76 
     77     private static class ImageBundle {
     78         public final BitmapRegionDecoder decoder;
     79         public final Bitmap backupImage;
     80 
     81         public ImageBundle(BitmapRegionDecoder decoder, Bitmap backupImage) {
     82             this.decoder = decoder;
     83             this.backupImage = backupImage;
     84         }
     85     }
     86 
     87     private FutureListener<BitmapRegionDecoder> mLargeListener =
     88             new FutureListener<BitmapRegionDecoder>() {
     89         @Override
     90         public void onFutureDone(Future<BitmapRegionDecoder> future) {
     91             BitmapRegionDecoder decoder = future.get();
     92             if (decoder == null) return;
     93             int width = decoder.getWidth();
     94             int height = decoder.getHeight();
     95             BitmapFactory.Options options = new BitmapFactory.Options();
     96             options.inSampleSize = BitmapUtils.computeSampleSize(
     97                     (float) SIZE_BACKUP / Math.max(width, height));
     98             Bitmap bitmap = decoder.decodeRegion(new Rect(0, 0, width, height), options);
     99             mHandler.sendMessage(mHandler.obtainMessage(
    100                     MSG_UPDATE_IMAGE, new ImageBundle(decoder, bitmap)));
    101         }
    102     };
    103 
    104     private FutureListener<Bitmap> mThumbListener =
    105             new FutureListener<Bitmap>() {
    106         @Override
    107         public void onFutureDone(Future<Bitmap> future) {
    108             mHandler.sendMessage(
    109                     mHandler.obtainMessage(MSG_UPDATE_IMAGE, future));
    110         }
    111     };
    112 
    113     @Override
    114     public boolean isEmpty() {
    115         return false;
    116     }
    117 
    118     private void setScreenNail(Bitmap bitmap, int width, int height) {
    119         mBitmapScreenNail = new BitmapScreenNail(bitmap);
    120         setScreenNail(mBitmapScreenNail, width, height);
    121     }
    122 
    123     private void onDecodeLargeComplete(ImageBundle bundle) {
    124         try {
    125             setScreenNail(bundle.backupImage,
    126                     bundle.decoder.getWidth(), bundle.decoder.getHeight());
    127             setRegionDecoder(bundle.decoder);
    128             mPhotoView.notifyImageChange(0);
    129         } catch (Throwable t) {
    130             Log.w(TAG, "fail to decode large", t);
    131         }
    132     }
    133 
    134     private void onDecodeThumbComplete(Future<Bitmap> future) {
    135         try {
    136             Bitmap backup = future.get();
    137             if (backup == null) {
    138                 mLoadingState = LOADING_FAIL;
    139                 return;
    140             } else {
    141                 mLoadingState = LOADING_COMPLETE;
    142             }
    143             setScreenNail(backup, backup.getWidth(), backup.getHeight());
    144             mPhotoView.notifyImageChange(0);
    145         } catch (Throwable t) {
    146             Log.w(TAG, "fail to decode thumb", t);
    147         }
    148     }
    149 
    150     @Override
    151     public void resume() {
    152         if (mTask == null) {
    153             if (mHasFullImage) {
    154                 mTask = mThreadPool.submit(
    155                         mItem.requestLargeImage(), mLargeListener);
    156             } else {
    157                 mTask = mThreadPool.submit(
    158                         mItem.requestImage(MediaItem.TYPE_THUMBNAIL),
    159                         mThumbListener);
    160             }
    161         }
    162     }
    163 
    164     @Override
    165     public void pause() {
    166         Future<?> task = mTask;
    167         task.cancel();
    168         task.waitDone();
    169         if (task.get() == null) {
    170             mTask = null;
    171         }
    172         if (mBitmapScreenNail != null) {
    173             mBitmapScreenNail.recycle();
    174             mBitmapScreenNail = null;
    175         }
    176     }
    177 
    178     @Override
    179     public void moveTo(int index) {
    180         throw new UnsupportedOperationException();
    181     }
    182 
    183     @Override
    184     public void getImageSize(int offset, PhotoView.Size size) {
    185         if (offset == 0) {
    186             size.width = mItem.getWidth();
    187             size.height = mItem.getHeight();
    188         } else {
    189             size.width = 0;
    190             size.height = 0;
    191         }
    192     }
    193 
    194     @Override
    195     public int getImageRotation(int offset) {
    196         return (offset == 0) ? mItem.getFullImageRotation() : 0;
    197     }
    198 
    199     @Override
    200     public ScreenNail getScreenNail(int offset) {
    201         return (offset == 0) ? getScreenNail() : null;
    202     }
    203 
    204     @Override
    205     public void setNeedFullImage(boolean enabled) {
    206         // currently not necessary.
    207     }
    208 
    209     @Override
    210     public boolean isCamera(int offset) {
    211         return false;
    212     }
    213 
    214     @Override
    215     public boolean isPanorama(int offset) {
    216         return false;
    217     }
    218 
    219     @Override
    220     public boolean isStaticCamera(int offset) {
    221         return false;
    222     }
    223 
    224     @Override
    225     public boolean isVideo(int offset) {
    226         return mItem.getMediaType() == MediaItem.MEDIA_TYPE_VIDEO;
    227     }
    228 
    229     @Override
    230     public boolean isDeletable(int offset) {
    231         return (mItem.getSupportedOperations() & MediaItem.SUPPORT_DELETE) != 0;
    232     }
    233 
    234     @Override
    235     public MediaItem getMediaItem(int offset) {
    236         return offset == 0 ? mItem : null;
    237     }
    238 
    239     @Override
    240     public int getCurrentIndex() {
    241         return 0;
    242     }
    243 
    244     @Override
    245     public void setCurrentPhoto(Path path, int indexHint) {
    246         // ignore
    247     }
    248 
    249     @Override
    250     public void setFocusHintDirection(int direction) {
    251         // ignore
    252     }
    253 
    254     @Override
    255     public void setFocusHintPath(Path path) {
    256         // ignore
    257     }
    258 
    259     @Override
    260     public int getLoadingState(int offset) {
    261         return mLoadingState;
    262     }
    263 }
    264