Camera on Surface Duo
Surface Duo uses the same camera hardware for both front and back camera, depending on the device posture. Switching cameras flips the image so that selfies render correctly.
Requesting the orientation be locked to portrait or landscape will result in letterboxing when the app is on a single screen and the other screen is showing a different app (or the Launcher). When a camera view is orientation-locked and letterboxed, the camera view is at a different rotation than the app. Your app can:
- Show a message
- Rotate the camera feed
android:screenOrientation="portrait"
or in code:
To show a message about the camera when portrait locked, use this PortraitLockHelper
class. The helper class can be wired up in onCreate
using a listener to detect states when the message should be displayed:
portraitHelper = new PortraitLockHelper(this);
portraitHelper.StateListener = new PortraitLockHelper.PortraitStateListener() {
@Override
public void PortraitStateChanged(int state) {
//...
if((state & PortraitLockHelper.PORTRAIT_STATE_LETTERBOXED_90) > 0 ){
if(showRotationMessage){
rotationMessageView.setVisibility(View.VISIBLE);
}
//...
}
}
See the camera sample code on GitHub for a complete implementation.
The PortraitLockHelper
class can also be used to detect device rotation, and rotate the camera data stream to match. For this to work, you need to make sure you use a TextureView
and not a SurfaceView
as your preview area. TextureView
can be transformed, for example textureView.setRotation(90)
will rotate the feed 90 degrees - setScaleX
and setScaleY
then need to be used to update the aspect ratio to compensate for the rotated screen dimensions.
In the camera sample code the following transform values are used for each device state:
State | Camera feed rotation | Scale X | Scale Y |
---|---|---|---|
default | 0 | 1 | 1 |
Flipped | 0 | 1 | 1 |
Spanned | 90 | 4/3 | 4/3 |
Letterboxed 90 | 90 | 4/3 | 4/3 |
Letterboxed 270 | 270 | 4/3 | 4/3 |
The sample code includes buttons to enable or disable the rotation. See the PortraitStateListener
implementation for the code required to adapt to each device state.
If your app does not need to lock the orientation, the same code for rotating the camera feed can be used. Refer to the rotate the camera feed section above.
To ensure there is no orientation lock, use the following code:
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
If you’re building a new app or planning to upgrade your camera code, adopting the CameraX API will give you a modern base to build on. Google’s CameraXBasic sample on GitHub runs great on the Surface Duo – but there are two small code tweaks that can improve the user experience:
AndroidManifest.xml
android:configChanges="orientation|screenLayout|screenSize|smallestScreenSize"
MainActivity.kt
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if(hasFocus){
container.postDelayed({
container.systemUiVisibility = FLAGS_FULLSCREEN
}, IMMERSIVE_FLAG_TIMEOUT)
}
}
These minor updates provide a smoother experience when spanning and unspanning the app, and when focus changes between the camera app and whatever is on the other screen.