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.