Allow saving panorama after the activity is paused. (do not merge)

bug: 5541182
Change-Id: I42237d8dd4f8c39ddc2041ae32b886328a9c45c0
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 1a64093..ee57c31 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -61,11 +61,16 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
+        <!-- Suppose users enter panorama from launcher, turn off the screen,
+             turn on the screen, and enter the camera from the lock screen.
+             They can switch to panorama from there. Use singleTask so there
+             will be only one panorama activity. -->
         <activity android:name="com.android.camera.panorama.PanoramaActivity"
                 android:label="@string/pano_dialog_title"
                 android:configChanges="orientation|screenSize|keyboardHidden"
                 android:clearTaskOnLaunch="true"
-                android:windowSoftInputMode="stateAlwaysHidden|adjustPan">
+                android:windowSoftInputMode="stateAlwaysHidden|adjustPan"
+                android:launchMode="singleTask">
         </activity>
     </application>
 </manifest>
diff --git a/src/com/android/camera/Thumbnail.java b/src/com/android/camera/Thumbnail.java
index 165614b..7775c37 100644
--- a/src/com/android/camera/Thumbnail.java
+++ b/src/com/android/camera/Thumbnail.java
@@ -52,6 +52,10 @@
     // whether this thumbnail is read from file
     private boolean mFromFile = false;
 
