I need to continuously scan QR codes in my Android app while the main View of the app is on the screen. The main view should contain a window with camera preview, but not a fullscreen camera preview.
An example of usage: Main view containing a list of scanned QR codes and a camera preview. When new QR code is scanned, it is added to the list.
Is it possible?
I don't have an fully working example, but I can give you snippets from an project of mine where I also put the camera previews in an smaller view than the full screen. I just want to convey the idea.
What you need is a FrameLayout which will hold the camera preview
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/absoluteLayout1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#android:color/transparent"
android:orientation="vertical" >
<FrameLayout
android:id="#+id/camera_preview"
android:layout_width="200dp"
android:layout_height="200dip" >
</FrameLayout>
</RelativeLayout>
Now we need a PreviewListener which is also an View
import java.io.IOException;
import android.content.Context;
import android.hardware.Camera;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
/** A basic Camera preview class */
public class CameraPreviewListener extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
public CameraPreviewListener(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.
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
}
public void surfaceDestroyed(SurfaceHolder holder) {
// Take care of releasing the Camera preview in your activity.
Log.d("camera", "surfaceDestroyed");
if(holder.equals(mHolder)){
holder.removeCallback(this);
}else{
holder.removeCallback(this);
mHolder.removeCallback(this);
}
}
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){
e.printStackTrace();
// 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();
} catch (Exception e){
Log.d("camera", "Error starting camera preview: " + e.getMessage());
}
}
public void removeCallback(){
mHolder = getHolder();
mHolder.removeCallback(this);
}
}
Finally you need to assemble everything in you activity
import android.hardware.Camera;
Camera mCamera = = getCameraInstance();
CameraPreviewListener cpl = new CameraPreviewListener(this, mCamera);
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.addView(cpl);
To get the camera you can use the following method
/** A safe way to get an instance of the Camera object. */
public Camera getCameraInstance() {
Camera c = null;
try {
c = Camera.open(); // attempt to get a Camera instance
Parameters p = c.getParameters();
List<Size> sizes = p.getSupportedPictureSizes();
Size x = null;
if (sizes.size() < 1) {
throw new Exception("there are not supported picture sizes at all !!!");
}
for (Size s : sizes) {
if (s.width == 640 && s.height == 480) {
x = s;
}
}
if (x == null) {
x = sizes.get(0);
p.setPictureSize(x.width, x.height);
} else {
p.setPictureSize(640, 480);
}
p.setJpegQuality(20);
p.setGpsLatitude(MapViewer.latitude);
p.setGpsLongitude(MapViewer.longitude);
c.setParameters(p);
} catch (Exception e) {
// Camera is not available (in use or does not exist)
Log.d(TAG + "(getCameraInstance)", e.getMessage());
}
return c; // returns null if camera is unavailable
}
Hope this work with you, You can capture image from background without opening camera app
https://stackoverflow.com/a/24849344/1312796
Related
I have a camera app that had a Camera Preview which worked. The app was developed some years ago (2012) and i decided to reuse parts of its code, but, upon testing again (on same hardware i9100, different OS 4.4 vs 3.0), my camera preview image has a problem
when i move the phone, i see the image change and it responds to light and dark patterns (therefore app is communicating with camera).
this is my code (it uses deprecated method setType):
class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = "TGCamera";
SurfaceHolder mHolder;
public Camera camera;
public final String fileName = "/download/CameraGPS/zdelTempPhotoPreview.jpg";
CameraPreview(Context context) {
super(context);
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, acquire the camera and tell it where
// to draw.
camera = Camera.open();
Parameters parameter02 = camera.getParameters();
parameter02.setJpegThumbnailSize(80, 60);
parameter02.setPictureSize(640, 480);
camera.setParameters(parameter02);
try {
camera.setPreviewDisplay(holder);
camera.setDisplayOrientation(90);
Parameters parameters00 = camera.getParameters();
//parameters00.setRotation(90);
//parameters00.setFlashMode("on");
//parameters00.setJpegQuality(20);
camera.setParameters(parameters00);
camera.setPreviewCallback(new PreviewCallback() {
public void onPreviewFrame(byte[] data, Camera arg1) {
CameraPreview.this.invalidate();
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// Surface will be destroyed when we return, so stop the preview.
// Because the CameraDevice object is not a shared resource, it's very
// important to release it when the activity is paused.
camera.stopPreview();
camera = null;
}
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.getParameters().setRotation();
//camera.getParameters().setJpegQuality(20);
//camera.getParameters().setPictureSize(width, height)
Parameters parameters = camera.getParameters();
parameters.setPreviewSize(w, h);
camera.setParameters(parameters);
camera.startPreview();
}
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
Log.w(TAG,"clicked in preview");
Paint p = new Paint(Color.RED);
Log.d(TAG, "draw");
canvas.drawText("PREVIEW", canvas.getWidth() / 2,
canvas.getHeight() / 2, p);
}
Any suggestions?
You cannot set the picture size and preview size to arbitrary values. Make sure you check the lists returned by getSupportedPictureSizes() and getSupportedPreviewSizes(), respectively.
Often (I don't remember if this applies to Samsung Galaxy S2), the camera does not work correctly when the aspect ratio for picture is different from the one for preview.
Furthermore, you are not supposed to draw on the preview surface, and definitely not from onPreviewFrame() callback.
The first thing would be to add camera.setPreviewDisplay(holder) in your surfaceChanged() method too
Update:
Please debug in surfaceCreated and surfaceChanged that holder.getSurface() != null
Also you should add camera.release() in surfaceDestroyed and then clean install app (uninstall first and then fresh install the app). The camera service could be hanging since a previous run of the app.
Update 2:
I just noticed that you missed registering to callbacks from the SurfaceHolder passed by the surfaceCreated() and surfaceChanged() methods. In this case it would make sense that surfaceChanged() is never reached and that means startPreview() is never actually called..
In surfaceCreated and surfaceChanged methods, please update the code to include:
mHolder.removeCallback(this); // unregister from old SurfaceHolder
holder.addCallback(this); // register to new holder
try {
camera.setPreviewDisplay(holder);
} catch (IOException e) {
e.printStackTrace();
}
mHolder = holder;
I've used the camera native app through my app for taking picture. I've used the below code for display the camera app in portrait mode only.
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
takePictureIntent.putExtra(MediaStore.EXTRA_SCREEN_ORIENTATION,
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
startActivityForResult(takePictureIntent, actionCode);
But, it's not working with the above code.
Any suggestions will be appreciated :-)
You can't control the orientation of an external application that you launch, so there is no way to do this.
But you could create your own camera activity.
considering your mCamera as your Camera, you can create your camera activity, set à cameraPReview and set the preview in protrait mode by adding mCamera.setDisplayOrientation(90); before starting preview
here is an example for camera preview in a FrameLayout in potrait mode:
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.
try {
if(mCamera!=null){
mCamera.setPreviewDisplay(holder);
mCamera.setDisplayOrientation(90);
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
}
// set preview size and make any resize, rotate or
// reformatting changes here
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e) {
}
}
and you set the preview from your MainActivity like below :
mPreview = new CameraPreview(getApplicationContext(), camera);
preview.addView(mPreview);
I have used zbar scanner for android and it captures the barcodes quite easily.
But the problem is that on phones which have autofocus, it captures the barcodes too quickly to detect it correctly.
If only it could wait for a few milliseconds more, it could then be able to capture more clearer image and thereby not show "not found" page.
How can I solve this problem?
Is there a provision to delay the focus on the barcode?
Maybe a delay in capturing the image?
Are you talking about the example code, CameraTestActivity.java?
Implement a counter that counts for similar scanning results. If the scanning result remains the same (e.g. for 10 times in a row), we can assume the result is quite reliable.
I really like #Juuso_Ohtonen's reply, and actually just used it in my own reader, however if you want an AutoFocus delay you can create a Camera.AutoFocusCallback object and implement its onAutoFocus method with a .postDelayed. This object is then used on your Camera camera.autoFocus() method.
// Mimic continuous auto-focusing
Camera.AutoFocusCallback autoFocusCB = new Camera.AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
autoFocusHandler.postDelayed(doAutoFocus, 1000);
}
};
This section is used in the class that extends SurfaceView, which then implements surfaceChanged();
public CameraPreview(Context context, Camera camera,
PreviewCallback previewCb,
AutoFocusCallback autoFocusCb) {
super(context);
mCamera = camera;
previewCallback = previewCb;
autoFocusCallback = autoFocusCb;
// 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 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 {
mCamera.setPreviewDisplay(mHolder);
mCamera.setPreviewCallback(previewCallback);
mCamera.startPreview();
mCamera.autoFocus(autoFocusCallback);
} catch (Exception e) {
Log.d("DBG", "Error starting camera preview: " + e.getMessage());
}
}
I've searched all over on the web and I can't find out what that 1001 error is. A few seconds after that I get the camera 100 error but I can't find out what the first error is. Does anyone have any ideas?
I encountered this error as well on my S3. I believe I tracked it down to how the camera preview surface was used by the MediaRecorder. In my case the preview display was getting reset when I was attempting to start recording. I solved it by cleaning out my code and just used the calls to set, start and stop the preview display in the SurfaceView implementation below (from the Android Camera developer guide):
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.
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();
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
}
Just thought I would add a post here for future reference. This issue bothered me for a long time.
It turns out that my problem was caused by an incorrect preview size, although the resolution set was obtained from the getSupportedPictureSize method.
So for example you can get the sizes as follows:
//first entry in list is 1392x1392 for front facing camera on an S3
List<Camera.Size> supportedPictureSizes = params.getSupportedPictureSizes();
Setting this resolution or neglecting to set a picture size alltogether will cause the dreaded error 1001.
If you encounter this on any other device I would recommend trying different picture sizes.
So there was another reason for why I got it on my Galaxy S3. I was using a TextureView to show my camera preview and got this dreaded error when pressing the home button after a successful preview and then entering the app again. In the onResume() function I started up the preview again and found that I had not released the SurfaceTexture instance variable in the onSurfaceTextureDestroyed() function.
I added the release line to this function and it now looks like this and works perfectly:
#Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
mSurfaceTexture = null; //This was the offending culprit.
releaseMediaPlayer();
releaseVideoRecorder();
releaseCamera();
return false;
}
In my case, in Samsung S3, the video-size parameter was not set and this led to the 1001 error. Setting the video size on the media recorder using preview size fixed the issue. However, this change may fail on other devices since the parameter may or may not be available/set in all devices. The following code addresses most of the devices:
if(params.get("video-size") != null && params.get("video-size").isEmpty()) {
int videoWidth = params.getPreviewSize().width;
int videoHeight = params.getPreviewSize().height;
mediaRecorder.setVideoSize(videoWidth, videoHeight);
} else {
mediaRecorder.setVideoSize(profile.videoFrameWidth, profile.videoFrameHeight);
}
I am developing a custom camera application.Given below is my Activity class.
public class MyCustomCam extends Activity {
private Camera mCamera;
private CameraPreview mPreview;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mCamera = getCameraInstance();
mPreview = new CameraPreview(this, mCamera);
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.addView(mPreview);
}
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}
}
}
Given below is my main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<FrameLayout
android:id="#+id/camera_preview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1" />
<Button android:id="#+id/button_capture"
android:text="Capture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
/></LinearLayout>
And this is my CameraPreview 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.
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
Log.d("", "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();
} catch (Exception e){
Log.d("", "Error starting camera preview: " + e.getMessage());
}
}
}
Here
mCamera.setPreviewDisplay(holder);
code is throwing null pointer exception...i cant fix that.please tell me why it is throwing exception and how can i fix that?
Release the camera in your surfaceDestroyed function
public void surfaceDestroyed(SurfaceHolder holder) {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
and make sure you add camera permission in your manifest file.
<uses-permission android:name="android.permission.CAMERA" />
I was having the same bug.
I used this Q&A to help solve my issue. Specifically, reference the adding of the function getBestPreviewSize(...) invoked prior to params.setPreviewSize(...) being set in surfaceChanged.
Android - cam.setPreviewDisplay(holder) running into IOError
As a side note (because this was my next bug), if you start doing any more customization of the Layout (e.g. removing the title bar), any "requests" to the UI to make changes should be made prior to setContentView(...), noted in the link below.
requestFeature() must be called before adding content
Most probably your problem would be that your emulator's camera settings. Go to AVD manager, edit your emulator and set your camera , may be to emulated. restart the emulator and launch your app.