What I have done so far:
I have implemented custom camera for reading qr code which need to continue focus the camera for better qr reading.
My problem is when I use to focus in every one second with the handler the camera flash on\off button dont works or it takes too much time to turning on and off the camera flash light. Every thing works fine when I remove the code of auto focusing the camera every second (The runnable and the handler).
What I want is to focus automatically and quickly whenever camera moves and also able to turn on and off the flash on demand quickly without using Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE
because its not available for API<14
.
I have used Camera.Parameters.FOCUS_MODE_AUTO
but its only focusing the camera once when started thats why i used handler to focus camera every second.
Min SDK Version of project is 9.
My Camera Activity is
public class CameraActivityNew extends Activity implements OnClickListener,
Camera.PreviewCallback {
CameraPreviewNew mPreview;
FrameLayout flCameraPreview;
ImageButton ibFlashButton;
Boolean isFlashOn = false;
Camera mCamera;
private Handler mAutoFocusHandler;
private boolean mPreviewing = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
mAutoFocusHandler = new Handler();
setContentView(R.layout.activity_camera);
findSetupViews();
mPreview = new CameraPreviewNew(getApplicationContext(), this,
autoFocusCB);
flCameraPreview.addView(mPreview);
}
private Runnable doAutoFocus = new Runnable() {
public void run() {
if (mCamera != null && mPreviewing) {
mCamera.autoFocus(autoFocusCB);
}
}
};
Camera.AutoFocusCallback autoFocusCB = new Camera.AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
}
};
@Override
protected void onResume() {
super.onResume();
try {
mCamera = Camera.open();
if (mCamera == null) {
return;
}
} catch (Exception e) {
e.printStackTrace();
return;
}
mPreview.setCamera(mCamera);
mPreview.showSurfaceView();
mPreviewing = true;
}
@Override
protected void onPause() {
super.onPause();
if (mCamera != null) {
mPreview.setCamera(null);
mCamera.cancelAutoFocus();
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
mPreview.hideSurfaceView();
mPreviewing = false;
mCamera = null;
}
}
private void findSetupViews() {
flCameraPreview = (FrameLayout) findViewById(R.id.flCameraPreview);
ibFlashButton = (ImageButton) findViewById(R.id.ibFlash);
ibFlashButton.setOnClickListener(this);
if (getPackageManager().hasSystemFeature(
PackageManager.FEATURE_CAMERA_FLASH)) {
ibFlashButton.setVisibility(View.VISIBLE);
ibFlashButton.setOnClickListener(this);
} else {
ibFlashButton.setVisibility(View.GONE);
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.ibFlash:
if (isFlashOn) {
mPreview.setCameraFlashLight(false);
isFlashOn = false;
ibFlashButton.setImageResource(R.drawable.flashoff);
} else {
mPreview.setCameraFlashLight(true);
ibFlashButton.setImageResource(R.drawable.flashon);
isFlashOn = true;
}
break;
}
}
@Override
public void onPreviewFrame(final byte[] data, final Camera camera) {
// processed here qr code and works fine if camera focus
//now removed to narrow the code for posting the question
}
}
And the Camera Preview class is:
public class CameraPreviewNew extends ViewGroup implements Callback {
public static final int CAMERA_BACK = 0;
public static final int CAMERA_FRONT = 1;
public Camera mCamera = null;
private Context context = null;
SurfaceView mSurfaceView;
SurfaceHolder mSurfaceHolder;
Size mPreviewSize;
List<Size> mSupportedPreviewSizes;
PreviewCallback mPreviewCallback;
AutoFocusCallback mAutoFocusCallback;
public CameraPreviewNew(Context context,
PreviewCallback previewCallback, AutoFocusCallback autoFocusCb) {
super(context);
mPreviewCallback = previewCallback;
mAutoFocusCallback = autoFocusCb;
this.context = context;
mSurfaceView = new SurfaceView(context);
addView(mSurfaceView);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(this);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
}
public void setCamera(Camera camera) {
mCamera = camera;
if (mCamera != null) {
mSupportedPreviewSizes = mCamera.getParameters()
.getSupportedPreviewSizes();
requestLayout();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int width = resolveSize(getSuggestedMinimumWidth(),
widthMeasureSpec);
final int height = resolveSize(getSuggestedMinimumHeight(),
heightMeasureSpec);
setMeasuredDimension(width, height);
}
public void hideSurfaceView() {
mSurfaceView.setVisibility(View.INVISIBLE);
}
public void showSurfaceView() {
mSurfaceView.setVisibility(View.VISIBLE);
}
public void surfaceCreated(SurfaceHolder holder) {
try {
if (mCamera != null) {
mCamera.setPreviewDisplay(holder);
}
} catch (IOException exception) {
Log.e("logtag", "IOException caused by setPreviewDisplay()",
exception);
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
if (mCamera != null) {
mCamera.cancelAutoFocus();
mCamera.stopPreview();
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
if (holder.getSurface() == null) {
return;
}
if (mCamera != null) {
Camera.Parameters parameters = mCamera.getParameters();
mPreviewSize = getBestPreviewSize(mCamera.getParameters(), w, h);
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
requestLayout();
mCamera.setParameters(parameters);
mCamera.setPreviewCallback(mPreviewCallback);
mCamera.startPreview();
mCamera.autoFocus(mAutoFocusCallback);
setCameraDisplayOrientation(0);
}
}
private void setCameraDisplayOrientation(int cameraId) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, info);
int rotation = ((WindowManager) context
.getSystemService(Context.WINDOW_SERVICE)).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);
}
protected static Comparator<Size> newSizeComparator() {
return new Comparator<Size>() {
@Override
public int compare(Size lhs, Size rhs) {
return Integer.valueOf(rhs.height * rhs.width).compareTo(
lhs.height * lhs.width);
}
};
}
private Size getBestPreviewSize(Parameters parameters, int screenWidth,
int screenHeight) {
List<Size> supportedSizes = parameters.getSupportedPreviewSizes();
Collections.sort(supportedSizes, newSizeComparator());
int previewHeight = screenHeight;
int previewWidth = screenWidth;
if (previewHeight > previewWidth) {
int swap = previewWidth;
previewWidth = previewHeight;
previewHeight = swap;
}
Size bestSize = null;
float bestRatio = 999;
for (Size s : supportedSizes) {
if (s.height > s.width) {
int swap = s.width;
s.width = s.height;
s.height = swap;
}
float cameraRatio = ((float) s.height / (float) s.width);
float screenRatio = ((float) previewHeight)
/ ((float) previewWidth);
if ((s.height >= previewHeight) && (s.width >= previewWidth)) {
float ratioDiff = cameraRatio - screenRatio;
if ((ratioDiff < 0.19) && (ratioDiff > -0.19)
&& (Math.abs(bestRatio) > Math.abs(ratioDiff))) {
bestSize = s;
bestRatio = ratioDiff;
}
}
}
return bestSize;
}
public void setCameraFlashLight(Boolean setFlash) {
Parameters _parameters = mCamera.getParameters();
if (setFlash) {
_parameters.setFlashMode(Parameters.FLASH_MODE_TORCH);
} else {
_parameters.setFlashMode(Parameters.FLASH_MODE_OFF);
}
mCamera.setParameters(_parameters);
mCamera.startPreview();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed && getChildCount() > 0) {
final View child = getChildAt(0);
final int width = r - l;
final int height = b - t;
int previewWidth = width;
int previewHeight = height;
if (mPreviewSize != null) {
previewWidth = mPreviewSize.width;
previewHeight = mPreviewSize.height;
}
if (width * previewHeight > height * previewWidth) {
final int scaledChildWidth = previewWidth * height
/ previewHeight;
child.layout((width - scaledChildWidth) / 2, 0,
(width + scaledChildWidth) / 2, height);
} else {
final int scaledChildHeight = previewHeight * width
/ previewWidth;
child.layout(0, (height - scaledChildHeight) / 2, width,
(height + scaledChildHeight) / 2);
}
}
}
}
I see some issue with your AutoFocus handling code.
Analysis Result
There is cycle in your autofocus.
Explanation
a) Camera Preview Class
mAutoFocusCallback
is set with theautoFocusCb
of the Camera Activity.
public CameraPreviewNew(Context context,...,AutoFocusCallback autoFocusCb)
{
super(context);
mAutoFocusCallback = autoFocusCb;
...
}
b)surfaceChanged is called once, at the time of loading the activity. The camera is requested to Auto Focus.
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h)
{
if (mCamera != null)
{
...
mCamera.startPreview();
/*Auto focus camera and call <code>mAutoFocusCallback</code> after autofocus.*/
mCamera.autoFocus(mAutoFocusCallback);
...
}
}
c) On completion of the autofocus the
mAutoFocusCallback
callback is called.mAutoFocusCallback->autoFocusCb->onAutoFocus()
Camera Activity
Camera.AutoFocusCallback autoFocusCB = new Camera.AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
}
};
d)
onAutoFocus
schedules one more autoFocus after 1000 millisecons, 1 sec.
Camera Activity
public void onAutoFocus(boolean success, Camera camera) {
mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
}
e)After one second the messages is passed to handler that calles the runnable
doAutoFocus
requesting camera to auto focus, similar to b) above.
private Runnable doAutoFocus = new Runnable() {
public void run() {
if (mCamera != null && mPreviewing) {
mCamera.autoFocus(autoFocusCB);
}
}
};
f) After completion of the autoFocus, the
autoFocusCB
is called again, similar to c) above. and cycle continues.
Camera.AutoFocusCallback autoFocusCB = new Camera.AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
}
};
Solution
I am confused why such implementation. The cycle may be reason behind not listening to the flash enable/disable calls. You need to remove the code below and do something meaningful else leave the onAutoFocus() empty.
Camera.AutoFocusCallback autoFocusCB = new Camera.AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
/*REMOVE LINE BELOW*/
mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
}
};
For auto focusing every time the camera moves you need to take help of the motion sensors provided with the phone. You can google it
Hope that helps.
Happy Coding...
It seems that you dont need to use AutoFocusCallBack
for your app, because you did nothing else than delay 1 second
.
What you can do to focus all the time is using FOCUS_MODE_CONTINUOUS_PICTURE
(read more here) like that(setFocus
method is in CameraPreview
, not in Activity
):
public void setFocus() {
Camera.Parameters p = mCamera.getParameters();
p.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
mCamera.setParameters(p);
mCamera.startPreview();
}
And call it in SurfaceChanged
:
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// Now that the size is known, set up the camera parameters and begin
// the preview.
Camera.Parameters parameters = mCamera.getParameters();
List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
// You need to choose the most appropriate previewSize for your app
Camera.Size previewSize = previewSizes.get(0);
parameters.setPreviewSize(previewSize.width, previewSize.height);
parameters.setRotation(90);
mCamera.setParameters(parameters);
mCamera.startPreview();
setFlash(true);
setZoomLevel(5);
setFocus();
Log.w(TAG, "surfaceChanged()");
}
For flash
you can use this method from CameraPreview
:
public void setFlash(boolean isFlashOn) {
Camera.Parameters p = mCamera.getParameters();
if (isFlashOn) {
p.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
mCamera.setParameters(p);
mCamera.startPreview();
} else {
p.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
mCamera.setParameters(p);
mCamera.startPreview();
}
Log.w(TAG, "setFlash()");
}
Hope it helps you! If you have any questions about my answer feel free to comment.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With