fix free space calculation / querying timing problem

also, be (a little) more agressive about updating
available storage space so app generally has the most
up-to-date space calculation more often

bug: 14275001
Change-Id: I1aa91419be017f1248bb4b3b0aa805bd4e1b05c6
diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java
index a9a6526..bd27f75 100644
--- a/src/com/android/camera/CameraActivity.java
+++ b/src/com/android/camera/CameraActivity.java
@@ -1051,6 +1051,7 @@
 
     @Override
     public void notifyNewMedia(Uri uri) {
+        updateStorageSpaceAndHint(null);
         ContentResolver cr = getContentResolver();
         String mimeType = cr.getType(uri);
         if (LocalDataUtil.isMimeTypeVideo(mimeType)) {
@@ -1480,6 +1481,7 @@
         Log.v(TAG, "Build info: " + Build.DISPLAY);
 
         mPaused = false;
+        updateStorageSpaceAndHint(null);
 
         mLastLayoutOrientation = getResources().getConfiguration().orientation;
 
@@ -1770,27 +1772,42 @@
         }
     }
 
-    protected void updateStorageSpaceAndHint() {
+    protected interface OnStorageUpdateDoneListener {
+        public void onStorageUpdateDone(long bytes);
+    }
+
+    protected void updateStorageSpaceAndHint(final OnStorageUpdateDoneListener callback) {
         /*
          * We execute disk operations on a background thread in order to
          * free up the UI thread.  Synchronizing on the lock below ensures
          * that when getStorageSpaceBytes is called, the main thread waits
          * until this method has completed.
+         *
+         * However, .execute() does not ensure this execution block will be
+         * run right away (.execute() schedules this AsyncTask for sometime
+         * in the future. executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
+         * tries to execute the task in parellel with other AsyncTasks, but
+         * there's still no guarantee).
+         * e.g. don't call this then immediately call getStorageSpaceBytes().
+         * Instead, pass in an OnStorageUpdateDoneListener.
          */
-        (new AsyncTask<Void, Void, Void>() {
+        (new AsyncTask<Void, Void, Long>() {
             @Override
-            protected Void doInBackground(Void ... arg) {
+            protected Long doInBackground(Void ... arg) {
                 synchronized (mStorageSpaceLock) {
                     mStorageSpaceBytes = Storage.getAvailableSpace();
+                    return mStorageSpaceBytes;
                 }
-                return null;
             }
 
             @Override
-            protected void onPostExecute(Void ignore) {
-                updateStorageHint(getStorageSpaceBytes());
+            protected void onPostExecute(Long bytes) {
+                updateStorageHint(bytes);
+                if (callback != null) {
+                    callback.onStorageUpdateDone(bytes);
+                }
             }
-        }).execute();
+        }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
     }
 
     protected void updateStorageHint(long storageSpace) {
diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java
index c92dfb4..28ed27d 100644
--- a/src/com/android/camera/PhotoModule.java
+++ b/src/com/android/camera/PhotoModule.java
@@ -612,7 +612,7 @@
         mFirstTimeInitialized = true;
         addIdleHandler();
 
-        mActivity.updateStorageSpaceAndHint();
+        mActivity.updateStorageSpaceAndHint(null);
     }
 
     // If the activity is paused and resumed, this method will be called in
@@ -832,7 +832,7 @@
             // latency. It's true that someone else could write to the SD card
             // in the mean time and fill it, but that could have happened
             // between the shutter press and saving the JPEG too.
-            mActivity.updateStorageSpaceAndHint();
+            mActivity.updateStorageSpaceAndHint(null);
 
             long now = System.currentTimeMillis();
             mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java
index 23a9f5b..70cc12e 100644
--- a/src/com/android/camera/VideoModule.java
+++ b/src/com/android/camera/VideoModule.java
@@ -329,6 +329,8 @@
         SettingsManager settingsManager = mActivity.getSettingsManager();
         mCameraId = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_CAMERA_ID));
 
+        mActivity.updateStorageSpaceAndHint(null);
+
         /*
          * To reduce startup time, we start the preview in another thread.
          * We make sure the preview is started at the end of onCreate.
@@ -352,7 +354,6 @@
 
         mShutterIconId = CameraUtil.getCameraShutterIconId(
                 mAppController.getCurrentModuleIndex(), mAppController.getAndroidContext());
-
     }
 
     @Override
@@ -1192,7 +1193,7 @@
         if (what == MediaRecorder.MEDIA_RECORDER_ERROR_UNKNOWN) {
             // We may have run out of space on the sdcard.
             stopVideoRecording();
-            mActivity.updateStorageSpaceAndHint();
+            mActivity.updateStorageSpaceAndHint(null);
         }
     }
 
@@ -1235,57 +1236,60 @@
         mUI.showFocusUI(false);
         mUI.showVideoRecordingHints(false);
 
-        mActivity.updateStorageSpaceAndHint();
-        if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
-            Log.w(TAG, "Storage issue, ignore the start request");
-            return;
-        }
+        mActivity.updateStorageSpaceAndHint(new CameraActivity.OnStorageUpdateDoneListener() {
+            @Override
+            public void onStorageUpdateDone(long bytes) {
+                if (bytes <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
+                    Log.w(TAG, "Storage issue, ignore the start request");
+                } else {
+                    //??
+                    //if (!mCameraDevice.waitDone()) return;
+                    mCurrentVideoUri = null;
 
-        //??
-        //if (!mCameraDevice.waitDone()) return;
-        mCurrentVideoUri = null;
+                    initializeRecorder();
+                    if (mMediaRecorder == null) {
+                        Log.e(TAG, "Fail to initialize media recorder");
+                        return;
+                    }
 
-        initializeRecorder();
-        if (mMediaRecorder == null) {
-            Log.e(TAG, "Fail to initialize media recorder");
-            return;
-        }
+                    pauseAudioPlayback();
 
-        pauseAudioPlayback();
+                    try {
+                        mMediaRecorder.start(); // Recording is now started
+                    } catch (RuntimeException e) {
+                        Log.e(TAG, "Could not start media recorder. ", e);
+                        releaseMediaRecorder();
+                        // If start fails, frameworks will not lock the camera for us.
+                        mCameraDevice.lock();
+                        return;
+                    }
+                    mAppController.getCameraAppUI().setSwipeEnabled(false);
 
-        try {
-            mMediaRecorder.start(); // Recording is now started
-        } catch (RuntimeException e) {
-            Log.e(TAG, "Could not start media recorder. ", e);
-            releaseMediaRecorder();
-            // If start fails, frameworks will not lock the camera for us.
-            mCameraDevice.lock();
-            return;
-        }
-        mAppController.getCameraAppUI().setSwipeEnabled(false);
+                    // The parameters might have been altered by MediaRecorder already.
+                    // We need to force mCameraDevice to refresh before getting it.
+                    mCameraDevice.refreshParameters();
+                    // The parameters may have been changed by MediaRecorder upon starting
+                    // recording. We need to alter the parameters if we support camcorder
+                    // zoom. To reduce latency when setting the parameters during zoom, we
+                    // update mParameters here once.
+                    mParameters = mCameraDevice.getParameters();
 
-        // The parameters might have been altered by MediaRecorder already.
-        // We need to force mCameraDevice to refresh before getting it.
-        mCameraDevice.refreshParameters();
-        // The parameters may have been changed by MediaRecorder upon starting
-        // recording. We need to alter the parameters if we support camcorder
-        // zoom. To reduce latency when setting the parameters during zoom, we
-        // update mParameters here once.
-        mParameters = mCameraDevice.getParameters();
+                    mMediaRecorderRecording = true;
+                    mActivity.lockOrientation();
+                    mRecordingStartTime = SystemClock.uptimeMillis();
 
-        mMediaRecorderRecording = true;
-        mActivity.lockOrientation();
-        mRecordingStartTime = SystemClock.uptimeMillis();
+                    // A special case of mode options closing: during capture it should
+                    // not be possible to change mode state.
+                    mAppController.getCameraAppUI().hideModeOptions();
+                    mAppController.getCameraAppUI().animateBottomBarToVideoStop(R.drawable.ic_stop);
+                    mUI.showRecordingUI(true);
 
-        // A special case of mode options closing: during capture it should
-        // not be possible to change mode state.
-        mAppController.getCameraAppUI().hideModeOptions();
-        mAppController.getCameraAppUI().animateBottomBarToVideoStop(R.drawable.ic_stop);
-        mUI.showRecordingUI(true);
-
-        setFocusParameters();
-        updateRecordingTime();
-        mActivity.enableKeepScreenOn(true);
+                    setFocusParameters();
+                    updateRecordingTime();
+                    mActivity.enableKeepScreenOn(true);
+                }
+            }
+        });
     }
 
     private Bitmap getVideoThumbnail() {
@@ -1387,6 +1391,13 @@
             // by MediaRecorder.
             mParameters = mCameraDevice.getParameters();
         }
+
+        // Check this in advance of each shot so we don't add to shutter
+        // latency. It's true that someone else could write to the SD card
+        // in the mean time and fill it, but that could have happened
+        // between the shutter press and saving the file too.
+        mActivity.updateStorageSpaceAndHint(null);
+
         return fail;
     }