Merge "Get new camera activity up to date" into gb-ub-photos-carlsbad
diff --git a/src/com/android/camera/NewCameraActivity.java b/src/com/android/camera/NewCameraActivity.java
index 2705bcf..3313ede 100644
--- a/src/com/android/camera/NewCameraActivity.java
+++ b/src/com/android/camera/NewCameraActivity.java
@@ -134,8 +134,12 @@
     }
 
     private void unbindMediaSaveService() {
-        mMediaSaveService.setListener(null);
-        unbindService(mConnection);
+        if (mMediaSaveService != null) {
+            mMediaSaveService.setListener(null);
+        }
+        if (mConnection != null) {
+            unbindService(mConnection);
+        }
     }
 
     @Override
@@ -262,9 +266,6 @@
 
     @Override
     public boolean dispatchTouchEvent(MotionEvent m) {
-        //if (mFilmStripView.isInCameraFullscreen()) {
-        //    return mCurrentModule.dispatchTouchEvent(m);
-        //}
         return mFilmStripView.dispatchTouchEvent(m);
     }
     public boolean isAutoRotateScreen() {
@@ -384,4 +385,40 @@
     @Override
     public void onShowSwitcherPopup() {
     }
+
+    // Accessor methods for getting latency times used in performance testing
+    public long getAutoFocusTime() {
+        return (mCurrentModule instanceof PhotoModule) ?
+                ((PhotoModule) mCurrentModule).mAutoFocusTime : -1;
+    }
+
+    public long getShutterLag() {
+        return (mCurrentModule instanceof PhotoModule) ?
+                ((PhotoModule) mCurrentModule).mShutterLag : -1;
+    }
+
+    public long getShutterToPictureDisplayedTime() {
+        return (mCurrentModule instanceof PhotoModule) ?
+                ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1;
+    }
+
+    public long getPictureDisplayedToJpegCallbackTime() {
+        return (mCurrentModule instanceof PhotoModule) ?
+                ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1;
+    }
+
+    public long getJpegCallbackFinishTime() {
+        return (mCurrentModule instanceof PhotoModule) ?
+                ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1;
+    }
+
+    public long getCaptureStartTime() {
+        return (mCurrentModule instanceof PhotoModule) ?
+                ((PhotoModule) mCurrentModule).mCaptureStartTime : -1;
+    }
+
+    public boolean isRecording() {
+        return (mCurrentModule instanceof VideoModule) ?
+                ((VideoModule) mCurrentModule).isRecording() : false;
+    }
 }
diff --git a/src/com/android/camera/NewPhotoModule.java b/src/com/android/camera/NewPhotoModule.java
index ecbbbf3..e5d50f3 100644
--- a/src/com/android/camera/NewPhotoModule.java
+++ b/src/com/android/camera/NewPhotoModule.java
@@ -263,8 +263,6 @@
                 @Override
                 public void onMediaSaved(Uri uri) {
                     if (uri != null) {
-                        // TODO: Commenting out the line below for now. need to get it working
-                        // mActivity.addSecureAlbumItemIfNeeded(false, uri);
                         mActivity.notifyNewMedia(uri);
                     }
                 }
