Add ability to add a ready-state changed listener to the camera.

  Bug: 16945820
  Bug: 16948752

This enables us to hook up memory intense cameras and make sure we don't
activate the shutter button if the camera is not ready.

Change-Id: I70a31c76e063d775c844404ee9e20e583fa233e3
diff --git a/src/com/android/camera/CaptureModule.java b/src/com/android/camera/CaptureModule.java
index 30b24cf..f3cb1df 100644
--- a/src/com/android/camera/CaptureModule.java
+++ b/src/com/android/camera/CaptureModule.java
@@ -17,7 +17,6 @@
 package com.android.camera;
 
 import android.app.Activity;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
@@ -94,6 +93,7 @@
         ModuleController,
         OneCamera.PictureCallback,
         OneCamera.FocusStateListener,
+        OneCamera.ReadyStateChangedListener,
         PreviewStatusListener.PreviewAreaChangedListener,
         RemoteCameraModule,
         SensorEventListener,
@@ -200,8 +200,6 @@
     /** Application context. */
     private final Context mContext;
     private CaptureModuleUI mUI;
-    /** Your standard content resolver. */
-    private ContentResolver mContentResolver;
     /** The camera manager used to open cameras. */
     private OneCameraManager mCameraManager;
     /** The currently opened camera device. */
@@ -311,7 +309,6 @@
         mIsResumeFromLockScreen = isResumeFromLockscreen(activity);
         mMainHandler = new Handler(activity.getMainLooper());
         mCameraManager = mAppController.getCameraManager();
-        mContentResolver = activity.getContentResolver();
         mDisplayRotation = CameraUtil.getDisplayRotation(mContext);
         mCameraFacing = getFacingFromCameraId(mSettingsManager.getInteger(
                 mAppController.getModuleScope(),
@@ -341,7 +338,6 @@
         if (mCamera == null) {
             return;
         }
-        mAppController.setShutterEnabled(false);
 
         // Set up the capture session.
         long sessionTime = System.currentTimeMillis();
@@ -464,6 +460,7 @@
                         Log.d(TAG, "Ready for capture.");
                         onPreviewStarted();
                         mCamera.setFocusStateListener(CaptureModule.this);
+                        mCamera.setReadyStateChangedListener(CaptureModule.this);
                     }
                 });
             }
@@ -520,6 +517,8 @@
         mAppController.addPreviewAreaSizeChangedListener(this);
         resetDefaultBufferSize();
         getServices().getRemoteShutterListener().onModuleReady(this);
+        // TODO: Check if we can really take a photo right now (memory, camera
+        // state, ... ).
         mAppController.setShutterEnabled(true);
     }
 
@@ -716,6 +715,11 @@
     }
 
     @Override
+    public void onReadyStateChanged(boolean readyForCapture) {
+        mAppController.setShutterEnabled(readyForCapture);
+    }
+
+    @Override
     public String getPeekAccessibilityString() {
         return mAppController.getAndroidContext()
                 .getResources().getString(R.string.photo_accessibility_peek);
@@ -728,8 +732,6 @@
 
     @Override
     public void onPictureTaken(CaptureSession session) {
-        // TODO, enough memory available? ProcessingService status, etc.
-        mAppController.setShutterEnabled(true);
     }
 
     @Override
@@ -744,7 +746,6 @@
 
     @Override
     public void onPictureTakenFailed() {
-        // TODO
     }
 
     @Override
diff --git a/src/com/android/camera/one/AbstractOneCamera.java b/src/com/android/camera/one/AbstractOneCamera.java
index d21e9f4..6bd75cf 100644
--- a/src/com/android/camera/one/AbstractOneCamera.java
+++ b/src/com/android/camera/one/AbstractOneCamera.java
@@ -27,6 +27,7 @@
 public abstract class AbstractOneCamera implements OneCamera {
     protected CameraErrorListener mCameraErrorListener;
     protected FocusStateListener mFocusStateListener;
+    protected ReadyStateChangedListener mReadyStateChangedListener;
 
     @Override
     public final void setCameraErrorListener(CameraErrorListener listener) {
@@ -38,6 +39,11 @@
         mFocusStateListener = listener;
     }
 
+    @Override
+    public void setReadyStateChangedListener(ReadyStateChangedListener listener) {
+        mReadyStateChangedListener = listener;
+    }
+
     /**
      * Create a directory we can use to store debugging information during Gcam
      * captures.
@@ -64,4 +70,13 @@
         String destFolderPath = destFolder.getAbsolutePath();
         return destFolderPath;
     }
+
+    /**
+     * If set, tells the ready state changed listener the new state.
+     */
+    protected void broadcastReadyState(boolean readyForCapture) {
+        if (mReadyStateChangedListener != null) {
+            mReadyStateChangedListener.onReadyStateChanged(readyForCapture);
+        }
+    }
 }
diff --git a/src/com/android/camera/one/OneCamera.java b/src/com/android/camera/one/OneCamera.java
index 555f6cb..d622210 100644
--- a/src/com/android/camera/one/OneCamera.java
+++ b/src/com/android/camera/one/OneCamera.java
@@ -53,7 +53,7 @@
         SCANNING,
         /** Indicates scan success (camera in focus). */
         STOPPED_FOCUSED,
-        /** Indicates scan or other failure.  */
+        /** Indicates scan or other failure. */
         STOPPED_UNFOCUSED
     }
 
