blob: d1ef1420a3fa235b5a27e1ed8d879de4ac8973ae [file] [log] [blame]
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001/*
2 * Copyright (C) 5163 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
19import android.app.Activity;
20import android.content.ContentResolver;
21import android.content.Context;
22import android.content.Intent;
23import android.content.SharedPreferences;
The Android Open Source Projectb97ccf32009-01-09 17:51:25 -080024import android.content.pm.ActivityInfo;
The Android Open Source Project1d4c7502008-10-21 07:00:00 -070025import android.graphics.Bitmap;
26import android.graphics.Matrix;
27import android.net.Uri;
28import android.os.Bundle;
29import android.os.Handler;
30import android.os.Message;
31import android.os.PowerManager;
32import android.provider.MediaStore;
33import android.util.AttributeSet;
34import android.util.Config;
35import android.util.Log;
36import android.view.GestureDetector;
37import android.view.KeyEvent;
38import android.view.Menu;
39import android.view.MenuItem;
40import android.view.MotionEvent;
41import android.view.View;
42import android.view.Window;
43import android.view.WindowManager;
44import android.view.View.OnClickListener;
45import android.view.ViewGroup.LayoutParams;
46import android.view.animation.Animation;
47import android.view.animation.AlphaAnimation;
48import android.view.animation.AnimationUtils;
49import android.widget.LinearLayout;
50import android.widget.RelativeLayout;
51import android.widget.Scroller;
52import android.widget.TextView;
The Android Open Source Projectb97ccf32009-01-09 17:51:25 -080053import android.widget.Toast;
The Android Open Source Project1d4c7502008-10-21 07:00:00 -070054import android.widget.ZoomControls;
55import android.preference.PreferenceManager;
56
57import com.android.camera.ImageManager.IImage;
58
59import java.util.Random;
60
The Android Open Source Projectb97ccf32009-01-09 17:51:25 -080061public class ViewImage extends Activity implements View.OnClickListener
The Android Open Source Project1d4c7502008-10-21 07:00:00 -070062{
63 static final String TAG = "ViewImage";
64 private ImageGetter mGetter;
65
66 static final boolean sSlideShowHidesStatusBar = true;
67
68 // Choices for what adjacents to load.
69 static private final int[] sOrder_adjacents = new int[] { 0, 1, -1 };
70 static private final int[] sOrder_slideshow = new int[] { 0 };
71
72 Handler mHandler = new Handler() {
73 @Override
74 public void handleMessage(Message msg) {
75 }
76 };
77
78 private Random mRandom = new Random(System.currentTimeMillis());
79 private int [] mShuffleOrder;
80 private boolean mUseShuffleOrder = false;
81 private boolean mSlideShowLoop = false;
82
83 private static final int MODE_NORMAL = 1;
84 private static final int MODE_SLIDESHOW = 2;
85 private int mMode = MODE_NORMAL;
The Android Open Source Projectb97ccf32009-01-09 17:51:25 -080086 private boolean mFullScreenInNormalMode;
87 private boolean mShowActionIcons;
88 private View mActionIconPanel;
The Android Open Source Project1d4c7502008-10-21 07:00:00 -070089
90 private boolean mSortAscending = false;
91 private int mSlideShowInterval;
92 private int mLastSlideShowImage;
93 private boolean mFirst = true;
94 private int mCurrentPosition = 0;
95 private boolean mLayoutComplete = false;
96
97 // represents which style animation to use
98 private int mAnimationIndex;
99 private Animation [] mSlideShowInAnimation;
100 private Animation [] mSlideShowOutAnimation;
101
102 private SharedPreferences mPrefs;
103 private MenuItem mFlipItem;
104
105 private View mNextImageView, mPrevImageView;
106 private Animation mHideNextImageViewAnimation = new AlphaAnimation(1F, 0F);
107 private Animation mHidePrevImageViewAnimation = new AlphaAnimation(1F, 0F);
108 private Animation mShowNextImageViewAnimation = new AlphaAnimation(0F, 1F);
109 private Animation mShowPrevImageViewAnimation = new AlphaAnimation(0F, 1F);
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800110
111
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700112 static final int sPadding = 20;
113 static final int sHysteresis = sPadding * 2;
114 static final int sBaseScrollDuration = 1000; // ms
115
116 private ImageManager.IImageList mAllImages;
117
118 private int mSlideShowImageCurrent = 0;
119 private ImageViewTouch [] mSlideShowImageViews = new ImageViewTouch[2];
120
121
122 // Array of image views. The center view is the one the user is focused
123 // on. The one at the zeroth position and the second position reflect
124 // the images to the left/right of the center image.
125 private ImageViewTouch[] mImageViews = new ImageViewTouch[3];
126
127 // Container for the three image views. This guy can be "scrolled"
128 // to reveal the image prior to and after the center image.
129 private ScrollHandler mScroller;
130
131 private MenuHelper.MenuItemsResult mImageMenuRunnable;
132
133 Runnable mDismissOnScreenControlsRunnable;
134 ZoomControls mZoomControls;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800135
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700136 public ViewImage() {
137 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800138
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700139 private void updateNextPrevControls() {
140 boolean showPrev = mCurrentPosition > 0;
141 boolean showNext = mCurrentPosition < mAllImages.getCount() - 1;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800142
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700143 boolean prevIsVisible = mPrevImageView.getVisibility() == View.VISIBLE;
144 boolean nextIsVisible = mNextImageView.getVisibility() == View.VISIBLE;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800145
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700146 if (showPrev && !prevIsVisible) {
147 Animation a = mShowPrevImageViewAnimation;
148 a.setDuration(500);
149 a.startNow();
150 mPrevImageView.setAnimation(a);
151 mPrevImageView.setVisibility(View.VISIBLE);
152 } else if (!showPrev && prevIsVisible) {
153 Animation a = mHidePrevImageViewAnimation;
154 a.setDuration(500);
155 a.startNow();
156 mPrevImageView.setAnimation(a);
157 mPrevImageView.setVisibility(View.GONE);
158 }
159
160 if (showNext && !nextIsVisible) {
161 Animation a = mShowNextImageViewAnimation;
162 a.setDuration(500);
163 a.startNow();
164 mNextImageView.setAnimation(a);
165 mNextImageView.setVisibility(View.VISIBLE);
166 } else if (!showNext && nextIsVisible) {
167 Animation a = mHideNextImageViewAnimation;
168 a.setDuration(500);
169 a.startNow();
170 mNextImageView.setAnimation(a);
171 mNextImageView.setVisibility(View.GONE);
172 }
173 }
174
175 private void showOnScreenControls() {
176 if (mZoomControls != null) {
177 if (mZoomControls.getVisibility() == View.GONE) {
178 mZoomControls.show();
179 mZoomControls.requestFocus(); // this shouldn't be necessary
180 }
181 updateNextPrevControls();
182 scheduleDismissOnScreenControls();
183 }
184 }
185
186 @Override
187 public boolean dispatchTouchEvent(MotionEvent m) {
188 boolean sup = super.dispatchTouchEvent(m);
189 if (sup == false) {
190 if (mMode == MODE_SLIDESHOW) {
191 mSlideShowImageViews[mSlideShowImageCurrent].handleTouchEvent(m);
192 } else if (mMode == MODE_NORMAL){
193 mImageViews[1].handleTouchEvent(m);
194 }
195 return true;
196 }
197 return true;
198 }
199
200 private void scheduleDismissOnScreenControls() {
201 mHandler.removeCallbacks(mDismissOnScreenControlsRunnable);
202 mHandler.postDelayed(mDismissOnScreenControlsRunnable, 1500);
203 }
204
205 public View getZoomControls() {
206 if (mZoomControls == null) {
207 mZoomControls = new ZoomControls(this);
208 mZoomControls.setVisibility(View.GONE);
209 mZoomControls.setZoomSpeed(0);
210 mDismissOnScreenControlsRunnable = new Runnable() {
211 public void run() {
212 mZoomControls.hide();
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800213
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700214 if (mNextImageView.getVisibility() == View.VISIBLE) {
215 Animation a = mHideNextImageViewAnimation;
216 a.setDuration(500);
217 a.startNow();
218 mNextImageView.setAnimation(a);
219 mNextImageView.setVisibility(View.INVISIBLE);
220 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800221
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700222 if (mPrevImageView.getVisibility() == View.VISIBLE) {
223 Animation a = mHidePrevImageViewAnimation;
224 a.setDuration(500);
225 a.startNow();
226 mPrevImageView.setAnimation(a);
227 mPrevImageView.setVisibility(View.INVISIBLE);
228 }
229 }
230 };
231 mZoomControls.setOnZoomInClickListener(new OnClickListener() {
232 public void onClick(View v) {
233 mHandler.removeCallbacks(mDismissOnScreenControlsRunnable);
234 mImageViews[1].zoomIn();
235 scheduleDismissOnScreenControls();
236 }
237 });
238 mZoomControls.setOnZoomOutClickListener(new OnClickListener() {
239 public void onClick(View v) {
240 mHandler.removeCallbacks(mDismissOnScreenControlsRunnable);
241 mImageViews[1].zoomOut();
242 scheduleDismissOnScreenControls();
243 }
244 });
245 }
246 return mZoomControls;
247 }
248
249 private boolean isPickIntent() {
250 String action = getIntent().getAction();
251 return (Intent.ACTION_PICK.equals(action) || Intent.ACTION_GET_CONTENT.equals(action));
252 }
253
254 private static final boolean sDragLeftRight = false;
255 private static final boolean sUseBounce = false;
256 private static final boolean sAnimateTransitions = false;
257
258 static public class ImageViewTouch extends ImageViewTouchBase {
259 private ViewImage mViewImage;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800260
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700261 private static int TOUCH_STATE_REST = 0;
262 private static int TOUCH_STATE_LEFT_PRESS = 1;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800263 private static int TOUCH_STATE_RIGHT_PRESS = 2;
264 private static int TOUCH_STATE_PANNING = 3;
265
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700266 private static int TOUCH_AREA_WIDTH = 60;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800267
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700268 private int mTouchState = TOUCH_STATE_REST;
269
270 public ImageViewTouch(Context context) {
271 super(context);
272 mViewImage = (ViewImage) context;
273 }
274
275 public ImageViewTouch(Context context, AttributeSet attrs) {
276 super(context, attrs);
277 mViewImage = (ViewImage) context;
278 }
279
280 protected void postTranslate(float dx, float dy, boolean bounceOK) {
281 super.postTranslate(dx, dy);
282 if (dx != 0F || dy != 0F)
283 mViewImage.showOnScreenControls();
284
285 if (!sUseBounce) {
286 center(true, false, false);
287 }
288 }
289
290 protected ScrollHandler scrollHandler() {
291 return mViewImage.mScroller;
292 }
293
294 public boolean handleTouchEvent(MotionEvent m) {
295 int viewWidth = getWidth();
296 ViewImage viewImage = mViewImage;
297 int x = (int) m.getX();
298 int y = (int) m.getY();
299
300 switch (m.getAction()) {
301 case MotionEvent.ACTION_DOWN:
302 viewImage.setMode(MODE_NORMAL);
303 viewImage.showOnScreenControls();
304 mLastXTouchPos = x;
305 mLastYTouchPos = y;
306 mTouchState = TOUCH_STATE_REST;
307 break;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800308 case MotionEvent.ACTION_MOVE:
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700309 if (x < TOUCH_AREA_WIDTH) {
310 if (mTouchState == TOUCH_STATE_REST) {
311 mTouchState = TOUCH_STATE_LEFT_PRESS;
312 }
313 if (mTouchState == TOUCH_STATE_LEFT_PRESS) {
314 viewImage.mPrevImageView.setPressed(true);
315 viewImage.mNextImageView.setPressed(false);
316 }
317 mLastXTouchPos = x;
318 mLastYTouchPos = y;
319 } else if (x > viewWidth - TOUCH_AREA_WIDTH) {
320 if (mTouchState == TOUCH_STATE_REST) {
321 mTouchState = TOUCH_STATE_RIGHT_PRESS;
322 }
323 if (mTouchState == TOUCH_STATE_RIGHT_PRESS) {
324 viewImage.mPrevImageView.setPressed(false);
325 viewImage.mNextImageView.setPressed(true);
326 }
327 mLastXTouchPos = x;
328 mLastYTouchPos = y;
329 } else {
330 mTouchState = TOUCH_STATE_PANNING;
331 viewImage.mPrevImageView.setPressed(false);
332 viewImage.mNextImageView.setPressed(false);
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800333
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700334 int deltaX;
335 int deltaY;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800336
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700337 if (mLastXTouchPos == -1) {
338 deltaX = 0;
339 deltaY = 0;
340 } else {
341 deltaX = x - mLastXTouchPos;
342 deltaY = y - mLastYTouchPos;
343 }
344
345 mLastXTouchPos = x;
346 mLastYTouchPos = y;
347
348 if (mBitmapDisplayed == null)
349 return true;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800350
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700351 if (deltaX != 0) {
352 // Second. Pan to whatever degree is possible.
353 if (getScale() > 1F) {
354 postTranslate(deltaX, deltaY, sUseBounce);
355 ImageViewTouch.this.center(true, true, false);
356 }
357 }
358 setImageMatrix(getImageViewMatrix());
359 }
360 break;
361 case MotionEvent.ACTION_UP:
362 int nextImagePos = -1;
363 if (mTouchState == TOUCH_STATE_LEFT_PRESS && x < TOUCH_AREA_WIDTH) {
364 nextImagePos = viewImage.mCurrentPosition - 1;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800365 } else if (mTouchState == TOUCH_STATE_RIGHT_PRESS &&
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700366 x > viewWidth - TOUCH_AREA_WIDTH) {
367 nextImagePos = viewImage.mCurrentPosition + 1;
368 }
369 if (nextImagePos >= 0
370 && nextImagePos < viewImage.mAllImages.getCount()) {
371 synchronized (viewImage) {
372 viewImage.setMode(MODE_NORMAL);
373 viewImage.setImage(nextImagePos);
374 }
375 }
376 viewImage.scheduleDismissOnScreenControls();
377 viewImage.mPrevImageView.setPressed(false);
378 viewImage.mNextImageView.setPressed(false);
379 mTouchState = TOUCH_STATE_REST;
380 break;
381 case MotionEvent.ACTION_CANCEL:
382 viewImage.mPrevImageView.setPressed(false);
383 viewImage.mNextImageView.setPressed(false);
384 mTouchState = TOUCH_STATE_REST;
385 break;
386 }
387 return true;
388 }
389
390 @Override
391 public boolean onKeyDown(int keyCode, KeyEvent event)
392 {
393 int current = mViewImage.mCurrentPosition;
394
395 int nextImagePos = -2; // default no next image
396 try {
397 switch (keyCode) {
398 case KeyEvent.KEYCODE_DPAD_CENTER: {
399 if (mViewImage.isPickIntent()) {
400 ImageManager.IImage img = mViewImage.mAllImages.getImageAt(mViewImage.mCurrentPosition);
401 mViewImage.setResult(RESULT_OK,
402 new Intent().setData(img.fullSizeImageUri()));
403 mViewImage.finish();
404 }
405 break;
406 }
407 case KeyEvent.KEYCODE_DPAD_LEFT: {
408 panBy(sPanRate, 0);
409 int maxOffset = (current == 0) ? 0 : sHysteresis;
410 if (getScale() <= 1F || isShiftedToNextImage(true, maxOffset)) {
411 nextImagePos = current - 1;
412 } else {
413 center(true, false, true);
414 }
415 return true;
416 }
417 case KeyEvent.KEYCODE_DPAD_RIGHT: {
418 panBy(-sPanRate, 0);
419 int maxOffset = (current == mViewImage.mAllImages.getCount()-1) ? 0 : sHysteresis;
420 if (getScale() <= 1F || isShiftedToNextImage(false, maxOffset)) {
421 nextImagePos = current + 1;
422 } else {
423 center(true, false, true);
424 }
425 return true;
426 }
427 case KeyEvent.KEYCODE_DPAD_UP: {
428 panBy(0, sPanRate);
429 center(true, false, false);
430 return true;
431 }
432 case KeyEvent.KEYCODE_DPAD_DOWN: {
433 panBy(0, -sPanRate);
434 center(true, false, false);
435 return true;
436 }
437 case KeyEvent.KEYCODE_DEL:
438 MenuHelper.deletePhoto(mViewImage, mViewImage.mDeletePhotoRunnable);
439 break;
440 }
441 } finally {
442 if (nextImagePos >= 0 && nextImagePos < mViewImage.mAllImages.getCount()) {
443 synchronized (mViewImage) {
444 mViewImage.setMode(MODE_NORMAL);
445 mViewImage.setImage(nextImagePos);
446 }
447 } else if (nextImagePos != -2) {
448 center(true, true, false);
449 }
450 }
451
452 return super.onKeyDown(keyCode, event);
453 }
454
455 protected boolean isShiftedToNextImage(boolean left, int maxOffset) {
456 boolean retval;
457 Bitmap bitmap = mBitmapDisplayed;
458 Matrix m = getImageViewMatrix();
459 if (left) {
460 float [] t1 = new float[] { 0, 0 };
461 m.mapPoints(t1);
462 retval = t1[0] > maxOffset;
463 } else {
464 int width = bitmap != null ? bitmap.getWidth() : getWidth();
465 float [] t1 = new float[] { width, 0 };
466 m.mapPoints(t1);
467 retval = t1[0] + maxOffset < getWidth();
468 }
469 return retval;
470 }
471
472 protected void scrollX(int deltaX) {
473 scrollHandler().scrollBy(deltaX, 0);
474 }
475
476 protected int getScrollOffset() {
477 return scrollHandler().getScrollX();
478 }
479
480 }
481
482 static class ScrollHandler extends LinearLayout {
483 private Runnable mFirstLayoutCompletedCallback = null;
484 private Scroller mScrollerHelper;
485 private int mWidth = -1;
486
487 public ScrollHandler(Context context) {
488 super(context);
489 mScrollerHelper = new Scroller(context);
490 }
491
492 public ScrollHandler(Context context, AttributeSet attrs) {
493 super(context, attrs);
494 mScrollerHelper = new Scroller(context);
495 }
496
497 public void setLayoutCompletedCallback(Runnable r) {
498 mFirstLayoutCompletedCallback = r;
499 }
500
501 public void startScrollTo(int newX, int newY) {
502 int oldX = getScrollX();
503 int oldY = getScrollY();
504
505 int deltaX = newX - oldX;
506 int deltaY = newY - oldY;
507
508 if (mWidth == -1) {
509 mWidth = findViewById(R.id.image2).getWidth();
510 }
511 int viewWidth = mWidth;
512
513 int duration = viewWidth > 0
514 ? sBaseScrollDuration * Math.abs(deltaX) / viewWidth
515 : 0;
516 mScrollerHelper.startScroll(oldX, oldY, deltaX, deltaY, duration);
517 invalidate();
518 }
519
520 @Override
521 public void computeScroll() {
522 if (mScrollerHelper.computeScrollOffset()) {
523 scrollTo(mScrollerHelper.getCurrX(), mScrollerHelper.getCurrY());
524 postInvalidate(); // So we draw again
525 }
526 }
527
528 @Override
529 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
530 int width = right - left;
531 int x = 0;
532 for (View v : new View[] {
533 findViewById(R.id.image1),
534 findViewById(R.id.image2),
535 findViewById(R.id.image3) }) {
536 v.layout(x, 0, x + width, bottom);
537 x += (width + sPadding);
538 }
539
540 findViewById(R.id.padding1).layout(width, 0, width + sPadding, bottom);
541 findViewById(R.id.padding2).layout(width+sPadding+width, 0, width+sPadding+width+sPadding, bottom);
542
543 if (changed) {
544 if (mFirstLayoutCompletedCallback != null) {
545 mFirstLayoutCompletedCallback.run();
546 }
547 }
548 }
549 }
550
551 private void animateScrollTo(int xNew, int yNew) {
552 mScroller.startScrollTo(xNew, yNew);
553 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800554
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700555 @Override
556 public boolean onCreateOptionsMenu(Menu menu)
557 {
558 super.onCreateOptionsMenu(menu);
559
560 if (true) {
561 MenuItem item = menu.add(Menu.CATEGORY_SECONDARY, 203, 0, R.string.slide_show);
562 item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
563 public boolean onMenuItemClick(MenuItem item) {
564 setMode(MODE_SLIDESHOW);
565 mLastSlideShowImage = mCurrentPosition;
566 loadNextImage(mCurrentPosition, 0, true);
567 return true;
568 }
569 });
570 item.setIcon(android.R.drawable.ic_menu_slideshow);
571 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800572
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700573 mFlipItem = MenuHelper.addFlipOrientation(menu, ViewImage.this, mPrefs);
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700574
575 final SelectedImageGetter selectedImageGetter = new SelectedImageGetter() {
576 public ImageManager.IImage getCurrentImage() {
577 return mAllImages.getImageAt(mCurrentPosition);
578 }
579
580 public Uri getCurrentImageUri() {
581 return mAllImages.getImageAt(mCurrentPosition).fullSizeImageUri();
582 }
583 };
584
585 mImageMenuRunnable = MenuHelper.addImageMenuItems(
586 menu,
587 MenuHelper.INCLUDE_ALL,
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800588 true,
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700589 ViewImage.this,
590 mHandler,
591 mDeletePhotoRunnable,
592 new MenuHelper.MenuInvoker() {
593 public void run(MenuHelper.MenuCallback cb) {
594 setMode(MODE_NORMAL);
595 cb.run(selectedImageGetter.getCurrentImageUri(), selectedImageGetter.getCurrentImage());
596 for (ImageViewTouchBase iv: mImageViews) {
597 iv.recycleBitmaps();
598 iv.setImageBitmap(null, true);
599 }
600 setImage(mCurrentPosition);
601 }
602 });
603
604 if (true) {
605 MenuItem item = menu.add(Menu.CATEGORY_SECONDARY, 203, 1000, R.string.camerasettings);
606 item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
607 public boolean onMenuItemClick(MenuItem item) {
608 Intent preferences = new Intent();
609 preferences.setClass(ViewImage.this, GallerySettings.class);
610 startActivity(preferences);
611 return true;
612 }
613 });
614 item.setAlphabeticShortcut('p');
615 item.setIcon(android.R.drawable.ic_menu_preferences);
616 }
617
618 // Hidden menu just so the shortcut will bring up the zoom controls
619 menu.add(Menu.CATEGORY_SECONDARY, 203, 0, R.string.camerasettings) // the string resource is a placeholder
620 .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
621 public boolean onMenuItemClick(MenuItem item) {
622 showOnScreenControls();
623 return true;
624 }
625 })
626 .setAlphabeticShortcut('z')
627 .setVisible(false);
628
629
630 return true;
631 }
632
633 protected Runnable mDeletePhotoRunnable = new Runnable() {
634 public void run() {
635 mAllImages.removeImageAt(mCurrentPosition);
636 if (mAllImages.getCount() == 0) {
637 finish();
638 } else {
639 if (mCurrentPosition == mAllImages.getCount()) {
640 mCurrentPosition -= 1;
641 }
642 }
643 for (ImageViewTouchBase iv: mImageViews) {
644 iv.setImageBitmapResetBase(null, true, true);
645 }
646 setImage(mCurrentPosition);
647 }
648 };
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800649
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700650 @Override
651 public boolean onPrepareOptionsMenu(Menu menu)
652 {
653 super.onPrepareOptionsMenu(menu);
654 setMode(MODE_NORMAL);
655
656 if (mImageMenuRunnable != null) {
657 mImageMenuRunnable.gettingReadyToOpen(menu, mAllImages.getImageAt(mCurrentPosition));
658 }
659
The Android Open Source Projectb97ccf32009-01-09 17:51:25 -0800660 MenuHelper.setFlipOrientationEnabled(this, mFlipItem);
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700661
662 menu.findItem(MenuHelper.MENU_IMAGE_SHARE).setEnabled(isCurrentImageShareable());
663
664 return true;
665 }
666
667 private boolean isCurrentImageShareable() {
668 IImage image = mAllImages.getImageAt(mCurrentPosition);
669 if (image != null){
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800670 Uri uri = image.fullSizeImageUri();
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700671 String fullUri = uri.toString();
672 return fullUri.startsWith(MediaStore.Images.Media.INTERNAL_CONTENT_URI.toString()) ||
673 fullUri.startsWith(MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString());
674 }
675 return true;
676 }
677
678 @Override
679 public void onConfigurationChanged(android.content.res.Configuration newConfig) {
680 super.onConfigurationChanged(newConfig);
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800681 if (newConfig.orientation != getResources().getConfiguration().orientation) {
682 for (ImageViewTouchBase iv: mImageViews) {
683 iv.setImageBitmapResetBase(null, false, true);
684 }
685 MenuHelper.requestOrientation(this, mPrefs);
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700686 }
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700687 }
688
689 @Override
690 public boolean onMenuItemSelected(int featureId, MenuItem item) {
691 boolean b = super.onMenuItemSelected(featureId, item);
692 if (mImageMenuRunnable != null)
693 mImageMenuRunnable.aboutToCall(item, mAllImages.getImageAt(mCurrentPosition));
694 return b;
695 }
696
697 /*
698 * Here's the loading strategy. For any given image, load the thumbnail
699 * into memory and post a callback to display the resulting bitmap.
700 *
701 * Then proceed to load the full image bitmap. Three things can
702 * happen at this point:
703 *
704 * 1. the image fails to load because the UI thread decided
705 * to move on to a different image. This "cancellation" happens
706 * by virtue of the UI thread closing the stream containing the
707 * image being decoded. BitmapFactory.decodeStream returns null
708 * in this case.
709 *
710 * 2. the image loaded successfully. At that point we post
711 * a callback to the UI thread to actually show the bitmap.
712 *
713 * 3. when the post runs it checks to see if the image that was
714 * loaded is still the one we want. The UI may have moved on
715 * to some other image and if so we just drop the newly loaded
716 * bitmap on the floor.
717 */
718
719 interface ImageGetterCallback {
720 public void imageLoaded(int pos, int offset, Bitmap bitmap, boolean isThumb);
721 public boolean wantsThumbnail(int pos, int offset);
722 public boolean wantsFullImage(int pos, int offset);
723 public int fullImageSizeToUse(int pos, int offset);
724 public void completed(boolean wasCanceled);
725 public int [] loadOrder();
726 }
727
728 class ImageGetter {
729 // The thread which does the work.
730 private Thread mGetterThread;
731
732 // The base position that's being retrieved. The actual images retrieved
733 // are this base plus each of the offets.
734 private int mCurrentPosition = -1;
735
736 // The callback to invoke for each image.
737 private ImageGetterCallback mCB;
738
739 // This is the loader cancelable that gets set while we're loading an image.
740 // If we change position we can cancel the current load using this.
741 private ImageManager.IGetBitmap_cancelable mLoad;
742
743 // True if we're canceling the current load.
744 private boolean mCancelCurrent = false;
745
746 // True when the therad should exit.
747 private boolean mDone = false;
748
749 // True when the loader thread is waiting for work.
750 private boolean mReady = false;
751
752 private void cancelCurrent() {
753 synchronized (this) {
754 if (!mReady) {
755 mCancelCurrent = true;
756 ImageManager.IGetBitmap_cancelable load = mLoad;
757 if (load != null) {
758 if (Config.LOGV)
759 Log.v(TAG, "canceling load object");
760 load.cancel();
761 }
762 mCancelCurrent = false;
763 }
764 }
765 }
766
767 public ImageGetter() {
768 mGetterThread = new Thread(new Runnable() {
769
770 private Runnable callback(final int position, final int offset, final boolean isThumb, final Bitmap bitmap) {
771 return new Runnable() {
772 public void run() {
773 // check for inflight callbacks that aren't applicable any longer
774 // before delivering them
775 if (!isCanceled() && position == mCurrentPosition) {
776 mCB.imageLoaded(position, offset, bitmap, isThumb);
777 } else {
778 if (bitmap != null)
779 bitmap.recycle();
780 }
781 }
782 };
783 }
784
785 private Runnable completedCallback(final boolean wasCanceled) {
786 return new Runnable() {
787 public void run() {
788 mCB.completed(wasCanceled);
789 }
790 };
791 }
792
793 public void run() {
794 int lastPosition = -1;
795 while (!mDone) {
796 synchronized (ImageGetter.this) {
797 mReady = true;
798 ImageGetter.this.notify();
799
800 if (mCurrentPosition == -1 || lastPosition == mCurrentPosition) {
801 try {
802 ImageGetter.this.wait();
803 } catch (InterruptedException ex) {
804 continue;
805 }
806 }
807
808 lastPosition = mCurrentPosition;
809 mReady = false;
810 }
811
812 if (lastPosition != -1) {
813 int imageCount = mAllImages.getCount();
814
815 int [] order = mCB.loadOrder();
816 for (int i = 0; i < order.length; i++) {
817 int offset = order[i];
818 int imageNumber = lastPosition + offset;
819 if (imageNumber >= 0 && imageNumber < imageCount) {
820 ImageManager.IImage image = mAllImages.getImageAt(lastPosition + offset);
821 if (image == null || isCanceled()) {
822 break;
823 }
824 if (mCB.wantsThumbnail(lastPosition, offset)) {
825 if (Config.LOGV)
826 Log.v(TAG, "starting THUMBNAIL load at offset " + offset);
827 Bitmap b = image.thumbBitmap();
828 mHandler.post(callback(lastPosition, offset, true, b));
829 }
830 }
831 }
832
833 for (int i = 0; i < order.length; i++) {
834 int offset = order[i];
835 int imageNumber = lastPosition + offset;
836 if (imageNumber >= 0 && imageNumber < imageCount) {
837 ImageManager.IImage image = mAllImages.getImageAt(lastPosition + offset);
838 if (mCB.wantsFullImage(lastPosition, offset)) {
839 if (Config.LOGV)
840 Log.v(TAG, "starting FULL IMAGE load at offset " + offset);
841 int sizeToUse = mCB.fullImageSizeToUse(lastPosition, offset);
842 if (image != null && !isCanceled()) {
843 mLoad = image.fullSizeBitmap_cancelable(sizeToUse);
844 }
845 if (mLoad != null) {
846 long t1;
847 if (Config.LOGV) t1 = System.currentTimeMillis();
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800848
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700849 Bitmap b = null;
850 try {
851 b = mLoad.get();
852 } catch (OutOfMemoryError e) {
853 Log.e(TAG, "couldn't load full size bitmap for " + "");
854 }
855 if (Config.LOGV && b != null) {
856 long t2 = System.currentTimeMillis();
857 Log.v(TAG, "loading full image for " + image.fullSizeImageUri()
858 + " with requested size " + sizeToUse
859 + " took " + (t2-t1)
860 + " and returned a bitmap with size "
861 + b.getWidth() + " / " + b.getHeight());
862 }
863
864 mLoad = null;
865 if (b != null) {
866 if (isCanceled()) {
867 b.recycle();
868 } else {
869 mHandler.post(callback(lastPosition, offset, false, b));
870 }
871 }
872 }
873 }
874 }
875 }
876 mHandler.post(completedCallback(isCanceled()));
877 }
878 }
879 }
880 });
881 mGetterThread.setName("ImageGettter");
882 mGetterThread.start();
883 }
884
885 private boolean isCanceled() {
886 synchronized (this) {
887 return mCancelCurrent;
888 }
889 }
890
891 public void setPosition(int position, ImageGetterCallback cb) {
892 synchronized (this) {
893 if (!mReady) {
894 try {
895 mCancelCurrent = true;
896 ImageManager.IGetBitmap_cancelable load = mLoad;
897 if (load != null) {
898 load.cancel();
899 }
900 // if the thread is waiting before loading the full size
901 // image then this will free it up
902 ImageGetter.this.notify();
903 ImageGetter.this.wait();
904 mCancelCurrent = false;
905 } catch (InterruptedException ex) {
906 // not sure what to do here
907 }
908 }
909 }
910
911 mCurrentPosition = position;
912 mCB = cb;
913
914 synchronized (this) {
915 ImageGetter.this.notify();
916 }
917 }
918
919 public void stop() {
920 synchronized (this) {
921 mDone = true;
922 ImageGetter.this.notify();
923 }
924 try {
925 mGetterThread.join();
926 } catch (InterruptedException ex) {
927
928 }
929 }
930 }
931
932 private void setImage(int pos) {
933 if (!mLayoutComplete) {
934 return;
935 }
936
937 final boolean left = mCurrentPosition > pos;
938
939 mCurrentPosition = pos;
940
941 ImageViewTouchBase current = mImageViews[1];
942 current.mSuppMatrix.reset();
943 current.setImageMatrix(current.getImageViewMatrix());
944
945 if (false) {
946 Log.v(TAG, "before...");
947 for (ImageViewTouchBase ivtb : mImageViews)
948 ivtb.dump();
949 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800950
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700951 if (!mFirst) {
952 if (left) {
953 mImageViews[2].copyFrom(mImageViews[1]);
954 mImageViews[1].copyFrom(mImageViews[0]);
955 } else {
956 mImageViews[0].copyFrom(mImageViews[1]);
957 mImageViews[1].copyFrom(mImageViews[2]);
958 }
959 }
960 if (false) {
961 Log.v(TAG, "after copy...");
962 for (ImageViewTouchBase ivtb : mImageViews)
963 ivtb.dump();
964 }
965
966 for (ImageViewTouchBase ivt: mImageViews) {
967 ivt.mIsZooming = false;
968 }
969 int width = mImageViews[1].getWidth();
970 int from;
971 int to = width + sPadding;
972 if (mFirst) {
973 from = to;
974 mFirst = false;
975 } else {
976 from = left ? (width + sPadding) + mScroller.getScrollX()
977 : mScroller.getScrollX() - (width + sPadding);
978 }
979
980 if (sAnimateTransitions) {
981 mScroller.scrollTo(from, 0);
982 animateScrollTo(to, 0);
983 } else {
984 mScroller.scrollTo(to, 0);
985 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800986
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700987 ImageGetterCallback cb = new ImageGetterCallback() {
988 public void completed(boolean wasCanceled) {
989 mImageViews[1].setFocusableInTouchMode(true);
990 mImageViews[1].requestFocus();
991 }
992
993 public boolean wantsThumbnail(int pos, int offset) {
994 ImageViewTouchBase ivt = mImageViews[1 + offset];
995 return ivt.mThumbBitmap == null;
996 }
997
998 public boolean wantsFullImage(int pos, int offset) {
999 ImageViewTouchBase ivt = mImageViews[1 + offset];
1000 if (ivt.mBitmapDisplayed != null && !ivt.mBitmapIsThumbnail) {
1001 return false;
1002 }
1003 if (offset != 0) {
1004 return false;
1005 }
1006 return true;
1007 }
1008
1009 public int fullImageSizeToUse(int pos, int offset) {
1010 // TODO
1011 // this number should be bigger so that we can zoom. we may need to
1012 // get fancier and read in the fuller size image as the user starts
1013 // to zoom. use -1 to get the full full size image.
1014 // for now use 480 so we don't run out of memory
1015 final int imageViewSize = 480;
1016 return imageViewSize;
1017 }
1018
1019 public int [] loadOrder() {
1020 return sOrder_adjacents;
1021 }
1022
1023 public void imageLoaded(int pos, int offset, Bitmap bitmap, boolean isThumb) {
1024 ImageViewTouchBase ivt = mImageViews[1 + offset];
1025 ivt.setImageBitmapResetBase(bitmap, isThumb, isThumb);
1026 }
1027 };
1028
1029 // Could be null if we're stopping a slide show in the course of pausing
1030 if (mGetter != null) {
1031 mGetter.setPosition(pos, cb);
1032 }
1033 showOnScreenControls();
1034 }
1035
1036 @Override
1037 public void onCreate(Bundle instanceState)
1038 {
1039 super.onCreate(instanceState);
1040 mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
1041
1042 setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT);
1043 requestWindowFeature(Window.FEATURE_NO_TITLE);
1044 setContentView(R.layout.viewimage);
1045
1046 mImageViews[0] = (ImageViewTouch) findViewById(R.id.image1);
1047 mImageViews[1] = (ImageViewTouch) findViewById(R.id.image2);
1048 mImageViews[2] = (ImageViewTouch) findViewById(R.id.image3);
1049
1050 mScroller = (ScrollHandler)findViewById(R.id.scroller);
1051 makeGetter();
1052
1053 mAnimationIndex = -1;
1054
1055 mSlideShowInAnimation = new Animation[] {
1056 makeInAnimation(R.anim.transition_in),
1057 makeInAnimation(R.anim.slide_in),
1058 makeInAnimation(R.anim.slide_in_vertical),
1059 };
1060
1061 mSlideShowOutAnimation = new Animation[] {
1062 makeOutAnimation(R.anim.transition_out),
1063 makeOutAnimation(R.anim.slide_out),
1064 makeOutAnimation(R.anim.slide_out_vertical),
1065 };
1066
1067 mSlideShowImageViews[0] = (ImageViewTouch) findViewById(R.id.image1_slideShow);
1068 mSlideShowImageViews[1] = (ImageViewTouch) findViewById(R.id.image2_slideShow);
1069 for (int i = 0; i < mSlideShowImageViews.length; i++) {
1070 mSlideShowImageViews[i].setImageBitmapResetBase(null, true, true);
1071 mSlideShowImageViews[i].setVisibility(View.INVISIBLE);
1072 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001073
The Android Open Source Projectb97ccf32009-01-09 17:51:25 -08001074 mActionIconPanel = findViewById(R.id.action_icon_panel);
1075 int[] ids = {R.id.capture, R.id.gallery, R.id.discard, R.id.share, R.id.setas};
1076 for(int id : ids) {
1077 findViewById(id).setOnClickListener(this);
1078 }
1079
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001080 Uri uri = getIntent().getData();
1081
1082 if (Config.LOGV)
1083 Log.v(TAG, "uri is " + uri);
1084 if (instanceState != null) {
1085 if (instanceState.containsKey("uri")) {
1086 uri = Uri.parse(instanceState.getString("uri"));
1087 }
1088 }
1089 if (uri == null) {
1090 finish();
1091 return;
1092 }
1093 init(uri);
The Android Open Source Projectb97ccf32009-01-09 17:51:25 -08001094 mFullScreenInNormalMode = getIntent().getBooleanExtra(
1095 MediaStore.EXTRA_SHOW_ACTION_ICONS, false);
1096 mShowActionIcons = getIntent().getBooleanExtra(
1097 MediaStore.EXTRA_SHOW_ACTION_ICONS, false);
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001098
1099 Bundle b = getIntent().getExtras();
The Android Open Source Projectb97ccf32009-01-09 17:51:25 -08001100
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001101 boolean slideShow = b != null ? b.getBoolean("slideshow", false) : false;
1102 if (slideShow) {
1103 setMode(MODE_SLIDESHOW);
1104 loadNextImage(mCurrentPosition, 0, true);
The Android Open Source Projectb97ccf32009-01-09 17:51:25 -08001105 } else {
1106 if (mFullScreenInNormalMode) {
1107 getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
1108 }
1109 if (mShowActionIcons) {
1110 mActionIconPanel.setVisibility(View.VISIBLE);
1111 }
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001112 }
1113
1114 // Get the zoom controls and add them to the bottom of the map
1115 View zoomControls = getZoomControls();
1116 RelativeLayout root = (RelativeLayout) findViewById(R.id.rootLayout);
1117 RelativeLayout.LayoutParams p = new RelativeLayout.LayoutParams(
1118 LayoutParams.WRAP_CONTENT,
1119 LayoutParams.WRAP_CONTENT);
1120 p.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
1121 p.addRule(RelativeLayout.CENTER_HORIZONTAL);
1122 root.addView(zoomControls, p);
1123
1124 mNextImageView = findViewById(R.id.next_image);
1125 mPrevImageView = findViewById(R.id.prev_image);
1126
The Android Open Source Projectb97ccf32009-01-09 17:51:25 -08001127 setOrientation();
1128 }
1129
1130 private void setOrientation() {
1131 Intent intent = getIntent();
1132 if (intent.hasExtra(MediaStore.EXTRA_SCREEN_ORIENTATION)) {
1133 int orientation = intent.getIntExtra(MediaStore.EXTRA_SCREEN_ORIENTATION,
1134 ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
1135 if (orientation != getRequestedOrientation()) {
1136 setRequestedOrientation(orientation);
1137 }
1138 } else {
1139 MenuHelper.requestOrientation(this, mPrefs);
1140 }
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001141 }
1142
1143 private Animation makeInAnimation(int id) {
1144 Animation inAnimation = AnimationUtils.loadAnimation(this, id);
1145 return inAnimation;
1146 }
1147
1148 private Animation makeOutAnimation(int id) {
1149 Animation outAnimation = AnimationUtils.loadAnimation(this, id);
1150 return outAnimation;
1151 }
1152
1153 private void setMode(int mode) {
1154 if (mMode == mode) {
1155 return;
1156 }
1157
1158 findViewById(R.id.slideShowContainer).setVisibility(mode == MODE_SLIDESHOW ? View.VISIBLE : View.GONE);
1159 findViewById(R.id.abs) .setVisibility(mode == MODE_NORMAL ? View.VISIBLE : View.GONE);
1160
1161 Window win = getWindow();
1162 mMode = mode;
1163 if (mode == MODE_SLIDESHOW) {
1164 win.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN
1165 | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1166 if (sSlideShowHidesStatusBar) {
1167 win.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
1168 }
1169 for (ImageViewTouchBase ivt: mImageViews) {
1170 ivt.clear();
1171 }
The Android Open Source Projectb97ccf32009-01-09 17:51:25 -08001172 mActionIconPanel.setVisibility(View.GONE);
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001173
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001174 if (false) {
1175 Log.v(TAG, "current is " + this.mSlideShowImageCurrent);
1176 this.mSlideShowImageViews[0].dump();
1177 this.mSlideShowImageViews[0].dump();
1178 }
1179
1180 findViewById(R.id.slideShowContainer).getRootView().requestLayout();
1181 mUseShuffleOrder = mPrefs.getBoolean("pref_gallery_slideshow_shuffle_key", false);
1182 mSlideShowLoop = mPrefs.getBoolean("pref_gallery_slideshow_repeat_key", false);
1183 try {
1184 mAnimationIndex = Integer.parseInt(mPrefs.getString("pref_gallery_slideshow_transition_key", "0"));
1185 } catch (Exception ex) {
1186 Log.e(TAG, "couldn't parse preference: " + ex.toString());
1187 mAnimationIndex = 0;
1188 }
1189 try {
1190 mSlideShowInterval = Integer.parseInt(mPrefs.getString("pref_gallery_slideshow_interval_key", "3")) * 1000;
1191 } catch (Exception ex) {
1192 Log.e(TAG, "couldn't parse preference: " + ex.toString());
1193 mSlideShowInterval = 3000;
1194 }
1195
1196 if (Config.LOGV) {
1197 Log.v(TAG, "read prefs... shuffle: " + mUseShuffleOrder);
1198 Log.v(TAG, "read prefs... loop: " + mSlideShowLoop);
1199 Log.v(TAG, "read prefs... animidx: " + mAnimationIndex);
1200 Log.v(TAG, "read prefs... interval: " + mSlideShowInterval);
1201 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001202
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001203 if (mUseShuffleOrder) {
1204 generateShuffleOrder();
1205 }
1206 } else {
1207 if (Config.LOGV)
1208 Log.v(TAG, "slide show mode off, mCurrentPosition == " + mCurrentPosition);
The Android Open Source Projectb97ccf32009-01-09 17:51:25 -08001209 win.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1210 if (mFullScreenInNormalMode) {
1211 win.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
1212 } else {
1213 win.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
1214 }
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001215
1216 if (mGetter != null)
1217 mGetter.cancelCurrent();
1218
1219 if (sSlideShowHidesStatusBar) {
1220 win.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
1221 }
The Android Open Source Projectb97ccf32009-01-09 17:51:25 -08001222 if (mShowActionIcons) {
1223 mActionIconPanel.setVisibility(View.VISIBLE);
1224 }
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001225
1226 ImageViewTouchBase dst = mImageViews[1];
1227 dst.mLastXTouchPos = -1;
1228 dst.mLastYTouchPos = -1;
1229
1230 for (ImageViewTouchBase ivt: mSlideShowImageViews) {
1231 ivt.clear();
1232 }
1233
1234 mShuffleOrder = null;
1235
1236 // mGetter null is a proxy for being paused
1237 if (mGetter != null) {
1238 mFirst = true; // don't animate
1239 setImage(mCurrentPosition);
1240 }
1241 }
1242
1243 // this line shouldn't be necessary but the view hierarchy doesn't
1244 // seem to realize that the window layout changed
1245 mScroller.requestLayout();
1246 }
1247
1248 private void generateShuffleOrder() {
1249 if (mShuffleOrder == null || mShuffleOrder.length != mAllImages.getCount()) {
1250 mShuffleOrder = new int[mAllImages.getCount()];
1251 }
1252
1253 for (int i = 0; i < mShuffleOrder.length; i++) {
1254 mShuffleOrder[i] = i;
1255 }
1256
1257 for (int i = mShuffleOrder.length - 1; i > 0; i--) {
1258 int r = mRandom.nextInt(i);
1259 int tmp = mShuffleOrder[r];
1260 mShuffleOrder[r] = mShuffleOrder[i];
1261 mShuffleOrder[i] = tmp;
1262 }
1263 }
1264
1265 private void loadNextImage(final int requestedPos, final long delay, final boolean firstCall) {
1266 if (firstCall && mUseShuffleOrder) {
1267 generateShuffleOrder();
1268 }
1269
1270 final long targetDisplayTime = System.currentTimeMillis() + delay;
1271
1272 ImageGetterCallback cb = new ImageGetterCallback() {
1273 public void completed(boolean wasCanceled) {
1274 }
1275
1276 public boolean wantsThumbnail(int pos, int offset) {
1277 return true;
1278 }
1279
1280 public boolean wantsFullImage(int pos, int offset) {
1281 return false;
1282 }
1283
1284 public int [] loadOrder() {
1285 return sOrder_slideshow;
1286 }
1287
1288 public int fullImageSizeToUse(int pos, int offset) {
1289 return 480; // TODO compute this
1290 }
1291
1292 public void imageLoaded(final int pos, final int offset, final Bitmap bitmap, final boolean isThumb) {
1293 long timeRemaining = Math.max(0, targetDisplayTime - System.currentTimeMillis());
1294 mHandler.postDelayed(new Runnable() {
1295 public void run() {
1296 if (mMode == MODE_NORMAL) {
1297 return;
1298 }
1299
1300 ImageViewTouchBase oldView = mSlideShowImageViews[mSlideShowImageCurrent];
1301
1302 if (++mSlideShowImageCurrent == mSlideShowImageViews.length) {
1303 mSlideShowImageCurrent = 0;
1304 }
1305
1306 ImageViewTouchBase newView = mSlideShowImageViews[mSlideShowImageCurrent];
1307 newView.setVisibility(View.VISIBLE);
1308 newView.setImageBitmapResetBase(bitmap, isThumb, isThumb);
1309 newView.bringToFront();
1310
1311 int animation = 0;
1312
1313 if (mAnimationIndex == -1) {
1314 int n = mRandom.nextInt(mSlideShowInAnimation.length);
1315 animation = n;
1316 } else {
1317 animation = mAnimationIndex;
1318 }
1319
1320 Animation aIn = mSlideShowInAnimation[animation];
1321 newView.setAnimation(aIn);
1322 newView.setVisibility(View.VISIBLE);
1323 aIn.startNow();
1324
1325 Animation aOut = mSlideShowOutAnimation[animation];
1326 oldView.setVisibility(View.INVISIBLE);
1327 oldView.setAnimation(aOut);
1328 aOut.startNow();
1329
1330 mCurrentPosition = requestedPos;
1331
1332 mHandler.post(new Runnable() {
1333 public void run() {
1334 if (mCurrentPosition == mLastSlideShowImage && !firstCall) {
1335 if (mSlideShowLoop) {
1336 if (mUseShuffleOrder) {
1337 generateShuffleOrder();
1338 }
1339 } else {
1340 setMode(MODE_NORMAL);
1341 return;
1342 }
1343 }
1344
1345 if (Config.LOGV)
1346 Log.v(TAG, "mCurrentPosition is now " + mCurrentPosition);
1347 loadNextImage((mCurrentPosition + 1) % mAllImages.getCount(), mSlideShowInterval, false);
1348 }
1349 });
1350 }
1351 }, timeRemaining);
1352 }
1353 };
1354 // Could be null if we're stopping a slide show in the course of pausing
1355 if (mGetter != null) {
1356 int pos = requestedPos;
1357 if (mShuffleOrder != null) {
1358 pos = mShuffleOrder[pos];
1359 }
1360 mGetter.setPosition(pos, cb);
1361 }
1362 }
1363
1364 private void makeGetter() {
1365 mGetter = new ImageGetter();
1366 }
1367
1368 private void init(Uri uri) {
1369 String sortOrder = mPrefs.getString("pref_gallery_sort_key", null);
1370 mSortAscending = false;
1371 if (sortOrder != null) {
1372 mSortAscending = sortOrder.equals("ascending");
1373 }
1374 int sort = mSortAscending ? ImageManager.SORT_ASCENDING : ImageManager.SORT_DESCENDING;
1375 mAllImages = ImageManager.makeImageList(uri, this, sort);
1376
1377 uri = uri.buildUpon().query(null).build();
1378 // TODO smarter/faster here please
1379 for (int i = 0; i < mAllImages.getCount(); i++) {
1380 ImageManager.IImage image = mAllImages.getImageAt(i);
1381 if (image.fullSizeImageUri().equals(uri)) {
1382 mCurrentPosition = i;
1383 mLastSlideShowImage = mCurrentPosition;
1384 break;
1385 }
1386 }
1387 }
1388
1389 @Override
1390 public void onSaveInstanceState(Bundle b) {
1391 super.onSaveInstanceState(b);
1392 ImageManager.IImage image = mAllImages.getImageAt(mCurrentPosition);
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001393
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001394 if (image != null){
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001395 Uri uri = image.fullSizeImageUri();
1396 String bucket = null;
1397 if(getIntent()!= null && getIntent().getData()!=null)
1398 bucket = getIntent().getData().getQueryParameter("bucketId");
1399
1400 if(bucket!=null)
1401 uri = uri.buildUpon().appendQueryParameter("bucketId", bucket).build();
1402
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001403 b.putString("uri", uri.toString());
1404 }
1405 if (mMode == MODE_SLIDESHOW)
1406 b.putBoolean("slideshow", true);
1407 }
1408
1409 @Override
1410 public void onResume()
1411 {
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001412 super.onResume();
1413
1414 // normally this will never be zero but if one "backs" into this
1415 // activity after removing the sdcard it could be zero. in that
1416 // case just "finish" since there's nothing useful that can happen.
1417 if (mAllImages.getCount() == 0) {
1418 finish();
1419 }
1420
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001421 ImageManager.IImage image = mAllImages.getImageAt(mCurrentPosition);
1422
1423 String sortOrder = mPrefs.getString("pref_gallery_sort_key", null);
1424 boolean sortAscending = false;
1425 if (sortOrder != null) {
1426 sortAscending = sortOrder.equals("ascending");
1427 }
1428 if (sortAscending != mSortAscending) {
1429 init(image.fullSizeImageUri());
1430 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001431
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001432 if (mGetter == null) {
1433 makeGetter();
1434 }
1435
1436 for (ImageViewTouchBase iv: mImageViews) {
1437 iv.setImageBitmap(null, true);
1438 }
1439
1440 mFirst = true;
1441 mScroller.setLayoutCompletedCallback(new Runnable() {
1442 public void run() {
1443 mLayoutComplete = true;
1444 setImage(mCurrentPosition);
1445 }
1446 });
1447 setImage(mCurrentPosition);
1448
The Android Open Source Projectb97ccf32009-01-09 17:51:25 -08001449 setOrientation();
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001450 }
1451
1452 @Override
1453 public void onPause()
1454 {
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001455 super.onPause();
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001456
1457 mGetter.cancelCurrent();
1458 mGetter.stop();
1459 mGetter = null;
1460 setMode(MODE_NORMAL);
1461
1462 mAllImages.deactivate();
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001463
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001464 for (ImageViewTouchBase iv: mImageViews) {
1465 iv.recycleBitmaps();
1466 iv.setImageBitmap(null, true);
1467 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001468
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001469 for (ImageViewTouchBase iv: mSlideShowImageViews) {
1470 iv.recycleBitmaps();
1471 iv.setImageBitmap(null, true);
1472 }
1473 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001474
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001475 @Override
1476 public void onStop() {
1477 super.onStop();
1478 }
The Android Open Source Projectb97ccf32009-01-09 17:51:25 -08001479
1480 public void onClick(View v) {
1481 switch (v.getId()) {
1482
1483 case R.id.capture: {
1484 MenuHelper.gotoStillImageCapture(this);
1485 }
1486 break;
1487
1488 case R.id.gallery: {
1489 MenuHelper.gotoCameraImageGallery(this);
1490 }
1491 break;
1492
1493 case R.id.discard: {
1494 MenuHelper.displayDeleteDialog(this, mDeletePhotoRunnable, true);
1495 }
1496 break;
1497
1498 case R.id.share: {
1499 Uri u = mAllImages.getImageAt(mCurrentPosition).fullSizeImageUri();
1500 Intent intent = new Intent();
1501 intent.setAction(Intent.ACTION_SEND);
1502 intent.setType("image/jpeg");
1503 intent.putExtra(Intent.EXTRA_STREAM, u);
1504 try {
1505 startActivity(Intent.createChooser(intent, getText(R.string.sendImage)));
1506 } catch (android.content.ActivityNotFoundException ex) {
1507 Toast.makeText(this, R.string.no_way_to_share_image, Toast.LENGTH_SHORT).show();
1508 }
1509 }
1510 break;
1511
1512 case R.id.setas: {
1513 Uri u = mAllImages.getImageAt(mCurrentPosition).fullSizeImageUri();
1514 Intent intent = new Intent(Intent.ACTION_ATTACH_DATA, u);
1515 try {
1516 startActivity(Intent.createChooser(intent, getText(R.string.setImage)));
1517 } catch (android.content.ActivityNotFoundException ex) {
1518 Toast.makeText(this, R.string.no_way_to_share_video, Toast.LENGTH_SHORT).show();
1519 }
1520 }
1521 break;
1522 }
1523 }
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001524}