Merge "Set the orientation of a panorama image." into ics-mr1
diff --git a/src/com/android/camera/Storage.java b/src/com/android/camera/Storage.java
index 38a6d48..ea281a9 100644
--- a/src/com/android/camera/Storage.java
+++ b/src/com/android/camera/Storage.java
@@ -52,7 +52,7 @@
     public static Uri addImage(ContentResolver resolver, String title, long date,
                 Location location, int orientation, byte[] jpeg, int width, int height) {
         // Save the image.
-        String path = DIRECTORY + '/' + title + ".jpg";
+        String path = generateFilepath(title);
         FileOutputStream out = null;
         try {
             out = new FileOutputStream(path);
@@ -98,6 +98,10 @@
         return uri;
     }
 
+    public static String generateFilepath(String title) {
+        return DIRECTORY + '/' + title + ".jpg";
+    }
+
     public static long getAvailableSpace() {
         String state = Environment.getExternalStorageState();
         Log.d(TAG, "External storage state=" + state);
diff --git a/src/com/android/camera/Util.java b/src/com/android/camera/Util.java
index ba8a4f7..d6ebb0a 100644
--- a/src/com/android/camera/Util.java
+++ b/src/com/android/camera/Util.java
@@ -350,6 +350,12 @@
         return result;
     }
 
+    public static int getCameraOrientation(int cameraId) {
+        Camera.CameraInfo info = new Camera.CameraInfo();
+        Camera.getCameraInfo(cameraId, info);
+        return info.orientation;
+    }
+
     public static int roundOrientation(int orientation, int orientationHistory) {
         boolean changeOrientation = false;
         if (orientationHistory == OrientationEventListener.ORIENTATION_UNKNOWN) {
diff --git a/src/com/android/camera/panorama/PanoramaActivity.java b/src/com/android/camera/panorama/PanoramaActivity.java
index e4c8d98..6978477 100755
--- a/src/com/android/camera/panorama/PanoramaActivity.java
+++ b/src/com/android/camera/panorama/PanoramaActivity.java
@@ -52,6 +52,7 @@
 import android.hardware.Camera.Sound;
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
+import android.media.ExifInterface;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -70,6 +71,7 @@
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.IOException;
 import java.util.List;
 
 /**
@@ -172,6 +174,8 @@
     // The value could be 0, 90, 180, 270 for the 4 different orientations measured in clockwise
     // respectively.
     private int mDeviceOrientation;
+    private int mDeviceOrientationAtCapture;
+    private int mCameraOrientation;
     private int mOrientationCompensation;
 
     private RotateDialogController mRotateDialog;
@@ -338,7 +342,9 @@
 
     private void openCamera() {
         try {
-            mCameraDevice = Util.openCamera(this, CameraHolder.instance().getBackCameraId());
+            int backCameraId = CameraHolder.instance().getBackCameraId();
+            mCameraDevice = Util.openCamera(this, backCameraId);
+            mCameraOrientation = Util.getCameraOrientation(backCameraId);
         } catch (CameraHardwareException e) {
             Util.showErrorAndFinish(this, R.string.cannot_connect_camera);
             return;
@@ -568,6 +574,7 @@
         mPanoProgressBar.setIndicatorWidth(20);
         mPanoProgressBar.setMaxProgress(DEFAULT_SWEEP_ANGLE);
         mPanoProgressBar.setVisibility(View.VISIBLE);
+        mDeviceOrientationAtCapture = mDeviceOrientation;
         keepScreenOn();
     }
 
@@ -799,7 +806,13 @@
                 } else if (!jpeg.isValid) {  // Error when generating mosaic.
                     mMainHandler.sendEmptyMessage(MSG_GENERATE_FINAL_MOSAIC_ERROR);
                 } else {
-                    int orientation = Exif.getOrientation(jpeg.data);
+                    // The panorama image returned from the library is orientated based on the
+                    // natural orientation of a camera. We need to set an orientation for the image
+                    // in its EXIF header, so the image can be displayed correctly.
+                    // The orientation is calculated from compensating the
+                    // device orientation at capture and the camera orientation respective to
+                    // the natural orientation of the device.
+                    int orientation = (mDeviceOrientationAtCapture + mCameraOrientation) % 360;
                     Uri uri = savePanorama(jpeg.data, jpeg.width, jpeg.height, orientation);
                     if (uri != null) {
                         // Create a thumbnail whose width or height is equal or bigger
@@ -889,14 +902,42 @@
 
     private Uri savePanorama(byte[] jpegData, int width, int height, int orientation) {
         if (jpegData != null) {
-            String imagePath = PanoUtil.createName(
+            String filename = PanoUtil.createName(
                     getResources().getString(R.string.pano_file_name_format), mTimeTaken);
-            return Storage.addImage(getContentResolver(), imagePath, mTimeTaken, null,
+            Uri uri = Storage.addImage(getContentResolver(), filename, mTimeTaken, null,
                     orientation, jpegData, width, height);
+            if (uri != null && orientation != 0) {
+                String filepath = Storage.generateFilepath(filename);
+                try {
+                    // Save the orientation in EXIF.
+                    ExifInterface exif = new ExifInterface(filepath);
+                    exif.setAttribute(ExifInterface.TAG_ORIENTATION,
+                            getExifOrientation(orientation));
+                    exif.saveAttributes();
+                } catch (IOException e) {
+                    Log.e(TAG, "cannot set exif data: " + filepath);
+                }
+            }
+            return uri;
         }
         return null;
     }
 
+    private static String getExifOrientation(int orientation) {
+        switch (orientation) {
+            case 0:
+                return String.valueOf(ExifInterface.ORIENTATION_NORMAL);
+            case 90:
+                return String.valueOf(ExifInterface.ORIENTATION_ROTATE_90);
+            case 180:
+                return String.valueOf(ExifInterface.ORIENTATION_ROTATE_180);
+            case 270:
+                return String.valueOf(ExifInterface.ORIENTATION_ROTATE_270);
+            default:
+                throw new AssertionError("invalid: " + orientation);
+        }
+    }
+
     private void clearMosaicFrameProcessorIfNeeded() {
         if (!mPausing || mThreadRunning) return;
         mMosaicFrameProcessor.clear();