If you were like me, you were hitting refresh constantly on Nexus Day waiting for the new devices to go live. What a rush! I got one! After what seemed like an eternity of waiting to get my hands on my Nexus 5x, it arrived and I locked myself in a dark room to start playing. I downloaded my favorite apps, the apps I work on, and even tried out some new ones. If you were even more like me, you realized very quickly that a lot of your favorite apps that leverage the camera had issues. Why is my image upside down? Why is my image rotated weirdly? As a developer, I began to wonder, is this a bug?? After a little research, I can assure you that it is in fact not a bug, but rather an error on the side of the developer when setting up the camera. This blog will take a deeper dive into a common overlook made by developers when building a camera application and why it is so darn easy to make.

What is the Issue?

Let's kick this off with the assumption that you already know how to set up a basic camera application and you are just trying to get the image, preview, and device orientation to all come in sync. Next let's cover the basics of the devices in question.

For the purposes of keeping this pure Google, lets look at the Nexus 5 and Nexus 5x. You will notice that the natural device orientation of both devices is 0°. However, do note the location of the camera sensor and where the top edge of it aligns with on each device. On the Nexus 5, the top edge of the camera sensor aligns with the right edge of the device as it sits in natural orientation. On the Nexus 5x the top edge of the camera sensor aligns with the left edge of the device as it sits in natural orientation.

Next lets look at how we define the orientation of the camera. Per the developer site, Camera.CameraInfo.orientation, is defined as "The orientation of the camera image. The value is the angle that the camera image needs to be rotated clockwise so it shows correctly on the display in its natural orientation. It should be 0, 90, 180, or 270. Or the angle between camera orientation and natural device orientation." When looking at the Nexus devices this is pretty simple to comprehend, but some devices have their natural orientation in landscape and that is when it gets interesting.

With that definition in mind, lets go back and take a look at the two Nexus devices again. With the top edge of the camera sensor aligning with the right side of the device, the orientation of the Nexus 5 camera is 90°. With the top edge of the camera sensor aligning with the left side of the device, the orientation of the Nexus 5x camera is 270°. If you are one of the developers seeing their image upside down, with that I am guessing you just had an "ahha moment." If you are seeing your image do something else funky, keep reading and hopefully we will get to your moment soon.

The Mistake

The common mistake for most developers is not realizing that the orientation of the camera is dependent upon the device manufacturer and not the OS. The reason why this is not a very commonly noticed mistake, is because most device manufacturers stay in sync and keep the camera sensor in a location that leaves the camera orientation at 90°. Since most camera applications want a full screen image capture, leaving the camera at 90° works as desired. This means that in order for your camera application to work consistently across all devices a little bit of work needs to be done.

The Fix

The fix is simple and relatively easy to implement. However, I must admit that I did not come up with the code myself but rather borrowed it from some code comments found inside of the Android Camera class. To determine the correct orientation for the camera consistently across devices utilize the method below, passing in your newly created Camera and CameraInfo. The method leverages the device's current orientation to determine the correct orientation to set the camera and camera display to.




public int getCorrectCameraOrientation(CameraInfo info, Camera camera) {
int rotation = context.getWindowManager().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;
}else{
result = (info.orientation - degrees + 360) % 360;
}
return result;
}


The above method will output 0, 90, 180, or 270. That integer value represents the orientation you will need to set your camera and camera display to. Note that you have to set both the Camera orientation and the Camera Display orientation to the newly output number. To set the Camera Display orientation, or what is returned with the onPreviewFrame, leverage the following method found in the Camera class.



mCamera.setDisplayOrientation(getCorrectCameraOrientation(mCameraInfo, mCamera));


This method alone will not fix your problem as it only rotates the display but does not rotate the image capture itself. In order to rotate the image capture to be in sync with the display leverage the following method on your camera's Parameters.



mCamera.getParameters().setRotation(getCorrectCameraOrientation(mCameraInfo, mCamera));


Leveraging the above code will ensure your camera application accurately displays and captures what is desired on all devices in sync with each's respective device orientation. If you have an application that allows rotation even during image capture, you might find it necessary to use a modified version of the above method in an OrientationEventListener.

Closing

In closing the Nexus 5x has one of the nicest camera's I have used to date (even when it is taking pictures upside down). At the time of writing this blog, I have yet to get to play with a Nexus 6p to see if the behavior is consistent with the 5x. I hope that this has solved your issue if you had one, or answers your question about why some of your applications may be displaying your camera strangely.