diff --git a/src/com/android/camera/NewPhotoUI.java b/src/com/android/camera/NewPhotoUI.java
index e3a098f..cd93a7e 100644
--- a/src/com/android/camera/NewPhotoUI.java
+++ b/src/com/android/camera/NewPhotoUI.java
@@ -274,6 +274,7 @@
         if (mPieRenderer == null) {
             mPieRenderer = new PieRenderer(mActivity);
             mPieRenderer.setPieListener(this);
+            mRenderOverlay.addRenderer(mPieRenderer);
         }
 
         if (mMenu == null) {
@@ -284,9 +285,8 @@
 
         if (mZoomRenderer == null) {
             mZoomRenderer = new ZoomRenderer(mActivity);
+            mRenderOverlay.addRenderer(mZoomRenderer);
         }
-        mRenderOverlay.addRenderer(mPieRenderer);
-        mRenderOverlay.addRenderer(mZoomRenderer);
 
         if (mGestures == null) {
             // this will handle gesture disambiguation and dispatching
diff --git a/src/com/android/camera/NewVideoMenu.java b/src/com/android/camera/NewVideoMenu.java
index 1038628..70f73ec 100644
--- a/src/com/android/camera/NewVideoMenu.java
+++ b/src/com/android/camera/NewVideoMenu.java
@@ -35,10 +35,6 @@
         TimeIntervalPopup.Listener {
 
     private static String TAG = "CAM_VideoMenu";
-    private static final int POS_WB = 1;
-    private static final int POS_SET = 2;
-    private static final int POS_FLASH = 3;
-    private static final int POS_SWITCH = 4;
 
     private NewVideoUI mUI;
     private String[] mOtherKeys;
diff --git a/src/com/android/camera/NewVideoModule.java b/src/com/android/camera/NewVideoModule.java
index 96f0312..d613eb4 100644
--- a/src/com/android/camera/NewVideoModule.java
+++ b/src/com/android/camera/NewVideoModule.java
@@ -45,6 +45,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.SystemClock;
 import android.provider.MediaStore;
+import android.provider.MediaStore.MediaColumns;
 import android.provider.MediaStore.Video;
 import android.util.Log;
 import android.view.KeyEvent;
@@ -93,6 +94,7 @@
     private static final int SWITCH_CAMERA = 8;
     private static final int SWITCH_CAMERA_START_ANIMATION = 9;
     private static final int HIDE_SURFACE_VIEW = 10;
+    private static final int CAPTURE_ANIMATION_DONE = 11;
 
     private static final int SCREEN_DELAY = 2 * 60 * 1000;
 
@@ -176,7 +178,6 @@
     private LocationManager mLocationManager;
     private OrientationManager mOrientationManager;
 
-    private VideoNamer mVideoNamer;
     private Surface mSurface;
     private int mPendingSwitchCameraId;
     private boolean mOpenCameraFail;
@@ -193,12 +194,24 @@
     private boolean mRestoreFlash;  // This is used to check if we need to restore the flash
                                     // status when going back from gallery.
 
-    private MediaSaveService.OnMediaSavedListener mOnMediaSavedListener =
+    private final MediaSaveService.OnMediaSavedListener mOnVideoSavedListener =
             new MediaSaveService.OnMediaSavedListener() {
                 @Override
                 public void onMediaSaved(Uri uri) {
                     if (uri != null) {
-                        mActivity.notifyNewMedia(uri);
+                        mActivity.sendBroadcast(
+                                new Intent(Util.ACTION_NEW_VIDEO, uri));
+                        Util.broadcastNewPicture(mActivity, uri);
+                    }
+                }
+            };
+
+    private final MediaSaveService.OnMediaSavedListener mOnPhotoSavedListener =
+            new MediaSaveService.OnMediaSavedListener() {
+                @Override
+                public void onMediaSaved(Uri uri) {
+                    if (uri != null) {
+                        Util.broadcastNewPicture(mActivity, uri);
                     }
                 }
             };
@@ -284,6 +297,11 @@
                     break;
                 }
 
+                case CAPTURE_ANIMATION_DONE: {
+                    mUI.enablePreviewThumb(false);
+                    break;
+                }
+
                 default:
                     Log.v(TAG, "Unhandled message: " + msg.what);
                     break;
@@ -395,7 +413,7 @@
 
         initializeVideoControl();
         mPendingSwitchCameraId = -1;
-        mUI.updateOnScreenIndicators(mParameters);
+        mUI.updateOnScreenIndicators(mParameters, mPreferences);
 
         // Disable the shutter button if effects are ON since it might take
         // a little more time for the effects preview to be ready. We do not
@@ -556,6 +574,14 @@
                 // will not be continuous for a short period of time.
                 // TODO: need to get the capture animation to work 
                 // ((CameraScreenNail) mActivity.mCameraScreenNail).animateCapture(mDisplayRotation);
+
+                mUI.enablePreviewThumb(true);
+
+                // Make sure to disable the thumbnail preview after the
+                // animation is done to disable the click target.
+                mHandler.removeMessages(CAPTURE_ANIMATION_DONE);
+                mHandler.sendEmptyMessageDelayed(CAPTURE_ANIMATION_DONE,
+                        CaptureAnimManager.getAnimationDuration());
             }
         }
     }
@@ -760,7 +786,6 @@
         // Dismiss open menu if exists.
         PopupManager.getInstance(mActivity).notifyShowPopup(null);
 
-        mVideoNamer = new VideoNamer();
         UsageStatistics.onContentViewChanged(
                 UsageStatistics.COMPONENT_CAMERA, "VideoModule");
     }
@@ -936,7 +961,6 @@
             // that will close down the effects are well, thus making this if
             // condition invalid.
             closeVideoFileDescriptor();
-            clearVideoNamer();
         }
 
         releasePreviewResources();
