android camera surfaceview orientation android camera surfaceview orientation android android

android camera surfaceview orientation


The way I implemented it:

private Camera mCamera;private OrientationEventListener mOrientationEventListener;private int mOrientation =  -1;private static final int ORIENTATION_PORTRAIT_NORMAL =  1;private static final int ORIENTATION_PORTRAIT_INVERTED =  2;private static final int ORIENTATION_LANDSCAPE_NORMAL =  3;private static final int ORIENTATION_LANDSCAPE_INVERTED =  4;@Overridepublic void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    // force Landscape layout    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR | ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);   /*   Your other initialization code here   */}@Override protected void onResume() {    super.onResume();    if (mOrientationEventListener == null) {                    mOrientationEventListener = new OrientationEventListener(this, SensorManager.SENSOR_DELAY_NORMAL) {            @Override            public void onOrientationChanged(int orientation) {                // determine our orientation based on sensor response                int lastOrientation = mOrientation;                if (orientation >= 315 || orientation < 45) {                    if (mOrientation != ORIENTATION_PORTRAIT_NORMAL) {                                                  mOrientation = ORIENTATION_PORTRAIT_NORMAL;                    }                }                else if (orientation < 315 && orientation >= 225) {                    if (mOrientation != ORIENTATION_LANDSCAPE_NORMAL) {                        mOrientation = ORIENTATION_LANDSCAPE_NORMAL;                    }                                       }                else if (orientation < 225 && orientation >= 135) {                    if (mOrientation != ORIENTATION_PORTRAIT_INVERTED) {                        mOrientation = ORIENTATION_PORTRAIT_INVERTED;                    }                                       }                else { // orientation <135 && orientation > 45                    if (mOrientation != ORIENTATION_LANDSCAPE_INVERTED) {                        mOrientation = ORIENTATION_LANDSCAPE_INVERTED;                    }                                       }                   if (lastOrientation != mOrientation) {                    changeRotation(mOrientation, lastOrientation);                }            }        };    }    if (mOrientationEventListener.canDetectOrientation()) {        mOrientationEventListener.enable();    }}@Override protected void onPause() {    super.onPause();    mOrientationEventListener.disable();}/** * Performs required action to accommodate new orientation * @param orientation * @param lastOrientation */private void changeRotation(int orientation, int lastOrientation) {    switch (orientation) {        case ORIENTATION_PORTRAIT_NORMAL:            mSnapButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_camera, 270));            mBackButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_revert, 270));            Log.v("CameraActivity", "Orientation = 90");            break;        case ORIENTATION_LANDSCAPE_NORMAL:            mSnapButton.setImageResource(android.R.drawable.ic_menu_camera);            mBackButton.setImageResource(android.R.drawable.ic_menu_revert);            Log.v("CameraActivity", "Orientation = 0");            break;        case ORIENTATION_PORTRAIT_INVERTED:            mSnapButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_camera, 90));            mBackButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_revert, 90));            Log.v("CameraActivity", "Orientation = 270");            break;        case ORIENTATION_LANDSCAPE_INVERTED:            mSnapButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_camera, 180));            mBackButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_revert, 180));                  Log.v("CameraActivity", "Orientation = 180");            break;    }}    /** * Rotates given Drawable * @param drawableId    Drawable Id to rotate * @param degrees       Rotate drawable by Degrees * @return              Rotated Drawable */private Drawable getRotatedImage(int drawableId, int degrees) {    Bitmap original = BitmapFactory.decodeResource(getResources(), drawableId);    Matrix matrix = new Matrix();    matrix.postRotate(degrees);    Bitmap rotated = Bitmap.createBitmap(original, 0, 0, original.getWidth(), original.getHeight(), matrix, true);    return new BitmapDrawable(rotated);}