@@ -114,6 +114,18 @@
     }
 
     /**
+     * Classes implementing this interface can be informed when the state of
+     * capture changes.
+     */
+    public static interface ReadyStateChangedListener {
+        /**
+         * Called when the camera is either ready or not ready to take a picture
+         * right now.
+         */
+        public void onReadyStateChanged(boolean readyForCapture);
+    }
+
+    /**
      * A class implementing this interface can be passed into the call to take a
      * picture in order to receive the resulting image or updated about the
      * progress.
@@ -135,7 +147,7 @@
         /**
          * Called when the picture has been saved to disk.
          *
-         *  @param uri the URI of the stored data.
+         * @param uri the URI of the stored data.
          */
         public void onPictureSaved(Uri uri);
 
@@ -165,7 +177,7 @@
 
     /**
      * Classes implementing this interface will be called when the state of the
-     * focus changes.  Guaranteed not to stay stuck in scanning state past some
+     * focus changes. Guaranteed not to stay stuck in scanning state past some
      * reasonable timeout even if Camera API is stuck.
      */
     public static interface FocusStateListener {
@@ -238,8 +250,8 @@
     /**
      * Meters and triggers auto focus scan with ROI around tap point.
      * <p/>
-     * Normalized coordinates are referenced to portrait preview window
-     * with 0,0 top left and 1,1 bottom right.  Rotation has no effect.
+     * Normalized coordinates are referenced to portrait preview window with 0,0
+     * top left and 1,1 bottom right. Rotation has no effect.
      *
      * @param nx normalized x coordinate.
      * @param nx normalized y coordinate.
@@ -267,6 +279,12 @@
     public void setFocusStateListener(FocusStateListener listener);
 
     /**
+     * Sets or replaces a listener that is called whenever the state of the
+     * camera changes to be either ready or not ready to take another picture.
+     */
+    public void setReadyStateChangedListener(ReadyStateChangedListener listener);
+
+    /**
      * Starts a preview stream and renders it to the given surface.
      */
     public void startPreview(Surface surface, CaptureReadyCallback listener);
diff --git a/src/com/android/camera/one/v2/OneCameraImpl.java b/src/com/android/camera/one/v2/OneCameraImpl.java
index f353c5f..d212fa5 100644
--- a/src/com/android/camera/one/v2/OneCameraImpl.java
+++ b/src/com/android/camera/one/v2/OneCameraImpl.java
@@ -229,6 +229,7 @@
                     byte[] imageBytes = acquireJpegBytesAndClose(reader);
                     // TODO: The savePicture call here seems to block UI thread.
                     savePicture(imageBytes, capture.parameters, capture.session);
+                    broadcastReadyState(true);
                     capture.parameters.callback.onPictureTaken(capture.session);
                 }
             };
@@ -266,6 +267,9 @@
             return;
         }
 
+        // Wait until the picture comes back.
+        broadcastReadyState(false);
+
         mTakePictureRunnable = new Runnable() {
             @Override
             public void run() {
@@ -336,6 +340,7 @@
             mCaptureSession.capture(request, mAutoFocusStateListener, mCameraHandler);
         } catch (CameraAccessException e) {
             Log.e(TAG, "Could not access camera for still image capture.");
+            broadcastReadyState(true);
             params.callback.onPictureTakenFailed();
             return;
         }