Search code examples
androidandroid-cameraandroid-orientation

Android Camera image rotated after save


I'm developing an application that when i press a button launches an activity (for result) that will take the picture and store it. Than the previous activity will display the photo. I'm using a CameraPreview class and a CameraActivity activity to achieve it. Everything must be in portrait mode and it all works perfectly: i start the new activity with the preview, take the picture in portrait mode and return to the previous activity with the picture shown in a box.

The only problem is that the picture is shown (and stored) turned 90° counterclockwise. How can I avoid it?

Here's my Activity:

public class CameraActivity extends Activity {

    private Camera mCamera;
    private CameraPreview mPreview;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera);

        // Create an instance of Camera
        mCamera = getCameraInstance();

        mCamera.setDisplayOrientation(90);
        Parameters params = mCamera.getParameters();
        params.setRotation(90);
        params.setPictureSize(100, 200);

        mCamera.setParameters(params);

        // Create our Preview view and set it as the content of our activity.
        mPreview = new CameraPreview(this, mCamera);
        FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
        preview.addView(mPreview);
    }

    private PictureCallback mPicture = new PictureCallback() {

        @Override
        public void onPictureTaken(byte[] data, Camera camera) {

            File pictureFile = getOutputMediaFile();
            if (pictureFile == null){
                Log.d(TAG, "Error creating media file, check storage permissions");
                Intent intent = getIntent();
                setResult(RESULT_CANCELED, intent);
                finish();

                if (mCamera != null){
                    mCamera.release(); // release the camera for other applications
                    mCamera = null;
                }
                return;
            }

            try {
                FileOutputStream fos = new FileOutputStream(pictureFile);
                fos.write(data);
                fos.close();

                Intent intent = getIntent();
                setResult(RESULT_OK, intent);
                finish();

                if (mCamera != null){
                    mCamera.release(); // release the camera for other applications
                    mCamera = null;
                }
                return;

            } catch (FileNotFoundException e) {
                Log.d(TAG, "File not found: " + e.getMessage());
            } catch (IOException e) {
                Log.d(TAG, "Error accessing file: " + e.getMessage());
            }
            Intent intent = getIntent();
            setResult(RESULT_CANCELED, intent);
            finish();

            if (mCamera != null){
                mCamera.release(); // release the camera for other applications
                mCamera = null;
            }
            return;
        }
    };

    public static Camera getCameraInstance(){
        Camera c = null;
        try {
            c = Camera.open(); // attempt to get a Camera instance
        }
        catch (Exception e){
            // Camera is not available (in use or does not exist)
        }
        return c; // returns null if camera is unavailable
    }

    public void scatta(View v) {

        mCamera.takePicture(null, null, mPicture);
    }

    private File getOutputMediaFile(){

        HERE I RETURN A VALID FILE.

        return file;
    }
}

