[camera] disable auto focus when using front facing camera on Android (#3383)

* Refactured Camera and fix issue front facing camera

* Update FPS range on Android to have correct brightness

* Formatted files

* Fix version conflict
diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md
index d38016a..55c7eb1 100644
--- a/packages/camera/camera/CHANGELOG.md
+++ b/packages/camera/camera/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.6.4+3
+
+* Detect if selected camera supports auto focus and act accordingly on Android. This solves a problem where front facing cameras are not capturing the picture because auto focus is not supported.
+
 ## 0.6.4+2
 
 * Set ImageStreamReader listener to null to prevent stale images when streaming images. 
diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java
index b7da94a..2db097c 100644
--- a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java
+++ b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java
@@ -33,12 +33,14 @@
 import android.os.Build.VERSION_CODES;
 import android.os.Handler;
 import android.os.Looper;
+import android.util.Log;
 import android.util.Range;
 import android.util.Rational;
 import android.util.Size;
 import android.view.OrientationEventListener;
 import android.view.Surface;
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import io.flutter.plugin.common.EventChannel;
 import io.flutter.plugin.common.MethodChannel.Result;
 import io.flutter.plugins.camera.PictureCaptureRequest.State;
@@ -59,6 +61,11 @@
 import java.util.Map;
 import java.util.concurrent.Executors;
 
+@FunctionalInterface
+interface ErrorCallback {
+  void onError(String errorCode, String errorMessage);
+}
+
 public class Camera {
   private final SurfaceTextureEntry flutterTexture;
   private final CameraManager cameraManager;
@@ -73,6 +80,7 @@
   private final CamcorderProfile recordingProfile;
   private final DartMessenger dartMessenger;
   private final CameraZoom cameraZoom;
+  private final CameraCharacteristics cameraCharacteristics;
 
   private CameraDevice cameraDevice;
   private CameraCaptureSession cameraCaptureSession;
@@ -88,6 +96,8 @@
   private PictureCaptureRequest pictureCaptureRequest;
   private CameraRegions cameraRegions;
   private int exposureOffset;
+  private boolean useAutoFocus;
+  private Range<Integer> fpsRange;
 
   public Camera(
       final Activity activity,
@@ -122,10 +132,12 @@
         };
     orientationEventListener.enable();
 
-    CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraName);
-    sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
+    cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraName);
+    initFps(cameraCharacteristics);
+    sensorOrientation = cameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
     isFrontFacing =
-        characteristics.get(CameraCharacteristics.LENS_FACING) == CameraMetadata.LENS_FACING_FRONT;
+        cameraCharacteristics.get(CameraCharacteristics.LENS_FACING)
+            == CameraMetadata.LENS_FACING_FRONT;
     ResolutionPreset preset = ResolutionPreset.valueOf(resolutionPreset);
     recordingProfile =
         CameraUtils.getBestAvailableCamcorderProfileForResolutionPreset(cameraName, preset);
