[camera] Fix CamcorderProfile Usages (#4423)

diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md
index e6495a8..29750f9 100644
--- a/packages/camera/camera/CHANGELOG.md
+++ b/packages/camera/camera/CHANGELOG.md
@@ -1,5 +1,8 @@
 ## 0.9.4+3
 
+* Change Android compileSdkVersion to 31.
+* Remove usages of deprecated Android API `CamcorderProfile`.
+* Update gradle version to 7.0.2 on Android.
 * Fix registerTexture and result being called on background thread on iOS.
 
 ## 0.9.4+2
diff --git a/packages/camera/camera/android/build.gradle b/packages/camera/camera/android/build.gradle
index 25285ad..264f7f8 100644
--- a/packages/camera/camera/android/build.gradle
+++ b/packages/camera/camera/android/build.gradle
@@ -9,7 +9,7 @@
     }
 
     dependencies {
-        classpath 'com.android.tools.build:gradle:3.5.0'
+        classpath 'com.android.tools.build:gradle:7.0.2'
     }
 }
 
@@ -27,9 +27,10 @@
 apply plugin: 'com.android.library'
 
 android {
-    compileSdkVersion 29
+    compileSdkVersion 31
 
     defaultConfig {
+        targetSdkVersion 31
         minSdkVersion 21
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
     }
@@ -60,7 +61,7 @@
 dependencies {
     compileOnly 'androidx.annotation:annotation:1.1.0'
     testImplementation 'junit:junit:4.12'
-    testImplementation 'org.mockito:mockito-inline:3.12.4'
+    testImplementation 'org.mockito:mockito-inline:4.0.0'
     testImplementation 'androidx.test:core:1.3.0'
-    testImplementation 'org.robolectric:robolectric:4.3'
+    testImplementation 'org.robolectric:robolectric:4.5'
 }
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 75ced53..e319039 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
@@ -20,6 +20,7 @@
 import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.params.SessionConfiguration;
 import android.media.CamcorderProfile;
+import android.media.EncoderProfiles;
 import android.media.Image;
 import android.media.ImageReader;
 import android.media.MediaRecorder;
@@ -199,8 +200,16 @@
         ((SensorOrientationFeature) cameraFeatures.getSensorOrientation())
             .getLockedCaptureOrientation();
 
+    MediaRecorderBuilder mediaRecorderBuilder;
+
+    if (Build.VERSION.SDK_INT >= 31) {
+      mediaRecorderBuilder = new MediaRecorderBuilder(getRecordingProfile(), outputFilePath);
+    } else {
+      mediaRecorderBuilder = new MediaRecorderBuilder(getRecordingProfileLegacy(), outputFilePath);
+    }
+
     mediaRecorder =
-        new MediaRecorderBuilder(getRecordingProfile(), outputFilePath)
+        mediaRecorderBuilder
             .setEnableAudio(enableAudio)
             .setMediaOrientation(
                 lockedOrientation == null
@@ -918,8 +927,12 @@
     return cameraFeatures.getZoomLevel().getMinimumZoomLevel();
   }
 
-  /** Shortcut to get current recording profile. */
-  CamcorderProfile getRecordingProfile() {
+  /** Shortcut to get current recording profile. Legacy method provides support for SDK < 31. */
+  CamcorderProfile getRecordingProfileLegacy() {
+    return cameraFeatures.getResolution().getRecordingProfileLegacy();
+  }
+
+  EncoderProfiles getRecordingProfile() {
     return cameraFeatures.getResolution().getRecordingProfile();
   }
 
diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java
index 67763dd..afbd7c3 100644
--- a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java
+++ b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java
@@ -4,12 +4,16 @@
 
 package io.flutter.plugins.camera.features.resolution;
 
+import android.annotation.TargetApi;
 import android.hardware.camera2.CaptureRequest;
 import android.media.CamcorderProfile;
+import android.media.EncoderProfiles;
+import android.os.Build;
 import android.util.Size;
 import androidx.annotation.VisibleForTesting;
 import io.flutter.plugins.camera.CameraProperties;
 import io.flutter.plugins.camera.features.CameraFeature;
+import java.util.List;
 
 /**
  * Controls the resolutions configuration on the {@link android.hardware.camera2} API.
@@ -21,7 +25,8 @@
 public class ResolutionFeature extends CameraFeature<ResolutionPreset> {
   private Size captureSize;
   private Size previewSize;
-  private CamcorderProfile recordingProfile;
+  private CamcorderProfile recordingProfileLegacy;
+  private EncoderProfiles recordingProfile;
   private ResolutionPreset currentSetting;
   private int cameraId;
 
@@ -51,7 +56,11 @@
    *
    * @return Resolution information to configure the {@link android.hardware.camera2} API.
    */
-  public CamcorderProfile getRecordingProfile() {
+  public CamcorderProfile getRecordingProfileLegacy() {
+    return this.recordingProfileLegacy;
+  }
+
+  public EncoderProfiles getRecordingProfile() {
     return this.recordingProfile;
   }
 
@@ -100,19 +109,29 @@
   }
 
   @VisibleForTesting
-  static Size computeBestPreviewSize(int cameraId, ResolutionPreset preset) {
+  static Size computeBestPreviewSize(int cameraId, ResolutionPreset preset)
+      throws IndexOutOfBoundsException {
     if (preset.ordinal() > ResolutionPreset.high.ordinal()) {
       preset = ResolutionPreset.high;
     }
+    if (Build.VERSION.SDK_INT >= 31) {
+      EncoderProfiles profile =
+          getBestAvailableCamcorderProfileForResolutionPreset(cameraId, preset);
+      List<EncoderProfiles.VideoProfile> videoProfiles = profile.getVideoProfiles();
+      EncoderProfiles.VideoProfile defaultVideoProfile = videoProfiles.get(0);
 
-    CamcorderProfile profile =
-        getBestAvailableCamcorderProfileForResolutionPreset(cameraId, preset);
-    return new Size(profile.videoFrameWidth, profile.videoFrameHeight);
+      return new Size(defaultVideoProfile.getWidth(), defaultVideoProfile.getHeight());
+    } else {
+      @SuppressWarnings("deprecation")
+      CamcorderProfile profile =
+          getBestAvailableCamcorderProfileForResolutionPresetLegacy(cameraId, preset);
+      return new Size(profile.videoFrameWidth, profile.videoFrameHeight);
+    }
   }
 
   /**
    * Gets the best possible {@link android.media.CamcorderProfile} for the supplied {@link
-   * ResolutionPreset}.
+   * ResolutionPreset}. Supports SDK < 31.
    *
    * @param cameraId Camera identifier which indicates the device's camera for which to select a
    *     {@link android.media.CamcorderProfile}.
@@ -121,7 +140,7 @@
    * @return The best possible {@link android.media.CamcorderProfile} that matches the supplied
    *     {@link ResolutionPreset}.
    */
-  public static CamcorderProfile getBestAvailableCamcorderProfileForResolutionPreset(
+  public static CamcorderProfile getBestAvailableCamcorderProfileForResolutionPresetLegacy(
       int cameraId, ResolutionPreset preset) {
     if (cameraId < 0) {
       throw new AssertionError(
@@ -164,13 +183,74 @@
     }
   }
 
-  private void configureResolution(ResolutionPreset resolutionPreset, int cameraId) {
+  @TargetApi(Build.VERSION_CODES.S)
+  public static EncoderProfiles getBestAvailableCamcorderProfileForResolutionPreset(
+      int cameraId, ResolutionPreset preset) {
+    if (cameraId < 0) {
+      throw new AssertionError(
+          "getBestAvailableCamcorderProfileForResolutionPreset can only be used with valid (>=0) camera identifiers.");
+    }
+
+    String cameraIdString = Integer.toString(cameraId);
+
+    switch (preset) {
+        // All of these cases deliberately fall through to get the best available profile.
+      case max:
+        if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_HIGH)) {
+          return CamcorderProfile.getAll(cameraIdString, CamcorderProfile.QUALITY_HIGH);
+        }
+      case ultraHigh:
+        if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_2160P)) {
+          return CamcorderProfile.getAll(cameraIdString, CamcorderProfile.QUALITY_2160P);
+        }
+      case veryHigh:
+        if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_1080P)) {
+          return CamcorderProfile.getAll(cameraIdString, CamcorderProfile.QUALITY_1080P);
+        }
+      case high:
+        if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_720P)) {
+          return CamcorderProfile.getAll(cameraIdString, CamcorderProfile.QUALITY_720P);
+        }
+      case medium:
+        if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_480P)) {
+          return CamcorderProfile.getAll(cameraIdString, CamcorderProfile.QUALITY_480P);
+        }
+      case low:
+        if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_QVGA)) {
+          return CamcorderProfile.getAll(cameraIdString, CamcorderProfile.QUALITY_QVGA);
+        }
+      default:
+        if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_LOW)) {
+          return CamcorderProfile.getAll(cameraIdString, CamcorderProfile.QUALITY_LOW);
+        }
+
+        throw new IllegalArgumentException(
+            "No capture session available for current capture session.");
+    }
+  }
+
+  private void configureResolution(ResolutionPreset resolutionPreset, int cameraId)
+      throws IndexOutOfBoundsException {
     if (!checkIsSupported()) {
       return;
     }
-    recordingProfile =
-        getBestAvailableCamcorderProfileForResolutionPreset(cameraId, resolutionPreset);
-    captureSize = new Size(recordingProfile.videoFrameWidth, recordingProfile.videoFrameHeight);
+
+    if (Build.VERSION.SDK_INT >= 31) {
+      recordingProfile =
+          getBestAvailableCamcorderProfileForResolutionPreset(cameraId, resolutionPreset);
+      List<EncoderProfiles.VideoProfile> videoProfiles = recordingProfile.getVideoProfiles();
+
+      EncoderProfiles.VideoProfile defaultVideoProfile = videoProfiles.get(0);
+      captureSize = new Size(defaultVideoProfile.getWidth(), defaultVideoProfile.getHeight());
+    } else {
+      @SuppressWarnings("deprecation")
+      CamcorderProfile camcorderProfile =
+          getBestAvailableCamcorderProfileForResolutionPresetLegacy(cameraId, resolutionPreset);
+      recordingProfileLegacy = camcorderProfile;
+      captureSize =
+          new Size(recordingProfileLegacy.videoFrameWidth, recordingProfileLegacy.videoFrameHeight);
+    }
+
     previewSize = computeBestPreviewSize(cameraId, resolutionPreset);
   }
 }
diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/media/MediaRecorderBuilder.java b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/media/MediaRecorderBuilder.java
index a78c2b4..0aebfee 100644
--- a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/media/MediaRecorderBuilder.java
+++ b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/media/MediaRecorderBuilder.java
@@ -5,11 +5,14 @@
 package io.flutter.plugins.camera.media;
 
 import android.media.CamcorderProfile;
+import android.media.EncoderProfiles;
 import android.media.MediaRecorder;
+import android.os.Build;
 import androidx.annotation.NonNull;
 import java.io.IOException;
 
 public class MediaRecorderBuilder {
+  @SuppressWarnings("deprecation")
   static class MediaRecorderFactory {
     MediaRecorder makeMediaRecorder() {
       return new MediaRecorder();
@@ -17,23 +20,40 @@
   }
 
   private final String outputFilePath;
-  private final CamcorderProfile recordingProfile;
+  private final CamcorderProfile camcorderProfile;
+  private final EncoderProfiles encoderProfiles;
   private final MediaRecorderFactory recorderFactory;
 
   private boolean enableAudio;
   private int mediaOrientation;
 
   public MediaRecorderBuilder(
-      @NonNull CamcorderProfile recordingProfile, @NonNull String outputFilePath) {
-    this(recordingProfile, outputFilePath, new MediaRecorderFactory());
+      @NonNull CamcorderProfile camcorderProfile, @NonNull String outputFilePath) {
+    this(camcorderProfile, outputFilePath, new MediaRecorderFactory());
+  }
+
+  public MediaRecorderBuilder(
+      @NonNull EncoderProfiles encoderProfiles, @NonNull String outputFilePath) {
+    this(encoderProfiles, outputFilePath, new MediaRecorderFactory());
   }
 
   MediaRecorderBuilder(
-      @NonNull CamcorderProfile recordingProfile,
+      @NonNull CamcorderProfile camcorderProfile,
       @NonNull String outputFilePath,
       MediaRecorderFactory helper) {
     this.outputFilePath = outputFilePath;
-    this.recordingProfile = recordingProfile;
+    this.camcorderProfile = camcorderProfile;
+    this.encoderProfiles = null;
+    this.recorderFactory = helper;
+  }
+
+  MediaRecorderBuilder(
+      @NonNull EncoderProfiles encoderProfiles,
+      @NonNull String outputFilePath,
+      MediaRecorderFactory helper) {
+    this.outputFilePath = outputFilePath;
+    this.encoderProfiles = encoderProfiles;
+    this.camcorderProfile = null;
     this.recorderFactory = helper;
   }
 
@@ -47,23 +67,43 @@
     return this;
   }
 
-  public MediaRecorder build() throws IOException {
+  public MediaRecorder build() throws IOException, NullPointerException, IndexOutOfBoundsException {
     MediaRecorder mediaRecorder = recorderFactory.makeMediaRecorder();
 
     // There's a fixed order that mediaRecorder expects. Only change these functions accordingly.
     // You can find the specifics here: https://developer.android.com/reference/android/media/MediaRecorder.
     if (enableAudio) mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
     mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
-    mediaRecorder.setOutputFormat(recordingProfile.fileFormat);
-    if (enableAudio) {
-      mediaRecorder.setAudioEncoder(recordingProfile.audioCodec);
-      mediaRecorder.setAudioEncodingBitRate(recordingProfile.audioBitRate);
-      mediaRecorder.setAudioSamplingRate(recordingProfile.audioSampleRate);
+
+    if (Build.VERSION.SDK_INT >= 31) {
+      EncoderProfiles.VideoProfile videoProfile = encoderProfiles.getVideoProfiles().get(0);
+      EncoderProfiles.AudioProfile audioProfile = encoderProfiles.getAudioProfiles().get(0);
+
+      mediaRecorder.setOutputFormat(encoderProfiles.getRecommendedFileFormat());
+      if (enableAudio) {
+        mediaRecorder.setAudioEncoder(audioProfile.getCodec());
+        mediaRecorder.setAudioEncodingBitRate(audioProfile.getBitrate());
+        mediaRecorder.setAudioSamplingRate(audioProfile.getSampleRate());
+      }
+      mediaRecorder.setVideoEncoder(videoProfile.getCodec());
+      mediaRecorder.setVideoEncodingBitRate(videoProfile.getBitrate());
+      mediaRecorder.setVideoFrameRate(videoProfile.getFrameRate());
+      mediaRecorder.setVideoSize(videoProfile.getWidth(), videoProfile.getHeight());
+      mediaRecorder.setVideoSize(videoProfile.getWidth(), videoProfile.getHeight());
+    } else {
+      mediaRecorder.setOutputFormat(camcorderProfile.fileFormat);
+      if (enableAudio) {
+        mediaRecorder.setAudioEncoder(camcorderProfile.audioCodec);
+        mediaRecorder.setAudioEncodingBitRate(camcorderProfile.audioBitRate);
+        mediaRecorder.setAudioSamplingRate(camcorderProfile.audioSampleRate);
+      }
+      mediaRecorder.setVideoEncoder(camcorderProfile.videoCodec);
+      mediaRecorder.setVideoEncodingBitRate(camcorderProfile.videoBitRate);
+      mediaRecorder.setVideoFrameRate(camcorderProfile.videoFrameRate);
+      mediaRecorder.setVideoSize(
+          camcorderProfile.videoFrameWidth, camcorderProfile.videoFrameHeight);
     }
-    mediaRecorder.setVideoEncoder(recordingProfile.videoCodec);
-    mediaRecorder.setVideoEncodingBitRate(recordingProfile.videoBitRate);
-    mediaRecorder.setVideoFrameRate(recordingProfile.videoFrameRate);
-    mediaRecorder.setVideoSize(recordingProfile.videoFrameWidth, recordingProfile.videoFrameHeight);
+
     mediaRecorder.setOutputFile(outputFilePath);
     mediaRecorder.setOrientationHint(this.mediaOrientation);
 
diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraTest.java b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraTest.java
index 9d97319..1ed2e4c 100644
--- a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraTest.java
+++ b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraTest.java
@@ -22,7 +22,6 @@
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraMetadata;
 import android.hardware.camera2.CaptureRequest;
-import android.media.CamcorderProfile;
 import android.media.MediaRecorder;
 import android.os.Build;
 import android.os.Handler;
@@ -250,20 +249,6 @@
   }
 
   @Test
-  public void getRecordingProfile() {
-    ResolutionFeature mockResolutionFeature =
-        mockCameraFeatureFactory.createResolutionFeature(mockCameraProperties, null, null);
-    CamcorderProfile mockCamcorderProfile = mock(CamcorderProfile.class);
-
-    when(mockResolutionFeature.getRecordingProfile()).thenReturn(mockCamcorderProfile);
-
-    CamcorderProfile actualRecordingProfile = camera.getRecordingProfile();
-
-    verify(mockResolutionFeature, times(1)).getRecordingProfile();
-    assertEquals(mockCamcorderProfile, actualRecordingProfile);
-  }
-
-  @Test
   public void setExposureMode_shouldUpdateExposureLockFeature() {
     ExposureLockFeature mockExposureLockFeature =
         mockCameraFeatureFactory.createExposureLockFeature(mockCameraProperties);
diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraTest_getRecordingProfileTest.java b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraTest_getRecordingProfileTest.java
new file mode 100644
index 0000000..04bab14
--- /dev/null
+++ b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraTest_getRecordingProfileTest.java
@@ -0,0 +1,205 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package io.flutter.plugins.camera;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CaptureRequest;
+import android.media.CamcorderProfile;
+import android.media.EncoderProfiles;
+import android.os.Handler;
+import android.os.HandlerThread;
+import androidx.annotation.NonNull;
+import io.flutter.plugins.camera.features.CameraFeatureFactory;
+import io.flutter.plugins.camera.features.autofocus.AutoFocusFeature;
+import io.flutter.plugins.camera.features.exposurelock.ExposureLockFeature;
+import io.flutter.plugins.camera.features.exposureoffset.ExposureOffsetFeature;
+import io.flutter.plugins.camera.features.exposurepoint.ExposurePointFeature;
+import io.flutter.plugins.camera.features.flash.FlashFeature;
+import io.flutter.plugins.camera.features.focuspoint.FocusPointFeature;
+import io.flutter.plugins.camera.features.fpsrange.FpsRangeFeature;
+import io.flutter.plugins.camera.features.noisereduction.NoiseReductionFeature;
+import io.flutter.plugins.camera.features.resolution.ResolutionFeature;
+import io.flutter.plugins.camera.features.resolution.ResolutionPreset;
+import io.flutter.plugins.camera.features.sensororientation.SensorOrientationFeature;
+import io.flutter.plugins.camera.features.zoomlevel.ZoomLevelFeature;
+import io.flutter.view.TextureRegistry;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockedStatic;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+@RunWith(RobolectricTestRunner.class)
+public class CameraTest_getRecordingProfileTest {
+
+  private CameraProperties mockCameraProperties;
+  private CameraFeatureFactory mockCameraFeatureFactory;
+  private DartMessenger mockDartMessenger;
+  private Camera camera;
+  private CameraCaptureSession mockCaptureSession;
+  private CaptureRequest.Builder mockPreviewRequestBuilder;
+  private MockedStatic<Camera.HandlerThreadFactory> mockHandlerThreadFactory;
+  private HandlerThread mockHandlerThread;
+  private MockedStatic<Camera.HandlerFactory> mockHandlerFactory;
+  private Handler mockHandler;
+
+  @Before
+  public void before() {
+    mockCameraProperties = mock(CameraProperties.class);
+    mockCameraFeatureFactory = new TestCameraFeatureFactory();
+    mockDartMessenger = mock(DartMessenger.class);
+
+    final Activity mockActivity = mock(Activity.class);
+    final TextureRegistry.SurfaceTextureEntry mockFlutterTexture =
+        mock(TextureRegistry.SurfaceTextureEntry.class);
+    final ResolutionPreset resolutionPreset = ResolutionPreset.high;
+    final boolean enableAudio = false;
+
+    camera =
+        new Camera(
+            mockActivity,
+            mockFlutterTexture,
+            mockCameraFeatureFactory,
+            mockDartMessenger,
+            mockCameraProperties,
+            resolutionPreset,
+            enableAudio);
+  }
+
+  @Config(maxSdk = 30)
+  @Test
+  public void getRecordingProfileLegacy() {
+    ResolutionFeature mockResolutionFeature =
+        mockCameraFeatureFactory.createResolutionFeature(mockCameraProperties, null, null);
+    CamcorderProfile mockCamcorderProfile = mock(CamcorderProfile.class);
+
+    when(mockResolutionFeature.getRecordingProfileLegacy()).thenReturn(mockCamcorderProfile);
+
+    CamcorderProfile actualRecordingProfile = camera.getRecordingProfileLegacy();
+
+    verify(mockResolutionFeature, times(1)).getRecordingProfileLegacy();
+    assertEquals(mockCamcorderProfile, actualRecordingProfile);
+  }
+
+  @Config(minSdk = 31)
+  @Test
+  public void getRecordingProfile() {
+    ResolutionFeature mockResolutionFeature =
+        mockCameraFeatureFactory.createResolutionFeature(mockCameraProperties, null, null);
+    EncoderProfiles mockRecordingProfile = mock(EncoderProfiles.class);
+
+    when(mockResolutionFeature.getRecordingProfile()).thenReturn(mockRecordingProfile);
+
+    EncoderProfiles actualRecordingProfile = camera.getRecordingProfile();
+
+    verify(mockResolutionFeature, times(1)).getRecordingProfile();
+    assertEquals(mockRecordingProfile, actualRecordingProfile);
+  }
+
+  private static class TestCameraFeatureFactory implements CameraFeatureFactory {
+    private final AutoFocusFeature mockAutoFocusFeature;
+    private final ExposureLockFeature mockExposureLockFeature;
+    private final ExposureOffsetFeature mockExposureOffsetFeature;
+    private final ExposurePointFeature mockExposurePointFeature;
+    private final FlashFeature mockFlashFeature;
+    private final FocusPointFeature mockFocusPointFeature;
+    private final FpsRangeFeature mockFpsRangeFeature;
+    private final NoiseReductionFeature mockNoiseReductionFeature;
+    private final ResolutionFeature mockResolutionFeature;
+    private final SensorOrientationFeature mockSensorOrientationFeature;
+    private final ZoomLevelFeature mockZoomLevelFeature;
+
+    public TestCameraFeatureFactory() {
+      this.mockAutoFocusFeature = mock(AutoFocusFeature.class);
+      this.mockExposureLockFeature = mock(ExposureLockFeature.class);
+      this.mockExposureOffsetFeature = mock(ExposureOffsetFeature.class);
+      this.mockExposurePointFeature = mock(ExposurePointFeature.class);
+      this.mockFlashFeature = mock(FlashFeature.class);
+      this.mockFocusPointFeature = mock(FocusPointFeature.class);
+      this.mockFpsRangeFeature = mock(FpsRangeFeature.class);
+      this.mockNoiseReductionFeature = mock(NoiseReductionFeature.class);
+      this.mockResolutionFeature = mock(ResolutionFeature.class);
+      this.mockSensorOrientationFeature = mock(SensorOrientationFeature.class);
+      this.mockZoomLevelFeature = mock(ZoomLevelFeature.class);
+    }
+
+    @Override
+    public AutoFocusFeature createAutoFocusFeature(
+        @NonNull CameraProperties cameraProperties, boolean recordingVideo) {
+      return mockAutoFocusFeature;
+    }
+
+    @Override
+    public ExposureLockFeature createExposureLockFeature(
+        @NonNull CameraProperties cameraProperties) {
+      return mockExposureLockFeature;
+    }
+
+    @Override
+    public ExposureOffsetFeature createExposureOffsetFeature(
+        @NonNull CameraProperties cameraProperties) {
+      return mockExposureOffsetFeature;
+    }
+
+    @Override
+    public FlashFeature createFlashFeature(@NonNull CameraProperties cameraProperties) {
+      return mockFlashFeature;
+    }
+
+    @Override
+    public ResolutionFeature createResolutionFeature(
+        @NonNull CameraProperties cameraProperties,
+        ResolutionPreset initialSetting,
+        String cameraName) {
+      return mockResolutionFeature;
+    }
+
+    @Override
+    public FocusPointFeature createFocusPointFeature(
+        @NonNull CameraProperties cameraProperties,
+        @NonNull SensorOrientationFeature sensorOrienttionFeature) {
+      return mockFocusPointFeature;
+    }
+
+    @Override
+    public FpsRangeFeature createFpsRangeFeature(@NonNull CameraProperties cameraProperties) {
+      return mockFpsRangeFeature;
+    }
+
+    @Override
+    public SensorOrientationFeature createSensorOrientationFeature(
+        @NonNull CameraProperties cameraProperties,
+        @NonNull Activity activity,
+        @NonNull DartMessenger dartMessenger) {
+      return mockSensorOrientationFeature;
+    }
+
+    @Override
+    public ZoomLevelFeature createZoomLevelFeature(@NonNull CameraProperties cameraProperties) {
+      return mockZoomLevelFeature;
+    }
+
+    @Override
+    public ExposurePointFeature createExposurePointFeature(
+        @NonNull CameraProperties cameraProperties,
+        @NonNull SensorOrientationFeature sensorOrientationFeature) {
+      return mockExposurePointFeature;
+    }
+
+    @Override
+    public NoiseReductionFeature createNoiseReductionFeature(
+        @NonNull CameraProperties cameraProperties) {
+      return mockNoiseReductionFeature;
+    }
+  }
+}
diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/resolution/ResolutionFeatureTest.java b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/resolution/ResolutionFeatureTest.java
index e09223d..957b57a 100644
--- a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/resolution/ResolutionFeatureTest.java
+++ b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/resolution/ResolutionFeatureTest.java
@@ -8,24 +8,33 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.when;
 
 import android.media.CamcorderProfile;
+import android.media.EncoderProfiles;
 import io.flutter.plugins.camera.CameraProperties;
+import java.util.List;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.MockedStatic;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
 
+@RunWith(RobolectricTestRunner.class)
 public class ResolutionFeatureTest {
   private static final String cameraName = "1";
-  private CamcorderProfile mockProfileLow;
+  private CamcorderProfile mockProfileLowLegacy;
+  private EncoderProfiles mockProfileLow;
   private MockedStatic<CamcorderProfile> mockedStaticProfile;
 
   @Before
-  public void before() {
+  @SuppressWarnings("deprecation")
+  public void beforeLegacy() {
     mockedStaticProfile = mockStatic(CamcorderProfile.class);
-    mockProfileLow = mock(CamcorderProfile.class);
-    CamcorderProfile mockProfile = mock(CamcorderProfile.class);
+    mockProfileLowLegacy = mock(CamcorderProfile.class);
+    CamcorderProfile mockProfileLegacy = mock(CamcorderProfile.class);
 
     mockedStaticProfile
         .when(() -> CamcorderProfile.hasProfile(1, CamcorderProfile.QUALITY_HIGH))
@@ -51,25 +60,58 @@
 
     mockedStaticProfile
         .when(() -> CamcorderProfile.get(1, CamcorderProfile.QUALITY_HIGH))
-        .thenReturn(mockProfile);
+        .thenReturn(mockProfileLegacy);
     mockedStaticProfile
         .when(() -> CamcorderProfile.get(1, CamcorderProfile.QUALITY_2160P))
-        .thenReturn(mockProfile);
+        .thenReturn(mockProfileLegacy);
     mockedStaticProfile
         .when(() -> CamcorderProfile.get(1, CamcorderProfile.QUALITY_1080P))
-        .thenReturn(mockProfile);
+        .thenReturn(mockProfileLegacy);
     mockedStaticProfile
         .when(() -> CamcorderProfile.get(1, CamcorderProfile.QUALITY_720P))
-        .thenReturn(mockProfile);
+        .thenReturn(mockProfileLegacy);
     mockedStaticProfile
         .when(() -> CamcorderProfile.get(1, CamcorderProfile.QUALITY_480P))
-        .thenReturn(mockProfile);
+        .thenReturn(mockProfileLegacy);
     mockedStaticProfile
         .when(() -> CamcorderProfile.get(1, CamcorderProfile.QUALITY_QVGA))
-        .thenReturn(mockProfile);
+        .thenReturn(mockProfileLegacy);
     mockedStaticProfile
         .when(() -> CamcorderProfile.get(1, CamcorderProfile.QUALITY_LOW))
+        .thenReturn(mockProfileLowLegacy);
+  }
+
+  public void before() {
+    mockProfileLow = mock(EncoderProfiles.class);
+    EncoderProfiles mockProfile = mock(EncoderProfiles.class);
+    EncoderProfiles.VideoProfile mockVideoProfile = mock(EncoderProfiles.VideoProfile.class);
+    List<EncoderProfiles.VideoProfile> mockVideoProfilesList = List.of(mockVideoProfile);
+
+    mockedStaticProfile
+        .when(() -> CamcorderProfile.getAll("1", CamcorderProfile.QUALITY_HIGH))
+        .thenReturn(mockProfile);
+    mockedStaticProfile
+        .when(() -> CamcorderProfile.getAll("1", CamcorderProfile.QUALITY_2160P))
+        .thenReturn(mockProfile);
+    mockedStaticProfile
+        .when(() -> CamcorderProfile.getAll("1", CamcorderProfile.QUALITY_1080P))
+        .thenReturn(mockProfile);
+    mockedStaticProfile
+        .when(() -> CamcorderProfile.getAll("1", CamcorderProfile.QUALITY_720P))
+        .thenReturn(mockProfile);
+    mockedStaticProfile
+        .when(() -> CamcorderProfile.getAll("1", CamcorderProfile.QUALITY_480P))
+        .thenReturn(mockProfile);
+    mockedStaticProfile
+        .when(() -> CamcorderProfile.getAll("1", CamcorderProfile.QUALITY_QVGA))
+        .thenReturn(mockProfile);
+    mockedStaticProfile
+        .when(() -> CamcorderProfile.getAll("1", CamcorderProfile.QUALITY_LOW))
         .thenReturn(mockProfileLow);
+
+    when(mockProfile.getVideoProfiles()).thenReturn(mockVideoProfilesList);
+    when(mockVideoProfile.getHeight()).thenReturn(100);
+    when(mockVideoProfile.getWidth()).thenReturn(100);
   }
 
   @After
@@ -116,6 +158,39 @@
     assertTrue(resolutionFeature.checkIsSupported());
   }
 
+  @Config(maxSdk = 30)
+  @SuppressWarnings("deprecation")
+  @Test
+  public void getBestAvailableCamcorderProfileForResolutionPreset_shouldFallThroughLegacy() {
+    mockedStaticProfile
+        .when(() -> CamcorderProfile.hasProfile(1, CamcorderProfile.QUALITY_HIGH))
+        .thenReturn(false);
+    mockedStaticProfile
+        .when(() -> CamcorderProfile.hasProfile(1, CamcorderProfile.QUALITY_2160P))
+        .thenReturn(false);
+    mockedStaticProfile
+        .when(() -> CamcorderProfile.hasProfile(1, CamcorderProfile.QUALITY_1080P))
+        .thenReturn(false);
+    mockedStaticProfile
+        .when(() -> CamcorderProfile.hasProfile(1, CamcorderProfile.QUALITY_720P))
+        .thenReturn(false);
+    mockedStaticProfile
+        .when(() -> CamcorderProfile.hasProfile(1, CamcorderProfile.QUALITY_480P))
+        .thenReturn(false);
+    mockedStaticProfile
+        .when(() -> CamcorderProfile.hasProfile(1, CamcorderProfile.QUALITY_QVGA))
+        .thenReturn(false);
+    mockedStaticProfile
+        .when(() -> CamcorderProfile.hasProfile(1, CamcorderProfile.QUALITY_LOW))
+        .thenReturn(true);
+
+    assertEquals(
+        mockProfileLowLegacy,
+        ResolutionFeature.getBestAvailableCamcorderProfileForResolutionPresetLegacy(
+            1, ResolutionPreset.max));
+  }
+
+  @Config(minSdk = 31)
   @Test
   public void getBestAvailableCamcorderProfileForResolutionPreset_shouldFallThrough() {
     mockedStaticProfile
@@ -146,45 +221,112 @@
             1, ResolutionPreset.max));
   }
 
+  @Config(maxSdk = 30)
+  @SuppressWarnings("deprecation")
   @Test
-  public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetMax() {
+  public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetMaxLegacy() {
     ResolutionFeature.computeBestPreviewSize(1, ResolutionPreset.max);
 
     mockedStaticProfile.verify(() -> CamcorderProfile.get(1, CamcorderProfile.QUALITY_720P));
   }
 
+  @Config(minSdk = 31)
   @Test
-  public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetUltraHigh() {
+  public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetMax() {
+    before();
+    ResolutionFeature.computeBestPreviewSize(1, ResolutionPreset.max);
+
+    mockedStaticProfile.verify(() -> CamcorderProfile.getAll("1", CamcorderProfile.QUALITY_720P));
+  }
+
+  @Config(maxSdk = 30)
+  @SuppressWarnings("deprecation")
+  @Test
+  public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetUltraHighLegacy() {
     ResolutionFeature.computeBestPreviewSize(1, ResolutionPreset.ultraHigh);
 
     mockedStaticProfile.verify(() -> CamcorderProfile.get(1, CamcorderProfile.QUALITY_720P));
   }
 
+  @Config(minSdk = 31)
   @Test
-  public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetVeryHigh() {
+  public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetUltraHigh() {
+    before();
+    ResolutionFeature.computeBestPreviewSize(1, ResolutionPreset.ultraHigh);
+
+    mockedStaticProfile.verify(() -> CamcorderProfile.getAll("1", CamcorderProfile.QUALITY_720P));
+  }
+
+  @Config(maxSdk = 30)
+  @SuppressWarnings("deprecation")
+  @Test
+  public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetVeryHighLegacy() {
     ResolutionFeature.computeBestPreviewSize(1, ResolutionPreset.veryHigh);
 
     mockedStaticProfile.verify(() -> CamcorderProfile.get(1, CamcorderProfile.QUALITY_720P));
   }
 
+  @Config(minSdk = 31)
+  @SuppressWarnings("deprecation")
   @Test
-  public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetHigh() {
+  public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetVeryHigh() {
+    before();
+    ResolutionFeature.computeBestPreviewSize(1, ResolutionPreset.veryHigh);
+
+    mockedStaticProfile.verify(() -> CamcorderProfile.getAll("1", CamcorderProfile.QUALITY_720P));
+  }
+
+  @Config(maxSdk = 30)
+  @SuppressWarnings("deprecation")
+  @Test
+  public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetHighLegacy() {
     ResolutionFeature.computeBestPreviewSize(1, ResolutionPreset.high);
 
     mockedStaticProfile.verify(() -> CamcorderProfile.get(1, CamcorderProfile.QUALITY_720P));
   }
 
+  @Config(minSdk = 31)
   @Test
-  public void computeBestPreviewSize_shouldUse480PWhenResolutionPresetMedium() {
+  public void computeBestPreviewSize_shouldUse720PWhenResolutionPresetHigh() {
+    before();
+    ResolutionFeature.computeBestPreviewSize(1, ResolutionPreset.high);
+
+    mockedStaticProfile.verify(() -> CamcorderProfile.getAll("1", CamcorderProfile.QUALITY_720P));
+  }
+
+  @Config(maxSdk = 30)
+  @SuppressWarnings("deprecation")
+  @Test
+  public void computeBestPreviewSize_shouldUse480PWhenResolutionPresetMediumLegacy() {
     ResolutionFeature.computeBestPreviewSize(1, ResolutionPreset.medium);
 
     mockedStaticProfile.verify(() -> CamcorderProfile.get(1, CamcorderProfile.QUALITY_480P));
   }
 
+  @Config(minSdk = 31)
   @Test
-  public void computeBestPreviewSize_shouldUseQVGAWhenResolutionPresetLow() {
+  public void computeBestPreviewSize_shouldUse480PWhenResolutionPresetMedium() {
+    before();
+    ResolutionFeature.computeBestPreviewSize(1, ResolutionPreset.medium);
+
+    mockedStaticProfile.verify(() -> CamcorderProfile.getAll("1", CamcorderProfile.QUALITY_480P));
+  }
+
+  @Config(maxSdk = 30)
+  @SuppressWarnings("deprecation")
+  @Test
+  public void computeBestPreviewSize_shouldUseQVGAWhenResolutionPresetLowLegacy() {
     ResolutionFeature.computeBestPreviewSize(1, ResolutionPreset.low);
 
     mockedStaticProfile.verify(() -> CamcorderProfile.get(1, CamcorderProfile.QUALITY_QVGA));
   }
+
+  @Config(minSdk = 31)
+  @Test
+  public void computeBestPreviewSize_shouldUseQVGAWhenResolutionPresetLow() {
+    before();
+    ResolutionFeature.computeBestPreviewSize(1, ResolutionPreset.low);
+
+    mockedStaticProfile.verify(() -> CamcorderProfile.getAll("1", CamcorderProfile.QUALITY_QVGA));
+  }
 }
diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManagerTest.java b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManagerTest.java
index 58f17cb..82449a1 100644
--- a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManagerTest.java
+++ b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManagerTest.java
@@ -36,6 +36,7 @@
   private DeviceOrientationManager deviceOrientationManager;
 
   @Before
+  @SuppressWarnings("deprecation")
   public void before() {
     mockActivity = mock(Activity.class);
     mockDartMessenger = mock(DartMessenger.class);
diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/media/MediaRecorderBuilderTest.java b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/media/MediaRecorderBuilderTest.java
index 5425409..6cc58ee 100644
--- a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/media/MediaRecorderBuilderTest.java
+++ b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/media/MediaRecorderBuilderTest.java
@@ -8,23 +8,42 @@
 import static org.mockito.Mockito.*;
 
 import android.media.CamcorderProfile;
+import android.media.EncoderProfiles;
 import android.media.MediaRecorder;
 import java.io.IOException;
 import java.lang.reflect.Constructor;
+import java.util.List;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.InOrder;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
 
+@RunWith(RobolectricTestRunner.class)
 public class MediaRecorderBuilderTest {
+  @Config(maxSdk = 30)
+  @SuppressWarnings("deprecation")
   @Test
-  public void ctor_test() {
+  public void ctor_testLegacy() {
     MediaRecorderBuilder builder =
         new MediaRecorderBuilder(CamcorderProfile.get(CamcorderProfile.QUALITY_1080P), "");
 
     assertNotNull(builder);
   }
 
+  @Config(minSdk = 31)
   @Test
-  public void build_shouldSetValuesInCorrectOrderWhenAudioIsDisabled() throws IOException {
+  public void ctor_test() {
+    MediaRecorderBuilder builder =
+        new MediaRecorderBuilder(CamcorderProfile.getAll("0", CamcorderProfile.QUALITY_1080P), "");
+
+    assertNotNull(builder);
+  }
+
+  @Config(maxSdk = 30)
+  @SuppressWarnings("deprecation")
+  @Test
+  public void build_shouldSetValuesInCorrectOrderWhenAudioIsDisabledLegacy() throws IOException {
     CamcorderProfile recorderProfile = getEmptyCamcorderProfile();
     MediaRecorderBuilder.MediaRecorderFactory mockFactory =
         mock(MediaRecorderBuilder.MediaRecorderFactory.class);
@@ -54,8 +73,67 @@
     inOrder.verify(recorder).prepare();
   }
 
+  @Config(minSdk = 31)
   @Test
-  public void build_shouldSetValuesInCorrectOrderWhenAudioIsEnabled() throws IOException {
+  public void build_shouldSetValuesInCorrectOrderWhenAudioIsDisabled() throws IOException {
+    EncoderProfiles recorderProfile = mock(EncoderProfiles.class);
+    List<EncoderProfiles.VideoProfile> mockVideoProfiles =
+        List.of(mock(EncoderProfiles.VideoProfile.class));
+    List<EncoderProfiles.AudioProfile> mockAudioProfiles =
+        List.of(mock(EncoderProfiles.AudioProfile.class));
+    MediaRecorderBuilder.MediaRecorderFactory mockFactory =
+        mock(MediaRecorderBuilder.MediaRecorderFactory.class);
+    MediaRecorder mockMediaRecorder = mock(MediaRecorder.class);
+    String outputFilePath = "mock_video_file_path";
+    int mediaOrientation = 1;
+    MediaRecorderBuilder builder =
+        new MediaRecorderBuilder(recorderProfile, outputFilePath, mockFactory)
+            .setEnableAudio(false)
+            .setMediaOrientation(mediaOrientation);
+
+    when(mockFactory.makeMediaRecorder()).thenReturn(mockMediaRecorder);
+    when(recorderProfile.getVideoProfiles()).thenReturn(mockVideoProfiles);
+    when(recorderProfile.getAudioProfiles()).thenReturn(mockAudioProfiles);
+
+    MediaRecorder recorder = builder.build();
+
+    EncoderProfiles.VideoProfile videoProfile = mockVideoProfiles.get(0);
+
+    InOrder inOrder = inOrder(recorder);
+    inOrder.verify(recorder).setVideoSource(MediaRecorder.VideoSource.SURFACE);
+    inOrder.verify(recorder).setOutputFormat(recorderProfile.getRecommendedFileFormat());
+    inOrder.verify(recorder).setVideoEncoder(videoProfile.getCodec());
+    inOrder.verify(recorder).setVideoEncodingBitRate(videoProfile.getBitrate());
+    inOrder.verify(recorder).setVideoFrameRate(videoProfile.getFrameRate());
+    inOrder.verify(recorder).setVideoSize(videoProfile.getWidth(), videoProfile.getHeight());
+    inOrder.verify(recorder).setOutputFile(outputFilePath);
+    inOrder.verify(recorder).setOrientationHint(mediaOrientation);
+    inOrder.verify(recorder).prepare();
+  }
+
+  @Config(minSdk = 31)
+  @Test(expected = IndexOutOfBoundsException.class)
+  public void build_shouldThrowExceptionWithoutVideoOrAudioProfiles() throws IOException {
+    EncoderProfiles recorderProfile = mock(EncoderProfiles.class);
+    MediaRecorderBuilder.MediaRecorderFactory mockFactory =
+        mock(MediaRecorderBuilder.MediaRecorderFactory.class);
+    MediaRecorder mockMediaRecorder = mock(MediaRecorder.class);
+    String outputFilePath = "mock_video_file_path";
+    int mediaOrientation = 1;
+    MediaRecorderBuilder builder =
+        new MediaRecorderBuilder(recorderProfile, outputFilePath, mockFactory)
+            .setEnableAudio(false)
+            .setMediaOrientation(mediaOrientation);
+
+    when(mockFactory.makeMediaRecorder()).thenReturn(mockMediaRecorder);
+
+    MediaRecorder recorder = builder.build();
+  }
+
+  @Config(maxSdk = 30)
+  @SuppressWarnings("deprecation")
+  @Test
+  public void build_shouldSetValuesInCorrectOrderWhenAudioIsEnabledLegacy() throws IOException {
     CamcorderProfile recorderProfile = getEmptyCamcorderProfile();
     MediaRecorderBuilder.MediaRecorderFactory mockFactory =
         mock(MediaRecorderBuilder.MediaRecorderFactory.class);
@@ -89,6 +167,49 @@
     inOrder.verify(recorder).prepare();
   }
 
+  @Config(minSdk = 31)
+  @Test
+  public void build_shouldSetValuesInCorrectOrderWhenAudioIsEnabled() throws IOException {
+    EncoderProfiles recorderProfile = mock(EncoderProfiles.class);
+    List<EncoderProfiles.VideoProfile> mockVideoProfiles =
+        List.of(mock(EncoderProfiles.VideoProfile.class));
+    List<EncoderProfiles.AudioProfile> mockAudioProfiles =
+        List.of(mock(EncoderProfiles.AudioProfile.class));
+    MediaRecorderBuilder.MediaRecorderFactory mockFactory =
+        mock(MediaRecorderBuilder.MediaRecorderFactory.class);
+    MediaRecorder mockMediaRecorder = mock(MediaRecorder.class);
+    String outputFilePath = "mock_video_file_path";
+    int mediaOrientation = 1;
+    MediaRecorderBuilder builder =
+        new MediaRecorderBuilder(recorderProfile, outputFilePath, mockFactory)
+            .setEnableAudio(true)
+            .setMediaOrientation(mediaOrientation);
+
+    when(mockFactory.makeMediaRecorder()).thenReturn(mockMediaRecorder);
+    when(recorderProfile.getVideoProfiles()).thenReturn(mockVideoProfiles);
+    when(recorderProfile.getAudioProfiles()).thenReturn(mockAudioProfiles);
+
+    MediaRecorder recorder = builder.build();
+
+    EncoderProfiles.VideoProfile videoProfile = mockVideoProfiles.get(0);
+    EncoderProfiles.AudioProfile audioProfile = mockAudioProfiles.get(0);
+
+    InOrder inOrder = inOrder(recorder);
+    inOrder.verify(recorder).setAudioSource(MediaRecorder.AudioSource.MIC);
+    inOrder.verify(recorder).setVideoSource(MediaRecorder.VideoSource.SURFACE);
+    inOrder.verify(recorder).setOutputFormat(recorderProfile.getRecommendedFileFormat());
+    inOrder.verify(recorder).setAudioEncoder(audioProfile.getCodec());
+    inOrder.verify(recorder).setAudioEncodingBitRate(audioProfile.getBitrate());
+    inOrder.verify(recorder).setAudioSamplingRate(audioProfile.getSampleRate());
+    inOrder.verify(recorder).setVideoEncoder(videoProfile.getCodec());
+    inOrder.verify(recorder).setVideoEncodingBitRate(videoProfile.getBitrate());
+    inOrder.verify(recorder).setVideoFrameRate(videoProfile.getFrameRate());
+    inOrder.verify(recorder).setVideoSize(videoProfile.getWidth(), videoProfile.getHeight());
+    inOrder.verify(recorder).setOutputFile(outputFilePath);
+    inOrder.verify(recorder).setOrientationHint(mediaOrientation);
+    inOrder.verify(recorder).prepare();
+  }
+
   private CamcorderProfile getEmptyCamcorderProfile() {
     try {
       Constructor<CamcorderProfile> constructor =
diff --git a/packages/camera/camera/android/src/test/resources/robolectric.properties b/packages/camera/camera/android/src/test/resources/robolectric.properties
new file mode 100644
index 0000000..90fbd74
--- /dev/null
+++ b/packages/camera/camera/android/src/test/resources/robolectric.properties
@@ -0,0 +1 @@
+sdk=30
\ No newline at end of file
diff --git a/packages/camera/camera/example/android/app/build.gradle b/packages/camera/camera/example/android/app/build.gradle
index 7d0e281..476d653 100644
--- a/packages/camera/camera/example/android/app/build.gradle
+++ b/packages/camera/camera/example/android/app/build.gradle
@@ -25,7 +25,7 @@
 apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
 
 android {
-    compileSdkVersion 29
+    compileSdkVersion 31
 
     lintOptions {
         disable 'InvalidPackage'
diff --git a/packages/camera/camera/example/android/gradle.properties b/packages/camera/camera/example/android/gradle.properties
index a673820..b253d8e 100644
--- a/packages/camera/camera/example/android/gradle.properties
+++ b/packages/camera/camera/example/android/gradle.properties
@@ -1,4 +1,4 @@
 org.gradle.jvmargs=-Xmx1536M
 android.useAndroidX=true
-android.enableJetifier=true
+android.enableJetifier=false
 android.enableR8=true