And then in your PictureCallback set metadata to indicate rotation level:

    private Camera.PictureCallback mJpegCallback = new Camera.PictureCallback() {    @Override    public void onPictureTaken(byte[] data, Camera camera) {        try {            // Populate image metadata            ContentValues image = new ContentValues();            // additional picture metadata            image.put(Media.DISPLAY_NAME, [picture name]);            image.put(Media.MIME_TYPE, "image/jpg");            image.put(Media.TITLE, [picture title]);            image.put(Media.DESCRIPTION, [picture description]);            image.put(Media.DATE_ADDED, [some time]);            image.put(Media.DATE_TAKEN, [some time]);            image.put(Media.DATE_MODIFIED, [some time]);            // do not rotate image, just put rotation info in            switch (mOrientation) {                case ORIENTATION_PORTRAIT_NORMAL:                    image.put(Media.ORIENTATION, 90);                    break;                case ORIENTATION_LANDSCAPE_NORMAL:                    image.put(Media.ORIENTATION, 0);                    break;                case ORIENTATION_PORTRAIT_INVERTED:                    image.put(Media.ORIENTATION, 270);                    break;                case ORIENTATION_LANDSCAPE_INVERTED:                    image.put(Media.ORIENTATION, 180);                    break;            }            // store the picture            Uri uri = getContentResolver().insert(                    Media.EXTERNAL_CONTENT_URI, image);            try {                Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0,                        data.length);                OutputStream out = getContentResolver().openOutputStream(                        uri);                boolean success = bitmap.compress(                        Bitmap.CompressFormat.JPEG, 75, out);                out.close();                if (!success) {                    finish(); // image output failed without any error,                                // silently finish                }            } catch (Exception e) {                e.printStackTrace();                // handle exceptions            }            mResultIntent = new Intent();            mResultIntent.setData(uri);        } catch (Exception e) {            e.printStackTrace();        }        finish();    }};

I hope it helps.

UPDATE Now when landscape based devices are appearing an additional check for it is required in OrientationEventListener.

Display display = ((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay();                                        if (display.getOrientation() == Surface.ROTATION_0) {     // landscape oriented devices} else {     // portrait oriented device}

Full code (a bit wasteful by LC, but easily demonstrates the approach)

@Overridepublic void onOrientationChanged(int orientation) {    // determine our orientation based on sensor response    int lastOrientation = mOrientation;    Display display = ((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay();                                            if (display.getOrientation() == Surface.ROTATION_0) {   // landscape oriented devices        if (orientation >= 315 || orientation < 45) {            if (mOrientation != ORIENTATION_LANDSCAPE_NORMAL) {                                         mOrientation = ORIENTATION_LANDSCAPE_NORMAL;            }        } else if (orientation < 315 && orientation >= 225) {            if (mOrientation != ORIENTATION_PORTRAIT_INVERTED) {                mOrientation = ORIENTATION_PORTRAIT_INVERTED;            }                               } else if (orientation < 225 && orientation >= 135) {            if (mOrientation != ORIENTATION_LANDSCAPE_INVERTED) {                mOrientation = ORIENTATION_LANDSCAPE_INVERTED;            }                               } else if (orientation <135 && orientation > 45) {             if (mOrientation != ORIENTATION_PORTRAIT_NORMAL) {                mOrientation = ORIENTATION_PORTRAIT_NORMAL;            }                               }                           } else {  // portrait oriented devices        if (orientation >= 315 || orientation < 45) {            if (mOrientation != ORIENTATION_PORTRAIT_NORMAL) {                                          mOrientation = ORIENTATION_PORTRAIT_NORMAL;            }        } else if (orientation < 315 && orientation >= 225) {            if (mOrientation != ORIENTATION_LANDSCAPE_NORMAL) {                mOrientation = ORIENTATION_LANDSCAPE_NORMAL;            }                               } else if (orientation < 225 && orientation >= 135) {            if (mOrientation != ORIENTATION_PORTRAIT_INVERTED) {                mOrientation = ORIENTATION_PORTRAIT_INVERTED;            }                               } else if (orientation <135 && orientation > 45) {             if (mOrientation != ORIENTATION_LANDSCAPE_INVERTED) {                mOrientation = ORIENTATION_LANDSCAPE_INVERTED;            }                               }    }    if (lastOrientation != mOrientation) {        changeRotation(mOrientation, lastOrientation);    }}


Have you considered using the standard method thats provided in the API doc, which you can call on surfaceChanged? You could store the degrees in a global variable to later use when saving the picture. Also could do a simple null checker on your camera variable, so you don't create it again in surfaceCreated.

public void setCameraDisplayOrientation() {             if (mCamera == null)     {         Log.d(TAG,"setCameraDisplayOrientation - camera null");         return;                  }     Camera.CameraInfo info = new Camera.CameraInfo();     Camera.getCameraInfo(CAM_ID, info);     WindowManager winManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);     int rotation = winManager.getDefaultDisplay().getRotation();     int degrees = 0;     switch (rotation)      {         case Surface.ROTATION_0: degrees = 0; break;         case Surface.ROTATION_90: degrees = 90; break;         case Surface.ROTATION_180: degrees = 180; break;         case Surface.ROTATION_270: degrees = 270; break;     }     int result;     if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT)      {         result = (info.orientation + degrees) % 360;         result = (360 - result) % 360;  // compensate the mirror     } else {  // back-facing         result = (info.orientation - degrees + 360) % 360;     }     mCamera.setDisplayOrientation(result);}


As you've seen from the other answers, this code gets very complicated. You may want to investigate using a library to help you provide this feature, for example, CWAC-Camera supports OS 2.3 and up (hopefully you can drop OS 2.1 and OS 2.2 support now):
https://github.com/commonsguy/cwac-camera

CWAC-Camera supports locking the camera preview to landscape, and will auto-rotate images into the correction orientation for you. Browse the project issues if you want a taste of all the device specific problems that need to be solved, which IMO are more reasons for trying to use a library instead of maintaining all this code and testing yourself.