@@ -1317,10 +1341,11 @@
         String mime = convertOutputFormatToMimeType(outputFileFormat);
         String path = Storage.DIRECTORY + '/' + filename;
         String tmpPath = path + ".tmp";
-        mCurrentVideoValues = new ContentValues(7);
+        mCurrentVideoValues = new ContentValues(9);
         mCurrentVideoValues.put(Video.Media.TITLE, title);
         mCurrentVideoValues.put(Video.Media.DISPLAY_NAME, filename);
         mCurrentVideoValues.put(Video.Media.DATE_TAKEN, dateTaken);
+        mCurrentVideoValues.put(MediaColumns.DATE_MODIFIED, dateTaken / 1000);
         mCurrentVideoValues.put(Video.Media.MIME_TYPE, mime);
         mCurrentVideoValues.put(Video.Media.DATA, path);
         mCurrentVideoValues.put(Video.Media.RESOLUTION,
@@ -1331,68 +1356,25 @@
             mCurrentVideoValues.put(Video.Media.LATITUDE, loc.getLatitude());
             mCurrentVideoValues.put(Video.Media.LONGITUDE, loc.getLongitude());
         }
-        mVideoNamer.prepareUri(mContentResolver, mCurrentVideoValues);
         mVideoFilename = tmpPath;
         Log.v(TAG, "New video filename: " + mVideoFilename);
     }
 
