diff options
78 files changed, 2952 insertions, 364 deletions
diff --git a/src/com/android/camera/ActivityBase.java b/src/com/android/camera/ActivityBase.java index 4e4143ef8..dc866f0e5 100644 --- a/src/com/android/camera/ActivityBase.java +++ b/src/com/android/camera/ActivityBase.java @@ -38,6 +38,7 @@ import android.view.animation.DecelerateInterpolator; import com.android.camera.ui.LayoutChangeNotifier; import com.android.camera.ui.PopupManager; +import com.android.gallery3d.R; import com.android.gallery3d.app.AbstractGalleryActivity; import com.android.gallery3d.app.AppBridge; import com.android.gallery3d.app.FilmstripPage; diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java index bf1ec2483..a207b2b71 100644 --- a/src/com/android/camera/CameraActivity.java +++ b/src/com/android/camera/CameraActivity.java @@ -23,11 +23,14 @@ import android.content.Context; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; +import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.IBinder; import android.provider.MediaStore; +import android.provider.Settings; +import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -37,6 +40,7 @@ import android.view.ViewGroup; import android.widget.FrameLayout; import com.android.camera.ui.CameraSwitcher; +import com.android.gallery3d.R; import com.android.gallery3d.app.PhotoPage; import com.android.gallery3d.common.ApiHelper; import com.android.gallery3d.util.LightCycleHelper; @@ -57,6 +61,7 @@ public class CameraActivity extends ActivityBase private Drawable[] mDrawables; private int mCurrentModuleIndex; private MotionEvent mDown; + private boolean mAutoRotateScreen; private MyOrientationEventListener mOrientationListener; // The degrees of the device rotated clockwise from its natural orientation. @@ -140,6 +145,12 @@ public class CameraActivity extends ActivityBase super.onDestroy(); } + // Return whether the auto-rotate screen in system settings + // is turned on. + public boolean isAutoRotateScreen() { + return mAutoRotateScreen; + } + private class MyOrientationEventListener extends OrientationEventListener { public MyOrientationEventListener(Context context) { @@ -317,6 +328,14 @@ public class CameraActivity extends ActivityBase @Override public void onResume() { mPaused = false; + if (Settings.System.getInt(getContentResolver(), + Settings.System.ACCELEROMETER_ROTATION, 0) == 0) {// auto-rotate off + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); + mAutoRotateScreen = false; + } else { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); + mAutoRotateScreen = true; + } mOrientationListener.enable(); mCurrentModule.onResumeBeforeSuper(); super.onResume(); diff --git a/src/com/android/camera/CameraPreference.java b/src/com/android/camera/CameraPreference.java index 0a4e9b3ce..5ddd86dbc 100644 --- a/src/com/android/camera/CameraPreference.java +++ b/src/com/android/camera/CameraPreference.java @@ -21,6 +21,8 @@ import android.content.SharedPreferences; import android.content.res.TypedArray; import android.util.AttributeSet; +import com.android.gallery3d.R; + /** * The base class of all Preferences used in Camera. The preferences can be * loaded from XML resource by <code>PreferenceInflater</code>. diff --git a/src/com/android/camera/CameraSettings.java b/src/com/android/camera/CameraSettings.java index 3bc58a034..31d31e128 100644 --- a/src/com/android/camera/CameraSettings.java +++ b/src/com/android/camera/CameraSettings.java @@ -30,6 +30,7 @@ import android.media.CamcorderProfile; import android.util.FloatMath; import android.util.Log; +import com.android.gallery3d.R; import com.android.gallery3d.common.ApiHelper; import java.util.ArrayList; diff --git a/src/com/android/camera/CaptureAnimManager.java b/src/com/android/camera/CaptureAnimManager.java index 64383aff7..4643c379f 100644 --- a/src/com/android/camera/CaptureAnimManager.java +++ b/src/com/android/camera/CaptureAnimManager.java @@ -30,13 +30,18 @@ import com.android.gallery3d.glrenderer.RawTexture; public class CaptureAnimManager { @SuppressWarnings("unused") private static final String TAG = "CAM_Capture"; + // times mark endpoint of animation phase private static final int TIME_FLASH = 200; private static final int TIME_HOLD = 400; - private static final int TIME_SLIDE = 400; // milliseconds. + private static final int TIME_SLIDE = 700; + private static final int TIME_HOLD2 = 1000; + private static final int TIME_SLIDE2 = 1200; private static final int ANIM_BOTH = 0; private static final int ANIM_FLASH = 1; private static final int ANIM_SLIDE = 2; + private static final int ANIM_HOLD2 = 3; + private static final int ANIM_SLIDE2 = 4; private final Interpolator mSlideInterpolator = new DecelerateInterpolator(); @@ -44,15 +49,22 @@ public class CaptureAnimManager { private long mAnimStartTime; // milliseconds. private float mX; // The center of the whole view including preview and review. private float mY; - private float mDelta; private int mDrawWidth; private int mDrawHeight; private int mAnimType; + private int mHoldX; + private int mHoldY; + private int mHoldW; + private int mHoldH; + + private int mOffset = 80; + /* preview: camera preview view. * review: view of picture just taken. */ public CaptureAnimManager() { + } public void setOrientation(int displayRotation) { @@ -83,18 +95,24 @@ public class CaptureAnimManager { mDrawHeight = h; mX = x; mY = y; + mHoldW = (int) (mDrawWidth * 0.7f); + mHoldH = (int) (mDrawHeight * 0.7f); switch (mAnimOrientation) { case 0: // Preview is on the left. - mDelta = w; + mHoldX = x + w - mOffset; + mHoldY = y + (mDrawHeight - mHoldH) / 2; break; case 90: // Preview is below. - mDelta = -h; + mHoldX = x + (mDrawWidth - mHoldW + 1) / 2; + mHoldY = y + mOffset- mHoldH; break; case 180: // Preview on the right. - mDelta = -w; + mHoldX = x - w + mOffset; + mHoldY = y + (mDrawHeight - mHoldH) / 2; break; case 270: // Preview is above. - mDelta = h; + mHoldX = x + (mDrawWidth - mHoldW + 1) / 2; + mHoldY = y + h - mOffset; break; } } @@ -104,14 +122,27 @@ public class CaptureAnimManager { RawTexture review) { long timeDiff = SystemClock.uptimeMillis() - mAnimStartTime; // Check if the animation is over - if (mAnimType == ANIM_SLIDE && timeDiff > TIME_SLIDE) return false; - if (mAnimType == ANIM_BOTH && timeDiff > TIME_HOLD + TIME_SLIDE) return false; + if (mAnimType == ANIM_SLIDE && timeDiff > TIME_SLIDE2 - TIME_HOLD) return false; + if (mAnimType == ANIM_BOTH && timeDiff > TIME_SLIDE2) return false; + // determine phase and time in phase int animStep = mAnimType; - if (mAnimType == ANIM_BOTH) { - animStep = (timeDiff < TIME_HOLD) ? ANIM_FLASH : ANIM_SLIDE; - if (animStep == ANIM_SLIDE) { + if (mAnimType == ANIM_SLIDE) { + timeDiff += TIME_HOLD; + } + if (mAnimType == ANIM_SLIDE || mAnimType == ANIM_BOTH) { + if (timeDiff < TIME_HOLD) { + animStep = ANIM_FLASH; + } else if (timeDiff < TIME_SLIDE) { + animStep = ANIM_SLIDE; timeDiff -= TIME_HOLD; + } else if (timeDiff < TIME_HOLD2) { + animStep = ANIM_HOLD2; + timeDiff -= TIME_SLIDE; + } else { + // SLIDE2 + animStep = ANIM_SLIDE2; + timeDiff -= TIME_HOLD2; } } @@ -123,24 +154,47 @@ public class CaptureAnimManager { canvas.fillRect(mX, mY, mDrawWidth, mDrawHeight, color); } } else if (animStep == ANIM_SLIDE) { - float fraction = (float) (timeDiff) / TIME_SLIDE; + float fraction = mSlideInterpolator.getInterpolation((float) (timeDiff) / (TIME_SLIDE - TIME_HOLD)); float x = mX; float y = mY; - if (mAnimOrientation == 0 || mAnimOrientation == 180) { - x = x + mDelta * mSlideInterpolator.getInterpolation(fraction); - } else { - y = y + mDelta * mSlideInterpolator.getInterpolation(fraction); + float w = 0; + float h = 0; + x = interpolate(mX, mHoldX, fraction); + y = interpolate(mY, mHoldY, fraction); + w = interpolate(mDrawWidth, mHoldW, fraction); + h = interpolate(mDrawHeight, mHoldH, fraction); + preview.directDraw(canvas, (int) mX, (int) mY, mDrawWidth, mDrawHeight); + review.draw(canvas, (int) x, (int) y, (int) w, (int) h); + } else if (animStep == ANIM_HOLD2) { + preview.directDraw(canvas, (int) mX, (int) mY, mDrawWidth, mDrawHeight); + review.draw(canvas, mHoldX, mHoldY, mHoldW, mHoldH); + } else if (animStep == ANIM_SLIDE2) { + float fraction = (float)(timeDiff) / (TIME_SLIDE2 - TIME_HOLD2); + float x = mHoldX; + float y = mHoldY; + float d = mOffset * fraction; + switch (mAnimOrientation) { + case 0: + x = mHoldX + d; + break; + case 180: + x = mHoldX - d; + break; + case 90: + y = mHoldY - d; + break; + case 270: + y = mHoldY + d; + break; } - // float alpha = canvas.getAlpha(); - // canvas.setAlpha(fraction); - preview.directDraw(canvas, (int) mX, (int) mY, - mDrawWidth, mDrawHeight); - // canvas.setAlpha(alpha); - - review.draw(canvas, (int) x, (int) y, mDrawWidth, mDrawHeight); - } else { - return false; + preview.directDraw(canvas, (int) mX, (int) mY, mDrawWidth, mDrawHeight); + review.draw(canvas, (int) x, (int) y, mHoldW, mHoldH); } return true; } + + private static float interpolate(float start, float end, float fraction) { + return start + (end - start) * fraction; + } + } diff --git a/src/com/android/camera/CountDownTimerPreference.java b/src/com/android/camera/CountDownTimerPreference.java index 6c0f67369..0bbf6bc9d 100644 --- a/src/com/android/camera/CountDownTimerPreference.java +++ b/src/com/android/camera/CountDownTimerPreference.java @@ -19,6 +19,8 @@ package com.android.camera; import android.content.Context; import android.util.AttributeSet; +import com.android.gallery3d.R; + import java.util.List; /* CountDownTimerPreference generates entries (i.e. what users see in the UI), diff --git a/src/com/android/camera/EffectsRecorder.java b/src/com/android/camera/EffectsRecorder.java index 4601ab9ec..2db44c76f 100644 --- a/src/com/android/camera/EffectsRecorder.java +++ b/src/com/android/camera/EffectsRecorder.java @@ -26,6 +26,7 @@ import android.os.Handler; import android.os.Looper; import android.util.Log; +import com.android.gallery3d.R; import com.android.gallery3d.common.ApiHelper; import java.io.FileDescriptor; diff --git a/src/com/android/camera/FocusOverlayManager.java b/src/com/android/camera/FocusOverlayManager.java index 2bec18760..a3726ed56 100644 --- a/src/com/android/camera/FocusOverlayManager.java +++ b/src/com/android/camera/FocusOverlayManager.java @@ -94,6 +94,8 @@ public class FocusOverlayManager { private ComboPreferences mPreferences; private Handler mHandler; Listener mListener; + private boolean mPreviousMoving; + private boolean mFocusDefault; public interface Listener { public void autoFocus(); @@ -131,6 +133,7 @@ public class FocusOverlayManager { setParameters(parameters); mListener = listener; setMirror(mirror); + mFocusDefault = true; } public void setFocusRenderer(PieRenderer renderer) { @@ -277,7 +280,7 @@ public class FocusOverlayManager { updateFocusUI(); // If this is triggered by touch focus, cancel focus after a // while. - if (mFocusArea != null) { + if (!mFocusDefault) { mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY); } if (shutterButtonPressed) { @@ -302,29 +305,28 @@ public class FocusOverlayManager { // continuous autofocus. if (mState != STATE_IDLE) return; - if (moving) { + // animate on false->true trasition only b/8219520 + if (moving && !mPreviousMoving) { mPieRenderer.showStart(); - } else { + } else if (!moving) { mPieRenderer.showSuccess(true); } + mPreviousMoving = moving; } @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) - private void initializeFocusAreas(int focusWidth, int focusHeight, - int x, int y, int previewWidth, int previewHeight) { + private void initializeFocusAreas(int x, int y) { if (mFocusArea == null) { mFocusArea = new ArrayList<Object>(); mFocusArea.add(new Area(new Rect(), 1)); } // Convert the coordinates to driver format. - calculateTapArea(focusWidth, focusHeight, 1f, x, y, previewWidth, previewHeight, - ((Area) mFocusArea.get(0)).rect); + calculateTapArea(x, y, 1f, ((Area) mFocusArea.get(0)).rect); } @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) - private void initializeMeteringAreas(int focusWidth, int focusHeight, - int x, int y, int previewWidth, int previewHeight) { + private void initializeMeteringAreas(int x, int y) { if (mMeteringArea == null) { mMeteringArea = new ArrayList<Object>(); mMeteringArea.add(new Area(new Rect(), 1)); @@ -333,34 +335,26 @@ public class FocusOverlayManager { // Convert the coordinates to driver format. // AE area is bigger because exposure is sensitive and // easy to over- or underexposure if area is too small. - calculateTapArea(focusWidth, focusHeight, 1.5f, x, y, previewWidth, previewHeight, - ((Area) mMeteringArea.get(0)).rect); + calculateTapArea(x, y, 1.5f, ((Area) mMeteringArea.get(0)).rect); } public void onSingleTapUp(int x, int y) { if (!mInitialized || mState == STATE_FOCUSING_SNAP_ON_FINISH) return; // Let users be able to cancel previous touch focus. - if ((mFocusArea != null) && (mState == STATE_FOCUSING || + if ((!mFocusDefault) && (mState == STATE_FOCUSING || mState == STATE_SUCCESS || mState == STATE_FAIL)) { cancelAutoFocus(); } - // Initialize variables. - int focusWidth = mPieRenderer.getSize(); - int focusHeight = mPieRenderer.getSize(); - if (focusWidth == 0 || mPieRenderer.getWidth() == 0 - || mPieRenderer.getHeight() == 0) return; - int previewWidth = mPreviewWidth; - int previewHeight = mPreviewHeight; + if (mPreviewWidth == 0 || mPreviewHeight == 0) return; + mFocusDefault = false; // Initialize mFocusArea. if (mFocusAreaSupported) { - initializeFocusAreas( - focusWidth, focusHeight, x, y, previewWidth, previewHeight); + initializeFocusAreas(x, y); } // Initialize mMeteringArea. if (mMeteringAreaSupported) { - initializeMeteringAreas( - focusWidth, focusHeight, x, y, previewWidth, previewHeight); + initializeMeteringAreas(x, y); } // Use margin to set the focus indicator to the touched area. @@ -430,9 +424,10 @@ public class FocusOverlayManager { public String getFocusMode() { if (mOverrideFocusMode != null) return mOverrideFocusMode; + if (mParameters == null) return Parameters.FOCUS_MODE_AUTO; List<String> supportedFocusModes = mParameters.getSupportedFocusModes(); - if (mFocusAreaSupported && mFocusArea != null) { + if (mFocusAreaSupported && !mFocusDefault) { // Always use autofocus in tap-to-focus. mFocusMode = Parameters.FOCUS_MODE_AUTO; } else { @@ -479,7 +474,7 @@ public class FocusOverlayManager { FocusIndicator focusIndicator = (faceExists) ? mFaceView : mPieRenderer; if (mState == STATE_IDLE) { - if (mFocusArea == null) { + if (mFocusDefault) { focusIndicator.clear(); } else { // Users touch on the preview and the indicator represents the @@ -506,19 +501,23 @@ public class FocusOverlayManager { // Put focus indicator to the center. clear reset position mPieRenderer.clear(); - - mFocusArea = null; - mMeteringArea = null; + // Initialize mFocusArea. + if (mFocusAreaSupported) { + initializeFocusAreas(mPreviewWidth / 2, mPreviewHeight / 2); + } + // Initialize mMeteringArea. + if (mMeteringAreaSupported) { + initializeMeteringAreas(mPreviewWidth / 2, mPreviewHeight / 2); + } + mFocusDefault = true; } - private void calculateTapArea(int focusWidth, int focusHeight, float areaMultiple, - int x, int y, int previewWidth, int previewHeight, Rect rect) { - int areaWidth = (int) (focusWidth * areaMultiple); - int areaHeight = (int) (focusHeight * areaMultiple); - int left = Util.clamp(x - areaWidth / 2, 0, previewWidth - areaWidth); - int top = Util.clamp(y - areaHeight / 2, 0, previewHeight - areaHeight); + private void calculateTapArea(int x, int y, float areaMultiple, Rect rect) { + int areaSize = (int) (Math.min(mPreviewWidth, mPreviewHeight) * areaMultiple / 20); + int left = Util.clamp(x - areaSize, 0, mPreviewWidth - 2 * areaSize); + int top = Util.clamp(y - areaSize, 0, mPreviewHeight - 2 * areaSize); - RectF rectF = new RectF(left, top, left + areaWidth, top + areaHeight); + RectF rectF = new RectF(left, top, left + 2 * areaSize, top + 2 * areaSize); mMatrix.mapRect(rectF); Util.rectFToRect(rectF, rect); } diff --git a/src/com/android/camera/IconListPreference.java b/src/com/android/camera/IconListPreference.java index 6bcd59df1..e5f75d3a5 100644 --- a/src/com/android/camera/IconListPreference.java +++ b/src/com/android/camera/IconListPreference.java @@ -21,6 +21,8 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.util.AttributeSet; +import com.android.gallery3d.R; + import java.util.List; /** A {@code ListPreference} where each entry has a corresponding icon. */ diff --git a/src/com/android/camera/ListPreference.java b/src/com/android/camera/ListPreference.java index 17266ea73..a29b19719 100644 --- a/src/com/android/camera/ListPreference.java +++ b/src/com/android/camera/ListPreference.java @@ -23,6 +23,8 @@ import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; +import com.android.gallery3d.R; + import java.util.ArrayList; import java.util.List; diff --git a/src/com/android/camera/OnScreenHint.java b/src/com/android/camera/OnScreenHint.java index 80063e429..4d7fa7088 100644 --- a/src/com/android/camera/OnScreenHint.java +++ b/src/com/android/camera/OnScreenHint.java @@ -25,6 +25,8 @@ import android.view.View; import android.view.WindowManager; import android.widget.TextView; +import com.android.gallery3d.R; + /** * 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 diff --git a/src/com/android/camera/PanoramaModule.java b/src/com/android/camera/PanoramaModule.java index 6302158e9..4edc68657 100644 --- a/src/com/android/camera/PanoramaModule.java +++ b/src/com/android/camera/PanoramaModule.java @@ -58,6 +58,7 @@ import com.android.camera.ui.LayoutChangeNotifier; import com.android.camera.ui.LayoutNotifyView; import com.android.camera.ui.PopupManager; import com.android.camera.ui.Rotatable; +import com.android.gallery3d.R; import com.android.gallery3d.common.ApiHelper; import com.android.gallery3d.exif.ExifData; import com.android.gallery3d.exif.ExifInvalidFormatException; diff --git a/src/com/android/camera/PhotoController.java b/src/com/android/camera/PhotoController.java index ad8659ee8..c2501c781 100644 --- a/src/com/android/camera/PhotoController.java +++ b/src/com/android/camera/PhotoController.java @@ -27,6 +27,7 @@ import com.android.camera.ui.PieItem; import com.android.camera.ui.PieItem.OnClickListener; import com.android.camera.ui.PieRenderer; import com.android.camera.ui.TimerSettingPopup; +import com.android.gallery3d.R; public class PhotoController extends PieController implements MoreSettingPopup.Listener, diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java index 60cb67896..15f40461d 100644 --- a/src/com/android/camera/PhotoModule.java +++ b/src/com/android/camera/PhotoModule.java @@ -72,6 +72,7 @@ import com.android.camera.ui.Rotatable; import com.android.camera.ui.RotateTextToast; import com.android.camera.ui.TwoStateImageView; import com.android.camera.ui.ZoomRenderer; +import com.android.gallery3d.R; import com.android.gallery3d.common.ApiHelper; import com.android.gallery3d.filtershow.CropExtras; import com.android.gallery3d.filtershow.FilterShowActivity; @@ -1133,7 +1134,15 @@ public class PhotoModule } // Set rotation and gps data. - mJpegRotation = Util.getJpegRotation(mCameraId, mOrientation); + int orientation; + // We need to be consistent with the framework orientation (i.e. the + // orientation of the UI.) when the auto-rotate screen setting is on. + if (mActivity.isAutoRotateScreen()) { + orientation = (360 - mDisplayRotation) % 360; + } else { + orientation = mOrientation; + } + mJpegRotation = Util.getJpegRotation(mCameraId, orientation); mParameters.setRotation(mJpegRotation); Location loc = mLocationManager.getCurrentLocation(); Util.setGpsParameters(mParameters, loc); diff --git a/src/com/android/camera/PreviewFrameLayout.java b/src/com/android/camera/PreviewFrameLayout.java index 451a35ad3..87e3c8dfc 100644 --- a/src/com/android/camera/PreviewFrameLayout.java +++ b/src/com/android/camera/PreviewFrameLayout.java @@ -25,6 +25,7 @@ import android.widget.RelativeLayout; import com.android.camera.ui.LayoutChangeHelper; import com.android.camera.ui.LayoutChangeNotifier; +import com.android.gallery3d.R; import com.android.gallery3d.common.ApiHelper; /** diff --git a/src/com/android/camera/PreviewGestures.java b/src/com/android/camera/PreviewGestures.java index 2dccc3e45..cb7ca09c7 100644 --- a/src/com/android/camera/PreviewGestures.java +++ b/src/com/android/camera/PreviewGestures.java @@ -27,6 +27,7 @@ import android.view.ViewConfiguration; import com.android.camera.ui.PieRenderer; import com.android.camera.ui.RenderOverlay; import com.android.camera.ui.ZoomRenderer; +import com.android.gallery3d.R; import java.util.ArrayList; import java.util.List; diff --git a/src/com/android/camera/RotateDialogController.java b/src/com/android/camera/RotateDialogController.java index 700d35434..5d5e5e70f 100644 --- a/src/com/android/camera/RotateDialogController.java +++ b/src/com/android/camera/RotateDialogController.java @@ -28,6 +28,7 @@ import android.widget.TextView; import com.android.camera.ui.Rotatable; import com.android.camera.ui.RotateLayout; +import com.android.gallery3d.R; public class RotateDialogController implements Rotatable { diff --git a/src/com/android/camera/SoundClips.java b/src/com/android/camera/SoundClips.java index b5e783105..6388fec56 100644 --- a/src/com/android/camera/SoundClips.java +++ b/src/com/android/camera/SoundClips.java @@ -23,6 +23,7 @@ import android.media.MediaActionSound; import android.media.SoundPool; import android.util.Log; +import com.android.gallery3d.R; import com.android.gallery3d.common.ApiHelper; /* diff --git a/src/com/android/camera/Util.java b/src/com/android/camera/Util.java index 2953d6ae7..ed42de85a 100644 --- a/src/com/android/camera/Util.java +++ b/src/com/android/camera/Util.java @@ -52,6 +52,7 @@ import android.view.WindowManager; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; +import com.android.gallery3d.R; import com.android.gallery3d.common.ApiHelper; import java.io.Closeable; diff --git a/src/com/android/camera/VideoController.java b/src/com/android/camera/VideoController.java index d84c1ad1f..3a220773b 100644 --- a/src/com/android/camera/VideoController.java +++ b/src/com/android/camera/VideoController.java @@ -26,6 +26,7 @@ import com.android.camera.ui.PieItem; import com.android.camera.ui.PieItem.OnClickListener; import com.android.camera.ui.PieRenderer; import com.android.camera.ui.TimeIntervalPopup; +import com.android.gallery3d.R; public class VideoController extends PieController implements MoreSettingPopup.Listener, diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java index b7bd20831..4d01bc320 100644 --- a/src/com/android/camera/VideoModule.java +++ b/src/com/android/camera/VideoModule.java @@ -75,6 +75,7 @@ import com.android.camera.ui.RotateLayout; import com.android.camera.ui.RotateTextToast; import com.android.camera.ui.TwoStateImageView; import com.android.camera.ui.ZoomRenderer; +import com.android.gallery3d.R; import com.android.gallery3d.common.ApiHelper; import com.android.gallery3d.util.AccessibilityUtils; diff --git a/src/com/android/camera/ui/AbstractSettingPopup.java b/src/com/android/camera/ui/AbstractSettingPopup.java index 49df77b30..783b6c771 100644 --- a/src/com/android/camera/ui/AbstractSettingPopup.java +++ b/src/com/android/camera/ui/AbstractSettingPopup.java @@ -21,7 +21,7 @@ import android.util.AttributeSet; import android.view.ViewGroup; import android.widget.TextView; -import com.android.camera.R; +import com.android.gallery3d.R; // A popup window that shows one or more camera settings. abstract public class AbstractSettingPopup extends RotateLayout { diff --git a/src/com/android/camera/ui/CameraSwitcher.java b/src/com/android/camera/ui/CameraSwitcher.java index 7b9fb6499..8d2cd7117 100644 --- a/src/com/android/camera/ui/CameraSwitcher.java +++ b/src/com/android/camera/ui/CameraSwitcher.java @@ -32,7 +32,7 @@ import android.view.View.OnTouchListener; import android.view.ViewGroup; import android.widget.LinearLayout; -import com.android.camera.R; +import com.android.gallery3d.R; import com.android.gallery3d.common.ApiHelper; public class CameraSwitcher extends RotateImageView diff --git a/src/com/android/camera/ui/CountDownView.java b/src/com/android/camera/ui/CountDownView.java index ade25c33a..907d33508 100644 --- a/src/com/android/camera/ui/CountDownView.java +++ b/src/com/android/camera/ui/CountDownView.java @@ -31,7 +31,7 @@ import android.view.animation.AnimationUtils; import android.widget.FrameLayout; import android.widget.TextView; -import com.android.camera.R; +import com.android.gallery3d.R; public class CountDownView extends FrameLayout { diff --git a/src/com/android/camera/ui/EffectSettingPopup.java b/src/com/android/camera/ui/EffectSettingPopup.java index 628d8155a..568781a01 100644 --- a/src/com/android/camera/ui/EffectSettingPopup.java +++ b/src/com/android/camera/ui/EffectSettingPopup.java @@ -26,8 +26,8 @@ import android.widget.GridView; import android.widget.SimpleAdapter; import com.android.camera.IconListPreference; -import com.android.camera.R; +import com.android.gallery3d.R; import com.android.gallery3d.common.ApiHelper; import java.util.ArrayList; diff --git a/src/com/android/camera/ui/FaceView.java b/src/com/android/camera/ui/FaceView.java index 9e6f98245..099f3bf4b 100644 --- a/src/com/android/camera/ui/FaceView.java +++ b/src/com/android/camera/ui/FaceView.java @@ -33,8 +33,8 @@ import android.view.View; import com.android.camera.CameraActivity; import com.android.camera.CameraScreenNail; -import com.android.camera.R; import com.android.camera.Util; +import com.android.gallery3d.R; import com.android.gallery3d.common.ApiHelper; @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) diff --git a/src/com/android/camera/ui/InLineSettingCheckBox.java b/src/com/android/camera/ui/InLineSettingCheckBox.java index 5d9cc388d..c1aa5a91c 100644 --- a/src/com/android/camera/ui/InLineSettingCheckBox.java +++ b/src/com/android/camera/ui/InLineSettingCheckBox.java @@ -25,7 +25,7 @@ import android.widget.CompoundButton.OnCheckedChangeListener; import com.android.camera.ListPreference; -import com.android.camera.R; +import com.android.gallery3d.R; /* A check box setting control which turns on/off the setting. */ public class InLineSettingCheckBox extends InLineSettingItem { diff --git a/src/com/android/camera/ui/InLineSettingItem.java b/src/com/android/camera/ui/InLineSettingItem.java index 4f88f2738..839a77fd0 100644 --- a/src/com/android/camera/ui/InLineSettingItem.java +++ b/src/com/android/camera/ui/InLineSettingItem.java @@ -23,7 +23,7 @@ import android.widget.LinearLayout; import android.widget.TextView; import com.android.camera.ListPreference; -import com.android.camera.R; +import com.android.gallery3d.R; /** * A one-line camera setting could be one of three types: knob, switch or restore diff --git a/src/com/android/camera/ui/InLineSettingMenu.java b/src/com/android/camera/ui/InLineSettingMenu.java index 2fe89349a..8e45c3e38 100644 --- a/src/com/android/camera/ui/InLineSettingMenu.java +++ b/src/com/android/camera/ui/InLineSettingMenu.java @@ -23,7 +23,7 @@ import android.view.accessibility.AccessibilityEvent; import android.widget.TextView; import com.android.camera.ListPreference; -import com.android.camera.R; +import com.android.gallery3d.R; /* Setting menu item that will bring up a menu when you click on it. */ public class InLineSettingMenu extends InLineSettingItem { diff --git a/src/com/android/camera/ui/ListPrefSettingPopup.java b/src/com/android/camera/ui/ListPrefSettingPopup.java index c0411c90d..cfef73f49 100644 --- a/src/com/android/camera/ui/ListPrefSettingPopup.java +++ b/src/com/android/camera/ui/ListPrefSettingPopup.java @@ -27,7 +27,7 @@ import android.widget.SimpleAdapter; import com.android.camera.IconListPreference; import com.android.camera.ListPreference; -import com.android.camera.R; +import com.android.gallery3d.R; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/com/android/camera/ui/MoreSettingPopup.java b/src/com/android/camera/ui/MoreSettingPopup.java index ab1babaab..5900058df 100644 --- a/src/com/android/camera/ui/MoreSettingPopup.java +++ b/src/com/android/camera/ui/MoreSettingPopup.java @@ -28,7 +28,7 @@ import android.widget.ListView; import com.android.camera.ListPreference; import com.android.camera.PreferenceGroup; -import com.android.camera.R; +import com.android.gallery3d.R; import java.util.ArrayList; diff --git a/src/com/android/camera/ui/PieRenderer.java b/src/com/android/camera/ui/PieRenderer.java index b592508e1..95597b2a1 100644 --- a/src/com/android/camera/ui/PieRenderer.java +++ b/src/com/android/camera/ui/PieRenderer.java @@ -36,7 +36,7 @@ import android.view.animation.Animation.AnimationListener; import android.view.animation.LinearInterpolator; import android.view.animation.Transformation; -import com.android.camera.R; +import com.android.gallery3d.R; import com.android.gallery3d.common.ApiHelper; import java.util.ArrayList; @@ -717,7 +717,7 @@ public class PieRenderer extends OverlayRenderer private void cancelFocus() { mFocusCancelled = true; mOverlay.removeCallbacks(mDisappear); - if (mAnimation != null) { + if (mAnimation != null && !mAnimation.hasEnded()) { mAnimation.cancel(); } mFocusCancelled = false; diff --git a/src/com/android/camera/ui/RotateTextToast.java b/src/com/android/camera/ui/RotateTextToast.java index f73c03362..c78a258b0 100644 --- a/src/com/android/camera/ui/RotateTextToast.java +++ b/src/com/android/camera/ui/RotateTextToast.java @@ -23,8 +23,8 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; -import com.android.camera.R; import com.android.camera.Util; +import com.android.gallery3d.R; public class RotateTextToast { private static final int TOAST_DURATION = 5000; // milliseconds diff --git a/src/com/android/camera/ui/Switch.java b/src/com/android/camera/ui/Switch.java index 5b1ab4c97..ac21758a7 100644 --- a/src/com/android/camera/ui/Switch.java +++ b/src/com/android/camera/ui/Switch.java @@ -42,7 +42,7 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.CompoundButton; -import com.android.camera.R; +import com.android.gallery3d.R; import com.android.gallery3d.common.ApiHelper; import java.util.Arrays; diff --git a/src/com/android/camera/ui/TimeIntervalPopup.java b/src/com/android/camera/ui/TimeIntervalPopup.java index b79663be2..18ad9f5da 100644 --- a/src/com/android/camera/ui/TimeIntervalPopup.java +++ b/src/com/android/camera/ui/TimeIntervalPopup.java @@ -28,7 +28,7 @@ import android.widget.TextView; import com.android.camera.IconListPreference; import com.android.camera.ListPreference; -import com.android.camera.R; +import com.android.gallery3d.R; /** * This is a popup window that allows users to turn on/off time lapse feature, diff --git a/src/com/android/camera/ui/TimerSettingPopup.java b/src/com/android/camera/ui/TimerSettingPopup.java index 06d7e4e50..983c7f21b 100644 --- a/src/com/android/camera/ui/TimerSettingPopup.java +++ b/src/com/android/camera/ui/TimerSettingPopup.java @@ -30,7 +30,7 @@ import android.widget.Switch; import android.widget.TextView; import com.android.camera.ListPreference; -import com.android.camera.R; +import com.android.gallery3d.R; /** * This is a popup window that allows users to turn on/off time lapse feature, diff --git a/src/com/android/camera/ui/ZoomRenderer.java b/src/com/android/camera/ui/ZoomRenderer.java index 10c5e80d4..86b82b459 100644 --- a/src/com/android/camera/ui/ZoomRenderer.java +++ b/src/com/android/camera/ui/ZoomRenderer.java @@ -24,7 +24,7 @@ import android.graphics.Paint; import android.graphics.Rect; import android.view.ScaleGestureDetector; -import com.android.camera.R; +import com.android.gallery3d.R; public class ZoomRenderer extends OverlayRenderer implements ScaleGestureDetector.OnScaleGestureListener { diff --git a/src/com/android/gallery3d/filtershow/FilterShowActivity.java b/src/com/android/gallery3d/filtershow/FilterShowActivity.java index 0660eda30..f7147eac1 100644 --- a/src/com/android/gallery3d/filtershow/FilterShowActivity.java +++ b/src/com/android/gallery3d/filtershow/FilterShowActivity.java @@ -88,6 +88,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, // fields for supporting crop action public static final String CROP_ACTION = "com.android.camera.action.CROP"; private CropExtras mCropExtras = null; + private String mAction = ""; MasterImage mMasterImage = null; public static final String TINY_PLANET_ACTION = "com.android.camera.action.TINY_PLANET"; @@ -146,6 +147,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, private FilterIconButton mNullBorderFilter; private int mIconSeedSize = 140; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -308,6 +310,8 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); } + mAction = intent.getAction(); + if (intent.getData() != null) { startLoadBitmap(intent.getData()); } else { @@ -315,8 +319,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, } // Handle behavior for various actions - String action = intent.getAction(); - if (action.equalsIgnoreCase(CROP_ACTION)) { + if (mAction.equalsIgnoreCase(CROP_ACTION)) { Bundle extras = intent.getExtras(); if (extras != null) { mCropExtras = new CropExtras(extras.getInt(CropExtras.KEY_OUTPUT_X, 0), @@ -345,9 +348,6 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, mPanelController.setFixedAspect(mCropExtras.getAspectX() > 0 && mCropExtras.getAspectY() > 0); } - mPanelController.showComponent(findViewById(R.id.cropButton)); - } else if (action.equalsIgnoreCase(TINY_PLANET_ACTION)) { - mPanelController.showComponent(findViewById(R.id.tinyplanetButton)); } } @@ -502,6 +502,13 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, } mLoadBitmapTask = null; + + if (mAction == CROP_ACTION) { + mPanelController.showComponent(findViewById(R.id.cropButton)); + } else if (mAction == TINY_PLANET_ACTION) { + mPanelController.showComponent(findViewById(R.id.tinyplanetButton)); + } + super.onPostExecute(result); } @@ -1037,31 +1044,31 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, private boolean mSaveToExtraUri = false; private boolean mSaveAsWallpaper = false; private boolean mReturnAsExtra = false; - private boolean outputted = false; + private boolean mOutputted = false; public void saveImage() { if (mCropExtras != null) { if (mCropExtras.getExtraOutput() != null) { mSaveToExtraUri = true; - outputted = true; + mOutputted = true; } if (mCropExtras.getSetAsWallpaper()) { mSaveAsWallpaper = true; - outputted = true; + mOutputted = true; } if (mCropExtras.getReturnData()) { mReturnAsExtra = true; - outputted = true; + mOutputted = true; } - if (outputted) { + if (mOutputted) { mImageShow.getImagePreset().mGeoData.setUseCropExtrasFlag(true); showSavingProgress(null); mImageShow.returnFilteredResult(this); } } - if (!outputted) { + if (!mOutputted) { if (mImageShow.hasModifications()) { // Get the name of the album, to which the image will be saved File saveDir = SaveCopyTask.getFinalSaveDirectory(this, mImageLoader.getUri()); @@ -1111,7 +1118,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, } public void done() { - if (outputted) { + if (mOutputted) { hideSavingProgress(); } finish(); diff --git a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java index 00fcf4e19..698992ac9 100644 --- a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java +++ b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java @@ -456,6 +456,7 @@ public class ImageLoader { Log.w(LOGTAG, "Failed to save image!"); return null; } + bitmap = param.applyGeometry(bitmap); return param.apply(bitmap); } }; diff --git a/src/com/android/gallery3d/filtershow/editors/EditorVignette.java b/src/com/android/gallery3d/filtershow/editors/EditorVignette.java new file mode 100644 index 000000000..a60c1681e --- /dev/null +++ b/src/com/android/gallery3d/filtershow/editors/EditorVignette.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2012 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.gallery3d.filtershow.editors; + +import android.content.Context; +import android.util.Log; +import android.widget.FrameLayout; + +import com.android.gallery3d.R; +import com.android.gallery3d.filtershow.filters.FilterRepresentation; +import com.android.gallery3d.filtershow.filters.FilterTinyPlanetRepresentation; +import com.android.gallery3d.filtershow.filters.FilterVignetteRepresentation; +import com.android.gallery3d.filtershow.imageshow.ImageVignette; + +public class EditorVignette extends BasicEditor { + public static final int ID = R.id.vignetteEditor; + private static final String LOGTAG = "EditorVignettePlanet"; + ImageVignette mImageVignette; + + public EditorVignette() { + super(ID, R.layout.filtershow_vignette_editor, R.id.imageVignette); + } + + @Override + public void createEditor(Context context, FrameLayout frameLayout) { + super.createEditor(context, frameLayout); + mImageVignette = (ImageVignette) mImageShow; + mImageVignette.setEditor(this); + } + + @Override + public void reflectCurrentFilter() { + super.reflectCurrentFilter(); + + FilterRepresentation rep = getLocalRepresentation(); + if (rep != null && getLocalRepresentation() instanceof FilterVignetteRepresentation) { + FilterVignetteRepresentation drawRep = (FilterVignetteRepresentation) rep; + mImageVignette.setRepresentation(drawRep); + } + } +} diff --git a/src/com/android/gallery3d/filtershow/filters/FilterPoint.java b/src/com/android/gallery3d/filtershow/filters/FilterPoint.java new file mode 100644 index 000000000..4520717a1 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/filters/FilterPoint.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2013 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.gallery3d.filtershow.filters; + +public interface FilterPoint { + +} diff --git a/src/com/android/gallery3d/filtershow/filters/FilterPointRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterPointRepresentation.java new file mode 100644 index 000000000..fc01650ae --- /dev/null +++ b/src/com/android/gallery3d/filtershow/filters/FilterPointRepresentation.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2013 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.gallery3d.filtershow.filters; + +import java.util.Vector; + +public abstract class FilterPointRepresentation extends FilterRepresentation { + private static final String LOGTAG = "FilterPointRepresentation"; + private Vector<FilterPoint> mCandidates = new Vector<FilterPoint>(); + + public FilterPointRepresentation(String type, int textid, int editorID) { + super(type); + setFilterClass(ImageFilterRedEye.class); + setPriority(FilterRepresentation.TYPE_NORMAL); + setTextId(textid); + setEditorId(editorID); + } + + @Override + public FilterRepresentation clone() throws CloneNotSupportedException { + FilterPointRepresentation representation = (FilterPointRepresentation) super + .clone(); + representation.mCandidates = (Vector<FilterPoint>) mCandidates.clone(); + return representation; + } + + public boolean hasCandidates() { + return mCandidates != null; + } + + public Vector<FilterPoint> getCandidates() { + return mCandidates; + } + + @Override + public boolean isNil() { + if (getCandidates() != null && getCandidates().size() > 0) { + return false; + } + return true; + } + + public Object getCandidate(int index) { + return this.mCandidates.get(index); + } + + public void addCandidate(FilterPoint c) { + this.mCandidates.add(c); + } + + @Override + public void useParametersFrom(FilterRepresentation a) { + if (a instanceof FilterPointRepresentation) { + FilterPointRepresentation representation = (FilterPointRepresentation) a; + mCandidates.clear(); + for (FilterPoint redEyeCandidate : representation.mCandidates) { + mCandidates.add(redEyeCandidate); + } + } + } + + public void removeCandidate(RedEyeCandidate c) { + this.mCandidates.remove(c); + } + + public void clearCandidates() { + this.mCandidates.clear(); + } + + public int getNumberOfCandidates() { + return mCandidates.size(); + } +} diff --git a/src/com/android/gallery3d/filtershow/filters/FilterRedEyeRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterRedEyeRepresentation.java index 7779211df..70d016f69 100644 --- a/src/com/android/gallery3d/filtershow/filters/FilterRedEyeRepresentation.java +++ b/src/com/android/gallery3d/filtershow/filters/FilterRedEyeRepresentation.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2013 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. @@ -24,80 +24,19 @@ import com.android.gallery3d.filtershow.editors.EditorRedEye; import java.util.Vector; -public class FilterRedEyeRepresentation extends FilterRepresentation { +public class FilterRedEyeRepresentation extends FilterPointRepresentation { private static final String LOGTAG = "FilterRedEyeRepresentation"; - private Vector<RedEyeCandidate> mCandidates = new Vector<RedEyeCandidate>(); public FilterRedEyeRepresentation() { - super("RedEye"); + super("RedEye",R.string.redeye,EditorRedEye.ID); setFilterClass(ImageFilterRedEye.class); - setPriority(FilterRepresentation.TYPE_NORMAL); - setTextId(R.string.redeye); - setEditorId(EditorRedEye.ID); setOverlayId(R.drawable.photoeditor_effect_redeye); } - @Override - public FilterRepresentation clone() throws CloneNotSupportedException { - FilterRedEyeRepresentation representation = (FilterRedEyeRepresentation) super - .clone(); - representation.mCandidates = (Vector<RedEyeCandidate>) mCandidates.clone(); - return representation; - } - - public boolean hasCandidates() { - return mCandidates != null; - } - - public Vector<RedEyeCandidate> getCandidates() { - return mCandidates; - } - - public void setCandidates(Vector<RedEyeCandidate> mCandidates) { - this.mCandidates = mCandidates; - } - - public RedEyeCandidate getCandidate(int index) { - return this.mCandidates.get(index); - } - - public void addCandidate(RedEyeCandidate c) { - this.mCandidates.add(c); - } - - @Override - public void useParametersFrom(FilterRepresentation a) { - if (a instanceof FilterRedEyeRepresentation) { - FilterRedEyeRepresentation representation = (FilterRedEyeRepresentation) a; - mCandidates.clear(); - for (RedEyeCandidate redEyeCandidate : representation.mCandidates) { - mCandidates.add(redEyeCandidate); - } - } - } - - public void removeCandidate(RedEyeCandidate c) { - this.mCandidates.remove(c); - } - - public void clearCandidates() { - this.mCandidates.clear(); - } - - public int getNumberOfCandidates() { - if (mCandidates == null) { - return 0; - } - return mCandidates.size(); - } - public void addRect(RectF rect, RectF bounds) { - if (!hasCandidates()) { - setCandidates(new Vector<RedEyeCandidate>()); - } Vector<RedEyeCandidate> intersects = new Vector<RedEyeCandidate>(); for (int i = 0; i < getCandidates().size(); i++) { - RedEyeCandidate r = getCandidate(i); + RedEyeCandidate r = (RedEyeCandidate) getCandidate(i); if (r.intersect(rect)) { intersects.add(r); } diff --git a/src/com/android/gallery3d/filtershow/filters/FilterVignetteRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterVignetteRepresentation.java new file mode 100644 index 000000000..eef54ef58 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/filters/FilterVignetteRepresentation.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2012 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.gallery3d.filtershow.filters; + +import com.android.gallery3d.R; +import com.android.gallery3d.filtershow.editors.EditorVignette; +import com.android.gallery3d.filtershow.imageshow.Oval; + +public class FilterVignetteRepresentation extends FilterBasicRepresentation implements Oval { + private static final String LOGTAG = "FilterVignetteRepresentation"; + private float mCenterX = Float.NaN; + private float mCenterY; + private float mRadiusX = Float.NaN; + private float mRadiusY; + + public FilterVignetteRepresentation() { + super("Vignette", -100, 50, 100); + setShowParameterValue(true); + setPriority(FilterRepresentation.TYPE_VIGNETTE); + setTextId(R.string.vignette); + setButtonId(R.id.vignetteEditor); + setEditorId(EditorVignette.ID); + setName("Vignette"); + setFilterClass(ImageFilterVignette.class); + + setMinimum(-100); + setMaximum(100); + setDefaultValue(0); + } + + @Override + public void useParametersFrom(FilterRepresentation a) { + super.useParametersFrom(a); + mCenterX = ((FilterVignetteRepresentation) a).mCenterX; + mCenterY = ((FilterVignetteRepresentation) a).mCenterY; + mRadiusX = ((FilterVignetteRepresentation) a).mRadiusX; + mRadiusY = ((FilterVignetteRepresentation) a).mRadiusY; + } + + @Override + public FilterRepresentation clone() throws CloneNotSupportedException { + FilterVignetteRepresentation representation = (FilterVignetteRepresentation) super + .clone(); + representation.mCenterX = mCenterX; + representation.mCenterY = mCenterY; + + return representation; + } + + @Override + public void setCenter(float centerX, float centerY) { + mCenterX = centerX; + mCenterY = centerY; + } + + @Override + public float getCenterX() { + return mCenterX; + } + + @Override + public float getCenterY() { + return mCenterY; + } + + @Override + public void setRadius(float radiusX, float radiusY) { + mRadiusX = radiusX; + mRadiusY = radiusY; + } + + @Override + public void setRadiusX(float radiusX) { + mRadiusX = radiusX; + } + + @Override + public void setRadiusY(float radiusY) { + mRadiusY = radiusY; + } + + @Override + public float getRadiusX() { + return mRadiusX; + } + + @Override + public float getRadiusY() { + return mRadiusY; + } + + public boolean isCenterSet() { + return mCenterX != Float.NaN; + } + + @Override + public boolean isNil() { + return getValue() == 0; + } +} diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilter.java b/src/com/android/gallery3d/filtershow/filters/ImageFilter.java index 7c5a75232..614c6a01d 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilter.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilter.java @@ -17,9 +17,11 @@ package com.android.gallery3d.filtershow.filters; import android.graphics.Bitmap; +import android.graphics.Matrix; import com.android.gallery3d.R; import com.android.gallery3d.filtershow.editors.BasicEditor; +import com.android.gallery3d.filtershow.imageshow.GeometryMetadata; import com.android.gallery3d.filtershow.presets.ImagePreset; public abstract class ImageFilter implements Cloneable { @@ -67,4 +69,12 @@ public abstract class ImageFilter implements Cloneable { return null; } + protected Matrix getOriginalToScreenMatrix(int w, int h) { + GeometryMetadata geo = getImagePreset().mGeoData; + Matrix originalToScreen = geo.getOriginalToScreen(true, + getImagePreset().getImageLoader().getOriginalBounds().width(), + getImagePreset().getImageLoader().getOriginalBounds().height(), + w, h); + return originalToScreen; + } } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterDraw.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterDraw.java index 4b21d3595..6d7614ec9 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterDraw.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterDraw.java @@ -267,11 +267,8 @@ public class ImageFilterDraw extends ImageFilter { public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { int w = bitmap.getWidth(); int h = bitmap.getHeight(); - ImagePreset imgPreset = getImagePreset(); - Rect bounds = imgPreset.getImageLoader().getOriginalBounds(); - Matrix m = imgPreset.mGeoData.getOriginalToScreen(true, - bounds.width(), - bounds.height(), w, h); + + Matrix m = getOriginalToScreenMatrix(w, h); drawData(new Canvas(bitmap), m, quality); return bitmap; } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterRedEye.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterRedEye.java index 42587c06f..511f9e90f 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterRedEye.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterRedEye.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2013 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. @@ -17,14 +17,8 @@ package com.android.gallery3d.filtershow.filters; import android.graphics.Bitmap; -import android.graphics.Canvas; import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics.Rect; import android.graphics.RectF; -import android.util.Log; - -import com.android.gallery3d.filtershow.imageshow.GeometryMetadata; import java.util.Vector; @@ -42,23 +36,14 @@ public class ImageFilterRedEye extends ImageFilter { } public boolean isNil() { - if (mParameters.getCandidates() != null && mParameters.getCandidates().size() > 0) { - return false; - } - return true; + return mParameters.isNil(); } - public Vector<RedEyeCandidate> getCandidates() { - if (!mParameters.hasCandidates()) { - mParameters.setCandidates(new Vector<RedEyeCandidate>()); - } + public Vector<FilterPoint> getCandidates() { return mParameters.getCandidates(); } public void clear() { - if (!mParameters.hasCandidates()) { - mParameters.setCandidates(new Vector<RedEyeCandidate>()); - } mParameters.clearCandidates(); } @@ -75,46 +60,19 @@ public class ImageFilterRedEye extends ImageFilter { int w = bitmap.getWidth(); int h = bitmap.getHeight(); short[] rect = new short[4]; + int size = mParameters.getNumberOfCandidates(); + Matrix originalToScreen = getOriginalToScreenMatrix(w, h); for (int i = 0; i < size; i++) { - RectF r = new RectF(mParameters.getCandidate(i).mRect); - GeometryMetadata geo = getImagePreset().mGeoData; - Matrix originalToScreen = geo.getOriginalToScreen(true, - getImagePreset().getImageLoader().getOriginalBounds().width(), - getImagePreset().getImageLoader().getOriginalBounds().height(), - w, h); + RectF r = new RectF(((RedEyeCandidate) (mParameters.getCandidate(i))).mRect); originalToScreen.mapRect(r); - if (r.left < 0) { - r.left = 0; - } - if (r.left > w) { - r.left = w; - } - if (r.top < 0) { - r.top = 0; + if (r.intersect(0, 0, w, h)) { + rect[0] = (short) r.left; + rect[1] = (short) r.top; + rect[2] = (short) r.width(); + rect[3] = (short) r.height(); + nativeApplyFilter(bitmap, w, h, rect); } - if (r.top > h) { - r.top = h; - } - if (r.right < 0) { - r.right = 0; - } - if (r.right > w) { - r.right = w; - } - if (r.bottom < 0) { - r.bottom = 0; - } - if (r.bottom > h) { - r.bottom = h; - } - - rect[0] = (short) r.left; - rect[1] = (short) r.top; - rect[2] = (short) r.width(); - rect[3] = (short) r.height(); - - nativeApplyFilter(bitmap, w, h, rect); } return bitmap; } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java index 465d90bfd..9ff737e32 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java @@ -16,47 +16,67 @@ package com.android.gallery3d.filtershow.filters; -import com.android.gallery3d.R; - import android.graphics.Bitmap; +import android.graphics.Matrix; + import com.android.gallery3d.app.Log; public class ImageFilterVignette extends SimpleImageFilter { - private static final String LOGTAG = "ImageFilterVignette"; public ImageFilterVignette() { mName = "Vignette"; } + @Override public FilterRepresentation getDefaultRepresentation() { - FilterBasicRepresentation representation = - (FilterBasicRepresentation) super.getDefaultRepresentation(); - representation.setName("Vignette"); - representation.setFilterClass(ImageFilterVignette.class); - representation.setPriority(FilterRepresentation.TYPE_VIGNETTE); - representation.setTextId(R.string.vignette); - representation.setButtonId(R.id.vignetteButton); - - representation.setMinimum(-100); - representation.setMaximum(100); - representation.setDefaultValue(0); - + FilterVignetteRepresentation representation = new FilterVignetteRepresentation(); return representation; } - native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, float strength); + native protected void nativeApplyFilter( + Bitmap bitmap, int w, int h, int cx, int cy, float radx, float rady, float strength); + + private float calcRadius(float cx, float cy, int w, int h) { + float d = cx; + if (d < (w - cx)) { + d = w - cx; + } + if (d < cy) { + d = cy; + } + if (d < (h - cy)) { + d = h - cy; + } + return d * d * 2.0f; + } @Override public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { - if (getParameters() == null) { + FilterVignetteRepresentation rep = (FilterVignetteRepresentation) getParameters(); + if (rep == null) { return bitmap; } int w = bitmap.getWidth(); int h = bitmap.getHeight(); - float value = getParameters().getValue() / 100.0f; - nativeApplyFilter(bitmap, w, h, value); - + float value = rep.getValue() / 100.0f; + float cx = w / 2; + float cy = h / 2; + float r = calcRadius(cx, cy, w, h); + float rx = r; + float ry = r; + if (rep.isCenterSet()) { + Matrix m = getOriginalToScreenMatrix(w, h); + cx = rep.getCenterX(); + cy = rep.getCenterY(); + float[] center = new float[] { cx, cy }; + m.mapPoints(center); + cx = center[0]; + cy = center[1]; + rx = m.mapRadius(rep.getRadiusX()); + ry = m.mapRadius(rep.getRadiusY()); + } + nativeApplyFilter(bitmap, w, h, (int) cx, (int) cy, rx, ry, value); return bitmap; } } diff --git a/src/com/android/gallery3d/filtershow/filters/RedEyeCandidate.java b/src/com/android/gallery3d/filtershow/filters/RedEyeCandidate.java index 58d3afa3b..a40d4fa3b 100644 --- a/src/com/android/gallery3d/filtershow/filters/RedEyeCandidate.java +++ b/src/com/android/gallery3d/filtershow/filters/RedEyeCandidate.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2013 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. @@ -18,7 +18,7 @@ package com.android.gallery3d.filtershow.filters; import android.graphics.RectF; -public class RedEyeCandidate { +public class RedEyeCandidate implements FilterPoint { RectF mRect = new RectF(); RectF mBounds = new RectF(); diff --git a/src/com/android/gallery3d/filtershow/imageshow/EclipseControl.java b/src/com/android/gallery3d/filtershow/imageshow/EclipseControl.java new file mode 100644 index 000000000..b4ca8e1f1 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/imageshow/EclipseControl.java @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2012 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.gallery3d.filtershow.imageshow; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.RadialGradient; +import android.graphics.RectF; +import android.graphics.Shader; + +import com.android.gallery3d.R; + +public class EclipseControl { + private float mCenterX = Float.NaN; + private float mCenterY = 0; + private float mRadiusX = 200; + private float mRadiusY = 300; + private static int MIN_TOUCH_DIST = 80;// should be a resource & in dips + + private float[] handlex = new float[9]; + private float[] handley = new float[9]; + private int mSliderColor; + private int mCenterDotSize = 40; + private float mDownX; + private float mDownY; + private float mDownCenterX; + private float mDownCenterY; + private float mDownRadiusX; + private float mDownRadiusY; + private Matrix mScrToImg; + + private final static int HAN_CENTER = 0; + private final static int HAN_NORTH = 7; + private final static int HAN_NE = 8; + private final static int HAN_EAST = 1; + private final static int HAN_SE = 2; + private final static int HAN_SOUTH = 3; + private final static int HAN_SW = 4; + private final static int HAN_WEST = 5; + private final static int HAN_NW = 6; + + public EclipseControl(Context context) { + mSliderColor = context.getResources().getColor(R.color.slider_line_color); + } + + public void setRadius(float x, float y) { + mRadiusX = x; + mRadiusY = y; + } + + public void setCenter(float x, float y) { + mCenterX = x; + mCenterY = y; + } + + public int getCloseHandle(float x, float y) { + float min = Float.MAX_VALUE; + int handle = -1; + for (int i = 0; i < handlex.length; i++) { + float dx = handlex[i] - x; + float dy = handley[i] - y; + float dist = dx * dx + dy * dy; + if (dist < min) { + min = dist; + handle = i; + } + } + + if (min < MIN_TOUCH_DIST * MIN_TOUCH_DIST) { + return handle; + } + for (int i = 0; i < handlex.length; i++) { + float dx = handlex[i] - x; + float dy = handley[i] - y; + float dist = (float) Math.sqrt(dx * dx + dy * dy); + } + + return -1; + } + + public void setScrToImageMatrix(Matrix scrToImg) { + mScrToImg = scrToImg; + } + + public void actionDown(float x, float y, Oval oval) { + float[] point = new float[] { + x, y }; + mScrToImg.mapPoints(point); + mDownX = point[0]; + mDownY = point[1]; + mDownCenterX = oval.getCenterX(); + mDownCenterY = oval.getCenterY(); + mDownRadiusX = oval.getRadiusX(); + mDownRadiusY = oval.getRadiusY(); + } + + public void actionMove(int handle, float x, float y, Oval oval) { + float[] point = new float[] { + x, y }; + mScrToImg.mapPoints(point); + x = point[0]; + y = point[1]; + int sign = 1; + switch (handle) { + case HAN_CENTER: + float ctrdx = mDownX - mDownCenterX; + float ctrdy = mDownY - mDownCenterY; + oval.setCenter(x - ctrdx, y - ctrdy); + // setRepresentation(mVignetteRep); + break; + case HAN_NORTH: + sign = -1; + case HAN_SOUTH: + float raddy = mDownRadiusY - Math.abs(mDownY - mDownCenterY); + oval.setRadiusY(Math.abs(y - oval.getCenterY() + sign * raddy)); + break; + case HAN_EAST: + sign = -1; + case HAN_WEST: + float raddx = mDownRadiusX - Math.abs(mDownX - mDownCenterX); + oval.setRadiusX(Math.abs(x - oval.getCenterX() - sign * raddx)); + break; + case HAN_SE: + case HAN_NE: + case HAN_SW: + case HAN_NW: + float sin45 = (float) Math.sin(45); + float dr = (mDownRadiusX + mDownRadiusY) * sin45; + float ctr_dx = mDownX - mDownCenterX; + float ctr_dy = mDownY - mDownCenterY; + float downRad = Math.abs(ctr_dx) + Math.abs(ctr_dy) - dr; + float rx = oval.getRadiusX(); + float ry = oval.getRadiusY(); + float r = (Math.abs(rx) + Math.abs(ry)) * sin45; + float dx = x - oval.getCenterX(); + float dy = y - oval.getCenterY(); + float nr = Math.abs(Math.abs(dx) + Math.abs(dy) - downRad); + oval.setRadius(rx * nr / r, ry * nr / r); + + break; + } + } + + void paintPoint(Canvas canvas, float x, float y) { + if (x == Float.NaN) { + return; + } + + Paint paint = new Paint(); + + paint.setStyle(Paint.Style.FILL); + paint.setColor(Color.BLUE); + int[] colors3 = new int[] { + mSliderColor, mSliderColor, 0x66000000, 0 }; + RadialGradient g = new RadialGradient(x, y, mCenterDotSize, colors3, new float[] { + 0, .3f, .31f, 1 }, Shader.TileMode.CLAMP); + paint.setShader(g); + canvas.drawCircle(x, y, mCenterDotSize, paint); + } + + void paintRadius(Canvas canvas, float cx, float cy, float rx, float ry) { + if (cx == Float.NaN) { + return; + } + int mSliderColor = 0xFF33B5E5; + Paint paint = new Paint(); + RectF rect = new RectF(cx - rx, cy - ry, cx + rx, cy + ry); + paint.setAntiAlias(true); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(6); + paint.setColor(Color.BLACK); + paintOvallines(canvas, rect, paint, cx, cy, rx, ry); + + paint.setStrokeWidth(3); + paint.setColor(Color.WHITE); + paintOvallines(canvas, rect, paint, cx, cy, rx, ry); + } + + public void paintOvallines( + Canvas canvas, RectF rect, Paint paint, float cx, float cy, float rx, float ry) { + canvas.drawOval(rect, paint); + float da = 4; + float arclen = da + da; + for (int i = 0; i < 361; i += 90) { + float dx = rx + 10; + float dy = ry + 10; + rect.left = cx - dx; + rect.top = cy - dy; + rect.right = cx + dx; + rect.bottom = cy + dy; + canvas.drawArc(rect, i - da, arclen, false, paint); + dx = rx - 10; + dy = ry - 10; + rect.left = cx - dx; + rect.top = cy - dy; + rect.right = cx + dx; + rect.bottom = cy + dy; + canvas.drawArc(rect, i - da, arclen, false, paint); + } + da *= 2; + for (int i = 45; i < 361; i += 90) { + double angle = Math.PI * i / 180.; + float x = cx + (float) (rx * Math.cos(angle)); + float y = cy + (float) (ry * Math.sin(angle)); + canvas.drawRect(x - da, y - da, x + da, y + da, paint); + } + rect.left = cx - rx; + rect.top = cy - ry; + rect.right = cx + rx; + rect.bottom = cy + ry; + } + + public void fillHandles(Canvas canvas, float cx, float cy, float rx, float ry) { + handlex[0] = cx; + handley[0] = cy; + int k = 1; + + for (int i = 0; i < 360; i += 45) { + double angle = Math.PI * i / 180.; + + float x = cx + (float) (rx * Math.cos(angle)); + float y = cy + (float) (ry * Math.sin(angle)); + handlex[k] = x; + handley[k] = y; + + k++; + } + } + + public void draw(Canvas canvas) { + paintRadius(canvas, mCenterX, mCenterY, mRadiusX, mRadiusY); + fillHandles(canvas, mCenterX, mCenterY, mRadiusX, mRadiusY); + paintPoint(canvas, mCenterX, mCenterY); + } + + public boolean isUndefined() { + return Float.isNaN(mCenterX); + } +} diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImagePoint.java b/src/com/android/gallery3d/filtershow/imageshow/ImagePoint.java new file mode 100644 index 000000000..625cdbe0d --- /dev/null +++ b/src/com/android/gallery3d/filtershow/imageshow/ImagePoint.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2013 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.gallery3d.filtershow.imageshow; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.util.Log; +import android.view.MotionEvent; + +import com.android.gallery3d.filtershow.editors.EditorRedEye; +import com.android.gallery3d.filtershow.filters.FilterPoint; +import com.android.gallery3d.filtershow.filters.FilterRedEyeRepresentation; +import com.android.gallery3d.filtershow.filters.ImageFilterRedEye; +import com.android.gallery3d.filtershow.filters.RedEyeCandidate; + +public abstract class ImagePoint extends ImageShow { + + private static final String LOGTAG = "ImageRedEyes"; + protected EditorRedEye mEditorRedEye; + protected FilterRedEyeRepresentation mRedEyeRep; + protected static float mTouchPadding = 80; + + public static void setTouchPadding(float padding) { + mTouchPadding = padding; + } + + public ImagePoint(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public ImagePoint(Context context) { + super(context); + } + + @Override + public void resetParameter() { + ImageFilterRedEye filter = (ImageFilterRedEye) getCurrentFilter(); + if (filter != null) { + filter.clear(); + } + invalidate(); + } + + @Override + public void updateImage() { + super.updateImage(); + invalidate(); + } + + @Override + public void onDraw(Canvas canvas) { + super.onDraw(canvas); + Paint paint = new Paint(); + paint.setStyle(Style.STROKE); + paint.setColor(Color.RED); + paint.setStrokeWidth(2); + + Matrix originalToScreen = getImageToScreenMatrix(false); + Matrix originalRotateToScreen = getImageToScreenMatrix(true); + + if (mRedEyeRep != null) { + for (FilterPoint candidate : mRedEyeRep.getCandidates()) { + drawPoint(candidate, canvas, originalToScreen, originalRotateToScreen, paint); + } + } + } + + protected abstract void drawPoint( + FilterPoint candidate, Canvas canvas, Matrix originalToScreen, + Matrix originalRotateToScreen, Paint paint); + + public void setEditor(EditorRedEye editorRedEye) { + mEditorRedEye = editorRedEye; + } + + public void setRepresentation(FilterRedEyeRepresentation redEyeRep) { + mRedEyeRep = redEyeRep; + } +} diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageRedEye.java b/src/com/android/gallery3d/filtershow/imageshow/ImageRedEye.java index ba3dcdd1d..9e667986e 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageRedEye.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageRedEye.java @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2013 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.gallery3d.filtershow.imageshow; @@ -8,30 +23,14 @@ import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.RectF; -import android.util.AttributeSet; -import android.util.Log; import android.view.MotionEvent; -import com.android.gallery3d.filtershow.editors.EditorRedEye; -import com.android.gallery3d.filtershow.filters.FilterRedEyeRepresentation; -import com.android.gallery3d.filtershow.filters.ImageFilterRedEye; +import com.android.gallery3d.filtershow.filters.FilterPoint; import com.android.gallery3d.filtershow.filters.RedEyeCandidate; -public class ImageRedEye extends ImageShow { - +public class ImageRedEye extends ImagePoint { private static final String LOGTAG = "ImageRedEyes"; private RectF mCurrentRect = null; - private EditorRedEye mEditorRedEye; - private FilterRedEyeRepresentation mRedEyeRep; - private static float mTouchPadding = 80; - - public static void setTouchPadding(float padding) { - mTouchPadding = padding; - } - - public ImageRedEye(Context context, AttributeSet attrs) { - super(context, attrs); - } public ImageRedEye(Context context) { super(context); @@ -39,21 +38,12 @@ public class ImageRedEye extends ImageShow { @Override public void resetParameter() { - ImageFilterRedEye filter = (ImageFilterRedEye) getCurrentFilter(); - if (filter != null) { - filter.clear(); - } - mCurrentRect = null; + super.resetParameter(); invalidate(); } @Override - public void updateImage() { - super.updateImage(); - invalidate(); - } - @Override public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); float ex = event.getX(); @@ -72,16 +62,8 @@ public class ImageRedEye extends ImageShow { if (event.getAction() == MotionEvent.ACTION_UP) { if (mCurrentRect != null) { // transform to original coordinates - GeometryMetadata geo = getImagePreset().mGeoData; - Matrix originalToScreen = geo.getOriginalToScreen(true, - mImageLoader.getOriginalBounds().width(), - mImageLoader.getOriginalBounds().height(), - getWidth(), getHeight()); - Matrix originalNoRotateToScreen = geo.getOriginalToScreen(false, - mImageLoader.getOriginalBounds().width(), - mImageLoader.getOriginalBounds().height(), - getWidth(), getHeight()); - + Matrix originalNoRotateToScreen = getImageToScreenMatrix(false); + Matrix originalToScreen = getImageToScreenMatrix(true); Matrix invert = new Matrix(); originalToScreen.invert(invert); RectF r = new RectF(mCurrentRect); @@ -112,49 +94,35 @@ public class ImageRedEye extends ImageShow { RectF drawRect = new RectF(mCurrentRect); canvas.drawRect(drawRect, paint); } - - GeometryMetadata geo = getImagePreset().mGeoData; - Matrix originalToScreen = geo.getOriginalToScreen(false, - mImageLoader.getOriginalBounds().width(), - mImageLoader.getOriginalBounds().height(), getWidth(), getHeight()); - Matrix originalRotateToScreen = geo.getOriginalToScreen(true, - mImageLoader.getOriginalBounds().width(), - mImageLoader.getOriginalBounds().height(), getWidth(), getHeight()); - if (mRedEyeRep != null) { - for (RedEyeCandidate candidate : mRedEyeRep.getCandidates()) { - RectF rect = candidate.getRect(); - RectF drawRect = new RectF(); - originalToScreen.mapRect(drawRect, rect); - RectF fullRect = new RectF(); - originalRotateToScreen.mapRect(fullRect, rect); - paint.setColor(Color.BLUE); - canvas.drawRect(fullRect, paint); - canvas.drawLine(fullRect.centerX(), fullRect.top, - fullRect.centerX(), fullRect.bottom, paint); - canvas.drawLine(fullRect.left, fullRect.centerY(), - fullRect.right, fullRect.centerY(), paint); - paint.setColor(Color.GREEN); - float dw = drawRect.width(); - float dh = drawRect.height(); - float dx = fullRect.centerX() - dw / 2; - float dy = fullRect.centerY() - dh / 2; - drawRect.set(dx, dy, dx + dw, dy + dh); - canvas.drawRect(drawRect, paint); - canvas.drawLine(drawRect.centerX(), drawRect.top, - drawRect.centerX(), drawRect.bottom, paint); - canvas.drawLine(drawRect.left, drawRect.centerY(), - drawRect.right, drawRect.centerY(), paint); - canvas.drawCircle(drawRect.centerX(), drawRect.centerY(), - mTouchPadding, paint); - } - } - } - - public void setEditor(EditorRedEye editorRedEye) { - mEditorRedEye = editorRedEye; } - public void setRepresentation(FilterRedEyeRepresentation redEyeRep) { - mRedEyeRep = redEyeRep; + @Override + protected void drawPoint(FilterPoint point, Canvas canvas, Matrix originalToScreen, + Matrix originalRotateToScreen, Paint paint) { + RedEyeCandidate candidate = (RedEyeCandidate) point; + RectF rect = candidate.getRect(); + RectF drawRect = new RectF(); + originalToScreen.mapRect(drawRect, rect); + RectF fullRect = new RectF(); + originalRotateToScreen.mapRect(fullRect, rect); + paint.setColor(Color.BLUE); + canvas.drawRect(fullRect, paint); + canvas.drawLine(fullRect.centerX(), fullRect.top, + fullRect.centerX(), fullRect.bottom, paint); + canvas.drawLine(fullRect.left, fullRect.centerY(), + fullRect.right, fullRect.centerY(), paint); + paint.setColor(Color.GREEN); + float dw = drawRect.width(); + float dh = drawRect.height(); + float dx = fullRect.centerX() - dw / 2; + float dy = fullRect.centerY() - dh / 2; + drawRect.set(dx, dy, dx + dw, dy + dh); + canvas.drawRect(drawRect, paint); + canvas.drawLine(drawRect.centerX(), drawRect.top, + drawRect.centerX(), drawRect.bottom, paint); + canvas.drawLine(drawRect.left, drawRect.centerY(), + drawRect.right, drawRect.centerY(), paint); + canvas.drawCircle(drawRect.centerX(), drawRect.centerY(), + mTouchPadding, paint); } } diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java index 2d50d116f..a215465c0 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java @@ -20,6 +20,7 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; @@ -269,6 +270,34 @@ public class ImageShow extends View implements OnGestureListener, return GeometryMath.roundNearest(getImagePreset().mGeoData.getPreviewCropBounds()); } + /* consider moving the following 2 methods into a subclass */ + /** + * This function calculates a Image to Screen Transformation matrix + * + * @param reflectRotation set true if you want the rotation encoded + * @return Image to Screen transformation matrix + */ + protected Matrix getImageToScreenMatrix(boolean reflectRotation) { + GeometryMetadata geo = getImagePreset().mGeoData; + Matrix m = geo.getOriginalToScreen(reflectRotation, + mImageLoader.getOriginalBounds().width(), + mImageLoader.getOriginalBounds().height(), getWidth(), getHeight()); + return m; + } + + /** + * This function calculates a to Screen Image Transformation matrix + * + * @param reflectRotation set true if you want the rotation encoded + * @return Screen to Image transformation matrix + */ + protected Matrix getScreenToImageMatrix(boolean reflectRotation) { + Matrix m = getImageToScreenMatrix(reflectRotation); + Matrix invert = new Matrix(); + m.invert(invert); + return invert; + } + public Rect getDisplayedImageBounds() { return mImageBounds; } diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageTinyPlanet.java b/src/com/android/gallery3d/filtershow/imageshow/ImageTinyPlanet.java index 3e95d4e15..3795d1f21 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageTinyPlanet.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageTinyPlanet.java @@ -22,6 +22,7 @@ import android.graphics.Paint; import android.util.AttributeSet; import android.view.MotionEvent; +import com.android.gallery3d.filtershow.editors.BasicEditor; import com.android.gallery3d.filtershow.editors.EditorTinyPlanet; import com.android.gallery3d.filtershow.filters.FilterCurvesRepresentation; import com.android.gallery3d.filtershow.filters.FilterTinyPlanetRepresentation; @@ -37,7 +38,7 @@ public class ImageTinyPlanet extends ImageShow { private float mCenterY = 0; private float mStartAngle = 0; private FilterTinyPlanetRepresentation mTinyPlanetRep; - private EditorTinyPlanet mEditorTinyPlanet; + private BasicEditor mEditorTinyPlanet; public ImageTinyPlanet(Context context) { super(context); @@ -94,7 +95,7 @@ public class ImageTinyPlanet extends ImageShow { mTinyPlanetRep = tinyPlanetRep; } - public void setEditor(EditorTinyPlanet editorTinyPlanet) { + public void setEditor(BasicEditor editorTinyPlanet) { mEditorTinyPlanet = editorTinyPlanet; } diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageVignette.java b/src/com/android/gallery3d/filtershow/imageshow/ImageVignette.java new file mode 100644 index 000000000..729ac7f41 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageVignette.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2012 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.gallery3d.filtershow.imageshow; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.util.AttributeSet; +import android.view.MotionEvent; + +import com.android.gallery3d.filtershow.editors.EditorVignette; +import com.android.gallery3d.filtershow.filters.FilterVignetteRepresentation; + +public class ImageVignette extends ImageShow { + private static final String LOGTAG = "ImageVignette"; + + private FilterVignetteRepresentation mVignetteRep; + private EditorVignette mEditorVignette; + + private int mActiveHandle = -1; + + EclipseControl mElipse; + + public ImageVignette(Context context) { + super(context); + mElipse = new EclipseControl(context); + } + + public ImageVignette(Context context, AttributeSet attrs) { + super(context, attrs); + mElipse = new EclipseControl(context); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + int mask = event.getActionMasked(); + if (MotionEvent.ACTION_UP == mask) { + mActiveHandle = -1; + } + if (MotionEvent.ACTION_DOWN == mask && event.getPointerCount() == 1) { + mActiveHandle = mElipse.getCloseHandle(event.getX(), event.getY()); + } + if (mActiveHandle == -1 || event.getPointerCount() > 1) { + mActiveHandle = -1; + + return super.onTouchEvent(event); + } + float x = event.getX(); + float y = event.getY(); + + mElipse.setScrToImageMatrix(getScreenToImageMatrix(true)); + + switch (mask) { + case (MotionEvent.ACTION_DOWN): + mElipse.actionDown(x, y, mVignetteRep); + break; + case (MotionEvent.ACTION_UP): + case (MotionEvent.ACTION_MOVE): + + mElipse.actionMove(mActiveHandle, x, y, mVignetteRep); + setRepresentation(mVignetteRep); + break; + } + resetImageCaches(this); + invalidate(); + mEditorVignette.commitLocalRepresentation(); + return true; + } + + public void setRepresentation(FilterVignetteRepresentation vignetteRep) { + mVignetteRep = vignetteRep; + Matrix toImg = getScreenToImageMatrix(false); + Matrix toScr = new Matrix(); + toImg.invert(toScr); + + float[] c = new float[] { + mVignetteRep.getCenterX(), mVignetteRep.getCenterY() }; + if (Float.isNaN(c[0])) { + float cx = mImageLoader.getOriginalBounds().width() / 2; + float cy = mImageLoader.getOriginalBounds().height() / 2; + float rx = Math.min(cx, cy) * .8f; + float ry = rx; + mVignetteRep.setCenter(cx, cy); + mVignetteRep.setRadius(rx, ry); + + c[0] = cx; + c[1] = cy; + toScr.mapPoints(c); + if (getWidth() != 0) { + mElipse.setCenter(c[0], c[1]); + mElipse.setRadius(c[0] * 0.8f, c[1] * 0.8f); + } + } else { + + toScr.mapPoints(c); + + mElipse.setCenter(c[0], c[1]); + mElipse.setRadius(toScr.mapRadius(mVignetteRep.getRadiusX()), + toScr.mapRadius(mVignetteRep.getRadiusY())); + } + } + + public void setEditor(EditorVignette editorVignette) { + mEditorVignette = editorVignette; + } + + @Override + public void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (mElipse.isUndefined()) { + setRepresentation(mVignetteRep); + } + mElipse.draw(canvas); + + } + +} diff --git a/src/com/android/gallery3d/filtershow/imageshow/Oval.java b/src/com/android/gallery3d/filtershow/imageshow/Oval.java new file mode 100644 index 000000000..28f278f1c --- /dev/null +++ b/src/com/android/gallery3d/filtershow/imageshow/Oval.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2012 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.gallery3d.filtershow.imageshow; + +public interface Oval { + void setCenter(float x, float y); + void setRadius(float w, float h); + float getCenterX(); + float getCenterY(); + float getRadiusX(); + float getRadiusY(); + void setRadiusY(float y); + void setRadiusX(float x); + +} diff --git a/src/com/android/gallery3d/ingest/data/MtpBitmapFetch.java b/src/com/android/gallery3d/ingest/data/MtpBitmapFetch.java index 88645e8d0..46a2051be 100644 --- a/src/com/android/gallery3d/ingest/data/MtpBitmapFetch.java +++ b/src/com/android/gallery3d/ingest/data/MtpBitmapFetch.java @@ -43,6 +43,7 @@ public class MtpBitmapFetch { public static Bitmap getThumbnail(MtpDevice device, MtpObjectInfo info) { byte[] imageBytes = device.getThumbnail(info.getObjectHandle()); + if (imageBytes == null) return null; BitmapFactory.Options o = new BitmapFactory.Options(); o.inJustDecodeBounds = true; BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length, o); @@ -60,6 +61,7 @@ public class MtpBitmapFetch { public static BitmapWithMetadata getFullsize(MtpDevice device, MtpObjectInfo info, int maxSide) { byte[] imageBytes = device.getObject(info.getObjectHandle(), info.getCompressedSize()); + if (imageBytes == null) return null; Bitmap created; if (maxSide > 0) { BitmapFactory.Options o = new BitmapFactory.Options(); diff --git a/src/com/android/gallery3d/ingest/ui/MtpImageView.java b/src/com/android/gallery3d/ingest/ui/MtpImageView.java index 9c197851e..5664d8a34 100644 --- a/src/com/android/gallery3d/ingest/ui/MtpImageView.java +++ b/src/com/android/gallery3d/ingest/ui/MtpImageView.java @@ -19,19 +19,32 @@ package com.android.gallery3d.ingest.ui; import android.content.Context; import android.mtp.MtpDevice; import android.mtp.MtpObjectInfo; -import android.os.AsyncTask; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; import android.util.AttributeSet; import android.widget.ImageView; import com.android.gallery3d.ingest.data.BitmapWithMetadata; import com.android.gallery3d.ingest.data.MtpBitmapFetch; -public class MtpImageView extends ImageView { - private static final int FADE_IN_TIME_MS = 80; +import java.lang.ref.WeakReference; +public class MtpImageView extends ImageView { private int mObjectHandle; private int mGeneration; + private WeakReference<MtpImageView> mWeakReference = new WeakReference<MtpImageView>(this); + private Object mFetchLock = new Object(); + private boolean mFetchPending = false; + private MtpObjectInfo mFetchObjectInfo; + private MtpDevice mFetchDevice; + private Object mFetchResult; + + private static final FetchImageHandler sFetchHandler = FetchImageHandler.createOnNewThread(); + private static final ShowImageHandler sFetchCompleteHandler = new ShowImageHandler(); + private void init() { showPlaceholder(); } @@ -55,8 +68,6 @@ public class MtpImageView extends ImageView { setImageResource(android.R.color.transparent); } - private LoadMtpImageTask mTask; - public void setMtpDeviceAndObjectInfo(MtpDevice device, MtpObjectInfo object, int gen) { int handle = object.getObjectHandle(); if (handle == mObjectHandle && gen == mGeneration) { @@ -66,8 +77,14 @@ public class MtpImageView extends ImageView { showPlaceholder(); mGeneration = gen; mObjectHandle = handle; - mTask = new LoadMtpImageTask(device); - mTask.execute(object); + synchronized (mFetchLock) { + mFetchObjectInfo = object; + mFetchDevice = device; + if (mFetchPending) return; + mFetchPending = true; + sFetchHandler.sendMessage( + sFetchHandler.obtainMessage(0, mWeakReference)); + } } protected Object fetchMtpImageDataFromDevice(MtpDevice device, MtpObjectInfo info) { @@ -80,51 +97,71 @@ public class MtpImageView extends ImageView { setRotation(bitmapWithMetadata.rotationDegrees); } - private class LoadMtpImageTask extends AsyncTask<MtpObjectInfo, Void, Object> { - private MtpDevice mDevice; - - public LoadMtpImageTask(MtpDevice device) { - mDevice = device; + protected void cancelLoadingAndClear() { + synchronized (mFetchLock) { + mFetchDevice = null; + mFetchObjectInfo = null; + mFetchResult = null; } + setImageResource(android.R.color.transparent); + setRotation(0); + } - @Override - protected Object doInBackground(MtpObjectInfo... args) { - Object result = null; - if (!isCancelled()) { - result = fetchMtpImageDataFromDevice(mDevice, args[0]); - } - mDevice = null; - return result; + @Override + public void onDetachedFromWindow() { + cancelLoadingAndClear(); + super.onDetachedFromWindow(); + } + + private static class FetchImageHandler extends Handler { + public FetchImageHandler(Looper l) { + super(l); } - @Override - protected void onPostExecute(Object result) { - if (isCancelled() || result == null) { - return; - } - setAlpha(0f); - onMtpImageDataFetchedFromDevice(result); - animate().alpha(1f).setDuration(FADE_IN_TIME_MS); + public static FetchImageHandler createOnNewThread() { + HandlerThread t = new HandlerThread("MtpImageView Fetch"); + t.start(); + return new FetchImageHandler(t.getLooper()); } @Override - protected void onCancelled() { + public void handleMessage(Message msg) { + @SuppressWarnings("unchecked") + MtpImageView parent = ((WeakReference<MtpImageView>) msg.obj).get(); + if (parent == null) return; + MtpObjectInfo objectInfo; + MtpDevice device; + synchronized (parent.mFetchLock) { + parent.mFetchPending = false; + device = parent.mFetchDevice; + objectInfo = parent.mFetchObjectInfo; + } + if (device == null) return; + Object result = parent.fetchMtpImageDataFromDevice(device, objectInfo); + if (result == null) return; + synchronized (parent.mFetchLock) { + if (parent.mFetchObjectInfo != objectInfo) return; + parent.mFetchResult = result; + parent.mFetchDevice = null; + parent.mFetchObjectInfo = null; + sFetchCompleteHandler.sendMessage( + sFetchCompleteHandler.obtainMessage(0, parent.mWeakReference)); + } } } - protected void cancelLoadingAndClear() { - if (mTask != null) { - mTask.cancel(true); + private static class ShowImageHandler extends Handler { + @Override + public void handleMessage(Message msg) { + @SuppressWarnings("unchecked") + MtpImageView parent = ((WeakReference<MtpImageView>) msg.obj).get(); + if (parent == null) return; + Object result; + synchronized (parent.mFetchLock) { + result = parent.mFetchResult; + } + if (result == null) return; + parent.onMtpImageDataFetchedFromDevice(result); } - mTask = null; - animate().cancel(); - setImageResource(android.R.color.transparent); - setRotation(0); - } - - @Override - public void onDetachedFromWindow() { - cancelLoadingAndClear(); - super.onDetachedFromWindow(); } } diff --git a/src_pd/com/android/gallery3d/filtershow/editors/EditorManager.java b/src_pd/com/android/gallery3d/filtershow/editors/EditorManager.java index 67ccf9c80..92962cbb1 100644 --- a/src_pd/com/android/gallery3d/filtershow/editors/EditorManager.java +++ b/src_pd/com/android/gallery3d/filtershow/editors/EditorManager.java @@ -28,6 +28,7 @@ public class EditorManager { editorPlaceHolder.addEditor(new EditorCurves()); editorPlaceHolder.addEditor(new EditorTinyPlanet()); editorPlaceHolder.addEditor(new EditorDraw()); + editorPlaceHolder.addEditor(new EditorVignette()); } } diff --git a/tests/src/com/android/gallery3d/CameraTestRunner.java b/tests/src/com/android/gallery3d/CameraTestRunner.java new file mode 100755 index 000000000..503233675 --- /dev/null +++ b/tests/src/com/android/gallery3d/CameraTestRunner.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2012 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.gallery3d; + +import android.test.InstrumentationTestRunner; +import android.test.InstrumentationTestSuite; + +import com.android.gallery3d.functional.CameraTest; +import com.android.gallery3d.functional.ImageCaptureIntentTest; +import com.android.gallery3d.functional.VideoCaptureIntentTest; +import com.android.gallery3d.unittest.CameraUnitTest; + +import junit.framework.TestSuite; + + +public class CameraTestRunner extends InstrumentationTestRunner { + + @Override + public TestSuite getAllTests() { + TestSuite suite = new InstrumentationTestSuite(this); + suite.addTestSuite(CameraTest.class); + suite.addTestSuite(ImageCaptureIntentTest.class); + suite.addTestSuite(VideoCaptureIntentTest.class); + suite.addTestSuite(CameraUnitTest.class); + return suite; + } + + @Override + public ClassLoader getLoader() { + return CameraTestRunner.class.getClassLoader(); + } +} diff --git a/tests/src/com/android/gallery3d/StressTests.java b/tests/src/com/android/gallery3d/StressTests.java new file mode 100755 index 000000000..32eefdcec --- /dev/null +++ b/tests/src/com/android/gallery3d/StressTests.java @@ -0,0 +1,47 @@ +/* + * 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.gallery3d; + +import com.android.gallery3d.stress.CameraLatency; +import com.android.gallery3d.stress.CameraStartUp; +import com.android.gallery3d.stress.ImageCapture; +import com.android.gallery3d.stress.SwitchPreview; + +import junit.framework.Test; +import junit.framework.TestSuite; + + +/** + * Instrumentation Test Runner for all Camera tests. + * + * Running all tests: + * + * adb shell am instrument \ + * -e class com.android.gallery3d.StressTests \ + * -w com.google.android.gallery3d.tests/com.android.gallery3d.stress.CameraStressTestRunner + */ + +public class StressTests extends TestSuite { + public static Test suite() { + TestSuite result = new TestSuite(); + result.addTestSuite(CameraLatency.class); + result.addTestSuite(CameraStartUp.class); + result.addTestSuite(ImageCapture.class); + result.addTestSuite(SwitchPreview.class); + return result; + } +} diff --git a/tests/src/com/android/gallery3d/functional/CameraTest.java b/tests/src/com/android/gallery3d/functional/CameraTest.java new file mode 100644 index 000000000..c293c0d4a --- /dev/null +++ b/tests/src/com/android/gallery3d/functional/CameraTest.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2010 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.gallery3d.functional; + +import com.android.camera.CameraActivity; + +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; +import android.os.Environment; +import android.os.Process; +import android.provider.MediaStore; +import android.test.InstrumentationTestCase; +import android.test.suitebuilder.annotation.LargeTest; + +import java.io.File; +import java.lang.ref.WeakReference; +import java.util.ArrayList; + +public class CameraTest extends InstrumentationTestCase { + @LargeTest + public void testVideoCaptureIntentFdLeak() throws Exception { + Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE); + intent.setClass(getInstrumentation().getTargetContext(), CameraActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.parse("file://" + + Environment.getExternalStorageDirectory().toString() + + "test_fd_leak.3gp")); + getInstrumentation().startActivitySync(intent).finish(); + // Test if the fd is closed. + for (File f: new File("/proc/" + Process.myPid() + "/fd").listFiles()) { + assertEquals(-1, f.getCanonicalPath().indexOf("test_fd_leak.3gp")); + } + } + + @LargeTest + public void testActivityLeak() throws Exception { + checkActivityLeak(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA); + checkActivityLeak(MediaStore.INTENT_ACTION_VIDEO_CAMERA); + } + + private void checkActivityLeak(String action) throws Exception { + final int TEST_COUNT = 5; + Intent intent = new Intent(action); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setClass(getInstrumentation().getTargetContext(), + CameraActivity.class); + ArrayList<WeakReference<Activity>> refs = + new ArrayList<WeakReference<Activity>>(); + for (int i = 0; i < TEST_COUNT; i++) { + Activity activity = getInstrumentation().startActivitySync(intent); + refs.add(new WeakReference<Activity>(activity)); + activity.finish(); + getInstrumentation().waitForIdleSync(); + activity = null; + } + Runtime.getRuntime().gc(); + Runtime.getRuntime().runFinalization(); + Runtime.getRuntime().gc(); + int refCount = 0; + for (WeakReference<Activity> c: refs) { + if (c.get() != null) refCount++; + } + // If applications are leaking activity, every reference is reachable. + assertTrue(refCount != TEST_COUNT); + } +} diff --git a/tests/src/com/android/gallery3d/functional/ImageCaptureIntentTest.java b/tests/src/com/android/gallery3d/functional/ImageCaptureIntentTest.java new file mode 100644 index 000000000..8d394b5db --- /dev/null +++ b/tests/src/com/android/gallery3d/functional/ImageCaptureIntentTest.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2011 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.gallery3d.functional; + +import com.android.camera.CameraActivity; +import com.android.gallery3d.R; + +import android.app.Activity; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.Environment; +import android.provider.MediaStore; +import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.LargeTest; +import android.view.KeyEvent; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; + +public class ImageCaptureIntentTest extends ActivityInstrumentationTestCase2 <CameraActivity> { + private Intent mIntent; + + public ImageCaptureIntentTest() { + super(CameraActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + } + + @LargeTest + public void testNoExtraOutput() throws Exception { + setActivityIntent(mIntent); + getActivity(); + + takePicture(); + pressDone(); + + assertTrue(getActivity().isFinishing()); + assertEquals(Activity.RESULT_OK, getActivity().getResultCode()); + Intent resultData = getActivity().getResultData(); + Bitmap bitmap = (Bitmap) resultData.getParcelableExtra("data"); + assertNotNull(bitmap); + assertTrue(bitmap.getWidth() > 0); + assertTrue(bitmap.getHeight() > 0); + } + + @LargeTest + public void testExtraOutput() throws Exception { + File file = new File(Environment.getExternalStorageDirectory(), + "test.jpg"); + BufferedInputStream stream = null; + byte[] jpegData; + + try { + mIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file)); + setActivityIntent(mIntent); + getActivity(); + + takePicture(); + pressDone(); + + assertTrue(getActivity().isFinishing()); + assertEquals(Activity.RESULT_OK, getActivity().getResultCode()); + + // Verify the jpeg file + int fileLength = (int) file.length(); + assertTrue(fileLength > 0); + jpegData = new byte[fileLength]; + stream = new BufferedInputStream(new FileInputStream(file)); + stream.read(jpegData); + } finally { + if (stream != null) stream.close(); + file.delete(); + } + + Bitmap b = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length); + assertTrue(b.getWidth() > 0); + assertTrue(b.getHeight() > 0); + } + + @LargeTest + public void testCancel() throws Exception { + setActivityIntent(mIntent); + getActivity(); + + pressCancel(); + + assertTrue(getActivity().isFinishing()); + assertEquals(Activity.RESULT_CANCELED, getActivity().getResultCode()); + } + + @LargeTest + public void testSnapshotCancel() throws Exception { + setActivityIntent(mIntent); + getActivity(); + + takePicture(); + pressCancel(); + + assertTrue(getActivity().isFinishing()); + assertEquals(Activity.RESULT_CANCELED, getActivity().getResultCode()); + } + + private void takePicture() throws Exception { + getInstrumentation().sendKeySync( + new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_FOCUS)); + getInstrumentation().sendCharacterSync(KeyEvent.KEYCODE_CAMERA); + Thread.sleep(4000); + } + + private void pressDone() { + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + getActivity().findViewById(R.id.btn_done).performClick(); + } + }); + } + + private void pressCancel() { + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + getActivity().findViewById(R.id.btn_cancel).performClick(); + } + }); + } +} diff --git a/tests/src/com/android/gallery3d/functional/VideoCaptureIntentTest.java b/tests/src/com/android/gallery3d/functional/VideoCaptureIntentTest.java new file mode 100644 index 000000000..c8d7bbb1c --- /dev/null +++ b/tests/src/com/android/gallery3d/functional/VideoCaptureIntentTest.java @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2011 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.gallery3d.functional; + +import com.android.camera.CameraActivity; +import com.android.gallery3d.R; + +import android.app.Activity; +import android.content.ContentResolver; +import android.content.Intent; +import android.database.Cursor; +import android.media.MediaMetadataRetriever; +import android.net.Uri; +import android.os.Environment; +import android.provider.MediaStore; +import android.provider.MediaStore.Video.VideoColumns; +import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; +import android.view.KeyEvent; + +import java.io.File; + +public class VideoCaptureIntentTest extends ActivityInstrumentationTestCase2 <CameraActivity> { + private static final String TAG = "VideoCaptureIntentTest"; + private Intent mIntent; + private Uri mVideoUri; + private File mFile, mFile2; + + public VideoCaptureIntentTest() { + super(CameraActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE); + } + + @Override + protected void tearDown() throws Exception { + if (mVideoUri != null) { + ContentResolver resolver = getActivity().getContentResolver(); + Uri query = mVideoUri.buildUpon().build(); + String[] projection = new String[] {VideoColumns.DATA}; + + Cursor cursor = null; + try { + cursor = resolver.query(query, projection, null, null, null); + if (cursor != null && cursor.moveToFirst()) { + new File(cursor.getString(0)).delete(); + } + } finally { + if (cursor != null) cursor.close(); + } + + resolver.delete(mVideoUri, null, null); + } + if (mFile != null) mFile.delete(); + if (mFile2 != null) mFile2.delete(); + super.tearDown(); + } + + @LargeTest + public void testNoExtraOutput() throws Exception { + setActivityIntent(mIntent); + getActivity(); + + recordVideo(); + pressDone(); + + Intent resultData = getActivity().getResultData(); + mVideoUri = resultData.getData(); + assertNotNull(mVideoUri); + verify(getActivity(), mVideoUri); + } + + @LargeTest + public void testExtraOutput() throws Exception { + mFile = new File(Environment.getExternalStorageDirectory(), "video.tmp"); + + Uri uri = Uri.fromFile(mFile); + mIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri); + setActivityIntent(mIntent); + getActivity(); + + recordVideo(); + pressDone(); + + verify(getActivity(), uri); + } + + @LargeTest + public void testCancel() throws Exception { + setActivityIntent(mIntent); + getActivity(); + + pressCancel(); + + assertTrue(getActivity().isFinishing()); + assertEquals(Activity.RESULT_CANCELED, getActivity().getResultCode()); + } + + @LargeTest + public void testRecordCancel() throws Exception { + setActivityIntent(mIntent); + getActivity(); + + recordVideo(); + pressCancel(); + + assertTrue(getActivity().isFinishing()); + assertEquals(Activity.RESULT_CANCELED, getActivity().getResultCode()); + } + + @LargeTest + public void testExtraSizeLimit() throws Exception { + mFile = new File(Environment.getExternalStorageDirectory(), "video.tmp"); + final long sizeLimit = 500000; // bytes + + Uri uri = Uri.fromFile(mFile); + mIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri); + mIntent.putExtra(MediaStore.EXTRA_SIZE_LIMIT, sizeLimit); + mIntent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 0); // use low quality to speed up + setActivityIntent(mIntent); + getActivity(); + + recordVideo(5000); + pressDone(); + + verify(getActivity(), uri); + long length = mFile.length(); + Log.v(TAG, "Video size is " + length + " bytes."); + assertTrue(length > 0); + assertTrue("Actual size=" + length, length <= sizeLimit); + } + + @LargeTest + public void testExtraDurationLimit() throws Exception { + mFile = new File(Environment.getExternalStorageDirectory(), "video.tmp"); + final int durationLimit = 2; // seconds + + Uri uri = Uri.fromFile(mFile); + mIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri); + mIntent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, durationLimit); + setActivityIntent(mIntent); + getActivity(); + + recordVideo(5000); + pressDone(); + + int duration = verify(getActivity(), uri); + // The duraion should be close to to the limit. The last video duration + // also has duration, so the total duration may exceeds the limit a + // little bit. + Log.v(TAG, "Video length is " + duration + " ms."); + assertTrue(duration < (durationLimit + 1) * 1000); + } + + @LargeTest + public void testExtraVideoQuality() throws Exception { + mFile = new File(Environment.getExternalStorageDirectory(), "video.tmp"); + mFile2 = new File(Environment.getExternalStorageDirectory(), "video2.tmp"); + + Uri uri = Uri.fromFile(mFile); + mIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri); + mIntent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 0); // low quality + setActivityIntent(mIntent); + getActivity(); + + recordVideo(); + pressDone(); + + verify(getActivity(), uri); + setActivity(null); + + uri = Uri.fromFile(mFile2); + mIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri); + mIntent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); // high quality + setActivityIntent(mIntent); + getActivity(); + + recordVideo(); + pressDone(); + + verify(getActivity(), uri); + assertTrue(mFile.length() <= mFile2.length()); + } + + // Verify result code, result data, and the duration. + private int verify(CameraActivity activity, Uri uri) throws Exception { + assertTrue(activity.isFinishing()); + assertEquals(Activity.RESULT_OK, activity.getResultCode()); + + // Verify the video file + MediaMetadataRetriever retriever = new MediaMetadataRetriever(); + retriever.setDataSource(activity, uri); + String duration = retriever.extractMetadata( + MediaMetadataRetriever.METADATA_KEY_DURATION); + assertNotNull(duration); + int durationValue = Integer.parseInt(duration); + Log.v(TAG, "Video duration is " + durationValue); + assertTrue(durationValue > 0); + return durationValue; + } + + private void recordVideo(int ms) throws Exception { + getInstrumentation().sendCharacterSync(KeyEvent.KEYCODE_CAMERA); + Thread.sleep(ms); + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + // If recording is in progress, stop it. Run these atomically in + // UI thread. + CameraActivity activity = getActivity(); + if (activity.isRecording()) { + activity.findViewById(R.id.shutter_button).performClick(); + } + } + }); + } + + private void recordVideo() throws Exception { + recordVideo(2000); + } + + private void pressDone() { + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + getActivity().findViewById(R.id.btn_done).performClick(); + } + }); + } + + private void pressCancel() { + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + getActivity().findViewById(R.id.btn_cancel).performClick(); + } + }); + } +} diff --git a/tests/src/com/android/gallery3d/power/ImageAndVideoCapture.java b/tests/src/com/android/gallery3d/power/ImageAndVideoCapture.java new file mode 100755 index 000000000..cd5079355 --- /dev/null +++ b/tests/src/com/android/gallery3d/power/ImageAndVideoCapture.java @@ -0,0 +1,116 @@ +/* + * 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.gallery3d.power; + +import com.android.camera.CameraActivity; + +import android.app.Instrumentation; +import android.provider.MediaStore; +import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; +import android.view.KeyEvent; +import android.content.Intent; +/** + * Junit / Instrumentation test case for camera power measurement + * + * Running the test suite: + * + * adb shell am instrument \ + * -e com.android.camera.power.ImageAndVideoCapture \ + * -w com.android.camera.tests/android.test.InstrumentationTestRunner + * + */ + +public class ImageAndVideoCapture extends ActivityInstrumentationTestCase2 <CameraActivity> { + private String TAG = "ImageAndVideoCapture"; + private static final int TOTAL_NUMBER_OF_IMAGECAPTURE = 5; + private static final int TOTAL_NUMBER_OF_VIDEOCAPTURE = 5; + private static final long WAIT_FOR_IMAGE_CAPTURE_TO_BE_TAKEN = 1500; //1.5 sedconds + private static final long WAIT_FOR_VIDEO_CAPTURE_TO_BE_TAKEN = 10000; //10 seconds + private static final long WAIT_FOR_PREVIEW = 1500; //1.5 seconds + private static final long WAIT_FOR_STABLE_STATE = 2000; //2 seconds + + public ImageAndVideoCapture() { + super(CameraActivity.class); + } + + @Override + protected void setUp() throws Exception { + getActivity(); + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + @LargeTest + public void testLaunchCamera() { + // This test case capture the baseline for the image preview. + try { + Thread.sleep(WAIT_FOR_STABLE_STATE); + } catch (Exception e) { + Log.v(TAG, "Got exception", e); + assertTrue("testImageCaptureDoNothing", false); + } + } + + @LargeTest + public void testCapture5Image() { + // This test case will use the default camera setting + Instrumentation inst = getInstrumentation(); + try { + for (int i = 0; i < TOTAL_NUMBER_OF_IMAGECAPTURE; i++) { + Thread.sleep(WAIT_FOR_IMAGE_CAPTURE_TO_BE_TAKEN); + inst.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_UP); + inst.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_CENTER); + Thread.sleep(WAIT_FOR_IMAGE_CAPTURE_TO_BE_TAKEN); + } + Thread.sleep(WAIT_FOR_STABLE_STATE); + } catch (Exception e) { + Log.v(TAG, "Got exception", e); + assertTrue("testImageCapture", false); + } + } + + @LargeTest + public void testCapture5Videos() { + // This test case will use the default camera setting + Instrumentation inst = getInstrumentation(); + try { + // Switch to the video mode + Intent intent = new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA); + intent.setClass(getInstrumentation().getTargetContext(), + CameraActivity.class); + getActivity().startActivity(intent); + for (int i = 0; i < TOTAL_NUMBER_OF_VIDEOCAPTURE; i++) { + Thread.sleep(WAIT_FOR_PREVIEW); + // record a video + inst.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_CENTER); + Thread.sleep(WAIT_FOR_VIDEO_CAPTURE_TO_BE_TAKEN); + inst.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_CENTER); + Thread.sleep(WAIT_FOR_PREVIEW); + } + Thread.sleep(WAIT_FOR_STABLE_STATE); + } catch (Exception e) { + Log.v(TAG, "Got exception", e); + assertTrue("testVideoCapture", false); + } + } +} diff --git a/tests/src/com/android/gallery3d/stress/CameraLatency.java b/tests/src/com/android/gallery3d/stress/CameraLatency.java new file mode 100755 index 000000000..7177abe6c --- /dev/null +++ b/tests/src/com/android/gallery3d/stress/CameraLatency.java @@ -0,0 +1,149 @@ +/* + * 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.gallery3d.stress; + +import com.android.camera.CameraActivity; + +import android.app.Instrumentation; +import android.os.Environment; +import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; +import android.view.KeyEvent; + +import java.io.BufferedWriter; +import java.io.FileWriter; + +/** + * Junit / Instrumentation test case for camera test + * + */ + +public class CameraLatency extends ActivityInstrumentationTestCase2 <CameraActivity> { + private String TAG = "CameraLatency"; + private static final int TOTAL_NUMBER_OF_IMAGECAPTURE = 20; + private static final long WAIT_FOR_IMAGE_CAPTURE_TO_BE_TAKEN = 4000; + private static final String CAMERA_TEST_OUTPUT_FILE = + Environment.getExternalStorageDirectory().toString() + "/mediaStressOut.txt"; + + private long mTotalAutoFocusTime; + private long mTotalShutterLag; + private long mTotalShutterToPictureDisplayedTime; + private long mTotalPictureDisplayedToJpegCallbackTime; + private long mTotalJpegCallbackFinishTime; + private long mAvgAutoFocusTime; + private long mAvgShutterLag = mTotalShutterLag; + private long mAvgShutterToPictureDisplayedTime; + private long mAvgPictureDisplayedToJpegCallbackTime; + private long mAvgJpegCallbackFinishTime; + + public CameraLatency() { + super(CameraActivity.class); + } + + @Override + protected void setUp() throws Exception { + getActivity(); + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + @LargeTest + public void testImageCapture() { + Log.v(TAG, "start testImageCapture test"); + Instrumentation inst = getInstrumentation(); + inst.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN); + try { + for (int i = 0; i < TOTAL_NUMBER_OF_IMAGECAPTURE; i++) { + Thread.sleep(WAIT_FOR_IMAGE_CAPTURE_TO_BE_TAKEN); + inst.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_CENTER); + Thread.sleep(WAIT_FOR_IMAGE_CAPTURE_TO_BE_TAKEN); + //skip the first measurement + if (i != 0) { + CameraActivity c = getActivity(); + + // if any of the latency var accessor methods return -1 then the + // camera is set to a different module other than PhotoModule so + // skip the shot and try again + if (c.getAutoFocusTime() != -1) { + mTotalAutoFocusTime += c.getAutoFocusTime(); + mTotalShutterLag += c.getShutterLag(); + mTotalShutterToPictureDisplayedTime += + c.getShutterToPictureDisplayedTime(); + mTotalPictureDisplayedToJpegCallbackTime += + c.getPictureDisplayedToJpegCallbackTime(); + mTotalJpegCallbackFinishTime += c.getJpegCallbackFinishTime(); + } + else { + i--; + continue; + } + } + } + } catch (Exception e) { + Log.v(TAG, "Got exception", e); + } + //ToDO: yslau + //1) Need to get the baseline from the cupcake so that we can add the + //failure condition of the camera latency. + //2) Only count those number with succesful capture. Set the timer to invalid + //before capture and ignore them if the value is invalid + int numberofRun = TOTAL_NUMBER_OF_IMAGECAPTURE - 1; + mAvgAutoFocusTime = mTotalAutoFocusTime / numberofRun; + mAvgShutterLag = mTotalShutterLag / numberofRun; + mAvgShutterToPictureDisplayedTime = + mTotalShutterToPictureDisplayedTime / numberofRun; + mAvgPictureDisplayedToJpegCallbackTime = + mTotalPictureDisplayedToJpegCallbackTime / numberofRun; + mAvgJpegCallbackFinishTime = + mTotalJpegCallbackFinishTime / numberofRun; + + try { + FileWriter fstream = null; + fstream = new FileWriter(CAMERA_TEST_OUTPUT_FILE, true); + BufferedWriter out = new BufferedWriter(fstream); + out.write("Camera Latency : \n"); + out.write("Number of loop: " + TOTAL_NUMBER_OF_IMAGECAPTURE + "\n"); + out.write("Avg AutoFocus = " + mAvgAutoFocusTime + "\n"); + out.write("Avg mShutterLag = " + mAvgShutterLag + "\n"); + out.write("Avg mShutterToPictureDisplayedTime = " + + mAvgShutterToPictureDisplayedTime + "\n"); + out.write("Avg mPictureDisplayedToJpegCallbackTime = " + + mAvgPictureDisplayedToJpegCallbackTime + "\n"); + out.write("Avg mJpegCallbackFinishTime = " + + mAvgJpegCallbackFinishTime + "\n"); + out.close(); + fstream.close(); + } catch (Exception e) { + fail("Camera Latency write output to file"); + } + Log.v(TAG, "The Image capture wait time = " + + WAIT_FOR_IMAGE_CAPTURE_TO_BE_TAKEN); + Log.v(TAG, "Avg AutoFocus = " + mAvgAutoFocusTime); + Log.v(TAG, "Avg mShutterLag = " + mAvgShutterLag); + Log.v(TAG, "Avg mShutterToPictureDisplayedTime = " + + mAvgShutterToPictureDisplayedTime); + Log.v(TAG, "Avg mPictureDisplayedToJpegCallbackTime = " + + mAvgPictureDisplayedToJpegCallbackTime); + Log.v(TAG, "Avg mJpegCallbackFinishTime = " + mAvgJpegCallbackFinishTime); + } +} + diff --git a/tests/src/com/android/gallery3d/stress/CameraStartUp.java b/tests/src/com/android/gallery3d/stress/CameraStartUp.java new file mode 100644 index 000000000..8524465ac --- /dev/null +++ b/tests/src/com/android/gallery3d/stress/CameraStartUp.java @@ -0,0 +1,157 @@ +/* + * 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.gallery3d.stress; + +import com.android.camera.CameraActivity; + +import android.app.Activity; +import android.app.Instrumentation; +import android.content.Intent; +import android.os.Environment; +import android.provider.MediaStore; +import android.test.InstrumentationTestCase; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; + +import java.io.FileWriter; +import java.io.BufferedWriter; + +/** + * Test cases to measure the camera and video recorder startup time. + */ +public class CameraStartUp extends InstrumentationTestCase { + + private static final int TOTAL_NUMBER_OF_STARTUP = 20; + + private String TAG = "CameraStartUp"; + private static final String CAMERA_TEST_OUTPUT_FILE = + Environment.getExternalStorageDirectory().toString() + "/mediaStressOut.txt"; + private static int WAIT_TIME_FOR_PREVIEW = 1500; //1.5 second + + private long launchCamera() { + long startupTime = 0; + try { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setClass(getInstrumentation().getTargetContext(), CameraActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + long beforeStart = System.currentTimeMillis(); + Instrumentation inst = getInstrumentation(); + Activity cameraActivity = inst.startActivitySync(intent); + long cameraStarted = System.currentTimeMillis(); + Thread.sleep(WAIT_TIME_FOR_PREVIEW); + cameraActivity.finish(); + startupTime = cameraStarted - beforeStart; + Thread.sleep(1000); + Log.v(TAG, "camera startup time: " + startupTime); + } catch (Exception e) { + Log.v(TAG, "Got exception", e); + fail("Fails to get the output file"); + } + return startupTime; + } + + private long launchVideo() { + long startupTime = 0; + + try { + Intent intent = new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA); + intent.setClass(getInstrumentation().getTargetContext(), CameraActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + long beforeStart = System.currentTimeMillis(); + Instrumentation inst = getInstrumentation(); + Activity recorderActivity = inst.startActivitySync(intent); + long cameraStarted = System.currentTimeMillis(); + recorderActivity.finish(); + startupTime = cameraStarted - beforeStart; + Log.v(TAG, "Video Startup Time = " + startupTime); + // wait for 1s to make sure it reach a clean stage + Thread.sleep(WAIT_TIME_FOR_PREVIEW); + Log.v(TAG, "video startup time: " + startupTime); + } catch (Exception e) { + Log.v(TAG, "Got exception", e); + fail("Fails to launch video output file"); + } + return startupTime; + } + + private void writeToOutputFile(long totalStartupTime, + String individualStartupTime, boolean firstStartUp, String Type) throws Exception { + // TODO (yslau) : Need to integrate the output data with central + // dashboard + try { + FileWriter fstream = null; + fstream = new FileWriter(CAMERA_TEST_OUTPUT_FILE, true); + BufferedWriter out = new BufferedWriter(fstream); + if (firstStartUp) { + out.write("First " + Type + " Startup: " + totalStartupTime + "\n"); + } else { + long averageStartupTime = totalStartupTime / (TOTAL_NUMBER_OF_STARTUP -1); + out.write(Type + "startup time: " + "\n"); + out.write("Number of loop: " + (TOTAL_NUMBER_OF_STARTUP -1) + "\n"); + out.write(individualStartupTime + "\n\n"); + out.write(Type + " average startup time: " + averageStartupTime + " ms\n\n"); + } + out.close(); + fstream.close(); + } catch (Exception e) { + fail("Camera write output to file"); + } + } + + @LargeTest + public void testLaunchVideo() throws Exception { + String individualStartupTime; + individualStartupTime = "Individual Video Startup Time = "; + long totalStartupTime = 0; + long startupTime = 0; + for (int i = 0; i < TOTAL_NUMBER_OF_STARTUP; i++) { + if (i == 0) { + // Capture the first startup time individually + long firstStartUpTime = launchVideo(); + writeToOutputFile(firstStartUpTime, "na", true, "Video"); + } else { + startupTime = launchVideo(); + totalStartupTime += startupTime; + individualStartupTime += startupTime + " ,"; + } + } + Log.v(TAG, "totalStartupTime =" + totalStartupTime); + writeToOutputFile(totalStartupTime, individualStartupTime, false, "Video"); + } + + @LargeTest + public void testLaunchCamera() throws Exception { + String individualStartupTime; + individualStartupTime = "Individual Camera Startup Time = "; + long totalStartupTime = 0; + long startupTime = 0; + for (int i = 0; i < TOTAL_NUMBER_OF_STARTUP; i++) { + if (i == 0) { + // Capture the first startup time individually + long firstStartUpTime = launchCamera(); + writeToOutputFile(firstStartUpTime, "na", true, "Camera"); + } else { + startupTime = launchCamera(); + totalStartupTime += startupTime; + individualStartupTime += startupTime + " ,"; + } + } + Log.v(TAG, "totalStartupTime =" + totalStartupTime); + writeToOutputFile(totalStartupTime, + individualStartupTime, false, "Camera"); + } +} diff --git a/tests/src/com/android/gallery3d/stress/CameraStressTestRunner.java b/tests/src/com/android/gallery3d/stress/CameraStressTestRunner.java new file mode 100755 index 000000000..57ae69125 --- /dev/null +++ b/tests/src/com/android/gallery3d/stress/CameraStressTestRunner.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 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.gallery3d.stress; + +import android.os.Bundle; +import android.test.InstrumentationTestRunner; +import android.test.InstrumentationTestSuite; +import junit.framework.TestSuite; + +public class CameraStressTestRunner extends InstrumentationTestRunner { + + // Default recorder settings + public static int mVideoDuration = 20000; // set default to 20 seconds + public static int mVideoIterations = 100; // set default to 100 videos + public static int mImageIterations = 100; // set default to 100 images + + @Override + public TestSuite getAllTests() { + TestSuite suite = new InstrumentationTestSuite(this); + suite.addTestSuite(ImageCapture.class); + suite.addTestSuite(VideoCapture.class); + return suite; + } + + @Override + public ClassLoader getLoader() { + return CameraStressTestRunner.class.getClassLoader(); + } + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + String video_iterations = (String) icicle.get("video_iterations"); + String image_iterations = (String) icicle.get("image_iterations"); + String video_duration = (String) icicle.get("video_duration"); + + if ( video_iterations != null ) { + mVideoIterations = Integer.parseInt(video_iterations); + } + if ( image_iterations != null) { + mImageIterations = Integer.parseInt(image_iterations); + } + if ( video_duration != null) { + mVideoDuration = Integer.parseInt(video_duration); + } + } +} diff --git a/tests/src/com/android/gallery3d/stress/ImageCapture.java b/tests/src/com/android/gallery3d/stress/ImageCapture.java new file mode 100755 index 000000000..e322eb516 --- /dev/null +++ b/tests/src/com/android/gallery3d/stress/ImageCapture.java @@ -0,0 +1,121 @@ +/* + * 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.gallery3d.stress; + +import com.android.camera.CameraActivity; +import com.android.gallery3d.stress.CameraStressTestRunner; + +import android.app.Instrumentation; +import android.content.Intent; +import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; +import android.view.KeyEvent; +import android.app.Activity; + +/** + * Junit / Instrumentation test case for camera test + * + * Running the test suite: + * + * adb shell am instrument \ + * -e class com.android.camera.stress.ImageCapture \ + * -w com.google.android.camera.tests/android.test.InstrumentationTestRunner + * + */ + +public class ImageCapture extends ActivityInstrumentationTestCase2 <CameraActivity> { + private String TAG = "ImageCapture"; + private static final long WAIT_FOR_IMAGE_CAPTURE_TO_BE_TAKEN = 1500; //1.5 sedconds + private static final long WAIT_FOR_SWITCH_CAMERA = 3000; //3 seconds + + private TestUtil testUtil = new TestUtil(); + + // Private intent extras. + private final static String EXTRAS_CAMERA_FACING = + "android.intent.extras.CAMERA_FACING"; + + public ImageCapture() { + super(CameraActivity.class); + } + + @Override + protected void setUp() throws Exception { + testUtil.prepareOutputFile(); + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + testUtil.closeOutputFile(); + super.tearDown(); + } + + public void captureImages(String reportTag, Instrumentation inst) { + int total_num_of_images = CameraStressTestRunner.mImageIterations; + Log.v(TAG, "no of images = " + total_num_of_images); + + //TODO(yslau): Need to integrate the outoput with the central dashboard, + //write to a txt file as a temp solution + boolean memoryResult = false; + KeyEvent focusEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_FOCUS); + + try { + testUtil.writeReportHeader(reportTag, total_num_of_images); + for (int i = 0; i < total_num_of_images; i++) { + Thread.sleep(WAIT_FOR_IMAGE_CAPTURE_TO_BE_TAKEN); + inst.sendKeySync(focusEvent); + inst.sendCharacterSync(KeyEvent.KEYCODE_CAMERA); + Thread.sleep(WAIT_FOR_IMAGE_CAPTURE_TO_BE_TAKEN); + testUtil.writeResult(i); + } + } catch (Exception e) { + Log.v(TAG, "Got exception: " + e.toString()); + assertTrue("testImageCapture", false); + } + } + + @LargeTest + public void testBackImageCapture() throws Exception { + Instrumentation inst = getInstrumentation(); + Intent intent = new Intent(); + + intent.setClass(getInstrumentation().getTargetContext(), CameraActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(EXTRAS_CAMERA_FACING, + android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK); + Activity act = inst.startActivitySync(intent); + Thread.sleep(WAIT_FOR_SWITCH_CAMERA); + captureImages("Back Camera Image Capture\n", inst); + act.finish(); + } + + @LargeTest + public void testFrontImageCapture() throws Exception { + Instrumentation inst = getInstrumentation(); + Intent intent = new Intent(); + + intent.setClass(getInstrumentation().getTargetContext(), CameraActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(EXTRAS_CAMERA_FACING, + android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT); + Activity act = inst.startActivitySync(intent); + Thread.sleep(WAIT_FOR_SWITCH_CAMERA); + captureImages("Front Camera Image Capture\n", inst); + act.finish(); + } +} diff --git a/tests/src/com/android/gallery3d/stress/ShotToShotLatency.java b/tests/src/com/android/gallery3d/stress/ShotToShotLatency.java new file mode 100644 index 000000000..a27bd90e6 --- /dev/null +++ b/tests/src/com/android/gallery3d/stress/ShotToShotLatency.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2012 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.gallery3d.stress; + +import android.app.Instrumentation; +import android.os.Environment; +import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; +import android.view.KeyEvent; +import com.android.camera.CameraActivity; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FilenameFilter; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; + +/** + * Junit / Instrumentation test case for measuring camera shot to shot latency + */ +public class ShotToShotLatency extends ActivityInstrumentationTestCase2<CameraActivity> { + private String TAG = "ShotToShotLatency"; + private static final int TOTAL_NUMBER_OF_SNAPSHOTS = 250; + private static final long SNAPSHOT_WAIT = 1000; + private static final String CAMERA_TEST_OUTPUT_FILE = + Environment.getExternalStorageDirectory().toString() + "/mediaStressOut.txt"; + private static final String CAMERA_IMAGE_DIRECTORY = + Environment.getExternalStorageDirectory().toString() + "/DCIM/Camera/"; + + public ShotToShotLatency() { + super(CameraActivity.class); + } + + @Override + protected void setUp() throws Exception { + getActivity(); + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + private void cleanupLatencyImages() { + try { + File sdcard = new File(CAMERA_IMAGE_DIRECTORY); + File[] pics = null; + FilenameFilter filter = new FilenameFilter() { + public boolean accept(File dir, String name) { + return name.endsWith(".jpg"); + } + }; + pics = sdcard.listFiles(filter); + for (File f : pics) { + f.delete(); + } + } catch (SecurityException e) { + Log.e(TAG, "Security manager access violation: " + e.toString()); + } + } + + private void sleep(long time) { + try { + Thread.sleep(time); + } catch (InterruptedException e) { + Log.e(TAG, "Sleep InterruptedException " + e.toString()); + } + } + + @LargeTest + public void testShotToShotLatency() { + long sigmaOfDiffFromMeanSquared = 0; + double mean = 0; + double standardDeviation = 0; + ArrayList<Long> captureTimes = new ArrayList<Long>(); + ArrayList<Long> latencyTimes = new ArrayList<Long>(); + + Log.v(TAG, "start testShotToShotLatency test"); + Instrumentation inst = getInstrumentation(); + + // Generate data points + for (int i = 0; i < TOTAL_NUMBER_OF_SNAPSHOTS; i++) { + inst.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_CENTER); + sleep(SNAPSHOT_WAIT); + CameraActivity c = getActivity(); + if (c.getCaptureStartTime() > 0) { + captureTimes.add(c.getCaptureStartTime()); + } + } + + // Calculate latencies + for (int j = 1; j < captureTimes.size(); j++) { + latencyTimes.add(captureTimes.get(j) - captureTimes.get(j - 1)); + } + + // Crunch numbers + for (long dataPoint : latencyTimes) { + mean += (double) dataPoint; + } + mean /= latencyTimes.size(); + + for (long dataPoint : latencyTimes) { + sigmaOfDiffFromMeanSquared += (dataPoint - mean) * (dataPoint - mean); + } + standardDeviation = Math.sqrt(sigmaOfDiffFromMeanSquared / latencyTimes.size()); + + // Report statistics + File outFile = new File(CAMERA_TEST_OUTPUT_FILE); + BufferedWriter output = null; + try { + output = new BufferedWriter(new FileWriter(outFile, true)); + output.write("Shot to shot latency - mean: " + mean + "\n"); + output.write("Shot to shot latency - standard deviation: " + standardDeviation + "\n"); + cleanupLatencyImages(); + } catch (IOException e) { + Log.e(TAG, "testShotToShotLatency IOException writing to log " + e.toString()); + } finally { + try { + if (output != null) { + output.close(); + } + } catch (IOException e) { + Log.e(TAG, "Error closing file: " + e.toString()); + } + } + } +} diff --git a/tests/src/com/android/gallery3d/stress/SwitchPreview.java b/tests/src/com/android/gallery3d/stress/SwitchPreview.java new file mode 100755 index 000000000..955d092a6 --- /dev/null +++ b/tests/src/com/android/gallery3d/stress/SwitchPreview.java @@ -0,0 +1,117 @@ +/* + * 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.gallery3d.stress; + +import com.android.camera.CameraActivity; + +import android.app.Instrumentation; +import android.content.Intent; +import android.provider.MediaStore; +import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.LargeTest; +import android.os.Environment; +import android.util.Log; + +import java.io.BufferedWriter; +import java.io.FileWriter; + +/** + * Junit / Instrumentation test case for camera test + * + * Running the test suite: + * + * adb shell am instrument \ + * -e class com.android.camera.stress.SwitchPreview \ + * -w com.android.camera.tests/com.android.camera.stress.CameraStressTestRunner + * + */ +public class SwitchPreview extends ActivityInstrumentationTestCase2 <CameraActivity>{ + private String TAG = "SwitchPreview"; + private static final int TOTAL_NUMBER_OF_SWITCHING = 200; + private static final long WAIT_FOR_PREVIEW = 4000; + + private static final String CAMERA_TEST_OUTPUT_FILE = + Environment.getExternalStorageDirectory().toString() + "/mediaStressOut.txt"; + private BufferedWriter mOut; + private FileWriter mfstream; + + public SwitchPreview() { + super(CameraActivity.class); + } + + @Override + protected void setUp() throws Exception { + getActivity(); + prepareOutputFile(); + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + getActivity().finish(); + closeOutputFile(); + super.tearDown(); + } + + private void prepareOutputFile(){ + try{ + mfstream = new FileWriter(CAMERA_TEST_OUTPUT_FILE, true); + mOut = new BufferedWriter(mfstream); + } catch (Exception e){ + assertTrue("Camera Switch Mode", false); + } + } + + private void closeOutputFile() { + try { + mOut.write("\n"); + mOut.close(); + mfstream.close(); + } catch (Exception e) { + assertTrue("CameraSwitchMode close output", false); + } + } + + @LargeTest + public void testSwitchMode() { + //Switching the video and the video recorder mode + Instrumentation inst = getInstrumentation(); + try{ + mOut.write("Camera Switch Mode:\n"); + mOut.write("No of loops :" + TOTAL_NUMBER_OF_SWITCHING + "\n"); + mOut.write("loop: "); + for (int i=0; i< TOTAL_NUMBER_OF_SWITCHING; i++) { + Thread.sleep(WAIT_FOR_PREVIEW); + Intent intent = new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + intent.setClass(getInstrumentation().getTargetContext(), + CameraActivity.class); + getActivity().startActivity(intent); + Thread.sleep(WAIT_FOR_PREVIEW); + intent = new Intent(); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + intent.setClass(getInstrumentation().getTargetContext(), + CameraActivity.class); + getActivity().startActivity(intent); + mOut.write(" ," + i); + mOut.flush(); + } + } catch (Exception e){ + Log.v(TAG, "Got exception", e); + } + } +} diff --git a/tests/src/com/android/gallery3d/stress/TestUtil.java b/tests/src/com/android/gallery3d/stress/TestUtil.java new file mode 100644 index 000000000..56ab715f7 --- /dev/null +++ b/tests/src/com/android/gallery3d/stress/TestUtil.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011 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.gallery3d.stress; + +import android.os.Environment; +import java.io.FileWriter; +import java.io.BufferedWriter; + + +/** + * Collection of utility functions used for the test. + */ +public class TestUtil { + public BufferedWriter mOut; + public FileWriter mfstream; + + public TestUtil() { + } + + public void prepareOutputFile() throws Exception { + String camera_test_output_file = + Environment.getExternalStorageDirectory().toString() + "/mediaStressOut.txt"; + mfstream = new FileWriter(camera_test_output_file, true); + mOut = new BufferedWriter(mfstream); + } + + public void closeOutputFile() throws Exception { + mOut.write("\n"); + mOut.close(); + mfstream.close(); + } + + public void writeReportHeader(String reportTag, int iteration) throws Exception { + mOut.write(reportTag); + mOut.write("No of loops :" + iteration + "\n"); + mOut.write("loop: "); + } + + public void writeResult(int iteration) throws Exception { + mOut.write(" ," + iteration); + mOut.flush(); + } +} diff --git a/tests/src/com/android/gallery3d/stress/VideoCapture.java b/tests/src/com/android/gallery3d/stress/VideoCapture.java new file mode 100755 index 000000000..dbbd124d0 --- /dev/null +++ b/tests/src/com/android/gallery3d/stress/VideoCapture.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2010 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.gallery3d.stress; + +import com.android.camera.CameraActivity; +import com.android.gallery3d.stress.TestUtil; + +import android.app.Activity; +import android.app.Instrumentation; +import android.content.Intent; +import android.provider.MediaStore; +import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.LargeTest; +import android.view.KeyEvent; + +import com.android.gallery3d.stress.CameraStressTestRunner; + +/** + * Junit / Instrumentation test case for camera test + * + * Running the test suite: + * + * adb shell am instrument \ + * -e class com.android.camera.stress.VideoCapture \ + * -w com.google.android.camera.tests/android.test.InstrumentationTestRunner + * + */ + +public class VideoCapture extends ActivityInstrumentationTestCase2 <CameraActivity> { + private static final long WAIT_FOR_PREVIEW = 1500; //1.5 seconds + private static final long WAIT_FOR_SWITCH_CAMERA = 3000; //2 seconds + + // Private intent extras which control the camera facing. + private final static String EXTRAS_CAMERA_FACING = + "android.intent.extras.CAMERA_FACING"; + + private TestUtil testUtil = new TestUtil(); + + public VideoCapture() { + super(CameraActivity.class); + } + + @Override + protected void setUp() throws Exception { + testUtil.prepareOutputFile(); + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + testUtil.closeOutputFile(); + super.tearDown(); + } + + @LargeTest + public void captureVideos(String reportTag, Instrumentation inst) throws Exception{ + boolean memoryResult = false; + int total_num_of_videos = CameraStressTestRunner.mVideoIterations; + int video_duration = CameraStressTestRunner.mVideoDuration; + testUtil.writeReportHeader(reportTag, total_num_of_videos); + + for (int i = 0; i < total_num_of_videos; i++) { + Thread.sleep(WAIT_FOR_PREVIEW); + // record a video + inst.sendCharacterSync(KeyEvent.KEYCODE_CAMERA); + Thread.sleep(video_duration); + inst.sendCharacterSync(KeyEvent.KEYCODE_CAMERA); + testUtil.writeResult(i); + } + } + + @LargeTest + public void testBackVideoCapture() throws Exception { + Instrumentation inst = getInstrumentation(); + Intent intent = new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA); + + intent.setClass(getInstrumentation().getTargetContext(), CameraActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(EXTRAS_CAMERA_FACING, + android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK); + Activity act = inst.startActivitySync(intent); + Thread.sleep(WAIT_FOR_SWITCH_CAMERA); + captureVideos("Back Camera Video Capture\n", inst); + act.finish(); + } + + @LargeTest + public void testFrontVideoCapture() throws Exception { + Instrumentation inst = getInstrumentation(); + Intent intent = new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA); + + intent.setClass(getInstrumentation().getTargetContext(), CameraActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(EXTRAS_CAMERA_FACING, + android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT); + Activity act = inst.startActivitySync(intent); + Thread.sleep(WAIT_FOR_SWITCH_CAMERA); + captureVideos("Front Camera Video Capture\n", inst); + act.finish(); + } +} diff --git a/tests/src/com/android/gallery3d/unittest/CameraUnitTest.java b/tests/src/com/android/gallery3d/unittest/CameraUnitTest.java new file mode 100644 index 000000000..b8fb05fc2 --- /dev/null +++ b/tests/src/com/android/gallery3d/unittest/CameraUnitTest.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2010 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.gallery3d.unittest; + +import com.android.camera.Util; + +import android.graphics.Matrix; +import android.test.suitebuilder.annotation.SmallTest; + +import junit.framework.TestCase; + +@SmallTest +public class CameraUnitTest extends TestCase { + public void testRoundOrientation() { + int h = Util.ORIENTATION_HYSTERESIS; + assertEquals(0, Util.roundOrientation(0, 0)); + assertEquals(0, Util.roundOrientation(359, 0)); + assertEquals(0, Util.roundOrientation(0 + 44 + h, 0)); + assertEquals(90, Util.roundOrientation(0 + 45 + h, 0)); + assertEquals(0, Util.roundOrientation(360 - 44 - h, 0)); + assertEquals(270, Util.roundOrientation(360 - 45 - h, 0)); + + assertEquals(90, Util.roundOrientation(90, 90)); + assertEquals(90, Util.roundOrientation(90 + 44 + h, 90)); + assertEquals(180, Util.roundOrientation(90 + 45 + h, 90)); + assertEquals(90, Util.roundOrientation(90 - 44 - h, 90)); + assertEquals(0, Util.roundOrientation(90 - 45 - h, 90)); + + assertEquals(180, Util.roundOrientation(180, 180)); + assertEquals(180, Util.roundOrientation(180 + 44 + h, 180)); + assertEquals(270, Util.roundOrientation(180 + 45 + h, 180)); + assertEquals(180, Util.roundOrientation(180 - 44 - h, 180)); + assertEquals(90, Util.roundOrientation(180 - 45 - h, 180)); + + assertEquals(270, Util.roundOrientation(270, 270)); + assertEquals(270, Util.roundOrientation(270 + 44 + h, 270)); + assertEquals(0, Util.roundOrientation(270 + 45 + h, 270)); + assertEquals(270, Util.roundOrientation(270 - 44 - h, 270)); + assertEquals(180, Util.roundOrientation(270 - 45 - h, 270)); + + assertEquals(90, Util.roundOrientation(90, 0)); + assertEquals(180, Util.roundOrientation(180, 0)); + assertEquals(270, Util.roundOrientation(270, 0)); + + assertEquals(0, Util.roundOrientation(0, 90)); + assertEquals(180, Util.roundOrientation(180, 90)); + assertEquals(270, Util.roundOrientation(270, 90)); + + assertEquals(0, Util.roundOrientation(0, 180)); + assertEquals(90, Util.roundOrientation(90, 180)); + assertEquals(270, Util.roundOrientation(270, 180)); + + assertEquals(0, Util.roundOrientation(0, 270)); + assertEquals(90, Util.roundOrientation(90, 270)); + assertEquals(180, Util.roundOrientation(180, 270)); + } + + public void testPrepareMatrix() { + Matrix matrix = new Matrix(); + float[] points; + int[] expected; + + Util.prepareMatrix(matrix, false, 0, 800, 480); + points = new float[] {-1000, -1000, 0, 0, 1000, 1000, 0, 1000, -750, 250}; + expected = new int[] {0, 0, 400, 240, 800, 480, 400, 480, 100, 300}; + matrix.mapPoints(points); + assertEquals(expected, points); + + Util.prepareMatrix(matrix, false, 90, 800, 480); + points = new float[] {-1000, -1000, 0, 0, 1000, 1000, 0, 1000, -750, 250}; + expected = new int[] {800, 0, 400, 240, 0, 480, 0, 240, 300, 60}; + matrix.mapPoints(points); + assertEquals(expected, points); + + Util.prepareMatrix(matrix, false, 180, 800, 480); + points = new float[] {-1000, -1000, 0, 0, 1000, 1000, 0, 1000, -750, 250}; + expected = new int[] {800, 480, 400, 240, 0, 0, 400, 0, 700, 180}; + matrix.mapPoints(points); + assertEquals(expected, points); + + Util.prepareMatrix(matrix, true, 180, 800, 480); + points = new float[] {-1000, -1000, 0, 0, 1000, 1000, 0, 1000, -750, 250}; + expected = new int[] {0, 480, 400, 240, 800, 0, 400, 0, 100, 180}; + matrix.mapPoints(points); + assertEquals(expected, points); + } + + private void assertEquals(int expected[], float[] actual) { + for (int i = 0; i < expected.length; i++) { + assertEquals("Array index " + i + " mismatch", expected[i], Math.round(actual[i])); + } + } +} diff --git a/tests_camera/src/com/android/camera/activity/CameraActivityTest.java b/tests_camera/src/com/android/camera/activity/CameraActivityTest.java index cc8ed83c3..eb027e9d3 100644 --- a/tests_camera/src/com/android/camera/activity/CameraActivityTest.java +++ b/tests_camera/src/com/android/camera/activity/CameraActivityTest.java @@ -21,7 +21,7 @@ import android.test.suitebuilder.annotation.LargeTest; import com.android.camera.CameraActivity; import com.android.camera.CameraHolder; -import com.android.camera.R; +import com.android.gallery3d.R; import static com.google.testing.littlemock.LittleMock.doReturn; diff --git a/tests_camera/src/com/android/camera/activity/CameraTestCase.java b/tests_camera/src/com/android/camera/activity/CameraTestCase.java index 7c7c38daf..27be3c7d3 100644 --- a/tests_camera/src/com/android/camera/activity/CameraTestCase.java +++ b/tests_camera/src/com/android/camera/activity/CameraTestCase.java @@ -31,8 +31,8 @@ import android.view.View; import com.android.camera.CameraHolder; import com.android.camera.CameraManager.CameraProxy; -import com.android.camera.R; import com.android.camera.Util; +import com.android.gallery3d.R; import static com.google.testing.littlemock.LittleMock.mock; import static com.google.testing.littlemock.LittleMock.doAnswer; diff --git a/tests_camera/src/com/android/camera/functional/ImageCaptureIntentTest.java b/tests_camera/src/com/android/camera/functional/ImageCaptureIntentTest.java index 620ff50b0..54ac1b497 100644 --- a/tests_camera/src/com/android/camera/functional/ImageCaptureIntentTest.java +++ b/tests_camera/src/com/android/camera/functional/ImageCaptureIntentTest.java @@ -17,7 +17,7 @@ package com.android.camera.functional; import com.android.camera.CameraActivity; -import com.android.camera.R; +import com.android.gallery3d.R; import android.app.Activity; import android.content.Intent; diff --git a/tests_camera/src/com/android/camera/functional/VideoCaptureIntentTest.java b/tests_camera/src/com/android/camera/functional/VideoCaptureIntentTest.java index 292543ccf..43e91ca84 100644 --- a/tests_camera/src/com/android/camera/functional/VideoCaptureIntentTest.java +++ b/tests_camera/src/com/android/camera/functional/VideoCaptureIntentTest.java @@ -17,7 +17,7 @@ package com.android.camera.functional; import com.android.camera.CameraActivity; -import com.android.camera.R; +import com.android.gallery3d.R; import android.app.Activity; import android.content.ContentResolver; |