Merge "Fix effects orientation handling." into ics-mr0
diff --git a/src/com/android/camera/Camera.java b/src/com/android/camera/Camera.java
index 589a5c8..70f70c8 100644
--- a/src/com/android/camera/Camera.java
+++ b/src/com/android/camera/Camera.java
@@ -124,7 +124,6 @@
     private boolean mMeteringAreaSupported;
     private boolean mAeLockSupported;
     private boolean mAwbLockSupported;
-    private boolean mAeAwbLock;
 
     private MyOrientationEventListener mOrientationListener;
     // The degrees of the device rotated clockwise from its natural orientation.
@@ -385,6 +384,7 @@
         Util.initializeScreenBrightness(getWindow(), getContentResolver());
         installIntentFilter();
         initializeZoom();
+        startFaceDetection();
         // Show the tap to focus toast if this is the first start.
         if (mFocusAreaSupported &&
                 mPreferences.getBoolean(CameraSettings.KEY_TAP_TO_FOCUS_PROMPT_SHOWN, true)) {
@@ -700,6 +700,7 @@
                 enableCameraControls(true);
 
                 startPreview();
+                startFaceDetection();
             }
 
             if (!mIsImageCaptureIntent) {
@@ -1258,6 +1259,7 @@
     public void onReviewRetakeClicked(View v) {
         hidePostCaptureAlert();
         startPreview();
+        startFaceDetection();
     }
 
     @OnClickAttr
@@ -1358,18 +1360,10 @@
         // Do not do focus if there is not enough storage.
         if (pressed && !canTakePicture()) return;
 
-        // Lock AE and AWB so users can half-press shutter and recompose.
-        mAeAwbLock = pressed;
-        if (mAeAwbLock && (mAeLockSupported || mAwbLockSupported)) {
-            setCameraParameters(UPDATE_PARAM_PREFERENCE);
-        }
-
-        mFocusManager.doFocus(pressed);
-
-        // Unlock AE and AWB after cancelAutoFocus. Camera API does not
-        // guarantee setParameters can be called during autofocus.
-        if (!mAeAwbLock && (mAeLockSupported || mAwbLockSupported)) {
-            setCameraParameters(UPDATE_PARAM_PREFERENCE);
+        if (pressed) {
+            mFocusManager.onShutterDown();
+        } else {
+            mFocusManager.onShutterUp();
         }
     }
 
@@ -1462,6 +1456,7 @@
                 initializeCapabilities();
                 resetExposureCompensation();
                 startPreview();
+                startFaceDetection();
             } catch (CameraHardwareException e) {
                 Util.showErrorAndFinish(this, R.string.cannot_connect_camera);
                 return;
@@ -1503,8 +1498,10 @@
 
         if (mFirstTimeInitialized) {
             mOrientationListener.disable();
-            mImageSaver.finish();
-            mImageSaver = null;
+            if (mImageSaver != null) {
+                mImageSaver.finish();
+                mImageSaver = null;
+            }
             if (!mIsImageCaptureIntent && mThumbnail != null && !mThumbnail.fromFile()) {
                 mThumbnail.saveTo(new File(getFilesDir(), Thumbnail.LAST_THUMB_FILENAME));
             }
@@ -1683,6 +1680,7 @@
         // display rotation in onCreate may not be what we want.
         if (mCameraState == PREVIEW_STOPPED) {
             startPreview();
+            startFaceDetection();
         } else {
             if (Util.getDisplayRotation(this) != mDisplayRotation) {
                 setDisplayOrientation();
@@ -1756,7 +1754,8 @@
 
         setPreviewDisplay(mSurfaceHolder);
         setDisplayOrientation();
-        mAeAwbLock = false; // Unlock AE and AWB.
+
+        mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
         setCameraParameters(UPDATE_PARAM_ALL);
         // If the focus mode is continuous autofocus, call cancelAutoFocus to
         // resume it because it may have been paused by autoFocus call.
@@ -1779,7 +1778,6 @@
             throw new RuntimeException("startPreview failed", ex);
         }
 
-        startFaceDetection();
         mZoomState = ZOOM_STOPPED;
         mCameraState = IDLE;
         mFocusManager.onPreviewStarted();
@@ -1831,11 +1829,11 @@
 
     private void updateCameraParametersPreference() {
         if (mAeLockSupported) {
-            mParameters.setAutoExposureLock(mAeAwbLock);
+            mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
         }
 
         if (mAwbLockSupported) {
-            mParameters.setAutoWhiteBalanceLock(mAeAwbLock);
+            mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
         }
 
         if (mFocusAreaSupported) {
diff --git a/src/com/android/camera/EffectsRecorder.java b/src/com/android/camera/EffectsRecorder.java
index ed44a79..7c81bf0 100644
--- a/src/com/android/camera/EffectsRecorder.java
+++ b/src/com/android/camera/EffectsRecorder.java
@@ -91,6 +91,7 @@
     private FileDescriptor mFd;
     private int mOrientationHint = 0;
     private long mMaxFileSize = 0;
+    private int mMaxDurationMs = 0;
     private int mCameraFacing = Camera.CameraInfo.CAMERA_FACING_BACK;
 
     private int mEffect = EFFECT_NONE;
@@ -234,6 +235,23 @@
         mMaxFileSize = maxFileSize;
     }
 
+    /**
+    * Sets the maximum recording duration (in ms) for the next recording session
+    * Setting it to zero (the default) disables the limit.
+    */
+    public synchronized void setMaxDuration(int maxDurationMs) {
+        switch (mState) {
+            case STATE_RECORD:
+                throw new RuntimeException("setMaxDuration cannot be called while recording!");
+            case STATE_RELEASED:
+                throw new RuntimeException("setMaxDuration called on an already released recorder!");
+            default:
+                break;
+        }
+        mMaxDurationMs = maxDurationMs;
+    }
+
+
     public void setCaptureRate(double fps) {
         switch (mState) {
             case STATE_RECORD:
@@ -638,6 +656,7 @@
             recorder.setInputValue("errorListener", mErrorListener);
         }
         recorder.setInputValue("maxFileSize", mMaxFileSize);
+        recorder.setInputValue("maxDurationMs", mMaxDurationMs);
         recorder.setInputValue("recording", true);
         if (mRecordSound != null) mRecordSound.play();
         mState = STATE_RECORD;
diff --git a/src/com/android/camera/FocusManager.java b/src/com/android/camera/FocusManager.java
index 6b9dbd6..228e2d1 100644
--- a/src/com/android/camera/FocusManager.java
+++ b/src/com/android/camera/FocusManager.java
@@ -55,6 +55,8 @@
     private boolean mInitialized;
     private boolean mFocusAreaSupported;
     private boolean mInLongPress;
+    private boolean mLockAeAwbNeeded;
+    private boolean mAeAwbLock;
     private SoundPlayer mSoundPlayer;
     private View mFocusIndicatorRotateLayout;
     private FocusIndicatorView mFocusIndicator;
@@ -104,6 +106,8 @@
         mFocusAreaSupported = (mParameters.getMaxNumFocusAreas() > 0
                 && isSupported(Parameters.FOCUS_MODE_AUTO,
                         mParameters.getSupportedFocusModes()));
+        mLockAeAwbNeeded = (mParameters.isAutoExposureLockSupported() ||
+                mParameters.isAutoWhiteBalanceLockSupported());
     }
 
     public void initialize(View focusIndicatorRotate, View previewFrame,
@@ -121,27 +125,42 @@
         }
     }
 
-    public void doFocus(boolean pressed) {
+    public void onShutterDown() {
         if (!mInitialized) return;
 
-        if (!(getFocusMode().equals(Parameters.FOCUS_MODE_INFINITY)
-                || getFocusMode().equals(Parameters.FOCUS_MODE_FIXED)
-                || getFocusMode().equals(Parameters.FOCUS_MODE_EDOF))) {
-            if (pressed) {  // Focus key down.
-                // Do not focus if touch focus has been triggered.
-                if (mState != STATE_SUCCESS && mState != STATE_FAIL) {
-                    autoFocus();
-                }
-            } else {  // Focus key up.
-                // User releases half-pressed focus key.
-                if (mState == STATE_FOCUSING || mState == STATE_SUCCESS
-                        || mState == STATE_FAIL) {
-                    cancelAutoFocus();
-                }
+        // Lock AE and AWB so users can half-press shutter and recompose.
+        if (mLockAeAwbNeeded && !mAeAwbLock) {
+            mAeAwbLock = true;
+            mListener.setFocusParameters();
+        }
+
+        if (needAutoFocusCall()) {
+            // Do not focus if touch focus has been triggered.
+            if (mState != STATE_SUCCESS && mState != STATE_FAIL) {
+                autoFocus();
             }
         }
     }
 
+    public void onShutterUp() {
+        if (!mInitialized) return;
+
+        if (needAutoFocusCall()) {
+            // User releases half-pressed focus key.
+            if (mState == STATE_FOCUSING || mState == STATE_SUCCESS
+                    || mState == STATE_FAIL) {
+                cancelAutoFocus();
+            }
+        }
+
+        // Unlock AE and AWB after cancelAutoFocus. Camera API does not
+        // guarantee setParameters can be called during autofocus.
+        if (mLockAeAwbNeeded && mAeAwbLock && (mState != STATE_FOCUSING_SNAP_ON_FINISH)) {
+            mAeAwbLock = false;
+            mListener.setFocusParameters();
+        }
+    }
+
     public void shutterLongPressed() {
         if (Parameters.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusMode)
                 && isSupported(Parameters.FOCUS_MODE_AUTO, mParameters.getSupportedFocusModes())) {
@@ -151,9 +170,9 @@
             mInLongPress = true;
             // Cancel any outstanding Auto focus requests. The auto focus mode
             // will be changed from CAF to auto in cancelAutoFocus.
-            doFocus(false);
+            onShutterUp();
             // Call Autofocus
-            doFocus(true);
+            onShutterDown();
             mInLongPress = false;
         }
     }
@@ -164,11 +183,7 @@
         // If the user has half-pressed the shutter and focus is completed, we
         // can take the photo right away. If the focus mode is infinity, we can
         // also take the photo.
-        if (getFocusMode().equals(Parameters.FOCUS_MODE_INFINITY)
-                || getFocusMode().equals(Parameters.FOCUS_MODE_FIXED)
-                || getFocusMode().equals(Parameters.FOCUS_MODE_EDOF)
-                || (mState == STATE_SUCCESS
-                || mState == STATE_FAIL)) {
+        if (!needAutoFocusCall() || (mState == STATE_SUCCESS || mState == STATE_FAIL)) {
             capture();
         } else if (mState == STATE_FOCUSING) {
             // Half pressing the shutter (i.e. the focus button event) will
@@ -467,7 +482,22 @@
         mOverrideFocusMode = focusMode;
     }
 
+    public void setAeAwbLock(boolean lock) {
+        mAeAwbLock = lock;
+    }
+
+    public boolean getAeAwbLock() {
+        return mAeAwbLock;
+    }
+
     private static boolean isSupported(String value, List<String> supported) {
         return supported == null ? false : supported.indexOf(value) >= 0;
     }
+
+    private boolean needAutoFocusCall() {
+        String focusMode = getFocusMode();
+        return !(focusMode.equals(Parameters.FOCUS_MODE_INFINITY)
+                || focusMode.equals(Parameters.FOCUS_MODE_FIXED)
+                || focusMode.equals(Parameters.FOCUS_MODE_EDOF));
+    }
 }
diff --git a/src/com/android/camera/MenuHelper.java b/src/com/android/camera/MenuHelper.java
index 2e1ec28..07caeb2 100644
--- a/src/com/android/camera/MenuHelper.java
+++ b/src/com/android/camera/MenuHelper.java
@@ -138,8 +138,9 @@
         startCameraActivity(activity, new Intent(action), className);
     }
 
-    public static void gotoVideoMode(Activity activity) {
+    public static void gotoVideoMode(Activity activity, boolean resetEffect) {
         Intent intent = new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA);
+        intent.putExtra(VideoCamera.RESET_EFFECT_EXTRA, resetEffect);
         startCameraActivity(activity, intent, VIDEO_CAMERA_CLASS);
     }
 
diff --git a/src/com/android/camera/VideoCamera.java b/src/com/android/camera/VideoCamera.java
index 8ac25e6..4694c90 100755
--- a/src/com/android/camera/VideoCamera.java
+++ b/src/com/android/camera/VideoCamera.java
@@ -183,6 +183,9 @@
     private int mEffectType = EffectsRecorder.EFFECT_NONE;
     private Object mEffectParameter = null;
     private String mEffectUriFromGallery = null;
+    private String mPrefVideoEffectDefault;
+    private boolean mResetEffect = true;
+    public static final String RESET_EFFECT_EXTRA = "reset_effect";
 
     private boolean mMediaRecorderRecording = false;
     private long mRecordingStartTime;
@@ -353,6 +356,11 @@
         CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
 
         mNumberOfCameras = CameraHolder.instance().getNumberOfCameras();
+        mPrefVideoEffectDefault = getString(R.string.pref_video_effect_default);
+        // Do not reset the effect if users are switching between back and front
+        // cameras.
+        mResetEffect = getIntent().getBooleanExtra(RESET_EFFECT_EXTRA, true);
+        resetEffect();
 
         /*
          * To reduce startup time, we start the preview in another thread.
@@ -556,7 +564,8 @@
     }
 
     private void startPlayVideoActivity() {
-        Intent intent = new Intent(Intent.ACTION_VIEW, mCurrentVideoUri);
+        Intent intent = new Intent(Intent.ACTION_VIEW);
+        intent.setDataAndType(mCurrentVideoUri, convertOutputFormatToMimeType(mProfile.fileFormat));
         try {
             startActivity(intent);
         } catch (ActivityNotFoundException ex) {
@@ -704,33 +713,17 @@
         mEffectType = CameraSettings.readEffectType(mPreferences);
         if (mEffectType != EffectsRecorder.EFFECT_NONE) {
             mEffectParameter = CameraSettings.readEffectParameter(mPreferences);
-            // When picking from gallery, mEffectParameter should have been
-            // initialized in onActivityResult. If not, fall back to no effect
-            if (mEffectType == EffectsRecorder.EFFECT_BACKDROPPER
-                    && ((String) mEffectParameter).equals(EFFECT_BG_FROM_GALLERY)
-                    && mEffectUriFromGallery == null) {
-                Log.w(TAG, "No URI from gallery, resetting to no effect");
-                mEffectType = EffectsRecorder.EFFECT_NONE;
-                mEffectParameter = null;
-                writeDefaultEffectToPrefs();
-                if (mIndicatorControlContainer != null) {
-                    mIndicatorControlContainer.overrideSettings(
+            // Set quality to 480p for effects, unless intent is overriding it
+            if (!intent.hasExtra(MediaStore.EXTRA_VIDEO_QUALITY)) {
+                quality = CamcorderProfile.QUALITY_480P;
+            }
+            // On initial startup, can get here before indicator control is
+            // enabled. In that case, UI quality override handled in
+            // initializeIndicatorControl.
+            if (mIndicatorControlContainer != null) {
+                mIndicatorControlContainer.overrideSettings(
                         CameraSettings.KEY_VIDEO_QUALITY,
-                        null);
-                }
-            } else {
-                // Set quality to 480p for effects, unless intent is overriding it
-                if (!intent.hasExtra(MediaStore.EXTRA_VIDEO_QUALITY)) {
-                    quality = CamcorderProfile.QUALITY_480P;
-                }
-                // On initial startup, can get here before indicator control is
-                // enabled. In that case, UI quality override handled in
-                // initializeIndicatorControl.
-                if (mIndicatorControlContainer != null) {
-                    mIndicatorControlContainer.overrideSettings(
-                            CameraSettings.KEY_VIDEO_QUALITY,
-                            Integer.toString(CamcorderProfile.QUALITY_480P));
-                }
+                        Integer.toString(CamcorderProfile.QUALITY_480P));
             }
         } else {
             mEffectParameter = null;
@@ -804,6 +797,10 @@
         // some time to get first orientation.
         mOrientationListener.enable();
         if (!mPreviewing) {
+            if (resetEffect()) {
+                mBgLearningMessageFrame.setVisibility(View.GONE);
+                mIndicatorControlContainer.reloadPreferences();
+            }
             try {
                 mCameraDevice = Util.openCamera(this, mCameraId);
                 readVideoPreferences();
@@ -1319,6 +1316,7 @@
             maxFileSize = requestedSizeLimit;
         }
         mEffectsRecorder.setMaxFileSize(maxFileSize);
+        mEffectsRecorder.setMaxDuration(mMaxVideoDurationInMs);
     }
 
 
@@ -1354,12 +1352,9 @@
     private void generateVideoFilename(int outputFileFormat) {
         long dateTaken = System.currentTimeMillis();
         String title = createName(dateTaken);
-        String filename = title + ".3gp"; // Used when emailing.
-        String mime = "video/3gpp";
-        if (outputFileFormat == MediaRecorder.OutputFormat.MPEG_4) {
-            filename = title + ".mp4";
-            mime = "video/mp4";
-        }
+        // Used when emailing.
+        String filename = title + convertOutputFormatToFileExt(outputFileFormat);
+        String mime = convertOutputFormatToMimeType(outputFileFormat);
         mVideoFilename = Storage.DIRECTORY + '/' + filename;
         mCurrentVideoValues = new ContentValues(7);
         mCurrentVideoValues.put(Video.Media.TITLE, title);
@@ -1933,6 +1928,11 @@
                     // seen by startPreview from onResume()
                     mEffectUriFromGallery = ((Uri) data.getData()).toString();
                     Log.v(TAG, "Received URI from gallery: " + mEffectUriFromGallery);
+                    mResetEffect = false;
+                } else {
+                    mEffectUriFromGallery = null;
+                    Log.w(TAG, "No URI from gallery");
+                    mResetEffect = true;
                 }
                 break;
             default:
@@ -2053,9 +2053,11 @@
                 // animation.
                 if (mIsVideoCaptureIntent) {
                     // If the intent is video capture, stay in video capture mode.
-                    MenuHelper.gotoVideoMode(this, getIntent());
+                    Intent intent = getIntent();
+                    intent.putExtra(RESET_EFFECT_EXTRA, false);
+                    MenuHelper.gotoVideoMode(this, intent);
                 } else {
-                    MenuHelper.gotoVideoMode(this);
+                    MenuHelper.gotoVideoMode(this, false);
                 }
                 finish();
             } else {
@@ -2346,4 +2348,31 @@
             Util.broadcastNewPicture(this, uri);
         }
     }
+
+    private boolean resetEffect() {
+        if (mResetEffect) {
+            String value = mPreferences.getString(CameraSettings.KEY_VIDEO_EFFECT,
+                    mPrefVideoEffectDefault);
+            if (!mPrefVideoEffectDefault.equals(value)) {
+                writeDefaultEffectToPrefs();
+                return true;
+            }
+        }
+        mResetEffect = true;
+        return false;
+    }
+
+    private String convertOutputFormatToMimeType(int outputFileFormat) {
+        if (outputFileFormat == MediaRecorder.OutputFormat.MPEG_4) {
+            return "video/mp4";
+        }
+        return "video/3gpp";
+    }
+
+    private String convertOutputFormatToFileExt(int outputFileFormat) {
+        if (outputFileFormat == MediaRecorder.OutputFormat.MPEG_4) {
+            return ".mp4";
+        }
+        return ".3gp";
+    }
 }