Create on click focus on CameraSource - Android QR Code detection

Pedro Oliveira picture Pedro Oliveira · Apr 16, 2016 · Viewed 7k times · Source

I'm developing an android app which allows user to check a QR Code content and execute something according read result.

In order to improve the performance i'd like to implement 2 methods:

  • onClickFocus (which allows user to focus the camera when screen is clicked)
  • turnOn/OFF flash (which allows user to turn on/off the flash)

I've done some digging and figured out that for manage camera and flash I need to be able to manage the Camera as object itself.

And here is where the nightmare begin.

I'm using the follow code to show camera result and track QR codes.

    import android.app.FragmentTransaction;
import android.content.Context;


import android.os.Bundle;
import android.os.Vibrator;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.util.SparseArray;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.TextView;

import com.google.android.gms.vision.Detector;
import com.google.android.gms.vision.barcode.Barcode;
import com.google.android.gms.vision.barcode.BarcodeDetector;
import java.io.IOException;



public class MainReadActivity extends AppCompatActivity {


    public SurfaceView cameraView;
    private TextView barcodeInfo;
    public BarcodeDetector barcodeDetector;
    public CameraSource cameraSource;
    public Vibrator v;
    public String textInfo;
    public DrawerLayout mDrawerLayout;




    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_read);
        v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);

        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout_main);


        getSupportFragmentManager().findFragmentById(R.id.drawer_layout_main);

        cameraView = (SurfaceView) findViewById(R.id.camera_view);


        //barcodeInfo = (TextView) findViewById(R.id.code_info);

        barcodeDetector = new BarcodeDetector.Builder(this)
                .setBarcodeFormats(Barcode.QR_CODE)
                .build();


        cameraSource = new CameraSource.Builder(this, barcodeDetector).build();



        cameraView.getHolder().addCallback(new SurfaceHolder.Callback() {

            @Override
            public void surfaceCreated(SurfaceHolder holder) {


                try {
                    cameraSource.start(cameraView.getHolder());
                } catch (IOException ie) {
                    Log.e("CAMERA SOURCE", ie.getMessage());
                }

            }


            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                cameraSource.stop();
            }

        });


        barcodeDetector.setProcessor(new Detector.Processor<Barcode>() {

            @Override
            public void release() {

            }


            @Override
            public void receiveDetections(Detector.Detections<Barcode> detections) {
                final SparseArray<Barcode> barcodes = detections.getDetectedItems();

                if (barcodes.size() != 0) {


                    new Runnable() {    // Use the post method of the TextView
                        public void run() {


                            v.vibrate(500);
                          // textInfo = barcodes.valueAt(0).displayValue;

                            MyFragmentDialog newf = new MyFragmentDialog();
                            FragmentTransaction transaction = getFragmentManager().beginTransaction();
                            transaction.replace(R.id.fragment_container, newf);

                            transaction.addToBackStack("tag");
                            transaction.commit();

                        }
                    };
                }
            }

        });







    }

    public void onBackPressed() {
        // do nothing
    }
}

So, I need to get access to Camera, from CameraSource (am I right?!) Once it is not possible, I tryed to use this CameraSource class from GoogleSamples's git which allows to use setFocusMode method... But unfortunately I wasn't successful.

I also tryed to use API 21, since API 22 no longer supports Camera and CameraPreferences.

I'm pretty sure this is not only my problem, but couldn't find a way to fix it.

Anyone can help?

FIXED:

Just use this CameraSource (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) . Yeah, I know, I've suggested it... But this time i fixed my problem! So, if you're going to use this, make sure your compile looks like this:

compile 'com.google.android.gms:play-services:8.1.0'

Answer

Mohamed Embaby picture Mohamed Embaby · May 9, 2017

Initialize these and define them in OnCreate

Camera.Parameters params;
Camera camera;
CameraSource cameraSource;
SurfaceView cameraView;
boolean isFlash = false;

Call changeFlashStatus() method to turn flash ON and call it again to turn flash OFF

public void changeFlashStatus() {
    Field[] declaredFields = CameraSource.class.getDeclaredFields();

    for (Field field : declaredFields) {
        if (field.getType() == Camera.class) {
            field.setAccessible(true);
            try {
                camera = (Camera) field.get(cameraSource);
                if (camera != null) {
                    params = camera.getParameters();
                    if (!isFlash) {
                        params.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
                        flashImage.setColorFilter(getResources().getColor(R.color.yellow));
                        isFlash = true;
                    } else {
                        params.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
                        flashImage.setColorFilter(getResources().getColor(R.color.greyLight));
                        isFlash = false;
                    }
                    camera.setParameters(params);
                }

            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }

            break;
        }
    }
}

To get the camera to focus, you need a specific area ( Rect ) to pass it to Camera to make focus on that area. So we have to implement onTouchListener() for surfaceView so when we touch the surfaceView we create MotionEvent which is determine where exactly you touch the surfaceView then we can extract Rect from MotionEvent.

Call initCameraFocusListener() in your OnCreate. Safely Call it after the start of the Camera

private void initCameraFocusListener() {
    cameraView.setOnTouchListener(new View.OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {

            cameraFocus(event, cameraSource, Camera.Parameters.FOCUS_MODE_AUTO);

            return false;
        }
    });
}

private boolean cameraFocus(MotionEvent event, @NonNull CameraSource cameraSource, @NonNull String focusMode) {
    Field[] declaredFields = CameraSource.class.getDeclaredFields();

    int pointerId = event.getPointerId(0);
    int pointerIndex = event.findPointerIndex(pointerId);
    // Get the pointer's current position
    float x = event.getX(pointerIndex);
    float y = event.getY(pointerIndex);

    float touchMajor = event.getTouchMajor();
    float touchMinor = event.getTouchMinor();

    Rect touchRect = new Rect((int)(x - touchMajor / 2), (int)(y - touchMinor / 2), (int)(x + touchMajor / 2), (int)(y + touchMinor / 2));

    Rect focusArea = new Rect();

    focusArea.set(touchRect.left * 2000 / cameraView.getWidth() - 1000,
            touchRect.top * 2000 / cameraView.getHeight() - 1000,
            touchRect.right * 2000 / cameraView.getWidth() - 1000,
            touchRect.bottom * 2000 / cameraView.getHeight() - 1000);

    // Submit focus area to camera

    ArrayList<Camera.Area> focusAreas = new ArrayList<Camera.Area>();
    focusAreas.add(new Camera.Area(focusArea, 1000));

    for (Field field : declaredFields) {
        if (field.getType() == Camera.class) {
            field.setAccessible(true);
            try {
                camera = (Camera) field.get(cameraSource);
                if (camera != null) {
                    params = camera.getParameters();
                    params.setFocusMode(focusMode);
                    params.setFocusAreas(focusAreas);
                    camera.setParameters(params);

                    // Start the autofocus operation

                    camera.autoFocus(new Camera.AutoFocusCallback() {
                        @Override
                        public void onAutoFocus(boolean b, Camera camera) {
                            // currently set to auto-focus on single touch
                        }
                    });
                    return true;
                }

                return false;
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }

            break;
        }
    }
    return false;
}