2
votes

I am trying to scan QR codes using the device Camera and mobile vision API. In order to do so, I created a SurfaceView and fetched stream of images to the SurfaceView and enabled a BarcodeDetector followed by required methods to read the detected QR codes. Here is the code to the Activity:

QrScanActivity.class

public class QrScanActivity extends AppCompatActivity
{
    private SurfaceView cameraView;
    private TextView barcodeInfo;
    private BarcodeDetector barcodeDetector;
    private CameraSource cameraSource;
    private static final String TAG = "QR_ACTIVITY";
    private static final int REQUEST_CAMERA = 0;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_qr_scan);

        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)
                .setRequestedPreviewSize(640, 480)
                .build();

        cameraView.getHolder().addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                if (ActivityCompat.checkSelfPermission(QrScanActivity.this, Manifest.permission.CAMERA)
                        != PackageManager.PERMISSION_GRANTED) {
                    requestCameraPermission();

                }
                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) {
                    barcodeInfo.post(new Runnable() {    // Use the post method of the TextView
                        public void run() {
                            barcodeInfo.setText(    // Update the TextView
                                    barcodes.valueAt(0).displayValue
                            );
                        }
                    });
                }
            }
        });
    }

        private void requestCameraPermission() {
        Log.i("CAM","CAMERA permission has NOT been granted. Requesting permission.");

        // BEGIN_INCLUDE(camera_permission_request)
        if (ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.CAMERA))
        {

            Snackbar.make(findViewById(android.R.id.content), R.string.permission_camera_rationale,
                    Snackbar.LENGTH_INDEFINITE)
                    .setAction(R.string.ok, new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            ActivityCompat.requestPermissions(QrScanActivity.this,
                                    new String[]{Manifest.permission.CAMERA},
                                    REQUEST_CAMERA);
                        }
                    })
                    .show();
        } else {


            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA},
                    REQUEST_CAMERA);
        }
        // END_INCLUDE(camera_permission_request)
    }


    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {

        if (requestCode == REQUEST_CAMERA)
        {

            Log.i(TAG, "Received response for Camera permission request.");
           if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                  Log.i(TAG, "CAMERA permission has now been granted. Showing preview.");
                Snackbar.make(findViewById(android.R.id.content), R.string.permision_available_camera,
                        Snackbar.LENGTH_SHORT).show();
            } else {
                Log.i(TAG, "CAMERA permission was NOT granted.");
                Snackbar.make(findViewById(android.R.id.content), R.string.permissions_not_granted,
                        Snackbar.LENGTH_SHORT).show();

            }


        }
        else {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }




}

activity_qr_scan.xml

    <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_qr_scan"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <SurfaceView
        android:layout_width="400dp"
        android:layout_height="280dp"
        android:layout_marginTop="30dp"
        android:id="@+id/camera_view"
        />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/code_info"
        android:layout_below="@+id/camera_view"
        android:textSize="20sp"
        android:layout_marginTop="30dp"
        android:layout_centerHorizontal="true"
        android:text="Nothing to read."

        />


</RelativeLayout>

The logcat obtained when the app is run:

Logcat

E/AndroidRuntime: FATAL EXCEPTION: main Process: com.shreybank.shrey, PID: 12044 java.lang.RuntimeException: Fail to connect to camera service at android.hardware.Camera.(Camera.java:518) at android.hardware.Camera.open(Camera.java:360) at com.google.android.gms.vision.CameraSource.zzBZ(Unknown Source) at com.google.android.gms.vision.CameraSource.start(Unknown Source) at com.shreybank.shrey.activities.QrScanActivity$1.surfaceCreated(QrScanActivity.java:64) at android.view.SurfaceView.updateWindow(SurfaceView.java:583) at android.view.SurfaceView$3.onPreDraw(SurfaceView.java:177) at android.view.ViewTreeObserver.dispatchOnPreDraw(ViewTreeObserver.java:944) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2055) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1107) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6013) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858) at android.view.Choreographer.doCallbacks(Choreographer.java:670) at android.view.Choreographer.doFrame(Choreographer.java:606) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844) at android.os.Handler.handleCallback(Handler.java:739) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5466) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

