blob: ef0857f5b7dfe2f09a98d56f4194649e665f0141 [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;
22
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080023import android.app.Activity;
24import android.content.Context;
25import android.content.Intent;
26import android.content.SharedPreferences;
27import android.content.pm.ActivityInfo;
28import 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 {
The Android Open Source Projectde365d82009-03-18 17:39:48 -070062 private static final String TAG = "ViewImage";
The Android Open Source Projectde365d82009-03-18 17:39:48 -070063
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080064 private ImageGetter mGetter;
Ray Chen72e1dfd2009-03-24 21:13:16 -070065 private Uri mSavedUri;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080066
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080067 // Choices for what adjacents to load.
Chih-Chung Chang3321d402009-04-02 21:45:14 -070068 private static final int[] sOrder_adjacents = new int[] {0, 1, -1};
69 private static final int[] sOrder_slideshow = new int[] {0};
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080070
Chih-Chung Chang3321d402009-04-02 21:45:14 -070071 LocalHandler mHandler = new LocalHandler();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080072
Owen Lin41a85782009-04-20 15:59:57 +080073 private final Random mRandom = new Random(System.currentTimeMillis());
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080074 private int [] mShuffleOrder;
75 private boolean mUseShuffleOrder = false;
76 private boolean mSlideShowLoop = false;
77
Chih-Chung Chang3321d402009-04-02 21:45:14 -070078 static final int MODE_NORMAL = 1;
79 static final int MODE_SLIDESHOW = 2;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080080 private int mMode = MODE_NORMAL;
81 private boolean mFullScreenInNormalMode;
82 private boolean mShowActionIcons;
83 private View mActionIconPanel;
84 private View mShutterButton;
85
86 private boolean mSortAscending = false;
87 private int mSlideShowInterval;
88 private int mLastSlideShowImage;
Chih-Chung Chang3321d402009-04-02 21:45:14 -070089 int mCurrentPosition = 0;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080090
91 // represents which style animation to use
92 private int mAnimationIndex;
93 private Animation [] mSlideShowInAnimation;
94 private Animation [] mSlideShowOutAnimation;
95
96 private SharedPreferences mPrefs;
97
98 private View mNextImageView, mPrevImageView;
Owen Lin41a85782009-04-20 15:59:57 +080099 private final Animation mHideNextImageViewAnimation = new AlphaAnimation(1F, 0F);
100 private final Animation mHidePrevImageViewAnimation = new AlphaAnimation(1F, 0F);
101 private final Animation mShowNextImageViewAnimation = new AlphaAnimation(0F, 1F);
102 private final Animation mShowPrevImageViewAnimation = new AlphaAnimation(0F, 1F);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800103
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700104 static final int PADDING = 20;
105 static final int HYSTERESIS = PADDING * 2;
106 static final int BASE_SCROLL_DURATION = 1000; // ms
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800107
Owen Lin101d5282009-04-03 16:20:08 -0700108 IImageList mAllImages;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800109
110 private int mSlideShowImageCurrent = 0;
Owen Lin41a85782009-04-20 15:59:57 +0800111 private final ImageViewTouchBase [] mSlideShowImageViews =
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700112 new ImageViewTouchBase[2];
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800113
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700114 GestureDetector mGestureDetector;
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700115 private ZoomButtonsController mZoomButtonsController;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800116
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700117 // The image view displayed for normal mode.
118 private ImageViewTouch mImageView;
119 // This is the cache for thumbnail bitmaps.
120 private BitmapCache mCache;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800121 private MenuHelper.MenuItemsResult mImageMenuRunnable;
122
123 private Runnable mDismissOnScreenControlsRunnable;
124 private boolean mCameraReviewMode;
125
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800126 private void updateNextPrevControls() {
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700127 boolean showPrev = mCurrentPosition > 0;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800128 boolean showNext = mCurrentPosition < mAllImages.getCount() - 1;
129
130 boolean prevIsVisible = mPrevImageView.getVisibility() == View.VISIBLE;
131 boolean nextIsVisible = mNextImageView.getVisibility() == View.VISIBLE;
132
133 if (showPrev && !prevIsVisible) {
134 Animation a = mShowPrevImageViewAnimation;
135 a.setDuration(500);
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700136 mPrevImageView.startAnimation(a);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800137 mPrevImageView.setVisibility(View.VISIBLE);
138 } else if (!showPrev && prevIsVisible) {
139 Animation a = mHidePrevImageViewAnimation;
140 a.setDuration(500);
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700141 mPrevImageView.startAnimation(a);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800142 mPrevImageView.setVisibility(View.GONE);
143 }
144
145 if (showNext && !nextIsVisible) {
146 Animation a = mShowNextImageViewAnimation;
147 a.setDuration(500);
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700148 mNextImageView.startAnimation(a);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800149 mNextImageView.setVisibility(View.VISIBLE);
150 } else if (!showNext && nextIsVisible) {
151 Animation a = mHideNextImageViewAnimation;
152 a.setDuration(500);
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700153 mNextImageView.startAnimation(a);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800154 mNextImageView.setVisibility(View.GONE);
155 }
156 }
157
158 private void showOnScreenControls() {
Owen Lin31817e92009-03-27 16:30:02 -0700159 mHandler.removeCallbacks(mDismissOnScreenControlsRunnable);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800160 updateNextPrevControls();
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700161 updateZoomButtonsEnabled();
162 mZoomButtonsController.setVisible(true);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800163 }
164
165 @Override
166 public boolean dispatchTouchEvent(MotionEvent m) {
167 boolean sup = super.dispatchTouchEvent(m);
Owen Lin31817e92009-03-27 16:30:02 -0700168
169 // This is a hack to show the on screen controls. We should make sure
170 // this event is not handled by others(ie. sup == false), and listen for
171 // the events on zoom/prev/next buttons.
172 // However, since we have no other pressable views, it is OK now.
173 // TODO: Fix the above issue.
174 if (mMode == MODE_NORMAL) {
175 switch (m.getAction()) {
176 case MotionEvent.ACTION_DOWN:
177 showOnScreenControls();
178 break;
179 case MotionEvent.ACTION_UP:
180 scheduleDismissOnScreenControls();
181 break;
182 }
183 }
184
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800185 if (sup == false) {
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700186 mGestureDetector.onTouchEvent(m);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800187 return true;
188 }
189 return true;
190 }
191
192 private void scheduleDismissOnScreenControls() {
193 mHandler.removeCallbacks(mDismissOnScreenControlsRunnable);
194 mHandler.postDelayed(mDismissOnScreenControlsRunnable, 1500);
195 }
196
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700197 private void updateZoomButtonsEnabled() {
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700198 ImageViewTouch imageView = mImageView;
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700199 float scale = imageView.getScale();
200 mZoomButtonsController.setZoomInEnabled(scale < imageView.mMaxZoom);
201 mZoomButtonsController.setZoomOutEnabled(scale > 1);
202 }
203
Andreas Huber5ae65322009-03-24 18:39:29 -0700204 @Override
205 protected void onDestroy() {
Owen Lin9e0bda22009-05-04 17:10:37 -0700206
Andreas Huber5ae65322009-03-24 18:39:29 -0700207 // This is necessary to make the ZoomButtonsController unregister
208 // its configuration change receiver.
209 if (mZoomButtonsController != null) {
210 mZoomButtonsController.setVisible(false);
211 }
212
213 super.onDestroy();
214 }
215
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700216 private void setupZoomButtonController(View rootView) {
217 mGestureDetector = new GestureDetector(this, new MyGestureListener());
218 mZoomButtonsController = new ZoomButtonsController(rootView);
219 mZoomButtonsController.setAutoDismissed(false);
220 mZoomButtonsController.setOnZoomListener(
221 new ZoomButtonsController.OnZoomListener() {
222 public void onVisibilityChanged(boolean visible) {
223 if (visible) {
224 updateZoomButtonsEnabled();
225 }
226 }
227
228 public void onZoom(boolean zoomIn) {
229 if (zoomIn) {
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700230 mImageView.zoomIn();
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700231 } else {
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700232 mImageView.zoomOut();
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700233 }
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700234 }
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700235 });
236 }
237
238 private class MyGestureListener extends
239 GestureDetector.SimpleOnGestureListener {
240
241 @Override
242 public boolean onScroll(MotionEvent e1, MotionEvent e2,
243 float distanceX, float distanceY) {
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700244 ImageViewTouch imageView = mImageView;
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700245 if (imageView.getScale() > 1F) {
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700246 imageView.postTranslateCenter(-distanceX, -distanceY);
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700247 }
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700248 return true;
249 }
250
251 @Override
252 public boolean onSingleTapUp(MotionEvent e) {
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700253 setMode(MODE_NORMAL);
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700254 return true;
255 }
256 }
257
258 private void setupDismissOnScreenControlRunnable() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800259 mDismissOnScreenControlsRunnable = new Runnable() {
260 public void run() {
261 if (!mShowActionIcons) {
262 if (mNextImageView.getVisibility() == View.VISIBLE) {
263 Animation a = mHideNextImageViewAnimation;
264 a.setDuration(500);
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700265 mNextImageView.startAnimation(a);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800266 mNextImageView.setVisibility(View.INVISIBLE);
267 }
268
269 if (mPrevImageView.getVisibility() == View.VISIBLE) {
270 Animation a = mHidePrevImageViewAnimation;
271 a.setDuration(500);
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700272 mPrevImageView.startAnimation(a);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800273 mPrevImageView.setVisibility(View.INVISIBLE);
274 }
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700275 mZoomButtonsController.setVisible(false);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800276 }
277 }
278 };
279 }
280
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700281 boolean isPickIntent() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800282 String action = getIntent().getAction();
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700283 return (Intent.ACTION_PICK.equals(action)
284 || Intent.ACTION_GET_CONTENT.equals(action));
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800285 }
286
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800287 @Override
Owen Lin9b2e9122009-03-27 15:10:14 -0700288 public boolean onCreateOptionsMenu(Menu menu) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800289 super.onCreateOptionsMenu(menu);
290
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700291 if (!mCameraReviewMode) {
292 MenuItem item = menu.add(Menu.CATEGORY_SECONDARY, 203, 0,
293 R.string.slide_show);
294 item.setOnMenuItemClickListener(
295 new MenuItem.OnMenuItemClickListener() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800296 public boolean onMenuItemClick(MenuItem item) {
297 setMode(MODE_SLIDESHOW);
298 mLastSlideShowImage = mCurrentPosition;
299 loadNextImage(mCurrentPosition, 0, true);
300 return true;
301 }
302 });
303 item.setIcon(android.R.drawable.ic_menu_slideshow);
304 }
305
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700306 final SelectedImageGetter selectedImageGetter =
307 new SelectedImageGetter() {
Owen Lin101d5282009-04-03 16:20:08 -0700308 public IImage getCurrentImage() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800309 return mAllImages.getImageAt(mCurrentPosition);
310 }
311
312 public Uri getCurrentImageUri() {
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700313 return mAllImages.getImageAt(mCurrentPosition)
314 .fullSizeImageUri();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800315 }
316 };
317
318 mImageMenuRunnable = MenuHelper.addImageMenuItems(
319 menu,
320 MenuHelper.INCLUDE_ALL,
321 true,
322 ViewImage.this,
Ray Chen993105a2009-04-10 02:11:35 -0700323 mHandler,
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800324 mDeletePhotoRunnable,
325 new MenuHelper.MenuInvoker() {
Ray Chen993105a2009-04-10 02:11:35 -0700326 public void run(final MenuHelper.MenuCallback cb) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800327 setMode(MODE_NORMAL);
Ray Chen993105a2009-04-10 02:11:35 -0700328 Thread t = new Thread() {
Owen Lin937fc482009-04-14 02:02:51 -0700329 @Override
Ray Chen993105a2009-04-10 02:11:35 -0700330 public void run() {
331 cb.run(selectedImageGetter.getCurrentImageUri(),
332 selectedImageGetter.getCurrentImage());
333 mHandler.post(new Runnable() {
334 public void run() {
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700335 mImageView.clear();
Ray Chen993105a2009-04-10 02:11:35 -0700336 setImage(mCurrentPosition);
337 }
338 });
339 }
340 };
Ray Chen993105a2009-04-10 02:11:35 -0700341 t.start();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800342 }
343 });
344
345 if (true) {
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700346 MenuItem item = menu.add(Menu.CATEGORY_SECONDARY, 203, 1000,
347 R.string.camerasettings);
348 item.setOnMenuItemClickListener(
349 new MenuItem.OnMenuItemClickListener() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800350 public boolean onMenuItemClick(MenuItem item) {
351 Intent preferences = new Intent();
352 preferences.setClass(ViewImage.this, GallerySettings.class);
353 startActivity(preferences);
354 return true;
355 }
356 });
357 item.setAlphabeticShortcut('p');
358 item.setIcon(android.R.drawable.ic_menu_preferences);
359 }
360
361 // Hidden menu just so the shortcut will bring up the zoom controls
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700362 // the string resource is a placeholder
363 menu.add(Menu.CATEGORY_SECONDARY, 203, 0, R.string.camerasettings)
364 .setOnMenuItemClickListener(
365 new MenuItem.OnMenuItemClickListener() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800366 public boolean onMenuItemClick(MenuItem item) {
367 showOnScreenControls();
Owen Lin31817e92009-03-27 16:30:02 -0700368 scheduleDismissOnScreenControls();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800369 return true;
370 }
371 })
372 .setAlphabeticShortcut('z')
373 .setVisible(false);
374
375
376 return true;
377 }
378
379 protected Runnable mDeletePhotoRunnable = new Runnable() {
380 public void run() {
381 mAllImages.removeImageAt(mCurrentPosition);
382 if (mAllImages.getCount() == 0) {
383 finish();
384 } else {
385 if (mCurrentPosition == mAllImages.getCount()) {
386 mCurrentPosition -= 1;
387 }
388 }
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700389 mImageView.clear();
390 mCache.clear(); // Because the position number is changed.
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800391 setImage(mCurrentPosition);
392 }
393 };
394
395 @Override
Owen Lin9b2e9122009-03-27 15:10:14 -0700396 public boolean onPrepareOptionsMenu(Menu menu) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800397 super.onPrepareOptionsMenu(menu);
398 setMode(MODE_NORMAL);
399
400 if (mImageMenuRunnable != null) {
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700401 mImageMenuRunnable.gettingReadyToOpen(menu,
402 mAllImages.getImageAt(mCurrentPosition));
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800403 }
404
Chih-Chung Changeb9d8a22009-03-27 16:07:25 -0700405 Uri uri = mAllImages.getImageAt(mCurrentPosition).fullSizeImageUri();
406 MenuHelper.enableShareMenuItem(menu, !MenuHelper.isMMSUri(uri));
407
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800408 return true;
409 }
410
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800411 @Override
412 public boolean onMenuItemSelected(int featureId, MenuItem item) {
413 boolean b = super.onMenuItemSelected(featureId, item);
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700414 if (mImageMenuRunnable != null) {
415 mImageMenuRunnable.aboutToCall(item,
416 mAllImages.getImageAt(mCurrentPosition));
417 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800418 return b;
419 }
420
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700421 void setImage(int pos) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800422 mCurrentPosition = pos;
423
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700424 Bitmap b = mCache.getBitmap(pos);
425 if (b != null) {
426 mImageView.setImageBitmapResetBase(b, true);
427 updateZoomButtonsEnabled();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800428 }
Owen Lin937fc482009-04-14 02:02:51 -0700429
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800430 ImageGetterCallback cb = new ImageGetterCallback() {
431 public void completed(boolean wasCanceled) {
432 if (!mShowActionIcons) {
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700433 mImageView.setFocusableInTouchMode(true);
434 mImageView.requestFocus();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800435 }
436 }
437
438 public boolean wantsThumbnail(int pos, int offset) {
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700439 return !mCache.hasBitmap(pos + offset);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800440 }
441
442 public boolean wantsFullImage(int pos, int offset) {
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700443 return (offset == 0);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800444 }
445
446 public int fullImageSizeToUse(int pos, int offset) {
447 // TODO
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700448 // this number should be bigger so that we can zoom. we may
449 // need to get fancier and read in the fuller size image as the
450 // user starts to zoom. use -1 to get the full full size image.
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800451 // for now use 480 so we don't run out of memory
452 final int imageViewSize = 480;
453 return imageViewSize;
454 }
455
456 public int [] loadOrder() {
457 return sOrder_adjacents;
458 }
459
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700460 public void imageLoaded(int pos, int offset, Bitmap bitmap,
461 boolean isThumb) {
Owen Lin3102f062009-03-24 20:38:25 -0700462 // shouldn't get here after onPause()
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700463 if (isThumb) {
464 mCache.put(pos + offset, bitmap);
465 }
Ray Chen993105a2009-04-10 02:11:35 -0700466 if (offset == 0) {
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700467 // isThumb: We always load thumb bitmap first, so we will
468 // reset the supp matrix for then thumb bitmap, and keep
469 // the supp matrix when the full bitmap is loaded.
470 mImageView.setImageBitmapResetBase(bitmap, isThumb);
Ray Chen993105a2009-04-10 02:11:35 -0700471 updateZoomButtonsEnabled();
472 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800473 }
474 };
475
476 // Could be null if we're stopping a slide show in the course of pausing
477 if (mGetter != null) {
478 mGetter.setPosition(pos, cb);
479 }
Owen Lin9b2e9122009-03-27 15:10:14 -0700480
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800481 showOnScreenControls();
Owen Lin31817e92009-03-27 16:30:02 -0700482 scheduleDismissOnScreenControls();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800483 }
484
485 @Override
Owen Lin9b2e9122009-03-27 15:10:14 -0700486 public void onCreate(Bundle instanceState) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800487 super.onCreate(instanceState);
488 Intent intent = getIntent();
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700489 mCameraReviewMode = intent.getBooleanExtra(
490 "com.android.camera.ReviewMode", false);
491 mFullScreenInNormalMode = intent.getBooleanExtra(
492 MediaStore.EXTRA_FULL_SCREEN, true);
493 mShowActionIcons = intent.getBooleanExtra(
494 MediaStore.EXTRA_SHOW_ACTION_ICONS, false);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800495
Owen Lin9b2e9122009-03-27 15:10:14 -0700496 setRequestedOrientation();
497
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800498 mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800499
500 setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT);
501 requestWindowFeature(Window.FEATURE_NO_TITLE);
502 setContentView(R.layout.viewimage);
503
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700504 mImageView = (ImageViewTouch) findViewById(R.id.image);
505 mImageView.setEnableTrackballScroll(!mShowActionIcons);
506 mCache = new BitmapCache(3);
507 mImageView.setRecycler(mCache);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800508
Owen Lin937fc482009-04-14 02:02:51 -0700509
Ray Chen993105a2009-04-10 02:11:35 -0700510 BitmapManager bitmapManager = BitmapManager.instance();
511 bitmapManager.setCheckResourceLock(false);
512 bitmapManager.allowAllDecoding();
513
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800514 makeGetter();
515
516 mAnimationIndex = -1;
517
518 mSlideShowInAnimation = new Animation[] {
519 makeInAnimation(R.anim.transition_in),
520 makeInAnimation(R.anim.slide_in),
521 makeInAnimation(R.anim.slide_in_vertical),
522 };
523
524 mSlideShowOutAnimation = new Animation[] {
525 makeOutAnimation(R.anim.transition_out),
526 makeOutAnimation(R.anim.slide_out),
527 makeOutAnimation(R.anim.slide_out_vertical),
528 };
529
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700530 mSlideShowImageViews[0] =
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700531 (ImageViewTouchBase) findViewById(R.id.image1_slideShow);
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700532 mSlideShowImageViews[1] =
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700533 (ImageViewTouchBase) findViewById(R.id.image2_slideShow);
534 for (ImageViewTouchBase v : mSlideShowImageViews) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800535 v.setVisibility(View.INVISIBLE);
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700536 v.setRecycler(mCache);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800537 }
538
539 mActionIconPanel = findViewById(R.id.action_icon_panel);
540 {
541 int[] pickIds = {R.id.attach, R.id.cancel};
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700542 int[] normalIds = {R.id.gallery, R.id.setas, R.id.share,
543 R.id.discard};
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800544 int[] hideIds = pickIds;
545 int[] connectIds = normalIds;
546 if (isPickIntent()) {
547 hideIds = normalIds;
548 connectIds = pickIds;
549 }
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700550 for (int id : hideIds) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800551 mActionIconPanel.findViewById(id).setVisibility(View.GONE);
552 }
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700553 for (int id : connectIds) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800554 View view = mActionIconPanel.findViewById(id);
555 view.setOnClickListener(this);
556 Animation animation = new AlphaAnimation(0F, 1F);
557 animation.setDuration(500);
558 view.setAnimation(animation);
559 }
560 }
561 mShutterButton = findViewById(R.id.shutter_button);
562 mShutterButton.setOnClickListener(this);
563
Ray Chen72e1dfd2009-03-24 21:13:16 -0700564 Uri uri = getIntent().getData();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800565
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800566 if (instanceState != null) {
567 if (instanceState.containsKey("uri")) {
Ray Chen72e1dfd2009-03-24 21:13:16 -0700568 uri = Uri.parse(instanceState.getString("uri"));
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800569 }
570 }
Ray Chen72e1dfd2009-03-24 21:13:16 -0700571 if (uri == null) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800572 finish();
573 return;
574 }
Ray Chen72e1dfd2009-03-24 21:13:16 -0700575 init(uri);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800576
577 Bundle b = getIntent().getExtras();
578
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700579 boolean slideShow = (b != null)
580 ? b.getBoolean("slideshow", false)
581 : false;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800582 if (slideShow) {
583 setMode(MODE_SLIDESHOW);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800584 } else {
585 if (mFullScreenInNormalMode) {
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700586 getWindow().addFlags(
587 WindowManager.LayoutParams.FLAG_FULLSCREEN);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800588 }
589 if (mShowActionIcons) {
590 mActionIconPanel.setVisibility(View.VISIBLE);
591 mShutterButton.setVisibility(View.VISIBLE);
592 }
593 }
594
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700595 setupZoomButtonController(findViewById(R.id.rootLayout));
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800596 setupDismissOnScreenControlRunnable();
597
598 mNextImageView = findViewById(R.id.next_image);
599 mPrevImageView = findViewById(R.id.prev_image);
600 mNextImageView.setOnClickListener(this);
601 mPrevImageView.setOnClickListener(this);
602
603 if (mShowActionIcons) {
604 mNextImageView.setFocusable(true);
605 mPrevImageView.setFocusable(true);
606 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800607 }
608
Owen Lin9b2e9122009-03-27 15:10:14 -0700609 private void setRequestedOrientation() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800610 Intent intent = getIntent();
611 if (intent.hasExtra(MediaStore.EXTRA_SCREEN_ORIENTATION)) {
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700612 int orientation = intent.getIntExtra(
613 MediaStore.EXTRA_SCREEN_ORIENTATION,
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800614 ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
615 if (orientation != getRequestedOrientation()) {
616 setRequestedOrientation(orientation);
617 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800618 }
619 }
620
621 private Animation makeInAnimation(int id) {
622 Animation inAnimation = AnimationUtils.loadAnimation(this, id);
623 return inAnimation;
624 }
625
626 private Animation makeOutAnimation(int id) {
627 Animation outAnimation = AnimationUtils.loadAnimation(this, id);
628 return outAnimation;
629 }
630
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700631 void setMode(int mode) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800632 if (mMode == mode) {
633 return;
634 }
635
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700636 findViewById(R.id.slideShowContainer).setVisibility(
637 mode == MODE_SLIDESHOW ? View.VISIBLE : View.GONE);
638 findViewById(R.id.abs).setVisibility(mode == MODE_NORMAL
639 ? View.VISIBLE : View.GONE);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800640
641 Window win = getWindow();
642 mMode = mode;
643 if (mode == MODE_SLIDESHOW) {
644 win.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN
645 | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700646 mImageView.clear();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800647 mActionIconPanel.setVisibility(View.GONE);
648 mShutterButton.setVisibility(View.GONE);
649
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800650 findViewById(R.id.slideShowContainer).getRootView().requestLayout();
Chih-Chung Chang2b82c4a2009-04-14 18:00:10 +0800651
652 // The preferences we want to read:
653 // mUseShuffleOrder
654 // mSlideShowLoop
655 // mAnimationIndex
656 // mSlideShowInterval
657
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700658 mUseShuffleOrder = mPrefs.getBoolean(
659 "pref_gallery_slideshow_shuffle_key", false);
660 mSlideShowLoop = mPrefs.getBoolean(
661 "pref_gallery_slideshow_repeat_key", false);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800662 try {
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700663 mAnimationIndex = Integer.parseInt(mPrefs.getString(
664 "pref_gallery_slideshow_transition_key", "0"));
Ray Chen993105a2009-04-10 02:11:35 -0700665 } catch (NumberFormatException ex) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800666 Log.e(TAG, "couldn't parse preference: " + ex.toString());
667 mAnimationIndex = 0;
668 }
669 try {
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700670 mSlideShowInterval = Integer.parseInt(mPrefs.getString(
671 "pref_gallery_slideshow_interval_key", "3")) * 1000;
Ray Chen993105a2009-04-10 02:11:35 -0700672 } catch (NumberFormatException ex) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800673 Log.e(TAG, "couldn't parse preference: " + ex.toString());
674 mSlideShowInterval = 3000;
675 }
676
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800677
678 if (mUseShuffleOrder) {
679 generateShuffleOrder();
680 }
681 } else {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800682 win.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
683 if (mFullScreenInNormalMode) {
684 win.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
685 } else {
686 win.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
687 }
688
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700689 if (mGetter != null) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800690 mGetter.cancelCurrent();
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700691 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800692
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800693 if (mShowActionIcons) {
694 mActionIconPanel.setVisibility(View.VISIBLE);
695 mShutterButton.setVisibility(View.VISIBLE);
696 }
697
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700698 ImageViewTouchBase dst = mImageView;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800699 dst.mLastXTouchPos = -1;
700 dst.mLastYTouchPos = -1;
701
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700702 for (ImageViewTouchBase ivt : mSlideShowImageViews) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800703 ivt.clear();
704 }
705
706 mShuffleOrder = null;
707
708 // mGetter null is a proxy for being paused
709 if (mGetter != null) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800710 setImage(mCurrentPosition);
711 }
712 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800713 }
714
715 private void generateShuffleOrder() {
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700716 if (mShuffleOrder == null
717 || mShuffleOrder.length != mAllImages.getCount()) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800718 mShuffleOrder = new int[mAllImages.getCount()];
719 }
720
721 for (int i = 0; i < mShuffleOrder.length; i++) {
722 mShuffleOrder[i] = i;
723 }
724
725 for (int i = mShuffleOrder.length - 1; i > 0; i--) {
726 int r = mRandom.nextInt(i);
727 int tmp = mShuffleOrder[r];
728 mShuffleOrder[r] = mShuffleOrder[i];
729 mShuffleOrder[i] = tmp;
730 }
731 }
732
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700733 private void loadNextImage(final int requestedPos, final long delay,
734 final boolean firstCall) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800735 if (firstCall && mUseShuffleOrder) {
736 generateShuffleOrder();
737 }
738
739 final long targetDisplayTime = System.currentTimeMillis() + delay;
740
741 ImageGetterCallback cb = new ImageGetterCallback() {
742 public void completed(boolean wasCanceled) {
743 }
744
745 public boolean wantsThumbnail(int pos, int offset) {
746 return true;
747 }
748
749 public boolean wantsFullImage(int pos, int offset) {
750 return false;
751 }
752
753 public int [] loadOrder() {
754 return sOrder_slideshow;
755 }
756
757 public int fullImageSizeToUse(int pos, int offset) {
758 return 480; // TODO compute this
759 }
760
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700761 public void imageLoaded(final int pos, final int offset,
762 final Bitmap bitmap, final boolean isThumb) {
763 long timeRemaining = Math.max(0,
764 targetDisplayTime - System.currentTimeMillis());
Owen Lin3102f062009-03-24 20:38:25 -0700765 mHandler.postDelayedGetterCallback(new Runnable() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800766 public void run() {
Ray Chen993105a2009-04-10 02:11:35 -0700767 if (mMode == MODE_NORMAL) {
768 return;
769 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800770
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700771 ImageViewTouchBase oldView =
772 mSlideShowImageViews[mSlideShowImageCurrent];
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800773
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700774 if (++mSlideShowImageCurrent
775 == mSlideShowImageViews.length) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800776 mSlideShowImageCurrent = 0;
777 }
778
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700779 ImageViewTouchBase newView =
780 mSlideShowImageViews[mSlideShowImageCurrent];
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800781 newView.setVisibility(View.VISIBLE);
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700782 newView.setImageBitmapResetBase(bitmap, true);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800783 newView.bringToFront();
784
785 int animation = 0;
786
787 if (mAnimationIndex == -1) {
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700788 int n = mRandom.nextInt(
789 mSlideShowInAnimation.length);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800790 animation = n;
791 } else {
792 animation = mAnimationIndex;
793 }
794
795 Animation aIn = mSlideShowInAnimation[animation];
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700796 newView.startAnimation(aIn);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800797 newView.setVisibility(View.VISIBLE);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800798
799 Animation aOut = mSlideShowOutAnimation[animation];
800 oldView.setVisibility(View.INVISIBLE);
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700801 oldView.startAnimation(aOut);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800802
803 mCurrentPosition = requestedPos;
804
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700805 if (mCurrentPosition == mLastSlideShowImage
806 && !firstCall) {
Owen Lin3102f062009-03-24 20:38:25 -0700807 if (mSlideShowLoop) {
808 if (mUseShuffleOrder) {
809 generateShuffleOrder();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800810 }
Owen Lin3102f062009-03-24 20:38:25 -0700811 } else {
812 setMode(MODE_NORMAL);
813 return;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800814 }
Owen Lin3102f062009-03-24 20:38:25 -0700815 }
816
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700817 loadNextImage(
818 (mCurrentPosition + 1) % mAllImages.getCount(),
819 mSlideShowInterval, false);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800820 }
821 }, timeRemaining);
822 }
823 };
824 // Could be null if we're stopping a slide show in the course of pausing
825 if (mGetter != null) {
826 int pos = requestedPos;
827 if (mShuffleOrder != null) {
828 pos = mShuffleOrder[pos];
829 }
830 mGetter.setPosition(pos, cb);
831 }
832 }
833
834 private void makeGetter() {
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700835 mGetter = new ImageGetter(this);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800836 }
837
838 private boolean desiredSortOrder() {
839 String sortOrder = mPrefs.getString("pref_gallery_sort_key", null);
840 boolean sortAscending = false;
841 if (sortOrder != null) {
842 sortAscending = sortOrder.equals("ascending");
843 }
844 if (mCameraReviewMode) {
845 // Force left-arrow older pictures, right-arrow newer pictures.
846 sortAscending = true;
847 }
848 return sortAscending;
849 }
850
851 private void init(Uri uri) {
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700852 if (uri == null) {
Ray Chen35937472009-03-24 20:39:36 -0700853 return;
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700854 }
Owen Lin9b2e9122009-03-27 15:10:14 -0700855
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800856 mSortAscending = desiredSortOrder();
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700857 int sort = mSortAscending
858 ? ImageManager.SORT_ASCENDING
859 : ImageManager.SORT_DESCENDING;
Chih-Chung Changa6e21442009-04-17 23:18:09 +0800860 mAllImages = ImageManager.makeImageList(uri, getContentResolver(),
861 sort);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800862
863 uri = uri.buildUpon().query(null).build();
Chih-Chung Changfd6da322009-05-04 16:49:30 +0800864
865 IImage image = mAllImages.getImageForUri(uri);
866 if (image != null) {
867 mCurrentPosition = image.getRow();
868 mLastSlideShowImage = mCurrentPosition;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800869 }
870 }
871
Ray Chen72e1dfd2009-03-24 21:13:16 -0700872 private Uri getCurrentUri() {
Owen Lin101d5282009-04-03 16:20:08 -0700873 IImage image = mAllImages.getImageAt(mCurrentPosition);
Ray Chen72e1dfd2009-03-24 21:13:16 -0700874 Uri uri = null;
875 if (image != null){
876 String bucket = null;
877 uri = image.fullSizeImageUri();
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700878 if (getIntent() != null && getIntent().getData() != null) {
Ray Chen72e1dfd2009-03-24 21:13:16 -0700879 bucket = getIntent().getData().getQueryParameter("bucketId");
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700880 }
Ray Chen72e1dfd2009-03-24 21:13:16 -0700881
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700882 if (bucket != null) {
883 uri = uri.buildUpon().appendQueryParameter("bucketId", bucket)
884 .build();
885 }
Ray Chen72e1dfd2009-03-24 21:13:16 -0700886 }
887 return uri;
888 }
Owen Lin9b2e9122009-03-27 15:10:14 -0700889
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800890 @Override
891 public void onSaveInstanceState(Bundle b) {
892 super.onSaveInstanceState(b);
Owen Lin9b2e9122009-03-27 15:10:14 -0700893
Ray Chen72e1dfd2009-03-24 21:13:16 -0700894 Uri uri = getCurrentUri();
895 if (uri != null) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800896 b.putString("uri", uri.toString());
897 }
Owen Lin9b2e9122009-03-27 15:10:14 -0700898
Ray Chen72e1dfd2009-03-24 21:13:16 -0700899 if (mMode == MODE_SLIDESHOW) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800900 b.putBoolean("slideshow", true);
Ray Chen35937472009-03-24 20:39:36 -0700901 }
902 }
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 Lin937fc482009-04-14 02:02:51 -0700907
Ray Chen993105a2009-04-10 02:11:35 -0700908 BitmapManager.instance().allowAllDecoding(false);
Owen Lin9b2e9122009-03-27 15:10:14 -0700909
Ray Chen72e1dfd2009-03-24 21:13:16 -0700910 init(mSavedUri);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800911
912 // normally this will never be zero but if one "backs" into this
913 // activity after removing the sdcard it could be zero. in that
914 // case just "finish" since there's nothing useful that can happen.
Ray Chen72e1dfd2009-03-24 21:13:16 -0700915 int count = mAllImages.getCount();
916 if (count == 0) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800917 finish();
Ray Chen72e1dfd2009-03-24 21:13:16 -0700918 } else if (count <= mCurrentPosition) {
919 mCurrentPosition = count - 1;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800920 }
921
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800922 if (mGetter == null) {
923 makeGetter();
924 }
925
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700926 if (mMode == MODE_SLIDESHOW) {
927 loadNextImage(mCurrentPosition, 0, true);
928 } else { // MODE_NORMAL
929 setImage(mCurrentPosition);
930 }
The Android Open Source Projecte3f45162009-03-11 12:11:58 -0700931 }
932
933 @Override
Owen Lin9e0bda22009-05-04 17:10:37 -0700934 public void onStop() {
935 super.onStop();
Ray Chen993105a2009-04-10 02:11:35 -0700936 BitmapManager.instance().cancelAllDecoding();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800937
938 mGetter.cancelCurrent();
939 mGetter.stop();
940 mGetter = null;
941 setMode(MODE_NORMAL);
942
Owen Lin3102f062009-03-24 20:38:25 -0700943 // removing all callback in the message queue
944 mHandler.removeAllGetterCallbacks();
Owen Lin9b2e9122009-03-27 15:10:14 -0700945
Ray Chen72e1dfd2009-03-24 21:13:16 -0700946 mSavedUri = getCurrentUri();
Owen Lin3102f062009-03-24 20:38:25 -0700947
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800948 mAllImages.deactivate();
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700949 mDismissOnScreenControlsRunnable.run();
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700950 if (mDismissOnScreenControlsRunnable != null) {
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700951 mHandler.removeCallbacks(mDismissOnScreenControlsRunnable);
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700952 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800953
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700954 mImageView.clear();
955 mCache.clear();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800956
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -0700957 for (ImageViewTouchBase iv : mSlideShowImageViews) {
958 iv.clear();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800959 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800960 }
961
962 public void onClick(View v) {
963 switch (v.getId()) {
964
965 case R.id.shutter_button: {
966 if (mCameraReviewMode) {
967 finish();
968 } else {
969 MenuHelper.gotoStillImageCapture(this);
970 }
971 }
972 break;
973
974 case R.id.gallery: {
975 MenuHelper.gotoCameraImageGallery(this);
976 }
977 break;
978
979 case R.id.discard: {
Ray Chen7638a542009-03-24 20:37:45 -0700980 MenuHelper.deletePhoto(this, mDeletePhotoRunnable);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800981 }
982 break;
983
984 case R.id.share: {
985 Uri u = mAllImages.getImageAt(mCurrentPosition).fullSizeImageUri();
Ray Chen993105a2009-04-10 02:11:35 -0700986 if (MenuHelper.isMMSUri(u)) {
987 return;
988 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800989 Intent intent = new Intent();
990 intent.setAction(Intent.ACTION_SEND);
991 intent.setType("image/jpeg");
992 intent.putExtra(Intent.EXTRA_STREAM, u);
993 try {
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700994 startActivity(Intent.createChooser(
995 intent, getText(R.string.sendImage)));
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800996 } catch (android.content.ActivityNotFoundException ex) {
Chih-Chung Chang3321d402009-04-02 21:45:14 -0700997 Toast.makeText(this, R.string.no_way_to_share_image,
998 Toast.LENGTH_SHORT).show();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800999 }
1000 }
1001 break;
1002
1003 case R.id.setas: {
1004 Uri u = mAllImages.getImageAt(mCurrentPosition).fullSizeImageUri();
1005 Intent intent = new Intent(Intent.ACTION_ATTACH_DATA, u);
1006 try {
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001007 startActivity(Intent.createChooser(
1008 intent, getText(R.string.setImage)));
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001009 } catch (android.content.ActivityNotFoundException ex) {
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001010 Toast.makeText(this, R.string.no_way_to_share_video,
1011 Toast.LENGTH_SHORT).show();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001012 }
1013 }
1014 break;
1015
1016 case R.id.next_image: {
1017 moveNextOrPrevious(1);
1018 }
1019 break;
1020
1021 case R.id.prev_image: {
1022 moveNextOrPrevious(-1);
1023 }
1024 break;
1025 }
1026 }
1027
1028 private void moveNextOrPrevious(int delta) {
1029 int nextImagePos = mCurrentPosition + delta;
1030 if ((0 <= nextImagePos) && (nextImagePos < mAllImages.getCount())) {
1031 setImage(nextImagePos);
1032 }
1033 }
1034
1035 @Override
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001036 protected void onActivityResult(int requestCode, int resultCode,
1037 Intent data) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001038 switch (requestCode) {
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001039 case MenuHelper.RESULT_COMMON_MENU_CROP:
1040 if (resultCode == RESULT_OK) {
1041 // The CropImage activity passes back the Uri of the
1042 // cropped image as the Action rather than the Data.
1043 mSavedUri = Uri.parse(data.getAction());
1044 }
1045 break;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001046 }
1047 }
Owen Lin3102f062009-03-24 20:38:25 -07001048
1049 static class LocalHandler extends Handler {
1050 private static final int IMAGE_GETTER_CALLBACK = 1;
1051
1052 @Override
1053 public void handleMessage(Message message) {
1054 switch(message.what) {
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001055 case IMAGE_GETTER_CALLBACK:
1056 ((Runnable) message.obj).run();
1057 break;
Owen Lin3102f062009-03-24 20:38:25 -07001058 }
1059 }
1060
1061 public void postGetterCallback(Runnable callback) {
1062 postDelayedGetterCallback(callback, 0);
1063 }
1064
1065 public void postDelayedGetterCallback(Runnable callback, long delay) {
Ray Chen993105a2009-04-10 02:11:35 -07001066 if (callback == null) {
1067 throw new NullPointerException();
1068 }
Owen Lin3102f062009-03-24 20:38:25 -07001069 Message message = Message.obtain();
1070 message.what = IMAGE_GETTER_CALLBACK;
1071 message.obj = callback;
1072 sendMessageDelayed(message, delay);
1073 }
1074
1075 public void removeAllGetterCallbacks() {
1076 removeMessages(IMAGE_GETTER_CALLBACK);
1077 }
1078 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001079}
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001080
1081class ImageViewTouch extends ImageViewTouchBase {
Owen Lin41a85782009-04-20 15:59:57 +08001082 private final ViewImage mViewImage;
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001083 private boolean mEnableTrackballScroll;
1084
1085 public ImageViewTouch(Context context) {
1086 super(context);
1087 mViewImage = (ViewImage) context;
1088 }
1089
1090 public ImageViewTouch(Context context, AttributeSet attrs) {
1091 super(context, attrs);
1092 mViewImage = (ViewImage) context;
1093 }
1094
1095 public void setEnableTrackballScroll(boolean enable) {
1096 mEnableTrackballScroll = enable;
1097 }
1098
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -07001099 protected void postTranslateCenter(float dx, float dy) {
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001100 super.postTranslate(dx, dy);
Chih-Chung Chang0a475e12009-04-16 11:42:12 +08001101 center(true, true);
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001102 }
1103
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -07001104 static final float PAN_RATE = 20;
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001105
1106 @Override
1107 public boolean onKeyDown(int keyCode, KeyEvent event) {
1108 // Don't respond to arrow keys if trackball scrolling is not enabled
1109 if (!mEnableTrackballScroll) {
1110 if ((keyCode >= KeyEvent.KEYCODE_DPAD_UP)
1111 && (keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT)) {
1112 return super.onKeyDown(keyCode, event);
1113 }
1114 }
1115
1116 int current = mViewImage.mCurrentPosition;
1117
1118 int nextImagePos = -2; // default no next image
1119 try {
1120 switch (keyCode) {
1121 case KeyEvent.KEYCODE_DPAD_CENTER: {
1122 if (mViewImage.isPickIntent()) {
Owen Lin101d5282009-04-03 16:20:08 -07001123 IImage img = mViewImage.mAllImages
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001124 .getImageAt(mViewImage.mCurrentPosition);
1125 mViewImage.setResult(ViewImage.RESULT_OK,
1126 new Intent().setData(img.fullSizeImageUri()));
1127 mViewImage.finish();
1128 }
1129 break;
1130 }
1131 case KeyEvent.KEYCODE_DPAD_LEFT: {
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001132 int maxOffset = (current == 0) ? 0 : ViewImage.HYSTERESIS;
Owen Lin101d5282009-04-03 16:20:08 -07001133 if (getScale() <= 1F
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001134 || isShiftedToNextImage(true, maxOffset)) {
1135 nextImagePos = current - 1;
1136 } else {
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -07001137 panBy(PAN_RATE, 0);
Chih-Chung Chang0a475e12009-04-16 11:42:12 +08001138 center(true, false);
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001139 }
1140 return true;
1141 }
1142 case KeyEvent.KEYCODE_DPAD_RIGHT: {
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001143 int maxOffset =
1144 (current == mViewImage.mAllImages.getCount() - 1)
1145 ? 0
1146 : ViewImage.HYSTERESIS;
1147 if (getScale() <= 1F
1148 || isShiftedToNextImage(false, maxOffset)) {
1149 nextImagePos = current + 1;
1150 } else {
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -07001151 panBy(-PAN_RATE, 0);
Chih-Chung Chang0a475e12009-04-16 11:42:12 +08001152 center(true, false);
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001153 }
1154 return true;
1155 }
1156 case KeyEvent.KEYCODE_DPAD_UP: {
1157 panBy(0, PAN_RATE);
Chih-Chung Chang0a475e12009-04-16 11:42:12 +08001158 center(false, true);
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001159 return true;
1160 }
1161 case KeyEvent.KEYCODE_DPAD_DOWN: {
1162 panBy(0, -PAN_RATE);
Chih-Chung Chang0a475e12009-04-16 11:42:12 +08001163 center(false, true);
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001164 return true;
1165 }
1166 case KeyEvent.KEYCODE_DEL:
1167 MenuHelper.deletePhoto(
1168 mViewImage, mViewImage.mDeletePhotoRunnable);
1169 break;
1170 }
1171 } finally {
1172 if (nextImagePos >= 0
1173 && nextImagePos < mViewImage.mAllImages.getCount()) {
1174 synchronized (mViewImage) {
1175 mViewImage.setMode(ViewImage.MODE_NORMAL);
1176 mViewImage.setImage(nextImagePos);
1177 }
1178 } else if (nextImagePos != -2) {
Chih-Chung Chang0a475e12009-04-16 11:42:12 +08001179 center(true, true);
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001180 }
1181 }
1182
1183 return super.onKeyDown(keyCode, event);
1184 }
1185
1186 protected boolean isShiftedToNextImage(boolean left, int maxOffset) {
1187 boolean retval;
1188 Bitmap bitmap = mBitmapDisplayed;
1189 Matrix m = getImageViewMatrix();
1190 if (left) {
1191 float [] t1 = new float[] { 0, 0 };
1192 m.mapPoints(t1);
1193 retval = t1[0] > maxOffset;
1194 } else {
1195 int width = bitmap != null ? bitmap.getWidth() : getWidth();
1196 float [] t1 = new float[] { width, 0 };
1197 m.mapPoints(t1);
1198 retval = t1[0] + maxOffset < getWidth();
1199 }
1200 return retval;
1201 }
1202}
1203
1204/*
1205 * Here's the loading strategy. For any given image, load the thumbnail
1206 * into memory and post a callback to display the resulting bitmap.
1207 *
1208 * Then proceed to load the full image bitmap. Three things can
1209 * happen at this point:
1210 *
1211 * 1. the image fails to load because the UI thread decided
1212 * to move on to a different image. This "cancellation" happens
1213 * by virtue of the UI thread closing the stream containing the
1214 * image being decoded. BitmapFactory.decodeStream returns null
1215 * in this case.
1216 *
1217 * 2. the image loaded successfully. At that point we post
1218 * a callback to the UI thread to actually show the bitmap.
1219 *
1220 * 3. when the post runs it checks to see if the image that was
1221 * loaded is still the one we want. The UI may have moved on
1222 * to some other image and if so we just drop the newly loaded
1223 * bitmap on the floor.
1224 */
1225
1226interface ImageGetterCallback {
1227 public void imageLoaded(int pos, int offset, Bitmap bitmap,
1228 boolean isThumb);
1229 public boolean wantsThumbnail(int pos, int offset);
1230 public boolean wantsFullImage(int pos, int offset);
1231 public int fullImageSizeToUse(int pos, int offset);
1232 public void completed(boolean wasCanceled);
1233 public int [] loadOrder();
1234}
1235
1236class ImageGetter {
Owen Lin937fc482009-04-14 02:02:51 -07001237
1238 @SuppressWarnings("unused")
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001239 private static final String TAG = "ImageGetter";
1240
1241 // The thread which does the work.
Owen Lin41a85782009-04-20 15:59:57 +08001242 private final Thread mGetterThread;
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001243
1244 // The base position that's being retrieved. The actual images retrieved
1245 // are this base plus each of the offets.
1246 private int mCurrentPosition = -1;
1247
1248 // The callback to invoke for each image.
1249 private ImageGetterCallback mCB;
1250
1251 // This is the loader cancelable that gets set while we're loading an image.
1252 // If we change position we can cancel the current load using this.
Owen Lin41a85782009-04-20 15:59:57 +08001253 private Cancelable<Bitmap> mLoad;
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001254
1255 // True if we're canceling the current load.
1256 private boolean mCancelCurrent = false;
1257
1258 // True when the therad should exit.
1259 private boolean mDone = false;
1260
1261 // True when the loader thread is waiting for work.
1262 private boolean mReady = false;
1263
1264 // The ViewImage this ImageGetter belongs to
1265 ViewImage mViewImage;
1266
1267 void cancelCurrent() {
1268 synchronized (this) {
1269 if (!mReady) {
1270 mCancelCurrent = true;
Owen Lin41a85782009-04-20 15:59:57 +08001271 Cancelable<Bitmap> load = mLoad;
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001272 if (load != null) {
Owen Lin41a85782009-04-20 15:59:57 +08001273 load.requestCancel();
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001274 }
1275 mCancelCurrent = false;
1276 }
1277 }
1278 }
1279
1280 private class ImageGetterRunnable implements Runnable {
1281 private Runnable callback(final int position, final int offset,
1282 final boolean isThumb, final Bitmap bitmap) {
1283 return new Runnable() {
1284 public void run() {
Owen Lin937fc482009-04-14 02:02:51 -07001285 // check for inflight callbacks that aren't applicable
Ray Chen993105a2009-04-10 02:11:35 -07001286 // any longer before delivering them
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001287 if (!isCanceled() && position == mCurrentPosition) {
1288 mCB.imageLoaded(position, offset, bitmap, isThumb);
1289 } else if (bitmap != null) {
1290 bitmap.recycle();
1291 }
1292 }
1293 };
1294 }
1295
1296 private Runnable completedCallback(final boolean wasCanceled) {
1297 return new Runnable() {
1298 public void run() {
1299 mCB.completed(wasCanceled);
1300 }
1301 };
1302 }
1303
1304 public void run() {
1305 int lastPosition = -1;
1306 while (!mDone) {
1307 synchronized (ImageGetter.this) {
1308 mReady = true;
1309 ImageGetter.this.notify();
1310
Owen Lin937fc482009-04-14 02:02:51 -07001311 if (mCurrentPosition == -1
Ray Chen993105a2009-04-10 02:11:35 -07001312 || lastPosition == mCurrentPosition) {
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001313 try {
1314 ImageGetter.this.wait();
1315 } catch (InterruptedException ex) {
1316 continue;
1317 }
1318 }
1319
1320 lastPosition = mCurrentPosition;
1321 mReady = false;
1322 }
1323
1324 if (lastPosition != -1) {
1325 int imageCount = mViewImage.mAllImages.getCount();
1326
1327 int [] order = mCB.loadOrder();
1328 for (int i = 0; i < order.length; i++) {
1329 int offset = order[i];
1330 int imageNumber = lastPosition + offset;
1331 if (imageNumber >= 0 && imageNumber < imageCount) {
Ray Chen993105a2009-04-10 02:11:35 -07001332 IImage image = mViewImage.mAllImages
1333 .getImageAt(lastPosition + offset);
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001334 if (image == null || isCanceled()) {
1335 break;
1336 }
1337 if (mCB.wantsThumbnail(lastPosition, offset)) {
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001338 Bitmap b = image.thumbBitmap();
Ray Chen993105a2009-04-10 02:11:35 -07001339 mViewImage.mHandler.postGetterCallback(
Owen Lin937fc482009-04-14 02:02:51 -07001340 callback(lastPosition, offset,
Ray Chen993105a2009-04-10 02:11:35 -07001341 true, b));
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001342 }
1343 }
1344 }
1345
1346 for (int i = 0; i < order.length; i++) {
1347 int offset = order[i];
1348 int imageNumber = lastPosition + offset;
1349 if (imageNumber >= 0 && imageNumber < imageCount) {
Ray Chen993105a2009-04-10 02:11:35 -07001350 IImage image = mViewImage.mAllImages
1351 .getImageAt(lastPosition + offset);
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001352 if (mCB.wantsFullImage(lastPosition, offset)) {
Ray Chen993105a2009-04-10 02:11:35 -07001353 int sizeToUse = mCB.fullImageSizeToUse(
1354 lastPosition, offset);
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001355 if (image != null && !isCanceled()) {
Ray Chen993105a2009-04-10 02:11:35 -07001356 mLoad = image.fullSizeBitmapCancelable(
1357 sizeToUse);
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001358 }
1359 if (mLoad != null) {
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001360 // The return value could be null if the
1361 // bitmap is too big, or we cancelled it.
Owen Lin41a85782009-04-20 15:59:57 +08001362 Bitmap b;
1363 try {
1364 b = mLoad.get();
1365 } catch (InterruptedException e) {
1366 b = null;
1367 } catch (ExecutionException e) {
1368 throw new RuntimeException(e);
1369 } catch (CancellationException e) {
1370 b = null;
1371 }
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001372 mLoad = null;
1373 if (b != null) {
1374 if (isCanceled()) {
1375 b.recycle();
1376 } else {
Ray Chen993105a2009-04-10 02:11:35 -07001377 Runnable cb = callback(
Owen Lin937fc482009-04-14 02:02:51 -07001378 lastPosition, offset,
Ray Chen993105a2009-04-10 02:11:35 -07001379 false, b);
1380 mViewImage.mHandler
1381 .postGetterCallback(cb);
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001382 }
1383 }
1384 }
1385 }
1386 }
1387 }
1388 mViewImage.mHandler.postGetterCallback(
1389 completedCallback(isCanceled()));
1390 }
1391 }
1392 }
1393 }
Owen Lin101d5282009-04-03 16:20:08 -07001394
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001395 public ImageGetter(ViewImage viewImage) {
1396 mViewImage = viewImage;
1397 mGetterThread = new Thread(new ImageGetterRunnable());
1398 mGetterThread.setName("ImageGettter");
Ray Chen993105a2009-04-10 02:11:35 -07001399 BitmapManager.instance().allowThreadDecoding(mGetterThread);
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001400 mGetterThread.start();
1401 }
1402
1403 private boolean isCanceled() {
1404 synchronized (this) {
1405 return mCancelCurrent;
1406 }
1407 }
1408
1409 public void setPosition(int position, ImageGetterCallback cb) {
1410 synchronized (this) {
1411 if (!mReady) {
1412 try {
1413 mCancelCurrent = true;
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001414 // if the thread is waiting before loading the full size
1415 // image then this will free it up
Ray Chen993105a2009-04-10 02:11:35 -07001416 BitmapManager.instance()
1417 .cancelThreadDecoding(mGetterThread);
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001418 ImageGetter.this.notify();
1419 ImageGetter.this.wait();
Ray Chen993105a2009-04-10 02:11:35 -07001420 BitmapManager.instance()
1421 .allowThreadDecoding(mGetterThread);
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001422 mCancelCurrent = false;
1423 } catch (InterruptedException ex) {
1424 // not sure what to do here
1425 }
1426 }
1427 }
1428
1429 mCurrentPosition = position;
1430 mCB = cb;
1431
1432 synchronized (this) {
1433 ImageGetter.this.notify();
1434 }
1435 }
1436
1437 public void stop() {
1438 synchronized (this) {
1439 mDone = true;
1440 ImageGetter.this.notify();
1441 }
1442 try {
1443 mGetterThread.join();
1444 } catch (InterruptedException ex) {
Ray Chen993105a2009-04-10 02:11:35 -07001445 // Ignore the exception
Chih-Chung Chang3321d402009-04-02 21:45:14 -07001446 }
1447 }
1448}
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -07001449
1450// This is a cache for Bitmap displayed in ViewImage (normal mode, thumb only).
1451class BitmapCache implements ImageViewTouchBase.Recycler {
Chih-Chung Chang73b7a3a2009-04-12 23:28:16 -07001452 public static class Entry {
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -07001453 int mPos;
1454 Bitmap mBitmap;
1455 public Entry() {
1456 clear();
1457 }
1458 public void clear() {
1459 mPos = -1;
1460 mBitmap = null;
1461 }
1462 }
1463
Owen Lin41a85782009-04-20 15:59:57 +08001464 private final Entry[] mCache;
Owen Lin937fc482009-04-14 02:02:51 -07001465
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -07001466 public BitmapCache(int size) {
1467 mCache = new Entry[size];
1468 for (int i = 0; i < mCache.length; i++) {
1469 mCache[i] = new Entry();
1470 }
1471 }
1472
1473 // Given the position, find the associated entry. Returns null if there is
1474 // no such entry.
1475 private Entry findEntry(int pos) {
1476 for (Entry e : mCache) {
1477 if (pos == e.mPos) {
1478 return e;
1479 }
1480 }
1481 return null;
1482 }
Owen Lin937fc482009-04-14 02:02:51 -07001483
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -07001484 // Returns the thumb bitmap if we have it, otherwise return null.
1485 public synchronized Bitmap getBitmap(int pos) {
1486 Entry e = findEntry(pos);
1487 if (e != null) {
1488 return e.mBitmap;
1489 }
1490 return null;
1491 }
1492
1493 public synchronized void put(int pos, Bitmap bitmap) {
1494 // First see if we already have this entry.
1495 if (findEntry(pos) != null) {
1496 return;
1497 }
1498
1499 // Find the best entry we should replace.
1500 // See if there is any empty entry.
1501 // Otherwise assuming sequential access, kick out the entry with the
1502 // greatest distance.
1503 Entry best = null;
1504 int maxDist = -1;
1505 for (Entry e : mCache) {
1506 if (e.mPos == -1) {
1507 best = e;
1508 break;
1509 } else {
1510 int dist = Math.abs(pos - e.mPos);
1511 if (dist > maxDist) {
1512 maxDist = dist;
1513 best = e;
1514 }
1515 }
1516 }
Owen Lin937fc482009-04-14 02:02:51 -07001517
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -07001518 // Recycle the image being kicked out.
1519 // This only works because our current usage is sequential, so we
1520 // do not happen to recycle the image being displayed.
1521 if (best.mBitmap != null) {
1522 best.mBitmap.recycle();
1523 }
1524
1525 best.mPos = pos;
1526 best.mBitmap = bitmap;
1527 }
Owen Lin937fc482009-04-14 02:02:51 -07001528
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -07001529 // Recycle all bitmaps in the cache and clear the cache.
1530 public synchronized void clear() {
1531 for (Entry e : mCache) {
1532 if (e.mBitmap != null) {
1533 e.mBitmap.recycle();
1534 }
1535 e.clear();
1536 }
1537 }
1538
1539 // Returns whether the bitmap is in the cache.
1540 public synchronized boolean hasBitmap(int pos) {
1541 Entry e = findEntry(pos);
1542 return (e != null);
1543 }
Owen Lin937fc482009-04-14 02:02:51 -07001544
Chih-Chung Chang7db87ab2009-04-12 21:42:43 -07001545 // Recycle the bitmap if it's not in the cache.
1546 // The input must be non-null.
1547 public synchronized void recycle(Bitmap b) {
1548 for (Entry e : mCache) {
1549 if (e.mPos != -1) {
1550 if (e.mBitmap == b) {
1551 return;
1552 }
1553 }
1554 }
1555 b.recycle();
1556 }
1557}