From 496ca83b08b04e0a6a71b3fa6bb2b09dd9b482d7 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Fri, 20 Jan 2023 14:07:21 -0800 Subject: [PATCH 01/22] Add code needed from proof of concept --- .../camerax/CameraAndroidCameraxPlugin.java | 16 +- .../flutter/plugins/camerax/CameraXProxy.java | 5 + .../camerax/PreviewFlutterApiImpl.java | 25 +++ .../plugins/camerax/PreviewHostApiImpl.java | 105 ++++++++++ .../lib/src/preview.dart | 191 ++++++++++++++++++ .../lib/src/surface.dart | 27 +++ .../pigeons/camerax_library.dart | 18 ++ 7 files changed, 381 insertions(+), 6 deletions(-) create mode 100644 packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewFlutterApiImpl.java create mode 100644 packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java create mode 100644 packages/camera/camera_android_camerax/lib/src/preview.dart create mode 100644 packages/camera/camera_android_camerax/lib/src/surface.dart diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java index b8fbaf539c32..c23d90f501ed 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java @@ -24,7 +24,7 @@ public final class CameraAndroidCameraxPlugin implements FlutterPlugin, Activity */ public CameraAndroidCameraxPlugin() {} - void setUp(BinaryMessenger binaryMessenger, Context context) { + void setUp(BinaryMessenger binaryMessenger, Context context, TextureRegistry textureRegistry) { // Set up instance manager. instanceManager = InstanceManager.open( @@ -44,15 +44,13 @@ void setUp(BinaryMessenger binaryMessenger, Context context) { new ProcessCameraProviderHostApiImpl(binaryMessenger, instanceManager, context); GeneratedCameraXLibrary.ProcessCameraProviderHostApi.setup( binaryMessenger, processCameraProviderHostApi); + GeneratedCameraXLibrary.PreviewHostApi.setup( + binaryMessenger, new PreviewHostApiImpl(binaryMessenger, instanceManager, textureRegistry)); } @Override public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { pluginBinding = flutterPluginBinding; - (new CameraAndroidCameraxPlugin()) - .setUp( - flutterPluginBinding.getBinaryMessenger(), - flutterPluginBinding.getApplicationContext()); } @Override @@ -66,7 +64,13 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { @Override public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) { - updateContext(activityPluginBinding.getActivity()); + plugin.setUp( + pluginBinding.getBinaryMessenger(), + pluginBinding.getApplicationContext(), + pluginBinding.getTextureRegistry()); + plugin.updateContext(pluginBinding.getApplicationContext()); + plugin.processCameraProviderHostApi.setLifecycleOwner( + (LifecycleOwner) activityPluginBinding.getActivity()); } @Override diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java index 8063866d2fc6..77709289982e 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java @@ -5,9 +5,14 @@ package io.flutter.plugins.camerax; import androidx.camera.core.CameraSelector; +import androidx.camera.core.Preview; public class CameraXProxy { public CameraSelector.Builder createCameraSelectorBuilder() { return new CameraSelector.Builder(); } + + public Preview.Builder createPreviewBuilder() { + return new Preview.Builder(); + } } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewFlutterApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewFlutterApiImpl.java new file mode 100644 index 000000000000..f668066268d2 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewFlutterApiImpl.java @@ -0,0 +1,25 @@ +// 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.camerax; + +import androidx.camera.core.Preview; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.PreviewFlutterApi; +import java.util.Map; + +public class PreviewFlutterApiImpl extends PreviewFlutterApi { + private final InstanceManager instanceManager; + + public PreviewFlutterApiImpl(BinaryMessenger binaryMessenger, InstanceManager instanceManager) { + super(binaryMessenger); + this.instanceManager = instanceManager; + } + + void create( + Preview preview, Long targetRotation, Map targetResolution, Reply reply) { + create( + instanceManager.addHostCreatedInstance(preview), targetRotation, targetResolution, reply); + } +} diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java new file mode 100644 index 000000000000..b8a5a2a19e8d --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java @@ -0,0 +1,105 @@ +// 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.camerax; + +import android.graphics.SurfaceTexture; +import android.util.Log; +import android.util.Size; +import android.view.Surface; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; +import androidx.camera.core.Preview; +import androidx.camera.core.SurfaceRequest; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.PreviewHostApi; +import io.flutter.view.TextureRegistry; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Executors; + +public class PreviewHostApiImpl implements PreviewHostApi { + private final BinaryMessenger binaryMessenger; + private final InstanceManager instanceManager; + private final TextureRegistry textureRegistry; + + private final String RESOLUTION_WIDTH_KEY = "width"; + private final String RESOLUTION_HEIGHT_KEY = "height"; + + @VisibleForTesting public CameraXProxy cameraXProxy = new CameraXProxy(); + + public PreviewHostApiImpl( + BinaryMessenger binaryMessenger, + InstanceManager instanceManager, + TextureRegistry textureRegistry) { + this.binaryMessenger = binaryMessenger; + this.instanceManager = instanceManager; + this.textureRegistry = textureRegistry; + } + + @Override + public void create( + @NonNull Long identifier, + @Nullable Long rotation, + @Nullable Map targetResolution) { + Preview.Builder previewBuilder = cameraXProxy.createPreviewBuilder(); + if (rotation != null) { + previewBuilder.setTargetRotation(rotation.intValue()); + } + if (targetResolution != null) { + previewBuilder.setTargetResolution( + new Size( + ((Number) targetResolution.get(RESOLUTION_WIDTH_KEY)).intValue(), + ((Number) targetResolution.get(RESOLUTION_HEIGHT_KEY)).intValue())); + } + Preview preview = previewBuilder.build(); + Log.e("FLUTTER", "CAMILLE preview built with identifier " + identifier); + instanceManager.addDartCreatedInstance(preview, identifier); + } + + @Override + public Long setSurfaceProvider(@NonNull Long identifier) { + Preview preview = (Preview) instanceManager.getInstance(identifier); + TextureRegistry.SurfaceTextureEntry flutterSurfaceTexture = + textureRegistry.createSurfaceTexture(); + SurfaceTexture surfaceTexture = flutterSurfaceTexture.surfaceTexture(); + Preview.SurfaceProvider surfaceProvider = + new Preview.SurfaceProvider() { + @Override + public void onSurfaceRequested(SurfaceRequest request) { + surfaceTexture.setDefaultBufferSize( + request.getResolution().getWidth(), request.getResolution().getHeight()); + Surface flutterSurface = new Surface(surfaceTexture); + request.provideSurface( + flutterSurface, Executors.newSingleThreadExecutor(), (result) -> {}); + }; + }; + preview.setSurfaceProvider(surfaceProvider); + return flutterSurfaceTexture.id(); + } + + @Override + public void setTargetRotation(@NonNull Long identifier, @NonNull Long targetRotation) { + Preview preview = (Preview) instanceManager.getInstance(identifier); + preview.setTargetRotation(targetRotation.intValue()); + } + + @Override + public Map getResolutionInfo(@NonNull Long identifier) { + Preview preview = (Preview) instanceManager.getInstance(identifier); + Size resolution = preview.getResolutionInfo().getResolution(); + + // TODO(camsim99): Establish constants for keys. + // TODO(camsim99): Determine why the values are swapped. + Map doubleBraceMap = + new HashMap() { + { + put("height", Long.valueOf(resolution.getWidth())); + put("width", Long.valueOf(resolution.getHeight())); + } + }; + return doubleBraceMap; + } +} diff --git a/packages/camera/camera_android_camerax/lib/src/preview.dart b/packages/camera/camera_android_camerax/lib/src/preview.dart new file mode 100644 index 000000000000..345f2c2311c1 --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/src/preview.dart @@ -0,0 +1,191 @@ +// 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. + +import 'package:flutter/services.dart' show BinaryMessenger; + +import 'android_camera_camerax_flutter_api_impls.dart'; +import 'camerax_library.pigeon.dart'; +import 'instance_manager.dart'; +import 'java_object.dart'; +import 'use_case.dart'; + +/// Use case that provides a camera preview stream for display. +/// +/// See https://developer.android.com/reference/androidx/camera/core/Preview. +class Preview extends UseCase { + /// Creates a [Preview]. + Preview( + {BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + this.targetRotation, + this.targetResolution}) + : super.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager) { + _api = PreviewHostApiImpl( + binaryMessenger: binaryMessenger, instanceManager: instanceManager); + AndroidCameraXCameraFlutterApis.instance.ensureSetUp(); + _api.createFromInstance(this, targetRotation, targetResolution); + } + + /// Constructs a [CameraInfo] that is not automatically attached to a native object. + Preview.detached( + {BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + this.targetRotation, + this.targetResolution}) + : super.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager) { + _api = PreviewHostApiImpl( + binaryMessenger: binaryMessenger, instanceManager: instanceManager); + AndroidCameraXCameraFlutterApis.instance.ensureSetUp(); + } + + late final PreviewHostApiImpl _api; + + /// Target rotation of the camera used for the preview stream. + int? targetRotation; + + /// Target resolution of the camera preview stream. + /// + /// Should include two entries: + /// + /// * 'width', width of resolution specification in pixels + /// * 'height', height of resolution specification in pixels + final Map? targetResolution; + + /// Sets surface provider for the preview stream. + /// + /// Returns the ID of the FlutterSurfaceTextureEntry used on the back end + /// used to display the preview stream on a [Texture] of the same ID. + Future setSurfaceProvider() { + return _api.setSurfaceProviderFromInstance(this); + } + + /// Reconfigures the preview stream to have the specified target rotation. + void setTargetRotation(int rotation) { + targetRotation = rotation; + _api.setTargetRotationFromInstance(this, rotation); + } + + Future> getResolutionInfo() { + return _api.getResolutionInfoFromInstance(this); + } +} + +/// Host API implementation of [Preview]. +class PreviewHostApiImpl extends PreviewHostApi { + /// Constructs a [PreviewHostApiImpl]. + PreviewHostApiImpl({this.binaryMessenger, InstanceManager? instanceManager}) { + this.instanceManager = instanceManager ?? JavaObject.globalInstanceManager; + } + + /// Receives binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; + + /// Maintains instances stored to communicate with native language objects. + late final InstanceManager instanceManager; + + /// Creates a [Preview] with the target rotation provided if specified. + void createFromInstance(Preview instance, int? targetRotation, + Map? targetResolution) { + int? identifier = instanceManager.getIdentifier(instance); + identifier ??= instanceManager.addDartCreatedInstance(instance, + onCopy: (Preview original) { + return Preview.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + targetRotation: original.targetRotation); + }); + create(identifier, targetRotation, targetResolution); + } + + /// Sets the surface provider of the provided [Preview] instance and returns + /// the ID corresponding to the surface it will provide. + Future setSurfaceProviderFromInstance(Preview instance) async { + int? identifier = instanceManager.getIdentifier(instance); + identifier ??= instanceManager.addDartCreatedInstance(instance, + onCopy: (Preview original) { + return Preview.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + targetRotation: original.targetRotation); + }); + + final int surfaceTextureEntryId = await setSurfaceProvider(identifier); + return surfaceTextureEntryId; + } + + /// Sets the [Preview]'s target rotation to be that which is specified. + void setTargetRotationFromInstance(Preview instance, int targetRotation) { + int? identifier = instanceManager.getIdentifier(instance); + identifier ??= instanceManager.addDartCreatedInstance(instance, + onCopy: (Preview original) { + return Preview.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + targetRotation: original.targetRotation); + }); + + setTargetRotation(identifier, targetRotation); + } + + Future> getResolutionInfoFromInstance( + Preview instance) async { + int? identifier = instanceManager.getIdentifier(instance); + identifier ??= instanceManager.addDartCreatedInstance(instance, + onCopy: (Preview original) { + return Preview.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + targetRotation: original.targetRotation); + }); + + final Map resolutionInfo = + await getResolutionInfo(identifier); + return resolutionInfo; + } +} + +/// Flutter API implementation of [Preview]. +class PreviewFlutterApiImpl extends PreviewFlutterApi { + /// Constructs a [PreviewFlutterApiImpl]. + PreviewFlutterApiImpl({ + this.binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager; + + /// Receives binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; + + /// Maintains instances stored to communicate with native language objects. + final InstanceManager instanceManager; + + @override + void create(int identifier, int? targetRotation, + Map? targetResolution) { + instanceManager.addHostCreatedInstance( + Preview.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + targetRotation: targetRotation, + targetResolution: targetResolution), + identifier, + onCopy: (Preview original) { + return Preview.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + targetRotation: targetRotation, + targetResolution: targetResolution); + }, + ); + } +} diff --git a/packages/camera/camera_android_camerax/lib/src/surface.dart b/packages/camera/camera_android_camerax/lib/src/surface.dart new file mode 100644 index 000000000000..3a7e2164295a --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/src/surface.dart @@ -0,0 +1,27 @@ +// 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. + +import 'package:flutter/services.dart' show BinaryMessenger; + +import 'instance_manager.dart'; +import 'java_object.dart'; + +/// Handle onto raw buffer managed by screen copmositor. +/// +/// See https://developer.android.com/reference/android/view/Surface.html#ROTATION_0--. +class Surface extends JavaObject { + /// Creates a detached [UseCase]. + Surface.detached( + {BinaryMessenger? binaryMessenger, InstanceManager? instanceManager}) + : super.detached( + binaryMessenger: binaryMessenger, instanceManager: instanceManager); + + static const ROTATION_0 = 0; + + static const ROTATION_180 = 2; + + static const ROTATION_270 = 3; + + static const ROTATION_90 = 1; +} diff --git a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart index 4d7d96910246..f89355167aea 100644 --- a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart +++ b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart @@ -70,3 +70,21 @@ abstract class ProcessCameraProviderHostApi { abstract class ProcessCameraProviderFlutterApi { void create(int identifier); } + +@HostApi(dartHostTestHandler: 'TestPreviewHostApi') +abstract class PreviewHostApi { + void create( + int identifier, int? rotation, Map? targetResolution); + + int setSurfaceProvider(int identifier); + + void setTargetRotation(int identifier, int targetRotation); + + Map getResolutionInfo(int identifier); +} + +@FlutterApi() +abstract class PreviewFlutterApi { + void create( + int identifier, int targetRotation, Map? targetResolution); +} From c9593ba163b771dd36fc9c939ed47ff6912ceb2f Mon Sep 17 00:00:00 2001 From: camsim99 Date: Fri, 27 Jan 2023 11:14:15 -0800 Subject: [PATCH 02/22] Add test files, delete unecessary method --- .../plugins/camerax/PreviewHostApiImpl.java | 6 - .../flutter/plugins/camerax/PreviewTest.java | 3 + .../lib/src/preview.dart | 20 --- .../pigeons/camerax_library.dart | 2 - .../test/preview_test.dart | 139 ++++++++++++++++++ 5 files changed, 142 insertions(+), 28 deletions(-) create mode 100644 packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java create mode 100644 packages/camera/camera_android_camerax/test/preview_test.dart diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java index b8a5a2a19e8d..9ad39795f3bd 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java @@ -80,12 +80,6 @@ public void onSurfaceRequested(SurfaceRequest request) { return flutterSurfaceTexture.id(); } - @Override - public void setTargetRotation(@NonNull Long identifier, @NonNull Long targetRotation) { - Preview preview = (Preview) instanceManager.getInstance(identifier); - preview.setTargetRotation(targetRotation.intValue()); - } - @Override public Map getResolutionInfo(@NonNull Long identifier) { Preview preview = (Preview) instanceManager.getInstance(identifier); diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java new file mode 100644 index 000000000000..5455676d9586 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java @@ -0,0 +1,3 @@ +public class PreviewTest { + +} diff --git a/packages/camera/camera_android_camerax/lib/src/preview.dart b/packages/camera/camera_android_camerax/lib/src/preview.dart index 345f2c2311c1..f8ec6573d29d 100644 --- a/packages/camera/camera_android_camerax/lib/src/preview.dart +++ b/packages/camera/camera_android_camerax/lib/src/preview.dart @@ -64,12 +64,6 @@ class Preview extends UseCase { return _api.setSurfaceProviderFromInstance(this); } - /// Reconfigures the preview stream to have the specified target rotation. - void setTargetRotation(int rotation) { - targetRotation = rotation; - _api.setTargetRotationFromInstance(this, rotation); - } - Future> getResolutionInfo() { return _api.getResolutionInfoFromInstance(this); } @@ -121,20 +115,6 @@ class PreviewHostApiImpl extends PreviewHostApi { return surfaceTextureEntryId; } - /// Sets the [Preview]'s target rotation to be that which is specified. - void setTargetRotationFromInstance(Preview instance, int targetRotation) { - int? identifier = instanceManager.getIdentifier(instance); - identifier ??= instanceManager.addDartCreatedInstance(instance, - onCopy: (Preview original) { - return Preview.detached( - binaryMessenger: binaryMessenger, - instanceManager: instanceManager, - targetRotation: original.targetRotation); - }); - - setTargetRotation(identifier, targetRotation); - } - Future> getResolutionInfoFromInstance( Preview instance) async { int? identifier = instanceManager.getIdentifier(instance); diff --git a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart index f89355167aea..07fd1b7d5220 100644 --- a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart +++ b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart @@ -78,8 +78,6 @@ abstract class PreviewHostApi { int setSurfaceProvider(int identifier); - void setTargetRotation(int identifier, int targetRotation); - Map getResolutionInfo(int identifier); } diff --git a/packages/camera/camera_android_camerax/test/preview_test.dart b/packages/camera/camera_android_camerax/test/preview_test.dart new file mode 100644 index 000000000000..0aabd2029c95 --- /dev/null +++ b/packages/camera/camera_android_camerax/test/preview_test.dart @@ -0,0 +1,139 @@ +// 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. + +import 'package:camera_android_camerax/src/camerax_library.pigeon.dart'; +import 'package:camera_android_camerax/src/instance_manager.dart'; +import 'package:camera_android_camerax/src/preview.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'preview_test.mocks.dart'; +import 'test_camerax_library.pigeon.dart'; + +@GenerateMocks([TestPreviewHostApi]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('Preview', () { + tearDown(() => TestCameraSelectorHostApi.setup(null)); + + test('detachedCreateTest', () async { + final MockPreviewHostApi mockApi = + MockTestPreviewHostApi(); + TestPreviewHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + Preview.detached( + instanceManager: instanceManager, + targetRotation: 90, + targetResolution: Map{ + width: 10, + height: 50, + }, + ); + + verifyNever(mockApi.create(argThat(isA()), argThat(isA()),argThat(isA()))); + }); + + test('createTest', () async { + final MockTestCPreviewHostApi mockApi = + MockTestPreviewHostApi(); + TestCPreviewHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + CameraSelector( + instanceManager: instanceManager, + targetRotation: 90, + targetResolution: Map{ + width: 10, + height: 50, + }, + ); + + verify(mockApi.create(argThat(isA(), argThat(equals(90)), argThat(equals( Map{ + width: 10, + height: 50, + }))), null)); + }); + + test('setSurfaceProviderTest', () async { + final MockTestPreviewHostApi mockApi = + MockTestPreviewHostApi(); + TestPreviewHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + final Preview preview = Preview.detached( + instanceManager: instanceManager, + ); + + instanceManager.addHostCreatedInstance( + preview, + 0, + onCopy: (_) => CameraSelector.detached(), + ); + + when(mockApi.setTargetRotation(instanceManager.getIdentifier(preview) + )).thenReturn(8); + expect(await preview.setSurfaceProvider(), + equals(8)); + + verify(mockApi.setSurfaceProvider(instanceManager.getIdentifier(preview))); + }); + + test('getResolutionInfoTest', () async { + final MockTestPreviewHostApi mockApi = + MockTestPreviewHostApi(); + TestPreviewHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + final Preview preview = Preview.detached( + instanceManager: instanceManager, + ); + + instanceManager.addHostCreatedInstance( + preview, + 0, + onCopy: (_) => CameraSelector.detached(), + ); + + when(mockApi.getResolutionInfo(instanceManager.getIdentifier(preview) + )).thenReturn(Map {'width': 10, 'height': 60}); + expect(await preview.getResolutionInfo(), + equals(Map {'width': 10, 'height': 60})); + + verify(mockApi.getResolutionInfo(instanceManager.getIdentifier(preview))); + }); + + test('flutterApiCreateTest', () { + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + final PreviewFlutterApi flutterApi = PreviewFlutterApiImpl( + instanceManager: instanceManager, + ); + + flutterApi.create(0, 270, Map{'width': 60, 'height': 10}); + + expect(instanceManager.getInstanceWithWeakReference(0), + isA()); + expect( + (instanceManager.getInstanceWithWeakReference(0)! as Preview) + .targetRotation, + equals(270)); + expect( + (instanceManager.getInstanceWithWeakReference(0)! as Preview) + .targetResolution, + equals(Map{'width': 60, 'height': 10})); + }); + }); +} From 6e5bc8e348167b2208b86641eb9cc84e6a50ef9e Mon Sep 17 00:00:00 2001 From: camsim99 Date: Fri, 27 Jan 2023 15:54:02 -0800 Subject: [PATCH 03/22] Add tests, remove unecessary code --- .../camerax/CameraAndroidCameraxPlugin.java | 8 +- .../flutter/plugins/camerax/CameraXProxy.java | 6 + .../camerax/GeneratedCameraXLibrary.java | 280 ++++++++++++++++++ .../camerax/PreviewFlutterApiImpl.java | 25 -- .../plugins/camerax/PreviewHostApiImpl.java | 28 +- .../flutter/plugins/camerax/PreviewTest.java | 133 ++++++++- .../lib/src/camerax_library.pigeon.dart | 210 +++++++++++++ .../lib/src/preview.dart | 52 +--- .../pigeons/camerax_library.dart | 21 +- .../test/preview_test.dart | 87 ++---- .../test/preview_test.mocks.dart | 81 +++++ .../test/test_camerax_library.pigeon.dart | 101 +++++++ 12 files changed, 873 insertions(+), 159 deletions(-) delete mode 100644 packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewFlutterApiImpl.java create mode 100644 packages/camera/camera_android_camerax/test/preview_test.mocks.dart diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java index 9dc84d5eb104..97f82c85d110 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java @@ -11,6 +11,7 @@ import io.flutter.embedding.engine.plugins.activity.ActivityAware; import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.view.TextureRegistry; /** Platform implementation of the camera_plugin implemented with the CameraX library. */ public final class CameraAndroidCameraxPlugin implements FlutterPlugin, ActivityAware { @@ -46,7 +47,7 @@ void setUp(BinaryMessenger binaryMessenger, Context context, TextureRegistry tex GeneratedCameraXLibrary.ProcessCameraProviderHostApi.setup( binaryMessenger, processCameraProviderHostApi); GeneratedCameraXLibrary.PreviewHostApi.setup( - binaryMessenger, new PreviewHostApiImpl(binaryMessenger, instanceManager, textureRegistry)); + binaryMessenger, new PreviewHostApiImpl(binaryMessenger, instanceManager, textureRegistry)); } @Override @@ -65,7 +66,10 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { @Override public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) { - setUp(pluginBinding.getBinaryMessenger(), pluginBinding.getApplicationContext(), pluginBinding.getTextureRegistry()); + setUp( + pluginBinding.getBinaryMessenger(), + pluginBinding.getApplicationContext(), + pluginBinding.getTextureRegistry()); updateContext(pluginBinding.getApplicationContext()); processCameraProviderHostApi.setLifecycleOwner( (LifecycleOwner) activityPluginBinding.getActivity()); diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java index 77709289982e..db5cd14121d3 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java @@ -4,6 +4,8 @@ package io.flutter.plugins.camerax; +import android.graphics.SurfaceTexture; +import android.view.Surface; import androidx.camera.core.CameraSelector; import androidx.camera.core.Preview; @@ -15,4 +17,8 @@ public CameraSelector.Builder createCameraSelectorBuilder() { public Preview.Builder createPreviewBuilder() { return new Preview.Builder(); } + + public Surface createSurface(SurfaceTexture surfaceTexture) { + return new Surface(surfaceTexture); + } } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java index 8c42a7911768..560ad1a683e3 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java @@ -13,6 +13,8 @@ import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.MessageCodec; import io.flutter.plugin.common.StandardMessageCodec; +import java.io.ByteArrayOutputStream; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -23,6 +25,82 @@ @SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression"}) public class GeneratedCameraXLibrary { + /** Generated class from Pigeon that represents data sent in messages. */ + public static class ResolutionInfo { + private @NonNull Long width; + + public @NonNull Long getWidth() { + return width; + } + + public void setWidth(@NonNull Long setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"width\" is null."); + } + this.width = setterArg; + } + + private @NonNull Long height; + + public @NonNull Long getHeight() { + return height; + } + + public void setHeight(@NonNull Long setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"height\" is null."); + } + this.height = setterArg; + } + + /** Constructor is private to enforce null safety; use Builder. */ + private ResolutionInfo() {} + + public static final class Builder { + private @Nullable Long width; + + public @NonNull Builder setWidth(@NonNull Long setterArg) { + this.width = setterArg; + return this; + } + + private @Nullable Long height; + + public @NonNull Builder setHeight(@NonNull Long setterArg) { + this.height = setterArg; + return this; + } + + public @NonNull ResolutionInfo build() { + ResolutionInfo pigeonReturn = new ResolutionInfo(); + pigeonReturn.setWidth(width); + pigeonReturn.setHeight(height); + return pigeonReturn; + } + } + + @NonNull + Map toMap() { + Map toMapResult = new HashMap<>(); + toMapResult.put("width", width); + toMapResult.put("height", height); + return toMapResult; + } + + static @NonNull ResolutionInfo fromMap(@NonNull Map map) { + ResolutionInfo pigeonResult = new ResolutionInfo(); + Object width = map.get("width"); + pigeonResult.setWidth( + (width == null) ? null : ((width instanceof Integer) ? (Integer) width : (Long) width)); + Object height = map.get("height"); + pigeonResult.setHeight( + (height == null) + ? null + : ((height instanceof Integer) ? (Integer) height : (Long) height)); + return pigeonResult; + } + } + public interface Result { void success(T result); @@ -590,6 +668,208 @@ public void create(@NonNull Long identifierArg, Reply callback) { } } + private static class PreviewHostApiCodec extends StandardMessageCodec { + public static final PreviewHostApiCodec INSTANCE = new PreviewHostApiCodec(); + + private PreviewHostApiCodec() {} + + @Override + protected Object readValueOfType(byte type, ByteBuffer buffer) { + switch (type) { + case (byte) 128: + return ResolutionInfo.fromMap((Map) readValue(buffer)); + + case (byte) 129: + return ResolutionInfo.fromMap((Map) readValue(buffer)); + + default: + return super.readValueOfType(type, buffer); + } + } + + @Override + protected void writeValue(ByteArrayOutputStream stream, Object value) { + if (value instanceof ResolutionInfo) { + stream.write(128); + writeValue(stream, ((ResolutionInfo) value).toMap()); + } else if (value instanceof ResolutionInfo) { + stream.write(129); + writeValue(stream, ((ResolutionInfo) value).toMap()); + } else { + super.writeValue(stream, value); + } + } + } + + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ + public interface PreviewHostApi { + void create( + @NonNull Long identifier, + @Nullable Long rotation, + @Nullable ResolutionInfo targetResolution); + + @NonNull + Long setSurfaceProvider(@NonNull Long identifier); + + @NonNull + ResolutionInfo getResolutionInfo(@NonNull Long identifier); + + /** The codec used by PreviewHostApi. */ + static MessageCodec getCodec() { + return PreviewHostApiCodec.INSTANCE; + } + + /** Sets up an instance of `PreviewHostApi` to handle messages through the `binaryMessenger`. */ + static void setup(BinaryMessenger binaryMessenger, PreviewHostApi api) { + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.PreviewHostApi.create", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + if (identifierArg == null) { + throw new NullPointerException("identifierArg unexpectedly null."); + } + Number rotationArg = (Number) args.get(1); + ResolutionInfo targetResolutionArg = (ResolutionInfo) args.get(2); + api.create( + (identifierArg == null) ? null : identifierArg.longValue(), + (rotationArg == null) ? null : rotationArg.longValue(), + targetResolutionArg); + wrapped.put("result", null); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.PreviewHostApi.setSurfaceProvider", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + if (identifierArg == null) { + throw new NullPointerException("identifierArg unexpectedly null."); + } + Long output = + api.setSurfaceProvider( + (identifierArg == null) ? null : identifierArg.longValue()); + wrapped.put("result", output); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.PreviewHostApi.getResolutionInfo", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + if (identifierArg == null) { + throw new NullPointerException("identifierArg unexpectedly null."); + } + ResolutionInfo output = + api.getResolutionInfo( + (identifierArg == null) ? null : identifierArg.longValue()); + wrapped.put("result", output); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + } + } + + private static class PreviewFlutterApiCodec extends StandardMessageCodec { + public static final PreviewFlutterApiCodec INSTANCE = new PreviewFlutterApiCodec(); + + private PreviewFlutterApiCodec() {} + + @Override + protected Object readValueOfType(byte type, ByteBuffer buffer) { + switch (type) { + case (byte) 128: + return ResolutionInfo.fromMap((Map) readValue(buffer)); + + default: + return super.readValueOfType(type, buffer); + } + } + + @Override + protected void writeValue(ByteArrayOutputStream stream, Object value) { + if (value instanceof ResolutionInfo) { + stream.write(128); + writeValue(stream, ((ResolutionInfo) value).toMap()); + } else { + super.writeValue(stream, value); + } + } + } + + /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */ + public static class PreviewFlutterApi { + private final BinaryMessenger binaryMessenger; + + public PreviewFlutterApi(BinaryMessenger argBinaryMessenger) { + this.binaryMessenger = argBinaryMessenger; + } + + public interface Reply { + void reply(T reply); + } + + static MessageCodec getCodec() { + return PreviewFlutterApiCodec.INSTANCE; + } + + public void create( + @NonNull Long identifierArg, + @NonNull Long targetRotationArg, + @Nullable ResolutionInfo targetResolutionArg, + Reply callback) { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.PreviewFlutterApi.create", getCodec()); + channel.send( + new ArrayList( + Arrays.asList(identifierArg, targetRotationArg, targetResolutionArg)), + channelReply -> { + callback.reply(null); + }); + } + } + private static Map wrapError(Throwable exception) { Map errorMap = new HashMap<>(); errorMap.put("message", exception.toString()); diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewFlutterApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewFlutterApiImpl.java deleted file mode 100644 index f668066268d2..000000000000 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewFlutterApiImpl.java +++ /dev/null @@ -1,25 +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.camerax; - -import androidx.camera.core.Preview; -import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.plugins.camerax.GeneratedCameraXLibrary.PreviewFlutterApi; -import java.util.Map; - -public class PreviewFlutterApiImpl extends PreviewFlutterApi { - private final InstanceManager instanceManager; - - public PreviewFlutterApiImpl(BinaryMessenger binaryMessenger, InstanceManager instanceManager) { - super(binaryMessenger); - this.instanceManager = instanceManager; - } - - void create( - Preview preview, Long targetRotation, Map targetResolution, Reply reply) { - create( - instanceManager.addHostCreatedInstance(preview), targetRotation, targetResolution, reply); - } -} diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java index 9ad39795f3bd..15f06bdf6e85 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java @@ -5,7 +5,6 @@ package io.flutter.plugins.camerax; import android.graphics.SurfaceTexture; -import android.util.Log; import android.util.Size; import android.view.Surface; import androidx.annotation.NonNull; @@ -16,8 +15,6 @@ import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugins.camerax.GeneratedCameraXLibrary.PreviewHostApi; import io.flutter.view.TextureRegistry; -import java.util.HashMap; -import java.util.Map; import java.util.concurrent.Executors; public class PreviewHostApiImpl implements PreviewHostApi { @@ -43,7 +40,7 @@ public PreviewHostApiImpl( public void create( @NonNull Long identifier, @Nullable Long rotation, - @Nullable Map targetResolution) { + @Nullable GeneratedCameraXLibrary.ResolutionInfo targetResolution) { Preview.Builder previewBuilder = cameraXProxy.createPreviewBuilder(); if (rotation != null) { previewBuilder.setTargetRotation(rotation.intValue()); @@ -51,11 +48,9 @@ public void create( if (targetResolution != null) { previewBuilder.setTargetResolution( new Size( - ((Number) targetResolution.get(RESOLUTION_WIDTH_KEY)).intValue(), - ((Number) targetResolution.get(RESOLUTION_HEIGHT_KEY)).intValue())); + targetResolution.getWidth().intValue(), targetResolution.getHeight().intValue())); } Preview preview = previewBuilder.build(); - Log.e("FLUTTER", "CAMILLE preview built with identifier " + identifier); instanceManager.addDartCreatedInstance(preview, identifier); } @@ -71,7 +66,7 @@ public Long setSurfaceProvider(@NonNull Long identifier) { public void onSurfaceRequested(SurfaceRequest request) { surfaceTexture.setDefaultBufferSize( request.getResolution().getWidth(), request.getResolution().getHeight()); - Surface flutterSurface = new Surface(surfaceTexture); + Surface flutterSurface = cameraXProxy.createSurface(surfaceTexture); request.provideSurface( flutterSurface, Executors.newSingleThreadExecutor(), (result) -> {}); }; @@ -81,19 +76,14 @@ public void onSurfaceRequested(SurfaceRequest request) { } @Override - public Map getResolutionInfo(@NonNull Long identifier) { + public GeneratedCameraXLibrary.ResolutionInfo getResolutionInfo(@NonNull Long identifier) { Preview preview = (Preview) instanceManager.getInstance(identifier); Size resolution = preview.getResolutionInfo().getResolution(); - // TODO(camsim99): Establish constants for keys. - // TODO(camsim99): Determine why the values are swapped. - Map doubleBraceMap = - new HashMap() { - { - put("height", Long.valueOf(resolution.getWidth())); - put("width", Long.valueOf(resolution.getHeight())); - } - }; - return doubleBraceMap; + GeneratedCameraXLibrary.ResolutionInfo.Builder resolutionInfo = + new GeneratedCameraXLibrary.ResolutionInfo.Builder() + .setWidth(Long.valueOf(resolution.getWidth())) + .setHeight(Long.valueOf(resolution.getHeight())); + return resolutionInfo.build(); } } diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java index 5455676d9586..705de388e56a 100644 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java @@ -1,3 +1,134 @@ +// 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.camerax; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.graphics.SurfaceTexture; +import android.util.Size; +import android.view.Surface; +import androidx.camera.core.Preview; +import androidx.camera.core.SurfaceRequest; +import androidx.core.util.Consumer; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.ResolutionInfo; +import io.flutter.view.TextureRegistry; +import java.util.concurrent.Executor; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) public class PreviewTest { - + @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock public Preview mockPreview; + @Mock public BinaryMessenger mockBinaryMessenger; + @Mock public TextureRegistry mockTextureRegistry; + @Mock public CameraXProxy mockCameraXProxy; + + InstanceManager testInstanceManager; + + @Before + public void setUp() { + testInstanceManager = spy(InstanceManager.open(identifier -> {})); + } + + @After + public void tearDown() { + testInstanceManager.close(); + } + + @Test + public void createTest() { + final PreviewHostApiImpl previewHostApi = + new PreviewHostApiImpl(mockBinaryMessenger, testInstanceManager, mockTextureRegistry); + final Preview.Builder mockPreviewBuilder = mock(Preview.Builder.class); + final Preview mockPreview = mock(Preview.class); + final GeneratedCameraXLibrary.ResolutionInfo resolutionInfo = + new GeneratedCameraXLibrary.ResolutionInfo.Builder().setWidth(10L).setHeight(50L).build(); + + previewHostApi.cameraXProxy = mockCameraXProxy; + when(mockCameraXProxy.createPreviewBuilder()).thenReturn(mockPreviewBuilder); + when(mockPreviewBuilder.build()).thenReturn(mockPreview); + + final ArgumentCaptor sizeCaptor = ArgumentCaptor.forClass(Size.class); + + previewHostApi.create(3L, 90L, resolutionInfo); + + verify(mockPreviewBuilder).setTargetRotation(90); + verify(mockPreviewBuilder).setTargetResolution(sizeCaptor.capture()); + assertEquals(sizeCaptor.getValue().getWidth(), 10); + assertEquals(sizeCaptor.getValue().getHeight(), 50); + verify(mockPreviewBuilder).build(); + verify(testInstanceManager).addDartCreatedInstance(mockPreview, 3L); + } + + @Test + public void setSurfaceProviderTest() { + final PreviewHostApiImpl previewHostApi = + new PreviewHostApiImpl(mockBinaryMessenger, testInstanceManager, mockTextureRegistry); + final TextureRegistry.SurfaceTextureEntry mockSurfaceTextureEntry = + mock(TextureRegistry.SurfaceTextureEntry.class); + final SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class); + final SurfaceRequest mockSurfaceRequest = mock(SurfaceRequest.class); + final Surface mockSurface = mock(Surface.class); + + previewHostApi.cameraXProxy = mockCameraXProxy; + testInstanceManager.addDartCreatedInstance(mockPreview, 5L); + + when(mockTextureRegistry.createSurfaceTexture()).thenReturn(mockSurfaceTextureEntry); + when(mockSurfaceTextureEntry.surfaceTexture()).thenReturn(mockSurfaceTexture); + when(mockSurfaceTextureEntry.id()).thenReturn(120L); + when(mockSurfaceRequest.getResolution()).thenReturn(new Size(200, 500)); + when(mockCameraXProxy.createSurface(mockSurfaceTexture)).thenReturn(mockSurface); + + final ArgumentCaptor surfaceProviderCaptor = + ArgumentCaptor.forClass(Preview.SurfaceProvider.class); + final ArgumentCaptor surfaceCaptor = ArgumentCaptor.forClass(Surface.class); + + // Test that surface provider was set and the surface texture ID was returned. + assertEquals((long) previewHostApi.setSurfaceProvider(5L), 120L); + verify(mockPreview).setSurfaceProvider(surfaceProviderCaptor.capture()); + + Preview.SurfaceProvider surfaceProvider = surfaceProviderCaptor.getValue(); + surfaceProvider.onSurfaceRequested(mockSurfaceRequest); + + // Test that the surface derived from the surface texture entry will be provided to the surface request. + verify(mockSurfaceTexture).setDefaultBufferSize(200, 500); + verify(mockSurfaceRequest) + .provideSurface(surfaceCaptor.capture(), any(Executor.class), any(Consumer.class)); + + assertEquals(surfaceCaptor.getValue(), mockSurface); + } + + @Test + public void getResolutionInfo() { + final PreviewHostApiImpl previewHostApi = + new PreviewHostApiImpl(mockBinaryMessenger, testInstanceManager, mockTextureRegistry); + final androidx.camera.core.ResolutionInfo mockResolutionInfo = + mock(androidx.camera.core.ResolutionInfo.class); + + testInstanceManager.addDartCreatedInstance(mockPreview, 23L); + when(mockPreview.getResolutionInfo()).thenReturn(mockResolutionInfo); + when(mockResolutionInfo.getResolution()).thenReturn(new Size(500, 200)); + + ResolutionInfo resolutionInfo = previewHostApi.getResolutionInfo(23L); + assertEquals((long) resolutionInfo.getWidth(), 500L); + assertEquals((long) resolutionInfo.getHeight(), 200L); + } } diff --git a/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart b/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart index 636a375145b9..5320a6c24f8d 100644 --- a/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart +++ b/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart @@ -10,6 +10,31 @@ import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; import 'package:flutter/services.dart'; +class ResolutionInfo { + ResolutionInfo({ + required this.width, + required this.height, + }); + + int width; + int height; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['width'] = width; + pigeonMap['height'] = height; + return pigeonMap; + } + + static ResolutionInfo decode(Object message) { + final Map pigeonMap = message as Map; + return ResolutionInfo( + width: pigeonMap['width']! as int, + height: pigeonMap['height']! as int, + ); + } +} + class _JavaObjectHostApiCodec extends StandardMessageCodec { const _JavaObjectHostApiCodec(); } @@ -486,3 +511,188 @@ abstract class CameraFlutterApi { } } } + +class _PreviewHostApiCodec extends StandardMessageCodec { + const _PreviewHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is ResolutionInfo) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is ResolutionInfo) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return ResolutionInfo.decode(readValue(buffer)!); + + case 129: + return ResolutionInfo.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +class PreviewHostApi { + /// Constructor for [PreviewHostApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + PreviewHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _PreviewHostApiCodec(); + + Future create(int arg_identifier, int? arg_rotation, + ResolutionInfo? arg_targetResolution) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PreviewHostApi.create', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_identifier, arg_rotation, arg_targetResolution]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setSurfaceProvider(int arg_identifier) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PreviewHostApi.setSurfaceProvider', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_identifier]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as int?)!; + } + } + + Future getResolutionInfo(int arg_identifier) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PreviewHostApi.getResolutionInfo', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_identifier]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as ResolutionInfo?)!; + } + } +} + +class _PreviewFlutterApiCodec extends StandardMessageCodec { + const _PreviewFlutterApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is ResolutionInfo) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return ResolutionInfo.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class PreviewFlutterApi { + static const MessageCodec codec = _PreviewFlutterApiCodec(); + + void create( + int identifier, int targetRotation, ResolutionInfo? targetResolution); + static void setup(PreviewFlutterApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PreviewFlutterApi.create', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.PreviewFlutterApi.create was null.'); + final List args = (message as List?)!; + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.PreviewFlutterApi.create was null, expected non-null int.'); + final int? arg_targetRotation = (args[1] as int?); + assert(arg_targetRotation != null, + 'Argument for dev.flutter.pigeon.PreviewFlutterApi.create was null, expected non-null int.'); + final ResolutionInfo? arg_targetResolution = + (args[2] as ResolutionInfo?); + api.create( + arg_identifier!, arg_targetRotation!, arg_targetResolution); + return; + }); + } + } + } +} diff --git a/packages/camera/camera_android_camerax/lib/src/preview.dart b/packages/camera/camera_android_camerax/lib/src/preview.dart index f8ec6573d29d..72c6dad8057e 100644 --- a/packages/camera/camera_android_camerax/lib/src/preview.dart +++ b/packages/camera/camera_android_camerax/lib/src/preview.dart @@ -54,7 +54,7 @@ class Preview extends UseCase { /// /// * 'width', width of resolution specification in pixels /// * 'height', height of resolution specification in pixels - final Map? targetResolution; + final ResolutionInfo? targetResolution; /// Sets surface provider for the preview stream. /// @@ -64,7 +64,7 @@ class Preview extends UseCase { return _api.setSurfaceProviderFromInstance(this); } - Future> getResolutionInfo() { + Future getResolutionInfo() { return _api.getResolutionInfoFromInstance(this); } } @@ -86,8 +86,8 @@ class PreviewHostApiImpl extends PreviewHostApi { late final InstanceManager instanceManager; /// Creates a [Preview] with the target rotation provided if specified. - void createFromInstance(Preview instance, int? targetRotation, - Map? targetResolution) { + void createFromInstance( + Preview instance, int? targetRotation, ResolutionInfo? targetResolution) { int? identifier = instanceManager.getIdentifier(instance); identifier ??= instanceManager.addDartCreatedInstance(instance, onCopy: (Preview original) { @@ -115,8 +115,7 @@ class PreviewHostApiImpl extends PreviewHostApi { return surfaceTextureEntryId; } - Future> getResolutionInfoFromInstance( - Preview instance) async { + Future getResolutionInfoFromInstance(Preview instance) async { int? identifier = instanceManager.getIdentifier(instance); identifier ??= instanceManager.addDartCreatedInstance(instance, onCopy: (Preview original) { @@ -126,46 +125,7 @@ class PreviewHostApiImpl extends PreviewHostApi { targetRotation: original.targetRotation); }); - final Map resolutionInfo = - await getResolutionInfo(identifier); + final ResolutionInfo resolutionInfo = await getResolutionInfo(identifier); return resolutionInfo; } } - -/// Flutter API implementation of [Preview]. -class PreviewFlutterApiImpl extends PreviewFlutterApi { - /// Constructs a [PreviewFlutterApiImpl]. - PreviewFlutterApiImpl({ - this.binaryMessenger, - InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager; - - /// Receives binary data across the Flutter platform barrier. - /// - /// If it is null, the default BinaryMessenger will be used which routes to - /// the host platform. - final BinaryMessenger? binaryMessenger; - - /// Maintains instances stored to communicate with native language objects. - final InstanceManager instanceManager; - - @override - void create(int identifier, int? targetRotation, - Map? targetResolution) { - instanceManager.addHostCreatedInstance( - Preview.detached( - binaryMessenger: binaryMessenger, - instanceManager: instanceManager, - targetRotation: targetRotation, - targetResolution: targetResolution), - identifier, - onCopy: (Preview original) { - return Preview.detached( - binaryMessenger: binaryMessenger, - instanceManager: instanceManager, - targetRotation: targetRotation, - targetResolution: targetResolution); - }, - ); - } -} diff --git a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart index c69be929d4c8..c783f8fc4fcd 100644 --- a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart +++ b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart @@ -26,6 +26,16 @@ import 'package:pigeon/pigeon.dart'; ), ), ) +class ResolutionInfo { + ResolutionInfo({ + required this.width, + required this.height, + }); + + int width; + int height; +} + @HostApi(dartHostTestHandler: 'TestJavaObjectHostApi') abstract class JavaObjectHostApi { void dispose(int identifier); @@ -85,16 +95,9 @@ abstract class CameraFlutterApi { @HostApi(dartHostTestHandler: 'TestPreviewHostApi') abstract class PreviewHostApi { - void create( - int identifier, int? rotation, Map? targetResolution); + void create(int identifier, int? rotation, ResolutionInfo? targetResolution); int setSurfaceProvider(int identifier); - Map getResolutionInfo(int identifier); -} - -@FlutterApi() -abstract class PreviewFlutterApi { - void create( - int identifier, int targetRotation, Map? targetResolution); + ResolutionInfo getResolutionInfo(int identifier); } diff --git a/packages/camera/camera_android_camerax/test/preview_test.dart b/packages/camera/camera_android_camerax/test/preview_test.dart index 0aabd2029c95..54d42d52b349 100644 --- a/packages/camera/camera_android_camerax/test/preview_test.dart +++ b/packages/camera/camera_android_camerax/test/preview_test.dart @@ -17,11 +17,10 @@ void main() { TestWidgetsFlutterBinding.ensureInitialized(); group('Preview', () { - tearDown(() => TestCameraSelectorHostApi.setup(null)); + tearDown(() => TestPreviewHostApi.setup(null)); test('detachedCreateTest', () async { - final MockPreviewHostApi mockApi = - MockTestPreviewHostApi(); + final MockTestPreviewHostApi mockApi = MockTestPreviewHostApi(); TestPreviewHostApi.setup(mockApi); final InstanceManager instanceManager = InstanceManager( @@ -30,41 +29,34 @@ void main() { Preview.detached( instanceManager: instanceManager, targetRotation: 90, - targetResolution: Map{ - width: 10, - height: 50, - }, + targetResolution: ResolutionInfo(width: 50, height: 10), ); - verifyNever(mockApi.create(argThat(isA()), argThat(isA()),argThat(isA()))); + verifyNever(mockApi.create(argThat(isA()), argThat(isA()), + argThat(isA()))); }); test('createTest', () async { - final MockTestCPreviewHostApi mockApi = - MockTestPreviewHostApi(); - TestCPreviewHostApi.setup(mockApi); + final MockTestPreviewHostApi mockApi = MockTestPreviewHostApi(); + TestPreviewHostApi.setup(mockApi); final InstanceManager instanceManager = InstanceManager( onWeakReferenceRemoved: (_) {}, ); - CameraSelector( + Preview( instanceManager: instanceManager, targetRotation: 90, - targetResolution: Map{ - width: 10, - height: 50, - }, + targetResolution: ResolutionInfo(width: 10, height: 50), ); - verify(mockApi.create(argThat(isA(), argThat(equals(90)), argThat(equals( Map{ - width: 10, - height: 50, - }))), null)); + final VerificationResult createVerification = verify( + mockApi.create(argThat(isA()), argThat(equals(90)), captureAny)); + expect(createVerification.captured.single.width, equals(10)); + expect(createVerification.captured.single.height, equals(50)); }); test('setSurfaceProviderTest', () async { - final MockTestPreviewHostApi mockApi = - MockTestPreviewHostApi(); + final MockTestPreviewHostApi mockApi = MockTestPreviewHostApi(); TestPreviewHostApi.setup(mockApi); final InstanceManager instanceManager = InstanceManager( @@ -77,20 +69,19 @@ void main() { instanceManager.addHostCreatedInstance( preview, 0, - onCopy: (_) => CameraSelector.detached(), + onCopy: (_) => Preview.detached(), ); - when(mockApi.setTargetRotation(instanceManager.getIdentifier(preview) - )).thenReturn(8); - expect(await preview.setSurfaceProvider(), - equals(8)); + when(mockApi.setSurfaceProvider(instanceManager.getIdentifier(preview))) + .thenReturn(8); + expect(await preview.setSurfaceProvider(), equals(8)); - verify(mockApi.setSurfaceProvider(instanceManager.getIdentifier(preview))); + verify( + mockApi.setSurfaceProvider(instanceManager.getIdentifier(preview))); }); test('getResolutionInfoTest', () async { - final MockTestPreviewHostApi mockApi = - MockTestPreviewHostApi(); + final MockTestPreviewHostApi mockApi = MockTestPreviewHostApi(); TestPreviewHostApi.setup(mockApi); final InstanceManager instanceManager = InstanceManager( @@ -99,41 +90,23 @@ void main() { final Preview preview = Preview.detached( instanceManager: instanceManager, ); + final ResolutionInfo testResolutionInfo = + ResolutionInfo(width: 10, height: 60); instanceManager.addHostCreatedInstance( preview, 0, - onCopy: (_) => CameraSelector.detached(), + onCopy: (_) => Preview.detached(), ); - when(mockApi.getResolutionInfo(instanceManager.getIdentifier(preview) - )).thenReturn(Map {'width': 10, 'height': 60}); - expect(await preview.getResolutionInfo(), - equals(Map {'width': 10, 'height': 60})); + when(mockApi.getResolutionInfo(instanceManager.getIdentifier(preview))) + .thenReturn(testResolutionInfo); - verify(mockApi.getResolutionInfo(instanceManager.getIdentifier(preview))); - }); - - test('flutterApiCreateTest', () { - final InstanceManager instanceManager = InstanceManager( - onWeakReferenceRemoved: (_) {}, - ); - final PreviewFlutterApi flutterApi = PreviewFlutterApiImpl( - instanceManager: instanceManager, - ); + ResolutionInfo previewResolutionInfo = await preview.getResolutionInfo(); + expect(previewResolutionInfo.width, equals(10)); + expect(previewResolutionInfo.height, equals(60)); - flutterApi.create(0, 270, Map{'width': 60, 'height': 10}); - - expect(instanceManager.getInstanceWithWeakReference(0), - isA()); - expect( - (instanceManager.getInstanceWithWeakReference(0)! as Preview) - .targetRotation, - equals(270)); - expect( - (instanceManager.getInstanceWithWeakReference(0)! as Preview) - .targetResolution, - equals(Map{'width': 60, 'height': 10})); + verify(mockApi.getResolutionInfo(instanceManager.getIdentifier(preview))); }); }); } diff --git a/packages/camera/camera_android_camerax/test/preview_test.mocks.dart b/packages/camera/camera_android_camerax/test/preview_test.mocks.dart new file mode 100644 index 000000000000..a1016b260dda --- /dev/null +++ b/packages/camera/camera_android_camerax/test/preview_test.mocks.dart @@ -0,0 +1,81 @@ +// Mocks generated by Mockito 5.3.2 from annotations +// in camera_android_camerax/test/preview_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:camera_android_camerax/src/camerax_library.pigeon.dart' as _i2; +import 'package:mockito/mockito.dart' as _i1; + +import 'test_camerax_library.pigeon.dart' as _i3; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeResolutionInfo_0 extends _i1.SmartFake + implements _i2.ResolutionInfo { + _FakeResolutionInfo_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [TestPreviewHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestPreviewHostApi extends _i1.Mock + implements _i3.TestPreviewHostApi { + MockTestPreviewHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void create( + int? identifier, + int? rotation, + _i2.ResolutionInfo? targetResolution, + ) => + super.noSuchMethod( + Invocation.method( + #create, + [ + identifier, + rotation, + targetResolution, + ], + ), + returnValueForMissingStub: null, + ); + @override + int setSurfaceProvider(int? identifier) => (super.noSuchMethod( + Invocation.method( + #setSurfaceProvider, + [identifier], + ), + returnValue: 0, + ) as int); + @override + _i2.ResolutionInfo getResolutionInfo(int? identifier) => (super.noSuchMethod( + Invocation.method( + #getResolutionInfo, + [identifier], + ), + returnValue: _FakeResolutionInfo_0( + this, + Invocation.method( + #getResolutionInfo, + [identifier], + ), + ), + ) as _i2.ResolutionInfo); +} diff --git a/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart b/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart index c6afe067a3b6..3ff73db50a71 100644 --- a/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart +++ b/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart @@ -259,3 +259,104 @@ abstract class TestProcessCameraProviderHostApi { } } } + +class _TestPreviewHostApiCodec extends StandardMessageCodec { + const _TestPreviewHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is ResolutionInfo) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is ResolutionInfo) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return ResolutionInfo.decode(readValue(buffer)!); + + case 129: + return ResolutionInfo.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class TestPreviewHostApi { + static const MessageCodec codec = _TestPreviewHostApiCodec(); + + void create(int identifier, int? rotation, ResolutionInfo? targetResolution); + int setSurfaceProvider(int identifier); + ResolutionInfo getResolutionInfo(int identifier); + static void setup(TestPreviewHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PreviewHostApi.create', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.PreviewHostApi.create was null.'); + final List args = (message as List?)!; + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.PreviewHostApi.create was null, expected non-null int.'); + final int? arg_rotation = (args[1] as int?); + final ResolutionInfo? arg_targetResolution = + (args[2] as ResolutionInfo?); + api.create(arg_identifier!, arg_rotation, arg_targetResolution); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PreviewHostApi.setSurfaceProvider', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.PreviewHostApi.setSurfaceProvider was null.'); + final List args = (message as List?)!; + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.PreviewHostApi.setSurfaceProvider was null, expected non-null int.'); + final int output = api.setSurfaceProvider(arg_identifier!); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PreviewHostApi.getResolutionInfo', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.PreviewHostApi.getResolutionInfo was null.'); + final List args = (message as List?)!; + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.PreviewHostApi.getResolutionInfo was null, expected non-null int.'); + final ResolutionInfo output = api.getResolutionInfo(arg_identifier!); + return {'result': output}; + }); + } + } + } +} From 268d9f5de8205b78539fddc9498c38cc2838f07e Mon Sep 17 00:00:00 2001 From: camsim99 Date: Mon, 30 Jan 2023 11:55:53 -0800 Subject: [PATCH 04/22] Fix analyze --- .../lib/src/preview.dart | 4 ++- .../lib/src/surface.dart | 33 +++++++++++-------- .../test/preview_test.dart | 9 +++-- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/packages/camera/camera_android_camerax/lib/src/preview.dart b/packages/camera/camera_android_camerax/lib/src/preview.dart index 72c6dad8057e..097fdbaf06b2 100644 --- a/packages/camera/camera_android_camerax/lib/src/preview.dart +++ b/packages/camera/camera_android_camerax/lib/src/preview.dart @@ -46,7 +46,7 @@ class Preview extends UseCase { late final PreviewHostApiImpl _api; /// Target rotation of the camera used for the preview stream. - int? targetRotation; + final int? targetRotation; /// Target resolution of the camera preview stream. /// @@ -64,6 +64,7 @@ class Preview extends UseCase { return _api.setSurfaceProviderFromInstance(this); } + /// Gets the selected resolution information of this [Preview]. Future getResolutionInfo() { return _api.getResolutionInfoFromInstance(this); } @@ -115,6 +116,7 @@ class PreviewHostApiImpl extends PreviewHostApi { return surfaceTextureEntryId; } + /// Gets the resolution information of the specified [Preview] instance. Future getResolutionInfoFromInstance(Preview instance) async { int? identifier = instanceManager.getIdentifier(instance); identifier ??= instanceManager.addDartCreatedInstance(instance, diff --git a/packages/camera/camera_android_camerax/lib/src/surface.dart b/packages/camera/camera_android_camerax/lib/src/surface.dart index 3a7e2164295a..c2f2d436d009 100644 --- a/packages/camera/camera_android_camerax/lib/src/surface.dart +++ b/packages/camera/camera_android_camerax/lib/src/surface.dart @@ -2,26 +2,33 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:flutter/services.dart' show BinaryMessenger; - -import 'instance_manager.dart'; import 'java_object.dart'; -/// Handle onto raw buffer managed by screen copmositor. +/// Handle onto raw buffer managed by screen compositor. /// -/// See https://developer.android.com/reference/android/view/Surface.html#ROTATION_0--. +/// See https://developer.android.com/reference/android/view/Surface.html. class Surface extends JavaObject { /// Creates a detached [UseCase]. - Surface.detached( - {BinaryMessenger? binaryMessenger, InstanceManager? instanceManager}) - : super.detached( - binaryMessenger: binaryMessenger, instanceManager: instanceManager); + Surface.detached({super.binaryMessenger, super.instanceManager}) + : super.detached(); - static const ROTATION_0 = 0; + /// Rotation constant to signify the natural orientation. + /// + /// See https://developer.android.com/reference/android/view/Surface.html#ROTATION_0. + static const int ROTATION_0 = 0; - static const ROTATION_180 = 2; + /// Rotation constant to signify a 90 degrees rotation. + /// + /// See https://developer.android.com/reference/android/view/Surface.html#ROTATION_90. + static const int ROTATION_90 = 1; - static const ROTATION_270 = 3; + /// Rotation constant to signify a 180 degrees rotation. + /// + /// See https://developer.android.com/reference/android/view/Surface.html#ROTATION_180. + static const int ROTATION_180 = 2; - static const ROTATION_90 = 1; + /// Rotation constant to signify a 270 degrees rotation. + /// + /// See https://developer.android.com/reference/android/view/Surface.html#ROTATION_270. + static const int ROTATION_270 = 3; } diff --git a/packages/camera/camera_android_camerax/test/preview_test.dart b/packages/camera/camera_android_camerax/test/preview_test.dart index 54d42d52b349..50b1337030a8 100644 --- a/packages/camera/camera_android_camerax/test/preview_test.dart +++ b/packages/camera/camera_android_camerax/test/preview_test.dart @@ -51,8 +51,10 @@ void main() { final VerificationResult createVerification = verify( mockApi.create(argThat(isA()), argThat(equals(90)), captureAny)); - expect(createVerification.captured.single.width, equals(10)); - expect(createVerification.captured.single.height, equals(50)); + final ResolutionInfo capturedResolutionInfo = + createVerification.captured.single as ResolutionInfo; + expect(capturedResolutionInfo.width, equals(10)); + expect(capturedResolutionInfo.height, equals(50)); }); test('setSurfaceProviderTest', () async { @@ -102,7 +104,8 @@ void main() { when(mockApi.getResolutionInfo(instanceManager.getIdentifier(preview))) .thenReturn(testResolutionInfo); - ResolutionInfo previewResolutionInfo = await preview.getResolutionInfo(); + final ResolutionInfo previewResolutionInfo = + await preview.getResolutionInfo(); expect(previewResolutionInfo.width, equals(10)); expect(previewResolutionInfo.height, equals(60)); From 2001c3356dae9210bc0f15f571d9989cb8800149 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Mon, 30 Jan 2023 11:57:17 -0800 Subject: [PATCH 05/22] Update changelog --- packages/camera/camera_android_camerax/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index 389fc31e26e7..e76ad2c87443 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -6,3 +6,4 @@ * Adds ProcessCameraProvider class. * Bump CameraX version to 1.3.0-alpha02. * Adds Camera and UseCase classes, along with methods for binding UseCases to a lifecycle with the ProcessCameraProvider. +* Adds Preview and Surface classes needed to implement camera perview. From 7ff2a9412ca5799b5c6c622d34180a538300ebae Mon Sep 17 00:00:00 2001 From: camsim99 Date: Mon, 30 Jan 2023 12:16:42 -0800 Subject: [PATCH 06/22] Cleanup: --- .../plugins/camerax/PreviewHostApiImpl.java | 3 -- .../flutter/plugins/camerax/PreviewTest.java | 1 - .../lib/src/preview.dart | 45 ++++++------------- .../lib/src/surface.dart | 2 +- 4 files changed, 14 insertions(+), 37 deletions(-) diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java index 15f06bdf6e85..3a77cda7b014 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java @@ -22,9 +22,6 @@ public class PreviewHostApiImpl implements PreviewHostApi { private final InstanceManager instanceManager; private final TextureRegistry textureRegistry; - private final String RESOLUTION_WIDTH_KEY = "width"; - private final String RESOLUTION_HEIGHT_KEY = "height"; - @VisibleForTesting public CameraXProxy cameraXProxy = new CameraXProxy(); public PreviewHostApiImpl( diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java index 705de388e56a..54ed98749c0c 100644 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java @@ -58,7 +58,6 @@ public void createTest() { final PreviewHostApiImpl previewHostApi = new PreviewHostApiImpl(mockBinaryMessenger, testInstanceManager, mockTextureRegistry); final Preview.Builder mockPreviewBuilder = mock(Preview.Builder.class); - final Preview mockPreview = mock(Preview.class); final GeneratedCameraXLibrary.ResolutionInfo resolutionInfo = new GeneratedCameraXLibrary.ResolutionInfo.Builder().setWidth(10L).setHeight(50L).build(); diff --git a/packages/camera/camera_android_camerax/lib/src/preview.dart b/packages/camera/camera_android_camerax/lib/src/preview.dart index 097fdbaf06b2..503cb86273c4 100644 --- a/packages/camera/camera_android_camerax/lib/src/preview.dart +++ b/packages/camera/camera_android_camerax/lib/src/preview.dart @@ -4,7 +4,6 @@ import 'package:flutter/services.dart' show BinaryMessenger; -import 'android_camera_camerax_flutter_api_impls.dart'; import 'camerax_library.pigeon.dart'; import 'instance_manager.dart'; import 'java_object.dart'; @@ -25,11 +24,10 @@ class Preview extends UseCase { instanceManager: instanceManager) { _api = PreviewHostApiImpl( binaryMessenger: binaryMessenger, instanceManager: instanceManager); - AndroidCameraXCameraFlutterApis.instance.ensureSetUp(); _api.createFromInstance(this, targetRotation, targetResolution); } - /// Constructs a [CameraInfo] that is not automatically attached to a native object. + /// Constructs a [Preview] that is not automatically attached to a native object. Preview.detached( {BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, @@ -40,7 +38,6 @@ class Preview extends UseCase { instanceManager: instanceManager) { _api = PreviewHostApiImpl( binaryMessenger: binaryMessenger, instanceManager: instanceManager); - AndroidCameraXCameraFlutterApis.instance.ensureSetUp(); } late final PreviewHostApiImpl _api; @@ -49,16 +46,11 @@ class Preview extends UseCase { final int? targetRotation; /// Target resolution of the camera preview stream. - /// - /// Should include two entries: - /// - /// * 'width', width of resolution specification in pixels - /// * 'height', height of resolution specification in pixels final ResolutionInfo? targetResolution; - /// Sets surface provider for the preview stream. + /// Sets the surface provider for the preview stream. /// - /// Returns the ID of the FlutterSurfaceTextureEntry used on the back end + /// Returns the ID of the FlutterSurfaceTextureEntry used on the native end /// used to display the preview stream on a [Texture] of the same ID. Future setSurfaceProvider() { return _api.setSurfaceProviderFromInstance(this); @@ -89,8 +81,7 @@ class PreviewHostApiImpl extends PreviewHostApi { /// Creates a [Preview] with the target rotation provided if specified. void createFromInstance( Preview instance, int? targetRotation, ResolutionInfo? targetResolution) { - int? identifier = instanceManager.getIdentifier(instance); - identifier ??= instanceManager.addDartCreatedInstance(instance, + final int identifier = instanceManager.addDartCreatedInstance(instance, onCopy: (Preview original) { return Preview.detached( binaryMessenger: binaryMessenger, @@ -100,34 +91,24 @@ class PreviewHostApiImpl extends PreviewHostApi { create(identifier, targetRotation, targetResolution); } - /// Sets the surface provider of the provided [Preview] instance and returns + /// Sets the surface provider of the specified [Preview] instance and returns /// the ID corresponding to the surface it will provide. Future setSurfaceProviderFromInstance(Preview instance) async { - int? identifier = instanceManager.getIdentifier(instance); - identifier ??= instanceManager.addDartCreatedInstance(instance, - onCopy: (Preview original) { - return Preview.detached( - binaryMessenger: binaryMessenger, - instanceManager: instanceManager, - targetRotation: original.targetRotation); - }); + final int? identifier = instanceManager.getIdentifier(instance); + assert(identifier != null, + 'No Preview has the identifer of that requested to set the surface provider on.'); - final int surfaceTextureEntryId = await setSurfaceProvider(identifier); + final int surfaceTextureEntryId = await setSurfaceProvider(identifier!); return surfaceTextureEntryId; } /// Gets the resolution information of the specified [Preview] instance. Future getResolutionInfoFromInstance(Preview instance) async { - int? identifier = instanceManager.getIdentifier(instance); - identifier ??= instanceManager.addDartCreatedInstance(instance, - onCopy: (Preview original) { - return Preview.detached( - binaryMessenger: binaryMessenger, - instanceManager: instanceManager, - targetRotation: original.targetRotation); - }); + final int? identifier = instanceManager.getIdentifier(instance); + assert(identifier != null, + 'No Preview has the identifer of that requested to get the resolution information for.'); - final ResolutionInfo resolutionInfo = await getResolutionInfo(identifier); + final ResolutionInfo resolutionInfo = await getResolutionInfo(identifier!); return resolutionInfo; } } diff --git a/packages/camera/camera_android_camerax/lib/src/surface.dart b/packages/camera/camera_android_camerax/lib/src/surface.dart index c2f2d436d009..ea8cf8cb751e 100644 --- a/packages/camera/camera_android_camerax/lib/src/surface.dart +++ b/packages/camera/camera_android_camerax/lib/src/surface.dart @@ -4,7 +4,7 @@ import 'java_object.dart'; -/// Handle onto raw buffer managed by screen compositor. +/// Handle onto the raw buffer managed by screen compositor. /// /// See https://developer.android.com/reference/android/view/Surface.html. class Surface extends JavaObject { From dfc3071cfc56bab36a696e206cc4c068bc3fa747 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Mon, 30 Jan 2023 13:48:15 -0800 Subject: [PATCH 07/22] Cleanup and add switch --- .../plugins/camerax/PreviewHostApiImpl.java | 15 ++++++++++++++- .../camera_android_camerax/lib/src/preview.dart | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java index 3a77cda7b014..197004f7857b 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java @@ -65,7 +65,20 @@ public void onSurfaceRequested(SurfaceRequest request) { request.getResolution().getWidth(), request.getResolution().getHeight()); Surface flutterSurface = cameraXProxy.createSurface(surfaceTexture); request.provideSurface( - flutterSurface, Executors.newSingleThreadExecutor(), (result) -> {}); + flutterSurface, Executors.newSingleThreadExecutor(), (result) -> { + switch(result) { + case SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY: + flutterSurfaceTexture.release(); + break; + case SurfaceRequest.Result.RESULT_REQUEST_CANCELLED: + case SurfaceRequest.Result.RESULT_INVALID_SURFACE: + case SurfaceRequest.Result.RESULT_SURFACE_ALREADY_PROVIDED: + case SurfaceRequest.Result.RESULT_WILL_NOT_PROVIDE_SURFACE: + default: + // TODO(camsim99): Use onCameraError to send these errors to the Dart side. + break; + } + }); }; }; preview.setSurfaceProvider(surfaceProvider); diff --git a/packages/camera/camera_android_camerax/lib/src/preview.dart b/packages/camera/camera_android_camerax/lib/src/preview.dart index 503cb86273c4..036426f689dc 100644 --- a/packages/camera/camera_android_camerax/lib/src/preview.dart +++ b/packages/camera/camera_android_camerax/lib/src/preview.dart @@ -56,7 +56,7 @@ class Preview extends UseCase { return _api.setSurfaceProviderFromInstance(this); } - /// Gets the selected resolution information of this [Preview]. + /// Retrieves the selected resolution information of this [Preview]. Future getResolutionInfo() { return _api.getResolutionInfoFromInstance(this); } From 4ba07e241ca189b5fc21c662660be199e711188f Mon Sep 17 00:00:00 2001 From: camsim99 Date: Mon, 30 Jan 2023 14:56:59 -0800 Subject: [PATCH 08/22] Finish todo --- .../java/io/flutter/plugins/camerax/PreviewHostApiImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java index 197004f7857b..c62939b2962a 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java @@ -75,7 +75,8 @@ public void onSurfaceRequested(SurfaceRequest request) { case SurfaceRequest.Result.RESULT_SURFACE_ALREADY_PROVIDED: case SurfaceRequest.Result.RESULT_WILL_NOT_PROVIDE_SURFACE: default: - // TODO(camsim99): Use onCameraError to send these errors to the Dart side. + // TODO(camsim99): Use onCameraError to send the errors to the Dart side. + // See https://github.com/flutter/flutter/issues/119571 for more context. break; } }); From fb44db3b8cdefe1c8a784585078d6c5d75d54bb4 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Tue, 31 Jan 2023 11:39:39 -0800 Subject: [PATCH 09/22] Add onCameraError --- .../camerax/CameraAndroidCameraxPlugin.java | 2 +- .../flutter/plugins/camerax/CameraXProxy.java | 8 +- .../camerax/GeneratedCameraXLibrary.java | 974 +++++++++++------- .../plugins/camerax/PreviewHostApiImpl.java | 41 +- .../camerax/SystemServicesFlutterApiImpl.java | 10 +- .../camerax/SystemServicesHostApiImpl.java | 3 +- .../flutter/plugins/camerax/PreviewTest.java | 37 +- .../lib/src/camerax_library.g.dart | 287 ++++-- .../lib/src/system_services.dart | 14 +- .../pigeons/camerax_library.dart | 2 + .../test/system_services_test.dart | 8 + .../test/test_camerax_library.g.dart | 236 +++-- 12 files changed, 1056 insertions(+), 566 deletions(-) diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java index 2285cd8eb2fd..b61e7ac72224 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java @@ -50,7 +50,7 @@ void setUp(BinaryMessenger binaryMessenger, Context context, TextureRegistry tex systemServicesHostApi = new SystemServicesHostApiImpl(binaryMessenger, instanceManager); GeneratedCameraXLibrary.SystemServicesHostApi.setup(binaryMessenger, systemServicesHostApi); GeneratedCameraXLibrary.PreviewHostApi.setup( - binaryMessenger, new PreviewHostApiImpl(binaryMessenger, instanceManager, textureRegistry)); + binaryMessenger, new PreviewHostApiImpl(binaryMessenger, instanceManager, textureRegistry)); } @Override diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java index 47e0af91795b..105a63e4b889 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java @@ -4,11 +4,12 @@ package io.flutter.plugins.camerax; +import android.app.Activity; import android.graphics.SurfaceTexture; import android.view.Surface; -import android.app.Activity; import androidx.camera.core.CameraSelector; import androidx.camera.core.Preview; +import io.flutter.plugin.common.BinaryMessenger; public class CameraXProxy { public CameraSelector.Builder createCameraSelectorBuilder() { @@ -34,4 +35,9 @@ public Preview.Builder createPreviewBuilder() { public Surface createSurface(SurfaceTexture surfaceTexture) { return new Surface(surfaceTexture); } + + public SystemServicesFlutterApiImpl createSystemServicesFlutterApi( + BinaryMessenger binaryMessenger) { + return new SystemServicesFlutterApiImpl(binaryMessenger); + } } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java index 9ea1d4fe3b1b..fe4a54a2f894 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java @@ -15,11 +15,11 @@ import io.flutter.plugin.common.StandardMessageCodec; import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; -import java.util.Arrays; import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.HashMap; /** Generated class from Pigeon. */ @SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression"}) @@ -28,7 +28,11 @@ public class GeneratedCameraXLibrary { /** Generated class from Pigeon that represents data sent in messages. */ public static class ResolutionInfo { private @NonNull Long width; - public @NonNull Long getWidth() { return width; } + + public @NonNull Long getWidth() { + return width; + } + public void setWidth(@NonNull Long setterArg) { if (setterArg == null) { throw new IllegalStateException("Nonnull field \"width\" is null."); @@ -37,7 +41,11 @@ public void setWidth(@NonNull Long setterArg) { } private @NonNull Long height; - public @NonNull Long getHeight() { return height; } + + public @NonNull Long getHeight() { + return height; + } + public void setHeight(@NonNull Long setterArg) { if (setterArg == null) { throw new IllegalStateException("Nonnull field \"height\" is null."); @@ -47,17 +55,22 @@ public void setHeight(@NonNull Long setterArg) { /** Constructor is private to enforce null safety; use Builder. */ private ResolutionInfo() {} + public static final class Builder { private @Nullable Long width; + public @NonNull Builder setWidth(@NonNull Long setterArg) { this.width = setterArg; return this; } + private @Nullable Long height; + public @NonNull Builder setHeight(@NonNull Long setterArg) { this.height = setterArg; return this; } + public @NonNull ResolutionInfo build() { ResolutionInfo pigeonReturn = new ResolutionInfo(); pigeonReturn.setWidth(width); @@ -65,18 +78,25 @@ public static final class Builder { return pigeonReturn; } } - @NonNull Map toMap() { + + @NonNull + Map toMap() { Map toMapResult = new HashMap<>(); toMapResult.put("width", width); toMapResult.put("height", height); return toMapResult; } + static @NonNull ResolutionInfo fromMap(@NonNull Map map) { ResolutionInfo pigeonResult = new ResolutionInfo(); Object width = map.get("width"); - pigeonResult.setWidth((width == null) ? null : ((width instanceof Integer) ? (Integer)width : (Long)width)); + pigeonResult.setWidth( + (width == null) ? null : ((width instanceof Integer) ? (Integer) width : (Long) width)); Object height = map.get("height"); - pigeonResult.setHeight((height == null) ? null : ((height instanceof Integer) ? (Integer)height : (Long)height)); + pigeonResult.setHeight( + (height == null) + ? null + : ((height instanceof Integer) ? (Integer) height : (Long) height)); return pigeonResult; } } @@ -84,7 +104,11 @@ public static final class Builder { /** Generated class from Pigeon that represents data sent in messages. */ public static class CameraPermissionsErrorData { private @NonNull String errorCode; - public @NonNull String getErrorCode() { return errorCode; } + + public @NonNull String getErrorCode() { + return errorCode; + } + public void setErrorCode(@NonNull String setterArg) { if (setterArg == null) { throw new IllegalStateException("Nonnull field \"errorCode\" is null."); @@ -93,7 +117,11 @@ public void setErrorCode(@NonNull String setterArg) { } private @NonNull String description; - public @NonNull String getDescription() { return description; } + + public @NonNull String getDescription() { + return description; + } + public void setDescription(@NonNull String setterArg) { if (setterArg == null) { throw new IllegalStateException("Nonnull field \"description\" is null."); @@ -103,17 +131,22 @@ public void setDescription(@NonNull String setterArg) { /** Constructor is private to enforce null safety; use Builder. */ private CameraPermissionsErrorData() {} + public static final class Builder { private @Nullable String errorCode; + public @NonNull Builder setErrorCode(@NonNull String setterArg) { this.errorCode = setterArg; return this; } + private @Nullable String description; + public @NonNull Builder setDescription(@NonNull String setterArg) { this.description = setterArg; return this; } + public @NonNull CameraPermissionsErrorData build() { CameraPermissionsErrorData pigeonReturn = new CameraPermissionsErrorData(); pigeonReturn.setErrorCode(errorCode); @@ -121,32 +154,38 @@ public static final class Builder { return pigeonReturn; } } - @NonNull Map toMap() { + + @NonNull + Map toMap() { Map toMapResult = new HashMap<>(); toMapResult.put("errorCode", errorCode); toMapResult.put("description", description); return toMapResult; } + static @NonNull CameraPermissionsErrorData fromMap(@NonNull Map map) { CameraPermissionsErrorData pigeonResult = new CameraPermissionsErrorData(); Object errorCode = map.get("errorCode"); - pigeonResult.setErrorCode((String)errorCode); + pigeonResult.setErrorCode((String) errorCode); Object description = map.get("description"); - pigeonResult.setDescription((String)description); + pigeonResult.setDescription((String) description); return pigeonResult; } } public interface Result { void success(T result); + void error(Throwable error); } + private static class JavaObjectHostApiCodec extends StandardMessageCodec { public static final JavaObjectHostApiCodec INSTANCE = new JavaObjectHostApiCodec(); + private JavaObjectHostApiCodec() {} } - /** Generated interface from Pigeon that represents a handler of messages from Flutter.*/ + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface JavaObjectHostApi { void dispose(@NonNull Long identifier); @@ -155,237 +194,302 @@ static MessageCodec getCodec() { return JavaObjectHostApiCodec.INSTANCE; } - /** Sets up an instance of `JavaObjectHostApi` to handle messages through the `binaryMessenger`. */ + /** + * Sets up an instance of `JavaObjectHostApi` to handle messages through the `binaryMessenger`. + */ static void setup(BinaryMessenger binaryMessenger, JavaObjectHostApi api) { { BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.JavaObjectHostApi.dispose", getCodec()); + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.JavaObjectHostApi.dispose", getCodec()); if (api != null) { - channel.setMessageHandler((message, reply) -> { - Map wrapped = new HashMap<>(); - try { - ArrayList args = (ArrayList)message; - Number identifierArg = (Number)args.get(0); - if (identifierArg == null) { - throw new NullPointerException("identifierArg unexpectedly null."); - } - api.dispose((identifierArg == null) ? null : identifierArg.longValue()); - wrapped.put("result", null); - } - catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - } - reply.reply(wrapped); - }); + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + if (identifierArg == null) { + throw new NullPointerException("identifierArg unexpectedly null."); + } + api.dispose((identifierArg == null) ? null : identifierArg.longValue()); + wrapped.put("result", null); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); } else { channel.setMessageHandler(null); } } } } + private static class JavaObjectFlutterApiCodec extends StandardMessageCodec { public static final JavaObjectFlutterApiCodec INSTANCE = new JavaObjectFlutterApiCodec(); + private JavaObjectFlutterApiCodec() {} } - /** Generated class from Pigeon that represents Flutter messages that can be called from Java.*/ + /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */ public static class JavaObjectFlutterApi { private final BinaryMessenger binaryMessenger; - public JavaObjectFlutterApi(BinaryMessenger argBinaryMessenger){ + + public JavaObjectFlutterApi(BinaryMessenger argBinaryMessenger) { this.binaryMessenger = argBinaryMessenger; } + public interface Reply { void reply(T reply); } + static MessageCodec getCodec() { return JavaObjectFlutterApiCodec.INSTANCE; } public void dispose(@NonNull Long identifierArg, Reply callback) { BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.JavaObjectFlutterApi.dispose", getCodec()); - channel.send(new ArrayList(Arrays.asList(identifierArg)), channelReply -> { - callback.reply(null); - }); + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.JavaObjectFlutterApi.dispose", getCodec()); + channel.send( + new ArrayList(Arrays.asList(identifierArg)), + channelReply -> { + callback.reply(null); + }); } } + private static class CameraInfoHostApiCodec extends StandardMessageCodec { public static final CameraInfoHostApiCodec INSTANCE = new CameraInfoHostApiCodec(); + private CameraInfoHostApiCodec() {} } - /** Generated interface from Pigeon that represents a handler of messages from Flutter.*/ + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface CameraInfoHostApi { - @NonNull Long getSensorRotationDegrees(@NonNull Long identifier); + @NonNull + Long getSensorRotationDegrees(@NonNull Long identifier); /** The codec used by CameraInfoHostApi. */ static MessageCodec getCodec() { return CameraInfoHostApiCodec.INSTANCE; } - /** Sets up an instance of `CameraInfoHostApi` to handle messages through the `binaryMessenger`. */ + /** + * Sets up an instance of `CameraInfoHostApi` to handle messages through the `binaryMessenger`. + */ static void setup(BinaryMessenger binaryMessenger, CameraInfoHostApi api) { { BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.CameraInfoHostApi.getSensorRotationDegrees", getCodec()); + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.CameraInfoHostApi.getSensorRotationDegrees", + getCodec()); if (api != null) { - channel.setMessageHandler((message, reply) -> { - Map wrapped = new HashMap<>(); - try { - ArrayList args = (ArrayList)message; - Number identifierArg = (Number)args.get(0); - if (identifierArg == null) { - throw new NullPointerException("identifierArg unexpectedly null."); - } - Long output = api.getSensorRotationDegrees((identifierArg == null) ? null : identifierArg.longValue()); - wrapped.put("result", output); - } - catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - } - reply.reply(wrapped); - }); + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + if (identifierArg == null) { + throw new NullPointerException("identifierArg unexpectedly null."); + } + Long output = + api.getSensorRotationDegrees( + (identifierArg == null) ? null : identifierArg.longValue()); + wrapped.put("result", output); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); } else { channel.setMessageHandler(null); } } } } + private static class CameraInfoFlutterApiCodec extends StandardMessageCodec { public static final CameraInfoFlutterApiCodec INSTANCE = new CameraInfoFlutterApiCodec(); + private CameraInfoFlutterApiCodec() {} } - /** Generated class from Pigeon that represents Flutter messages that can be called from Java.*/ + /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */ public static class CameraInfoFlutterApi { private final BinaryMessenger binaryMessenger; - public CameraInfoFlutterApi(BinaryMessenger argBinaryMessenger){ + + public CameraInfoFlutterApi(BinaryMessenger argBinaryMessenger) { this.binaryMessenger = argBinaryMessenger; } + public interface Reply { void reply(T reply); } + static MessageCodec getCodec() { return CameraInfoFlutterApiCodec.INSTANCE; } public void create(@NonNull Long identifierArg, Reply callback) { BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.CameraInfoFlutterApi.create", getCodec()); - channel.send(new ArrayList(Arrays.asList(identifierArg)), channelReply -> { - callback.reply(null); - }); + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.CameraInfoFlutterApi.create", getCodec()); + channel.send( + new ArrayList(Arrays.asList(identifierArg)), + channelReply -> { + callback.reply(null); + }); } } + private static class CameraSelectorHostApiCodec extends StandardMessageCodec { public static final CameraSelectorHostApiCodec INSTANCE = new CameraSelectorHostApiCodec(); + private CameraSelectorHostApiCodec() {} } - /** Generated interface from Pigeon that represents a handler of messages from Flutter.*/ + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface CameraSelectorHostApi { void create(@NonNull Long identifier, @Nullable Long lensFacing); - @NonNull List filter(@NonNull Long identifier, @NonNull List cameraInfoIds); + + @NonNull + List filter(@NonNull Long identifier, @NonNull List cameraInfoIds); /** The codec used by CameraSelectorHostApi. */ static MessageCodec getCodec() { return CameraSelectorHostApiCodec.INSTANCE; } - /** Sets up an instance of `CameraSelectorHostApi` to handle messages through the `binaryMessenger`. */ + /** + * Sets up an instance of `CameraSelectorHostApi` to handle messages through the + * `binaryMessenger`. + */ static void setup(BinaryMessenger binaryMessenger, CameraSelectorHostApi api) { { BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.CameraSelectorHostApi.create", getCodec()); + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.CameraSelectorHostApi.create", getCodec()); if (api != null) { - channel.setMessageHandler((message, reply) -> { - Map wrapped = new HashMap<>(); - try { - ArrayList args = (ArrayList)message; - Number identifierArg = (Number)args.get(0); - if (identifierArg == null) { - throw new NullPointerException("identifierArg unexpectedly null."); - } - Number lensFacingArg = (Number)args.get(1); - api.create((identifierArg == null) ? null : identifierArg.longValue(), (lensFacingArg == null) ? null : lensFacingArg.longValue()); - wrapped.put("result", null); - } - catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - } - reply.reply(wrapped); - }); + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + if (identifierArg == null) { + throw new NullPointerException("identifierArg unexpectedly null."); + } + Number lensFacingArg = (Number) args.get(1); + api.create( + (identifierArg == null) ? null : identifierArg.longValue(), + (lensFacingArg == null) ? null : lensFacingArg.longValue()); + wrapped.put("result", null); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); } else { channel.setMessageHandler(null); } } { BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.CameraSelectorHostApi.filter", getCodec()); + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.CameraSelectorHostApi.filter", getCodec()); if (api != null) { - channel.setMessageHandler((message, reply) -> { - Map wrapped = new HashMap<>(); - try { - ArrayList args = (ArrayList)message; - Number identifierArg = (Number)args.get(0); - if (identifierArg == null) { - throw new NullPointerException("identifierArg unexpectedly null."); - } - List cameraInfoIdsArg = (List)args.get(1); - if (cameraInfoIdsArg == null) { - throw new NullPointerException("cameraInfoIdsArg unexpectedly null."); - } - List output = api.filter((identifierArg == null) ? null : identifierArg.longValue(), cameraInfoIdsArg); - wrapped.put("result", output); - } - catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - } - reply.reply(wrapped); - }); + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + if (identifierArg == null) { + throw new NullPointerException("identifierArg unexpectedly null."); + } + List cameraInfoIdsArg = (List) args.get(1); + if (cameraInfoIdsArg == null) { + throw new NullPointerException("cameraInfoIdsArg unexpectedly null."); + } + List output = + api.filter( + (identifierArg == null) ? null : identifierArg.longValue(), + cameraInfoIdsArg); + wrapped.put("result", output); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); } else { channel.setMessageHandler(null); } } } } + private static class CameraSelectorFlutterApiCodec extends StandardMessageCodec { - public static final CameraSelectorFlutterApiCodec INSTANCE = new CameraSelectorFlutterApiCodec(); + public static final CameraSelectorFlutterApiCodec INSTANCE = + new CameraSelectorFlutterApiCodec(); + private CameraSelectorFlutterApiCodec() {} } - /** Generated class from Pigeon that represents Flutter messages that can be called from Java.*/ + /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */ public static class CameraSelectorFlutterApi { private final BinaryMessenger binaryMessenger; - public CameraSelectorFlutterApi(BinaryMessenger argBinaryMessenger){ + + public CameraSelectorFlutterApi(BinaryMessenger argBinaryMessenger) { this.binaryMessenger = argBinaryMessenger; } + public interface Reply { void reply(T reply); } + static MessageCodec getCodec() { return CameraSelectorFlutterApiCodec.INSTANCE; } - public void create(@NonNull Long identifierArg, @Nullable Long lensFacingArg, Reply callback) { + public void create( + @NonNull Long identifierArg, @Nullable Long lensFacingArg, Reply callback) { BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.CameraSelectorFlutterApi.create", getCodec()); - channel.send(new ArrayList(Arrays.asList(identifierArg, lensFacingArg)), channelReply -> { - callback.reply(null); - }); + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.CameraSelectorFlutterApi.create", getCodec()); + channel.send( + new ArrayList(Arrays.asList(identifierArg, lensFacingArg)), + channelReply -> { + callback.reply(null); + }); } } + private static class ProcessCameraProviderHostApiCodec extends StandardMessageCodec { - public static final ProcessCameraProviderHostApiCodec INSTANCE = new ProcessCameraProviderHostApiCodec(); + public static final ProcessCameraProviderHostApiCodec INSTANCE = + new ProcessCameraProviderHostApiCodec(); + private ProcessCameraProviderHostApiCodec() {} } - /** Generated interface from Pigeon that represents a handler of messages from Flutter.*/ + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface ProcessCameraProviderHostApi { void getInstance(Result result); - @NonNull List getAvailableCameraInfos(@NonNull Long identifier); - @NonNull Long bindToLifecycle(@NonNull Long identifier, @NonNull Long cameraSelectorIdentifier, @NonNull List useCaseIds); + + @NonNull + List getAvailableCameraInfos(@NonNull Long identifier); + + @NonNull + Long bindToLifecycle( + @NonNull Long identifier, + @NonNull Long cameraSelectorIdentifier, + @NonNull List useCaseIds); + void unbind(@NonNull Long identifier, @NonNull List useCaseIds); + void unbindAll(@NonNull Long identifier); /** The codec used by ProcessCameraProviderHostApi. */ @@ -393,229 +497,284 @@ static MessageCodec getCodec() { return ProcessCameraProviderHostApiCodec.INSTANCE; } - /** Sets up an instance of `ProcessCameraProviderHostApi` to handle messages through the `binaryMessenger`. */ + /** + * Sets up an instance of `ProcessCameraProviderHostApi` to handle messages through the + * `binaryMessenger`. + */ static void setup(BinaryMessenger binaryMessenger, ProcessCameraProviderHostApi api) { { BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.ProcessCameraProviderHostApi.getInstance", getCodec()); + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.ProcessCameraProviderHostApi.getInstance", + getCodec()); if (api != null) { - channel.setMessageHandler((message, reply) -> { - Map wrapped = new HashMap<>(); - try { - Result resultCallback = new Result() { - public void success(Long result) { - wrapped.put("result", result); - reply.reply(wrapped); - } - public void error(Throwable error) { - wrapped.put("error", wrapError(error)); + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + Result resultCallback = + new Result() { + public void success(Long result) { + wrapped.put("result", result); + reply.reply(wrapped); + } + + public void error(Throwable error) { + wrapped.put("error", wrapError(error)); + reply.reply(wrapped); + } + }; + + api.getInstance(resultCallback); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); reply.reply(wrapped); } - }; - - api.getInstance(resultCallback); - } - catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - reply.reply(wrapped); - } - }); + }); } else { channel.setMessageHandler(null); } } { BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.ProcessCameraProviderHostApi.getAvailableCameraInfos", getCodec()); + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.ProcessCameraProviderHostApi.getAvailableCameraInfos", + getCodec()); if (api != null) { - channel.setMessageHandler((message, reply) -> { - Map wrapped = new HashMap<>(); - try { - ArrayList args = (ArrayList)message; - Number identifierArg = (Number)args.get(0); - if (identifierArg == null) { - throw new NullPointerException("identifierArg unexpectedly null."); - } - List output = api.getAvailableCameraInfos((identifierArg == null) ? null : identifierArg.longValue()); - wrapped.put("result", output); - } - catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - } - reply.reply(wrapped); - }); + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + if (identifierArg == null) { + throw new NullPointerException("identifierArg unexpectedly null."); + } + List output = + api.getAvailableCameraInfos( + (identifierArg == null) ? null : identifierArg.longValue()); + wrapped.put("result", output); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); } else { channel.setMessageHandler(null); } } { BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.ProcessCameraProviderHostApi.bindToLifecycle", getCodec()); + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.ProcessCameraProviderHostApi.bindToLifecycle", + getCodec()); if (api != null) { - channel.setMessageHandler((message, reply) -> { - Map wrapped = new HashMap<>(); - try { - ArrayList args = (ArrayList)message; - Number identifierArg = (Number)args.get(0); - if (identifierArg == null) { - throw new NullPointerException("identifierArg unexpectedly null."); - } - Number cameraSelectorIdentifierArg = (Number)args.get(1); - if (cameraSelectorIdentifierArg == null) { - throw new NullPointerException("cameraSelectorIdentifierArg unexpectedly null."); - } - List useCaseIdsArg = (List)args.get(2); - if (useCaseIdsArg == null) { - throw new NullPointerException("useCaseIdsArg unexpectedly null."); - } - Long output = api.bindToLifecycle((identifierArg == null) ? null : identifierArg.longValue(), (cameraSelectorIdentifierArg == null) ? null : cameraSelectorIdentifierArg.longValue(), useCaseIdsArg); - wrapped.put("result", output); - } - catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - } - reply.reply(wrapped); - }); + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + if (identifierArg == null) { + throw new NullPointerException("identifierArg unexpectedly null."); + } + Number cameraSelectorIdentifierArg = (Number) args.get(1); + if (cameraSelectorIdentifierArg == null) { + throw new NullPointerException( + "cameraSelectorIdentifierArg unexpectedly null."); + } + List useCaseIdsArg = (List) args.get(2); + if (useCaseIdsArg == null) { + throw new NullPointerException("useCaseIdsArg unexpectedly null."); + } + Long output = + api.bindToLifecycle( + (identifierArg == null) ? null : identifierArg.longValue(), + (cameraSelectorIdentifierArg == null) + ? null + : cameraSelectorIdentifierArg.longValue(), + useCaseIdsArg); + wrapped.put("result", output); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); } else { channel.setMessageHandler(null); } } { BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.ProcessCameraProviderHostApi.unbind", getCodec()); + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.ProcessCameraProviderHostApi.unbind", + getCodec()); if (api != null) { - channel.setMessageHandler((message, reply) -> { - Map wrapped = new HashMap<>(); - try { - ArrayList args = (ArrayList)message; - Number identifierArg = (Number)args.get(0); - if (identifierArg == null) { - throw new NullPointerException("identifierArg unexpectedly null."); - } - List useCaseIdsArg = (List)args.get(1); - if (useCaseIdsArg == null) { - throw new NullPointerException("useCaseIdsArg unexpectedly null."); - } - api.unbind((identifierArg == null) ? null : identifierArg.longValue(), useCaseIdsArg); - wrapped.put("result", null); - } - catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - } - reply.reply(wrapped); - }); + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + if (identifierArg == null) { + throw new NullPointerException("identifierArg unexpectedly null."); + } + List useCaseIdsArg = (List) args.get(1); + if (useCaseIdsArg == null) { + throw new NullPointerException("useCaseIdsArg unexpectedly null."); + } + api.unbind( + (identifierArg == null) ? null : identifierArg.longValue(), useCaseIdsArg); + wrapped.put("result", null); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); } else { channel.setMessageHandler(null); } } { BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.ProcessCameraProviderHostApi.unbindAll", getCodec()); + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.ProcessCameraProviderHostApi.unbindAll", + getCodec()); if (api != null) { - channel.setMessageHandler((message, reply) -> { - Map wrapped = new HashMap<>(); - try { - ArrayList args = (ArrayList)message; - Number identifierArg = (Number)args.get(0); - if (identifierArg == null) { - throw new NullPointerException("identifierArg unexpectedly null."); - } - api.unbindAll((identifierArg == null) ? null : identifierArg.longValue()); - wrapped.put("result", null); - } - catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - } - reply.reply(wrapped); - }); + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + if (identifierArg == null) { + throw new NullPointerException("identifierArg unexpectedly null."); + } + api.unbindAll((identifierArg == null) ? null : identifierArg.longValue()); + wrapped.put("result", null); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); } else { channel.setMessageHandler(null); } } } } + private static class ProcessCameraProviderFlutterApiCodec extends StandardMessageCodec { - public static final ProcessCameraProviderFlutterApiCodec INSTANCE = new ProcessCameraProviderFlutterApiCodec(); + public static final ProcessCameraProviderFlutterApiCodec INSTANCE = + new ProcessCameraProviderFlutterApiCodec(); + private ProcessCameraProviderFlutterApiCodec() {} } - /** Generated class from Pigeon that represents Flutter messages that can be called from Java.*/ + /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */ public static class ProcessCameraProviderFlutterApi { private final BinaryMessenger binaryMessenger; - public ProcessCameraProviderFlutterApi(BinaryMessenger argBinaryMessenger){ + + public ProcessCameraProviderFlutterApi(BinaryMessenger argBinaryMessenger) { this.binaryMessenger = argBinaryMessenger; } + public interface Reply { void reply(T reply); } + static MessageCodec getCodec() { return ProcessCameraProviderFlutterApiCodec.INSTANCE; } public void create(@NonNull Long identifierArg, Reply callback) { BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.ProcessCameraProviderFlutterApi.create", getCodec()); - channel.send(new ArrayList(Arrays.asList(identifierArg)), channelReply -> { - callback.reply(null); - }); + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.ProcessCameraProviderFlutterApi.create", + getCodec()); + channel.send( + new ArrayList(Arrays.asList(identifierArg)), + channelReply -> { + callback.reply(null); + }); } } + private static class CameraFlutterApiCodec extends StandardMessageCodec { public static final CameraFlutterApiCodec INSTANCE = new CameraFlutterApiCodec(); + private CameraFlutterApiCodec() {} } - /** Generated class from Pigeon that represents Flutter messages that can be called from Java.*/ + /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */ public static class CameraFlutterApi { private final BinaryMessenger binaryMessenger; - public CameraFlutterApi(BinaryMessenger argBinaryMessenger){ + + public CameraFlutterApi(BinaryMessenger argBinaryMessenger) { this.binaryMessenger = argBinaryMessenger; } + public interface Reply { void reply(T reply); } + static MessageCodec getCodec() { return CameraFlutterApiCodec.INSTANCE; } public void create(@NonNull Long identifierArg, Reply callback) { BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.CameraFlutterApi.create", getCodec()); - channel.send(new ArrayList(Arrays.asList(identifierArg)), channelReply -> { - callback.reply(null); - }); + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.CameraFlutterApi.create", getCodec()); + channel.send( + new ArrayList(Arrays.asList(identifierArg)), + channelReply -> { + callback.reply(null); + }); } } + private static class SystemServicesHostApiCodec extends StandardMessageCodec { public static final SystemServicesHostApiCodec INSTANCE = new SystemServicesHostApiCodec(); + private SystemServicesHostApiCodec() {} + @Override protected Object readValueOfType(byte type, ByteBuffer buffer) { switch (type) { - case (byte)128: + case (byte) 128: return CameraPermissionsErrorData.fromMap((Map) readValue(buffer)); - - default: + + default: return super.readValueOfType(type, buffer); - } } + @Override - protected void writeValue(ByteArrayOutputStream stream, Object value) { + protected void writeValue(ByteArrayOutputStream stream, Object value) { if (value instanceof CameraPermissionsErrorData) { stream.write(128); writeValue(stream, ((CameraPermissionsErrorData) value).toMap()); - } else -{ + } else { super.writeValue(stream, value); } } } - /** Generated interface from Pigeon that represents a handler of messages from Flutter.*/ + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface SystemServicesHostApi { - void requestCameraPermissions(@NonNull Boolean enableAudio, Result result); - void startListeningForDeviceOrientationChange(@NonNull Boolean isFrontFacing, @NonNull Long sensorOrientation); + void requestCameraPermissions( + @NonNull Boolean enableAudio, Result result); + + void startListeningForDeviceOrientationChange( + @NonNull Boolean isFrontFacing, @NonNull Long sensorOrientation); + void stopListeningForDeviceOrientationChange(); /** The codec used by SystemServicesHostApi. */ @@ -623,155 +782,203 @@ static MessageCodec getCodec() { return SystemServicesHostApiCodec.INSTANCE; } - /** Sets up an instance of `SystemServicesHostApi` to handle messages through the `binaryMessenger`. */ + /** + * Sets up an instance of `SystemServicesHostApi` to handle messages through the + * `binaryMessenger`. + */ static void setup(BinaryMessenger binaryMessenger, SystemServicesHostApi api) { { BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.SystemServicesHostApi.requestCameraPermissions", getCodec()); + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.SystemServicesHostApi.requestCameraPermissions", + getCodec()); if (api != null) { - channel.setMessageHandler((message, reply) -> { - Map wrapped = new HashMap<>(); - try { - ArrayList args = (ArrayList)message; - Boolean enableAudioArg = (Boolean)args.get(0); - if (enableAudioArg == null) { - throw new NullPointerException("enableAudioArg unexpectedly null."); - } - Result resultCallback = new Result() { - public void success(CameraPermissionsErrorData result) { - wrapped.put("result", result); - reply.reply(wrapped); - } - public void error(Throwable error) { - wrapped.put("error", wrapError(error)); + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Boolean enableAudioArg = (Boolean) args.get(0); + if (enableAudioArg == null) { + throw new NullPointerException("enableAudioArg unexpectedly null."); + } + Result resultCallback = + new Result() { + public void success(CameraPermissionsErrorData result) { + wrapped.put("result", result); + reply.reply(wrapped); + } + + public void error(Throwable error) { + wrapped.put("error", wrapError(error)); + reply.reply(wrapped); + } + }; + + api.requestCameraPermissions(enableAudioArg, resultCallback); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); reply.reply(wrapped); } - }; - - api.requestCameraPermissions(enableAudioArg, resultCallback); - } - catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - reply.reply(wrapped); - } - }); + }); } else { channel.setMessageHandler(null); } } { BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.SystemServicesHostApi.startListeningForDeviceOrientationChange", getCodec()); + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.SystemServicesHostApi.startListeningForDeviceOrientationChange", + getCodec()); if (api != null) { - channel.setMessageHandler((message, reply) -> { - Map wrapped = new HashMap<>(); - try { - ArrayList args = (ArrayList)message; - Boolean isFrontFacingArg = (Boolean)args.get(0); - if (isFrontFacingArg == null) { - throw new NullPointerException("isFrontFacingArg unexpectedly null."); - } - Number sensorOrientationArg = (Number)args.get(1); - if (sensorOrientationArg == null) { - throw new NullPointerException("sensorOrientationArg unexpectedly null."); - } - api.startListeningForDeviceOrientationChange(isFrontFacingArg, (sensorOrientationArg == null) ? null : sensorOrientationArg.longValue()); - wrapped.put("result", null); - } - catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - } - reply.reply(wrapped); - }); + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Boolean isFrontFacingArg = (Boolean) args.get(0); + if (isFrontFacingArg == null) { + throw new NullPointerException("isFrontFacingArg unexpectedly null."); + } + Number sensorOrientationArg = (Number) args.get(1); + if (sensorOrientationArg == null) { + throw new NullPointerException("sensorOrientationArg unexpectedly null."); + } + api.startListeningForDeviceOrientationChange( + isFrontFacingArg, + (sensorOrientationArg == null) ? null : sensorOrientationArg.longValue()); + wrapped.put("result", null); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); } else { channel.setMessageHandler(null); } } { BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.SystemServicesHostApi.stopListeningForDeviceOrientationChange", getCodec()); + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.SystemServicesHostApi.stopListeningForDeviceOrientationChange", + getCodec()); if (api != null) { - channel.setMessageHandler((message, reply) -> { - Map wrapped = new HashMap<>(); - try { - api.stopListeningForDeviceOrientationChange(); - wrapped.put("result", null); - } - catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - } - reply.reply(wrapped); - }); + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + api.stopListeningForDeviceOrientationChange(); + wrapped.put("result", null); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); } else { channel.setMessageHandler(null); } } } } + private static class SystemServicesFlutterApiCodec extends StandardMessageCodec { - public static final SystemServicesFlutterApiCodec INSTANCE = new SystemServicesFlutterApiCodec(); + public static final SystemServicesFlutterApiCodec INSTANCE = + new SystemServicesFlutterApiCodec(); + private SystemServicesFlutterApiCodec() {} } - /** Generated class from Pigeon that represents Flutter messages that can be called from Java.*/ + /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */ public static class SystemServicesFlutterApi { private final BinaryMessenger binaryMessenger; - public SystemServicesFlutterApi(BinaryMessenger argBinaryMessenger){ + + public SystemServicesFlutterApi(BinaryMessenger argBinaryMessenger) { this.binaryMessenger = argBinaryMessenger; } + public interface Reply { void reply(T reply); } + static MessageCodec getCodec() { return SystemServicesFlutterApiCodec.INSTANCE; } public void onDeviceOrientationChanged(@NonNull String orientationArg, Reply callback) { BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.SystemServicesFlutterApi.onDeviceOrientationChanged", getCodec()); - channel.send(new ArrayList(Arrays.asList(orientationArg)), channelReply -> { - callback.reply(null); - }); + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.SystemServicesFlutterApi.onDeviceOrientationChanged", + getCodec()); + channel.send( + new ArrayList(Arrays.asList(orientationArg)), + channelReply -> { + callback.reply(null); + }); + } + + public void onCameraError(@NonNull String errorDescriptionArg, Reply callback) { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.SystemServicesFlutterApi.onCameraError", + getCodec()); + channel.send( + new ArrayList(Arrays.asList(errorDescriptionArg)), + channelReply -> { + callback.reply(null); + }); } } + private static class PreviewHostApiCodec extends StandardMessageCodec { public static final PreviewHostApiCodec INSTANCE = new PreviewHostApiCodec(); + private PreviewHostApiCodec() {} + @Override protected Object readValueOfType(byte type, ByteBuffer buffer) { switch (type) { - case (byte)128: + case (byte) 128: return ResolutionInfo.fromMap((Map) readValue(buffer)); - - case (byte)129: + + case (byte) 129: return ResolutionInfo.fromMap((Map) readValue(buffer)); - - default: + + default: return super.readValueOfType(type, buffer); - } } + @Override - protected void writeValue(ByteArrayOutputStream stream, Object value) { + protected void writeValue(ByteArrayOutputStream stream, Object value) { if (value instanceof ResolutionInfo) { stream.write(128); writeValue(stream, ((ResolutionInfo) value).toMap()); - } else - if (value instanceof ResolutionInfo) { + } else if (value instanceof ResolutionInfo) { stream.write(129); writeValue(stream, ((ResolutionInfo) value).toMap()); - } else -{ + } else { super.writeValue(stream, value); } } } - /** Generated interface from Pigeon that represents a handler of messages from Flutter.*/ + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface PreviewHostApi { - void create(@NonNull Long identifier, @Nullable Long rotation, @Nullable ResolutionInfo targetResolution); - @NonNull Long setSurfaceProvider(@NonNull Long identifier); - @NonNull ResolutionInfo getResolutionInfo(@NonNull Long identifier); + void create( + @NonNull Long identifier, + @Nullable Long rotation, + @Nullable ResolutionInfo targetResolution); + + @NonNull + Long setSurfaceProvider(@NonNull Long identifier); + + @NonNull + ResolutionInfo getResolutionInfo(@NonNull Long identifier); /** The codec used by PreviewHostApi. */ static MessageCodec getCodec() { @@ -782,85 +989,100 @@ static MessageCodec getCodec() { static void setup(BinaryMessenger binaryMessenger, PreviewHostApi api) { { BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.PreviewHostApi.create", getCodec()); + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.PreviewHostApi.create", getCodec()); if (api != null) { - channel.setMessageHandler((message, reply) -> { - Map wrapped = new HashMap<>(); - try { - ArrayList args = (ArrayList)message; - Number identifierArg = (Number)args.get(0); - if (identifierArg == null) { - throw new NullPointerException("identifierArg unexpectedly null."); - } - Number rotationArg = (Number)args.get(1); - ResolutionInfo targetResolutionArg = (ResolutionInfo)args.get(2); - api.create((identifierArg == null) ? null : identifierArg.longValue(), (rotationArg == null) ? null : rotationArg.longValue(), targetResolutionArg); - wrapped.put("result", null); - } - catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - } - reply.reply(wrapped); - }); + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + if (identifierArg == null) { + throw new NullPointerException("identifierArg unexpectedly null."); + } + Number rotationArg = (Number) args.get(1); + ResolutionInfo targetResolutionArg = (ResolutionInfo) args.get(2); + api.create( + (identifierArg == null) ? null : identifierArg.longValue(), + (rotationArg == null) ? null : rotationArg.longValue(), + targetResolutionArg); + wrapped.put("result", null); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); } else { channel.setMessageHandler(null); } } { BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.PreviewHostApi.setSurfaceProvider", getCodec()); + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.PreviewHostApi.setSurfaceProvider", + getCodec()); if (api != null) { - channel.setMessageHandler((message, reply) -> { - Map wrapped = new HashMap<>(); - try { - ArrayList args = (ArrayList)message; - Number identifierArg = (Number)args.get(0); - if (identifierArg == null) { - throw new NullPointerException("identifierArg unexpectedly null."); - } - Long output = api.setSurfaceProvider((identifierArg == null) ? null : identifierArg.longValue()); - wrapped.put("result", output); - } - catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - } - reply.reply(wrapped); - }); + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + if (identifierArg == null) { + throw new NullPointerException("identifierArg unexpectedly null."); + } + Long output = + api.setSurfaceProvider( + (identifierArg == null) ? null : identifierArg.longValue()); + wrapped.put("result", output); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); } else { channel.setMessageHandler(null); } } { BasicMessageChannel channel = - new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.PreviewHostApi.getResolutionInfo", getCodec()); + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.PreviewHostApi.getResolutionInfo", getCodec()); if (api != null) { - channel.setMessageHandler((message, reply) -> { - Map wrapped = new HashMap<>(); - try { - ArrayList args = (ArrayList)message; - Number identifierArg = (Number)args.get(0); - if (identifierArg == null) { - throw new NullPointerException("identifierArg unexpectedly null."); - } - ResolutionInfo output = api.getResolutionInfo((identifierArg == null) ? null : identifierArg.longValue()); - wrapped.put("result", output); - } - catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - } - reply.reply(wrapped); - }); + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + if (identifierArg == null) { + throw new NullPointerException("identifierArg unexpectedly null."); + } + ResolutionInfo output = + api.getResolutionInfo( + (identifierArg == null) ? null : identifierArg.longValue()); + wrapped.put("result", output); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); } else { channel.setMessageHandler(null); } } } } + private static Map wrapError(Throwable exception) { Map errorMap = new HashMap<>(); errorMap.put("message", exception.toString()); errorMap.put("code", exception.getClass().getSimpleName()); - errorMap.put("details", "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); + errorMap.put( + "details", + "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); return errorMap; } } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java index c62939b2962a..1a881fb7b995 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java @@ -33,6 +33,7 @@ public PreviewHostApiImpl( this.textureRegistry = textureRegistry; } + /** Creates a {@link Preview} with the target rotation and resolution if specified. */ @Override public void create( @NonNull Long identifier, @@ -51,6 +52,10 @@ public void create( instanceManager.addDartCreatedInstance(preview, identifier); } + /** + * Sets the {@link Preview.SurfaceProvider} that will be used to provide a {@code Surface} backed + * by a Flutter {@link TextureRegistry.SurfaceTextureEntry} used to build the {@link Preview}. + */ @Override public Long setSurfaceProvider(@NonNull Long identifier) { Preview preview = (Preview) instanceManager.getInstance(identifier); @@ -65,18 +70,24 @@ public void onSurfaceRequested(SurfaceRequest request) { request.getResolution().getWidth(), request.getResolution().getHeight()); Surface flutterSurface = cameraXProxy.createSurface(surfaceTexture); request.provideSurface( - flutterSurface, Executors.newSingleThreadExecutor(), (result) -> { - switch(result) { + flutterSurface, + Executors.newSingleThreadExecutor(), + (result) -> { + SystemServicesFlutterApiImpl systemServicesFlutterApi = + cameraXProxy.createSystemServicesFlutterApi(binaryMessenger); + int resultCode = result.getResultCode(); + switch (resultCode) { case SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY: flutterSurfaceTexture.release(); break; case SurfaceRequest.Result.RESULT_REQUEST_CANCELLED: case SurfaceRequest.Result.RESULT_INVALID_SURFACE: - case SurfaceRequest.Result.RESULT_SURFACE_ALREADY_PROVIDED: case SurfaceRequest.Result.RESULT_WILL_NOT_PROVIDE_SURFACE: + flutterSurfaceTexture.release(); + case SurfaceRequest.Result.RESULT_SURFACE_ALREADY_PROVIDED: default: - // TODO(camsim99): Use onCameraError to send the errors to the Dart side. - // See https://github.com/flutter/flutter/issues/119571 for more context. + systemServicesFlutterApi.onCameraError( + getProvideSurfaceErrorDescription(resultCode), reply -> {}); break; } }); @@ -86,6 +97,26 @@ public void onSurfaceRequested(SurfaceRequest request) { return flutterSurfaceTexture.id(); } + /** + * Returns an error description for each {@link SurfaceRequest.Result} that represents an error + * with providing a surface. + */ + private String getProvideSurfaceErrorDescription(int resultCode) { + switch (resultCode) { + case SurfaceRequest.Result.RESULT_REQUEST_CANCELLED: + return "Provided surface was never attached to the camera becausethe SurfaceRequest was cancelled by the camera."; + case SurfaceRequest.Result.RESULT_INVALID_SURFACE: + return "Provided surface could not be used by the camera."; + case SurfaceRequest.Result.RESULT_SURFACE_ALREADY_PROVIDED: + return "Provided surface was never attached to the camera because the SurfaceRequest was cancelled by the camera."; + case SurfaceRequest.Result.RESULT_WILL_NOT_PROVIDE_SURFACE: + return "Surface was not attached to the camera because the SurfaceRequest was marked as 'will not provide surface'."; + default: + return "There was an error with providing a surface for the camera preview."; + } + } + + /** Returns the resolution information for the specified {@link Preview}. */ @Override public GeneratedCameraXLibrary.ResolutionInfo getResolutionInfo(@NonNull Long identifier) { Preview preview = (Preview) instanceManager.getInstance(identifier); diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesFlutterApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesFlutterApiImpl.java index 1e9f33b092bb..3c1ed1914aff 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesFlutterApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesFlutterApiImpl.java @@ -8,15 +8,15 @@ import io.flutter.plugins.camerax.GeneratedCameraXLibrary.SystemServicesFlutterApi; public class SystemServicesFlutterApiImpl extends SystemServicesFlutterApi { - public SystemServicesFlutterApiImpl( - BinaryMessenger binaryMessenger, InstanceManager instanceManager) { + public SystemServicesFlutterApiImpl(BinaryMessenger binaryMessenger) { super(binaryMessenger); - this.instanceManager = instanceManager; } - private final InstanceManager instanceManager; - public void onDeviceOrientationChanged(String orientation, Reply reply) { super.onDeviceOrientationChanged(orientation, reply); } + + public void onCameraError(String errorDescription, Reply reply) { + super.onCameraError(errorDescription, reply); + } } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesHostApiImpl.java index e8eb715a7b3a..9ce14e73a189 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesHostApiImpl.java @@ -28,8 +28,7 @@ public SystemServicesHostApiImpl( BinaryMessenger binaryMessenger, InstanceManager instanceManager) { this.binaryMessenger = binaryMessenger; this.instanceManager = instanceManager; - this.systemServicesFlutterApi = - new SystemServicesFlutterApiImpl(binaryMessenger, instanceManager); + this.systemServicesFlutterApi = new SystemServicesFlutterApiImpl(binaryMessenger); } public void setActivity(Activity activity) { diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java index 54ed98749c0c..690379b8180c 100644 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java @@ -6,8 +6,11 @@ import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -19,6 +22,7 @@ import androidx.core.util.Consumer; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugins.camerax.GeneratedCameraXLibrary.ResolutionInfo; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.SystemServicesFlutterApi.Reply; import io.flutter.view.TextureRegistry; import java.util.concurrent.Executor; import org.junit.After; @@ -86,6 +90,9 @@ public void setSurfaceProviderTest() { final SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class); final SurfaceRequest mockSurfaceRequest = mock(SurfaceRequest.class); final Surface mockSurface = mock(Surface.class); + final SystemServicesFlutterApiImpl mockSystemServicesFlutterApi = + mock(SystemServicesFlutterApiImpl.class); + final SurfaceRequest.Result mockSurfaceRequestResult = mock(SurfaceRequest.Result.class); previewHostApi.cameraXProxy = mockCameraXProxy; testInstanceManager.addDartCreatedInstance(mockPreview, 5L); @@ -93,12 +100,16 @@ public void setSurfaceProviderTest() { when(mockTextureRegistry.createSurfaceTexture()).thenReturn(mockSurfaceTextureEntry); when(mockSurfaceTextureEntry.surfaceTexture()).thenReturn(mockSurfaceTexture); when(mockSurfaceTextureEntry.id()).thenReturn(120L); + doNothing().when(mockSurfaceTextureEntry).release(); when(mockSurfaceRequest.getResolution()).thenReturn(new Size(200, 500)); when(mockCameraXProxy.createSurface(mockSurfaceTexture)).thenReturn(mockSurface); + when(mockCameraXProxy.createSystemServicesFlutterApi(mockBinaryMessenger)) + .thenReturn(mockSystemServicesFlutterApi); final ArgumentCaptor surfaceProviderCaptor = ArgumentCaptor.forClass(Preview.SurfaceProvider.class); final ArgumentCaptor surfaceCaptor = ArgumentCaptor.forClass(Surface.class); + final ArgumentCaptor consumerCaptor = ArgumentCaptor.forClass(Consumer.class); // Test that surface provider was set and the surface texture ID was returned. assertEquals((long) previewHostApi.setSurfaceProvider(5L), 120L); @@ -107,12 +118,34 @@ public void setSurfaceProviderTest() { Preview.SurfaceProvider surfaceProvider = surfaceProviderCaptor.getValue(); surfaceProvider.onSurfaceRequested(mockSurfaceRequest); - // Test that the surface derived from the surface texture entry will be provided to the surface request. verify(mockSurfaceTexture).setDefaultBufferSize(200, 500); verify(mockSurfaceRequest) - .provideSurface(surfaceCaptor.capture(), any(Executor.class), any(Consumer.class)); + .provideSurface(surfaceCaptor.capture(), any(Executor.class), consumerCaptor.capture()); + // Test that the surface derived from the surface texture entry will be provided to the surface request. assertEquals(surfaceCaptor.getValue(), mockSurface); + + // Test that the Consumer used to handle surface request result releases Flutter surface texture appropriately + // and sends camera errors appropriately. + Consumer capturedConsumer = consumerCaptor.getValue(); + when(mockSurfaceRequestResult.getResultCode()) + .thenReturn(SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY); + capturedConsumer.accept(mockSurfaceRequestResult); + when(mockSurfaceRequestResult.getResultCode()) + .thenReturn(SurfaceRequest.Result.RESULT_REQUEST_CANCELLED); + capturedConsumer.accept(mockSurfaceRequestResult); + when(mockSurfaceRequestResult.getResultCode()) + .thenReturn(SurfaceRequest.Result.RESULT_INVALID_SURFACE); + capturedConsumer.accept(mockSurfaceRequestResult); + when(mockSurfaceRequestResult.getResultCode()) + .thenReturn(SurfaceRequest.Result.RESULT_WILL_NOT_PROVIDE_SURFACE); + capturedConsumer.accept(mockSurfaceRequestResult); + when(mockSurfaceRequestResult.getResultCode()) + .thenReturn(SurfaceRequest.Result.RESULT_SURFACE_ALREADY_PROVIDED); + capturedConsumer.accept(mockSurfaceRequestResult); + + verify(mockSurfaceTextureEntry, times(4)).release(); + verify(mockSystemServicesFlutterApi, times(4)).onCameraError(anyString(), any(Reply.class)); } @Test diff --git a/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart b/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart index bed457178ada..889443dbe2d9 100644 --- a/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart +++ b/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart @@ -68,7 +68,8 @@ class JavaObjectHostApi { /// Constructor for [JavaObjectHostApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - JavaObjectHostApi({BinaryMessenger? binaryMessenger}) : _binaryMessenger = binaryMessenger; + JavaObjectHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; final BinaryMessenger? _binaryMessenger; @@ -76,7 +77,8 @@ class JavaObjectHostApi { Future dispose(int arg_identifier) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.JavaObjectHostApi.dispose', codec, binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.JavaObjectHostApi.dispose', codec, + binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([arg_identifier]) as Map?; if (replyMap == null) { @@ -85,7 +87,8 @@ class JavaObjectHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = (replyMap['error'] as Map?)!; + final Map error = + (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -100,22 +103,27 @@ class JavaObjectHostApi { class _JavaObjectFlutterApiCodec extends StandardMessageCodec { const _JavaObjectFlutterApiCodec(); } + abstract class JavaObjectFlutterApi { static const MessageCodec codec = _JavaObjectFlutterApiCodec(); void dispose(int identifier); - static void setup(JavaObjectFlutterApi? api, {BinaryMessenger? binaryMessenger}) { + static void setup(JavaObjectFlutterApi? api, + {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.JavaObjectFlutterApi.dispose', codec, binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.JavaObjectFlutterApi.dispose', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMessageHandler(null); } else { channel.setMessageHandler((Object? message) async { - assert(message != null, 'Argument for dev.flutter.pigeon.JavaObjectFlutterApi.dispose was null.'); + assert(message != null, + 'Argument for dev.flutter.pigeon.JavaObjectFlutterApi.dispose was null.'); final List args = (message as List?)!; final int? arg_identifier = (args[0] as int?); - assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.JavaObjectFlutterApi.dispose was null, expected non-null int.'); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.JavaObjectFlutterApi.dispose was null, expected non-null int.'); api.dispose(arg_identifier!); return; }); @@ -132,7 +140,8 @@ class CameraInfoHostApi { /// Constructor for [CameraInfoHostApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - CameraInfoHostApi({BinaryMessenger? binaryMessenger}) : _binaryMessenger = binaryMessenger; + CameraInfoHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; final BinaryMessenger? _binaryMessenger; @@ -140,7 +149,8 @@ class CameraInfoHostApi { Future getSensorRotationDegrees(int arg_identifier) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.CameraInfoHostApi.getSensorRotationDegrees', codec, binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.CameraInfoHostApi.getSensorRotationDegrees', codec, + binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([arg_identifier]) as Map?; if (replyMap == null) { @@ -149,7 +159,8 @@ class CameraInfoHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = (replyMap['error'] as Map?)!; + final Map error = + (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -169,22 +180,27 @@ class CameraInfoHostApi { class _CameraInfoFlutterApiCodec extends StandardMessageCodec { const _CameraInfoFlutterApiCodec(); } + abstract class CameraInfoFlutterApi { static const MessageCodec codec = _CameraInfoFlutterApiCodec(); void create(int identifier); - static void setup(CameraInfoFlutterApi? api, {BinaryMessenger? binaryMessenger}) { + static void setup(CameraInfoFlutterApi? api, + {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.CameraInfoFlutterApi.create', codec, binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.CameraInfoFlutterApi.create', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMessageHandler(null); } else { channel.setMessageHandler((Object? message) async { - assert(message != null, 'Argument for dev.flutter.pigeon.CameraInfoFlutterApi.create was null.'); + assert(message != null, + 'Argument for dev.flutter.pigeon.CameraInfoFlutterApi.create was null.'); final List args = (message as List?)!; final int? arg_identifier = (args[0] as int?); - assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.CameraInfoFlutterApi.create was null, expected non-null int.'); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.CameraInfoFlutterApi.create was null, expected non-null int.'); api.create(arg_identifier!); return; }); @@ -201,7 +217,8 @@ class CameraSelectorHostApi { /// Constructor for [CameraSelectorHostApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - CameraSelectorHostApi({BinaryMessenger? binaryMessenger}) : _binaryMessenger = binaryMessenger; + CameraSelectorHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; final BinaryMessenger? _binaryMessenger; @@ -209,16 +226,19 @@ class CameraSelectorHostApi { Future create(int arg_identifier, int? arg_lensFacing) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.CameraSelectorHostApi.create', codec, binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.CameraSelectorHostApi.create', codec, + binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_identifier, arg_lensFacing]) as Map?; + await channel.send([arg_identifier, arg_lensFacing]) + as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = (replyMap['error'] as Map?)!; + final Map error = + (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -229,18 +249,22 @@ class CameraSelectorHostApi { } } - Future> filter(int arg_identifier, List arg_cameraInfoIds) async { + Future> filter( + int arg_identifier, List arg_cameraInfoIds) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.CameraSelectorHostApi.filter', codec, binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.CameraSelectorHostApi.filter', codec, + binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_identifier, arg_cameraInfoIds]) as Map?; + await channel.send([arg_identifier, arg_cameraInfoIds]) + as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = (replyMap['error'] as Map?)!; + final Map error = + (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -260,22 +284,27 @@ class CameraSelectorHostApi { class _CameraSelectorFlutterApiCodec extends StandardMessageCodec { const _CameraSelectorFlutterApiCodec(); } + abstract class CameraSelectorFlutterApi { static const MessageCodec codec = _CameraSelectorFlutterApiCodec(); void create(int identifier, int? lensFacing); - static void setup(CameraSelectorFlutterApi? api, {BinaryMessenger? binaryMessenger}) { + static void setup(CameraSelectorFlutterApi? api, + {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.CameraSelectorFlutterApi.create', codec, binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.CameraSelectorFlutterApi.create', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMessageHandler(null); } else { channel.setMessageHandler((Object? message) async { - assert(message != null, 'Argument for dev.flutter.pigeon.CameraSelectorFlutterApi.create was null.'); + assert(message != null, + 'Argument for dev.flutter.pigeon.CameraSelectorFlutterApi.create was null.'); final List args = (message as List?)!; final int? arg_identifier = (args[0] as int?); - assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.CameraSelectorFlutterApi.create was null, expected non-null int.'); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.CameraSelectorFlutterApi.create was null, expected non-null int.'); final int? arg_lensFacing = (args[1] as int?); api.create(arg_identifier!, arg_lensFacing); return; @@ -293,15 +322,18 @@ class ProcessCameraProviderHostApi { /// Constructor for [ProcessCameraProviderHostApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - ProcessCameraProviderHostApi({BinaryMessenger? binaryMessenger}) : _binaryMessenger = binaryMessenger; + ProcessCameraProviderHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; final BinaryMessenger? _binaryMessenger; - static const MessageCodec codec = _ProcessCameraProviderHostApiCodec(); + static const MessageCodec codec = + _ProcessCameraProviderHostApiCodec(); Future getInstance() async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ProcessCameraProviderHostApi.getInstance', codec, binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.ProcessCameraProviderHostApi.getInstance', codec, + binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send(null) as Map?; if (replyMap == null) { @@ -310,7 +342,8 @@ class ProcessCameraProviderHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = (replyMap['error'] as Map?)!; + final Map error = + (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -328,7 +361,9 @@ class ProcessCameraProviderHostApi { Future> getAvailableCameraInfos(int arg_identifier) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ProcessCameraProviderHostApi.getAvailableCameraInfos', codec, binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.ProcessCameraProviderHostApi.getAvailableCameraInfos', + codec, + binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([arg_identifier]) as Map?; if (replyMap == null) { @@ -337,7 +372,8 @@ class ProcessCameraProviderHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = (replyMap['error'] as Map?)!; + final Map error = + (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -353,18 +389,25 @@ class ProcessCameraProviderHostApi { } } - Future bindToLifecycle(int arg_identifier, int arg_cameraSelectorIdentifier, List arg_useCaseIds) async { + Future bindToLifecycle(int arg_identifier, + int arg_cameraSelectorIdentifier, List arg_useCaseIds) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ProcessCameraProviderHostApi.bindToLifecycle', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_identifier, arg_cameraSelectorIdentifier, arg_useCaseIds]) as Map?; + 'dev.flutter.pigeon.ProcessCameraProviderHostApi.bindToLifecycle', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel.send([ + arg_identifier, + arg_cameraSelectorIdentifier, + arg_useCaseIds + ]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = (replyMap['error'] as Map?)!; + final Map error = + (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -382,16 +425,19 @@ class ProcessCameraProviderHostApi { Future unbind(int arg_identifier, List arg_useCaseIds) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ProcessCameraProviderHostApi.unbind', codec, binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.ProcessCameraProviderHostApi.unbind', codec, + binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_identifier, arg_useCaseIds]) as Map?; + await channel.send([arg_identifier, arg_useCaseIds]) + as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = (replyMap['error'] as Map?)!; + final Map error = + (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -404,7 +450,8 @@ class ProcessCameraProviderHostApi { Future unbindAll(int arg_identifier) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ProcessCameraProviderHostApi.unbindAll', codec, binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.ProcessCameraProviderHostApi.unbindAll', codec, + binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([arg_identifier]) as Map?; if (replyMap == null) { @@ -413,7 +460,8 @@ class ProcessCameraProviderHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = (replyMap['error'] as Map?)!; + final Map error = + (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -428,22 +476,28 @@ class ProcessCameraProviderHostApi { class _ProcessCameraProviderFlutterApiCodec extends StandardMessageCodec { const _ProcessCameraProviderFlutterApiCodec(); } + abstract class ProcessCameraProviderFlutterApi { - static const MessageCodec codec = _ProcessCameraProviderFlutterApiCodec(); + static const MessageCodec codec = + _ProcessCameraProviderFlutterApiCodec(); void create(int identifier); - static void setup(ProcessCameraProviderFlutterApi? api, {BinaryMessenger? binaryMessenger}) { + static void setup(ProcessCameraProviderFlutterApi? api, + {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ProcessCameraProviderFlutterApi.create', codec, binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.ProcessCameraProviderFlutterApi.create', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMessageHandler(null); } else { channel.setMessageHandler((Object? message) async { - assert(message != null, 'Argument for dev.flutter.pigeon.ProcessCameraProviderFlutterApi.create was null.'); + assert(message != null, + 'Argument for dev.flutter.pigeon.ProcessCameraProviderFlutterApi.create was null.'); final List args = (message as List?)!; final int? arg_identifier = (args[0] as int?); - assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.ProcessCameraProviderFlutterApi.create was null, expected non-null int.'); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.ProcessCameraProviderFlutterApi.create was null, expected non-null int.'); api.create(arg_identifier!); return; }); @@ -455,6 +509,7 @@ abstract class ProcessCameraProviderFlutterApi { class _CameraFlutterApiCodec extends StandardMessageCodec { const _CameraFlutterApiCodec(); } + abstract class CameraFlutterApi { static const MessageCodec codec = _CameraFlutterApiCodec(); @@ -462,15 +517,18 @@ abstract class CameraFlutterApi { static void setup(CameraFlutterApi? api, {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.CameraFlutterApi.create', codec, binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.CameraFlutterApi.create', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMessageHandler(null); } else { channel.setMessageHandler((Object? message) async { - assert(message != null, 'Argument for dev.flutter.pigeon.CameraFlutterApi.create was null.'); + assert(message != null, + 'Argument for dev.flutter.pigeon.CameraFlutterApi.create was null.'); final List args = (message as List?)!; final int? arg_identifier = (args[0] as int?); - assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.CameraFlutterApi.create was null, expected non-null int.'); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.CameraFlutterApi.create was null, expected non-null int.'); api.create(arg_identifier!); return; }); @@ -486,20 +544,19 @@ class _SystemServicesHostApiCodec extends StandardMessageCodec { if (value is CameraPermissionsErrorData) { buffer.putUint8(128); writeValue(buffer, value.encode()); - } else -{ + } else { super.writeValue(buffer, value); } } + @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: + case 128: return CameraPermissionsErrorData.decode(readValue(buffer)!); - - default: + + default: return super.readValueOfType(type, buffer); - } } } @@ -508,24 +565,29 @@ class SystemServicesHostApi { /// Constructor for [SystemServicesHostApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - SystemServicesHostApi({BinaryMessenger? binaryMessenger}) : _binaryMessenger = binaryMessenger; + SystemServicesHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; final BinaryMessenger? _binaryMessenger; static const MessageCodec codec = _SystemServicesHostApiCodec(); - Future requestCameraPermissions(bool arg_enableAudio) async { + Future requestCameraPermissions( + bool arg_enableAudio) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.SystemServicesHostApi.requestCameraPermissions', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_enableAudio]) as Map?; + 'dev.flutter.pigeon.SystemServicesHostApi.requestCameraPermissions', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_enableAudio]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = (replyMap['error'] as Map?)!; + final Map error = + (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -536,18 +598,23 @@ class SystemServicesHostApi { } } - Future startListeningForDeviceOrientationChange(bool arg_isFrontFacing, int arg_sensorOrientation) async { + Future startListeningForDeviceOrientationChange( + bool arg_isFrontFacing, int arg_sensorOrientation) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.SystemServicesHostApi.startListeningForDeviceOrientationChange', codec, binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.SystemServicesHostApi.startListeningForDeviceOrientationChange', + codec, + binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_isFrontFacing, arg_sensorOrientation]) as Map?; + await channel.send([arg_isFrontFacing, arg_sensorOrientation]) + as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = (replyMap['error'] as Map?)!; + final Map error = + (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -560,7 +627,9 @@ class SystemServicesHostApi { Future stopListeningForDeviceOrientationChange() async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.SystemServicesHostApi.stopListeningForDeviceOrientationChange', codec, binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.SystemServicesHostApi.stopListeningForDeviceOrientationChange', + codec, + binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send(null) as Map?; if (replyMap == null) { @@ -569,7 +638,8 @@ class SystemServicesHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = (replyMap['error'] as Map?)!; + final Map error = + (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -584,27 +654,53 @@ class SystemServicesHostApi { class _SystemServicesFlutterApiCodec extends StandardMessageCodec { const _SystemServicesFlutterApiCodec(); } + abstract class SystemServicesFlutterApi { static const MessageCodec codec = _SystemServicesFlutterApiCodec(); void onDeviceOrientationChanged(String orientation); - static void setup(SystemServicesFlutterApi? api, {BinaryMessenger? binaryMessenger}) { + void onCameraError(String errorDescription); + static void setup(SystemServicesFlutterApi? api, + {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.SystemServicesFlutterApi.onDeviceOrientationChanged', codec, binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.SystemServicesFlutterApi.onDeviceOrientationChanged', + codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMessageHandler(null); } else { channel.setMessageHandler((Object? message) async { - assert(message != null, 'Argument for dev.flutter.pigeon.SystemServicesFlutterApi.onDeviceOrientationChanged was null.'); + assert(message != null, + 'Argument for dev.flutter.pigeon.SystemServicesFlutterApi.onDeviceOrientationChanged was null.'); final List args = (message as List?)!; final String? arg_orientation = (args[0] as String?); - assert(arg_orientation != null, 'Argument for dev.flutter.pigeon.SystemServicesFlutterApi.onDeviceOrientationChanged was null, expected non-null String.'); + assert(arg_orientation != null, + 'Argument for dev.flutter.pigeon.SystemServicesFlutterApi.onDeviceOrientationChanged was null, expected non-null String.'); api.onDeviceOrientationChanged(arg_orientation!); return; }); } } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.SystemServicesFlutterApi.onCameraError', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.SystemServicesFlutterApi.onCameraError was null.'); + final List args = (message as List?)!; + final String? arg_errorDescription = (args[0] as String?); + assert(arg_errorDescription != null, + 'Argument for dev.flutter.pigeon.SystemServicesFlutterApi.onCameraError was null, expected non-null String.'); + api.onCameraError(arg_errorDescription!); + return; + }); + } + } } } @@ -615,27 +711,25 @@ class _PreviewHostApiCodec extends StandardMessageCodec { if (value is ResolutionInfo) { buffer.putUint8(128); writeValue(buffer, value.encode()); - } else - if (value is ResolutionInfo) { + } else if (value is ResolutionInfo) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else -{ + } else { super.writeValue(buffer, value); } } + @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: + case 128: return ResolutionInfo.decode(readValue(buffer)!); - - case 129: + + case 129: return ResolutionInfo.decode(readValue(buffer)!); - - default: + + default: return super.readValueOfType(type, buffer); - } } } @@ -644,24 +738,29 @@ class PreviewHostApi { /// Constructor for [PreviewHostApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - PreviewHostApi({BinaryMessenger? binaryMessenger}) : _binaryMessenger = binaryMessenger; + PreviewHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; final BinaryMessenger? _binaryMessenger; static const MessageCodec codec = _PreviewHostApiCodec(); - Future create(int arg_identifier, int? arg_rotation, ResolutionInfo? arg_targetResolution) async { + Future create(int arg_identifier, int? arg_rotation, + ResolutionInfo? arg_targetResolution) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.PreviewHostApi.create', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_identifier, arg_rotation, arg_targetResolution]) as Map?; + 'dev.flutter.pigeon.PreviewHostApi.create', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_identifier, arg_rotation, arg_targetResolution]) + as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = (replyMap['error'] as Map?)!; + final Map error = + (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -674,7 +773,8 @@ class PreviewHostApi { Future setSurfaceProvider(int arg_identifier) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.PreviewHostApi.setSurfaceProvider', codec, binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.PreviewHostApi.setSurfaceProvider', codec, + binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([arg_identifier]) as Map?; if (replyMap == null) { @@ -683,7 +783,8 @@ class PreviewHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = (replyMap['error'] as Map?)!; + final Map error = + (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -701,7 +802,8 @@ class PreviewHostApi { Future getResolutionInfo(int arg_identifier) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.PreviewHostApi.getResolutionInfo', codec, binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.PreviewHostApi.getResolutionInfo', codec, + binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([arg_identifier]) as Map?; if (replyMap == null) { @@ -710,7 +812,8 @@ class PreviewHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = (replyMap['error'] as Map?)!; + final Map error = + (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, diff --git a/packages/camera/camera_android_camerax/lib/src/system_services.dart b/packages/camera/camera_android_camerax/lib/src/system_services.dart index bc6477e0dcb8..4ca90e257a95 100644 --- a/packages/camera/camera_android_camerax/lib/src/system_services.dart +++ b/packages/camera/camera_android_camerax/lib/src/system_services.dart @@ -16,7 +16,7 @@ import 'camerax_library.g.dart'; // ignore_for_file: avoid_classes_with_only_static_members /// Utility class that offers access to Android system services needed for -/// camera usage. +/// camera usage and other informational streams. class SystemServices { /// Stream that emits the device orientation whenever it is changed. /// @@ -26,6 +26,10 @@ class SystemServices { deviceOrientationChangedStreamController = StreamController.broadcast(); + /// Stream that emits the errors caused by camera usage on the native side. + static final StreamController cameraErrorStreamController = + StreamController.broadcast(); + /// Requests permission to access the camera and audio if specified. static Future requestCameraPermissions(bool enableAudio, {BinaryMessenger? binaryMessenger}) { @@ -134,4 +138,12 @@ class SystemServicesFlutterApiImpl implements SystemServicesFlutterApi { '"$orientation" is not a valid DeviceOrientation value'); } } + + /// Callback method for any errors caused by camera usage on the Java side. + @override + void onCameraError(String errorDescription) { + // TODO(camsim99): Use this to implement onCameraError method in plugin. + // See https://github.com/flutter/flutter/issues/119571 for context. + SystemServices.cameraErrorStreamController.add(errorDescription); + } } diff --git a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart index 23f479b996c9..7a7ee196d30d 100644 --- a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart +++ b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart @@ -117,6 +117,8 @@ abstract class SystemServicesHostApi { @FlutterApi() abstract class SystemServicesFlutterApi { void onDeviceOrientationChanged(String orientation); + + void onCameraError(String errorDescription); } @HostApi(dartHostTestHandler: 'TestPreviewHostApi') diff --git a/packages/camera/camera_android_camerax/test/system_services_test.dart b/packages/camera/camera_android_camerax/test/system_services_test.dart index 2d2cea6d9190..cd792be089b4 100644 --- a/packages/camera/camera_android_camerax/test/system_services_test.dart +++ b/packages/camera/camera_android_camerax/test/system_services_test.dart @@ -97,5 +97,13 @@ void main() { 'message', '"FAKE_ORIENTATION" is not a valid DeviceOrientation value'))); }); + + test('onCameraError adds new error to stream', () { + SystemServices.cameraErrorStreamController.stream + .listen((String errorDescription) { + expect(errorDescription, equals('Test error description!')); + }); + SystemServicesFlutterApiImpl().onCameraError('Test error description!'); + }); }); } diff --git a/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart b/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart index ecb8f49cbf73..55ce41f007f0 100644 --- a/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart +++ b/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart @@ -16,22 +16,27 @@ import 'package:camera_android_camerax/src/camerax_library.g.dart'; class _TestJavaObjectHostApiCodec extends StandardMessageCodec { const _TestJavaObjectHostApiCodec(); } + abstract class TestJavaObjectHostApi { static const MessageCodec codec = _TestJavaObjectHostApiCodec(); void dispose(int identifier); - static void setup(TestJavaObjectHostApi? api, {BinaryMessenger? binaryMessenger}) { + static void setup(TestJavaObjectHostApi? api, + {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.JavaObjectHostApi.dispose', codec, binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.JavaObjectHostApi.dispose', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, 'Argument for dev.flutter.pigeon.JavaObjectHostApi.dispose was null.'); + assert(message != null, + 'Argument for dev.flutter.pigeon.JavaObjectHostApi.dispose was null.'); final List args = (message as List?)!; final int? arg_identifier = (args[0] as int?); - assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.JavaObjectHostApi.dispose was null, expected non-null int.'); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.JavaObjectHostApi.dispose was null, expected non-null int.'); api.dispose(arg_identifier!); return {}; }); @@ -43,22 +48,28 @@ abstract class TestJavaObjectHostApi { class _TestCameraInfoHostApiCodec extends StandardMessageCodec { const _TestCameraInfoHostApiCodec(); } + abstract class TestCameraInfoHostApi { static const MessageCodec codec = _TestCameraInfoHostApiCodec(); int getSensorRotationDegrees(int identifier); - static void setup(TestCameraInfoHostApi? api, {BinaryMessenger? binaryMessenger}) { + static void setup(TestCameraInfoHostApi? api, + {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.CameraInfoHostApi.getSensorRotationDegrees', codec, binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.CameraInfoHostApi.getSensorRotationDegrees', + codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, 'Argument for dev.flutter.pigeon.CameraInfoHostApi.getSensorRotationDegrees was null.'); + assert(message != null, + 'Argument for dev.flutter.pigeon.CameraInfoHostApi.getSensorRotationDegrees was null.'); final List args = (message as List?)!; final int? arg_identifier = (args[0] as int?); - assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.CameraInfoHostApi.getSensorRotationDegrees was null, expected non-null int.'); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.CameraInfoHostApi.getSensorRotationDegrees was null, expected non-null int.'); final int output = api.getSensorRotationDegrees(arg_identifier!); return {'result': output}; }); @@ -70,23 +81,28 @@ abstract class TestCameraInfoHostApi { class _TestCameraSelectorHostApiCodec extends StandardMessageCodec { const _TestCameraSelectorHostApiCodec(); } + abstract class TestCameraSelectorHostApi { static const MessageCodec codec = _TestCameraSelectorHostApiCodec(); void create(int identifier, int? lensFacing); List filter(int identifier, List cameraInfoIds); - static void setup(TestCameraSelectorHostApi? api, {BinaryMessenger? binaryMessenger}) { + static void setup(TestCameraSelectorHostApi? api, + {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.CameraSelectorHostApi.create', codec, binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.CameraSelectorHostApi.create', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, 'Argument for dev.flutter.pigeon.CameraSelectorHostApi.create was null.'); + assert(message != null, + 'Argument for dev.flutter.pigeon.CameraSelectorHostApi.create was null.'); final List args = (message as List?)!; final int? arg_identifier = (args[0] as int?); - assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.CameraSelectorHostApi.create was null, expected non-null int.'); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.CameraSelectorHostApi.create was null, expected non-null int.'); final int? arg_lensFacing = (args[1] as int?); api.create(arg_identifier!, arg_lensFacing); return {}; @@ -95,18 +111,24 @@ abstract class TestCameraSelectorHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.CameraSelectorHostApi.filter', codec, binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.CameraSelectorHostApi.filter', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, 'Argument for dev.flutter.pigeon.CameraSelectorHostApi.filter was null.'); + assert(message != null, + 'Argument for dev.flutter.pigeon.CameraSelectorHostApi.filter was null.'); final List args = (message as List?)!; final int? arg_identifier = (args[0] as int?); - assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.CameraSelectorHostApi.filter was null, expected non-null int.'); - final List? arg_cameraInfoIds = (args[1] as List?)?.cast(); - assert(arg_cameraInfoIds != null, 'Argument for dev.flutter.pigeon.CameraSelectorHostApi.filter was null, expected non-null List.'); - final List output = api.filter(arg_identifier!, arg_cameraInfoIds!); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.CameraSelectorHostApi.filter was null, expected non-null int.'); + final List? arg_cameraInfoIds = + (args[1] as List?)?.cast(); + assert(arg_cameraInfoIds != null, + 'Argument for dev.flutter.pigeon.CameraSelectorHostApi.filter was null, expected non-null List.'); + final List output = + api.filter(arg_identifier!, arg_cameraInfoIds!); return {'result': output}; }); } @@ -117,18 +139,23 @@ abstract class TestCameraSelectorHostApi { class _TestProcessCameraProviderHostApiCodec extends StandardMessageCodec { const _TestProcessCameraProviderHostApiCodec(); } + abstract class TestProcessCameraProviderHostApi { - static const MessageCodec codec = _TestProcessCameraProviderHostApiCodec(); + static const MessageCodec codec = + _TestProcessCameraProviderHostApiCodec(); Future getInstance(); List getAvailableCameraInfos(int identifier); - int bindToLifecycle(int identifier, int cameraSelectorIdentifier, List useCaseIds); + int bindToLifecycle( + int identifier, int cameraSelectorIdentifier, List useCaseIds); void unbind(int identifier, List useCaseIds); void unbindAll(int identifier); - static void setup(TestProcessCameraProviderHostApi? api, {BinaryMessenger? binaryMessenger}) { + static void setup(TestProcessCameraProviderHostApi? api, + {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ProcessCameraProviderHostApi.getInstance', codec, binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.ProcessCameraProviderHostApi.getInstance', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { @@ -141,53 +168,71 @@ abstract class TestProcessCameraProviderHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ProcessCameraProviderHostApi.getAvailableCameraInfos', codec, binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.ProcessCameraProviderHostApi.getAvailableCameraInfos', + codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.getAvailableCameraInfos was null.'); + assert(message != null, + 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.getAvailableCameraInfos was null.'); final List args = (message as List?)!; final int? arg_identifier = (args[0] as int?); - assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.getAvailableCameraInfos was null, expected non-null int.'); - final List output = api.getAvailableCameraInfos(arg_identifier!); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.getAvailableCameraInfos was null, expected non-null int.'); + final List output = + api.getAvailableCameraInfos(arg_identifier!); return {'result': output}; }); } } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ProcessCameraProviderHostApi.bindToLifecycle', codec, binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.ProcessCameraProviderHostApi.bindToLifecycle', + codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.bindToLifecycle was null.'); + assert(message != null, + 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.bindToLifecycle was null.'); final List args = (message as List?)!; final int? arg_identifier = (args[0] as int?); - assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.bindToLifecycle was null, expected non-null int.'); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.bindToLifecycle was null, expected non-null int.'); final int? arg_cameraSelectorIdentifier = (args[1] as int?); - assert(arg_cameraSelectorIdentifier != null, 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.bindToLifecycle was null, expected non-null int.'); - final List? arg_useCaseIds = (args[2] as List?)?.cast(); - assert(arg_useCaseIds != null, 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.bindToLifecycle was null, expected non-null List.'); - final int output = api.bindToLifecycle(arg_identifier!, arg_cameraSelectorIdentifier!, arg_useCaseIds!); + assert(arg_cameraSelectorIdentifier != null, + 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.bindToLifecycle was null, expected non-null int.'); + final List? arg_useCaseIds = + (args[2] as List?)?.cast(); + assert(arg_useCaseIds != null, + 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.bindToLifecycle was null, expected non-null List.'); + final int output = api.bindToLifecycle( + arg_identifier!, arg_cameraSelectorIdentifier!, arg_useCaseIds!); return {'result': output}; }); } } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ProcessCameraProviderHostApi.unbind', codec, binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.ProcessCameraProviderHostApi.unbind', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.unbind was null.'); + assert(message != null, + 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.unbind was null.'); final List args = (message as List?)!; final int? arg_identifier = (args[0] as int?); - assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.unbind was null, expected non-null int.'); - final List? arg_useCaseIds = (args[1] as List?)?.cast(); - assert(arg_useCaseIds != null, 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.unbind was null, expected non-null List.'); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.unbind was null, expected non-null int.'); + final List? arg_useCaseIds = + (args[1] as List?)?.cast(); + assert(arg_useCaseIds != null, + 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.unbind was null, expected non-null List.'); api.unbind(arg_identifier!, arg_useCaseIds!); return {}; }); @@ -195,15 +240,18 @@ abstract class TestProcessCameraProviderHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ProcessCameraProviderHostApi.unbindAll', codec, binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.ProcessCameraProviderHostApi.unbindAll', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.unbindAll was null.'); + assert(message != null, + 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.unbindAll was null.'); final List args = (message as List?)!; final int? arg_identifier = (args[0] as int?); - assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.unbindAll was null, expected non-null int.'); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.unbindAll was null, expected non-null int.'); api.unbindAll(arg_identifier!); return {}; }); @@ -219,67 +267,83 @@ class _TestSystemServicesHostApiCodec extends StandardMessageCodec { if (value is CameraPermissionsErrorData) { buffer.putUint8(128); writeValue(buffer, value.encode()); - } else -{ + } else { super.writeValue(buffer, value); } } + @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: + case 128: return CameraPermissionsErrorData.decode(readValue(buffer)!); - - default: + + default: return super.readValueOfType(type, buffer); - } } } + abstract class TestSystemServicesHostApi { static const MessageCodec codec = _TestSystemServicesHostApiCodec(); - Future requestCameraPermissions(bool enableAudio); - void startListeningForDeviceOrientationChange(bool isFrontFacing, int sensorOrientation); + Future requestCameraPermissions( + bool enableAudio); + void startListeningForDeviceOrientationChange( + bool isFrontFacing, int sensorOrientation); void stopListeningForDeviceOrientationChange(); - static void setup(TestSystemServicesHostApi? api, {BinaryMessenger? binaryMessenger}) { + static void setup(TestSystemServicesHostApi? api, + {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.SystemServicesHostApi.requestCameraPermissions', codec, binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.SystemServicesHostApi.requestCameraPermissions', + codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, 'Argument for dev.flutter.pigeon.SystemServicesHostApi.requestCameraPermissions was null.'); + assert(message != null, + 'Argument for dev.flutter.pigeon.SystemServicesHostApi.requestCameraPermissions was null.'); final List args = (message as List?)!; final bool? arg_enableAudio = (args[0] as bool?); - assert(arg_enableAudio != null, 'Argument for dev.flutter.pigeon.SystemServicesHostApi.requestCameraPermissions was null, expected non-null bool.'); - final CameraPermissionsErrorData? output = await api.requestCameraPermissions(arg_enableAudio!); + assert(arg_enableAudio != null, + 'Argument for dev.flutter.pigeon.SystemServicesHostApi.requestCameraPermissions was null, expected non-null bool.'); + final CameraPermissionsErrorData? output = + await api.requestCameraPermissions(arg_enableAudio!); return {'result': output}; }); } } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.SystemServicesHostApi.startListeningForDeviceOrientationChange', codec, binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.SystemServicesHostApi.startListeningForDeviceOrientationChange', + codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, 'Argument for dev.flutter.pigeon.SystemServicesHostApi.startListeningForDeviceOrientationChange was null.'); + assert(message != null, + 'Argument for dev.flutter.pigeon.SystemServicesHostApi.startListeningForDeviceOrientationChange was null.'); final List args = (message as List?)!; final bool? arg_isFrontFacing = (args[0] as bool?); - assert(arg_isFrontFacing != null, 'Argument for dev.flutter.pigeon.SystemServicesHostApi.startListeningForDeviceOrientationChange was null, expected non-null bool.'); + assert(arg_isFrontFacing != null, + 'Argument for dev.flutter.pigeon.SystemServicesHostApi.startListeningForDeviceOrientationChange was null, expected non-null bool.'); final int? arg_sensorOrientation = (args[1] as int?); - assert(arg_sensorOrientation != null, 'Argument for dev.flutter.pigeon.SystemServicesHostApi.startListeningForDeviceOrientationChange was null, expected non-null int.'); - api.startListeningForDeviceOrientationChange(arg_isFrontFacing!, arg_sensorOrientation!); + assert(arg_sensorOrientation != null, + 'Argument for dev.flutter.pigeon.SystemServicesHostApi.startListeningForDeviceOrientationChange was null, expected non-null int.'); + api.startListeningForDeviceOrientationChange( + arg_isFrontFacing!, arg_sensorOrientation!); return {}; }); } } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.SystemServicesHostApi.stopListeningForDeviceOrientationChange', codec, binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.SystemServicesHostApi.stopListeningForDeviceOrientationChange', + codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { @@ -300,50 +364,54 @@ class _TestPreviewHostApiCodec extends StandardMessageCodec { if (value is ResolutionInfo) { buffer.putUint8(128); writeValue(buffer, value.encode()); - } else - if (value is ResolutionInfo) { + } else if (value is ResolutionInfo) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else -{ + } else { super.writeValue(buffer, value); } } + @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: + case 128: return ResolutionInfo.decode(readValue(buffer)!); - - case 129: + + case 129: return ResolutionInfo.decode(readValue(buffer)!); - - default: + + default: return super.readValueOfType(type, buffer); - } } } + abstract class TestPreviewHostApi { static const MessageCodec codec = _TestPreviewHostApiCodec(); void create(int identifier, int? rotation, ResolutionInfo? targetResolution); int setSurfaceProvider(int identifier); ResolutionInfo getResolutionInfo(int identifier); - static void setup(TestPreviewHostApi? api, {BinaryMessenger? binaryMessenger}) { + static void setup(TestPreviewHostApi? api, + {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.PreviewHostApi.create', codec, binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.PreviewHostApi.create', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, 'Argument for dev.flutter.pigeon.PreviewHostApi.create was null.'); + assert(message != null, + 'Argument for dev.flutter.pigeon.PreviewHostApi.create was null.'); final List args = (message as List?)!; final int? arg_identifier = (args[0] as int?); - assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.PreviewHostApi.create was null, expected non-null int.'); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.PreviewHostApi.create was null, expected non-null int.'); final int? arg_rotation = (args[1] as int?); - final ResolutionInfo? arg_targetResolution = (args[2] as ResolutionInfo?); + final ResolutionInfo? arg_targetResolution = + (args[2] as ResolutionInfo?); api.create(arg_identifier!, arg_rotation, arg_targetResolution); return {}; }); @@ -351,15 +419,18 @@ abstract class TestPreviewHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.PreviewHostApi.setSurfaceProvider', codec, binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.PreviewHostApi.setSurfaceProvider', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, 'Argument for dev.flutter.pigeon.PreviewHostApi.setSurfaceProvider was null.'); + assert(message != null, + 'Argument for dev.flutter.pigeon.PreviewHostApi.setSurfaceProvider was null.'); final List args = (message as List?)!; final int? arg_identifier = (args[0] as int?); - assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.PreviewHostApi.setSurfaceProvider was null, expected non-null int.'); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.PreviewHostApi.setSurfaceProvider was null, expected non-null int.'); final int output = api.setSurfaceProvider(arg_identifier!); return {'result': output}; }); @@ -367,15 +438,18 @@ abstract class TestPreviewHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.PreviewHostApi.getResolutionInfo', codec, binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.PreviewHostApi.getResolutionInfo', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, 'Argument for dev.flutter.pigeon.PreviewHostApi.getResolutionInfo was null.'); + assert(message != null, + 'Argument for dev.flutter.pigeon.PreviewHostApi.getResolutionInfo was null.'); final List args = (message as List?)!; final int? arg_identifier = (args[0] as int?); - assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.PreviewHostApi.getResolutionInfo was null, expected non-null int.'); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.PreviewHostApi.getResolutionInfo was null, expected non-null int.'); final ResolutionInfo output = api.getResolutionInfo(arg_identifier!); return {'result': output}; }); From 858815d4e6fc4b437b291d11f4154a9770a916f8 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Tue, 31 Jan 2023 11:58:51 -0800 Subject: [PATCH 10/22] Fix pigeon file --- packages/camera/camera_android_camerax/lib/src/preview.dart | 2 +- packages/camera/camera_android_camerax/test/preview_test.dart | 4 ++-- .../camera_android_camerax/test/preview_test.mocks.dart | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/camera/camera_android_camerax/lib/src/preview.dart b/packages/camera/camera_android_camerax/lib/src/preview.dart index 036426f689dc..6adc153d8d69 100644 --- a/packages/camera/camera_android_camerax/lib/src/preview.dart +++ b/packages/camera/camera_android_camerax/lib/src/preview.dart @@ -4,7 +4,7 @@ import 'package:flutter/services.dart' show BinaryMessenger; -import 'camerax_library.pigeon.dart'; +import 'camerax_library.g.dart'; import 'instance_manager.dart'; import 'java_object.dart'; import 'use_case.dart'; diff --git a/packages/camera/camera_android_camerax/test/preview_test.dart b/packages/camera/camera_android_camerax/test/preview_test.dart index 50b1337030a8..71e1a0b3a8ac 100644 --- a/packages/camera/camera_android_camerax/test/preview_test.dart +++ b/packages/camera/camera_android_camerax/test/preview_test.dart @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:camera_android_camerax/src/camerax_library.pigeon.dart'; +import 'package:camera_android_camerax/src/camerax_library.g.dart'; import 'package:camera_android_camerax/src/instance_manager.dart'; import 'package:camera_android_camerax/src/preview.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -10,7 +10,7 @@ import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'preview_test.mocks.dart'; -import 'test_camerax_library.pigeon.dart'; +import 'test_camerax_library.g.dart'; @GenerateMocks([TestPreviewHostApi]) void main() { diff --git a/packages/camera/camera_android_camerax/test/preview_test.mocks.dart b/packages/camera/camera_android_camerax/test/preview_test.mocks.dart index a1016b260dda..d4cb88cf65d6 100644 --- a/packages/camera/camera_android_camerax/test/preview_test.mocks.dart +++ b/packages/camera/camera_android_camerax/test/preview_test.mocks.dart @@ -3,10 +3,10 @@ // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'package:camera_android_camerax/src/camerax_library.pigeon.dart' as _i2; +import 'package:camera_android_camerax/src/camerax_library.g.dart' as _i2; import 'package:mockito/mockito.dart' as _i1; -import 'test_camerax_library.pigeon.dart' as _i3; +import 'test_camerax_library.g.dart' as _i3; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values From 78edaa2f1871b4b21588908c227a64cb0e02ffe4 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Tue, 31 Jan 2023 14:33:23 -0800 Subject: [PATCH 11/22] Add method for releasing flutter texture and cleanup surface logic --- .../camerax/GeneratedCameraXLibrary.java | 24 +++++++++++++++++++ .../plugins/camerax/PreviewHostApiImpl.java | 20 ++++++++++++---- .../flutter/plugins/camerax/PreviewTest.java | 4 +--- .../lib/src/camerax_library.g.dart | 24 +++++++++++++++++++ .../lib/src/preview.dart | 12 ++++++++++ .../pigeons/camerax_library.dart | 2 ++ .../test/test_camerax_library.g.dart | 16 +++++++++++++ 7 files changed, 95 insertions(+), 7 deletions(-) diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java index fe4a54a2f894..1e61ea699292 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java @@ -977,6 +977,8 @@ void create( @NonNull Long setSurfaceProvider(@NonNull Long identifier); + void releaseFlutterSurfaceTexture(); + @NonNull ResolutionInfo getResolutionInfo(@NonNull Long identifier); @@ -1046,6 +1048,28 @@ static void setup(BinaryMessenger binaryMessenger, PreviewHostApi api) { channel.setMessageHandler(null); } } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.PreviewHostApi.releaseFlutterSurfaceTexture", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + api.releaseFlutterSurfaceTexture(); + wrapped.put("result", null); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } { BasicMessageChannel channel = new BasicMessageChannel<>( diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java index 1a881fb7b995..19b48e51b706 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java @@ -24,6 +24,8 @@ public class PreviewHostApiImpl implements PreviewHostApi { @VisibleForTesting public CameraXProxy cameraXProxy = new CameraXProxy(); + private TextureRegistry.SurfaceTextureEntry flutterSurfaceTexture; + public PreviewHostApiImpl( BinaryMessenger binaryMessenger, InstanceManager instanceManager, @@ -59,8 +61,7 @@ public void create( @Override public Long setSurfaceProvider(@NonNull Long identifier) { Preview preview = (Preview) instanceManager.getInstance(identifier); - TextureRegistry.SurfaceTextureEntry flutterSurfaceTexture = - textureRegistry.createSurfaceTexture(); + flutterSurfaceTexture = textureRegistry.createSurfaceTexture(); SurfaceTexture surfaceTexture = flutterSurfaceTexture.surfaceTexture(); Preview.SurfaceProvider surfaceProvider = new Preview.SurfaceProvider() { @@ -78,12 +79,12 @@ public void onSurfaceRequested(SurfaceRequest request) { int resultCode = result.getResultCode(); switch (resultCode) { case SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY: - flutterSurfaceTexture.release(); + flutterSurface.release(); break; case SurfaceRequest.Result.RESULT_REQUEST_CANCELLED: case SurfaceRequest.Result.RESULT_INVALID_SURFACE: case SurfaceRequest.Result.RESULT_WILL_NOT_PROVIDE_SURFACE: - flutterSurfaceTexture.release(); + flutterSurface.release(); case SurfaceRequest.Result.RESULT_SURFACE_ALREADY_PROVIDED: default: systemServicesFlutterApi.onCameraError( @@ -116,6 +117,17 @@ private String getProvideSurfaceErrorDescription(int resultCode) { } } + /** + * Releases the Flutter {@link TextureRegistry.SurfaceTextureEntry} if used to provide a surface + * for a {@link Preview}. + */ + @Override + public void releaseFlutterSurfaceTexture() { + if (flutterSurfaceTexture != null) { + flutterSurfaceTexture.release(); + } + } + /** Returns the resolution information for the specified {@link Preview}. */ @Override public GeneratedCameraXLibrary.ResolutionInfo getResolutionInfo(@NonNull Long identifier) { diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java index 690379b8180c..38dce9323078 100644 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java @@ -7,7 +7,6 @@ import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.anyString; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -100,7 +99,6 @@ public void setSurfaceProviderTest() { when(mockTextureRegistry.createSurfaceTexture()).thenReturn(mockSurfaceTextureEntry); when(mockSurfaceTextureEntry.surfaceTexture()).thenReturn(mockSurfaceTexture); when(mockSurfaceTextureEntry.id()).thenReturn(120L); - doNothing().when(mockSurfaceTextureEntry).release(); when(mockSurfaceRequest.getResolution()).thenReturn(new Size(200, 500)); when(mockCameraXProxy.createSurface(mockSurfaceTexture)).thenReturn(mockSurface); when(mockCameraXProxy.createSystemServicesFlutterApi(mockBinaryMessenger)) @@ -144,7 +142,7 @@ public void setSurfaceProviderTest() { .thenReturn(SurfaceRequest.Result.RESULT_SURFACE_ALREADY_PROVIDED); capturedConsumer.accept(mockSurfaceRequestResult); - verify(mockSurfaceTextureEntry, times(4)).release(); + verify(mockSurface, times(4)).release(); verify(mockSystemServicesFlutterApi, times(4)).onCameraError(anyString(), any(Reply.class)); } diff --git a/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart b/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart index 889443dbe2d9..1d315e5a1600 100644 --- a/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart +++ b/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart @@ -800,6 +800,30 @@ class PreviewHostApi { } } + Future releaseFlutterSurfaceTexture() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PreviewHostApi.releaseFlutterSurfaceTexture', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send(null) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + Future getResolutionInfo(int arg_identifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.PreviewHostApi.getResolutionInfo', codec, diff --git a/packages/camera/camera_android_camerax/lib/src/preview.dart b/packages/camera/camera_android_camerax/lib/src/preview.dart index 6adc153d8d69..57a87194f283 100644 --- a/packages/camera/camera_android_camerax/lib/src/preview.dart +++ b/packages/camera/camera_android_camerax/lib/src/preview.dart @@ -56,6 +56,12 @@ class Preview extends UseCase { return _api.setSurfaceProviderFromInstance(this); } + /// Releases Flutter surface texture used to provide a surface for the preview + /// stream. + void releaseFlutterSurfaceTexture() { + _api.releaseFlutterSurfaceTextureFromInstance(); + } + /// Retrieves the selected resolution information of this [Preview]. Future getResolutionInfo() { return _api.getResolutionInfoFromInstance(this); @@ -102,6 +108,12 @@ class PreviewHostApiImpl extends PreviewHostApi { return surfaceTextureEntryId; } + /// Releases Flutter surface texture used to provide a surface for the preview + /// stream if so requested. + void releaseFlutterSurfaceTextureFromInstance() { + releaseFlutterSurfaceTexture(); + } + /// Gets the resolution information of the specified [Preview] instance. Future getResolutionInfoFromInstance(Preview instance) async { final int? identifier = instanceManager.getIdentifier(instance); diff --git a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart index 7a7ee196d30d..4172cd7db073 100644 --- a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart +++ b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart @@ -127,5 +127,7 @@ abstract class PreviewHostApi { int setSurfaceProvider(int identifier); + void releaseFlutterSurfaceTexture(); + ResolutionInfo getResolutionInfo(int identifier); } diff --git a/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart b/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart index 55ce41f007f0..3f0e9c2d38a5 100644 --- a/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart +++ b/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart @@ -392,6 +392,7 @@ abstract class TestPreviewHostApi { void create(int identifier, int? rotation, ResolutionInfo? targetResolution); int setSurfaceProvider(int identifier); + void releaseFlutterSurfaceTexture(); ResolutionInfo getResolutionInfo(int identifier); static void setup(TestPreviewHostApi? api, {BinaryMessenger? binaryMessenger}) { @@ -436,6 +437,21 @@ abstract class TestPreviewHostApi { }); } } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PreviewHostApi.releaseFlutterSurfaceTexture', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + // ignore message + api.releaseFlutterSurfaceTexture(); + return {}; + }); + } + } { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.PreviewHostApi.getResolutionInfo', codec, From 352732213685e91c8d36e6cbe04ca18d214c8d0d Mon Sep 17 00:00:00 2001 From: camsim99 Date: Tue, 31 Jan 2023 14:36:28 -0800 Subject: [PATCH 12/22] Add test for release method --- .../flutter/plugins/camerax/PreviewHostApiImpl.java | 2 +- .../io/flutter/plugins/camerax/PreviewTest.java | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java index 19b48e51b706..bcbe7a2a1414 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java @@ -24,7 +24,7 @@ public class PreviewHostApiImpl implements PreviewHostApi { @VisibleForTesting public CameraXProxy cameraXProxy = new CameraXProxy(); - private TextureRegistry.SurfaceTextureEntry flutterSurfaceTexture; + @VisibleForTesting public TextureRegistry.SurfaceTextureEntry flutterSurfaceTexture; public PreviewHostApiImpl( BinaryMessenger binaryMessenger, diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java index 38dce9323078..0f2c86ad65c8 100644 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java @@ -146,6 +146,19 @@ public void setSurfaceProviderTest() { verify(mockSystemServicesFlutterApi, times(4)).onCameraError(anyString(), any(Reply.class)); } + @Test + public void releaseFlutterSurfaceTextureTest() { + final PreviewHostApiImpl previewHostApi = + new PreviewHostApiImpl(mockBinaryMessenger, testInstanceManager, mockTextureRegistry); + final TextureRegistry.SurfaceTextureEntry mockSurfaceTextureEntry = + mock(TextureRegistry.SurfaceTextureEntry.class); + + previewHostApi.flutterSurfaceTexture = mockSurfaceTextureEntry; + + previewHostApi.releaseFlutterSurfaceTexture(); + verify(mockSurfaceTextureEntry).release(); + } + @Test public void getResolutionInfo() { final PreviewHostApiImpl previewHostApi = From 335372b61692edf2f96905b7faf461f939385177 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Tue, 31 Jan 2023 14:40:54 -0800 Subject: [PATCH 13/22] Add dart test --- .../camera_android_camerax/test/preview_test.dart | 11 +++++++++++ .../test/preview_test.mocks.dart | 8 ++++++++ 2 files changed, 19 insertions(+) diff --git a/packages/camera/camera_android_camerax/test/preview_test.dart b/packages/camera/camera_android_camerax/test/preview_test.dart index 71e1a0b3a8ac..956b350f784b 100644 --- a/packages/camera/camera_android_camerax/test/preview_test.dart +++ b/packages/camera/camera_android_camerax/test/preview_test.dart @@ -82,6 +82,17 @@ void main() { mockApi.setSurfaceProvider(instanceManager.getIdentifier(preview))); }); + test('setSurfaceProviderTest', () async { + final MockTestPreviewHostApi mockApi = MockTestPreviewHostApi(); + TestPreviewHostApi.setup(mockApi); + + final Preview preview = Preview.detached(); + + preview.releaseFlutterSurfaceTexture(); + + verify(mockApi.releaseFlutterSurfaceTexture()); + }); + test('getResolutionInfoTest', () async { final MockTestPreviewHostApi mockApi = MockTestPreviewHostApi(); TestPreviewHostApi.setup(mockApi); diff --git a/packages/camera/camera_android_camerax/test/preview_test.mocks.dart b/packages/camera/camera_android_camerax/test/preview_test.mocks.dart index d4cb88cf65d6..60fa1527487b 100644 --- a/packages/camera/camera_android_camerax/test/preview_test.mocks.dart +++ b/packages/camera/camera_android_camerax/test/preview_test.mocks.dart @@ -65,6 +65,14 @@ class MockTestPreviewHostApi extends _i1.Mock returnValue: 0, ) as int); @override + void releaseFlutterSurfaceTexture() => super.noSuchMethod( + Invocation.method( + #releaseFlutterSurfaceTexture, + [], + ), + returnValueForMissingStub: null, + ); + @override _i2.ResolutionInfo getResolutionInfo(int? identifier) => (super.noSuchMethod( Invocation.method( #getResolutionInfo, From 4dd9c4c04c66b4a49ee8a2eea6c4edb3dcfe076a Mon Sep 17 00:00:00 2001 From: camsim99 Date: Tue, 31 Jan 2023 14:46:41 -0800 Subject: [PATCH 14/22] Update changelog --- packages/camera/camera_android_camerax/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index 27504672fb60..1a9e834f92d1 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -8,4 +8,4 @@ * Adds Camera and UseCase classes, along with methods for binding UseCases to a lifecycle with the ProcessCameraProvider. * Bump CameraX version to 1.3.0-alpha03 and Kotlin version to 1.8.0. * Changes instance manager to allow the separate creation of identical objects. -* Adds Preview and Surface classes needed to implement camera perview. +* Adds Preview and Surface classes, along with other methods needed to implement camera previews. From 24b1bdb44b5caeeaa4a9f437b0aaaaa8ca2b0481 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Tue, 31 Jan 2023 14:51:43 -0800 Subject: [PATCH 15/22] Modify flutter api names to avoid stack overflow --- .../java/io/flutter/plugins/camerax/PreviewHostApiImpl.java | 2 +- .../flutter/plugins/camerax/SystemServicesFlutterApiImpl.java | 4 ++-- .../io/flutter/plugins/camerax/SystemServicesHostApiImpl.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java index bcbe7a2a1414..76c42e14d678 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java @@ -87,7 +87,7 @@ public void onSurfaceRequested(SurfaceRequest request) { flutterSurface.release(); case SurfaceRequest.Result.RESULT_SURFACE_ALREADY_PROVIDED: default: - systemServicesFlutterApi.onCameraError( + systemServicesFlutterApi.sendCameraError( getProvideSurfaceErrorDescription(resultCode), reply -> {}); break; } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesFlutterApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesFlutterApiImpl.java index 3c1ed1914aff..17680cb33c96 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesFlutterApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesFlutterApiImpl.java @@ -12,11 +12,11 @@ public SystemServicesFlutterApiImpl(BinaryMessenger binaryMessenger) { super(binaryMessenger); } - public void onDeviceOrientationChanged(String orientation, Reply reply) { + public void sendDeviceOrientationChanged(String orientation, Reply reply) { super.onDeviceOrientationChanged(orientation, reply); } - public void onCameraError(String errorDescription, Reply reply) { + public void sendCameraError(String errorDescription, Reply reply) { super.onCameraError(errorDescription, reply); } } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesHostApiImpl.java index 9ce14e73a189..32aa9c1fa4bf 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesHostApiImpl.java @@ -85,7 +85,7 @@ public void startListeningForDeviceOrientationChange( isFrontFacing, sensorOrientation.intValue(), (DeviceOrientation newOrientation) -> { - systemServicesFlutterApi.onDeviceOrientationChanged( + systemServicesFlutterApi.sendDeviceOrientationChanged( serializeDeviceOrientation(newOrientation), reply -> {}); }); deviceOrientationManager.start(); From e0efef219465b42af9b59fad893d6ba6f4eea7cf Mon Sep 17 00:00:00 2001 From: camsim99 Date: Tue, 31 Jan 2023 14:55:32 -0800 Subject: [PATCH 16/22] Cleanup --- packages/camera/camera_android_camerax/CHANGELOG.md | 2 +- packages/camera/camera_android_camerax/lib/src/preview.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index 1a9e834f92d1..080240a64f42 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -8,4 +8,4 @@ * Adds Camera and UseCase classes, along with methods for binding UseCases to a lifecycle with the ProcessCameraProvider. * Bump CameraX version to 1.3.0-alpha03 and Kotlin version to 1.8.0. * Changes instance manager to allow the separate creation of identical objects. -* Adds Preview and Surface classes, along with other methods needed to implement camera previews. +* Adds Preview and Surface classes, along with other methods needed to implement camera preview. diff --git a/packages/camera/camera_android_camerax/lib/src/preview.dart b/packages/camera/camera_android_camerax/lib/src/preview.dart index 57a87194f283..602bcb3da76a 100644 --- a/packages/camera/camera_android_camerax/lib/src/preview.dart +++ b/packages/camera/camera_android_camerax/lib/src/preview.dart @@ -109,7 +109,7 @@ class PreviewHostApiImpl extends PreviewHostApi { } /// Releases Flutter surface texture used to provide a surface for the preview - /// stream if so requested. + /// stream if a surface provider was set for a [Preview] instance. void releaseFlutterSurfaceTextureFromInstance() { releaseFlutterSurfaceTexture(); } From e90f45c7a60dc1b70a18f7acb6e291cbfd2292dd Mon Sep 17 00:00:00 2001 From: camsim99 Date: Tue, 31 Jan 2023 16:14:42 -0800 Subject: [PATCH 17/22] Fix tests --- .../flutter/plugins/camerax/SystemServicesFlutterApiImpl.java | 2 +- .../io/flutter/plugins/camerax/SystemServicesHostApiImpl.java | 2 +- .../src/test/java/io/flutter/plugins/camerax/PreviewTest.java | 2 +- .../java/io/flutter/plugins/camerax/SystemServicesTest.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesFlutterApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesFlutterApiImpl.java index 17680cb33c96..d05dc43bc03c 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesFlutterApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesFlutterApiImpl.java @@ -12,7 +12,7 @@ public SystemServicesFlutterApiImpl(BinaryMessenger binaryMessenger) { super(binaryMessenger); } - public void sendDeviceOrientationChanged(String orientation, Reply reply) { + public void sendDeviceOrientationChangedEvent(String orientation, Reply reply) { super.onDeviceOrientationChanged(orientation, reply); } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesHostApiImpl.java index 32aa9c1fa4bf..a6985811531f 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesHostApiImpl.java @@ -85,7 +85,7 @@ public void startListeningForDeviceOrientationChange( isFrontFacing, sensorOrientation.intValue(), (DeviceOrientation newOrientation) -> { - systemServicesFlutterApi.sendDeviceOrientationChanged( + systemServicesFlutterApi.sendDeviceOrientationChangedEvent( serializeDeviceOrientation(newOrientation), reply -> {}); }); deviceOrientationManager.start(); diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java index 0f2c86ad65c8..51862f802449 100644 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java @@ -143,7 +143,7 @@ public void setSurfaceProviderTest() { capturedConsumer.accept(mockSurfaceRequestResult); verify(mockSurface, times(4)).release(); - verify(mockSystemServicesFlutterApi, times(4)).onCameraError(anyString(), any(Reply.class)); + verify(mockSystemServicesFlutterApi, times(4)).sendCameraError(anyString(), any(Reply.class)); } @Test diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/SystemServicesTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/SystemServicesTest.java index d90c2633271c..b6d99ec1a42f 100644 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/SystemServicesTest.java +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/SystemServicesTest.java @@ -129,7 +129,7 @@ public void deviceOrientationChangeTest() { deviceOrientationChangeCallback.onChange(DeviceOrientation.PORTRAIT_DOWN); verify(systemServicesFlutterApi) - .onDeviceOrientationChanged(eq("PORTRAIT_DOWN"), any(Reply.class)); + .sendDeviceOrientationChangedEvent(eq("PORTRAIT_DOWN"), any(Reply.class)); // Test that the DeviceOrientationManager starts listening for device orientation changes. verify(mockDeviceOrientationManager).start(); From 73cb5f8dbd0bd7c9281fda0105478c6d7706573b Mon Sep 17 00:00:00 2001 From: camsim99 Date: Tue, 31 Jan 2023 16:41:17 -0800 Subject: [PATCH 18/22] Delete space --- .../main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java index 76c42e14d678..fa3f8d18cc62 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java @@ -23,7 +23,6 @@ public class PreviewHostApiImpl implements PreviewHostApi { private final TextureRegistry textureRegistry; @VisibleForTesting public CameraXProxy cameraXProxy = new CameraXProxy(); - @VisibleForTesting public TextureRegistry.SurfaceTextureEntry flutterSurfaceTexture; public PreviewHostApiImpl( From abf4e58b3ef14ff26e8de1c021d0b51813af8e36 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Wed, 1 Feb 2023 15:51:01 -0800 Subject: [PATCH 19/22] Address review 1 --- .../flutter/plugins/camerax/CameraXProxy.java | 9 +- .../plugins/camerax/PreviewHostApiImpl.java | 85 ++++++++++--------- .../flutter/plugins/camerax/PreviewTest.java | 66 +++++++++----- .../plugins/camerax/SystemServicesTest.java | 3 +- .../test/preview_test.dart | 16 ++-- 5 files changed, 108 insertions(+), 71 deletions(-) diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java index 105a63e4b889..fbe2490b4a9f 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java @@ -11,6 +11,7 @@ import androidx.camera.core.Preview; import io.flutter.plugin.common.BinaryMessenger; +/** Utility class used to create CameraX-related objects primarily for testing purposes. */ public class CameraXProxy { public CameraSelector.Builder createCameraSelectorBuilder() { return new CameraSelector.Builder(); @@ -36,7 +37,13 @@ public Surface createSurface(SurfaceTexture surfaceTexture) { return new Surface(surfaceTexture); } - public SystemServicesFlutterApiImpl createSystemServicesFlutterApi( + /** + * Creates an instance of the {@code SystemServicesFlutterApiImpl}. + * + *

Included in this class to utilize the callback methods it provides, e.g. {@code + * onCameraError(String)}. + */ + public SystemServicesFlutterApiImpl createSystemServicesFlutterApiImpl( BinaryMessenger binaryMessenger) { return new SystemServicesFlutterApiImpl(binaryMessenger); } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java index fa3f8d18cc62..33063637e842 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java @@ -15,6 +15,7 @@ import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugins.camerax.GeneratedCameraXLibrary.PreviewHostApi; import io.flutter.view.TextureRegistry; +import java.util.Objects; import java.util.concurrent.Executors; public class PreviewHostApiImpl implements PreviewHostApi { @@ -59,60 +60,62 @@ public void create( */ @Override public Long setSurfaceProvider(@NonNull Long identifier) { - Preview preview = (Preview) instanceManager.getInstance(identifier); + Preview preview = (Preview) Objects.requireNonNull(instanceManager.getInstance(identifier)); flutterSurfaceTexture = textureRegistry.createSurfaceTexture(); SurfaceTexture surfaceTexture = flutterSurfaceTexture.surfaceTexture(); - Preview.SurfaceProvider surfaceProvider = - new Preview.SurfaceProvider() { - @Override - public void onSurfaceRequested(SurfaceRequest request) { - surfaceTexture.setDefaultBufferSize( - request.getResolution().getWidth(), request.getResolution().getHeight()); - Surface flutterSurface = cameraXProxy.createSurface(surfaceTexture); - request.provideSurface( - flutterSurface, - Executors.newSingleThreadExecutor(), - (result) -> { - SystemServicesFlutterApiImpl systemServicesFlutterApi = - cameraXProxy.createSystemServicesFlutterApi(binaryMessenger); - int resultCode = result.getResultCode(); - switch (resultCode) { - case SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY: - flutterSurface.release(); - break; - case SurfaceRequest.Result.RESULT_REQUEST_CANCELLED: - case SurfaceRequest.Result.RESULT_INVALID_SURFACE: - case SurfaceRequest.Result.RESULT_WILL_NOT_PROVIDE_SURFACE: - flutterSurface.release(); - case SurfaceRequest.Result.RESULT_SURFACE_ALREADY_PROVIDED: - default: - systemServicesFlutterApi.sendCameraError( - getProvideSurfaceErrorDescription(resultCode), reply -> {}); - break; - } - }); - }; - }; + Preview.SurfaceProvider surfaceProvider = createSurfaceProvider(surfaceTexture); preview.setSurfaceProvider(surfaceProvider); + return flutterSurfaceTexture.id(); } + /** + * Creates a {@link Preview.SurfaceProvider} that specifies how to provide a {@link Surface} to a + * {@code Preview} that is backed by a Flutter {@link TextureRegistry.SurfaceTextureEntry}. + */ + @VisibleForTesting + public Preview.SurfaceProvider createSurfaceProvider(SurfaceTexture surfaceTexture) { + return new Preview.SurfaceProvider() { + @Override + public void onSurfaceRequested(SurfaceRequest request) { + surfaceTexture.setDefaultBufferSize( + request.getResolution().getWidth(), request.getResolution().getHeight()); + Surface flutterSurface = cameraXProxy.createSurface(surfaceTexture); + request.provideSurface( + flutterSurface, + Executors.newSingleThreadExecutor(), + (result) -> { + SystemServicesFlutterApiImpl systemServicesFlutterApi = + cameraXProxy.createSystemServicesFlutterApiImpl(binaryMessenger); + int resultCode = result.getResultCode(); + switch (resultCode) { + case SurfaceRequest.Result.RESULT_REQUEST_CANCELLED: + case SurfaceRequest.Result.RESULT_WILL_NOT_PROVIDE_SURFACE: + case SurfaceRequest.Result.RESULT_SURFACE_ALREADY_PROVIDED: + case SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY: + flutterSurface.release(); + break; + case SurfaceRequest.Result.RESULT_INVALID_SURFACE: + default: + systemServicesFlutterApi.sendCameraError( + getProvideSurfaceErrorDescription(resultCode), reply -> {}); + break; + } + }); + }; + }; + } + /** * Returns an error description for each {@link SurfaceRequest.Result} that represents an error * with providing a surface. */ private String getProvideSurfaceErrorDescription(int resultCode) { switch (resultCode) { - case SurfaceRequest.Result.RESULT_REQUEST_CANCELLED: - return "Provided surface was never attached to the camera becausethe SurfaceRequest was cancelled by the camera."; case SurfaceRequest.Result.RESULT_INVALID_SURFACE: - return "Provided surface could not be used by the camera."; - case SurfaceRequest.Result.RESULT_SURFACE_ALREADY_PROVIDED: - return "Provided surface was never attached to the camera because the SurfaceRequest was cancelled by the camera."; - case SurfaceRequest.Result.RESULT_WILL_NOT_PROVIDE_SURFACE: - return "Surface was not attached to the camera because the SurfaceRequest was marked as 'will not provide surface'."; + return resultCode + ": Provided surface could not be used by the camera."; default: - return "There was an error with providing a surface for the camera preview."; + return resultCode + ": Attempt to provide a surface resulted with unrecognizable code."; } } @@ -130,7 +133,7 @@ public void releaseFlutterSurfaceTexture() { /** Returns the resolution information for the specified {@link Preview}. */ @Override public GeneratedCameraXLibrary.ResolutionInfo getResolutionInfo(@NonNull Long identifier) { - Preview preview = (Preview) instanceManager.getInstance(identifier); + Preview preview = (Preview) Objects.requireNonNull(instanceManager.getInstance(identifier)); Size resolution = preview.getResolutionInfo().getResolution(); GeneratedCameraXLibrary.ResolutionInfo.Builder resolutionInfo = diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java index 51862f802449..606d216db068 100644 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java @@ -57,7 +57,7 @@ public void tearDown() { } @Test - public void createTest() { + public void create_createsPreviewWithCorrectConfiguration() { final PreviewHostApiImpl previewHostApi = new PreviewHostApiImpl(mockBinaryMessenger, testInstanceManager, mockTextureRegistry); final Preview.Builder mockPreviewBuilder = mock(Preview.Builder.class); @@ -81,17 +81,12 @@ public void createTest() { } @Test - public void setSurfaceProviderTest() { + public void setSurfaceProviderTest_createsSurfaceProviderAndReturnsTextureEntryId() { final PreviewHostApiImpl previewHostApi = - new PreviewHostApiImpl(mockBinaryMessenger, testInstanceManager, mockTextureRegistry); + spy(new PreviewHostApiImpl(mockBinaryMessenger, testInstanceManager, mockTextureRegistry)); final TextureRegistry.SurfaceTextureEntry mockSurfaceTextureEntry = mock(TextureRegistry.SurfaceTextureEntry.class); final SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class); - final SurfaceRequest mockSurfaceRequest = mock(SurfaceRequest.class); - final Surface mockSurface = mock(Surface.class); - final SystemServicesFlutterApiImpl mockSystemServicesFlutterApi = - mock(SystemServicesFlutterApiImpl.class); - final SurfaceRequest.Result mockSurfaceRequestResult = mock(SurfaceRequest.Result.class); previewHostApi.cameraXProxy = mockCameraXProxy; testInstanceManager.addDartCreatedInstance(mockPreview, 5L); @@ -99,10 +94,6 @@ public void setSurfaceProviderTest() { when(mockTextureRegistry.createSurfaceTexture()).thenReturn(mockSurfaceTextureEntry); when(mockSurfaceTextureEntry.surfaceTexture()).thenReturn(mockSurfaceTexture); when(mockSurfaceTextureEntry.id()).thenReturn(120L); - when(mockSurfaceRequest.getResolution()).thenReturn(new Size(200, 500)); - when(mockCameraXProxy.createSurface(mockSurfaceTexture)).thenReturn(mockSurface); - when(mockCameraXProxy.createSystemServicesFlutterApi(mockBinaryMessenger)) - .thenReturn(mockSystemServicesFlutterApi); final ArgumentCaptor surfaceProviderCaptor = ArgumentCaptor.forClass(Preview.SurfaceProvider.class); @@ -112,9 +103,32 @@ public void setSurfaceProviderTest() { // Test that surface provider was set and the surface texture ID was returned. assertEquals((long) previewHostApi.setSurfaceProvider(5L), 120L); verify(mockPreview).setSurfaceProvider(surfaceProviderCaptor.capture()); + verify(previewHostApi).createSurfaceProvider(mockSurfaceTexture); + } + + @Test + public void createSurfaceProvider_createsExpectedPreviewSurfaceProvider() { + final PreviewHostApiImpl previewHostApi = + new PreviewHostApiImpl(mockBinaryMessenger, testInstanceManager, mockTextureRegistry); + final SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class); + final Surface mockSurface = mock(Surface.class); + final SurfaceRequest mockSurfaceRequest = mock(SurfaceRequest.class); + final SurfaceRequest.Result mockSurfaceRequestResult = mock(SurfaceRequest.Result.class); + final SystemServicesFlutterApiImpl mockSystemServicesFlutterApi = + mock(SystemServicesFlutterApiImpl.class); - Preview.SurfaceProvider surfaceProvider = surfaceProviderCaptor.getValue(); - surfaceProvider.onSurfaceRequested(mockSurfaceRequest); + previewHostApi.cameraXProxy = mockCameraXProxy; + when(mockCameraXProxy.createSurface(mockSurfaceTexture)).thenReturn(mockSurface); + when(mockSurfaceRequest.getResolution()).thenReturn(new Size(200, 500)); + when(mockCameraXProxy.createSystemServicesFlutterApiImpl(mockBinaryMessenger)) + .thenReturn(mockSystemServicesFlutterApi); + + final ArgumentCaptor surfaceCaptor = ArgumentCaptor.forClass(Surface.class); + final ArgumentCaptor consumerCaptor = ArgumentCaptor.forClass(Consumer.class); + + Preview.SurfaceProvider previewSurfaceProvider = + previewHostApi.createSurfaceProvider(mockSurfaceTexture); + previewSurfaceProvider.onSurfaceRequested(mockSurfaceRequest); verify(mockSurfaceTexture).setDefaultBufferSize(200, 500); verify(mockSurfaceRequest) @@ -126,28 +140,34 @@ public void setSurfaceProviderTest() { // Test that the Consumer used to handle surface request result releases Flutter surface texture appropriately // and sends camera errors appropriately. Consumer capturedConsumer = consumerCaptor.getValue(); - when(mockSurfaceRequestResult.getResultCode()) - .thenReturn(SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY); - capturedConsumer.accept(mockSurfaceRequestResult); + + // Case where Surface should be released. when(mockSurfaceRequestResult.getResultCode()) .thenReturn(SurfaceRequest.Result.RESULT_REQUEST_CANCELLED); capturedConsumer.accept(mockSurfaceRequestResult); + verify(mockSurface, times(1)).release(); when(mockSurfaceRequestResult.getResultCode()) - .thenReturn(SurfaceRequest.Result.RESULT_INVALID_SURFACE); + .thenReturn(SurfaceRequest.Result.RESULT_REQUEST_CANCELLED); capturedConsumer.accept(mockSurfaceRequestResult); + verify(mockSurface, times(2)).release(); when(mockSurfaceRequestResult.getResultCode()) .thenReturn(SurfaceRequest.Result.RESULT_WILL_NOT_PROVIDE_SURFACE); capturedConsumer.accept(mockSurfaceRequestResult); + verify(mockSurface, times(3)).release(); when(mockSurfaceRequestResult.getResultCode()) - .thenReturn(SurfaceRequest.Result.RESULT_SURFACE_ALREADY_PROVIDED); + .thenReturn(SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY); capturedConsumer.accept(mockSurfaceRequestResult); - verify(mockSurface, times(4)).release(); - verify(mockSystemServicesFlutterApi, times(4)).sendCameraError(anyString(), any(Reply.class)); + + // Case where error must be sent. + when(mockSurfaceRequestResult.getResultCode()) + .thenReturn(SurfaceRequest.Result.RESULT_INVALID_SURFACE); + capturedConsumer.accept(mockSurfaceRequestResult); + verify(mockSystemServicesFlutterApi, times(1)).sendCameraError(anyString(), any(Reply.class)); } @Test - public void releaseFlutterSurfaceTextureTest() { + public void releaseFlutterSurfaceTexture_makesCallToReleaseFlutterSurfaceTexture() { final PreviewHostApiImpl previewHostApi = new PreviewHostApiImpl(mockBinaryMessenger, testInstanceManager, mockTextureRegistry); final TextureRegistry.SurfaceTextureEntry mockSurfaceTextureEntry = @@ -160,7 +180,7 @@ public void releaseFlutterSurfaceTextureTest() { } @Test - public void getResolutionInfo() { + public void getResolutionInfo_makesCallToRetrievePreviewResolutionInfo() { final PreviewHostApiImpl previewHostApi = new PreviewHostApiImpl(mockBinaryMessenger, testInstanceManager, mockTextureRegistry); final androidx.camera.core.ResolutionInfo mockResolutionInfo = diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/SystemServicesTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/SystemServicesTest.java index b6d99ec1a42f..eb36c452ec3b 100644 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/SystemServicesTest.java +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/SystemServicesTest.java @@ -129,7 +129,8 @@ public void deviceOrientationChangeTest() { deviceOrientationChangeCallback.onChange(DeviceOrientation.PORTRAIT_DOWN); verify(systemServicesFlutterApi) - .sendDeviceOrientationChangedEvent(eq("PORTRAIT_DOWN"), any(Reply.class)); + .sendDeviceOrientationChangedEvent( + eq(DeviceOrientation.PORTRAIT_DOWN.toString()), any(Reply.class)); // Test that the DeviceOrientationManager starts listening for device orientation changes. verify(mockDeviceOrientationManager).start(); diff --git a/packages/camera/camera_android_camerax/test/preview_test.dart b/packages/camera/camera_android_camerax/test/preview_test.dart index 956b350f784b..66cd7157bd76 100644 --- a/packages/camera/camera_android_camerax/test/preview_test.dart +++ b/packages/camera/camera_android_camerax/test/preview_test.dart @@ -19,7 +19,7 @@ void main() { group('Preview', () { tearDown(() => TestPreviewHostApi.setup(null)); - test('detachedCreateTest', () async { + test('detached create does not call create on the Java side', () async { final MockTestPreviewHostApi mockApi = MockTestPreviewHostApi(); TestPreviewHostApi.setup(mockApi); @@ -36,7 +36,7 @@ void main() { argThat(isA()))); }); - test('createTest', () async { + test('create calls create on the Java side', () async { final MockTestPreviewHostApi mockApi = MockTestPreviewHostApi(); TestPreviewHostApi.setup(mockApi); @@ -57,7 +57,9 @@ void main() { expect(capturedResolutionInfo.height, equals(50)); }); - test('setSurfaceProviderTest', () async { + test( + 'setSurfaceProvider makes call to set surface provider for preview instance', + () async { final MockTestPreviewHostApi mockApi = MockTestPreviewHostApi(); TestPreviewHostApi.setup(mockApi); @@ -82,7 +84,9 @@ void main() { mockApi.setSurfaceProvider(instanceManager.getIdentifier(preview))); }); - test('setSurfaceProviderTest', () async { + test( + 'releaseFlutterSurfaceTexture makes call to relase flutter surface texture entry', + () async { final MockTestPreviewHostApi mockApi = MockTestPreviewHostApi(); TestPreviewHostApi.setup(mockApi); @@ -93,7 +97,9 @@ void main() { verify(mockApi.releaseFlutterSurfaceTexture()); }); - test('getResolutionInfoTest', () async { + test( + 'getResolutionInfo makes call to get resolution information for preview instance', + () async { final MockTestPreviewHostApi mockApi = MockTestPreviewHostApi(); TestPreviewHostApi.setup(mockApi); From 659c2e01b01739f7eb5a3e5d0009ba3588926dd9 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Thu, 2 Feb 2023 13:36:45 -0800 Subject: [PATCH 20/22] Update switch --- .../io/flutter/plugins/camerax/PreviewHostApiImpl.java | 7 +++---- .../test/java/io/flutter/plugins/camerax/PreviewTest.java | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java index 33063637e842..e785021ec113 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java @@ -89,16 +89,15 @@ public void onSurfaceRequested(SurfaceRequest request) { cameraXProxy.createSystemServicesFlutterApiImpl(binaryMessenger); int resultCode = result.getResultCode(); switch (resultCode) { + case SurfaceRequest.Result.RESULT_INVALID_SURFACE: + systemServicesFlutterApi.sendCameraError( + getProvideSurfaceErrorDescription(resultCode), reply -> {}); case SurfaceRequest.Result.RESULT_REQUEST_CANCELLED: case SurfaceRequest.Result.RESULT_WILL_NOT_PROVIDE_SURFACE: case SurfaceRequest.Result.RESULT_SURFACE_ALREADY_PROVIDED: case SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY: flutterSurface.release(); - break; - case SurfaceRequest.Result.RESULT_INVALID_SURFACE: default: - systemServicesFlutterApi.sendCameraError( - getProvideSurfaceErrorDescription(resultCode), reply -> {}); break; } }); diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java index 606d216db068..525e0df4274b 100644 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java @@ -163,6 +163,7 @@ public void createSurfaceProvider_createsExpectedPreviewSurfaceProvider() { when(mockSurfaceRequestResult.getResultCode()) .thenReturn(SurfaceRequest.Result.RESULT_INVALID_SURFACE); capturedConsumer.accept(mockSurfaceRequestResult); + verify(mockSurface, times(5)).release(); verify(mockSystemServicesFlutterApi, times(1)).sendCameraError(anyString(), any(Reply.class)); } From 5f269c75e6b9ebaf358370ed3425f3a2e126491f Mon Sep 17 00:00:00 2001 From: camsim99 Date: Fri, 3 Feb 2023 13:15:38 -0800 Subject: [PATCH 21/22] Add annotations and constants in tests --- .../flutter/plugins/camerax/CameraXProxy.java | 13 ++--- .../plugins/camerax/PreviewHostApiImpl.java | 27 ++++++----- .../camerax/SystemServicesFlutterApiImpl.java | 8 ++-- .../flutter/plugins/camerax/PreviewTest.java | 48 ++++++++++++------- .../test/preview_test.dart | 30 +++++++----- .../test/system_services_test.dart | 5 +- 6 files changed, 81 insertions(+), 50 deletions(-) diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java index fbe2490b4a9f..4a3d277a4dc3 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java @@ -7,6 +7,7 @@ import android.app.Activity; import android.graphics.SurfaceTexture; import android.view.Surface; +import androidx.annotation.NonNull; import androidx.camera.core.CameraSelector; import androidx.camera.core.Preview; import io.flutter.plugin.common.BinaryMessenger; @@ -22,10 +23,10 @@ public CameraPermissionsManager createCameraPermissionsManager() { } public DeviceOrientationManager createDeviceOrientationManager( - Activity activity, - Boolean isFrontFacing, - int sensorOrientation, - DeviceOrientationManager.DeviceOrientationChangeCallback callback) { + @NonNull Activity activity, + @NonNull Boolean isFrontFacing, + @NonNull int sensorOrientation, + @NonNull DeviceOrientationManager.DeviceOrientationChangeCallback callback) { return new DeviceOrientationManager(activity, isFrontFacing, sensorOrientation, callback); } @@ -33,7 +34,7 @@ public Preview.Builder createPreviewBuilder() { return new Preview.Builder(); } - public Surface createSurface(SurfaceTexture surfaceTexture) { + public Surface createSurface(@NonNull SurfaceTexture surfaceTexture) { return new Surface(surfaceTexture); } @@ -44,7 +45,7 @@ public Surface createSurface(SurfaceTexture surfaceTexture) { * onCameraError(String)}. */ public SystemServicesFlutterApiImpl createSystemServicesFlutterApiImpl( - BinaryMessenger binaryMessenger) { + @NonNull BinaryMessenger binaryMessenger) { return new SystemServicesFlutterApiImpl(binaryMessenger); } } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java index e785021ec113..838f0b3d656c 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java @@ -27,9 +27,9 @@ public class PreviewHostApiImpl implements PreviewHostApi { @VisibleForTesting public TextureRegistry.SurfaceTextureEntry flutterSurfaceTexture; public PreviewHostApiImpl( - BinaryMessenger binaryMessenger, - InstanceManager instanceManager, - TextureRegistry textureRegistry) { + @NonNull BinaryMessenger binaryMessenger, + @NonNull InstanceManager instanceManager, + @NonNull TextureRegistry textureRegistry) { this.binaryMessenger = binaryMessenger; this.instanceManager = instanceManager; this.textureRegistry = textureRegistry; @@ -74,7 +74,7 @@ public Long setSurfaceProvider(@NonNull Long identifier) { * {@code Preview} that is backed by a Flutter {@link TextureRegistry.SurfaceTextureEntry}. */ @VisibleForTesting - public Preview.SurfaceProvider createSurfaceProvider(SurfaceTexture surfaceTexture) { + public Preview.SurfaceProvider createSurfaceProvider(@NonNull SurfaceTexture surfaceTexture) { return new Preview.SurfaceProvider() { @Override public void onSurfaceRequested(SurfaceRequest request) { @@ -85,19 +85,24 @@ public void onSurfaceRequested(SurfaceRequest request) { flutterSurface, Executors.newSingleThreadExecutor(), (result) -> { - SystemServicesFlutterApiImpl systemServicesFlutterApi = - cameraXProxy.createSystemServicesFlutterApiImpl(binaryMessenger); + // See https://developer.android.com/reference/androidx/camera/core/SurfaceRequest.Result for documentation. + // Always attempt a release. + flutterSurface.release(); int resultCode = result.getResultCode(); switch (resultCode) { - case SurfaceRequest.Result.RESULT_INVALID_SURFACE: - systemServicesFlutterApi.sendCameraError( - getProvideSurfaceErrorDescription(resultCode), reply -> {}); case SurfaceRequest.Result.RESULT_REQUEST_CANCELLED: case SurfaceRequest.Result.RESULT_WILL_NOT_PROVIDE_SURFACE: case SurfaceRequest.Result.RESULT_SURFACE_ALREADY_PROVIDED: case SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY: - flutterSurface.release(); + // Only need to release, do nothing. + break; + case SurfaceRequest.Result.RESULT_INVALID_SURFACE: // Intentional fall through. default: + // Release and send error. + SystemServicesFlutterApiImpl systemServicesFlutterApi = + cameraXProxy.createSystemServicesFlutterApiImpl(binaryMessenger); + systemServicesFlutterApi.sendCameraError( + getProvideSurfaceErrorDescription(resultCode), reply -> {}); break; } }); @@ -109,7 +114,7 @@ public void onSurfaceRequested(SurfaceRequest request) { * Returns an error description for each {@link SurfaceRequest.Result} that represents an error * with providing a surface. */ - private String getProvideSurfaceErrorDescription(int resultCode) { + private String getProvideSurfaceErrorDescription(@Nullable int resultCode) { switch (resultCode) { case SurfaceRequest.Result.RESULT_INVALID_SURFACE: return resultCode + ": Provided surface could not be used by the camera."; diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesFlutterApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesFlutterApiImpl.java index d05dc43bc03c..63158974f43a 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesFlutterApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/SystemServicesFlutterApiImpl.java @@ -4,19 +4,21 @@ package io.flutter.plugins.camerax; +import androidx.annotation.NonNull; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugins.camerax.GeneratedCameraXLibrary.SystemServicesFlutterApi; public class SystemServicesFlutterApiImpl extends SystemServicesFlutterApi { - public SystemServicesFlutterApiImpl(BinaryMessenger binaryMessenger) { + public SystemServicesFlutterApiImpl(@NonNull BinaryMessenger binaryMessenger) { super(binaryMessenger); } - public void sendDeviceOrientationChangedEvent(String orientation, Reply reply) { + public void sendDeviceOrientationChangedEvent( + @NonNull String orientation, @NonNull Reply reply) { super.onDeviceOrientationChanged(orientation, reply); } - public void sendCameraError(String errorDescription, Reply reply) { + public void sendCameraError(@NonNull String errorDescription, @NonNull Reply reply) { super.onCameraError(errorDescription, reply); } } diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java index 525e0df4274b..28f3fccf8b2c 100644 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java @@ -61,8 +61,15 @@ public void create_createsPreviewWithCorrectConfiguration() { final PreviewHostApiImpl previewHostApi = new PreviewHostApiImpl(mockBinaryMessenger, testInstanceManager, mockTextureRegistry); final Preview.Builder mockPreviewBuilder = mock(Preview.Builder.class); + final int targetRotation = 90; + final int targetResolutionWidth = 10; + final int targetResolutionHeight = 50; + final Long previewIdentifier = 3L; final GeneratedCameraXLibrary.ResolutionInfo resolutionInfo = - new GeneratedCameraXLibrary.ResolutionInfo.Builder().setWidth(10L).setHeight(50L).build(); + new GeneratedCameraXLibrary.ResolutionInfo.Builder() + .setWidth(Long.valueOf(targetResolutionWidth)) + .setHeight(Long.valueOf(targetResolutionHeight)) + .build(); previewHostApi.cameraXProxy = mockCameraXProxy; when(mockCameraXProxy.createPreviewBuilder()).thenReturn(mockPreviewBuilder); @@ -70,14 +77,14 @@ public void create_createsPreviewWithCorrectConfiguration() { final ArgumentCaptor sizeCaptor = ArgumentCaptor.forClass(Size.class); - previewHostApi.create(3L, 90L, resolutionInfo); + previewHostApi.create(previewIdentifier, Long.valueOf(targetRotation), resolutionInfo); - verify(mockPreviewBuilder).setTargetRotation(90); + verify(mockPreviewBuilder).setTargetRotation(targetRotation); verify(mockPreviewBuilder).setTargetResolution(sizeCaptor.capture()); - assertEquals(sizeCaptor.getValue().getWidth(), 10); - assertEquals(sizeCaptor.getValue().getHeight(), 50); + assertEquals(sizeCaptor.getValue().getWidth(), targetResolutionWidth); + assertEquals(sizeCaptor.getValue().getHeight(), targetResolutionHeight); verify(mockPreviewBuilder).build(); - verify(testInstanceManager).addDartCreatedInstance(mockPreview, 3L); + verify(testInstanceManager).addDartCreatedInstance(mockPreview, previewIdentifier); } @Test @@ -87,13 +94,15 @@ public void setSurfaceProviderTest_createsSurfaceProviderAndReturnsTextureEntryI final TextureRegistry.SurfaceTextureEntry mockSurfaceTextureEntry = mock(TextureRegistry.SurfaceTextureEntry.class); final SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class); + final Long previewIdentifier = 5L; + final Long surfaceTextureEntryId = 120L; previewHostApi.cameraXProxy = mockCameraXProxy; - testInstanceManager.addDartCreatedInstance(mockPreview, 5L); + testInstanceManager.addDartCreatedInstance(mockPreview, previewIdentifier); when(mockTextureRegistry.createSurfaceTexture()).thenReturn(mockSurfaceTextureEntry); when(mockSurfaceTextureEntry.surfaceTexture()).thenReturn(mockSurfaceTexture); - when(mockSurfaceTextureEntry.id()).thenReturn(120L); + when(mockSurfaceTextureEntry.id()).thenReturn(surfaceTextureEntryId); final ArgumentCaptor surfaceProviderCaptor = ArgumentCaptor.forClass(Preview.SurfaceProvider.class); @@ -101,7 +110,7 @@ public void setSurfaceProviderTest_createsSurfaceProviderAndReturnsTextureEntryI final ArgumentCaptor consumerCaptor = ArgumentCaptor.forClass(Consumer.class); // Test that surface provider was set and the surface texture ID was returned. - assertEquals((long) previewHostApi.setSurfaceProvider(5L), 120L); + assertEquals(previewHostApi.setSurfaceProvider(previewIdentifier), surfaceTextureEntryId); verify(mockPreview).setSurfaceProvider(surfaceProviderCaptor.capture()); verify(previewHostApi).createSurfaceProvider(mockSurfaceTexture); } @@ -116,10 +125,13 @@ public void createSurfaceProvider_createsExpectedPreviewSurfaceProvider() { final SurfaceRequest.Result mockSurfaceRequestResult = mock(SurfaceRequest.Result.class); final SystemServicesFlutterApiImpl mockSystemServicesFlutterApi = mock(SystemServicesFlutterApiImpl.class); + final int resolutionWidth = 200; + final int resolutionHeight = 500; previewHostApi.cameraXProxy = mockCameraXProxy; when(mockCameraXProxy.createSurface(mockSurfaceTexture)).thenReturn(mockSurface); - when(mockSurfaceRequest.getResolution()).thenReturn(new Size(200, 500)); + when(mockSurfaceRequest.getResolution()) + .thenReturn(new Size(resolutionWidth, resolutionHeight)); when(mockCameraXProxy.createSystemServicesFlutterApiImpl(mockBinaryMessenger)) .thenReturn(mockSystemServicesFlutterApi); @@ -130,7 +142,7 @@ public void createSurfaceProvider_createsExpectedPreviewSurfaceProvider() { previewHostApi.createSurfaceProvider(mockSurfaceTexture); previewSurfaceProvider.onSurfaceRequested(mockSurfaceRequest); - verify(mockSurfaceTexture).setDefaultBufferSize(200, 500); + verify(mockSurfaceTexture).setDefaultBufferSize(resolutionWidth, resolutionHeight); verify(mockSurfaceRequest) .provideSurface(surfaceCaptor.capture(), any(Executor.class), consumerCaptor.capture()); @@ -186,13 +198,17 @@ public void getResolutionInfo_makesCallToRetrievePreviewResolutionInfo() { new PreviewHostApiImpl(mockBinaryMessenger, testInstanceManager, mockTextureRegistry); final androidx.camera.core.ResolutionInfo mockResolutionInfo = mock(androidx.camera.core.ResolutionInfo.class); + final Long previewIdentifier = 23L; + final int resolutionWidth = 500; + final int resolutionHeight = 200; - testInstanceManager.addDartCreatedInstance(mockPreview, 23L); + testInstanceManager.addDartCreatedInstance(mockPreview, previewIdentifier); when(mockPreview.getResolutionInfo()).thenReturn(mockResolutionInfo); - when(mockResolutionInfo.getResolution()).thenReturn(new Size(500, 200)); + when(mockResolutionInfo.getResolution()) + .thenReturn(new Size(resolutionWidth, resolutionHeight)); - ResolutionInfo resolutionInfo = previewHostApi.getResolutionInfo(23L); - assertEquals((long) resolutionInfo.getWidth(), 500L); - assertEquals((long) resolutionInfo.getHeight(), 200L); + ResolutionInfo resolutionInfo = previewHostApi.getResolutionInfo(previewIdentifier); + assertEquals(resolutionInfo.getWidth(), Long.valueOf(resolutionWidth)); + assertEquals(resolutionInfo.getHeight(), Long.valueOf(resolutionHeight)); } } diff --git a/packages/camera/camera_android_camerax/test/preview_test.dart b/packages/camera/camera_android_camerax/test/preview_test.dart index 66cd7157bd76..36b56f0046e1 100644 --- a/packages/camera/camera_android_camerax/test/preview_test.dart +++ b/packages/camera/camera_android_camerax/test/preview_test.dart @@ -43,18 +43,22 @@ void main() { final InstanceManager instanceManager = InstanceManager( onWeakReferenceRemoved: (_) {}, ); + const int targetRotation = 90; + const int targetResolutionWidth = 10; + const int targetResolutionHeight = 50; Preview( instanceManager: instanceManager, - targetRotation: 90, - targetResolution: ResolutionInfo(width: 10, height: 50), + targetRotation: targetRotation, + targetResolution: ResolutionInfo( + width: targetResolutionWidth, height: targetResolutionHeight), ); - final VerificationResult createVerification = verify( - mockApi.create(argThat(isA()), argThat(equals(90)), captureAny)); + final VerificationResult createVerification = verify(mockApi.create( + argThat(isA()), argThat(equals(targetRotation)), captureAny)); final ResolutionInfo capturedResolutionInfo = createVerification.captured.single as ResolutionInfo; - expect(capturedResolutionInfo.width, equals(10)); - expect(capturedResolutionInfo.height, equals(50)); + expect(capturedResolutionInfo.width, equals(targetResolutionWidth)); + expect(capturedResolutionInfo.height, equals(targetResolutionHeight)); }); test( @@ -66,10 +70,10 @@ void main() { final InstanceManager instanceManager = InstanceManager( onWeakReferenceRemoved: (_) {}, ); + const int textureId = 8; final Preview preview = Preview.detached( instanceManager: instanceManager, ); - instanceManager.addHostCreatedInstance( preview, 0, @@ -77,8 +81,8 @@ void main() { ); when(mockApi.setSurfaceProvider(instanceManager.getIdentifier(preview))) - .thenReturn(8); - expect(await preview.setSurfaceProvider(), equals(8)); + .thenReturn(textureId); + expect(await preview.setSurfaceProvider(), equals(textureId)); verify( mockApi.setSurfaceProvider(instanceManager.getIdentifier(preview))); @@ -109,8 +113,10 @@ void main() { final Preview preview = Preview.detached( instanceManager: instanceManager, ); + const int resolutionWidth = 10; + const int resolutionHeight = 60; final ResolutionInfo testResolutionInfo = - ResolutionInfo(width: 10, height: 60); + ResolutionInfo(width: resolutionWidth, height: resolutionHeight); instanceManager.addHostCreatedInstance( preview, @@ -123,8 +129,8 @@ void main() { final ResolutionInfo previewResolutionInfo = await preview.getResolutionInfo(); - expect(previewResolutionInfo.width, equals(10)); - expect(previewResolutionInfo.height, equals(60)); + expect(previewResolutionInfo.width, equals(resolutionWidth)); + expect(previewResolutionInfo.height, equals(resolutionHeight)); verify(mockApi.getResolutionInfo(instanceManager.getIdentifier(preview))); }); diff --git a/packages/camera/camera_android_camerax/test/system_services_test.dart b/packages/camera/camera_android_camerax/test/system_services_test.dart index cd792be089b4..38037eaa135c 100644 --- a/packages/camera/camera_android_camerax/test/system_services_test.dart +++ b/packages/camera/camera_android_camerax/test/system_services_test.dart @@ -99,11 +99,12 @@ void main() { }); test('onCameraError adds new error to stream', () { + const String testErrorDescription = 'Test error description!'; SystemServices.cameraErrorStreamController.stream .listen((String errorDescription) { - expect(errorDescription, equals('Test error description!')); + expect(errorDescription, equals(testErrorDescription)); }); - SystemServicesFlutterApiImpl().onCameraError('Test error description!'); + SystemServicesFlutterApiImpl().onCameraError(testErrorDescription); }); }); } From 78e0d0f3a2f4b80d34407f5fda6dd4b2ce4f8607 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Mon, 6 Feb 2023 10:41:13 -0800 Subject: [PATCH 22/22] Reset verification behavior --- .../flutter/plugins/camerax/PreviewTest.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java index 28f3fccf8b2c..9cb4e910dbb8 100644 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java @@ -8,8 +8,8 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -157,26 +157,33 @@ public void createSurfaceProvider_createsExpectedPreviewSurfaceProvider() { when(mockSurfaceRequestResult.getResultCode()) .thenReturn(SurfaceRequest.Result.RESULT_REQUEST_CANCELLED); capturedConsumer.accept(mockSurfaceRequestResult); - verify(mockSurface, times(1)).release(); + verify(mockSurface).release(); + reset(mockSurface); + when(mockSurfaceRequestResult.getResultCode()) .thenReturn(SurfaceRequest.Result.RESULT_REQUEST_CANCELLED); capturedConsumer.accept(mockSurfaceRequestResult); - verify(mockSurface, times(2)).release(); + verify(mockSurface).release(); + reset(mockSurface); + when(mockSurfaceRequestResult.getResultCode()) .thenReturn(SurfaceRequest.Result.RESULT_WILL_NOT_PROVIDE_SURFACE); capturedConsumer.accept(mockSurfaceRequestResult); - verify(mockSurface, times(3)).release(); + verify(mockSurface).release(); + reset(mockSurface); + when(mockSurfaceRequestResult.getResultCode()) .thenReturn(SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY); capturedConsumer.accept(mockSurfaceRequestResult); - verify(mockSurface, times(4)).release(); + verify(mockSurface).release(); + reset(mockSurface); // Case where error must be sent. when(mockSurfaceRequestResult.getResultCode()) .thenReturn(SurfaceRequest.Result.RESULT_INVALID_SURFACE); capturedConsumer.accept(mockSurfaceRequestResult); - verify(mockSurface, times(5)).release(); - verify(mockSystemServicesFlutterApi, times(1)).sendCameraError(anyString(), any(Reply.class)); + verify(mockSurface).release(); + verify(mockSystemServicesFlutterApi).sendCameraError(anyString(), any(Reply.class)); } @Test