2
It says it can not open camera. Looking at your code I saw that you are requesting camera permission and immeditely after that you are starting camera preview, that is not correct. You should only open camera when the permission is granted.dumb_terminal
I am new to the run time permission system! How should I change the code such a way that I open camera only when permission is granted?OBX
check the answer below, it shows how you have to start your camera after granting permission.dumb_terminal

2 Answers

0
votes

You have to set the run time permission to overcome this problem. Before start the QrScanActivity you have call this.

if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA}, REQUEST_READ_CAMERA);
    }
    else {
        // Already grant Camera permission. Now call your QR scan Activity
    }
}else {
    // call your QR scan Activity
}

When user grant the permission you can open your QrScanActivity onRequestPermissionsResult method .

@Override
public void onRequestPermissionsResult(int requestCode,
                                       String permissions[], int[] grantResults) {
    switch (requestCode) {
        case REQUEST_READ_CAMERA: {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // permission has granted . Call the QR scan Activity
            } else {
                // permission denied
            }
            return;
        }
    }
}
0
votes

Update your QrScanActivity, move your camera source creation code to initCameraSource method, check permission for CAMERA, if granted call initCameraSource method otherwise request permission. When permission granted then call initCameraSource from onRequestPermissionsResult.

public class QrScanActivity extends AppCompatActivity
{
    private SurfaceView cameraView;
    private TextView barcodeInfo;
    private BarcodeDetector barcodeDetector;
    private CameraSource cameraSource;
    private static final String TAG = "QR_ACTIVITY";
    private static final int REQUEST_CAMERA = 0;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_qr_scan);
        int permissionCheck = ContextCompat.checkSelfPermission(this,
                Manifest.permission.CAMERA);
        if(permissionCheck ==  PackageManager.PERMISSION_GRANTED)
            initCameraSource
        else
            requestCameraPermission();

    }

        private void requestCameraPermission() {
        Log.i("CAM","CAMERA permission has NOT been granted. Requesting permission.");

        // BEGIN_INCLUDE(camera_permission_request)
        if (ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.CAMERA))
        {

            Snackbar.make(findViewById(android.R.id.content), R.string.permission_camera_rationale,
                    Snackbar.LENGTH_INDEFINITE)
                    .setAction(R.string.ok, new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            ActivityCompat.requestPermissions(QrScanActivity.this,
                                    new String[]{Manifest.permission.CAMERA},
                                    REQUEST_CAMERA);
                        }
                    })
                    .show();
        } else {


            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA},
                    REQUEST_CAMERA);
        }
        // END_INCLUDE(camera_permission_request)
    }


    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {

        if (requestCode == REQUEST_CAMERA)
        {

            Log.i(TAG, "Received response for Camera permission request.");
           if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                  Log.i(TAG, "CAMERA permission has now been granted. Showing preview.");
                  initCameraSource();
            } else {
                Log.i(TAG, "CAMERA permission was NOT granted.");
                Snackbar.make(findViewById(android.R.id.content), R.string.permissions_not_granted,
                        Snackbar.LENGTH_SHORT).show();

            }


        }
        else {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }


    private void initCameraSource(){
         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)
                 .setRequestedPreviewSize(640, 480)
                 .build();

         cameraView.getHolder().addCallback(new SurfaceHolder.Callback() {
             @Override
             public void surfaceCreated(SurfaceHolder holder) {
                 if (ActivityCompat.checkSelfPermission(QrScanActivity.this, Manifest.permission.CAMERA)
                         != PackageManager.PERMISSION_GRANTED) {
                     requestCameraPermission();

                 }
                 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) {
                     barcodeInfo.post(new Runnable() {    // Use the post method of the TextView
                         public void run() {
                             barcodeInfo.setText(    // Update the TextView
                                     barcodes.valueAt(0).displayValue
                             );
                         }
                     });
                 }
             }
         });
    }


}