-    private boolean addVideoToMediaStore() {
-        boolean fail = false;
+    private void saveVideo() {
         if (mVideoFileDescriptor == null) {
-            mCurrentVideoValues.put(Video.Media.SIZE,
-                    new File(mCurrentVideoFilename).length());
             long duration = SystemClock.uptimeMillis() - mRecordingStartTime;
             if (duration > 0) {
                 if (mCaptureTimeLapse) {
                     duration = getTimeLapseVideoLength(duration);
                 }
-                mCurrentVideoValues.put(Video.Media.DURATION, duration);
             } else {
                 Log.w(TAG, "Video duration <= 0 : " + duration);
             }
-            try {
-                mCurrentVideoUri = mVideoNamer.getUri();
-                //TODO: mActivity.addSecureAlbumItemIfNeeded(true, mCurrentVideoUri);
-
-                // Rename the video file to the final name. This avoids other
-                // apps reading incomplete data.  We need to do it after the
-                // above mVideoNamer.getUri() call, so we are certain that the
-                // previous insert to MediaProvider is completed.
-                String finalName = mCurrentVideoValues.getAsString(
-                        Video.Media.DATA);
-                if (new File(mCurrentVideoFilename).renameTo(new File(finalName))) {
-                    mCurrentVideoFilename = finalName;
-                }
-
-                mContentResolver.update(mCurrentVideoUri, mCurrentVideoValues
-                        , null, null);
-                mActivity.notifyNewMedia(mCurrentVideoUri);
-            } catch (Exception e) {
-                // We failed to insert into the database. This can happen if
-                // the SD card is unmounted.
-                Log.e(TAG, "failed to add video to media store", e);
-                mCurrentVideoUri = null;
-                mCurrentVideoFilename = null;
-                fail = true;
-            } finally {
-                Log.v(TAG, "Current video URI: " + mCurrentVideoUri);
-            }
+            mActivity.getMediaSaveService().addVideo(mCurrentVideoFilename,
+                    duration, mCurrentVideoValues,
+                    mOnVideoSavedListener, mContentResolver);
         }
         mCurrentVideoValues = null;
-        return fail;
-    }
-
-    private void deleteCurrentVideo() {
-        // Remove the video and the uri if the uri is not passed in by intent.
-        if (mCurrentVideoFilename != null) {
-            deleteVideoFile(mCurrentVideoFilename);
-            mCurrentVideoFilename = null;
-            if (mCurrentVideoUri != null) {
-                mContentResolver.delete(mCurrentVideoUri, null, null);
-                mCurrentVideoUri = null;
-            }
-        }
-        mActivity.updateStorageSpaceAndHint();
     }
 
     private void deleteVideoFile(String fileName) {
@@ -1463,6 +1445,7 @@
 
     private void startVideoRecording() {
         Log.v(TAG, "startVideoRecording");
+        mUI.enablePreviewThumb(false);
         // TODO: mActivity.setSwipingEnabled(false);
 
         mActivity.updateStorageSpaceAndHint();
@@ -1471,6 +1454,7 @@
             return;
         }
 
+        if (!mCameraDevice.waitDone()) return;
         mCurrentVideoUri = null;
         if (effectsActive()) {
             initializeEffectsRecording();
@@ -1581,7 +1565,7 @@
             try {
                 if (effectsActive()) {
                     // This is asynchronous, so we can't add to media store now because thumbnail
-                    // may not be ready. In such case addVideoToMediaStore is called later
+                    // may not be ready. In such case saveVideo() is called later
                     // through a callback from the MediaEncoderFilter to EffectsRecorder,
                     // and then to the VideoModule.
                     mEffectsRecorder.stopRecording();
@@ -1629,7 +1613,7 @@
             mUI.setOrientationIndicator(0, true);
             keepScreenOnAwhile();
             if (shouldAddToMediaStoreNow) {
-                if (addVideoToMediaStore()) fail = true;
+                saveVideo();
             }
         }
         // always release media recorder if no effects running
@@ -1888,7 +1872,8 @@
             checkQualityAndStartPreview();
         } else if (effectMsg == EffectsRecorder.EFFECT_MSG_RECORDING_DONE) {
             // This follows the codepath from onStopVideoRecording.
-            if (mEffectsDisplayResult && !addVideoToMediaStore()) {
+            if (mEffectsDisplayResult) {
+                saveVideo();
                 if (mIsVideoCaptureIntent) {
                     if (mQuickCapture) {
                         doReturnToCaller(true);
@@ -1902,7 +1887,6 @@
             // had to wait till the effects recording is complete to do this.
             if (mPaused) {
                 closeVideoFileDescriptor();
-                clearVideoNamer();
             }
         } else if (effectMsg == EffectsRecorder.EFFECT_MSG_PREVIEW_RUNNING) {
             // Enable the shutter button once the preview is complete.
@@ -1994,7 +1978,7 @@
             } else {
                 setCameraParameters();
             }
-            mUI.updateOnScreenIndicators(mParameters);
+            mUI.updateOnScreenIndicators(mParameters, mPreferences);
         }
     }
 
@@ -2030,7 +2014,7 @@
         // Start switch camera animation. Post a message because
         // onFrameAvailable from the old camera may already exist.
         mHandler.sendEmptyMessage(SWITCH_CAMERA_START_ANIMATION);
-        mUI.updateOnScreenIndicators(mParameters);
+        mUI.updateOnScreenIndicators(mParameters, mPreferences);
     }
 
     // Preview texture has been copied. Now camera can be released and the
@@ -2169,7 +2153,7 @@
         Size s = mParameters.getPictureSize();
         mActivity.getMediaSaveService().addImage(
                 data, title, dateTaken, loc, s.width, s.height, orientation,
-                exif, mOnMediaSavedListener, mContentResolver);
+                exif, mOnPhotoSavedListener, mContentResolver);
     }
 
     private boolean resetEffect() {
@@ -2219,90 +2203,6 @@
         editor.apply();
     }
 
-    private void clearVideoNamer() {
-        if (mVideoNamer != null) {
-            mVideoNamer.finish();
-            mVideoNamer = null;
-        }
-    }
-
-    private static class VideoNamer extends Thread {
-        private boolean mRequestPending;
-        private ContentResolver mResolver;
-        private ContentValues mValues;
-        private boolean mStop;
-        private Uri mUri;
-
-        // Runs in main thread
-        public VideoNamer() {
-            start();
-        }
-
-        // Runs in main thread
-        public synchronized void prepareUri(
-                ContentResolver resolver, ContentValues values) {
-            mRequestPending = true;
-            mResolver = resolver;
-            mValues = new ContentValues(values);
-            notifyAll();
-        }
-
-        // Runs in main thread
-        public synchronized Uri getUri() {
-            // wait until the request is done.
-            while (mRequestPending) {
-                try {
-                    wait();
-                } catch (InterruptedException ex) {
-                    // ignore.
-                }
-            }
-            Uri uri = mUri;
-            mUri = null;
-            return uri;
-        }
-
-        // Runs in namer thread
-        @Override
-        public synchronized void run() {
-            while (true) {
-                if (mStop) break;
-                if (!mRequestPending) {
-                    try {
-                        wait();
-                    } catch (InterruptedException ex) {
-                        // ignore.
-                    }
-                    continue;
-                }
-                cleanOldUri();
-                generateUri();
-                mRequestPending = false;
-                notifyAll();
-            }
-            cleanOldUri();
-        }
-
-        // Runs in main thread
-        public synchronized void finish() {
-            mStop = true;
-            notifyAll();
-        }
-
-        // Runs in namer thread
-        private void generateUri() {
-            Uri videoTable = Uri.parse("content://media/external/video/media");
-            mUri = mResolver.insert(videoTable, mValues);
-        }
-
-        // Runs in namer thread
-        private void cleanOldUri() {
-            if (mUri == null) return;
-            mResolver.delete(mUri, null, null);
-            mUri = null;
-        }
-    }
-
     @Override
     public boolean updateStorageHintOnResume() {
         return true;
diff --git a/src/com/android/camera/NewVideoUI.java b/src/com/android/camera/NewVideoUI.java
index ffc1c35..9152690 100644
--- a/src/com/android/camera/NewVideoUI.java
+++ b/src/com/android/camera/NewVideoUI.java
@@ -82,14 +82,14 @@
     private NewPreviewGestures mGestures;
     private View mMenuButton;
     private View mBlocker;
-    private View mOnScreenIndicators;
-    private ImageView mFlashIndicator;
+    private OnScreenIndicators mOnScreenIndicators;
     private RotateLayout mRecordingTimeRect;
     private final Object mLock = new Object();
     private SurfaceTexture mSurfaceTexture;
     private VideoController mController;
     private int mZoomMax;
     private List<Integer> mZoomRatios;
+    private View mPreviewThumb;
 
     private SurfaceView mSurfaceView = null;
     private int mPreviewWidth = 0;
@@ -168,8 +168,9 @@
         });
 
         mCameraControls = mActivity.findViewById(R.id.camera_controls);
-        mOnScreenIndicators = mActivity.findViewById(R.id.on_screen_indicators);
-        mFlashIndicator = (ImageView) mActivity.findViewById(R.id.menu_flash_indicator);
+        mOnScreenIndicators = new OnScreenIndicators(mActivity,
+                mActivity.findViewById(R.id.on_screen_indicators));
+        mOnScreenIndicators.resetToDefault();
         if (mController.isVideoCaptureIntent()) {
             hideSwitcher();
             mActivity.getLayoutInflater().inflate(R.layout.review_module_control, (ViewGroup) mCameraControls);
@@ -362,6 +363,14 @@
             mRenderOverlay.setGestures(mGestures);
         }
         mGestures.setRenderOverlay(mRenderOverlay);
+
+        mPreviewThumb = mActivity.findViewById(R.id.preview_thumb);
+        mPreviewThumb.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                // TODO: Go to filmstrip view
+            }
+        });
     }
 
     public void setPrefChangedListener(OnPreferenceChangedListener listener) {
@@ -383,22 +392,12 @@
         mLabelsLinearLayout = (LinearLayout) mRootView.findViewById(R.id.labels);
     }
 
