From 9bc2ff9988de204264f15ee84e696ad12811c387 Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Thu, 4 Apr 2024 13:27:58 -0700 Subject: [PATCH 01/10] Migrate camera from SurfaceTexture to SurfaceProducer. --- .../java/io/flutter/plugins/camera/Camera.java | 14 +++++++------- .../plugins/camera/MethodCallHandlerImpl.java | 9 ++++----- .../java/io/flutter/plugins/camera/CameraTest.java | 12 ++++++------ 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 13dd0d74b122..063579a59e57 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -60,6 +60,7 @@ import io.flutter.plugins.camera.media.MediaRecorderBuilder; import io.flutter.plugins.camera.types.CameraCaptureProperties; import io.flutter.plugins.camera.types.CaptureTimeoutsWrapper; +import io.flutter.view.TextureRegistry; import io.flutter.view.TextureRegistry.SurfaceTextureEntry; import java.io.File; import java.io.IOException; @@ -110,7 +111,7 @@ class Camera */ private int initialCameraFacing; - private final SurfaceTextureEntry flutterTexture; + private final TextureRegistry.SurfaceProducer surfaceProducer; private final ResolutionPreset resolutionPreset; private final boolean enableAudio; private final Context applicationContext; @@ -187,7 +188,7 @@ public void close() { public Camera( final Activity activity, - final SurfaceTextureEntry flutterTexture, + final TextureRegistry.SurfaceProducer surfaceProducer, final CameraFeatureFactory cameraFeatureFactory, final DartMessenger dartMessenger, final CameraProperties cameraProperties, @@ -199,7 +200,7 @@ public Camera( } this.activity = activity; this.enableAudio = enableAudio; - this.flutterTexture = flutterTexture; + this.surfaceProducer = surfaceProducer; this.dartMessenger = dartMessenger; this.applicationContext = activity.getApplicationContext(); this.cameraProperties = cameraProperties; @@ -403,11 +404,10 @@ private void createCaptureSession( // Build Flutter surface to render to. ResolutionFeature resolutionFeature = cameraFeatures.getResolution(); - SurfaceTexture surfaceTexture = flutterTexture.surfaceTexture(); - surfaceTexture.setDefaultBufferSize( + surfaceProducer.setSize( resolutionFeature.getPreviewSize().getWidth(), resolutionFeature.getPreviewSize().getHeight()); - Surface flutterSurface = new Surface(surfaceTexture); + Surface flutterSurface = surfaceProducer.getSurface(); previewRequestBuilder.addTarget(flutterSurface); List remainingSurfaces = Arrays.asList(surfaces); @@ -1329,7 +1329,7 @@ public void dispose() { Log.i(TAG, "dispose"); close(); - flutterTexture.release(); + surfaceProducer.release(); getDeviceOrientationManager().stop(); } diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java index aad62bbaba85..0e0946edc86c 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java @@ -389,11 +389,10 @@ private void instantiateCamera(MethodCall call, Result result) throws CameraAcce String preset = call.argument("resolutionPreset"); boolean enableAudio = call.argument("enableAudio"); - TextureRegistry.SurfaceTextureEntry flutterSurfaceTexture = - textureRegistry.createSurfaceTexture(); + TextureRegistry.SurfaceProducer surfaceProducer = textureRegistry.createSurfaceProducer(); DartMessenger dartMessenger = new DartMessenger( - messenger, flutterSurfaceTexture.id(), new Handler(Looper.getMainLooper())); + messenger, surfaceProducer.id(), new Handler(Looper.getMainLooper())); CameraProperties cameraProperties = new CameraPropertiesImpl(cameraName, CameraUtils.getCameraManager(activity)); ResolutionPreset resolutionPreset = ResolutionPreset.valueOf(preset); @@ -401,7 +400,7 @@ private void instantiateCamera(MethodCall call, Result result) throws CameraAcce camera = new Camera( activity, - flutterSurfaceTexture, + surfaceProducer, new CameraFeatureFactoryImpl(), dartMessenger, cameraProperties, @@ -409,7 +408,7 @@ private void instantiateCamera(MethodCall call, Result result) throws CameraAcce enableAudio); Map reply = new HashMap<>(); - reply.put("cameraId", flutterSurfaceTexture.id()); + reply.put("cameraId", surfaceProducer.id()); result.success(reply); } diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java index 7fdfa24d99fe..0baffc824b1d 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java @@ -122,8 +122,8 @@ public void before() { mockHandler = mock(Handler.class); final Activity mockActivity = mock(Activity.class); - final TextureRegistry.SurfaceTextureEntry mockFlutterTexture = - mock(TextureRegistry.SurfaceTextureEntry.class); + final TextureRegistry.SurfaceProducer mockSurfaceProducer = + mock(TextureRegistry.SurfaceProducer.class); final String cameraName = "1"; final ResolutionPreset resolutionPreset = ResolutionPreset.high; final boolean enableAudio = false; @@ -137,7 +137,7 @@ public void before() { camera = new Camera( mockActivity, - mockFlutterTexture, + mockSurfaceProducer, mockCameraFeatureFactory, mockDartMessenger, mockCameraProperties, @@ -165,8 +165,8 @@ public void shouldNotImplementLifecycleObserverInterface() { @Test public void shouldCreateCameraPluginAndSetAllFeatures() { final Activity mockActivity = mock(Activity.class); - final TextureRegistry.SurfaceTextureEntry mockFlutterTexture = - mock(TextureRegistry.SurfaceTextureEntry.class); + final TextureRegistry.SurfaceProducer mockSurfaceProducer = + mock(TextureRegistry.SurfaceProducer.class); final CameraFeatureFactory mockCameraFeatureFactory = mock(CameraFeatureFactory.class); final String cameraName = "1"; final ResolutionPreset resolutionPreset = ResolutionPreset.high; @@ -180,7 +180,7 @@ public void shouldCreateCameraPluginAndSetAllFeatures() { Camera camera = new Camera( mockActivity, - mockFlutterTexture, + mockSurfaceProducer, mockCameraFeatureFactory, mockDartMessenger, mockCameraProperties, From 0e9666ad476cf98ec51ef31a5460d1c8dd3c67fd Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Thu, 4 Apr 2024 14:05:33 -0700 Subject: [PATCH 02/10] Migrate test. --- .../plugins/camera/CameraTest_getRecordingProfileTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest_getRecordingProfileTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest_getRecordingProfileTest.java index 04bab14f26ac..d0d6ff9073d2 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest_getRecordingProfileTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest_getRecordingProfileTest.java @@ -60,15 +60,15 @@ public void before() { mockDartMessenger = mock(DartMessenger.class); final Activity mockActivity = mock(Activity.class); - final TextureRegistry.SurfaceTextureEntry mockFlutterTexture = - mock(TextureRegistry.SurfaceTextureEntry.class); + final TextureRegistry.SurfaceProducer mockSurfaceProducer = + mock(TextureRegistry.SurfaceProducer.class); final ResolutionPreset resolutionPreset = ResolutionPreset.high; final boolean enableAudio = false; camera = new Camera( mockActivity, - mockFlutterTexture, + mockSurfaceProducer, mockCameraFeatureFactory, mockDartMessenger, mockCameraProperties, From 2eeeedf140f6fd706c78e5e6da37d8f1844880e3 Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Thu, 4 Apr 2024 14:38:51 -0700 Subject: [PATCH 03/10] Remove private field check that no longer exists. --- .../src/test/java/io/flutter/plugins/camera/CameraTest.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java index 0baffc824b1d..c6df3888f283 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java @@ -1123,15 +1123,10 @@ public void createCaptureSession_shouldNotAddPictureImageSurfaceToPreviewRequest TestUtils.setPrivateField(camera, "pictureImageReader", mockPictureImageReader); CaptureRequest.Builder mockPreviewRequestBuilder = mock(CaptureRequest.Builder.class); - TextureRegistry.SurfaceTextureEntry cameraFlutterTexture = - (TextureRegistry.SurfaceTextureEntry) TestUtils.getPrivateField(camera, "flutterTexture"); - CameraFeatures cameraFeatures = - (CameraFeatures) TestUtils.getPrivateField(camera, "cameraFeatures"); ResolutionFeature resolutionFeature = (ResolutionFeature) TestUtils.getPrivateField(mockCameraFeatureFactory, "mockResolutionFeature"); - when(cameraFlutterTexture.surfaceTexture()).thenReturn(mockSurfaceTexture); when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); when(fakeCamera.createCaptureRequest(anyInt())).thenReturn(mockPreviewRequestBuilder); when(mockPictureImageReader.getSurface()).thenReturn(mockSurface); From dbcafff147c8d9c20297ce13a5b1b9182799b568 Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Mon, 8 Apr 2024 14:37:13 -0700 Subject: [PATCH 04/10] ++ --- .../src/test/java/io/flutter/plugins/camera/CameraTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java index 424e963b6045..832ef8b61809 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java @@ -1168,8 +1168,8 @@ public void close_doesNotCloseCaptureSessionWhenCameraDeviceNonNull() { public void startVideoRecording_shouldApplySettingsToMediaRecorder() throws InterruptedException, IOException, CameraAccessException { final Activity mockActivity = mock(Activity.class); - final TextureRegistry.SurfaceTextureEntry mockFlutterTexture = - mock(TextureRegistry.SurfaceTextureEntry.class); + final TextureRegistry.SurfaceProducer mockSurfaceProducer = + mock(TextureRegistry.SurfaceProducer.class); final String cameraName = "1"; final ResolutionPreset resolutionPreset = ResolutionPreset.high; final boolean enableAudio = true; @@ -1207,7 +1207,7 @@ public void startVideoRecording_shouldApplySettingsToMediaRecorder() final FpsRangeFeature fpsRangeFeature = new FpsRangeFeature(mockCameraProperties); - final Camera camera = spy(new Camera(mockActivity, mockFlutterTexture, + final Camera camera = spy(new Camera(mockActivity, mockSurfaceProducer, mockCameraFeatureFactory, mockDartMessenger, mockCameraProperties, parameters)); final CamcorderProfile mockProfileLegacy = mock(CamcorderProfile.class); From 5bebf83a7f50708c8aab881420d404289b096244 Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Thu, 16 May 2024 13:07:08 -0700 Subject: [PATCH 05/10] Address merge conflicts. --- .../io/flutter/plugins/camera/Camera.java | 2 +- .../io/flutter/plugins/camera/CameraTest.java | 29 ------------------- 2 files changed, 1 insertion(+), 30 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index b6475d956cbe..972c7d3641ac 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -113,7 +113,7 @@ class Camera implements CameraCaptureCallback.CameraCaptureStateListener, */ @VisibleForTesting int initialCameraFacing; - private final TextureRegistry.SurfaceProducer surfaceProducer; + @VisibleForTesting final TextureRegistry.SurfaceProducer surfaceProducer; private final VideoCaptureSettings videoCaptureSettings; private final Context applicationContext; final DartMessenger dartMessenger; diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java index ee20e006b3d0..9c8071ad264a 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java @@ -32,7 +32,6 @@ import io.flutter.plugin.common.EventChannel; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugins.camera.features.CameraFeatureFactory; -import io.flutter.plugins.camera.features.CameraFeatures; import io.flutter.plugins.camera.features.Point; import io.flutter.plugins.camera.features.autofocus.AutoFocusFeature; import io.flutter.plugins.camera.features.autofocus.FocusMode; @@ -663,7 +662,6 @@ public void startPreview_shouldPullStreamFromVideoRenderer() VideoRenderer mockVideoRenderer = mock(VideoRenderer.class); ArrayList mockRequestBuilders = new ArrayList<>(); mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); - SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class); Size mockSize = mock(Size.class); camera.recordingVideo = true; camera.videoRenderer = mockVideoRenderer; @@ -672,10 +670,8 @@ public void startPreview_shouldPullStreamFromVideoRenderer() ImageReader mockPictureImageReader = mock(ImageReader.class); camera.pictureImageReader = mockPictureImageReader; - TextureRegistry.SurfaceTextureEntry cameraFlutterTexture = camera.flutterTexture; ResolutionFeature resolutionFeature = mockCameraFeatureFactory.mockResolutionFeature; - when(cameraFlutterTexture.surfaceTexture()).thenReturn(mockSurfaceTexture); when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); camera.startPreview(); @@ -688,7 +684,6 @@ public void startPreview_shouldPullStreamFromImageReader() throws InterruptedException, CameraAccessException { ArrayList mockRequestBuilders = new ArrayList<>(); mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); - SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class); Size mockSize = mock(Size.class); ImageReader mockImageReader = mock(ImageReader.class); camera.recordingVideo = false; @@ -696,10 +691,8 @@ public void startPreview_shouldPullStreamFromImageReader() CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders); camera.cameraDevice = fakeCamera; - TextureRegistry.SurfaceTextureEntry cameraFlutterTexture = camera.flutterTexture; ResolutionFeature resolutionFeature = mockCameraFeatureFactory.mockResolutionFeature; - when(cameraFlutterTexture.surfaceTexture()).thenReturn(mockSurfaceTexture); when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); when(mockImageReader.getSurface()).thenReturn(mock(Surface.class)); @@ -713,7 +706,6 @@ public void startPreview_shouldFlipRotation() throws InterruptedException, Camer VideoRenderer mockVideoRenderer = mock(VideoRenderer.class); ArrayList mockRequestBuilders = new ArrayList<>(); mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); - SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class); Size mockSize = mock(Size.class); camera.recordingVideo = true; camera.videoRenderer = mockVideoRenderer; @@ -723,10 +715,8 @@ public void startPreview_shouldFlipRotation() throws InterruptedException, Camer ImageReader mockPictureImageReader = mock(ImageReader.class); camera.pictureImageReader = mockPictureImageReader; - TextureRegistry.SurfaceTextureEntry cameraFlutterTexture = camera.flutterTexture; ResolutionFeature resolutionFeature = mockCameraFeatureFactory.mockResolutionFeature; - when(cameraFlutterTexture.surfaceTexture()).thenReturn(mockSurfaceTexture); when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); when(mockCameraProperties.getLensFacing()).thenReturn(CameraMetadata.LENS_FACING_FRONT); @@ -749,10 +739,8 @@ public void startPreviewWithImageStream_shouldPullStreamsFromImageReaders() camera.cameraDevice = fakeCamera; camera.imageStreamReader = mockImageStreamReader; - TextureRegistry.SurfaceTextureEntry cameraFlutterTexture = camera.flutterTexture; ResolutionFeature resolutionFeature = mockCameraFeatureFactory.mockResolutionFeature; - when(cameraFlutterTexture.surfaceTexture()).thenReturn(mockSurfaceTexture); when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); camera.startPreviewWithImageStream(mock(EventChannel.class)); @@ -868,7 +856,6 @@ public void startVideoRecording_shouldPullStreamsFromMediaRecorderAndImageReader Camera cameraSpy = spy(camera); ArrayList mockRequestBuilders = new ArrayList<>(); mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); - SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class); Size mockSize = mock(Size.class); MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); ImageReader mockPictureImageReader = mock(ImageReader.class); @@ -879,10 +866,8 @@ public void startVideoRecording_shouldPullStreamsFromMediaRecorderAndImageReader cameraSpy.cameraDevice = fakeCamera; MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - TextureRegistry.SurfaceTextureEntry cameraFlutterTexture = cameraSpy.flutterTexture; ResolutionFeature resolutionFeature = mockCameraFeatureFactory.mockResolutionFeature; - when(cameraFlutterTexture.surfaceTexture()).thenReturn(mockSurfaceTexture); when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); doNothing().when(cameraSpy).prepareRecording(mockResult); @@ -1077,19 +1062,14 @@ public void onConverge_shouldTakePictureWithoutAbortingSession() throws CameraAc @Test public void createCaptureSession_doesNotCloseCaptureSession() throws CameraAccessException { Surface mockSurface = mock(Surface.class); - SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class); - ResolutionFeature mockResolutionFeature = mock(ResolutionFeature.class); Size mockSize = mock(Size.class); ArrayList mockRequestBuilders = new ArrayList<>(); mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders); camera.cameraDevice = fakeCamera; - TextureRegistry.SurfaceTextureEntry cameraFlutterTexture = camera.flutterTexture; - CameraFeatures cameraFeatures = camera.cameraFeatures; ResolutionFeature resolutionFeature = mockCameraFeatureFactory.mockResolutionFeature; - when(cameraFlutterTexture.surfaceTexture()).thenReturn(mockSurfaceTexture); when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); camera.createCaptureSession(CameraDevice.TEMPLATE_PREVIEW, mockSurface); @@ -1102,8 +1082,6 @@ public void createCaptureSession_shouldNotAddPictureImageSurfaceToPreviewRequest throws CameraAccessException { Surface mockSurface = mock(Surface.class); Surface mockSecondarySurface = mock(Surface.class); - SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class); - ResolutionFeature mockResolutionFeature = mock(ResolutionFeature.class); Size mockSize = mock(Size.class); ArrayList mockRequestBuilders = new ArrayList<>(); mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); @@ -1113,8 +1091,6 @@ public void createCaptureSession_shouldNotAddPictureImageSurfaceToPreviewRequest camera.pictureImageReader = mockPictureImageReader; CaptureRequest.Builder mockPreviewRequestBuilder = mock(CaptureRequest.Builder.class); - TextureRegistry.SurfaceTextureEntry cameraFlutterTexture = camera.flutterTexture; - CameraFeatures cameraFeatures = camera.cameraFeatures; ResolutionFeature resolutionFeature = mockCameraFeatureFactory.mockResolutionFeature; when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); @@ -1223,13 +1199,8 @@ public void startVideoRecording_shouldApplySettingsToMediaRecorder() camera.cameraDevice = fakeCamera; MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - TextureRegistry.SurfaceTextureEntry cameraFlutterTexture = camera.flutterTexture; - ResolutionFeature resolutionFeature = mockCameraFeatureFactory.mockResolutionFeature; - assertNotNull(cameraFlutterTexture); - when(cameraFlutterTexture.surfaceTexture()).thenReturn(mockSurfaceTexture); - assertNotNull(resolutionFeature); when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); From 8394d9bf253c58613e25061e88bf1cb348db8178 Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Thu, 16 May 2024 13:12:04 -0700 Subject: [PATCH 06/10] + --- packages/camera/camera_android/CHANGELOG.md | 3 ++- packages/camera/camera_android/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index 74d8e31b8a48..f1714b24c5a7 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,6 +1,7 @@ ## NEXT -* Updates minimum supported SDK version to Flutter 3.16/Dart 3.2. +* Updates minimum supported SDK version to Flutter 3.22/Dart 3.2. +* [Supports Impeller](https://docs.flutter.dev/release/breaking-changes/android-surface-plugins) ## 0.10.9+2 diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index 90d32f51d9c7..c71bb316a1e9 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -7,7 +7,7 @@ version: 0.10.9+2 environment: sdk: ^3.2.0 - flutter: ">=3.16.0" + flutter: ">=3.22.0" flutter: plugin: From c9a57d780de5ee3e045b5ed6416e0866928f371d Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Fri, 24 May 2024 10:01:29 -0700 Subject: [PATCH 07/10] formatting --- packages/camera/camera_android/CHANGELOG.md | 4 +- .../io/flutter/plugins/camera/Camera.java | 475 +++--- .../plugins/camera/MethodCallHandlerImpl.java | 3 +- .../io/flutter/plugins/camera/CameraTest.java | 133 +- .../plugins/camera/CameraTest.java.orig | 1364 +++++++++++++++++ packages/camera/camera_android/pubspec.yaml | 4 +- 6 files changed, 1730 insertions(+), 253 deletions(-) create mode 100644 packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java.orig diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index f1714b24c5a7..9fe40dfb42f9 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,4 +1,4 @@ -## NEXT +## 0.10.10 * Updates minimum supported SDK version to Flutter 3.22/Dart 3.2. * [Supports Impeller](https://docs.flutter.dev/release/breaking-changes/android-surface-plugins) @@ -36,7 +36,7 @@ ## 0.10.8+14 -* Fixes `pausePreview` null pointer error. `pausePreview` should not be called +* Fixes `pausePreview` null pointer error. `pausePreview` should not be called when camera is closed or not configured. * Updates minimum supported SDK version to Flutter 3.10/Dart 3.0. diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 972c7d3641ac..52c5e59f1815 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -9,7 +9,6 @@ import android.app.Activity; import android.content.Context; import android.graphics.ImageFormat; -import android.graphics.SurfaceTexture; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraDevice; @@ -64,7 +63,6 @@ import io.flutter.plugins.camera.types.CameraCaptureProperties; import io.flutter.plugins.camera.types.CaptureTimeoutsWrapper; import io.flutter.view.TextureRegistry; -import io.flutter.view.TextureRegistry.SurfaceTextureEntry; import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -79,8 +77,9 @@ interface ErrorCallback { void onError(String errorCode, String errorMessage); } -class Camera implements CameraCaptureCallback.CameraCaptureStateListener, - ImageReader.OnImageAvailableListener { +class Camera + implements CameraCaptureCallback.CameraCaptureStateListener, + ImageReader.OnImageAvailableListener { private static final String TAG = "Camera"; private static final HashMap supportedImageFormats; @@ -173,8 +172,10 @@ public void createCaptureSession(SessionConfiguration config) throws CameraAcces @SuppressWarnings("deprecation") @Override - public void createCaptureSession(@NonNull List outputs, - @NonNull CameraCaptureSession.StateCallback callback, @Nullable Handler handler) + public void createCaptureSession( + @NonNull List outputs, + @NonNull CameraCaptureSession.StateCallback callback, + @Nullable Handler handler) throws CameraAccessException { cameraDevice.createCaptureSession(outputs, callback, backgroundHandler); } @@ -192,8 +193,12 @@ public static class VideoCaptureSettings { @Nullable public final Integer videoBitrate; @Nullable public final Integer audioBitrate; - public VideoCaptureSettings(@NonNull ResolutionPreset resolutionPreset, boolean enableAudio, - @Nullable Integer fps, @Nullable Integer videoBitrate, @Nullable Integer audioBitrate) { + public VideoCaptureSettings( + @NonNull ResolutionPreset resolutionPreset, + boolean enableAudio, + @Nullable Integer fps, + @Nullable Integer videoBitrate, + @Nullable Integer audioBitrate) { this.resolutionPreset = resolutionPreset; this.enableAudio = enableAudio; this.fps = fps; @@ -206,9 +211,13 @@ public VideoCaptureSettings(@NonNull ResolutionPreset resolutionPreset, boolean } } - public Camera(final Activity activity, final TextureRegistry.SurfaceProducer surfaceProducer, - final CameraFeatureFactory cameraFeatureFactory, final DartMessenger dartMessenger, - final CameraProperties cameraProperties, final VideoCaptureSettings videoCaptureSettings) { + public Camera( + final Activity activity, + final TextureRegistry.SurfaceProducer surfaceProducer, + final CameraFeatureFactory cameraFeatureFactory, + final DartMessenger dartMessenger, + final CameraProperties cameraProperties, + final VideoCaptureSettings videoCaptureSettings) { if (activity == null) { throw new IllegalStateException("No activity available!"); } @@ -219,8 +228,13 @@ public Camera(final Activity activity, final TextureRegistry.SurfaceProducer sur this.cameraProperties = cameraProperties; this.cameraFeatureFactory = cameraFeatureFactory; this.videoCaptureSettings = videoCaptureSettings; - this.cameraFeatures = CameraFeatures.init(cameraFeatureFactory, cameraProperties, activity, - dartMessenger, videoCaptureSettings.resolutionPreset); + this.cameraFeatures = + CameraFeatures.init( + cameraFeatureFactory, + cameraProperties, + activity, + dartMessenger, + videoCaptureSettings.resolutionPreset); Integer recordingFps = null; @@ -293,18 +307,30 @@ private void prepareMediaRecorder(String outputFilePath) throws IOException { // null once this has largely been fixed on the Android side. // https://github.com/flutter/flutter/issues/119668 if (SdkCapabilityChecker.supportsEncoderProfiles() && getRecordingProfile() != null) { - mediaRecorderBuilder = new MediaRecorderBuilder(getRecordingProfile(), - new MediaRecorderBuilder.RecordingParameters(outputFilePath, videoCaptureSettings.fps, - videoCaptureSettings.videoBitrate, videoCaptureSettings.audioBitrate)); + mediaRecorderBuilder = + new MediaRecorderBuilder( + getRecordingProfile(), + new MediaRecorderBuilder.RecordingParameters( + outputFilePath, + videoCaptureSettings.fps, + videoCaptureSettings.videoBitrate, + videoCaptureSettings.audioBitrate)); } else { - mediaRecorderBuilder = new MediaRecorderBuilder(getRecordingProfileLegacy(), - new MediaRecorderBuilder.RecordingParameters(outputFilePath, videoCaptureSettings.fps, - videoCaptureSettings.videoBitrate, videoCaptureSettings.audioBitrate)); + mediaRecorderBuilder = + new MediaRecorderBuilder( + getRecordingProfileLegacy(), + new MediaRecorderBuilder.RecordingParameters( + outputFilePath, + videoCaptureSettings.fps, + videoCaptureSettings.videoBitrate, + videoCaptureSettings.audioBitrate)); } mediaRecorder = - mediaRecorderBuilder.setEnableAudio(videoCaptureSettings.enableAudio) - .setMediaOrientation(lockedOrientation == null + mediaRecorderBuilder + .setEnableAudio(videoCaptureSettings.enableAudio) + .setMediaOrientation( + lockedOrientation == null ? getDeviceOrientationManager().getVideoOrientation() : getDeviceOrientationManager().getVideoOrientation(lockedOrientation)) .build(); @@ -319,14 +345,20 @@ public void open(String imageFormatGroup) throws CameraAccessException { // Tell the user that the camera they are trying to open is not supported, // as its {@link android.media.CamcorderProfile} cannot be fetched due to the name // not being a valid parsable integer. - dartMessenger.sendCameraErrorEvent("Camera with name \"" + cameraProperties.getCameraName() - + "\" is not supported by this plugin."); + dartMessenger.sendCameraErrorEvent( + "Camera with name \"" + + cameraProperties.getCameraName() + + "\" is not supported by this plugin."); return; } // Always capture using JPEG format. - pictureImageReader = ImageReader.newInstance(resolutionFeature.getCaptureSize().getWidth(), - resolutionFeature.getCaptureSize().getHeight(), ImageFormat.JPEG, 1); + pictureImageReader = + ImageReader.newInstance( + resolutionFeature.getCaptureSize().getWidth(), + resolutionFeature.getCaptureSize().getHeight(), + ImageFormat.JPEG, + 1); // For image streaming, use the provided image format or fall back to YUV420. Integer imageFormat = supportedImageFormats.get(imageFormatGroup); @@ -334,82 +366,90 @@ public void open(String imageFormatGroup) throws CameraAccessException { Log.w(TAG, "The selected imageFormatGroup is not supported by Android. Defaulting to yuv420"); imageFormat = ImageFormat.YUV_420_888; } - imageStreamReader = new ImageStreamReader(resolutionFeature.getPreviewSize().getWidth(), - resolutionFeature.getPreviewSize().getHeight(), imageFormat, 1); + imageStreamReader = + new ImageStreamReader( + resolutionFeature.getPreviewSize().getWidth(), + resolutionFeature.getPreviewSize().getHeight(), + imageFormat, + 1); // Open the camera. CameraManager cameraManager = CameraUtils.getCameraManager(activity); - cameraManager.openCamera(cameraProperties.getCameraName(), new CameraDevice.StateCallback() { - @Override - public void onOpened(@NonNull CameraDevice device) { - cameraDevice = new DefaultCameraDeviceWrapper(device); - try { - startPreview(); - if (!recordingVideo) { // only send initialization if we werent already recording and - // switching cameras - dartMessenger.sendCameraInitializedEvent(resolutionFeature.getPreviewSize().getWidth(), - resolutionFeature.getPreviewSize().getHeight(), - cameraFeatures.getExposureLock().getValue(), - cameraFeatures.getAutoFocus().getValue(), - cameraFeatures.getExposurePoint().checkIsSupported(), - cameraFeatures.getFocusPoint().checkIsSupported()); - } - } catch (Exception e) { - if (BuildConfig.DEBUG) { - Log.i(TAG, "open | onOpened error: " + e.getMessage()); + cameraManager.openCamera( + cameraProperties.getCameraName(), + new CameraDevice.StateCallback() { + @Override + public void onOpened(@NonNull CameraDevice device) { + cameraDevice = new DefaultCameraDeviceWrapper(device); + try { + startPreview(); + if (!recordingVideo) { // only send initialization if we werent already recording and + // switching cameras + dartMessenger.sendCameraInitializedEvent( + resolutionFeature.getPreviewSize().getWidth(), + resolutionFeature.getPreviewSize().getHeight(), + cameraFeatures.getExposureLock().getValue(), + cameraFeatures.getAutoFocus().getValue(), + cameraFeatures.getExposurePoint().checkIsSupported(), + cameraFeatures.getFocusPoint().checkIsSupported()); + } + } catch (Exception e) { + if (BuildConfig.DEBUG) { + Log.i(TAG, "open | onOpened error: " + e.getMessage()); + } + dartMessenger.sendCameraErrorEvent(e.getMessage()); + close(); + } } - dartMessenger.sendCameraErrorEvent(e.getMessage()); - close(); - } - } - - @Override - public void onClosed(@NonNull CameraDevice camera) { - Log.i(TAG, "open | onClosed"); - // Prevents calls to methods that would otherwise result in IllegalStateException - // exceptions. - cameraDevice = null; - closeCaptureSession(); - dartMessenger.sendCameraClosingEvent(); - } + @Override + public void onClosed(@NonNull CameraDevice camera) { + Log.i(TAG, "open | onClosed"); + + // Prevents calls to methods that would otherwise result in IllegalStateException + // exceptions. + cameraDevice = null; + closeCaptureSession(); + dartMessenger.sendCameraClosingEvent(); + } - @Override - public void onDisconnected(@NonNull CameraDevice cameraDevice) { - Log.i(TAG, "open | onDisconnected"); + @Override + public void onDisconnected(@NonNull CameraDevice cameraDevice) { + Log.i(TAG, "open | onDisconnected"); - close(); - dartMessenger.sendCameraErrorEvent("The camera was disconnected."); - } + close(); + dartMessenger.sendCameraErrorEvent("The camera was disconnected."); + } - @Override - public void onError(@NonNull CameraDevice cameraDevice, int errorCode) { - Log.i(TAG, "open | onError"); - - close(); - String errorDescription; - switch (errorCode) { - case ERROR_CAMERA_IN_USE: - errorDescription = "The camera device is in use already."; - break; - case ERROR_MAX_CAMERAS_IN_USE: - errorDescription = "Max cameras in use"; - break; - case ERROR_CAMERA_DISABLED: - errorDescription = "The camera device could not be opened due to a device policy."; - break; - case ERROR_CAMERA_DEVICE: - errorDescription = "The camera device has encountered a fatal error"; - break; - case ERROR_CAMERA_SERVICE: - errorDescription = "The camera service has encountered a fatal error."; - break; - default: - errorDescription = "Unknown camera error"; - } - dartMessenger.sendCameraErrorEvent(errorDescription); - } - }, backgroundHandler); + @Override + public void onError(@NonNull CameraDevice cameraDevice, int errorCode) { + Log.i(TAG, "open | onError"); + + close(); + String errorDescription; + switch (errorCode) { + case ERROR_CAMERA_IN_USE: + errorDescription = "The camera device is in use already."; + break; + case ERROR_MAX_CAMERAS_IN_USE: + errorDescription = "Max cameras in use"; + break; + case ERROR_CAMERA_DISABLED: + errorDescription = "The camera device could not be opened due to a device policy."; + break; + case ERROR_CAMERA_DEVICE: + errorDescription = "The camera device has encountered a fatal error"; + break; + case ERROR_CAMERA_SERVICE: + errorDescription = "The camera service has encountered a fatal error."; + break; + default: + errorDescription = "Unknown camera error"; + } + dartMessenger.sendCameraErrorEvent(errorDescription); + } + }, + backgroundHandler); } @VisibleForTesting @@ -417,8 +457,9 @@ void createCaptureSession(int templateType, Surface... surfaces) throws CameraAc createCaptureSession(templateType, null, surfaces); } - private void createCaptureSession(int templateType, Runnable onSuccessCallback, - Surface... surfaces) throws CameraAccessException { + private void createCaptureSession( + int templateType, Runnable onSuccessCallback, Surface... surfaces) + throws CameraAccessException { // Close any existing capture session. captureSession = null; @@ -427,7 +468,8 @@ private void createCaptureSession(int templateType, Runnable onSuccessCallback, // Build Flutter surface to render to. ResolutionFeature resolutionFeature = cameraFeatures.getResolution(); - surfaceProducer.setSize(resolutionFeature.getPreviewSize().getWidth(), + surfaceProducer.setSize( + resolutionFeature.getPreviewSize().getWidth(), resolutionFeature.getPreviewSize().getHeight()); Surface flutterSurface = surfaceProducer.getSurface(); previewRequestBuilder.addTarget(flutterSurface); @@ -453,38 +495,39 @@ private void createCaptureSession(int templateType, Runnable onSuccessCallback, cameraFeatures.getFocusPoint().setCameraBoundaries(cameraBoundaries); // Prepare the callback. - CameraCaptureSession.StateCallback callback = new CameraCaptureSession.StateCallback() { - boolean captureSessionClosed = false; - - @Override - public void onConfigured(@NonNull CameraCaptureSession session) { - Log.i(TAG, "CameraCaptureSession onConfigured"); - // Camera was already closed. - if (cameraDevice == null || captureSessionClosed) { - dartMessenger.sendCameraErrorEvent("The camera was closed during configuration."); - return; - } - captureSession = session; + CameraCaptureSession.StateCallback callback = + new CameraCaptureSession.StateCallback() { + boolean captureSessionClosed = false; - Log.i(TAG, "Updating builder settings"); - updateBuilderSettings(previewRequestBuilder); + @Override + public void onConfigured(@NonNull CameraCaptureSession session) { + Log.i(TAG, "CameraCaptureSession onConfigured"); + // Camera was already closed. + if (cameraDevice == null || captureSessionClosed) { + dartMessenger.sendCameraErrorEvent("The camera was closed during configuration."); + return; + } + captureSession = session; - refreshPreviewCaptureSession( - onSuccessCallback, (code, message) -> dartMessenger.sendCameraErrorEvent(message)); - } + Log.i(TAG, "Updating builder settings"); + updateBuilderSettings(previewRequestBuilder); - @Override - public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) { - Log.i(TAG, "CameraCaptureSession onConfigureFailed"); - dartMessenger.sendCameraErrorEvent("Failed to configure camera session."); - } + refreshPreviewCaptureSession( + onSuccessCallback, (code, message) -> dartMessenger.sendCameraErrorEvent(message)); + } - @Override - public void onClosed(@NonNull CameraCaptureSession session) { - Log.i(TAG, "CameraCaptureSession onClosed"); - captureSessionClosed = true; - } - }; + @Override + public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) { + Log.i(TAG, "CameraCaptureSession onConfigureFailed"); + dartMessenger.sendCameraErrorEvent("Failed to configure camera session."); + } + + @Override + public void onClosed(@NonNull CameraCaptureSession session) { + Log.i(TAG, "CameraCaptureSession onClosed"); + captureSessionClosed = true; + } + }; // Start the session. if (SdkCapabilityChecker.supportsSessionConfiguration()) { @@ -505,15 +548,21 @@ public void onClosed(@NonNull CameraCaptureSession session) { } @TargetApi(VERSION_CODES.P) - private void createCaptureSessionWithSessionConfig(List outputConfigs, - CameraCaptureSession.StateCallback callback) throws CameraAccessException { - cameraDevice.createCaptureSession(new SessionConfiguration(SessionConfiguration.SESSION_REGULAR, - outputConfigs, Executors.newSingleThreadExecutor(), callback)); + private void createCaptureSessionWithSessionConfig( + List outputConfigs, CameraCaptureSession.StateCallback callback) + throws CameraAccessException { + cameraDevice.createCaptureSession( + new SessionConfiguration( + SessionConfiguration.SESSION_REGULAR, + outputConfigs, + Executors.newSingleThreadExecutor(), + callback)); } @SuppressWarnings("deprecation") - private void createCaptureSession(List surfaces, - CameraCaptureSession.StateCallback callback) throws CameraAccessException { + private void createCaptureSession( + List surfaces, CameraCaptureSession.StateCallback callback) + throws CameraAccessException { cameraDevice.createCaptureSession(surfaces, callback, backgroundHandler); } @@ -523,7 +572,8 @@ void refreshPreviewCaptureSession( Log.i(TAG, "refreshPreviewCaptureSession"); if (captureSession == null) { - Log.i(TAG, + Log.i( + TAG, "refreshPreviewCaptureSession: captureSession not yet initialized, " + "skipping preview capture session refresh."); return; @@ -604,19 +654,22 @@ private void runPrecaptureSequence() { Log.i(TAG, "runPrecaptureSequence"); try { // First set precapture state to idle or else it can hang in STATE_WAITING_PRECAPTURE_START. - previewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, + previewRequestBuilder.set( + CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE); captureSession.capture( previewRequestBuilder.build(), cameraCaptureCallback, backgroundHandler); // Repeating request to refresh preview session. - refreshPreviewCaptureSession(null, + refreshPreviewCaptureSession( + null, (code, message) -> dartMessenger.error(flutterResult, "cameraAccess", message, null)); // Start precapture. cameraCaptureCallback.setCameraState(CameraState.STATE_WAITING_PRECAPTURE_START); - previewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, + previewRequestBuilder.set( + CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); // Trigger one capture to start AE sequence. @@ -650,7 +703,8 @@ private void takePictureAfterPrecapture() { stillBuilder.addTarget(pictureImageReader.getSurface()); // Zoom. - stillBuilder.set(CaptureRequest.SCALER_CROP_REGION, + stillBuilder.set( + CaptureRequest.SCALER_CROP_REGION, previewRequestBuilder.get(CaptureRequest.SCALER_CROP_REGION)); // Have all features update the builder. @@ -659,7 +713,8 @@ private void takePictureAfterPrecapture() { // Orientation. final PlatformChannel.DeviceOrientation lockedOrientation = cameraFeatures.getSensorOrientation().getLockedCaptureOrientation(); - stillBuilder.set(CaptureRequest.JPEG_ORIENTATION, + stillBuilder.set( + CaptureRequest.JPEG_ORIENTATION, lockedOrientation == null ? getDeviceOrientationManager().getPhotoOrientation() : getDeviceOrientationManager().getPhotoOrientation(lockedOrientation)); @@ -667,8 +722,10 @@ private void takePictureAfterPrecapture() { CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() { @Override - public void onCaptureCompleted(@NonNull CameraCaptureSession session, - @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { + public void onCaptureCompleted( + @NonNull CameraCaptureSession session, + @NonNull CaptureRequest request, + @NonNull TotalCaptureResult result) { unlockAutoFocus(); } }; @@ -759,9 +816,10 @@ void unlockAutoFocus() { return; } - refreshPreviewCaptureSession(null, - (errorCode, - errorMessage) -> dartMessenger.error(flutterResult, errorCode, errorMessage, null)); + refreshPreviewCaptureSession( + null, + (errorCode, errorMessage) -> + dartMessenger.error(flutterResult, errorCode, errorMessage, null)); } public void startVideoRecording( @@ -872,8 +930,8 @@ public void setFlashMode(@NonNull final Result result, @NonNull FlashMode newMod flashFeature.setValue(newMode); flashFeature.updateBuilder(previewRequestBuilder); - refreshPreviewCaptureSession(() - -> result.success(null), + refreshPreviewCaptureSession( + () -> result.success(null), (code, message) -> result.error("setFlashModeFailed", "Could not set flash mode.", null)); } @@ -888,10 +946,10 @@ public void setExposureMode(@NonNull final Result result, @NonNull ExposureMode exposureLockFeature.setValue(newMode); exposureLockFeature.updateBuilder(previewRequestBuilder); - refreshPreviewCaptureSession(() - -> result.success(null), - (code, message) - -> result.error("setExposureModeFailed", "Could not set exposure mode.", null)); + refreshPreviewCaptureSession( + () -> result.success(null), + (code, message) -> + result.error("setExposureModeFailed", "Could not set exposure mode.", null)); } /** @@ -905,10 +963,10 @@ public void setExposurePoint(@NonNull final Result result, @Nullable Point point exposurePointFeature.setValue(point); exposurePointFeature.updateBuilder(previewRequestBuilder); - refreshPreviewCaptureSession(() - -> result.success(null), - (code, message) - -> result.error("setExposurePointFailed", "Could not set exposure point.", null)); + refreshPreviewCaptureSession( + () -> result.success(null), + (code, message) -> + result.error("setExposurePointFailed", "Could not set exposure point.", null)); } /** Return the max exposure offset value supported by the camera to dart. */ @@ -989,8 +1047,8 @@ public void setFocusPoint(@NonNull final Result result, @Nullable Point point) { focusPointFeature.setValue(point); focusPointFeature.updateBuilder(previewRequestBuilder); - refreshPreviewCaptureSession(() - -> result.success(null), + refreshPreviewCaptureSession( + () -> result.success(null), (code, message) -> result.error("setFocusPointFailed", "Could not set focus point.", null)); this.setFocusMode(null, cameraFeatures.getAutoFocus().getValue()); @@ -1008,10 +1066,10 @@ public void setExposureOffset(@NonNull final Result result, double offset) { exposureOffsetFeature.setValue(offset); exposureOffsetFeature.updateBuilder(previewRequestBuilder); - refreshPreviewCaptureSession(() - -> result.success(exposureOffsetFeature.getValue()), - (code, message) - -> result.error("setExposureOffsetFailed", "Could not set exposure offset.", null)); + refreshPreviewCaptureSession( + () -> result.success(exposureOffsetFeature.getValue()), + (code, message) -> + result.error("setExposureOffsetFailed", "Could not set exposure offset.", null)); } public float getMaxZoomLevel() { @@ -1048,8 +1106,12 @@ public void setZoomLevel(@NonNull final Result result, float zoom) throws Camera float minZoom = zoomLevel.getMinimumZoomLevel(); 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); + 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; } @@ -1057,8 +1119,8 @@ public void setZoomLevel(@NonNull final Result result, float zoom) throws Camera zoomLevel.setValue(zoom); zoomLevel.updateBuilder(previewRequestBuilder); - refreshPreviewCaptureSession(() - -> result.success(null), + refreshPreviewCaptureSession( + () -> result.success(null), (code, message) -> result.error("setZoomLevelFailed", "Could not set zoom level.", null)); } @@ -1105,16 +1167,14 @@ public void startPreview() throws CameraAccessException, InterruptedException { } private void startRegularPreview() throws CameraAccessException { - if (pictureImageReader == null || pictureImageReader.getSurface() == null) - return; + if (pictureImageReader == null || pictureImageReader.getSurface() == null) return; Log.i(TAG, "startPreview"); createCaptureSession(CameraDevice.TEMPLATE_PREVIEW, pictureImageReader.getSurface()); } private void startPreviewWithVideoRendererStream() throws CameraAccessException, InterruptedException { - if (videoRenderer == null) - return; + if (videoRenderer == null) return; // get rotation for rendered video final PlatformChannel.DeviceOrientation lockedOrientation = @@ -1124,9 +1184,10 @@ private void startPreviewWithVideoRendererStream() int rotation = 0; if (orientationManager != null) { - rotation = lockedOrientation == null - ? orientationManager.getVideoOrientation() - : orientationManager.getVideoOrientation(lockedOrientation); + rotation = + lockedOrientation == null + ? orientationManager.getVideoOrientation() + : orientationManager.getVideoOrientation(lockedOrientation); } if (cameraProperties.getLensFacing() != initialCameraFacing) { @@ -1161,17 +1222,21 @@ public void onImageAvailable(ImageReader reader) { return; } - backgroundHandler.post(new ImageSaver(image, captureFile, new ImageSaver.Callback() { - @Override - public void onComplete(String absolutePath) { - dartMessenger.finish(flutterResult, absolutePath); - } - - @Override - public void onError(String errorCode, String errorMessage) { - dartMessenger.error(flutterResult, errorCode, errorMessage, null); - } - })); + backgroundHandler.post( + new ImageSaver( + image, + captureFile, + new ImageSaver.Callback() { + @Override + public void onComplete(String absolutePath) { + dartMessenger.finish(flutterResult, absolutePath); + } + + @Override + public void onError(String errorCode, String errorMessage) { + dartMessenger.error(flutterResult, errorCode, errorMessage, null); + } + })); cameraCaptureCallback.setCameraState(CameraState.STATE_PREVIEW); } @@ -1198,21 +1263,22 @@ void prepareRecording(@NonNull Result result) { } private void setStreamHandler(EventChannel imageStreamChannel) { - imageStreamChannel.setStreamHandler(new EventChannel.StreamHandler() { - @Override - public void onListen(Object o, EventChannel.EventSink imageStreamSink) { - setImageStreamImageAvailableListener(imageStreamSink); - } + imageStreamChannel.setStreamHandler( + new EventChannel.StreamHandler() { + @Override + public void onListen(Object o, EventChannel.EventSink imageStreamSink) { + setImageStreamImageAvailableListener(imageStreamSink); + } - @Override - public void onCancel(Object o) { - if (imageStreamReader == null) { - return; - } + @Override + public void onCancel(Object o) { + if (imageStreamReader == null) { + return; + } - imageStreamReader.removeListener(backgroundHandler); - } - }); + imageStreamReader.removeListener(backgroundHandler); + } + }); } void setImageStreamImageAvailableListener(final EventChannel.EventSink imageStreamSink) { @@ -1269,8 +1335,7 @@ private void stopAndReleaseCamera() { } private void prepareVideoRenderer() { - if (videoRenderer != null) - return; + if (videoRenderer != null) return; final ResolutionFeature resolutionFeature = cameraFeatures.getResolution(); // handle videoRenderer errors @@ -1284,8 +1349,11 @@ public void uncaughtException(Thread thread, Throwable ex) { }; videoRenderer = - new VideoRenderer(mediaRecorder.getSurface(), resolutionFeature.getCaptureSize().getWidth(), - resolutionFeature.getCaptureSize().getHeight(), videoRendererUncaughtExceptionHandler); + new VideoRenderer( + mediaRecorder.getSurface(), + resolutionFeature.getCaptureSize().getWidth(), + resolutionFeature.getCaptureSize().getHeight(), + videoRendererUncaughtExceptionHandler); } public void setDescriptionWhileRecording( @@ -1298,16 +1366,23 @@ public void setDescriptionWhileRecording( // See VideoRenderer.java; support for this EGL extension is required to switch camera while // recording. if (!SdkCapabilityChecker.supportsEglRecordableAndroid()) { - result.error("setDescriptionWhileRecordingFailed", - "Device does not support switching the camera while recording", null); + result.error( + "setDescriptionWhileRecordingFailed", + "Device does not support switching the camera while recording", + null); return; } stopAndReleaseCamera(); prepareVideoRenderer(); cameraProperties = properties; - cameraFeatures = CameraFeatures.init(cameraFeatureFactory, cameraProperties, activity, - dartMessenger, videoCaptureSettings.resolutionPreset); + cameraFeatures = + CameraFeatures.init( + cameraFeatureFactory, + cameraProperties, + activity, + dartMessenger, + videoCaptureSettings.resolutionPreset); cameraFeatures.setAutoFocus( cameraFeatureFactory.createAutoFocusFeature(cameraProperties, true)); try { diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java index 8a22482c6630..b3f4da808688 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java @@ -395,8 +395,7 @@ private void instantiateCamera(MethodCall call, Result result) throws CameraAcce TextureRegistry.SurfaceProducer surfaceProducer = textureRegistry.createSurfaceProducer(); DartMessenger dartMessenger = - new DartMessenger( - messenger, surfaceProducer.id(), new Handler(Looper.getMainLooper())); + new DartMessenger(messenger, surfaceProducer.id(), new Handler(Looper.getMainLooper())); CameraProperties cameraProperties = new CameraPropertiesImpl(cameraName, CameraUtils.getCameraManager(activity)); ResolutionPreset resolutionPreset = ResolutionPreset.valueOf(preset); diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java index 9c8071ad264a..31d6f48ee793 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java @@ -93,8 +93,10 @@ public void createCaptureSession(SessionConfiguration config) { } @Override - public void createCaptureSession(@NonNull List outputs, - @NonNull CameraCaptureSession.StateCallback callback, @Nullable Handler handler) { + public void createCaptureSession( + @NonNull List outputs, + @NonNull CameraCaptureSession.StateCallback callback, + @Nullable Handler handler) { if (session != null) { callback.onConfigured(session); } @@ -139,7 +141,8 @@ public void before() { when(mockCameraProperties.getCameraName()).thenReturn(cameraName); mockHandlerFactory.when(() -> Camera.HandlerFactory.create(any())).thenReturn(mockHandler); - mockHandlerThreadFactory.when(() -> Camera.HandlerThreadFactory.create(any())) + mockHandlerThreadFactory + .when(() -> Camera.HandlerThreadFactory.create(any())) .thenReturn(mockHandlerThread); // Use a wildcard, since `new Range[] {...}` @@ -156,8 +159,13 @@ public void before() { mockCameraFeatureFactory = new TestCameraFeatureFactory(fpsRangeFeature); camera = - new Camera(mockActivity, mockSurfaceProducer, mockCameraFeatureFactory, mockDartMessenger, - mockCameraProperties, new Camera.VideoCaptureSettings(resolutionPreset, enableAudio)); + new Camera( + mockActivity, + mockSurfaceProducer, + mockCameraFeatureFactory, + mockDartMessenger, + mockCameraProperties, + new Camera.VideoCaptureSettings(resolutionPreset, enableAudio)); final CamcorderProfile mockProfileLegacy = mock(CamcorderProfile.class); mockProfileLegacy.videoFrameRate = 15; @@ -197,9 +205,14 @@ public void shouldCreateCameraPluginAndSetAllFeatures() { when(spyMockCameraFeatureFactory.createSensorOrientationFeature(any(), any(), any())) .thenReturn(mockSensorOrientationFeature); - Camera camera = new Camera(mockActivity, mockSurfaceProducer, spyMockCameraFeatureFactory, - mockDartMessenger, mockCameraProperties, - new Camera.VideoCaptureSettings(resolutionPreset, enableAudio)); + Camera camera = + new Camera( + mockActivity, + mockSurfaceProducer, + spyMockCameraFeatureFactory, + mockDartMessenger, + mockCameraProperties, + new Camera.VideoCaptureSettings(resolutionPreset, enableAudio)); verify(spyMockCameraFeatureFactory, times(1)) .createSensorOrientationFeature(mockCameraProperties, mockActivity, mockDartMessenger); @@ -434,8 +447,9 @@ public void setFlashMode_shouldCallErrorOnResultOnCameraAccessException() @Test public void setFocusPoint_shouldUpdateFocusPointFeature() { SensorOrientationFeature mockSensorOrientationFeature = mock(SensorOrientationFeature.class); - FocusPointFeature mockFocusPointFeature = mockCameraFeatureFactory.createFocusPointFeature( - mockCameraProperties, mockSensorOrientationFeature); + FocusPointFeature mockFocusPointFeature = + mockCameraFeatureFactory.createFocusPointFeature( + mockCameraProperties, mockSensorOrientationFeature); AutoFocusFeature mockAutoFocusFeature = mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, false); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); @@ -452,8 +466,9 @@ public void setFocusPoint_shouldUpdateFocusPointFeature() { @Test public void setFocusPoint_shouldUpdateBuilder() { SensorOrientationFeature mockSensorOrientationFeature = mock(SensorOrientationFeature.class); - FocusPointFeature mockFocusPointFeature = mockCameraFeatureFactory.createFocusPointFeature( - mockCameraProperties, mockSensorOrientationFeature); + FocusPointFeature mockFocusPointFeature = + mockCameraFeatureFactory.createFocusPointFeature( + mockCameraProperties, mockSensorOrientationFeature); AutoFocusFeature mockAutoFocusFeature = mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, false); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); @@ -577,7 +592,7 @@ public void pauseVideoRecording_shouldSendVideoRecordingFailedErrorWhenVersionCo @Test public void - pauseVideoRecording_shouldSendVideoRecordingFailedErrorWhenMediaRecorderPauseThrowsIllegalStateException() { + pauseVideoRecording_shouldSendVideoRecordingFailedErrorWhenMediaRecorderPauseThrowsIllegalStateException() { MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); camera.mediaRecorder = mockMediaRecorder; camera.recordingVideo = true; @@ -635,8 +650,10 @@ public void setDescriptionWhileRecording_errorsWhenUnsupported() { camera.setDescriptionWhileRecording(mockResult, newCameraProperties); verify(mockResult, times(1)) - .error(eq("setDescriptionWhileRecordingFailed"), - eq("Device does not support switching the camera while recording"), eq(null)); + .error( + eq("setDescriptionWhileRecordingFailed"), + eq("Device does not support switching the camera while recording"), + eq(null)); } @Test @@ -746,8 +763,9 @@ public void startPreviewWithImageStream_shouldPullStreamsFromImageReaders() camera.startPreviewWithImageStream(mock(EventChannel.class)); verify(mockImageStreamReader, times(1)) .getSurface(); // stream pulled from image streaming imageReader's surface. - verify(mockPictureImageReader, - times(2)) // we expect one call to start the capture, one to create the capture session. + verify( + mockPictureImageReader, + times(2)) // we expect one call to start the capture, one to create the capture session. .getSurface(); // stream pulled from regular imageReader's surface. } @@ -765,7 +783,7 @@ public void setDescriptionWhileRecording_shouldErrorWhenNotRecording() { @Test public void - resumeVideoRecording_shouldSendVideoRecordingFailedErrorWhenVersionCodeSmallerThanN() { + resumeVideoRecording_shouldSendVideoRecordingFailedErrorWhenVersionCodeSmallerThanN() { camera.recordingVideo = true; SdkCapabilityChecker.SDK_VERSION = 23; @@ -780,7 +798,7 @@ public void setDescriptionWhileRecording_shouldErrorWhenNotRecording() { @Test public void - resumeVideoRecording_shouldSendVideoRecordingFailedErrorWhenMediaRecorderPauseThrowsIllegalStateException() { + resumeVideoRecording_shouldSendVideoRecordingFailedErrorWhenMediaRecorderPauseThrowsIllegalStateException() { MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); camera.mediaRecorder = mockMediaRecorder; camera.recordingVideo = true; @@ -874,8 +892,9 @@ public void startVideoRecording_shouldPullStreamsFromMediaRecorderAndImageReader cameraSpy.startVideoRecording(mockResult, null); verify(mockMediaRecorder, times(1)) .getSurface(); // stream pulled from media recorder's surface. - verify(mockPictureImageReader, - times(2)) // we expect one call to start the capture, one to create the capture session. + verify( + mockPictureImageReader, + times(2)) // we expect one call to start the capture, one to create the capture session. .getSurface(); // stream pulled from image streaming imageReader's surface. } @@ -1144,8 +1163,9 @@ public void startVideoRecording_shouldApplySettingsToMediaRecorder() when(mockCameraProperties.getCameraName()).thenReturn(cameraName); - final Camera.VideoCaptureSettings parameters = new Camera.VideoCaptureSettings( - resolutionPreset, enableAudio, fps, videoBitrate, audioBitrate); + final Camera.VideoCaptureSettings parameters = + new Camera.VideoCaptureSettings( + resolutionPreset, enableAudio, fps, videoBitrate, audioBitrate); // Use a wildcard, since `new Range[] {...}` // results in a 'Generic array creation' error. @@ -1160,17 +1180,25 @@ public void startVideoRecording_shouldApplySettingsToMediaRecorder() when(mockActivity.getApplicationContext()).thenReturn(mockApplicationContext); try (final MockedStatic mockFile = mockStatic(File.class); - final MockedConstruction mockMediaRecorder = - Mockito.mockConstruction(MediaRecorder.class)) { + final MockedConstruction mockMediaRecorder = + Mockito.mockConstruction(MediaRecorder.class)) { assertNotNull(mockMediaRecorder); - mockFile.when(() -> File.createTempFile(any(), any(), any())) + mockFile + .when(() -> File.createTempFile(any(), any(), any())) .thenReturn(new File("/tmp/file.mp4")); final FpsRangeFeature fpsRangeFeature = new FpsRangeFeature(mockCameraProperties); - final Camera camera = spy(new Camera(mockActivity, mockSurfaceProducer, - mockCameraFeatureFactory, mockDartMessenger, mockCameraProperties, parameters)); + final Camera camera = + spy( + new Camera( + mockActivity, + mockSurfaceProducer, + mockCameraFeatureFactory, + mockDartMessenger, + mockCameraProperties, + parameters)); final CamcorderProfile mockProfileLegacy = mock(CamcorderProfile.class); mockProfileLegacy.videoFrameRate = fps; @@ -1211,7 +1239,8 @@ public void startVideoRecording_shouldApplySettingsToMediaRecorder() assertEquals(camera.cameraFeatures.getFpsRange().getValue().getUpper(), Integer.valueOf(fps)); verify(mockRequestBuilder) - .set(eq(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE), + .set( + eq(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE), argThat( (Range range) -> range.getLower() == fps && range.getUpper() == fps)); // endregion @@ -1244,23 +1273,29 @@ private static class RangeConstruction implements Closeable { final Map, Integer> lowers = new HashMap<>(); final Map, Integer> uppers = new HashMap<>(); - @SuppressWarnings({"rawtypes"}) final MockedConstruction rangeMockedConstruction; + @SuppressWarnings({"rawtypes"}) + final MockedConstruction rangeMockedConstruction; @SuppressWarnings({"unchecked"}) public RangeConstruction() { - this.rangeMockedConstruction = Mockito.mockConstruction(Range.class, (mock, context) -> { - int lower = (int) context.arguments().get(0); - int upper = (int) context.arguments().get(1); - lowers.put((Range) mock, lower); - uppers.put((Range) mock, upper); - when(((Range) mock).getUpper()) - .thenReturn(lowers.getOrDefault((Range) mock, 15)); - when(((Range) mock).getLower()) - .thenReturn(uppers.getOrDefault((Range) mock, 15)); - when(mock.toString()) - .thenReturn(String.format( - "mocked [%s, %s]", lowers.getOrDefault(mock, 15), uppers.getOrDefault(mock, 15))); - }); + this.rangeMockedConstruction = + Mockito.mockConstruction( + Range.class, + (mock, context) -> { + int lower = (int) context.arguments().get(0); + int upper = (int) context.arguments().get(1); + lowers.put((Range) mock, lower); + uppers.put((Range) mock, upper); + when(((Range) mock).getUpper()) + .thenReturn(lowers.getOrDefault((Range) mock, 15)); + when(((Range) mock).getLower()) + .thenReturn(uppers.getOrDefault((Range) mock, 15)); + when(mock.toString()) + .thenReturn( + String.format( + "mocked [%s, %s]", + lowers.getOrDefault(mock, 15), uppers.getOrDefault(mock, 15))); + }); } @Override @@ -1320,13 +1355,16 @@ public FlashFeature createFlashFeature(@NonNull CameraProperties cameraPropertie } @Override - public ResolutionFeature createResolutionFeature(@NonNull CameraProperties cameraProperties, - ResolutionPreset initialSetting, String cameraName) { + public ResolutionFeature createResolutionFeature( + @NonNull CameraProperties cameraProperties, + ResolutionPreset initialSetting, + String cameraName) { return mockResolutionFeature; } @Override - public FocusPointFeature createFocusPointFeature(@NonNull CameraProperties cameraProperties, + public FocusPointFeature createFocusPointFeature( + @NonNull CameraProperties cameraProperties, @NonNull SensorOrientationFeature sensorOrienttionFeature) { return mockFocusPointFeature; } @@ -1338,7 +1376,8 @@ public FpsRangeFeature createFpsRangeFeature(@NonNull CameraProperties cameraPro @Override public SensorOrientationFeature createSensorOrientationFeature( - @NonNull CameraProperties cameraProperties, @NonNull Activity activity, + @NonNull CameraProperties cameraProperties, + @NonNull Activity activity, @NonNull DartMessenger dartMessenger) { return mockSensorOrientationFeature; } diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java.orig b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java.orig new file mode 100644 index 000000000000..9c8071ad264a --- /dev/null +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java.orig @@ -0,0 +1,1364 @@ +// 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.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +import android.app.Activity; +import android.content.Context; +import android.graphics.SurfaceTexture; +import android.hardware.camera2.*; +import android.hardware.camera2.params.SessionConfiguration; +import android.media.CamcorderProfile; +import android.media.ImageReader; +import android.media.MediaRecorder; +import android.os.Build; +import android.os.Handler; +import android.os.HandlerThread; +import android.util.Range; +import android.util.Size; +import android.view.Surface; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.lifecycle.LifecycleObserver; +import io.flutter.embedding.engine.systemchannels.PlatformChannel; +import io.flutter.plugin.common.EventChannel; +import io.flutter.plugin.common.MethodChannel; +import io.flutter.plugins.camera.features.CameraFeatureFactory; +import io.flutter.plugins.camera.features.Point; +import io.flutter.plugins.camera.features.autofocus.AutoFocusFeature; +import io.flutter.plugins.camera.features.autofocus.FocusMode; +import io.flutter.plugins.camera.features.exposurelock.ExposureLockFeature; +import io.flutter.plugins.camera.features.exposurelock.ExposureMode; +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.flash.FlashMode; +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.DeviceOrientationManager; +import io.flutter.plugins.camera.features.sensororientation.SensorOrientationFeature; +import io.flutter.plugins.camera.features.zoomlevel.ZoomLevelFeature; +import io.flutter.plugins.camera.media.ImageStreamReader; +import io.flutter.view.TextureRegistry; +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.MockedConstruction; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +class FakeCameraDeviceWrapper implements CameraDeviceWrapper { + final List captureRequests; + @Nullable final CameraCaptureSession session; + + FakeCameraDeviceWrapper(List captureRequests) { + this(captureRequests, null); + } + + FakeCameraDeviceWrapper( + List captureRequests, CameraCaptureSession session) { + this.captureRequests = captureRequests; + this.session = session; + } + + @NonNull + @Override + public CaptureRequest.Builder createCaptureRequest(int var1) { + return captureRequests.remove(0); + } + + @Override + public void createCaptureSession(SessionConfiguration config) { + if (session != null) { + config.getStateCallback().onConfigured(session); + } + } + + @Override + public void createCaptureSession(@NonNull List outputs, + @NonNull CameraCaptureSession.StateCallback callback, @Nullable Handler handler) { + if (session != null) { + callback.onConfigured(session); + } + } + + @Override + public void close() {} +} + +public class CameraTest { + private CameraProperties mockCameraProperties; + private TestCameraFeatureFactory mockCameraFeatureFactory; + private DartMessenger mockDartMessenger; + private Camera camera; + private CameraCaptureSession mockCaptureSession; + private CaptureRequest.Builder mockPreviewRequestBuilder; + private MockedStatic mockHandlerThreadFactory; + private HandlerThread mockHandlerThread; + private MockedStatic mockHandlerFactory; + private Handler mockHandler; + + private RangeConstruction mockRangeConstruction; + + @Before + public void before() { + mockRangeConstruction = new RangeConstruction(); + mockCameraProperties = mock(CameraProperties.class); + mockDartMessenger = mock(DartMessenger.class); + mockCaptureSession = mock(CameraCaptureSession.class); + mockPreviewRequestBuilder = mock(CaptureRequest.Builder.class); + mockHandlerThreadFactory = mockStatic(Camera.HandlerThreadFactory.class); + mockHandlerThread = mock(HandlerThread.class); + mockHandlerFactory = mockStatic(Camera.HandlerFactory.class); + mockHandler = mock(Handler.class); + + final Activity mockActivity = mock(Activity.class); + final TextureRegistry.SurfaceProducer mockSurfaceProducer = + mock(TextureRegistry.SurfaceProducer.class); + final String cameraName = "1"; + final ResolutionPreset resolutionPreset = ResolutionPreset.high; + final boolean enableAudio = false; + + when(mockCameraProperties.getCameraName()).thenReturn(cameraName); + mockHandlerFactory.when(() -> Camera.HandlerFactory.create(any())).thenReturn(mockHandler); + mockHandlerThreadFactory.when(() -> Camera.HandlerThreadFactory.create(any())) + .thenReturn(mockHandlerThread); + + // Use a wildcard, since `new Range[] {...}` + // results in a 'Generic array creation' error. + @SuppressWarnings("unchecked") + final Range[] mockRanges = + (Range[]) new Range[] {new Range(10, 20)}; + + when(mockCameraProperties.getControlAutoExposureAvailableTargetFpsRanges()) + .thenReturn(mockRanges); + + final FpsRangeFeature fpsRangeFeature = new FpsRangeFeature(mockCameraProperties); + + mockCameraFeatureFactory = new TestCameraFeatureFactory(fpsRangeFeature); + + camera = + new Camera(mockActivity, mockSurfaceProducer, mockCameraFeatureFactory, mockDartMessenger, + mockCameraProperties, new Camera.VideoCaptureSettings(resolutionPreset, enableAudio)); + + final CamcorderProfile mockProfileLegacy = mock(CamcorderProfile.class); + mockProfileLegacy.videoFrameRate = 15; + when(camera.getRecordingProfileLegacy()).thenReturn(mockProfileLegacy); + + camera.captureSession = mockCaptureSession; + camera.previewRequestBuilder = mockPreviewRequestBuilder; + } + + @After + public void after() throws IOException { + SdkCapabilityChecker.SDK_VERSION = 0; + mockHandlerThreadFactory.close(); + mockHandlerFactory.close(); + mockRangeConstruction.close(); + } + + @Test + public void shouldNotImplementLifecycleObserverInterface() { + Class cameraClass = Camera.class; + + assertFalse(LifecycleObserver.class.isAssignableFrom(cameraClass)); + } + + @Test + public void shouldCreateCameraPluginAndSetAllFeatures() { + final Activity mockActivity = mock(Activity.class); + final TextureRegistry.SurfaceProducer mockSurfaceProducer = + mock(TextureRegistry.SurfaceProducer.class); + final CameraFeatureFactory spyMockCameraFeatureFactory = spy(mockCameraFeatureFactory); + final String cameraName = "1"; + final ResolutionPreset resolutionPreset = ResolutionPreset.high; + final boolean enableAudio = false; + + when(mockCameraProperties.getCameraName()).thenReturn(cameraName); + SensorOrientationFeature mockSensorOrientationFeature = mock(SensorOrientationFeature.class); + when(spyMockCameraFeatureFactory.createSensorOrientationFeature(any(), any(), any())) + .thenReturn(mockSensorOrientationFeature); + + Camera camera = new Camera(mockActivity, mockSurfaceProducer, spyMockCameraFeatureFactory, + mockDartMessenger, mockCameraProperties, + new Camera.VideoCaptureSettings(resolutionPreset, enableAudio)); + + verify(spyMockCameraFeatureFactory, times(1)) + .createSensorOrientationFeature(mockCameraProperties, mockActivity, mockDartMessenger); + verify(spyMockCameraFeatureFactory, times(1)) + .createAutoFocusFeature(mockCameraProperties, false); + verify(spyMockCameraFeatureFactory, times(1)).createExposureLockFeature(mockCameraProperties); + verify(spyMockCameraFeatureFactory, times(1)) + .createExposurePointFeature(eq(mockCameraProperties), eq(mockSensorOrientationFeature)); + verify(spyMockCameraFeatureFactory, times(1)).createExposureOffsetFeature(mockCameraProperties); + verify(spyMockCameraFeatureFactory, times(1)).createFlashFeature(mockCameraProperties); + verify(spyMockCameraFeatureFactory, times(1)) + .createFocusPointFeature(eq(mockCameraProperties), eq(mockSensorOrientationFeature)); + verify(spyMockCameraFeatureFactory, times(1)).createFpsRangeFeature(mockCameraProperties); + verify(spyMockCameraFeatureFactory, times(1)).createNoiseReductionFeature(mockCameraProperties); + verify(spyMockCameraFeatureFactory, times(1)) + .createResolutionFeature(mockCameraProperties, resolutionPreset, cameraName); + verify(spyMockCameraFeatureFactory, times(1)).createZoomLevelFeature(mockCameraProperties); + assertNotNull("should create a camera", camera); + } + + @Test + public void getDeviceOrientationManager() { + SensorOrientationFeature mockSensorOrientationFeature = + mockCameraFeatureFactory.createSensorOrientationFeature(mockCameraProperties, null, null); + DeviceOrientationManager mockDeviceOrientationManager = mock(DeviceOrientationManager.class); + + when(mockSensorOrientationFeature.getDeviceOrientationManager()) + .thenReturn(mockDeviceOrientationManager); + + DeviceOrientationManager actualDeviceOrientationManager = camera.getDeviceOrientationManager(); + + verify(mockSensorOrientationFeature, times(1)).getDeviceOrientationManager(); + assertEquals(mockDeviceOrientationManager, actualDeviceOrientationManager); + } + + @Test + public void getExposureOffsetStepSize() { + ExposureOffsetFeature mockExposureOffsetFeature = + mockCameraFeatureFactory.createExposureOffsetFeature(mockCameraProperties); + double stepSize = 2.3; + + when(mockExposureOffsetFeature.getExposureOffsetStepSize()).thenReturn(stepSize); + + double actualSize = camera.getExposureOffsetStepSize(); + + verify(mockExposureOffsetFeature, times(1)).getExposureOffsetStepSize(); + assertEquals(stepSize, actualSize, 0); + } + + @Test + public void getMaxExposureOffset() { + ExposureOffsetFeature mockExposureOffsetFeature = + mockCameraFeatureFactory.createExposureOffsetFeature(mockCameraProperties); + double expectedMaxOffset = 42.0; + + when(mockExposureOffsetFeature.getMaxExposureOffset()).thenReturn(expectedMaxOffset); + + double actualMaxOffset = camera.getMaxExposureOffset(); + + verify(mockExposureOffsetFeature, times(1)).getMaxExposureOffset(); + assertEquals(expectedMaxOffset, actualMaxOffset, 0); + } + + @Test + public void getMinExposureOffset() { + ExposureOffsetFeature mockExposureOffsetFeature = + mockCameraFeatureFactory.createExposureOffsetFeature(mockCameraProperties); + double expectedMinOffset = 21.5; + + when(mockExposureOffsetFeature.getMinExposureOffset()).thenReturn(21.5); + + double actualMinOffset = camera.getMinExposureOffset(); + + verify(mockExposureOffsetFeature, times(1)).getMinExposureOffset(); + assertEquals(expectedMinOffset, actualMinOffset, 0); + } + + @Test + public void getMaxZoomLevel() { + ZoomLevelFeature mockZoomLevelFeature = + mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); + float expectedMaxZoomLevel = 4.2f; + + when(mockZoomLevelFeature.getMaximumZoomLevel()).thenReturn(expectedMaxZoomLevel); + + float actualMaxZoomLevel = camera.getMaxZoomLevel(); + + verify(mockZoomLevelFeature, times(1)).getMaximumZoomLevel(); + assertEquals(expectedMaxZoomLevel, actualMaxZoomLevel, 0); + } + + @Test + public void getMinZoomLevel() { + ZoomLevelFeature mockZoomLevelFeature = + mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); + float expectedMinZoomLevel = 4.2f; + + when(mockZoomLevelFeature.getMinimumZoomLevel()).thenReturn(expectedMinZoomLevel); + + float actualMinZoomLevel = camera.getMinZoomLevel(); + + verify(mockZoomLevelFeature, times(1)).getMinimumZoomLevel(); + assertEquals(expectedMinZoomLevel, actualMinZoomLevel, 0); + } + + @Test + public void setExposureMode_shouldUpdateExposureLockFeature() { + ExposureLockFeature mockExposureLockFeature = + mockCameraFeatureFactory.createExposureLockFeature(mockCameraProperties); + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + ExposureMode exposureMode = ExposureMode.locked; + + camera.setExposureMode(mockResult, exposureMode); + + verify(mockExposureLockFeature, times(1)).setValue(exposureMode); + verify(mockResult, never()).error(any(), any(), any()); + verify(mockResult, times(1)).success(null); + } + + @Test + public void setExposureMode_shouldUpdateBuilder() { + ExposureLockFeature mockExposureLockFeature = + mockCameraFeatureFactory.createExposureLockFeature(mockCameraProperties); + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + ExposureMode exposureMode = ExposureMode.locked; + + camera.setExposureMode(mockResult, exposureMode); + + verify(mockExposureLockFeature, times(1)).updateBuilder(any()); + } + + @Test + public void setExposureMode_shouldCallErrorOnResultOnCameraAccessException() + throws CameraAccessException { + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + ExposureMode exposureMode = ExposureMode.locked; + when(mockCaptureSession.setRepeatingRequest(any(), any(), any())) + .thenThrow(new CameraAccessException(0, "")); + + camera.setExposureMode(mockResult, exposureMode); + + verify(mockResult, never()).success(any()); + verify(mockResult, times(1)) + .error("setExposureModeFailed", "Could not set exposure mode.", null); + } + + @Test + public void setExposurePoint_shouldUpdateExposurePointFeature() { + SensorOrientationFeature mockSensorOrientationFeature = mock(SensorOrientationFeature.class); + ExposurePointFeature mockExposurePointFeature = + mockCameraFeatureFactory.createExposurePointFeature( + mockCameraProperties, mockSensorOrientationFeature); + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + Point point = new Point(42d, 42d); + + camera.setExposurePoint(mockResult, point); + + verify(mockExposurePointFeature, times(1)).setValue(point); + verify(mockResult, never()).error(any(), any(), any()); + verify(mockResult, times(1)).success(null); + } + + @Test + public void setExposurePoint_shouldUpdateBuilder() { + SensorOrientationFeature mockSensorOrientationFeature = mock(SensorOrientationFeature.class); + ExposurePointFeature mockExposurePointFeature = + mockCameraFeatureFactory.createExposurePointFeature( + mockCameraProperties, mockSensorOrientationFeature); + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + Point point = new Point(42d, 42d); + + camera.setExposurePoint(mockResult, point); + + verify(mockExposurePointFeature, times(1)).updateBuilder(any()); + } + + @Test + public void setExposurePoint_shouldCallErrorOnResultOnCameraAccessException() + throws CameraAccessException { + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + Point point = new Point(42d, 42d); + when(mockCaptureSession.setRepeatingRequest(any(), any(), any())) + .thenThrow(new CameraAccessException(0, "")); + + camera.setExposurePoint(mockResult, point); + + verify(mockResult, never()).success(any()); + verify(mockResult, times(1)) + .error("setExposurePointFailed", "Could not set exposure point.", null); + } + + @Test + public void setFlashMode_shouldUpdateFlashFeature() { + FlashFeature mockFlashFeature = + mockCameraFeatureFactory.createFlashFeature(mockCameraProperties); + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + FlashMode flashMode = FlashMode.always; + + camera.setFlashMode(mockResult, flashMode); + + verify(mockFlashFeature, times(1)).setValue(flashMode); + verify(mockResult, never()).error(any(), any(), any()); + verify(mockResult, times(1)).success(null); + } + + @Test + public void setFlashMode_shouldUpdateBuilder() { + FlashFeature mockFlashFeature = + mockCameraFeatureFactory.createFlashFeature(mockCameraProperties); + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + FlashMode flashMode = FlashMode.always; + + camera.setFlashMode(mockResult, flashMode); + + verify(mockFlashFeature, times(1)).updateBuilder(any()); + } + + @Test + public void setFlashMode_shouldCallErrorOnResultOnCameraAccessException() + throws CameraAccessException { + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + FlashMode flashMode = FlashMode.always; + when(mockCaptureSession.setRepeatingRequest(any(), any(), any())) + .thenThrow(new CameraAccessException(0, "")); + + camera.setFlashMode(mockResult, flashMode); + + verify(mockResult, never()).success(any()); + verify(mockResult, times(1)).error("setFlashModeFailed", "Could not set flash mode.", null); + } + + @Test + public void setFocusPoint_shouldUpdateFocusPointFeature() { + SensorOrientationFeature mockSensorOrientationFeature = mock(SensorOrientationFeature.class); + FocusPointFeature mockFocusPointFeature = mockCameraFeatureFactory.createFocusPointFeature( + mockCameraProperties, mockSensorOrientationFeature); + AutoFocusFeature mockAutoFocusFeature = + mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, false); + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + Point point = new Point(42d, 42d); + when(mockAutoFocusFeature.getValue()).thenReturn(FocusMode.auto); + + camera.setFocusPoint(mockResult, point); + + verify(mockFocusPointFeature, times(1)).setValue(point); + verify(mockResult, never()).error(any(), any(), any()); + verify(mockResult, times(1)).success(null); + } + + @Test + public void setFocusPoint_shouldUpdateBuilder() { + SensorOrientationFeature mockSensorOrientationFeature = mock(SensorOrientationFeature.class); + FocusPointFeature mockFocusPointFeature = mockCameraFeatureFactory.createFocusPointFeature( + mockCameraProperties, mockSensorOrientationFeature); + AutoFocusFeature mockAutoFocusFeature = + mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, false); + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + Point point = new Point(42d, 42d); + when(mockAutoFocusFeature.getValue()).thenReturn(FocusMode.auto); + + camera.setFocusPoint(mockResult, point); + + verify(mockFocusPointFeature, times(1)).updateBuilder(any()); + } + + @Test + public void setFocusPoint_shouldCallErrorOnResultOnCameraAccessException() + throws CameraAccessException { + AutoFocusFeature mockAutoFocusFeature = + mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, false); + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + Point point = new Point(42d, 42d); + when(mockAutoFocusFeature.getValue()).thenReturn(FocusMode.auto); + when(mockCaptureSession.setRepeatingRequest(any(), any(), any())) + .thenThrow(new CameraAccessException(0, "")); + + camera.setFocusPoint(mockResult, point); + + verify(mockResult, never()).success(any()); + verify(mockResult, times(1)).error("setFocusPointFailed", "Could not set focus point.", null); + } + + @Test + public void setZoomLevel_shouldUpdateZoomLevelFeature() throws CameraAccessException { + ZoomLevelFeature mockZoomLevelFeature = + mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + float zoomLevel = 1.0f; + + when(mockZoomLevelFeature.getValue()).thenReturn(zoomLevel); + when(mockZoomLevelFeature.getMinimumZoomLevel()).thenReturn(0f); + when(mockZoomLevelFeature.getMaximumZoomLevel()).thenReturn(2f); + + camera.setZoomLevel(mockResult, zoomLevel); + + verify(mockZoomLevelFeature, times(1)).setValue(zoomLevel); + verify(mockResult, never()).error(any(), any(), any()); + verify(mockResult, times(1)).success(null); + } + + @Test + public void setZoomLevel_shouldUpdateBuilder() throws CameraAccessException { + ZoomLevelFeature mockZoomLevelFeature = + mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + float zoomLevel = 1.0f; + + when(mockZoomLevelFeature.getValue()).thenReturn(zoomLevel); + when(mockZoomLevelFeature.getMinimumZoomLevel()).thenReturn(0f); + when(mockZoomLevelFeature.getMaximumZoomLevel()).thenReturn(2f); + + camera.setZoomLevel(mockResult, zoomLevel); + + verify(mockZoomLevelFeature, times(1)).updateBuilder(any()); + } + + @Test + public void setZoomLevel_shouldCallErrorOnResultOnCameraAccessException() + throws CameraAccessException { + ZoomLevelFeature mockZoomLevelFeature = + mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + float zoomLevel = 1.0f; + + when(mockZoomLevelFeature.getValue()).thenReturn(zoomLevel); + when(mockZoomLevelFeature.getMinimumZoomLevel()).thenReturn(0f); + when(mockZoomLevelFeature.getMaximumZoomLevel()).thenReturn(2f); + when(mockCaptureSession.setRepeatingRequest(any(), any(), any())) + .thenThrow(new CameraAccessException(0, "")); + + camera.setZoomLevel(mockResult, zoomLevel); + + verify(mockResult, never()).success(any()); + verify(mockResult, times(1)).error("setZoomLevelFailed", "Could not set zoom level.", null); + } + + @Test + public void pauseVideoRecording_shouldSendNullResultWhenNotRecording() { + camera.recordingVideo = false; + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + + camera.pauseVideoRecording(mockResult); + + verify(mockResult, times(1)).success(null); + verify(mockResult, never()).error(any(), any(), any()); + } + + @Test + public void pauseVideoRecording_shouldCallPauseWhenRecordingAndOnAPIN() { + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); + camera.mediaRecorder = mockMediaRecorder; + camera.recordingVideo = true; + SdkCapabilityChecker.SDK_VERSION = 24; + + camera.pauseVideoRecording(mockResult); + + verify(mockMediaRecorder, times(1)).pause(); + verify(mockResult, times(1)).success(null); + verify(mockResult, never()).error(any(), any(), any()); + } + + @Test + public void pauseVideoRecording_shouldSendVideoRecordingFailedErrorWhenVersionCodeSmallerThenN() { + camera.recordingVideo = true; + SdkCapabilityChecker.SDK_VERSION = 23; + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + + camera.pauseVideoRecording(mockResult); + + verify(mockResult, times(1)) + .error("videoRecordingFailed", "pauseVideoRecording requires Android API +24.", null); + verify(mockResult, never()).success(any()); + } + + @Test + public void + pauseVideoRecording_shouldSendVideoRecordingFailedErrorWhenMediaRecorderPauseThrowsIllegalStateException() { + MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); + camera.mediaRecorder = mockMediaRecorder; + camera.recordingVideo = true; + SdkCapabilityChecker.SDK_VERSION = 24; + + IllegalStateException expectedException = new IllegalStateException("Test error message"); + + doThrow(expectedException).when(mockMediaRecorder).pause(); + + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + + camera.pauseVideoRecording(mockResult); + + verify(mockResult, times(1)).error("videoRecordingFailed", "Test error message", null); + verify(mockResult, never()).success(any()); + } + + @Test + public void resumeVideoRecording_shouldSendNullResultWhenNotRecording() { + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + camera.recordingVideo = false; + + camera.resumeVideoRecording(mockResult); + + verify(mockResult, times(1)).success(null); + verify(mockResult, never()).error(any(), any(), any()); + } + + @Test + public void resumeVideoRecording_shouldCallPauseWhenRecordingAndOnAPIN() { + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); + camera.mediaRecorder = mockMediaRecorder; + camera.recordingVideo = true; + SdkCapabilityChecker.SDK_VERSION = 24; + + camera.resumeVideoRecording(mockResult); + + verify(mockMediaRecorder, times(1)).resume(); + verify(mockResult, times(1)).success(null); + verify(mockResult, never()).error(any(), any(), any()); + } + + @Test + public void setDescriptionWhileRecording_errorsWhenUnsupported() { + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); + VideoRenderer mockVideoRenderer = mock(VideoRenderer.class); + camera.mediaRecorder = mockMediaRecorder; + camera.recordingVideo = true; + camera.videoRenderer = mockVideoRenderer; + SdkCapabilityChecker.SDK_VERSION = Build.VERSION_CODES.LOLLIPOP; + + final CameraProperties newCameraProperties = mock(CameraProperties.class); + camera.setDescriptionWhileRecording(mockResult, newCameraProperties); + + verify(mockResult, times(1)) + .error(eq("setDescriptionWhileRecordingFailed"), + eq("Device does not support switching the camera while recording"), eq(null)); + } + + @Test + public void setDescriptionWhileRecording_succeedsWhenSupported() { + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); + VideoRenderer mockVideoRenderer = mock(VideoRenderer.class); + camera.mediaRecorder = mockMediaRecorder; + camera.recordingVideo = true; + camera.videoRenderer = mockVideoRenderer; + SdkCapabilityChecker.SDK_VERSION = Build.VERSION_CODES.O; + + final CameraProperties newCameraProperties = mock(CameraProperties.class); + camera.setDescriptionWhileRecording(mockResult, newCameraProperties); + + verify(mockResult, times(1)).success(null); + verify(mockResult, never()).error(any(), any(), any()); + } + + @Test + public void startPreview_shouldPullStreamFromVideoRenderer() + throws InterruptedException, CameraAccessException { + VideoRenderer mockVideoRenderer = mock(VideoRenderer.class); + ArrayList mockRequestBuilders = new ArrayList<>(); + mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); + Size mockSize = mock(Size.class); + camera.recordingVideo = true; + camera.videoRenderer = mockVideoRenderer; + CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders); + camera.cameraDevice = fakeCamera; + ImageReader mockPictureImageReader = mock(ImageReader.class); + camera.pictureImageReader = mockPictureImageReader; + + ResolutionFeature resolutionFeature = mockCameraFeatureFactory.mockResolutionFeature; + + when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); + + camera.startPreview(); + verify(mockVideoRenderer, times(1)) + .getInputSurface(); // stream pulled from videoRenderer's surface. + } + + @Test + public void startPreview_shouldPullStreamFromImageReader() + throws InterruptedException, CameraAccessException { + ArrayList mockRequestBuilders = new ArrayList<>(); + mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); + Size mockSize = mock(Size.class); + ImageReader mockImageReader = mock(ImageReader.class); + camera.recordingVideo = false; + camera.pictureImageReader = mockImageReader; + CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders); + camera.cameraDevice = fakeCamera; + + ResolutionFeature resolutionFeature = mockCameraFeatureFactory.mockResolutionFeature; + + when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); + when(mockImageReader.getSurface()).thenReturn(mock(Surface.class)); + + camera.startPreview(); + verify(mockImageReader, times(2)) // we expect two calls to start regular preview. + .getSurface(); // stream pulled from regular imageReader's surface. + } + + @Test + public void startPreview_shouldFlipRotation() throws InterruptedException, CameraAccessException { + VideoRenderer mockVideoRenderer = mock(VideoRenderer.class); + ArrayList mockRequestBuilders = new ArrayList<>(); + mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); + Size mockSize = mock(Size.class); + camera.recordingVideo = true; + camera.videoRenderer = mockVideoRenderer; + camera.initialCameraFacing = CameraMetadata.LENS_FACING_BACK; + CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders); + camera.cameraDevice = fakeCamera; + ImageReader mockPictureImageReader = mock(ImageReader.class); + camera.pictureImageReader = mockPictureImageReader; + + ResolutionFeature resolutionFeature = mockCameraFeatureFactory.mockResolutionFeature; + + when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); + when(mockCameraProperties.getLensFacing()).thenReturn(CameraMetadata.LENS_FACING_FRONT); + + camera.startPreview(); + verify(mockVideoRenderer, times(1)).setRotation(180); + } + + @Test + public void startPreviewWithImageStream_shouldPullStreamsFromImageReaders() + throws InterruptedException, CameraAccessException { + ArrayList mockRequestBuilders = new ArrayList<>(); + mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); + SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class); + Size mockSize = mock(Size.class); + ImageReader mockPictureImageReader = mock(ImageReader.class); + ImageStreamReader mockImageStreamReader = mock(ImageStreamReader.class); + camera.recordingVideo = false; + camera.pictureImageReader = mockPictureImageReader; + CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders); + camera.cameraDevice = fakeCamera; + camera.imageStreamReader = mockImageStreamReader; + + ResolutionFeature resolutionFeature = mockCameraFeatureFactory.mockResolutionFeature; + + when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); + + camera.startPreviewWithImageStream(mock(EventChannel.class)); + verify(mockImageStreamReader, times(1)) + .getSurface(); // stream pulled from image streaming imageReader's surface. + verify(mockPictureImageReader, + times(2)) // we expect one call to start the capture, one to create the capture session. + .getSurface(); // stream pulled from regular imageReader's surface. + } + + @Test + public void setDescriptionWhileRecording_shouldErrorWhenNotRecording() { + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + camera.recordingVideo = false; + final CameraProperties newCameraProperties = mock(CameraProperties.class); + camera.setDescriptionWhileRecording(mockResult, newCameraProperties); + + verify(mockResult, times(1)) + .error("setDescriptionWhileRecordingFailed", "Device was not recording", null); + verify(mockResult, never()).success(any()); + } + + @Test + public void + resumeVideoRecording_shouldSendVideoRecordingFailedErrorWhenVersionCodeSmallerThanN() { + camera.recordingVideo = true; + SdkCapabilityChecker.SDK_VERSION = 23; + + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + + camera.resumeVideoRecording(mockResult); + + verify(mockResult, times(1)) + .error("videoRecordingFailed", "resumeVideoRecording requires Android API +24.", null); + verify(mockResult, never()).success(any()); + } + + @Test + public void + resumeVideoRecording_shouldSendVideoRecordingFailedErrorWhenMediaRecorderPauseThrowsIllegalStateException() { + MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); + camera.mediaRecorder = mockMediaRecorder; + camera.recordingVideo = true; + SdkCapabilityChecker.SDK_VERSION = 24; + + IllegalStateException expectedException = new IllegalStateException("Test error message"); + + doThrow(expectedException).when(mockMediaRecorder).resume(); + + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + + camera.resumeVideoRecording(mockResult); + + verify(mockResult, times(1)).error("videoRecordingFailed", "Test error message", null); + verify(mockResult, never()).success(any()); + } + + @Test + public void setFocusMode_shouldUpdateAutoFocusFeature() { + AutoFocusFeature mockAutoFocusFeature = + mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, false); + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + + camera.setFocusMode(mockResult, FocusMode.auto); + + verify(mockAutoFocusFeature, times(1)).setValue(FocusMode.auto); + verify(mockResult, never()).error(any(), any(), any()); + verify(mockResult, times(1)).success(null); + } + + @Test + public void setFocusMode_shouldUpdateBuilder() { + AutoFocusFeature mockAutoFocusFeature = + mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, false); + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + + camera.setFocusMode(mockResult, FocusMode.auto); + + verify(mockAutoFocusFeature, times(1)).updateBuilder(any()); + } + + @Test + public void setFocusMode_shouldUnlockAutoFocusForAutoMode() { + camera.setFocusMode(mock(MethodChannel.Result.class), FocusMode.auto); + verify(mockPreviewRequestBuilder, times(1)) + .set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL); + verify(mockPreviewRequestBuilder, times(1)) + .set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_IDLE); + } + + @Test + public void setFocusMode_shouldSkipUnlockAutoFocusWhenNullCaptureSession() { + camera.captureSession = null; + camera.setFocusMode(mock(MethodChannel.Result.class), FocusMode.auto); + verify(mockPreviewRequestBuilder, never()) + .set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL); + verify(mockPreviewRequestBuilder, never()) + .set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_IDLE); + } + + @Test + public void setFocusMode_shouldSendErrorEventOnUnlockAutoFocusCameraAccessException() + throws CameraAccessException { + when(mockCaptureSession.capture(any(), any(), any())) + .thenThrow(new CameraAccessException(0, "")); + camera.setFocusMode(mock(MethodChannel.Result.class), FocusMode.auto); + verify(mockDartMessenger, times(1)).sendCameraErrorEvent(any()); + } + + @Test + public void startVideoRecording_shouldPullStreamsFromMediaRecorderAndImageReader() + throws InterruptedException, IOException, CameraAccessException { + Camera cameraSpy = spy(camera); + ArrayList mockRequestBuilders = new ArrayList<>(); + mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); + Size mockSize = mock(Size.class); + MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); + ImageReader mockPictureImageReader = mock(ImageReader.class); + cameraSpy.mediaRecorder = mockMediaRecorder; + cameraSpy.recordingVideo = false; + cameraSpy.pictureImageReader = mockPictureImageReader; + CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders); + cameraSpy.cameraDevice = fakeCamera; + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + + ResolutionFeature resolutionFeature = mockCameraFeatureFactory.mockResolutionFeature; + + when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); + doNothing().when(cameraSpy).prepareRecording(mockResult); + + cameraSpy.startVideoRecording(mockResult, null); + verify(mockMediaRecorder, times(1)) + .getSurface(); // stream pulled from media recorder's surface. + verify(mockPictureImageReader, + times(2)) // we expect one call to start the capture, one to create the capture session. + .getSurface(); // stream pulled from image streaming imageReader's surface. + } + + @Test + public void setFocusMode_shouldLockAutoFocusForLockedMode() throws CameraAccessException { + camera.setFocusMode(mock(MethodChannel.Result.class), FocusMode.locked); + verify(mockPreviewRequestBuilder, times(1)) + .set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START); + verify(mockCaptureSession, times(1)).capture(any(), any(), any()); + verify(mockCaptureSession, times(1)).setRepeatingRequest(any(), any(), any()); + } + + @Test + public void setFocusMode_shouldSkipLockAutoFocusWhenNullCaptureSession() { + camera.captureSession = null; + camera.setFocusMode(mock(MethodChannel.Result.class), FocusMode.locked); + verify(mockPreviewRequestBuilder, never()) + .set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START); + } + + @Test + public void setFocusMode_shouldSendErrorEventOnLockAutoFocusCameraAccessException() + throws CameraAccessException { + when(mockCaptureSession.capture(any(), any(), any())) + .thenThrow(new CameraAccessException(0, "")); + camera.setFocusMode(mock(MethodChannel.Result.class), FocusMode.locked); + verify(mockDartMessenger, times(1)).sendCameraErrorEvent(any()); + } + + @Test + public void setFocusMode_shouldCallErrorOnResultOnCameraAccessException() + throws CameraAccessException { + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + when(mockCaptureSession.setRepeatingRequest(any(), any(), any())) + .thenThrow(new CameraAccessException(0, "")); + + camera.setFocusMode(mockResult, FocusMode.locked); + + verify(mockResult, never()).success(any()); + verify(mockResult, times(1)) + .error("setFocusModeFailed", "Error setting focus mode: null", null); + } + + @Test + public void setExposureOffset_shouldUpdateExposureOffsetFeature() { + ExposureOffsetFeature mockExposureOffsetFeature = + mockCameraFeatureFactory.createExposureOffsetFeature(mockCameraProperties); + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + + when(mockExposureOffsetFeature.getValue()).thenReturn(1.0); + + camera.setExposureOffset(mockResult, 1.0); + + verify(mockExposureOffsetFeature, times(1)).setValue(1.0); + verify(mockResult, never()).error(any(), any(), any()); + verify(mockResult, times(1)).success(1.0); + } + + @Test + public void setExposureOffset_shouldAndUpdateBuilder() { + ExposureOffsetFeature mockExposureOffsetFeature = + mockCameraFeatureFactory.createExposureOffsetFeature(mockCameraProperties); + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + + camera.setExposureOffset(mockResult, 1.0); + + verify(mockExposureOffsetFeature, times(1)).updateBuilder(any()); + } + + @Test + public void setExposureOffset_shouldCallErrorOnResultOnCameraAccessException() + throws CameraAccessException { + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + when(mockCaptureSession.setRepeatingRequest(any(), any(), any())) + .thenThrow(new CameraAccessException(0, "")); + + camera.setExposureOffset(mockResult, 1.0); + + verify(mockResult, never()).success(any()); + verify(mockResult, times(1)) + .error("setExposureOffsetFailed", "Could not set exposure offset.", null); + } + + @Test + public void lockCaptureOrientation_shouldLockCaptureOrientation() { + final Activity mockActivity = mock(Activity.class); + SensorOrientationFeature mockSensorOrientationFeature = + mockCameraFeatureFactory.createSensorOrientationFeature( + mockCameraProperties, mockActivity, mockDartMessenger); + + camera.lockCaptureOrientation(PlatformChannel.DeviceOrientation.PORTRAIT_UP); + + verify(mockSensorOrientationFeature, times(1)) + .lockCaptureOrientation(PlatformChannel.DeviceOrientation.PORTRAIT_UP); + } + + @Test + public void unlockCaptureOrientation_shouldUnlockCaptureOrientation() { + final Activity mockActivity = mock(Activity.class); + SensorOrientationFeature mockSensorOrientationFeature = + mockCameraFeatureFactory.createSensorOrientationFeature( + mockCameraProperties, mockActivity, mockDartMessenger); + + camera.unlockCaptureOrientation(); + + verify(mockSensorOrientationFeature, times(1)).unlockCaptureOrientation(); + } + + @Test + public void pausePreview_shouldPausePreview() throws CameraAccessException { + camera.pausePreview(); + + camera.pausedPreview = true; + verify(mockCaptureSession, times(1)).stopRepeating(); + } + + @Test + public void resumePreview_shouldResumePreview() throws CameraAccessException { + camera.resumePreview(); + + camera.pausedPreview = false; + verify(mockCaptureSession, times(1)).setRepeatingRequest(any(), any(), any()); + } + + @Test + public void resumePreview_shouldSendErrorEventOnCameraAccessException() + throws CameraAccessException { + when(mockCaptureSession.setRepeatingRequest(any(), any(), any())) + .thenThrow(new CameraAccessException(0)); + + camera.resumePreview(); + + verify(mockDartMessenger, times(1)).sendCameraErrorEvent(any()); + } + + @Test + public void startBackgroundThread_shouldStartNewThread() { + camera.startBackgroundThread(); + + verify(mockHandlerThread, times(1)).start(); + assertEquals(mockHandler, camera.backgroundHandler); + } + + @Test + public void startBackgroundThread_shouldNotStartNewThreadWhenAlreadyCreated() { + camera.startBackgroundThread(); + camera.startBackgroundThread(); + + verify(mockHandlerThread, times(1)).start(); + } + + @Test + public void stopBackgroundThread_quitsSafely() throws InterruptedException { + camera.startBackgroundThread(); + camera.stopBackgroundThread(); + + verify(mockHandlerThread).quitSafely(); + verify(mockHandlerThread, never()).join(); + } + + @Test + public void onConverge_shouldTakePictureWithoutAbortingSession() throws CameraAccessException { + ArrayList mockRequestBuilders = new ArrayList<>(); + mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); + CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders); + // Stub out other features used by the flow. + camera.cameraDevice = fakeCamera; + camera.pictureImageReader = mock(ImageReader.class); + SensorOrientationFeature mockSensorOrientationFeature = + mockCameraFeatureFactory.createSensorOrientationFeature(mockCameraProperties, null, null); + DeviceOrientationManager mockDeviceOrientationManager = mock(DeviceOrientationManager.class); + when(mockSensorOrientationFeature.getDeviceOrientationManager()) + .thenReturn(mockDeviceOrientationManager); + + // Simulate a post-precapture flow. + camera.onConverged(); + // A picture should be taken. + verify(mockCaptureSession, times(1)).capture(any(), any(), any()); + // The session shuold not be aborted as part of this flow, as this breaks capture on some + // devices, and causes delays on others. + verify(mockCaptureSession, never()).abortCaptures(); + } + + @Test + public void createCaptureSession_doesNotCloseCaptureSession() throws CameraAccessException { + Surface mockSurface = mock(Surface.class); + Size mockSize = mock(Size.class); + ArrayList mockRequestBuilders = new ArrayList<>(); + mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); + CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders); + camera.cameraDevice = fakeCamera; + + ResolutionFeature resolutionFeature = mockCameraFeatureFactory.mockResolutionFeature; + + when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); + + camera.createCaptureSession(CameraDevice.TEMPLATE_PREVIEW, mockSurface); + + verify(mockCaptureSession, never()).close(); + } + + @Test + public void createCaptureSession_shouldNotAddPictureImageSurfaceToPreviewRequest() + throws CameraAccessException { + Surface mockSurface = mock(Surface.class); + Surface mockSecondarySurface = mock(Surface.class); + Size mockSize = mock(Size.class); + ArrayList mockRequestBuilders = new ArrayList<>(); + mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); + CameraDeviceWrapper fakeCamera = spy(new FakeCameraDeviceWrapper(mockRequestBuilders)); + ImageReader mockPictureImageReader = mock(ImageReader.class); + camera.cameraDevice = fakeCamera; + camera.pictureImageReader = mockPictureImageReader; + CaptureRequest.Builder mockPreviewRequestBuilder = mock(CaptureRequest.Builder.class); + + ResolutionFeature resolutionFeature = mockCameraFeatureFactory.mockResolutionFeature; + + when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); + when(fakeCamera.createCaptureRequest(anyInt())).thenReturn(mockPreviewRequestBuilder); + when(mockPictureImageReader.getSurface()).thenReturn(mockSurface); + + // Test with preview template. + camera.createCaptureSession(CameraDevice.TEMPLATE_PREVIEW, mockSurface, mockSecondarySurface); + verify(mockPreviewRequestBuilder, times(0)).addTarget(mockSurface); + + // Test with non-preview template. + camera.createCaptureSession(CameraDevice.TEMPLATE_RECORD, mockSurface, mockSecondarySurface); + verify(mockPreviewRequestBuilder, times(0)).addTarget(mockSurface); + verify(mockPreviewRequestBuilder).addTarget(mockSecondarySurface); + } + + @Test + public void close_doesCloseCaptureSessionWhenCameraDeviceNull() { + camera.close(); + + verify(mockCaptureSession).close(); + } + + @Test + public void close_doesNotCloseCaptureSessionWhenCameraDeviceNonNull() { + ArrayList mockRequestBuilders = new ArrayList<>(); + mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); + CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders); + camera.cameraDevice = fakeCamera; + + camera.close(); + + verify(mockCaptureSession, never()).close(); + } + + @Test + public void startVideoRecording_shouldApplySettingsToMediaRecorder() + throws InterruptedException, IOException, CameraAccessException { + final Activity mockActivity = mock(Activity.class); + final TextureRegistry.SurfaceProducer mockSurfaceProducer = + mock(TextureRegistry.SurfaceProducer.class); + final String cameraName = "1"; + final ResolutionPreset resolutionPreset = ResolutionPreset.high; + final boolean enableAudio = true; + + // region These parameters should be set in android MediaRecorder. + final int fps = 15; + final int videoBitrate = 200000; + final int audioBitrate = 32000; + // endregion + + when(mockCameraProperties.getCameraName()).thenReturn(cameraName); + + final Camera.VideoCaptureSettings parameters = new Camera.VideoCaptureSettings( + resolutionPreset, enableAudio, fps, videoBitrate, audioBitrate); + + // Use a wildcard, since `new Range[] {...}` + // results in a 'Generic array creation' error. + @SuppressWarnings("unchecked") + final Range[] mockRanges = + (Range[]) new Range[] {new Range(10, 20)}; + + when(mockCameraProperties.getControlAutoExposureAvailableTargetFpsRanges()) + .thenReturn(mockRanges); + + final Context mockApplicationContext = mock(Context.class); + when(mockActivity.getApplicationContext()).thenReturn(mockApplicationContext); + + try (final MockedStatic mockFile = mockStatic(File.class); + final MockedConstruction mockMediaRecorder = + Mockito.mockConstruction(MediaRecorder.class)) { + assertNotNull(mockMediaRecorder); + + mockFile.when(() -> File.createTempFile(any(), any(), any())) + .thenReturn(new File("/tmp/file.mp4")); + + final FpsRangeFeature fpsRangeFeature = new FpsRangeFeature(mockCameraProperties); + + final Camera camera = spy(new Camera(mockActivity, mockSurfaceProducer, + mockCameraFeatureFactory, mockDartMessenger, mockCameraProperties, parameters)); + + final CamcorderProfile mockProfileLegacy = mock(CamcorderProfile.class); + mockProfileLegacy.videoFrameRate = fps; + when(camera.getRecordingProfileLegacy()).thenReturn(mockProfileLegacy); + + final SensorOrientationFeature mockSensorOrientationFeature = + mockCameraFeatureFactory.createSensorOrientationFeature(mockCameraProperties, null, null); + DeviceOrientationManager mockDeviceOrientationManager = mock(DeviceOrientationManager.class); + + when(mockSensorOrientationFeature.getDeviceOrientationManager()) + .thenReturn(mockDeviceOrientationManager); + + camera.captureSession = mockCaptureSession; + camera.previewRequestBuilder = mockPreviewRequestBuilder; + + final ArrayList mockRequestBuilders = new ArrayList<>(); + CaptureRequest.Builder mockRequestBuilder = mock(CaptureRequest.Builder.class); + mockRequestBuilders.add(mockRequestBuilder); + final SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class); + final Size mockSize = mock(Size.class); + final ImageReader mockPictureImageReader = mock(ImageReader.class); + camera.pictureImageReader = mockPictureImageReader; + final CameraDeviceWrapper fakeCamera = + new FakeCameraDeviceWrapper(mockRequestBuilders, mockCaptureSession); + + camera.cameraDevice = fakeCamera; + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + + ResolutionFeature resolutionFeature = mockCameraFeatureFactory.mockResolutionFeature; + + assertNotNull(resolutionFeature); + when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); + + camera.startVideoRecording(mockResult, null); + + // region Check that FPS parameter affects AE range at which the camera captures frames. + assertEquals(camera.cameraFeatures.getFpsRange().getValue().getLower(), Integer.valueOf(fps)); + assertEquals(camera.cameraFeatures.getFpsRange().getValue().getUpper(), Integer.valueOf(fps)); + + verify(mockRequestBuilder) + .set(eq(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE), + argThat( + (Range range) -> range.getLower() == fps && range.getUpper() == fps)); + // endregion + + final MediaRecorder recorder = camera.mediaRecorder; + + // region Check that parameters affects movies, written by MediaRecorder. + verify(recorder).setVideoFrameRate(fps); + verify(recorder).setAudioEncodingBitRate(audioBitrate); + verify(recorder).setVideoEncodingBitRate(videoBitrate); + // endregion + } + } + + @Test + public void pausePreview_doesNotCallStopRepeatingWhenCameraClosed() throws CameraAccessException { + ArrayList mockRequestBuilders = new ArrayList<>(); + mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); + CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders); + camera.cameraDevice = fakeCamera; + + camera.close(); + camera.pausePreview(); + + verify(mockCaptureSession, never()).stopRepeating(); + } + + /// Allow to use `new android.util.Range(Integer, Integer)` + private static class RangeConstruction implements Closeable { + final Map, Integer> lowers = new HashMap<>(); + final Map, Integer> uppers = new HashMap<>(); + + @SuppressWarnings({"rawtypes"}) final MockedConstruction rangeMockedConstruction; + + @SuppressWarnings({"unchecked"}) + public RangeConstruction() { + this.rangeMockedConstruction = Mockito.mockConstruction(Range.class, (mock, context) -> { + int lower = (int) context.arguments().get(0); + int upper = (int) context.arguments().get(1); + lowers.put((Range) mock, lower); + uppers.put((Range) mock, upper); + when(((Range) mock).getUpper()) + .thenReturn(lowers.getOrDefault((Range) mock, 15)); + when(((Range) mock).getLower()) + .thenReturn(uppers.getOrDefault((Range) mock, 15)); + when(mock.toString()) + .thenReturn(String.format( + "mocked [%s, %s]", lowers.getOrDefault(mock, 15), uppers.getOrDefault(mock, 15))); + }); + } + + @Override + public void close() throws IOException { + rangeMockedConstruction.close(); + } + } + + 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(FpsRangeFeature fpsRangeFeature) { + 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 = fpsRangeFeature; + 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/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index c71bb316a1e9..a12526068c36 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -3,10 +3,10 @@ description: Android implementation of the camera plugin. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.10.9+2 +version: 0.10.10 environment: - sdk: ^3.2.0 + sdk: ^3.4.0 flutter: ">=3.22.0" flutter: From f7076e30c619d5a9957541467e9b790a37788f61 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Fri, 24 May 2024 10:23:09 -0700 Subject: [PATCH 08/10] redo --- .../io/flutter/plugins/camera/CameraTest.java | 252 ++- .../plugins/camera/CameraTest.java.orig | 1364 ----------------- 2 files changed, 114 insertions(+), 1502 deletions(-) delete mode 100644 packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java.orig diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java index 31d6f48ee793..bf65f6da4d09 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java @@ -67,7 +67,8 @@ class FakeCameraDeviceWrapper implements CameraDeviceWrapper { final List captureRequests; - @Nullable final CameraCaptureSession session; + @Nullable + final CameraCaptureSession session; FakeCameraDeviceWrapper(List captureRequests) { this(captureRequests, null); @@ -103,7 +104,8 @@ public void createCaptureSession( } @Override - public void close() {} + public void close() { + } } public class CameraTest { @@ -133,8 +135,7 @@ public void before() { mockHandler = mock(Handler.class); final Activity mockActivity = mock(Activity.class); - final TextureRegistry.SurfaceProducer mockSurfaceProducer = - mock(TextureRegistry.SurfaceProducer.class); + final TextureRegistry.SurfaceProducer mockSurfaceProducer = mock(TextureRegistry.SurfaceProducer.class); final String cameraName = "1"; final ResolutionPreset resolutionPreset = ResolutionPreset.high; final boolean enableAudio = false; @@ -148,8 +149,7 @@ public void before() { // Use a wildcard, since `new Range[] {...}` // results in a 'Generic array creation' error. @SuppressWarnings("unchecked") - final Range[] mockRanges = - (Range[]) new Range[] {new Range(10, 20)}; + final Range[] mockRanges = (Range[]) new Range[] { new Range(10, 20) }; when(mockCameraProperties.getControlAutoExposureAvailableTargetFpsRanges()) .thenReturn(mockRanges); @@ -158,14 +158,13 @@ public void before() { mockCameraFeatureFactory = new TestCameraFeatureFactory(fpsRangeFeature); - camera = - new Camera( - mockActivity, - mockSurfaceProducer, - mockCameraFeatureFactory, - mockDartMessenger, - mockCameraProperties, - new Camera.VideoCaptureSettings(resolutionPreset, enableAudio)); + camera = new Camera( + mockActivity, + mockSurfaceProducer, + mockCameraFeatureFactory, + mockDartMessenger, + mockCameraProperties, + new Camera.VideoCaptureSettings(resolutionPreset, enableAudio)); final CamcorderProfile mockProfileLegacy = mock(CamcorderProfile.class); mockProfileLegacy.videoFrameRate = 15; @@ -193,8 +192,7 @@ public void shouldNotImplementLifecycleObserverInterface() { @Test public void shouldCreateCameraPluginAndSetAllFeatures() { final Activity mockActivity = mock(Activity.class); - final TextureRegistry.SurfaceProducer mockSurfaceProducer = - mock(TextureRegistry.SurfaceProducer.class); + final TextureRegistry.SurfaceProducer mockSurfaceProducer = mock(TextureRegistry.SurfaceProducer.class); final CameraFeatureFactory spyMockCameraFeatureFactory = spy(mockCameraFeatureFactory); final String cameraName = "1"; final ResolutionPreset resolutionPreset = ResolutionPreset.high; @@ -205,14 +203,13 @@ public void shouldCreateCameraPluginAndSetAllFeatures() { when(spyMockCameraFeatureFactory.createSensorOrientationFeature(any(), any(), any())) .thenReturn(mockSensorOrientationFeature); - Camera camera = - new Camera( - mockActivity, - mockSurfaceProducer, - spyMockCameraFeatureFactory, - mockDartMessenger, - mockCameraProperties, - new Camera.VideoCaptureSettings(resolutionPreset, enableAudio)); + Camera camera = new Camera( + mockActivity, + mockSurfaceProducer, + spyMockCameraFeatureFactory, + mockDartMessenger, + mockCameraProperties, + new Camera.VideoCaptureSettings(resolutionPreset, enableAudio)); verify(spyMockCameraFeatureFactory, times(1)) .createSensorOrientationFeature(mockCameraProperties, mockActivity, mockDartMessenger); @@ -235,8 +232,8 @@ public void shouldCreateCameraPluginAndSetAllFeatures() { @Test public void getDeviceOrientationManager() { - SensorOrientationFeature mockSensorOrientationFeature = - mockCameraFeatureFactory.createSensorOrientationFeature(mockCameraProperties, null, null); + SensorOrientationFeature mockSensorOrientationFeature = mockCameraFeatureFactory + .createSensorOrientationFeature(mockCameraProperties, null, null); DeviceOrientationManager mockDeviceOrientationManager = mock(DeviceOrientationManager.class); when(mockSensorOrientationFeature.getDeviceOrientationManager()) @@ -250,8 +247,8 @@ public void getDeviceOrientationManager() { @Test public void getExposureOffsetStepSize() { - ExposureOffsetFeature mockExposureOffsetFeature = - mockCameraFeatureFactory.createExposureOffsetFeature(mockCameraProperties); + ExposureOffsetFeature mockExposureOffsetFeature = mockCameraFeatureFactory + .createExposureOffsetFeature(mockCameraProperties); double stepSize = 2.3; when(mockExposureOffsetFeature.getExposureOffsetStepSize()).thenReturn(stepSize); @@ -264,8 +261,8 @@ public void getExposureOffsetStepSize() { @Test public void getMaxExposureOffset() { - ExposureOffsetFeature mockExposureOffsetFeature = - mockCameraFeatureFactory.createExposureOffsetFeature(mockCameraProperties); + ExposureOffsetFeature mockExposureOffsetFeature = mockCameraFeatureFactory + .createExposureOffsetFeature(mockCameraProperties); double expectedMaxOffset = 42.0; when(mockExposureOffsetFeature.getMaxExposureOffset()).thenReturn(expectedMaxOffset); @@ -278,8 +275,8 @@ public void getMaxExposureOffset() { @Test public void getMinExposureOffset() { - ExposureOffsetFeature mockExposureOffsetFeature = - mockCameraFeatureFactory.createExposureOffsetFeature(mockCameraProperties); + ExposureOffsetFeature mockExposureOffsetFeature = mockCameraFeatureFactory + .createExposureOffsetFeature(mockCameraProperties); double expectedMinOffset = 21.5; when(mockExposureOffsetFeature.getMinExposureOffset()).thenReturn(21.5); @@ -292,8 +289,7 @@ public void getMinExposureOffset() { @Test public void getMaxZoomLevel() { - ZoomLevelFeature mockZoomLevelFeature = - mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); + ZoomLevelFeature mockZoomLevelFeature = mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); float expectedMaxZoomLevel = 4.2f; when(mockZoomLevelFeature.getMaximumZoomLevel()).thenReturn(expectedMaxZoomLevel); @@ -306,8 +302,7 @@ public void getMaxZoomLevel() { @Test public void getMinZoomLevel() { - ZoomLevelFeature mockZoomLevelFeature = - mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); + ZoomLevelFeature mockZoomLevelFeature = mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); float expectedMinZoomLevel = 4.2f; when(mockZoomLevelFeature.getMinimumZoomLevel()).thenReturn(expectedMinZoomLevel); @@ -320,8 +315,8 @@ public void getMinZoomLevel() { @Test public void setExposureMode_shouldUpdateExposureLockFeature() { - ExposureLockFeature mockExposureLockFeature = - mockCameraFeatureFactory.createExposureLockFeature(mockCameraProperties); + ExposureLockFeature mockExposureLockFeature = mockCameraFeatureFactory + .createExposureLockFeature(mockCameraProperties); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); ExposureMode exposureMode = ExposureMode.locked; @@ -334,8 +329,8 @@ public void setExposureMode_shouldUpdateExposureLockFeature() { @Test public void setExposureMode_shouldUpdateBuilder() { - ExposureLockFeature mockExposureLockFeature = - mockCameraFeatureFactory.createExposureLockFeature(mockCameraProperties); + ExposureLockFeature mockExposureLockFeature = mockCameraFeatureFactory + .createExposureLockFeature(mockCameraProperties); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); ExposureMode exposureMode = ExposureMode.locked; @@ -362,9 +357,8 @@ public void setExposureMode_shouldCallErrorOnResultOnCameraAccessException() @Test public void setExposurePoint_shouldUpdateExposurePointFeature() { SensorOrientationFeature mockSensorOrientationFeature = mock(SensorOrientationFeature.class); - ExposurePointFeature mockExposurePointFeature = - mockCameraFeatureFactory.createExposurePointFeature( - mockCameraProperties, mockSensorOrientationFeature); + ExposurePointFeature mockExposurePointFeature = mockCameraFeatureFactory.createExposurePointFeature( + mockCameraProperties, mockSensorOrientationFeature); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); Point point = new Point(42d, 42d); @@ -378,9 +372,8 @@ public void setExposurePoint_shouldUpdateExposurePointFeature() { @Test public void setExposurePoint_shouldUpdateBuilder() { SensorOrientationFeature mockSensorOrientationFeature = mock(SensorOrientationFeature.class); - ExposurePointFeature mockExposurePointFeature = - mockCameraFeatureFactory.createExposurePointFeature( - mockCameraProperties, mockSensorOrientationFeature); + ExposurePointFeature mockExposurePointFeature = mockCameraFeatureFactory.createExposurePointFeature( + mockCameraProperties, mockSensorOrientationFeature); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); Point point = new Point(42d, 42d); @@ -406,8 +399,7 @@ public void setExposurePoint_shouldCallErrorOnResultOnCameraAccessException() @Test public void setFlashMode_shouldUpdateFlashFeature() { - FlashFeature mockFlashFeature = - mockCameraFeatureFactory.createFlashFeature(mockCameraProperties); + FlashFeature mockFlashFeature = mockCameraFeatureFactory.createFlashFeature(mockCameraProperties); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); FlashMode flashMode = FlashMode.always; @@ -420,8 +412,7 @@ public void setFlashMode_shouldUpdateFlashFeature() { @Test public void setFlashMode_shouldUpdateBuilder() { - FlashFeature mockFlashFeature = - mockCameraFeatureFactory.createFlashFeature(mockCameraProperties); + FlashFeature mockFlashFeature = mockCameraFeatureFactory.createFlashFeature(mockCameraProperties); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); FlashMode flashMode = FlashMode.always; @@ -447,11 +438,10 @@ public void setFlashMode_shouldCallErrorOnResultOnCameraAccessException() @Test public void setFocusPoint_shouldUpdateFocusPointFeature() { SensorOrientationFeature mockSensorOrientationFeature = mock(SensorOrientationFeature.class); - FocusPointFeature mockFocusPointFeature = - mockCameraFeatureFactory.createFocusPointFeature( - mockCameraProperties, mockSensorOrientationFeature); - AutoFocusFeature mockAutoFocusFeature = - mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, false); + FocusPointFeature mockFocusPointFeature = mockCameraFeatureFactory.createFocusPointFeature( + mockCameraProperties, mockSensorOrientationFeature); + AutoFocusFeature mockAutoFocusFeature = mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, + false); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); Point point = new Point(42d, 42d); when(mockAutoFocusFeature.getValue()).thenReturn(FocusMode.auto); @@ -466,11 +456,10 @@ public void setFocusPoint_shouldUpdateFocusPointFeature() { @Test public void setFocusPoint_shouldUpdateBuilder() { SensorOrientationFeature mockSensorOrientationFeature = mock(SensorOrientationFeature.class); - FocusPointFeature mockFocusPointFeature = - mockCameraFeatureFactory.createFocusPointFeature( - mockCameraProperties, mockSensorOrientationFeature); - AutoFocusFeature mockAutoFocusFeature = - mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, false); + FocusPointFeature mockFocusPointFeature = mockCameraFeatureFactory.createFocusPointFeature( + mockCameraProperties, mockSensorOrientationFeature); + AutoFocusFeature mockAutoFocusFeature = mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, + false); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); Point point = new Point(42d, 42d); when(mockAutoFocusFeature.getValue()).thenReturn(FocusMode.auto); @@ -483,8 +472,8 @@ public void setFocusPoint_shouldUpdateBuilder() { @Test public void setFocusPoint_shouldCallErrorOnResultOnCameraAccessException() throws CameraAccessException { - AutoFocusFeature mockAutoFocusFeature = - mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, false); + AutoFocusFeature mockAutoFocusFeature = mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, + false); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); Point point = new Point(42d, 42d); when(mockAutoFocusFeature.getValue()).thenReturn(FocusMode.auto); @@ -499,8 +488,7 @@ public void setFocusPoint_shouldCallErrorOnResultOnCameraAccessException() @Test public void setZoomLevel_shouldUpdateZoomLevelFeature() throws CameraAccessException { - ZoomLevelFeature mockZoomLevelFeature = - mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); + ZoomLevelFeature mockZoomLevelFeature = mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); float zoomLevel = 1.0f; @@ -517,8 +505,7 @@ public void setZoomLevel_shouldUpdateZoomLevelFeature() throws CameraAccessExcep @Test public void setZoomLevel_shouldUpdateBuilder() throws CameraAccessException { - ZoomLevelFeature mockZoomLevelFeature = - mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); + ZoomLevelFeature mockZoomLevelFeature = mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); float zoomLevel = 1.0f; @@ -534,8 +521,7 @@ public void setZoomLevel_shouldUpdateBuilder() throws CameraAccessException { @Test public void setZoomLevel_shouldCallErrorOnResultOnCameraAccessException() throws CameraAccessException { - ZoomLevelFeature mockZoomLevelFeature = - mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); + ZoomLevelFeature mockZoomLevelFeature = mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); float zoomLevel = 1.0f; @@ -591,8 +577,7 @@ public void pauseVideoRecording_shouldSendVideoRecordingFailedErrorWhenVersionCo } @Test - public void - pauseVideoRecording_shouldSendVideoRecordingFailedErrorWhenMediaRecorderPauseThrowsIllegalStateException() { + public void pauseVideoRecording_shouldSendVideoRecordingFailedErrorWhenMediaRecorderPauseThrowsIllegalStateException() { MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); camera.mediaRecorder = mockMediaRecorder; camera.recordingVideo = true; @@ -764,8 +749,8 @@ public void startPreviewWithImageStream_shouldPullStreamsFromImageReaders() verify(mockImageStreamReader, times(1)) .getSurface(); // stream pulled from image streaming imageReader's surface. verify( - mockPictureImageReader, - times(2)) // we expect one call to start the capture, one to create the capture session. + mockPictureImageReader, + times(2)) // we expect one call to start the capture, one to create the capture session. .getSurface(); // stream pulled from regular imageReader's surface. } @@ -782,8 +767,7 @@ public void setDescriptionWhileRecording_shouldErrorWhenNotRecording() { } @Test - public void - resumeVideoRecording_shouldSendVideoRecordingFailedErrorWhenVersionCodeSmallerThanN() { + public void resumeVideoRecording_shouldSendVideoRecordingFailedErrorWhenVersionCodeSmallerThanN() { camera.recordingVideo = true; SdkCapabilityChecker.SDK_VERSION = 23; @@ -797,8 +781,7 @@ public void setDescriptionWhileRecording_shouldErrorWhenNotRecording() { } @Test - public void - resumeVideoRecording_shouldSendVideoRecordingFailedErrorWhenMediaRecorderPauseThrowsIllegalStateException() { + public void resumeVideoRecording_shouldSendVideoRecordingFailedErrorWhenMediaRecorderPauseThrowsIllegalStateException() { MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); camera.mediaRecorder = mockMediaRecorder; camera.recordingVideo = true; @@ -818,8 +801,8 @@ public void setDescriptionWhileRecording_shouldErrorWhenNotRecording() { @Test public void setFocusMode_shouldUpdateAutoFocusFeature() { - AutoFocusFeature mockAutoFocusFeature = - mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, false); + AutoFocusFeature mockAutoFocusFeature = mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, + false); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); camera.setFocusMode(mockResult, FocusMode.auto); @@ -831,8 +814,8 @@ public void setFocusMode_shouldUpdateAutoFocusFeature() { @Test public void setFocusMode_shouldUpdateBuilder() { - AutoFocusFeature mockAutoFocusFeature = - mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, false); + AutoFocusFeature mockAutoFocusFeature = mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, + false); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); camera.setFocusMode(mockResult, FocusMode.auto); @@ -893,8 +876,8 @@ public void startVideoRecording_shouldPullStreamsFromMediaRecorderAndImageReader verify(mockMediaRecorder, times(1)) .getSurface(); // stream pulled from media recorder's surface. verify( - mockPictureImageReader, - times(2)) // we expect one call to start the capture, one to create the capture session. + mockPictureImageReader, + times(2)) // we expect one call to start the capture, one to create the capture session. .getSurface(); // stream pulled from image streaming imageReader's surface. } @@ -940,8 +923,8 @@ public void setFocusMode_shouldCallErrorOnResultOnCameraAccessException() @Test public void setExposureOffset_shouldUpdateExposureOffsetFeature() { - ExposureOffsetFeature mockExposureOffsetFeature = - mockCameraFeatureFactory.createExposureOffsetFeature(mockCameraProperties); + ExposureOffsetFeature mockExposureOffsetFeature = mockCameraFeatureFactory + .createExposureOffsetFeature(mockCameraProperties); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); when(mockExposureOffsetFeature.getValue()).thenReturn(1.0); @@ -955,8 +938,8 @@ public void setExposureOffset_shouldUpdateExposureOffsetFeature() { @Test public void setExposureOffset_shouldAndUpdateBuilder() { - ExposureOffsetFeature mockExposureOffsetFeature = - mockCameraFeatureFactory.createExposureOffsetFeature(mockCameraProperties); + ExposureOffsetFeature mockExposureOffsetFeature = mockCameraFeatureFactory + .createExposureOffsetFeature(mockCameraProperties); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); camera.setExposureOffset(mockResult, 1.0); @@ -981,9 +964,8 @@ public void setExposureOffset_shouldCallErrorOnResultOnCameraAccessException() @Test public void lockCaptureOrientation_shouldLockCaptureOrientation() { final Activity mockActivity = mock(Activity.class); - SensorOrientationFeature mockSensorOrientationFeature = - mockCameraFeatureFactory.createSensorOrientationFeature( - mockCameraProperties, mockActivity, mockDartMessenger); + SensorOrientationFeature mockSensorOrientationFeature = mockCameraFeatureFactory.createSensorOrientationFeature( + mockCameraProperties, mockActivity, mockDartMessenger); camera.lockCaptureOrientation(PlatformChannel.DeviceOrientation.PORTRAIT_UP); @@ -994,9 +976,8 @@ public void lockCaptureOrientation_shouldLockCaptureOrientation() { @Test public void unlockCaptureOrientation_shouldUnlockCaptureOrientation() { final Activity mockActivity = mock(Activity.class); - SensorOrientationFeature mockSensorOrientationFeature = - mockCameraFeatureFactory.createSensorOrientationFeature( - mockCameraProperties, mockActivity, mockDartMessenger); + SensorOrientationFeature mockSensorOrientationFeature = mockCameraFeatureFactory.createSensorOrientationFeature( + mockCameraProperties, mockActivity, mockDartMessenger); camera.unlockCaptureOrientation(); @@ -1063,8 +1044,8 @@ public void onConverge_shouldTakePictureWithoutAbortingSession() throws CameraAc // Stub out other features used by the flow. camera.cameraDevice = fakeCamera; camera.pictureImageReader = mock(ImageReader.class); - SensorOrientationFeature mockSensorOrientationFeature = - mockCameraFeatureFactory.createSensorOrientationFeature(mockCameraProperties, null, null); + SensorOrientationFeature mockSensorOrientationFeature = mockCameraFeatureFactory + .createSensorOrientationFeature(mockCameraProperties, null, null); DeviceOrientationManager mockDeviceOrientationManager = mock(DeviceOrientationManager.class); when(mockSensorOrientationFeature.getDeviceOrientationManager()) .thenReturn(mockDeviceOrientationManager); @@ -1073,7 +1054,8 @@ public void onConverge_shouldTakePictureWithoutAbortingSession() throws CameraAc camera.onConverged(); // A picture should be taken. verify(mockCaptureSession, times(1)).capture(any(), any(), any()); - // The session shuold not be aborted as part of this flow, as this breaks capture on some + // The session shuold not be aborted as part of this flow, as this breaks + // capture on some // devices, and causes delays on others. verify(mockCaptureSession, never()).abortCaptures(); } @@ -1149,8 +1131,7 @@ public void close_doesNotCloseCaptureSessionWhenCameraDeviceNonNull() { public void startVideoRecording_shouldApplySettingsToMediaRecorder() throws InterruptedException, IOException, CameraAccessException { final Activity mockActivity = mock(Activity.class); - final TextureRegistry.SurfaceProducer mockSurfaceProducer = - mock(TextureRegistry.SurfaceProducer.class); + final TextureRegistry.SurfaceProducer mockSurfaceProducer = mock(TextureRegistry.SurfaceProducer.class); final String cameraName = "1"; final ResolutionPreset resolutionPreset = ResolutionPreset.high; final boolean enableAudio = true; @@ -1163,15 +1144,13 @@ public void startVideoRecording_shouldApplySettingsToMediaRecorder() when(mockCameraProperties.getCameraName()).thenReturn(cameraName); - final Camera.VideoCaptureSettings parameters = - new Camera.VideoCaptureSettings( - resolutionPreset, enableAudio, fps, videoBitrate, audioBitrate); + final Camera.VideoCaptureSettings parameters = new Camera.VideoCaptureSettings( + resolutionPreset, enableAudio, fps, videoBitrate, audioBitrate); // Use a wildcard, since `new Range[] {...}` // results in a 'Generic array creation' error. @SuppressWarnings("unchecked") - final Range[] mockRanges = - (Range[]) new Range[] {new Range(10, 20)}; + final Range[] mockRanges = (Range[]) new Range[] { new Range(10, 20) }; when(mockCameraProperties.getControlAutoExposureAvailableTargetFpsRanges()) .thenReturn(mockRanges); @@ -1180,8 +1159,7 @@ public void startVideoRecording_shouldApplySettingsToMediaRecorder() when(mockActivity.getApplicationContext()).thenReturn(mockApplicationContext); try (final MockedStatic mockFile = mockStatic(File.class); - final MockedConstruction mockMediaRecorder = - Mockito.mockConstruction(MediaRecorder.class)) { + final MockedConstruction mockMediaRecorder = Mockito.mockConstruction(MediaRecorder.class)) { assertNotNull(mockMediaRecorder); mockFile @@ -1190,22 +1168,21 @@ public void startVideoRecording_shouldApplySettingsToMediaRecorder() final FpsRangeFeature fpsRangeFeature = new FpsRangeFeature(mockCameraProperties); - final Camera camera = - spy( - new Camera( - mockActivity, - mockSurfaceProducer, - mockCameraFeatureFactory, - mockDartMessenger, - mockCameraProperties, - parameters)); + final Camera camera = spy( + new Camera( + mockActivity, + mockSurfaceProducer, + mockCameraFeatureFactory, + mockDartMessenger, + mockCameraProperties, + parameters)); final CamcorderProfile mockProfileLegacy = mock(CamcorderProfile.class); mockProfileLegacy.videoFrameRate = fps; when(camera.getRecordingProfileLegacy()).thenReturn(mockProfileLegacy); - final SensorOrientationFeature mockSensorOrientationFeature = - mockCameraFeatureFactory.createSensorOrientationFeature(mockCameraProperties, null, null); + final SensorOrientationFeature mockSensorOrientationFeature = mockCameraFeatureFactory + .createSensorOrientationFeature(mockCameraProperties, null, null); DeviceOrientationManager mockDeviceOrientationManager = mock(DeviceOrientationManager.class); when(mockSensorOrientationFeature.getDeviceOrientationManager()) @@ -1221,8 +1198,7 @@ public void startVideoRecording_shouldApplySettingsToMediaRecorder() final Size mockSize = mock(Size.class); final ImageReader mockPictureImageReader = mock(ImageReader.class); camera.pictureImageReader = mockPictureImageReader; - final CameraDeviceWrapper fakeCamera = - new FakeCameraDeviceWrapper(mockRequestBuilders, mockCaptureSession); + final CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders, mockCaptureSession); camera.cameraDevice = fakeCamera; MethodChannel.Result mockResult = mock(MethodChannel.Result.class); @@ -1234,7 +1210,8 @@ public void startVideoRecording_shouldApplySettingsToMediaRecorder() camera.startVideoRecording(mockResult, null); - // region Check that FPS parameter affects AE range at which the camera captures frames. + // region Check that FPS parameter affects AE range at which the camera captures + // frames. assertEquals(camera.cameraFeatures.getFpsRange().getValue().getLower(), Integer.valueOf(fps)); assertEquals(camera.cameraFeatures.getFpsRange().getValue().getUpper(), Integer.valueOf(fps)); @@ -1273,29 +1250,28 @@ private static class RangeConstruction implements Closeable { final Map, Integer> lowers = new HashMap<>(); final Map, Integer> uppers = new HashMap<>(); - @SuppressWarnings({"rawtypes"}) + @SuppressWarnings({ "rawtypes" }) final MockedConstruction rangeMockedConstruction; - @SuppressWarnings({"unchecked"}) + @SuppressWarnings({ "unchecked" }) public RangeConstruction() { - this.rangeMockedConstruction = - Mockito.mockConstruction( - Range.class, - (mock, context) -> { - int lower = (int) context.arguments().get(0); - int upper = (int) context.arguments().get(1); - lowers.put((Range) mock, lower); - uppers.put((Range) mock, upper); - when(((Range) mock).getUpper()) - .thenReturn(lowers.getOrDefault((Range) mock, 15)); - when(((Range) mock).getLower()) - .thenReturn(uppers.getOrDefault((Range) mock, 15)); - when(mock.toString()) - .thenReturn( - String.format( - "mocked [%s, %s]", - lowers.getOrDefault(mock, 15), uppers.getOrDefault(mock, 15))); - }); + this.rangeMockedConstruction = Mockito.mockConstruction( + Range.class, + (mock, context) -> { + int lower = (int) context.arguments().get(0); + int upper = (int) context.arguments().get(1); + lowers.put((Range) mock, lower); + uppers.put((Range) mock, upper); + when(((Range) mock).getUpper()) + .thenReturn(lowers.getOrDefault((Range) mock, 15)); + when(((Range) mock).getLower()) + .thenReturn(uppers.getOrDefault((Range) mock, 15)); + when(mock.toString()) + .thenReturn( + String.format( + "mocked [%s, %s]", + lowers.getOrDefault(mock, 15), uppers.getOrDefault(mock, 15))); + }); } @Override diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java.orig b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java.orig deleted file mode 100644 index 9c8071ad264a..000000000000 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java.orig +++ /dev/null @@ -1,1364 +0,0 @@ -// 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.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; - -import android.app.Activity; -import android.content.Context; -import android.graphics.SurfaceTexture; -import android.hardware.camera2.*; -import android.hardware.camera2.params.SessionConfiguration; -import android.media.CamcorderProfile; -import android.media.ImageReader; -import android.media.MediaRecorder; -import android.os.Build; -import android.os.Handler; -import android.os.HandlerThread; -import android.util.Range; -import android.util.Size; -import android.view.Surface; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.lifecycle.LifecycleObserver; -import io.flutter.embedding.engine.systemchannels.PlatformChannel; -import io.flutter.plugin.common.EventChannel; -import io.flutter.plugin.common.MethodChannel; -import io.flutter.plugins.camera.features.CameraFeatureFactory; -import io.flutter.plugins.camera.features.Point; -import io.flutter.plugins.camera.features.autofocus.AutoFocusFeature; -import io.flutter.plugins.camera.features.autofocus.FocusMode; -import io.flutter.plugins.camera.features.exposurelock.ExposureLockFeature; -import io.flutter.plugins.camera.features.exposurelock.ExposureMode; -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.flash.FlashMode; -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.DeviceOrientationManager; -import io.flutter.plugins.camera.features.sensororientation.SensorOrientationFeature; -import io.flutter.plugins.camera.features.zoomlevel.ZoomLevelFeature; -import io.flutter.plugins.camera.media.ImageStreamReader; -import io.flutter.view.TextureRegistry; -import java.io.Closeable; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.MockedConstruction; -import org.mockito.MockedStatic; -import org.mockito.Mockito; - -class FakeCameraDeviceWrapper implements CameraDeviceWrapper { - final List captureRequests; - @Nullable final CameraCaptureSession session; - - FakeCameraDeviceWrapper(List captureRequests) { - this(captureRequests, null); - } - - FakeCameraDeviceWrapper( - List captureRequests, CameraCaptureSession session) { - this.captureRequests = captureRequests; - this.session = session; - } - - @NonNull - @Override - public CaptureRequest.Builder createCaptureRequest(int var1) { - return captureRequests.remove(0); - } - - @Override - public void createCaptureSession(SessionConfiguration config) { - if (session != null) { - config.getStateCallback().onConfigured(session); - } - } - - @Override - public void createCaptureSession(@NonNull List outputs, - @NonNull CameraCaptureSession.StateCallback callback, @Nullable Handler handler) { - if (session != null) { - callback.onConfigured(session); - } - } - - @Override - public void close() {} -} - -public class CameraTest { - private CameraProperties mockCameraProperties; - private TestCameraFeatureFactory mockCameraFeatureFactory; - private DartMessenger mockDartMessenger; - private Camera camera; - private CameraCaptureSession mockCaptureSession; - private CaptureRequest.Builder mockPreviewRequestBuilder; - private MockedStatic mockHandlerThreadFactory; - private HandlerThread mockHandlerThread; - private MockedStatic mockHandlerFactory; - private Handler mockHandler; - - private RangeConstruction mockRangeConstruction; - - @Before - public void before() { - mockRangeConstruction = new RangeConstruction(); - mockCameraProperties = mock(CameraProperties.class); - mockDartMessenger = mock(DartMessenger.class); - mockCaptureSession = mock(CameraCaptureSession.class); - mockPreviewRequestBuilder = mock(CaptureRequest.Builder.class); - mockHandlerThreadFactory = mockStatic(Camera.HandlerThreadFactory.class); - mockHandlerThread = mock(HandlerThread.class); - mockHandlerFactory = mockStatic(Camera.HandlerFactory.class); - mockHandler = mock(Handler.class); - - final Activity mockActivity = mock(Activity.class); - final TextureRegistry.SurfaceProducer mockSurfaceProducer = - mock(TextureRegistry.SurfaceProducer.class); - final String cameraName = "1"; - final ResolutionPreset resolutionPreset = ResolutionPreset.high; - final boolean enableAudio = false; - - when(mockCameraProperties.getCameraName()).thenReturn(cameraName); - mockHandlerFactory.when(() -> Camera.HandlerFactory.create(any())).thenReturn(mockHandler); - mockHandlerThreadFactory.when(() -> Camera.HandlerThreadFactory.create(any())) - .thenReturn(mockHandlerThread); - - // Use a wildcard, since `new Range[] {...}` - // results in a 'Generic array creation' error. - @SuppressWarnings("unchecked") - final Range[] mockRanges = - (Range[]) new Range[] {new Range(10, 20)}; - - when(mockCameraProperties.getControlAutoExposureAvailableTargetFpsRanges()) - .thenReturn(mockRanges); - - final FpsRangeFeature fpsRangeFeature = new FpsRangeFeature(mockCameraProperties); - - mockCameraFeatureFactory = new TestCameraFeatureFactory(fpsRangeFeature); - - camera = - new Camera(mockActivity, mockSurfaceProducer, mockCameraFeatureFactory, mockDartMessenger, - mockCameraProperties, new Camera.VideoCaptureSettings(resolutionPreset, enableAudio)); - - final CamcorderProfile mockProfileLegacy = mock(CamcorderProfile.class); - mockProfileLegacy.videoFrameRate = 15; - when(camera.getRecordingProfileLegacy()).thenReturn(mockProfileLegacy); - - camera.captureSession = mockCaptureSession; - camera.previewRequestBuilder = mockPreviewRequestBuilder; - } - - @After - public void after() throws IOException { - SdkCapabilityChecker.SDK_VERSION = 0; - mockHandlerThreadFactory.close(); - mockHandlerFactory.close(); - mockRangeConstruction.close(); - } - - @Test - public void shouldNotImplementLifecycleObserverInterface() { - Class cameraClass = Camera.class; - - assertFalse(LifecycleObserver.class.isAssignableFrom(cameraClass)); - } - - @Test - public void shouldCreateCameraPluginAndSetAllFeatures() { - final Activity mockActivity = mock(Activity.class); - final TextureRegistry.SurfaceProducer mockSurfaceProducer = - mock(TextureRegistry.SurfaceProducer.class); - final CameraFeatureFactory spyMockCameraFeatureFactory = spy(mockCameraFeatureFactory); - final String cameraName = "1"; - final ResolutionPreset resolutionPreset = ResolutionPreset.high; - final boolean enableAudio = false; - - when(mockCameraProperties.getCameraName()).thenReturn(cameraName); - SensorOrientationFeature mockSensorOrientationFeature = mock(SensorOrientationFeature.class); - when(spyMockCameraFeatureFactory.createSensorOrientationFeature(any(), any(), any())) - .thenReturn(mockSensorOrientationFeature); - - Camera camera = new Camera(mockActivity, mockSurfaceProducer, spyMockCameraFeatureFactory, - mockDartMessenger, mockCameraProperties, - new Camera.VideoCaptureSettings(resolutionPreset, enableAudio)); - - verify(spyMockCameraFeatureFactory, times(1)) - .createSensorOrientationFeature(mockCameraProperties, mockActivity, mockDartMessenger); - verify(spyMockCameraFeatureFactory, times(1)) - .createAutoFocusFeature(mockCameraProperties, false); - verify(spyMockCameraFeatureFactory, times(1)).createExposureLockFeature(mockCameraProperties); - verify(spyMockCameraFeatureFactory, times(1)) - .createExposurePointFeature(eq(mockCameraProperties), eq(mockSensorOrientationFeature)); - verify(spyMockCameraFeatureFactory, times(1)).createExposureOffsetFeature(mockCameraProperties); - verify(spyMockCameraFeatureFactory, times(1)).createFlashFeature(mockCameraProperties); - verify(spyMockCameraFeatureFactory, times(1)) - .createFocusPointFeature(eq(mockCameraProperties), eq(mockSensorOrientationFeature)); - verify(spyMockCameraFeatureFactory, times(1)).createFpsRangeFeature(mockCameraProperties); - verify(spyMockCameraFeatureFactory, times(1)).createNoiseReductionFeature(mockCameraProperties); - verify(spyMockCameraFeatureFactory, times(1)) - .createResolutionFeature(mockCameraProperties, resolutionPreset, cameraName); - verify(spyMockCameraFeatureFactory, times(1)).createZoomLevelFeature(mockCameraProperties); - assertNotNull("should create a camera", camera); - } - - @Test - public void getDeviceOrientationManager() { - SensorOrientationFeature mockSensorOrientationFeature = - mockCameraFeatureFactory.createSensorOrientationFeature(mockCameraProperties, null, null); - DeviceOrientationManager mockDeviceOrientationManager = mock(DeviceOrientationManager.class); - - when(mockSensorOrientationFeature.getDeviceOrientationManager()) - .thenReturn(mockDeviceOrientationManager); - - DeviceOrientationManager actualDeviceOrientationManager = camera.getDeviceOrientationManager(); - - verify(mockSensorOrientationFeature, times(1)).getDeviceOrientationManager(); - assertEquals(mockDeviceOrientationManager, actualDeviceOrientationManager); - } - - @Test - public void getExposureOffsetStepSize() { - ExposureOffsetFeature mockExposureOffsetFeature = - mockCameraFeatureFactory.createExposureOffsetFeature(mockCameraProperties); - double stepSize = 2.3; - - when(mockExposureOffsetFeature.getExposureOffsetStepSize()).thenReturn(stepSize); - - double actualSize = camera.getExposureOffsetStepSize(); - - verify(mockExposureOffsetFeature, times(1)).getExposureOffsetStepSize(); - assertEquals(stepSize, actualSize, 0); - } - - @Test - public void getMaxExposureOffset() { - ExposureOffsetFeature mockExposureOffsetFeature = - mockCameraFeatureFactory.createExposureOffsetFeature(mockCameraProperties); - double expectedMaxOffset = 42.0; - - when(mockExposureOffsetFeature.getMaxExposureOffset()).thenReturn(expectedMaxOffset); - - double actualMaxOffset = camera.getMaxExposureOffset(); - - verify(mockExposureOffsetFeature, times(1)).getMaxExposureOffset(); - assertEquals(expectedMaxOffset, actualMaxOffset, 0); - } - - @Test - public void getMinExposureOffset() { - ExposureOffsetFeature mockExposureOffsetFeature = - mockCameraFeatureFactory.createExposureOffsetFeature(mockCameraProperties); - double expectedMinOffset = 21.5; - - when(mockExposureOffsetFeature.getMinExposureOffset()).thenReturn(21.5); - - double actualMinOffset = camera.getMinExposureOffset(); - - verify(mockExposureOffsetFeature, times(1)).getMinExposureOffset(); - assertEquals(expectedMinOffset, actualMinOffset, 0); - } - - @Test - public void getMaxZoomLevel() { - ZoomLevelFeature mockZoomLevelFeature = - mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); - float expectedMaxZoomLevel = 4.2f; - - when(mockZoomLevelFeature.getMaximumZoomLevel()).thenReturn(expectedMaxZoomLevel); - - float actualMaxZoomLevel = camera.getMaxZoomLevel(); - - verify(mockZoomLevelFeature, times(1)).getMaximumZoomLevel(); - assertEquals(expectedMaxZoomLevel, actualMaxZoomLevel, 0); - } - - @Test - public void getMinZoomLevel() { - ZoomLevelFeature mockZoomLevelFeature = - mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); - float expectedMinZoomLevel = 4.2f; - - when(mockZoomLevelFeature.getMinimumZoomLevel()).thenReturn(expectedMinZoomLevel); - - float actualMinZoomLevel = camera.getMinZoomLevel(); - - verify(mockZoomLevelFeature, times(1)).getMinimumZoomLevel(); - assertEquals(expectedMinZoomLevel, actualMinZoomLevel, 0); - } - - @Test - public void setExposureMode_shouldUpdateExposureLockFeature() { - ExposureLockFeature mockExposureLockFeature = - mockCameraFeatureFactory.createExposureLockFeature(mockCameraProperties); - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - ExposureMode exposureMode = ExposureMode.locked; - - camera.setExposureMode(mockResult, exposureMode); - - verify(mockExposureLockFeature, times(1)).setValue(exposureMode); - verify(mockResult, never()).error(any(), any(), any()); - verify(mockResult, times(1)).success(null); - } - - @Test - public void setExposureMode_shouldUpdateBuilder() { - ExposureLockFeature mockExposureLockFeature = - mockCameraFeatureFactory.createExposureLockFeature(mockCameraProperties); - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - ExposureMode exposureMode = ExposureMode.locked; - - camera.setExposureMode(mockResult, exposureMode); - - verify(mockExposureLockFeature, times(1)).updateBuilder(any()); - } - - @Test - public void setExposureMode_shouldCallErrorOnResultOnCameraAccessException() - throws CameraAccessException { - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - ExposureMode exposureMode = ExposureMode.locked; - when(mockCaptureSession.setRepeatingRequest(any(), any(), any())) - .thenThrow(new CameraAccessException(0, "")); - - camera.setExposureMode(mockResult, exposureMode); - - verify(mockResult, never()).success(any()); - verify(mockResult, times(1)) - .error("setExposureModeFailed", "Could not set exposure mode.", null); - } - - @Test - public void setExposurePoint_shouldUpdateExposurePointFeature() { - SensorOrientationFeature mockSensorOrientationFeature = mock(SensorOrientationFeature.class); - ExposurePointFeature mockExposurePointFeature = - mockCameraFeatureFactory.createExposurePointFeature( - mockCameraProperties, mockSensorOrientationFeature); - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - Point point = new Point(42d, 42d); - - camera.setExposurePoint(mockResult, point); - - verify(mockExposurePointFeature, times(1)).setValue(point); - verify(mockResult, never()).error(any(), any(), any()); - verify(mockResult, times(1)).success(null); - } - - @Test - public void setExposurePoint_shouldUpdateBuilder() { - SensorOrientationFeature mockSensorOrientationFeature = mock(SensorOrientationFeature.class); - ExposurePointFeature mockExposurePointFeature = - mockCameraFeatureFactory.createExposurePointFeature( - mockCameraProperties, mockSensorOrientationFeature); - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - Point point = new Point(42d, 42d); - - camera.setExposurePoint(mockResult, point); - - verify(mockExposurePointFeature, times(1)).updateBuilder(any()); - } - - @Test - public void setExposurePoint_shouldCallErrorOnResultOnCameraAccessException() - throws CameraAccessException { - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - Point point = new Point(42d, 42d); - when(mockCaptureSession.setRepeatingRequest(any(), any(), any())) - .thenThrow(new CameraAccessException(0, "")); - - camera.setExposurePoint(mockResult, point); - - verify(mockResult, never()).success(any()); - verify(mockResult, times(1)) - .error("setExposurePointFailed", "Could not set exposure point.", null); - } - - @Test - public void setFlashMode_shouldUpdateFlashFeature() { - FlashFeature mockFlashFeature = - mockCameraFeatureFactory.createFlashFeature(mockCameraProperties); - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - FlashMode flashMode = FlashMode.always; - - camera.setFlashMode(mockResult, flashMode); - - verify(mockFlashFeature, times(1)).setValue(flashMode); - verify(mockResult, never()).error(any(), any(), any()); - verify(mockResult, times(1)).success(null); - } - - @Test - public void setFlashMode_shouldUpdateBuilder() { - FlashFeature mockFlashFeature = - mockCameraFeatureFactory.createFlashFeature(mockCameraProperties); - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - FlashMode flashMode = FlashMode.always; - - camera.setFlashMode(mockResult, flashMode); - - verify(mockFlashFeature, times(1)).updateBuilder(any()); - } - - @Test - public void setFlashMode_shouldCallErrorOnResultOnCameraAccessException() - throws CameraAccessException { - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - FlashMode flashMode = FlashMode.always; - when(mockCaptureSession.setRepeatingRequest(any(), any(), any())) - .thenThrow(new CameraAccessException(0, "")); - - camera.setFlashMode(mockResult, flashMode); - - verify(mockResult, never()).success(any()); - verify(mockResult, times(1)).error("setFlashModeFailed", "Could not set flash mode.", null); - } - - @Test - public void setFocusPoint_shouldUpdateFocusPointFeature() { - SensorOrientationFeature mockSensorOrientationFeature = mock(SensorOrientationFeature.class); - FocusPointFeature mockFocusPointFeature = mockCameraFeatureFactory.createFocusPointFeature( - mockCameraProperties, mockSensorOrientationFeature); - AutoFocusFeature mockAutoFocusFeature = - mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, false); - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - Point point = new Point(42d, 42d); - when(mockAutoFocusFeature.getValue()).thenReturn(FocusMode.auto); - - camera.setFocusPoint(mockResult, point); - - verify(mockFocusPointFeature, times(1)).setValue(point); - verify(mockResult, never()).error(any(), any(), any()); - verify(mockResult, times(1)).success(null); - } - - @Test - public void setFocusPoint_shouldUpdateBuilder() { - SensorOrientationFeature mockSensorOrientationFeature = mock(SensorOrientationFeature.class); - FocusPointFeature mockFocusPointFeature = mockCameraFeatureFactory.createFocusPointFeature( - mockCameraProperties, mockSensorOrientationFeature); - AutoFocusFeature mockAutoFocusFeature = - mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, false); - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - Point point = new Point(42d, 42d); - when(mockAutoFocusFeature.getValue()).thenReturn(FocusMode.auto); - - camera.setFocusPoint(mockResult, point); - - verify(mockFocusPointFeature, times(1)).updateBuilder(any()); - } - - @Test - public void setFocusPoint_shouldCallErrorOnResultOnCameraAccessException() - throws CameraAccessException { - AutoFocusFeature mockAutoFocusFeature = - mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, false); - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - Point point = new Point(42d, 42d); - when(mockAutoFocusFeature.getValue()).thenReturn(FocusMode.auto); - when(mockCaptureSession.setRepeatingRequest(any(), any(), any())) - .thenThrow(new CameraAccessException(0, "")); - - camera.setFocusPoint(mockResult, point); - - verify(mockResult, never()).success(any()); - verify(mockResult, times(1)).error("setFocusPointFailed", "Could not set focus point.", null); - } - - @Test - public void setZoomLevel_shouldUpdateZoomLevelFeature() throws CameraAccessException { - ZoomLevelFeature mockZoomLevelFeature = - mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - float zoomLevel = 1.0f; - - when(mockZoomLevelFeature.getValue()).thenReturn(zoomLevel); - when(mockZoomLevelFeature.getMinimumZoomLevel()).thenReturn(0f); - when(mockZoomLevelFeature.getMaximumZoomLevel()).thenReturn(2f); - - camera.setZoomLevel(mockResult, zoomLevel); - - verify(mockZoomLevelFeature, times(1)).setValue(zoomLevel); - verify(mockResult, never()).error(any(), any(), any()); - verify(mockResult, times(1)).success(null); - } - - @Test - public void setZoomLevel_shouldUpdateBuilder() throws CameraAccessException { - ZoomLevelFeature mockZoomLevelFeature = - mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - float zoomLevel = 1.0f; - - when(mockZoomLevelFeature.getValue()).thenReturn(zoomLevel); - when(mockZoomLevelFeature.getMinimumZoomLevel()).thenReturn(0f); - when(mockZoomLevelFeature.getMaximumZoomLevel()).thenReturn(2f); - - camera.setZoomLevel(mockResult, zoomLevel); - - verify(mockZoomLevelFeature, times(1)).updateBuilder(any()); - } - - @Test - public void setZoomLevel_shouldCallErrorOnResultOnCameraAccessException() - throws CameraAccessException { - ZoomLevelFeature mockZoomLevelFeature = - mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - float zoomLevel = 1.0f; - - when(mockZoomLevelFeature.getValue()).thenReturn(zoomLevel); - when(mockZoomLevelFeature.getMinimumZoomLevel()).thenReturn(0f); - when(mockZoomLevelFeature.getMaximumZoomLevel()).thenReturn(2f); - when(mockCaptureSession.setRepeatingRequest(any(), any(), any())) - .thenThrow(new CameraAccessException(0, "")); - - camera.setZoomLevel(mockResult, zoomLevel); - - verify(mockResult, never()).success(any()); - verify(mockResult, times(1)).error("setZoomLevelFailed", "Could not set zoom level.", null); - } - - @Test - public void pauseVideoRecording_shouldSendNullResultWhenNotRecording() { - camera.recordingVideo = false; - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - - camera.pauseVideoRecording(mockResult); - - verify(mockResult, times(1)).success(null); - verify(mockResult, never()).error(any(), any(), any()); - } - - @Test - public void pauseVideoRecording_shouldCallPauseWhenRecordingAndOnAPIN() { - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); - camera.mediaRecorder = mockMediaRecorder; - camera.recordingVideo = true; - SdkCapabilityChecker.SDK_VERSION = 24; - - camera.pauseVideoRecording(mockResult); - - verify(mockMediaRecorder, times(1)).pause(); - verify(mockResult, times(1)).success(null); - verify(mockResult, never()).error(any(), any(), any()); - } - - @Test - public void pauseVideoRecording_shouldSendVideoRecordingFailedErrorWhenVersionCodeSmallerThenN() { - camera.recordingVideo = true; - SdkCapabilityChecker.SDK_VERSION = 23; - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - - camera.pauseVideoRecording(mockResult); - - verify(mockResult, times(1)) - .error("videoRecordingFailed", "pauseVideoRecording requires Android API +24.", null); - verify(mockResult, never()).success(any()); - } - - @Test - public void - pauseVideoRecording_shouldSendVideoRecordingFailedErrorWhenMediaRecorderPauseThrowsIllegalStateException() { - MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); - camera.mediaRecorder = mockMediaRecorder; - camera.recordingVideo = true; - SdkCapabilityChecker.SDK_VERSION = 24; - - IllegalStateException expectedException = new IllegalStateException("Test error message"); - - doThrow(expectedException).when(mockMediaRecorder).pause(); - - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - - camera.pauseVideoRecording(mockResult); - - verify(mockResult, times(1)).error("videoRecordingFailed", "Test error message", null); - verify(mockResult, never()).success(any()); - } - - @Test - public void resumeVideoRecording_shouldSendNullResultWhenNotRecording() { - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - camera.recordingVideo = false; - - camera.resumeVideoRecording(mockResult); - - verify(mockResult, times(1)).success(null); - verify(mockResult, never()).error(any(), any(), any()); - } - - @Test - public void resumeVideoRecording_shouldCallPauseWhenRecordingAndOnAPIN() { - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); - camera.mediaRecorder = mockMediaRecorder; - camera.recordingVideo = true; - SdkCapabilityChecker.SDK_VERSION = 24; - - camera.resumeVideoRecording(mockResult); - - verify(mockMediaRecorder, times(1)).resume(); - verify(mockResult, times(1)).success(null); - verify(mockResult, never()).error(any(), any(), any()); - } - - @Test - public void setDescriptionWhileRecording_errorsWhenUnsupported() { - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); - VideoRenderer mockVideoRenderer = mock(VideoRenderer.class); - camera.mediaRecorder = mockMediaRecorder; - camera.recordingVideo = true; - camera.videoRenderer = mockVideoRenderer; - SdkCapabilityChecker.SDK_VERSION = Build.VERSION_CODES.LOLLIPOP; - - final CameraProperties newCameraProperties = mock(CameraProperties.class); - camera.setDescriptionWhileRecording(mockResult, newCameraProperties); - - verify(mockResult, times(1)) - .error(eq("setDescriptionWhileRecordingFailed"), - eq("Device does not support switching the camera while recording"), eq(null)); - } - - @Test - public void setDescriptionWhileRecording_succeedsWhenSupported() { - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); - VideoRenderer mockVideoRenderer = mock(VideoRenderer.class); - camera.mediaRecorder = mockMediaRecorder; - camera.recordingVideo = true; - camera.videoRenderer = mockVideoRenderer; - SdkCapabilityChecker.SDK_VERSION = Build.VERSION_CODES.O; - - final CameraProperties newCameraProperties = mock(CameraProperties.class); - camera.setDescriptionWhileRecording(mockResult, newCameraProperties); - - verify(mockResult, times(1)).success(null); - verify(mockResult, never()).error(any(), any(), any()); - } - - @Test - public void startPreview_shouldPullStreamFromVideoRenderer() - throws InterruptedException, CameraAccessException { - VideoRenderer mockVideoRenderer = mock(VideoRenderer.class); - ArrayList mockRequestBuilders = new ArrayList<>(); - mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); - Size mockSize = mock(Size.class); - camera.recordingVideo = true; - camera.videoRenderer = mockVideoRenderer; - CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders); - camera.cameraDevice = fakeCamera; - ImageReader mockPictureImageReader = mock(ImageReader.class); - camera.pictureImageReader = mockPictureImageReader; - - ResolutionFeature resolutionFeature = mockCameraFeatureFactory.mockResolutionFeature; - - when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); - - camera.startPreview(); - verify(mockVideoRenderer, times(1)) - .getInputSurface(); // stream pulled from videoRenderer's surface. - } - - @Test - public void startPreview_shouldPullStreamFromImageReader() - throws InterruptedException, CameraAccessException { - ArrayList mockRequestBuilders = new ArrayList<>(); - mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); - Size mockSize = mock(Size.class); - ImageReader mockImageReader = mock(ImageReader.class); - camera.recordingVideo = false; - camera.pictureImageReader = mockImageReader; - CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders); - camera.cameraDevice = fakeCamera; - - ResolutionFeature resolutionFeature = mockCameraFeatureFactory.mockResolutionFeature; - - when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); - when(mockImageReader.getSurface()).thenReturn(mock(Surface.class)); - - camera.startPreview(); - verify(mockImageReader, times(2)) // we expect two calls to start regular preview. - .getSurface(); // stream pulled from regular imageReader's surface. - } - - @Test - public void startPreview_shouldFlipRotation() throws InterruptedException, CameraAccessException { - VideoRenderer mockVideoRenderer = mock(VideoRenderer.class); - ArrayList mockRequestBuilders = new ArrayList<>(); - mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); - Size mockSize = mock(Size.class); - camera.recordingVideo = true; - camera.videoRenderer = mockVideoRenderer; - camera.initialCameraFacing = CameraMetadata.LENS_FACING_BACK; - CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders); - camera.cameraDevice = fakeCamera; - ImageReader mockPictureImageReader = mock(ImageReader.class); - camera.pictureImageReader = mockPictureImageReader; - - ResolutionFeature resolutionFeature = mockCameraFeatureFactory.mockResolutionFeature; - - when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); - when(mockCameraProperties.getLensFacing()).thenReturn(CameraMetadata.LENS_FACING_FRONT); - - camera.startPreview(); - verify(mockVideoRenderer, times(1)).setRotation(180); - } - - @Test - public void startPreviewWithImageStream_shouldPullStreamsFromImageReaders() - throws InterruptedException, CameraAccessException { - ArrayList mockRequestBuilders = new ArrayList<>(); - mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); - SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class); - Size mockSize = mock(Size.class); - ImageReader mockPictureImageReader = mock(ImageReader.class); - ImageStreamReader mockImageStreamReader = mock(ImageStreamReader.class); - camera.recordingVideo = false; - camera.pictureImageReader = mockPictureImageReader; - CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders); - camera.cameraDevice = fakeCamera; - camera.imageStreamReader = mockImageStreamReader; - - ResolutionFeature resolutionFeature = mockCameraFeatureFactory.mockResolutionFeature; - - when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); - - camera.startPreviewWithImageStream(mock(EventChannel.class)); - verify(mockImageStreamReader, times(1)) - .getSurface(); // stream pulled from image streaming imageReader's surface. - verify(mockPictureImageReader, - times(2)) // we expect one call to start the capture, one to create the capture session. - .getSurface(); // stream pulled from regular imageReader's surface. - } - - @Test - public void setDescriptionWhileRecording_shouldErrorWhenNotRecording() { - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - camera.recordingVideo = false; - final CameraProperties newCameraProperties = mock(CameraProperties.class); - camera.setDescriptionWhileRecording(mockResult, newCameraProperties); - - verify(mockResult, times(1)) - .error("setDescriptionWhileRecordingFailed", "Device was not recording", null); - verify(mockResult, never()).success(any()); - } - - @Test - public void - resumeVideoRecording_shouldSendVideoRecordingFailedErrorWhenVersionCodeSmallerThanN() { - camera.recordingVideo = true; - SdkCapabilityChecker.SDK_VERSION = 23; - - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - - camera.resumeVideoRecording(mockResult); - - verify(mockResult, times(1)) - .error("videoRecordingFailed", "resumeVideoRecording requires Android API +24.", null); - verify(mockResult, never()).success(any()); - } - - @Test - public void - resumeVideoRecording_shouldSendVideoRecordingFailedErrorWhenMediaRecorderPauseThrowsIllegalStateException() { - MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); - camera.mediaRecorder = mockMediaRecorder; - camera.recordingVideo = true; - SdkCapabilityChecker.SDK_VERSION = 24; - - IllegalStateException expectedException = new IllegalStateException("Test error message"); - - doThrow(expectedException).when(mockMediaRecorder).resume(); - - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - - camera.resumeVideoRecording(mockResult); - - verify(mockResult, times(1)).error("videoRecordingFailed", "Test error message", null); - verify(mockResult, never()).success(any()); - } - - @Test - public void setFocusMode_shouldUpdateAutoFocusFeature() { - AutoFocusFeature mockAutoFocusFeature = - mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, false); - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - - camera.setFocusMode(mockResult, FocusMode.auto); - - verify(mockAutoFocusFeature, times(1)).setValue(FocusMode.auto); - verify(mockResult, never()).error(any(), any(), any()); - verify(mockResult, times(1)).success(null); - } - - @Test - public void setFocusMode_shouldUpdateBuilder() { - AutoFocusFeature mockAutoFocusFeature = - mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, false); - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - - camera.setFocusMode(mockResult, FocusMode.auto); - - verify(mockAutoFocusFeature, times(1)).updateBuilder(any()); - } - - @Test - public void setFocusMode_shouldUnlockAutoFocusForAutoMode() { - camera.setFocusMode(mock(MethodChannel.Result.class), FocusMode.auto); - verify(mockPreviewRequestBuilder, times(1)) - .set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL); - verify(mockPreviewRequestBuilder, times(1)) - .set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_IDLE); - } - - @Test - public void setFocusMode_shouldSkipUnlockAutoFocusWhenNullCaptureSession() { - camera.captureSession = null; - camera.setFocusMode(mock(MethodChannel.Result.class), FocusMode.auto); - verify(mockPreviewRequestBuilder, never()) - .set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL); - verify(mockPreviewRequestBuilder, never()) - .set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_IDLE); - } - - @Test - public void setFocusMode_shouldSendErrorEventOnUnlockAutoFocusCameraAccessException() - throws CameraAccessException { - when(mockCaptureSession.capture(any(), any(), any())) - .thenThrow(new CameraAccessException(0, "")); - camera.setFocusMode(mock(MethodChannel.Result.class), FocusMode.auto); - verify(mockDartMessenger, times(1)).sendCameraErrorEvent(any()); - } - - @Test - public void startVideoRecording_shouldPullStreamsFromMediaRecorderAndImageReader() - throws InterruptedException, IOException, CameraAccessException { - Camera cameraSpy = spy(camera); - ArrayList mockRequestBuilders = new ArrayList<>(); - mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); - Size mockSize = mock(Size.class); - MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); - ImageReader mockPictureImageReader = mock(ImageReader.class); - cameraSpy.mediaRecorder = mockMediaRecorder; - cameraSpy.recordingVideo = false; - cameraSpy.pictureImageReader = mockPictureImageReader; - CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders); - cameraSpy.cameraDevice = fakeCamera; - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - - ResolutionFeature resolutionFeature = mockCameraFeatureFactory.mockResolutionFeature; - - when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); - doNothing().when(cameraSpy).prepareRecording(mockResult); - - cameraSpy.startVideoRecording(mockResult, null); - verify(mockMediaRecorder, times(1)) - .getSurface(); // stream pulled from media recorder's surface. - verify(mockPictureImageReader, - times(2)) // we expect one call to start the capture, one to create the capture session. - .getSurface(); // stream pulled from image streaming imageReader's surface. - } - - @Test - public void setFocusMode_shouldLockAutoFocusForLockedMode() throws CameraAccessException { - camera.setFocusMode(mock(MethodChannel.Result.class), FocusMode.locked); - verify(mockPreviewRequestBuilder, times(1)) - .set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START); - verify(mockCaptureSession, times(1)).capture(any(), any(), any()); - verify(mockCaptureSession, times(1)).setRepeatingRequest(any(), any(), any()); - } - - @Test - public void setFocusMode_shouldSkipLockAutoFocusWhenNullCaptureSession() { - camera.captureSession = null; - camera.setFocusMode(mock(MethodChannel.Result.class), FocusMode.locked); - verify(mockPreviewRequestBuilder, never()) - .set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START); - } - - @Test - public void setFocusMode_shouldSendErrorEventOnLockAutoFocusCameraAccessException() - throws CameraAccessException { - when(mockCaptureSession.capture(any(), any(), any())) - .thenThrow(new CameraAccessException(0, "")); - camera.setFocusMode(mock(MethodChannel.Result.class), FocusMode.locked); - verify(mockDartMessenger, times(1)).sendCameraErrorEvent(any()); - } - - @Test - public void setFocusMode_shouldCallErrorOnResultOnCameraAccessException() - throws CameraAccessException { - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - when(mockCaptureSession.setRepeatingRequest(any(), any(), any())) - .thenThrow(new CameraAccessException(0, "")); - - camera.setFocusMode(mockResult, FocusMode.locked); - - verify(mockResult, never()).success(any()); - verify(mockResult, times(1)) - .error("setFocusModeFailed", "Error setting focus mode: null", null); - } - - @Test - public void setExposureOffset_shouldUpdateExposureOffsetFeature() { - ExposureOffsetFeature mockExposureOffsetFeature = - mockCameraFeatureFactory.createExposureOffsetFeature(mockCameraProperties); - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - - when(mockExposureOffsetFeature.getValue()).thenReturn(1.0); - - camera.setExposureOffset(mockResult, 1.0); - - verify(mockExposureOffsetFeature, times(1)).setValue(1.0); - verify(mockResult, never()).error(any(), any(), any()); - verify(mockResult, times(1)).success(1.0); - } - - @Test - public void setExposureOffset_shouldAndUpdateBuilder() { - ExposureOffsetFeature mockExposureOffsetFeature = - mockCameraFeatureFactory.createExposureOffsetFeature(mockCameraProperties); - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - - camera.setExposureOffset(mockResult, 1.0); - - verify(mockExposureOffsetFeature, times(1)).updateBuilder(any()); - } - - @Test - public void setExposureOffset_shouldCallErrorOnResultOnCameraAccessException() - throws CameraAccessException { - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - when(mockCaptureSession.setRepeatingRequest(any(), any(), any())) - .thenThrow(new CameraAccessException(0, "")); - - camera.setExposureOffset(mockResult, 1.0); - - verify(mockResult, never()).success(any()); - verify(mockResult, times(1)) - .error("setExposureOffsetFailed", "Could not set exposure offset.", null); - } - - @Test - public void lockCaptureOrientation_shouldLockCaptureOrientation() { - final Activity mockActivity = mock(Activity.class); - SensorOrientationFeature mockSensorOrientationFeature = - mockCameraFeatureFactory.createSensorOrientationFeature( - mockCameraProperties, mockActivity, mockDartMessenger); - - camera.lockCaptureOrientation(PlatformChannel.DeviceOrientation.PORTRAIT_UP); - - verify(mockSensorOrientationFeature, times(1)) - .lockCaptureOrientation(PlatformChannel.DeviceOrientation.PORTRAIT_UP); - } - - @Test - public void unlockCaptureOrientation_shouldUnlockCaptureOrientation() { - final Activity mockActivity = mock(Activity.class); - SensorOrientationFeature mockSensorOrientationFeature = - mockCameraFeatureFactory.createSensorOrientationFeature( - mockCameraProperties, mockActivity, mockDartMessenger); - - camera.unlockCaptureOrientation(); - - verify(mockSensorOrientationFeature, times(1)).unlockCaptureOrientation(); - } - - @Test - public void pausePreview_shouldPausePreview() throws CameraAccessException { - camera.pausePreview(); - - camera.pausedPreview = true; - verify(mockCaptureSession, times(1)).stopRepeating(); - } - - @Test - public void resumePreview_shouldResumePreview() throws CameraAccessException { - camera.resumePreview(); - - camera.pausedPreview = false; - verify(mockCaptureSession, times(1)).setRepeatingRequest(any(), any(), any()); - } - - @Test - public void resumePreview_shouldSendErrorEventOnCameraAccessException() - throws CameraAccessException { - when(mockCaptureSession.setRepeatingRequest(any(), any(), any())) - .thenThrow(new CameraAccessException(0)); - - camera.resumePreview(); - - verify(mockDartMessenger, times(1)).sendCameraErrorEvent(any()); - } - - @Test - public void startBackgroundThread_shouldStartNewThread() { - camera.startBackgroundThread(); - - verify(mockHandlerThread, times(1)).start(); - assertEquals(mockHandler, camera.backgroundHandler); - } - - @Test - public void startBackgroundThread_shouldNotStartNewThreadWhenAlreadyCreated() { - camera.startBackgroundThread(); - camera.startBackgroundThread(); - - verify(mockHandlerThread, times(1)).start(); - } - - @Test - public void stopBackgroundThread_quitsSafely() throws InterruptedException { - camera.startBackgroundThread(); - camera.stopBackgroundThread(); - - verify(mockHandlerThread).quitSafely(); - verify(mockHandlerThread, never()).join(); - } - - @Test - public void onConverge_shouldTakePictureWithoutAbortingSession() throws CameraAccessException { - ArrayList mockRequestBuilders = new ArrayList<>(); - mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); - CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders); - // Stub out other features used by the flow. - camera.cameraDevice = fakeCamera; - camera.pictureImageReader = mock(ImageReader.class); - SensorOrientationFeature mockSensorOrientationFeature = - mockCameraFeatureFactory.createSensorOrientationFeature(mockCameraProperties, null, null); - DeviceOrientationManager mockDeviceOrientationManager = mock(DeviceOrientationManager.class); - when(mockSensorOrientationFeature.getDeviceOrientationManager()) - .thenReturn(mockDeviceOrientationManager); - - // Simulate a post-precapture flow. - camera.onConverged(); - // A picture should be taken. - verify(mockCaptureSession, times(1)).capture(any(), any(), any()); - // The session shuold not be aborted as part of this flow, as this breaks capture on some - // devices, and causes delays on others. - verify(mockCaptureSession, never()).abortCaptures(); - } - - @Test - public void createCaptureSession_doesNotCloseCaptureSession() throws CameraAccessException { - Surface mockSurface = mock(Surface.class); - Size mockSize = mock(Size.class); - ArrayList mockRequestBuilders = new ArrayList<>(); - mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); - CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders); - camera.cameraDevice = fakeCamera; - - ResolutionFeature resolutionFeature = mockCameraFeatureFactory.mockResolutionFeature; - - when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); - - camera.createCaptureSession(CameraDevice.TEMPLATE_PREVIEW, mockSurface); - - verify(mockCaptureSession, never()).close(); - } - - @Test - public void createCaptureSession_shouldNotAddPictureImageSurfaceToPreviewRequest() - throws CameraAccessException { - Surface mockSurface = mock(Surface.class); - Surface mockSecondarySurface = mock(Surface.class); - Size mockSize = mock(Size.class); - ArrayList mockRequestBuilders = new ArrayList<>(); - mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); - CameraDeviceWrapper fakeCamera = spy(new FakeCameraDeviceWrapper(mockRequestBuilders)); - ImageReader mockPictureImageReader = mock(ImageReader.class); - camera.cameraDevice = fakeCamera; - camera.pictureImageReader = mockPictureImageReader; - CaptureRequest.Builder mockPreviewRequestBuilder = mock(CaptureRequest.Builder.class); - - ResolutionFeature resolutionFeature = mockCameraFeatureFactory.mockResolutionFeature; - - when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); - when(fakeCamera.createCaptureRequest(anyInt())).thenReturn(mockPreviewRequestBuilder); - when(mockPictureImageReader.getSurface()).thenReturn(mockSurface); - - // Test with preview template. - camera.createCaptureSession(CameraDevice.TEMPLATE_PREVIEW, mockSurface, mockSecondarySurface); - verify(mockPreviewRequestBuilder, times(0)).addTarget(mockSurface); - - // Test with non-preview template. - camera.createCaptureSession(CameraDevice.TEMPLATE_RECORD, mockSurface, mockSecondarySurface); - verify(mockPreviewRequestBuilder, times(0)).addTarget(mockSurface); - verify(mockPreviewRequestBuilder).addTarget(mockSecondarySurface); - } - - @Test - public void close_doesCloseCaptureSessionWhenCameraDeviceNull() { - camera.close(); - - verify(mockCaptureSession).close(); - } - - @Test - public void close_doesNotCloseCaptureSessionWhenCameraDeviceNonNull() { - ArrayList mockRequestBuilders = new ArrayList<>(); - mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); - CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders); - camera.cameraDevice = fakeCamera; - - camera.close(); - - verify(mockCaptureSession, never()).close(); - } - - @Test - public void startVideoRecording_shouldApplySettingsToMediaRecorder() - throws InterruptedException, IOException, CameraAccessException { - final Activity mockActivity = mock(Activity.class); - final TextureRegistry.SurfaceProducer mockSurfaceProducer = - mock(TextureRegistry.SurfaceProducer.class); - final String cameraName = "1"; - final ResolutionPreset resolutionPreset = ResolutionPreset.high; - final boolean enableAudio = true; - - // region These parameters should be set in android MediaRecorder. - final int fps = 15; - final int videoBitrate = 200000; - final int audioBitrate = 32000; - // endregion - - when(mockCameraProperties.getCameraName()).thenReturn(cameraName); - - final Camera.VideoCaptureSettings parameters = new Camera.VideoCaptureSettings( - resolutionPreset, enableAudio, fps, videoBitrate, audioBitrate); - - // Use a wildcard, since `new Range[] {...}` - // results in a 'Generic array creation' error. - @SuppressWarnings("unchecked") - final Range[] mockRanges = - (Range[]) new Range[] {new Range(10, 20)}; - - when(mockCameraProperties.getControlAutoExposureAvailableTargetFpsRanges()) - .thenReturn(mockRanges); - - final Context mockApplicationContext = mock(Context.class); - when(mockActivity.getApplicationContext()).thenReturn(mockApplicationContext); - - try (final MockedStatic mockFile = mockStatic(File.class); - final MockedConstruction mockMediaRecorder = - Mockito.mockConstruction(MediaRecorder.class)) { - assertNotNull(mockMediaRecorder); - - mockFile.when(() -> File.createTempFile(any(), any(), any())) - .thenReturn(new File("/tmp/file.mp4")); - - final FpsRangeFeature fpsRangeFeature = new FpsRangeFeature(mockCameraProperties); - - final Camera camera = spy(new Camera(mockActivity, mockSurfaceProducer, - mockCameraFeatureFactory, mockDartMessenger, mockCameraProperties, parameters)); - - final CamcorderProfile mockProfileLegacy = mock(CamcorderProfile.class); - mockProfileLegacy.videoFrameRate = fps; - when(camera.getRecordingProfileLegacy()).thenReturn(mockProfileLegacy); - - final SensorOrientationFeature mockSensorOrientationFeature = - mockCameraFeatureFactory.createSensorOrientationFeature(mockCameraProperties, null, null); - DeviceOrientationManager mockDeviceOrientationManager = mock(DeviceOrientationManager.class); - - when(mockSensorOrientationFeature.getDeviceOrientationManager()) - .thenReturn(mockDeviceOrientationManager); - - camera.captureSession = mockCaptureSession; - camera.previewRequestBuilder = mockPreviewRequestBuilder; - - final ArrayList mockRequestBuilders = new ArrayList<>(); - CaptureRequest.Builder mockRequestBuilder = mock(CaptureRequest.Builder.class); - mockRequestBuilders.add(mockRequestBuilder); - final SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class); - final Size mockSize = mock(Size.class); - final ImageReader mockPictureImageReader = mock(ImageReader.class); - camera.pictureImageReader = mockPictureImageReader; - final CameraDeviceWrapper fakeCamera = - new FakeCameraDeviceWrapper(mockRequestBuilders, mockCaptureSession); - - camera.cameraDevice = fakeCamera; - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - - ResolutionFeature resolutionFeature = mockCameraFeatureFactory.mockResolutionFeature; - - assertNotNull(resolutionFeature); - when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); - - camera.startVideoRecording(mockResult, null); - - // region Check that FPS parameter affects AE range at which the camera captures frames. - assertEquals(camera.cameraFeatures.getFpsRange().getValue().getLower(), Integer.valueOf(fps)); - assertEquals(camera.cameraFeatures.getFpsRange().getValue().getUpper(), Integer.valueOf(fps)); - - verify(mockRequestBuilder) - .set(eq(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE), - argThat( - (Range range) -> range.getLower() == fps && range.getUpper() == fps)); - // endregion - - final MediaRecorder recorder = camera.mediaRecorder; - - // region Check that parameters affects movies, written by MediaRecorder. - verify(recorder).setVideoFrameRate(fps); - verify(recorder).setAudioEncodingBitRate(audioBitrate); - verify(recorder).setVideoEncodingBitRate(videoBitrate); - // endregion - } - } - - @Test - public void pausePreview_doesNotCallStopRepeatingWhenCameraClosed() throws CameraAccessException { - ArrayList mockRequestBuilders = new ArrayList<>(); - mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); - CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders); - camera.cameraDevice = fakeCamera; - - camera.close(); - camera.pausePreview(); - - verify(mockCaptureSession, never()).stopRepeating(); - } - - /// Allow to use `new android.util.Range(Integer, Integer)` - private static class RangeConstruction implements Closeable { - final Map, Integer> lowers = new HashMap<>(); - final Map, Integer> uppers = new HashMap<>(); - - @SuppressWarnings({"rawtypes"}) final MockedConstruction rangeMockedConstruction; - - @SuppressWarnings({"unchecked"}) - public RangeConstruction() { - this.rangeMockedConstruction = Mockito.mockConstruction(Range.class, (mock, context) -> { - int lower = (int) context.arguments().get(0); - int upper = (int) context.arguments().get(1); - lowers.put((Range) mock, lower); - uppers.put((Range) mock, upper); - when(((Range) mock).getUpper()) - .thenReturn(lowers.getOrDefault((Range) mock, 15)); - when(((Range) mock).getLower()) - .thenReturn(uppers.getOrDefault((Range) mock, 15)); - when(mock.toString()) - .thenReturn(String.format( - "mocked [%s, %s]", lowers.getOrDefault(mock, 15), uppers.getOrDefault(mock, 15))); - }); - } - - @Override - public void close() throws IOException { - rangeMockedConstruction.close(); - } - } - - 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(FpsRangeFeature fpsRangeFeature) { - 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 = fpsRangeFeature; - 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; - } - } -} From 3967f1f6a3ce54b50985411d38781a6eeae6ce8d Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 24 May 2024 13:50:22 -0400 Subject: [PATCH 09/10] autoformat --- .../io/flutter/plugins/camera/CameraTest.java | 246 ++++++++++-------- 1 file changed, 136 insertions(+), 110 deletions(-) diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java index bf65f6da4d09..976380851cae 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java @@ -67,8 +67,7 @@ class FakeCameraDeviceWrapper implements CameraDeviceWrapper { final List captureRequests; - @Nullable - final CameraCaptureSession session; + @Nullable final CameraCaptureSession session; FakeCameraDeviceWrapper(List captureRequests) { this(captureRequests, null); @@ -104,8 +103,7 @@ public void createCaptureSession( } @Override - public void close() { - } + public void close() {} } public class CameraTest { @@ -135,7 +133,8 @@ public void before() { mockHandler = mock(Handler.class); final Activity mockActivity = mock(Activity.class); - final TextureRegistry.SurfaceProducer mockSurfaceProducer = mock(TextureRegistry.SurfaceProducer.class); + final TextureRegistry.SurfaceProducer mockSurfaceProducer = + mock(TextureRegistry.SurfaceProducer.class); final String cameraName = "1"; final ResolutionPreset resolutionPreset = ResolutionPreset.high; final boolean enableAudio = false; @@ -149,7 +148,8 @@ public void before() { // Use a wildcard, since `new Range[] {...}` // results in a 'Generic array creation' error. @SuppressWarnings("unchecked") - final Range[] mockRanges = (Range[]) new Range[] { new Range(10, 20) }; + final Range[] mockRanges = + (Range[]) new Range[] {new Range(10, 20)}; when(mockCameraProperties.getControlAutoExposureAvailableTargetFpsRanges()) .thenReturn(mockRanges); @@ -158,13 +158,14 @@ public void before() { mockCameraFeatureFactory = new TestCameraFeatureFactory(fpsRangeFeature); - camera = new Camera( - mockActivity, - mockSurfaceProducer, - mockCameraFeatureFactory, - mockDartMessenger, - mockCameraProperties, - new Camera.VideoCaptureSettings(resolutionPreset, enableAudio)); + camera = + new Camera( + mockActivity, + mockSurfaceProducer, + mockCameraFeatureFactory, + mockDartMessenger, + mockCameraProperties, + new Camera.VideoCaptureSettings(resolutionPreset, enableAudio)); final CamcorderProfile mockProfileLegacy = mock(CamcorderProfile.class); mockProfileLegacy.videoFrameRate = 15; @@ -192,7 +193,8 @@ public void shouldNotImplementLifecycleObserverInterface() { @Test public void shouldCreateCameraPluginAndSetAllFeatures() { final Activity mockActivity = mock(Activity.class); - final TextureRegistry.SurfaceProducer mockSurfaceProducer = mock(TextureRegistry.SurfaceProducer.class); + final TextureRegistry.SurfaceProducer mockSurfaceProducer = + mock(TextureRegistry.SurfaceProducer.class); final CameraFeatureFactory spyMockCameraFeatureFactory = spy(mockCameraFeatureFactory); final String cameraName = "1"; final ResolutionPreset resolutionPreset = ResolutionPreset.high; @@ -203,13 +205,14 @@ public void shouldCreateCameraPluginAndSetAllFeatures() { when(spyMockCameraFeatureFactory.createSensorOrientationFeature(any(), any(), any())) .thenReturn(mockSensorOrientationFeature); - Camera camera = new Camera( - mockActivity, - mockSurfaceProducer, - spyMockCameraFeatureFactory, - mockDartMessenger, - mockCameraProperties, - new Camera.VideoCaptureSettings(resolutionPreset, enableAudio)); + Camera camera = + new Camera( + mockActivity, + mockSurfaceProducer, + spyMockCameraFeatureFactory, + mockDartMessenger, + mockCameraProperties, + new Camera.VideoCaptureSettings(resolutionPreset, enableAudio)); verify(spyMockCameraFeatureFactory, times(1)) .createSensorOrientationFeature(mockCameraProperties, mockActivity, mockDartMessenger); @@ -232,8 +235,8 @@ public void shouldCreateCameraPluginAndSetAllFeatures() { @Test public void getDeviceOrientationManager() { - SensorOrientationFeature mockSensorOrientationFeature = mockCameraFeatureFactory - .createSensorOrientationFeature(mockCameraProperties, null, null); + SensorOrientationFeature mockSensorOrientationFeature = + mockCameraFeatureFactory.createSensorOrientationFeature(mockCameraProperties, null, null); DeviceOrientationManager mockDeviceOrientationManager = mock(DeviceOrientationManager.class); when(mockSensorOrientationFeature.getDeviceOrientationManager()) @@ -247,8 +250,8 @@ public void getDeviceOrientationManager() { @Test public void getExposureOffsetStepSize() { - ExposureOffsetFeature mockExposureOffsetFeature = mockCameraFeatureFactory - .createExposureOffsetFeature(mockCameraProperties); + ExposureOffsetFeature mockExposureOffsetFeature = + mockCameraFeatureFactory.createExposureOffsetFeature(mockCameraProperties); double stepSize = 2.3; when(mockExposureOffsetFeature.getExposureOffsetStepSize()).thenReturn(stepSize); @@ -261,8 +264,8 @@ public void getExposureOffsetStepSize() { @Test public void getMaxExposureOffset() { - ExposureOffsetFeature mockExposureOffsetFeature = mockCameraFeatureFactory - .createExposureOffsetFeature(mockCameraProperties); + ExposureOffsetFeature mockExposureOffsetFeature = + mockCameraFeatureFactory.createExposureOffsetFeature(mockCameraProperties); double expectedMaxOffset = 42.0; when(mockExposureOffsetFeature.getMaxExposureOffset()).thenReturn(expectedMaxOffset); @@ -275,8 +278,8 @@ public void getMaxExposureOffset() { @Test public void getMinExposureOffset() { - ExposureOffsetFeature mockExposureOffsetFeature = mockCameraFeatureFactory - .createExposureOffsetFeature(mockCameraProperties); + ExposureOffsetFeature mockExposureOffsetFeature = + mockCameraFeatureFactory.createExposureOffsetFeature(mockCameraProperties); double expectedMinOffset = 21.5; when(mockExposureOffsetFeature.getMinExposureOffset()).thenReturn(21.5); @@ -289,7 +292,8 @@ public void getMinExposureOffset() { @Test public void getMaxZoomLevel() { - ZoomLevelFeature mockZoomLevelFeature = mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); + ZoomLevelFeature mockZoomLevelFeature = + mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); float expectedMaxZoomLevel = 4.2f; when(mockZoomLevelFeature.getMaximumZoomLevel()).thenReturn(expectedMaxZoomLevel); @@ -302,7 +306,8 @@ public void getMaxZoomLevel() { @Test public void getMinZoomLevel() { - ZoomLevelFeature mockZoomLevelFeature = mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); + ZoomLevelFeature mockZoomLevelFeature = + mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); float expectedMinZoomLevel = 4.2f; when(mockZoomLevelFeature.getMinimumZoomLevel()).thenReturn(expectedMinZoomLevel); @@ -315,8 +320,8 @@ public void getMinZoomLevel() { @Test public void setExposureMode_shouldUpdateExposureLockFeature() { - ExposureLockFeature mockExposureLockFeature = mockCameraFeatureFactory - .createExposureLockFeature(mockCameraProperties); + ExposureLockFeature mockExposureLockFeature = + mockCameraFeatureFactory.createExposureLockFeature(mockCameraProperties); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); ExposureMode exposureMode = ExposureMode.locked; @@ -329,8 +334,8 @@ public void setExposureMode_shouldUpdateExposureLockFeature() { @Test public void setExposureMode_shouldUpdateBuilder() { - ExposureLockFeature mockExposureLockFeature = mockCameraFeatureFactory - .createExposureLockFeature(mockCameraProperties); + ExposureLockFeature mockExposureLockFeature = + mockCameraFeatureFactory.createExposureLockFeature(mockCameraProperties); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); ExposureMode exposureMode = ExposureMode.locked; @@ -357,8 +362,9 @@ public void setExposureMode_shouldCallErrorOnResultOnCameraAccessException() @Test public void setExposurePoint_shouldUpdateExposurePointFeature() { SensorOrientationFeature mockSensorOrientationFeature = mock(SensorOrientationFeature.class); - ExposurePointFeature mockExposurePointFeature = mockCameraFeatureFactory.createExposurePointFeature( - mockCameraProperties, mockSensorOrientationFeature); + ExposurePointFeature mockExposurePointFeature = + mockCameraFeatureFactory.createExposurePointFeature( + mockCameraProperties, mockSensorOrientationFeature); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); Point point = new Point(42d, 42d); @@ -372,8 +378,9 @@ public void setExposurePoint_shouldUpdateExposurePointFeature() { @Test public void setExposurePoint_shouldUpdateBuilder() { SensorOrientationFeature mockSensorOrientationFeature = mock(SensorOrientationFeature.class); - ExposurePointFeature mockExposurePointFeature = mockCameraFeatureFactory.createExposurePointFeature( - mockCameraProperties, mockSensorOrientationFeature); + ExposurePointFeature mockExposurePointFeature = + mockCameraFeatureFactory.createExposurePointFeature( + mockCameraProperties, mockSensorOrientationFeature); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); Point point = new Point(42d, 42d); @@ -399,7 +406,8 @@ public void setExposurePoint_shouldCallErrorOnResultOnCameraAccessException() @Test public void setFlashMode_shouldUpdateFlashFeature() { - FlashFeature mockFlashFeature = mockCameraFeatureFactory.createFlashFeature(mockCameraProperties); + FlashFeature mockFlashFeature = + mockCameraFeatureFactory.createFlashFeature(mockCameraProperties); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); FlashMode flashMode = FlashMode.always; @@ -412,7 +420,8 @@ public void setFlashMode_shouldUpdateFlashFeature() { @Test public void setFlashMode_shouldUpdateBuilder() { - FlashFeature mockFlashFeature = mockCameraFeatureFactory.createFlashFeature(mockCameraProperties); + FlashFeature mockFlashFeature = + mockCameraFeatureFactory.createFlashFeature(mockCameraProperties); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); FlashMode flashMode = FlashMode.always; @@ -438,10 +447,11 @@ public void setFlashMode_shouldCallErrorOnResultOnCameraAccessException() @Test public void setFocusPoint_shouldUpdateFocusPointFeature() { SensorOrientationFeature mockSensorOrientationFeature = mock(SensorOrientationFeature.class); - FocusPointFeature mockFocusPointFeature = mockCameraFeatureFactory.createFocusPointFeature( - mockCameraProperties, mockSensorOrientationFeature); - AutoFocusFeature mockAutoFocusFeature = mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, - false); + FocusPointFeature mockFocusPointFeature = + mockCameraFeatureFactory.createFocusPointFeature( + mockCameraProperties, mockSensorOrientationFeature); + AutoFocusFeature mockAutoFocusFeature = + mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, false); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); Point point = new Point(42d, 42d); when(mockAutoFocusFeature.getValue()).thenReturn(FocusMode.auto); @@ -456,10 +466,11 @@ public void setFocusPoint_shouldUpdateFocusPointFeature() { @Test public void setFocusPoint_shouldUpdateBuilder() { SensorOrientationFeature mockSensorOrientationFeature = mock(SensorOrientationFeature.class); - FocusPointFeature mockFocusPointFeature = mockCameraFeatureFactory.createFocusPointFeature( - mockCameraProperties, mockSensorOrientationFeature); - AutoFocusFeature mockAutoFocusFeature = mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, - false); + FocusPointFeature mockFocusPointFeature = + mockCameraFeatureFactory.createFocusPointFeature( + mockCameraProperties, mockSensorOrientationFeature); + AutoFocusFeature mockAutoFocusFeature = + mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, false); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); Point point = new Point(42d, 42d); when(mockAutoFocusFeature.getValue()).thenReturn(FocusMode.auto); @@ -472,8 +483,8 @@ public void setFocusPoint_shouldUpdateBuilder() { @Test public void setFocusPoint_shouldCallErrorOnResultOnCameraAccessException() throws CameraAccessException { - AutoFocusFeature mockAutoFocusFeature = mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, - false); + AutoFocusFeature mockAutoFocusFeature = + mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, false); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); Point point = new Point(42d, 42d); when(mockAutoFocusFeature.getValue()).thenReturn(FocusMode.auto); @@ -488,7 +499,8 @@ public void setFocusPoint_shouldCallErrorOnResultOnCameraAccessException() @Test public void setZoomLevel_shouldUpdateZoomLevelFeature() throws CameraAccessException { - ZoomLevelFeature mockZoomLevelFeature = mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); + ZoomLevelFeature mockZoomLevelFeature = + mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); float zoomLevel = 1.0f; @@ -505,7 +517,8 @@ public void setZoomLevel_shouldUpdateZoomLevelFeature() throws CameraAccessExcep @Test public void setZoomLevel_shouldUpdateBuilder() throws CameraAccessException { - ZoomLevelFeature mockZoomLevelFeature = mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); + ZoomLevelFeature mockZoomLevelFeature = + mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); float zoomLevel = 1.0f; @@ -521,7 +534,8 @@ public void setZoomLevel_shouldUpdateBuilder() throws CameraAccessException { @Test public void setZoomLevel_shouldCallErrorOnResultOnCameraAccessException() throws CameraAccessException { - ZoomLevelFeature mockZoomLevelFeature = mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); + ZoomLevelFeature mockZoomLevelFeature = + mockCameraFeatureFactory.createZoomLevelFeature(mockCameraProperties); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); float zoomLevel = 1.0f; @@ -577,7 +591,8 @@ public void pauseVideoRecording_shouldSendVideoRecordingFailedErrorWhenVersionCo } @Test - public void pauseVideoRecording_shouldSendVideoRecordingFailedErrorWhenMediaRecorderPauseThrowsIllegalStateException() { + public void + pauseVideoRecording_shouldSendVideoRecordingFailedErrorWhenMediaRecorderPauseThrowsIllegalStateException() { MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); camera.mediaRecorder = mockMediaRecorder; camera.recordingVideo = true; @@ -749,8 +764,8 @@ public void startPreviewWithImageStream_shouldPullStreamsFromImageReaders() verify(mockImageStreamReader, times(1)) .getSurface(); // stream pulled from image streaming imageReader's surface. verify( - mockPictureImageReader, - times(2)) // we expect one call to start the capture, one to create the capture session. + mockPictureImageReader, + times(2)) // we expect one call to start the capture, one to create the capture session. .getSurface(); // stream pulled from regular imageReader's surface. } @@ -767,7 +782,8 @@ public void setDescriptionWhileRecording_shouldErrorWhenNotRecording() { } @Test - public void resumeVideoRecording_shouldSendVideoRecordingFailedErrorWhenVersionCodeSmallerThanN() { + public void + resumeVideoRecording_shouldSendVideoRecordingFailedErrorWhenVersionCodeSmallerThanN() { camera.recordingVideo = true; SdkCapabilityChecker.SDK_VERSION = 23; @@ -781,7 +797,8 @@ public void resumeVideoRecording_shouldSendVideoRecordingFailedErrorWhenVersionC } @Test - public void resumeVideoRecording_shouldSendVideoRecordingFailedErrorWhenMediaRecorderPauseThrowsIllegalStateException() { + public void + resumeVideoRecording_shouldSendVideoRecordingFailedErrorWhenMediaRecorderPauseThrowsIllegalStateException() { MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); camera.mediaRecorder = mockMediaRecorder; camera.recordingVideo = true; @@ -801,8 +818,8 @@ public void resumeVideoRecording_shouldSendVideoRecordingFailedErrorWhenMediaRec @Test public void setFocusMode_shouldUpdateAutoFocusFeature() { - AutoFocusFeature mockAutoFocusFeature = mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, - false); + AutoFocusFeature mockAutoFocusFeature = + mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, false); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); camera.setFocusMode(mockResult, FocusMode.auto); @@ -814,8 +831,8 @@ public void setFocusMode_shouldUpdateAutoFocusFeature() { @Test public void setFocusMode_shouldUpdateBuilder() { - AutoFocusFeature mockAutoFocusFeature = mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, - false); + AutoFocusFeature mockAutoFocusFeature = + mockCameraFeatureFactory.createAutoFocusFeature(mockCameraProperties, false); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); camera.setFocusMode(mockResult, FocusMode.auto); @@ -876,8 +893,8 @@ public void startVideoRecording_shouldPullStreamsFromMediaRecorderAndImageReader verify(mockMediaRecorder, times(1)) .getSurface(); // stream pulled from media recorder's surface. verify( - mockPictureImageReader, - times(2)) // we expect one call to start the capture, one to create the capture session. + mockPictureImageReader, + times(2)) // we expect one call to start the capture, one to create the capture session. .getSurface(); // stream pulled from image streaming imageReader's surface. } @@ -923,8 +940,8 @@ public void setFocusMode_shouldCallErrorOnResultOnCameraAccessException() @Test public void setExposureOffset_shouldUpdateExposureOffsetFeature() { - ExposureOffsetFeature mockExposureOffsetFeature = mockCameraFeatureFactory - .createExposureOffsetFeature(mockCameraProperties); + ExposureOffsetFeature mockExposureOffsetFeature = + mockCameraFeatureFactory.createExposureOffsetFeature(mockCameraProperties); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); when(mockExposureOffsetFeature.getValue()).thenReturn(1.0); @@ -938,8 +955,8 @@ public void setExposureOffset_shouldUpdateExposureOffsetFeature() { @Test public void setExposureOffset_shouldAndUpdateBuilder() { - ExposureOffsetFeature mockExposureOffsetFeature = mockCameraFeatureFactory - .createExposureOffsetFeature(mockCameraProperties); + ExposureOffsetFeature mockExposureOffsetFeature = + mockCameraFeatureFactory.createExposureOffsetFeature(mockCameraProperties); MethodChannel.Result mockResult = mock(MethodChannel.Result.class); camera.setExposureOffset(mockResult, 1.0); @@ -964,8 +981,9 @@ public void setExposureOffset_shouldCallErrorOnResultOnCameraAccessException() @Test public void lockCaptureOrientation_shouldLockCaptureOrientation() { final Activity mockActivity = mock(Activity.class); - SensorOrientationFeature mockSensorOrientationFeature = mockCameraFeatureFactory.createSensorOrientationFeature( - mockCameraProperties, mockActivity, mockDartMessenger); + SensorOrientationFeature mockSensorOrientationFeature = + mockCameraFeatureFactory.createSensorOrientationFeature( + mockCameraProperties, mockActivity, mockDartMessenger); camera.lockCaptureOrientation(PlatformChannel.DeviceOrientation.PORTRAIT_UP); @@ -976,8 +994,9 @@ public void lockCaptureOrientation_shouldLockCaptureOrientation() { @Test public void unlockCaptureOrientation_shouldUnlockCaptureOrientation() { final Activity mockActivity = mock(Activity.class); - SensorOrientationFeature mockSensorOrientationFeature = mockCameraFeatureFactory.createSensorOrientationFeature( - mockCameraProperties, mockActivity, mockDartMessenger); + SensorOrientationFeature mockSensorOrientationFeature = + mockCameraFeatureFactory.createSensorOrientationFeature( + mockCameraProperties, mockActivity, mockDartMessenger); camera.unlockCaptureOrientation(); @@ -1044,8 +1063,8 @@ public void onConverge_shouldTakePictureWithoutAbortingSession() throws CameraAc // Stub out other features used by the flow. camera.cameraDevice = fakeCamera; camera.pictureImageReader = mock(ImageReader.class); - SensorOrientationFeature mockSensorOrientationFeature = mockCameraFeatureFactory - .createSensorOrientationFeature(mockCameraProperties, null, null); + SensorOrientationFeature mockSensorOrientationFeature = + mockCameraFeatureFactory.createSensorOrientationFeature(mockCameraProperties, null, null); DeviceOrientationManager mockDeviceOrientationManager = mock(DeviceOrientationManager.class); when(mockSensorOrientationFeature.getDeviceOrientationManager()) .thenReturn(mockDeviceOrientationManager); @@ -1131,7 +1150,8 @@ public void close_doesNotCloseCaptureSessionWhenCameraDeviceNonNull() { public void startVideoRecording_shouldApplySettingsToMediaRecorder() throws InterruptedException, IOException, CameraAccessException { final Activity mockActivity = mock(Activity.class); - final TextureRegistry.SurfaceProducer mockSurfaceProducer = mock(TextureRegistry.SurfaceProducer.class); + final TextureRegistry.SurfaceProducer mockSurfaceProducer = + mock(TextureRegistry.SurfaceProducer.class); final String cameraName = "1"; final ResolutionPreset resolutionPreset = ResolutionPreset.high; final boolean enableAudio = true; @@ -1144,13 +1164,15 @@ public void startVideoRecording_shouldApplySettingsToMediaRecorder() when(mockCameraProperties.getCameraName()).thenReturn(cameraName); - final Camera.VideoCaptureSettings parameters = new Camera.VideoCaptureSettings( - resolutionPreset, enableAudio, fps, videoBitrate, audioBitrate); + final Camera.VideoCaptureSettings parameters = + new Camera.VideoCaptureSettings( + resolutionPreset, enableAudio, fps, videoBitrate, audioBitrate); // Use a wildcard, since `new Range[] {...}` // results in a 'Generic array creation' error. @SuppressWarnings("unchecked") - final Range[] mockRanges = (Range[]) new Range[] { new Range(10, 20) }; + final Range[] mockRanges = + (Range[]) new Range[] {new Range(10, 20)}; when(mockCameraProperties.getControlAutoExposureAvailableTargetFpsRanges()) .thenReturn(mockRanges); @@ -1159,7 +1181,8 @@ public void startVideoRecording_shouldApplySettingsToMediaRecorder() when(mockActivity.getApplicationContext()).thenReturn(mockApplicationContext); try (final MockedStatic mockFile = mockStatic(File.class); - final MockedConstruction mockMediaRecorder = Mockito.mockConstruction(MediaRecorder.class)) { + final MockedConstruction mockMediaRecorder = + Mockito.mockConstruction(MediaRecorder.class)) { assertNotNull(mockMediaRecorder); mockFile @@ -1168,21 +1191,22 @@ public void startVideoRecording_shouldApplySettingsToMediaRecorder() final FpsRangeFeature fpsRangeFeature = new FpsRangeFeature(mockCameraProperties); - final Camera camera = spy( - new Camera( - mockActivity, - mockSurfaceProducer, - mockCameraFeatureFactory, - mockDartMessenger, - mockCameraProperties, - parameters)); + final Camera camera = + spy( + new Camera( + mockActivity, + mockSurfaceProducer, + mockCameraFeatureFactory, + mockDartMessenger, + mockCameraProperties, + parameters)); final CamcorderProfile mockProfileLegacy = mock(CamcorderProfile.class); mockProfileLegacy.videoFrameRate = fps; when(camera.getRecordingProfileLegacy()).thenReturn(mockProfileLegacy); - final SensorOrientationFeature mockSensorOrientationFeature = mockCameraFeatureFactory - .createSensorOrientationFeature(mockCameraProperties, null, null); + final SensorOrientationFeature mockSensorOrientationFeature = + mockCameraFeatureFactory.createSensorOrientationFeature(mockCameraProperties, null, null); DeviceOrientationManager mockDeviceOrientationManager = mock(DeviceOrientationManager.class); when(mockSensorOrientationFeature.getDeviceOrientationManager()) @@ -1198,7 +1222,8 @@ public void startVideoRecording_shouldApplySettingsToMediaRecorder() final Size mockSize = mock(Size.class); final ImageReader mockPictureImageReader = mock(ImageReader.class); camera.pictureImageReader = mockPictureImageReader; - final CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders, mockCaptureSession); + final CameraDeviceWrapper fakeCamera = + new FakeCameraDeviceWrapper(mockRequestBuilders, mockCaptureSession); camera.cameraDevice = fakeCamera; MethodChannel.Result mockResult = mock(MethodChannel.Result.class); @@ -1250,28 +1275,29 @@ private static class RangeConstruction implements Closeable { final Map, Integer> lowers = new HashMap<>(); final Map, Integer> uppers = new HashMap<>(); - @SuppressWarnings({ "rawtypes" }) + @SuppressWarnings({"rawtypes"}) final MockedConstruction rangeMockedConstruction; - @SuppressWarnings({ "unchecked" }) + @SuppressWarnings({"unchecked"}) public RangeConstruction() { - this.rangeMockedConstruction = Mockito.mockConstruction( - Range.class, - (mock, context) -> { - int lower = (int) context.arguments().get(0); - int upper = (int) context.arguments().get(1); - lowers.put((Range) mock, lower); - uppers.put((Range) mock, upper); - when(((Range) mock).getUpper()) - .thenReturn(lowers.getOrDefault((Range) mock, 15)); - when(((Range) mock).getLower()) - .thenReturn(uppers.getOrDefault((Range) mock, 15)); - when(mock.toString()) - .thenReturn( - String.format( - "mocked [%s, %s]", - lowers.getOrDefault(mock, 15), uppers.getOrDefault(mock, 15))); - }); + this.rangeMockedConstruction = + Mockito.mockConstruction( + Range.class, + (mock, context) -> { + int lower = (int) context.arguments().get(0); + int upper = (int) context.arguments().get(1); + lowers.put((Range) mock, lower); + uppers.put((Range) mock, upper); + when(((Range) mock).getUpper()) + .thenReturn(lowers.getOrDefault((Range) mock, 15)); + when(((Range) mock).getLower()) + .thenReturn(uppers.getOrDefault((Range) mock, 15)); + when(mock.toString()) + .thenReturn( + String.format( + "mocked [%s, %s]", + lowers.getOrDefault(mock, 15), uppers.getOrDefault(mock, 15))); + }); } @Override From 357494dbbe6affe02118a2e23dd1fe6f49440e14 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Wed, 29 May 2024 11:27:51 -0700 Subject: [PATCH 10/10] Update packages/camera/camera_android/CHANGELOG.md Co-authored-by: Camille Simon <43054281+camsim99@users.noreply.github.com> --- packages/camera/camera_android/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index 99716df514a2..102fe72726be 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,6 +1,6 @@ ## 0.10.9+4 -* [Supports Impeller](https://docs.flutter.dev/release/breaking-changes/android-surface-plugins) +* [Supports Impeller](https://docs.flutter.dev/release/breaking-changes/android-surface-plugins). ## 0.10.9+3