And here's the Preview:

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
    private SurfaceHolder mHolder;
    private Camera mCamera;

    public CameraPreview(Context context, Camera camera) {
        super(context);
        mCamera = camera;

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        mHolder = getHolder();
        mHolder.addCallback(this);
        // deprecated setting, but required on Android versions prior to 3.0
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void surfaceCreated(SurfaceHolder holder) {
        // The Surface has been created, now tell the camera where to draw the preview.
        try {
            mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();
        } catch (IOException e) {
            Log.d(TAG, "Error setting camera preview: " + e.getMessage());
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // empty. Take care of releasing the Camera preview in your activity.
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        // If your preview can change or rotate, take care of those events here.
        // Make sure to stop the preview before resizing or reformatting it.

        if (mHolder.getSurface() == null){
          // preview surface does not exist
          return;
        }

        // stop preview before making changes
        try {
            mCamera.stopPreview();
        } catch (Exception e){
          // ignore: tried to stop a non-existent preview
        }

        // set preview size and make any resize, rotate or
        // reformatting changes here

        mCamera.setDisplayOrientation(90);
        Parameters params = mCamera.getParameters();
        params.setRotation(90);

        mCamera.setParameters(params);

        // start preview with new settings
        try {
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();

        } catch (Exception e){
            Log.d(TAG, "Error starting camera preview: " + e.getMessage());
        }
    }
}

Thank you


Solution

  • Yes, I've found the solution:

    This is my Camera activity:

    package it......utils;
    
    import it.......R;
    
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;
    
    import android.app.Activity;
    import android.content.Intent;
    import android.graphics.ImageFormat;
    import android.hardware.Camera;
    import android.hardware.Camera.AutoFocusCallback;
    import android.hardware.Camera.Parameters;
    import android.hardware.Camera.PictureCallback;
    import android.hardware.Camera.Size;
    import android.os.Bundle;
    import android.os.Environment;
    import android.util.Log;
    import android.view.View;
    import android.widget.FrameLayout;
    import android.widget.TextView;
    
    public class CameraActivity extends Activity {
    
        private Camera mCamera;
        private CameraPreview mPreview;
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_cam);
    
            // Create an instance of Camera
            mCamera = getCameraInstance();
            Parameters params = mCamera.getParameters();
            params.setFocusMode(Parameters.FOCUS_MODE_AUTO);
            params.setPictureFormat(ImageFormat.JPEG);
    
            Camera.Size size = getSmallestPictureSize(params);
    
            params.setPictureSize(size.width, size.height);
            mCamera.setParameters(params);
    
            // Create our Preview view and set it as the content of our activity.
            mPreview = new CameraPreview(this, mCamera);
            FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
            preview.addView(mPreview);
        }
    
        private Camera.Size getSmallestPictureSize(Camera.Parameters parameters) {
            List<Size> list = parameters.getSupportedPictureSizes();
            Collections.sort(list, new AreasComparator());
    
            return list.get(1); // I choose the second one becasue the first one is too small.
        }
    
    
        private PictureCallback mPicture = new PictureCallback() {
    
            @Override
            public void onPictureTaken(byte[] data, Camera camera) {
    
                File pictureFile = getOutputMediaFile();
                if (pictureFile == null){
                    Intent intent = getIntent();
                    setResult(RESULT_CANCELED, intent);
                    finish();
    
                    if (mCamera != null){
                        mCamera.release(); // release the camera for other applications
                        mCamera = null;
                    }
                    System.gc();
                    return;
                }
    
                try {
    
                    FileOutputStream fos = new FileOutputStream(pictureFile);
    
                    fos.write(data);
                    fos.close();
    
                    System.gc();
    
                    if (mCamera != null){
                        mCamera.release(); // release the camera for other applications
                        mCamera = null;
                    }
    
                    Intent intent = getIntent();
                    setResult(RESULT_OK, intent);
                    finish();
    
                    System.gc();
                    return;
    
                } catch (FileNotFoundException e) {
                    //TODO
                } catch (IOException e) {
                    //TODO
                }
                Intent intent = getIntent();
                setResult(RESULT_CANCELED, intent);
                finish();
    
                if (mCamera != null){
                    mCamera.release(); // release the camera for other applications
                    mCamera = null;
                }
                System.gc();
                return;
            }
        };
    
        public static Camera getCameraInstance(){
            Camera c = null;
            try {
                c = Camera.open(); // attempt to get a Camera instance
            }
            catch (Exception e){
                // Camera is not available (in use or does not exist)
            }
            return c; // returns null if camera is unavailable
        }
    
        public void scatta(View v) {
    
            mCamera.autoFocus(new AutoFocusCallback() {
    
                @Override
                public void onAutoFocus(boolean success, Camera camera) {
    
                    if (success) {
                        mCamera.takePicture(null, null, mPicture);
                    }
                }
            });
    
        }
    
        private File getOutputMediaFile(){
            File downloads = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
            String filePath = downloads.getParent() + File.separator + "PicturesTaken";
            File path = new File(filePath);
            path.mkdirs();
            File file;
    
            file = new File(path, "pic.jpg");
    
            if (file.exists()) {
                file.delete();
              }
    
            return file;
        }
    
        @Override
        public void onBackPressed() {
            if (mCamera != null){
                mCamera.release(); // release the camera for other applications
                mCamera = null;
            }
            mPreview = null;
            System.gc();
            super.onBackPressed();
        }
    
        public class AreasComparator implements Comparator<Camera.Size> {
            @Override
            public int compare(Camera.Size s1, Camera.Size s2) {
    
                int resultArea=s1.width * s1.height;
                int newArea=s2.width * s2.height;
    
                if (newArea < resultArea) {
                    return 1;
                }
    
                return -1;
            }
        }
    }
    

    With its' layout:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="20dp"
        tools:context=".CameraActivity" >
    
        <FrameLayout
            android:id="@+id/camera_preview"
            android:onClick="scatta"
            android:layout_width="match_parent"
            android:layout_height="700dp" >
        </FrameLayout>
    
    </RelativeLayout>
    

    And this is my Camera Preview:

    package it........utils;
    
    import java.io.IOException;
    import java.util.List;
    
    import android.content.Context;
    import android.hardware.Camera;
    import android.hardware.Camera.Parameters;
    import android.hardware.Camera.Size;
    import android.util.Log;
    import android.view.SurfaceHolder;
    import android.view.SurfaceView;
    
    /** A basic Camera preview class */
    public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
        private SurfaceHolder mHolder;
        private Camera mCamera;
    
        public CameraPreview(Context context, Camera camera) {
            super(context);
            mCamera = camera;
    
            // Install a SurfaceHolder.Callback so we get notified when the
            // underlying surface is created and destroyed.
            mHolder = getHolder();
            mHolder.addCallback(this);
            // deprecated setting, but required on Android versions prior to 3.0
            mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        }
    
        public void surfaceCreated(SurfaceHolder holder) {
            // The Surface has been created, now tell the camera where to draw the preview.
            try {
                mCamera.setPreviewDisplay(holder);
                mCamera.startPreview();
            } catch (IOException e) {
                //TODO
            }
        }
    
        public void surfaceDestroyed(SurfaceHolder holder) {
            // empty. Take care of releasing the Camera preview in your activity.
        }
    
        public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
            // If your preview can change or rotate, take care of those events here.
            // Make sure to stop the preview before resizing or reformatting it.
    
            if (mHolder.getSurface() == null){
              // preview surface does not exist
              return;
            }
    
            // stop preview before making changes
            try {
                mCamera.stopPreview();
            } catch (Exception e){
              // ignore: tried to stop a non-existent preview
            }
    
            // set preview size and make any resize, rotate or
            // reformatting changes here
    
            mCamera.setDisplayOrientation(90);
            Parameters params = mCamera.getParameters();
            params.setRotation(90);
    
            mCamera.setParameters(params);
    
            // start preview with new settings
            try {
                mCamera.setPreviewDisplay(mHolder);
                mCamera.startPreview();
    
            } catch (Exception e){
                //TODO
            }
        }
    }
    

    And finally, this is what I do in my main activity to call the Camera and show the taken picture:

    private void startCamera() {
        Intent intent = new Intent(Pag4Activity.this, CameraActivity.class);
        startActivityForResult(intent, CODE_CAM);
    }
    
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
    
        if(resultCode==RESULT_OK) {
            if(requestCode == CODE_CAM){
    
                File downloads = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
                String filePath = downloads.getParent() + File.separator + "PicturesTaken";
                File path = new File(filePath);
                path.mkdirs();
    
                File file = new File(path, "pic.jpg");
                ImageView iv = (ImageView)findViewById(R.id.imageTaken);
                Bitmap img = decodeScaledBitmapFromSdCard(file.getPath(), 170, 210);
    
                if(img!=null) {
                    iv.setImageBitmap(img);
                }
    
            }
        }
    }
    
    public static Bitmap decodeScaledBitmapFromSdCard(String filePath,
            int reqWidth, int reqHeight) {
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(filePath, options);
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFile(filePath, options);
    }
    
    public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;
    
        if (height > reqHeight || width > reqWidth) {
    
            final int heightRatio = Math.round((float) height / (float) reqHeight);
            final int widthRatio = Math.round((float) width / (float) reqWidth);
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
        }
    
        return inSampleSize;
    }