I'm trying to take a picture in the background from IntentService. On Android 4.4 all is ok, but on Android 5.1 I've got an error:
03-18 14:35:54.497 7659-8956/xyz.bringoff.proximityphoto.app E/InvisibleCameraServiceļ¹ Can't use a camera: Fail to connect to camera service
My code looks like this:
private void handleActionShot() {
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
mProximity = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
releaseCamera();
mCamera = getCameraInstance();
}
public Camera getCameraInstance() {
Camera c = null;
int numCams = Camera.getNumberOfCameras();
if (numCams > 0) {
try {
c = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
} catch (RuntimeException e) {
Log.e(TAG, "Can't use a camera: " + e.getMessage());
releaseCamera();
return null;
}
}
if (c != null) {
c.setParameters(getProperParametersForCurrentDevice(c));
c.lock();
}
return c;
}
I did not find a documented difference between camera requests on this two android versions.
I have found a problem. Receiver caught intent with STATE_OFFHOOK twice and respectively started IntentService twice. I get a camera instance on the first start, so the second time I caught onHandleIntent() camera was already locked. So, I added a check in onHandleIntent() method
if (ACTION_SHOT.equals(action) && !mAlreadyRunning) {
mAlreadyRunning = true;
handleActionShot();
}
and now it works.
Related
Problem statement:
When someone tries to open the device with the wrong pattern / PIN, my application should trigger an alarm, send an alert SMS to the registered mobile number. AND it should capture the image of the person trying to unlock the device, and send this image to registered Email ID.
What I have achieved:
I am getting notification of wrong Pattern / PIN in my DeviceAdmin class.
I start the service for background tasks. This service plays alarm successfully.
I send an alert SMS to the registered mobile number.
I sent an alert Email to the registered email ID successfully. (BUT without image.)
I am confused about how can I capture image in background IntentService when the device is locked, and that, too without preview.
I cannot use the Camera intent obviously. Because startActivityForResult can't be called from Service. PLUS, I don't want user to capture the image after opening the Camera app.
My research led me to these posts already.
Can I use Android Camera in service without preview?
How to Capture Image When Device is Locked
Issue is:
Camera API is deprecate`. Camera2 API requires Minimum sdk version 21,
but my client's requirement is minSdkVersion 15, which I can't change. I am unable to figure out what should I do now. Any reference or help please?
I solved my issue with help of this blog
So I capture the image within background service using this code:
#Override
public void onStart(Intent intent, int startId) {
mCamera = getAvailableFrontCamera(); // globally declared instance of camera
if (mCamera == null){
mCamera = Camera.open(); //Take rear facing camera only if no front camera available
}
SurfaceView sv = new SurfaceView(getApplicationContext());
SurfaceTexture surfaceTexture = new SurfaceTexture(10);
try {
mCamera.setPreviewTexture(surfaceTexture);
//mCamera.setPreviewDisplay(sv.getHolder());
parameters = mCamera.getParameters();
//set camera parameters
mCamera.setParameters(parameters);
//This boolean is used as app crashes while writing images to file if simultaneous calls are made to takePicture
if(safeToCapture) {
mCamera.startPreview();
mCamera.takePicture(null, null, mCall);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//Get a surface
sHolder = sv.getHolder();
//tells Android that this surface will have its data constantly replaced
sHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
Camera.PictureCallback mCall = new Camera.PictureCallback()
{
public void onPictureTaken(byte[] data, Camera camera)
{
safeToCapture = false;
//decode the data obtained by the camera into a Bitmap
FileOutputStream outStream = null;
try{
// create a File object for the parent directory
File myDirectory = new File(Environment.getExternalStorageDirectory()+"/Test");
// have the object build the directory structure, if needed.
myDirectory.mkdirs();
//SDF for getting current time for unique image name
SimpleDateFormat curTimeFormat = new SimpleDateFormat("ddMMyyyyhhmmss");
String curTime = curTimeFormat.format(new java.util.Date());
// create a File object for the output file
outStream = new FileOutputStream(myDirectory+"/user"+curTime+".jpg");
outStream.write(data);
outStream.close();
mCamera.release();
mCamera = null;
String strImagePath = Environment.getExternalStorageDirectory()+"/"+myDirectory.getName()+"/user"+curTime+".jpg";
sendEmailWithImage(strImagePath);
Log.d("CAMERA", "picture clicked - "+strImagePath);
} catch (FileNotFoundException e){
Log.d("CAMERA", e.getMessage());
} catch (IOException e){
Log.d("CAMERA", e.getMessage());
}
safeToCapture = true; //Set a boolean to true again after saving file.
}
};
private Camera getAvailableFrontCamera (){
int cameraCount = 0;
Camera cam = null;
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
cameraCount = Camera.getNumberOfCameras();
for (int camIdx = 0; camIdx < cameraCount; camIdx++) {
Camera.getCameraInfo(camIdx, cameraInfo);
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
try {
cam = Camera.open(camIdx);
} catch (RuntimeException e) {
Log.e("CAMERA", "Camera failed to open: " + e.getLocalizedMessage());
}
}
}
return cam;
}
//Send Email using javamail API as user will not be allowed to choose available
// application using a Chooser dialog for intent.
public void sendEmailWithImage(String imageFile){
.
.
.
}
Following uses-features and permissions will be needed for this in manifest file:
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-feature
android:name="android.hardware.camera.front"
android:required="false" />
<uses-permission android:name="android.permission.SEND_MAIL" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
I have set property required to false, so that user will be able to install my app even without any of the Camera available. May be this can help someone in future.
I am trying to develop an Android application that will take pictures in a service. Before implementing a service I am trying to implement the functionality on a button click. I've used code available on following locations
how to capture an image in background without using the camera application
However I am getting an excecption while executing Camera.takePicture() call. The LogCat shows following
12-10 14:46:56.135 589-18809/? E/QCameraStateMachine: int32_t qcamera::QCameraStateMachine::procEvtPreviewReadyState(qcamera_sm_evt_enum_t, void *): Error!! cannot handle evt(24) in state(1)
12-10 14:46:56.135 589-4574/? I/QCamera2HWI: [KPI Perf] static int qcamera::QCamera2HardwareInterface::pre_take_picture(struct camera_device *): X
12-10 14:46:56.135 589-4574/? E/QCamera2HWI: static int qcamera::QCamera2HardwareInterface::take_picture(struct camera_device *): pre_take_picture failed with ret = -38
12-10 14:46:56.135 589-4574/? I/QCameraHalWatchdog: Stopped Watchdog Thread (0ms)[take_picture]
I could not find any information on "cannot handle evt(24) in state(1)". Neither on "qcamera::QCamera2HardwareInterface::pre_take_picture(struct camera_device *): X"
I have not copied the code here since the code is already presnet in the link I've provided earlier
My min compile version is KitKat and I am using Motorola Lenovo.
Your help is highly appreciated.
Edit****
Here is the code
Camera.PictureCallback mCall = new Camera.PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
Log.d(TAG, "------ In onPictureTaken");
// decode the data obtained by the camera into a Bitmap
// display.setImageBitmap(photo);
Bitmap bitmapPicture = BitmapFactory.decodeByteArray(data, 0,
data.length);
}
};
...
Camera c = null;
try {
c = Camera.open();
SurfaceView sv = new SurfaceView(this);
SurfaceHolder holder = sv.getHolder();
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
c.setPreviewDisplay(holder);
Camera.Parameters params = c.getParameters();
params.setJpegQuality(100);
c.setParameters(params);
c.startPreview();
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
c.setPreviewDisplay(holder);
c.takePicture(null, null, mCall);
} catch (Exception e) {
Log.e(TAG, "---- problems with camera operation " + e.getMessage(), e);
e.printStackTrace();
Log.d(TAG, "------ 333333 ");
} finally {
if (c != null) {
c.stopPreview();
c.release();
}
}
I'm working with the new Camera2 API on a Samsung S5. The supported hardware level this device is reporting is LEGACY, which is fine.
However, I cannot seem to be able to auto-focus on this device. The request to trigger auto-focus looks like this:
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO);
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
state = STATE_PREVIEW;
try {
captureSession.setRepeatingRequest(previewRequestBuilder.build(), captureCallback, backgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
After the request is sent, the result of the request is always CONTROL_AF_STATE_ACTIVE_SCAN and occasionally CONTROL_AF_STATE_NOT_FOCUSED_LOCKED.
The strange thing is that, when the state is CONTROL_AF_STATE_NOT_FOCUSED_LOCKED, the auto-focus goes back into the CONTROL_AF_STATE_ACTIVE_SCAN state for a while and then back to CONTROL_AF_STATE_NOT_FOCUSED_LOCKED, resulting in a infinite focus loop. According to the docs, when state is CONTROL_AF_STATE_NOT_FOCUSED_LOCKED...
The lens will remain stationary until the AF mode (android.control.afMode) is changed or a new AF trigger is sent to the camera device (android.control.afTrigger).
I'm wondering if this discrepancy is because of the fact that the hardware level is LEGACY and that I should go back to using the deprecated Camera API, but that seems crazy for such a prevalent feature such as auto focus.
Is there any reccomendations how how to treat devices that are reporting LEGACY?
I branched form google's Camera2Basic example and changed it to use CaptureRequest.CONTROL_AF_MODE_AUTO instead of CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE
You can take the project from git and test it - https://github.com/pinhassi/android-Camera2Basic
Or just add this to Camera2BasicFragment:
private static final long LOCK_FOCUS_DELAY_ON_FOCUSED = 5000;
private static final long LOCK_FOCUS_DELAY_ON_UNFOCUSED = 1000;
private Integer mLastAfState = null;
private Handler mUiHandler = new Handler(); // UI handler
private Runnable mLockAutoFocusRunnable = new Runnable() {
#Override
public void run() {
lockAutoFocus();
}
};
public void lockAutoFocus() {
try {
// This is how to tell the camera to lock focus.
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START);
CaptureRequest captureRequest = mPreviewRequestBuilder.build();
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, null); // prevent CONTROL_AF_TRIGGER_START from calling over and over again
mCaptureSession.capture(captureRequest, mCaptureCallback, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
/**
*
* #return
*/
private float getMinimumFocusDistance() {
if (mCameraId == null)
return 0;
Float minimumLens = null;
try {
CameraManager manager = (CameraManager) getActivity().getSystemService(Context.CAMERA_SERVICE);
CameraCharacteristics c = manager.getCameraCharacteristics(mCameraId);
minimumLens = c.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE);
} catch (Exception e) {
Log.e(TAG, "isHardwareLevelSupported Error", e);
}
if (minimumLens != null)
return minimumLens;
return 0;
}
/**
*
* #return
*/
private boolean isAutoFocusSupported() {
return isHardwareLevelSupported(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) || getMinimumFocusDistance() > 0;
}
// Returns true if the device supports the required hardware level, or better.
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private boolean isHardwareLevelSupported(int requiredLevel) {
boolean res = false;
if (mCameraId == null)
return res;
try {
CameraManager manager = (CameraManager) getActivity().getSystemService(Context.CAMERA_SERVICE);
CameraCharacteristics cameraCharacteristics = manager.getCameraCharacteristics(mCameraId);
int deviceLevel = cameraCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
switch (deviceLevel) {
case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3:
Log.d(TAG, "Camera support level: INFO_SUPPORTED_HARDWARE_LEVEL_3");
break;
case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL:
Log.d(TAG, "Camera support level: INFO_SUPPORTED_HARDWARE_LEVEL_FULL");
break;
case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY:
Log.d(TAG, "Camera support level: INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY");
break;
case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED:
Log.d(TAG, "Camera support level: INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED");
break;
default:
Log.d(TAG, "Unknown INFO_SUPPORTED_HARDWARE_LEVEL: " + deviceLevel);
break;
}
if (deviceLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
res = requiredLevel == deviceLevel;
} else {
// deviceLevel is not LEGACY, can use numerical sort
res = requiredLevel <= deviceLevel;
}
} catch (Exception e) {
Log.e(TAG, "isHardwareLevelSupported Error", e);
}
return res;
}
Then, add to STATE_PREVIEW block:
case STATE_PREVIEW: {
// We have nothing to do when the camera preview is working normally.
// TODO: handle auto focus
Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
if (afState != null && !afState.equals(mLastAfState)) {
switch (afState) {
case CaptureResult.CONTROL_AF_STATE_INACTIVE:
Log.d(TAG, "CaptureResult.CONTROL_AF_STATE_INACTIVE");
lockAutoFocus();
break;
case CaptureResult.CONTROL_AF_STATE_ACTIVE_SCAN:
Log.d(TAG, "CaptureResult.CONTROL_AF_STATE_ACTIVE_SCAN");
break;
case CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED:
Log.d(TAG, "CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED");
mUiHandler.removeCallbacks(mLockAutoFocusRunnable);
mUiHandler.postDelayed(mLockAutoFocusRunnable, LOCK_FOCUS_DELAY_ON_FOCUSED);
break;
case CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED:
mUiHandler.removeCallbacks(mLockAutoFocusRunnable);
mUiHandler.postDelayed(mLockAutoFocusRunnable, LOCK_FOCUS_DELAY_ON_UNFOCUSED);
Log.d(TAG, "CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED");
break;
case CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED:
mUiHandler.removeCallbacks(mLockAutoFocusRunnable);
//mUiHandler.postDelayed(mLockAutoFocusRunnable, LOCK_FOCUS_DELAY_ON_UNFOCUSED);
Log.d(TAG, "CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED");
break;
case CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN:
Log.d(TAG, "CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN");
break;
case CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED:
mUiHandler.removeCallbacks(mLockAutoFocusRunnable);
//mUiHandler.postDelayed(mLockAutoFocusRunnable, LOCK_FOCUS_DELAY_ON_FOCUSED);
Log.d(TAG, "CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED");
break;
}
}
mLastAfState = afState;
break;
}
And replace all occurrences of:
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
With:
if (isAutoFocusSupported())
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_AUTO);
else
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
I think the issue is with your setRepeatingRequest. As far as I know, CaptureRequest.CONTROL_AF_MODE_AUTO should only cause an autofocus to occur once, but setRepeatingRequest will send continuous requests. Try using capture instead:
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO);
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
state = STATE_PREVIEW;
try {
mCaptureSession.capture(mPreviewRequestBuilder.build(), mPreCaptureCallback, mBackgroundHandler);
} catch (Exception e) {e.printStackTrace();}
I experience the same issue with a Galaxy Note 4 running Android 5.1.1 - while the same code works fine on a variety of other Android devices. There have been reports of similar issues with Galaxy-S4/S5/S6.
http://developer.samsung.com/forum/board/thread/view.do?boardName=SDK&messageId=289824&startId=zzzzz~
https://www.youtube.com/watch?v=lnMoYZwVaFM
So to anwer you question: This is most likely a bug in Samsung's implementation of the Camera-2 implementation - which seems to be of very low quality, unfourtunately.
The Samsung S5 with autofocus returned INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY, which means it does not support Camera2 api.
I have the below filter for using camera in my application.
if (Build.VERSION.SDK_INT >= 21 && isDeviceCompatibleOfCamera2()) {
// Use camera2
} else {
// Use old camera
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public boolean isDeviceCompatibleOfCamera2() {
try {
CameraManager manager = (CameraManager) getActivity().getSystemService(Context.CAMERA_SERVICE);
String backCameraId = manager.getCameraIdList()[0];
CameraCharacteristics backCameraInfo = manager.getCameraCharacteristics(backCameraId);
int level = backCameraInfo.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
return level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL;
} catch (CameraAccessException e) {
ETLog.d(TAG, "Device not compatible of camera2 api" + e);
}
return false;
}
I have a problem regarding the camera in the most recent Marshmallow build, more specifically the flashlight.
On any pre-Marshmallow version all I need to do to turn the flash on/off was the following:
private void turnFlashOn(final Camera camera, int flashLightDurationMs) {
if (!isFlashOn()) {
final List<String> supportedFlashModes = camera.getParameters().getSupportedFlashModes();
if (supportedFlashModes != null && supportedFlashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) {
mParams.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
camera.setParameters(mParams);
}
}
}
and
private void turnFlashOff(Camera camera) {
if (camera != null) {
final List<String> supportedFlashModes = camera.getParameters().getSupportedFlashModes();
if (supportedFlashModes != null && supportedFlashModes.contains(Camera.Parameters.FLASH_MODE_OFF)) {
mParams.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
camera.setParameters(mParams);
}
}
}
Unfortunately, Marshmallow devices began to crash in the wild. Somehow camera.getParameters() and camera.setParameters() began to fail with messages such as:
RuntimeException: getParameters failed (empty parameters)
RuntimeException: setParameters failed
I tried starting and stopping the preview before getting the parameters, which no longer throws errors. However the preview is not resumed when I call camera.startPreview().
I fear releasing the camera and reopening it is out of the question as this takes some seconds and would produce a bad experience.
Any suggestions on how to turn the flashlight on/off in Marshmallow reliably?
Google has introduced torchmode in OS 6 (Android M).
if your purpose is only to turn on/off the flash, below code can help you with that:
private static void handleActionTurnOnFlashLight(Context context){
try{
CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
String[] list = manager.getCameraIdList();
manager.setTorchMode(list[0], true);
}
catch (CameraAccessException cae){
Log.e(TAG, cae.getMessage());
cae.printStackTrace();
}
}
private static void handleActionTurnOffFlashLight(Context context){
try{
CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
manager.setTorchMode(manager.getCameraIdList()[0], false);
}
catch (CameraAccessException cae){
Log.e(TAG, cae.getMessage());
cae.printStackTrace();
}
}
All you have to do is: Get cameraid's list out of which camera ID zero(0) is your primary camera for which you want to turn flash on/off. Simply pass the cameraID to setTochMode API with boolean value for turning it on or off.
Do note that this piece of code will work only with OS 6, so you need to check for device OS and based upon that you need to select which API's to call for pre-marshmallow devices.
Kindly mark this as solution if it solves your problem.
As Saurabh7474 has responded, to check the version of Android and use setTorchMode API it's very correct.
Although you can also use params.setFlashMode (...) in marshmallow using
mCamera.setPreviewTexture (new SurfaceTexture (100))
after Camera.open (...) and before calling mCamera.startPreview();
try {
Log.i(TAG, "getCamera");
int requestedCameraId = getIdForRequestedCamera(mFacing);
if (requestedCameraId == -1) {
throw new RuntimeException("Could not find requested camera.");
}
mCamera = Camera.open(requestedCameraId);
mCamera.setPreviewTexture(new SurfaceTexture(DUMMY_TEXTURE_NAME));
params = mCamera.getParameters();
} catch (RuntimeException e) {
Log.e("Failed to Open. Error:", e.getMessage());
} catch (IOException e) {
Log.e("Failed to Open. can't setPreviewTexture:", e.getMessage());
}
then when you want, you can use
mParams.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
camera.setParameters(mParams);
My answer is based on CameraSource examples of Vision API that uses params.setFlashMode (...) and works in Api 23 and above.
If you decide to inspect CameraSource, the key method that has solved the same problem is "start ()", in the line 312 ...
https://github.com/googlesamples/android-vision/blob/master/visionSamples/barcode-reader/app/src/main/java/com/google/android/gms/samples/vision/barcodereader/ui/camera/CameraSource.java
The reason you can find here
https://stackoverflow.com/a/33333046/4114846
Update your app to check for permissions at runtime. You have to have android.permission.CAMERA granted. Including it in the manifest of your app is not going to grant it to you on Marshmallow. You'll need to detect whether or not it has been granted and request it.
Building off of Saurabh7474's answer, you can toggle Marshmallow's torchMode by registering a torchCallback:
final CameraManager mCameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
CameraManager.TorchCallback torchCallback = new CameraManager.TorchCallback() {
#Override
public void onTorchModeUnavailable(String cameraId) {
super.onTorchModeUnavailable(cameraId);
}
#Override
public void onTorchModeChanged(String cameraId, boolean enabled) {
super.onTorchModeChanged(cameraId, enabled);
boolean currentTorchState = enabled;
try {
mCameraManager.setTorchMode(cameraId, !currentTorchState);
} catch (CameraAccessException e){}
}
};
mCameraManager.registerTorchCallback(torchCallback, null);//fires onTorchModeChanged upon register
mCameraManager.unregisterTorchCallback(torchCallback);
I am integrating barcode scanning functionality using zxing in android, my app includes turn on/off flash light functionality by button press.
When the flash button is pressed it sets the flash to mode torch and that parameter is set to be a camera parameter. I got an exception: Set Paramters failed exception.
Here is my code.
Parameters p = camera.getParameters();
List<String> supportedFlashModes =p.getSupportedFlashModes();
if (active)
{
if(supportedFlashModes!= null)
{
if(supportedFlashModes.contains(Parameters.FLASH_MODE_TORCH))
{
p.setFlashMode(Parameters.FLASH_MODE_TORCH);
}
}
CaptureActivity.flashLightON=true;
}
else
{
p.setFlashMode(Parameters.FLASH_MODE_OFF);
}
camera.setParameters(p);
I got the following exception:
06-07 12:15:26.107: E/AndroidRuntime(24642): FATAL EXCEPTION: main
06-07 12:15:26.107: E/AndroidRuntime(24642): java.lang.RuntimeException:
setParameters failed
06-07 12:15:26.107: E/AndroidRuntime(24642): at
android.hardware.Camera.native_setParameters(Native Method)
Please advice me how to resolve this issue?
I had the same problem with my Google Nexus One.
The problem was solved for me by canceling autofocus, set parameters and then reset autofocus
camera.cancelAutoFocus();
camera.setParameters(parameters);
camera.autoFocus(autoFocusCallback);
Unfortunately its not an universal workaround since it does not work for S3 or Galaxy Nexus who totally loses it...
S-G starts to flashes and S3 stop autofocus.
I solved the issue for S3 by stoping preview and then restarting after the parameters were set
I had the same problem, turning the flaslight with the camera on causes some autofocus collision, when setting the parameters at the same time (especially when your autofocus interval is low).
Paste this right before enabling/disabling the flashlight, it will slow down the enabling event, but hey, no crash (not 100% crash proof though).
//sleep time should be long, 3000ms should be enough to prevent crash on some devices, 2000 may by to little (still crashes on Sony Xperia devices) - I have no idea wy this works that way :D
try {
Thread.currentThread().sleep(3000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
Edit:
Better way to solve this problem, is to put your setParameters method in a loop, and catch runtime exception everytime. Exit the loop when there is no exception, or when your (optional) loop counter reaches max value.
final int MAX_TRIES = 100;
boolean success = false;
int triesCounter = 0;
while (!success) {
try {
triesCounter++;
if (triesCounter > MAX_TRIES) {
success = true;
}
parameters = camera.getParameters();
if (parameters != null) {
parameters.setFlashMode(Parameters.FLASH_MODE_OFF);
camera.setParameters(parameters);
}
success = true;
} catch (Exception exception) {
}
}
Some of the devices do not support and gives such problems. You can refer the link bellow, so that you can get a clear idea about it.
How to turn on camera flash light programmatically in Android?
One solution that works is to create a queue of camera parameters. Setting the torch would add a torch parameter to the queue.
Inside the onAutoFocus callback, call a function that iterates through all of the queue items and commits them. This way you are guaranteed that you are not autofocusing.
turning the flaslight with the camera on/trouch (FLASH_MODE_TORCH)
causes some autofocus collision, when setting the parameters at the
same time (especially when your autofocus interval is low).
I don't like the thread workaround. So I fixed it like this
...
private Boolean _flashOn = null;
...
private Runnable doAutoFocus = new Runnable() {
public void run() {
if (previewing) {
if (_flashOn != null) {
Parameters p = mCamera.getParameters();
if (_flashOn) {
p.setFlashMode(Parameters.FLASH_MODE_TORCH);
}else{
p.setFlashMode(Parameters.FLASH_MODE_OFF);
}
mCamera.setParameters(p);
_flashOn = null;
}
mCamera.autoFocus(autoFocusCB);
}
}
};
No RuntimeException now.
But still has FLASH_MODE_TORCH + autoFocus bug on some adroid devices, e.g. Motorala Milestone/Samsun I9100G/Blahblah...
See also a declined issue for Android: https://code.google.com/p/android/issues/detail?id=14360
BTW. ZBar is faster than ZXing :)
I wanted to start the flash light at the start of scanning and solve it with this.
I have changed in com.google.zxing.client.android.camera method openDriver
Camera theCamera = camera;
if (theCamera == null) {
theCamera = Camera.open();
if (theCamera == null) {
throw new IOException();
}
final Parameters p = theCamera.getParameters();
p.setFlashMode(Parameters.FLASH_MODE_TORCH);
theCamera.setParameters(p);
camera = theCamera;
}
than I have removed from com.google.zxing.client.android.camera.CameraConfigurationManager
initializeTorch(parameters, prefs);
finaly I have changed AndroidManifest.xml
<uses-feature android:name="android.hardware.camera.flash" android:required="true"/>
Try out this code. It worked fine with me.
private void turnOnFlashLight(){
if (!pm.hasSystemFeature(PackageManager.FEATURE_CAMERA))
{
//Check for Device Camera
Toast.makeText(this, getString(R.string.no_device_camera_msg), Toast.LENGTH_SHORT).show();
flashLightControl.setChecked(false);
return;
}else if (!pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH))
{
//Check for Camera flash
Toast.makeText(this, getString(R.string.no_camera_flash), Toast.LENGTH_SHORT).show();
flashLightControl.setChecked(false);
return;
}else
{
//BIG ISSUE Fash mode is device specific
//Turn On Flash
String _model = android.os.Build.MODEL;
String _manufaturer = android.os.Build.MANUFACTURER;
if((_model.contains("GT-S5830") && _manufaturer.contains("samsung"))) //|| (_manufaturer.contains("lge")))
{
new Thread(new Runnable()
{
#Override
public void run()
{
// TODO Auto-generated method stub
Log.d(TAG, "From TimerTask..!!!");
cameraParams = CameraManager.camera.getParameters();
//cameraParams.setFlashMode(Parameters.FLASH_MODE_TORCH);
cameraParams.set("flash-mode", "on");
CameraManager.camera.setParameters(cameraParams);
CameraManager.camera.startPreview();
isFlash_On_Mode_Device = true;
isLightOn = true;
try{
Thread.sleep(2000);
Log.d(TAG, "From TimerTask After sleep!!!");
cameraParams = CameraManager.camera.getParameters();
cameraParams.setFlashMode(Parameters.FLASH_MODE_OFF);
CameraManager.camera.setParameters(cameraParams);
CameraManager.camera.startPreview();
isLightOn = true;
}
catch(Exception e){}
}
}).start();
}else if(_manufaturer.contains("lge"))
{
//Log.d(TAG, "From LG");
cameraParams = CameraManager.camera.getParameters();
cameraParams.setFlashMode(Parameters.FLASH_MODE_ON);
CameraManager.camera.setParameters(cameraParams);
CameraManager.camera.startPreview();
isLightOn = true;
}
else if(CameraManager.camera != null)
{
cameraParams = CameraManager.camera.getParameters();
//cameraParams.setFlashMode(Parameters.FLASH_MODE_TORCH);
cameraParams.set("flash-mode", "torch");
CameraManager.camera.setParameters(cameraParams);
CameraManager.camera.startPreview();
isLightOn = true;
}
}
private void turnOffFlashLight()
{
if (isLightOn)
{
if(CameraManager.camera != null)
{
if(isFlash_On_Mode_Device)
{
CameraManager.get().closeDriver();
SurfaceHolder surfaceHolder = surfaceView.getHolder();
if (hasSurface)
{
initCamera(surfaceHolder);
if(CameraManager.camera != null)
CameraManager.camera.startPreview();
}
//Log.d(TAG, "Stopping camera..!!!");
}else
{
cameraParams = CameraManager.camera.getParameters();
cameraParams.setFlashMode(Parameters.FLASH_MODE_OFF);
CameraManager.camera.setParameters(cameraParams);
CameraManager.camera.startPreview();
}
isLightOn = false;
isFlash_On_Mode_Device = false;
}
}
}