@@ -133,8 +145,29 @@
     previewSize = computeBestPreviewSize(cameraName, preset);
     cameraZoom =
         new CameraZoom(
-            characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE),
-            characteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM));
+            cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE),
+            cameraCharacteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM));
+  }
+
+  private void initFps(CameraCharacteristics cameraCharacteristics) {
+    try {
+      Range<Integer>[] ranges =
+          cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
+      if (ranges != null) {
+        for (Range<Integer> range : ranges) {
+          int upper = range.getUpper();
+          Log.i("Camera", "[FPS Range Available] is:" + range);
+          if (upper >= 10) {
+            if (fpsRange == null || upper < fpsRange.getUpper()) {
+              fpsRange = range;
+            }
+          }
+        }
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+    Log.i("Camera", "[FPS Range] is:" + fpsRange);
   }
 
   private void prepareMediaRecorder(String outputFilePath) throws IOException {
@@ -221,6 +254,118 @@
         null);
   }
 
+  private void createCaptureSession(int templateType, Surface... surfaces)
+      throws CameraAccessException {
+    createCaptureSession(templateType, null, surfaces);
+  }
+
+  private void createCaptureSession(
+      int templateType, Runnable onSuccessCallback, Surface... surfaces)
+      throws CameraAccessException {
+    // Close any existing capture session.
+    closeCaptureSession();
+
+    // Create a new capture builder.
+    captureRequestBuilder = cameraDevice.createCaptureRequest(templateType);
+
+    // Build Flutter surface to render to
+    SurfaceTexture surfaceTexture = flutterTexture.surfaceTexture();
+    surfaceTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
+    Surface flutterSurface = new Surface(surfaceTexture);
+    captureRequestBuilder.addTarget(flutterSurface);
+
+    List<Surface> remainingSurfaces = Arrays.asList(surfaces);
+    if (templateType != CameraDevice.TEMPLATE_PREVIEW) {
+      // If it is not preview mode, add all surfaces as targets.
+      for (Surface surface : remainingSurfaces) {
+        captureRequestBuilder.addTarget(surface);
+      }
+    }
+
+    // Prepare the callback
+    CameraCaptureSession.StateCallback callback =
+        new CameraCaptureSession.StateCallback() {
+          @Override
+          public void onConfigured(@NonNull CameraCaptureSession session) {
+            if (cameraDevice == null) {
+              dartMessenger.sendCameraErrorEvent("The camera was closed during configuration.");
+              return;
+            }
+            cameraCaptureSession = session;
+
+            updateFpsRange();
+            updateAutoFocus();
+            updateFlash(flashMode);
+            updateExposure(exposureMode);
+
+            refreshPreviewCaptureSession(
+                onSuccessCallback, (code, message) -> dartMessenger.sendCameraErrorEvent(message));
+          }
+
+          @Override
+          public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
+            dartMessenger.sendCameraErrorEvent("Failed to configure camera session.");
+          }
+        };
+
+    // Start the session
+    if (VERSION.SDK_INT >= VERSION_CODES.P) {
+      // Collect all surfaces we want to render to.
+      List<OutputConfiguration> configs = new ArrayList<>();
+      configs.add(new OutputConfiguration(flutterSurface));
+      for (Surface surface : remainingSurfaces) {
+        configs.add(new OutputConfiguration(surface));
+      }
+      createCaptureSessionWithSessionConfig(configs, callback);
+    } else {
+      // Collect all surfaces we want to render to.
+      List<Surface> surfaceList = new ArrayList<>();
+      surfaceList.add(flutterSurface);
+      surfaceList.addAll(remainingSurfaces);
+      createCaptureSession(surfaceList, callback);
+    }
+  }
+
+  @TargetApi(VERSION_CODES.P)
+  private void createCaptureSessionWithSessionConfig(
+      List<OutputConfiguration> outputConfigs, CameraCaptureSession.StateCallback callback)
+      throws CameraAccessException {
+    cameraDevice.createCaptureSession(
+        new SessionConfiguration(
+            SessionConfiguration.SESSION_REGULAR,
+            outputConfigs,
+            Executors.newSingleThreadExecutor(),
+            callback));
+  }
+
+  @TargetApi(VERSION_CODES.LOLLIPOP)
+  @SuppressWarnings("deprecation")
+  private void createCaptureSession(
+      List<Surface> surfaces, CameraCaptureSession.StateCallback callback)
+      throws CameraAccessException {
+    cameraDevice.createCaptureSession(surfaces, callback, null);
+  }
+
+  private void refreshPreviewCaptureSession(
+      @Nullable Runnable onSuccessCallback, @NonNull ErrorCallback onErrorCallback) {
+    if (cameraCaptureSession == null) {
+      return;
+    }
+
+    try {
+      cameraCaptureSession.setRepeatingRequest(
+          captureRequestBuilder.build(),
+          pictureCaptureCallback,
+          new Handler(Looper.getMainLooper()));
+
+      if (onSuccessCallback != null) {
+        onSuccessCallback.run();
+      }
+    } catch (CameraAccessException | IllegalStateException | IllegalArgumentException e) {
+      onErrorCallback.onError("cameraAccess", e.getMessage());
+    }
+  }
+
   private void writeToFile(ByteBuffer buffer, File file) throws IOException {
     try (FileOutputStream outputStream = new FileOutputStream(file)) {
       while (0 < buffer.remaining()) {
@@ -261,7 +406,11 @@
         },
         null);
 
-    runPictureAutoFocus();
+    if (useAutoFocus) {
+      runPictureAutoFocus();
+    } else {
+      runPicturePreCapture();
+    }
   }
 
   private final CameraCaptureSession.CaptureCallback pictureCaptureCallback =
