summaryrefslogtreecommitdiffstats
path: root/src/com/android/camera/ViewImage.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/camera/ViewImage.java')
-rw-r--r--src/com/android/camera/ViewImage.java1677
1 files changed, 1677 insertions, 0 deletions
diff --git a/src/com/android/camera/ViewImage.java b/src/com/android/camera/ViewImage.java
new file mode 100644
index 0000000..07d396f
--- /dev/null
+++ b/src/com/android/camera/ViewImage.java
@@ -0,0 +1,1677 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.camera;
+
+import java.util.Random;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.ActivityInfo;
+import android.graphics.Bitmap;
+import android.graphics.Matrix;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.preference.PreferenceManager;
+import android.provider.MediaStore;
+import android.util.AttributeSet;
+import android.util.Config;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.LinearLayout;
+import android.widget.Scroller;
+import android.widget.Toast;
+import android.widget.ZoomButtonsController;
+import android.widget.ZoomRingController;
+
+import com.android.camera.ImageManager.IImage;
+
+public class ViewImage extends Activity implements View.OnClickListener
+{
+ static final String TAG = "ViewImage";
+ private ImageGetter mGetter;
+
+ static final boolean sSlideShowHidesStatusBar = true;
+
+ // Choices for what adjacents to load.
+ static private final int[] sOrder_adjacents = new int[] { 0, 1, -1 };
+ static private final int[] sOrder_slideshow = new int[] { 0 };
+
+ Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ }
+ };
+
+ private Random mRandom = new Random(System.currentTimeMillis());
+ private int [] mShuffleOrder;
+ private boolean mUseShuffleOrder = false;
+ private boolean mSlideShowLoop = false;
+
+ private static final int MODE_NORMAL = 1;
+ private static final int MODE_SLIDESHOW = 2;
+ private int mMode = MODE_NORMAL;
+ private boolean mFullScreenInNormalMode;
+ private boolean mShowActionIcons;
+ private View mActionIconPanel;
+ private View mShutterButton;
+
+ private boolean mSortAscending = false;
+ private int mSlideShowInterval;
+ private int mLastSlideShowImage;
+ private boolean mFirst = true;
+ private int mCurrentPosition = 0;
+ private boolean mLayoutComplete = false;
+
+ // represents which style animation to use
+ private int mAnimationIndex;
+ private Animation [] mSlideShowInAnimation;
+ private Animation [] mSlideShowOutAnimation;
+
+ private SharedPreferences mPrefs;
+
+ private View mNextImageView, mPrevImageView;
+ private Animation mHideNextImageViewAnimation = new AlphaAnimation(1F, 0F);
+ private Animation mHidePrevImageViewAnimation = new AlphaAnimation(1F, 0F);
+ private Animation mShowNextImageViewAnimation = new AlphaAnimation(0F, 1F);
+ private Animation mShowPrevImageViewAnimation = new AlphaAnimation(0F, 1F);
+
+
+ static final int sPadding = 20;
+ static final int sHysteresis = sPadding * 2;
+ static final int sBaseScrollDuration = 1000; // ms
+
+ private ImageManager.IImageList mAllImages;
+
+ private int mSlideShowImageCurrent = 0;
+ private ImageViewTouch [] mSlideShowImageViews = new ImageViewTouch[2];
+
+
+ // Array of image views. The center view is the one the user is focused
+ // on. The one at the zeroth position and the second position reflect
+ // the images to the left/right of the center image.
+ private ImageViewTouch[] mImageViews = new ImageViewTouch[3];
+
+ // Container for the three image views. This guy can be "scrolled"
+ // to reveal the image prior to and after the center image.
+ private ScrollHandler mScroller;
+
+ private MenuHelper.MenuItemsResult mImageMenuRunnable;
+
+ private Runnable mDismissOnScreenControlsRunnable;
+ private boolean mCameraReviewMode;
+
+ private int mCurrentOrientation;
+
+
+ public ViewImage() {
+ }
+
+ private void updateNextPrevControls() {
+ boolean showPrev = mCurrentPosition > 0;
+ boolean showNext = mCurrentPosition < mAllImages.getCount() - 1;
+
+ boolean prevIsVisible = mPrevImageView.getVisibility() == View.VISIBLE;
+ boolean nextIsVisible = mNextImageView.getVisibility() == View.VISIBLE;
+
+ if (showPrev && !prevIsVisible) {
+ Animation a = mShowPrevImageViewAnimation;
+ a.setDuration(500);
+ a.startNow();
+ mPrevImageView.setAnimation(a);
+ mPrevImageView.setVisibility(View.VISIBLE);
+ } else if (!showPrev && prevIsVisible) {
+ Animation a = mHidePrevImageViewAnimation;
+ a.setDuration(500);
+ a.startNow();
+ mPrevImageView.setAnimation(a);
+ mPrevImageView.setVisibility(View.GONE);
+ }
+
+ if (showNext && !nextIsVisible) {
+ Animation a = mShowNextImageViewAnimation;
+ a.setDuration(500);
+ a.startNow();
+ mNextImageView.setAnimation(a);
+ mNextImageView.setVisibility(View.VISIBLE);
+ } else if (!showNext && nextIsVisible) {
+ Animation a = mHideNextImageViewAnimation;
+ a.setDuration(500);
+ a.startNow();
+ mNextImageView.setAnimation(a);
+ mNextImageView.setVisibility(View.GONE);
+ }
+ }
+
+ private void showOnScreenControls() {
+ updateNextPrevControls();
+ scheduleDismissOnScreenControls();
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent m) {
+ boolean sup = super.dispatchTouchEvent(m);
+ if (sup == false) {
+ if (mMode == MODE_SLIDESHOW) {
+ mSlideShowImageViews[mSlideShowImageCurrent].handleTouchEvent(m);
+ } else if (mMode == MODE_NORMAL){
+ mImageViews[1].handleTouchEvent(m);
+ }
+ return true;
+ }
+ return true;
+ }
+
+ private void scheduleDismissOnScreenControls() {
+ mHandler.removeCallbacks(mDismissOnScreenControlsRunnable);
+ mHandler.postDelayed(mDismissOnScreenControlsRunnable, 1500);
+ }
+
+ public void setupDismissOnScreenControlRunnable() {
+ mDismissOnScreenControlsRunnable = new Runnable() {
+ public void run() {
+ if (!mShowActionIcons) {
+ if (mNextImageView.getVisibility() == View.VISIBLE) {
+ Animation a = mHideNextImageViewAnimation;
+ a.setDuration(500);
+ a.startNow();
+ mNextImageView.setAnimation(a);
+ mNextImageView.setVisibility(View.INVISIBLE);
+ }
+
+ if (mPrevImageView.getVisibility() == View.VISIBLE) {
+ Animation a = mHidePrevImageViewAnimation;
+ a.setDuration(500);
+ a.startNow();
+ mPrevImageView.setAnimation(a);
+ mPrevImageView.setVisibility(View.INVISIBLE);
+ }
+ }
+ }
+ };
+ }
+
+ private boolean isPickIntent() {
+ String action = getIntent().getAction();
+ return (Intent.ACTION_PICK.equals(action) || Intent.ACTION_GET_CONTENT.equals(action));
+ }
+
+ private static final boolean sUseBounce = false;
+ private static final boolean sAnimateTransitions = false;
+
+ static public class ImageViewTouch extends ImageViewTouchBase {
+ private ViewImage mViewImage;
+ private boolean mEnableTrackballScroll;
+ private GestureDetector mGestureDetector;
+
+ private static int TOUCH_AREA_WIDTH = 60;
+
+ // The zoom ring setup:
+ // We limit the thumb on the zoom ring in the range 0 to 5/3*PI. The
+ // last PI/3 of the ring is left as a region that the thumb can't go in.
+ // The 5/3*PI range is divided into 60 steps. Each step scales the image
+ // by mScaleRate. We make mScaleRate^60 = maxZoom().
+
+ // This is the max step value we can have for the zoom ring.
+ private static int MAX_STEP = 60;
+ // This is the angle we used to separate each step.
+ private static float STEP_ANGLE = (5 * (float) Math.PI / 3) / MAX_STEP;
+ // The scale rate for each step.
+ private float mScaleRate;
+
+ // Returns current scale step (numbered from 0 to MAX_STEP).
+ private int getCurrentStep() {
+ float s = getScale();
+ float b = mScaleRate;
+ int step = (int)Math.round(Math.log(s) / Math.log(b));
+ return Math.max(0, Math.min(MAX_STEP, step));
+ }
+
+ // Limit the thumb on the zoom ring in the range 0 to 5/3*PI. (clockwise
+ // angle is negative, and we need to mod 2*PI for the API to work.)
+ private void setZoomRingBounds() {
+ mScaleRate = (float) Math.pow(maxZoom(), 1.0 / MAX_STEP);
+ float limit = (2 - 5 / 3F) * (float) Math.PI;
+ mZoomRingController.setThumbClockwiseBound(limit);
+ mZoomRingController.setThumbCounterclockwiseBound(0);
+ }
+
+ private ZoomButtonsController mZoomButtonsController;
+
+ // The zoom ring is set to visible by a double tap.
+ private ZoomRingController mZoomRingController;
+ private ZoomRingController.OnZoomListener mZoomListener =
+ new ZoomRingController.OnZoomListener() {
+ public void onCenter(int x, int y) {
+ }
+
+ public void onBeginPan() {
+ }
+
+ public boolean onPan(int deltaX, int deltaY) {
+ postTranslate(-deltaX, -deltaY, sUseBounce);
+ ImageViewTouch.this.center(true, true, false);
+ return true;
+ }
+
+ public void onEndPan() {
+ }
+
+ // The clockwise angle is negative, so we need to mod 2*PI
+ private float stepToAngle(int step) {
+ float angle = step * STEP_ANGLE;
+ angle = (float) Math.PI * 2 - angle;
+ return angle;
+ }
+
+ private int angleToStep(double angle) {
+ angle = Math.PI * 2 - angle;
+ int step = (int)Math.round(angle / STEP_ANGLE);
+ return step;
+ }
+
+ public void onVisibilityChanged(boolean visible) {
+ if (visible) {
+ int step = getCurrentStep();
+ float angle = stepToAngle(step);
+ mZoomRingController.setThumbAngle(angle);
+ }
+ }
+
+ public void onBeginDrag() {
+ setZoomRingBounds();
+ }
+
+ public void onEndDrag() {
+ }
+
+ public boolean onDragZoom(int deltaZoomLevel, int centerX,
+ int centerY, float startAngle, float curAngle) {
+ setZoomRingBounds();
+ int deltaStep = angleToStep(curAngle) - getCurrentStep();
+ if ((deltaZoomLevel > 0) && (deltaStep < 0)) return false;
+ if ((deltaZoomLevel < 0) && (deltaStep > 0)) return false;
+ if ((deltaZoomLevel == 0) || (deltaStep == 0)) return false;
+
+ float oldScale = getScale();
+
+ // First move centerX/centerY to the center of the view.
+ int deltaX = getWidth() / 2 - centerX;
+ int deltaY = getHeight() / 2 - centerY;
+ panBy(deltaX, deltaY);
+
+ // Do zoom in/out.
+ if (deltaStep > 0) {
+ zoomIn((float) Math.pow(mScaleRate, deltaStep));
+ } else if (deltaStep < 0) {
+ zoomOut((float) Math.pow(mScaleRate, -deltaStep));
+ }
+
+ // Reverse the first centering.
+ panBy(-deltaX, -deltaY);
+
+ // Return true if the zoom succeeds.
+ return (oldScale != getScale());
+ }
+
+ public void onSimpleZoom(boolean zoomIn) {
+ if (zoomIn) zoomIn();
+ else zoomOut();
+ }
+ };
+
+ public ImageViewTouch(Context context) {
+ super(context);
+ setup(context);
+ }
+
+ public ImageViewTouch(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setup(context);
+ }
+
+ private void setup(Context context) {
+ mViewImage = (ViewImage) context;
+ mZoomRingController = new ZoomRingController(context, this);
+ mZoomRingController.setVibration(false);
+ mZoomRingController.setZoomCallbackThreshold(STEP_ANGLE);
+ mZoomRingController.setResetThumbAutomatically(false);
+ mZoomRingController.setCallback(mZoomListener);
+ mGestureDetector = new GestureDetector(getContext(), new MyGestureListener());
+ mGestureDetector.setOnDoubleTapListener(new MyDoubleTapListener());
+ mZoomButtonsController = new ZoomButtonsController(context, this);
+ mZoomButtonsController.setOverviewVisible(false);
+ mZoomButtonsController.setCallback(new ZoomButtonsController.OnZoomListener() {
+
+ public void onCenter(int x, int y) {
+ mZoomListener.onCenter(x, y);
+ }
+
+ public void onOverview() {
+ }
+
+ public void onVisibilityChanged(boolean visible) {
+ mZoomListener.onVisibilityChanged(visible);
+ }
+
+ public void onZoom(boolean zoomIn) {
+ mZoomListener.onSimpleZoom(zoomIn);
+ }
+ });
+ }
+
+ public void setEnableTrackballScroll(boolean enable) {
+ mEnableTrackballScroll = enable;
+ }
+
+ protected void postTranslate(float dx, float dy, boolean bounceOK) {
+ super.postTranslate(dx, dy);
+ if (dx != 0F || dy != 0F)
+ mViewImage.showOnScreenControls();
+
+ if (!sUseBounce) {
+ center(true, false, false);
+ }
+ }
+
+ protected ScrollHandler scrollHandler() {
+ return mViewImage.mScroller;
+ }
+
+ public boolean handleTouchEvent(MotionEvent m) {
+ return mGestureDetector.onTouchEvent(m);
+ }
+
+ private class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
+ public boolean onScroll(MotionEvent e1, MotionEvent e2,
+ float distanceX, float distanceY) {
+ if (getScale() > 1F) {
+ postTranslate(-distanceX, -distanceY, sUseBounce);
+ ImageViewTouch.this.center(true, true, false);
+ }
+ return true;
+ }
+ }
+
+ private class MyDoubleTapListener implements GestureDetector.OnDoubleTapListener {
+ // On single tap, we show the arrows. We also change to the
+ // prev/next image if the user taps on the left/right region.
+ public boolean onSingleTapConfirmed(MotionEvent e) {
+ ViewImage viewImage = mViewImage;
+
+ int viewWidth = getWidth();
+ int x = (int) e.getX();
+ int y = (int) e.getY();
+ if (x < TOUCH_AREA_WIDTH) {
+ viewImage.moveNextOrPrevious(-1);
+ } else if (x > viewWidth - TOUCH_AREA_WIDTH) {
+ viewImage.moveNextOrPrevious(1);
+ }
+
+ viewImage.setMode(MODE_NORMAL);
+ viewImage.showOnScreenControls();
+
+ return true;
+ }
+
+ // On double tap, we show the zoom ring control.
+ public boolean onDoubleTapEvent(MotionEvent e) {
+ mViewImage.setMode(MODE_NORMAL);
+ mZoomRingController.handleDoubleTapEvent(e);
+ mZoomButtonsController.handleDoubleTapEvent(e);
+ return true;
+ }
+
+ public boolean onDoubleTap(MotionEvent e) {
+ return false;
+ }
+
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event)
+ {
+ // Don't respond to arrow keys if trackball scrolling is not enabled
+ if (!mEnableTrackballScroll) {
+ if ((keyCode >= KeyEvent.KEYCODE_DPAD_UP)
+ && (keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT)) {
+ return super.onKeyDown(keyCode, event);
+ }
+ }
+
+ int current = mViewImage.mCurrentPosition;
+
+ int nextImagePos = -2; // default no next image
+ try {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_CENTER: {
+ if (mViewImage.isPickIntent()) {
+ ImageManager.IImage img = mViewImage.mAllImages.getImageAt(mViewImage.mCurrentPosition);
+ mViewImage.setResult(RESULT_OK,
+ new Intent().setData(img.fullSizeImageUri()));
+ mViewImage.finish();
+ }
+ break;
+ }
+ case KeyEvent.KEYCODE_DPAD_LEFT: {
+ panBy(sPanRate, 0);
+ int maxOffset = (current == 0) ? 0 : sHysteresis;
+ if (getScale() <= 1F || isShiftedToNextImage(true, maxOffset)) {
+ nextImagePos = current - 1;
+ } else {
+ center(true, false, true);
+ }
+ return true;
+ }
+ case KeyEvent.KEYCODE_DPAD_RIGHT: {
+ panBy(-sPanRate, 0);
+ int maxOffset = (current == mViewImage.mAllImages.getCount()-1) ? 0 : sHysteresis;
+ if (getScale() <= 1F || isShiftedToNextImage(false, maxOffset)) {
+ nextImagePos = current + 1;
+ } else {
+ center(true, false, true);
+ }
+ return true;
+ }
+ case KeyEvent.KEYCODE_DPAD_UP: {
+ panBy(0, sPanRate);
+ center(true, false, false);
+ return true;
+ }
+ case KeyEvent.KEYCODE_DPAD_DOWN: {
+ panBy(0, -sPanRate);
+ center(true, false, false);
+ return true;
+ }
+ case KeyEvent.KEYCODE_DEL:
+ MenuHelper.deletePhoto(mViewImage, mViewImage.mDeletePhotoRunnable);
+ break;
+ }
+ } finally {
+ if (nextImagePos >= 0 && nextImagePos < mViewImage.mAllImages.getCount()) {
+ synchronized (mViewImage) {
+ mViewImage.setMode(MODE_NORMAL);
+ mViewImage.setImage(nextImagePos);
+ }
+ } else if (nextImagePos != -2) {
+ center(true, true, false);
+ }
+ }
+
+ return super.onKeyDown(keyCode, event);
+ }
+
+ protected boolean isShiftedToNextImage(boolean left, int maxOffset) {
+ boolean retval;
+ Bitmap bitmap = mBitmapDisplayed;
+ Matrix m = getImageViewMatrix();
+ if (left) {
+ float [] t1 = new float[] { 0, 0 };
+ m.mapPoints(t1);
+ retval = t1[0] > maxOffset;
+ } else {
+ int width = bitmap != null ? bitmap.getWidth() : getWidth();
+ float [] t1 = new float[] { width, 0 };
+ m.mapPoints(t1);
+ retval = t1[0] + maxOffset < getWidth();
+ }
+ return retval;
+ }
+
+ protected void scrollX(int deltaX) {
+ scrollHandler().scrollBy(deltaX, 0);
+ }
+
+ protected int getScrollOffset() {
+ return scrollHandler().getScrollX();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ mZoomRingController.setVisible(false);
+ mZoomButtonsController.setVisible(false);
+ }
+
+ }
+
+ static class ScrollHandler extends LinearLayout {
+ private Runnable mFirstLayoutCompletedCallback = null;
+ private Scroller mScrollerHelper;
+ private int mWidth = -1;
+
+ public ScrollHandler(Context context) {
+ super(context);
+ mScrollerHelper = new Scroller(context);
+ }
+
+ public ScrollHandler(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mScrollerHelper = new Scroller(context);
+ }
+
+ public void setLayoutCompletedCallback(Runnable r) {
+ mFirstLayoutCompletedCallback = r;
+ }
+
+ public void startScrollTo(int newX, int newY) {
+ int oldX = getScrollX();
+ int oldY = getScrollY();
+
+ int deltaX = newX - oldX;
+ int deltaY = newY - oldY;
+
+ if (mWidth == -1) {
+ mWidth = findViewById(R.id.image2).getWidth();
+ }
+ int viewWidth = mWidth;
+
+ int duration = viewWidth > 0
+ ? sBaseScrollDuration * Math.abs(deltaX) / viewWidth
+ : 0;
+ mScrollerHelper.startScroll(oldX, oldY, deltaX, deltaY, duration);
+ invalidate();
+ }
+
+ @Override
+ public void computeScroll() {
+ if (mScrollerHelper.computeScrollOffset()) {
+ scrollTo(mScrollerHelper.getCurrX(), mScrollerHelper.getCurrY());
+ postInvalidate(); // So we draw again
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ int width = right - left;
+ int x = 0;
+ for (View v : new View[] {
+ findViewById(R.id.image1),
+ findViewById(R.id.image2),
+ findViewById(R.id.image3) }) {
+ v.layout(x, 0, x + width, bottom);
+ x += (width + sPadding);
+ }
+
+ findViewById(R.id.padding1).layout(width, 0, width + sPadding, bottom);
+ findViewById(R.id.padding2).layout(width+sPadding+width, 0, width+sPadding+width+sPadding, bottom);
+
+ if (changed) {
+ if (mFirstLayoutCompletedCallback != null) {
+ mFirstLayoutCompletedCallback.run();
+ }
+ }
+ }
+ }
+
+ private void animateScrollTo(int xNew, int yNew) {
+ mScroller.startScrollTo(xNew, yNew);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu)
+ {
+ super.onCreateOptionsMenu(menu);
+
+ if (! mCameraReviewMode) {
+ MenuItem item = menu.add(Menu.CATEGORY_SECONDARY, 203, 0, R.string.slide_show);
+ item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ setMode(MODE_SLIDESHOW);
+ mLastSlideShowImage = mCurrentPosition;
+ loadNextImage(mCurrentPosition, 0, true);
+ return true;
+ }
+ });
+ item.setIcon(android.R.drawable.ic_menu_slideshow);
+ }
+
+ final SelectedImageGetter selectedImageGetter = new SelectedImageGetter() {
+ public ImageManager.IImage getCurrentImage() {
+ return mAllImages.getImageAt(mCurrentPosition);
+ }
+
+ public Uri getCurrentImageUri() {
+ return mAllImages.getImageAt(mCurrentPosition).fullSizeImageUri();
+ }
+ };
+
+ mImageMenuRunnable = MenuHelper.addImageMenuItems(
+ menu,
+ MenuHelper.INCLUDE_ALL,
+ true,
+ ViewImage.this,
+ mHandler,
+ mDeletePhotoRunnable,
+ new MenuHelper.MenuInvoker() {
+ public void run(MenuHelper.MenuCallback cb) {
+ setMode(MODE_NORMAL);
+ cb.run(selectedImageGetter.getCurrentImageUri(), selectedImageGetter.getCurrentImage());
+ for (ImageViewTouchBase iv: mImageViews) {
+ iv.recycleBitmaps();
+ iv.setImageBitmap(null, true);
+ }
+ setImage(mCurrentPosition);
+ }
+ });
+
+ if (true) {
+ MenuItem item = menu.add(Menu.CATEGORY_SECONDARY, 203, 1000, R.string.camerasettings);
+ item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ Intent preferences = new Intent();
+ preferences.setClass(ViewImage.this, GallerySettings.class);
+ startActivity(preferences);
+ return true;
+ }
+ });
+ item.setAlphabeticShortcut('p');
+ item.setIcon(android.R.drawable.ic_menu_preferences);
+ }
+
+ // Hidden menu just so the shortcut will bring up the zoom controls
+ menu.add(Menu.CATEGORY_SECONDARY, 203, 0, R.string.camerasettings) // the string resource is a placeholder
+ .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ showOnScreenControls();
+ return true;
+ }
+ })
+ .setAlphabeticShortcut('z')
+ .setVisible(false);
+
+
+ return true;
+ }
+
+ protected Runnable mDeletePhotoRunnable = new Runnable() {
+ public void run() {
+ mAllImages.removeImageAt(mCurrentPosition);
+ if (mAllImages.getCount() == 0) {
+ finish();
+ } else {
+ if (mCurrentPosition == mAllImages.getCount()) {
+ mCurrentPosition -= 1;
+ }
+ }
+ for (ImageViewTouchBase iv: mImageViews) {
+ iv.setImageBitmapResetBase(null, true, true);
+ }
+ setImage(mCurrentPosition);
+ }
+ };
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu)
+ {
+ super.onPrepareOptionsMenu(menu);
+ setMode(MODE_NORMAL);
+
+ if (mImageMenuRunnable != null) {
+ mImageMenuRunnable.gettingReadyToOpen(menu, mAllImages.getImageAt(mCurrentPosition));
+ }
+
+ return true;
+ }
+
+ @Override
+ public void onConfigurationChanged(android.content.res.Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ boolean changed = mCurrentOrientation != newConfig.orientation;
+ mCurrentOrientation = newConfig.orientation;
+ if (changed) {
+ if (mGetter != null) {
+ // kill off any background image fetching
+ mGetter.cancelCurrent();
+ mGetter.stop();
+ }
+ makeGetter();
+ mFirst = true;
+
+ // clear off the current set of images since we need to reload
+ // them at the right size
+ for (ImageViewTouchBase iv: mImageViews) {
+ iv.clear();
+ }
+ MenuHelper.requestOrientation(this, mPrefs);
+ }
+ }
+
+ @Override
+ public boolean onMenuItemSelected(int featureId, MenuItem item) {
+ boolean b = super.onMenuItemSelected(featureId, item);
+ if (mImageMenuRunnable != null)
+ mImageMenuRunnable.aboutToCall(item, mAllImages.getImageAt(mCurrentPosition));
+ return b;
+ }
+
+ /*
+ * Here's the loading strategy. For any given image, load the thumbnail
+ * into memory and post a callback to display the resulting bitmap.
+ *
+ * Then proceed to load the full image bitmap. Three things can
+ * happen at this point:
+ *
+ * 1. the image fails to load because the UI thread decided
+ * to move on to a different image. This "cancellation" happens
+ * by virtue of the UI thread closing the stream containing the
+ * image being decoded. BitmapFactory.decodeStream returns null
+ * in this case.
+ *
+ * 2. the image loaded successfully. At that point we post
+ * a callback to the UI thread to actually show the bitmap.
+ *
+ * 3. when the post runs it checks to see if the image that was
+ * loaded is still the one we want. The UI may have moved on
+ * to some other image and if so we just drop the newly loaded
+ * bitmap on the floor.
+ */
+
+ interface ImageGetterCallback {
+ public void imageLoaded(int pos, int offset, Bitmap bitmap, boolean isThumb);
+ public boolean wantsThumbnail(int pos, int offset);
+ public boolean wantsFullImage(int pos, int offset);
+ public int fullImageSizeToUse(int pos, int offset);
+ public void completed(boolean wasCanceled);
+ public int [] loadOrder();
+ }
+
+ class ImageGetter {
+ // The thread which does the work.
+ private Thread mGetterThread;
+
+ // The base position that's being retrieved. The actual images retrieved
+ // are this base plus each of the offets.
+ private int mCurrentPosition = -1;
+
+ // The callback to invoke for each image.
+ private ImageGetterCallback mCB;
+
+ // This is the loader cancelable that gets set while we're loading an image.
+ // If we change position we can cancel the current load using this.
+ private ImageManager.IGetBitmap_cancelable mLoad;
+
+ // True if we're canceling the current load.
+ private boolean mCancelCurrent = false;
+
+ // True when the therad should exit.
+ private boolean mDone = false;
+
+ // True when the loader thread is waiting for work.
+ private boolean mReady = false;
+
+ private void cancelCurrent() {
+ synchronized (this) {
+ if (!mReady) {
+ mCancelCurrent = true;
+ ImageManager.IGetBitmap_cancelable load = mLoad;
+ if (load != null) {
+ if (Config.LOGV)
+ Log.v(TAG, "canceling load object");
+ load.cancel();
+ }
+ mCancelCurrent = false;
+ }
+ }
+ }
+
+ public ImageGetter() {
+ mGetterThread = new Thread(new Runnable() {
+
+ private Runnable callback(final int position, final int offset, final boolean isThumb, final Bitmap bitmap) {
+ return new Runnable() {
+ public void run() {
+ // check for inflight callbacks that aren't applicable any longer
+ // before delivering them
+ if (!isCanceled() && position == mCurrentPosition) {
+ mCB.imageLoaded(position, offset, bitmap, isThumb);
+ } else {
+ if (bitmap != null)
+ bitmap.recycle();
+ }
+ }
+ };
+ }
+
+ private Runnable completedCallback(final boolean wasCanceled) {
+ return new Runnable() {
+ public void run() {
+ mCB.completed(wasCanceled);
+ }
+ };
+ }
+
+ public void run() {
+ int lastPosition = -1;
+ while (!mDone) {
+ synchronized (ImageGetter.this) {
+ mReady = true;
+ ImageGetter.this.notify();
+
+ if (mCurrentPosition == -1 || lastPosition == mCurrentPosition) {
+ try {
+ ImageGetter.this.wait();
+ } catch (InterruptedException ex) {
+ continue;
+ }
+ }
+
+ lastPosition = mCurrentPosition;
+ mReady = false;
+ }
+
+ if (lastPosition != -1) {
+ int imageCount = mAllImages.getCount();
+
+ int [] order = mCB.loadOrder();
+ for (int i = 0; i < order.length; i++) {
+ int offset = order[i];
+ int imageNumber = lastPosition + offset;
+ if (imageNumber >= 0 && imageNumber < imageCount) {
+ ImageManager.IImage image = mAllImages.getImageAt(lastPosition + offset);
+ if (image == null || isCanceled()) {
+ break;
+ }
+ if (mCB.wantsThumbnail(lastPosition, offset)) {
+ if (Config.LOGV)
+ Log.v(TAG, "starting THUMBNAIL load at offset " + offset);
+ Bitmap b = image.thumbBitmap();
+ mHandler.post(callback(lastPosition, offset, true, b));
+ }
+ }
+ }
+
+ for (int i = 0; i < order.length; i++) {
+ int offset = order[i];
+ int imageNumber = lastPosition + offset;
+ if (imageNumber >= 0 && imageNumber < imageCount) {
+ ImageManager.IImage image = mAllImages.getImageAt(lastPosition + offset);
+ if (mCB.wantsFullImage(lastPosition, offset)) {
+ if (Config.LOGV)
+ Log.v(TAG, "starting FULL IMAGE load at offset " + offset);
+ int sizeToUse = mCB.fullImageSizeToUse(lastPosition, offset);
+ if (image != null && !isCanceled()) {
+ mLoad = image.fullSizeBitmap_cancelable(sizeToUse);
+ }
+ if (mLoad != null) {
+ long t1;
+ if (Config.LOGV) t1 = System.currentTimeMillis();
+
+ Bitmap b = null;
+ try {
+ b = mLoad.get();
+ } catch (OutOfMemoryError e) {
+ Log.e(TAG, "couldn't load full size bitmap for " + "");
+ }
+ if (Config.LOGV && b != null) {
+ long t2 = System.currentTimeMillis();
+ Log.v(TAG, "loading full image for " + image.fullSizeImageUri()
+ + " with requested size " + sizeToUse
+ + " took " + (t2-t1)
+ + " and returned a bitmap with size "
+ + b.getWidth() + " / " + b.getHeight());
+ }
+
+ mLoad = null;
+ if (b != null) {
+ if (isCanceled()) {
+ b.recycle();
+ } else {
+ mHandler.post(callback(lastPosition, offset, false, b));
+ }
+ }
+ }
+ }
+ }
+ }
+ mHandler.post(completedCallback(isCanceled()));
+ }
+ }
+ }
+ });
+ mGetterThread.setName("ImageGettter");
+ mGetterThread.start();
+ }
+
+ private boolean isCanceled() {
+ synchronized (this) {
+ return mCancelCurrent;
+ }
+ }
+
+ public void setPosition(int position, ImageGetterCallback cb) {
+ synchronized (this) {
+ if (!mReady) {
+ try {
+ mCancelCurrent = true;
+ ImageManager.IGetBitmap_cancelable load = mLoad;
+ if (load != null) {
+ load.cancel();
+ }
+ // if the thread is waiting before loading the full size
+ // image then this will free it up
+ ImageGetter.this.notify();
+ ImageGetter.this.wait();
+ mCancelCurrent = false;
+ } catch (InterruptedException ex) {
+ // not sure what to do here
+ }
+ }
+ }
+
+ mCurrentPosition = position;
+ mCB = cb;
+
+ synchronized (this) {
+ ImageGetter.this.notify();
+ }
+ }
+
+ public void stop() {
+ synchronized (this) {
+ mDone = true;
+ ImageGetter.this.notify();
+ }
+ try {
+ mGetterThread.join();
+ } catch (InterruptedException ex) {
+
+ }
+ }
+ }
+
+ private void setImage(int pos) {
+ if (!mLayoutComplete) {
+ return;
+ }
+
+ final boolean left = mCurrentPosition > pos;
+
+ mCurrentPosition = pos;
+
+ ImageViewTouchBase current = mImageViews[1];
+ current.mSuppMatrix.reset();
+ current.setImageMatrix(current.getImageViewMatrix());
+
+ if (false) {
+ Log.v(TAG, "before...");
+ for (ImageViewTouchBase ivtb : mImageViews)
+ ivtb.dump();
+ }
+
+ if (!mFirst) {
+ if (left) {
+ mImageViews[2].copyFrom(mImageViews[1]);
+ mImageViews[1].copyFrom(mImageViews[0]);
+ } else {
+ mImageViews[0].copyFrom(mImageViews[1]);
+ mImageViews[1].copyFrom(mImageViews[2]);
+ }
+ }
+ if (false) {
+ Log.v(TAG, "after copy...");
+ for (ImageViewTouchBase ivtb : mImageViews)
+ ivtb.dump();
+ }
+
+ for (ImageViewTouchBase ivt: mImageViews) {
+ ivt.mIsZooming = false;
+ }
+ int width = mImageViews[1].getWidth();
+ int from;
+ int to = width + sPadding;
+ if (mFirst) {
+ from = to;
+ mFirst = false;
+ } else {
+ from = left ? (width + sPadding) + mScroller.getScrollX()
+ : mScroller.getScrollX() - (width + sPadding);
+ }
+
+ if (sAnimateTransitions) {
+ mScroller.scrollTo(from, 0);
+ animateScrollTo(to, 0);
+ } else {
+ mScroller.scrollTo(to, 0);
+ }
+
+ ImageGetterCallback cb = new ImageGetterCallback() {
+ public void completed(boolean wasCanceled) {
+ if (!mShowActionIcons) {
+ mImageViews[1].setFocusableInTouchMode(true);
+ mImageViews[1].requestFocus();
+ }
+ }
+
+ public boolean wantsThumbnail(int pos, int offset) {
+ ImageViewTouchBase ivt = mImageViews[1 + offset];
+ return ivt.mThumbBitmap == null;
+ }
+
+ public boolean wantsFullImage(int pos, int offset) {
+ ImageViewTouchBase ivt = mImageViews[1 + offset];
+ if (ivt.mBitmapDisplayed != null && !ivt.mBitmapIsThumbnail) {
+ return false;
+ }
+ if (offset != 0) {
+ return false;
+ }
+ return true;
+ }
+
+ public int fullImageSizeToUse(int pos, int offset) {
+ // TODO
+ // this number should be bigger so that we can zoom. we may need to
+ // get fancier and read in the fuller size image as the user starts
+ // to zoom. use -1 to get the full full size image.
+ // for now use 480 so we don't run out of memory
+ final int imageViewSize = 480;
+ return imageViewSize;
+ }
+
+ public int [] loadOrder() {
+ return sOrder_adjacents;
+ }
+
+ public void imageLoaded(int pos, int offset, Bitmap bitmap, boolean isThumb) {
+ ImageViewTouchBase ivt = mImageViews[1 + offset];
+ ivt.setImageBitmapResetBase(bitmap, isThumb, isThumb);
+ }
+ };
+
+ // Could be null if we're stopping a slide show in the course of pausing
+ if (mGetter != null) {
+ mGetter.setPosition(pos, cb);
+ }
+ showOnScreenControls();
+ }
+
+ @Override
+ public void onCreate(Bundle instanceState)
+ {
+ super.onCreate(instanceState);
+ Intent intent = getIntent();
+ mCameraReviewMode = intent.getBooleanExtra("com.android.camera.ReviewMode", false);
+ mFullScreenInNormalMode = intent.getBooleanExtra(MediaStore.EXTRA_FULL_SCREEN, true);
+ mShowActionIcons = intent.getBooleanExtra(MediaStore.EXTRA_SHOW_ACTION_ICONS, false);
+
+ mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
+ mCurrentOrientation = getResources().getConfiguration().orientation;
+
+ setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT);
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ setContentView(R.layout.viewimage);
+
+ mImageViews[0] = (ImageViewTouch) findViewById(R.id.image1);
+ mImageViews[1] = (ImageViewTouch) findViewById(R.id.image2);
+ mImageViews[2] = (ImageViewTouch) findViewById(R.id.image3);
+
+ for(ImageViewTouch v : mImageViews) {
+ v.setEnableTrackballScroll(!mShowActionIcons);
+ }
+
+ mScroller = (ScrollHandler)findViewById(R.id.scroller);
+ makeGetter();
+
+ mAnimationIndex = -1;
+
+ mSlideShowInAnimation = new Animation[] {
+ makeInAnimation(R.anim.transition_in),
+ makeInAnimation(R.anim.slide_in),
+ makeInAnimation(R.anim.slide_in_vertical),
+ };
+
+ mSlideShowOutAnimation = new Animation[] {
+ makeOutAnimation(R.anim.transition_out),
+ makeOutAnimation(R.anim.slide_out),
+ makeOutAnimation(R.anim.slide_out_vertical),
+ };
+
+ mSlideShowImageViews[0] = (ImageViewTouch) findViewById(R.id.image1_slideShow);
+ mSlideShowImageViews[1] = (ImageViewTouch) findViewById(R.id.image2_slideShow);
+ for (ImageViewTouch v : mSlideShowImageViews) {
+ v.setImageBitmapResetBase(null, true, true);
+ v.setVisibility(View.INVISIBLE);
+ v.setEnableTrackballScroll(!mShowActionIcons);
+ }
+
+ mActionIconPanel = findViewById(R.id.action_icon_panel);
+ {
+ int[] pickIds = {R.id.attach, R.id.cancel};
+ int[] normalIds = {R.id.gallery, R.id.setas, R.id.share, R.id.discard};
+ int[] hideIds = pickIds;
+ int[] connectIds = normalIds;
+ if (isPickIntent()) {
+ hideIds = normalIds;
+ connectIds = pickIds;
+ }
+ for(int id : hideIds) {
+ mActionIconPanel.findViewById(id).setVisibility(View.GONE);
+ }
+ for(int id : connectIds) {
+ View view = mActionIconPanel.findViewById(id);
+ view.setOnClickListener(this);
+ Animation animation = new AlphaAnimation(0F, 1F);
+ animation.setDuration(500);
+ view.setAnimation(animation);
+ }
+ }
+ mShutterButton = findViewById(R.id.shutter_button);
+ mShutterButton.setOnClickListener(this);
+
+ Uri uri = getIntent().getData();
+
+ if (Config.LOGV)
+ Log.v(TAG, "uri is " + uri);
+ if (instanceState != null) {
+ if (instanceState.containsKey("uri")) {
+ uri = Uri.parse(instanceState.getString("uri"));
+ }
+ }
+ if (uri == null) {
+ finish();
+ return;
+ }
+ init(uri);
+
+ Bundle b = getIntent().getExtras();
+
+ boolean slideShow = b != null ? b.getBoolean("slideshow", false) : false;
+ if (slideShow) {
+ setMode(MODE_SLIDESHOW);
+ loadNextImage(mCurrentPosition, 0, true);
+ } else {
+ if (mFullScreenInNormalMode) {
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ }
+ if (mShowActionIcons) {
+ mActionIconPanel.setVisibility(View.VISIBLE);
+ mShutterButton.setVisibility(View.VISIBLE);
+ }
+ }
+
+ setupDismissOnScreenControlRunnable();
+
+ mNextImageView = findViewById(R.id.next_image);
+ mPrevImageView = findViewById(R.id.prev_image);
+ mNextImageView.setOnClickListener(this);
+ mPrevImageView.setOnClickListener(this);
+
+ if (mShowActionIcons) {
+ mNextImageView.setFocusable(true);
+ mPrevImageView.setFocusable(true);
+ }
+
+ setOrientation();
+ }
+
+ private void setOrientation() {
+ Intent intent = getIntent();
+ if (intent.hasExtra(MediaStore.EXTRA_SCREEN_ORIENTATION)) {
+ int orientation = intent.getIntExtra(MediaStore.EXTRA_SCREEN_ORIENTATION,
+ ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
+ if (orientation != getRequestedOrientation()) {
+ setRequestedOrientation(orientation);
+ }
+ } else {
+ MenuHelper.requestOrientation(this, mPrefs);
+ }
+ }
+
+ private Animation makeInAnimation(int id) {
+ Animation inAnimation = AnimationUtils.loadAnimation(this, id);
+ return inAnimation;
+ }
+
+ private Animation makeOutAnimation(int id) {
+ Animation outAnimation = AnimationUtils.loadAnimation(this, id);
+ return outAnimation;
+ }
+
+ private void setMode(int mode) {
+ if (mMode == mode) {
+ return;
+ }
+
+ findViewById(R.id.slideShowContainer).setVisibility(mode == MODE_SLIDESHOW ? View.VISIBLE : View.GONE);
+ findViewById(R.id.abs) .setVisibility(mode == MODE_NORMAL ? View.VISIBLE : View.GONE);
+
+ Window win = getWindow();
+ mMode = mode;
+ if (mode == MODE_SLIDESHOW) {
+ win.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN
+ | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ if (sSlideShowHidesStatusBar) {
+ win.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ }
+ for (ImageViewTouchBase ivt: mImageViews) {
+ ivt.clear();
+ }
+ mActionIconPanel.setVisibility(View.GONE);
+ mShutterButton.setVisibility(View.GONE);
+
+ if (false) {
+ Log.v(TAG, "current is " + this.mSlideShowImageCurrent);
+ this.mSlideShowImageViews[0].dump();
+ this.mSlideShowImageViews[0].dump();
+ }
+
+ findViewById(R.id.slideShowContainer).getRootView().requestLayout();
+ mUseShuffleOrder = mPrefs.getBoolean("pref_gallery_slideshow_shuffle_key", false);
+ mSlideShowLoop = mPrefs.getBoolean("pref_gallery_slideshow_repeat_key", false);
+ try {
+ mAnimationIndex = Integer.parseInt(mPrefs.getString("pref_gallery_slideshow_transition_key", "0"));
+ } catch (Exception ex) {
+ Log.e(TAG, "couldn't parse preference: " + ex.toString());
+ mAnimationIndex = 0;
+ }
+ try {
+ mSlideShowInterval = Integer.parseInt(mPrefs.getString("pref_gallery_slideshow_interval_key", "3")) * 1000;
+ } catch (Exception ex) {
+ Log.e(TAG, "couldn't parse preference: " + ex.toString());
+ mSlideShowInterval = 3000;
+ }
+
+ if (Config.LOGV) {
+ Log.v(TAG, "read prefs... shuffle: " + mUseShuffleOrder);
+ Log.v(TAG, "read prefs... loop: " + mSlideShowLoop);
+ Log.v(TAG, "read prefs... animidx: " + mAnimationIndex);
+ Log.v(TAG, "read prefs... interval: " + mSlideShowInterval);
+ }
+
+ if (mUseShuffleOrder) {
+ generateShuffleOrder();
+ }
+ } else {
+ if (Config.LOGV)
+ Log.v(TAG, "slide show mode off, mCurrentPosition == " + mCurrentPosition);
+ win.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ if (mFullScreenInNormalMode) {
+ win.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ } else {
+ win.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ }
+
+ if (mGetter != null)
+ mGetter.cancelCurrent();
+
+ if (sSlideShowHidesStatusBar) {
+ win.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ }
+ if (mShowActionIcons) {
+ mActionIconPanel.setVisibility(View.VISIBLE);
+ mShutterButton.setVisibility(View.VISIBLE);
+ }
+
+ ImageViewTouchBase dst = mImageViews[1];
+ dst.mLastXTouchPos = -1;
+ dst.mLastYTouchPos = -1;
+
+ for (ImageViewTouchBase ivt: mSlideShowImageViews) {
+ ivt.clear();
+ }
+
+ mShuffleOrder = null;
+
+ // mGetter null is a proxy for being paused
+ if (mGetter != null) {
+ mFirst = true; // don't animate
+ setImage(mCurrentPosition);
+ }
+ }
+
+ // this line shouldn't be necessary but the view hierarchy doesn't
+ // seem to realize that the window layout changed
+ mScroller.requestLayout();
+ }
+
+ private void generateShuffleOrder() {
+ if (mShuffleOrder == null || mShuffleOrder.length != mAllImages.getCount()) {
+ mShuffleOrder = new int[mAllImages.getCount()];
+ }
+
+ for (int i = 0; i < mShuffleOrder.length; i++) {
+ mShuffleOrder[i] = i;
+ }
+
+ for (int i = mShuffleOrder.length - 1; i > 0; i--) {
+ int r = mRandom.nextInt(i);
+ int tmp = mShuffleOrder[r];
+ mShuffleOrder[r] = mShuffleOrder[i];
+ mShuffleOrder[i] = tmp;
+ }
+ }
+
+ private void loadNextImage(final int requestedPos, final long delay, final boolean firstCall) {
+ if (firstCall && mUseShuffleOrder) {
+ generateShuffleOrder();
+ }
+
+ final long targetDisplayTime = System.currentTimeMillis() + delay;
+
+ ImageGetterCallback cb = new ImageGetterCallback() {
+ public void completed(boolean wasCanceled) {
+ }
+
+ public boolean wantsThumbnail(int pos, int offset) {
+ return true;
+ }
+
+ public boolean wantsFullImage(int pos, int offset) {
+ return false;
+ }
+
+ public int [] loadOrder() {
+ return sOrder_slideshow;
+ }
+
+ public int fullImageSizeToUse(int pos, int offset) {
+ return 480; // TODO compute this
+ }
+
+ public void imageLoaded(final int pos, final int offset, final Bitmap bitmap, final boolean isThumb) {
+ long timeRemaining = Math.max(0, targetDisplayTime - System.currentTimeMillis());
+ mHandler.postDelayed(new Runnable() {
+ public void run() {
+ if (mMode == MODE_NORMAL) {
+ return;
+ }
+
+ ImageViewTouchBase oldView = mSlideShowImageViews[mSlideShowImageCurrent];
+
+ if (++mSlideShowImageCurrent == mSlideShowImageViews.length) {
+ mSlideShowImageCurrent = 0;
+ }
+
+ ImageViewTouchBase newView = mSlideShowImageViews[mSlideShowImageCurrent];
+ newView.setVisibility(View.VISIBLE);
+ newView.setImageBitmapResetBase(bitmap, isThumb, isThumb);
+ newView.bringToFront();
+
+ int animation = 0;
+
+ if (mAnimationIndex == -1) {
+ int n = mRandom.nextInt(mSlideShowInAnimation.length);
+ animation = n;
+ } else {
+ animation = mAnimationIndex;
+ }
+
+ Animation aIn = mSlideShowInAnimation[animation];
+ newView.setAnimation(aIn);
+ newView.setVisibility(View.VISIBLE);
+ aIn.startNow();
+
+ Animation aOut = mSlideShowOutAnimation[animation];
+ oldView.setVisibility(View.INVISIBLE);
+ oldView.setAnimation(aOut);
+ aOut.startNow();
+
+ mCurrentPosition = requestedPos;
+
+ mHandler.post(new Runnable() {
+ public void run() {
+ if (mCurrentPosition == mLastSlideShowImage && !firstCall) {
+ if (mSlideShowLoop) {
+ if (mUseShuffleOrder) {
+ generateShuffleOrder();
+ }
+ } else {
+ setMode(MODE_NORMAL);
+ return;
+ }
+ }
+
+ if (Config.LOGV)
+ Log.v(TAG, "mCurrentPosition is now " + mCurrentPosition);
+ loadNextImage((mCurrentPosition + 1) % mAllImages.getCount(), mSlideShowInterval, false);
+ }
+ });
+ }
+ }, timeRemaining);
+ }
+ };
+ // Could be null if we're stopping a slide show in the course of pausing
+ if (mGetter != null) {
+ int pos = requestedPos;
+ if (mShuffleOrder != null) {
+ pos = mShuffleOrder[pos];
+ }
+ mGetter.setPosition(pos, cb);
+ }
+ }
+
+ private void makeGetter() {
+ mGetter = new ImageGetter();
+ }
+
+ private boolean desiredSortOrder() {
+ String sortOrder = mPrefs.getString("pref_gallery_sort_key", null);
+ boolean sortAscending = false;
+ if (sortOrder != null) {
+ sortAscending = sortOrder.equals("ascending");
+ }
+ if (mCameraReviewMode) {
+ // Force left-arrow older pictures, right-arrow newer pictures.
+ sortAscending = true;
+ }
+ return sortAscending;
+ }
+
+ private void init(Uri uri) {
+ mSortAscending = desiredSortOrder();
+ int sort = mSortAscending ? ImageManager.SORT_ASCENDING : ImageManager.SORT_DESCENDING;
+ mAllImages = ImageManager.makeImageList(uri, this, sort);
+
+ uri = uri.buildUpon().query(null).build();
+ // TODO smarter/faster here please
+ for (int i = 0; i < mAllImages.getCount(); i++) {
+ ImageManager.IImage image = mAllImages.getImageAt(i);
+ if (image.fullSizeImageUri().equals(uri)) {
+ mCurrentPosition = i;
+ mLastSlideShowImage = mCurrentPosition;
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle b) {
+ super.onSaveInstanceState(b);
+ ImageManager.IImage image = mAllImages.getImageAt(mCurrentPosition);
+
+ if (image != null){
+ Uri uri = image.fullSizeImageUri();
+ String bucket = null;
+ if(getIntent()!= null && getIntent().getData()!=null)
+ bucket = getIntent().getData().getQueryParameter("bucketId");
+
+ if(bucket!=null)
+ uri = uri.buildUpon().appendQueryParameter("bucketId", bucket).build();
+
+ b.putString("uri", uri.toString());
+ }
+ if (mMode == MODE_SLIDESHOW)
+ b.putBoolean("slideshow", true);
+ }
+
+ @Override
+ public void onResume()
+ {
+ super.onResume();
+
+ // normally this will never be zero but if one "backs" into this
+ // activity after removing the sdcard it could be zero. in that
+ // case just "finish" since there's nothing useful that can happen.
+ if (mAllImages.getCount() == 0) {
+ finish();
+ }
+
+ ImageManager.IImage image = mAllImages.getImageAt(mCurrentPosition);
+
+ if (desiredSortOrder() != mSortAscending) {
+ init(image.fullSizeImageUri());
+ }
+
+ if (mGetter == null) {
+ makeGetter();
+ }
+
+ for (ImageViewTouchBase iv: mImageViews) {
+ iv.setImageBitmap(null, true);
+ }
+
+ mFirst = true;
+ mScroller.setLayoutCompletedCallback(new Runnable() {
+ public void run() {
+ mLayoutComplete = true;
+ setImage(mCurrentPosition);
+ }
+ });
+ setImage(mCurrentPosition);
+
+ setOrientation();
+
+ // Show a tutorial for the new zoom interaction (the method ensure we only show it once)
+ ZoomRingController.showZoomTutorialOnce(this);
+ }
+
+ @Override
+ public void onPause()
+ {
+ super.onPause();
+
+ mGetter.cancelCurrent();
+ mGetter.stop();
+ mGetter = null;
+ setMode(MODE_NORMAL);
+
+ mAllImages.deactivate();
+
+ for (ImageViewTouch iv: mImageViews) {
+ iv.recycleBitmaps();
+ iv.setImageBitmap(null, true);
+ }
+
+ for (ImageViewTouch iv: mSlideShowImageViews) {
+ iv.recycleBitmaps();
+ iv.setImageBitmap(null, true);
+ }
+ ZoomRingController.finishZoomTutorial(this, false);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ }
+
+ public void onClick(View v) {
+ switch (v.getId()) {
+
+ case R.id.shutter_button: {
+ if (mCameraReviewMode) {
+ finish();
+ } else {
+ MenuHelper.gotoStillImageCapture(this);
+ }
+ }
+ break;
+
+ case R.id.gallery: {
+ MenuHelper.gotoCameraImageGallery(this);
+ }
+ break;
+
+ case R.id.discard: {
+ if (mCameraReviewMode) {
+ mDeletePhotoRunnable.run();
+ } else {
+ MenuHelper.deletePhoto(this, mDeletePhotoRunnable);
+ }
+ }
+ break;
+
+ case R.id.share: {
+ Uri u = mAllImages.getImageAt(mCurrentPosition).fullSizeImageUri();
+ Intent intent = new Intent();
+ intent.setAction(Intent.ACTION_SEND);
+ intent.setType("image/jpeg");
+ intent.putExtra(Intent.EXTRA_STREAM, u);
+ try {
+ startActivity(Intent.createChooser(intent, getText(R.string.sendImage)));
+ } catch (android.content.ActivityNotFoundException ex) {
+ Toast.makeText(this, R.string.no_way_to_share_image, Toast.LENGTH_SHORT).show();
+ }
+ }
+ break;
+
+ case R.id.setas: {
+ Uri u = mAllImages.getImageAt(mCurrentPosition).fullSizeImageUri();
+ Intent intent = new Intent(Intent.ACTION_ATTACH_DATA, u);
+ try {
+ startActivity(Intent.createChooser(intent, getText(R.string.setImage)));
+ } catch (android.content.ActivityNotFoundException ex) {
+ Toast.makeText(this, R.string.no_way_to_share_video, Toast.LENGTH_SHORT).show();
+ }
+ }
+ break;
+
+ case R.id.next_image: {
+ moveNextOrPrevious(1);
+ }
+ break;
+
+ case R.id.prev_image: {
+ moveNextOrPrevious(-1);
+ }
+ break;
+ }
+ }
+
+ private void moveNextOrPrevious(int delta) {
+ int nextImagePos = mCurrentPosition + delta;
+ if ((0 <= nextImagePos) && (nextImagePos < mAllImages.getCount())) {
+ setImage(nextImagePos);
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case MenuHelper.RESULT_COMMON_MENU_CROP:
+ if (resultCode == RESULT_OK) {
+ // The CropImage activity passes back the Uri of the cropped image as
+ // the Action rather than the Data.
+ Uri dataUri = Uri.parse(data.getAction());
+ init(dataUri);
+ }
+ break;
+ }
+ }
+}