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;
@Override
public 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)
@Override
public 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);
}