@@ -344,6 +493,7 @@
 
   private void runPictureAutoFocus() {
     assert (pictureCaptureRequest != null);
+
     pictureCaptureRequest.setState(PictureCaptureRequest.State.focusing);
     lockAutoFocus();
   }
@@ -355,14 +505,13 @@
     captureRequestBuilder.set(
         CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
         CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
-    try {
-      cameraCaptureSession.capture(captureRequestBuilder.build(), pictureCaptureCallback, null);
-      captureRequestBuilder.set(
-          CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
-          CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
-    } catch (CameraAccessException e) {
-      pictureCaptureRequest.error("cameraAccess", e.getMessage(), null);
-    }
+
+    refreshPreviewCaptureSession(
+        () ->
+            captureRequestBuilder.set(
+                CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
+                CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE),
+        (code, message) -> pictureCaptureRequest.error(code, message, null));
   }
 
   private void runPictureCapture() {
@@ -409,125 +558,25 @@
   private void lockAutoFocus() {
     captureRequestBuilder.set(
         CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
-    try {
-      cameraCaptureSession.capture(captureRequestBuilder.build(), pictureCaptureCallback, null);
-    } catch (CameraAccessException e) {
-      pictureCaptureRequest.error("cameraAccess", e.getMessage(), null);
-    }
+
+    refreshPreviewCaptureSession(
+        null, (code, message) -> pictureCaptureRequest.error(code, message, null));
   }
 
   private void unlockAutoFocus() {
     captureRequestBuilder.set(
         CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
-    initPreviewCaptureBuilder();
+    updateAutoFocus();
     try {
       cameraCaptureSession.capture(captureRequestBuilder.build(), null, null);
     } catch (CameraAccessException ignored) {
     }
     captureRequestBuilder.set(
         CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
-    try {
-      cameraCaptureSession.setRepeatingRequest(
-          captureRequestBuilder.build(), pictureCaptureCallback, null);
-    } catch (CameraAccessException e) {
-      pictureCaptureRequest.error("cameraAccess", e.getMessage(), null);
-    }
-  }
 
-  private void createCaptureSession(int templateType, Surface... surfaces)
-      throws CameraAccessException {
-    createCaptureSession(templateType, null, surfaces);
-  }
-
-  private void createCaptureSession(
-      int templateType, Runnable onSuccessCallback, Surface... surfaces)
-      throws CameraAccessException {
-    // Close any existing capture session.
-    closeCaptureSession();
-
-    // Create a new capture builder.
-    captureRequestBuilder = cameraDevice.createCaptureRequest(templateType);
-
-    // Build Flutter surface to render to
-    SurfaceTexture surfaceTexture = flutterTexture.surfaceTexture();
-    surfaceTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
-    Surface flutterSurface = new Surface(surfaceTexture);
-    captureRequestBuilder.addTarget(flutterSurface);
-
-    List<Surface> remainingSurfaces = Arrays.asList(surfaces);
-    if (templateType != CameraDevice.TEMPLATE_PREVIEW) {
-      // If it is not preview mode, add all surfaces as targets.
-      for (Surface surface : remainingSurfaces) {
-        captureRequestBuilder.addTarget(surface);
-      }
-    }
-
-    // Prepare the callback
-    CameraCaptureSession.StateCallback callback =
-        new CameraCaptureSession.StateCallback() {
-          @Override
-          public void onConfigured(@NonNull CameraCaptureSession session) {
-            try {
-              if (cameraDevice == null) {
-                dartMessenger.sendCameraErrorEvent("The camera was closed during configuration.");
-                return;
-              }
-              cameraCaptureSession = session;
-              initPreviewCaptureBuilder();
-              cameraCaptureSession.setRepeatingRequest(
-                  captureRequestBuilder.build(),
-                  pictureCaptureCallback,
-                  new Handler(Looper.getMainLooper()));
-              if (onSuccessCallback != null) {
-                onSuccessCallback.run();
-              }
-            } catch (CameraAccessException | IllegalStateException | IllegalArgumentException e) {
-              dartMessenger.sendCameraErrorEvent(e.getMessage());
-            }
-          }
-
-          @Override
-          public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
-            dartMessenger.sendCameraErrorEvent("Failed to configure camera session.");
-          }
-        };
-
-    // Start the session
-    if (VERSION.SDK_INT >= VERSION_CODES.P) {
-      // Collect all surfaces we want to render to.
-      List<OutputConfiguration> configs = new ArrayList<>();
-      configs.add(new OutputConfiguration(flutterSurface));
-      for (Surface surface : remainingSurfaces) {
-        configs.add(new OutputConfiguration(surface));
-      }
-      createCaptureSessionWithSessionConfig(configs, callback);
-    } else {
-      // Collect all surfaces we want to render to.
-      List<Surface> surfaceList = new ArrayList<>();
-      surfaceList.add(flutterSurface);
-      surfaceList.addAll(remainingSurfaces);
-      createCaptureSession(surfaceList, callback);
-    }
-  }
-
-  @TargetApi(VERSION_CODES.P)
-  private void createCaptureSessionWithSessionConfig(
-      List<OutputConfiguration> outputConfigs, CameraCaptureSession.StateCallback callback)
-      throws CameraAccessException {
-    cameraDevice.createCaptureSession(
-        new SessionConfiguration(
-            SessionConfiguration.SESSION_REGULAR,
-            outputConfigs,
-            Executors.newSingleThreadExecutor(),
-            callback));
-  }
-
-  @TargetApi(VERSION_CODES.LOLLIPOP)
-  @SuppressWarnings("deprecation")
-  private void createCaptureSession(
-      List<Surface> surfaces, CameraCaptureSession.StateCallback callback)
-      throws CameraAccessException {
-    cameraDevice.createCaptureSession(surfaces, callback, null);
+    refreshPreviewCaptureSession(
+        null,
+        (errorCode, errorMessage) -> pictureCaptureRequest.error(errorCode, errorMessage, null));
   }
 
   public void startVideoRecording(Result result) {
@@ -560,8 +609,14 @@
 
     try {
       recordingVideo = false;
-      closeCaptureSession();
-      mediaRecorder.stop();
+
+      try {
+        cameraCaptureSession.abortCaptures();
+        mediaRecorder.stop();
+      } catch (CameraAccessException | IllegalStateException e) {
+        // Ignore exceptions and try to continue (changes are camera session already aborted capture)
+      }
+
       mediaRecorder.reset();
       startPreview();
       result.success(videoRecordingFile.getAbsolutePath());
@@ -630,8 +685,8 @@
 
     // If switching directly from torch to auto or on, make sure we turn off the torch.
     if (flashMode == FlashMode.torch && mode != FlashMode.torch && mode != FlashMode.off) {
-      this.flashMode = FlashMode.off;
-      initPreviewCaptureBuilder();
+      updateFlash(FlashMode.off);
+
       this.cameraCaptureSession.setRepeatingRequest(
           captureRequestBuilder.build(),
           new CaptureCallback() {
@@ -647,8 +702,13 @@
               }
 
               updateFlash(mode);
-              result.success(null);
-              isFinished = true;
+              refreshPreviewCaptureSession(
+                  () -> {
+                    result.success(null);
+                    isFinished = true;
+                  },
+                  (code, message) ->
+                      result.error("setFlashModeFailed", "Could not set flash mode.", null));
             }
 
             @Override
@@ -667,26 +727,16 @@
           null);
     } else {
       updateFlash(mode);
-      result.success(null);
-    }
-  }
 
-  private void updateFlash(FlashMode mode) {
-    // Get flash
-    flashMode = mode;
-    initPreviewCaptureBuilder();
-    try {
-      cameraCaptureSession.setRepeatingRequest(
-          captureRequestBuilder.build(), pictureCaptureCallback, null);
-    } catch (CameraAccessException e) {
-      pictureCaptureRequest.error("cameraAccess", e.getMessage(), null);
+      refreshPreviewCaptureSession(
+          () -> result.success(null),
+          (code, message) -> result.error("setFlashModeFailed", "Could not set flash mode.", null));
     }
   }
 
   public void setExposureMode(@NonNull final Result result, ExposureMode mode)
       throws CameraAccessException {
-    this.exposureMode = mode;
-    initPreviewCaptureBuilder();
+    updateExposure(mode);
     cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, null);
     result.success(null);
   }
@@ -713,10 +763,9 @@
     // Set the metering rectangle
     cameraRegions.setAutoExposureMeteringRectangleFromPoint(x, y);
     // Apply it
-    initPreviewCaptureBuilder();
-    this.cameraCaptureSession.setRepeatingRequest(
-        captureRequestBuilder.build(), pictureCaptureCallback, null);
-    result.success(null);
+    updateExposure(exposureMode);
+    refreshPreviewCaptureSession(
+        () -> result.success(null), (code, message) -> result.error("CameraAccess", message, null));
   }
 
   @TargetApi(VERSION_CODES.P)
@@ -802,13 +851,97 @@
     double stepSize = getExposureOffsetStepSize();
     exposureOffset = (int) (offset / stepSize);
     // Apply it
-    initPreviewCaptureBuilder();
+    updateExposure(exposureMode);
     this.cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, null);
     result.success(offset);
   }
 