+    // Camera, VideoCamera, and Panorama share the same thumbnail. Use sLock
+    // to serialize the access.
+    private static Object sLock = new Object();
+
     public Thumbnail(Uri uri, Bitmap bitmap, int orientation) {
         mUri = uri;
         mBitmap = rotateImage(bitmap, orientation);
@@ -100,19 +104,21 @@
         FileOutputStream f = null;
         BufferedOutputStream b = null;
         DataOutputStream d = null;
-        try {
-            f = new FileOutputStream(file);
-            b = new BufferedOutputStream(f, BUFSIZE);
-            d = new DataOutputStream(b);
-            d.writeUTF(mUri.toString());
-            mBitmap.compress(Bitmap.CompressFormat.JPEG, 90, d);
-            d.close();
-        } catch (IOException e) {
-            Log.e(TAG, "Fail to store bitmap. path=" + file.getPath(), e);
-        } finally {
-            Util.closeSilently(f);
-            Util.closeSilently(b);
-            Util.closeSilently(d);
+        synchronized (sLock) {
+            try {
+                f = new FileOutputStream(file);
+                b = new BufferedOutputStream(f, BUFSIZE);
+                d = new DataOutputStream(b);
+                d.writeUTF(mUri.toString());
+                mBitmap.compress(Bitmap.CompressFormat.JPEG, 90, d);
+                d.close();
+            } catch (IOException e) {
+                Log.e(TAG, "Fail to store bitmap. path=" + file.getPath(), e);
+            } finally {
+                Util.closeSilently(f);
+                Util.closeSilently(b);
+                Util.closeSilently(d);
+            }
         }
     }
 
@@ -124,20 +130,22 @@
         FileInputStream f = null;
         BufferedInputStream b = null;
         DataInputStream d = null;
-        try {
-            f = new FileInputStream(file);
-            b = new BufferedInputStream(f, BUFSIZE);
-            d = new DataInputStream(b);
-            uri = Uri.parse(d.readUTF());
-            bitmap = BitmapFactory.decodeStream(d);
-            d.close();
-        } catch (IOException e) {
-            Log.i(TAG, "Fail to load bitmap. " + e);
-            return null;
-        } finally {
-            Util.closeSilently(f);
-            Util.closeSilently(b);
-            Util.closeSilently(d);
+        synchronized (sLock) {
+            try {
+                f = new FileInputStream(file);
+                b = new BufferedInputStream(f, BUFSIZE);
+                d = new DataInputStream(b);
+                uri = Uri.parse(d.readUTF());
+                bitmap = BitmapFactory.decodeStream(d);
+                d.close();
+            } catch (IOException e) {
+                Log.i(TAG, "Fail to load bitmap. " + e);
+                return null;
+            } finally {
+                Util.closeSilently(f);
+                Util.closeSilently(b);
+                Util.closeSilently(d);
+            }
         }
         Thumbnail thumbnail = createThumbnail(uri, bitmap, 0);
         if (thumbnail != null) thumbnail.setFromFile(true);
diff --git a/src/com/android/camera/panorama/PanoramaActivity.java b/src/com/android/camera/panorama/PanoramaActivity.java
index 1a5a5aa..e45a841 100755
--- a/src/com/android/camera/panorama/PanoramaActivity.java
+++ b/src/com/android/camera/panorama/PanoramaActivity.java
@@ -59,6 +59,7 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
+import android.os.PowerManager;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.Menu;
@@ -152,6 +153,7 @@
     private int mCaptureState;
     private SensorManager mSensorManager;
     private Sensor mSensor;
+    private PowerManager.WakeLock mPartialWakeLock;
     private ModePicker mModePicker;
     private MosaicFrameProcessor mMosaicFrameProcessor;
     private long mTimeTaken;
@@ -263,6 +265,8 @@
         if (mSensor == null) {
             mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
         }
+        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+        mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Panorama");
 
         mOrientationEventListener = new PanoOrientationEventListener(this);
 
@@ -287,6 +291,9 @@
                         break;
                     case MSG_RESET_TO_PREVIEW_WITH_THUMBNAIL:
                         onBackgroundThreadFinished();
+                        // If the activity is paused, save the thumbnail to the file here.
+                        // If not, it will be saved in onPause.
+                        if (mPausing) saveThumbnailToFile();
                         // Set the thumbnail bitmap here because mThumbnailView must be accessed
                         // from the UI thread.
                         updateThumbnailButton();
@@ -439,7 +446,11 @@
         runOnUiThread(new Runnable() {
             @Override
             public void run() {
-                if (!mPausing) {
+                // If panorama is generating low res or high res mosaic, it
+                // means users exit and come back to panorama. Do not start the
+                // preview. Preview will be started after final mosaic is
+                // generated.
+                if (!mPausing && !mThreadRunning) {
                     startCameraPreview();
                 }
             }
@@ -800,11 +811,23 @@
         }
     }
 
+    private void saveThumbnailToFile() {
+        if (mThumbnail != null && !mThumbnail.fromFile()) {
+            mThumbnail.saveTo(new File(getFilesDir(), Thumbnail.LAST_THUMB_FILENAME));
+        }
+    }
+
     public void saveHighResMosaic() {
         runBackgroundThread(new Thread() {
             @Override
             public void run() {
-                MosaicJpeg jpeg = generateFinalMosaic(true);
+                mPartialWakeLock.acquire();
+                MosaicJpeg jpeg;
+                try {
+                    jpeg = generateFinalMosaic(true);
+                } finally {
+                    mPartialWakeLock.release();
+                }
 
                 if (jpeg == null) {  // Cancelled by user.
                     mMainHandler.sendEmptyMessage(MSG_RESET_TO_PREVIEW);
@@ -964,7 +987,6 @@
         super.onPause();
 
         mPausing = true;
-        cancelHighResComputation();
         // Stop the capturing first.
         if (mCaptureState == CAPTURE_STATE_MOSAIC) {
             stopCapture(true);
@@ -972,9 +994,7 @@
         }
         if (mSharePopup != null) mSharePopup.dismiss();
 
-        if (mThumbnail != null && !mThumbnail.fromFile()) {
-            mThumbnail.saveTo(new File(getFilesDir(), Thumbnail.LAST_THUMB_FILENAME));
-        }
+        saveThumbnailToFile();
 
         releaseCamera();
         mMosaicView.onPause();
@@ -1105,6 +1125,14 @@
         if (mCaptureState != CAPTURE_STATE_MOSAIC) keepScreenOnAwhile();
     }
 
+    @Override
+    public void onBackPressed() {
+        // If panorama is generating low res or high res mosaic, ignore back
+        // key. So the activity will not be destroyed.
+        if (mThreadRunning) return;
+        super.onBackPressed();
+    }
+
     private void resetScreenOn() {
         mMainHandler.removeMessages(MSG_CLEAR_SCREEN_DELAY);
         getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);