-    public void updateOnScreenIndicators(Parameters param) {
-        if (param == null) return;
-        String value = param.getFlashMode();
-        if (mFlashIndicator == null) return;
-        if (value == null || Parameters.FLASH_MODE_OFF.equals(value)) {
-            mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_off);
-        } else {
-            if (Parameters.FLASH_MODE_AUTO.equals(value)) {
-                mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_auto);
-            } else if (Parameters.FLASH_MODE_ON.equals(value)
-                    || Parameters.FLASH_MODE_TORCH.equals(value)) {
-                mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_on);
-            } else {
-                mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_off);
-            }
-        }
+    public void updateOnScreenIndicators(Parameters param, ComboPreferences prefs) {
+      mOnScreenIndicators.updateFlashOnScreenIndicator(param.getFlashMode());
+      boolean location = RecordLocationPreference.get(
+              prefs, mActivity.getContentResolver());
+      mOnScreenIndicators.updateLocationIndicator(location);
+
     }
 
     public void setAspectRatio(double ratio) {
@@ -434,6 +433,9 @@
     }
 
     public void dismissPopup(boolean topLevelPopupOnly, boolean fullScreen) {
+        // In review mode, we do not want to bring up the camera UI
+        if (mController.isInReviewMode()) return;
+
         if (fullScreen) {
             showUI();
             mBlocker.setVisibility(View.VISIBLE);
@@ -611,6 +613,17 @@
         return mTextureView.getVisibility() == View.VISIBLE;
     }
 
+    /**
+     * Enable or disable the preview thumbnail for click events.
+     */
+    public void enablePreviewThumb(boolean enabled) {
+        if (enabled) {
+            mPreviewThumb.setVisibility(View.VISIBLE);
+        } else {
+            mPreviewThumb.setVisibility(View.GONE);
+        }
+    }
+
     private class ZoomChangeListener implements ZoomRenderer.OnZoomChangedListener {
         @Override
         public void onZoomValueChanged(int index) {