summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-02-19 10:57:35 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-02-19 10:57:35 -0800
commitd8bccef54d490ce562e61fc907201da0c666314e (patch)
treef254350cce4028765c3b357809cbca4784b18d02 /src
parent5055ba62e22becc9c8d03898b80cda8ed68cfe80 (diff)
downloadLegacyCamera-d8bccef54d490ce562e61fc907201da0c666314e.zip
LegacyCamera-d8bccef54d490ce562e61fc907201da0c666314e.tar.gz
LegacyCamera-d8bccef54d490ce562e61fc907201da0c666314e.tar.bz2
auto import from //branches/cupcake/...@132276
Diffstat (limited to 'src')
-rw-r--r--src/com/android/camera/Camera.java215
-rw-r--r--src/com/android/camera/ImageGallery2.java5
-rwxr-xr-xsrc/com/android/camera/ImageManager.java82
-rw-r--r--src/com/android/camera/OnScreenHint.java296
-rw-r--r--src/com/android/camera/VideoCamera.java164
-rw-r--r--src/com/android/camera/ViewImage.java111
6 files changed, 697 insertions, 176 deletions
diff --git a/src/com/android/camera/Camera.java b/src/com/android/camera/Camera.java
index 079528b..9bf101c 100644
--- a/src/com/android/camera/Camera.java
+++ b/src/com/android/camera/Camera.java
@@ -16,7 +16,12 @@
package com.android.camera;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -68,12 +73,13 @@ import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.OrientationListener;
+import android.view.OrientationEventListener;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.MenuItem.OnMenuItemClickListener;
+import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
@@ -81,8 +87,6 @@ import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.Toast;
-import com.android.camera.ImageManager.IImageList;
-
public class Camera extends Activity implements View.OnClickListener,
ShutterButton.OnShutterButtonListener, SurfaceHolder.Callback {
@@ -117,8 +121,8 @@ public class Camera extends Activity implements View.OnClickListener,
public static final int MENU_SAVE_CAMERA_VIDEO_DONE = 37;
private Toast mToast;
- private OrientationListener mOrientationListener;
- private int mLastOrientation = OrientationListener.ORIENTATION_UNKNOWN;
+ private OrientationEventListener mOrientationListener;
+ private int mLastOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
private SharedPreferences mPreferences;
private static final int IDLE = 1;
@@ -132,7 +136,7 @@ public class Camera extends Activity implements View.OnClickListener,
private android.hardware.Camera.Parameters mParameters;
private VideoPreview mSurfaceView;
private SurfaceHolder mSurfaceHolder = null;
- private ImageView mBlackout = null;
+ private View mBlackout = null;
private int mOriginalViewFinderWidth, mOriginalViewFinderHeight;
private int mViewFinderWidth, mViewFinderHeight;
@@ -165,6 +169,7 @@ public class Camera extends Activity implements View.OnClickListener,
private Drawable[] mThumbnails;
private boolean mShouldTransitionThumbnails;
private Uri mLastPictureUri;
+ private Bitmap mLastPictureThumb;
private LocationManager mLocationManager = null;
private ShutterButton mShutterButton;
@@ -182,6 +187,7 @@ public class Camera extends Activity implements View.OnClickListener,
private boolean mKeepAndRestartPreview;
+ // mPostCaptureAlert is non-null only if isImageCaptureIntent() is true.
private View mPostCaptureAlert;
@@ -257,18 +263,16 @@ public class Camera extends Activity implements View.OnClickListener,
String action = intent.getAction();
if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
// SD card available
- // TODO put up a "please wait" message
- // TODO also listen for the media scanner finished message
- showStorageToast();
+ updateStorageHint();
} else if (action.equals(Intent.ACTION_MEDIA_UNMOUNTED) ||
action.equals(Intent.ACTION_MEDIA_CHECKING)) {
// SD card unavailable
mPicturesRemaining = MenuHelper.NO_STORAGE_ERROR;
- showStorageToast(mPicturesRemaining);
+ updateStorageHint(mPicturesRemaining);
} else if (action.equals(Intent.ACTION_MEDIA_SCANNER_STARTED)) {
Toast.makeText(Camera.this, getResources().getString(R.string.wait), 5000);
} else if (action.equals(Intent.ACTION_MEDIA_SCANNER_FINISHED)) {
- showStorageToast();
+ updateStorageHint();
}
}
};
@@ -321,7 +325,6 @@ public class Camera extends Activity implements View.OnClickListener,
Log.v(TAG, "********** Total shutter lag " + (now - mShutterPressTime) + " ms");
}
if (mClickSound != null) {
- mClickSound.seekTo(0);
mClickSound.start();
}
}
@@ -450,17 +453,18 @@ public class Camera extends Activity implements View.OnClickListener,
startTiming();
}
long dateTaken = System.currentTimeMillis();
+ String name = createName(dateTaken) + ".jpg";
mLastContentUri = ImageManager.instance().addImage(
Camera.this,
mContentResolver,
- createName(dateTaken),
+ name,
"",
dateTaken,
// location for the database goes here
loc,
0, // the dsp will use the right orientation so don't "double set it"
ImageManager.CAMERA_IMAGE_BUCKET_NAME,
- null);
+ name);
if (mLastContentUri == null) {
// this means we got an error
@@ -626,6 +630,10 @@ public class Camera extends Activity implements View.OnClickListener,
mCameraDevice.setParameters(mParameters);
mCameraDevice.takePicture(mShutterCallback, mRawPictureCallback, new JpegPictureCallback(loc));
+ // Prepare the sound to play in shutter callback.
+ if (mClickSound != null) {
+ mClickSound.seekTo(0);
+ }
mBlackout.setVisibility(View.VISIBLE);
// Comment this out for now until we can decode the preview frame. This currently
@@ -652,7 +660,7 @@ public class Camera extends Activity implements View.OnClickListener,
// cached value which was calculated when the preview was restarted.
if (DEBUG_TIME_OPERATIONS) mShutterPressTime = System.currentTimeMillis();
if (mPicturesRemaining < 1) {
- showStorageToast(mPicturesRemaining);
+ updateStorageHint(mPicturesRemaining);
return;
}
@@ -713,6 +721,7 @@ public class Camera extends Activity implements View.OnClickListener,
if (mLastPictureButton.getVisibility() != View.VISIBLE) {
mShouldShowLastPictureButton = true;
}
+ mLastPictureThumb = lastPictureThumb;
mLastPictureUri = uri;
}
@@ -759,12 +768,21 @@ public class Camera extends Activity implements View.OnClickListener,
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
+ // To reduce startup time, we open camera device in another thread.
+ // We make sure the camera is opened at the end of onCreate.
+ Thread openCameraThread = new Thread(new Runnable() {
+ public void run() {
+ mCameraDevice = android.hardware.Camera.open();
+ }
+ });
+ openCameraThread.start();
+
// To reduce startup time, we run some service creation code in another thread.
// We make sure the services are loaded at the end of onCreate().
Thread loadServiceThread = new Thread(new Runnable() {
public void run() {
mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
- mOrientationListener = new OrientationListener(Camera.this) {
+ mOrientationListener = new OrientationEventListener(Camera.this) {
public void onOrientationChanged(int orientation) {
mLastOrientation = orientation;
}
@@ -776,9 +794,6 @@ public class Camera extends Activity implements View.OnClickListener,
mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
mContentResolver = getContentResolver();
- //setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT);
- requestWindowFeature(Window.FEATURE_PROGRESS);
-
Window win = getWindow();
win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.camera);
@@ -792,28 +807,12 @@ public class Camera extends Activity implements View.OnClickListener,
holder.addCallback(this);
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
- mBlackout = (ImageView) findViewById(R.id.blackout);
- mBlackout.setBackgroundDrawable(new ColorDrawable(0xFF000000));
+ mBlackout = findViewById(R.id.blackout);
- mLastPictureButton = (ImageView) findViewById(R.id.last_picture_button);
if (!isImageCaptureIntent()) {
- ImageManager.IImageList images = ImageManager.instance().allImages(
- this,
- getContentResolver(),
- ImageManager.DataLocation.ALL,
- ImageManager.INCLUDE_IMAGES,
- ImageManager.SORT_DESCENDING,
- ImageManager.CAMERA_IMAGE_BUCKET_ID);
- ImageManager.IImage lastPicture =
- images.isEmpty() ? null : images.getImageAt(0);
+ mLastPictureButton = (ImageView) findViewById(R.id.last_picture_button);
mLastPictureButton.setOnClickListener(this);
- if (lastPicture == null) {
- mLastPictureButton.setVisibility(View.GONE);
- } else {
- Bitmap miniThumb = lastPicture.miniThumbBitmap();
- setLastPictureThumb(miniThumb, lastPicture.fullSizeImageUri());
- }
- images.deactivate();
+ loadLastThumb();
}
mShutterButton = (ShutterButton) findViewById(R.id.shutter_button);
@@ -840,10 +839,17 @@ public class Camera extends Activity implements View.OnClickListener,
mFocusBlinkAnimation.setRepeatCount(Animation.INFINITE);
mFocusBlinkAnimation.setRepeatMode(Animation.REVERSE);
- mPostCaptureAlert = findViewById(R.id.post_picture_panel);
+ // We load the post_picture_panel layout only if it is needed.
+ if (isImageCaptureIntent()) {
+ ViewGroup cameraView = (ViewGroup)findViewById(R.id.camera);
+ getLayoutInflater().inflate(R.layout.post_picture_panel,
+ cameraView);
+ mPostCaptureAlert = findViewById(R.id.post_picture_panel);
+ }
// Make sure the services are loaded.
try {
+ openCameraThread.join();
loadServiceThread.join();
} catch (InterruptedException ex) {
}
@@ -860,7 +866,7 @@ public class Camera extends Activity implements View.OnClickListener,
if (!storageOK) {
mHandler.post(new Runnable() {
public void run() {
- showStorageToast(mPicturesRemaining);
+ updateStorageHint(mPicturesRemaining);
}
});
}
@@ -1005,12 +1011,37 @@ public class Camera extends Activity implements View.OnClickListener,
}
}
- private void showStorageToast() {
- MenuHelper.showStorageToast(this);
+ private void updateStorageHint() {
+ updateStorageHint(MenuHelper.calculatePicturesRemaining());
}
- private void showStorageToast(int remainingPictures) {
- MenuHelper.showStorageToast(this, remainingPictures);
+ private OnScreenHint mStorageHint;
+
+ private void updateStorageHint(int remaining) {
+ String noStorageText = null;
+
+ if (remaining == MenuHelper.NO_STORAGE_ERROR) {
+ String state = Environment.getExternalStorageState();
+ if (state == Environment.MEDIA_CHECKING) {
+ noStorageText = getString(R.string.preparing_sd);
+ } else {
+ noStorageText = getString(R.string.no_storage);
+ }
+ } else if (remaining < 1) {
+ noStorageText = getString(R.string.not_enough_space);
+ }
+
+ if (noStorageText != null) {
+ if (mStorageHint == null) {
+ mStorageHint = OnScreenHint.makeText(this, noStorageText);
+ } else {
+ mStorageHint.setText(noStorageText);
+ }
+ mStorageHint.show();
+ } else if (mStorageHint != null) {
+ mStorageHint.cancel();
+ mStorageHint = null;
+ }
}
@Override
@@ -1048,20 +1079,78 @@ public class Camera extends Activity implements View.OnClickListener,
}
mBlackout.setVisibility(View.GONE);
+ }
- if (mLastPictureUri != null) {
- IImageList list = ImageManager.makeImageList(mLastPictureUri, this,
- ImageManager.SORT_ASCENDING);
- if (list.getImageForUri(mLastPictureUri) == null) {
- mLastPictureUri = null;
- mLastPictureButton.setVisibility(View.GONE);
+ private ImageManager.DataLocation dataLocation() {
+ return ImageManager.DataLocation.EXTERNAL;
+ }
+
+ private static final int BUFSIZE = 4096;
+
+ // Stores the thumbnail and URI of last-picture-taken to SD card, so we can
+ // load it the next time the Camera app starts.
+ private void storeLastThumb() {
+ if (mLastPictureUri != null && mLastPictureThumb != null) {
+ try {
+ FileOutputStream f = new FileOutputStream(ImageManager.getLastThumbPath());
+ try {
+ BufferedOutputStream b = new BufferedOutputStream(f, BUFSIZE);
+ try {
+ DataOutputStream d = new DataOutputStream(b);
+ try {
+ d.writeUTF(mLastPictureUri.toString());
+ mLastPictureThumb.compress(Bitmap.CompressFormat.PNG, 100, d);
+ } finally {
+ d.close();
+ b = null;
+ f = null;
+ }
+ } finally {
+ if (b != null) {
+ b.close();
+ f = null;
+ }
+ }
+ } finally {
+ if (f != null) {
+ f.close();
+ }
+ }
+ } catch (IOException e) {
}
- list.deactivate();
}
}
- private ImageManager.DataLocation dataLocation() {
- return ImageManager.DataLocation.EXTERNAL;
+ // Loads the thumbnail and URI of last-picture-taken from SD card.
+ private void loadLastThumb() {
+ try {
+ FileInputStream f = new FileInputStream(ImageManager.getLastThumbPath());
+ try {
+ BufferedInputStream b = new BufferedInputStream(f, BUFSIZE);
+ try {
+ DataInputStream d = new DataInputStream(b);
+ try {
+ Uri lastUri = Uri.parse(d.readUTF());
+ Bitmap lastThumb = BitmapFactory.decodeStream(d);
+ setLastPictureThumb(lastThumb, lastUri);
+ } finally {
+ d.close();
+ b = null;
+ f = null;
+ }
+ } finally {
+ if (b != null) {
+ b.close();
+ f = null;
+ }
+ }
+ } finally {
+ if (f != null) {
+ f.close();
+ }
+ }
+ } catch (IOException e) {
+ }
}
@Override
@@ -1096,6 +1185,11 @@ public class Camera extends Activity implements View.OnClickListener,
mFocusToneGenerator = null;
}
+ storeLastThumb();
+ if (mStorageHint != null) {
+ mStorageHint.cancel();
+ mStorageHint = null;
+ }
super.onPause();
}
@@ -1184,6 +1278,9 @@ public class Camera extends Activity implements View.OnClickListener,
// If we get a dpad center event without any focused view, move the
// focus to the shutter button and press it.
if (event.getRepeatCount() == 0) {
+ // Start auto-focus immediately to reduce shutter lag. After the shutter button
+ // gets the focus, doFocus() will be called again but it is fine.
+ doFocus(true);
if (mShutterButton.isInTouchMode()) {
mShutterButton.requestFocusFromTouch();
} else {
@@ -1299,7 +1396,7 @@ public class Camera extends Activity implements View.OnClickListener,
Animation a = mShowLastPictureButtonAnimation;
a.setDuration(500);
mLastPictureButton.setAnimation(a);
- }
+ }
if (mShouldTransitionThumbnails) {
mShouldTransitionThumbnails = false;
@@ -1606,10 +1703,8 @@ public class Camera extends Activity implements View.OnClickListener,
}
private void showPostCaptureAlert() {
- boolean isPick = isImageCaptureIntent();
- int pickVisible = isPick ? View.VISIBLE : View.GONE;
- mPostCaptureAlert.setVisibility(pickVisible);
- if (isPick) {
+ if (isImageCaptureIntent()) {
+ mPostCaptureAlert.setVisibility(View.VISIBLE);
int[] pickIds = {R.id.attach, R.id.cancel};
for(int id : pickIds) {
View view = mPostCaptureAlert.findViewById(id);
@@ -1622,7 +1717,9 @@ public class Camera extends Activity implements View.OnClickListener,
}
private void hidePostCaptureAlert() {
- mPostCaptureAlert.setVisibility(View.GONE);
+ if (isImageCaptureIntent()) {
+ mPostCaptureAlert.setVisibility(View.INVISIBLE);
+ }
}
@Override
diff --git a/src/com/android/camera/ImageGallery2.java b/src/com/android/camera/ImageGallery2.java
index 89afd9e..566bfcb 100644
--- a/src/com/android/camera/ImageGallery2.java
+++ b/src/com/android/camera/ImageGallery2.java
@@ -763,7 +763,7 @@ public class ImageGallery2 extends Activity {
setVerticalScrollBarEnabled(true);
initializeScrollbars(context.obtainStyledAttributes(android.R.styleable.View));
- mGestureDetector = new GestureDetector(context, new SimpleOnGestureListener() {
+ mGestureDetector = new GestureDetector(context, new SimpleOnGestureListener() {
@Override
public boolean onDown(MotionEvent e) {
if (mScroller != null && !mScroller.isFinished()) {
@@ -1464,7 +1464,8 @@ public class ImageGallery2 extends Activity {
}
return retVal;
- }}
+ }
+ }
Bitmap resizeBitmap(Bitmap b) {
// assume they're both square for now
diff --git a/src/com/android/camera/ImageManager.java b/src/com/android/camera/ImageManager.java
index fb02f9e..4a83958 100755
--- a/src/com/android/camera/ImageManager.java
+++ b/src/com/android/camera/ImageManager.java
@@ -691,7 +691,17 @@ public class ImageManager {
dbMagic = mMiniThumbMagic;
byte [] data = mContainer.getMiniThumbFromFile(id, sMiniThumbData, dbMagic);
if (data == null) {
- dbMagic = ((BaseImageList)getContainer()).checkThumbnail(this, getCursor(), getRow());
+ byte[][] createdThumbData = new byte[1][];
+ try {
+ dbMagic = ((BaseImageList)getContainer()).checkThumbnail(this, getCursor(),
+ getRow(), createdThumbData);
+ } catch (IOException ex) {
+ // Typically IOException because the sd card is full.
+ // But createdThumbData may have been filled in, so continue on.
+ }
+ data = createdThumbData[0];
+ }
+ if (data == null) {
data = mContainer.getMiniThumbFromFile(id, sMiniThumbData, dbMagic);
}
if (data == null) {
@@ -724,7 +734,7 @@ public class ImageManager {
mContainer.mCache.remove(mId);
}
- protected void saveMiniThumb(Bitmap source) {
+ protected void saveMiniThumb(Bitmap source) throws IOException {
mContainer.saveMiniThumbToFile(source, fullSizeImageId(), 0);
}
@@ -1039,13 +1049,33 @@ public class ImageManager {
}
// returns id
- public long checkThumbnail(BaseImage existingImage, Cursor c, int i) {
+ public long checkThumbnail(BaseImage existingImage, Cursor c, int i) throws IOException {
+ return checkThumbnail(existingImage, c, i, null);
+ }
+
+ /**
+ * Checks to see if a mini thumbnail exists in the cache. If not, tries to create it and
+ * add it to the cache.
+ * @param existingImage
+ * @param c
+ * @param i
+ * @param createdThumbnailData if this parameter is non-null, and a new mini-thumbnail
+ * bitmap is created, the new bitmap's data will be stored in createdThumbnailData[0].
+ * Note that if the sdcard is full, it's possible that
+ * createdThumbnailData[0] will be set even if the method throws an IOException. This is
+ * actually useful, because it allows the caller to use the created thumbnail even if
+ * the sdcard is full.
+ * @return
+ * @throws IOException
+ */
+ public long checkThumbnail(BaseImage existingImage, Cursor c, int i,
+ byte[][] createdThumbnailData) throws IOException {
long magic, fileMagic = 0, id;
try {
mLock.lock();
if (existingImage == null) {
// if we don't have an Image object then get the id and magic from
- // the cursor. Synchonize on the cursor object.
+ // the cursor. Synchronize on the cursor object.
synchronized (c) {
if (!c.moveToPosition(i)) {
return -1;
@@ -1090,7 +1120,6 @@ public class ImageManager {
// If we can't retrieve the thumbnail, first check if there is one embedded in the
// EXIF data. If not, or it's not big enough, decompress the full size image.
Bitmap bitmap = null;
-
String filePath = null;
synchronized (c) {
if (c.moveToPosition(i)) {
@@ -1129,8 +1158,11 @@ public class ImageManager {
magic = mRandom.nextLong();
} while (magic == 0);
if (bitmap != null) {
- saveMiniThumbToFile(bitmap, id, magic);
- bitmap.recycle();
+ byte [] data = miniThumbData(bitmap);
+ if (createdThumbnailData != null) {
+ createdThumbnailData[0] = data;
+ }
+ saveMiniThumbToFile(data, id, magic);
}
synchronized (c) {
@@ -1483,7 +1515,12 @@ public class ImageManager {
mCursorDeactivated = false;
}
- protected void saveMiniThumbToFile(Bitmap source, long id, long magic) {
+ protected void saveMiniThumbToFile(Bitmap bitmap, long id, long magic) throws IOException {
+ byte[] data = miniThumbData(bitmap);
+ saveMiniThumbToFile(data, id, magic);
+ }
+
+ protected void saveMiniThumbToFile(byte[] data, long id, long magic) throws IOException {
RandomAccessFile r = miniThumbDataFile();
if (r == null)
return;
@@ -1493,7 +1530,6 @@ public class ImageManager {
synchronized (r) {
try {
long t1 = System.currentTimeMillis();
- byte [] data = miniThumbData(source);
long t2 = System.currentTimeMillis();
if (data != null) {
if (data.length > sBytesPerMiniThumb) {
@@ -1518,9 +1554,8 @@ public class ImageManager {
if (VERBOSE) Log.v(TAG, "saveMiniThumbToFile took " + (t3-t0) + "; " + (t1-t0) + " " + (t2-t1) + " " + (t3-t2));
}
} catch (IOException ex) {
- if (VERBOSE) {
- Log.e(TAG, "couldn't save mini thumbnail data for " + id + "; " + ex.toString());
- }
+ Log.e(TAG, "couldn't save mini thumbnail data for " + id + "; " + ex.toString());
+ throw ex;
}
}
}
@@ -1929,7 +1964,11 @@ public class ImageManager {
long t4 = System.currentTimeMillis();
checkCanceled();
if (VERBOSE) Log.v(TAG, ">>>>>>>>>>>>>>>>>>>>> rotating by " + orientation);
- saveMiniThumb(rotate(thumbnail, orientation));
+ try {
+ saveMiniThumb(rotate(thumbnail, orientation));
+ } catch (IOException e) {
+ // Ignore if unable to save thumb.
+ }
long t5 = System.currentTimeMillis();
checkCanceled();
@@ -2007,7 +2046,11 @@ public class ImageManager {
// setting this to zero will force the call to checkCursor to generate fresh thumbs
mMiniThumbMagic = 0;
- mContainer.checkThumbnail(this, mContainer.getCursor(), this.getRow());
+ try {
+ mContainer.checkThumbnail(this, mContainer.getCursor(), this.getRow());
+ } catch (IOException e) {
+ // Ignore inability to store mini thumbnail.
+ }
return true;
}
@@ -3604,8 +3647,10 @@ public class ImageManager {
return sInstance;
}
-
- static public byte [] miniThumbData(Bitmap source) {
+ /**
+ * Creates a byte[] for a given bitmap of the desired size. Recycles the input bitmap.
+ */
+ static public byte[] miniThumbData(Bitmap source) {
if (source == null)
return null;
@@ -4130,4 +4175,9 @@ public class ImageManager {
}
return bitmap;
}
+
+ public static String getLastThumbPath() {
+ return Environment.getExternalStorageDirectory().toString() +
+ "/DCIM/.thumbnails/camera_last_thumb";
+ }
}
diff --git a/src/com/android/camera/OnScreenHint.java b/src/com/android/camera/OnScreenHint.java
new file mode 100644
index 0000000..96190a0
--- /dev/null
+++ b/src/com/android/camera/OnScreenHint.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2009 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 android.app.INotificationManager;
+import android.app.ITransientNotification;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.os.RemoteException;
+import android.os.Handler;
+import android.os.ServiceManager;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+/**
+ * A on-screen hint is a view containing a little message for the user and will
+ * be shown on the screen continuously. This class helps you create and show
+ * those.
+ *
+ * <p>
+ * When the view is shown to the user, appears as a floating view over the
+ * application.
+ * <p>
+ * The easiest way to use this class is to call one of the static methods that
+ * constructs everything you need and returns a new OnScreenHint object.
+ */
+public class OnScreenHint {
+ static final String TAG = "OnScreenHint";
+ static final boolean localLOGV = false;
+
+ final Context mContext;
+ int mGravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
+ int mX, mY;
+ float mHorizontalMargin;
+ float mVerticalMargin;
+ View mView;
+ View mNextView;
+
+ private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
+ private WindowManager mWM;
+ private final Handler mHandler = new Handler();
+
+ /**
+ * Construct an empty OnScreenHint object. You must call {@link #setView} before you
+ * can call {@link #show}.
+ *
+ * @param context The context to use. Usually your {@link android.app.Application}
+ * or {@link android.app.Activity} object.
+ */
+ public OnScreenHint(Context context) {
+ mContext = context;
+ mWM = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ mY = context.getResources().getDimensionPixelSize(R.dimen.hint_y_offset);
+
+ mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
+ mParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+ mParams.format = PixelFormat.TRANSLUCENT;
+ mParams.windowAnimations = R.style.Animation_OnScreenHint;
+ mParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
+ mParams.setTitle("OnScreenHint");
+ }
+
+ /**
+ * Show the view on the screen.
+ */
+ public void show() {
+ if (mNextView == null) {
+ throw new RuntimeException("setView must have been called");
+ }
+ if (localLOGV) Log.v(TAG, "SHOW: " + this);
+ mHandler.post(mShow);
+ }
+
+ /**
+ * Close the view if it's showing.
+ */
+ public void cancel() {
+ if (localLOGV) Log.v(TAG, "HIDE: " + this);
+ mHandler.post(mHide);
+ }
+
+ /**
+ * Set the view to show.
+ * @see #getView
+ */
+ public void setView(View view) {
+ mNextView = view;
+ }
+
+ /**
+ * Return the view.
+ * @see #setView
+ */
+ public View getView() {
+ return mNextView;
+ }
+
+ /**
+ * Set the margins of the view.
+ *
+ * @param horizontalMargin The horizontal margin, in percentage of the
+ * container width, between the container's edges and the
+ * notification
+ * @param verticalMargin The vertical margin, in percentage of the
+ * container height, between the container's edges and the
+ * notification
+ */
+ public void setMargin(float horizontalMargin, float verticalMargin) {
+ mHorizontalMargin = horizontalMargin;
+ mVerticalMargin = verticalMargin;
+ }
+
+ /**
+ * Return the horizontal margin.
+ */
+ public float getHorizontalMargin() {
+ return mHorizontalMargin;
+ }
+
+ /**
+ * Return the vertical margin.
+ */
+ public float getVerticalMargin() {
+ return mVerticalMargin;
+ }
+
+ /**
+ * Set the location at which the notification should appear on the screen.
+ * @see android.view.Gravity
+ * @see #getGravity
+ */
+ public void setGravity(int gravity, int xOffset, int yOffset) {
+ mGravity = gravity;
+ mX = xOffset;
+ mY = yOffset;
+ }
+
+ /**
+ * Get the location at which the notification should appear on the screen.
+ * @see android.view.Gravity
+ * @see #getGravity
+ */
+ public int getGravity() {
+ return mGravity;
+ }
+
+ /**
+ * Return the X offset in pixels to apply to the gravity's location.
+ */
+ public int getXOffset() {
+ return mX;
+ }
+
+ /**
+ * Return the Y offset in pixels to apply to the gravity's location.
+ */
+ public int getYOffset() {
+ return mY;
+ }
+
+ /**
+ * Make a standard hint that just contains a text view.
+ *
+ * @param context The context to use. Usually your {@link android.app.Application}
+ * or {@link android.app.Activity} object.
+ * @param text The text to show. Can be formatted text.
+ *
+ */
+ public static OnScreenHint makeText(Context context, CharSequence text) {
+ OnScreenHint result = new OnScreenHint(context);
+
+ LayoutInflater inflate = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View v = inflate.inflate(R.layout.on_screen_hint, null);
+ TextView tv = (TextView)v.findViewById(R.id.message);
+ tv.setText(text);
+
+ result.mNextView = v;
+
+ return result;
+ }
+
+ /**
+ * Make a standard hint that just contains a text view with the text from a resource.
+ *
+ * @param context The context to use. Usually your {@link android.app.Application}
+ * or {@link android.app.Activity} object.
+ * @param resId The resource id of the string resource to use. Can be formatted text.
+ *
+ * @throws Resources.NotFoundException if the resource can't be found.
+ */
+ public static OnScreenHint makeText(Context context, int resId)
+ throws Resources.NotFoundException {
+ return makeText(context, context.getResources().getText(resId));
+ }
+
+ /**
+ * Update the text in a OnScreenHint that was previously created using one of the makeText() methods.
+ * @param resId The new text for the OnScreenHint.
+ */
+ public void setText(int resId) {
+ setText(mContext.getText(resId));
+ }
+
+ /**
+ * Update the text in a OnScreenHint that was previously created using one of the makeText() methods.
+ * @param s The new text for the OnScreenHint.
+ */
+ public void setText(CharSequence s) {
+ if (mNextView == null) {
+ throw new RuntimeException("This OnScreenHint was not created with OnScreenHint.makeText()");
+ }
+ TextView tv = (TextView) mNextView.findViewById(R.id.message);
+ if (tv == null) {
+ throw new RuntimeException("This OnScreenHint was not created with OnScreenHint.makeText()");
+ }
+ tv.setText(s);
+ }
+
+ private synchronized void handleShow() {
+ if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView
+ + " mNextView=" + mNextView);
+ if (mView != mNextView) {
+ // remove the old view if necessary
+ handleHide();
+ mView = mNextView;
+ final int gravity = mGravity;
+ mParams.gravity = gravity;
+ if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
+ mParams.horizontalWeight = 1.0f;
+ }
+ if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
+ mParams.verticalWeight = 1.0f;
+ }
+ mParams.x = mX;
+ mParams.y = mY;
+ mParams.verticalMargin = mVerticalMargin;
+ mParams.horizontalMargin = mHorizontalMargin;
+ if (mView.getParent() != null) {
+ if (localLOGV) Log.v(
+ TAG, "REMOVE! " + mView + " in " + this);
+ mWM.removeView(mView);
+ }
+ if (localLOGV) Log.v(TAG, "ADD! " + mView + " in " + this);
+ mWM.addView(mView, mParams);
+ }
+ }
+
+ private synchronized void handleHide() {
+ if (localLOGV) Log.v(TAG, "HANDLE HIDE: " + this + " mView=" + mView);
+ if (mView != null) {
+ // note: checking parent() just to make sure the view has
+ // been added... i have seen cases where we get here when
+ // the view isn't yet added, so let's try not to crash.
+ if (mView.getParent() != null) {
+ if (localLOGV) Log.v(
+ TAG, "REMOVE! " + mView + " in " + this);
+ mWM.removeView(mView);
+ }
+ mView = null;
+ }
+ }
+
+ private Runnable mShow = new Runnable() {
+ public void run() {
+ handleShow();
+ }
+ };
+
+ private Runnable mHide = new Runnable() {
+ public void run() {
+ handleHide();
+ }
+ };
+}
+
diff --git a/src/com/android/camera/VideoCamera.java b/src/com/android/camera/VideoCamera.java
index e3b7ebe..05e33a6 100644
--- a/src/com/android/camera/VideoCamera.java
+++ b/src/com/android/camera/VideoCamera.java
@@ -17,6 +17,7 @@
package com.android.camera;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -108,6 +109,7 @@ public class VideoCamera extends Activity implements View.OnClickListener,
// The video file that the hardware camera is about to record into
// (or is recording into.)
private String mCameraVideoFilename;
+ private FileDescriptor mCameraVideoFileDescriptor;
// The video file that has already been recorded, and that is being
// examined by the user.
@@ -197,16 +199,16 @@ public class VideoCamera extends Activity implements View.OnClickListener,
// SD card available
// TODO put up a "please wait" message
// TODO also listen for the media scanner finished message
- showStorageToast();
+ updateStorageHint();
mHasSdCard = true;
} else if (action.equals(Intent.ACTION_MEDIA_UNMOUNTED)) {
// SD card unavailable
- showStorageToast();
+ updateStorageHint();
mHasSdCard = false;
} else if (action.equals(Intent.ACTION_MEDIA_SCANNER_STARTED)) {
Toast.makeText(VideoCamera.this, getResources().getString(R.string.wait), 5000);
} else if (action.equals(Intent.ACTION_MEDIA_SCANNER_FINISHED)) {
- showStorageToast();
+ updateStorageHint();
}
}
};
@@ -273,7 +275,7 @@ public class VideoCamera extends Activity implements View.OnClickListener,
if (!storageOK) {
mHandler.post(new Runnable() {
public void run() {
- showStorageToast();
+ updateStorageHint();
}
});
}
@@ -285,6 +287,10 @@ public class VideoCamera extends Activity implements View.OnClickListener,
public void onClick(View v) {
switch (v.getId()) {
+ case R.id.gallery:
+ MenuHelper.gotoCameraVideoGallery(this);
+ break;
+
case R.id.attach:
doReturnToCaller(true);
break;
@@ -362,15 +368,30 @@ public class VideoCamera extends Activity implements View.OnClickListener,
hideVideoFrameAndStartPreview();
}
- private void showStorageToast() {
- long remaining = getAvailableStorage();
+ private OnScreenHint mStorageHint;
+ private void updateStorageHint() {
+ long remaining = getAvailableStorage();
+ String errorMessage = null;
if (remaining == NO_STORAGE_ERROR) {
- Toast.makeText(this, getString(R.string.no_storage), Toast.LENGTH_LONG).show();
+ errorMessage = getString(R.string.no_storage);
} else if (remaining < LOW_STORAGE_THRESHOLD) {
- new AlertDialog.Builder(this).setTitle(R.string.spaceIsLow_title)
- .setMessage(R.string.spaceIsLow_content)
- .show();
+ errorMessage = getString(R.string.spaceIsLow_content);
+ if (mStorageHint != null) {
+ mStorageHint.cancel();
+ mStorageHint = null;
+ }
+ }
+ if (errorMessage != null) {
+ if (mStorageHint == null) {
+ mStorageHint = OnScreenHint.makeText(this, errorMessage);
+ } else {
+ mStorageHint.setText(errorMessage);
+ }
+ mStorageHint.show();
+ } else if (mStorageHint != null) {
+ mStorageHint.cancel();
+ mStorageHint = null;
}
}
@@ -431,6 +452,11 @@ public class VideoCamera extends Activity implements View.OnClickListener,
}
mBlackout.setVisibility(View.VISIBLE);
setScreenTimeoutSystemDefault();
+
+ if (mStorageHint != null) {
+ mStorageHint.cancel();
+ mStorageHint = null;
+ }
}
@Override
@@ -576,53 +602,7 @@ public class VideoCamera extends Activity implements View.OnClickListener,
int resultCode;
if (success) {
resultCode = RESULT_OK;
- Uri saveUri = null;
-
- Bundle myExtras = getIntent().getExtras();
- if (myExtras != null) {
- saveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
- }
-
- if (saveUri != null) {
- // TODO: Record the video directly into the content provider stream when
- // bug 1582062 is fixed. Until then we copy the video data from the
- // original location to the requested location and then delete the original.
- OutputStream outputStream = null;
- InputStream inputStream = null;
-
- try {
- inputStream = mContentResolver.openInputStream(mCurrentVideoUri);
- outputStream = mContentResolver.openOutputStream(saveUri);
- byte[] buffer = new byte[64*1024];
- while(true) {
- int bytesRead = inputStream.read(buffer);
- if (bytesRead < 0) {
- break;
- }
- outputStream.write(buffer, 0, bytesRead);
- }
- } catch (IOException ex) {
- Log.e(TAG, "Could not copy video file to Uri", ex);
- } finally {
- if (inputStream != null) {
- try {
- inputStream.close();
- } catch (IOException ex) {
- Log.e(TAG, "Could not close video file", ex);
- }
- }
- if (outputStream != null) {
- try {
- outputStream.close();
- } catch (IOException ex) {
- Log.e(TAG, "Could not close output uri", ex);
- }
- }
- deleteCurrentVideo();
- }
- } else {
- resultIntent.setData(mCurrentVideoUri);
- }
+ resultIntent.setData(mCurrentVideoUri);
} else {
resultCode = RESULT_CANCELED;
}
@@ -651,8 +631,36 @@ public class VideoCamera extends Activity implements View.OnClickListener,
}
}
+ private void cleanupEmptyFile() {
+ if (mCameraVideoFilename != null) {
+ File f = new File(mCameraVideoFilename);
+ if (f.length() == 0 && f.delete()) {
+ Log.v(TAG, "Empty video file deleted: " + mCameraVideoFilename);
+ mCameraVideoFilename = null;
+ }
+ }
+ }
+
private void initializeVideo() {
Log.v(TAG, "initializeVideo");
+ boolean isCaptureIntent = isVideoCaptureIntent();
+ Intent intent = getIntent();
+ Bundle myExtras = intent.getExtras();
+
+ if (isCaptureIntent && myExtras != null) {
+ Uri saveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
+ if (saveUri != null) {
+ try {
+ mCameraVideoFileDescriptor = mContentResolver.
+ openFileDescriptor(saveUri, "rw").getFileDescriptor();
+ mCurrentVideoUri = saveUri;
+ }
+ catch (java.io.FileNotFoundException ex) {
+ // invalid uri
+ Log.e(TAG, ex.toString());
+ }
+ }
+ }
releaseMediaRecorder();
if (mSurfaceHolder == null) {
@@ -670,17 +678,22 @@ public class VideoCamera extends Activity implements View.OnClickListener,
}
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
- createVideoPath();
- mMediaRecorder.setOutputFile(mCameraVideoFilename);
+
+ // We try Uri in intent first. If it doesn't work, use our own instead.
+ if (mCameraVideoFileDescriptor != null) {
+ mMediaRecorder.setOutputFile(mCameraVideoFileDescriptor);
+ } else {
+ createVideoPath();
+ mMediaRecorder.setOutputFile(mCameraVideoFilename);
+ }
+
boolean videoQualityHigh = getBooleanPreference(CameraSettings.KEY_VIDEO_QUALITY,
CameraSettings.DEFAULT_VIDEO_QUALITY_VALUE);
- {
- Intent intent = getIntent();
- if (intent.hasExtra(MediaStore.EXTRA_VIDEO_QUALITY)) {
- int extraVideoQuality = intent.getIntExtra(MediaStore.EXTRA_VIDEO_QUALITY, 0);
- videoQualityHigh = (extraVideoQuality > 0);
- }
+
+ if (intent.hasExtra(MediaStore.EXTRA_VIDEO_QUALITY)) {
+ int extraVideoQuality = intent.getIntExtra(MediaStore.EXTRA_VIDEO_QUALITY, 0);
+ videoQualityHigh = (extraVideoQuality > 0);
}
// Use the same frame rate for both, since internally
@@ -712,6 +725,7 @@ public class VideoCamera extends Activity implements View.OnClickListener,
private void releaseMediaRecorder() {
Log.v(TAG, "Releasing media recorder.");
if (mMediaRecorder != null) {
+ cleanupEmptyFile();
mMediaRecorder.reset();
mMediaRecorder.release();
mMediaRecorder = null;
@@ -773,10 +787,12 @@ public class VideoCamera extends Activity implements View.OnClickListener,
}
private void registerVideo() {
- Uri videoTable = Uri.parse("content://media/external/video/media");
- mCurrentVideoUri = mContentResolver.insert(videoTable,
- mCurrentVideoValues);
- Log.v(TAG, "Current video URI: " + mCurrentVideoUri);
+ if (mCameraVideoFileDescriptor == null) {
+ Uri videoTable = Uri.parse("content://media/external/video/media");
+ mCurrentVideoUri = mContentResolver.insert(videoTable,
+ mCurrentVideoValues);
+ Log.v(TAG, "Current video URI: " + mCurrentVideoUri);
+ }
mCurrentVideoValues = null;
}
@@ -837,10 +853,8 @@ public class VideoCamera extends Activity implements View.OnClickListener,
Log.v(TAG, "startVideoRecording");
if (!mMediaRecorderRecording) {
- if (!mHasSdCard) {
- Toast.makeText(this, getString(
- R.string.no_storage), Toast.LENGTH_LONG).show();
- Log.v(TAG, "No SD card, ignore start recording");
+ if (mStorageHint != null) {
+ Log.v(TAG, "Storage issue, ignore the start request");
return;
}
@@ -882,7 +896,7 @@ public class VideoCamera extends Activity implements View.OnClickListener,
private void showPostRecordingAlert() {
int[] pickIds = {R.id.attach, R.id.cancel};
- int[] normalIds = {R.id.share, R.id.discard};
+ int[] normalIds = {R.id.gallery, R.id.share, R.id.discard};
int[] alwaysOnIds = {R.id.play};
int[] hideIds = pickIds;
int[] connectIds = normalIds;
@@ -923,7 +937,6 @@ public class VideoCamera extends Activity implements View.OnClickListener,
mMediaRecorder.stop();
mCurrentVideoFilename = mCameraVideoFilename;
Log.v(TAG, "Setting current video filename: " + mCurrentVideoFilename);
- mCameraVideoFilename = null;
mNeedToRegisterRecording = true;
mMediaRecorderRecording = false;
}
@@ -937,6 +950,7 @@ public class VideoCamera extends Activity implements View.OnClickListener,
mNeedToRegisterRecording = false;
}
mCameraVideoFilename = null;
+ mCameraVideoFileDescriptor = null;
}
private void setScreenTimeoutSystemDefault() {
diff --git a/src/com/android/camera/ViewImage.java b/src/com/android/camera/ViewImage.java
index 56150ac..57d7051 100644
--- a/src/com/android/camera/ViewImage.java
+++ b/src/com/android/camera/ViewImage.java
@@ -39,6 +39,7 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
@@ -237,7 +238,36 @@ public class ViewImage extends Activity implements View.OnClickListener
// The event time of the previous touch up.
private long mPreviousUpTime;
// The duration in milliseconds we will wait to see if it is a double tap.
- private static final int DOUBLE_TAP_TIMEOUT = 200;
+ private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();
+
+ // Returns current scale step (numbered from 0 to 10).
+ private int getCurrentStep() {
+ float s = getScale();
+ float b = sScaleRate;
+ int step = (int)Math.round(Math.log(s) / Math.log(b));
+ return Math.max(0, Math.min(10, step));
+ }
+
+ // Returns the max scale step this image can use.
+ private int getMaxStep() {
+ float s = maxZoom();
+ float b = sScaleRate;
+ int step = (int)Math.ceil(Math.log(s) / Math.log(b));
+ return Math.max(0, Math.min(10, step));
+ }
+
+ // The setup we use here is to have 12 steps (only 0 to 10 are used),
+ // each separated by angle PI/6. We allow clockwise rotation (zoom-in)
+ // from the beginning position only. So we set counter clockwise bound
+ // to 0 and set clockwise bound to (2 - N/6) * PI. (clockwise angle
+ // is negative, and we need to mod 2*PI for the API to work.)
+ private void setZoomRingBounds() {
+ int max_step = getMaxStep();
+ float max_angle = (2 - max_step / 6F) * (float)Math.PI;
+ mZoomRingController.setResetThumbAutomatically(false);
+ mZoomRingController.setThumbClockwiseBound(max_angle);
+ mZoomRingController.setThumbCounterclockwiseBound(0);
+ }
// The zoom ring is set to visible by a double tap.
private ZoomRingController mZoomRingController;
@@ -246,38 +276,69 @@ public class ViewImage extends Activity implements View.OnClickListener
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 * (float)Math.PI / 6;
+ angle = (float)Math.PI * 2 - angle;
+ return angle;
+ }
+
+ private int angleToStep(double angle) {
+ angle = Math.PI * 2 - angle;
+ int step = (int)Math.round(angle / (Math.PI / 6));
+ return step;
+ }
+
public void onVisibilityChanged(boolean visible) {
+ if (visible) {
+ int step = getCurrentStep();
+ float angle = stepToAngle(step);
+ mZoomRingController.setThumbAngle(angle);
+ }
}
- public void onBeginDrag(float startAngle) {
+ public void onBeginDrag() {
+ setZoomRingBounds();
}
- public void onEndDrag(float endAngle) {
+ 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.
- while (deltaZoomLevel > 0) {
+ while (deltaStep > 0) {
zoomIn();
- deltaZoomLevel--;
+ deltaStep--;
}
- while (deltaZoomLevel < 0) {
+ while (deltaStep < 0) {
zoomOut();
- deltaZoomLevel++;
+ deltaStep++;
}
// Reverse the first centering.
@@ -335,11 +396,16 @@ public class ViewImage extends Activity implements View.OnClickListener
switch (m.getAction()) {
case MotionEvent.ACTION_DOWN:
- viewImage.setMode(MODE_NORMAL);
- viewImage.showOnScreenControls();
- mLastXTouchPos = x;
- mLastYTouchPos = y;
- mTouchState = TOUCH_STATE_REST;
+ long downTime = m.getEventTime();
+ if ((downTime - mPreviousUpTime) < DOUBLE_TAP_TIMEOUT) {
+ mZoomRingController.setVisible(true);
+ } else {
+ viewImage.setMode(MODE_NORMAL);
+ viewImage.showOnScreenControls();
+ mLastXTouchPos = x;
+ mLastYTouchPos = y;
+ mTouchState = TOUCH_STATE_REST;
+ }
break;
case MotionEvent.ACTION_MOVE:
if (x < TOUCH_AREA_WIDTH) {
@@ -413,15 +479,7 @@ public class ViewImage extends Activity implements View.OnClickListener
viewImage.mPrevImageView.setPressed(false);
viewImage.mNextImageView.setPressed(false);
mTouchState = TOUCH_STATE_REST;
-
- long eventTime = m.getEventTime();
- if (eventTime - mPreviousUpTime < DOUBLE_TAP_TIMEOUT) {
- mZoomRingController.setVisible(true);
- mPreviousUpTime = 0;
- } else {
- mPreviousUpTime = eventTime;
- }
-
+ mPreviousUpTime = m.getEventTime();
break;
case MotionEvent.ACTION_CANCEL:
viewImage.mPrevImageView.setPressed(false);
@@ -530,6 +588,11 @@ public class ViewImage extends Activity implements View.OnClickListener
return scrollHandler().getScrollX();
}
+ @Override
+ protected void onDetachedFromWindow() {
+ mZoomRingController.setVisible(false);
+ }
+
}
static class ScrollHandler extends LinearLayout {
@@ -1544,12 +1607,12 @@ public class ViewImage extends Activity implements View.OnClickListener
mAllImages.deactivate();
- for (ImageViewTouchBase iv: mImageViews) {
+ for (ImageViewTouch iv: mImageViews) {
iv.recycleBitmaps();
iv.setImageBitmap(null, true);
}
- for (ImageViewTouchBase iv: mSlideShowImageViews) {
+ for (ImageViewTouch iv: mSlideShowImageViews) {
iv.recycleBitmaps();
iv.setImageBitmap(null, true);
}