content: package com.adafruit.bluefruit.le.connect.app.settings;
  
  // Based on Android-Vision sample by Google: https://github.com/googlesamples/android-vision
  
  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.media.AudioManager;
  import android.media.ToneGenerator;
  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.MotionEvent;
  import android.view.ScaleGestureDetector;
  import android.view.View;
  import android.widget.Toast;
  
  import com.adafruit.bluefruit.le.connect.R;
  import com.adafruit.bluefruit.le.connect.app.settings.barcode.BarcodeGraphic;
  import com.adafruit.bluefruit.le.connect.app.settings.barcode.BarcodeGraphicTracker;
  import com.adafruit.bluefruit.le.connect.app.settings.barcode.BarcodeTrackerFactory;
  import com.adafruit.bluefruit.le.connect.app.settings.barcode.CameraSource;
  import com.adafruit.bluefruit.le.connect.app.settings.barcode.CameraSourcePreview;
  import com.adafruit.bluefruit.le.connect.app.settings.barcode.GraphicOverlay;
  import com.google.android.gms.common.ConnectionResult;
  import com.google.android.gms.common.GoogleApiAvailability;
  import com.google.android.gms.vision.MultiProcessor;
  import com.google.android.gms.vision.barcode.BarcodeDetector;
  
  import java.io.IOException;
  
  public class MqttUartSettingsCodeReaderActivity extends AppCompatActivity implements BarcodeGraphicTracker.BarcodeTrackerListener {
      // Constants
      private final static String TAG = MqttUartSettingsCodeReaderActivity.class.getSimpleName();
      private boolean kAutoFocus = true;
      private boolean kUseFlash = false;
  
      // constants used to pass extra data in the intent
      public static final String kActivityResult_ScannedContents = "Barcode";
  
      // intent request code to handle updating play services if needed.
      private static final int RC_HANDLE_GMS = 9001;
  
      // permission request codes need to be < 256
      private static final int RC_HANDLE_CAMERA_PERM = 2;
  
      // camera graphics
      private CameraSource mCameraSource;
      private CameraSourcePreview mPreview;
      private GraphicOverlay<BarcodeGraphic> mGraphicOverlay;
  
      // helper objects for detecting taps and pinches.
      private ScaleGestureDetector scaleGestureDetector;
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_mqttsettingscodereader);
  
          mPreview = (CameraSourcePreview) findViewById(R.id.preview);
          mGraphicOverlay = (GraphicOverlay<BarcodeGraphic>) findViewById(R.id.graphicOverlay);
  
          // 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(kAutoFocus, kUseFlash);
          } else {
              requestCameraPermission();
          }
  
          //gestureDetector = new GestureDetector(this, new CaptureGestureListener());
          scaleGestureDetector = new ScaleGestureDetector(this, new ScaleListener());
      }
  
      @Override
      public void onCodeScanned(String contents) {
          Log.d(TAG, "Code Scanned: " + contents);
  
          // Beep
          final ToneGenerator tg = new ToneGenerator(AudioManager.STREAM_NOTIFICATION, 100);
          tg.startTone(ToneGenerator.TONE_PROP_BEEP);
  
          //
          Intent data = new Intent();
          data.putExtra(kActivityResult_ScannedContents, contents);
          setResult(RESULT_OK, data);
          finish();
      }
  
      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.mqttcodereader_cameraneeded,
                  Snackbar.LENGTH_INDEFINITE)
                  .setAction(android.R.string.ok, listener)
                  .show();
      }
  
      @Override
      public boolean onTouchEvent(MotionEvent e) {
          final boolean isScaleDetected = scaleGestureDetector.onTouchEvent(e);
          return isScaleDetected || super.onTouchEvent(e);
          // boolean c = gestureDetector.onTouchEvent(e);
  
          //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.
       * <p/>
       * 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, this);
          barcodeDetector.setProcessor(
                  new MultiProcessor.Builder<>(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.mqttcodereader_lowstorageerror, Toast.LENGTH_LONG).show();
                  Log.w(TAG, getString(R.string.mqttcodereader_lowstorageerror));
              }
          }
  
          // 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);
  
          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)}.
       * <p>
       * <strong>Note:</strong> 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.
       * </p>
       *
       * @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 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
              Log.d(TAG, "Camera permission granted - initialize the camera source");
              // we have permission, so create the camerasource
              createCameraSource(kAutoFocus, kUseFlash);
              return;
          }
  
          Log.e(TAG, "Permission not granted: results len = " + grantResults.length +
                  " Result code = " + (grantResults.length > 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.mqttcodereader_nocamerapermission)
                  .setPositiveButton(android.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;
              }
          }
      }
  
      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.
           * <p/>
           * 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());
          }
      }
  }
  