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