I'm writing an app for Android using Xamarin which features a custom activity used to capture pictures using the Camera
API. This activity works perfectly on all devices I've tested, however some users were reporting the app crashing entirely when attempting to take a picture. It was quickly evident that all these users were using Samsung phones, and unfortunately I don't have one available to test on.
Thankfully I've been able to capture the exception and stack trace, but I'm at a loss as to what could be causing this issue. The exception, stack trace and the problematic code are below.
This is a fairly simple activity with a full-screen camera preview, flash toggle and capture button. It uses a custom CameraHelper
class to set up and interact with the Camera
API. The camera is configured and the preview is displayed by the OnSurfaceTextureAvailable
method before the user is able to interact and trigger the TakePicture
method.
Exception Stack Trace
java.lang.RuntimeException: takePicture failed
android.hardware.Camera.native_takePicture(Native Method):0
android.hardware.Camera.takePicture(Camera.java:1523):0
android.hardware.Camera.takePicture(Camera.java:1468):0
md5efa7d89b8a471e1a97a183b83296df21.CameraHelper.n_onAutoFocus(Native Method):0
md5efa7d89b8a471e1a97a183b83296df21.CameraHelper.onAutoFocus(CameraHelper.java:39):0
Methods in CameraHelper
// Implements Camera.IPictureCallback and Camera.IAutoFocusCallback
public void OnSurfaceTextureAvailable(object sender, TextureView.SurfaceTextureAvailableEventArgs e)
{
// Get the camera and set its orientation
try
{
_camera = Camera.Open(_cameraInt);
}
catch (Exception ex)
{
_callback.OnInitializationFailed(ex);
return;
}
var orientation = GetDisplayOrientation();
_camera.SetDisplayOrientation(orientation);
// Set the camera parameters
var cameraParameters = _camera.GetParameters();
if (cameraParameters.SupportedFocusModes != null && cameraParameters.SupportedFocusModes.Contains(Camera.Parameters.FocusModeContinuousPicture))
cameraParameters.FocusMode = Camera.Parameters.FocusModeContinuousPicture;
if (cameraParameters.SupportedFlashModes != null && cameraParameters.SupportedFlashModes.Contains(Camera.Parameters.FlashModeAuto))
{
cameraParameters.FlashMode = Camera.Parameters.FlashModeAuto;
HasFlash = true;
}
cameraParameters.JpegQuality = JPEG_QUALITY;
// Set the picture resolution
var pictureSize = GetIdealPictureSize(cameraParameters.SupportedPictureSizes, MAX_MEGAPIXELS);
_imageWidth = pictureSize.Width;
_imageHeight = pictureSize.Height;
cameraParameters.SetPictureSize(pictureSize.Width, pictureSize.Height);
// Set the preview resolution to best match the TextureView
var previewSize = GetIdealPreviewSize(cameraParameters.SupportedPreviewSizes, _previewTexture.Height, _previewTexture.Width);
cameraParameters.SetPreviewSize(previewSize.Width, previewSize.Height);
// Begin outputting camera preview
_camera.SetParameters(cameraParameters);
_camera.SetPreviewTexture(_previewTexture.SurfaceTexture);
_camera.StartPreview();
UpdatePreviewTextureMatrix(); // Ensure the preview is displayed without warping
// Wait for the preview
EventHandler<TextureView.SurfaceTextureUpdatedEventArgs> h = null;
_previewTexture.SurfaceTextureUpdated += h = (s, e2) =>
{
_previewTexture.SurfaceTextureUpdated -= h;
_callback.OnCameraPreviewReady();
_ready = true;
};
}
public void TakePicture()
{
if (!_ready || _busy)
{
var e = new Exception("Camera not ready");
OnTakePictureFailed(e);
return;
}
_busy = true;
_camera.AutoFocus(this);
}
public void OnAutoFocus(bool success, Camera camera)
{
try
{
_camera.TakePicture(null, null, this);
}
catch (Exception e)
{
// On Samsung phones the exception is always thrown here
OnTakePictureFailed(e);
}
}
public void OnPictureTaken(byte[] data, Camera camera)
{
_busy = false;
var rotation = GetPictureRotation();
_callback.OnPictureTaken(data, rotation, _imageWidth, _imageHeight);
}
private void OnTakePictureFailed(Exception e)
{
_busy = false;
_callback.OnTakePictureFailed(e);
}
The camera is available and the preview is showing without issue, and the exception is only thrown on Samsung devices.