-  private void initPreviewCaptureBuilder() {
-    captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
+  public float getMaxZoomLevel() {
+    return cameraZoom.maxZoom;
+  }
+
+  public float getMinZoomLevel() {
+    return CameraZoom.DEFAULT_ZOOM_FACTOR;
+  }
+
+  public void setZoomLevel(@NonNull final Result result, float zoom) throws CameraAccessException {
+    float maxZoom = cameraZoom.maxZoom;
+    float minZoom = CameraZoom.DEFAULT_ZOOM_FACTOR;
+
+    if (zoom > maxZoom || zoom < minZoom) {
+      String errorMessage =
+          String.format(
+              Locale.ENGLISH,
+              "Zoom level out of bounds (zoom level should be between %f and %f).",
+              minZoom,
+              maxZoom);
+      result.error("ZOOM_ERROR", errorMessage, null);
+      return;
+    }
+
+    //Zoom area is calculated relative to sensor area (activeRect)
+    if (captureRequestBuilder != null) {
+      final Rect computedZoom = cameraZoom.computeZoom(zoom);
+      captureRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, computedZoom);
+      cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, null);
+    }
+
+    result.success(null);
+  }
+
+  private void updateFpsRange() {
+    if (fpsRange == null) {
+      return;
+    }
+
+    captureRequestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
+  }
+
+  private void updateAutoFocus() {
+    if (useAutoFocus) {
+      int[] modes = cameraCharacteristics.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
+      // Auto focus is not supported
+      if (modes == null
+          || modes.length == 0
+          || (modes.length == 1 && modes[0] == CameraCharacteristics.CONTROL_AF_MODE_OFF)) {
+        useAutoFocus = false;
+        captureRequestBuilder.set(
+            CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF);
+      } else {
+        captureRequestBuilder.set(
+            CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
+      }
+    } else {
+      captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF);
+    }
+  }
+
+  private void updateExposure(ExposureMode mode) {
+    exposureMode = mode;
+
+    // Applying auto exposure
+    MeteringRectangle aeRect = cameraRegions.getAEMeteringRectangle();
+    captureRequestBuilder.set(
+        CaptureRequest.CONTROL_AE_REGIONS,
+        aeRect == null ? null : new MeteringRectangle[] {cameraRegions.getAEMeteringRectangle()});
+
+    switch (mode) {
+      case locked:
+        captureRequestBuilder.set(CaptureRequest.CONTROL_AE_LOCK, true);
+        break;
+      case auto:
+      default:
+        captureRequestBuilder.set(CaptureRequest.CONTROL_AE_LOCK, false);
+        break;
+    }
+
+    captureRequestBuilder.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, exposureOffset);
+  }
+
+  private void updateFlash(FlashMode mode) {
+    // Get flash
+    flashMode = mode;
+
     // Applying flash modes
     switch (flashMode) {
       case off:
@@ -833,24 +966,6 @@
         captureRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH);
         break;
     }
