Related
I want to implement zoom function while video recording in android.But, I am not able to access Camera parameters or Camera startSmoothZoom() method in neither main activity nor surface class. If you access camera parametera in media recorder method(prepareMediaRecorder()) it will throw null pointer exception.
this activity class- in prepareMediaRecorder() method not able to access camera parameters and also not able to set startSmoothZoom(). here camera object giving null pointer exception.
public class CustomCameraPreview extends BaseActivity implements
OnClickListener, AlertPositiveListener, OrientationListener,
ActivityCompat.OnRequestPermissionsResultCallback {
RelativeLayout mLayout;
MediaRecorder mediaRecorder;
private PictureCallback mPictureCallback = new PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
try {
cameraData = data;
captureAngle = getRotation();
mBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
}
catch (OutOfMemoryError e){
System.gc();
mBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
}
// int rotation=getRotation();
Matrix matrix = new Matrix();
matrix.postRotate(getRotation());
/*mBitmap = Bitmap.createBitmap(mBitmap, 0, 0, mBitmap.getWidth(),
mBitmap.getHeight(), matrix, true);*/
mBitmap = Bitmap.createBitmap(mBitmap, 0, 0, mBitmap.getWidth(),
mBitmap.getHeight(), matrix, false);
if (mBitmap != null) {
mButtonRetake.setEnabled(true);
} else {
Message.displayToast(CustomCameraPreview.this,
getString(R.string.picture_not_taken));
}
mCamera.release();
mButtonCapture.setEnabled(false);
}
};
protected void onCreate(){
initiCameraForVideo();
}
private void initiCameraForVideo() {
params = mCamera.getParameters();
mButtonCapture.setBackgroundResource(R.drawable.videostart);
mShowCamera = new CameraSurfaceHolder(CustomCameraPreview.this,
mCamera);
mLayout = (RelativeLayout) findViewById(R.id.relativeLayout);
mLayout.removeAllViews();
mLayout.addView(mShowCamera);
List<Camera.Size> mSizeList_Video = null;// params.getSupportedPreviewSizes();
if (params.getSupportedVideoSizes() != null) {
mSizeList_Video = params.getSupportedVideoSizes();
} else {
// Video sizes may be null, which indicates that all the
// supported preview sizes are supported for video
// recording.
mSizeList_Video = mCamera.getParameters()
.getSupportedPreviewSizes();
}
}
#Override
public void onClick(View v) {
int viewId = v.getId();
switch (viewId) {
case R.id.button_Capture:
releaseCamera();
if (!prepareMediaRecorder()) {
Message.displayToast(
CustomCameraPreview.this,
getString(R.string.somethign_went_wrong));
} else {
mediaRecorder.start();
recording = true;
}
}
private boolean prepareMediaRecorder() \*method to setup media player to record video
{
mCamera = isCameraAvailable();
mediaRecorder = new MediaRecorder();
mCamera.unlock();
mediaRecorder.setCamera(mCamera);
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
if(CamcorderProfile.hasProfile(findCameraID(), CamcorderProfile.QUALITY_480P)) {
mediaRecorder.setProfile(CamcorderProfile
.get(CamcorderProfile.QUALITY_480P));
}else{
mediaRecorder.setProfile(CamcorderProfile
.get(CamcorderProfile.QUALITY_LOW));
}
mediaRecorder.setOutputFile(getOutputVideoFile());
mediaRecorder.setMaxDuration(60000);
// mediaRecorder.setMaxFileSize(100 * 1000 * 1000);
mediaRecorder.setPreviewDisplay(mShowCamera.getHolder().getSurface());
try {
mediaRecorder.prepare();
}
}
#Override
protected void onPause() {
super.onPause();
releaseMediaRecorder();
releaseCamera();
}
private void releaseCamera() {
if (mCamera != null) {
mCamera.release();
mCamera = null;
}
}
and this surface preview class-
public class CameraSurfaceHolder extends SurfaceView implements
SurfaceHolder.Callback {
private static final String TAG = "CameraSurfaceHolder";
Context mContext;
private String errorMessage = "";
private SurfaceHolder mSurfaceHolder;
private Camera mCamera;
public CameraSurfaceHolder(Context context, Camera camera) {
super(context);
mContext = context;
mCamera = camera;
mSurfaceHolder = getHolder();
mSurfaceHolder.addCallback(this);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
/*if (holder.getSurface() == null){
// preview surface does not exist
return;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e){
// ignore: tried to stop a non-existent preview
}
// set preview size and make any resize, rotate or
// reformatting changes here
setCameraDisplayOrientation((Activity)mContext, Camera.CameraInfo.CAMERA_FACING_BACK, mCamera);
// start preview with new settings
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
}
*/
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
mCamera.setPreviewDisplay(mSurfaceHolder);
mCamera.startPreview();
} catch (Exception e) {
Logger.ex(e);
}
}
public void setCameraDisplayOrientation(Activity activity, int cameraId,
Camera camera) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, info);
/*Display mDisplay = ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int rotation = mDisplay.getDisplayId();*/
int rotation = activity.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; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
Camera.Parameters mParameters = camera.getParameters();
mParameters.setRotation(rotation);
camera.setDisplayOrientation(result);
camera.setParameters(mParameters);
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
}
in above class I added some code like below and when user touch on camera preview its throwing null pointer exception in onTouchEvent() on access of camera paramters. Also tried like I set again camera object to surface in activity after configure media recorder(prepareMediaRecorder()), but zoom function working but video is not recording.
#Override
public boolean onTouchEvent(MotionEvent event) {
// Get the pointer ID
Camera.Parameters params = mCamera.getParameters();
int action = event.getAction();
if (event.getPointerCount() > 1) {
// handle multi-touch events
if (action == MotionEvent.ACTION_POINTER_DOWN) {
mDist = getFingerSpacing(event);
} else if (action == MotionEvent.ACTION_MOVE && params.isZoomSupported()) {
mCamera.cancelAutoFocus();
handleZoom(event, params);
}
} else {
// handle single touch events
if (action == MotionEvent.ACTION_UP) {
handleFocus(event, params);
}
}
return true;
}
private void handleZoom(MotionEvent event, Camera.Parameters params) {
int maxZoom = params.getMaxZoom();
int zoom = params.getZoom();
float newDist = getFingerSpacing(event);
if (newDist > mDist) {
//zoom in
if (zoom < maxZoom)
zoom++;
} else if (newDist < mDist) {
//zoom out
if (zoom > 0)
zoom--;
}
mDist = newDist;
params.setZoom(zoom);
mCamera.setParameters(params);
}
public void handleFocus(MotionEvent event, Camera.Parameters params) {
int pointerId = event.getPointerId(0);
int pointerIndex = event.findPointerIndex(pointerId);
// Get the pointer's current position
float x = event.getX(pointerIndex);
float y = event.getY(pointerIndex);
List<String> supportedFocusModes = params.getSupportedFocusModes();
if (supportedFocusModes != null && supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
mCamera.autoFocus(new Camera.AutoFocusCallback() {
#Override
public void onAutoFocus(boolean b, Camera camera) {
// currently set to auto-focus on single touch
}
});
}
}
/** Determine the space between the first two fingers */
private float getFingerSpacing(MotionEvent event) {
// ...
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return (float)Math.sqrt(x * x + y * y);
}
I have a method called switchCamera, I'm trying to switch camera from front to back on the click of a button, in one smooth transition. My application freezes when I call this method - I know I'm not doing something right. Can anyone help me out here?
Any help is much appreciated.
public void switchCamera(){
int camNum = 0;
camNum = Camera.getNumberOfCameras();
int camBackId = Camera.CameraInfo.CAMERA_FACING_BACK;
int camFrontId = Camera.CameraInfo.CAMERA_FACING_FRONT;
Camera.CameraInfo currentCamInfo = new Camera.CameraInfo();
//if camera is running
if (camera != null){
//and there is more than one camera
if (camNum > 1){
//stop current camera
camera.stopPreview();
camera.setPreviewCallback(null);
//camera.takePicture(null, null, PictureCallback);
camera.release();
camera = null;
//stop surfaceHolder?
if (currentCamInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT){
//switch camera to back camera
camera=Camera.open(camBackId);
}
else{
//switch camera to front camera
camera=Camera.open(camFrontId);
}
//switch camera back on
//specify surface?
try {
camera.setPreviewDisplay(surfaceHolder);
camera.setPreviewCallback((PreviewCallback) this);
camera.startPreview();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Button otherCamera = (Button) findViewById(R.id.OtherCamera);
otherCamera.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (inPreview) {
camera.stopPreview();
}
//NB: if you don't release the current camera before switching, you app will crash
camera.release();
//swap the id of the camera to be used
if(currentCameraId == Camera.CameraInfo.CAMERA_FACING_BACK){
currentCameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;
}
else {
currentCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
}
camera = Camera.open(currentCameraId);
setCameraDisplayOrientation(CameraActivity.this, currentCameraId, camera);
try {
camera.setPreviewDisplay(previewHolder);
} catch (IOException e) {
e.printStackTrace();
}
camera.startPreview();
}
If you want to make the camera image show in the same orientation as
the display, you can use the following code.
public static void setCameraDisplayOrientation(Activity activity,
int cameraId, android.hardware.Camera camera) {
android.hardware.Camera.CameraInfo info =
new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info);
int rotation = activity.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; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
camera.setDisplayOrientation(result);
}
First you need to destroy the SurfacePreview of previous camera, then need to create a new object of camera(Back/Front)
`//Code to destroy SurfacePreview
mPreview.surfaceDestroyed(mPreview.getHolder());
mPreview.getHolder().removeCallback(mPreview);
mPreview.destroyDrawingCache();
preview.removeView(mPreview);
mCamera.stopPreview();
mCamera.stopPreview();
mCamera.setPreviewCallback(null);
mCamera.release();
//Now create new camera object
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
mCamera = Camera.open(camIdx);
mPreview = new CameraPreview(CameraActivity.this, mCamera);
preview.addView(mPreview);
mCamera.setPreviewDisplay(mPreview.getHolder());
mCamera.startPreview();
}`
After a long search finally i can switched camera successfully. mjosh's answer is a useful answer but it did not worked for me. The trick i found finally is create new CameraPreview class and add it again.
Here is my CameraPreview class.
#SuppressLint("ViewConstructor")
class CameraPreview(context: Context?,
private var camera: Camera,
private val displayRotation: Int) : SurfaceView(context), SurfaceHolder.Callback {
companion object {
private const val TAG = "TAG"
private const val FOCUS_AREA_SIZE = 300
}
val surfaceHolder: SurfaceHolder = holder
private var previewSize: Camera.Size? = null
private val supportedPreviewSizes: MutableList<Camera.Size>?
init {
surfaceHolder.addCallback(this)
supportedPreviewSizes = camera.parameters.supportedPreviewSizes
}
private val surfaceViewTouchListener: View.OnTouchListener = OnTouchListener { v, event ->
camera.cancelAutoFocus()
val focusRect = calculateFocusArea(event.x, event.y)
val parameters = camera.parameters
if (parameters.focusMode == Camera.Parameters.FOCUS_MODE_AUTO) {
parameters.focusMode = Camera.Parameters.FOCUS_MODE_AUTO
}
if (parameters.maxNumFocusAreas > 0) {
val areaList = ArrayList<Camera.Area>()
areaList.add(Camera.Area(focusRect, 1000))
parameters.focusAreas = areaList
}
try {
camera.cancelAutoFocus()
camera.parameters = parameters
camera.startPreview()
camera.autoFocus { _, cam ->
if (cam.parameters.focusMode == Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE) {
val parameters = cam.parameters;
parameters.focusMode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE
if (parameters.maxNumFocusAreas > 0) {
parameters.focusAreas = null
}
camera.parameters = parameters
camera.startPreview()
}
}
} catch (e: Exception) {
e.printStackTrace()
}
return#OnTouchListener true
}
override fun surfaceCreated(holder: SurfaceHolder?) {
setOnTouchListener(surfaceViewTouchListener)
// The Surface has been created, now tell the camera where to draw the preview.
try {
camera.setPreviewDisplay(holder)
camera.setDisplayOrientation(displayRotation)
camera.startPreview()
} catch (e: IOException) {
Log.d(TAG, "Error setting camera preview: " + e.message)
}
}
override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (holder?.surface == null) {
// preview surface does not exist
return
}
// stop preview before making changes
try {
camera.stopPreview()
} catch (e: Exception) {
// ignore: tried to stop a non-existent preview
}
// set preview size and make any resize, rotate or
// reformatting changes here
// start preview with new settings
try {
val parameters = camera.parameters
val bestPictureSize = getBestPictureSize(width, height, parameters)
bestPictureSize?.let {
parameters.setPictureSize(it.width, it.height)
}
previewSize?.let {
parameters.setPreviewSize(it.width, it.height)
}
camera.parameters = parameters
camera.setPreviewDisplay(holder)
camera.startPreview()
} catch (e: Exception) {
Log.d(TAG, "Error starting camera preview: " + e.message)
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val width = View.resolveSize(suggestedMinimumWidth, widthMeasureSpec)
val height = View.resolveSize(suggestedMinimumHeight, heightMeasureSpec)
setMeasuredDimension(width, height)
if (supportedPreviewSizes != null) {
previewSize = getOptimalPreviewSize(supportedPreviewSizes, width, height)
}
}
private fun getOptimalPreviewSize(sizes: List<Camera.Size>?, w: Int, h: Int): Camera.Size? {
val ASPECT_TOLERANCE = 0.1
val targetRatio = h.toDouble() / w
if (sizes == null) return null
var optimalSize: Camera.Size? = null
var minDiff = java.lang.Double.MAX_VALUE
for (size in sizes) {
val ratio = size.width.toDouble() / size.height
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue
if (Math.abs(size.height - h) < minDiff) {
optimalSize = size
minDiff = Math.abs(size.height - h).toDouble()
}
}
if (optimalSize == null) {
minDiff = java.lang.Double.MAX_VALUE
for (size in sizes) {
if (Math.abs(size.height - h) < minDiff) {
optimalSize = size
minDiff = Math.abs(size.height - h).toDouble()
}
}
}
return optimalSize
}
override fun surfaceDestroyed(holder: SurfaceHolder?) {
// no-op
}
private fun getBestPictureSize(width: Int, height: Int, parameters: Camera.Parameters): Camera.Size? {
var bestSize: Camera.Size?
val sizeList = parameters.supportedPictureSizes
bestSize = sizeList[0]
for (i in 1 until sizeList.size) {
if (sizeList[i].width * sizeList[i].height > bestSize!!.width * bestSize.height) {
bestSize = sizeList[i]
}
}
return bestSize
}
private fun calculateFocusArea(x: Float, y: Float): Rect {
val left = clamp(java.lang.Float.valueOf(x / width * 2000 - 1000).toInt(), FOCUS_AREA_SIZE)
val top = clamp(java.lang.Float.valueOf(y / height * 2000 - 1000).toInt(), FOCUS_AREA_SIZE)
return Rect(left, top, left + FOCUS_AREA_SIZE, top + FOCUS_AREA_SIZE)
}
private fun clamp(touchCoordinateInCameraReper: Int, focusAreaSize: Int): Int {
return if (Math.abs(touchCoordinateInCameraReper) + focusAreaSize / 2 > 1000) {
if (touchCoordinateInCameraReper > 0) {
1000 - focusAreaSize / 2
} else {
-1000 + focusAreaSize / 2
}
} else {
touchCoordinateInCameraReper - focusAreaSize / 2
}
}
fun turnFlashOnOrOff() {
try {
camera.stopPreview()
} catch (e: Exception) {
// ignore
}
val params = camera.parameters
params?.let {
if (params.flashMode == Camera.Parameters.FLASH_MODE_TORCH) {
params.flashMode = Camera.Parameters.FLASH_MODE_OFF
//flash.setImageResource(R.mipmap.baseline_flash_off_white_24dp)
} else {
params.flashMode = Camera.Parameters.FLASH_MODE_TORCH
//flash.setImageResource(R.mipmap.baseline_flash_on_white_24dp)
}
camera.setPreviewDisplay(holder)
try {
camera.parameters = params
} catch (e: Exception) {
e.printStackTrace()
}
camera.startPreview()
}
}
}
My openCamera method which i open camera with it:
private fun openCamera() {
camera = CameraUtil.getCameraInstance(getCameraId())
rotation = getDisplayRotation()
cameraPreview = CameraPreview(activity, camera!!, rotation)
fl_camera.addView(cameraPreview)
}
Before you create CameraPreview you have to calculate the rotation of camera and set it as displayOrientation
private fun getDisplayRotation(): Int {
val info = Camera.CameraInfo()
Camera.getCameraInfo(getCameraId(), info)
val rotation = activity.windowManager.defaultDisplay.rotation
var degrees = 0
when (rotation) {
Surface.ROTATION_0 -> degrees = 0
Surface.ROTATION_90 -> degrees = 90
Surface.ROTATION_180 -> degrees = 180
Surface.ROTATION_270 -> degrees = 270
}
var result: Int
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;
}
return result
}
And i get cameraId like below:
private fun getCameraId(): Int {
val numberOfCameras = Camera.getNumberOfCameras()
var cameraInfo: Camera.CameraInfo
for (i in 0 until numberOfCameras) {
cameraInfo = Camera.CameraInfo()
Camera.getCameraInfo(i, cameraInfo)
if (cameraInfo.facing == currentCamera) {
return i
}
}
return 0
}
And finally my SwtichCamera button works like this:
switch_camera.setOnClickListener {
try {
camera?.stopPreview()
} catch (e: Exception) {
e.printStackTrace()
}
camera?.release()
currentCamera = if (currentCamera === android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK) {
Camera.CameraInfo.CAMERA_FACING_FRONT
} else {
Camera.CameraInfo.CAMERA_FACING_BACK
}
fl_camera.removeView(cameraPreview)
openCamera()
}
This is a working solution for me. I hope this'll help you some others too.
Edit: Camera preview can be a problem for Samsung devices. Here's an alternative method for getting best preview size.
private fun getOptimalPreviewSize(sizes: List<Camera.Size>?, w: Int, h: Int): Camera.Size? {
if (sizes == null) return null
var optimalSize: Camera.Size? = null
val ratio = h.toDouble() / w
var minDiff = java.lang.Double.MAX_VALUE
var newDiff: Double
for (size in sizes) {
newDiff = Math.abs(size.width.toDouble() / size.height - ratio)
if (newDiff < minDiff) {
optimalSize = size
minDiff = newDiff
}
}
return optimalSize
}
I am trying to make a camera app that shows an overlay when a picture is taken.
When this overlay is shown the other UI components (except for the FrameLayout that shows the picture) should go invisible.
But it seems that while my 2 imagebuttons go invisble, my imageview(ivCompass) doesn't.
Here is the code that gets called when a picture is taken
Camera.PictureCallback mPicture = new Camera.PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
//create a new intent...
String path = createFile(data);
intent = new Intent();
intent.putExtra("path", path);
mBearingProvider.updateBearing();
bearing = mBearingProvider.getBearing();
cardinalDirection = bearingToString(bearing);
//((TextView) findViewById(R.id.tvPicDirection)).setText(cardinalDirection);
Log.e("Direction", cardinalDirection + "," + bearing);
findViewById(R.id.btnFlash).setVisibility(View.INVISIBLE);
findViewById(R.id.btnCapture).setVisibility(View.INVISIBLE);
findViewById(R.id.ivCompass).setVisibility(View.INVISIBLE);
findViewById(R.id.pictureOverlay).setVisibility(View.VISIBLE);
}
};
And here is the layout file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/camera_preview"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
<ImageButton
android:id="#+id/btnFlash"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerVertical="true"
android:layout_margin="10dp"
android:src="#drawable/camera_flash_on"
android:background="#drawable/circle_flash"
android:onClick="changeFlashMode"/>
<ImageButton
android:id="#+id/btnCapture"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_margin="10dp"
android:src="#drawable/icon_camera"
android:background="#drawable/circle_camera"/>
<ImageView
android:id="#+id/ivCompass"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_alignParentRight="true"
android:src="#drawable/camera_compass"
android:background="#android:color/transparent"/>
<RelativeLayout
android:id="#+id/pictureOverlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="20dp"
android:background="#color/alphaBlack"
android:visibility="invisible">
</RelativeLayout>
I think It's just a mistake with naming, syntax or something like that, but I can't seem to find it.
EDIT:
Here is the entire Activity
public class CameraActivity extends AppCompatActivity implements BearingToNorthProvider.ChangeEventListener {
private Camera mCamera;
private CameraView mCameraView;
private float mDist = 0f;
private String flashMode;
private ImageButton flashButton;
private Intent intent;
private BearingToNorthProvider mBearingProvider;
private double bearing;
private double currentBearing = 0d;
private String cardinalDirection = "?";
private final int REQUEST_CODE_ASK_PERMISSIONS = 2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
mCamera = getCameraInstance();
mCameraView = new CameraView(this, mCamera);
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.addView(mCameraView);
ImageButton captureButton = (ImageButton) findViewById(R.id.btnCapture);
captureButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Camera.Parameters params = mCamera.getParameters();
params.setFlashMode(flashMode);
mCamera.setParameters(params);
mCamera.takePicture(null, null, mPicture);
}
});
SharedPreferences sharedPref = this.getSharedPreferences(getString(R.string.apiKey), Context.MODE_PRIVATE);
flashMode = sharedPref.getString(getString(R.string.flashMode), Camera.Parameters.FLASH_MODE_OFF);
flashButton = (ImageButton) findViewById(R.id.btnFlash);
setFlashButton();
mBearingProvider = new BearingToNorthProvider(this,this);
mBearingProvider.setChangeEventListener(this);
mBearingProvider.start();
}
#Override
protected void onPause() {
super.onPause();
mBearingProvider.stop();
}
/**
* Helper method to access the camera returns null if it cannot get the
* camera or does not exist
*
* #return the instance of the camera
*/
private Camera getCameraInstance() {
Camera camera = null;
try {
camera = Camera.open();
} catch (Exception e) {
Log.e("CamException", e.toString());
}
return camera;
}
Camera.PictureCallback mPicture = new Camera.PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
//create a new intent...
String path = createFile(data);
intent = new Intent();
intent.putExtra("path", path);
mBearingProvider.updateBearing();
bearing = mBearingProvider.getBearing();
cardinalDirection = bearingToString(bearing);
//((TextView) findViewById(R.id.tvPicDirection)).setText(cardinalDirection);
Log.e("Direction", cardinalDirection + "," + bearing);
findViewById(R.id.btnFlash).setVisibility(View.INVISIBLE);
findViewById(R.id.btnCapture).setVisibility(View.INVISIBLE);
findViewById(R.id.ivCompass).setVisibility(View.INVISIBLE);
findViewById(R.id.pictureOverlay).setVisibility(View.VISIBLE);
}
};
private void confirmPicture(View v) {
/*String direction = String.valueOf(((TextView) findViewById(R.id.tvPicDirection)).getText());
String description = String.valueOf(((EditText) findViewById(R.id.tvPicDescription)).getText());
intent.putExtra("direction", direction);
intent.putExtra("description", description);*/
//close this Activity...
setResult(Activity.RESULT_OK, intent);
finish();
}
//region File Methods
/**
* Method that creates a file from the given byte array and saves the file in the Pictures Directory
* #param data is the array of bytes that represent the picture taken by the camera
* #return the path of created file
*/
private String createFile(byte[] data){
checkFilePermissions();
File picFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + File.separator + "tempPic.jpg" + File.separator);
String path = picFile.getPath();
try {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(picFile));
bos.write(data);
bos.flush();
bos.close();
return path;
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
/**
* Checks the permission for reading to and writing from the external storage
*/
private void checkFilePermissions() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
int hasWriteExternalStoragePermission = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (hasWriteExternalStoragePermission != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUEST_CODE_ASK_PERMISSIONS);
return;
}
}
}
//endregion
//region Zoom Methods
#Override
public boolean onTouchEvent(MotionEvent event) {
// Get the pointer ID
Camera.Parameters params = mCamera.getParameters();
int action = event.getAction();
if (event.getPointerCount() > 1) {
// handle multi-touch events
if (action == MotionEvent.ACTION_POINTER_DOWN) {
mDist = getFingerSpacing(event);
} else if (action == MotionEvent.ACTION_MOVE && params.isZoomSupported()) {
mCamera.cancelAutoFocus();
handleZoom(event, params);
}
} else {
// handle single touch events
if (action == MotionEvent.ACTION_UP) {
handleFocus(event, params);
}
}
return true;
}
private void handleZoom(MotionEvent event, Camera.Parameters params) {
int maxZoom = params.getMaxZoom();
int zoom = params.getZoom();
float newDist = getFingerSpacing(event);
if (newDist > mDist) {
//zoom in
if (zoom < maxZoom)
zoom++;
} else if (newDist < mDist) {
//zoom out
if (zoom > 0)
zoom--;
}
mDist = newDist;
params.setZoom(zoom);
mCamera.setParameters(params);
}
public void handleFocus(MotionEvent event, Camera.Parameters params) {
int pointerId = event.getPointerId(0);
int pointerIndex = event.findPointerIndex(pointerId);
// Get the pointer's current position
float x = event.getX(pointerIndex);
float y = event.getY(pointerIndex);
List<String> supportedFocusModes = params.getSupportedFocusModes();
if (supportedFocusModes != null && supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
mCamera.autoFocus(new Camera.AutoFocusCallback() {
#Override
public void onAutoFocus(boolean b, Camera camera) {
// currently set to auto-focus on single touch
}
});
}
}
/** Determine the space between the first two fingers */
private float getFingerSpacing(MotionEvent event) {
double x = event.getX(0) - event.getX(1);
double y = event.getY(0) - event.getY(1);
return (float) Math.sqrt(x * x + y * y);
}
//endregion
//region Flash Methods
public void changeFlashMode(View v) {
switch (flashMode) {
case Camera.Parameters.FLASH_MODE_ON :
flashMode = Camera.Parameters.FLASH_MODE_AUTO;
break;
case Camera.Parameters.FLASH_MODE_AUTO :
flashMode = Camera.Parameters.FLASH_MODE_OFF;
break;
case Camera.Parameters.FLASH_MODE_OFF :
flashMode = Camera.Parameters.FLASH_MODE_ON;;
break;
}
SharedPreferences sharedPref = getSharedPreferences(getString(R.string.flashMode), Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString(getString(R.string.flashMode), flashMode);
editor.commit();
setFlashButton();
}
public void setFlashButton() {
switch (flashMode) {
case Camera.Parameters.FLASH_MODE_ON :
flashButton.setImageDrawable(getResources().getDrawable(R.drawable.camera_flash_on));
break;
case Camera.Parameters.FLASH_MODE_AUTO :
flashButton.setImageDrawable(getResources().getDrawable(R.drawable.camera_flash_auto));
break;
case Camera.Parameters.FLASH_MODE_OFF :
flashButton.setImageDrawable(getResources().getDrawable(R.drawable.camera_flash_off));
break;
}
}
//endregion
//region Bearing Methods
/**
* Method that gives a cardinal direction based on the current bearing to the true north
* #param bearing is the bearing to the true north
* #return cardinal direction that belongs to the bearing
*/
private String bearingToString(Double bearing) {
String strHeading = "?";
if (isBetween(bearing,-180.0,-157.5)) { strHeading = "South"; }
else if (isBetween(bearing,-157.5,-112.5)) { strHeading = "SouthWest"; }
else if (isBetween(bearing,-112.5,-67.5)) { strHeading = "West"; }
else if (isBetween(bearing,-67.5,-22.5)) { strHeading = "NorthWest"; }
else if (isBetween(bearing,-22.5,22.5)) { strHeading = "North"; }
else if (isBetween(bearing,22.5,67.5)) { strHeading = "NorthEast"; }
else if (isBetween(bearing,67.5,112.5)) { strHeading = "East"; }
else if (isBetween(bearing,112.5,157.5)) { strHeading = "SouthEast"; }
else if (isBetween(bearing,157.5,180.0)) { strHeading = "South"; }
return strHeading;
}
/**
* Method that checks if a certain number is in a certain range of numbers
* #param x is the number to check
* #param lower is the number that defines the lower boundary of the number range
* #param upper is the number that defines the upper boundary of the number range
* #return true if the number is between the other numbers, false otherwise
*/
private boolean isBetween(double x, double lower, double upper) {
return lower <= x && x <= upper;
}
/*
Method that triggers when the bearing changes, it sets the current bearing and sends an updated context to the provider
*/
#Override
public void onBearingChanged(double bearing) {
this.bearing = bearing;
mBearingProvider.setContext(this);
ImageView image = (ImageView) findViewById(R.id.ivCompass);
// create a rotation animation (reverse turn degree degrees)
if (bearing < 0) {
bearing += 360;
}
RotateAnimation ra = new RotateAnimation((float)currentBearing,(float)-bearing, Animation.RELATIVE_TO_SELF, 0.5f,Animation.RELATIVE_TO_SELF,0.5f);
// how long the animation will take place
ra.setDuration(210);
// set the animation after the end of the reservation status
ra.setFillAfter(true);
// Start the animation
image.startAnimation(ra);
currentBearing = -bearing;
mBearingProvider.setContext(this);
}
//endregion
}
EDIT 2:
I have made a small change to the onBearingChanged Method and now the compass is still visible, but thinks it's invisible and isn't moving because of my new if statement
#Override
public void onBearingChanged(double bearing) {
this.bearing = bearing;
mBearingProvider.setContext(this);
ImageView image = (ImageView) findViewById(R.id.ivCompass);
if (image.getVisibility() == View.VISIBLE) {
// create a rotation animation (reverse turn degree degrees)
if (bearing < 0) {
bearing += 360;
}
RotateAnimation ra = new RotateAnimation((float) currentBearing, (float) -bearing, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
// how long the animation will take place
ra.setDuration(210);
// set the animation after the end of the reservation status
ra.setFillAfter(true);
// Start the animation
image.startAnimation(ra);
currentBearing = -bearing;
}
mBearingProvider.setContext(this);
}
If you have some kind of animation, the animation probably intefieres on the calling to invisibility. Try with this just before calling INVISIBLE:
findViewById(R.id.ivCompass).clearAnimation();
I'm trying to build a custom Camera in Android without using the Camera API (Using intents). I have a MainActivity.java, I need to call the CameraPreview class in order to see the camera preview, but I don't know how to call that class.
I tried
public CameraPreview cameraPreview;
and later
mPreview = new CameraPreview(this, mCamera);
if(hasCamera) {
// Create an instance of Camera
mCamera = getCameraInstance();
// Create our Preview view and set it as the content of our activity.
mPreview = new CameraPreview(this, mCamera);
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.addView(mPreview);
}
It doesn't work. Please advice!
My code compiles but I don't see the preview on screen.
public class MainActivity extends AppCompatActivity implements SensorEventListener{
public CameraPreview cameraPreview;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
hasCamera = checkCameraHardware(this);
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
if (mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) != null){
// Success! There's a accelerometer.
mAccel = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
Toast.makeText(MainActivity.this, "Accelerometer Found!", Toast.LENGTH_SHORT).show();
}
else {
// Failure! No accelerometer.
Toast.makeText(MainActivity.this, "Accelerometer Not Found!", Toast.LENGTH_SHORT).show();
}
if(hasCamera) {
// Create an instance of Camera
mCamera = getCameraInstance();
// Create our Preview view and set it as the content of our activity.
mPreview = new CameraPreview(this, mCamera);
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.addView(mPreview);
/* Button captureButton = (Button) findViewById(R.id.button_capture);
captureButton.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
// get an image from the camera
mCamera.takePicture(null, null, mPicture);
}
}
); */
}
//Accelerometer Handling
mAccel = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(this, mAccel, SensorManager.SENSOR_DELAY_NORMAL);
if(safeToTakePicture) {
// cameraPreview.surfaceCreated(mHolder);
mCamera.takePicture(null, null, mPicture);
safeToTakePicture = false;
}
}
#Override
public void onSensorChanged(SensorEvent event) {
long currentTime = System.currentTimeMillis();
// update every 1 sec
if((currentTime - prevTime) > 10000){
mDiff = currentTime - prevTime;
mX = event.values[0];
mY = event.values[1];
mZ = event.values[2];
float speed = Math.abs(mX + mY + mZ - mPrev_x - mPrev_y - mPrev_z) / mDiff;
if (speed > SHAKE_THRESHOLD) {
Log.d("sensor", "Shake detected with speed: " + speed);
mCamera.takePicture(null, null, mPicture);
safeToTakePicture = false;
Toast.makeText(this, "Shake detected with speed: " + speed, Toast.LENGTH_SHORT).show();
}
}
mPrev_x = mX;
mPrev_y = mY;
mPrev_z = mZ;
prevTime = currentTime;
// mCamera.takePicture(null, null, mPicture);
// Toast.makeText(MainActivity.this, "SensorChanged Event! x:" + event.values[0], Toast.LENGTH_SHORT).show();
}
private boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
// this device has a camera
return true;
} else {
// no camera on this device
return false;
}
}
/** A safe way to get an instance of the Camera object. */
//Deprecated Camera instance, but ok
public static Camera getCameraInstance(){
Camera c = null;
try {
c = Camera.open(); // attempt to get a Camera instance
}
catch (Exception e){
// Camera is not available (in use or does not exist)
}
return c; // returns null if camera is unavailable
}
// receive data in a JPEG format,
// you must implement an Camera.PictureCallback interface to receive the image data and write it to a file
private Camera.PictureCallback mPicture = new Camera.PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
if (pictureFile == null){
Log.d(TAG, "Error creating media file, check storage permissions: " /* +
e.getMessage()*/);
return;
}
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.close();
} catch (FileNotFoundException e) {
Log.d(TAG, "File not found: " + e.getMessage());
} catch (IOException e) {
Log.d(TAG, "Error accessing file: " + e.getMessage());
}
}
};
private static File getOutputMediaFile(int type){
// To be safe, you should check that the SDCard is mounted
// using Environment.getExternalStorageState() before doing this.
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), "MyCameraApp");
// This location works best if you want the created images to be shared
// between applications and persist after your app has been uninstalled.
// Create the storage directory if it does not exist
if (! mediaStorageDir.exists()){
if (! mediaStorageDir.mkdirs()){
Log.d("MyCameraApp", "failed to create directory");
return null;
}
}
// Create a media file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File mediaFile;
if (type == MEDIA_TYPE_IMAGE){
mediaFile = new File(mediaStorageDir.getPath() + File.separator +
"IMG_"+ timeStamp + ".jpg");
} else if(type == MEDIA_TYPE_VIDEO) {
mediaFile = new File(mediaStorageDir.getPath() + File.separator +
"VID_"+ timeStamp + ".mp4");
} else {
return null;
}
return mediaFile;
}
public final void onAccuracyChanged(Sensor sensor, int accuracy){
//mCamera.takePicture(null, null, mPicture);
//Toast.makeText(this, "Taking a picture now", Toast.LENGTH_SHORT).show();
}
#Override
protected void onPause() {
super.onPause();
letGo();
}
#Override
protected void onDestroy() {
super.onDestroy();
letGo();
}
private void letGo(){
if(mCamera != null){
mCamera.stopPreview();
mCamera.setPreviewCallback(null);
mCamera.release();
mCamera = null;
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// Surface will be destroyed when we return, so stop the preview.
if (mCamera != null) {
// Call stopPreview() to stop updating the preview surface.
mCamera.stopPreview();
}
}
/**
* When this function returns, mCamera will be null.
*/
private void stopPreviewAndFreeCamera() {
if (mCamera != null) {
// Call stopPreview() to stop updating the preview surface.
mCamera.stopPreview();
// Important: Call release() to release the camera for use by other
// applications. Applications should release the camera immediately
// during onPause() and re-open() it during onResume()).
mCamera.release();
mCamera = null;
}
}
}
CameraPreview
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private static final String TAG = MainActivity.class.getSimpleName();
SurfaceView mSurfaceView;
private Camera mCamera;
private boolean safeToTakePicture = false;
public CameraPreview(Context context, Camera camera) {
super(context);
mCamera = camera;
mSurfaceView = new SurfaceView(context);
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = mSurfaceView.getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, now tell the camera where to draw the preview.
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
Log.d(TAG, "Error setting camera preview: " + e.getMessage());
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (mHolder.getSurface() == null){
// preview surface does not exist
return;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e){
// ignore: tried to stop a non-existent preview
}
// set preview size and make any resize, rotate or
// reformatting changes here
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
safeToTakePicture = true;
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
}
Ok guys, sharing my stable variant,
Here is improved Custom camera preview class (make attention! preview size set to 800x600 and image size around 5mpx for better performance, this can be removed, look in code):
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
private Context mContext;
private Camera.PreviewCallback previewCallback;
private Camera.AutoFocusCallback autoFocusCallback;
private static final String TAG = "CameraPreview";
private List<Camera.Size> mSupportedPreviewSizes;
private Camera.Size mPreviewSize;
public CameraPreview(Context context, Camera camera,
Camera.PreviewCallback previewCb,
Camera.AutoFocusCallback autoFocusCb) {
super(context);
mContext = context;
mCamera = camera;
previewCallback = previewCb;
autoFocusCallback = autoFocusCb;
// supported preview sizes
mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
// for(Camera.Size str: mSupportedPreviewSizes)
// Log.e(TAG, str.width + "/" + str.height);
// for(Camera.Size str: mCamera.getParameters().getSupportedPictureSizes())
// Log.e(TAG, str.width + "/" + str.height);
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
// // deprecated setting, but required on Android versions prior to 3.0
// mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
/*
* Set camera to continuous focus if supported, otherwise use
* software auto-focus. Only works for API level >=9.
*/
/*
Camera.Parameters parameters = camera.getParameters();
for (String f : parameters.getSupportedFocusModes()) {
if (f == Parameters.FOCUS_MODE_CONTINUOUS_PICTURE) {
mCamera.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
autoFocusCallback = null;
break;
}
}
*/
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException e) {
Log.d("DBG", "Error setting camera preview: " + e.getMessage());
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
/*
* If your preview can change or rotate, take care of those events here.
* Make sure to stop the preview before resizing or reformatting it.
*/
if (mHolder.getSurface() == null) {
// preview surface does not exist
return;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e) {
// ignore: tried to stop a non-existent preview
}
try {
// Hard code camera surface rotation 90 degs to match Activity view in portrait
// Camera.Parameters parameters = mCamera.getParameters();
// parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
// mCamera.setParameters(parameters);
Camera.Parameters params = mCamera.getParameters();
for(Camera.Size str: params.getSupportedPictureSizes()) {
if (str.width >= 2400 && str.width <= 2600 && str.height >= 1800 && str.height <= 2000) {
params.setPictureSize(str.width, str.height);
Log.e("Picture size: ", str.width + "/" + str.height);
}
}
for(Camera.Size str: params.getSupportedPreviewSizes())
if(str.width == 800 && str.height == 600){
params.setPreviewSize(str.width, str.height);
Log.e("Preview size: ", str.width + "/" + str.height);
}
mCamera.setParameters(params);
mCamera.setDisplayOrientation(90);
mCamera.setPreviewDisplay(mHolder);
mCamera.setPreviewCallback(previewCallback);
mCamera.startPreview();
mCamera.autoFocus(autoFocusCallback);
if(!(SettingsService.settings.autofocusOn))
mCamera.cancelAutoFocus();
} catch (Exception e) {
Log.d("DBG", "Error starting camera preview: " + e.getMessage());
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
this.getHolder().removeCallback(this);
try {
mCamera.stopPreview();
mCamera.release();
}catch(Exception e){
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
if (mSupportedPreviewSizes != null) {
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
}
float ratio;
if(mPreviewSize.height >= mPreviewSize.width)
ratio = (float) mPreviewSize.height / (float) mPreviewSize.width;
else
ratio = (float) mPreviewSize.width / (float) mPreviewSize.height;
// One of these methods should be used, second method squishes preview slightly
setMeasuredDimension(width, (int) (width * ratio));
// setMeasuredDimension((int) (width * ratio), height);
}
private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) h / w;
if (sizes == null)
return null;
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
for (Camera.Size size : sizes) {
double ratio = (double) size.height / size.width;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Camera.Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}
}
And here is usage in activity or fragment:
public class ScanFragment extends Fragment {
private View mFragmentView;
private static Camera mCamera;
private CameraPreview mPreview;
private Handler autoFocusHandler;
private Context mContext;
public boolean previewing = true;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mContext = (MainActivity)getActivity();
// Inflate the layout for this fragment
mFragmentView = inflater.inflate(R.layout.fragment_scan, container, false);
}
public void cameraPreviewStop(){
previewing = false;
if(mCamera != null) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
}
}
public void cameraPreviewStart(){
try {
if (mCamera != null) {
mCamera.setPreviewCallback(previewCb);
mCamera.startPreview();
previewing = true;
mCamera.autoFocus(autoFocusCB);
}else{
initControls();
cameraPreviewStart();
}
}catch (Exception e){
releaseCamera();
initControls();
cameraPreviewStart();
}
}
public void initControls() {
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
autoFocusHandler = new Handler();
try{
mCamera = getCameraInstance();
}catch(Exception e){
}
// Instance barcode scanner
initPreviewCamera();
}
public void initPreviewCamera(){
autoFocusCB = new Camera.AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
autoFocusHandler.postDelayed(doAutoFocus, 1000);
}
};
mPreview = new CameraPreview(mContext, mCamera, previewCb,
autoFocusCB);
FrameLayout preview = (FrameLayout) mFragmentView.findViewById(R.id.cameraPreview);
preview.removeAllViews();
preview.addView(mPreview);
}
/**
* A safe way to get an instance of the Camera object.
*/
public static Camera getCameraInstance() {
Camera c = null;
try {
c = Camera.open();
} catch (Exception e) {
}
return c;
}
public void releaseCamera() {
if (mCamera != null) {
previewing = false;
try {
mCamera.setPreviewCallback(null);
mCamera.release();
}catch (Exception e){
}
mCamera = null;
}
}
private Runnable doAutoFocus = new Runnable() {
public void run() {
if (previewing)
mCamera.autoFocus(autoFocusCB);
}
};
Camera.PreviewCallback previewCb = new Camera.PreviewCallback() {
public void onPreviewFrame(byte[] data, Camera camera) {
Camera.Parameters parameters = camera.getParameters();
Camera.Size size = parameters.getPreviewSize();
Image imageFromCamera = new Image(size.width, size.height, "Y800");
// Do something...
}
};
// Mimic continuous auto-focusing
Camera.AutoFocusCallback autoFocusCB = new Camera.AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
autoFocusHandler.postDelayed(doAutoFocus, 1000);
}
};
}
If in activity then override some things:
#Override
protected void onResume() {
super.onResume();
if(previewing) {
initControls();
cameraPreviewStart();
}
}
#Override
protected void onStop() {
super.onStop();
releaseCamera();
}
And the fragment view with camera fragment (you can add here your custom views and anything you want to customize, size of preview, absolutely anything you want):
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:local="http://schemas.android.com/apk/res-auto">
<FrameLayout
android:id="#+id/cameraPreview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
And final note, this is very important! Especially for Android api 23+ devices, from android 6.0 you must use only one instance of camera if you got null on camera object then you are trying to open camera that is already opened. Have a nice day ;)
I have a custom camera app , that previews the camera video output on SurfaceView and attempts to take pictures , the pics should be will be processed by “xzing “scanner API to decode any barcodes in the image.
My app previews correctly and does not throw any errors or expectations, however my onPictureTaken(byte[] imgData, Camera camera) method & PictureCallback are never called, therefore I’m unable to get the image & continue with further scanning.
Below is the actual code for activity that takes care of Camera logic. The onPictureTaken(byte[] imgData, Camera camera) method & PictureCallback are in this class(ScanVinFromBarcodeActivity.java) see blow :
package com.ty.tyownerspoc.barcode;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.ChecksumException;
import com.google.zxing.FormatException;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.NotFoundException;
import com.google.zxing.RGBLuminanceSource;
import com.google.zxing.Reader;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.Point;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PictureCallback;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.Display;
import android.view.SurfaceView;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.ty.tyownerspoc.R;
public class ScanVinFromBarcodeActivity extends Activity {
private Camera globalCamera;
private int cameraId = 0;
private TextView VINtext = null;
private View scanButton = null;
// bitmap from camera
private Bitmap bmpOfTheImageFromCamera = null;
// global flag whether a camera has been detected
private boolean isThereACamera = false;
// surfaceView for preview object
private FrameLayout frameLayoutBarcodeScanner = null;
private CameraPreview newCameraPreview = null;
private SurfaceView surfaceViewBarcodeScanner = null;
private int counter = 0;
private volatile boolean finishedPictureTask = false;
/*
* This method , finds FEATURE_CAMERA, opens the camera, set parameters ,
* add CameraPreview to layout, set camera surface holder, start preview
*/
private void initializeGlobalCamera() {
try {
if (!getPackageManager().hasSystemFeature(
PackageManager.FEATURE_CAMERA)) {
Toast.makeText(this, "No camera on this device",
Toast.LENGTH_LONG).show();
} else { // check for front camera ,and get the ID
cameraId = findFrontFacingCamera();
if (cameraId < 0) {
Toast.makeText(this, "No front facing camera found.",
Toast.LENGTH_LONG).show();
} else {
Log.d("ClassScanViewBarcodeActivity",
"camera was found , ID: " + cameraId);
// camera was found , set global camera flag to true
isThereACamera = true;
// OPEN
globalCamera = Camera.open(cameraId);
// pass surfaceView to CameraPreview
newCameraPreview = new CameraPreview(this, globalCamera);
// pass CameraPreview to Layout
frameLayoutBarcodeScanner.addView(newCameraPreview);
try {
globalCamera
.setPreviewDisplay(surfaceViewBarcodeScanner
.getHolder());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// PREVIEW
globalCamera.startPreview();
Log.d("ClassScanViewBarcodeActivity",
"camera opened & previewing");
}
}// end else ,check for front camera
}// end try
catch (Exception exc) {
// in case of exception release resources & cleanup
if (globalCamera != null) {
globalCamera.stopPreview();
globalCamera.setPreviewCallback(null);
globalCamera.release();
globalCamera = null;
}
Log.d("ClassScanViewBarcodeActivity initializeGlobalCamera() exception:",
exc.getMessage());
}// end catch
}
// onCreate, instantiates layouts & surfaceView used for video preview
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_barcode_vin_scanner);
Log.d("ClassScanViewBarcodeActivity", "onCreate ");
// create surfaceView for previewing of camera image
frameLayoutBarcodeScanner = (FrameLayout) findViewById(R.id.FrameLayoutForPreview);
surfaceViewBarcodeScanner = (SurfaceView) findViewById(R.id.surfaceViewBarcodeScanner);
initializeGlobalCamera();
// create text area & scan button
VINtext = (TextView) findViewById(R.id.mytext);
scanButton = findViewById(R.id.webbutton);
// on click listener, onClick take a picture
scanButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
// if true take a picture
if (isThereACamera) {
Log.d("ClassScanViewBarcodeActivity",
"setOnClickListener() isThereACamera: "+ isThereACamera);
//set picture format to JPEG, everytime makesure JPEg callback is called
Parameters parameters = globalCamera.getParameters();
parameters.setPictureFormat(ImageFormat.JPEG);
globalCamera.setParameters(parameters);
//take pic , should call Callback
globalCamera.takePicture(null, null, jpegCallback);
// wait 1 sec , than start preview again
Thread.sleep(1000);
//STOP
globalCamera.stopPreview();
//start previewing again onthe SurfaceView in case use wants to take another pic/scan
globalCamera.startPreview();
}
}// end try
catch (Exception exc) {
// in case of exception release resources & cleanup
if (globalCamera != null) {
globalCamera.stopPreview();
globalCamera.setPreviewCallback(null);
globalCamera.release();
globalCamera = null;
}
Log.d("ClassScanViewBarcodeActivity setOnClickListener() exceprtion:",
exc.getMessage());
}// end catch
}// end on Click
});// end OnClickListener() implementation
}// end onCreate
#Override
protected void onResume() {
Log.d("ClassScanViewBarcodeActivity, onResume() globalCamera:",
String.valueOf(globalCamera));
if (globalCamera != null) {
// START PREVIEW
globalCamera.startPreview();
} else {
initializeGlobalCamera();
}
super.onResume();
}
#Override
protected void onStop() {
if (globalCamera != null) {
globalCamera.stopPreview();
globalCamera.setPreviewCallback(null);
globalCamera.release();
globalCamera = null;
}
super.onStop();
}
//callback used by takePicture()
PictureCallback jpegCallback = new PictureCallback() {
public void onPictureTaken(byte[] imgData, Camera camera) {
BinaryBitmap bitmap = null;
try {
Log.d("ClassScanViewBarcodeActivity" ,"onPictureTaken()");
//save image to sd card
savePicture(imgData);
// get the bitmap from camera imageData
bmpOfTheImageFromCamera = BitmapFactory.decodeByteArray(
imgData, 0, imgData.length);
if (bmpOfTheImageFromCamera != null) {
//Galaxy S3 , incorrect rotation issue rotate to correct rotation
Matrix matrix = new Matrix();
matrix.postRotate(90);
Bitmap rotatedBitmap = Bitmap.createBitmap(bmpOfTheImageFromCamera, 0, 0, bmpOfTheImageFromCamera.getWidth(), bmpOfTheImageFromCamera.getHeight(), matrix, true);
// convert bitmap to binary bitmap
bitmap = cameraBytesToBinaryBitmap(rotatedBitmap);
if (bitmap != null) {
// decode the VIN
String VIN = decodeBitmapToString(bitmap);
Log.d("***ClassScanViewBarcodeActivity ,onPictureTaken(): VIN ",
VIN);
VINtext.setText(VIN);
} else {
Log.d("ClassScanViewBarcodeActivity ,onPictureTaken(): bitmap=",String.valueOf(bitmap));
}
} else {
Log.d("ClassScanViewBarcodeActivity , onPictureTaken(): bmpOfTheImageFromCamera = ",
String.valueOf(bmpOfTheImageFromCamera));
}
}// end try
catch (Exception exc) {
exc.getMessage();
Log.d("ClassScanViewBarcodeActivity , scanButton.setOnClickListener(): exception = ",
exc.getMessage());
}
}// end onPictureTaken()
};// jpegCallback implementation
/*
* created savePicture(byte [] data) for testing
*/
public void savePicture(byte [] data)
{
Log.d( "ScanVinFromBarcodeActivity " , "savePicture(byte [] data)");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyymmddhhmmss");
String date = dateFormat.format(new Date());
String photoFile = "Picture_"+counter+"_"+ date + ".jpg";
File sdDir = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
String filename =sdDir + File.separator + photoFile;
File pictureFile = new File(filename);
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.close();
Toast.makeText(this, "New Image saved:" + photoFile,
Toast.LENGTH_LONG).show();
} catch (Exception error) {
Log.d( "File not saved: " , error.getMessage());
Toast.makeText(this, "Image could not be saved.",
Toast.LENGTH_LONG).show();
}
counter++;
}
private int findFrontFacingCamera() {
int cameraId = -1;
// Search for the front facing camera
int numberOfCameras = Camera.getNumberOfCameras();
for (int i = 0; i < numberOfCameras; i++) {
CameraInfo info = new CameraInfo();
Camera.getCameraInfo(i, info);
if (info.facing == CameraInfo.CAMERA_FACING_BACK) {
Log.d("ClassScanViewBarcodeActivity , findFrontFacingCamera(): ",
"Camera found");
cameraId = i;
break;
}
}
return cameraId;
}// end findFrontFacingCamera()
#Override
protected void onPause() {
if (globalCamera != null) {
globalCamera.stopPreview();
globalCamera.setPreviewCallback(null);
globalCamera.release();
globalCamera = null;
}
super.onPause();
}// end onPause()
public String decodeBitmapToString(BinaryBitmap bitmap) {
Reader reader = null;
Result result = null;
String textResult = null;
try {
reader = new MultiFormatReader();
if (bitmap != null) {
result = reader.decode(bitmap);
if (result != null) {
textResult = result.getText();
} else {
Log.d("ClassScanViewBarcodeActivity , String decodeBitmapToString (BinaryBitmap bitmap): result = ",
String.valueOf(result));
}
} else {
Log.d("ClassScanViewBarcodeActivity , String decodeBitmapToString (BinaryBitmap bitmap): bitmap = ",
String.valueOf(bitmap));
}
/*
* byte[] rawBytes = result.getRawBytes(); BarcodeFormat format =
* result.getBarcodeFormat(); ResultPoint[] points =
* result.getResultPoints();
*/
} catch (NotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ChecksumException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (FormatException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return textResult;
}// end decodeBitmapToString (BinaryBitmap bitmap)
public BinaryBitmap cameraBytesToBinaryBitmap(Bitmap bitmap) {
BinaryBitmap binaryBitmap = null;
if (bitmap != null) {
int[] pixels = new int[bitmap.getHeight() * bitmap.getWidth()];
bitmap.getPixels(pixels, 0, 0, bitmap.getWidth() - 1,
bitmap.getHeight() - 1, bitmap.getWidth(),
bitmap.getHeight());
RGBLuminanceSource source = new RGBLuminanceSource(
bitmap.getWidth(), bitmap.getHeight(), pixels);
HybridBinarizer bh = new HybridBinarizer(source);
binaryBitmap = new BinaryBitmap(bh);
} else {
Log.d("ClassScanViewBarcodeActivity , cameraBytesToBinaryBitmap (Bitmap bitmap): bitmap = ",
String.valueOf(bitmap));
}
return binaryBitmap;
}
#SuppressLint("NewApi")
#SuppressWarnings("deprecation")
/*
* The method getScreenOrientation() return screen orientation either
* landscape or portrait. IF width < height , than orientation = portrait,
* ELSE landscape For backwards compatibility we use to methods to detect
* the orientation. The first method is for API versions prior to 13 or
* HONEYCOMB.
*/
public int getScreenOrientation() {
int currentapiVersion = android.os.Build.VERSION.SDK_INT;
// if API version less than 13
Display getOrient = getWindowManager().getDefaultDisplay();
int orientation = Configuration.ORIENTATION_UNDEFINED;
if (currentapiVersion < android.os.Build.VERSION_CODES.HONEYCOMB) {
// Do something for API version less than HONEYCOMB
if (getOrient.getWidth() == getOrient.getHeight()) {
orientation = Configuration.ORIENTATION_SQUARE;
} else {
if (getOrient.getWidth() < getOrient.getHeight()) {
orientation = Configuration.ORIENTATION_PORTRAIT;
} else {
orientation = Configuration.ORIENTATION_LANDSCAPE;
}
}
} else {
// Do something for API version greater or equal to HONEYCOMB
Point size = new Point();
this.getWindowManager().getDefaultDisplay().getSize(size);
int width = size.x;
int height = size.y;
if (width < height) {
orientation = Configuration.ORIENTATION_PORTRAIT;
} else {
orientation = Configuration.ORIENTATION_LANDSCAPE;
}
}
return orientation;
}// end getScreenOrientation()
}// end class ScanVinFromBarcodeActivity
Preview class used to display live camera on SurfaceView (CameraPreview.java):
package com.ty.tyownerspoc.barcode;
import java.io.IOException;
import java.util.List;
import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.Size;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.ViewGroup;
import android.hardware.Camera.CameraInfo;
/** A basic Camera preview class */
public class CameraPreview extends SurfaceView implements
SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
private Context context;
public CameraPreview(Context context, Camera camera) {
super(context);
mCamera = camera;
this.context = context;
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, now tell the camera where to draw the
// preview.
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (mHolder.getSurface() == null) {
// preview surface does not exist
return;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e) {
// ignore: tried to stop a non-existent preview
}
Camera.Parameters p = mCamera.getParameters();
// get width & height of the SurfaceView
int SurfaceViewWidth = this.getWidth();
int SurfaceViewHeight = this.getHeight();
List<Size> sizes = p.getSupportedPreviewSizes();
Size optimalSize = getOptimalPreviewSize(sizes, SurfaceViewWidth, SurfaceViewHeight);
// set parameters
p.setPreviewSize(optimalSize.width, optimalSize.height);
/*rotate the image by 90 degrees clockwise , in order to correctly displayed the image , images seem to be -90 degrees (counter clockwise) rotated
* I even tried setting it to p.setRotation(0); , but still no effect.
*/
mCamera.setDisplayOrientation(90);
mCamera.setParameters(p);
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e) {
Log.d("CameraPreview , surfaceCreated() , orientation: ",
String.valueOf(e.getMessage()));
}
}// end surfaceChanged()
static Size getOptimalPreviewSize(List <Camera.Size>sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.1;
final double MAX_DOWNSIZE = 1.5;
double targetRatio = (double) w / h;
if (sizes == null) return null;
Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
// Try to find an size match aspect ratio and size
for (Camera.Size size : sizes) {
double ratio = (double) size.width / size.height;
double downsize = (double) size.width / w;
if (downsize > MAX_DOWNSIZE) {
//if the preview is a lot larger than our display surface ignore it
//reason - on some phones there is not enough heap available to show the larger preview sizes
continue;
}
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
// Cannot find the one match the aspect ratio, ignore the requirement
//keep the max_downsize requirement
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Size size : sizes) {
double downsize = (double) size.width / w;
if (downsize > MAX_DOWNSIZE) {
continue;
}
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
//everything else failed, just take the closest match
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}
}//end Preview class
Layout for the “ScanVinFromBarcodeActivity” activity (activity_barcode_vin_scanner.xml):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:padding="20dip" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:padding="20dip"
android:text="#string/decode_label"
android:textColor="#color/mbackground1" />
<TextView
android:id="#+id/mytext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#color/mbackground2"
android:gravity="center_horizontal"
android:padding="20dip"
android:textColor="#color/mytextcolor" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:padding="20dip"
android:text="#string/continue_label"
android:textColor="#color/mytextcolor" />
<Button
android:id="#+id/webbutton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/web_button"
android:textColor="#color/mytextcolor" />
<FrameLayout
android:id="#+id/FrameLayoutForPreview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1">
<SurfaceView
android:id="#+id/surfaceViewBarcodeScanner"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</FrameLayout>
</LinearLayout>
Any help will be appreciated.
Thanks
Updated & working onTakePicturen method, the that works , the PictureCallback returns (stopPreview & StartPreview were moved to inside this method in its finally block).
public void onPictureTaken(byte[] imgData, Camera camera) {
BinaryBitmap bitmap;
try {
Log.d("ClassScanViewBarcodeActivity" ,"onPictureTaken()");
//save image to sd card
savePicture(imgData);
// get the bitmap from camera imageData
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
//down sample
bmpOfTheImageFromCamera = BitmapFactory.decodeByteArray(
imgData, 0, imgData.length,options);
if (bmpOfTheImageFromCamera != null) {
Log.d("***ClassScanViewBarcodeActivity ,onPictureTaken(): bmpOfTheImageFromCamera getByteCount(): ",
String.valueOf(bmpOfTheImageFromCamera.getByteCount()));
//Galaxy S3 , incorrect rotation issue rotate to correct rotation
/*Matrix matrix = new Matrix();
matrix.postRotate(90);
Bitmap rotatedBitmap = Bitmap.createBitmap(bmpOfTheImageFromCamera, 0, 0, bmpOfTheImageFromCamera.getWidth(), bmpOfTheImageFromCamera.getHeight(), matrix, true);*/
// convert bitmap to binary bitmap
bitmap = cameraBytesToBinaryBitmap(bmpOfTheImageFromCamera);
if (bitmap != null) {
// decode the VIN
String VIN = decodeBitmapToString(bitmap);
Log.d("***ClassScanViewBarcodeActivity ,onPictureTaken(): VIN ",
VIN);
VINtext.setText(VIN);
} else {
Log.d("ClassScanViewBarcodeActivity ,onPictureTaken(): bitmap=",String.valueOf(bitmap));
}
} else {
Log.d("ClassScanViewBarcodeActivity , onPictureTaken(): bmpOfTheImageFromCamera = ",
String.valueOf(bmpOfTheImageFromCamera));
}
}// end try
catch (Exception exc) {
exc.getMessage();
Log.d("ClassScanViewBarcodeActivity , scanButton.setOnClickListener(): exception = ",
exc.getMessage());
}
finally
{
globalCamera.stopPreview();
//start previewing again onthe SurfaceView in case use wants to take another pic/scan
globalCamera.startPreview();
}
}// end onPictureTaken()
};// jpegCallback implementation
The only thing I can find that might be the culprit is that you're starting the preview again before the jpegCallback has returned. According to the javadoc, this is not allowed:
After calling this method, you must not call startPreview() or take another picture until the JPEG callback has returned.
The callback is actually put on the UI thread queue - which will be suspended in your Thread.sleep() and the same thread will have called stopPreview() and startPreview() before actually coming to the callback. Generally speaking, if you call Thread.sleep() on the UI thread - you're doing it wrong. So, hopefully if you remove the sleep() and put what comes after it in the jpegCallback your problem should be solved.