title: Detect and capture barcode automatically when it is read by camera
body: <p>I used this <a href="https://github.com/googlesamples/android-vision" rel="nofollow">android-vision project</a> to scan a barcode. When the camera detects the barcode, I am currently required to manually tap to capture it. But, I want to change the code a little bit so that it is captured automatically when the barcode is detected. How can I do this? This is my <code>BarcodeCaptureActivity</code> class: </p>
  
  <pre><code>package com.example.fs.barcodescanner;
  
  import android.Manifest;
  import android.annotation.SuppressLint;
  import android.app.Activity;
  import android.app.AlertDialog;
  import android.app.Dialog;
  import android.content.Context;
  import android.content.DialogInterface;
  import android.content.Intent;
  import android.content.IntentFilter;
  import android.content.pm.PackageManager;
  import android.hardware.Camera;
  import android.os.Build;
  import android.os.Bundle;
  import android.support.annotation.NonNull;
  import android.support.design.widget.Snackbar;
  import android.support.v4.app.ActivityCompat;
  import android.support.v7.app.AppCompatActivity;
  import android.util.Log;
  import android.view.GestureDetector;
  import android.view.MotionEvent;
  import android.view.ScaleGestureDetector;
  import android.view.View;
  import android.view.WindowId;
  import android.widget.Toast;
  
  import com.example.fs.barcodescanner.ui.camera.CameraSource;
  import com.example.fs.barcodescanner.ui.camera.CameraSourcePreview;
  import com.example.fs.barcodescanner.ui.camera.GraphicOverlay;
  import com.google.android.gms.common.ConnectionResult;
  import com.google.android.gms.common.GoogleApiAvailability;
  import com.google.android.gms.common.api.CommonStatusCodes;
  import com.google.android.gms.vision.MultiProcessor;
  import com.google.android.gms.vision.barcode.Barcode;
  import com.google.android.gms.vision.barcode.BarcodeDetector;
  
  import java.io.IOException;
  
  public final class BarcodeCaptureActivity extends AppCompatActivity {
      private static final String TAG = "Barcode-reader";
  
      // intent request code to handle updating play services if needed.
      private static final int RC_HANDLE_GMS = 9001;
  
      // permission request codes need to be &lt; 256
      private static final int RC_HANDLE_CAMERA_PERM = 2;
  
      // constants used to pass extra data in the intent
      public static final String AutoFocus = "AutoFocus";
      public static final String UseFlash = "UseFlash";
      public static final String BarcodeObject = "Barcode";
  
      private CameraSource mCameraSource;
      private CameraSourcePreview mPreview;
      private GraphicOverlay&lt;BarcodeGraphic&gt; mGraphicOverlay;
  
      // helper objects for detecting taps and pinches.
      private ScaleGestureDetector scaleGestureDetector;
      private GestureDetector gestureDetector;
      private WindowId.FocusObserver fo;
  
      /**
       * Initializes the UI and creates the detector pipeline.
       */
      @Override
      public void onCreate(Bundle icicle) {
          super.onCreate(icicle);
          setContentView(R.layout.activity_barcode_capture);
  
          mPreview = (CameraSourcePreview) findViewById(R.id.preview);
          mGraphicOverlay = (GraphicOverlay&lt;BarcodeGraphic&gt;) findViewById(R.id.graphicOverlay);
  
          // read parameters from the intent used to launch the activity.
          boolean autoFocus = getIntent().getBooleanExtra(AutoFocus, false);
          boolean useFlash = getIntent().getBooleanExtra(UseFlash, false);
  
          // Check for the camera permission before accessing the camera.  If the
          // permission is not granted yet, request permission.
          int rc = ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
          if (rc == PackageManager.PERMISSION_GRANTED) {
              createCameraSource(autoFocus, useFlash);
          } else {
              requestCameraPermission();
          }
  
          gestureDetector = new GestureDetector(this, new CaptureGestureListener());
          scaleGestureDetector = new ScaleGestureDetector(this, new ScaleListener());
  
          Snackbar.make(mGraphicOverlay, "Tap to capture. Pinch/Stretch to zoom",
                  Snackbar.LENGTH_LONG)
                  .show();
      }
  
      /**
       * Handles the requesting of the camera permission.  This includes
       * showing a "Snackbar" message of why the permission is needed then
       * sending the request.
       */
      private void requestCameraPermission() {
          Log.w(TAG, "Camera permission is not granted. Requesting permission");
  
          final String[] permissions = new String[]{Manifest.permission.CAMERA};
  
          if (!ActivityCompat.shouldShowRequestPermissionRationale(this,
                  Manifest.permission.CAMERA)) {
              ActivityCompat.requestPermissions(this, permissions, RC_HANDLE_CAMERA_PERM);
              return;
          }
  
          final Activity thisActivity = this;
  
          View.OnClickListener listener = new View.OnClickListener() {
              @Override
              public void onClick(View view) {
                  ActivityCompat.requestPermissions(thisActivity, permissions,
                          RC_HANDLE_CAMERA_PERM);
              }
          };
  
          Snackbar.make(mGraphicOverlay, R.string.permission_camera_rationale,
                  Snackbar.LENGTH_INDEFINITE)
                  .setAction(R.string.ok, listener)
                  .show();
      }
  
      @Override
      public boolean onTouchEvent(MotionEvent e) {
          boolean b = scaleGestureDetector.onTouchEvent(e);
          System.out.println("scale gesture"+b);
          boolean c = gestureDetector.onTouchEvent(e);
          System.out.println("tap gesture"+c);
          return b || c || super.onTouchEvent(e);
      }
  
  
      /**
       * Creates and starts the camera.  Note that this uses a higher resolution in comparison
       * to other detection examples to enable the barcode detector to detect small barcodes
       * at long distances.
       *
       * Suppressing InlinedApi since there is a check that the minimum version is met before using
       * the constant.
       */
      @SuppressLint("InlinedApi")
      private void createCameraSource(boolean autoFocus, boolean useFlash) {
          Context context = getApplicationContext();
  
          // A barcode detector is created to track barcodes.  An associated multi-processor instance
          // is set to receive the barcode detection results, track the barcodes, and maintain
          // graphics for each barcode on screen.  The factory is used by the multi-processor to
          // create a separate tracker instance for each barcode.
          BarcodeDetector barcodeDetector = new BarcodeDetector.Builder(context).build();
          BarcodeTrackerFactory barcodeFactory = new BarcodeTrackerFactory(mGraphicOverlay);
          barcodeDetector.setProcessor(
                  new MultiProcessor.Builder&lt;&gt;(barcodeFactory).build());
  
          if (!barcodeDetector.isOperational()) {
              // Note: The first time that an app using the barcode or face API is installed on a
              // device, GMS will download a native libraries to the device in order to do detection.
              // Usually this completes before the app is run for the first time.  But if that
              // download has not yet completed, then the above call will not detect any barcodes
              // and/or faces.
              //
              // isOperational() can be used to check if the required native libraries are currently
              // available.  The detectors will automatically become operational once the library
              // downloads complete on device.
              Log.w(TAG, "Detector dependencies are not yet available.");
  
              // Check for low storage.  If there is low storage, the native library will not be
              // downloaded, so detection will not become operational.
              IntentFilter lowstorageFilter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW);
              boolean hasLowStorage = registerReceiver(null, lowstorageFilter) != null;
  
              if (hasLowStorage) {
                  Toast.makeText(this, R.string.low_storage_error, Toast.LENGTH_LONG).show();
                  Log.w(TAG, getString(R.string.low_storage_error));
              }
          }
  
          // Creates and starts the camera.  Note that this uses a higher resolution in comparison
          // to other detection examples to enable the barcode detector to detect small barcodes
          // at long distances.
          CameraSource.Builder builder = new CameraSource.Builder(getApplicationContext(), barcodeDetector)
                  .setFacing(CameraSource.CAMERA_FACING_BACK)
                  .setRequestedPreviewSize(1600, 1024)
                  .setRequestedFps(15.0f);
  
          // make sure that auto focus is an available option
          if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
              builder = builder.setFocusMode(
                      autoFocus ? Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE : null);
          }
  
          mCameraSource = builder
                  .setFlashMode(useFlash ? Camera.Parameters.FLASH_MODE_TORCH : null)
                  .build();
      }
  
      /**
       * Restarts the camera.
       */
      @Override
      protected void onResume() {
          super.onResume();
          startCameraSource();
      }
  
      /**
       * Stops the camera.
       */
      @Override
      protected void onPause() {
          super.onPause();
          if (mPreview != null) {
              mPreview.stop();
          }
      }
  
      /**
       * Releases the resources associated with the camera source, the associated detectors, and the
       * rest of the processing pipeline.
       */
      @Override
      protected void onDestroy() {
          super.onDestroy();
          if (mPreview != null) {
              mPreview.release();
          }
      }
  
      /**
       * Callback for the result from requesting permissions. This method
       * is invoked for every call on {@link #requestPermissions(String[], int)}.
       * &lt;p&gt;
       * &lt;strong&gt;Note:&lt;/strong&gt; It is possible that the permissions request interaction
       * with the user is interrupted. In this case you will receive empty permissions
       * and results arrays which should be treated as a cancellation.
       * &lt;/p&gt;
       *
       * @param requestCode  The request code passed in {@link #requestPermissions(String[], int)}.
       * @param permissions  The requested permissions. Never null.
       * @param grantResults The grant results for the corresponding permissions
       *                     which is either {@link PackageManager#PERMISSION_GRANTED}
       *                     or {@link PackageManager#PERMISSION_DENIED}. Never null.
       * @see #requestPermissions(String[], int)
       */
      @Override
      public void onRequestPermissionsResult(int requestCode,
                                             @NonNull String[] permissions,
                                             @NonNull int[] grantResults) {
          if (requestCode != RC_HANDLE_CAMERA_PERM) {
              Log.d(TAG, "Got unexpected permission result: " + requestCode);
              super.onRequestPermissionsResult(requestCode, permissions, grantResults);
              return;
          }
  
          if (grantResults.length != 0 &amp;&amp; grantResults[0] == PackageManager.PERMISSION_GRANTED) {
              Log.d(TAG, "Camera permission granted - initialize the camera source");
              // we have permission, so create the camerasource
              boolean autoFocus = getIntent().getBooleanExtra(AutoFocus,false);
              boolean useFlash = getIntent().getBooleanExtra(UseFlash, false);
              createCameraSource(autoFocus, useFlash);
              return;
          }
  
          Log.e(TAG, "Permission not granted: results len = " + grantResults.length +
                  " Result code = " + (grantResults.length &gt; 0 ? grantResults[0] : "(empty)"));
  
          DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
              public void onClick(DialogInterface dialog, int id) {
                  finish();
              }
          };
  
          AlertDialog.Builder builder = new AlertDialog.Builder(this);
          builder.setTitle("Multitracker sample")
                  .setMessage(R.string.no_camera_permission)
                  .setPositiveButton(R.string.ok, listener)
                  .show();
      }
  
      /**
       * Starts or restarts the camera source, if it exists.  If the camera source doesn't exist yet
       * (e.g., because onResume was called before the camera source was created), this will be called
       * again when the camera source is created.
       */
      private void startCameraSource() throws SecurityException {
          // check that the device has play services available.
          int code = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(
                  getApplicationContext());
          if (code != ConnectionResult.SUCCESS) {
              Dialog dlg =
                      GoogleApiAvailability.getInstance().getErrorDialog(this, code, RC_HANDLE_GMS);
              dlg.show();
          }
  
          if (mCameraSource != null) {
              try {
                  mPreview.start(mCameraSource, mGraphicOverlay);
              } catch (IOException e) {
                  Log.e(TAG, "Unable to start camera source.", e);
                  mCameraSource.release();
                  mCameraSource = null;
              }
          }
      }
  
      /**
       * onTap is called to capture the oldest barcode currently detected and
       * return it to the caller.
       *
       * @param rawX - the raw position of the tap
       * @param rawY - the raw position of the tap.
       * @return true if the activity is ending.
       */
      private boolean onTap(float rawX, float rawY) {
  
          //TODO: use the tap position to select the barcode.
          BarcodeGraphic graphic = mGraphicOverlay.getFirstGraphic();
          Barcode barcode = null;
          if (graphic != null) {
              barcode = graphic.getBarcode();
              if (barcode != null) {
                  Intent data = new Intent();
                  data.putExtra(BarcodeObject, barcode);
                  setResult(CommonStatusCodes.SUCCESS, data);
                  finish();
              }
              else {
                  Log.d(TAG, "barcode data is null");
              }
          }
          else {
              Log.d(TAG,"no barcode detected");
          }
          return barcode != null;
      }
  
      private class CaptureGestureListener extends GestureDetector.SimpleOnGestureListener {
  
          @Override
          public boolean onSingleTapConfirmed(MotionEvent e) {
              System.out.println(e.getRawX());
              System.out.println(e.getRawY());
              return onTap(e.getRawX(), e.getRawY()) || super.onSingleTapConfirmed(e);
          }
      }
  
      private class ScaleListener implements ScaleGestureDetector.OnScaleGestureListener {
  
          /**
           * Responds to scaling events for a gesture in progress.
           * Reported by pointer motion.
           *
           * @param detector The detector reporting the event - use this to
           *                 retrieve extended info about event state.
           * @return Whether or not the detector should consider this event
           * as handled. If an event was not handled, the detector
           * will continue to accumulate movement until an event is
           * handled. This can be useful if an application, for example,
           * only wants to update scaling factors if the change is
           * greater than 0.01.
           */
          @Override
          public boolean onScale(ScaleGestureDetector detector) {
              return false;
          }
  
          /**
           * Responds to the beginning of a scaling gesture. Reported by
           * new pointers going down.
           *
           * @param detector The detector reporting the event - use this to
           *                 retrieve extended info about event state.
           * @return Whether or not the detector should continue recognizing
           * this gesture. For example, if a gesture is beginning
           * with a focal point outside of a region where it makes
           * sense, onScaleBegin() may return false to ignore the
           * rest of the gesture.
           */
          @Override
          public boolean onScaleBegin(ScaleGestureDetector detector) {
              return true;
          }
  
          /**
           * Responds to the end of a scale gesture. Reported by existing
           * pointers going up.
           * &lt;p/&gt;
           * Once a scale has ended, {@link ScaleGestureDetector#getFocusX()}
           * and {@link ScaleGestureDetector#getFocusY()} will return focal point
           * of the pointers remaining on the screen.
           *
           * @param detector The detector reporting the event - use this to
           *                 retrieve extended info about event state.
           */
          @Override
          public void onScaleEnd(ScaleGestureDetector detector) {
              mCameraSource.doZoom(detector.getScaleFactor());
          }
      }
  }
  </code></pre>