-    // Applying auto exposure
-    MeteringRectangle aeRect = cameraRegions.getAEMeteringRectangle();
-    captureRequestBuilder.set(
-        CaptureRequest.CONTROL_AE_REGIONS,
-        aeRect == null ? null : new MeteringRectangle[] {cameraRegions.getAEMeteringRectangle()});
-    switch (exposureMode) {
-      case locked:
-        captureRequestBuilder.set(CaptureRequest.CONTROL_AE_LOCK, true);
-        break;
-      case auto:
-      default:
-        captureRequestBuilder.set(CaptureRequest.CONTROL_AE_LOCK, false);
-        break;
-    }
-    captureRequestBuilder.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, exposureOffset);
-    // Applying auto focus
-    captureRequestBuilder.set(
-        CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
   }
 
   public void startPreview() throws CameraAccessException {
@@ -910,39 +1025,6 @@
         null);
   }
 
-  public float getMaxZoomLevel() {
-    return cameraZoom.maxZoom;
-  }
-
-  public float getMinZoomLevel() {
-    return CameraZoom.DEFAULT_ZOOM_FACTOR;
-  }
-
-  public void setZoomLevel(@NonNull final Result result, float zoom) throws CameraAccessException {
-    float maxZoom = cameraZoom.maxZoom;
-    float minZoom = CameraZoom.DEFAULT_ZOOM_FACTOR;
-
-    if (zoom > maxZoom || zoom < minZoom) {
-      String errorMessage =
-          String.format(
-              Locale.ENGLISH,
-              "Zoom level out of bounds (zoom level should be between %f and %f).",
-              minZoom,
-              maxZoom);
-      result.error("ZOOM_ERROR", errorMessage, null);
-      return;
-    }
-
-    //Zoom area is calculated relative to sensor area (activeRect)
-    if (captureRequestBuilder != null) {
-      final Rect computedZoom = cameraZoom.computeZoom(zoom);
-      captureRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, computedZoom);
-      cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, null);
-    }
-
-    result.success(null);
-  }
-
   public void stopImageStream() throws CameraAccessException {
     if (imageStreamReader != null) {
       imageStreamReader.setOnImageAvailableListener(null, null);
diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml
index 02f874b..99f41fb 100644
--- a/packages/camera/camera/pubspec.yaml
+++ b/packages/camera/camera/pubspec.yaml
@@ -2,7 +2,7 @@
 description: A Flutter plugin for getting information about and controlling the
   camera on Android and iOS. Supports previewing the camera feed, capturing images, capturing video,
   and streaming image buffers to dart.
-version: 0.6.4+2
+version: 0.6.4+3
 homepage: https://github.com/flutter/plugins/tree/master/packages/camera/camera
 
 dependencies: