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