blob: 94045a290a78a36b55428976c7b6862b4e570cf0 [file] [log] [blame]
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001/*
2 * Copyright (C) 2007 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
17package com.android.camera;
18
Owen Lin41a85782009-04-20 15:59:57 +080019import com.android.camera.gallery.Cancelable;
Owen Lin101d5282009-04-03 16:20:08 -070020import com.android.camera.gallery.IImage;
21import com.android.camera.gallery.IImageList;
Owen Linf2718932009-06-03 17:07:33 -070022import com.android.camera.gallery.VideoObject;
Owen Lin101d5282009-04-03 16:20:08 -070023
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080024import android.app.Activity;
25import android.content.Context;
26import android.content.Intent;
27import android.content.SharedPreferences;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080028import android.graphics.Bitmap;
29import android.graphics.Matrix;
30import android.net.Uri;
31import android.os.Bundle;
32import android.os.Handler;
33import android.os.Message;
34import android.preference.PreferenceManager;
35import android.provider.MediaStore;
36import android.util.AttributeSet;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080037import android.util.Log;
38import android.view.GestureDetector;
39import android.view.KeyEvent;
40import android.view.Menu;
41import android.view.MenuItem;
42import android.view.MotionEvent;
43import android.view.View;
44import android.view.Window;
45import android.view.WindowManager;
46import android.view.animation.AlphaAnimation;
47import android.view.animation.Animation;
48import android.view.animation.AnimationUtils;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080049import android.widget.Toast;
50import android.widget.ZoomButtonsController;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080051
Chih-Chung Chang063f09d2009-04-01 04:02:00 -070052import java.util.Random;
Owen Lin41a85782009-04-20 15:59:57 +080053import java.util.concurrent.CancellationException;
54import java.util.concurrent.ExecutionException;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080055
Chih-Chung Chang73b7a3a2009-04-12 23:28:16 -070056// This activity can display a whole picture and navigate them in a specific
57// gallery. It has two modes: normal mode and slide show mode. In normal mode
58// the user view one image at a time, and can click "previous" and "next"
59// button to see the previous or next image. In slide show mode it shows one
60// image after another, with some transition effect.
Chih-Chung Chang063f09d2009-04-01 04:02:00 -070061public class ViewImage extends Activity implements View.OnClickListener {
Owen Line2950602009-05-22 01:50:27 -070062 private static final String PREF_SLIDESHOW_REPEAT =
63 "pref_gallery_slideshow_repeat_key";
64 private static final String PREF_SHUFFLE_SLIDESHOW =
65 "pref_gallery_slideshow_shuffle_key";
66 private static final String STATE_URI = "uri";
67 private static final String STATE_SLIDESHOW = "slideshow";
68 private static final String EXTRA_SLIDESHOW = "slideshow";
The Android Open Source Projectde365d82009-03-18 17:39:48 -070069 private static final String TAG = "ViewImage";
The Android Open Source Projectde365d82009-03-18 17:39:48 -070070
Owen Line2950602009-05-22 01:50:27 -070071 private static final boolean AUTO_DISMISS = true;
72 private static final boolean NO_AUTO_DISMISS = false;
73
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080074 private ImageGetter mGetter;
Ray Chen72e1dfd2009-03-24 21:13:16 -070075 private Uri mSavedUri;
Owen Linb0e12822009-06-22 15:53:26 -070076 private boolean mPaused = true;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080077
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080078 // Choices for what adjacents to load.
Owen Line2950602009-05-22 01:50:27 -070079 private static final int[] sOrderAdjacents = new int[] {0, 1, -1};
80 private static final int[] sOrderSlideshow = new int[] {0};
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080081
Owen Line2950602009-05-22 01:50:27 -070082 final LocalHandler mHandler = new LocalHandler();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080083
Owen Lin41a85782009-04-20 15:59:57 +080084 private final Random mRandom = new Random(System.currentTimeMillis());
Owen Line2950602009-05-22 01:50:27 -070085 private int [] mShuffleOrder = null;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080086 private boolean mUseShuffleOrder = false;
87 private boolean mSlideShowLoop = false;
88
Chih-Chung Chang3321d402009-04-02 21:45:14 -070089 static final int MODE_NORMAL = 1;
90 static final int MODE_SLIDESHOW = 2;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080091 private int mMode = MODE_NORMAL;
Owen Line2950602009-05-22 01:50:27 -070092
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080093 private boolean mFullScreenInNormalMode;
94 private boolean mShowActionIcons;
95 private View mActionIconPanel;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080096
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080097 private int mSlideShowInterval;
98 private int mLastSlideShowImage;
Chih-Chung Chang3321d402009-04-02 21:45:14 -070099 int mCurrentPosition = 0;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800100
101 // represents which style animation to use
102 private int mAnimationIndex;
103 private Animation [] mSlideShowInAnimation;
104 private Animation [] mSlideShowOutAnimation;
105
106 private SharedPreferences mPrefs;
107
Owen Line2950602009-05-22 01:50:27 -0700108 private View mNextImageView;
109 private View mPrevImageView;
Chih-Chung Chang91acfc92009-07-06 15:37:24 +0800110 private final Animation mHideNextImageViewAnimation =
111 new AlphaAnimation(1F, 0F);
112 private final Animation mHidePrevImageViewAnimation =
113 new AlphaAnimation(1F, 0F);
114 private final Animation mShowNextImageViewAnimation =
115 new AlphaAnimation(0F, 1F);
116 private final Animation mShowPrevImageViewAnimation =
117 new AlphaAnimation(0F, 1F);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800118
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700119 static final int PADDING = 20;
120 static final int HYSTERESIS = PADDING * 2;
121 static final int BASE_SCROLL_DURATION = 1000; // ms
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800122
Owen Linf2718932009-06-03 17:07:33 -0700123 public static final String KEY_IMAGE_LIST = "image_list";
124
Owen Lin101d5282009-04-03 16:20:08 -0700125 IImageList mAllImages;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800126
Owen Linb0e12822009-06-22 15:53:26 -0700127 // this is used to store the state of the image list. Right now, it is the
128 // image list itself.
129 private IImageList mAllImagesState;
130
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800131 private int mSlideShowImageCurrent = 0;
Owen Lin41a85782009-04-20 15:59:57 +0800132 private final ImageViewTouchBase [] mSlideShowImageViews =
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700133 new ImageViewTouchBase[2];
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800134
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700135 GestureDetector mGestureDetector;
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700136 private ZoomButtonsController mZoomButtonsController;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800137
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700138 // The image view displayed for normal mode.
139 private ImageViewTouch mImageView;
140 // This is the cache for thumbnail bitmaps.
141 private BitmapCache mCache;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800142 private MenuHelper.MenuItemsResult mImageMenuRunnable;
143
144 private Runnable mDismissOnScreenControlsRunnable;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800145
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800146 private void updateNextPrevControls() {
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700147 boolean showPrev = mCurrentPosition > 0;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800148 boolean showNext = mCurrentPosition < mAllImages.getCount() - 1;
149
150 boolean prevIsVisible = mPrevImageView.getVisibility() == View.VISIBLE;
151 boolean nextIsVisible = mNextImageView.getVisibility() == View.VISIBLE;
152
153 if (showPrev && !prevIsVisible) {
154 Animation a = mShowPrevImageViewAnimation;
155 a.setDuration(500);
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700156 mPrevImageView.startAnimation(a);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800157 mPrevImageView.setVisibility(View.VISIBLE);
158 } else if (!showPrev && prevIsVisible) {
159 Animation a = mHidePrevImageViewAnimation;
160 a.setDuration(500);
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700161 mPrevImageView.startAnimation(a);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800162 mPrevImageView.setVisibility(View.GONE);
163 }
164
165 if (showNext && !nextIsVisible) {
166 Animation a = mShowNextImageViewAnimation;
167 a.setDuration(500);
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700168 mNextImageView.startAnimation(a);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800169 mNextImageView.setVisibility(View.VISIBLE);
170 } else if (!showNext && nextIsVisible) {
171 Animation a = mHideNextImageViewAnimation;
172 a.setDuration(500);
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700173 mNextImageView.startAnimation(a);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800174 mNextImageView.setVisibility(View.GONE);
175 }
176 }
177
Owen Line2950602009-05-22 01:50:27 -0700178 private void showOnScreenControls(final boolean autoDismiss) {
Owen Lin145b61a2009-06-25 11:12:11 -0700179 if (mPaused) return;
Owen Line2950602009-05-22 01:50:27 -0700180 // If the view has not been attached to the window yet, the
181 // zoomButtonControls will not able to show up. So delay it until the
182 // view has attached to window.
183 if (mActionIconPanel.getWindowToken() == null) {
184 mHandler.postGetterCallback(new Runnable() {
185 public void run() {
186 showOnScreenControls(autoDismiss);
187 }
188 });
189 return;
190 }
Owen Lin31817e92009-03-27 16:30:02 -0700191 mHandler.removeCallbacks(mDismissOnScreenControlsRunnable);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800192 updateNextPrevControls();
Owen Line2950602009-05-22 01:50:27 -0700193
Owen Lin317fe842009-06-05 18:28:07 -0700194 IImage image = mAllImages.getImageAt(mCurrentPosition);
195 if (image instanceof VideoObject) {
196 mZoomButtonsController.setVisible(false);
197 } else {
198 updateZoomButtonsEnabled();
199 mZoomButtonsController.setVisible(true);
200 }
Owen Linf0abe582009-06-08 17:26:52 -0700201
Owen Line2950602009-05-22 01:50:27 -0700202 if (mShowActionIcons
203 && mActionIconPanel.getVisibility() != View.VISIBLE) {
204 Animation animation = new AlphaAnimation(0, 1);
205 animation.setDuration(500);
206 mActionIconPanel.startAnimation(animation);
207 mActionIconPanel.setVisibility(View.VISIBLE);
208 }
209 if (autoDismiss) scheduleDismissOnScreenControls();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800210 }
211
212 @Override
213 public boolean dispatchTouchEvent(MotionEvent m) {
214 boolean sup = super.dispatchTouchEvent(m);
Owen Lin31817e92009-03-27 16:30:02 -0700215
216 // This is a hack to show the on screen controls. We should make sure
217 // this event is not handled by others(ie. sup == false), and listen for
218 // the events on zoom/prev/next buttons.
219 // However, since we have no other pressable views, it is OK now.
220 // TODO: Fix the above issue.
221 if (mMode == MODE_NORMAL) {
222 switch (m.getAction()) {
223 case MotionEvent.ACTION_DOWN:
Owen Line2950602009-05-22 01:50:27 -0700224 showOnScreenControls(NO_AUTO_DISMISS);
Owen Lin31817e92009-03-27 16:30:02 -0700225 break;
226 case MotionEvent.ACTION_UP:
227 scheduleDismissOnScreenControls();
228 break;
229 }
230 }
231
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800232 if (sup == false) {
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700233 mGestureDetector.onTouchEvent(m);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800234 return true;
235 }
236 return true;
237 }
238
239 private void scheduleDismissOnScreenControls() {
240 mHandler.removeCallbacks(mDismissOnScreenControlsRunnable);
241 mHandler.postDelayed(mDismissOnScreenControlsRunnable, 1500);
242 }
243
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700244 private void updateZoomButtonsEnabled() {
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700245 ImageViewTouch imageView = mImageView;
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700246 float scale = imageView.getScale();
247 mZoomButtonsController.setZoomInEnabled(scale < imageView.mMaxZoom);
248 mZoomButtonsController.setZoomOutEnabled(scale > 1);
249 }
250
Andreas Huber5ae65322009-03-24 18:39:29 -0700251 @Override
252 protected void onDestroy() {
Owen Lin9e0bda22009-05-04 17:10:37 -0700253
Andreas Huber5ae65322009-03-24 18:39:29 -0700254 // This is necessary to make the ZoomButtonsController unregister
255 // its configuration change receiver.
256 if (mZoomButtonsController != null) {
257 mZoomButtonsController.setVisible(false);
258 }
259
260 super.onDestroy();
261 }
262
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700263 private void setupZoomButtonController(View rootView) {
264 mGestureDetector = new GestureDetector(this, new MyGestureListener());
265 mZoomButtonsController = new ZoomButtonsController(rootView);
266 mZoomButtonsController.setAutoDismissed(false);
Wu-cheng Li559899a2009-06-30 15:26:48 +0800267 mZoomButtonsController.setZoomSpeed(100);
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700268 mZoomButtonsController.setOnZoomListener(
269 new ZoomButtonsController.OnZoomListener() {
270 public void onVisibilityChanged(boolean visible) {
271 if (visible) {
272 updateZoomButtonsEnabled();
273 }
274 }
275
276 public void onZoom(boolean zoomIn) {
277 if (zoomIn) {
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700278 mImageView.zoomIn();
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700279 } else {
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700280 mImageView.zoomOut();
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700281 }
Owen Lin61dda552009-05-12 18:14:56 -0700282 updateZoomButtonsEnabled();
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700283 }
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700284 });
285 }
286
287 private class MyGestureListener extends
288 GestureDetector.SimpleOnGestureListener {
289
290 @Override
291 public boolean onScroll(MotionEvent e1, MotionEvent e2,
292 float distanceX, float distanceY) {
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700293 ImageViewTouch imageView = mImageView;
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700294 if (imageView.getScale() > 1F) {
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700295 imageView.postTranslateCenter(-distanceX, -distanceY);
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700296 }
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700297 return true;
298 }
299
300 @Override
301 public boolean onSingleTapUp(MotionEvent e) {
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700302 setMode(MODE_NORMAL);
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700303 return true;
304 }
305 }
306
307 private void setupDismissOnScreenControlRunnable() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800308 mDismissOnScreenControlsRunnable = new Runnable() {
309 public void run() {
Owen Line2950602009-05-22 01:50:27 -0700310 if (mNextImageView.getVisibility() == View.VISIBLE) {
311 Animation a = mHideNextImageViewAnimation;
312 a.setDuration(500);
313 mNextImageView.startAnimation(a);
314 mNextImageView.setVisibility(View.INVISIBLE);
315 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800316
Owen Line2950602009-05-22 01:50:27 -0700317 if (mPrevImageView.getVisibility() == View.VISIBLE) {
318 Animation a = mHidePrevImageViewAnimation;
319 a.setDuration(500);
320 mPrevImageView.startAnimation(a);
321 mPrevImageView.setVisibility(View.INVISIBLE);
322 }
323 mZoomButtonsController.setVisible(false);
324
325 if (mShowActionIcons
326 && mActionIconPanel.getVisibility() == View.VISIBLE) {
327 Animation animation = new AlphaAnimation(1, 0);
328 animation.setDuration(500);
329 mActionIconPanel.startAnimation(animation);
330 mActionIconPanel.setVisibility(View.INVISIBLE);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800331 }
332 }
333 };
334 }
335
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700336 boolean isPickIntent() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800337 String action = getIntent().getAction();
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700338 return (Intent.ACTION_PICK.equals(action)
339 || Intent.ACTION_GET_CONTENT.equals(action));
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800340 }
341
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800342 @Override
Owen Lin9b2e9122009-03-27 15:10:14 -0700343 public boolean onCreateOptionsMenu(Menu menu) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800344 super.onCreateOptionsMenu(menu);
345
Owen Lin6149fad2009-06-25 14:58:54 -0700346 MenuItem item = menu.add(Menu.CATEGORY_SECONDARY, 203, 0,
347 R.string.slide_show);
348 item.setOnMenuItemClickListener(
349 new MenuItem.OnMenuItemClickListener() {
350 public boolean onMenuItemClick(MenuItem item) {
351 setMode(MODE_SLIDESHOW);
352 mLastSlideShowImage = mCurrentPosition;
353 loadNextImage(mCurrentPosition, 0, true);
354 return true;
355 }
356 });
357 item.setIcon(android.R.drawable.ic_menu_slideshow);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800358
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800359 mImageMenuRunnable = MenuHelper.addImageMenuItems(
360 menu,
361 MenuHelper.INCLUDE_ALL,
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800362 ViewImage.this,
Ray Chen993105a2009-04-10 02:11:35 -0700363 mHandler,
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800364 mDeletePhotoRunnable,
365 new MenuHelper.MenuInvoker() {
Ray Chen993105a2009-04-10 02:11:35 -0700366 public void run(final MenuHelper.MenuCallback cb) {
Chih-Chung Chang06f43ba2009-07-02 17:07:20 +0800367 if (mPaused) return;
368 setMode(MODE_NORMAL);
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800369
Chih-Chung Chang06f43ba2009-07-02 17:07:20 +0800370 IImage image = mAllImages.getImageAt(mCurrentPosition);
371 Uri uri = image.fullSizeImageUri();
372 cb.run(uri, image);
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800373
Chih-Chung Chang06f43ba2009-07-02 17:07:20 +0800374 mImageView.clear();
375 setImage(mCurrentPosition);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800376 }
377 });
378
Chih-Chung Chang91acfc92009-07-06 15:37:24 +0800379 item = menu.add(Menu.CATEGORY_SECONDARY, 203, 1000,
380 R.string.camerasettings);
381 item.setOnMenuItemClickListener(
382 new MenuItem.OnMenuItemClickListener() {
383 public boolean onMenuItemClick(MenuItem item) {
384 Intent preferences = new Intent();
385 preferences.setClass(ViewImage.this, GallerySettings.class);
386 startActivity(preferences);
387 return true;
388 }
389 });
390 item.setAlphabeticShortcut('p');
391 item.setIcon(android.R.drawable.ic_menu_preferences);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800392
393 // Hidden menu just so the shortcut will bring up the zoom controls
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700394 // the string resource is a placeholder
395 menu.add(Menu.CATEGORY_SECONDARY, 203, 0, R.string.camerasettings)
396 .setOnMenuItemClickListener(
397 new MenuItem.OnMenuItemClickListener() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800398 public boolean onMenuItemClick(MenuItem item) {
Owen Line2950602009-05-22 01:50:27 -0700399 showOnScreenControls(AUTO_DISMISS);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800400 return true;
401 }
402 })
403 .setAlphabeticShortcut('z')
404 .setVisible(false);
405
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800406 return true;
407 }
408
409 protected Runnable mDeletePhotoRunnable = new Runnable() {
410 public void run() {
411 mAllImages.removeImageAt(mCurrentPosition);
412 if (mAllImages.getCount() == 0) {
413 finish();
414 } else {
415 if (mCurrentPosition == mAllImages.getCount()) {
416 mCurrentPosition -= 1;
417 }
418 }
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700419 mImageView.clear();
420 mCache.clear(); // Because the position number is changed.
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800421 setImage(mCurrentPosition);
422 }
423 };
424
425 @Override
Owen Lin9b2e9122009-03-27 15:10:14 -0700426 public boolean onPrepareOptionsMenu(Menu menu) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800427
Owen Linb0e12822009-06-22 15:53:26 -0700428 super.onPrepareOptionsMenu(menu);
429 if (mPaused) return false;
430
431 setMode(MODE_NORMAL);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800432 if (mImageMenuRunnable != null) {
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700433 mImageMenuRunnable.gettingReadyToOpen(menu,
434 mAllImages.getImageAt(mCurrentPosition));
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800435 }
436
Chih-Chung Changeb9d8a22009-03-27 16:07:25 -0700437 Uri uri = mAllImages.getImageAt(mCurrentPosition).fullSizeImageUri();
438 MenuHelper.enableShareMenuItem(menu, !MenuHelper.isMMSUri(uri));
439
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800440 return true;
441 }
442
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800443 @Override
444 public boolean onMenuItemSelected(int featureId, MenuItem item) {
445 boolean b = super.onMenuItemSelected(featureId, item);
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700446 if (mImageMenuRunnable != null) {
447 mImageMenuRunnable.aboutToCall(item,
448 mAllImages.getImageAt(mCurrentPosition));
449 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800450 return b;
451 }
452
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700453 void setImage(int pos) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800454 mCurrentPosition = pos;
455
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700456 Bitmap b = mCache.getBitmap(pos);
457 if (b != null) {
458 mImageView.setImageBitmapResetBase(b, true);
459 updateZoomButtonsEnabled();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800460 }
Owen Lin937fc482009-04-14 02:02:51 -0700461
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800462 ImageGetterCallback cb = new ImageGetterCallback() {
463 public void completed(boolean wasCanceled) {
464 if (!mShowActionIcons) {
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700465 mImageView.setFocusableInTouchMode(true);
466 mImageView.requestFocus();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800467 }
468 }
469
470 public boolean wantsThumbnail(int pos, int offset) {
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700471 return !mCache.hasBitmap(pos + offset);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800472 }
473
474 public boolean wantsFullImage(int pos, int offset) {
Owen Line2950602009-05-22 01:50:27 -0700475 return offset == 0;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800476 }
477
478 public int fullImageSizeToUse(int pos, int offset) {
479 // TODO
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700480 // this number should be bigger so that we can zoom. we may
481 // need to get fancier and read in the fuller size image as the
482 // user starts to zoom. use -1 to get the full full size image.
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800483 // for now use 480 so we don't run out of memory
484 final int imageViewSize = 480;
485 return imageViewSize;
486 }
487
488 public int [] loadOrder() {
Owen Line2950602009-05-22 01:50:27 -0700489 return sOrderAdjacents;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800490 }
491
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700492 public void imageLoaded(int pos, int offset, Bitmap bitmap,
493 boolean isThumb) {
Owen Lin3102f062009-03-24 20:38:25 -0700494 // shouldn't get here after onPause()
Chih-Chung Chang3b63aaa2009-05-25 11:16:48 +0800495
496 // We may get a result from a previous request. Ignore it.
497 if (pos != mCurrentPosition) {
498 bitmap.recycle();
499 return;
500 }
501
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700502 if (isThumb) {
503 mCache.put(pos + offset, bitmap);
504 }
Ray Chen993105a2009-04-10 02:11:35 -0700505 if (offset == 0) {
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700506 // isThumb: We always load thumb bitmap first, so we will
507 // reset the supp matrix for then thumb bitmap, and keep
508 // the supp matrix when the full bitmap is loaded.
509 mImageView.setImageBitmapResetBase(bitmap, isThumb);
Ray Chen993105a2009-04-10 02:11:35 -0700510 updateZoomButtonsEnabled();
511 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800512 }
513 };
514
515 // Could be null if we're stopping a slide show in the course of pausing
516 if (mGetter != null) {
517 mGetter.setPosition(pos, cb);
518 }
Owen Lin317fe842009-06-05 18:28:07 -0700519 updateActionIcons();
Owen Line2950602009-05-22 01:50:27 -0700520 showOnScreenControls(AUTO_DISMISS);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800521 }
522
523 @Override
Owen Lin9b2e9122009-03-27 15:10:14 -0700524 public void onCreate(Bundle instanceState) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800525 super.onCreate(instanceState);
Owen Line2950602009-05-22 01:50:27 -0700526
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800527 Intent intent = getIntent();
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700528 mFullScreenInNormalMode = intent.getBooleanExtra(
529 MediaStore.EXTRA_FULL_SCREEN, true);
530 mShowActionIcons = intent.getBooleanExtra(
Owen Line2950602009-05-22 01:50:27 -0700531 MediaStore.EXTRA_SHOW_ACTION_ICONS, true);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800532
533 mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800534
535 setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT);
536 requestWindowFeature(Window.FEATURE_NO_TITLE);
537 setContentView(R.layout.viewimage);
538
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700539 mImageView = (ImageViewTouch) findViewById(R.id.image);
540 mImageView.setEnableTrackballScroll(!mShowActionIcons);
541 mCache = new BitmapCache(3);
542 mImageView.setRecycler(mCache);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800543
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800544 makeGetter();
545
546 mAnimationIndex = -1;
547
548 mSlideShowInAnimation = new Animation[] {
549 makeInAnimation(R.anim.transition_in),
550 makeInAnimation(R.anim.slide_in),
551 makeInAnimation(R.anim.slide_in_vertical),
552 };
553
554 mSlideShowOutAnimation = new Animation[] {
555 makeOutAnimation(R.anim.transition_out),
556 makeOutAnimation(R.anim.slide_out),
557 makeOutAnimation(R.anim.slide_out_vertical),
558 };
559
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700560 mSlideShowImageViews[0] =
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700561 (ImageViewTouchBase) findViewById(R.id.image1_slideShow);
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700562 mSlideShowImageViews[1] =
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700563 (ImageViewTouchBase) findViewById(R.id.image2_slideShow);
564 for (ImageViewTouchBase v : mSlideShowImageViews) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800565 v.setVisibility(View.INVISIBLE);
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700566 v.setRecycler(mCache);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800567 }
568
569 mActionIconPanel = findViewById(R.id.action_icon_panel);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800570
Ray Chen72e1dfd2009-03-24 21:13:16 -0700571 Uri uri = getIntent().getData();
Owen Linf2718932009-06-03 17:07:33 -0700572 IImageList imageList = getIntent().getParcelableExtra(KEY_IMAGE_LIST);
Owen Line2950602009-05-22 01:50:27 -0700573 boolean slideshow = intent.getBooleanExtra(EXTRA_SLIDESHOW, false);
574
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800575 if (instanceState != null) {
Owen Line2950602009-05-22 01:50:27 -0700576 uri = instanceState.getParcelable(STATE_URI);
577 slideshow = instanceState.getBoolean(STATE_SLIDESHOW, false);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800578 }
Owen Line2950602009-05-22 01:50:27 -0700579
Owen Linf2718932009-06-03 17:07:33 -0700580 if (!init(uri, imageList)) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800581 finish();
582 return;
583 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800584
Owen Linf0abe582009-06-08 17:26:52 -0700585 // We don't show action icons for MMS uri because we don't have
586 // delete and share action icons for MMS. It is obvious that we don't
587 // need the "delete" action, but for the share part, although we get
588 // read permission (for the image) from the MMS application, we cannot
589 // pass the permission to other activities due to the current framework
590 // design.
591 if (MenuHelper.isMMSUri(uri)) {
592 mShowActionIcons = false;
593 }
594
Owen Line2950602009-05-22 01:50:27 -0700595 if (mShowActionIcons) {
596 int[] pickIds = {R.id.attach, R.id.cancel};
Owen Lin317fe842009-06-05 18:28:07 -0700597 int[] normalIds = {R.id.setas, R.id.play, R.id.share, R.id.discard};
Owen Lin6149fad2009-06-25 14:58:54 -0700598 int[] connectIds = isPickIntent() ? pickIds : normalIds;
Owen Line2950602009-05-22 01:50:27 -0700599 for (int id : connectIds) {
600 View view = mActionIconPanel.findViewById(id);
601 view.setVisibility(View.VISIBLE);
602 view.setOnClickListener(this);
603 }
604 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800605
Owen Line2950602009-05-22 01:50:27 -0700606 if (slideshow) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800607 setMode(MODE_SLIDESHOW);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800608 } else {
609 if (mFullScreenInNormalMode) {
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700610 getWindow().addFlags(
611 WindowManager.LayoutParams.FLAG_FULLSCREEN);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800612 }
613 if (mShowActionIcons) {
614 mActionIconPanel.setVisibility(View.VISIBLE);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800615 }
616 }
617
Owen Lin6149fad2009-06-25 14:58:54 -0700618 setupZoomButtonController(findViewById(R.id.abs));
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800619 setupDismissOnScreenControlRunnable();
620
621 mNextImageView = findViewById(R.id.next_image);
622 mPrevImageView = findViewById(R.id.prev_image);
623 mNextImageView.setOnClickListener(this);
624 mPrevImageView.setOnClickListener(this);
625
626 if (mShowActionIcons) {
627 mNextImageView.setFocusable(true);
628 mPrevImageView.setFocusable(true);
629 }
Owen Lin059daa32009-05-18 15:31:17 -0700630
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800631 }
632
Owen Lin317fe842009-06-05 18:28:07 -0700633 private void updateActionIcons() {
634 if (isPickIntent()) return;
635
636 IImage image = mAllImages.getImageAt(mCurrentPosition);
637 View panel = mActionIconPanel;
638 if (image instanceof VideoObject) {
639 panel.findViewById(R.id.setas).setVisibility(View.GONE);
640 panel.findViewById(R.id.play).setVisibility(View.VISIBLE);
641 } else {
642 panel.findViewById(R.id.setas).setVisibility(View.VISIBLE);
643 panel.findViewById(R.id.play).setVisibility(View.GONE);
644 }
645 }
646
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800647 private Animation makeInAnimation(int id) {
648 Animation inAnimation = AnimationUtils.loadAnimation(this, id);
649 return inAnimation;
650 }
651
652 private Animation makeOutAnimation(int id) {
653 Animation outAnimation = AnimationUtils.loadAnimation(this, id);
654 return outAnimation;
655 }
656
Owen Line2950602009-05-22 01:50:27 -0700657 private static int getPreferencesInteger(
658 SharedPreferences prefs, String key, int defaultValue) {
659 String value = prefs.getString(key, null);
660 try {
661 return value == null ? defaultValue : Integer.parseInt(value);
662 } catch (NumberFormatException ex) {
663 Log.e(TAG, "couldn't parse preference: " + value, ex);
664 return defaultValue;
665 }
666 }
667
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700668 void setMode(int mode) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800669 if (mMode == mode) {
670 return;
671 }
Owen Line2950602009-05-22 01:50:27 -0700672 View slideshowPanel = findViewById(R.id.slideShowContainer);
673 View normalPanel = findViewById(R.id.abs);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800674
675 Window win = getWindow();
676 mMode = mode;
677 if (mode == MODE_SLIDESHOW) {
Owen Line2950602009-05-22 01:50:27 -0700678 slideshowPanel.setVisibility(View.VISIBLE);
679 normalPanel.setVisibility(View.GONE);
680
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800681 win.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN
682 | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
Owen Line2950602009-05-22 01:50:27 -0700683
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700684 mImageView.clear();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800685 mActionIconPanel.setVisibility(View.GONE);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800686
Owen Line2950602009-05-22 01:50:27 -0700687 slideshowPanel.getRootView().requestLayout();
Chih-Chung Chang2b82c4a2009-04-14 18:00:10 +0800688
689 // The preferences we want to read:
690 // mUseShuffleOrder
691 // mSlideShowLoop
692 // mAnimationIndex
693 // mSlideShowInterval
694
Owen Line2950602009-05-22 01:50:27 -0700695 mUseShuffleOrder = mPrefs.getBoolean(PREF_SHUFFLE_SLIDESHOW, false);
696 mSlideShowLoop = mPrefs.getBoolean(PREF_SLIDESHOW_REPEAT, false);
697 mAnimationIndex = getPreferencesInteger(
698 mPrefs, "pref_gallery_slideshow_transition_key", 0);
699 mSlideShowInterval = getPreferencesInteger(
700 mPrefs, "pref_gallery_slideshow_interval_key", 3) * 1000;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800701 if (mUseShuffleOrder) {
702 generateShuffleOrder();
703 }
704 } else {
Owen Line2950602009-05-22 01:50:27 -0700705 slideshowPanel.setVisibility(View.GONE);
706 normalPanel.setVisibility(View.VISIBLE);
707
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800708 win.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
709 if (mFullScreenInNormalMode) {
710 win.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
711 } else {
712 win.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
713 }
714
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700715 if (mGetter != null) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800716 mGetter.cancelCurrent();
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700717 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800718
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800719 if (mShowActionIcons) {
Owen Line2950602009-05-22 01:50:27 -0700720 Animation animation = new AlphaAnimation(0F, 1F);
721 animation.setDuration(500);
722 mActionIconPanel.setAnimation(animation);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800723 mActionIconPanel.setVisibility(View.VISIBLE);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800724 }
725
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700726 ImageViewTouchBase dst = mImageView;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800727 dst.mLastXTouchPos = -1;
728 dst.mLastYTouchPos = -1;
729
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700730 for (ImageViewTouchBase ivt : mSlideShowImageViews) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800731 ivt.clear();
732 }
733
734 mShuffleOrder = null;
735
736 // mGetter null is a proxy for being paused
737 if (mGetter != null) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800738 setImage(mCurrentPosition);
739 }
740 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800741 }
742
743 private void generateShuffleOrder() {
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700744 if (mShuffleOrder == null
745 || mShuffleOrder.length != mAllImages.getCount()) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800746 mShuffleOrder = new int[mAllImages.getCount()];
Owen Line2950602009-05-22 01:50:27 -0700747 for (int i = 0, n = mShuffleOrder.length; i < n; i++) {
748 mShuffleOrder[i] = i;
749 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800750 }
751
Owen Line2950602009-05-22 01:50:27 -0700752 for (int i = mShuffleOrder.length - 1; i >= 0; i--) {
753 int r = mRandom.nextInt(i + 1);
754 if (r != i) {
755 int tmp = mShuffleOrder[r];
756 mShuffleOrder[r] = mShuffleOrder[i];
757 mShuffleOrder[i] = tmp;
758 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800759 }
760 }
761
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700762 private void loadNextImage(final int requestedPos, final long delay,
763 final boolean firstCall) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800764 if (firstCall && mUseShuffleOrder) {
765 generateShuffleOrder();
766 }
767
768 final long targetDisplayTime = System.currentTimeMillis() + delay;
769
770 ImageGetterCallback cb = new ImageGetterCallback() {
771 public void completed(boolean wasCanceled) {
772 }
773
774 public boolean wantsThumbnail(int pos, int offset) {
775 return true;
776 }
777
778 public boolean wantsFullImage(int pos, int offset) {
779 return false;
780 }
781
782 public int [] loadOrder() {
Owen Line2950602009-05-22 01:50:27 -0700783 return sOrderSlideshow;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800784 }
785
786 public int fullImageSizeToUse(int pos, int offset) {
787 return 480; // TODO compute this
788 }
789
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700790 public void imageLoaded(final int pos, final int offset,
791 final Bitmap bitmap, final boolean isThumb) {
792 long timeRemaining = Math.max(0,
793 targetDisplayTime - System.currentTimeMillis());
Owen Lin3102f062009-03-24 20:38:25 -0700794 mHandler.postDelayedGetterCallback(new Runnable() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800795 public void run() {
Ray Chen993105a2009-04-10 02:11:35 -0700796 if (mMode == MODE_NORMAL) {
797 return;
798 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800799
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700800 ImageViewTouchBase oldView =
801 mSlideShowImageViews[mSlideShowImageCurrent];
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800802
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700803 if (++mSlideShowImageCurrent
804 == mSlideShowImageViews.length) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800805 mSlideShowImageCurrent = 0;
806 }
807
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700808 ImageViewTouchBase newView =
809 mSlideShowImageViews[mSlideShowImageCurrent];
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800810 newView.setVisibility(View.VISIBLE);
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700811 newView.setImageBitmapResetBase(bitmap, true);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800812 newView.bringToFront();
813
814 int animation = 0;
815
816 if (mAnimationIndex == -1) {
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700817 int n = mRandom.nextInt(
818 mSlideShowInAnimation.length);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800819 animation = n;
820 } else {
821 animation = mAnimationIndex;
822 }
823
824 Animation aIn = mSlideShowInAnimation[animation];
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700825 newView.startAnimation(aIn);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800826 newView.setVisibility(View.VISIBLE);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800827
828 Animation aOut = mSlideShowOutAnimation[animation];
829 oldView.setVisibility(View.INVISIBLE);
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700830 oldView.startAnimation(aOut);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800831
832 mCurrentPosition = requestedPos;
833
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700834 if (mCurrentPosition == mLastSlideShowImage
835 && !firstCall) {
Owen Lin3102f062009-03-24 20:38:25 -0700836 if (mSlideShowLoop) {
837 if (mUseShuffleOrder) {
838 generateShuffleOrder();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800839 }
Owen Lin3102f062009-03-24 20:38:25 -0700840 } else {
841 setMode(MODE_NORMAL);
842 return;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800843 }
Owen Lin3102f062009-03-24 20:38:25 -0700844 }
845
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700846 loadNextImage(
847 (mCurrentPosition + 1) % mAllImages.getCount(),
848 mSlideShowInterval, false);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800849 }
850 }, timeRemaining);
851 }
852 };
853 // Could be null if we're stopping a slide show in the course of pausing
854 if (mGetter != null) {
855 int pos = requestedPos;
856 if (mShuffleOrder != null) {
857 pos = mShuffleOrder[pos];
858 }
859 mGetter.setPosition(pos, cb);
860 }
861 }
862
863 private void makeGetter() {
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700864 mGetter = new ImageGetter(this);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800865 }
866
Owen Lin7c2eb952009-06-04 14:04:30 -0700867 private IImageList buildImageListFromUri(Uri uri) {
868 String sortOrder = mPrefs.getString(
869 "pref_gallery_sort_key", "descending");
Owen Lin6149fad2009-06-25 14:58:54 -0700870 int sort = sortOrder.equals("ascending")
Owen Lin7c2eb952009-06-04 14:04:30 -0700871 ? ImageManager.SORT_ASCENDING
872 : ImageManager.SORT_DESCENDING;
873 return ImageManager.makeImageList(uri, getContentResolver(), sort);
874 }
875
Owen Linf2718932009-06-03 17:07:33 -0700876 private boolean init(Uri uri, IImageList imageList) {
877 if (uri == null) return false;
Owen Linb0e12822009-06-22 15:53:26 -0700878 mAllImagesState = (imageList == null)
Owen Lin7c2eb952009-06-04 14:04:30 -0700879 ? buildImageListFromUri(uri)
880 : imageList;
Owen Linb0e12822009-06-22 15:53:26 -0700881 mAllImages = mAllImagesState;
Owen Linf2718932009-06-03 17:07:33 -0700882 mAllImages.open(getContentResolver());
Chih-Chung Changfd6da322009-05-04 16:49:30 +0800883 IImage image = mAllImages.getImageForUri(uri);
Owen Linf2718932009-06-03 17:07:33 -0700884 if (image == null) return false;
885 mCurrentPosition = mAllImages.getImageIndex(image);
886 mLastSlideShowImage = mCurrentPosition;
887 return true;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800888 }
889
Ray Chen72e1dfd2009-03-24 21:13:16 -0700890 private Uri getCurrentUri() {
Owen Lin387833a2009-06-29 17:30:24 -0700891 if (mAllImages.getCount() == 0) return null;
Owen Lin101d5282009-04-03 16:20:08 -0700892 IImage image = mAllImages.getImageAt(mCurrentPosition);
Owen Lin317fe842009-06-05 18:28:07 -0700893 return image.fullSizeImageUri();
Ray Chen72e1dfd2009-03-24 21:13:16 -0700894 }
Owen Lin9b2e9122009-03-27 15:10:14 -0700895
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800896 @Override
897 public void onSaveInstanceState(Bundle b) {
898 super.onSaveInstanceState(b);
Owen Line2950602009-05-22 01:50:27 -0700899 b.putParcelable(STATE_URI,
Owen Linf2718932009-06-03 17:07:33 -0700900 mAllImages.getImageAt(mCurrentPosition).fullSizeImageUri());
Owen Line2950602009-05-22 01:50:27 -0700901 b.putBoolean(STATE_SLIDESHOW, mMode == MODE_SLIDESHOW);
Ray Chen35937472009-03-24 20:39:36 -0700902 }
Owen Lin9b2e9122009-03-27 15:10:14 -0700903
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800904 @Override
Owen Lin9e0bda22009-05-04 17:10:37 -0700905 public void onStart() {
906 super.onStart();
Owen Linb0e12822009-06-22 15:53:26 -0700907 mPaused = false;
Owen Lin937fc482009-04-14 02:02:51 -0700908
Owen Linb0e12822009-06-22 15:53:26 -0700909 init(mSavedUri, mAllImagesState);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800910
911 // normally this will never be zero but if one "backs" into this
912 // activity after removing the sdcard it could be zero. in that
913 // case just "finish" since there's nothing useful that can happen.
Ray Chen72e1dfd2009-03-24 21:13:16 -0700914 int count = mAllImages.getCount();
915 if (count == 0) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800916 finish();
Ray Chen72e1dfd2009-03-24 21:13:16 -0700917 } else if (count <= mCurrentPosition) {
918 mCurrentPosition = count - 1;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800919 }
920
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800921 if (mGetter == null) {
922 makeGetter();
923 }
924
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700925 if (mMode == MODE_SLIDESHOW) {
926 loadNextImage(mCurrentPosition, 0, true);
927 } else { // MODE_NORMAL
928 setImage(mCurrentPosition);
929 }
The Android Open Source Projecte3f45162009-03-11 12:11:58 -0700930 }
931
932 @Override
Owen Lin9e0bda22009-05-04 17:10:37 -0700933 public void onStop() {
934 super.onStop();
Owen Linb0e12822009-06-22 15:53:26 -0700935 mPaused = true;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800936
937 mGetter.cancelCurrent();
938 mGetter.stop();
939 mGetter = null;
940 setMode(MODE_NORMAL);
941
Owen Lin3102f062009-03-24 20:38:25 -0700942 // removing all callback in the message queue
943 mHandler.removeAllGetterCallbacks();
Owen Lin9b2e9122009-03-27 15:10:14 -0700944
Ray Chen72e1dfd2009-03-24 21:13:16 -0700945 mSavedUri = getCurrentUri();
Owen Lin3102f062009-03-24 20:38:25 -0700946
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800947 mAllImages.deactivate();
Owen Linb0e12822009-06-22 15:53:26 -0700948 mAllImages.close();
949 mAllImages = null;
950
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700951 mDismissOnScreenControlsRunnable.run();
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700952 if (mDismissOnScreenControlsRunnable != null) {
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700953 mHandler.removeCallbacks(mDismissOnScreenControlsRunnable);
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700954 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800955
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700956 mImageView.clear();
957 mCache.clear();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800958
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700959 for (ImageViewTouchBase iv : mSlideShowImageViews) {
960 iv.clear();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800961 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800962 }
963
Owen Lin317fe842009-06-05 18:28:07 -0700964 private void startShareMediaActivity(IImage image) {
965 boolean isVideo = image instanceof VideoObject;
966 Intent intent = new Intent();
967 intent.setAction(Intent.ACTION_SEND);
968 intent.setType(image.getMimeType());
969 intent.putExtra(Intent.EXTRA_STREAM, image.fullSizeImageUri());
970 try {
971 startActivity(Intent.createChooser(intent, getText(
972 isVideo ? R.string.sendVideo : R.string.sendImage)));
973 } catch (android.content.ActivityNotFoundException ex) {
974 Toast.makeText(this, isVideo
975 ? R.string.no_way_to_share_image
976 : R.string.no_way_to_share_video,
977 Toast.LENGTH_SHORT).show();
978 }
979 }
980
981 private void startPlayVideoActivity() {
982 IImage image = mAllImages.getImageAt(mCurrentPosition);
983 Intent intent = new Intent(
984 Intent.ACTION_VIEW, image.fullSizeImageUri());
985 try {
986 startActivity(intent);
987 } catch (android.content.ActivityNotFoundException ex) {
988 Log.e(TAG, "Couldn't view video " + image.fullSizeImageUri(), ex);
989 }
990 }
991
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800992 public void onClick(View v) {
993 switch (v.getId()) {
Owen Lin317fe842009-06-05 18:28:07 -0700994 case R.id.discard:
995 MenuHelper.deletePhoto(this, mDeletePhotoRunnable);
996 break;
997 case R.id.play:
998 startPlayVideoActivity();
999 break;
1000 case R.id.share: {
1001 IImage image = mAllImages.getImageAt(mCurrentPosition);
1002 if (MenuHelper.isMMSUri(image.fullSizeImageUri())) {
1003 return;
1004 }
1005 startShareMediaActivity(image);
1006 break;
Ray Chen993105a2009-04-10 02:11:35 -07001007 }
Owen Lin317fe842009-06-05 18:28:07 -07001008 case R.id.setas: {
Chih-Chung Changbb187782009-07-02 16:46:30 +08001009 IImage image = mAllImages.getImageAt(mCurrentPosition);
1010 Intent intent = Util.createSetAsIntent(image);
Owen Lin317fe842009-06-05 18:28:07 -07001011 try {
1012 startActivity(Intent.createChooser(
1013 intent, getText(R.string.setImage)));
1014 } catch (android.content.ActivityNotFoundException ex) {
1015 Toast.makeText(this, R.string.no_way_to_share_video,
1016 Toast.LENGTH_SHORT).show();
1017 }
1018 break;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001019 }
Owen Lin317fe842009-06-05 18:28:07 -07001020 case R.id.next_image:
1021 moveNextOrPrevious(1);
1022 break;
1023 case R.id.prev_image:
1024 moveNextOrPrevious(-1);
1025 break;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001026 }
1027 }
1028
1029 private void moveNextOrPrevious(int delta) {
1030 int nextImagePos = mCurrentPosition + delta;
1031 if ((0 <= nextImagePos) && (nextImagePos < mAllImages.getCount())) {
1032 setImage(nextImagePos);
1033 }
1034 }
1035
1036 @Override
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001037 protected void onActivityResult(int requestCode, int resultCode,
1038 Intent data) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001039 switch (requestCode) {
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001040 case MenuHelper.RESULT_COMMON_MENU_CROP:
1041 if (resultCode == RESULT_OK) {
1042 // The CropImage activity passes back the Uri of the
1043 // cropped image as the Action rather than the Data.
1044 mSavedUri = Uri.parse(data.getAction());
Owen Lin145b61a2009-06-25 11:12:11 -07001045
1046 // if onStart() runs before, then set the returned
1047 // image as currentImage.
1048 if (mAllImages != null) {
1049 IImage image = mAllImages.getImageForUri(mSavedUri);
1050 mCurrentPosition = mAllImages.getImageIndex(image);
1051 setImage(mCurrentPosition);
1052 }
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001053 }
1054 break;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001055 }
1056 }
Owen Lin3102f062009-03-24 20:38:25 -07001057
1058 static class LocalHandler extends Handler {
1059 private static final int IMAGE_GETTER_CALLBACK = 1;
1060
1061 @Override
1062 public void handleMessage(Message message) {
1063 switch(message.what) {
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001064 case IMAGE_GETTER_CALLBACK:
1065 ((Runnable) message.obj).run();
1066 break;
Owen Lin3102f062009-03-24 20:38:25 -07001067 }
1068 }
1069
1070 public void postGetterCallback(Runnable callback) {
1071 postDelayedGetterCallback(callback, 0);
1072 }
1073
1074 public void postDelayedGetterCallback(Runnable callback, long delay) {
Ray Chen993105a2009-04-10 02:11:35 -07001075 if (callback == null) {
1076 throw new NullPointerException();
1077 }
Owen Lin3102f062009-03-24 20:38:25 -07001078 Message message = Message.obtain();
1079 message.what = IMAGE_GETTER_CALLBACK;
1080 message.obj = callback;
1081 sendMessageDelayed(message, delay);
1082 }
1083
1084 public void removeAllGetterCallbacks() {
1085 removeMessages(IMAGE_GETTER_CALLBACK);
1086 }
1087 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001088}
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001089
1090class ImageViewTouch extends ImageViewTouchBase {
Owen Lin41a85782009-04-20 15:59:57 +08001091 private final ViewImage mViewImage;
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001092 private boolean mEnableTrackballScroll;
1093
1094 public ImageViewTouch(Context context) {
1095 super(context);
1096 mViewImage = (ViewImage) context;
1097 }
1098
1099 public ImageViewTouch(Context context, AttributeSet attrs) {
1100 super(context, attrs);
1101 mViewImage = (ViewImage) context;
1102 }
1103
1104 public void setEnableTrackballScroll(boolean enable) {
1105 mEnableTrackballScroll = enable;
1106 }
1107
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -07001108 protected void postTranslateCenter(float dx, float dy) {
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001109 super.postTranslate(dx, dy);
Chih-Chung Chang0a475e12009-04-16 11:42:12 +08001110 center(true, true);
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001111 }
1112
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -07001113 static final float PAN_RATE = 20;
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001114
1115 @Override
1116 public boolean onKeyDown(int keyCode, KeyEvent event) {
1117 // Don't respond to arrow keys if trackball scrolling is not enabled
1118 if (!mEnableTrackballScroll) {
1119 if ((keyCode >= KeyEvent.KEYCODE_DPAD_UP)
1120 && (keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT)) {
1121 return super.onKeyDown(keyCode, event);
1122 }
1123 }
1124
1125 int current = mViewImage.mCurrentPosition;
1126
1127 int nextImagePos = -2; // default no next image
1128 try {
1129 switch (keyCode) {
1130 case KeyEvent.KEYCODE_DPAD_CENTER: {
1131 if (mViewImage.isPickIntent()) {
Owen Lin101d5282009-04-03 16:20:08 -07001132 IImage img = mViewImage.mAllImages
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001133 .getImageAt(mViewImage.mCurrentPosition);
1134 mViewImage.setResult(ViewImage.RESULT_OK,
1135 new Intent().setData(img.fullSizeImageUri()));
1136 mViewImage.finish();
1137 }
1138 break;
1139 }
1140 case KeyEvent.KEYCODE_DPAD_LEFT: {
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001141 int maxOffset = (current == 0) ? 0 : ViewImage.HYSTERESIS;
Owen Lin101d5282009-04-03 16:20:08 -07001142 if (getScale() <= 1F
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001143 || isShiftedToNextImage(true, maxOffset)) {
1144 nextImagePos = current - 1;
1145 } else {
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -07001146 panBy(PAN_RATE, 0);
Chih-Chung Chang0a475e12009-04-16 11:42:12 +08001147 center(true, false);
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001148 }
1149 return true;
1150 }
1151 case KeyEvent.KEYCODE_DPAD_RIGHT: {
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001152 int maxOffset =
1153 (current == mViewImage.mAllImages.getCount() - 1)
1154 ? 0
1155 : ViewImage.HYSTERESIS;
1156 if (getScale() <= 1F
1157 || isShiftedToNextImage(false, maxOffset)) {
1158 nextImagePos = current + 1;
1159 } else {
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -07001160 panBy(-PAN_RATE, 0);
Chih-Chung Chang0a475e12009-04-16 11:42:12 +08001161 center(true, false);
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001162 }
1163 return true;
1164 }
1165 case KeyEvent.KEYCODE_DPAD_UP: {
1166 panBy(0, PAN_RATE);
Chih-Chung Chang0a475e12009-04-16 11:42:12 +08001167 center(false, true);
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001168 return true;
1169 }
1170 case KeyEvent.KEYCODE_DPAD_DOWN: {
1171 panBy(0, -PAN_RATE);
Chih-Chung Chang0a475e12009-04-16 11:42:12 +08001172 center(false, true);
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001173 return true;
1174 }
1175 case KeyEvent.KEYCODE_DEL:
1176 MenuHelper.deletePhoto(
1177 mViewImage, mViewImage.mDeletePhotoRunnable);
1178 break;
1179 }
1180 } finally {
1181 if (nextImagePos >= 0
1182 && nextImagePos < mViewImage.mAllImages.getCount()) {
1183 synchronized (mViewImage) {
1184 mViewImage.setMode(ViewImage.MODE_NORMAL);
1185 mViewImage.setImage(nextImagePos);
1186 }
1187 } else if (nextImagePos != -2) {
Chih-Chung Chang0a475e12009-04-16 11:42:12 +08001188 center(true, true);
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001189 }
1190 }
1191
1192 return super.onKeyDown(keyCode, event);
1193 }
1194
1195 protected boolean isShiftedToNextImage(boolean left, int maxOffset) {
1196 boolean retval;
1197 Bitmap bitmap = mBitmapDisplayed;
1198 Matrix m = getImageViewMatrix();
1199 if (left) {
1200 float [] t1 = new float[] { 0, 0 };
1201 m.mapPoints(t1);
1202 retval = t1[0] > maxOffset;
1203 } else {
1204 int width = bitmap != null ? bitmap.getWidth() : getWidth();
1205 float [] t1 = new float[] { width, 0 };
1206 m.mapPoints(t1);
1207 retval = t1[0] + maxOffset < getWidth();
1208 }
1209 return retval;
1210 }
1211}
1212
1213/*
1214 * Here's the loading strategy. For any given image, load the thumbnail
1215 * into memory and post a callback to display the resulting bitmap.
1216 *
1217 * Then proceed to load the full image bitmap. Three things can
1218 * happen at this point:
1219 *
1220 * 1. the image fails to load because the UI thread decided
1221 * to move on to a different image. This "cancellation" happens
1222 * by virtue of the UI thread closing the stream containing the
1223 * image being decoded. BitmapFactory.decodeStream returns null
1224 * in this case.
1225 *
1226 * 2. the image loaded successfully. At that point we post
1227 * a callback to the UI thread to actually show the bitmap.
1228 *
1229 * 3. when the post runs it checks to see if the image that was
1230 * loaded is still the one we want. The UI may have moved on
1231 * to some other image and if so we just drop the newly loaded
1232 * bitmap on the floor.
1233 */
1234
1235interface ImageGetterCallback {
1236 public void imageLoaded(int pos, int offset, Bitmap bitmap,
1237 boolean isThumb);
1238 public boolean wantsThumbnail(int pos, int offset);
1239 public boolean wantsFullImage(int pos, int offset);
1240 public int fullImageSizeToUse(int pos, int offset);
1241 public void completed(boolean wasCanceled);
1242 public int [] loadOrder();
1243}
1244
1245class ImageGetter {
Owen Lin937fc482009-04-14 02:02:51 -07001246
1247 @SuppressWarnings("unused")
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001248 private static final String TAG = "ImageGetter";
1249
1250 // The thread which does the work.
Owen Lin41a85782009-04-20 15:59:57 +08001251 private final Thread mGetterThread;
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001252
1253 // The base position that's being retrieved. The actual images retrieved
1254 // are this base plus each of the offets.
1255 private int mCurrentPosition = -1;
1256
1257 // The callback to invoke for each image.
1258 private ImageGetterCallback mCB;
1259
1260 // This is the loader cancelable that gets set while we're loading an image.
1261 // If we change position we can cancel the current load using this.
Owen Lin41a85782009-04-20 15:59:57 +08001262 private Cancelable<Bitmap> mLoad;
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001263
1264 // True if we're canceling the current load.
1265 private boolean mCancelCurrent = false;
1266
1267 // True when the therad should exit.
1268 private boolean mDone = false;
1269
1270 // True when the loader thread is waiting for work.
1271 private boolean mReady = false;
1272
1273 // The ViewImage this ImageGetter belongs to
1274 ViewImage mViewImage;
1275
1276 void cancelCurrent() {
1277 synchronized (this) {
1278 if (!mReady) {
1279 mCancelCurrent = true;
Owen Lin41a85782009-04-20 15:59:57 +08001280 Cancelable<Bitmap> load = mLoad;
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001281 if (load != null) {
Owen Lin41a85782009-04-20 15:59:57 +08001282 load.requestCancel();
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001283 }
1284 mCancelCurrent = false;
1285 }
1286 }
1287 }
1288
1289 private class ImageGetterRunnable implements Runnable {
1290 private Runnable callback(final int position, final int offset,
1291 final boolean isThumb, final Bitmap bitmap) {
1292 return new Runnable() {
1293 public void run() {
Owen Lin937fc482009-04-14 02:02:51 -07001294 // check for inflight callbacks that aren't applicable
Ray Chen993105a2009-04-10 02:11:35 -07001295 // any longer before delivering them
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001296 if (!isCanceled() && position == mCurrentPosition) {
1297 mCB.imageLoaded(position, offset, bitmap, isThumb);
1298 } else if (bitmap != null) {
1299 bitmap.recycle();
1300 }
1301 }
1302 };
1303 }
1304
1305 private Runnable completedCallback(final boolean wasCanceled) {
1306 return new Runnable() {
1307 public void run() {
1308 mCB.completed(wasCanceled);
1309 }
1310 };
1311 }
1312
1313 public void run() {
1314 int lastPosition = -1;
1315 while (!mDone) {
1316 synchronized (ImageGetter.this) {
1317 mReady = true;
1318 ImageGetter.this.notify();
1319
Owen Lin937fc482009-04-14 02:02:51 -07001320 if (mCurrentPosition == -1
Ray Chen993105a2009-04-10 02:11:35 -07001321 || lastPosition == mCurrentPosition) {
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001322 try {
1323 ImageGetter.this.wait();
1324 } catch (InterruptedException ex) {
1325 continue;
1326 }
1327 }
1328
1329 lastPosition = mCurrentPosition;
1330 mReady = false;
1331 }
1332
1333 if (lastPosition != -1) {
1334 int imageCount = mViewImage.mAllImages.getCount();
1335
1336 int [] order = mCB.loadOrder();
1337 for (int i = 0; i < order.length; i++) {
1338 int offset = order[i];
1339 int imageNumber = lastPosition + offset;
1340 if (imageNumber >= 0 && imageNumber < imageCount) {
Ray Chen993105a2009-04-10 02:11:35 -07001341 IImage image = mViewImage.mAllImages
1342 .getImageAt(lastPosition + offset);
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001343 if (image == null || isCanceled()) {
1344 break;
1345 }
1346 if (mCB.wantsThumbnail(lastPosition, offset)) {
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001347 Bitmap b = image.thumbBitmap();
Ray Chen993105a2009-04-10 02:11:35 -07001348 mViewImage.mHandler.postGetterCallback(
Owen Lin937fc482009-04-14 02:02:51 -07001349 callback(lastPosition, offset,
Ray Chen993105a2009-04-10 02:11:35 -07001350 true, b));
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001351 }
1352 }
1353 }
1354
1355 for (int i = 0; i < order.length; i++) {
1356 int offset = order[i];
1357 int imageNumber = lastPosition + offset;
1358 if (imageNumber >= 0 && imageNumber < imageCount) {
Ray Chen993105a2009-04-10 02:11:35 -07001359 IImage image = mViewImage.mAllImages
1360 .getImageAt(lastPosition + offset);
Owen Linf2718932009-06-03 17:07:33 -07001361 if (mCB.wantsFullImage(lastPosition, offset)
1362 && !(image instanceof VideoObject)) {
Ray Chen993105a2009-04-10 02:11:35 -07001363 int sizeToUse = mCB.fullImageSizeToUse(
1364 lastPosition, offset);
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001365 if (image != null && !isCanceled()) {
Ray Chen993105a2009-04-10 02:11:35 -07001366 mLoad = image.fullSizeBitmapCancelable(
1367 sizeToUse);
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001368 }
1369 if (mLoad != null) {
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001370 // The return value could be null if the
1371 // bitmap is too big, or we cancelled it.
Owen Lin41a85782009-04-20 15:59:57 +08001372 Bitmap b;
1373 try {
1374 b = mLoad.get();
1375 } catch (InterruptedException e) {
1376 b = null;
1377 } catch (ExecutionException e) {
1378 throw new RuntimeException(e);
1379 } catch (CancellationException e) {
1380 b = null;
1381 }
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001382 mLoad = null;
1383 if (b != null) {
1384 if (isCanceled()) {
1385 b.recycle();
1386 } else {
Ray Chen993105a2009-04-10 02:11:35 -07001387 Runnable cb = callback(
Owen Lin937fc482009-04-14 02:02:51 -07001388 lastPosition, offset,
Ray Chen993105a2009-04-10 02:11:35 -07001389 false, b);
1390 mViewImage.mHandler
1391 .postGetterCallback(cb);
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001392 }
1393 }
1394 }
1395 }
1396 }
1397 }
1398 mViewImage.mHandler.postGetterCallback(
1399 completedCallback(isCanceled()));
1400 }
1401 }
1402 }
1403 }
Owen Lin101d5282009-04-03 16:20:08 -07001404
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001405 public ImageGetter(ViewImage viewImage) {
1406 mViewImage = viewImage;
1407 mGetterThread = new Thread(new ImageGetterRunnable());
1408 mGetterThread.setName("ImageGettter");
1409 mGetterThread.start();
1410 }
1411
1412 private boolean isCanceled() {
1413 synchronized (this) {
1414 return mCancelCurrent;
1415 }
1416 }
1417
1418 public void setPosition(int position, ImageGetterCallback cb) {
1419 synchronized (this) {
1420 if (!mReady) {
1421 try {
1422 mCancelCurrent = true;
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001423 // if the thread is waiting before loading the full size
1424 // image then this will free it up
Ray Chen993105a2009-04-10 02:11:35 -07001425 BitmapManager.instance()
1426 .cancelThreadDecoding(mGetterThread);
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001427 ImageGetter.this.notify();
1428 ImageGetter.this.wait();
Ray Chen993105a2009-04-10 02:11:35 -07001429 BitmapManager.instance()
1430 .allowThreadDecoding(mGetterThread);
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001431 mCancelCurrent = false;
1432 } catch (InterruptedException ex) {
1433 // not sure what to do here
1434 }
1435 }
1436 }
1437
1438 mCurrentPosition = position;
1439 mCB = cb;
1440
1441 synchronized (this) {
1442 ImageGetter.this.notify();
1443 }
1444 }
1445
1446 public void stop() {
1447 synchronized (this) {
1448 mDone = true;
1449 ImageGetter.this.notify();
1450 }
1451 try {
Ray Chen896182a2009-05-13 16:21:52 +08001452 BitmapManager.instance().cancelThreadDecoding(mGetterThread);
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001453 mGetterThread.join();
1454 } catch (InterruptedException ex) {
Ray Chen993105a2009-04-10 02:11:35 -07001455 // Ignore the exception
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001456 }
1457 }
1458}
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -07001459
1460// This is a cache for Bitmap displayed in ViewImage (normal mode, thumb only).
1461class BitmapCache implements ImageViewTouchBase.Recycler {
Chih-Chung Chang73b7a3a2009-04-12 23:28:16 -07001462 public static class Entry {
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -07001463 int mPos;
1464 Bitmap mBitmap;
1465 public Entry() {
1466 clear();
1467 }
1468 public void clear() {
1469 mPos = -1;
1470 mBitmap = null;
1471 }
1472 }
1473
Owen Lin41a85782009-04-20 15:59:57 +08001474 private final Entry[] mCache;
Owen Lin937fc482009-04-14 02:02:51 -07001475
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -07001476 public BitmapCache(int size) {
1477 mCache = new Entry[size];
1478 for (int i = 0; i < mCache.length; i++) {
1479 mCache[i] = new Entry();
1480 }
1481 }
1482
1483 // Given the position, find the associated entry. Returns null if there is
1484 // no such entry.
1485 private Entry findEntry(int pos) {
1486 for (Entry e : mCache) {
1487 if (pos == e.mPos) {
1488 return e;
1489 }
1490 }
1491 return null;
1492 }
Owen Lin937fc482009-04-14 02:02:51 -07001493
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -07001494 // Returns the thumb bitmap if we have it, otherwise return null.
1495 public synchronized Bitmap getBitmap(int pos) {
1496 Entry e = findEntry(pos);
1497 if (e != null) {
1498 return e.mBitmap;
1499 }
1500 return null;
1501 }
1502
1503 public synchronized void put(int pos, Bitmap bitmap) {
1504 // First see if we already have this entry.
1505 if (findEntry(pos) != null) {
1506 return;
1507 }
1508
1509 // Find the best entry we should replace.
1510 // See if there is any empty entry.
1511 // Otherwise assuming sequential access, kick out the entry with the
1512 // greatest distance.
1513 Entry best = null;
1514 int maxDist = -1;
1515 for (Entry e : mCache) {
1516 if (e.mPos == -1) {
1517 best = e;
1518 break;
1519 } else {
1520 int dist = Math.abs(pos - e.mPos);
1521 if (dist > maxDist) {
1522 maxDist = dist;
1523 best = e;
1524 }
1525 }
1526 }
Owen Lin937fc482009-04-14 02:02:51 -07001527
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -07001528 // Recycle the image being kicked out.
1529 // This only works because our current usage is sequential, so we
1530 // do not happen to recycle the image being displayed.
1531 if (best.mBitmap != null) {
1532 best.mBitmap.recycle();
1533 }
1534
1535 best.mPos = pos;
1536 best.mBitmap = bitmap;
1537 }
Owen Lin937fc482009-04-14 02:02:51 -07001538
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -07001539 // Recycle all bitmaps in the cache and clear the cache.
1540 public synchronized void clear() {
1541 for (Entry e : mCache) {
1542 if (e.mBitmap != null) {
1543 e.mBitmap.recycle();
1544 }
1545 e.clear();
1546 }
1547 }
1548
1549 // Returns whether the bitmap is in the cache.
1550 public synchronized boolean hasBitmap(int pos) {
1551 Entry e = findEntry(pos);
1552 return (e != null);
1553 }
Owen Lin937fc482009-04-14 02:02:51 -07001554
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -07001555 // Recycle the bitmap if it's not in the cache.
1556 // The input must be non-null.
1557 public synchronized void recycle(Bitmap b) {
1558 for (Entry e : mCache) {
1559 if (e.mPos != -1) {
1560 if (e.mBitmap == b) {
1561 return;
1562 }
1563 }
1564 }
1565 b.recycle();
1566 }
1567}