So I have a dialogue where I want to show the front and back camera previews sequentially, say after 2 seconds delay. Problem is, I can always set 1 camera view to the frame, how to I change it on the fly, automatically?
Here is where I want to change it on the fly:
public class CameraExample extends AnimatedViewContainer {
private final static String TAG = "CameraExample";
private Camera mCamera;
private CameraPreview mPreview;
private Context mContext;
public CameraExample(Context context, int i) {
super(context, i);
mPreview = null;
mContext = context;
initCamera(mContext);
}
// A safe way to get an instance of the Camera object.
public static Camera getCameraInstance(int cameraId) {
Camera c = null;
try {
// attempt to get a Camera instance
c = Camera.open(cameraId);
} catch (Exception e) {
// Camera is not available (in use or does not exist)
Log.e(TAG, "CameraExample: " + "camera not available (in use or does not exist); " + e.getMessage());
}
return c; // returns null if camera is unavailable
}
private void initCamera(Context context) {
// Check if this device has a camera
if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
// no camera on this device
Log.e(TAG, "CameraExample: " + "this device has no camera");
} else {
// this device has a camera
int numCameras = Camera.getNumberOfCameras();
if (numCameras >= 0) {
for (int cameraId = 0; cameraId < numCameras; cameraId++) {
mCamera = getCameraInstance(cameraId);
if (mCamera != null) {
CameraInfo cameraInfo = new CameraInfo();
Camera.getCameraInfo(cameraId, cameraInfo);
if (cameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT) {
try {
//Create our Preview view and set it as the content of this LinearLayout View
mPreview = new CameraPreview(context, mCamera, cameraId);
} catch (RuntimeException e) {
Log.e(TAG, "Camera failed to open: " + e.getLocalizedMessage());
}
}
if (createView() == false) {
break;
}
}
}
}
}
}
#Override
public void onCreateViewContent(LayoutInflater layoutInflater, ViewGroup parentGroup, View[] containerViews, int index) {
containerViews[index] = layoutInflater.inflate(R.layout.example_camera, parentGroup, false);
FrameLayout previewFrame = (FrameLayout) containerViews[index].findViewById(R.id.preview);
// Add preview for inflation
previewFrame.addView(mPreview);
}
#Override
public void cleanup() {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
}
The CameraPreview class:
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = "CameraPreview";
private Context mContext;
private SurfaceHolder mHolder;
private Camera mCamera;
private int mCameraId;
public CameraPreview(Context context, Camera camera, int cameraId) {
super(context);
mContext = context;
mCamera = camera;
mCameraId = cameraId;
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
}
#Override
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.e(TAG, "CameraExample: " + "Error setting camera preview: " + e.getMessage());
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
}
#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
}
}
}
I set my view here:
#Override
public void onCreateViewContent(LayoutInflater layoutInflater, ViewGroup parentGroup, View[] containerViews, int index) {
containerViews[index] = layoutInflater.inflate(R.layout.example_camera, parentGroup, false);
FrameLayout previewFrame = (FrameLayout) containerViews[index].findViewById(R.id.preview);
previewFrame.addView(mPreview);
}
Problem is, I don't see how I can have 2 instances of the 2 different cameras that a device generally has and change them automatically after certain seconds so that my frame displays the front and back camera preview one after other after every certain amount of seconds. Any solution is highly appreciated! I think I have to handle it in the surfaceChanged() method, but I really don't know how!
As asked, here is the, AnimatedViewContainer class:
public abstract class AnimatedViewContainer extends Example {
Context mContext;
int mAnimationDuration;
int mAnimationDurationShort;
LayoutInflater mLayoutInflater;
ViewGroup mParentGroup;
View[] mContainerViews;
boolean hasBeenClicked = false;
int mCurrentIndex;
int mMaxNumItems;
int mIndexVisibleItem;
public AnimatedViewContainer(Context context, int maxNumItems) {
super(context);
mContext = context;
mMaxNumItems = maxNumItems;
mContainerViews = new View[mMaxNumItems];
// Retrieve and cache the system's default "medium" animation time
mAnimationDuration = getResources().getInteger(android.R.integer.config_mediumAnimTime);
// and "short"
mAnimationDurationShort = getResources().getInteger(android.R.integer.config_shortAnimTime);
mCurrentIndex = 0;
mLayoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//TODO: shouldn't be null, should be any ViewGroup with the right LayoutParams
mParentGroup = null;
}
public abstract void onCreateViewContent(LayoutInflater layoutInflater, ViewGroup parentGroup, View[] containerViews, int index);
public boolean createView() {
if (mCurrentIndex >= mMaxNumItems) {
return false; // indicates to terminate the loop
}
// handle/execute the concrete definition of the view content defined by the child class
onCreateViewContent(mLayoutInflater, mParentGroup, mContainerViews, mCurrentIndex);
// only the first container view should be visible
if (mCurrentIndex == 0) {
mContainerViews[mCurrentIndex].setVisibility(View.VISIBLE);
mIndexVisibleItem = mCurrentIndex;
} else {
mContainerViews[mCurrentIndex].setVisibility(View.GONE);
}
// if you click on the container view, show next container view with a crossfade animation
mContainerViews[mCurrentIndex].setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
crossfade(true);
hasBeenClicked = true;
}
});
// add the container view to the FrameLayout
addView(mContainerViews[mCurrentIndex]);
mCurrentIndex++;
return true;
}
public void crossfade(boolean manuallyClicked) {
//only rotate when example is actually shown and at least one content item was created. This may also prevent NPEs due to incompletely loaded views.
if(!this.isShown() || mCurrentIndex == 0)
return;
//when example was previously clicked, don't do anything
if(!manuallyClicked && hasBeenClicked){
hasBeenClicked = false;
return;
}
int numTotalItems = mCurrentIndex;
final int indexVisibleItem = mIndexVisibleItem;
int nextIndex = indexVisibleItem + 1;
if (nextIndex >= numTotalItems) {
nextIndex = 0;
}
final boolean hasOnlyOneItem;
if (numTotalItems == 1) {
hasOnlyOneItem = true;
} else {
hasOnlyOneItem = false;
}
if (hasOnlyOneItem) { //there is only one item in the mContainerViews
mContainerViews[indexVisibleItem].animate().alpha(0.5f).setDuration(mAnimationDurationShort).setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
mContainerViews[indexVisibleItem].animate().alpha(1f).setDuration(mAnimationDurationShort).setListener(null);
}
});
} else {
// Set the next view to 0% opacity but visible, so that it is visible (but fully transparent) during the animation.
mContainerViews[nextIndex].setAlpha(0f);
mContainerViews[nextIndex].setVisibility(View.VISIBLE);
// Animate the next view to 100% opacity, and clear any animation
// listener set on the view.
mContainerViews[nextIndex].animate().alpha(1f).setDuration(mAnimationDuration).setListener(null);
// Animate the current view to 0% opacity. After the animation ends,
// set its visibility to GONE as an optimization step (it won't participate in layout passes, etc.)
mContainerViews[indexVisibleItem].animate().alpha(0f).setDuration(mAnimationDuration).setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
mContainerViews[indexVisibleItem].setVisibility(View.GONE);
}
});
}
mIndexVisibleItem = nextIndex;
}
#Override
public void cleanup() {
}
}
I could find a solution to change camera in some seconds(But keep in mind as Alex Cohn said you can't change the camera in 2 seconds because, normally it takes more than 2 seconds to start preview and it depends on the device) by changing your code a little bit. please use below code and check.
Note: I have not implemented any orientation changes and taking picture functions, I Hope you have already developed those functions, in fact you have only asked for changing the camera automatically in some seconds.
I used dialog fragment to show the preview in a dialog. Here is the code for CameraExample
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.Camera;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.support.v4.app.DialogFragment;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
/**
* Created by Admin on 6/26/2017.
*/
public class CameraExample extends DialogFragment {
private final static String TAG = "CameraExample";
private Camera mCamera;
private CameraPreview mPreview;
private Context mContext;
private View view;
private int mCamId = 0;
public CameraExample() {
mPreview = null;
mContext = getContext();
}
// A safe way to get an instance of the Camera object.
public static Camera getCameraInstance(int cameraId) {
Camera c = null;
try {
// attempt to get a Camera instance
c = Camera.open(cameraId);
} catch (Exception e) {
// Camera is not available (in use or does not exist)
Log.e(TAG, "CameraExample: " + "camera not available (in use or does not exist); " + e.getMessage());
}
return c; // returns null if camera is unavailable
}
private void initCamera(Context context, int cameraId) {
// Check if this device has a camera
if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
// no camera on this device
Log.e(TAG, "CameraExample: " + "this device has no camera");
} else {
// this device has a camera
int numCameras = Camera.getNumberOfCameras();
if (numCameras >= 0) {
mCamera = getCameraInstance(cameraId);
if (mCamera != null) {
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, cameraInfo);
try {
//Create our Preview view and set it as the content of this LinearLayout View
mPreview = new CameraPreview(context, mCamera, cameraId);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
mPreview.setLayoutParams(layoutParams);
} catch (RuntimeException e) {
Log.e(TAG, "Camera failed to open: " + e.getLocalizedMessage());
}
}
}
}
}
private CountDownTimer countDownTimer;
private void switchCam() {
//10 seconds
countDownTimer = new CountDownTimer(10000, 1000) {
#Override
public void onTick(long l) {
System.out.println(l + " left");
}
#Override
public void onFinish() {
cleanup();
startCam();
}
}.start();
}
private void startCam() {
initCamera(getContext(), mCamId);
FrameLayout previewFrame = (FrameLayout) view.findViewById(R.id.preview);
previewFrame.removeAllViews();
// Add preview for inflation
previewFrame.addView(mPreview);
mCamId = mCamId == 0 ? 1 : 0;
switchCam();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
getDialog().getWindow().setGravity(Gravity.CENTER);
// getDialog().getWindow().setBackgroundDrawableResource(android.R.color.transparent);
view = inflater.inflate(R.layout.camera_fragment, container, false);
startCam();
return view;
}
#Override
public void onPause() {
super.onPause();
cleanup();
if (countDownTimer != null)
countDownTimer.cancel();
}
#Override
public void onStart() {
super.onStart();
getDialog().getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
}
public void cleanup() {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
}
And also I had to change your preview class also. See below for the code.
import android.content.Context;
import android.hardware.Camera;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import java.io.IOException;
/**
* Created by Admin on 6/26/2017.
*/
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = "CameraPreview";
private Context mContext;
private SurfaceHolder mHolder;
private Camera mCamera;
private int mCameraId;
public CameraPreview(Context context, Camera camera, int cameraId) {
super(context);
mContext = context;
mCamera = camera;
mCameraId = cameraId;
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
}
#Override
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.e(TAG, "CameraExample: " + "Error setting camera preview: " + e.getMessage());
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e) {
// ignore: tried to stop a non-existent preview
}
}
#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;
}
}
}
There is a double init bug in your class. I could have it running and see the camera switch from 0 to 1 and back, after 10 sec, after the following fix:
I removed call to initCamera() from the CameraExample constructor. Instead, I put there call to CreateView(). Alternatively, you can call CreateView(), which is a public method, from the place where you create new CameraExample(context, i).
Note that this refers to the code in dropbox, not what is posted in the question.
You have to stop the camera, switch the facing and then start it again:
Use a timer and call switchFacing() periodically every 2 seconds.
But be aware of:
This class was deprecated in API level 21.
We recommend using the new android.hardware.camera2 API for new
applications.
edit2: Here is the complete class ready to use.
//This class uses Camera1 API to be backwards compatible.
private static String TAG = "CameraManager";
private Context mContext = null;
private SurfaceView mPreview = null;
private SurfaceHolder mHolder = null;
private Camera mCamera = null;
private int mFrontFaceID = -1;
private int mBackFaceID = -1;
private int mActualFacingID = -1;
public CameraManager(Context context, SurfaceView preview) {
mContext = context;
mPreview = preview;
mHolder = mPreview.getHolder();
mHolder.addCallback(this);
}
//called in onCreate
public void init() {
Camera.CameraInfo info = new Camera.CameraInfo();
for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
Camera.getCameraInfo(i, info);
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
mFrontFaceID = i;
}
if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
mBackFaceID = i;
}
}
if (mActualFacingID == -1) {
if (mFrontFaceID != -1) {
mActualFacingID = mFrontFaceID;
} else {
mActualFacingID = mBackFaceID;
}
}
//At least one one camera will be available because of manifest declaration
}
//called first on surface created
public void start() {
Log.i(TAG, "startCamera()");
if (mCamera == null) {
mCamera = getCameraInstance(mActualFacingID);
}
if (mCamera == null) {
Log.i(TAG, "can't get camera instance");
return;
}
try {
mCamera.setPreviewDisplay(mHolder);
} catch (IOException e) {
e.printStackTrace();
}
setCameraDisplayOrientation();
setBestSupportedSizes();
mCamera.startPreview();
}
public void stop() {
Log.i(TAG, "stopCamera()");
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
public void switchFacing() {
if (mFrontFaceID == -1 || mBackFaceID == -1) {
return;
}
stop();
if (mActualFacingID == mFrontFaceID) {
mActualFacingID = mBackFaceID;
} else {
mActualFacingID = mFrontFaceID;
}
start();
}
public Camera getCameraInstance(int cameraID) {
Camera c = null;
if (cameraID != -1) {
try {
c = Camera.open(cameraID);
} catch (Exception e) {
e.printStackTrace();
Log.i(TAG, "error opening camera: " + cameraID);
}
}
return c;
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
Log.i(TAG, "surfaceCreated()");
start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.i(TAG, "surfaceChanged()");
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.i(TAG, "surfaceDestroyed()");
stop();
}
private void setBestSupportedSizes() {
if (mCamera == null) {
return;
}
Camera.Parameters parameters = mCamera.getParameters();
List<Point> pictureSizes=getSortedSizes(parameters.getSupportedPictureSizes());
List<Point> previewSizes=getSortedSizes(parameters.getSupportedPreviewSizes());
Point previewResult=null;
for (Point size:previewSizes){
float ratio = (float) size.y / size.x;
if(Math.abs(ratio-4/(float)3)<0.05){ //Aspect ratio of 4/3 because otherwise the image scales to much.
previewResult=size;
break;
}
}
Log.i(TAG,"preview: "+previewResult.x+"x"+previewResult.y);
Point pictureResult=null;
if(previewResult!=null){
float previewRatio=(float)previewResult.y/previewResult.x;
for (Point size:pictureSizes){
float ratio = (float) size.y / size.x;
if(Math.abs(previewRatio-ratio)<0.05){
pictureResult=size;
break;
}
}
}
Log.i(TAG,"preview: "+pictureResult.x+"x"+pictureResult.y);
if(previewResult!=null && pictureResult!=null){
Log.i(TAG,"best preview: "+previewResult.x+"x"+previewResult.y);
Log.i(TAG, "best picture: " + pictureResult.x + "x" + pictureResult.y);
parameters.setPreviewSize(previewResult.y, previewResult.x);
parameters.setPictureSize(pictureResult.y, pictureResult.x);
mCamera.setParameters(parameters);
mPreview.setBackgroundColor(Color.TRANSPARENT); //in the case of errors needed
}else{
mCamera.stopPreview();
mPreview.setBackgroundColor(Color.BLACK);
}
}
private List<Point> getSortedSizes(List<Camera.Size> sizes) {
ArrayList<Point> list = new ArrayList<>();
for (Camera.Size size : sizes) {
int height;
int width;
if (size.width > size.height) {
height = size.width;
width = size.height;
} else {
height = size.height;
width = size.width;
}
list.add(new Point(width, height));
}
Collections.sort(list, new Comparator<Point>() {
#Override
public int compare(Point lhs, Point rhs) {
long lhsCount = lhs.x * (long) lhs.y;
long rhsCount = rhs.x * (long) rhs.y;
if (lhsCount < rhsCount) {
return 1;
}
if (lhsCount > rhsCount) {
return -1;
}
return 0;
}
});
return list;
}
//TAKE PICTURE
public void takePhoto() {
if (mCamera != null) {
mCamera.takePicture(null, null, this);
}
}
#Override
public void onPictureTaken(byte[] data, Camera camera) {
//do something with your picture
}
//ROTATION
private void setCameraDisplayOrientation() {
if (mCamera != null) {
mCamera.setDisplayOrientation(getRotation());
}
}
public int getRotation() {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(mActualFacingID, info);
int rotation = ((WindowManager) mContext.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;
}
return result;
}
In some class call:
SurfaceView preview = (SurfaceView) findViewById(R.id.surfaceView);
CameraManager mgr = new CameraManager(MainActivity.this, MainActivity.this, preview);
mgr.init();
...
mgr.takePhoto(); //surface must already be created
mgr.switchFacing();
mgr.takePhoto();
This code should support almost all devices. The most supported aspect ratio is 4:3, the code takes care of that.
edit3: The surface view must be in the xml of course
<SurfaceView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/surfaceView" />
There is no way to switch the camera quickly. The time it takes to stop the camera, close it, open another camera, and start preview depends on the device, but in many cases (and sometimes on powerful modern devices) it will be more than 2 seconds that you put as your goal.
On the other hand, some Android devices support simultaneous operation of two cameras, see Is it possible to use front and back Camera at same time in Android and Android, Open Front and Back Cameras Simultaneously. So, on some Snapdragon 801 based devices, you can keep both cameras 'ready' and switch the video flow up to 30 times per second.
i think You should use this
mCamera= Camera.open(cameraId);
0 for CAMERA_FACING_BACK
1 for CAMERA_FACING_FRONT
for more reference flow this:-
https://developer.android.com/reference/android/hardware/Camera.html#open(int)
https://developer.android.com/reference/android/hardware/Camera.CameraInfo.html#CAMERA_FACING_BACK
We can use threads to keep one camera active and let it stay for certain time. Swap camera and repaeat the same for infinite time.
private boolean isActive;
public void onStart(){
super.onStart();
isActive = true;
continuousCameraChange(your_camera)
}
public void onResume(){
super.onResume();
isActive = true;
continuousCameraChange(your_camera)
}
public void onPause(){
super.onPause();
isActive = false;
}
public void onDestroy(){
super.onDestroy();
isActive = false;
}
public void onStop(){
super.onStop();
isActive = false;
}
private void continuousCameraChange(Camera camera){
do{
switchCamera(camera);
}while(isActive);
}
private void switchCamera(Camera camera){
if (Camera.CameraInfo.facing == CAMERA_FACING_BACK){
try{
Thread.sleep(2000);
camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
}catch(InterruptedException ex){
Thread.currentThread().interrupt();
}
//change your camera
Camera.CameraInfo.facing == CAMERA_FACING_FRONT;
}else{
try{//change 2000 to change the time for which your camera stays available
Thread.sleep(2000);
camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);
}catch(InterruptedException ex){
Thread.currentThread().interrupt();
}
//change your camera
Camera.CameraInfo.facing == CAMERA_FACING_BACK;
}
}
I guess this is what you are looking for:
ImageButton useOtherCamera = (ImageButton) findViewById(R.id.useOtherCamera);
//if phone has only one camera, hide "switch camera" button
if(Camera.getNumberOfCameras() == 1){
useOtherCamera.setVisibility(View.INVISIBLE);
}
else {
useOtherCamera.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);
//Code snippet for this method from somewhere on android developers, i forget where
setCameraDisplayOrientation(CameraActivity.this, currentCameraId, camera);
try {
//this step is critical or preview on new camera will no know where to render to
camera.setPreviewDisplay(previewHolder);
} catch (IOException e) {
e.printStackTrace();
}
camera.startPreview();
}
This is a sample code where I am switching between front and back camera on the fly. Hope it will help.
I have been following two links to create custom camera for my application. I have been successful in opening the camera. The problem is that it reduces that image quality. The links I have been following are these:
http://www.coderzheaven.com/2011/12/28/how-to-create-a-custom-layout-for-your-camera-in-android/
http://blog.rhesoft.com/2015/04/02/tutorial-how-to-use-camera-with-android-and-android-studio/
and my code is:
Camera Activity:
public class CameraSetter extends Activity {
public Context context;
public int CAMERA;
// ImageView photo_setter;
private Camera mCamera = null;
private CameraView mCameraView = null;
public CameraSetter() {
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TypefaceProvider.registerDefaultIconSets();
setContentView(R.layout.camera_preview);
Initialize();
}
private void Initialize() {
//photo_setter = (ImageView)findViewById(R.id.imview_camera_setter);
try {
mCamera = Camera.open();//you can use open(int) to use different cameras
} catch (Exception e) {
Log.d("ERROR", "Failed to get camera: " + e.getMessage());
}
if (mCamera != null) {
mCameraView = new CameraView(this, mCamera);//create a SurfaceView to show camera data
FrameLayout camera_view = (FrameLayout) findViewById(R.id.camera_view);
camera_view.addView(mCameraView);//add the SurfaceView to the layout
}
//btn to close the application
ImageButton imgClose = (ImageButton) findViewById(R.id.imgClose);
imgClose.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
System.exit(0);
}
});
}
}
and my cameraview class is:
public class CameraView extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
public CameraView(Context context, Camera camera) {
super(context);
mCamera = camera;
mCamera.setDisplayOrientation(90);
//get the holder and set this class as the callback, so we can get camera data here
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_NORMAL);
}
#Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
try {
//when the surface is created, we can set the camera to draw images in this surfaceholder
mCamera.setPreviewDisplay(surfaceHolder);
mCamera.startPreview();
} catch (IOException e) {
Log.d("ERROR", "Camera error on surfaceCreated " + e.getMessage());
}
}
#Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i2, int i3) {
if (mHolder.getSurface() == null)//check if the surface is ready to receive camera data
return;
try {
mCamera.stopPreview();
} catch (Exception e) {
//this will happen when you are trying the camera if it's not running
}
//now, recreate the camera preview
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (IOException e) {
Log.d("ERROR", "Camera error on surfaceChanged " + e.getMessage());
}
}
#Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
//our app has only one screen, so we'll destroy the camera in the surface
//if you are unsing with more screens, please move this code your activity
mCamera.stopPreview();
mCamera.release();
}
}
Can I increase the image quality using the same code or I have to do something else?
problem is solved by adding in surfacechanged:
Camera.Parameters parameters = mCamera.getParameters();
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
parameters.setJpegQuality(100);
parameters.setRotation(90);
List<Camera.Size> sizes = parameters.getSupportedPictureSizes();
Camera.Size size = sizes.get(0);
for(int j=0;j<sizes.size();j++)
{
if(sizes.get(j).width > size.width)
size = sizes.get(j);
}
parameters.setPictureSize(size.width, size.height);
Display display = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
if (display.getRotation() == Surface.ROTATION_0) {
mCamera.setDisplayOrientation(90);
} else if (display.getRotation() == Surface.ROTATION_270) {
mCamera.setDisplayOrientation(180);
}
mCamera.setParameters(parameters);
I copied the code Google example in:
https://developer.android.com/guide/topics/media/camera.html
For the first time I open the Camera, face detection works correctly and the callback is fired, I take the photo, close the camera, open the camera again but this time the face detection callback is not called.
If I'll restart my app, the same thing will happen - face detection will only work for the first time I'll use the camera.
here is my code:
private PictureTakenListener mPictureTakenListener;
AlertDialog mAlertDialog;
private Camera mCamera;
boolean mFaceDetectionAvailable;
private Camera.PictureCallback mPicture = new Camera.PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
showText("took a photo with a size of " + data.length);
releaseCamera();
if (mAlertDialog != null) mAlertDialog.dismiss();
mPictureTakenListener.photoTaken(data);
}
};
public class MyFaceDetectionListener implements Camera.FaceDetectionListener {
#Override
public void onFaceDetection(Camera.Face[] faces, Camera camera) {
if (faces != null)
if (faces.length > 0)
if (faces[0].score >= 40)
if (!takingPhotoFlag.get()){
takingPhotoFlag.set(true);
mCamera.takePicture(null, null, mPicture);
}
}
}
private void takePicture(PictureTakenListener listener){
mPictureTakenListener = listener;
if (this.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
showText("this device has a camera");
showText("device has " + String.valueOf(Camera.getNumberOfCameras()) + " cameras");
int foundCamera = -1;
for (int i = 0; i < Camera.getNumberOfCameras(); i++){
Camera.CameraInfo info = new Camera.CameraInfo(); ;
Camera.getCameraInfo(i, info);
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT){
foundCamera = i;
break;
}
}
if (foundCamera == -1){
return;
}
mCamera = null;
try {
mCamera = Camera.open(foundCamera); // attempt to get a Camera instance
}
catch (Exception e){
e.printStackTrace();
showText("could not open the front camera. " + e.getMessage());
return;
}
showText("successfully opened the camera");
mCamera.setDisplayOrientation(90);
Camera.Parameters params = mCamera.getParameters();
params.setPreviewSize(640, 480);
params.setRotation(270);
params.setPictureSize(640, 480);
mFaceDetectionAvailable = params.getMaxNumDetectedFaces() > 0;
mCamera.setParameters(params);
FrameLayout frameLayout = new FrameLayout(this);
if (!mFaceDetectionAvailable) {
frameLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mCamera.takePicture(null, null, mPicture);
}
});
}
AlertDialog.Builder alert = new AlertDialog.Builder(this);
if (!mFaceDetectionAvailable)
alert.setTitle("CLICK IMAGE TO CAPTURE");
alert.setView(frameLayout).setCancelable(true).setOnCancelListener(new DialogInterface.OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
releaseCamera();
runOnUiThread(new Runnable() {
#Override
public void run() {
//do UI stuff...
}
});
}
});
mAlertDialog = alert.create();
mAlertDialog.show();
mAlertDialog.getCurrentFocus();
mAlertDialog.getWindow().setLayout(960, 1280); //Controlling width and height.
mAlertDialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
CameraPreview mPreview = new CameraPreview(this, mCamera);
frameLayout.addView(mPreview);
} else {
showText("no camera on this device");
}
}
public interface PictureTakenListener{
void photoTaken(byte[] image);
}
/** A basic Camera preview class */
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
public CameraPreview(Context context, Camera camera) {
super(context);
mCamera = camera;
// 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.
}
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();
if (mFaceDetectionAvailable) {
mCamera.startFaceDetection();
mCamera.setFaceDetectionListener(new MyFaceDetectionListener());
}
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
}
private void releaseCamera(){
if (mCamera != null){
mCamera.setPreviewCallback(null);
mCamera.setFaceDetectionListener(null);
mCamera.stopFaceDetection();
mCamera.setErrorCallback(null);
mCamera.release();
mCamera = null;
showText("released camera");
}
}
Thank you.
I have the following exception on Galaxy s6 edge device
java.lang.RuntimeException: Camera is being used after Camera.release() was called
at android.hardware.Camera.setPreviewSurface(Native Method)
at android.hardware.Camera.setPreviewDisplay(Camera.java:702)
at com.forsale.forsale.view.uicomponent.qrcode.CameraPreview.surfaceCreated(CameraPreview.java:59)
at android.view.SurfaceView.updateWindow(SurfaceView.java:712)
at android.view.SurfaceView.onWindowVisibilityChanged(SurfaceView.java:316)
at android.view.View.dispatchWindowVisibilityChanged(View.java:10434)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1328)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1328)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1328)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1328)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1328)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1750)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1437)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7397)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:920)
at android.view.Choreographer.doCallbacks(Choreographer.java:695)
at android.view.Choreographer.doFrame(Choreographer.java:631)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:906)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:158)
at android.app.ActivityThread.main(ActivityThread.java:7224)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
this is my code :
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
private PreviewCallback previewCallback;
private AutoFocusCallback autoFocusCallback;
public CameraPreview(Context context, Camera camera,
PreviewCallback previewCb,
AutoFocusCallback autoFocusCb) {
super(context);
mCamera = camera;
previewCallback = previewCb;
autoFocusCallback = autoFocusCb;
/*
* 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);
}
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, now tell the camera where to draw the preview.
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException e) {
Log.d("DBG", "Error setting camera preview: " + e.getMessage());
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// Camera preview released in activity
}
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
mCamera.setDisplayOrientation(90);
mCamera.setPreviewDisplay(mHolder);
mCamera.setPreviewCallback(previewCallback);
mCamera.startPreview();
mCamera.autoFocus(autoFocusCallback);
} catch (Exception e){
Log.d("DBG", "Error starting camera preview: " + e.getMessage());
}
}
}
this is the code of the activity
public class QRCodeActivity extends BaseActivity
{
private Camera mCamera;
private CameraPreview mPreview;
private Handler mAutoFocusHandler;
private ImageScanner mScanner;
private boolean mBarcodeScanned = false;
private boolean mPreviewing = true;
static {
System.loadLibrary("iconv");
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_qrcode);
initializeActionBar();
setActionBarTitle(getString(R.string.qrcode));
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
mAutoFocusHandler = new Handler();
mCamera = getCameraInstance();
mScanner = new ImageScanner();
mScanner.setConfig(0, Config.X_DENSITY, 3);
mScanner.setConfig(0, Config.Y_DENSITY, 3);
mPreview = new CameraPreview(this, mCamera, previewCb, autoFocusCB);
FrameLayout preview = (FrameLayout)findViewById(R.id.cameraPreview);
preview.addView(mPreview);
}
public void onPause() {
super.onPause();
releaseCamera();
}
public static Camera getCameraInstance(){
Camera c = null;
try {
c = Camera.open();
} catch (Exception e){
}
return c;
}
private void releaseCamera() {
if (mCamera != null) {
mPreviewing = false;
mCamera.setPreviewCallback(null);
mCamera.release();
mCamera = null;
}
}
private Runnable doAutoFocus = new Runnable() {
public void run() {
if (mPreviewing)
mCamera.autoFocus(autoFocusCB);
}
};
PreviewCallback previewCb = new PreviewCallback() {
public void onPreviewFrame(byte[] data, Camera camera) {
Parameters parameters = camera.getParameters();
Size size = parameters.getPreviewSize();
Image barcode = new Image(size.width, size.height, "Y800");
barcode.setData(data);
int result = mScanner.scanImage(barcode);
if (result != 0) {
mPreviewing = false;
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
SymbolSet syms = mScanner.getResults();
for (Symbol sym : syms) {
showProgressDialog();
ForSaleServerManager.getInstance().verifyQRCode(QRCodeActivity.this, PhoneUtils.getDeviceId(QRCodeActivity.this) , sym.getData() , new VerifyQRCodeUIListener() {
#Override
public void onVerifyQRCodeCompleted(QRCodeResponse response, AppError error) {
hideProgressDialog();
if (error != null) {
DialogUtils.showDialogMessage(
QRCodeActivity.this,
getString(R.string.error),
PhoneUtils.getErrorMessage(QRCodeActivity.this, error),
getString(R.string.ok), null);
} else {
if (response != null && response.getError() == null) {
DialogUtils.showDialogMessage(QRCodeActivity.this, getString(R.string.info),
response.getMessage(), getString(R.string.ok), new View.OnClickListener() {
#Override
public void onClick(View view) {
finish();
}
});
} else if (response != null && response.getError() != null) {
DialogUtils.showDialogMessage(
QRCodeActivity.this,
getString(R.string.error),
response.getError().getMessage(),
getString(R.string.ok), null);
}
}
}
});
mBarcodeScanned = true;
}
}
}
};
AutoFocusCallback autoFocusCB = new AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
}
};
#Override
protected void initializeUIComponents() {
}
#Override
protected void initializeUIComponentsData() {
}
#Override
protected void initializeUIComponentsTheme() {
}
#Override
protected void initializeUIComponentsAction() {
}
#Override
public void goodTimeToReleaseMemory() {
/*mCamera = null;
mPreview = null;
mAutoFocusHandler = null;
mScanner = null;*/
Runtime.getRuntime().gc();
finish();
}
}
There is a serious flaw in your code: mCamera.release() is called on every onPause, but Camera.open() is only called onCreate.
This said, it is a bad practice to open camera on the main (UI) thread: this call may take too long on some devices (not on Samsung Galaxy 6) and even cause ANR.
The best practice is to open camera on a background HandlerThread (see https://stackoverflow.com/a/19154438/192373). This will guarantee that the camera callbacks (including onPictureTaken()) don't freeze your UI thread.
At any rate, to keep the mCamera object synchronized with the lifecycle of your activity and its SurfaceView, I recommend to start Camera.open() from surfaceCreated and mCamera.release() - from surfaceDestroyed.
In my app I am capturing images automatically using a service.
From my service I am opening the camera activity.
Camera Activity
public class CameraActivity extends Activity {
private static final String TAG = "CameraDemo";
SharedPreferences pref;
CameraPreview preview;
Button click;
// enter email adress from where the email will be sent
String fromPassWord;
int cameraType;
private static final int FRONT_CAMERA = 0;
private static final int BACK_CAMERA = 1;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.camera);
cameraType = getIntent().getIntExtra("cameraType",BACK_CAMERA);
preview = new CameraPreview(this,cameraType);
((FrameLayout) findViewById(com.abc.xyz.R.id.preview)).addView(preview);
handler.sendEmptyMessageDelayed(0, 100);
Log.d(TAG, "onCreate'd");
}
Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
try{
if(preview.camera != null ){
preview.camera.takePicture(shutterCallback, rawCallback, jpegCallback);
}else{
finish();
}
}catch (Exception e) {
// TODO: handle exception
finish();
}
};
};
ShutterCallback shutterCallback = new ShutterCallback() {
public void onShutter() {
Log.d(TAG, "onShutter'd");
Log.v(TAG, "onShutter'd");
}
};
PictureCallback rawCallback = new PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
Log.d(TAG, "onPictureTaken - raw");
Log.v(TAG, "onPictureTaken - raw");
}
};
PictureCallback jpegCallback = new PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
try {
//my method for storing image
StoreByteImage(data, 75, "ImageName");
Log.v("on picture taken", "picture callback");
} catch (Exception e) {
Log.d(TAG, "Exception--------------------1");
e.printStackTrace();
}
finish();
}
};
}
CameraPreview.java
class CameraPreview extends SurfaceView implements SurfaceHolder.Callback{
private static final String TAG = "Preview";
SurfaceHolder mHolder;
public Camera camera;
int cameraType;
Context _context;
private static final int FRONT_CAMERA = 0;
private static final int BACK_CAMERA = 1;
//SurfaceHolder controls the surface size and format, edit the pixels in the surface, and monitor changes to the surface
CameraPreview(Context context, int type){
super(context);
//Return the SurfaceHolder providing access and control over this SurfaceView's underlying surface
mHolder = getHolder();
//Add a callback interface to this holder
mHolder.addCallback(this);
//Set the surface type
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
this.cameraType = type;
_context = context;
}
//Create a surface
public void surfaceCreated(SurfaceHolder sHolder){
if(cameraType == FRONT_CAMERA){
try{
camera = openFrontFacingCameraGingerbread(); //method to open camera...
Log.v("cameraFornt", "value"+camera);
}catch (Exception e) {
e.printStackTrace();
}
}
}else{
//do nothing
}
if(camera == null)
return;
try{
camera.setPreviewDisplay(sHolder);
//Installs a callback to be invoked for every preview frame in addition to displaying them on the screen
camera.setOneShotPreviewCallback(new PreviewCallback(){
//Called as preview frames are displayed
public void onPreviewFrame(byte[] data, Camera arg1){
CameraPreview.this.invalidate();
}
});
} catch (Exception e){
e.printStackTrace();
}
}
//called immediately before a surface is being destroyed
public void surfaceDestroyed(SurfaceHolder sHolder){
if(camera == null){
return;
}
Log.v("Surface dESTroyed ","Camera Preview");
camera.release();
}
Log.v("Camera",""+camera);
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h){
Log.v("SurfaceChangesCalled", "true");
if(camera == null){
return;
}
}
public void draw(Canvas canvas){
super.draw(canvas);
//The Paint class holds the style and color information about how to draw geometries, text and bitmaps
Paint paint = new Paint(Color.RED);
Log.d(TAG, "draw");
Log.v("Draw", "Canvas");
//Draw the text, with origin at (canvas.getWidth()/2, canvas.getHeight()/2), using the specified paint
canvas.drawText("Preview", canvas.getWidth() / 2, canvas.getHeight() / 2, paint);
}
}
Now my problem is that i got ANR when my main application is open and the camera activity starts. it freezes at the line camera.release();
But when my main app is not open it works like a charm.
I also tried releasing camera in onPause() method of CameraActivity but no luck.