From 9ba97966c20d8438d60ccf8f33e72e0e207763b9 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Sat, 28 May 2022 20:27:27 -0400 Subject: [PATCH 01/13] Copy/move app-facing files to make new implementation packages --- packages/camera/camera_android/AUTHORS | 66 + packages/camera/camera_android/CHANGELOG.md | 665 ++++++++++ packages/camera/camera_android/LICENSE | 25 + packages/camera/camera_android/README.md | 176 +++ .../android/build.gradle | 0 .../android/lint-baseline.xml | 0 .../android/settings.gradle | 0 .../android/src/main/AndroidManifest.xml | 0 .../io/flutter/plugins/camera/Camera.java | 0 .../plugins/camera/CameraCaptureCallback.java | 0 .../plugins/camera/CameraPermissions.java | 0 .../flutter/plugins/camera/CameraPlugin.java | 0 .../plugins/camera/CameraProperties.java | 0 .../plugins/camera/CameraRegionUtils.java | 0 .../flutter/plugins/camera/CameraState.java | 0 .../flutter/plugins/camera/CameraUtils.java | 0 .../io/flutter/plugins/camera/CameraZoom.java | 0 .../flutter/plugins/camera/DartMessenger.java | 0 .../io/flutter/plugins/camera/ImageSaver.java | 0 .../plugins/camera/MethodCallHandlerImpl.java | 0 .../camera/features/CameraFeature.java | 0 .../camera/features/CameraFeatureFactory.java | 0 .../features/CameraFeatureFactoryImpl.java | 0 .../camera/features/CameraFeatures.java | 0 .../plugins/camera/features/Point.java | 0 .../features/autofocus/AutoFocusFeature.java | 0 .../camera/features/autofocus/FocusMode.java | 0 .../exposurelock/ExposureLockFeature.java | 0 .../features/exposurelock/ExposureMode.java | 0 .../exposureoffset/ExposureOffsetFeature.java | 0 .../exposurepoint/ExposurePointFeature.java | 0 .../camera/features/flash/FlashFeature.java | 0 .../camera/features/flash/FlashMode.java | 0 .../focuspoint/FocusPointFeature.java | 0 .../features/fpsrange/FpsRangeFeature.java | 0 .../noisereduction/NoiseReductionFeature.java | 0 .../noisereduction/NoiseReductionMode.java | 0 .../resolution/ResolutionFeature.java | 0 .../features/resolution/ResolutionPreset.java | 0 .../DeviceOrientationManager.java | 0 .../SensorOrientationFeature.java | 0 .../features/zoomlevel/ZoomLevelFeature.java | 0 .../camera/features/zoomlevel/ZoomUtils.java | 0 .../camera/media/MediaRecorderBuilder.java | 0 .../camera/types/CameraCaptureProperties.java | 0 .../camera/types/CaptureTimeoutsWrapper.java | 0 .../plugins/camera/types/ExposureMode.java | 0 .../plugins/camera/types/FlashMode.java | 0 .../plugins/camera/types/FocusMode.java | 0 .../camera/types/ResolutionPreset.java | 0 .../flutter/plugins/camera/types/Timeout.java | 0 .../CameraCaptureCallbackStatesTest.java | 0 .../camera/CameraCaptureCallbackTest.java | 0 .../plugins/camera/CameraPermissionsTest.java | 0 .../camera/CameraPropertiesImplTest.java | 0 ...s_convertPointToMeteringRectangleTest.java | 0 ...raRegionUtils_getCameraBoundariesTest.java | 0 .../io/flutter/plugins/camera/CameraTest.java | 0 .../CameraTest_getRecordingProfileTest.java | 0 .../plugins/camera/CameraUtilsTest.java | 0 .../plugins/camera/CameraZoomTest.java | 0 .../plugins/camera/DartMessengerTest.java | 0 .../plugins/camera/ImageSaverTests.java | 0 .../camera/MethodCallHandlerImplTest.java | 0 .../autofocus/AutoFocusFeatureTest.java | 0 .../features/autofocus/FocusModeTest.java | 0 .../exposurelock/ExposureLockFeatureTest.java | 0 .../exposurelock/ExposureModeTest.java | 0 .../ExposureOffsetFeatureTest.java | 0 .../ExposurePointFeatureTest.java | 0 .../features/flash/FlashFeatureTest.java | 0 .../focuspoint/FocusPointFeatureTest.java | 0 .../fpsrange/FpsRangeFeaturePixel4aTest.java | 0 .../fpsrange/FpsRangeFeatureTest.java | 0 .../NoiseReductionFeatureTest.java | 0 .../resolution/ResolutionFeatureTest.java | 0 .../DeviceOrientationManagerTest.java | 0 .../SensorOrientationFeatureTest.java | 0 .../zoomlevel/ZoomLevelFeatureTest.java | 0 .../features/zoomlevel/ZoomUtilsTest.java | 0 .../media/MediaRecorderBuilderTest.java | 0 .../camera/types/ExposureModeTest.java | 0 .../plugins/camera/types/FlashModeTest.java | 0 .../plugins/camera/types/FocusModeTest.java | 0 .../plugins/camera/utils/TestUtils.java | 0 .../src/test/resources/robolectric.properties | 0 .../example/android/app/build.gradle | 64 + .../gradle/wrapper/gradle-wrapper.properties | 5 + .../flutter/plugins/DartIntegrationTest.java | 14 + .../cameraexample/FlutterActivityTest.java | 19 + .../android/app/src/main/AndroidManifest.xml | 28 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values/styles.xml | 8 + .../example/android/build.gradle | 29 + .../example/android/gradle.properties | 4 + .../gradle/wrapper/gradle-wrapper.properties | 5 + .../example/android/settings.gradle | 15 + .../example/integration_test/camera_test.dart | 297 +++++ .../camera_android/example/lib/main.dart | 1091 +++++++++++++++++ .../camera_android/example/pubspec.yaml | 32 + .../example/test_driver/integration_test.dart | 64 + packages/camera/camera_android/pubspec.yaml | 39 + packages/camera/camera_avfoundation/AUTHORS | 66 + .../camera/camera_avfoundation/CHANGELOG.md | 665 ++++++++++ packages/camera/camera_avfoundation/LICENSE | 25 + packages/camera/camera_avfoundation/README.md | 176 +++ .../example/integration_test/camera_test.dart | 297 +++++ .../ios/Flutter/AppFrameworkInfo.plist | 30 + .../example/ios/Flutter/Debug.xcconfig | 3 + .../example/ios/Flutter/Release.xcconfig | 3 + .../camera_avfoundation/example/ios/Podfile | 45 + .../ios/Runner.xcodeproj/project.pbxproj | 712 +++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/xcschemes/Runner.xcscheme | 104 ++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/ios/Runner/AppDelegate.h | 10 + .../example/ios/Runner/AppDelegate.m | 17 + .../AppIcon.appiconset/Contents.json | 121 ++ .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 564 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 1588 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 1025 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 1716 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 1920 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 1895 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 3831 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 1888 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 3294 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 3612 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/README.md | 5 + .../Runner/Base.lproj/LaunchScreen.storyboard | 37 + .../ios/Runner/Base.lproj/Main.storyboard | 26 + .../example/ios/Runner/Info.plist | 56 + .../example/ios/Runner/main.m | 19 + .../ios/RunnerTests/AvailableCamerasTest.m | 0 ...eraCaptureSessionQueueRaceConditionTests.m | 0 .../ios/RunnerTests/CameraExposureTests.m | 0 .../ios/RunnerTests/CameraFocusTests.m | 0 .../RunnerTests/CameraMethodChannelTests.m | 0 .../ios/RunnerTests/CameraOrientationTests.m | 0 .../ios/RunnerTests/CameraPermissionTests.m | 0 .../ios/RunnerTests/CameraPreviewPauseTests.m | 0 .../ios/RunnerTests/CameraPropertiesTests.m | 0 .../example/ios/RunnerTests/CameraTestUtils.h | 0 .../example/ios/RunnerTests/CameraTestUtils.m | 0 .../example/ios/RunnerTests/CameraUtilTests.m | 0 .../ios/RunnerTests/FLTCamPhotoCaptureTests.m | 0 .../ios/RunnerTests/FLTCamSampleBufferTests.m | 0 .../RunnerTests/FLTSavePhotoDelegateTests.m | 0 .../example/ios/RunnerTests/Info.plist | 0 .../MockFLTThreadSafeFlutterResult.h | 0 .../MockFLTThreadSafeFlutterResult.m | 0 .../example/ios/RunnerTests/QueueUtilsTests.m | 0 .../example/ios/RunnerTests/StreamingTest.m | 0 .../RunnerTests/ThreadSafeEventChannelTests.m | 0 .../ThreadSafeFlutterResultTests.m | 0 .../ThreadSafeMethodChannelTests.m | 0 .../ThreadSafeTextureRegistryTests.m | 0 .../camera_avfoundation/example/lib/main.dart | 1091 +++++++++++++++++ .../camera_avfoundation/example/pubspec.yaml | 32 + .../example/test_driver/integration_test.dart | 64 + .../ios/Assets/.gitkeep | 0 .../ios/Classes/CameraPermissionUtils.h | 0 .../ios/Classes/CameraPermissionUtils.m | 0 .../ios/Classes/CameraPlugin.h | 0 .../ios/Classes/CameraPlugin.m | 0 .../ios/Classes/CameraPlugin.modulemap | 0 .../ios/Classes/CameraPlugin_Test.h | 0 .../ios/Classes/CameraProperties.h | 0 .../ios/Classes/CameraProperties.m | 0 .../ios/Classes/FLTCam.h | 0 .../ios/Classes/FLTCam.m | 0 .../ios/Classes/FLTCam_Test.h | 0 .../ios/Classes/FLTSavePhotoDelegate.h | 0 .../ios/Classes/FLTSavePhotoDelegate.m | 0 .../ios/Classes/FLTSavePhotoDelegate_Test.h | 0 .../ios/Classes/FLTThreadSafeEventChannel.h | 0 .../ios/Classes/FLTThreadSafeEventChannel.m | 0 .../ios/Classes/FLTThreadSafeFlutterResult.h | 0 .../ios/Classes/FLTThreadSafeFlutterResult.m | 0 .../ios/Classes/FLTThreadSafeMethodChannel.h | 0 .../ios/Classes/FLTThreadSafeMethodChannel.m | 0 .../Classes/FLTThreadSafeTextureRegistry.h | 0 .../Classes/FLTThreadSafeTextureRegistry.m | 0 .../ios/Classes/QueueUtils.h | 0 .../ios/Classes/QueueUtils.m | 0 .../ios/Classes/camera-umbrella.h | 0 .../ios/camera.podspec | 0 .../camera/camera_avfoundation/pubspec.yaml | 39 + 202 files changed, 6349 insertions(+) create mode 100644 packages/camera/camera_android/AUTHORS create mode 100644 packages/camera/camera_android/CHANGELOG.md create mode 100644 packages/camera/camera_android/LICENSE create mode 100644 packages/camera/camera_android/README.md rename packages/camera/{camera => camera_android}/android/build.gradle (100%) rename packages/camera/{camera => camera_android}/android/lint-baseline.xml (100%) rename packages/camera/{camera => camera_android}/android/settings.gradle (100%) rename packages/camera/{camera => camera_android}/android/src/main/AndroidManifest.xml (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/Camera.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/CameraCaptureCallback.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/CameraPermissions.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/CameraState.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/CameraUtils.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/CameraZoom.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/DartMessenger.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/ImageSaver.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/CameraFeature.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactory.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactoryImpl.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatures.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/Point.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/autofocus/AutoFocusFeature.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/autofocus/FocusMode.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/exposurelock/ExposureLockFeature.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/exposurelock/ExposureMode.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/exposureoffset/ExposureOffsetFeature.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeature.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/flash/FlashFeature.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/flash/FlashMode.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeature.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeature.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionMode.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionPreset.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManager.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/SensorOrientationFeature.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeature.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/zoomlevel/ZoomUtils.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/media/MediaRecorderBuilder.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/types/CameraCaptureProperties.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/types/CaptureTimeoutsWrapper.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/types/ExposureMode.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/types/FlashMode.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/types/FocusMode.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/types/ResolutionPreset.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/types/Timeout.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackStatesTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/CameraPermissionsTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/CameraPropertiesImplTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtils_convertPointToMeteringRectangleTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtils_getCameraBoundariesTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/CameraTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/CameraTest_getRecordingProfileTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/CameraUtilsTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/CameraZoomTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/DartMessengerTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/ImageSaverTests.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/MethodCallHandlerImplTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/autofocus/AutoFocusFeatureTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/autofocus/FocusModeTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/exposurelock/ExposureLockFeatureTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/exposurelock/ExposureModeTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/exposureoffset/ExposureOffsetFeatureTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeatureTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/flash/FlashFeatureTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeatureTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeaturePixel4aTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeatureTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeatureTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/resolution/ResolutionFeatureTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManagerTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/SensorOrientationFeatureTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeatureTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/zoomlevel/ZoomUtilsTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/media/MediaRecorderBuilderTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/types/ExposureModeTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/types/FlashModeTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/types/FocusModeTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/utils/TestUtils.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/resources/robolectric.properties (100%) create mode 100644 packages/camera/camera_android/example/android/app/build.gradle create mode 100644 packages/camera/camera_android/example/android/app/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/camera/camera_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java create mode 100644 packages/camera/camera_android/example/android/app/src/androidTest/java/io/flutter/plugins/cameraexample/FlutterActivityTest.java create mode 100644 packages/camera/camera_android/example/android/app/src/main/AndroidManifest.xml create mode 100644 packages/camera/camera_android/example/android/app/src/main/res/drawable/launch_background.xml create mode 100644 packages/camera/camera_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 packages/camera/camera_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 packages/camera/camera_android/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 packages/camera/camera_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 packages/camera/camera_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 packages/camera/camera_android/example/android/app/src/main/res/values/styles.xml create mode 100644 packages/camera/camera_android/example/android/build.gradle create mode 100644 packages/camera/camera_android/example/android/gradle.properties create mode 100644 packages/camera/camera_android/example/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/camera/camera_android/example/android/settings.gradle create mode 100644 packages/camera/camera_android/example/integration_test/camera_test.dart create mode 100644 packages/camera/camera_android/example/lib/main.dart create mode 100644 packages/camera/camera_android/example/pubspec.yaml create mode 100644 packages/camera/camera_android/example/test_driver/integration_test.dart create mode 100644 packages/camera/camera_android/pubspec.yaml create mode 100644 packages/camera/camera_avfoundation/AUTHORS create mode 100644 packages/camera/camera_avfoundation/CHANGELOG.md create mode 100644 packages/camera/camera_avfoundation/LICENSE create mode 100644 packages/camera/camera_avfoundation/README.md create mode 100644 packages/camera/camera_avfoundation/example/integration_test/camera_test.dart create mode 100644 packages/camera/camera_avfoundation/example/ios/Flutter/AppFrameworkInfo.plist create mode 100644 packages/camera/camera_avfoundation/example/ios/Flutter/Debug.xcconfig create mode 100644 packages/camera/camera_avfoundation/example/ios/Flutter/Release.xcconfig create mode 100644 packages/camera/camera_avfoundation/example/ios/Podfile create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/AppDelegate.h create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/AppDelegate.m create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Base.lproj/Main.storyboard create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Info.plist create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/main.m rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/AvailableCamerasTest.m (100%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m (100%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/CameraExposureTests.m (100%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/CameraFocusTests.m (100%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/CameraMethodChannelTests.m (100%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/CameraOrientationTests.m (100%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/CameraPermissionTests.m (100%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/CameraPreviewPauseTests.m (100%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/CameraPropertiesTests.m (100%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/CameraTestUtils.h (100%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/CameraTestUtils.m (100%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/CameraUtilTests.m (100%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m (100%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/FLTCamSampleBufferTests.m (100%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m (100%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/Info.plist (100%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.h (100%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.m (100%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/QueueUtilsTests.m (100%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/StreamingTest.m (100%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/ThreadSafeEventChannelTests.m (100%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/ThreadSafeFlutterResultTests.m (100%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m (100%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m (100%) create mode 100644 packages/camera/camera_avfoundation/example/lib/main.dart create mode 100644 packages/camera/camera_avfoundation/example/pubspec.yaml create mode 100644 packages/camera/camera_avfoundation/example/test_driver/integration_test.dart rename packages/camera/{camera => camera_avfoundation}/ios/Assets/.gitkeep (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/CameraPermissionUtils.h (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/CameraPermissionUtils.m (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/CameraPlugin.h (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/CameraPlugin.m (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/CameraPlugin.modulemap (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/CameraPlugin_Test.h (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/CameraProperties.h (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/CameraProperties.m (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTCam.h (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTCam.m (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTCam_Test.h (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTSavePhotoDelegate.h (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTSavePhotoDelegate.m (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTSavePhotoDelegate_Test.h (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTThreadSafeEventChannel.h (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTThreadSafeEventChannel.m (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTThreadSafeFlutterResult.h (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTThreadSafeFlutterResult.m (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTThreadSafeMethodChannel.h (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTThreadSafeMethodChannel.m (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTThreadSafeTextureRegistry.h (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTThreadSafeTextureRegistry.m (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/QueueUtils.h (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/QueueUtils.m (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/camera-umbrella.h (100%) rename packages/camera/{camera => camera_avfoundation}/ios/camera.podspec (100%) create mode 100644 packages/camera/camera_avfoundation/pubspec.yaml diff --git a/packages/camera/camera_android/AUTHORS b/packages/camera/camera_android/AUTHORS new file mode 100644 index 000000000000..493a0b4ef9c2 --- /dev/null +++ b/packages/camera/camera_android/AUTHORS @@ -0,0 +1,66 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md new file mode 100644 index 000000000000..72af38a9f9de --- /dev/null +++ b/packages/camera/camera_android/CHANGELOG.md @@ -0,0 +1,665 @@ +## 0.9.7+1 + +* Moves streaming implementation to the platform interface package. + +## 0.9.7 + +* Returns all the available cameras on iOS. + +## 0.9.6 + +* Adds audio access permission handling logic on iOS to fix an issue with `prepareForVideoRecording` not awaiting for the audio permission request result. + +## 0.9.5+1 + +* Suppresses warnings for pre-iOS-11 codepaths. + +## 0.9.5 + +* Adds camera access permission handling logic on iOS to fix a related crash when using the camera for the first time. + +## 0.9.4+24 + +* Fixes preview orientation when pausing preview with locked orientation. + +## 0.9.4+23 + +* Minor fixes for new analysis options. + +## 0.9.4+22 + +* Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + +## 0.9.4+21 + +* Fixes README code samples. + +## 0.9.4+20 + +* Fixes an issue with the orientation of videos recorded in landscape on Android. + +## 0.9.4+19 + +* Migrate deprecated Scaffold SnackBar methods to ScaffoldMessenger. + +## 0.9.4+18 + +* Fixes a crash in iOS when streaming on low-performance devices. + +## 0.9.4+17 + +* Removes obsolete information from README, and adds OS support table. + +## 0.9.4+16 + +* Fixes a bug resulting in a `CameraAccessException` that prevents image + capture on some Android devices. + +## 0.9.4+15 + +* Uses dispatch queue for pixel buffer synchronization on iOS. +* Minor iOS internal code cleanup related to queue helper functions. + +## 0.9.4+14 + +* Restores compatibility with Flutter 2.5 and 2.8. + +## 0.9.4+13 + +* Updates iOS camera's photo capture delegate reference on a background queue to prevent potential race conditions, and some related internal code cleanup. + +## 0.9.4+12 + +* Skips unnecessary AppDelegate setup for unit tests on iOS. +* Internal code cleanup for stricter analysis options. + +## 0.9.4+11 + +* Manages iOS camera's orientation-related states on a background queue to prevent potential race conditions. + +## 0.9.4+10 + +* iOS performance improvement by moving file writing from the main queue to a background IO queue. + +## 0.9.4+9 + +* iOS performance improvement by moving sample buffer handling from the main queue to a background session queue. +* Minor iOS internal code cleanup related to camera class and its delegate. +* Minor iOS internal code cleanup related to resolution preset, video format, focus mode, exposure mode and device orientation. +* Minor iOS internal code cleanup related to flash mode. + +## 0.9.4+8 + +* Fixes a bug where ImageFormatGroup was ignored in `startImageStream` on iOS. + +## 0.9.4+7 + +* Fixes a crash in iOS when passing null queue pointer into AVFoundation API due to race condition. +* Minor iOS internal code cleanup related to dispatch queue. + +## 0.9.4+6 + +* Fixes a crash in iOS when using image stream due to calling Flutter engine API on non-main thread. + +## 0.9.4+5 + +* Fixes bug where calling a method after the camera was closed resulted in a Java `IllegalStateException` exception. +* Fixes integration tests. + +## 0.9.4+4 + +* Change Android compileSdkVersion to 31. +* Remove usages of deprecated Android API `CamcorderProfile`. +* Update gradle version to 7.0.2 on Android. + +## 0.9.4+3 + +* Fix registerTexture and result being called on background thread on iOS. + +## 0.9.4+2 + +* Updated package description; +* Refactor unit test on iOS to make it compatible with new restrictions in Xcode 13 which only supports the use of the `XCUIDevice` in Xcode UI tests. + +## 0.9.4+1 + +* Fixed Android implementation throwing IllegalStateException when switching to a different activity. + +## 0.9.4 + +* Add web support by endorsing `package:camera_web`. + +## 0.9.3+1 + +* Remove iOS 9 availability check around ultra high capture sessions. + +## 0.9.3 + +* Update minimum Flutter SDK to 2.5 and iOS deployment target to 9.0. + +## 0.9.2+2 + +* Ensure that setting the exposure offset returns the new offset value on Android. + +## 0.9.2+1 + +* Fixed camera controller throwing an exception when being replaced in the preview widget. + +## 0.9.2 + +* Added functions to pause and resume the camera preview. + +## 0.9.1+1 + +* Replace `device_info` reference with `device_info_plus` in the [README.md](README.md) + +## 0.9.1 + +* Added `lensAperture`, `sensorExposureTime` and `sensorSensitivity` properties to the `CameraImage` dto. + +## 0.9.0 + +* Complete rewrite of Android plugin to fix many capture, focus, flash, orientation and exposure issues. +* Fixed crash when opening front-facing cameras on some legacy android devices like Sony XZ. +* Android Flash mode works with full precapture sequence. +* Updated Android lint settings. + +## 0.8.1+7 + +* Fix device orientation sometimes not affecting the camera preview orientation. + +## 0.8.1+6 + +* Remove references to the Android V1 embedding. + +## 0.8.1+5 + +* Make sure the `setFocusPoint` and `setExposurePoint` coordinates work correctly in all orientations on iOS (instead of only in portrait mode). + +## 0.8.1+4 + +* Silenced warnings that may occur during build when using a very + recent version of Flutter relating to null safety. + +## 0.8.1+3 + +* Do not change camera orientation when iOS device is flat. + +## 0.8.1+2 + +* Fix iOS crash when selecting an unsupported FocusMode. + +## 0.8.1+1 + +* Migrate maven repository from jcenter to mavenCentral. + +## 0.8.1 + +* Solved a rotation issue on iOS which caused the default preview to be displayed as landscape right instead of portrait. + +## 0.8.0 + +* Stable null safety release. +* Solved delay when using the zoom feature on iOS. +* Added a timeout to the pre-capture sequence on Android to prevent crashes when the camera cannot get a focus. +* Updates the example code listed in the [README.md](README.md), so it runs without errors when you simply copy/ paste it into a Flutter App. + +## 0.7.0+4 + +* Fix crash when taking picture with orientation lock + +## 0.7.0+3 + +* Clockwise rotation of focus point in android + +## 0.7.0+2 + +* Fix example reference in README. +* Revert compileSdkVersion back to 29 (from 30) as this is causing problems with add-to-app configurations. + +## 0.7.0+1 + +* Ensure communication from JAVA to Dart is done on the main UI thread. + +## 0.7.0 + +* BREAKING CHANGE: `CameraValue.aspectRatio` now returns `width / height` rather than `height / width`. [(commit)](https://github.com/flutter/plugins/commit/100c7470d4066b1d0f8f7e4ec6d7c943e736f970) + * Added support for capture orientation locking on Android and iOS. + * Fixed camera preview not rotating correctly on Android and iOS. + * Fixed camera preview sometimes appearing stretched on Android and iOS. + * Fixed videos & photos saving with the incorrect rotation on iOS. +* New Features: + * Adds auto focus support for Android and iOS implementations. [(commmit)](https://github.com/flutter/plugins/commit/71a831790220f898bf8120c8a23840ac6e742db5) + * Adds ImageFormat selection for ImageStream and Video(iOS only). [(commit)](https://github.com/flutter/plugins/commit/da1b4638b750a5ff832d7be86a42831c42c6d6c0) +* Bug Fixes: + * Fixes crash when taking a picture on iOS devices without flash. [(commit)](https://github.com/flutter/plugins/commit/831344490984b1feec007afc9c8595d80b6c13f4) + * Make sure the configured zoom scale is copied over to the final capture builder on Android. Fixes the issue where the preview is zoomed but the final picture is not. [(commit)](https://github.com/flutter/plugins/commit/5916f55664e1772a4c3f0c02c5c71fc11e491b76) + * Fixes crash with using inner camera on some Android devices. [(commit)](https://github.com/flutter/plugins/commit/980b674cb4020c1927917426211a87e275346d5e) + * Improved error feedback by differentiating between uninitialized and disposed camera controllers. [(commit)](https://github.com/flutter/plugins/commit/d0b7109f6b00a0eda03506fed2c74cc123ffc6f3) + * Fixes picture captures causing a crash on some Huawei devices. [(commit)](https://github.com/flutter/plugins/commit/6d18db83f00f4861ffe485aba2d1f8aa08845ce6) + +## 0.6.4+5 + +* Update the example app: remove the deprecated `RaisedButton` and `FlatButton` widgets. + +## 0.6.4+4 + +* Set camera auto focus enabled by default. + +## 0.6.4+3 + +* Detect if selected camera supports auto focus and act accordingly on Android. This solves a problem where front facing cameras are not capturing the picture because auto focus is not supported. + +## 0.6.4+2 + +* Set ImageStreamReader listener to null to prevent stale images when streaming images. + +## 0.6.4+1 + +* Added closeCaptureSession() to stopVideoRecording in Camera.java to fix an Android 6 crash. + +## 0.6.4 + +* Adds auto exposure support for Android and iOS implementations. + +## 0.6.3+4 + +* Revert previous dependency update: Changed dependency on camera_platform_interface to >=1.04 <1.1.0. + +## 0.6.3+3 + +* Updated dependency on camera_platform_interface to ^1.2.0. + +## 0.6.3+2 + +* Fixes crash on Android which occurs after video recording has stopped just before taking a picture. + +## 0.6.3+1 + +* Fixes flash & torch modes not working on some Android devices. + +## 0.6.3 + +* Adds torch mode as a flash mode for Android and iOS implementations. + +## 0.6.2+1 + +* Fix the API documentation for the `CameraController.takePicture` method. + +## 0.6.2 + +* Add zoom support for Android and iOS implementations. + +## 0.6.1+1 + +* Added implementation of the `didFinishProcessingPhoto` on iOS which allows saving image metadata (EXIF) on iOS 11 and up. + +## 0.6.1 + +* Add flash support for Android and iOS implementations. + +## 0.6.0+2 + +* Fix outdated links across a number of markdown files ([#3276](https://github.com/flutter/plugins/pull/3276)) + +## 0.6.0+1 + +Updated README to inform users that iOS 10.0+ is needed for use + +## 0.6.0 + +As part of implementing federated architecture and making the interface compatible with the web this version contains the following **breaking changes**: + +Method changes in `CameraController`: +- The `takePicture` method no longer accepts the `path` parameter, but instead returns the captured image as an instance of the `XFile` class; +- The `startVideoRecording` method no longer accepts the `filePath`. Instead the recorded video is now returned as a `XFile` instance when the `stopVideoRecording` method completes; +- The `stopVideoRecording` method now returns the captured video when it completes; +- Added the `buildPreview` method which is now used to implement the CameraPreview widget. + +## 0.5.8+19 + +* Update Flutter SDK constraint. + +## 0.5.8+18 + +* Suppress unchecked warning in Android tests which prevented the tests to compile. + +## 0.5.8+17 + +* Added Android 30 support. + +## 0.5.8+16 + +* Moved package to camera/camera subdir, to allow for federated implementations. + +## 0.5.8+15 + +* Added the `debugCheckIsDisposed` method which can be used in debug mode to validate if the `CameraController` class has been disposed. + +## 0.5.8+14 + +* Changed the order of the setters for `mediaRecorder` in `MediaRecorderBuilder.java` to make it more readable. + +## 0.5.8+13 + +* Added Dartdocs for all public APIs. + +## 0.5.8+12 + +* Added information of video not working correctly on Android emulators to `README.md`. + +## 0.5.8+11 + +* Fix rare nullptr exception on Android. +* Updated README.md with information about handling App lifecycle changes. + +## 0.5.8+10 + +* Suppress the `deprecated_member_use` warning in the example app for `ScaffoldMessenger.showSnackBar`. + +## 0.5.8+9 + +* Update android compileSdkVersion to 29. + +## 0.5.8+8 + +* Fixed garbled audio (in video) by setting audio encoding bitrate. + +## 0.5.8+7 + +* Keep handling deprecated Android v1 classes for backward compatibility. + +## 0.5.8+6 + +* Avoiding uses or overrides a deprecated API in CameraPlugin.java. + +## 0.5.8+5 + +* Fix compilation/availability issues on iOS. + +## 0.5.8+4 + +* Fixed bug caused by casting a `CameraAccessException` on Android. + +## 0.5.8+3 + +* Fix bug in usage example in README.md + +## 0.5.8+2 + +* Post-v2 embedding cleanups. + +## 0.5.8+1 + +* Update lower bound of dart dependency to 2.1.0. + +## 0.5.8 + +* Remove Android dependencies fallback. +* Require Flutter SDK 1.12.13+hotfix.5 or greater. + +## 0.5.7+5 + +* Replace deprecated `getFlutterEngine` call on Android. + +## 0.5.7+4 + +* Add `pedantic` to dev_dependency. + +## 0.5.7+3 + +* Fix an Android crash when permissions are requested multiple times. + +## 0.5.7+2 + +* Remove the deprecated `author:` field from pubspec.yaml +* Migrate the plugin to the pubspec platforms manifest. +* Require Flutter SDK 1.10.0 or greater. + +## 0.5.7+1 + +* Fix example null exception. + +## 0.5.7 + +* Fix unawaited futures. + +## 0.5.6+4 + +* Android: Use CameraDevice.TEMPLATE_RECORD to improve image streaming. + +## 0.5.6+3 + +* Remove AndroidX warning. + +## 0.5.6+2 + +* Include lifecycle dependency as a compileOnly one on Android to resolve + potential version conflicts with other transitive libraries. + +## 0.5.6+1 + +* Android: Use android.arch.lifecycle instead of androidx.lifecycle:lifecycle in `build.gradle` to support apps that has not been migrated to AndroidX. + +## 0.5.6 + +* Add support for the v2 Android embedding. This shouldn't affect existing + functionality. + +## 0.5.5+1 + +* Fix event type check + +## 0.5.5 + +* Define clang modules for iOS. + +## 0.5.4+3 + +* Update and migrate iOS example project. + +## 0.5.4+2 + +* Fix Android NullPointerException on devices with only front-facing camera. + +## 0.5.4+1 + +* Fix Android pause and resume video crash when executing in APIs below 24. + +## 0.5.4 + +* Add feature to pause and resume video recording. + +## 0.5.3+1 + +* Fix too large request code for FragmentActivity users. + +## 0.5.3 + +* Added new quality presets. +* Now all quality presets can be used to control image capture quality. + +## 0.5.2+2 + +* Fix memory leak related to not unregistering stream handler in FlutterEventChannel when disposing camera. + +## 0.5.2+1 + +* Fix bug that prevented video recording with audio. + +## 0.5.2 + +* Added capability to disable audio for the `CameraController`. (e.g. `CameraController(_, _, + enableAudio: false);`) + +## 0.5.1 + +* Can now be compiled with earlier Android sdks below 21 when +`` has been added to the project +`AndroidManifest.xml`. For sdks below 21, the plugin won't be registered and calls to it will throw +a `MissingPluginException.` + +## 0.5.0 + +* **Breaking Change** This plugin no longer handles closing and opening the camera on Android + lifecycle changes. Please use `WidgetsBindingObserver` to control camera resources on lifecycle + changes. See example project for example using `WidgetsBindingObserver`. + +## 0.4.3+2 + +* Bump the minimum Flutter version to 1.2.0. +* Add template type parameter to `invokeMethod` calls. + +## 0.4.3+1 + +* Catch additional `Exception`s from Android and throw as `CameraException`s. + +## 0.4.3 + +* Add capability to prepare the capture session for video recording on iOS. + +## 0.4.2 + +* Add sensor orientation value to `CameraDescription`. + +## 0.4.1 + +* Camera methods are ran in a background thread on iOS. + +## 0.4.0+3 + +* Fixed a crash when the plugin is registered by a background FlutterView. + +## 0.4.0+2 + +* Fix orientation of captured photos when camera is used for the first time on Android. + +## 0.4.0+1 + +* Remove categories. + +## 0.4.0 + +* **Breaking Change** Change iOS image stream format to `ImageFormatGroup.bgra8888` from + `ImageFormatGroup.yuv420`. + +## 0.3.0+4 + +* Fixed bug causing black screen on some Android devices. + +## 0.3.0+3 + +* Log a more detailed warning at build time about the previous AndroidX + migration. + +## 0.3.0+2 + +* Fix issue with calculating iOS image orientation in certain edge cases. + +## 0.3.0+1 + +* Remove initial method call invocation from static camera method. + +## 0.3.0 + +* **Breaking change**. Migrate from the deprecated original Android Support + Library to AndroidX. This shouldn't result in any functional changes, but it + requires any Android apps using this plugin to [also + migrate](https://developer.android.com/jetpack/androidx/migrate) if they're + using the original support library. + +## 0.2.9+1 + +* Fix a crash when failing to start preview. + +## 0.2.9 + +* Save photo orientation data on iOS. + +## 0.2.8 + +* Add access to the image stream from Dart. +* Use `cameraController.startImageStream(listener)` to process the images. + +## 0.2.7 + +* Fix issue with crash when the physical device's orientation is unknown. + +## 0.2.6 + +* Update the camera to use the physical device's orientation instead of the UI + orientation on Android. + +## 0.2.5 + +* Fix preview and video size with satisfying conditions of multiple outputs. + +## 0.2.4 + +* Unregister the activity lifecycle callbacks when disposing the camera. + +## 0.2.3 + +* Added path_provider and video_player as dev dependencies because the example uses them. +* Updated example path_provider version to get Dart 2 support. + +## 0.2.2 + +* iOS image capture is done in high quality (full camera size) + +## 0.2.1 + +* Updated Gradle tooling to match Android Studio 3.1.2. + +## 0.2.0 + +* Added support for video recording. +* Changed the example app to add video recording. + +A lot of **breaking changes** in this version: + +Getter changes: + - Removed `isStarted` + - Renamed `initialized` to `isInitialized` + - Added `isRecordingVideo` + +Method changes: + - Renamed `capture` to `takePicture` + - Removed `start` (the preview starts automatically when `initialize` is called) + - Added `startVideoRecording(String filePath)` + - Removed `stop` (the preview stops automatically when `dispose` is called) + - Added `stopVideoRecording` + +## 0.1.2 + +* Fix Dart 2 runtime errors. + +## 0.1.1 + +* Fix Dart 2 runtime error. + +## 0.1.0 + +* **Breaking change**. Set SDK constraints to match the Flutter beta release. + +## 0.0.4 + +* Revert regression of `CameraController.capture()` introduced in v. 0.0.3. + +## 0.0.3 + +* Improved resource cleanup on Android. Avoids crash on Activity restart. +* Made the Future returned by `CameraController.dispose()` and `CameraController.capture()` actually complete on + Android. + +## 0.0.2 + +* Simplified and upgraded Android project template to Android SDK 27. +* Moved Android package to io.flutter.plugins. +* Fixed warnings from the Dart 2.0 analyzer. + +## 0.0.1 + +* Initial release diff --git a/packages/camera/camera_android/LICENSE b/packages/camera/camera_android/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/camera/camera_android/LICENSE @@ -0,0 +1,25 @@ +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/camera/camera_android/README.md b/packages/camera/camera_android/README.md new file mode 100644 index 000000000000..ec9d7379c60b --- /dev/null +++ b/packages/camera/camera_android/README.md @@ -0,0 +1,176 @@ +# Camera Plugin + + + +[![pub package](https://img.shields.io/pub/v/camera.svg)](https://pub.dev/packages/camera) + +A Flutter plugin for iOS, Android and Web allowing access to the device cameras. + +| | Android | iOS | Web | +|----------------|---------|----------|------------------------| +| **Support** | SDK 21+ | iOS 10+* | [See `camera_web `][1] | + +## Features + +* Display live camera preview in a widget. +* Snapshots can be captured and saved to a file. +* Record video. +* Add access to the image stream from Dart. + +## Installation + +First, add `camera` as a [dependency in your pubspec.yaml file](https://flutter.dev/using-packages/). + +### iOS + +\* The camera plugin compiles for any version of iOS, but its functionality +requires iOS 10 or higher. If compiling for iOS 9, make sure to programmatically +check the version of iOS running on the device before using any camera plugin features. +The [device_info_plus](https://pub.dev/packages/device_info_plus) plugin, for example, can be used to check the iOS version. + +Add two rows to the `ios/Runner/Info.plist`: + +* one with the key `Privacy - Camera Usage Description` and a usage description. +* and one with the key `Privacy - Microphone Usage Description` and a usage description. + +If editing `Info.plist` as text, add: + +```xml +NSCameraUsageDescription +your usage description here +NSMicrophoneUsageDescription +your usage description here +``` + +### Android + +Change the minimum Android sdk version to 21 (or higher) in your `android/app/build.gradle` file. + +```groovy +minSdkVersion 21 +``` + +It's important to note that the `MediaRecorder` class is not working properly on emulators, as stated in the documentation: https://developer.android.com/reference/android/media/MediaRecorder. Specifically, when recording a video with sound enabled and trying to play it back, the duration won't be correct and you will only see the first frame. + +### Web integration + +For web integration details, see the +[`camera_web` package](https://pub.dev/packages/camera_web). + +### Handling Lifecycle states + +As of version [0.5.0](https://github.com/flutter/plugins/blob/master/packages/camera/CHANGELOG.md#050) of the camera plugin, lifecycle changes are no longer handled by the plugin. This means developers are now responsible to control camera resources when the lifecycle state is updated. Failure to do so might lead to unexpected behavior (for example as described in issue [#39109](https://github.com/flutter/flutter/issues/39109)). Handling lifecycle changes can be done by overriding the `didChangeAppLifecycleState` method like so: + + +```dart +@override +void didChangeAppLifecycleState(AppLifecycleState state) { + final CameraController? cameraController = controller; + + // App state changed before we got the chance to initialize. + if (cameraController == null || !cameraController.value.isInitialized) { + return; + } + + if (state == AppLifecycleState.inactive) { + cameraController.dispose(); + } else if (state == AppLifecycleState.resumed) { + onNewCameraSelected(cameraController.description); + } +} +``` + +### Handling camera access permissions + +Permission errors may be thrown when initializing the camera controller, and you are expected to handle them properly. + +Here is a list of all permission error codes that can be thrown: + +- `CameraAccessDenied`: Thrown when user denies the camera access permission. + +- `CameraAccessDeniedWithoutPrompt`: iOS only for now. Thrown when user has previously denied the permission. iOS does not allow prompting alert dialog a second time. Users will have to go to Settings > Privacy > Camera in order to enable camera access. + +- `CameraAccessRestricted`: iOS only for now. Thrown when camera access is restricted and users cannot grant permission (parental control). + +- `AudioAccessDenied`: Thrown when user denies the audio access permission. + +- `AudioAccessDeniedWithoutPrompt`: iOS only for now. Thrown when user has previously denied the permission. iOS does not allow prompting alert dialog a second time. Users will have to go to Settings > Privacy > Microphone in order to enable audio access. + +- `AudioAccessRestricted`: iOS only for now. Thrown when audio access is restricted and users cannot grant permission (parental control). + +- `cameraPermission`: Android and Web only. A legacy error code for all kinds of camera permission errors. + +### Example + +Here is a small example flutter app displaying a full screen camera preview. + + +```dart +import 'package:camera/camera.dart'; +import 'package:flutter/material.dart'; + +late List _cameras; + +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + + _cameras = await availableCameras(); + runApp(const CameraApp()); +} + +/// CameraApp is the Main Application. +class CameraApp extends StatefulWidget { + /// Default Constructor + const CameraApp({Key? key}) : super(key: key); + + @override + State createState() => _CameraAppState(); +} + +class _CameraAppState extends State { + late CameraController controller; + + @override + void initState() { + super.initState(); + controller = CameraController(_cameras[0], ResolutionPreset.max); + controller.initialize().then((_) { + if (!mounted) { + return; + } + setState(() {}); + }).catchError((Object e) { + if (e is CameraException) { + switch (e.code) { + case 'CameraAccessDenied': + print('User denied camera access.'); + break; + default: + print('Handle other errors.'); + break; + } + } + }); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (!controller.value.isInitialized) { + return Container(); + } + return MaterialApp( + home: CameraPreview(controller), + ); + } +} +``` + +For a more elaborate usage example see [here](https://github.com/flutter/plugins/tree/main/packages/camera/camera/example). + +[1]: https://pub.dev/packages/camera_web#limitations-on-the-web-platform diff --git a/packages/camera/camera/android/build.gradle b/packages/camera/camera_android/android/build.gradle similarity index 100% rename from packages/camera/camera/android/build.gradle rename to packages/camera/camera_android/android/build.gradle diff --git a/packages/camera/camera/android/lint-baseline.xml b/packages/camera/camera_android/android/lint-baseline.xml similarity index 100% rename from packages/camera/camera/android/lint-baseline.xml rename to packages/camera/camera_android/android/lint-baseline.xml diff --git a/packages/camera/camera/android/settings.gradle b/packages/camera/camera_android/android/settings.gradle similarity index 100% rename from packages/camera/camera/android/settings.gradle rename to packages/camera/camera_android/android/settings.gradle diff --git a/packages/camera/camera/android/src/main/AndroidManifest.xml b/packages/camera/camera_android/android/src/main/AndroidManifest.xml similarity index 100% rename from packages/camera/camera/android/src/main/AndroidManifest.xml rename to packages/camera/camera_android/android/src/main/AndroidManifest.xml diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraCaptureCallback.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraCaptureCallback.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraCaptureCallback.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraCaptureCallback.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraPermissions.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPermissions.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraPermissions.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPermissions.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraState.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraState.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraState.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraState.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraUtils.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraUtils.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraUtils.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraUtils.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraZoom.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraZoom.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraZoom.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraZoom.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/DartMessenger.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/DartMessenger.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/DartMessenger.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/DartMessenger.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/ImageSaver.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/ImageSaver.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/ImageSaver.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/ImageSaver.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/CameraFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeature.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/CameraFeature.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeature.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactory.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactory.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactory.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactory.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactoryImpl.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactoryImpl.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactoryImpl.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactoryImpl.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatures.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatures.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatures.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatures.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/Point.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/Point.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/Point.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/Point.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/autofocus/AutoFocusFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/autofocus/AutoFocusFeature.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/autofocus/AutoFocusFeature.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/autofocus/AutoFocusFeature.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/autofocus/FocusMode.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/autofocus/FocusMode.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/autofocus/FocusMode.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/autofocus/FocusMode.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposurelock/ExposureLockFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/exposurelock/ExposureLockFeature.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposurelock/ExposureLockFeature.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/exposurelock/ExposureLockFeature.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposurelock/ExposureMode.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/exposurelock/ExposureMode.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposurelock/ExposureMode.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/exposurelock/ExposureMode.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposureoffset/ExposureOffsetFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/exposureoffset/ExposureOffsetFeature.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposureoffset/ExposureOffsetFeature.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/exposureoffset/ExposureOffsetFeature.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeature.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeature.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeature.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/flash/FlashFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/flash/FlashFeature.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/flash/FlashFeature.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/flash/FlashFeature.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/flash/FlashMode.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/flash/FlashMode.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/flash/FlashMode.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/flash/FlashMode.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeature.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeature.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeature.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeature.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeature.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeature.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionMode.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionMode.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionMode.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionMode.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionPreset.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionPreset.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionPreset.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionPreset.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManager.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManager.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManager.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManager.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/SensorOrientationFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/SensorOrientationFeature.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/SensorOrientationFeature.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/SensorOrientationFeature.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeature.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeature.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeature.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/zoomlevel/ZoomUtils.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/zoomlevel/ZoomUtils.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/zoomlevel/ZoomUtils.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/zoomlevel/ZoomUtils.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/media/MediaRecorderBuilder.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/media/MediaRecorderBuilder.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/media/MediaRecorderBuilder.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/media/MediaRecorderBuilder.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/CameraCaptureProperties.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/CameraCaptureProperties.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/CameraCaptureProperties.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/CameraCaptureProperties.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/CaptureTimeoutsWrapper.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/CaptureTimeoutsWrapper.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/CaptureTimeoutsWrapper.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/CaptureTimeoutsWrapper.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/ExposureMode.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/ExposureMode.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/ExposureMode.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/ExposureMode.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/FlashMode.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/FlashMode.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/FlashMode.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/FlashMode.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/FocusMode.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/FocusMode.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/FocusMode.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/FocusMode.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/ResolutionPreset.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/ResolutionPreset.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/ResolutionPreset.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/ResolutionPreset.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/Timeout.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/Timeout.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/Timeout.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/Timeout.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackStatesTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackStatesTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackStatesTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackStatesTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraPermissionsTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraPermissionsTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraPermissionsTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraPermissionsTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraPropertiesImplTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraPropertiesImplTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraPropertiesImplTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraPropertiesImplTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtils_convertPointToMeteringRectangleTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtils_convertPointToMeteringRectangleTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtils_convertPointToMeteringRectangleTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtils_convertPointToMeteringRectangleTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtils_getCameraBoundariesTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtils_getCameraBoundariesTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtils_getCameraBoundariesTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtils_getCameraBoundariesTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraTest_getRecordingProfileTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest_getRecordingProfileTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraTest_getRecordingProfileTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest_getRecordingProfileTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraUtilsTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraUtilsTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraUtilsTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraUtilsTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraZoomTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraZoomTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraZoomTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraZoomTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/DartMessengerTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/DartMessengerTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/DartMessengerTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/DartMessengerTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/ImageSaverTests.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/ImageSaverTests.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/ImageSaverTests.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/ImageSaverTests.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/MethodCallHandlerImplTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/MethodCallHandlerImplTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/MethodCallHandlerImplTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/MethodCallHandlerImplTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/autofocus/AutoFocusFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/autofocus/AutoFocusFeatureTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/autofocus/AutoFocusFeatureTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/autofocus/AutoFocusFeatureTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/autofocus/FocusModeTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/autofocus/FocusModeTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/autofocus/FocusModeTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/autofocus/FocusModeTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/exposurelock/ExposureLockFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/exposurelock/ExposureLockFeatureTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/exposurelock/ExposureLockFeatureTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/exposurelock/ExposureLockFeatureTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/exposurelock/ExposureModeTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/exposurelock/ExposureModeTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/exposurelock/ExposureModeTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/exposurelock/ExposureModeTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/exposureoffset/ExposureOffsetFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/exposureoffset/ExposureOffsetFeatureTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/exposureoffset/ExposureOffsetFeatureTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/exposureoffset/ExposureOffsetFeatureTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeatureTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeatureTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeatureTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/flash/FlashFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/flash/FlashFeatureTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/flash/FlashFeatureTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/flash/FlashFeatureTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeatureTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeatureTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeatureTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeaturePixel4aTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeaturePixel4aTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeaturePixel4aTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeaturePixel4aTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeatureTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeatureTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeatureTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeatureTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeatureTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeatureTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/resolution/ResolutionFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/resolution/ResolutionFeatureTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/resolution/ResolutionFeatureTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/resolution/ResolutionFeatureTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManagerTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManagerTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManagerTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManagerTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/SensorOrientationFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/SensorOrientationFeatureTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/SensorOrientationFeatureTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/SensorOrientationFeatureTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeatureTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeatureTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeatureTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/zoomlevel/ZoomUtilsTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/zoomlevel/ZoomUtilsTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/zoomlevel/ZoomUtilsTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/zoomlevel/ZoomUtilsTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/media/MediaRecorderBuilderTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/media/MediaRecorderBuilderTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/media/MediaRecorderBuilderTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/media/MediaRecorderBuilderTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/types/ExposureModeTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/types/ExposureModeTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/types/ExposureModeTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/types/ExposureModeTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/types/FlashModeTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/types/FlashModeTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/types/FlashModeTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/types/FlashModeTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/types/FocusModeTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/types/FocusModeTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/types/FocusModeTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/types/FocusModeTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/utils/TestUtils.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/utils/TestUtils.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/utils/TestUtils.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/utils/TestUtils.java diff --git a/packages/camera/camera/android/src/test/resources/robolectric.properties b/packages/camera/camera_android/android/src/test/resources/robolectric.properties similarity index 100% rename from packages/camera/camera/android/src/test/resources/robolectric.properties rename to packages/camera/camera_android/android/src/test/resources/robolectric.properties diff --git a/packages/camera/camera_android/example/android/app/build.gradle b/packages/camera/camera_android/example/android/app/build.gradle new file mode 100644 index 000000000000..476d65373723 --- /dev/null +++ b/packages/camera/camera_android/example/android/app/build.gradle @@ -0,0 +1,64 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 31 + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + applicationId "io.flutter.plugins.cameraexample" + minSdkVersion 21 + targetSdkVersion 28 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + profile { + matchingFallbacks = ['debug', 'release'] + } + } +} + +flutter { + source '../..' +} + +dependencies { + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test:rules:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' +} diff --git a/packages/camera/camera_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/camera/camera_android/example/android/app/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..9a4163a4f5ee --- /dev/null +++ b/packages/camera/camera_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/packages/camera/camera_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java b/packages/camera/camera_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java new file mode 100644 index 000000000000..0f4298dca155 --- /dev/null +++ b/packages/camera/camera_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java @@ -0,0 +1,14 @@ +// 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface DartIntegrationTest {} diff --git a/packages/camera/camera_android/example/android/app/src/androidTest/java/io/flutter/plugins/cameraexample/FlutterActivityTest.java b/packages/camera/camera_android/example/android/app/src/androidTest/java/io/flutter/plugins/cameraexample/FlutterActivityTest.java new file mode 100644 index 000000000000..39cae489d9fa --- /dev/null +++ b/packages/camera/camera_android/example/android/app/src/androidTest/java/io/flutter/plugins/cameraexample/FlutterActivityTest.java @@ -0,0 +1,19 @@ +// 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.cameraexample; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.integration_test.FlutterTestRunner; +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.plugins.DartIntegrationTest; +import org.junit.Rule; +import org.junit.runner.RunWith; + +@DartIntegrationTest +@RunWith(FlutterTestRunner.class) +public class FlutterActivityTest { + @Rule + public ActivityTestRule rule = new ActivityTestRule<>(FlutterActivity.class); +} diff --git a/packages/camera/camera_android/example/android/app/src/main/AndroidManifest.xml b/packages/camera/camera_android/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..cef23162ddb6 --- /dev/null +++ b/packages/camera/camera_android/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + diff --git a/packages/camera/camera_android/example/android/app/src/main/res/drawable/launch_background.xml b/packages/camera/camera_android/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 000000000000..304732f88420 --- /dev/null +++ b/packages/camera/camera_android/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/packages/camera/camera_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/camera/camera_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/packages/camera/camera_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/camera/camera_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/packages/camera/camera_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/camera/camera_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/packages/camera/camera_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/camera/camera_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/packages/camera/camera_android/example/android/app/src/main/res/values/styles.xml b/packages/camera/camera_android/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 000000000000..00fa4417cfbe --- /dev/null +++ b/packages/camera/camera_android/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + diff --git a/packages/camera/camera_android/example/android/build.gradle b/packages/camera/camera_android/example/android/build.gradle new file mode 100644 index 000000000000..456d020f6e2c --- /dev/null +++ b/packages/camera/camera_android/example/android/build.gradle @@ -0,0 +1,29 @@ +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.5.0' + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/packages/camera/camera_android/example/android/gradle.properties b/packages/camera/camera_android/example/android/gradle.properties new file mode 100644 index 000000000000..b253d8e5f746 --- /dev/null +++ b/packages/camera/camera_android/example/android/gradle.properties @@ -0,0 +1,4 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=false +android.enableR8=true diff --git a/packages/camera/camera_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/camera/camera_android/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..01a286e96a21 --- /dev/null +++ b/packages/camera/camera_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip diff --git a/packages/camera/camera_android/example/android/settings.gradle b/packages/camera/camera_android/example/android/settings.gradle new file mode 100644 index 000000000000..115da6cb4f4d --- /dev/null +++ b/packages/camera/camera_android/example/android/settings.gradle @@ -0,0 +1,15 @@ +include ':app' + +def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + +def plugins = new Properties() +def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') +if (pluginsFile.exists()) { + pluginsFile.withInputStream { stream -> plugins.load(stream) } +} + +plugins.each { name, path -> + def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() + include ":$name" + project(":$name").projectDir = pluginDirectory +} diff --git a/packages/camera/camera_android/example/integration_test/camera_test.dart b/packages/camera/camera_android/example/integration_test/camera_test.dart new file mode 100644 index 000000000000..557f4858acab --- /dev/null +++ b/packages/camera/camera_android/example/integration_test/camera_test.dart @@ -0,0 +1,297 @@ +// 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 'dart:async'; +import 'dart:io'; +import 'dart:ui'; + +import 'package:camera/camera.dart'; +import 'package:flutter/painting.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:video_player/video_player.dart'; + +void main() { + late Directory testDir; + + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + setUpAll(() async { + final Directory extDir = await getTemporaryDirectory(); + testDir = await Directory('${extDir.path}/test').create(recursive: true); + }); + + tearDownAll(() async { + await testDir.delete(recursive: true); + }); + + final Map presetExpectedSizes = + { + ResolutionPreset.low: + Platform.isAndroid ? const Size(240, 320) : const Size(288, 352), + ResolutionPreset.medium: + Platform.isAndroid ? const Size(480, 720) : const Size(480, 640), + ResolutionPreset.high: const Size(720, 1280), + ResolutionPreset.veryHigh: const Size(1080, 1920), + ResolutionPreset.ultraHigh: const Size(2160, 3840), + // Don't bother checking for max here since it could be anything. + }; + + /// Verify that [actual] has dimensions that are at least as large as + /// [expectedSize]. Allows for a mismatch in portrait vs landscape. Returns + /// whether the dimensions exactly match. + bool assertExpectedDimensions(Size expectedSize, Size actual) { + expect(actual.shortestSide, lessThanOrEqualTo(expectedSize.shortestSide)); + expect(actual.longestSide, lessThanOrEqualTo(expectedSize.longestSide)); + return actual.shortestSide == expectedSize.shortestSide && + actual.longestSide == expectedSize.longestSide; + } + + // This tests that the capture is no bigger than the preset, since we have + // automatic code to fall back to smaller sizes when we need to. Returns + // whether the image is exactly the desired resolution. + Future testCaptureImageResolution( + CameraController controller, ResolutionPreset preset) async { + final Size expectedSize = presetExpectedSizes[preset]!; + print( + 'Capturing photo at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.description.name}'); + + // Take Picture + final XFile file = await controller.takePicture(); + + // Load picture + final File fileImage = File(file.path); + final Image image = await decodeImageFromList(fileImage.readAsBytesSync()); + + // Verify image dimensions are as expected + expect(image, isNotNull); + return assertExpectedDimensions( + expectedSize, Size(image.height.toDouble(), image.width.toDouble())); + } + + testWidgets( + 'Capture specific image resolutions', + (WidgetTester tester) async { + final List cameras = await availableCameras(); + if (cameras.isEmpty) { + return; + } + for (final CameraDescription cameraDescription in cameras) { + bool previousPresetExactlySupported = true; + for (final MapEntry preset + in presetExpectedSizes.entries) { + final CameraController controller = + CameraController(cameraDescription, preset.key); + await controller.initialize(); + final bool presetExactlySupported = + await testCaptureImageResolution(controller, preset.key); + assert(!(!previousPresetExactlySupported && presetExactlySupported), + 'The camera took higher resolution pictures at a lower resolution.'); + previousPresetExactlySupported = presetExactlySupported; + await controller.dispose(); + } + } + }, + // TODO(egarciad): Fix https://github.com/flutter/flutter/issues/93686. + skip: true, + ); + + // This tests that the capture is no bigger than the preset, since we have + // automatic code to fall back to smaller sizes when we need to. Returns + // whether the image is exactly the desired resolution. + Future testCaptureVideoResolution( + CameraController controller, ResolutionPreset preset) async { + final Size expectedSize = presetExpectedSizes[preset]!; + print( + 'Capturing video at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.description.name}'); + + // Take Video + await controller.startVideoRecording(); + sleep(const Duration(milliseconds: 300)); + final XFile file = await controller.stopVideoRecording(); + + // Load video metadata + final File videoFile = File(file.path); + final VideoPlayerController videoController = + VideoPlayerController.file(videoFile); + await videoController.initialize(); + final Size video = videoController.value.size; + + // Verify image dimensions are as expected + expect(video, isNotNull); + return assertExpectedDimensions( + expectedSize, Size(video.height, video.width)); + } + + testWidgets( + 'Capture specific video resolutions', + (WidgetTester tester) async { + final List cameras = await availableCameras(); + if (cameras.isEmpty) { + return; + } + for (final CameraDescription cameraDescription in cameras) { + bool previousPresetExactlySupported = true; + for (final MapEntry preset + in presetExpectedSizes.entries) { + final CameraController controller = + CameraController(cameraDescription, preset.key); + await controller.initialize(); + await controller.prepareForVideoRecording(); + final bool presetExactlySupported = + await testCaptureVideoResolution(controller, preset.key); + assert(!(!previousPresetExactlySupported && presetExactlySupported), + 'The camera took higher resolution pictures at a lower resolution.'); + previousPresetExactlySupported = presetExactlySupported; + await controller.dispose(); + } + } + }, + // TODO(egarciad): Fix https://github.com/flutter/flutter/issues/93686. + skip: true, + ); + + testWidgets('Pause and resume video recording', (WidgetTester tester) async { + final List cameras = await availableCameras(); + if (cameras.isEmpty) { + return; + } + + final CameraController controller = CameraController( + cameras[0], + ResolutionPreset.low, + enableAudio: false, + ); + + await controller.initialize(); + await controller.prepareForVideoRecording(); + + int startPause; + int timePaused = 0; + + await controller.startVideoRecording(); + final int recordingStart = DateTime.now().millisecondsSinceEpoch; + sleep(const Duration(milliseconds: 500)); + + await controller.pauseVideoRecording(); + startPause = DateTime.now().millisecondsSinceEpoch; + sleep(const Duration(milliseconds: 500)); + await controller.resumeVideoRecording(); + timePaused += DateTime.now().millisecondsSinceEpoch - startPause; + + sleep(const Duration(milliseconds: 500)); + + await controller.pauseVideoRecording(); + startPause = DateTime.now().millisecondsSinceEpoch; + sleep(const Duration(milliseconds: 500)); + await controller.resumeVideoRecording(); + timePaused += DateTime.now().millisecondsSinceEpoch - startPause; + + sleep(const Duration(milliseconds: 500)); + + final XFile file = await controller.stopVideoRecording(); + final int recordingTime = + DateTime.now().millisecondsSinceEpoch - recordingStart; + + final File videoFile = File(file.path); + final VideoPlayerController videoController = VideoPlayerController.file( + videoFile, + ); + await videoController.initialize(); + final int duration = videoController.value.duration.inMilliseconds; + await videoController.dispose(); + + expect(duration, lessThan(recordingTime - timePaused)); + }, skip: !Platform.isAndroid); + + testWidgets( + 'Android image streaming', + (WidgetTester tester) async { + final List cameras = await availableCameras(); + if (cameras.isEmpty) { + return; + } + + final CameraController controller = CameraController( + cameras[0], + ResolutionPreset.low, + enableAudio: false, + ); + + await controller.initialize(); + bool _isDetecting = false; + + await controller.startImageStream((CameraImage image) { + if (_isDetecting) { + return; + } + + _isDetecting = true; + + expectLater(image, isNotNull).whenComplete(() => _isDetecting = false); + }); + + expect(controller.value.isStreamingImages, true); + + sleep(const Duration(milliseconds: 500)); + + await controller.stopImageStream(); + await controller.dispose(); + }, + skip: !Platform.isAndroid, + ); + + /// Start streaming with specifying the ImageFormatGroup. + Future startStreaming(List cameras, + ImageFormatGroup? imageFormatGroup) async { + final CameraController controller = CameraController( + cameras.first, + ResolutionPreset.low, + enableAudio: false, + imageFormatGroup: imageFormatGroup, + ); + + await controller.initialize(); + final Completer _completer = Completer(); + + await controller.startImageStream((CameraImage image) { + if (!_completer.isCompleted) { + Future(() async { + await controller.stopImageStream(); + await controller.dispose(); + }).then((Object? value) { + _completer.complete(image); + }); + } + }); + return _completer.future; + } + + testWidgets( + 'iOS image streaming with imageFormatGroup', + (WidgetTester tester) async { + final List cameras = await availableCameras(); + if (cameras.isEmpty) { + return; + } + + CameraImage _image = await startStreaming(cameras, null); + expect(_image, isNotNull); + expect(_image.format.group, ImageFormatGroup.bgra8888); + expect(_image.planes.length, 1); + + _image = await startStreaming(cameras, ImageFormatGroup.yuv420); + expect(_image, isNotNull); + expect(_image.format.group, ImageFormatGroup.yuv420); + expect(_image.planes.length, 2); + + _image = await startStreaming(cameras, ImageFormatGroup.bgra8888); + expect(_image, isNotNull); + expect(_image.format.group, ImageFormatGroup.bgra8888); + expect(_image.planes.length, 1); + }, + skip: !Platform.isIOS, + ); +} diff --git a/packages/camera/camera_android/example/lib/main.dart b/packages/camera/camera_android/example/lib/main.dart new file mode 100644 index 000000000000..c0181a5d36a1 --- /dev/null +++ b/packages/camera/camera_android/example/lib/main.dart @@ -0,0 +1,1091 @@ +// 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 'dart:async'; +import 'dart:io'; + +import 'package:camera/camera.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:video_player/video_player.dart'; + +/// Camera example home widget. +class CameraExampleHome extends StatefulWidget { + /// Default Constructor + const CameraExampleHome({Key? key}) : super(key: key); + + @override + State createState() { + return _CameraExampleHomeState(); + } +} + +/// Returns a suitable camera icon for [direction]. +IconData getCameraLensIcon(CameraLensDirection direction) { + switch (direction) { + case CameraLensDirection.back: + return Icons.camera_rear; + case CameraLensDirection.front: + return Icons.camera_front; + case CameraLensDirection.external: + return Icons.camera; + default: + throw ArgumentError('Unknown lens direction'); + } +} + +void _logError(String code, String? message) { + if (message != null) { + print('Error: $code\nError Message: $message'); + } else { + print('Error: $code'); + } +} + +class _CameraExampleHomeState extends State + with WidgetsBindingObserver, TickerProviderStateMixin { + CameraController? controller; + XFile? imageFile; + XFile? videoFile; + VideoPlayerController? videoController; + VoidCallback? videoPlayerListener; + bool enableAudio = true; + double _minAvailableExposureOffset = 0.0; + double _maxAvailableExposureOffset = 0.0; + double _currentExposureOffset = 0.0; + late AnimationController _flashModeControlRowAnimationController; + late Animation _flashModeControlRowAnimation; + late AnimationController _exposureModeControlRowAnimationController; + late Animation _exposureModeControlRowAnimation; + late AnimationController _focusModeControlRowAnimationController; + late Animation _focusModeControlRowAnimation; + double _minAvailableZoom = 1.0; + double _maxAvailableZoom = 1.0; + double _currentScale = 1.0; + double _baseScale = 1.0; + + // Counting pointers (number of user fingers on screen) + int _pointers = 0; + + @override + void initState() { + super.initState(); + _ambiguate(WidgetsBinding.instance)?.addObserver(this); + + _flashModeControlRowAnimationController = AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + ); + _flashModeControlRowAnimation = CurvedAnimation( + parent: _flashModeControlRowAnimationController, + curve: Curves.easeInCubic, + ); + _exposureModeControlRowAnimationController = AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + ); + _exposureModeControlRowAnimation = CurvedAnimation( + parent: _exposureModeControlRowAnimationController, + curve: Curves.easeInCubic, + ); + _focusModeControlRowAnimationController = AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + ); + _focusModeControlRowAnimation = CurvedAnimation( + parent: _focusModeControlRowAnimationController, + curve: Curves.easeInCubic, + ); + } + + @override + void dispose() { + _ambiguate(WidgetsBinding.instance)?.removeObserver(this); + _flashModeControlRowAnimationController.dispose(); + _exposureModeControlRowAnimationController.dispose(); + super.dispose(); + } + + // #docregion AppLifecycle + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + final CameraController? cameraController = controller; + + // App state changed before we got the chance to initialize. + if (cameraController == null || !cameraController.value.isInitialized) { + return; + } + + if (state == AppLifecycleState.inactive) { + cameraController.dispose(); + } else if (state == AppLifecycleState.resumed) { + onNewCameraSelected(cameraController.description); + } + } + // #enddocregion AppLifecycle + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Camera example'), + ), + body: Column( + children: [ + Expanded( + child: Container( + decoration: BoxDecoration( + color: Colors.black, + border: Border.all( + color: + controller != null && controller!.value.isRecordingVideo + ? Colors.redAccent + : Colors.grey, + width: 3.0, + ), + ), + child: Padding( + padding: const EdgeInsets.all(1.0), + child: Center( + child: _cameraPreviewWidget(), + ), + ), + ), + ), + _captureControlRowWidget(), + _modeControlRowWidget(), + Padding( + padding: const EdgeInsets.all(5.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + _cameraTogglesRowWidget(), + _thumbnailWidget(), + ], + ), + ), + ], + ), + ); + } + + /// Display the preview from the camera (or a message if the preview is not available). + Widget _cameraPreviewWidget() { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isInitialized) { + return const Text( + 'Tap a camera', + style: TextStyle( + color: Colors.white, + fontSize: 24.0, + fontWeight: FontWeight.w900, + ), + ); + } else { + return Listener( + onPointerDown: (_) => _pointers++, + onPointerUp: (_) => _pointers--, + child: CameraPreview( + controller!, + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onScaleStart: _handleScaleStart, + onScaleUpdate: _handleScaleUpdate, + onTapDown: (TapDownDetails details) => + onViewFinderTap(details, constraints), + ); + }), + ), + ); + } + } + + void _handleScaleStart(ScaleStartDetails details) { + _baseScale = _currentScale; + } + + Future _handleScaleUpdate(ScaleUpdateDetails details) async { + // When there are not exactly two fingers on screen don't scale + if (controller == null || _pointers != 2) { + return; + } + + _currentScale = (_baseScale * details.scale) + .clamp(_minAvailableZoom, _maxAvailableZoom); + + await controller!.setZoomLevel(_currentScale); + } + + /// Display the thumbnail of the captured image or video. + Widget _thumbnailWidget() { + final VideoPlayerController? localVideoController = videoController; + + return Expanded( + child: Align( + alignment: Alignment.centerRight, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (localVideoController == null && imageFile == null) + Container() + else + SizedBox( + width: 64.0, + height: 64.0, + child: (localVideoController == null) + ? ( + // The captured image on the web contains a network-accessible URL + // pointing to a location within the browser. It may be displayed + // either with Image.network or Image.memory after loading the image + // bytes to memory. + kIsWeb + ? Image.network(imageFile!.path) + : Image.file(File(imageFile!.path))) + : Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.pink)), + child: Center( + child: AspectRatio( + aspectRatio: + localVideoController.value.size != null + ? localVideoController.value.aspectRatio + : 1.0, + child: VideoPlayer(localVideoController)), + ), + ), + ), + ], + ), + ), + ); + } + + /// Display a bar with buttons to change the flash and exposure modes + Widget _modeControlRowWidget() { + return Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + IconButton( + icon: const Icon(Icons.flash_on), + color: Colors.blue, + onPressed: controller != null ? onFlashModeButtonPressed : null, + ), + // The exposure and focus mode are currently not supported on the web. + ...!kIsWeb + ? [ + IconButton( + icon: const Icon(Icons.exposure), + color: Colors.blue, + onPressed: controller != null + ? onExposureModeButtonPressed + : null, + ), + IconButton( + icon: const Icon(Icons.filter_center_focus), + color: Colors.blue, + onPressed: + controller != null ? onFocusModeButtonPressed : null, + ) + ] + : [], + IconButton( + icon: Icon(enableAudio ? Icons.volume_up : Icons.volume_mute), + color: Colors.blue, + onPressed: controller != null ? onAudioModeButtonPressed : null, + ), + IconButton( + icon: Icon(controller?.value.isCaptureOrientationLocked ?? false + ? Icons.screen_lock_rotation + : Icons.screen_rotation), + color: Colors.blue, + onPressed: controller != null + ? onCaptureOrientationLockButtonPressed + : null, + ), + ], + ), + _flashModeControlRowWidget(), + _exposureModeControlRowWidget(), + _focusModeControlRowWidget(), + ], + ); + } + + Widget _flashModeControlRowWidget() { + return SizeTransition( + sizeFactor: _flashModeControlRowAnimation, + child: ClipRect( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + IconButton( + icon: const Icon(Icons.flash_off), + color: controller?.value.flashMode == FlashMode.off + ? Colors.orange + : Colors.blue, + onPressed: controller != null + ? () => onSetFlashModeButtonPressed(FlashMode.off) + : null, + ), + IconButton( + icon: const Icon(Icons.flash_auto), + color: controller?.value.flashMode == FlashMode.auto + ? Colors.orange + : Colors.blue, + onPressed: controller != null + ? () => onSetFlashModeButtonPressed(FlashMode.auto) + : null, + ), + IconButton( + icon: const Icon(Icons.flash_on), + color: controller?.value.flashMode == FlashMode.always + ? Colors.orange + : Colors.blue, + onPressed: controller != null + ? () => onSetFlashModeButtonPressed(FlashMode.always) + : null, + ), + IconButton( + icon: const Icon(Icons.highlight), + color: controller?.value.flashMode == FlashMode.torch + ? Colors.orange + : Colors.blue, + onPressed: controller != null + ? () => onSetFlashModeButtonPressed(FlashMode.torch) + : null, + ), + ], + ), + ), + ); + } + + Widget _exposureModeControlRowWidget() { + final ButtonStyle styleAuto = TextButton.styleFrom( + primary: controller?.value.exposureMode == ExposureMode.auto + ? Colors.orange + : Colors.blue, + ); + final ButtonStyle styleLocked = TextButton.styleFrom( + primary: controller?.value.exposureMode == ExposureMode.locked + ? Colors.orange + : Colors.blue, + ); + + return SizeTransition( + sizeFactor: _exposureModeControlRowAnimation, + child: ClipRect( + child: Container( + color: Colors.grey.shade50, + child: Column( + children: [ + const Center( + child: Text('Exposure Mode'), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + TextButton( + style: styleAuto, + onPressed: controller != null + ? () => + onSetExposureModeButtonPressed(ExposureMode.auto) + : null, + onLongPress: () { + if (controller != null) { + controller!.setExposurePoint(null); + showInSnackBar('Resetting exposure point'); + } + }, + child: const Text('AUTO'), + ), + TextButton( + style: styleLocked, + onPressed: controller != null + ? () => + onSetExposureModeButtonPressed(ExposureMode.locked) + : null, + child: const Text('LOCKED'), + ), + TextButton( + style: styleLocked, + onPressed: controller != null + ? () => controller!.setExposureOffset(0.0) + : null, + child: const Text('RESET OFFSET'), + ), + ], + ), + const Center( + child: Text('Exposure Offset'), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + Text(_minAvailableExposureOffset.toString()), + Slider( + value: _currentExposureOffset, + min: _minAvailableExposureOffset, + max: _maxAvailableExposureOffset, + label: _currentExposureOffset.toString(), + onChanged: _minAvailableExposureOffset == + _maxAvailableExposureOffset + ? null + : setExposureOffset, + ), + Text(_maxAvailableExposureOffset.toString()), + ], + ), + ], + ), + ), + ), + ); + } + + Widget _focusModeControlRowWidget() { + final ButtonStyle styleAuto = TextButton.styleFrom( + primary: controller?.value.focusMode == FocusMode.auto + ? Colors.orange + : Colors.blue, + ); + final ButtonStyle styleLocked = TextButton.styleFrom( + primary: controller?.value.focusMode == FocusMode.locked + ? Colors.orange + : Colors.blue, + ); + + return SizeTransition( + sizeFactor: _focusModeControlRowAnimation, + child: ClipRect( + child: Container( + color: Colors.grey.shade50, + child: Column( + children: [ + const Center( + child: Text('Focus Mode'), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + TextButton( + style: styleAuto, + onPressed: controller != null + ? () => onSetFocusModeButtonPressed(FocusMode.auto) + : null, + onLongPress: () { + if (controller != null) { + controller!.setFocusPoint(null); + } + showInSnackBar('Resetting focus point'); + }, + child: const Text('AUTO'), + ), + TextButton( + style: styleLocked, + onPressed: controller != null + ? () => onSetFocusModeButtonPressed(FocusMode.locked) + : null, + child: const Text('LOCKED'), + ), + ], + ), + ], + ), + ), + ), + ); + } + + /// Display the control bar with buttons to take pictures and record videos. + Widget _captureControlRowWidget() { + final CameraController? cameraController = controller; + + return Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + IconButton( + icon: const Icon(Icons.camera_alt), + color: Colors.blue, + onPressed: cameraController != null && + cameraController.value.isInitialized && + !cameraController.value.isRecordingVideo + ? onTakePictureButtonPressed + : null, + ), + IconButton( + icon: const Icon(Icons.videocam), + color: Colors.blue, + onPressed: cameraController != null && + cameraController.value.isInitialized && + !cameraController.value.isRecordingVideo + ? onVideoRecordButtonPressed + : null, + ), + IconButton( + icon: cameraController != null && + cameraController.value.isRecordingPaused + ? const Icon(Icons.play_arrow) + : const Icon(Icons.pause), + color: Colors.blue, + onPressed: cameraController != null && + cameraController.value.isInitialized && + cameraController.value.isRecordingVideo + ? (cameraController.value.isRecordingPaused) + ? onResumeButtonPressed + : onPauseButtonPressed + : null, + ), + IconButton( + icon: const Icon(Icons.stop), + color: Colors.red, + onPressed: cameraController != null && + cameraController.value.isInitialized && + cameraController.value.isRecordingVideo + ? onStopButtonPressed + : null, + ), + IconButton( + icon: const Icon(Icons.pause_presentation), + color: + cameraController != null && cameraController.value.isPreviewPaused + ? Colors.red + : Colors.blue, + onPressed: + cameraController == null ? null : onPausePreviewButtonPressed, + ), + ], + ); + } + + /// Display a row of toggle to select the camera (or a message if no camera is available). + Widget _cameraTogglesRowWidget() { + final List toggles = []; + + void onChanged(CameraDescription? description) { + if (description == null) { + return; + } + + onNewCameraSelected(description); + } + + if (_cameras.isEmpty) { + _ambiguate(SchedulerBinding.instance)?.addPostFrameCallback((_) async { + showInSnackBar('No camera found.'); + }); + return const Text('None'); + } else { + for (final CameraDescription cameraDescription in _cameras) { + toggles.add( + SizedBox( + width: 90.0, + child: RadioListTile( + title: Icon(getCameraLensIcon(cameraDescription.lensDirection)), + groupValue: controller?.description, + value: cameraDescription, + onChanged: + controller != null && controller!.value.isRecordingVideo + ? null + : onChanged, + ), + ), + ); + } + } + + return Row(children: toggles); + } + + String timestamp() => DateTime.now().millisecondsSinceEpoch.toString(); + + void showInSnackBar(String message) { + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text(message))); + } + + void onViewFinderTap(TapDownDetails details, BoxConstraints constraints) { + if (controller == null) { + return; + } + + final CameraController cameraController = controller!; + + final Offset offset = Offset( + details.localPosition.dx / constraints.maxWidth, + details.localPosition.dy / constraints.maxHeight, + ); + cameraController.setExposurePoint(offset); + cameraController.setFocusPoint(offset); + } + + Future onNewCameraSelected(CameraDescription cameraDescription) async { + final CameraController? oldController = controller; + if (oldController != null) { + // `controller` needs to be set to null before getting disposed, + // to avoid a race condition when we use the controller that is being + // disposed. This happens when camera permission dialog shows up, + // which triggers `didChangeAppLifecycleState`, which disposes and + // re-creates the controller. + controller = null; + await oldController.dispose(); + } + + final CameraController cameraController = CameraController( + cameraDescription, + kIsWeb ? ResolutionPreset.max : ResolutionPreset.medium, + enableAudio: enableAudio, + imageFormatGroup: ImageFormatGroup.jpeg, + ); + + controller = cameraController; + + // If the controller is updated then update the UI. + cameraController.addListener(() { + if (mounted) { + setState(() {}); + } + if (cameraController.value.hasError) { + showInSnackBar( + 'Camera error ${cameraController.value.errorDescription}'); + } + }); + + try { + await cameraController.initialize(); + await Future.wait(>[ + // The exposure mode is currently not supported on the web. + ...!kIsWeb + ? >[ + cameraController.getMinExposureOffset().then( + (double value) => _minAvailableExposureOffset = value), + cameraController + .getMaxExposureOffset() + .then((double value) => _maxAvailableExposureOffset = value) + ] + : >[], + cameraController + .getMaxZoomLevel() + .then((double value) => _maxAvailableZoom = value), + cameraController + .getMinZoomLevel() + .then((double value) => _minAvailableZoom = value), + ]); + } on CameraException catch (e) { + switch (e.code) { + case 'CameraAccessDenied': + showInSnackBar('You have denied camera access.'); + break; + case 'CameraAccessDeniedWithoutPrompt': + // iOS only + showInSnackBar('Please go to Settings app to enable camera access.'); + break; + case 'CameraAccessRestricted': + // iOS only + showInSnackBar('Camera access is restricted.'); + break; + case 'AudioAccessDenied': + showInSnackBar('You have denied audio access.'); + break; + case 'AudioAccessDeniedWithoutPrompt': + // iOS only + showInSnackBar('Please go to Settings app to enable audio access.'); + break; + case 'AudioAccessRestricted': + // iOS only + showInSnackBar('Audio access is restricted.'); + break; + case 'cameraPermission': + // Android & web only + showInSnackBar('Unknown permission error.'); + break; + default: + _showCameraException(e); + break; + } + } + + if (mounted) { + setState(() {}); + } + } + + void onTakePictureButtonPressed() { + takePicture().then((XFile? file) { + if (mounted) { + setState(() { + imageFile = file; + videoController?.dispose(); + videoController = null; + }); + if (file != null) { + showInSnackBar('Picture saved to ${file.path}'); + } + } + }); + } + + void onFlashModeButtonPressed() { + if (_flashModeControlRowAnimationController.value == 1) { + _flashModeControlRowAnimationController.reverse(); + } else { + _flashModeControlRowAnimationController.forward(); + _exposureModeControlRowAnimationController.reverse(); + _focusModeControlRowAnimationController.reverse(); + } + } + + void onExposureModeButtonPressed() { + if (_exposureModeControlRowAnimationController.value == 1) { + _exposureModeControlRowAnimationController.reverse(); + } else { + _exposureModeControlRowAnimationController.forward(); + _flashModeControlRowAnimationController.reverse(); + _focusModeControlRowAnimationController.reverse(); + } + } + + void onFocusModeButtonPressed() { + if (_focusModeControlRowAnimationController.value == 1) { + _focusModeControlRowAnimationController.reverse(); + } else { + _focusModeControlRowAnimationController.forward(); + _flashModeControlRowAnimationController.reverse(); + _exposureModeControlRowAnimationController.reverse(); + } + } + + void onAudioModeButtonPressed() { + enableAudio = !enableAudio; + if (controller != null) { + onNewCameraSelected(controller!.description); + } + } + + Future onCaptureOrientationLockButtonPressed() async { + try { + if (controller != null) { + final CameraController cameraController = controller!; + if (cameraController.value.isCaptureOrientationLocked) { + await cameraController.unlockCaptureOrientation(); + showInSnackBar('Capture orientation unlocked'); + } else { + await cameraController.lockCaptureOrientation(); + showInSnackBar( + 'Capture orientation locked to ${cameraController.value.lockedCaptureOrientation.toString().split('.').last}'); + } + } + } on CameraException catch (e) { + _showCameraException(e); + } + } + + void onSetFlashModeButtonPressed(FlashMode mode) { + setFlashMode(mode).then((_) { + if (mounted) { + setState(() {}); + } + showInSnackBar('Flash mode set to ${mode.toString().split('.').last}'); + }); + } + + void onSetExposureModeButtonPressed(ExposureMode mode) { + setExposureMode(mode).then((_) { + if (mounted) { + setState(() {}); + } + showInSnackBar('Exposure mode set to ${mode.toString().split('.').last}'); + }); + } + + void onSetFocusModeButtonPressed(FocusMode mode) { + setFocusMode(mode).then((_) { + if (mounted) { + setState(() {}); + } + showInSnackBar('Focus mode set to ${mode.toString().split('.').last}'); + }); + } + + void onVideoRecordButtonPressed() { + startVideoRecording().then((_) { + if (mounted) { + setState(() {}); + } + }); + } + + void onStopButtonPressed() { + stopVideoRecording().then((XFile? file) { + if (mounted) { + setState(() {}); + } + if (file != null) { + showInSnackBar('Video recorded to ${file.path}'); + videoFile = file; + _startVideoPlayer(); + } + }); + } + + Future onPausePreviewButtonPressed() async { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isInitialized) { + showInSnackBar('Error: select a camera first.'); + return; + } + + if (cameraController.value.isPreviewPaused) { + await cameraController.resumePreview(); + } else { + await cameraController.pausePreview(); + } + + if (mounted) { + setState(() {}); + } + } + + void onPauseButtonPressed() { + pauseVideoRecording().then((_) { + if (mounted) { + setState(() {}); + } + showInSnackBar('Video recording paused'); + }); + } + + void onResumeButtonPressed() { + resumeVideoRecording().then((_) { + if (mounted) { + setState(() {}); + } + showInSnackBar('Video recording resumed'); + }); + } + + Future startVideoRecording() async { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isInitialized) { + showInSnackBar('Error: select a camera first.'); + return; + } + + if (cameraController.value.isRecordingVideo) { + // A recording is already started, do nothing. + return; + } + + try { + await cameraController.startVideoRecording(); + } on CameraException catch (e) { + _showCameraException(e); + return; + } + } + + Future stopVideoRecording() async { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isRecordingVideo) { + return null; + } + + try { + return cameraController.stopVideoRecording(); + } on CameraException catch (e) { + _showCameraException(e); + return null; + } + } + + Future pauseVideoRecording() async { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isRecordingVideo) { + return; + } + + try { + await cameraController.pauseVideoRecording(); + } on CameraException catch (e) { + _showCameraException(e); + rethrow; + } + } + + Future resumeVideoRecording() async { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isRecordingVideo) { + return; + } + + try { + await cameraController.resumeVideoRecording(); + } on CameraException catch (e) { + _showCameraException(e); + rethrow; + } + } + + Future setFlashMode(FlashMode mode) async { + if (controller == null) { + return; + } + + try { + await controller!.setFlashMode(mode); + } on CameraException catch (e) { + _showCameraException(e); + rethrow; + } + } + + Future setExposureMode(ExposureMode mode) async { + if (controller == null) { + return; + } + + try { + await controller!.setExposureMode(mode); + } on CameraException catch (e) { + _showCameraException(e); + rethrow; + } + } + + Future setExposureOffset(double offset) async { + if (controller == null) { + return; + } + + setState(() { + _currentExposureOffset = offset; + }); + try { + offset = await controller!.setExposureOffset(offset); + } on CameraException catch (e) { + _showCameraException(e); + rethrow; + } + } + + Future setFocusMode(FocusMode mode) async { + if (controller == null) { + return; + } + + try { + await controller!.setFocusMode(mode); + } on CameraException catch (e) { + _showCameraException(e); + rethrow; + } + } + + Future _startVideoPlayer() async { + if (videoFile == null) { + return; + } + + final VideoPlayerController vController = kIsWeb + ? VideoPlayerController.network(videoFile!.path) + : VideoPlayerController.file(File(videoFile!.path)); + + videoPlayerListener = () { + if (videoController != null && videoController!.value.size != null) { + // Refreshing the state to update video player with the correct ratio. + if (mounted) { + setState(() {}); + } + videoController!.removeListener(videoPlayerListener!); + } + }; + vController.addListener(videoPlayerListener!); + await vController.setLooping(true); + await vController.initialize(); + await videoController?.dispose(); + if (mounted) { + setState(() { + imageFile = null; + videoController = vController; + }); + } + await vController.play(); + } + + Future takePicture() async { + final CameraController? cameraController = controller; + if (cameraController == null || !cameraController.value.isInitialized) { + showInSnackBar('Error: select a camera first.'); + return null; + } + + if (cameraController.value.isTakingPicture) { + // A capture is already pending, do nothing. + return null; + } + + try { + final XFile file = await cameraController.takePicture(); + return file; + } on CameraException catch (e) { + _showCameraException(e); + return null; + } + } + + void _showCameraException(CameraException e) { + _logError(e.code, e.description); + showInSnackBar('Error: ${e.code}\n${e.description}'); + } +} + +/// CameraApp is the Main Application. +class CameraApp extends StatelessWidget { + /// Default Constructor + const CameraApp({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return const MaterialApp( + home: CameraExampleHome(), + ); + } +} + +List _cameras = []; + +Future main() async { + // Fetch the available cameras before initializing the app. + try { + WidgetsFlutterBinding.ensureInitialized(); + _cameras = await availableCameras(); + } on CameraException catch (e) { + _logError(e.code, e.description); + } + runApp(const CameraApp()); +} + +/// This allows a value of type T or T? to be treated as a value of type T?. +/// +/// We use this so that APIs that have become non-nullable can still be used +/// with `!` and `?` on the stable branch. +// TODO(ianh): Remove this once we roll stable in late 2021. +T? _ambiguate(T? value) => value; diff --git a/packages/camera/camera_android/example/pubspec.yaml b/packages/camera/camera_android/example/pubspec.yaml new file mode 100644 index 000000000000..e9ae2c74a6be --- /dev/null +++ b/packages/camera/camera_android/example/pubspec.yaml @@ -0,0 +1,32 @@ +name: camera_example +description: Demonstrates how to use the camera plugin. +publish_to: none + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.8.0" + +dependencies: + camera: + # When depending on this package from a real application you should use: + # camera: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + flutter: + sdk: flutter + path_provider: ^2.0.0 + video_player: ^2.1.4 + +dev_dependencies: + build_runner: ^2.1.10 + flutter_driver: + sdk: flutter + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/camera/camera_android/example/test_driver/integration_test.dart b/packages/camera/camera_android/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4ec97e66d36c --- /dev/null +++ b/packages/camera/camera_android/example/test_driver/integration_test.dart @@ -0,0 +1,64 @@ +// 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 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:flutter_driver/flutter_driver.dart'; + +const String _examplePackage = 'io.flutter.plugins.cameraexample'; + +Future main() async { + if (!(Platform.isLinux || Platform.isMacOS)) { + print('This test must be run on a POSIX host. Skipping...'); + exit(0); + } + final bool adbExists = + Process.runSync('which', ['adb']).exitCode == 0; + if (!adbExists) { + print(r'This test needs ADB to exist on the $PATH. Skipping...'); + exit(0); + } + print('Granting camera permissions...'); + Process.runSync('adb', [ + 'shell', + 'pm', + 'grant', + _examplePackage, + 'android.permission.CAMERA' + ]); + Process.runSync('adb', [ + 'shell', + 'pm', + 'grant', + _examplePackage, + 'android.permission.RECORD_AUDIO' + ]); + print('Starting test.'); + final FlutterDriver driver = await FlutterDriver.connect(); + final String data = await driver.requestData( + null, + timeout: const Duration(minutes: 1), + ); + await driver.close(); + print('Test finished. Revoking camera permissions...'); + Process.runSync('adb', [ + 'shell', + 'pm', + 'revoke', + _examplePackage, + 'android.permission.CAMERA' + ]); + Process.runSync('adb', [ + 'shell', + 'pm', + 'revoke', + _examplePackage, + 'android.permission.RECORD_AUDIO' + ]); + + final Map result = jsonDecode(data) as Map; + exit(result['result'] == 'true' ? 0 : 1); +} diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml new file mode 100644 index 000000000000..d1f70d906626 --- /dev/null +++ b/packages/camera/camera_android/pubspec.yaml @@ -0,0 +1,39 @@ +name: camera +description: A Flutter plugin for controlling the camera. Supports previewing + the camera feed, capturing images and video, and streaming image buffers to + Dart. +repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 +version: 0.9.7+1 + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.8.0" + +flutter: + plugin: + platforms: + android: + package: io.flutter.plugins.camera + pluginClass: CameraPlugin + ios: + pluginClass: CameraPlugin + web: + default_package: camera_web + +dependencies: + camera_platform_interface: ^2.2.0 + camera_web: ^0.2.1 + flutter: + sdk: flutter + flutter_plugin_android_lifecycle: ^2.0.2 + quiver: ^3.0.0 + +dev_dependencies: + flutter_driver: + sdk: flutter + flutter_test: + sdk: flutter + mockito: ^5.0.0 + plugin_platform_interface: ^2.0.0 + video_player: ^2.0.0 diff --git a/packages/camera/camera_avfoundation/AUTHORS b/packages/camera/camera_avfoundation/AUTHORS new file mode 100644 index 000000000000..493a0b4ef9c2 --- /dev/null +++ b/packages/camera/camera_avfoundation/AUTHORS @@ -0,0 +1,66 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md new file mode 100644 index 000000000000..72af38a9f9de --- /dev/null +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -0,0 +1,665 @@ +## 0.9.7+1 + +* Moves streaming implementation to the platform interface package. + +## 0.9.7 + +* Returns all the available cameras on iOS. + +## 0.9.6 + +* Adds audio access permission handling logic on iOS to fix an issue with `prepareForVideoRecording` not awaiting for the audio permission request result. + +## 0.9.5+1 + +* Suppresses warnings for pre-iOS-11 codepaths. + +## 0.9.5 + +* Adds camera access permission handling logic on iOS to fix a related crash when using the camera for the first time. + +## 0.9.4+24 + +* Fixes preview orientation when pausing preview with locked orientation. + +## 0.9.4+23 + +* Minor fixes for new analysis options. + +## 0.9.4+22 + +* Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + +## 0.9.4+21 + +* Fixes README code samples. + +## 0.9.4+20 + +* Fixes an issue with the orientation of videos recorded in landscape on Android. + +## 0.9.4+19 + +* Migrate deprecated Scaffold SnackBar methods to ScaffoldMessenger. + +## 0.9.4+18 + +* Fixes a crash in iOS when streaming on low-performance devices. + +## 0.9.4+17 + +* Removes obsolete information from README, and adds OS support table. + +## 0.9.4+16 + +* Fixes a bug resulting in a `CameraAccessException` that prevents image + capture on some Android devices. + +## 0.9.4+15 + +* Uses dispatch queue for pixel buffer synchronization on iOS. +* Minor iOS internal code cleanup related to queue helper functions. + +## 0.9.4+14 + +* Restores compatibility with Flutter 2.5 and 2.8. + +## 0.9.4+13 + +* Updates iOS camera's photo capture delegate reference on a background queue to prevent potential race conditions, and some related internal code cleanup. + +## 0.9.4+12 + +* Skips unnecessary AppDelegate setup for unit tests on iOS. +* Internal code cleanup for stricter analysis options. + +## 0.9.4+11 + +* Manages iOS camera's orientation-related states on a background queue to prevent potential race conditions. + +## 0.9.4+10 + +* iOS performance improvement by moving file writing from the main queue to a background IO queue. + +## 0.9.4+9 + +* iOS performance improvement by moving sample buffer handling from the main queue to a background session queue. +* Minor iOS internal code cleanup related to camera class and its delegate. +* Minor iOS internal code cleanup related to resolution preset, video format, focus mode, exposure mode and device orientation. +* Minor iOS internal code cleanup related to flash mode. + +## 0.9.4+8 + +* Fixes a bug where ImageFormatGroup was ignored in `startImageStream` on iOS. + +## 0.9.4+7 + +* Fixes a crash in iOS when passing null queue pointer into AVFoundation API due to race condition. +* Minor iOS internal code cleanup related to dispatch queue. + +## 0.9.4+6 + +* Fixes a crash in iOS when using image stream due to calling Flutter engine API on non-main thread. + +## 0.9.4+5 + +* Fixes bug where calling a method after the camera was closed resulted in a Java `IllegalStateException` exception. +* Fixes integration tests. + +## 0.9.4+4 + +* Change Android compileSdkVersion to 31. +* Remove usages of deprecated Android API `CamcorderProfile`. +* Update gradle version to 7.0.2 on Android. + +## 0.9.4+3 + +* Fix registerTexture and result being called on background thread on iOS. + +## 0.9.4+2 + +* Updated package description; +* Refactor unit test on iOS to make it compatible with new restrictions in Xcode 13 which only supports the use of the `XCUIDevice` in Xcode UI tests. + +## 0.9.4+1 + +* Fixed Android implementation throwing IllegalStateException when switching to a different activity. + +## 0.9.4 + +* Add web support by endorsing `package:camera_web`. + +## 0.9.3+1 + +* Remove iOS 9 availability check around ultra high capture sessions. + +## 0.9.3 + +* Update minimum Flutter SDK to 2.5 and iOS deployment target to 9.0. + +## 0.9.2+2 + +* Ensure that setting the exposure offset returns the new offset value on Android. + +## 0.9.2+1 + +* Fixed camera controller throwing an exception when being replaced in the preview widget. + +## 0.9.2 + +* Added functions to pause and resume the camera preview. + +## 0.9.1+1 + +* Replace `device_info` reference with `device_info_plus` in the [README.md](README.md) + +## 0.9.1 + +* Added `lensAperture`, `sensorExposureTime` and `sensorSensitivity` properties to the `CameraImage` dto. + +## 0.9.0 + +* Complete rewrite of Android plugin to fix many capture, focus, flash, orientation and exposure issues. +* Fixed crash when opening front-facing cameras on some legacy android devices like Sony XZ. +* Android Flash mode works with full precapture sequence. +* Updated Android lint settings. + +## 0.8.1+7 + +* Fix device orientation sometimes not affecting the camera preview orientation. + +## 0.8.1+6 + +* Remove references to the Android V1 embedding. + +## 0.8.1+5 + +* Make sure the `setFocusPoint` and `setExposurePoint` coordinates work correctly in all orientations on iOS (instead of only in portrait mode). + +## 0.8.1+4 + +* Silenced warnings that may occur during build when using a very + recent version of Flutter relating to null safety. + +## 0.8.1+3 + +* Do not change camera orientation when iOS device is flat. + +## 0.8.1+2 + +* Fix iOS crash when selecting an unsupported FocusMode. + +## 0.8.1+1 + +* Migrate maven repository from jcenter to mavenCentral. + +## 0.8.1 + +* Solved a rotation issue on iOS which caused the default preview to be displayed as landscape right instead of portrait. + +## 0.8.0 + +* Stable null safety release. +* Solved delay when using the zoom feature on iOS. +* Added a timeout to the pre-capture sequence on Android to prevent crashes when the camera cannot get a focus. +* Updates the example code listed in the [README.md](README.md), so it runs without errors when you simply copy/ paste it into a Flutter App. + +## 0.7.0+4 + +* Fix crash when taking picture with orientation lock + +## 0.7.0+3 + +* Clockwise rotation of focus point in android + +## 0.7.0+2 + +* Fix example reference in README. +* Revert compileSdkVersion back to 29 (from 30) as this is causing problems with add-to-app configurations. + +## 0.7.0+1 + +* Ensure communication from JAVA to Dart is done on the main UI thread. + +## 0.7.0 + +* BREAKING CHANGE: `CameraValue.aspectRatio` now returns `width / height` rather than `height / width`. [(commit)](https://github.com/flutter/plugins/commit/100c7470d4066b1d0f8f7e4ec6d7c943e736f970) + * Added support for capture orientation locking on Android and iOS. + * Fixed camera preview not rotating correctly on Android and iOS. + * Fixed camera preview sometimes appearing stretched on Android and iOS. + * Fixed videos & photos saving with the incorrect rotation on iOS. +* New Features: + * Adds auto focus support for Android and iOS implementations. [(commmit)](https://github.com/flutter/plugins/commit/71a831790220f898bf8120c8a23840ac6e742db5) + * Adds ImageFormat selection for ImageStream and Video(iOS only). [(commit)](https://github.com/flutter/plugins/commit/da1b4638b750a5ff832d7be86a42831c42c6d6c0) +* Bug Fixes: + * Fixes crash when taking a picture on iOS devices without flash. [(commit)](https://github.com/flutter/plugins/commit/831344490984b1feec007afc9c8595d80b6c13f4) + * Make sure the configured zoom scale is copied over to the final capture builder on Android. Fixes the issue where the preview is zoomed but the final picture is not. [(commit)](https://github.com/flutter/plugins/commit/5916f55664e1772a4c3f0c02c5c71fc11e491b76) + * Fixes crash with using inner camera on some Android devices. [(commit)](https://github.com/flutter/plugins/commit/980b674cb4020c1927917426211a87e275346d5e) + * Improved error feedback by differentiating between uninitialized and disposed camera controllers. [(commit)](https://github.com/flutter/plugins/commit/d0b7109f6b00a0eda03506fed2c74cc123ffc6f3) + * Fixes picture captures causing a crash on some Huawei devices. [(commit)](https://github.com/flutter/plugins/commit/6d18db83f00f4861ffe485aba2d1f8aa08845ce6) + +## 0.6.4+5 + +* Update the example app: remove the deprecated `RaisedButton` and `FlatButton` widgets. + +## 0.6.4+4 + +* Set camera auto focus enabled by default. + +## 0.6.4+3 + +* Detect if selected camera supports auto focus and act accordingly on Android. This solves a problem where front facing cameras are not capturing the picture because auto focus is not supported. + +## 0.6.4+2 + +* Set ImageStreamReader listener to null to prevent stale images when streaming images. + +## 0.6.4+1 + +* Added closeCaptureSession() to stopVideoRecording in Camera.java to fix an Android 6 crash. + +## 0.6.4 + +* Adds auto exposure support for Android and iOS implementations. + +## 0.6.3+4 + +* Revert previous dependency update: Changed dependency on camera_platform_interface to >=1.04 <1.1.0. + +## 0.6.3+3 + +* Updated dependency on camera_platform_interface to ^1.2.0. + +## 0.6.3+2 + +* Fixes crash on Android which occurs after video recording has stopped just before taking a picture. + +## 0.6.3+1 + +* Fixes flash & torch modes not working on some Android devices. + +## 0.6.3 + +* Adds torch mode as a flash mode for Android and iOS implementations. + +## 0.6.2+1 + +* Fix the API documentation for the `CameraController.takePicture` method. + +## 0.6.2 + +* Add zoom support for Android and iOS implementations. + +## 0.6.1+1 + +* Added implementation of the `didFinishProcessingPhoto` on iOS which allows saving image metadata (EXIF) on iOS 11 and up. + +## 0.6.1 + +* Add flash support for Android and iOS implementations. + +## 0.6.0+2 + +* Fix outdated links across a number of markdown files ([#3276](https://github.com/flutter/plugins/pull/3276)) + +## 0.6.0+1 + +Updated README to inform users that iOS 10.0+ is needed for use + +## 0.6.0 + +As part of implementing federated architecture and making the interface compatible with the web this version contains the following **breaking changes**: + +Method changes in `CameraController`: +- The `takePicture` method no longer accepts the `path` parameter, but instead returns the captured image as an instance of the `XFile` class; +- The `startVideoRecording` method no longer accepts the `filePath`. Instead the recorded video is now returned as a `XFile` instance when the `stopVideoRecording` method completes; +- The `stopVideoRecording` method now returns the captured video when it completes; +- Added the `buildPreview` method which is now used to implement the CameraPreview widget. + +## 0.5.8+19 + +* Update Flutter SDK constraint. + +## 0.5.8+18 + +* Suppress unchecked warning in Android tests which prevented the tests to compile. + +## 0.5.8+17 + +* Added Android 30 support. + +## 0.5.8+16 + +* Moved package to camera/camera subdir, to allow for federated implementations. + +## 0.5.8+15 + +* Added the `debugCheckIsDisposed` method which can be used in debug mode to validate if the `CameraController` class has been disposed. + +## 0.5.8+14 + +* Changed the order of the setters for `mediaRecorder` in `MediaRecorderBuilder.java` to make it more readable. + +## 0.5.8+13 + +* Added Dartdocs for all public APIs. + +## 0.5.8+12 + +* Added information of video not working correctly on Android emulators to `README.md`. + +## 0.5.8+11 + +* Fix rare nullptr exception on Android. +* Updated README.md with information about handling App lifecycle changes. + +## 0.5.8+10 + +* Suppress the `deprecated_member_use` warning in the example app for `ScaffoldMessenger.showSnackBar`. + +## 0.5.8+9 + +* Update android compileSdkVersion to 29. + +## 0.5.8+8 + +* Fixed garbled audio (in video) by setting audio encoding bitrate. + +## 0.5.8+7 + +* Keep handling deprecated Android v1 classes for backward compatibility. + +## 0.5.8+6 + +* Avoiding uses or overrides a deprecated API in CameraPlugin.java. + +## 0.5.8+5 + +* Fix compilation/availability issues on iOS. + +## 0.5.8+4 + +* Fixed bug caused by casting a `CameraAccessException` on Android. + +## 0.5.8+3 + +* Fix bug in usage example in README.md + +## 0.5.8+2 + +* Post-v2 embedding cleanups. + +## 0.5.8+1 + +* Update lower bound of dart dependency to 2.1.0. + +## 0.5.8 + +* Remove Android dependencies fallback. +* Require Flutter SDK 1.12.13+hotfix.5 or greater. + +## 0.5.7+5 + +* Replace deprecated `getFlutterEngine` call on Android. + +## 0.5.7+4 + +* Add `pedantic` to dev_dependency. + +## 0.5.7+3 + +* Fix an Android crash when permissions are requested multiple times. + +## 0.5.7+2 + +* Remove the deprecated `author:` field from pubspec.yaml +* Migrate the plugin to the pubspec platforms manifest. +* Require Flutter SDK 1.10.0 or greater. + +## 0.5.7+1 + +* Fix example null exception. + +## 0.5.7 + +* Fix unawaited futures. + +## 0.5.6+4 + +* Android: Use CameraDevice.TEMPLATE_RECORD to improve image streaming. + +## 0.5.6+3 + +* Remove AndroidX warning. + +## 0.5.6+2 + +* Include lifecycle dependency as a compileOnly one on Android to resolve + potential version conflicts with other transitive libraries. + +## 0.5.6+1 + +* Android: Use android.arch.lifecycle instead of androidx.lifecycle:lifecycle in `build.gradle` to support apps that has not been migrated to AndroidX. + +## 0.5.6 + +* Add support for the v2 Android embedding. This shouldn't affect existing + functionality. + +## 0.5.5+1 + +* Fix event type check + +## 0.5.5 + +* Define clang modules for iOS. + +## 0.5.4+3 + +* Update and migrate iOS example project. + +## 0.5.4+2 + +* Fix Android NullPointerException on devices with only front-facing camera. + +## 0.5.4+1 + +* Fix Android pause and resume video crash when executing in APIs below 24. + +## 0.5.4 + +* Add feature to pause and resume video recording. + +## 0.5.3+1 + +* Fix too large request code for FragmentActivity users. + +## 0.5.3 + +* Added new quality presets. +* Now all quality presets can be used to control image capture quality. + +## 0.5.2+2 + +* Fix memory leak related to not unregistering stream handler in FlutterEventChannel when disposing camera. + +## 0.5.2+1 + +* Fix bug that prevented video recording with audio. + +## 0.5.2 + +* Added capability to disable audio for the `CameraController`. (e.g. `CameraController(_, _, + enableAudio: false);`) + +## 0.5.1 + +* Can now be compiled with earlier Android sdks below 21 when +`` has been added to the project +`AndroidManifest.xml`. For sdks below 21, the plugin won't be registered and calls to it will throw +a `MissingPluginException.` + +## 0.5.0 + +* **Breaking Change** This plugin no longer handles closing and opening the camera on Android + lifecycle changes. Please use `WidgetsBindingObserver` to control camera resources on lifecycle + changes. See example project for example using `WidgetsBindingObserver`. + +## 0.4.3+2 + +* Bump the minimum Flutter version to 1.2.0. +* Add template type parameter to `invokeMethod` calls. + +## 0.4.3+1 + +* Catch additional `Exception`s from Android and throw as `CameraException`s. + +## 0.4.3 + +* Add capability to prepare the capture session for video recording on iOS. + +## 0.4.2 + +* Add sensor orientation value to `CameraDescription`. + +## 0.4.1 + +* Camera methods are ran in a background thread on iOS. + +## 0.4.0+3 + +* Fixed a crash when the plugin is registered by a background FlutterView. + +## 0.4.0+2 + +* Fix orientation of captured photos when camera is used for the first time on Android. + +## 0.4.0+1 + +* Remove categories. + +## 0.4.0 + +* **Breaking Change** Change iOS image stream format to `ImageFormatGroup.bgra8888` from + `ImageFormatGroup.yuv420`. + +## 0.3.0+4 + +* Fixed bug causing black screen on some Android devices. + +## 0.3.0+3 + +* Log a more detailed warning at build time about the previous AndroidX + migration. + +## 0.3.0+2 + +* Fix issue with calculating iOS image orientation in certain edge cases. + +## 0.3.0+1 + +* Remove initial method call invocation from static camera method. + +## 0.3.0 + +* **Breaking change**. Migrate from the deprecated original Android Support + Library to AndroidX. This shouldn't result in any functional changes, but it + requires any Android apps using this plugin to [also + migrate](https://developer.android.com/jetpack/androidx/migrate) if they're + using the original support library. + +## 0.2.9+1 + +* Fix a crash when failing to start preview. + +## 0.2.9 + +* Save photo orientation data on iOS. + +## 0.2.8 + +* Add access to the image stream from Dart. +* Use `cameraController.startImageStream(listener)` to process the images. + +## 0.2.7 + +* Fix issue with crash when the physical device's orientation is unknown. + +## 0.2.6 + +* Update the camera to use the physical device's orientation instead of the UI + orientation on Android. + +## 0.2.5 + +* Fix preview and video size with satisfying conditions of multiple outputs. + +## 0.2.4 + +* Unregister the activity lifecycle callbacks when disposing the camera. + +## 0.2.3 + +* Added path_provider and video_player as dev dependencies because the example uses them. +* Updated example path_provider version to get Dart 2 support. + +## 0.2.2 + +* iOS image capture is done in high quality (full camera size) + +## 0.2.1 + +* Updated Gradle tooling to match Android Studio 3.1.2. + +## 0.2.0 + +* Added support for video recording. +* Changed the example app to add video recording. + +A lot of **breaking changes** in this version: + +Getter changes: + - Removed `isStarted` + - Renamed `initialized` to `isInitialized` + - Added `isRecordingVideo` + +Method changes: + - Renamed `capture` to `takePicture` + - Removed `start` (the preview starts automatically when `initialize` is called) + - Added `startVideoRecording(String filePath)` + - Removed `stop` (the preview stops automatically when `dispose` is called) + - Added `stopVideoRecording` + +## 0.1.2 + +* Fix Dart 2 runtime errors. + +## 0.1.1 + +* Fix Dart 2 runtime error. + +## 0.1.0 + +* **Breaking change**. Set SDK constraints to match the Flutter beta release. + +## 0.0.4 + +* Revert regression of `CameraController.capture()` introduced in v. 0.0.3. + +## 0.0.3 + +* Improved resource cleanup on Android. Avoids crash on Activity restart. +* Made the Future returned by `CameraController.dispose()` and `CameraController.capture()` actually complete on + Android. + +## 0.0.2 + +* Simplified and upgraded Android project template to Android SDK 27. +* Moved Android package to io.flutter.plugins. +* Fixed warnings from the Dart 2.0 analyzer. + +## 0.0.1 + +* Initial release diff --git a/packages/camera/camera_avfoundation/LICENSE b/packages/camera/camera_avfoundation/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/camera/camera_avfoundation/LICENSE @@ -0,0 +1,25 @@ +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/camera/camera_avfoundation/README.md b/packages/camera/camera_avfoundation/README.md new file mode 100644 index 000000000000..ec9d7379c60b --- /dev/null +++ b/packages/camera/camera_avfoundation/README.md @@ -0,0 +1,176 @@ +# Camera Plugin + + + +[![pub package](https://img.shields.io/pub/v/camera.svg)](https://pub.dev/packages/camera) + +A Flutter plugin for iOS, Android and Web allowing access to the device cameras. + +| | Android | iOS | Web | +|----------------|---------|----------|------------------------| +| **Support** | SDK 21+ | iOS 10+* | [See `camera_web `][1] | + +## Features + +* Display live camera preview in a widget. +* Snapshots can be captured and saved to a file. +* Record video. +* Add access to the image stream from Dart. + +## Installation + +First, add `camera` as a [dependency in your pubspec.yaml file](https://flutter.dev/using-packages/). + +### iOS + +\* The camera plugin compiles for any version of iOS, but its functionality +requires iOS 10 or higher. If compiling for iOS 9, make sure to programmatically +check the version of iOS running on the device before using any camera plugin features. +The [device_info_plus](https://pub.dev/packages/device_info_plus) plugin, for example, can be used to check the iOS version. + +Add two rows to the `ios/Runner/Info.plist`: + +* one with the key `Privacy - Camera Usage Description` and a usage description. +* and one with the key `Privacy - Microphone Usage Description` and a usage description. + +If editing `Info.plist` as text, add: + +```xml +NSCameraUsageDescription +your usage description here +NSMicrophoneUsageDescription +your usage description here +``` + +### Android + +Change the minimum Android sdk version to 21 (or higher) in your `android/app/build.gradle` file. + +```groovy +minSdkVersion 21 +``` + +It's important to note that the `MediaRecorder` class is not working properly on emulators, as stated in the documentation: https://developer.android.com/reference/android/media/MediaRecorder. Specifically, when recording a video with sound enabled and trying to play it back, the duration won't be correct and you will only see the first frame. + +### Web integration + +For web integration details, see the +[`camera_web` package](https://pub.dev/packages/camera_web). + +### Handling Lifecycle states + +As of version [0.5.0](https://github.com/flutter/plugins/blob/master/packages/camera/CHANGELOG.md#050) of the camera plugin, lifecycle changes are no longer handled by the plugin. This means developers are now responsible to control camera resources when the lifecycle state is updated. Failure to do so might lead to unexpected behavior (for example as described in issue [#39109](https://github.com/flutter/flutter/issues/39109)). Handling lifecycle changes can be done by overriding the `didChangeAppLifecycleState` method like so: + + +```dart +@override +void didChangeAppLifecycleState(AppLifecycleState state) { + final CameraController? cameraController = controller; + + // App state changed before we got the chance to initialize. + if (cameraController == null || !cameraController.value.isInitialized) { + return; + } + + if (state == AppLifecycleState.inactive) { + cameraController.dispose(); + } else if (state == AppLifecycleState.resumed) { + onNewCameraSelected(cameraController.description); + } +} +``` + +### Handling camera access permissions + +Permission errors may be thrown when initializing the camera controller, and you are expected to handle them properly. + +Here is a list of all permission error codes that can be thrown: + +- `CameraAccessDenied`: Thrown when user denies the camera access permission. + +- `CameraAccessDeniedWithoutPrompt`: iOS only for now. Thrown when user has previously denied the permission. iOS does not allow prompting alert dialog a second time. Users will have to go to Settings > Privacy > Camera in order to enable camera access. + +- `CameraAccessRestricted`: iOS only for now. Thrown when camera access is restricted and users cannot grant permission (parental control). + +- `AudioAccessDenied`: Thrown when user denies the audio access permission. + +- `AudioAccessDeniedWithoutPrompt`: iOS only for now. Thrown when user has previously denied the permission. iOS does not allow prompting alert dialog a second time. Users will have to go to Settings > Privacy > Microphone in order to enable audio access. + +- `AudioAccessRestricted`: iOS only for now. Thrown when audio access is restricted and users cannot grant permission (parental control). + +- `cameraPermission`: Android and Web only. A legacy error code for all kinds of camera permission errors. + +### Example + +Here is a small example flutter app displaying a full screen camera preview. + + +```dart +import 'package:camera/camera.dart'; +import 'package:flutter/material.dart'; + +late List _cameras; + +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + + _cameras = await availableCameras(); + runApp(const CameraApp()); +} + +/// CameraApp is the Main Application. +class CameraApp extends StatefulWidget { + /// Default Constructor + const CameraApp({Key? key}) : super(key: key); + + @override + State createState() => _CameraAppState(); +} + +class _CameraAppState extends State { + late CameraController controller; + + @override + void initState() { + super.initState(); + controller = CameraController(_cameras[0], ResolutionPreset.max); + controller.initialize().then((_) { + if (!mounted) { + return; + } + setState(() {}); + }).catchError((Object e) { + if (e is CameraException) { + switch (e.code) { + case 'CameraAccessDenied': + print('User denied camera access.'); + break; + default: + print('Handle other errors.'); + break; + } + } + }); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (!controller.value.isInitialized) { + return Container(); + } + return MaterialApp( + home: CameraPreview(controller), + ); + } +} +``` + +For a more elaborate usage example see [here](https://github.com/flutter/plugins/tree/main/packages/camera/camera/example). + +[1]: https://pub.dev/packages/camera_web#limitations-on-the-web-platform diff --git a/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart new file mode 100644 index 000000000000..557f4858acab --- /dev/null +++ b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart @@ -0,0 +1,297 @@ +// 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 'dart:async'; +import 'dart:io'; +import 'dart:ui'; + +import 'package:camera/camera.dart'; +import 'package:flutter/painting.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:video_player/video_player.dart'; + +void main() { + late Directory testDir; + + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + setUpAll(() async { + final Directory extDir = await getTemporaryDirectory(); + testDir = await Directory('${extDir.path}/test').create(recursive: true); + }); + + tearDownAll(() async { + await testDir.delete(recursive: true); + }); + + final Map presetExpectedSizes = + { + ResolutionPreset.low: + Platform.isAndroid ? const Size(240, 320) : const Size(288, 352), + ResolutionPreset.medium: + Platform.isAndroid ? const Size(480, 720) : const Size(480, 640), + ResolutionPreset.high: const Size(720, 1280), + ResolutionPreset.veryHigh: const Size(1080, 1920), + ResolutionPreset.ultraHigh: const Size(2160, 3840), + // Don't bother checking for max here since it could be anything. + }; + + /// Verify that [actual] has dimensions that are at least as large as + /// [expectedSize]. Allows for a mismatch in portrait vs landscape. Returns + /// whether the dimensions exactly match. + bool assertExpectedDimensions(Size expectedSize, Size actual) { + expect(actual.shortestSide, lessThanOrEqualTo(expectedSize.shortestSide)); + expect(actual.longestSide, lessThanOrEqualTo(expectedSize.longestSide)); + return actual.shortestSide == expectedSize.shortestSide && + actual.longestSide == expectedSize.longestSide; + } + + // This tests that the capture is no bigger than the preset, since we have + // automatic code to fall back to smaller sizes when we need to. Returns + // whether the image is exactly the desired resolution. + Future testCaptureImageResolution( + CameraController controller, ResolutionPreset preset) async { + final Size expectedSize = presetExpectedSizes[preset]!; + print( + 'Capturing photo at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.description.name}'); + + // Take Picture + final XFile file = await controller.takePicture(); + + // Load picture + final File fileImage = File(file.path); + final Image image = await decodeImageFromList(fileImage.readAsBytesSync()); + + // Verify image dimensions are as expected + expect(image, isNotNull); + return assertExpectedDimensions( + expectedSize, Size(image.height.toDouble(), image.width.toDouble())); + } + + testWidgets( + 'Capture specific image resolutions', + (WidgetTester tester) async { + final List cameras = await availableCameras(); + if (cameras.isEmpty) { + return; + } + for (final CameraDescription cameraDescription in cameras) { + bool previousPresetExactlySupported = true; + for (final MapEntry preset + in presetExpectedSizes.entries) { + final CameraController controller = + CameraController(cameraDescription, preset.key); + await controller.initialize(); + final bool presetExactlySupported = + await testCaptureImageResolution(controller, preset.key); + assert(!(!previousPresetExactlySupported && presetExactlySupported), + 'The camera took higher resolution pictures at a lower resolution.'); + previousPresetExactlySupported = presetExactlySupported; + await controller.dispose(); + } + } + }, + // TODO(egarciad): Fix https://github.com/flutter/flutter/issues/93686. + skip: true, + ); + + // This tests that the capture is no bigger than the preset, since we have + // automatic code to fall back to smaller sizes when we need to. Returns + // whether the image is exactly the desired resolution. + Future testCaptureVideoResolution( + CameraController controller, ResolutionPreset preset) async { + final Size expectedSize = presetExpectedSizes[preset]!; + print( + 'Capturing video at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.description.name}'); + + // Take Video + await controller.startVideoRecording(); + sleep(const Duration(milliseconds: 300)); + final XFile file = await controller.stopVideoRecording(); + + // Load video metadata + final File videoFile = File(file.path); + final VideoPlayerController videoController = + VideoPlayerController.file(videoFile); + await videoController.initialize(); + final Size video = videoController.value.size; + + // Verify image dimensions are as expected + expect(video, isNotNull); + return assertExpectedDimensions( + expectedSize, Size(video.height, video.width)); + } + + testWidgets( + 'Capture specific video resolutions', + (WidgetTester tester) async { + final List cameras = await availableCameras(); + if (cameras.isEmpty) { + return; + } + for (final CameraDescription cameraDescription in cameras) { + bool previousPresetExactlySupported = true; + for (final MapEntry preset + in presetExpectedSizes.entries) { + final CameraController controller = + CameraController(cameraDescription, preset.key); + await controller.initialize(); + await controller.prepareForVideoRecording(); + final bool presetExactlySupported = + await testCaptureVideoResolution(controller, preset.key); + assert(!(!previousPresetExactlySupported && presetExactlySupported), + 'The camera took higher resolution pictures at a lower resolution.'); + previousPresetExactlySupported = presetExactlySupported; + await controller.dispose(); + } + } + }, + // TODO(egarciad): Fix https://github.com/flutter/flutter/issues/93686. + skip: true, + ); + + testWidgets('Pause and resume video recording', (WidgetTester tester) async { + final List cameras = await availableCameras(); + if (cameras.isEmpty) { + return; + } + + final CameraController controller = CameraController( + cameras[0], + ResolutionPreset.low, + enableAudio: false, + ); + + await controller.initialize(); + await controller.prepareForVideoRecording(); + + int startPause; + int timePaused = 0; + + await controller.startVideoRecording(); + final int recordingStart = DateTime.now().millisecondsSinceEpoch; + sleep(const Duration(milliseconds: 500)); + + await controller.pauseVideoRecording(); + startPause = DateTime.now().millisecondsSinceEpoch; + sleep(const Duration(milliseconds: 500)); + await controller.resumeVideoRecording(); + timePaused += DateTime.now().millisecondsSinceEpoch - startPause; + + sleep(const Duration(milliseconds: 500)); + + await controller.pauseVideoRecording(); + startPause = DateTime.now().millisecondsSinceEpoch; + sleep(const Duration(milliseconds: 500)); + await controller.resumeVideoRecording(); + timePaused += DateTime.now().millisecondsSinceEpoch - startPause; + + sleep(const Duration(milliseconds: 500)); + + final XFile file = await controller.stopVideoRecording(); + final int recordingTime = + DateTime.now().millisecondsSinceEpoch - recordingStart; + + final File videoFile = File(file.path); + final VideoPlayerController videoController = VideoPlayerController.file( + videoFile, + ); + await videoController.initialize(); + final int duration = videoController.value.duration.inMilliseconds; + await videoController.dispose(); + + expect(duration, lessThan(recordingTime - timePaused)); + }, skip: !Platform.isAndroid); + + testWidgets( + 'Android image streaming', + (WidgetTester tester) async { + final List cameras = await availableCameras(); + if (cameras.isEmpty) { + return; + } + + final CameraController controller = CameraController( + cameras[0], + ResolutionPreset.low, + enableAudio: false, + ); + + await controller.initialize(); + bool _isDetecting = false; + + await controller.startImageStream((CameraImage image) { + if (_isDetecting) { + return; + } + + _isDetecting = true; + + expectLater(image, isNotNull).whenComplete(() => _isDetecting = false); + }); + + expect(controller.value.isStreamingImages, true); + + sleep(const Duration(milliseconds: 500)); + + await controller.stopImageStream(); + await controller.dispose(); + }, + skip: !Platform.isAndroid, + ); + + /// Start streaming with specifying the ImageFormatGroup. + Future startStreaming(List cameras, + ImageFormatGroup? imageFormatGroup) async { + final CameraController controller = CameraController( + cameras.first, + ResolutionPreset.low, + enableAudio: false, + imageFormatGroup: imageFormatGroup, + ); + + await controller.initialize(); + final Completer _completer = Completer(); + + await controller.startImageStream((CameraImage image) { + if (!_completer.isCompleted) { + Future(() async { + await controller.stopImageStream(); + await controller.dispose(); + }).then((Object? value) { + _completer.complete(image); + }); + } + }); + return _completer.future; + } + + testWidgets( + 'iOS image streaming with imageFormatGroup', + (WidgetTester tester) async { + final List cameras = await availableCameras(); + if (cameras.isEmpty) { + return; + } + + CameraImage _image = await startStreaming(cameras, null); + expect(_image, isNotNull); + expect(_image.format.group, ImageFormatGroup.bgra8888); + expect(_image.planes.length, 1); + + _image = await startStreaming(cameras, ImageFormatGroup.yuv420); + expect(_image, isNotNull); + expect(_image.format.group, ImageFormatGroup.yuv420); + expect(_image.planes.length, 2); + + _image = await startStreaming(cameras, ImageFormatGroup.bgra8888); + expect(_image, isNotNull); + expect(_image.format.group, ImageFormatGroup.bgra8888); + expect(_image.planes.length, 1); + }, + skip: !Platform.isIOS, + ); +} diff --git a/packages/camera/camera_avfoundation/example/ios/Flutter/AppFrameworkInfo.plist b/packages/camera/camera_avfoundation/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 000000000000..3a9c234f96d4 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + UIRequiredDeviceCapabilities + + arm64 + + MinimumOSVersion + 9.0 + + diff --git a/packages/camera/camera_avfoundation/example/ios/Flutter/Debug.xcconfig b/packages/camera/camera_avfoundation/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 000000000000..b2f5fae9c254 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,3 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/packages/camera/camera_avfoundation/example/ios/Flutter/Release.xcconfig b/packages/camera/camera_avfoundation/example/ios/Flutter/Release.xcconfig new file mode 100644 index 000000000000..88c29144c836 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,3 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/packages/camera/camera_avfoundation/example/ios/Podfile b/packages/camera/camera_avfoundation/example/ios/Podfile new file mode 100644 index 000000000000..5bc7b7e85717 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Podfile @@ -0,0 +1,45 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + + target 'RunnerTests' do + platform :ios, '9.0' + inherit! :search_paths + # Pods for testing + pod 'OCMock', '~> 3.8.1' + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..03c80d79c578 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,712 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 033B94BE269C40A200B4DF97 /* CameraMethodChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 033B94BD269C40A200B4DF97 /* CameraMethodChannelTests.m */; }; + 03BB766B2665316900CE5A93 /* CameraFocusTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03BB766A2665316900CE5A93 /* CameraFocusTests.m */; }; + 03F6F8B226CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03F6F8B126CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m */; }; + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 236906D1621AE863A5B2E770 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 89D82918721FABF772705DB0 /* libPods-Runner.a */; }; + 25C3919135C3D981E6F800D0 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1944D8072499F3B5E7653D44 /* libPods-RunnerTests.a */; }; + 334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03BB767226653ABE00CE5A93 /* CameraOrientationTests.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 43ED1537282570DE00EB00DE /* AvailableCamerasTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 43ED1536282570DE00EB00DE /* AvailableCamerasTest.m */; }; + 788A065A27B0E02900533D74 /* StreamingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 788A065927B0E02900533D74 /* StreamingTest.m */; }; + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; + 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + E01EE4A82799F3A5008C1950 /* QueueUtilsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E01EE4A72799F3A5008C1950 /* QueueUtilsTests.m */; }; + E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */; }; + E04F108627A87CA600573D0C /* FLTSavePhotoDelegateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */; }; + E071CF7227B3061B006EF3BA /* FLTCamPhotoCaptureTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */; }; + E071CF7427B31DE4006EF3BA /* FLTCamSampleBufferTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */; }; + E0B0D2BB27DFF2AF00E71E4B /* CameraPermissionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0B0D2BA27DFF2AF00E71E4B /* CameraPermissionTests.m */; }; + E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */; }; + E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */; }; + E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */; }; + E0CDBAC227CD9729002561D9 /* CameraTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */; }; + E0F95E3D27A32AB900699390 /* CameraPropertiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */; }; + E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */; }; + F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */ = {isa = PBXBuildFile; fileRef = F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 03BB766D2665316900CE5A93 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 033B94BD269C40A200B4DF97 /* CameraMethodChannelTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraMethodChannelTests.m; sourceTree = ""; }; + 03BB76682665316900CE5A93 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 03BB766A2665316900CE5A93 /* CameraFocusTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraFocusTests.m; sourceTree = ""; }; + 03BB766C2665316900CE5A93 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 03BB767226653ABE00CE5A93 /* CameraOrientationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CameraOrientationTests.m; sourceTree = ""; }; + 03F6F8B126CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeFlutterResultTests.m; sourceTree = ""; }; + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 14AE82C910C2A12F2ECB2094 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 1944D8072499F3B5E7653D44 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 43ED1536282570DE00EB00DE /* AvailableCamerasTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AvailableCamerasTest.m; sourceTree = ""; }; + 59848A7CA98C1FADF8840207 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 788A065927B0E02900533D74 /* StreamingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StreamingTest.m; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 89D82918721FABF772705DB0 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9C5CC6CAD53AD388B2694F3A /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + A24F9E418BA48BCC7409B117 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + E01EE4A72799F3A5008C1950 /* QueueUtilsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QueueUtilsTests.m; sourceTree = ""; }; + E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CameraCaptureSessionQueueRaceConditionTests.m; sourceTree = ""; }; + E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTSavePhotoDelegateTests.m; sourceTree = ""; }; + E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTCamPhotoCaptureTests.m; sourceTree = ""; }; + E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTCamSampleBufferTests.m; sourceTree = ""; }; + E0B0D2BA27DFF2AF00E71E4B /* CameraPermissionTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPermissionTests.m; sourceTree = ""; }; + E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeMethodChannelTests.m; sourceTree = ""; }; + E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeTextureRegistryTests.m; sourceTree = ""; }; + E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeEventChannelTests.m; sourceTree = ""; }; + E0CDBAC027CD9729002561D9 /* CameraTestUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CameraTestUtils.h; sourceTree = ""; }; + E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraTestUtils.m; sourceTree = ""; }; + E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPropertiesTests.m; sourceTree = ""; }; + E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPreviewPauseTests.m; sourceTree = ""; }; + F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockFLTThreadSafeFlutterResult.h; sourceTree = ""; }; + F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockFLTThreadSafeFlutterResult.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 03BB76652665316900CE5A93 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 25C3919135C3D981E6F800D0 /* libPods-RunnerTests.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 236906D1621AE863A5B2E770 /* libPods-Runner.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 03BB76692665316900CE5A93 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 03BB766A2665316900CE5A93 /* CameraFocusTests.m */, + 03BB767226653ABE00CE5A93 /* CameraOrientationTests.m */, + 03BB766C2665316900CE5A93 /* Info.plist */, + 033B94BD269C40A200B4DF97 /* CameraMethodChannelTests.m */, + 03F6F8B126CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m */, + E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */, + E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */, + E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */, + E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */, + E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */, + E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */, + E0B0D2BA27DFF2AF00E71E4B /* CameraPermissionTests.m */, + E01EE4A72799F3A5008C1950 /* QueueUtilsTests.m */, + E0CDBAC027CD9729002561D9 /* CameraTestUtils.h */, + E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */, + E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */, + F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */, + F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */, + E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */, + E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */, + 788A065927B0E02900533D74 /* StreamingTest.m */, + 43ED1536282570DE00EB00DE /* AvailableCamerasTest.m */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 3242FD2B467C15C62200632F /* Frameworks */ = { + isa = PBXGroup; + children = ( + 89D82918721FABF772705DB0 /* libPods-Runner.a */, + 1944D8072499F3B5E7653D44 /* libPods-RunnerTests.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 03BB76692665316900CE5A93 /* RunnerTests */, + 97C146EF1CF9000F007C117D /* Products */, + FD386F00E98D73419C929072 /* Pods */, + 3242FD2B467C15C62200632F /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 03BB76682665316900CE5A93 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 97C146F11CF9000F007C117D /* Supporting Files */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + ); + path = Runner; + sourceTree = ""; + }; + 97C146F11CF9000F007C117D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 97C146F21CF9000F007C117D /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + FD386F00E98D73419C929072 /* Pods */ = { + isa = PBXGroup; + children = ( + 59848A7CA98C1FADF8840207 /* Pods-Runner.debug.xcconfig */, + 14AE82C910C2A12F2ECB2094 /* Pods-Runner.release.xcconfig */, + 9C5CC6CAD53AD388B2694F3A /* Pods-RunnerTests.debug.xcconfig */, + A24F9E418BA48BCC7409B117 /* Pods-RunnerTests.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 03BB76672665316900CE5A93 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 03BB76712665316900CE5A93 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 422786A96136AA9087A2041B /* [CP] Check Pods Manifest.lock */, + 03BB76642665316900CE5A93 /* Sources */, + 03BB76652665316900CE5A93 /* Frameworks */, + 03BB76662665316900CE5A93 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 03BB766E2665316900CE5A93 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = camera_exampleTests; + productReference = 03BB76682665316900CE5A93 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9872F2A25E8A171A111468CD /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = "The Flutter Authors"; + TargetAttributes = { + 03BB76672665316900CE5A93 = { + CreatedOnToolsVersion = 12.5; + ProvisioningStyle = Automatic; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 03BB76672665316900CE5A93 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 03BB76662665316900CE5A93 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 422786A96136AA9087A2041B /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + 9872F2A25E8A171A111468CD /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 03BB76642665316900CE5A93 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 03F6F8B226CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m in Sources */, + 033B94BE269C40A200B4DF97 /* CameraMethodChannelTests.m in Sources */, + E071CF7227B3061B006EF3BA /* FLTCamPhotoCaptureTests.m in Sources */, + E0F95E3D27A32AB900699390 /* CameraPropertiesTests.m in Sources */, + 03BB766B2665316900CE5A93 /* CameraFocusTests.m in Sources */, + E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */, + E071CF7427B31DE4006EF3BA /* FLTCamSampleBufferTests.m in Sources */, + E04F108627A87CA600573D0C /* FLTSavePhotoDelegateTests.m in Sources */, + 43ED1537282570DE00EB00DE /* AvailableCamerasTest.m in Sources */, + F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */, + E0CDBAC227CD9729002561D9 /* CameraTestUtils.m in Sources */, + 334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */, + E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */, + 788A065A27B0E02900533D74 /* StreamingTest.m in Sources */, + E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */, + E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */, + E0B0D2BB27DFF2AF00E71E4B /* CameraPermissionTests.m in Sources */, + E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */, + E01EE4A82799F3A5008C1950 /* QueueUtilsTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, + 97C146F31CF9000F007C117D /* main.m in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 03BB766E2665316900CE5A93 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 03BB766D2665316900CE5A93 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 03BB766F2665316900CE5A93 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9C5CC6CAD53AD388B2694F3A /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = RunnerTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "dev.flutter.plugins.cameraExample.camera-exampleTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Debug; + }; + 03BB76702665316900CE5A93 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A24F9E418BA48BCC7409B117 /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = RunnerTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "dev.flutter.plugins.cameraExample.camera-exampleTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Release; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = ""; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.cameraExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = ""; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.cameraExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 03BB76712665316900CE5A93 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 03BB766F2665316900CE5A93 /* Debug */, + 03BB76702665316900CE5A93 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..919434a6254f --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000000..f4b3c1099001 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/camera/camera_avfoundation/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/camera/camera_avfoundation/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..21a3cc14c74e --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/camera/camera_avfoundation/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/camera/camera_avfoundation/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/AppDelegate.h b/packages/camera/camera_avfoundation/example/ios/Runner/AppDelegate.h new file mode 100644 index 000000000000..0681d288bb70 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Runner/AppDelegate.h @@ -0,0 +1,10 @@ +// 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 +#import + +@interface AppDelegate : FlutterAppDelegate + +@end diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/AppDelegate.m b/packages/camera/camera_avfoundation/example/ios/Runner/AppDelegate.m new file mode 100644 index 000000000000..30b87969f44a --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Runner/AppDelegate.m @@ -0,0 +1,17 @@ +// 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. + +#include "AppDelegate.h" +#include "GeneratedPluginRegistrant.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [GeneratedPluginRegistrant registerWithRegistry:self]; + // Override point for customization after application launch. + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +@end diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000000..d225b3c2cfe2 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,121 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..28c6bf03016f6c994b70f38d1b7346e5831b531f GIT binary patch literal 564 zcmV-40?Yl0P)Px$?ny*JR5%f>l)FnDQ543{x%ZCiu33$Wg!pQFfT_}?5Q|_VSlIbLC`dpoMXL}9 zHfd9&47Mo(7D231gb+kjFxZHS4-m~7WurTH&doVX2KI5sU4v(sJ1@T9eCIKPjsqSr z)C01LsCxk=72-vXmX}CQD#BD;Cthymh&~=f$Q8nn0J<}ZrusBy4PvRNE}+1ceuj8u z0mW5k8fmgeLnTbWHGwfKA3@PdZxhn|PypR&^p?weGftrtCbjF#+zk_5BJh7;0`#Wr zgDpM_;Ax{jO##IrT`Oz;MvfwGfV$zD#c2xckpcXC6oou4ML~ezCc2EtnsQTB4tWNg z?4bkf;hG7IMfhgNI(FV5Gs4|*GyMTIY0$B=_*mso9Ityq$m^S>15>-?0(zQ<8Qy<_TjHE33(?_M8oaM zyc;NxzRVK@DL6RJnX%U^xW0Gpg(lXp(!uK1v0YgHjs^ZXSQ|m#lV7ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 literal 0 HcmV?d00001 diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f091b6b0bca859a3f474b03065bef75ba58a9e4c GIT binary patch literal 1588 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ literal 0 HcmV?d00001 diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d0ef06e7edb86cdfe0d15b4b0d98334a86163658 GIT binary patch literal 1716 zcmds$`#;kQ7{|XelZftyR5~xW7?MLxS4^|Hw3&P7^y)@A9Fj{Xm1~_CIV^XZ%SLBn zA;!r`GqGHg=7>xrB{?psZQs88ZaedDoagm^KF{a*>G|dJWRSe^I$DNW008I^+;Kjt z>9p3GNR^I;v>5_`+91i(*G;u5|L+Bu6M=(afLjtkya#yZ175|z$pU~>2#^Z_pCZ7o z1c6UNcv2B3?; zX%qdxCXQpdKRz=#b*q0P%b&o)5ZrNZt7$fiETSK_VaY=mb4GK`#~0K#~9^ zcY!`#Af+4h?UMR-gMKOmpuYeN5P*RKF!(tb`)oe0j2BH1l?=>y#S5pMqkx6i{*=V9JF%>N8`ewGhRE(|WohnD59R^$_36{4>S zDFlPC5|k?;SPsDo87!B{6*7eqmMdU|QZ84>6)Kd9wNfh90=y=TFQay-0__>=<4pk& zYDjgIhL-jQ9o>z32K)BgAH+HxamL{ZL~ozu)Qqe@a`FpH=oQRA8=L-m-1dam(Ix2V z?du;LdMO+ooBelr^_y4{|44tmgH^2hSzPFd;U^!1p>6d|o)(-01z{i&Kj@)z-yfWQ)V#3Uo!_U}q3u`(fOs`_f^ueFii1xBNUB z6MecwJN$CqV&vhc+)b(p4NzGGEgwWNs z@*lUV6LaduZH)4_g!cE<2G6#+hJrWd5(|p1Z;YJ7ifVHv+n49btR}dq?HHDjl{m$T z!jLZcGkb&XS2OG~u%&R$(X+Z`CWec%QKt>NGYvd5g20)PU(dOn^7%@6kQb}C(%=vr z{?RP(z~C9DPnL{q^@pVw@|Vx~@3v!9dCaBtbh2EdtoNHm4kGxp>i#ct)7p|$QJs+U z-a3qtcPvhihub?wnJqEt>zC@)2suY?%-96cYCm$Q8R%-8$PZYsx3~QOLMDf(piXMm zB=<63yQk1AdOz#-qsEDX>>c)EES%$owHKue;?B3)8aRd}m~_)>SL3h2(9X;|+2#7X z+#2)NpD%qJvCQ0a-uzZLmz*ms+l*N}w)3LRQ*6>|Ub-fyptY(keUxw+)jfwF5K{L9 z|Cl_w=`!l_o><384d&?)$6Nh(GAm=4p_;{qVn#hI8lqewW7~wUlyBM-4Z|)cZr?Rh z=xZ&Ol>4(CU85ea(CZ^aO@2N18K>ftl8>2MqetAR53_JA>Fal`^)1Y--Am~UDa4th zKfCYpcXky$XSFDWBMIl(q=Mxj$iMBX=|j9P)^fDmF(5(5$|?Cx}DKEJa&XZP%OyE`*GvvYQ4PV&!g2|L^Q z?YG}tx;sY@GzMmsY`7r$P+F_YLz)(e}% zyakqFB<6|x9R#TdoP{R$>o7y(-`$$p0NxJ6?2B8tH)4^yF(WhqGZlM3=9Ibs$%U1w zWzcss*_c0=v_+^bfb`kBFsI`d;ElwiU%frgRB%qBjn@!0U2zZehBn|{%uNIKBA7n= zzE`nnwTP85{g;8AkYxA68>#muXa!G>xH22D1I*SiD~7C?7Za+9y7j1SHiuSkKK*^O zsZ==KO(Ua#?YUpXl{ViynyT#Hzk=}5X$e04O@fsMQjb}EMuPWFO0e&8(2N(29$@Vd zn1h8Yd>6z(*p^E{c(L0Lg=wVdupg!z@WG;E0k|4a%s7Up5C0c)55XVK*|x9RQeZ1J@1v9MX;>n34(i>=YE@Iur`0Vah(inE3VUFZNqf~tSz{1fz3Fsn_x4F>o(Yo;kpqvBe-sbwH(*Y zu$JOl0b83zu$JMvy<#oH^Wl>aWL*?aDwnS0iEAwC?DK@aT)GHRLhnz2WCvf3Ba;o=aY7 z2{Asu5MEjGOY4O#Ggz@@J;q*0`kd2n8I3BeNuMmYZf{}pg=jTdTCrIIYuW~luKecn z+E-pHY%ohj@uS0%^ z&(OxwPFPD$+#~`H?fMvi9geVLci(`K?Kj|w{rZ9JgthFHV+=6vMbK~0)Ea<&WY-NC zy-PnZft_k2tfeQ*SuC=nUj4H%SQ&Y$gbH4#2sT0cU0SdFs=*W*4hKGpuR1{)mV;Qf5pw4? zfiQgy0w3fC*w&Bj#{&=7033qFR*<*61B4f9K%CQvxEn&bsWJ{&winp;FP!KBj=(P6 z4Z_n4L7cS;ao2)ax?Tm|I1pH|uLpDSRVghkA_UtFFuZ0b2#>!8;>-_0ELjQSD-DRd z4im;599VHDZYtnWZGAB25W-e(2VrzEh|etsv2YoP#VbIZ{aFkwPrzJ#JvCvA*mXS& z`}Q^v9(W4GiSs}#s7BaN!WA2bniM$0J(#;MR>uIJ^uvgD3GS^%*ikdW6-!VFUU?JV zZc2)4cMsX@j z5HQ^e3BUzOdm}yC-xA%SY``k$rbfk z;CHqifhU*jfGM@DkYCecD9vl*qr58l6x<8URB=&%{!Cu3RO*MrKZ4VO}V6R0a zZw3Eg^0iKWM1dcTYZ0>N899=r6?+adUiBKPciJw}L$=1f4cs^bio&cr9baLF>6#BM z(F}EXe-`F=f_@`A7+Q&|QaZ??Txp_dB#lg!NH=t3$G8&06MFhwR=Iu*Im0s_b2B@| znW>X}sy~m#EW)&6E&!*0%}8UAS)wjt+A(io#wGI@Z2S+Ms1Cxl%YVE800007ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 literal 0 HcmV?d00001 diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c8f9ed8f5cee1c98386d13b17e89f719e83555b2 GIT binary patch literal 1895 zcmV-t2blPYP)FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;QgjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 literal 0 HcmV?d00001 diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..75b2d164a5a98e212cca15ea7bf2ab5de5108680 GIT binary patch literal 3831 zcmVjJBgitF5mAp-i>4+KS_oR{|13AP->1TD4=w)g|)JHOx|a2Wk1Va z!k)vP$UcQ#mdj%wNQoaJ!w>jv_6&JPyutpQps?s5dmDQ>`%?Bvj>o<%kYG!YW6H-z zu`g$@mp`;qDR!51QaS}|ZToSuAGcJ7$2HF0z`ln4t!#Yg46>;vGG9N9{V@9z#}6v* zfP?}r6b{*-C*)(S>NECI_E~{QYzN5SXRmVnP<=gzP+_Sp(Aza_hKlZ{C1D&l*(7IKXxQC1Z9#6wx}YrGcn~g%;icdw>T0Rf^w0{ z$_wn1J+C0@!jCV<%Go5LA45e{5gY9PvZp8uM$=1}XDI+9m7!A95L>q>>oe0$nC->i zeexUIvq%Uk<-$>DiDb?!In)lAmtuMWxvWlk`2>4lNuhSsjAf2*2tjT`y;@d}($o)S zn(+W&hJ1p0xy@oxP%AM15->wPLp{H!k)BdBD$toBpJh+crWdsNV)qsHaqLg2_s|Ih z`8E9z{E3sA!}5aKu?T!#enD(wLw?IT?k-yWVHZ8Akz4k5(TZJN^zZgm&zM28sfTD2BYJ|Fde3Xzh;;S` z=GXTnY4Xc)8nYoz6&vF;P7{xRF-{|2Xs5>a5)@BrnQ}I(_x7Cgpx#5&Td^4Q9_FnQ zX5so*;#8-J8#c$OlA&JyPp$LKUhC~-e~Ij!L%uSMu!-VZG7Hx-L{m2DVR2i=GR(_% zCVD!4N`I)&Q5S`?P&fQZ=4#Dgt_v2-DzkT}K(9gF0L(owe-Id$Rc2qZVLqI_M_DyO z9@LC#U28_LU{;wGZ&))}0R2P4MhajKCd^K#D+JJ&JIXZ_p#@+7J9A&P<0kdRujtQ_ zOy>3=C$kgi6$0pW06KaLz!21oOryKM3ZUOWqppndxfH}QpgjEJ`j7Tzn5bk6K&@RA?vl##y z$?V~1E(!wB5rH`>3nc&@)|#<1dN2cMzzm=PGhQ|Yppne(C-Vlt450IXc`J4R0W@I7 zd1e5uW6juvO%ni(WX7BsKx3MLngO7rHO;^R5I~0^nE^9^E_eYLgiR9&KnJ)pBbfno zSVnW$0R+&6jOOsZ82}nJ126+c|%svPo;TeUku<2G7%?$oft zyaO;tVo}(W)VsTUhq^XmFi#2z%-W9a{7mXn{uzivYQ_d6b7VJG{77naW(vHt-uhnY zVN#d!JTqVh(7r-lhtXVU6o})aZbDt_;&wJVGl2FKYFBFpU-#9U)z#(A%=IVnqytR$SY-sO( z($oNE09{D^@OuYPz&w~?9>Fl5`g9u&ecFGhqX=^#fmR=we0CJw+5xna*@oHnkahk+ z9aWeE3v|An+O5%?4fA&$Fgu~H_YmqR!yIU!bFCk4!#pAj%(lI(A5n)n@Id#M)O9Yx zJU9oKy{sRAIV3=5>(s8n{8ryJ!;ho}%pn6hZKTKbqk=&m=f*UnK$zW3YQP*)pw$O* zIfLA^!-bmBl6%d_n$#tP8Zd_(XdA*z*WH|E_yILwjtI~;jK#v-6jMl^?<%Y%`gvpwv&cFb$||^v4D&V=aNy?NGo620jL3VZnA%s zH~I|qPzB~e(;p;b^gJr7Ure#7?8%F0m4vzzPy^^(q4q1OdthF}Fi*RmVZN1OwTsAP zn9CZP`FazX3^kG(KodIZ=Kty8DLTy--UKfa1$6XugS zk%6v$Kmxt6U!YMx0JQ)0qX*{CXwZZk$vEROidEc7=J-1;peNat!vS<3P-FT5po>iE z!l3R+<`#x|+_hw!HjQGV=8!q|76y8L7N8gP3$%0kfush|u0uU^?dKBaeRSBUpOZ0c z62;D&Mdn2}N}xHRFTRI?zRv=>=AjHgH}`2k4WK=#AHB)UFrR-J87GgX*x5fL^W2#d z=(%K8-oZfMO=i{aWRDg=FX}UubM4eotRDcn;OR#{3q=*?3mE3_oJ-~prjhxh%PgQT zyn)Qozaq0@o&|LEgS{Ind4Swsr;b`u185hZPOBLL<`d2%^Yp1?oL)=jnLi;Zo0ZDliTtQ^b5SmfIMe{T==zZkbvn$KTQGlbG8w}s@M3TZnde;1Am46P3juKb zl9GU&3F=q`>j!`?SyH#r@O59%@aMX^rx}Nxe<>NqpUp5=lX1ojGDIR*-D^SDuvCKF z?3$xG(gVUsBERef_YjPFl^rU9EtD{pt z0CXwpN7BN3!8>hajGaTVk-wl=9rxmfWtIhC{mheHgStLi^+Nz12a?4r(fz)?3A%at zMlvQmL<2-R)-@G1wJ0^zQK%mR=r4d{Y3fHp){nWXUL#|CqXl(+v+qDh>FkF9`eWrW zfr^D%LNfOcTNvtx0JXR35J0~Jpi2#P3Q&80w+nqNfc}&G0A~*)lGHKv=^FE+b(37|)zL;KLF>oiGfb(?&1 zV3XRu!Sw>@quKiab%g6jun#oZ%!>V#A%+lNc?q>6+VvyAn=kf_6z^(TZUa4Eelh{{ zqFX-#dY(EV@7l$NE&kv9u9BR8&Ojd#ZGJ6l8_BW}^r?DIS_rU2(XaGOK z225E@kH5Opf+CgD^{y29jD4gHbGf{1MD6ggQ&%>UG4WyPh5q_tb`{@_34B?xfSO*| zZv8!)q;^o-bz`MuxXk*G^}(6)ACb@=Lfs`Hxoh>`Y0NE8QRQ!*p|SH@{r8=%RKd4p z+#Ty^-0kb=-H-O`nAA3_6>2z(D=~Tbs(n8LHxD0`R0_ATFqp-SdY3(bZ3;VUM?J=O zKCNsxsgt@|&nKMC=*+ZqmLHhX1KHbAJs{nGVMs6~TiF%Q)P@>!koa$%oS zjXa=!5>P`vC-a}ln!uH1ooeI&v?=?v7?1n~P(wZ~0>xWxd_Aw;+}9#eULM7M8&E?Y zC-ZLhi3RoM92SXUb-5i-Lmt5_rfjE{6y^+24`y$1lywLyHO!)Boa7438K4#iLe?rh z2O~YGSgFUBH?og*6=r9rme=peP~ah`(8Zt7V)j5!V0KPFf_mebo3z95U8(up$-+EA^9dTRLq>Yl)YMBuch9%=e5B`Vnb>o zt03=kq;k2TgGe4|lGne&zJa~h(UGutjP_zr?a7~#b)@15XNA>Dj(m=gg2Q5V4-$)D|Q9}R#002ovPDHLkV1o7DH3k3x literal 0 HcmV?d00001 diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..c4df70d39da7941ef3f6dcb7f06a192d8dcb308d GIT binary patch literal 1888 zcmV-m2cP(fP)x~L`~4d)Rspd&<9kFh{hn*KP1LP0~$;u(LfAu zp%fx&qLBcRHx$G|3q(bv@+b;o0*D|jwD-Q9uQR(l*ST}s+uPgQ-MeFwZ#GS?b332? z&Tk$&_miXn3IGq)AmQ)3sisq{raD4(k*bHvpCe-TdWq^NRTEVM)i9xbgQ&ccnUVx* zEY%vS%gDcSg=!tuIK8$Th2_((_h^+7;R|G{n06&O2#6%LK`a}n?h_fL18btz<@lFG za}xS}u?#DBMB> zw^b($1Z)`9G?eP95EKi&$eOy@K%h;ryrR3la%;>|o*>CgB(s>dDcNOXg}CK9SPmD? zmr-s{0wRmxUnbDrYfRvnZ@d z6johZ2sMX{YkGSKWd}m|@V7`Degt-43=2M?+jR%8{(H$&MLLmS;-|JxnX2pnz;el1jsvqQz}pGSF<`mqEXRQ5sC4#BbwnB_4` zc5bFE-Gb#JV3tox9fp-vVEN{(tOCpRse`S+@)?%pz+zVJXSooTrNCUg`R6`hxwb{) zC@{O6MKY8tfZ5@!yy=p5Y|#+myRL=^{tc(6YgAnkg3I(Cd!r5l;|;l-MQ8B`;*SCE z{u)uP^C$lOPM z5d~UhKhRRmvv{LIa^|oavk1$QiEApSrP@~Jjbg`<*dW4TO?4qG%a%sTPUFz(QtW5( zM)lA+5)0TvH~aBaOAs|}?u2FO;yc-CZ1gNM1dAxJ?%m?YsGR`}-xk2*dxC}r5j$d* zE!#Vtbo69h>V4V`BL%_&$} z+oJAo@jQ^Tk`;%xw-4G>hhb&)B?##U+(6Fi7nno`C<|#PVA%$Y{}N-?(Gc$1%tr4Pc}}hm~yY#fTOe!@v9s-ik$dX~|ygArPhByaXn8 zpI^FUjNWMsTFKTP3X7m?UK)3m zp6rI^_zxRYrx6_QmhoWoDR`fp4R7gu6;gdO)!KexaoO2D88F9x#TM1(9Bn7g;|?|o z)~$n&Lh#hCP6_LOPD>a)NmhW})LADx2kq=X7}7wYRj-0?dXr&bHaRWCfSqvzFa=sn z-8^gSyn-RmH=BZ{AJZ~!8n5621GbUJV7Qvs%JNv&$%Q17s_X%s-41vAPfIR>;x0Wlqr5?09S>x#%Qkt>?(&XjFRY}*L6BeQ3 z<6XEBh^S7>AbwGm@XP{RkeEKj6@_o%oV?hDuUpUJ+r#JZO?!IUc;r0R?>mi)*ZpQ) z#((dn=A#i_&EQn|hd)N$#A*fjBFuiHcYvo?@y1 z5|fV=a^a~d!c-%ZbMNqkMKiSzM{Yq=7_c&1H!mXk60Uv32dV;vMg&-kQ)Q{+PFtwc zj|-uQ;b^gts??J*9VxxOro}W~Q9j4Em|zSRv)(WSO9$F$s=Ydu%Q+5DOid~lwk&we zY%W(Z@ofdwPHncEZzZgmqS|!gTj3wQq9rxQy+^eNYKr1mj&?tm@wkO*9@UtnRMG>c aR{jt9+;fr}hV%pg00001^@s67{VYS000c7NklQEG_j zup^)eW&WUIApqy$=APz8jE@awGp)!bsTjDbrJO`$x^ZR^dr;>)LW>{ zs70vpsD38v)19rI=GNk1b(0?Js9~rjsQsu*K;@SD40RB-3^gKU-MYC7G!Bw{fZsqp zih4iIi;Hr_xZ033Iu{sQxLS=}yBXgLMn40d++>aQ0#%8D1EbGZp7+ z5=mK?t31BkVYbGOxE9`i748x`YgCMwL$qMsChbSGSE1`p{nSmadR zcQ#R)(?!~dmtD0+D2!K zR9%!Xp1oOJzm(vbLvT^$IKp@+W2=-}qTzTgVtQ!#Y7Gxz}stUIm<1;oBQ^Sh2X{F4ibaOOx;5ZGSNK z0maF^@(UtV$=p6DXLgRURwF95C=|U8?osGhgOED*b z7woJ_PWXBD>V-NjQAm{~T%sjyJ{5tn2f{G%?J!KRSrrGvQ1(^`YLA5B!~eycY(e5_ z*%aa{at13SxC(=7JT7$IQF~R3sy`Nn%EMv!$-8ZEAryB*yB1k&stni)=)8-ODo41g zkJu~roIgAih94tb=YsL%iH5@^b~kU9M-=aqgXIrbtxMpFy5mekFm#edF9z7RQ6V}R zBIhbXs~pMzt0VWy1Fi$^fh+1xxLDoK09&5&MJl(q#THjPm(0=z2H2Yfm^a&E)V+a5 zbi>08u;bJsDRUKR9(INSc7XyuWv(JsD+BB*0hS)FO&l&7MdViuur@-<-EHw>kHRGY zqoT}3fDv2-m{NhBG8X}+rgOEZ;amh*DqN?jEfQdqxdj08`Sr=C-KmT)qU1 z+9Cl)a1mgXxhQiHVB}l`m;-RpmKy?0*|yl?FXvJkFxuu!fKlcmz$kN(a}i*saM3nr z0!;a~_%Xqy24IxA2rz<+08=B-Q|2PT)O4;EaxP^6qixOv7-cRh?*T?zZU`{nIM-at zTKYWr9rJ=tppQ9I#Z#mLgINVB!pO-^FOcvFw6NhV0gztuO?g ztoA*C-52Q-Z-P#xB4HAY3KQVd%dz1S4PA3vHp0aa=zAO?FCt zC_GaTyVBg2F!bBr3U@Zy2iJgIAt>1sf$JWA9kh{;L+P*HfUBX1Zy{4MgNbDfBV_ly z!y#+753arsZUt@366jIC0klaC@ckuk!qu=pAyf7&QmiBUT^L1&tOHzsK)4n|pmrVT zs2($4=?s~VejTFHbFdDOwG;_58LkIj1Fh@{glkO#F1>a==ymJS$z;gdedT1zPx4Kj ztjS`y_C}%af-RtpehdQDt3a<=W5C4$)9W@QAse;WUry$WYmr51ml9lkeunUrE`-3e zmq1SgSOPNEE-Mf+AGJ$g0M;3@w!$Ej;hMh=v=I+Lpz^n%Pg^MgwyqOkNyu2c^of)C z1~ALor3}}+RiF*K4+4{(1%1j3pif1>sv0r^mTZ?5Jd-It!tfPfiG_p$AY*Vfak%FG z4z#;wLtw&E&?}w+eKG^=#jF7HQzr8rV0mY<1YAJ_uGz~$E13p?F^fPSzXSn$8UcI$ z8er9{5w5iv0qf8%70zV71T1IBB1N}R5Kp%NO0=5wJalZt8;xYp;b{1K) zHY>2wW-`Sl{=NpR%iu3(u6l&)rc%%cSA#aV7WCowfbFR4wcc{LQZv~o1u_`}EJA3>ki`?9CKYTA!rhO)if*zRdd}Kn zEPfYbhoVE~!FI_2YbC5qAj1kq;xP6%J8+?2PAs?`V3}nyFVD#sV3+uP`pi}{$l9U^ zSz}_M9f7RgnnRhaoIJgT8us!1aB&4!*vYF07Hp&}L zCRlop0oK4DL@ISz{2_BPlezc;xj2|I z23RlDNpi9LgTG_#(w%cMaS)%N`e>~1&a3<{Xy}>?WbF>OOLuO+j&hc^YohQ$4F&ze z+hwnro1puQjnKm;vFG~o>`kCeUIlkA-2tI?WBKCFLMBY=J{hpSsQ=PDtU$=duS_hq zHpymHt^uuV1q@uc4bFb{MdG*|VoW@15Osrqt2@8ll0qO=j*uOXn{M0UJX#SUztui9FN4)K3{9!y8PC-AHHvpVTU;x|-7P+taAtyglk#rjlH2 z5Gq8ik}BPaGiM{#Woyg;*&N9R2{J0V+WGB69cEtH7F?U~Kbi6ksi*`CFXsi931q7Y zGO82?whBhN%w1iDetv%~wM*Y;E^)@Vl?VDj-f*RX>{;o_=$fU!&KAXbuadYZ46Zbg z&6jMF=49$uL^73y;;N5jaHYv)BTyfh&`qVLYn?`o6BCA_z-0niZz=qPG!vonK3MW_ zo$V96zM!+kJRs{P-5-rQVse0VBH*n6A58)4uc&gfHMa{gIhV2fGf{st>E8sKyP-$8zp~wJX^A*@DI&-;8>gANXZj zU)R+Y)PB?=)a|Kj>8NXEu^S_h^7R`~Q&7*Kn!xyvzVv&^>?^iu;S~R2e-2fJx-oUb cX)(b1KSk$MOV07*qoM6N<$f&6$jw%VRuvdN2+38CZWny1cRtlsl+0_KtW)EU14Ei(F!UtWuj4IK+3{sK@>rh zs1Z;=(DD&U6+tlyL?UnHVN^&g6QhFi2#HS+*qz;(>63G(`|jRtW|nz$Pv7qTovP!^ zP_jES{mr@O-02w%!^a?^1ZP!_KmQiz0L~jZ=W@Qt`8wzOoclQsAS<5YdH;a(4bGLE zk8s}1If(PSIgVi!XE!5kA?~z*sobvNyohr;=Q_@h2@$6Flyej3J)D-6YfheRGl`HEcPk|~huT_2-U?PfL=4BPV)f1o!%rQ!NMt_MYw-5bUSwQ9Z&zC>u zOrl~UJglJNa%f50Ok}?WB{on`Ci`p^Y!xBA?m@rcJXLxtrE0FhRF3d*ir>yzO|BD$ z3V}HpFcCh6bTzY}Nt_(W%QYd3NG)jJ4<`F<1Od) zfQblTdC&h2lCz`>y?>|9o2CdvC8qZeIZt%jN;B7Hdn2l*k4M4MFEtq`q_#5?}c$b$pf_3y{Y!cRDafZBEj-*OD|gz#PBDeu3QoueOesLzB+O zxjf2wvf6Wwz>@AiOo2mO4=TkAV+g~%_n&R;)l#!cBxjuoD$aS-`IIJv7cdX%2{WT7 zOm%5rs(wqyPE^k5SIpUZ!&Lq4<~%{*>_Hu$2|~Xa;iX*tz8~G6O3uFOS?+)tWtdi| zV2b#;zRN!m@H&jd=!$7YY6_}|=!IU@=SjvGDFtL;aCtw06U;-v^0%k0FOyESt z1Wv$={b_H&8FiRV?MrzoHWd>%v6KTRU;-v^Miiz+@q`(BoT!+<37CKhoKb)|8!+RG z6BQFU^@fRW;s8!mOf2QViKQGk0TVER6EG1`#;Nm39Do^PoT!+<37AD!%oJe86(=et zZ~|sLzU>V-qYiU6V8$0GmU7_K8|Fd0B?+9Un1BhKAz#V~Fk^`mJtlCX#{^8^M8!me z8Yg;8-~>!e<-iG;h*0B1kBKm}hItVGY6WnjVpgnTTAC$rqQ^v)4KvOtpY|sIj@WYg zyw##ZZ5AC2IKNC;^hwg9BPk0wLStlmBr;E|$5GoAo$&Ui_;S9WY62n3)i49|T%C#i017z3J=$RF|KyZWnci*@lW4 z=AKhNN6+m`Q!V3Ye68|8y@%=am>YD0nG99M)NWc20%)gwO!96j7muR}Fr&54SxKP2 zP30S~lt=a*qDlbu3+Av57=9v&vr<6g0&`!8E2fq>I|EJGKs}t|{h7+KT@)LfIV-3K zK)r_fr2?}FFyn*MYoLC>oV-J~eavL2ho4a4^r{E-8m2hi>~hA?_vIG4a*KT;2eyl1 zh_hUvUJpNCFwBvRq5BI*srSle>c6%n`#VNsyC|MGa{(P&08p=C9+WUw9Hl<1o9T4M zdD=_C0F7#o8A_bRR?sFNmU0R6tW`ElnF8p53IdHo#S9(JoZCz}fHwJ6F<&?qrpVqE zte|m%89JQD+XwaPU#%#lVs-@-OL);|MdfINd6!XwP2h(eyafTUsoRkA%&@fe?9m@jw-v(yTTiV2(*fthQH9}SqmsRPVnwwbV$1E(_lkmo&S zF-truCU914_$jpqjr(>Ha4HkM4YMT>m~NosUu&UZ>zirfHo%N6PPs9^_o$WqPA0#5 z%tG>qFCL+b*0s?sZ;Sht0nE7Kl>OVXy=gjWxxK;OJ3yGd7-pZf7JYNcZo2*1SF`u6 zHJyRRxGw9mDlOiXqVMsNe#WX`fC`vrtjSQ%KmLcl(lC>ZOQzG^%iql2w-f_K@r?OE zwCICifM#L-HJyc7Gm>Ern?+Sk3&|Khmu4(~3qa$(m6Ub^U0E5RHq49za|XklN#?kP zl;EstdW?(_4D>kwjWy2f!LM)y?F94kyU3`W!6+AyId-89v}sXJpuic^NLL7GJItl~ zsiuB98AI-(#Mnm|=A-R6&2fwJ0JVSY#Q>&3$zFh|@;#%0qeF=j5Ajq@4i0tIIW z&}sk$&fGwoJpe&u-JeGLi^r?dO`m=y(QO{@h zQqAC7$rvz&5+mo3IqE?h=a~6m>%r5Quapvzq;{y~p zJpyXOBgD9VrW7@#p6l7O?o3feml(DtSL>D^R) zZUY%T2b0-vBAFN7VB;M88!~HuOXi4KcI6aRQ&h|XQ0A?m%j2=l1f0cGP}h(oVfJ`N zz#PpmFC*ieab)zJK<4?^k=g%OjPnkANzbAbmGZHoVRk*mTfm75s_cWVa`l*f$B@xu z5E*?&@seIo#*Y~1rBm!7sF9~~u6Wrj5oICUOuz}CS)jdNIznfzCA(stJ(7$c^e5wN z?lt>eYgbA!kvAR7zYSD&*r1$b|(@;9dcZ^67R0 zXAXJKa|5Sdmj!g578Nwt6d$sXuc&MWezA0Whd`94$h{{?1IwXP4)Tx4obDK%xoFZ_Z zjjHJ_P@R_e5blG@yEjnaJb`l;s%Lb2&=8$&Ct-fV`E^4CUs)=jTk!I}2d&n!f@)bm z@ z_4Dc86+3l2*p|~;o-Sb~oXb_RuLmoifDU^&Te$*FevycC0*nE3Xws8gsWp|Rj2>SM zns)qcYj?^2sd8?N!_w~4v+f-HCF|a$TNZDoNl$I1Uq87euoNgKb6&r26TNrfkUa@o zfdiFA@p{K&mH3b8i!lcoz)V{n8Q@g(vR4ns4r6w;K z>1~ecQR0-<^J|Ndg5fvVUM9g;lbu-){#ghGw(fg>L zh)T5Ljb%lWE;V9L!;Cqk>AV1(rULYF07ZBJbGb9qbSoLAd;in9{)95YqX$J43-dY7YU*k~vrM25 zxh5_IqO0LYZW%oxQ5HOzmk4x{atE*vipUk}sh88$b2tn?!ujEHn`tQLe&vo}nMb&{ zio`xzZ&GG6&ZyN3jnaQy#iVqXE9VT(3tWY$n-)uWDQ|tc{`?fq2F`oQ{;d3aWPg4Hp-(iE{ry>MIPWL> iW8Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 000000000000..89c2725b70f1 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/camera/camera_avfoundation/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 000000000000..f2e259c7c939 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Base.lproj/Main.storyboard b/packages/camera/camera_avfoundation/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 000000000000..f3c28516fb38 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Info.plist b/packages/camera/camera_avfoundation/example/ios/Runner/Info.plist new file mode 100644 index 000000000000..ff2e341a1803 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Runner/Info.plist @@ -0,0 +1,56 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + camera_example + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSApplicationCategoryType + + LSRequiresIPhoneOS + + NSCameraUsageDescription + Can I use the camera please? Only for demo purpose of the app + NSMicrophoneUsageDescription + Only for demo purpose of the app + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/main.m b/packages/camera/camera_avfoundation/example/ios/Runner/main.m new file mode 100644 index 000000000000..d1224fea37ed --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Runner/main.m @@ -0,0 +1,19 @@ +// 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 +#import +#import "AppDelegate.h" + +int main(int argc, char *argv[]) { + @autoreleasepool { + // The setup logic in `AppDelegate::didFinishLaunchingWithOptions:` eventually sends camera + // operations on the background queue, which would run concurrently with the test cases during + // unit tests, making the debugging process confusing. This setup is actually not necessary for + // the unit tests, so it is better to skip the AppDelegate when running unit tests. + BOOL isTesting = NSClassFromString(@"XCTestCase") != nil; + return UIApplicationMain(argc, argv, nil, + isTesting ? nil : NSStringFromClass([AppDelegate class])); + } +} diff --git a/packages/camera/camera/example/ios/RunnerTests/AvailableCamerasTest.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTest.m similarity index 100% rename from packages/camera/camera/example/ios/RunnerTests/AvailableCamerasTest.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTest.m diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m similarity index 100% rename from packages/camera/camera/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraExposureTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.m similarity index 100% rename from packages/camera/camera/example/ios/RunnerTests/CameraExposureTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.m diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraFocusTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.m similarity index 100% rename from packages/camera/camera/example/ios/RunnerTests/CameraFocusTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.m diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraMethodChannelTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraMethodChannelTests.m similarity index 100% rename from packages/camera/camera/example/ios/RunnerTests/CameraMethodChannelTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraMethodChannelTests.m diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraOrientationTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraOrientationTests.m similarity index 100% rename from packages/camera/camera/example/ios/RunnerTests/CameraOrientationTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraOrientationTests.m diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraPermissionTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.m similarity index 100% rename from packages/camera/camera/example/ios/RunnerTests/CameraPermissionTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.m diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraPreviewPauseTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.m similarity index 100% rename from packages/camera/camera/example/ios/RunnerTests/CameraPreviewPauseTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.m diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraPropertiesTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.m similarity index 100% rename from packages/camera/camera/example/ios/RunnerTests/CameraPropertiesTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.m diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraTestUtils.h b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.h similarity index 100% rename from packages/camera/camera/example/ios/RunnerTests/CameraTestUtils.h rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.h diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraTestUtils.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m similarity index 100% rename from packages/camera/camera/example/ios/RunnerTests/CameraTestUtils.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraUtilTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraUtilTests.m similarity index 100% rename from packages/camera/camera/example/ios/RunnerTests/CameraUtilTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraUtilTests.m diff --git a/packages/camera/camera/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m similarity index 100% rename from packages/camera/camera/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m diff --git a/packages/camera/camera/example/ios/RunnerTests/FLTCamSampleBufferTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSampleBufferTests.m similarity index 100% rename from packages/camera/camera/example/ios/RunnerTests/FLTCamSampleBufferTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSampleBufferTests.m diff --git a/packages/camera/camera/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m similarity index 100% rename from packages/camera/camera/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m diff --git a/packages/camera/camera/example/ios/RunnerTests/Info.plist b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Info.plist similarity index 100% rename from packages/camera/camera/example/ios/RunnerTests/Info.plist rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/Info.plist diff --git a/packages/camera/camera/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.h b/packages/camera/camera_avfoundation/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.h similarity index 100% rename from packages/camera/camera/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.h rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.h diff --git a/packages/camera/camera/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.m similarity index 100% rename from packages/camera/camera/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.m diff --git a/packages/camera/camera/example/ios/RunnerTests/QueueUtilsTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.m similarity index 100% rename from packages/camera/camera/example/ios/RunnerTests/QueueUtilsTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.m diff --git a/packages/camera/camera/example/ios/RunnerTests/StreamingTest.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/StreamingTest.m similarity index 100% rename from packages/camera/camera/example/ios/RunnerTests/StreamingTest.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/StreamingTest.m diff --git a/packages/camera/camera/example/ios/RunnerTests/ThreadSafeEventChannelTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeEventChannelTests.m similarity index 100% rename from packages/camera/camera/example/ios/RunnerTests/ThreadSafeEventChannelTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeEventChannelTests.m diff --git a/packages/camera/camera/example/ios/RunnerTests/ThreadSafeFlutterResultTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeFlutterResultTests.m similarity index 100% rename from packages/camera/camera/example/ios/RunnerTests/ThreadSafeFlutterResultTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeFlutterResultTests.m diff --git a/packages/camera/camera/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m similarity index 100% rename from packages/camera/camera/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m diff --git a/packages/camera/camera/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m similarity index 100% rename from packages/camera/camera/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m diff --git a/packages/camera/camera_avfoundation/example/lib/main.dart b/packages/camera/camera_avfoundation/example/lib/main.dart new file mode 100644 index 000000000000..c0181a5d36a1 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/lib/main.dart @@ -0,0 +1,1091 @@ +// 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 'dart:async'; +import 'dart:io'; + +import 'package:camera/camera.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:video_player/video_player.dart'; + +/// Camera example home widget. +class CameraExampleHome extends StatefulWidget { + /// Default Constructor + const CameraExampleHome({Key? key}) : super(key: key); + + @override + State createState() { + return _CameraExampleHomeState(); + } +} + +/// Returns a suitable camera icon for [direction]. +IconData getCameraLensIcon(CameraLensDirection direction) { + switch (direction) { + case CameraLensDirection.back: + return Icons.camera_rear; + case CameraLensDirection.front: + return Icons.camera_front; + case CameraLensDirection.external: + return Icons.camera; + default: + throw ArgumentError('Unknown lens direction'); + } +} + +void _logError(String code, String? message) { + if (message != null) { + print('Error: $code\nError Message: $message'); + } else { + print('Error: $code'); + } +} + +class _CameraExampleHomeState extends State + with WidgetsBindingObserver, TickerProviderStateMixin { + CameraController? controller; + XFile? imageFile; + XFile? videoFile; + VideoPlayerController? videoController; + VoidCallback? videoPlayerListener; + bool enableAudio = true; + double _minAvailableExposureOffset = 0.0; + double _maxAvailableExposureOffset = 0.0; + double _currentExposureOffset = 0.0; + late AnimationController _flashModeControlRowAnimationController; + late Animation _flashModeControlRowAnimation; + late AnimationController _exposureModeControlRowAnimationController; + late Animation _exposureModeControlRowAnimation; + late AnimationController _focusModeControlRowAnimationController; + late Animation _focusModeControlRowAnimation; + double _minAvailableZoom = 1.0; + double _maxAvailableZoom = 1.0; + double _currentScale = 1.0; + double _baseScale = 1.0; + + // Counting pointers (number of user fingers on screen) + int _pointers = 0; + + @override + void initState() { + super.initState(); + _ambiguate(WidgetsBinding.instance)?.addObserver(this); + + _flashModeControlRowAnimationController = AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + ); + _flashModeControlRowAnimation = CurvedAnimation( + parent: _flashModeControlRowAnimationController, + curve: Curves.easeInCubic, + ); + _exposureModeControlRowAnimationController = AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + ); + _exposureModeControlRowAnimation = CurvedAnimation( + parent: _exposureModeControlRowAnimationController, + curve: Curves.easeInCubic, + ); + _focusModeControlRowAnimationController = AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + ); + _focusModeControlRowAnimation = CurvedAnimation( + parent: _focusModeControlRowAnimationController, + curve: Curves.easeInCubic, + ); + } + + @override + void dispose() { + _ambiguate(WidgetsBinding.instance)?.removeObserver(this); + _flashModeControlRowAnimationController.dispose(); + _exposureModeControlRowAnimationController.dispose(); + super.dispose(); + } + + // #docregion AppLifecycle + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + final CameraController? cameraController = controller; + + // App state changed before we got the chance to initialize. + if (cameraController == null || !cameraController.value.isInitialized) { + return; + } + + if (state == AppLifecycleState.inactive) { + cameraController.dispose(); + } else if (state == AppLifecycleState.resumed) { + onNewCameraSelected(cameraController.description); + } + } + // #enddocregion AppLifecycle + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Camera example'), + ), + body: Column( + children: [ + Expanded( + child: Container( + decoration: BoxDecoration( + color: Colors.black, + border: Border.all( + color: + controller != null && controller!.value.isRecordingVideo + ? Colors.redAccent + : Colors.grey, + width: 3.0, + ), + ), + child: Padding( + padding: const EdgeInsets.all(1.0), + child: Center( + child: _cameraPreviewWidget(), + ), + ), + ), + ), + _captureControlRowWidget(), + _modeControlRowWidget(), + Padding( + padding: const EdgeInsets.all(5.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + _cameraTogglesRowWidget(), + _thumbnailWidget(), + ], + ), + ), + ], + ), + ); + } + + /// Display the preview from the camera (or a message if the preview is not available). + Widget _cameraPreviewWidget() { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isInitialized) { + return const Text( + 'Tap a camera', + style: TextStyle( + color: Colors.white, + fontSize: 24.0, + fontWeight: FontWeight.w900, + ), + ); + } else { + return Listener( + onPointerDown: (_) => _pointers++, + onPointerUp: (_) => _pointers--, + child: CameraPreview( + controller!, + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onScaleStart: _handleScaleStart, + onScaleUpdate: _handleScaleUpdate, + onTapDown: (TapDownDetails details) => + onViewFinderTap(details, constraints), + ); + }), + ), + ); + } + } + + void _handleScaleStart(ScaleStartDetails details) { + _baseScale = _currentScale; + } + + Future _handleScaleUpdate(ScaleUpdateDetails details) async { + // When there are not exactly two fingers on screen don't scale + if (controller == null || _pointers != 2) { + return; + } + + _currentScale = (_baseScale * details.scale) + .clamp(_minAvailableZoom, _maxAvailableZoom); + + await controller!.setZoomLevel(_currentScale); + } + + /// Display the thumbnail of the captured image or video. + Widget _thumbnailWidget() { + final VideoPlayerController? localVideoController = videoController; + + return Expanded( + child: Align( + alignment: Alignment.centerRight, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (localVideoController == null && imageFile == null) + Container() + else + SizedBox( + width: 64.0, + height: 64.0, + child: (localVideoController == null) + ? ( + // The captured image on the web contains a network-accessible URL + // pointing to a location within the browser. It may be displayed + // either with Image.network or Image.memory after loading the image + // bytes to memory. + kIsWeb + ? Image.network(imageFile!.path) + : Image.file(File(imageFile!.path))) + : Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.pink)), + child: Center( + child: AspectRatio( + aspectRatio: + localVideoController.value.size != null + ? localVideoController.value.aspectRatio + : 1.0, + child: VideoPlayer(localVideoController)), + ), + ), + ), + ], + ), + ), + ); + } + + /// Display a bar with buttons to change the flash and exposure modes + Widget _modeControlRowWidget() { + return Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + IconButton( + icon: const Icon(Icons.flash_on), + color: Colors.blue, + onPressed: controller != null ? onFlashModeButtonPressed : null, + ), + // The exposure and focus mode are currently not supported on the web. + ...!kIsWeb + ? [ + IconButton( + icon: const Icon(Icons.exposure), + color: Colors.blue, + onPressed: controller != null + ? onExposureModeButtonPressed + : null, + ), + IconButton( + icon: const Icon(Icons.filter_center_focus), + color: Colors.blue, + onPressed: + controller != null ? onFocusModeButtonPressed : null, + ) + ] + : [], + IconButton( + icon: Icon(enableAudio ? Icons.volume_up : Icons.volume_mute), + color: Colors.blue, + onPressed: controller != null ? onAudioModeButtonPressed : null, + ), + IconButton( + icon: Icon(controller?.value.isCaptureOrientationLocked ?? false + ? Icons.screen_lock_rotation + : Icons.screen_rotation), + color: Colors.blue, + onPressed: controller != null + ? onCaptureOrientationLockButtonPressed + : null, + ), + ], + ), + _flashModeControlRowWidget(), + _exposureModeControlRowWidget(), + _focusModeControlRowWidget(), + ], + ); + } + + Widget _flashModeControlRowWidget() { + return SizeTransition( + sizeFactor: _flashModeControlRowAnimation, + child: ClipRect( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + IconButton( + icon: const Icon(Icons.flash_off), + color: controller?.value.flashMode == FlashMode.off + ? Colors.orange + : Colors.blue, + onPressed: controller != null + ? () => onSetFlashModeButtonPressed(FlashMode.off) + : null, + ), + IconButton( + icon: const Icon(Icons.flash_auto), + color: controller?.value.flashMode == FlashMode.auto + ? Colors.orange + : Colors.blue, + onPressed: controller != null + ? () => onSetFlashModeButtonPressed(FlashMode.auto) + : null, + ), + IconButton( + icon: const Icon(Icons.flash_on), + color: controller?.value.flashMode == FlashMode.always + ? Colors.orange + : Colors.blue, + onPressed: controller != null + ? () => onSetFlashModeButtonPressed(FlashMode.always) + : null, + ), + IconButton( + icon: const Icon(Icons.highlight), + color: controller?.value.flashMode == FlashMode.torch + ? Colors.orange + : Colors.blue, + onPressed: controller != null + ? () => onSetFlashModeButtonPressed(FlashMode.torch) + : null, + ), + ], + ), + ), + ); + } + + Widget _exposureModeControlRowWidget() { + final ButtonStyle styleAuto = TextButton.styleFrom( + primary: controller?.value.exposureMode == ExposureMode.auto + ? Colors.orange + : Colors.blue, + ); + final ButtonStyle styleLocked = TextButton.styleFrom( + primary: controller?.value.exposureMode == ExposureMode.locked + ? Colors.orange + : Colors.blue, + ); + + return SizeTransition( + sizeFactor: _exposureModeControlRowAnimation, + child: ClipRect( + child: Container( + color: Colors.grey.shade50, + child: Column( + children: [ + const Center( + child: Text('Exposure Mode'), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + TextButton( + style: styleAuto, + onPressed: controller != null + ? () => + onSetExposureModeButtonPressed(ExposureMode.auto) + : null, + onLongPress: () { + if (controller != null) { + controller!.setExposurePoint(null); + showInSnackBar('Resetting exposure point'); + } + }, + child: const Text('AUTO'), + ), + TextButton( + style: styleLocked, + onPressed: controller != null + ? () => + onSetExposureModeButtonPressed(ExposureMode.locked) + : null, + child: const Text('LOCKED'), + ), + TextButton( + style: styleLocked, + onPressed: controller != null + ? () => controller!.setExposureOffset(0.0) + : null, + child: const Text('RESET OFFSET'), + ), + ], + ), + const Center( + child: Text('Exposure Offset'), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + Text(_minAvailableExposureOffset.toString()), + Slider( + value: _currentExposureOffset, + min: _minAvailableExposureOffset, + max: _maxAvailableExposureOffset, + label: _currentExposureOffset.toString(), + onChanged: _minAvailableExposureOffset == + _maxAvailableExposureOffset + ? null + : setExposureOffset, + ), + Text(_maxAvailableExposureOffset.toString()), + ], + ), + ], + ), + ), + ), + ); + } + + Widget _focusModeControlRowWidget() { + final ButtonStyle styleAuto = TextButton.styleFrom( + primary: controller?.value.focusMode == FocusMode.auto + ? Colors.orange + : Colors.blue, + ); + final ButtonStyle styleLocked = TextButton.styleFrom( + primary: controller?.value.focusMode == FocusMode.locked + ? Colors.orange + : Colors.blue, + ); + + return SizeTransition( + sizeFactor: _focusModeControlRowAnimation, + child: ClipRect( + child: Container( + color: Colors.grey.shade50, + child: Column( + children: [ + const Center( + child: Text('Focus Mode'), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + TextButton( + style: styleAuto, + onPressed: controller != null + ? () => onSetFocusModeButtonPressed(FocusMode.auto) + : null, + onLongPress: () { + if (controller != null) { + controller!.setFocusPoint(null); + } + showInSnackBar('Resetting focus point'); + }, + child: const Text('AUTO'), + ), + TextButton( + style: styleLocked, + onPressed: controller != null + ? () => onSetFocusModeButtonPressed(FocusMode.locked) + : null, + child: const Text('LOCKED'), + ), + ], + ), + ], + ), + ), + ), + ); + } + + /// Display the control bar with buttons to take pictures and record videos. + Widget _captureControlRowWidget() { + final CameraController? cameraController = controller; + + return Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + IconButton( + icon: const Icon(Icons.camera_alt), + color: Colors.blue, + onPressed: cameraController != null && + cameraController.value.isInitialized && + !cameraController.value.isRecordingVideo + ? onTakePictureButtonPressed + : null, + ), + IconButton( + icon: const Icon(Icons.videocam), + color: Colors.blue, + onPressed: cameraController != null && + cameraController.value.isInitialized && + !cameraController.value.isRecordingVideo + ? onVideoRecordButtonPressed + : null, + ), + IconButton( + icon: cameraController != null && + cameraController.value.isRecordingPaused + ? const Icon(Icons.play_arrow) + : const Icon(Icons.pause), + color: Colors.blue, + onPressed: cameraController != null && + cameraController.value.isInitialized && + cameraController.value.isRecordingVideo + ? (cameraController.value.isRecordingPaused) + ? onResumeButtonPressed + : onPauseButtonPressed + : null, + ), + IconButton( + icon: const Icon(Icons.stop), + color: Colors.red, + onPressed: cameraController != null && + cameraController.value.isInitialized && + cameraController.value.isRecordingVideo + ? onStopButtonPressed + : null, + ), + IconButton( + icon: const Icon(Icons.pause_presentation), + color: + cameraController != null && cameraController.value.isPreviewPaused + ? Colors.red + : Colors.blue, + onPressed: + cameraController == null ? null : onPausePreviewButtonPressed, + ), + ], + ); + } + + /// Display a row of toggle to select the camera (or a message if no camera is available). + Widget _cameraTogglesRowWidget() { + final List toggles = []; + + void onChanged(CameraDescription? description) { + if (description == null) { + return; + } + + onNewCameraSelected(description); + } + + if (_cameras.isEmpty) { + _ambiguate(SchedulerBinding.instance)?.addPostFrameCallback((_) async { + showInSnackBar('No camera found.'); + }); + return const Text('None'); + } else { + for (final CameraDescription cameraDescription in _cameras) { + toggles.add( + SizedBox( + width: 90.0, + child: RadioListTile( + title: Icon(getCameraLensIcon(cameraDescription.lensDirection)), + groupValue: controller?.description, + value: cameraDescription, + onChanged: + controller != null && controller!.value.isRecordingVideo + ? null + : onChanged, + ), + ), + ); + } + } + + return Row(children: toggles); + } + + String timestamp() => DateTime.now().millisecondsSinceEpoch.toString(); + + void showInSnackBar(String message) { + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text(message))); + } + + void onViewFinderTap(TapDownDetails details, BoxConstraints constraints) { + if (controller == null) { + return; + } + + final CameraController cameraController = controller!; + + final Offset offset = Offset( + details.localPosition.dx / constraints.maxWidth, + details.localPosition.dy / constraints.maxHeight, + ); + cameraController.setExposurePoint(offset); + cameraController.setFocusPoint(offset); + } + + Future onNewCameraSelected(CameraDescription cameraDescription) async { + final CameraController? oldController = controller; + if (oldController != null) { + // `controller` needs to be set to null before getting disposed, + // to avoid a race condition when we use the controller that is being + // disposed. This happens when camera permission dialog shows up, + // which triggers `didChangeAppLifecycleState`, which disposes and + // re-creates the controller. + controller = null; + await oldController.dispose(); + } + + final CameraController cameraController = CameraController( + cameraDescription, + kIsWeb ? ResolutionPreset.max : ResolutionPreset.medium, + enableAudio: enableAudio, + imageFormatGroup: ImageFormatGroup.jpeg, + ); + + controller = cameraController; + + // If the controller is updated then update the UI. + cameraController.addListener(() { + if (mounted) { + setState(() {}); + } + if (cameraController.value.hasError) { + showInSnackBar( + 'Camera error ${cameraController.value.errorDescription}'); + } + }); + + try { + await cameraController.initialize(); + await Future.wait(>[ + // The exposure mode is currently not supported on the web. + ...!kIsWeb + ? >[ + cameraController.getMinExposureOffset().then( + (double value) => _minAvailableExposureOffset = value), + cameraController + .getMaxExposureOffset() + .then((double value) => _maxAvailableExposureOffset = value) + ] + : >[], + cameraController + .getMaxZoomLevel() + .then((double value) => _maxAvailableZoom = value), + cameraController + .getMinZoomLevel() + .then((double value) => _minAvailableZoom = value), + ]); + } on CameraException catch (e) { + switch (e.code) { + case 'CameraAccessDenied': + showInSnackBar('You have denied camera access.'); + break; + case 'CameraAccessDeniedWithoutPrompt': + // iOS only + showInSnackBar('Please go to Settings app to enable camera access.'); + break; + case 'CameraAccessRestricted': + // iOS only + showInSnackBar('Camera access is restricted.'); + break; + case 'AudioAccessDenied': + showInSnackBar('You have denied audio access.'); + break; + case 'AudioAccessDeniedWithoutPrompt': + // iOS only + showInSnackBar('Please go to Settings app to enable audio access.'); + break; + case 'AudioAccessRestricted': + // iOS only + showInSnackBar('Audio access is restricted.'); + break; + case 'cameraPermission': + // Android & web only + showInSnackBar('Unknown permission error.'); + break; + default: + _showCameraException(e); + break; + } + } + + if (mounted) { + setState(() {}); + } + } + + void onTakePictureButtonPressed() { + takePicture().then((XFile? file) { + if (mounted) { + setState(() { + imageFile = file; + videoController?.dispose(); + videoController = null; + }); + if (file != null) { + showInSnackBar('Picture saved to ${file.path}'); + } + } + }); + } + + void onFlashModeButtonPressed() { + if (_flashModeControlRowAnimationController.value == 1) { + _flashModeControlRowAnimationController.reverse(); + } else { + _flashModeControlRowAnimationController.forward(); + _exposureModeControlRowAnimationController.reverse(); + _focusModeControlRowAnimationController.reverse(); + } + } + + void onExposureModeButtonPressed() { + if (_exposureModeControlRowAnimationController.value == 1) { + _exposureModeControlRowAnimationController.reverse(); + } else { + _exposureModeControlRowAnimationController.forward(); + _flashModeControlRowAnimationController.reverse(); + _focusModeControlRowAnimationController.reverse(); + } + } + + void onFocusModeButtonPressed() { + if (_focusModeControlRowAnimationController.value == 1) { + _focusModeControlRowAnimationController.reverse(); + } else { + _focusModeControlRowAnimationController.forward(); + _flashModeControlRowAnimationController.reverse(); + _exposureModeControlRowAnimationController.reverse(); + } + } + + void onAudioModeButtonPressed() { + enableAudio = !enableAudio; + if (controller != null) { + onNewCameraSelected(controller!.description); + } + } + + Future onCaptureOrientationLockButtonPressed() async { + try { + if (controller != null) { + final CameraController cameraController = controller!; + if (cameraController.value.isCaptureOrientationLocked) { + await cameraController.unlockCaptureOrientation(); + showInSnackBar('Capture orientation unlocked'); + } else { + await cameraController.lockCaptureOrientation(); + showInSnackBar( + 'Capture orientation locked to ${cameraController.value.lockedCaptureOrientation.toString().split('.').last}'); + } + } + } on CameraException catch (e) { + _showCameraException(e); + } + } + + void onSetFlashModeButtonPressed(FlashMode mode) { + setFlashMode(mode).then((_) { + if (mounted) { + setState(() {}); + } + showInSnackBar('Flash mode set to ${mode.toString().split('.').last}'); + }); + } + + void onSetExposureModeButtonPressed(ExposureMode mode) { + setExposureMode(mode).then((_) { + if (mounted) { + setState(() {}); + } + showInSnackBar('Exposure mode set to ${mode.toString().split('.').last}'); + }); + } + + void onSetFocusModeButtonPressed(FocusMode mode) { + setFocusMode(mode).then((_) { + if (mounted) { + setState(() {}); + } + showInSnackBar('Focus mode set to ${mode.toString().split('.').last}'); + }); + } + + void onVideoRecordButtonPressed() { + startVideoRecording().then((_) { + if (mounted) { + setState(() {}); + } + }); + } + + void onStopButtonPressed() { + stopVideoRecording().then((XFile? file) { + if (mounted) { + setState(() {}); + } + if (file != null) { + showInSnackBar('Video recorded to ${file.path}'); + videoFile = file; + _startVideoPlayer(); + } + }); + } + + Future onPausePreviewButtonPressed() async { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isInitialized) { + showInSnackBar('Error: select a camera first.'); + return; + } + + if (cameraController.value.isPreviewPaused) { + await cameraController.resumePreview(); + } else { + await cameraController.pausePreview(); + } + + if (mounted) { + setState(() {}); + } + } + + void onPauseButtonPressed() { + pauseVideoRecording().then((_) { + if (mounted) { + setState(() {}); + } + showInSnackBar('Video recording paused'); + }); + } + + void onResumeButtonPressed() { + resumeVideoRecording().then((_) { + if (mounted) { + setState(() {}); + } + showInSnackBar('Video recording resumed'); + }); + } + + Future startVideoRecording() async { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isInitialized) { + showInSnackBar('Error: select a camera first.'); + return; + } + + if (cameraController.value.isRecordingVideo) { + // A recording is already started, do nothing. + return; + } + + try { + await cameraController.startVideoRecording(); + } on CameraException catch (e) { + _showCameraException(e); + return; + } + } + + Future stopVideoRecording() async { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isRecordingVideo) { + return null; + } + + try { + return cameraController.stopVideoRecording(); + } on CameraException catch (e) { + _showCameraException(e); + return null; + } + } + + Future pauseVideoRecording() async { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isRecordingVideo) { + return; + } + + try { + await cameraController.pauseVideoRecording(); + } on CameraException catch (e) { + _showCameraException(e); + rethrow; + } + } + + Future resumeVideoRecording() async { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isRecordingVideo) { + return; + } + + try { + await cameraController.resumeVideoRecording(); + } on CameraException catch (e) { + _showCameraException(e); + rethrow; + } + } + + Future setFlashMode(FlashMode mode) async { + if (controller == null) { + return; + } + + try { + await controller!.setFlashMode(mode); + } on CameraException catch (e) { + _showCameraException(e); + rethrow; + } + } + + Future setExposureMode(ExposureMode mode) async { + if (controller == null) { + return; + } + + try { + await controller!.setExposureMode(mode); + } on CameraException catch (e) { + _showCameraException(e); + rethrow; + } + } + + Future setExposureOffset(double offset) async { + if (controller == null) { + return; + } + + setState(() { + _currentExposureOffset = offset; + }); + try { + offset = await controller!.setExposureOffset(offset); + } on CameraException catch (e) { + _showCameraException(e); + rethrow; + } + } + + Future setFocusMode(FocusMode mode) async { + if (controller == null) { + return; + } + + try { + await controller!.setFocusMode(mode); + } on CameraException catch (e) { + _showCameraException(e); + rethrow; + } + } + + Future _startVideoPlayer() async { + if (videoFile == null) { + return; + } + + final VideoPlayerController vController = kIsWeb + ? VideoPlayerController.network(videoFile!.path) + : VideoPlayerController.file(File(videoFile!.path)); + + videoPlayerListener = () { + if (videoController != null && videoController!.value.size != null) { + // Refreshing the state to update video player with the correct ratio. + if (mounted) { + setState(() {}); + } + videoController!.removeListener(videoPlayerListener!); + } + }; + vController.addListener(videoPlayerListener!); + await vController.setLooping(true); + await vController.initialize(); + await videoController?.dispose(); + if (mounted) { + setState(() { + imageFile = null; + videoController = vController; + }); + } + await vController.play(); + } + + Future takePicture() async { + final CameraController? cameraController = controller; + if (cameraController == null || !cameraController.value.isInitialized) { + showInSnackBar('Error: select a camera first.'); + return null; + } + + if (cameraController.value.isTakingPicture) { + // A capture is already pending, do nothing. + return null; + } + + try { + final XFile file = await cameraController.takePicture(); + return file; + } on CameraException catch (e) { + _showCameraException(e); + return null; + } + } + + void _showCameraException(CameraException e) { + _logError(e.code, e.description); + showInSnackBar('Error: ${e.code}\n${e.description}'); + } +} + +/// CameraApp is the Main Application. +class CameraApp extends StatelessWidget { + /// Default Constructor + const CameraApp({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return const MaterialApp( + home: CameraExampleHome(), + ); + } +} + +List _cameras = []; + +Future main() async { + // Fetch the available cameras before initializing the app. + try { + WidgetsFlutterBinding.ensureInitialized(); + _cameras = await availableCameras(); + } on CameraException catch (e) { + _logError(e.code, e.description); + } + runApp(const CameraApp()); +} + +/// This allows a value of type T or T? to be treated as a value of type T?. +/// +/// We use this so that APIs that have become non-nullable can still be used +/// with `!` and `?` on the stable branch. +// TODO(ianh): Remove this once we roll stable in late 2021. +T? _ambiguate(T? value) => value; diff --git a/packages/camera/camera_avfoundation/example/pubspec.yaml b/packages/camera/camera_avfoundation/example/pubspec.yaml new file mode 100644 index 000000000000..e9ae2c74a6be --- /dev/null +++ b/packages/camera/camera_avfoundation/example/pubspec.yaml @@ -0,0 +1,32 @@ +name: camera_example +description: Demonstrates how to use the camera plugin. +publish_to: none + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.8.0" + +dependencies: + camera: + # When depending on this package from a real application you should use: + # camera: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + flutter: + sdk: flutter + path_provider: ^2.0.0 + video_player: ^2.1.4 + +dev_dependencies: + build_runner: ^2.1.10 + flutter_driver: + sdk: flutter + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/camera/camera_avfoundation/example/test_driver/integration_test.dart b/packages/camera/camera_avfoundation/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4ec97e66d36c --- /dev/null +++ b/packages/camera/camera_avfoundation/example/test_driver/integration_test.dart @@ -0,0 +1,64 @@ +// 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 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:flutter_driver/flutter_driver.dart'; + +const String _examplePackage = 'io.flutter.plugins.cameraexample'; + +Future main() async { + if (!(Platform.isLinux || Platform.isMacOS)) { + print('This test must be run on a POSIX host. Skipping...'); + exit(0); + } + final bool adbExists = + Process.runSync('which', ['adb']).exitCode == 0; + if (!adbExists) { + print(r'This test needs ADB to exist on the $PATH. Skipping...'); + exit(0); + } + print('Granting camera permissions...'); + Process.runSync('adb', [ + 'shell', + 'pm', + 'grant', + _examplePackage, + 'android.permission.CAMERA' + ]); + Process.runSync('adb', [ + 'shell', + 'pm', + 'grant', + _examplePackage, + 'android.permission.RECORD_AUDIO' + ]); + print('Starting test.'); + final FlutterDriver driver = await FlutterDriver.connect(); + final String data = await driver.requestData( + null, + timeout: const Duration(minutes: 1), + ); + await driver.close(); + print('Test finished. Revoking camera permissions...'); + Process.runSync('adb', [ + 'shell', + 'pm', + 'revoke', + _examplePackage, + 'android.permission.CAMERA' + ]); + Process.runSync('adb', [ + 'shell', + 'pm', + 'revoke', + _examplePackage, + 'android.permission.RECORD_AUDIO' + ]); + + final Map result = jsonDecode(data) as Map; + exit(result['result'] == 'true' ? 0 : 1); +} diff --git a/packages/camera/camera/ios/Assets/.gitkeep b/packages/camera/camera_avfoundation/ios/Assets/.gitkeep similarity index 100% rename from packages/camera/camera/ios/Assets/.gitkeep rename to packages/camera/camera_avfoundation/ios/Assets/.gitkeep diff --git a/packages/camera/camera/ios/Classes/CameraPermissionUtils.h b/packages/camera/camera_avfoundation/ios/Classes/CameraPermissionUtils.h similarity index 100% rename from packages/camera/camera/ios/Classes/CameraPermissionUtils.h rename to packages/camera/camera_avfoundation/ios/Classes/CameraPermissionUtils.h diff --git a/packages/camera/camera/ios/Classes/CameraPermissionUtils.m b/packages/camera/camera_avfoundation/ios/Classes/CameraPermissionUtils.m similarity index 100% rename from packages/camera/camera/ios/Classes/CameraPermissionUtils.m rename to packages/camera/camera_avfoundation/ios/Classes/CameraPermissionUtils.m diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.h b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.h similarity index 100% rename from packages/camera/camera/ios/Classes/CameraPlugin.h rename to packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.h diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m similarity index 100% rename from packages/camera/camera/ios/Classes/CameraPlugin.m rename to packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.modulemap b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.modulemap similarity index 100% rename from packages/camera/camera/ios/Classes/CameraPlugin.modulemap rename to packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.modulemap diff --git a/packages/camera/camera/ios/Classes/CameraPlugin_Test.h b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin_Test.h similarity index 100% rename from packages/camera/camera/ios/Classes/CameraPlugin_Test.h rename to packages/camera/camera_avfoundation/ios/Classes/CameraPlugin_Test.h diff --git a/packages/camera/camera/ios/Classes/CameraProperties.h b/packages/camera/camera_avfoundation/ios/Classes/CameraProperties.h similarity index 100% rename from packages/camera/camera/ios/Classes/CameraProperties.h rename to packages/camera/camera_avfoundation/ios/Classes/CameraProperties.h diff --git a/packages/camera/camera/ios/Classes/CameraProperties.m b/packages/camera/camera_avfoundation/ios/Classes/CameraProperties.m similarity index 100% rename from packages/camera/camera/ios/Classes/CameraProperties.m rename to packages/camera/camera_avfoundation/ios/Classes/CameraProperties.m diff --git a/packages/camera/camera/ios/Classes/FLTCam.h b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h similarity index 100% rename from packages/camera/camera/ios/Classes/FLTCam.h rename to packages/camera/camera_avfoundation/ios/Classes/FLTCam.h diff --git a/packages/camera/camera/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m similarity index 100% rename from packages/camera/camera/ios/Classes/FLTCam.m rename to packages/camera/camera_avfoundation/ios/Classes/FLTCam.m diff --git a/packages/camera/camera/ios/Classes/FLTCam_Test.h b/packages/camera/camera_avfoundation/ios/Classes/FLTCam_Test.h similarity index 100% rename from packages/camera/camera/ios/Classes/FLTCam_Test.h rename to packages/camera/camera_avfoundation/ios/Classes/FLTCam_Test.h diff --git a/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.h b/packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate.h similarity index 100% rename from packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.h rename to packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate.h diff --git a/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.m b/packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate.m similarity index 100% rename from packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.m rename to packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate.m diff --git a/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate_Test.h b/packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate_Test.h similarity index 100% rename from packages/camera/camera/ios/Classes/FLTSavePhotoDelegate_Test.h rename to packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate_Test.h diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.h b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeEventChannel.h similarity index 100% rename from packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.h rename to packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeEventChannel.h diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.m b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeEventChannel.m similarity index 100% rename from packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.m rename to packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeEventChannel.m diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.h b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeFlutterResult.h similarity index 100% rename from packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.h rename to packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeFlutterResult.h diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeFlutterResult.m similarity index 100% rename from packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m rename to packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeFlutterResult.m diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.h b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeMethodChannel.h similarity index 100% rename from packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.h rename to packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeMethodChannel.h diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.m b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeMethodChannel.m similarity index 100% rename from packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.m rename to packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeMethodChannel.m diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.h b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeTextureRegistry.h similarity index 100% rename from packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.h rename to packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeTextureRegistry.h diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.m b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeTextureRegistry.m similarity index 100% rename from packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.m rename to packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeTextureRegistry.m diff --git a/packages/camera/camera/ios/Classes/QueueUtils.h b/packages/camera/camera_avfoundation/ios/Classes/QueueUtils.h similarity index 100% rename from packages/camera/camera/ios/Classes/QueueUtils.h rename to packages/camera/camera_avfoundation/ios/Classes/QueueUtils.h diff --git a/packages/camera/camera/ios/Classes/QueueUtils.m b/packages/camera/camera_avfoundation/ios/Classes/QueueUtils.m similarity index 100% rename from packages/camera/camera/ios/Classes/QueueUtils.m rename to packages/camera/camera_avfoundation/ios/Classes/QueueUtils.m diff --git a/packages/camera/camera/ios/Classes/camera-umbrella.h b/packages/camera/camera_avfoundation/ios/Classes/camera-umbrella.h similarity index 100% rename from packages/camera/camera/ios/Classes/camera-umbrella.h rename to packages/camera/camera_avfoundation/ios/Classes/camera-umbrella.h diff --git a/packages/camera/camera/ios/camera.podspec b/packages/camera/camera_avfoundation/ios/camera.podspec similarity index 100% rename from packages/camera/camera/ios/camera.podspec rename to packages/camera/camera_avfoundation/ios/camera.podspec diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml new file mode 100644 index 000000000000..d1f70d906626 --- /dev/null +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -0,0 +1,39 @@ +name: camera +description: A Flutter plugin for controlling the camera. Supports previewing + the camera feed, capturing images and video, and streaming image buffers to + Dart. +repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 +version: 0.9.7+1 + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.8.0" + +flutter: + plugin: + platforms: + android: + package: io.flutter.plugins.camera + pluginClass: CameraPlugin + ios: + pluginClass: CameraPlugin + web: + default_package: camera_web + +dependencies: + camera_platform_interface: ^2.2.0 + camera_web: ^0.2.1 + flutter: + sdk: flutter + flutter_plugin_android_lifecycle: ^2.0.2 + quiver: ^3.0.0 + +dev_dependencies: + flutter_driver: + sdk: flutter + flutter_test: + sdk: flutter + mockito: ^5.0.0 + plugin_platform_interface: ^2.0.0 + video_player: ^2.0.0 From 15f3a54dae0a8fb5f85803cde2a75aa23ded1c47 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Sat, 28 May 2022 20:31:00 -0400 Subject: [PATCH 02/13] README/CHANGELOG updates --- packages/camera/camera/CHANGELOG.md | 4 + packages/camera/camera_android/CHANGELOG.md | 664 +----------------- packages/camera/camera_android/README.md | 179 +---- .../camera/camera_avfoundation/CHANGELOG.md | 664 +----------------- packages/camera/camera_avfoundation/README.md | 179 +---- 5 files changed, 20 insertions(+), 1670 deletions(-) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 72af38a9f9de..90561b045401 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Moves Android and iOS implementations to federated packages. + ## 0.9.7+1 * Moves streaming implementation to the platform interface package. diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index 72af38a9f9de..c57f301fae95 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,665 +1,3 @@ ## 0.9.7+1 -* Moves streaming implementation to the platform interface package. - -## 0.9.7 - -* Returns all the available cameras on iOS. - -## 0.9.6 - -* Adds audio access permission handling logic on iOS to fix an issue with `prepareForVideoRecording` not awaiting for the audio permission request result. - -## 0.9.5+1 - -* Suppresses warnings for pre-iOS-11 codepaths. - -## 0.9.5 - -* Adds camera access permission handling logic on iOS to fix a related crash when using the camera for the first time. - -## 0.9.4+24 - -* Fixes preview orientation when pausing preview with locked orientation. - -## 0.9.4+23 - -* Minor fixes for new analysis options. - -## 0.9.4+22 - -* Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - -## 0.9.4+21 - -* Fixes README code samples. - -## 0.9.4+20 - -* Fixes an issue with the orientation of videos recorded in landscape on Android. - -## 0.9.4+19 - -* Migrate deprecated Scaffold SnackBar methods to ScaffoldMessenger. - -## 0.9.4+18 - -* Fixes a crash in iOS when streaming on low-performance devices. - -## 0.9.4+17 - -* Removes obsolete information from README, and adds OS support table. - -## 0.9.4+16 - -* Fixes a bug resulting in a `CameraAccessException` that prevents image - capture on some Android devices. - -## 0.9.4+15 - -* Uses dispatch queue for pixel buffer synchronization on iOS. -* Minor iOS internal code cleanup related to queue helper functions. - -## 0.9.4+14 - -* Restores compatibility with Flutter 2.5 and 2.8. - -## 0.9.4+13 - -* Updates iOS camera's photo capture delegate reference on a background queue to prevent potential race conditions, and some related internal code cleanup. - -## 0.9.4+12 - -* Skips unnecessary AppDelegate setup for unit tests on iOS. -* Internal code cleanup for stricter analysis options. - -## 0.9.4+11 - -* Manages iOS camera's orientation-related states on a background queue to prevent potential race conditions. - -## 0.9.4+10 - -* iOS performance improvement by moving file writing from the main queue to a background IO queue. - -## 0.9.4+9 - -* iOS performance improvement by moving sample buffer handling from the main queue to a background session queue. -* Minor iOS internal code cleanup related to camera class and its delegate. -* Minor iOS internal code cleanup related to resolution preset, video format, focus mode, exposure mode and device orientation. -* Minor iOS internal code cleanup related to flash mode. - -## 0.9.4+8 - -* Fixes a bug where ImageFormatGroup was ignored in `startImageStream` on iOS. - -## 0.9.4+7 - -* Fixes a crash in iOS when passing null queue pointer into AVFoundation API due to race condition. -* Minor iOS internal code cleanup related to dispatch queue. - -## 0.9.4+6 - -* Fixes a crash in iOS when using image stream due to calling Flutter engine API on non-main thread. - -## 0.9.4+5 - -* Fixes bug where calling a method after the camera was closed resulted in a Java `IllegalStateException` exception. -* Fixes integration tests. - -## 0.9.4+4 - -* Change Android compileSdkVersion to 31. -* Remove usages of deprecated Android API `CamcorderProfile`. -* Update gradle version to 7.0.2 on Android. - -## 0.9.4+3 - -* Fix registerTexture and result being called on background thread on iOS. - -## 0.9.4+2 - -* Updated package description; -* Refactor unit test on iOS to make it compatible with new restrictions in Xcode 13 which only supports the use of the `XCUIDevice` in Xcode UI tests. - -## 0.9.4+1 - -* Fixed Android implementation throwing IllegalStateException when switching to a different activity. - -## 0.9.4 - -* Add web support by endorsing `package:camera_web`. - -## 0.9.3+1 - -* Remove iOS 9 availability check around ultra high capture sessions. - -## 0.9.3 - -* Update minimum Flutter SDK to 2.5 and iOS deployment target to 9.0. - -## 0.9.2+2 - -* Ensure that setting the exposure offset returns the new offset value on Android. - -## 0.9.2+1 - -* Fixed camera controller throwing an exception when being replaced in the preview widget. - -## 0.9.2 - -* Added functions to pause and resume the camera preview. - -## 0.9.1+1 - -* Replace `device_info` reference with `device_info_plus` in the [README.md](README.md) - -## 0.9.1 - -* Added `lensAperture`, `sensorExposureTime` and `sensorSensitivity` properties to the `CameraImage` dto. - -## 0.9.0 - -* Complete rewrite of Android plugin to fix many capture, focus, flash, orientation and exposure issues. -* Fixed crash when opening front-facing cameras on some legacy android devices like Sony XZ. -* Android Flash mode works with full precapture sequence. -* Updated Android lint settings. - -## 0.8.1+7 - -* Fix device orientation sometimes not affecting the camera preview orientation. - -## 0.8.1+6 - -* Remove references to the Android V1 embedding. - -## 0.8.1+5 - -* Make sure the `setFocusPoint` and `setExposurePoint` coordinates work correctly in all orientations on iOS (instead of only in portrait mode). - -## 0.8.1+4 - -* Silenced warnings that may occur during build when using a very - recent version of Flutter relating to null safety. - -## 0.8.1+3 - -* Do not change camera orientation when iOS device is flat. - -## 0.8.1+2 - -* Fix iOS crash when selecting an unsupported FocusMode. - -## 0.8.1+1 - -* Migrate maven repository from jcenter to mavenCentral. - -## 0.8.1 - -* Solved a rotation issue on iOS which caused the default preview to be displayed as landscape right instead of portrait. - -## 0.8.0 - -* Stable null safety release. -* Solved delay when using the zoom feature on iOS. -* Added a timeout to the pre-capture sequence on Android to prevent crashes when the camera cannot get a focus. -* Updates the example code listed in the [README.md](README.md), so it runs without errors when you simply copy/ paste it into a Flutter App. - -## 0.7.0+4 - -* Fix crash when taking picture with orientation lock - -## 0.7.0+3 - -* Clockwise rotation of focus point in android - -## 0.7.0+2 - -* Fix example reference in README. -* Revert compileSdkVersion back to 29 (from 30) as this is causing problems with add-to-app configurations. - -## 0.7.0+1 - -* Ensure communication from JAVA to Dart is done on the main UI thread. - -## 0.7.0 - -* BREAKING CHANGE: `CameraValue.aspectRatio` now returns `width / height` rather than `height / width`. [(commit)](https://github.com/flutter/plugins/commit/100c7470d4066b1d0f8f7e4ec6d7c943e736f970) - * Added support for capture orientation locking on Android and iOS. - * Fixed camera preview not rotating correctly on Android and iOS. - * Fixed camera preview sometimes appearing stretched on Android and iOS. - * Fixed videos & photos saving with the incorrect rotation on iOS. -* New Features: - * Adds auto focus support for Android and iOS implementations. [(commmit)](https://github.com/flutter/plugins/commit/71a831790220f898bf8120c8a23840ac6e742db5) - * Adds ImageFormat selection for ImageStream and Video(iOS only). [(commit)](https://github.com/flutter/plugins/commit/da1b4638b750a5ff832d7be86a42831c42c6d6c0) -* Bug Fixes: - * Fixes crash when taking a picture on iOS devices without flash. [(commit)](https://github.com/flutter/plugins/commit/831344490984b1feec007afc9c8595d80b6c13f4) - * Make sure the configured zoom scale is copied over to the final capture builder on Android. Fixes the issue where the preview is zoomed but the final picture is not. [(commit)](https://github.com/flutter/plugins/commit/5916f55664e1772a4c3f0c02c5c71fc11e491b76) - * Fixes crash with using inner camera on some Android devices. [(commit)](https://github.com/flutter/plugins/commit/980b674cb4020c1927917426211a87e275346d5e) - * Improved error feedback by differentiating between uninitialized and disposed camera controllers. [(commit)](https://github.com/flutter/plugins/commit/d0b7109f6b00a0eda03506fed2c74cc123ffc6f3) - * Fixes picture captures causing a crash on some Huawei devices. [(commit)](https://github.com/flutter/plugins/commit/6d18db83f00f4861ffe485aba2d1f8aa08845ce6) - -## 0.6.4+5 - -* Update the example app: remove the deprecated `RaisedButton` and `FlatButton` widgets. - -## 0.6.4+4 - -* Set camera auto focus enabled by default. - -## 0.6.4+3 - -* Detect if selected camera supports auto focus and act accordingly on Android. This solves a problem where front facing cameras are not capturing the picture because auto focus is not supported. - -## 0.6.4+2 - -* Set ImageStreamReader listener to null to prevent stale images when streaming images. - -## 0.6.4+1 - -* Added closeCaptureSession() to stopVideoRecording in Camera.java to fix an Android 6 crash. - -## 0.6.4 - -* Adds auto exposure support for Android and iOS implementations. - -## 0.6.3+4 - -* Revert previous dependency update: Changed dependency on camera_platform_interface to >=1.04 <1.1.0. - -## 0.6.3+3 - -* Updated dependency on camera_platform_interface to ^1.2.0. - -## 0.6.3+2 - -* Fixes crash on Android which occurs after video recording has stopped just before taking a picture. - -## 0.6.3+1 - -* Fixes flash & torch modes not working on some Android devices. - -## 0.6.3 - -* Adds torch mode as a flash mode for Android and iOS implementations. - -## 0.6.2+1 - -* Fix the API documentation for the `CameraController.takePicture` method. - -## 0.6.2 - -* Add zoom support for Android and iOS implementations. - -## 0.6.1+1 - -* Added implementation of the `didFinishProcessingPhoto` on iOS which allows saving image metadata (EXIF) on iOS 11 and up. - -## 0.6.1 - -* Add flash support for Android and iOS implementations. - -## 0.6.0+2 - -* Fix outdated links across a number of markdown files ([#3276](https://github.com/flutter/plugins/pull/3276)) - -## 0.6.0+1 - -Updated README to inform users that iOS 10.0+ is needed for use - -## 0.6.0 - -As part of implementing federated architecture and making the interface compatible with the web this version contains the following **breaking changes**: - -Method changes in `CameraController`: -- The `takePicture` method no longer accepts the `path` parameter, but instead returns the captured image as an instance of the `XFile` class; -- The `startVideoRecording` method no longer accepts the `filePath`. Instead the recorded video is now returned as a `XFile` instance when the `stopVideoRecording` method completes; -- The `stopVideoRecording` method now returns the captured video when it completes; -- Added the `buildPreview` method which is now used to implement the CameraPreview widget. - -## 0.5.8+19 - -* Update Flutter SDK constraint. - -## 0.5.8+18 - -* Suppress unchecked warning in Android tests which prevented the tests to compile. - -## 0.5.8+17 - -* Added Android 30 support. - -## 0.5.8+16 - -* Moved package to camera/camera subdir, to allow for federated implementations. - -## 0.5.8+15 - -* Added the `debugCheckIsDisposed` method which can be used in debug mode to validate if the `CameraController` class has been disposed. - -## 0.5.8+14 - -* Changed the order of the setters for `mediaRecorder` in `MediaRecorderBuilder.java` to make it more readable. - -## 0.5.8+13 - -* Added Dartdocs for all public APIs. - -## 0.5.8+12 - -* Added information of video not working correctly on Android emulators to `README.md`. - -## 0.5.8+11 - -* Fix rare nullptr exception on Android. -* Updated README.md with information about handling App lifecycle changes. - -## 0.5.8+10 - -* Suppress the `deprecated_member_use` warning in the example app for `ScaffoldMessenger.showSnackBar`. - -## 0.5.8+9 - -* Update android compileSdkVersion to 29. - -## 0.5.8+8 - -* Fixed garbled audio (in video) by setting audio encoding bitrate. - -## 0.5.8+7 - -* Keep handling deprecated Android v1 classes for backward compatibility. - -## 0.5.8+6 - -* Avoiding uses or overrides a deprecated API in CameraPlugin.java. - -## 0.5.8+5 - -* Fix compilation/availability issues on iOS. - -## 0.5.8+4 - -* Fixed bug caused by casting a `CameraAccessException` on Android. - -## 0.5.8+3 - -* Fix bug in usage example in README.md - -## 0.5.8+2 - -* Post-v2 embedding cleanups. - -## 0.5.8+1 - -* Update lower bound of dart dependency to 2.1.0. - -## 0.5.8 - -* Remove Android dependencies fallback. -* Require Flutter SDK 1.12.13+hotfix.5 or greater. - -## 0.5.7+5 - -* Replace deprecated `getFlutterEngine` call on Android. - -## 0.5.7+4 - -* Add `pedantic` to dev_dependency. - -## 0.5.7+3 - -* Fix an Android crash when permissions are requested multiple times. - -## 0.5.7+2 - -* Remove the deprecated `author:` field from pubspec.yaml -* Migrate the plugin to the pubspec platforms manifest. -* Require Flutter SDK 1.10.0 or greater. - -## 0.5.7+1 - -* Fix example null exception. - -## 0.5.7 - -* Fix unawaited futures. - -## 0.5.6+4 - -* Android: Use CameraDevice.TEMPLATE_RECORD to improve image streaming. - -## 0.5.6+3 - -* Remove AndroidX warning. - -## 0.5.6+2 - -* Include lifecycle dependency as a compileOnly one on Android to resolve - potential version conflicts with other transitive libraries. - -## 0.5.6+1 - -* Android: Use android.arch.lifecycle instead of androidx.lifecycle:lifecycle in `build.gradle` to support apps that has not been migrated to AndroidX. - -## 0.5.6 - -* Add support for the v2 Android embedding. This shouldn't affect existing - functionality. - -## 0.5.5+1 - -* Fix event type check - -## 0.5.5 - -* Define clang modules for iOS. - -## 0.5.4+3 - -* Update and migrate iOS example project. - -## 0.5.4+2 - -* Fix Android NullPointerException on devices with only front-facing camera. - -## 0.5.4+1 - -* Fix Android pause and resume video crash when executing in APIs below 24. - -## 0.5.4 - -* Add feature to pause and resume video recording. - -## 0.5.3+1 - -* Fix too large request code for FragmentActivity users. - -## 0.5.3 - -* Added new quality presets. -* Now all quality presets can be used to control image capture quality. - -## 0.5.2+2 - -* Fix memory leak related to not unregistering stream handler in FlutterEventChannel when disposing camera. - -## 0.5.2+1 - -* Fix bug that prevented video recording with audio. - -## 0.5.2 - -* Added capability to disable audio for the `CameraController`. (e.g. `CameraController(_, _, - enableAudio: false);`) - -## 0.5.1 - -* Can now be compiled with earlier Android sdks below 21 when -`` has been added to the project -`AndroidManifest.xml`. For sdks below 21, the plugin won't be registered and calls to it will throw -a `MissingPluginException.` - -## 0.5.0 - -* **Breaking Change** This plugin no longer handles closing and opening the camera on Android - lifecycle changes. Please use `WidgetsBindingObserver` to control camera resources on lifecycle - changes. See example project for example using `WidgetsBindingObserver`. - -## 0.4.3+2 - -* Bump the minimum Flutter version to 1.2.0. -* Add template type parameter to `invokeMethod` calls. - -## 0.4.3+1 - -* Catch additional `Exception`s from Android and throw as `CameraException`s. - -## 0.4.3 - -* Add capability to prepare the capture session for video recording on iOS. - -## 0.4.2 - -* Add sensor orientation value to `CameraDescription`. - -## 0.4.1 - -* Camera methods are ran in a background thread on iOS. - -## 0.4.0+3 - -* Fixed a crash when the plugin is registered by a background FlutterView. - -## 0.4.0+2 - -* Fix orientation of captured photos when camera is used for the first time on Android. - -## 0.4.0+1 - -* Remove categories. - -## 0.4.0 - -* **Breaking Change** Change iOS image stream format to `ImageFormatGroup.bgra8888` from - `ImageFormatGroup.yuv420`. - -## 0.3.0+4 - -* Fixed bug causing black screen on some Android devices. - -## 0.3.0+3 - -* Log a more detailed warning at build time about the previous AndroidX - migration. - -## 0.3.0+2 - -* Fix issue with calculating iOS image orientation in certain edge cases. - -## 0.3.0+1 - -* Remove initial method call invocation from static camera method. - -## 0.3.0 - -* **Breaking change**. Migrate from the deprecated original Android Support - Library to AndroidX. This shouldn't result in any functional changes, but it - requires any Android apps using this plugin to [also - migrate](https://developer.android.com/jetpack/androidx/migrate) if they're - using the original support library. - -## 0.2.9+1 - -* Fix a crash when failing to start preview. - -## 0.2.9 - -* Save photo orientation data on iOS. - -## 0.2.8 - -* Add access to the image stream from Dart. -* Use `cameraController.startImageStream(listener)` to process the images. - -## 0.2.7 - -* Fix issue with crash when the physical device's orientation is unknown. - -## 0.2.6 - -* Update the camera to use the physical device's orientation instead of the UI - orientation on Android. - -## 0.2.5 - -* Fix preview and video size with satisfying conditions of multiple outputs. - -## 0.2.4 - -* Unregister the activity lifecycle callbacks when disposing the camera. - -## 0.2.3 - -* Added path_provider and video_player as dev dependencies because the example uses them. -* Updated example path_provider version to get Dart 2 support. - -## 0.2.2 - -* iOS image capture is done in high quality (full camera size) - -## 0.2.1 - -* Updated Gradle tooling to match Android Studio 3.1.2. - -## 0.2.0 - -* Added support for video recording. -* Changed the example app to add video recording. - -A lot of **breaking changes** in this version: - -Getter changes: - - Removed `isStarted` - - Renamed `initialized` to `isInitialized` - - Added `isRecordingVideo` - -Method changes: - - Renamed `capture` to `takePicture` - - Removed `start` (the preview starts automatically when `initialize` is called) - - Added `startVideoRecording(String filePath)` - - Removed `stop` (the preview stops automatically when `dispose` is called) - - Added `stopVideoRecording` - -## 0.1.2 - -* Fix Dart 2 runtime errors. - -## 0.1.1 - -* Fix Dart 2 runtime error. - -## 0.1.0 - -* **Breaking change**. Set SDK constraints to match the Flutter beta release. - -## 0.0.4 - -* Revert regression of `CameraController.capture()` introduced in v. 0.0.3. - -## 0.0.3 - -* Improved resource cleanup on Android. Avoids crash on Activity restart. -* Made the Future returned by `CameraController.dispose()` and `CameraController.capture()` actually complete on - Android. - -## 0.0.2 - -* Simplified and upgraded Android project template to Android SDK 27. -* Moved Android package to io.flutter.plugins. -* Fixed warnings from the Dart 2.0 analyzer. - -## 0.0.1 - -* Initial release +* Splits from `camera` as a federated implementation. diff --git a/packages/camera/camera_android/README.md b/packages/camera/camera_android/README.md index ec9d7379c60b..de8897c1727a 100644 --- a/packages/camera/camera_android/README.md +++ b/packages/camera/camera_android/README.md @@ -1,176 +1,11 @@ -# Camera Plugin +# camera\_android - +The Android implementation of [`camera`][1]. -[![pub package](https://img.shields.io/pub/v/camera.svg)](https://pub.dev/packages/camera) +## Usage -A Flutter plugin for iOS, Android and Web allowing access to the device cameras. +This package is [endorsed][2], which means you can simply use `camera` +normally. This package will be automatically included in your app when you do. -| | Android | iOS | Web | -|----------------|---------|----------|------------------------| -| **Support** | SDK 21+ | iOS 10+* | [See `camera_web `][1] | - -## Features - -* Display live camera preview in a widget. -* Snapshots can be captured and saved to a file. -* Record video. -* Add access to the image stream from Dart. - -## Installation - -First, add `camera` as a [dependency in your pubspec.yaml file](https://flutter.dev/using-packages/). - -### iOS - -\* The camera plugin compiles for any version of iOS, but its functionality -requires iOS 10 or higher. If compiling for iOS 9, make sure to programmatically -check the version of iOS running on the device before using any camera plugin features. -The [device_info_plus](https://pub.dev/packages/device_info_plus) plugin, for example, can be used to check the iOS version. - -Add two rows to the `ios/Runner/Info.plist`: - -* one with the key `Privacy - Camera Usage Description` and a usage description. -* and one with the key `Privacy - Microphone Usage Description` and a usage description. - -If editing `Info.plist` as text, add: - -```xml -NSCameraUsageDescription -your usage description here -NSMicrophoneUsageDescription -your usage description here -``` - -### Android - -Change the minimum Android sdk version to 21 (or higher) in your `android/app/build.gradle` file. - -```groovy -minSdkVersion 21 -``` - -It's important to note that the `MediaRecorder` class is not working properly on emulators, as stated in the documentation: https://developer.android.com/reference/android/media/MediaRecorder. Specifically, when recording a video with sound enabled and trying to play it back, the duration won't be correct and you will only see the first frame. - -### Web integration - -For web integration details, see the -[`camera_web` package](https://pub.dev/packages/camera_web). - -### Handling Lifecycle states - -As of version [0.5.0](https://github.com/flutter/plugins/blob/master/packages/camera/CHANGELOG.md#050) of the camera plugin, lifecycle changes are no longer handled by the plugin. This means developers are now responsible to control camera resources when the lifecycle state is updated. Failure to do so might lead to unexpected behavior (for example as described in issue [#39109](https://github.com/flutter/flutter/issues/39109)). Handling lifecycle changes can be done by overriding the `didChangeAppLifecycleState` method like so: - - -```dart -@override -void didChangeAppLifecycleState(AppLifecycleState state) { - final CameraController? cameraController = controller; - - // App state changed before we got the chance to initialize. - if (cameraController == null || !cameraController.value.isInitialized) { - return; - } - - if (state == AppLifecycleState.inactive) { - cameraController.dispose(); - } else if (state == AppLifecycleState.resumed) { - onNewCameraSelected(cameraController.description); - } -} -``` - -### Handling camera access permissions - -Permission errors may be thrown when initializing the camera controller, and you are expected to handle them properly. - -Here is a list of all permission error codes that can be thrown: - -- `CameraAccessDenied`: Thrown when user denies the camera access permission. - -- `CameraAccessDeniedWithoutPrompt`: iOS only for now. Thrown when user has previously denied the permission. iOS does not allow prompting alert dialog a second time. Users will have to go to Settings > Privacy > Camera in order to enable camera access. - -- `CameraAccessRestricted`: iOS only for now. Thrown when camera access is restricted and users cannot grant permission (parental control). - -- `AudioAccessDenied`: Thrown when user denies the audio access permission. - -- `AudioAccessDeniedWithoutPrompt`: iOS only for now. Thrown when user has previously denied the permission. iOS does not allow prompting alert dialog a second time. Users will have to go to Settings > Privacy > Microphone in order to enable audio access. - -- `AudioAccessRestricted`: iOS only for now. Thrown when audio access is restricted and users cannot grant permission (parental control). - -- `cameraPermission`: Android and Web only. A legacy error code for all kinds of camera permission errors. - -### Example - -Here is a small example flutter app displaying a full screen camera preview. - - -```dart -import 'package:camera/camera.dart'; -import 'package:flutter/material.dart'; - -late List _cameras; - -Future main() async { - WidgetsFlutterBinding.ensureInitialized(); - - _cameras = await availableCameras(); - runApp(const CameraApp()); -} - -/// CameraApp is the Main Application. -class CameraApp extends StatefulWidget { - /// Default Constructor - const CameraApp({Key? key}) : super(key: key); - - @override - State createState() => _CameraAppState(); -} - -class _CameraAppState extends State { - late CameraController controller; - - @override - void initState() { - super.initState(); - controller = CameraController(_cameras[0], ResolutionPreset.max); - controller.initialize().then((_) { - if (!mounted) { - return; - } - setState(() {}); - }).catchError((Object e) { - if (e is CameraException) { - switch (e.code) { - case 'CameraAccessDenied': - print('User denied camera access.'); - break; - default: - print('Handle other errors.'); - break; - } - } - }); - } - - @override - void dispose() { - controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - if (!controller.value.isInitialized) { - return Container(); - } - return MaterialApp( - home: CameraPreview(controller), - ); - } -} -``` - -For a more elaborate usage example see [here](https://github.com/flutter/plugins/tree/main/packages/camera/camera/example). - -[1]: https://pub.dev/packages/camera_web#limitations-on-the-web-platform +[1]: https://pub.dev/packages/camera +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index 72af38a9f9de..c57f301fae95 100644 --- a/packages/camera/camera_avfoundation/CHANGELOG.md +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -1,665 +1,3 @@ ## 0.9.7+1 -* Moves streaming implementation to the platform interface package. - -## 0.9.7 - -* Returns all the available cameras on iOS. - -## 0.9.6 - -* Adds audio access permission handling logic on iOS to fix an issue with `prepareForVideoRecording` not awaiting for the audio permission request result. - -## 0.9.5+1 - -* Suppresses warnings for pre-iOS-11 codepaths. - -## 0.9.5 - -* Adds camera access permission handling logic on iOS to fix a related crash when using the camera for the first time. - -## 0.9.4+24 - -* Fixes preview orientation when pausing preview with locked orientation. - -## 0.9.4+23 - -* Minor fixes for new analysis options. - -## 0.9.4+22 - -* Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - -## 0.9.4+21 - -* Fixes README code samples. - -## 0.9.4+20 - -* Fixes an issue with the orientation of videos recorded in landscape on Android. - -## 0.9.4+19 - -* Migrate deprecated Scaffold SnackBar methods to ScaffoldMessenger. - -## 0.9.4+18 - -* Fixes a crash in iOS when streaming on low-performance devices. - -## 0.9.4+17 - -* Removes obsolete information from README, and adds OS support table. - -## 0.9.4+16 - -* Fixes a bug resulting in a `CameraAccessException` that prevents image - capture on some Android devices. - -## 0.9.4+15 - -* Uses dispatch queue for pixel buffer synchronization on iOS. -* Minor iOS internal code cleanup related to queue helper functions. - -## 0.9.4+14 - -* Restores compatibility with Flutter 2.5 and 2.8. - -## 0.9.4+13 - -* Updates iOS camera's photo capture delegate reference on a background queue to prevent potential race conditions, and some related internal code cleanup. - -## 0.9.4+12 - -* Skips unnecessary AppDelegate setup for unit tests on iOS. -* Internal code cleanup for stricter analysis options. - -## 0.9.4+11 - -* Manages iOS camera's orientation-related states on a background queue to prevent potential race conditions. - -## 0.9.4+10 - -* iOS performance improvement by moving file writing from the main queue to a background IO queue. - -## 0.9.4+9 - -* iOS performance improvement by moving sample buffer handling from the main queue to a background session queue. -* Minor iOS internal code cleanup related to camera class and its delegate. -* Minor iOS internal code cleanup related to resolution preset, video format, focus mode, exposure mode and device orientation. -* Minor iOS internal code cleanup related to flash mode. - -## 0.9.4+8 - -* Fixes a bug where ImageFormatGroup was ignored in `startImageStream` on iOS. - -## 0.9.4+7 - -* Fixes a crash in iOS when passing null queue pointer into AVFoundation API due to race condition. -* Minor iOS internal code cleanup related to dispatch queue. - -## 0.9.4+6 - -* Fixes a crash in iOS when using image stream due to calling Flutter engine API on non-main thread. - -## 0.9.4+5 - -* Fixes bug where calling a method after the camera was closed resulted in a Java `IllegalStateException` exception. -* Fixes integration tests. - -## 0.9.4+4 - -* Change Android compileSdkVersion to 31. -* Remove usages of deprecated Android API `CamcorderProfile`. -* Update gradle version to 7.0.2 on Android. - -## 0.9.4+3 - -* Fix registerTexture and result being called on background thread on iOS. - -## 0.9.4+2 - -* Updated package description; -* Refactor unit test on iOS to make it compatible with new restrictions in Xcode 13 which only supports the use of the `XCUIDevice` in Xcode UI tests. - -## 0.9.4+1 - -* Fixed Android implementation throwing IllegalStateException when switching to a different activity. - -## 0.9.4 - -* Add web support by endorsing `package:camera_web`. - -## 0.9.3+1 - -* Remove iOS 9 availability check around ultra high capture sessions. - -## 0.9.3 - -* Update minimum Flutter SDK to 2.5 and iOS deployment target to 9.0. - -## 0.9.2+2 - -* Ensure that setting the exposure offset returns the new offset value on Android. - -## 0.9.2+1 - -* Fixed camera controller throwing an exception when being replaced in the preview widget. - -## 0.9.2 - -* Added functions to pause and resume the camera preview. - -## 0.9.1+1 - -* Replace `device_info` reference with `device_info_plus` in the [README.md](README.md) - -## 0.9.1 - -* Added `lensAperture`, `sensorExposureTime` and `sensorSensitivity` properties to the `CameraImage` dto. - -## 0.9.0 - -* Complete rewrite of Android plugin to fix many capture, focus, flash, orientation and exposure issues. -* Fixed crash when opening front-facing cameras on some legacy android devices like Sony XZ. -* Android Flash mode works with full precapture sequence. -* Updated Android lint settings. - -## 0.8.1+7 - -* Fix device orientation sometimes not affecting the camera preview orientation. - -## 0.8.1+6 - -* Remove references to the Android V1 embedding. - -## 0.8.1+5 - -* Make sure the `setFocusPoint` and `setExposurePoint` coordinates work correctly in all orientations on iOS (instead of only in portrait mode). - -## 0.8.1+4 - -* Silenced warnings that may occur during build when using a very - recent version of Flutter relating to null safety. - -## 0.8.1+3 - -* Do not change camera orientation when iOS device is flat. - -## 0.8.1+2 - -* Fix iOS crash when selecting an unsupported FocusMode. - -## 0.8.1+1 - -* Migrate maven repository from jcenter to mavenCentral. - -## 0.8.1 - -* Solved a rotation issue on iOS which caused the default preview to be displayed as landscape right instead of portrait. - -## 0.8.0 - -* Stable null safety release. -* Solved delay when using the zoom feature on iOS. -* Added a timeout to the pre-capture sequence on Android to prevent crashes when the camera cannot get a focus. -* Updates the example code listed in the [README.md](README.md), so it runs without errors when you simply copy/ paste it into a Flutter App. - -## 0.7.0+4 - -* Fix crash when taking picture with orientation lock - -## 0.7.0+3 - -* Clockwise rotation of focus point in android - -## 0.7.0+2 - -* Fix example reference in README. -* Revert compileSdkVersion back to 29 (from 30) as this is causing problems with add-to-app configurations. - -## 0.7.0+1 - -* Ensure communication from JAVA to Dart is done on the main UI thread. - -## 0.7.0 - -* BREAKING CHANGE: `CameraValue.aspectRatio` now returns `width / height` rather than `height / width`. [(commit)](https://github.com/flutter/plugins/commit/100c7470d4066b1d0f8f7e4ec6d7c943e736f970) - * Added support for capture orientation locking on Android and iOS. - * Fixed camera preview not rotating correctly on Android and iOS. - * Fixed camera preview sometimes appearing stretched on Android and iOS. - * Fixed videos & photos saving with the incorrect rotation on iOS. -* New Features: - * Adds auto focus support for Android and iOS implementations. [(commmit)](https://github.com/flutter/plugins/commit/71a831790220f898bf8120c8a23840ac6e742db5) - * Adds ImageFormat selection for ImageStream and Video(iOS only). [(commit)](https://github.com/flutter/plugins/commit/da1b4638b750a5ff832d7be86a42831c42c6d6c0) -* Bug Fixes: - * Fixes crash when taking a picture on iOS devices without flash. [(commit)](https://github.com/flutter/plugins/commit/831344490984b1feec007afc9c8595d80b6c13f4) - * Make sure the configured zoom scale is copied over to the final capture builder on Android. Fixes the issue where the preview is zoomed but the final picture is not. [(commit)](https://github.com/flutter/plugins/commit/5916f55664e1772a4c3f0c02c5c71fc11e491b76) - * Fixes crash with using inner camera on some Android devices. [(commit)](https://github.com/flutter/plugins/commit/980b674cb4020c1927917426211a87e275346d5e) - * Improved error feedback by differentiating between uninitialized and disposed camera controllers. [(commit)](https://github.com/flutter/plugins/commit/d0b7109f6b00a0eda03506fed2c74cc123ffc6f3) - * Fixes picture captures causing a crash on some Huawei devices. [(commit)](https://github.com/flutter/plugins/commit/6d18db83f00f4861ffe485aba2d1f8aa08845ce6) - -## 0.6.4+5 - -* Update the example app: remove the deprecated `RaisedButton` and `FlatButton` widgets. - -## 0.6.4+4 - -* Set camera auto focus enabled by default. - -## 0.6.4+3 - -* Detect if selected camera supports auto focus and act accordingly on Android. This solves a problem where front facing cameras are not capturing the picture because auto focus is not supported. - -## 0.6.4+2 - -* Set ImageStreamReader listener to null to prevent stale images when streaming images. - -## 0.6.4+1 - -* Added closeCaptureSession() to stopVideoRecording in Camera.java to fix an Android 6 crash. - -## 0.6.4 - -* Adds auto exposure support for Android and iOS implementations. - -## 0.6.3+4 - -* Revert previous dependency update: Changed dependency on camera_platform_interface to >=1.04 <1.1.0. - -## 0.6.3+3 - -* Updated dependency on camera_platform_interface to ^1.2.0. - -## 0.6.3+2 - -* Fixes crash on Android which occurs after video recording has stopped just before taking a picture. - -## 0.6.3+1 - -* Fixes flash & torch modes not working on some Android devices. - -## 0.6.3 - -* Adds torch mode as a flash mode for Android and iOS implementations. - -## 0.6.2+1 - -* Fix the API documentation for the `CameraController.takePicture` method. - -## 0.6.2 - -* Add zoom support for Android and iOS implementations. - -## 0.6.1+1 - -* Added implementation of the `didFinishProcessingPhoto` on iOS which allows saving image metadata (EXIF) on iOS 11 and up. - -## 0.6.1 - -* Add flash support for Android and iOS implementations. - -## 0.6.0+2 - -* Fix outdated links across a number of markdown files ([#3276](https://github.com/flutter/plugins/pull/3276)) - -## 0.6.0+1 - -Updated README to inform users that iOS 10.0+ is needed for use - -## 0.6.0 - -As part of implementing federated architecture and making the interface compatible with the web this version contains the following **breaking changes**: - -Method changes in `CameraController`: -- The `takePicture` method no longer accepts the `path` parameter, but instead returns the captured image as an instance of the `XFile` class; -- The `startVideoRecording` method no longer accepts the `filePath`. Instead the recorded video is now returned as a `XFile` instance when the `stopVideoRecording` method completes; -- The `stopVideoRecording` method now returns the captured video when it completes; -- Added the `buildPreview` method which is now used to implement the CameraPreview widget. - -## 0.5.8+19 - -* Update Flutter SDK constraint. - -## 0.5.8+18 - -* Suppress unchecked warning in Android tests which prevented the tests to compile. - -## 0.5.8+17 - -* Added Android 30 support. - -## 0.5.8+16 - -* Moved package to camera/camera subdir, to allow for federated implementations. - -## 0.5.8+15 - -* Added the `debugCheckIsDisposed` method which can be used in debug mode to validate if the `CameraController` class has been disposed. - -## 0.5.8+14 - -* Changed the order of the setters for `mediaRecorder` in `MediaRecorderBuilder.java` to make it more readable. - -## 0.5.8+13 - -* Added Dartdocs for all public APIs. - -## 0.5.8+12 - -* Added information of video not working correctly on Android emulators to `README.md`. - -## 0.5.8+11 - -* Fix rare nullptr exception on Android. -* Updated README.md with information about handling App lifecycle changes. - -## 0.5.8+10 - -* Suppress the `deprecated_member_use` warning in the example app for `ScaffoldMessenger.showSnackBar`. - -## 0.5.8+9 - -* Update android compileSdkVersion to 29. - -## 0.5.8+8 - -* Fixed garbled audio (in video) by setting audio encoding bitrate. - -## 0.5.8+7 - -* Keep handling deprecated Android v1 classes for backward compatibility. - -## 0.5.8+6 - -* Avoiding uses or overrides a deprecated API in CameraPlugin.java. - -## 0.5.8+5 - -* Fix compilation/availability issues on iOS. - -## 0.5.8+4 - -* Fixed bug caused by casting a `CameraAccessException` on Android. - -## 0.5.8+3 - -* Fix bug in usage example in README.md - -## 0.5.8+2 - -* Post-v2 embedding cleanups. - -## 0.5.8+1 - -* Update lower bound of dart dependency to 2.1.0. - -## 0.5.8 - -* Remove Android dependencies fallback. -* Require Flutter SDK 1.12.13+hotfix.5 or greater. - -## 0.5.7+5 - -* Replace deprecated `getFlutterEngine` call on Android. - -## 0.5.7+4 - -* Add `pedantic` to dev_dependency. - -## 0.5.7+3 - -* Fix an Android crash when permissions are requested multiple times. - -## 0.5.7+2 - -* Remove the deprecated `author:` field from pubspec.yaml -* Migrate the plugin to the pubspec platforms manifest. -* Require Flutter SDK 1.10.0 or greater. - -## 0.5.7+1 - -* Fix example null exception. - -## 0.5.7 - -* Fix unawaited futures. - -## 0.5.6+4 - -* Android: Use CameraDevice.TEMPLATE_RECORD to improve image streaming. - -## 0.5.6+3 - -* Remove AndroidX warning. - -## 0.5.6+2 - -* Include lifecycle dependency as a compileOnly one on Android to resolve - potential version conflicts with other transitive libraries. - -## 0.5.6+1 - -* Android: Use android.arch.lifecycle instead of androidx.lifecycle:lifecycle in `build.gradle` to support apps that has not been migrated to AndroidX. - -## 0.5.6 - -* Add support for the v2 Android embedding. This shouldn't affect existing - functionality. - -## 0.5.5+1 - -* Fix event type check - -## 0.5.5 - -* Define clang modules for iOS. - -## 0.5.4+3 - -* Update and migrate iOS example project. - -## 0.5.4+2 - -* Fix Android NullPointerException on devices with only front-facing camera. - -## 0.5.4+1 - -* Fix Android pause and resume video crash when executing in APIs below 24. - -## 0.5.4 - -* Add feature to pause and resume video recording. - -## 0.5.3+1 - -* Fix too large request code for FragmentActivity users. - -## 0.5.3 - -* Added new quality presets. -* Now all quality presets can be used to control image capture quality. - -## 0.5.2+2 - -* Fix memory leak related to not unregistering stream handler in FlutterEventChannel when disposing camera. - -## 0.5.2+1 - -* Fix bug that prevented video recording with audio. - -## 0.5.2 - -* Added capability to disable audio for the `CameraController`. (e.g. `CameraController(_, _, - enableAudio: false);`) - -## 0.5.1 - -* Can now be compiled with earlier Android sdks below 21 when -`` has been added to the project -`AndroidManifest.xml`. For sdks below 21, the plugin won't be registered and calls to it will throw -a `MissingPluginException.` - -## 0.5.0 - -* **Breaking Change** This plugin no longer handles closing and opening the camera on Android - lifecycle changes. Please use `WidgetsBindingObserver` to control camera resources on lifecycle - changes. See example project for example using `WidgetsBindingObserver`. - -## 0.4.3+2 - -* Bump the minimum Flutter version to 1.2.0. -* Add template type parameter to `invokeMethod` calls. - -## 0.4.3+1 - -* Catch additional `Exception`s from Android and throw as `CameraException`s. - -## 0.4.3 - -* Add capability to prepare the capture session for video recording on iOS. - -## 0.4.2 - -* Add sensor orientation value to `CameraDescription`. - -## 0.4.1 - -* Camera methods are ran in a background thread on iOS. - -## 0.4.0+3 - -* Fixed a crash when the plugin is registered by a background FlutterView. - -## 0.4.0+2 - -* Fix orientation of captured photos when camera is used for the first time on Android. - -## 0.4.0+1 - -* Remove categories. - -## 0.4.0 - -* **Breaking Change** Change iOS image stream format to `ImageFormatGroup.bgra8888` from - `ImageFormatGroup.yuv420`. - -## 0.3.0+4 - -* Fixed bug causing black screen on some Android devices. - -## 0.3.0+3 - -* Log a more detailed warning at build time about the previous AndroidX - migration. - -## 0.3.0+2 - -* Fix issue with calculating iOS image orientation in certain edge cases. - -## 0.3.0+1 - -* Remove initial method call invocation from static camera method. - -## 0.3.0 - -* **Breaking change**. Migrate from the deprecated original Android Support - Library to AndroidX. This shouldn't result in any functional changes, but it - requires any Android apps using this plugin to [also - migrate](https://developer.android.com/jetpack/androidx/migrate) if they're - using the original support library. - -## 0.2.9+1 - -* Fix a crash when failing to start preview. - -## 0.2.9 - -* Save photo orientation data on iOS. - -## 0.2.8 - -* Add access to the image stream from Dart. -* Use `cameraController.startImageStream(listener)` to process the images. - -## 0.2.7 - -* Fix issue with crash when the physical device's orientation is unknown. - -## 0.2.6 - -* Update the camera to use the physical device's orientation instead of the UI - orientation on Android. - -## 0.2.5 - -* Fix preview and video size with satisfying conditions of multiple outputs. - -## 0.2.4 - -* Unregister the activity lifecycle callbacks when disposing the camera. - -## 0.2.3 - -* Added path_provider and video_player as dev dependencies because the example uses them. -* Updated example path_provider version to get Dart 2 support. - -## 0.2.2 - -* iOS image capture is done in high quality (full camera size) - -## 0.2.1 - -* Updated Gradle tooling to match Android Studio 3.1.2. - -## 0.2.0 - -* Added support for video recording. -* Changed the example app to add video recording. - -A lot of **breaking changes** in this version: - -Getter changes: - - Removed `isStarted` - - Renamed `initialized` to `isInitialized` - - Added `isRecordingVideo` - -Method changes: - - Renamed `capture` to `takePicture` - - Removed `start` (the preview starts automatically when `initialize` is called) - - Added `startVideoRecording(String filePath)` - - Removed `stop` (the preview stops automatically when `dispose` is called) - - Added `stopVideoRecording` - -## 0.1.2 - -* Fix Dart 2 runtime errors. - -## 0.1.1 - -* Fix Dart 2 runtime error. - -## 0.1.0 - -* **Breaking change**. Set SDK constraints to match the Flutter beta release. - -## 0.0.4 - -* Revert regression of `CameraController.capture()` introduced in v. 0.0.3. - -## 0.0.3 - -* Improved resource cleanup on Android. Avoids crash on Activity restart. -* Made the Future returned by `CameraController.dispose()` and `CameraController.capture()` actually complete on - Android. - -## 0.0.2 - -* Simplified and upgraded Android project template to Android SDK 27. -* Moved Android package to io.flutter.plugins. -* Fixed warnings from the Dart 2.0 analyzer. - -## 0.0.1 - -* Initial release +* Splits from `camera` as a federated implementation. diff --git a/packages/camera/camera_avfoundation/README.md b/packages/camera/camera_avfoundation/README.md index ec9d7379c60b..a063492e6c15 100644 --- a/packages/camera/camera_avfoundation/README.md +++ b/packages/camera/camera_avfoundation/README.md @@ -1,176 +1,11 @@ -# Camera Plugin +# camera\_avfoundation - +The iOS implementation of [`camera`][1]. -[![pub package](https://img.shields.io/pub/v/camera.svg)](https://pub.dev/packages/camera) +## Usage -A Flutter plugin for iOS, Android and Web allowing access to the device cameras. +This package is [endorsed][2], which means you can simply use `camera` +normally. This package will be automatically included in your app when you do. -| | Android | iOS | Web | -|----------------|---------|----------|------------------------| -| **Support** | SDK 21+ | iOS 10+* | [See `camera_web `][1] | - -## Features - -* Display live camera preview in a widget. -* Snapshots can be captured and saved to a file. -* Record video. -* Add access to the image stream from Dart. - -## Installation - -First, add `camera` as a [dependency in your pubspec.yaml file](https://flutter.dev/using-packages/). - -### iOS - -\* The camera plugin compiles for any version of iOS, but its functionality -requires iOS 10 or higher. If compiling for iOS 9, make sure to programmatically -check the version of iOS running on the device before using any camera plugin features. -The [device_info_plus](https://pub.dev/packages/device_info_plus) plugin, for example, can be used to check the iOS version. - -Add two rows to the `ios/Runner/Info.plist`: - -* one with the key `Privacy - Camera Usage Description` and a usage description. -* and one with the key `Privacy - Microphone Usage Description` and a usage description. - -If editing `Info.plist` as text, add: - -```xml -NSCameraUsageDescription -your usage description here -NSMicrophoneUsageDescription -your usage description here -``` - -### Android - -Change the minimum Android sdk version to 21 (or higher) in your `android/app/build.gradle` file. - -```groovy -minSdkVersion 21 -``` - -It's important to note that the `MediaRecorder` class is not working properly on emulators, as stated in the documentation: https://developer.android.com/reference/android/media/MediaRecorder. Specifically, when recording a video with sound enabled and trying to play it back, the duration won't be correct and you will only see the first frame. - -### Web integration - -For web integration details, see the -[`camera_web` package](https://pub.dev/packages/camera_web). - -### Handling Lifecycle states - -As of version [0.5.0](https://github.com/flutter/plugins/blob/master/packages/camera/CHANGELOG.md#050) of the camera plugin, lifecycle changes are no longer handled by the plugin. This means developers are now responsible to control camera resources when the lifecycle state is updated. Failure to do so might lead to unexpected behavior (for example as described in issue [#39109](https://github.com/flutter/flutter/issues/39109)). Handling lifecycle changes can be done by overriding the `didChangeAppLifecycleState` method like so: - - -```dart -@override -void didChangeAppLifecycleState(AppLifecycleState state) { - final CameraController? cameraController = controller; - - // App state changed before we got the chance to initialize. - if (cameraController == null || !cameraController.value.isInitialized) { - return; - } - - if (state == AppLifecycleState.inactive) { - cameraController.dispose(); - } else if (state == AppLifecycleState.resumed) { - onNewCameraSelected(cameraController.description); - } -} -``` - -### Handling camera access permissions - -Permission errors may be thrown when initializing the camera controller, and you are expected to handle them properly. - -Here is a list of all permission error codes that can be thrown: - -- `CameraAccessDenied`: Thrown when user denies the camera access permission. - -- `CameraAccessDeniedWithoutPrompt`: iOS only for now. Thrown when user has previously denied the permission. iOS does not allow prompting alert dialog a second time. Users will have to go to Settings > Privacy > Camera in order to enable camera access. - -- `CameraAccessRestricted`: iOS only for now. Thrown when camera access is restricted and users cannot grant permission (parental control). - -- `AudioAccessDenied`: Thrown when user denies the audio access permission. - -- `AudioAccessDeniedWithoutPrompt`: iOS only for now. Thrown when user has previously denied the permission. iOS does not allow prompting alert dialog a second time. Users will have to go to Settings > Privacy > Microphone in order to enable audio access. - -- `AudioAccessRestricted`: iOS only for now. Thrown when audio access is restricted and users cannot grant permission (parental control). - -- `cameraPermission`: Android and Web only. A legacy error code for all kinds of camera permission errors. - -### Example - -Here is a small example flutter app displaying a full screen camera preview. - - -```dart -import 'package:camera/camera.dart'; -import 'package:flutter/material.dart'; - -late List _cameras; - -Future main() async { - WidgetsFlutterBinding.ensureInitialized(); - - _cameras = await availableCameras(); - runApp(const CameraApp()); -} - -/// CameraApp is the Main Application. -class CameraApp extends StatefulWidget { - /// Default Constructor - const CameraApp({Key? key}) : super(key: key); - - @override - State createState() => _CameraAppState(); -} - -class _CameraAppState extends State { - late CameraController controller; - - @override - void initState() { - super.initState(); - controller = CameraController(_cameras[0], ResolutionPreset.max); - controller.initialize().then((_) { - if (!mounted) { - return; - } - setState(() {}); - }).catchError((Object e) { - if (e is CameraException) { - switch (e.code) { - case 'CameraAccessDenied': - print('User denied camera access.'); - break; - default: - print('Handle other errors.'); - break; - } - } - }); - } - - @override - void dispose() { - controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - if (!controller.value.isInitialized) { - return Container(); - } - return MaterialApp( - home: CameraPreview(controller), - ); - } -} -``` - -For a more elaborate usage example see [here](https://github.com/flutter/plugins/tree/main/packages/camera/camera/example). - -[1]: https://pub.dev/packages/camera_web#limitations-on-the-web-platform +[1]: https://pub.dev/packages/camera +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin From 18f7d54975704ce751ca62f6dd69bdb8e611c408 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Sun, 29 May 2022 12:24:57 -0400 Subject: [PATCH 03/13] pubspec updates --- packages/camera/camera/pubspec.yaml | 13 ++++++++++--- .../camera_android/example/pubspec.yaml | 4 ++-- packages/camera/camera_android/pubspec.yaml | 17 +++-------------- .../camera_avfoundation/example/pubspec.yaml | 4 ++-- .../camera/camera_avfoundation/pubspec.yaml | 19 +++---------------- 5 files changed, 20 insertions(+), 37 deletions(-) diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index d1f70d906626..dd2ce04d2d61 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -5,6 +5,9 @@ description: A Flutter plugin for controlling the camera. Supports previewing repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 version: 0.9.7+1 +# Temporarily disable publishing to allow moving Android and iOS +# implementations. +publish_to: none environment: sdk: ">=2.14.0 <3.0.0" @@ -14,14 +17,18 @@ flutter: plugin: platforms: android: - package: io.flutter.plugins.camera - pluginClass: CameraPlugin + default_package: camera_android ios: - pluginClass: CameraPlugin + default_package: camera_avfoundation web: default_package: camera_web dependencies: + # Temporary path dependencies to allow moving Android and iOS implementations. + camera_android: + path: ../camera_android + camera_avfoundation: + path: ../camera_avfoundation camera_platform_interface: ^2.2.0 camera_web: ^0.2.1 flutter: diff --git a/packages/camera/camera_android/example/pubspec.yaml b/packages/camera/camera_android/example/pubspec.yaml index e9ae2c74a6be..8b319b099e2e 100644 --- a/packages/camera/camera_android/example/pubspec.yaml +++ b/packages/camera/camera_android/example/pubspec.yaml @@ -7,9 +7,9 @@ environment: flutter: ">=2.8.0" dependencies: - camera: + camera_android: # When depending on this package from a real application you should use: - # camera: ^x.y.z + # camera_android: ^x.y.z # See https://dart.dev/tools/pub/dependencies#version-constraints # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index d1f70d906626..a4c6470a4ab3 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -1,8 +1,6 @@ -name: camera -description: A Flutter plugin for controlling the camera. Supports previewing - the camera feed, capturing images and video, and streaming image buffers to - Dart. -repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera +name: camera_android +description: Android implementation of the camera plugin. +repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 version: 0.9.7+1 @@ -16,24 +14,15 @@ flutter: android: package: io.flutter.plugins.camera pluginClass: CameraPlugin - ios: - pluginClass: CameraPlugin - web: - default_package: camera_web dependencies: camera_platform_interface: ^2.2.0 - camera_web: ^0.2.1 flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.2 - quiver: ^3.0.0 dev_dependencies: flutter_driver: sdk: flutter flutter_test: sdk: flutter - mockito: ^5.0.0 - plugin_platform_interface: ^2.0.0 - video_player: ^2.0.0 diff --git a/packages/camera/camera_avfoundation/example/pubspec.yaml b/packages/camera/camera_avfoundation/example/pubspec.yaml index e9ae2c74a6be..8449e7c5acd0 100644 --- a/packages/camera/camera_avfoundation/example/pubspec.yaml +++ b/packages/camera/camera_avfoundation/example/pubspec.yaml @@ -7,9 +7,9 @@ environment: flutter: ">=2.8.0" dependencies: - camera: + camera_avfoundation: # When depending on this package from a real application you should use: - # camera: ^x.y.z + # camera_avfoundation: ^x.y.z # See https://dart.dev/tools/pub/dependencies#version-constraints # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index d1f70d906626..cac33b33c2f9 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -1,8 +1,6 @@ -name: camera -description: A Flutter plugin for controlling the camera. Supports previewing - the camera feed, capturing images and video, and streaming image buffers to - Dart. -repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera +name: camera_avfoundation +description: iOS implementation of the camera plugin. +repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 version: 0.9.7+1 @@ -13,27 +11,16 @@ environment: flutter: plugin: platforms: - android: - package: io.flutter.plugins.camera - pluginClass: CameraPlugin ios: pluginClass: CameraPlugin - web: - default_package: camera_web dependencies: camera_platform_interface: ^2.2.0 - camera_web: ^0.2.1 flutter: sdk: flutter - flutter_plugin_android_lifecycle: ^2.0.2 - quiver: ^3.0.0 dev_dependencies: flutter_driver: sdk: flutter flutter_test: sdk: flutter - mockito: ^5.0.0 - plugin_platform_interface: ^2.0.0 - video_player: ^2.0.0 From 3ef0a2976f47da4c6d15a75cd2d34af7005909f9 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Sun, 29 May 2022 12:29:54 -0400 Subject: [PATCH 04/13] settings.gradle and podspec fix, and remove old VALID_ARCHS --- packages/camera/camera_android/android/settings.gradle | 2 +- .../ios/{camera.podspec => camera_avfoundation.podspec} | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) rename packages/camera/camera_avfoundation/ios/{camera.podspec => camera_avfoundation.podspec} (76%) diff --git a/packages/camera/camera_android/android/settings.gradle b/packages/camera/camera_android/android/settings.gradle index 5222c9172f70..94a1bae9d6cd 100644 --- a/packages/camera/camera_android/android/settings.gradle +++ b/packages/camera/camera_android/android/settings.gradle @@ -1 +1 @@ -rootProject.name = 'camera' +rootProject.name = 'camera_android' diff --git a/packages/camera/camera_avfoundation/ios/camera.podspec b/packages/camera/camera_avfoundation/ios/camera_avfoundation.podspec similarity index 76% rename from packages/camera/camera_avfoundation/ios/camera.podspec rename to packages/camera/camera_avfoundation/ios/camera_avfoundation.podspec index 098b011a47a9..27f569c8b9be 100644 --- a/packages/camera/camera_avfoundation/ios/camera.podspec +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation.podspec @@ -2,7 +2,7 @@ # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html # Pod::Spec.new do |s| - s.name = 'camera' + s.name = 'camera_avfoundation' s.version = '0.0.1' s.summary = 'Flutter Camera' s.description = <<-DESC @@ -11,13 +11,13 @@ A Flutter plugin to use the camera from your Flutter app. s.homepage = 'https://github.com/flutter/plugins' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/camera' } - s.documentation_url = 'https://pub.dev/packages/camera' + s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/camera_avfoundation' } + s.documentation_url = 'https://pub.dev/packages/camera_avfoundation' s.source_files = 'Classes/**/*.{h,m}' s.public_header_files = 'Classes/**/*.h' s.module_map = 'Classes/CameraPlugin.modulemap' s.dependency 'Flutter' s.platform = :ios, '9.0' - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' } + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } end From d2740b89a538b6c96f3c2bc217103039fd656829 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Sun, 29 May 2022 12:48:15 -0400 Subject: [PATCH 05/13] Fix up iOS native unit tests --- .../ios/Runner.xcodeproj/project.pbxproj | 240 ------------------ .../ios/RunnerTests/AvailableCamerasTest.m | 4 +- ...eraCaptureSessionQueueRaceConditionTests.m | 4 +- .../ios/RunnerTests/CameraExposureTests.m | 2 +- .../ios/RunnerTests/CameraFocusTests.m | 4 +- .../RunnerTests/CameraMethodChannelTests.m | 4 +- .../ios/RunnerTests/CameraOrientationTests.m | 4 +- .../ios/RunnerTests/CameraPermissionTests.m | 4 +- .../ios/RunnerTests/CameraPreviewPauseTests.m | 4 +- .../ios/RunnerTests/CameraPropertiesTests.m | 2 +- .../example/ios/RunnerTests/CameraTestUtils.h | 2 +- .../example/ios/RunnerTests/CameraUtilTests.m | 2 +- .../ios/RunnerTests/FLTCamPhotoCaptureTests.m | 4 +- .../ios/RunnerTests/FLTCamSampleBufferTests.m | 4 +- .../RunnerTests/FLTSavePhotoDelegateTests.m | 4 +- .../MockFLTThreadSafeFlutterResult.m | 2 +- .../example/ios/RunnerTests/QueueUtilsTests.m | 2 +- .../example/ios/RunnerTests/StreamingTest.m | 4 +- .../RunnerTests/ThreadSafeEventChannelTests.m | 2 +- .../ThreadSafeFlutterResultTests.m | 2 +- .../ThreadSafeMethodChannelTests.m | 2 +- .../ThreadSafeTextureRegistryTests.m | 2 +- .../ios/Classes/CameraPlugin.modulemap | 4 +- .../ios/Classes/CameraPlugin_Test.h | 8 +- ...rella.h => camera_avfoundation-umbrella.h} | 0 25 files changed, 38 insertions(+), 278 deletions(-) rename packages/camera/camera_avfoundation/ios/Classes/{camera-umbrella.h => camera_avfoundation-umbrella.h} (100%) diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj index 03c80d79c578..99433b084f27 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj @@ -7,46 +7,16 @@ objects = { /* Begin PBXBuildFile section */ - 033B94BE269C40A200B4DF97 /* CameraMethodChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 033B94BD269C40A200B4DF97 /* CameraMethodChannelTests.m */; }; - 03BB766B2665316900CE5A93 /* CameraFocusTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03BB766A2665316900CE5A93 /* CameraFocusTests.m */; }; - 03F6F8B226CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03F6F8B126CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 236906D1621AE863A5B2E770 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 89D82918721FABF772705DB0 /* libPods-Runner.a */; }; - 25C3919135C3D981E6F800D0 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1944D8072499F3B5E7653D44 /* libPods-RunnerTests.a */; }; - 334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03BB767226653ABE00CE5A93 /* CameraOrientationTests.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 43ED1537282570DE00EB00DE /* AvailableCamerasTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 43ED1536282570DE00EB00DE /* AvailableCamerasTest.m */; }; - 788A065A27B0E02900533D74 /* StreamingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 788A065927B0E02900533D74 /* StreamingTest.m */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - E01EE4A82799F3A5008C1950 /* QueueUtilsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E01EE4A72799F3A5008C1950 /* QueueUtilsTests.m */; }; - E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */; }; - E04F108627A87CA600573D0C /* FLTSavePhotoDelegateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */; }; - E071CF7227B3061B006EF3BA /* FLTCamPhotoCaptureTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */; }; - E071CF7427B31DE4006EF3BA /* FLTCamSampleBufferTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */; }; - E0B0D2BB27DFF2AF00E71E4B /* CameraPermissionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0B0D2BA27DFF2AF00E71E4B /* CameraPermissionTests.m */; }; - E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */; }; - E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */; }; - E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */; }; - E0CDBAC227CD9729002561D9 /* CameraTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */; }; - E0F95E3D27A32AB900699390 /* CameraPropertiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */; }; - E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */; }; - F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */ = {isa = PBXBuildFile; fileRef = F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */; }; /* End PBXBuildFile section */ -/* Begin PBXContainerItemProxy section */ - 03BB766D2665316900CE5A93 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 97C146E61CF9000F007C117D /* Project object */; - proxyType = 1; - remoteGlobalIDString = 97C146ED1CF9000F007C117D; - remoteInfo = Runner; - }; -/* End PBXContainerItemProxy section */ - /* Begin PBXCopyFilesBuildPhase section */ 9705A1C41CF9048500538489 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; @@ -61,20 +31,12 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 033B94BD269C40A200B4DF97 /* CameraMethodChannelTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraMethodChannelTests.m; sourceTree = ""; }; - 03BB76682665316900CE5A93 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 03BB766A2665316900CE5A93 /* CameraFocusTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraFocusTests.m; sourceTree = ""; }; - 03BB766C2665316900CE5A93 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 03BB767226653ABE00CE5A93 /* CameraOrientationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CameraOrientationTests.m; sourceTree = ""; }; - 03F6F8B126CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeFlutterResultTests.m; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 14AE82C910C2A12F2ECB2094 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 1944D8072499F3B5E7653D44 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 43ED1536282570DE00EB00DE /* AvailableCamerasTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AvailableCamerasTest.m; sourceTree = ""; }; 59848A7CA98C1FADF8840207 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - 788A065927B0E02900533D74 /* StreamingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StreamingTest.m; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -89,32 +51,9 @@ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9C5CC6CAD53AD388B2694F3A /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; A24F9E418BA48BCC7409B117 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; - E01EE4A72799F3A5008C1950 /* QueueUtilsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QueueUtilsTests.m; sourceTree = ""; }; - E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CameraCaptureSessionQueueRaceConditionTests.m; sourceTree = ""; }; - E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTSavePhotoDelegateTests.m; sourceTree = ""; }; - E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTCamPhotoCaptureTests.m; sourceTree = ""; }; - E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTCamSampleBufferTests.m; sourceTree = ""; }; - E0B0D2BA27DFF2AF00E71E4B /* CameraPermissionTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPermissionTests.m; sourceTree = ""; }; - E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeMethodChannelTests.m; sourceTree = ""; }; - E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeTextureRegistryTests.m; sourceTree = ""; }; - E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeEventChannelTests.m; sourceTree = ""; }; - E0CDBAC027CD9729002561D9 /* CameraTestUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CameraTestUtils.h; sourceTree = ""; }; - E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraTestUtils.m; sourceTree = ""; }; - E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPropertiesTests.m; sourceTree = ""; }; - E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPreviewPauseTests.m; sourceTree = ""; }; - F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockFLTThreadSafeFlutterResult.h; sourceTree = ""; }; - F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockFLTThreadSafeFlutterResult.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 03BB76652665316900CE5A93 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 25C3919135C3D981E6F800D0 /* libPods-RunnerTests.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 97C146EB1CF9000F007C117D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -126,35 +65,6 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 03BB76692665316900CE5A93 /* RunnerTests */ = { - isa = PBXGroup; - children = ( - 03BB766A2665316900CE5A93 /* CameraFocusTests.m */, - 03BB767226653ABE00CE5A93 /* CameraOrientationTests.m */, - 03BB766C2665316900CE5A93 /* Info.plist */, - 033B94BD269C40A200B4DF97 /* CameraMethodChannelTests.m */, - 03F6F8B126CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m */, - E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */, - E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */, - E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */, - E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */, - E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */, - E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */, - E0B0D2BA27DFF2AF00E71E4B /* CameraPermissionTests.m */, - E01EE4A72799F3A5008C1950 /* QueueUtilsTests.m */, - E0CDBAC027CD9729002561D9 /* CameraTestUtils.h */, - E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */, - E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */, - F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */, - F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */, - E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */, - E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */, - 788A065927B0E02900533D74 /* StreamingTest.m */, - 43ED1536282570DE00EB00DE /* AvailableCamerasTest.m */, - ); - path = RunnerTests; - sourceTree = ""; - }; 3242FD2B467C15C62200632F /* Frameworks */ = { isa = PBXGroup; children = ( @@ -180,7 +90,6 @@ children = ( 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, - 03BB76692665316900CE5A93 /* RunnerTests */, 97C146EF1CF9000F007C117D /* Products */, FD386F00E98D73419C929072 /* Pods */, 3242FD2B467C15C62200632F /* Frameworks */, @@ -191,7 +100,6 @@ isa = PBXGroup; children = ( 97C146EE1CF9000F007C117D /* Runner.app */, - 03BB76682665316900CE5A93 /* RunnerTests.xctest */, ); name = Products; sourceTree = ""; @@ -234,25 +142,6 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 03BB76672665316900CE5A93 /* RunnerTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 03BB76712665316900CE5A93 /* Build configuration list for PBXNativeTarget "RunnerTests" */; - buildPhases = ( - 422786A96136AA9087A2041B /* [CP] Check Pods Manifest.lock */, - 03BB76642665316900CE5A93 /* Sources */, - 03BB76652665316900CE5A93 /* Frameworks */, - 03BB76662665316900CE5A93 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 03BB766E2665316900CE5A93 /* PBXTargetDependency */, - ); - name = RunnerTests; - productName = camera_exampleTests; - productReference = 03BB76682665316900CE5A93 /* RunnerTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; 97C146ED1CF9000F007C117D /* Runner */ = { isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; @@ -283,11 +172,6 @@ LastUpgradeCheck = 1300; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { - 03BB76672665316900CE5A93 = { - CreatedOnToolsVersion = 12.5; - ProvisioningStyle = Automatic; - TestTargetID = 97C146ED1CF9000F007C117D; - }; 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; }; @@ -307,19 +191,11 @@ projectRoot = ""; targets = ( 97C146ED1CF9000F007C117D /* Runner */, - 03BB76672665316900CE5A93 /* RunnerTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 03BB76662665316900CE5A93 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; 97C146EC1CF9000F007C117D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -348,28 +224,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 422786A96136AA9087A2041B /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -409,32 +263,6 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 03BB76642665316900CE5A93 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 03F6F8B226CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m in Sources */, - 033B94BE269C40A200B4DF97 /* CameraMethodChannelTests.m in Sources */, - E071CF7227B3061B006EF3BA /* FLTCamPhotoCaptureTests.m in Sources */, - E0F95E3D27A32AB900699390 /* CameraPropertiesTests.m in Sources */, - 03BB766B2665316900CE5A93 /* CameraFocusTests.m in Sources */, - E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */, - E071CF7427B31DE4006EF3BA /* FLTCamSampleBufferTests.m in Sources */, - E04F108627A87CA600573D0C /* FLTSavePhotoDelegateTests.m in Sources */, - 43ED1537282570DE00EB00DE /* AvailableCamerasTest.m in Sources */, - F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */, - E0CDBAC227CD9729002561D9 /* CameraTestUtils.m in Sources */, - 334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */, - E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */, - 788A065A27B0E02900533D74 /* StreamingTest.m in Sources */, - E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */, - E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */, - E0B0D2BB27DFF2AF00E71E4B /* CameraPermissionTests.m in Sources */, - E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */, - E01EE4A82799F3A5008C1950 /* QueueUtilsTests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 97C146EA1CF9000F007C117D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -447,14 +275,6 @@ }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - 03BB766E2665316900CE5A93 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 97C146ED1CF9000F007C117D /* Runner */; - targetProxy = 03BB766D2665316900CE5A93 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - /* Begin PBXVariantGroup section */ 97C146FA1CF9000F007C117D /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -475,57 +295,6 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ - 03BB766F2665316900CE5A93 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9C5CC6CAD53AD388B2694F3A /* Pods-RunnerTests.debug.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = ""; - GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = RunnerTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "dev.flutter.plugins.cameraExample.camera-exampleTests"; - PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; - }; - name = Debug; - }; - 03BB76702665316900CE5A93 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = A24F9E418BA48BCC7409B117 /* Pods-RunnerTests.release.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = ""; - GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = RunnerTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "dev.flutter.plugins.cameraExample.camera-exampleTests"; - PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; - }; - name = Release; - }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -679,15 +448,6 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 03BB76712665316900CE5A93 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 03BB766F2665316900CE5A93 /* Debug */, - 03BB76702665316900CE5A93 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTest.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTest.m index 2caac0a03891..6074b871cd02 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTest.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTest.m @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@import camera; -@import camera.Test; +@import camera_avfoundation; +@import camera_avfoundation.Test; @import XCTest; @import AVFoundation; #import diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m index e99ce4e89a94..89f40307933c 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@import camera; -@import camera.Test; +@import camera_avfoundation; +@import camera_avfoundation.Test; @import XCTest; @interface CameraCaptureSessionQueueRaceConditionTests : XCTestCase diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.m index ee43d3f155f4..7b641a5746c0 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.m @@ -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 camera; +@import camera_avfoundation; @import XCTest; @import AVFoundation; #import diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.m index e0f5fdaa3c97..1b6ada564dd8 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.m @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@import camera; -@import camera.Test; +@import camera_avfoundation; +@import camera_avfoundation.Test; @import XCTest; @import AVFoundation; #import diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraMethodChannelTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraMethodChannelTests.m index 62b9cda2ef7b..bd20134db561 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraMethodChannelTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraMethodChannelTests.m @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@import camera; -@import camera.Test; +@import camera_avfoundation; +@import camera_avfoundation.Test; @import XCTest; @import AVFoundation; #import diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraOrientationTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraOrientationTests.m index 50f3416e7869..29fc325dffc0 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraOrientationTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraOrientationTests.m @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@import camera; -@import camera.Test; +@import camera_avfoundation; +@import camera_avfoundation.Test; @import XCTest; @import Flutter; diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.m index 541e0288254c..24ca5b6525c9 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.m @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@import camera; -@import camera.Test; +@import camera_avfoundation; +@import camera_avfoundation.Test; @import AVFoundation; @import XCTest; #import diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.m index 2c1adbef468b..1dfc90b27f1b 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.m @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@import camera; -@import camera.Test; +@import camera_avfoundation; +@import camera_avfoundation.Test; @import XCTest; @import AVFoundation; #import diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.m index 865791586382..18c01e599907 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.m @@ -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 camera.Test; +@import camera_avfoundation.Test; @import AVFoundation; @import XCTest; diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.h b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.h index 9fe67dc650e2..f2d46114a0c5 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.h +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.h @@ -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 camera; +@import camera_avfoundation; NS_ASSUME_NONNULL_BEGIN diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraUtilTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraUtilTests.m index 380f6e93de58..d1a835c36efe 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraUtilTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraUtilTests.m @@ -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 camera; +@import camera_avfoundation; @import XCTest; @import AVFoundation; #import diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m index ed3e6a9793fd..8a7c34cc2731 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@import camera; -@import camera.Test; +@import camera_avfoundation; +@import camera_avfoundation.Test; @import AVFoundation; @import XCTest; #import diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSampleBufferTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSampleBufferTests.m index 8f65c4e9eaa5..94426ab3aeb8 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSampleBufferTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSampleBufferTests.m @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@import camera; -@import camera.Test; +@import camera_avfoundation; +@import camera_avfoundation.Test; @import AVFoundation; @import XCTest; #import diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m index a70a572ac0f2..f7633591ccb6 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@import camera; -@import camera.Test; +@import camera_avfoundation; +@import camera_avfoundation.Test; @import AVFoundation; @import XCTest; #import diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.m index da2fc2d936ba..d3d7b6ac15b3 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.m @@ -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 camera; +@import camera_avfoundation; @import XCTest; #import "MockFLTThreadSafeFlutterResult.h" diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.m index de11b4f6961f..a9fc7396bb99 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.m @@ -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 camera; +@import camera_avfoundation; @import XCTest; @interface QueueUtilsTests : XCTestCase diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/StreamingTest.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/StreamingTest.m index 1843cce12152..14a611852dcc 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/StreamingTest.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/StreamingTest.m @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@import camera; -@import camera.Test; +@import camera_avfoundation; +@import camera_avfoundation.Test; @import XCTest; @import AVFoundation; #import diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeEventChannelTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeEventChannelTests.m index dd7ca39c2e98..e7460de6977e 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeEventChannelTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeEventChannelTests.m @@ -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 camera; +@import camera_avfoundation; @import XCTest; #import diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeFlutterResultTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeFlutterResultTests.m index 5fdbd49e5d40..b8de19ce4ab5 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeFlutterResultTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeFlutterResultTests.m @@ -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 camera; +@import camera_avfoundation; @import XCTest; @interface ThreadSafeFlutterResultTests : XCTestCase diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m index 5075be7a81e2..ce1b641cef6f 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m @@ -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 camera; +@import camera_avfoundation; @import XCTest; #import diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m index 067ebab3642f..31f196ffdb9e 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m @@ -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 camera; +@import camera_avfoundation; @import XCTest; #import diff --git a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.modulemap b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.modulemap index 897302799497..abdad1ab575c 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.modulemap +++ b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.modulemap @@ -1,5 +1,5 @@ -framework module camera { - umbrella header "camera-umbrella.h" +framework module camera_avfoundation { + umbrella header "camera_avfoundation-umbrella.h" export * module * { export * } diff --git a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin_Test.h b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin_Test.h index d1903e0829b4..77a758d8dea3 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin_Test.h +++ b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin_Test.h @@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// This header is available in the Test module. Import via "@import camera.Test;" +// This header is available in the Test module. Import via "@import camera_avfoundation.Test;" -#import -#import -#import +#import "CameraPlugin.h" +#import "FLTCam.h" +#import "FLTThreadSafeFlutterResult.h" /// Methods exposed for unit testing. @interface CameraPlugin () diff --git a/packages/camera/camera_avfoundation/ios/Classes/camera-umbrella.h b/packages/camera/camera_avfoundation/ios/Classes/camera_avfoundation-umbrella.h similarity index 100% rename from packages/camera/camera_avfoundation/ios/Classes/camera-umbrella.h rename to packages/camera/camera_avfoundation/ios/Classes/camera_avfoundation-umbrella.h From e99d37f405702641827e1531d85a2099879c6e01 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Wed, 1 Jun 2022 09:21:09 -0400 Subject: [PATCH 06/13] example WIP --- .../example/integration_test/camera_test.dart | 2 +- .../example/lib/camera_controller_lite.dart | 594 ++++++++++++++++++ .../example/lib/camera_preview_lite.dart | 83 +++ .../camera_android/example/lib/main.dart | 7 +- 4 files changed, 682 insertions(+), 4 deletions(-) create mode 100644 packages/camera/camera_android/example/lib/camera_controller_lite.dart create mode 100644 packages/camera/camera_android/example/lib/camera_preview_lite.dart diff --git a/packages/camera/camera_android/example/integration_test/camera_test.dart b/packages/camera/camera_android/example/integration_test/camera_test.dart index 557f4858acab..d1f79299b6dc 100644 --- a/packages/camera/camera_android/example/integration_test/camera_test.dart +++ b/packages/camera/camera_android/example/integration_test/camera_test.dart @@ -6,7 +6,7 @@ import 'dart:async'; import 'dart:io'; import 'dart:ui'; -import 'package:camera/camera.dart'; +import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/painting.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; diff --git a/packages/camera/camera_android/example/lib/camera_controller_lite.dart b/packages/camera/camera_android/example/lib/camera_controller_lite.dart new file mode 100644 index 000000000000..92bd1261b380 --- /dev/null +++ b/packages/camera/camera_android/example/lib/camera_controller_lite.dart @@ -0,0 +1,594 @@ +// 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 'dart:async'; +import 'dart:math'; + +import 'package:camera_platform_interface/camera_platform_interface.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +/// Completes with a list of available cameras. +/// +/// May throw a [CameraException]. +Future> availableCameras() async { + return CameraPlatform.instance.availableCameras(); +} + +// TODO(stuartmorgan): Remove this once the package requires 2.10, where the +// dart:async `unawaited` accepts a nullable future. +void _unawaited(Future? future) {} + +/// The state of a [CameraController]. +class CameraValue { + /// Creates a new camera controller state. + const CameraValue({ + required this.isInitialized, + this.errorDescription, + this.previewSize, + required this.isRecordingVideo, + required this.isTakingPicture, + required this.isStreamingImages, + required bool isRecordingPaused, + required this.flashMode, + required this.exposureMode, + required this.focusMode, + required this.exposurePointSupported, + required this.focusPointSupported, + required this.deviceOrientation, + this.lockedCaptureOrientation, + this.recordingOrientation, + this.isPreviewPaused = false, + this.previewPauseOrientation, + }) : _isRecordingPaused = isRecordingPaused; + + /// Creates a new camera controller state for an uninitialized controller. + const CameraValue.uninitialized() + : this( + isInitialized: false, + isRecordingVideo: false, + isTakingPicture: false, + isStreamingImages: false, + isRecordingPaused: false, + flashMode: FlashMode.auto, + exposureMode: ExposureMode.auto, + exposurePointSupported: false, + focusMode: FocusMode.auto, + focusPointSupported: false, + deviceOrientation: DeviceOrientation.portraitUp, + isPreviewPaused: false, + ); + + /// True after [CameraController.initialize] has completed successfully. + final bool isInitialized; + + /// True when a picture capture request has been sent but as not yet returned. + final bool isTakingPicture; + + /// True when the camera is recording (not the same as previewing). + final bool isRecordingVideo; + + /// True when images from the camera are being streamed. + final bool isStreamingImages; + + final bool _isRecordingPaused; + + /// True when the preview widget has been paused manually. + final bool isPreviewPaused; + + /// Set to the orientation the preview was paused in, if it is currently paused. + final DeviceOrientation? previewPauseOrientation; + + /// True when camera [isRecordingVideo] and recording is paused. + bool get isRecordingPaused => isRecordingVideo && _isRecordingPaused; + + /// Description of an error state. + /// + /// This is null while the controller is not in an error state. + /// When [hasError] is true this contains the error description. + final String? errorDescription; + + /// The size of the preview in pixels. + /// + /// Is `null` until [isInitialized] is `true`. + final Size? previewSize; + + /// Convenience getter for `previewSize.width / previewSize.height`. + /// + /// Can only be called when [initialize] is done. + double get aspectRatio => previewSize!.width / previewSize!.height; + + /// Whether the controller is in an error state. + /// + /// When true [errorDescription] describes the error. + bool get hasError => errorDescription != null; + + /// The flash mode the camera is currently set to. + final FlashMode flashMode; + + /// The exposure mode the camera is currently set to. + final ExposureMode exposureMode; + + /// The focus mode the camera is currently set to. + final FocusMode focusMode; + + /// Whether setting the exposure point is supported. + final bool exposurePointSupported; + + /// Whether setting the focus point is supported. + final bool focusPointSupported; + + /// The current device UI orientation. + final DeviceOrientation deviceOrientation; + + /// The currently locked capture orientation. + final DeviceOrientation? lockedCaptureOrientation; + + /// Whether the capture orientation is currently locked. + bool get isCaptureOrientationLocked => lockedCaptureOrientation != null; + + /// The orientation of the currently running video recording. + final DeviceOrientation? recordingOrientation; + + /// Creates a modified copy of the object. + /// + /// Explicitly specified fields get the specified value, all other fields get + /// the same value of the current object. + CameraValue copyWith({ + bool? isInitialized, + bool? isRecordingVideo, + bool? isTakingPicture, + bool? isStreamingImages, + String? errorDescription, + Size? previewSize, + bool? isRecordingPaused, + FlashMode? flashMode, + ExposureMode? exposureMode, + FocusMode? focusMode, + bool? exposurePointSupported, + bool? focusPointSupported, + DeviceOrientation? deviceOrientation, + Optional? lockedCaptureOrientation, + Optional? recordingOrientation, + bool? isPreviewPaused, + Optional? previewPauseOrientation, + }) { + return CameraValue( + isInitialized: isInitialized ?? this.isInitialized, + errorDescription: errorDescription, + previewSize: previewSize ?? this.previewSize, + isRecordingVideo: isRecordingVideo ?? this.isRecordingVideo, + isTakingPicture: isTakingPicture ?? this.isTakingPicture, + isStreamingImages: isStreamingImages ?? this.isStreamingImages, + isRecordingPaused: isRecordingPaused ?? _isRecordingPaused, + flashMode: flashMode ?? this.flashMode, + exposureMode: exposureMode ?? this.exposureMode, + focusMode: focusMode ?? this.focusMode, + exposurePointSupported: + exposurePointSupported ?? this.exposurePointSupported, + focusPointSupported: focusPointSupported ?? this.focusPointSupported, + deviceOrientation: deviceOrientation ?? this.deviceOrientation, + lockedCaptureOrientation: lockedCaptureOrientation == null + ? this.lockedCaptureOrientation + : lockedCaptureOrientation.orNull, + recordingOrientation: recordingOrientation == null + ? this.recordingOrientation + : recordingOrientation.orNull, + isPreviewPaused: isPreviewPaused ?? this.isPreviewPaused, + previewPauseOrientation: previewPauseOrientation == null + ? this.previewPauseOrientation + : previewPauseOrientation.orNull, + ); + } + + @override + String toString() { + return '${objectRuntimeType(this, 'CameraValue')}(' + 'isRecordingVideo: $isRecordingVideo, ' + 'isInitialized: $isInitialized, ' + 'errorDescription: $errorDescription, ' + 'previewSize: $previewSize, ' + 'isStreamingImages: $isStreamingImages, ' + 'flashMode: $flashMode, ' + 'exposureMode: $exposureMode, ' + 'focusMode: $focusMode, ' + 'exposurePointSupported: $exposurePointSupported, ' + 'focusPointSupported: $focusPointSupported, ' + 'deviceOrientation: $deviceOrientation, ' + 'lockedCaptureOrientation: $lockedCaptureOrientation, ' + 'recordingOrientation: $recordingOrientation, ' + 'isPreviewPaused: $isPreviewPaused, ' + 'previewPausedOrientation: $previewPauseOrientation)'; + } +} + +/// Controls a device camera. +/// +/// Use [availableCameras] to get a list of available cameras. +/// +/// Before using a [CameraController] a call to [initialize] must complete. +/// +/// To show the camera preview on the screen use a [CameraPreview] widget. +class CameraController extends ValueNotifier { + /// Creates a new camera controller in an uninitialized state. + CameraController( + this.description, + this.resolutionPreset, { + this.enableAudio = true, + this.imageFormatGroup, + }) : super(const CameraValue.uninitialized()); + + /// The properties of the camera device controlled by this controller. + final CameraDescription description; + + /// The resolution this controller is targeting. + /// + /// This resolution preset is not guaranteed to be available on the device, + /// if unavailable a lower resolution will be used. + /// + /// See also: [ResolutionPreset]. + final ResolutionPreset resolutionPreset; + + /// Whether to include audio when recording a video. + final bool enableAudio; + + /// The [ImageFormatGroup] describes the output of the raw image format. + /// + /// When null the imageFormat will fallback to the platforms default. + final ImageFormatGroup? imageFormatGroup; + + /// The id of a camera that hasn't been initialized. + @visibleForTesting + static const int kUninitializedCameraId = -1; + int _cameraId = kUninitializedCameraId; + + bool _isDisposed = false; + StreamSubscription? _imageStreamSubscription; + FutureOr? _initCalled; + StreamSubscription? + _deviceOrientationSubscription; + + /// Checks whether [CameraController.dispose] has completed successfully. + /// + /// This is a no-op when asserts are disabled. + void debugCheckIsDisposed() { + assert(_isDisposed); + } + + /// The camera identifier with which the controller is associated. + int get cameraId => _cameraId; + + /// Initializes the camera on the device. + /// + /// Throws a [CameraException] if the initialization fails. + Future initialize() async { + if (_isDisposed) { + throw CameraException( + 'Disposed CameraController', + 'initialize was called on a disposed CameraController', + ); + } + final Completer _initializeCompleter = + Completer(); + + _deviceOrientationSubscription = CameraPlatform.instance + .onDeviceOrientationChanged() + .listen((DeviceOrientationChangedEvent event) { + value = value.copyWith( + deviceOrientation: event.orientation, + ); + }); + + _cameraId = await CameraPlatform.instance.createCamera( + description, + resolutionPreset, + enableAudio: enableAudio, + ); + + _unawaited(CameraPlatform.instance + .onCameraInitialized(_cameraId) + .first + .then((CameraInitializedEvent event) { + _initializeCompleter.complete(event); + })); + + await CameraPlatform.instance.initializeCamera( + _cameraId, + imageFormatGroup: imageFormatGroup ?? ImageFormatGroup.unknown, + ); + + value = value.copyWith( + isInitialized: true, + previewSize: await _initializeCompleter.future + .then((CameraInitializedEvent event) => Size( + event.previewWidth, + event.previewHeight, + )), + exposureMode: await _initializeCompleter.future + .then((CameraInitializedEvent event) => event.exposureMode), + focusMode: await _initializeCompleter.future + .then((CameraInitializedEvent event) => event.focusMode), + exposurePointSupported: await _initializeCompleter.future + .then((CameraInitializedEvent event) => event.exposurePointSupported), + focusPointSupported: await _initializeCompleter.future + .then((CameraInitializedEvent event) => event.focusPointSupported), + ); + + _initCalled = true; + } + + /// Prepare the capture session for video recording. + /// + /// Use of this method is optional, but it may be called for performance + /// reasons on iOS. + /// + /// Preparing audio can cause a minor delay in the CameraPreview view on iOS. + /// If video recording is intended, calling this early eliminates this delay + /// that would otherwise be experienced when video recording is started. + /// This operation is a no-op on Android and Web. + /// + /// Throws a [CameraException] if the prepare fails. + Future prepareForVideoRecording() async { + await CameraPlatform.instance.prepareForVideoRecording(); + } + + /// Pauses the current camera preview + Future pausePreview() async { + if (value.isPreviewPaused) { + return; + } + await CameraPlatform.instance.pausePreview(_cameraId); + value = value.copyWith( + isPreviewPaused: true, + previewPauseOrientation: Optional.of( + value.lockedCaptureOrientation ?? value.deviceOrientation)); + } + + /// Resumes the current camera preview + Future resumePreview() async { + if (!value.isPreviewPaused) { + return; + } + await CameraPlatform.instance.resumePreview(_cameraId); + value = value.copyWith( + isPreviewPaused: false, + previewPauseOrientation: const Optional.absent()); + } + + /// Captures an image and returns the file where it was saved. + /// + /// Throws a [CameraException] if the capture fails. + Future takePicture() async { + value = value.copyWith(isTakingPicture: true); + final XFile file = await CameraPlatform.instance.takePicture(_cameraId); + value = value.copyWith(isTakingPicture: false); + return file; + } + + /// Start a video recording. + /// + /// The video is returned as a [XFile] after calling [stopVideoRecording]. + /// Throws a [CameraException] if the capture fails. + Future startVideoRecording() async { + await CameraPlatform.instance.startVideoRecording(_cameraId); + value = value.copyWith( + isRecordingVideo: true, + isRecordingPaused: false, + recordingOrientation: Optional.of( + value.lockedCaptureOrientation ?? value.deviceOrientation)); + } + + /// Stops the video recording and returns the file where it was saved. + /// + /// Throws a [CameraException] if the capture failed. + Future stopVideoRecording() async { + final XFile file = + await CameraPlatform.instance.stopVideoRecording(_cameraId); + value = value.copyWith( + isRecordingVideo: false, + recordingOrientation: const Optional.absent(), + ); + return file; + } + + /// Pause video recording. + /// + /// This feature is only available on iOS and Android sdk 24+. + Future pauseVideoRecording() async { + await CameraPlatform.instance.pauseVideoRecording(_cameraId); + value = value.copyWith(isRecordingPaused: true); + } + + /// Resume video recording after pausing. + /// + /// This feature is only available on iOS and Android sdk 24+. + Future resumeVideoRecording() async { + await CameraPlatform.instance.resumeVideoRecording(_cameraId); + value = value.copyWith(isRecordingPaused: false); + } + + /// Returns a widget showing a live camera preview. + Widget buildPreview() { + return CameraPlatform.instance.buildPreview(_cameraId); + } + + /// Gets the maximum supported zoom level for the selected camera. + Future getMaxZoomLevel() { + return CameraPlatform.instance.getMaxZoomLevel(_cameraId); + } + + /// Gets the minimum supported zoom level for the selected camera. + Future getMinZoomLevel() { + return CameraPlatform.instance.getMinZoomLevel(_cameraId); + } + + /// Set the zoom level for the selected camera. + /// + /// The supplied [zoom] value should be between 1.0 and the maximum supported + /// zoom level returned by the `getMaxZoomLevel`. Throws an `CameraException` + /// when an illegal zoom level is suplied. + Future setZoomLevel(double zoom) { + return CameraPlatform.instance.setZoomLevel(_cameraId, zoom); + } + + /// Sets the flash mode for taking pictures. + Future setFlashMode(FlashMode mode) async { + await CameraPlatform.instance.setFlashMode(_cameraId, mode); + value = value.copyWith(flashMode: mode); + } + + /// Sets the exposure mode for taking pictures. + Future setExposureMode(ExposureMode mode) async { + await CameraPlatform.instance.setExposureMode(_cameraId, mode); + value = value.copyWith(exposureMode: mode); + } + + /// Sets the exposure point for automatically determining the exposure value. + /// + /// Supplying a `null` value will reset the exposure point to it's default + /// value. + Future setExposurePoint(Offset? point) async { + if (point != null && + (point.dx < 0 || point.dx > 1 || point.dy < 0 || point.dy > 1)) { + throw ArgumentError( + 'The values of point should be anywhere between (0,0) and (1,1).'); + } + + await CameraPlatform.instance.setExposurePoint( + _cameraId, + point == null + ? null + : Point( + point.dx, + point.dy, + ), + ); + } + + /// Gets the minimum supported exposure offset for the selected camera in EV units. + Future getMinExposureOffset() async { + return CameraPlatform.instance.getMinExposureOffset(_cameraId); + } + + /// Gets the maximum supported exposure offset for the selected camera in EV units. + Future getMaxExposureOffset() async { + return CameraPlatform.instance.getMaxExposureOffset(_cameraId); + } + + /// Gets the supported step size for exposure offset for the selected camera in EV units. + /// + /// Returns 0 when the camera supports using a free value without stepping. + Future getExposureOffsetStepSize() async { + return CameraPlatform.instance.getExposureOffsetStepSize(_cameraId); + } + + /// Sets the exposure offset for the selected camera. + /// + /// The supplied [offset] value should be in EV units. 1 EV unit represents a + /// doubling in brightness. It should be between the minimum and maximum offsets + /// obtained through `getMinExposureOffset` and `getMaxExposureOffset` respectively. + /// Throws a `CameraException` when an illegal offset is supplied. + /// + /// When the supplied [offset] value does not align with the step size obtained + /// through `getExposureStepSize`, it will automatically be rounded to the nearest step. + /// + /// Returns the (rounded) offset value that was set. + Future setExposureOffset(double offset) async { + // Check if offset is in range + final List range = await Future.wait( + >[getMinExposureOffset(), getMaxExposureOffset()]); + if (offset < range[0] || offset > range[1]) { + throw CameraException( + 'exposureOffsetOutOfBounds', + 'The provided exposure offset was outside the supported range for this device.', + ); + } + + // Round to the closest step if needed + final double stepSize = await getExposureOffsetStepSize(); + if (stepSize > 0) { + final double inv = 1.0 / stepSize; + double roundedOffset = (offset * inv).roundToDouble() / inv; + if (roundedOffset > range[1]) { + roundedOffset = (offset * inv).floorToDouble() / inv; + } else if (roundedOffset < range[0]) { + roundedOffset = (offset * inv).ceilToDouble() / inv; + } + offset = roundedOffset; + } + + return CameraPlatform.instance.setExposureOffset(_cameraId, offset); + } + + /// Locks the capture orientation. + /// + /// If [orientation] is omitted, the current device orientation is used. + Future lockCaptureOrientation([DeviceOrientation? orientation]) async { + await CameraPlatform.instance.lockCaptureOrientation( + _cameraId, orientation ?? value.deviceOrientation); + value = value.copyWith( + lockedCaptureOrientation: Optional.of( + orientation ?? value.deviceOrientation)); + } + + /// Sets the focus mode for taking pictures. + Future setFocusMode(FocusMode mode) async { + await CameraPlatform.instance.setFocusMode(_cameraId, mode); + value = value.copyWith(focusMode: mode); + } + + /// Unlocks the capture orientation. + Future unlockCaptureOrientation() async { + await CameraPlatform.instance.unlockCaptureOrientation(_cameraId); + value = value.copyWith( + lockedCaptureOrientation: const Optional.absent()); + } + + /// Sets the focus point for automatically determining the focus value. + /// + /// Supplying a `null` value will reset the focus point to it's default + /// value. + Future setFocusPoint(Offset? point) async { + if (point != null && + (point.dx < 0 || point.dx > 1 || point.dy < 0 || point.dy > 1)) { + throw ArgumentError( + 'The values of point should be anywhere between (0,0) and (1,1).'); + } + await CameraPlatform.instance.setFocusPoint( + _cameraId, + point == null + ? null + : Point( + point.dx, + point.dy, + ), + ); + } + + /// Releases the resources of this camera. + @override + Future dispose() async { + if (_isDisposed) { + return; + } + _unawaited(_deviceOrientationSubscription?.cancel()); + _isDisposed = true; + super.dispose(); + if (_initCalled != null) { + await _initCalled; + await CameraPlatform.instance.dispose(_cameraId); + } + } + + @override + void removeListener(VoidCallback listener) { + // Prevent ValueListenableBuilder in CameraPreview widget from causing an + // exception to be thrown by attempting to remove its own listener after + // the controller has already been disposed. + if (!_isDisposed) { + super.removeListener(listener); + } + } +} diff --git a/packages/camera/camera_android/example/lib/camera_preview_lite.dart b/packages/camera/camera_android/example/lib/camera_preview_lite.dart new file mode 100644 index 000000000000..acacaeef5599 --- /dev/null +++ b/packages/camera/camera_android/example/lib/camera_preview_lite.dart @@ -0,0 +1,83 @@ +// 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_platform_interface/camera_platform_interface.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import 'camera_controller_lite.dart'; + +/// A widget showing a live camera preview. +class CameraPreview extends StatelessWidget { + /// Creates a preview widget for the given camera controller. + const CameraPreview(this.controller, {Key? key, this.child}) + : super(key: key); + + /// The controller for the camera that the preview is shown for. + final CameraController controller; + + /// A widget to overlay on top of the camera preview + final Widget? child; + + @override + Widget build(BuildContext context) { + return controller.value.isInitialized + ? ValueListenableBuilder( + valueListenable: controller, + builder: (BuildContext context, Object? value, Widget? child) { + return AspectRatio( + aspectRatio: _isLandscape() + ? controller.value.aspectRatio + : (1 / controller.value.aspectRatio), + child: Stack( + fit: StackFit.expand, + children: [ + _wrapInRotatedBox(child: controller.buildPreview()), + child ?? Container(), + ], + ), + ); + }, + child: child, + ) + : Container(); + } + + Widget _wrapInRotatedBox({required Widget child}) { + if (kIsWeb || defaultTargetPlatform != TargetPlatform.android) { + return child; + } + + return RotatedBox( + quarterTurns: _getQuarterTurns(), + child: child, + ); + } + + bool _isLandscape() { + return [ + DeviceOrientation.landscapeLeft, + DeviceOrientation.landscapeRight + ].contains(_getApplicableOrientation()); + } + + int _getQuarterTurns() { + final Map turns = { + DeviceOrientation.portraitUp: 0, + DeviceOrientation.landscapeRight: 1, + DeviceOrientation.portraitDown: 2, + DeviceOrientation.landscapeLeft: 3, + }; + return turns[_getApplicableOrientation()]!; + } + + DeviceOrientation _getApplicableOrientation() { + return controller.value.isRecordingVideo + ? controller.value.recordingOrientation! + : (controller.value.previewPauseOrientation ?? + controller.value.lockedCaptureOrientation ?? + controller.value.deviceOrientation); + } +} diff --git a/packages/camera/camera_android/example/lib/main.dart b/packages/camera/camera_android/example/lib/main.dart index c0181a5d36a1..5fc6cd6d35ef 100644 --- a/packages/camera/camera_android/example/lib/main.dart +++ b/packages/camera/camera_android/example/lib/main.dart @@ -5,12 +5,15 @@ import 'dart:async'; import 'dart:io'; -import 'package:camera/camera.dart'; +import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:video_player/video_player.dart'; +import 'camera_controller_lite.dart'; +import 'camera_preview_lite.dart'; + /// Camera example home widget. class CameraExampleHome extends StatefulWidget { /// Default Constructor @@ -108,7 +111,6 @@ class _CameraExampleHomeState extends State super.dispose(); } - // #docregion AppLifecycle @override void didChangeAppLifecycleState(AppLifecycleState state) { final CameraController? cameraController = controller; @@ -124,7 +126,6 @@ class _CameraExampleHomeState extends State onNewCameraSelected(cameraController.description); } } - // #enddocregion AppLifecycle @override Widget build(BuildContext context) { From 56b8dcd17ae492b2ab0c82321197d42b854b2df0 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Wed, 8 Jun 2022 15:44:27 -0400 Subject: [PATCH 07/13] Update examples and integration tests to compile --- .../example/integration_test/camera_test.dart | 26 +- ...oller_lite.dart => camera_controller.dart} | 250 ++-------- ..._preview_lite.dart => camera_preview.dart} | 10 +- .../camera_android/example/lib/main.dart | 47 +- .../camera_android/example/pubspec.yaml | 1 + .../example/integration_test/camera_test.dart | 28 +- .../example/lib/camera_controller.dart | 438 ++++++++++++++++++ .../example/lib/camera_preview.dart | 85 ++++ .../camera_avfoundation/example/lib/main.dart | 50 +- .../camera_avfoundation/example/pubspec.yaml | 1 + 10 files changed, 663 insertions(+), 273 deletions(-) rename packages/camera/camera_android/example/lib/{camera_controller_lite.dart => camera_controller.dart} (64%) rename packages/camera/camera_android/example/lib/{camera_preview_lite.dart => camera_preview.dart} (89%) create mode 100644 packages/camera/camera_avfoundation/example/lib/camera_controller.dart create mode 100644 packages/camera/camera_avfoundation/example/lib/camera_preview.dart diff --git a/packages/camera/camera_android/example/integration_test/camera_test.dart b/packages/camera/camera_android/example/integration_test/camera_test.dart index d1f79299b6dc..2043a162b9cc 100644 --- a/packages/camera/camera_android/example/integration_test/camera_test.dart +++ b/packages/camera/camera_android/example/integration_test/camera_test.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:io'; import 'dart:ui'; +import 'package:camera_example/camera_controller.dart'; import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/painting.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -74,7 +75,8 @@ void main() { testWidgets( 'Capture specific image resolutions', (WidgetTester tester) async { - final List cameras = await availableCameras(); + final List cameras = + await CameraPlatform.instance.availableCameras(); if (cameras.isEmpty) { return; } @@ -128,7 +130,8 @@ void main() { testWidgets( 'Capture specific video resolutions', (WidgetTester tester) async { - final List cameras = await availableCameras(); + final List cameras = + await CameraPlatform.instance.availableCameras(); if (cameras.isEmpty) { return; } @@ -154,7 +157,8 @@ void main() { ); testWidgets('Pause and resume video recording', (WidgetTester tester) async { - final List cameras = await availableCameras(); + final List cameras = + await CameraPlatform.instance.availableCameras(); if (cameras.isEmpty) { return; } @@ -209,7 +213,8 @@ void main() { testWidgets( 'Android image streaming', (WidgetTester tester) async { - final List cameras = await availableCameras(); + final List cameras = + await CameraPlatform.instance.availableCameras(); if (cameras.isEmpty) { return; } @@ -223,7 +228,7 @@ void main() { await controller.initialize(); bool _isDetecting = false; - await controller.startImageStream((CameraImage image) { + await controller.startImageStream((CameraImageData image) { if (_isDetecting) { return; } @@ -244,7 +249,7 @@ void main() { ); /// Start streaming with specifying the ImageFormatGroup. - Future startStreaming(List cameras, + Future startStreaming(List cameras, ImageFormatGroup? imageFormatGroup) async { final CameraController controller = CameraController( cameras.first, @@ -254,9 +259,9 @@ void main() { ); await controller.initialize(); - final Completer _completer = Completer(); + final Completer _completer = Completer(); - await controller.startImageStream((CameraImage image) { + await controller.startImageStream((CameraImageData image) { if (!_completer.isCompleted) { Future(() async { await controller.stopImageStream(); @@ -272,12 +277,13 @@ void main() { testWidgets( 'iOS image streaming with imageFormatGroup', (WidgetTester tester) async { - final List cameras = await availableCameras(); + final List cameras = + await CameraPlatform.instance.availableCameras(); if (cameras.isEmpty) { return; } - CameraImage _image = await startStreaming(cameras, null); + CameraImageData _image = await startStreaming(cameras, null); expect(_image, isNotNull); expect(_image.format.group, ImageFormatGroup.bgra8888); expect(_image.planes.length, 1); diff --git a/packages/camera/camera_android/example/lib/camera_controller_lite.dart b/packages/camera/camera_android/example/lib/camera_controller.dart similarity index 64% rename from packages/camera/camera_android/example/lib/camera_controller_lite.dart rename to packages/camera/camera_android/example/lib/camera_controller.dart index 92bd1261b380..2015b1f0ad3a 100644 --- a/packages/camera/camera_android/example/lib/camera_controller_lite.dart +++ b/packages/camera/camera_android/example/lib/camera_controller.dart @@ -9,40 +9,27 @@ import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; - -/// Completes with a list of available cameras. -/// -/// May throw a [CameraException]. -Future> availableCameras() async { - return CameraPlatform.instance.availableCameras(); -} - -// TODO(stuartmorgan): Remove this once the package requires 2.10, where the -// dart:async `unawaited` accepts a nullable future. -void _unawaited(Future? future) {} +import 'package:quiver/core.dart'; /// The state of a [CameraController]. class CameraValue { /// Creates a new camera controller state. const CameraValue({ required this.isInitialized, - this.errorDescription, this.previewSize, required this.isRecordingVideo, required this.isTakingPicture, required this.isStreamingImages, - required bool isRecordingPaused, + required this.isRecordingPaused, required this.flashMode, required this.exposureMode, required this.focusMode, - required this.exposurePointSupported, - required this.focusPointSupported, required this.deviceOrientation, this.lockedCaptureOrientation, this.recordingOrientation, this.isPreviewPaused = false, this.previewPauseOrientation, - }) : _isRecordingPaused = isRecordingPaused; + }); /// Creates a new camera controller state for an uninitialized controller. const CameraValue.uninitialized() @@ -54,9 +41,7 @@ class CameraValue { isRecordingPaused: false, flashMode: FlashMode.auto, exposureMode: ExposureMode.auto, - exposurePointSupported: false, focusMode: FocusMode.auto, - focusPointSupported: false, deviceOrientation: DeviceOrientation.portraitUp, isPreviewPaused: false, ); @@ -73,7 +58,8 @@ class CameraValue { /// True when images from the camera are being streamed. final bool isStreamingImages; - final bool _isRecordingPaused; + /// True when video recording is paused. + final bool isRecordingPaused; /// True when the preview widget has been paused manually. final bool isPreviewPaused; @@ -81,30 +67,11 @@ class CameraValue { /// Set to the orientation the preview was paused in, if it is currently paused. final DeviceOrientation? previewPauseOrientation; - /// True when camera [isRecordingVideo] and recording is paused. - bool get isRecordingPaused => isRecordingVideo && _isRecordingPaused; - - /// Description of an error state. - /// - /// This is null while the controller is not in an error state. - /// When [hasError] is true this contains the error description. - final String? errorDescription; - /// The size of the preview in pixels. /// /// Is `null` until [isInitialized] is `true`. final Size? previewSize; - /// Convenience getter for `previewSize.width / previewSize.height`. - /// - /// Can only be called when [initialize] is done. - double get aspectRatio => previewSize!.width / previewSize!.height; - - /// Whether the controller is in an error state. - /// - /// When true [errorDescription] describes the error. - bool get hasError => errorDescription != null; - /// The flash mode the camera is currently set to. final FlashMode flashMode; @@ -114,12 +81,6 @@ class CameraValue { /// The focus mode the camera is currently set to. final FocusMode focusMode; - /// Whether setting the exposure point is supported. - final bool exposurePointSupported; - - /// Whether setting the focus point is supported. - final bool focusPointSupported; - /// The current device UI orientation. final DeviceOrientation deviceOrientation; @@ -141,7 +102,6 @@ class CameraValue { bool? isRecordingVideo, bool? isTakingPicture, bool? isStreamingImages, - String? errorDescription, Size? previewSize, bool? isRecordingPaused, FlashMode? flashMode, @@ -157,18 +117,14 @@ class CameraValue { }) { return CameraValue( isInitialized: isInitialized ?? this.isInitialized, - errorDescription: errorDescription, previewSize: previewSize ?? this.previewSize, isRecordingVideo: isRecordingVideo ?? this.isRecordingVideo, isTakingPicture: isTakingPicture ?? this.isTakingPicture, isStreamingImages: isStreamingImages ?? this.isStreamingImages, - isRecordingPaused: isRecordingPaused ?? _isRecordingPaused, + isRecordingPaused: isRecordingPaused ?? this.isRecordingPaused, flashMode: flashMode ?? this.flashMode, exposureMode: exposureMode ?? this.exposureMode, focusMode: focusMode ?? this.focusMode, - exposurePointSupported: - exposurePointSupported ?? this.exposurePointSupported, - focusPointSupported: focusPointSupported ?? this.focusPointSupported, deviceOrientation: deviceOrientation ?? this.deviceOrientation, lockedCaptureOrientation: lockedCaptureOrientation == null ? this.lockedCaptureOrientation @@ -188,14 +144,11 @@ class CameraValue { return '${objectRuntimeType(this, 'CameraValue')}(' 'isRecordingVideo: $isRecordingVideo, ' 'isInitialized: $isInitialized, ' - 'errorDescription: $errorDescription, ' 'previewSize: $previewSize, ' 'isStreamingImages: $isStreamingImages, ' 'flashMode: $flashMode, ' 'exposureMode: $exposureMode, ' 'focusMode: $focusMode, ' - 'exposurePointSupported: $exposurePointSupported, ' - 'focusPointSupported: $focusPointSupported, ' 'deviceOrientation: $deviceOrientation, ' 'lockedCaptureOrientation: $lockedCaptureOrientation, ' 'recordingOrientation: $recordingOrientation, ' @@ -206,11 +159,10 @@ class CameraValue { /// Controls a device camera. /// -/// Use [availableCameras] to get a list of available cameras. -/// -/// Before using a [CameraController] a call to [initialize] must complete. -/// -/// To show the camera preview on the screen use a [CameraPreview] widget. +/// This is a stripped-down version of the app-facing controller to serve as a +/// utility for the example and integration tests. It wraps only the calls that +/// have state associated with them, to consolidate tracking of camera state +/// outside of the overall example code. class CameraController extends ValueNotifier { /// Creates a new camera controller in an uninitialized state. CameraController( @@ -239,10 +191,7 @@ class CameraController extends ValueNotifier { /// When null the imageFormat will fallback to the platforms default. final ImageFormatGroup? imageFormatGroup; - /// The id of a camera that hasn't been initialized. - @visibleForTesting - static const int kUninitializedCameraId = -1; - int _cameraId = kUninitializedCameraId; + late int _cameraId; bool _isDisposed = false; StreamSubscription? _imageStreamSubscription; @@ -250,26 +199,11 @@ class CameraController extends ValueNotifier { StreamSubscription? _deviceOrientationSubscription; - /// Checks whether [CameraController.dispose] has completed successfully. - /// - /// This is a no-op when asserts are disabled. - void debugCheckIsDisposed() { - assert(_isDisposed); - } - /// The camera identifier with which the controller is associated. int get cameraId => _cameraId; /// Initializes the camera on the device. - /// - /// Throws a [CameraException] if the initialization fails. Future initialize() async { - if (_isDisposed) { - throw CameraException( - 'Disposed CameraController', - 'initialize was called on a disposed CameraController', - ); - } final Completer _initializeCompleter = Completer(); @@ -287,12 +221,12 @@ class CameraController extends ValueNotifier { enableAudio: enableAudio, ); - _unawaited(CameraPlatform.instance + CameraPlatform.instance .onCameraInitialized(_cameraId) .first .then((CameraInitializedEvent event) { _initializeCompleter.complete(event); - })); + }); await CameraPlatform.instance.initializeCamera( _cameraId, @@ -320,25 +254,12 @@ class CameraController extends ValueNotifier { } /// Prepare the capture session for video recording. - /// - /// Use of this method is optional, but it may be called for performance - /// reasons on iOS. - /// - /// Preparing audio can cause a minor delay in the CameraPreview view on iOS. - /// If video recording is intended, calling this early eliminates this delay - /// that would otherwise be experienced when video recording is started. - /// This operation is a no-op on Android and Web. - /// - /// Throws a [CameraException] if the prepare fails. Future prepareForVideoRecording() async { await CameraPlatform.instance.prepareForVideoRecording(); } /// Pauses the current camera preview Future pausePreview() async { - if (value.isPreviewPaused) { - return; - } await CameraPlatform.instance.pausePreview(_cameraId); value = value.copyWith( isPreviewPaused: true, @@ -348,9 +269,6 @@ class CameraController extends ValueNotifier { /// Resumes the current camera preview Future resumePreview() async { - if (!value.isPreviewPaused) { - return; - } await CameraPlatform.instance.resumePreview(_cameraId); value = value.copyWith( isPreviewPaused: false, @@ -367,6 +285,24 @@ class CameraController extends ValueNotifier { return file; } + /// Start streaming images from platform camera. + Future startImageStream( + Function(CameraImageData image) onAvailable) async { + _imageStreamSubscription = CameraPlatform.instance + .onStreamedFrameAvailable(_cameraId) + .listen((CameraImageData imageData) { + onAvailable(imageData); + }); + value = value.copyWith(isStreamingImages: true); + } + + /// Stop streaming images from platform camera. + Future stopImageStream() async { + value = value.copyWith(isStreamingImages: false); + await _imageStreamSubscription?.cancel(); + _imageStreamSubscription = null; + } + /// Start a video recording. /// /// The video is returned as a [XFile] after calling [stopVideoRecording]. @@ -414,25 +350,6 @@ class CameraController extends ValueNotifier { return CameraPlatform.instance.buildPreview(_cameraId); } - /// Gets the maximum supported zoom level for the selected camera. - Future getMaxZoomLevel() { - return CameraPlatform.instance.getMaxZoomLevel(_cameraId); - } - - /// Gets the minimum supported zoom level for the selected camera. - Future getMinZoomLevel() { - return CameraPlatform.instance.getMinZoomLevel(_cameraId); - } - - /// Set the zoom level for the selected camera. - /// - /// The supplied [zoom] value should be between 1.0 and the maximum supported - /// zoom level returned by the `getMaxZoomLevel`. Throws an `CameraException` - /// when an illegal zoom level is suplied. - Future setZoomLevel(double zoom) { - return CameraPlatform.instance.setZoomLevel(_cameraId, zoom); - } - /// Sets the flash mode for taking pictures. Future setFlashMode(FlashMode mode) async { await CameraPlatform.instance.setFlashMode(_cameraId, mode); @@ -445,69 +362,17 @@ class CameraController extends ValueNotifier { value = value.copyWith(exposureMode: mode); } - /// Sets the exposure point for automatically determining the exposure value. - /// - /// Supplying a `null` value will reset the exposure point to it's default - /// value. - Future setExposurePoint(Offset? point) async { - if (point != null && - (point.dx < 0 || point.dx > 1 || point.dy < 0 || point.dy > 1)) { - throw ArgumentError( - 'The values of point should be anywhere between (0,0) and (1,1).'); - } - - await CameraPlatform.instance.setExposurePoint( - _cameraId, - point == null - ? null - : Point( - point.dx, - point.dy, - ), - ); - } - - /// Gets the minimum supported exposure offset for the selected camera in EV units. - Future getMinExposureOffset() async { - return CameraPlatform.instance.getMinExposureOffset(_cameraId); - } - - /// Gets the maximum supported exposure offset for the selected camera in EV units. - Future getMaxExposureOffset() async { - return CameraPlatform.instance.getMaxExposureOffset(_cameraId); - } - - /// Gets the supported step size for exposure offset for the selected camera in EV units. - /// - /// Returns 0 when the camera supports using a free value without stepping. - Future getExposureOffsetStepSize() async { - return CameraPlatform.instance.getExposureOffsetStepSize(_cameraId); - } - /// Sets the exposure offset for the selected camera. - /// - /// The supplied [offset] value should be in EV units. 1 EV unit represents a - /// doubling in brightness. It should be between the minimum and maximum offsets - /// obtained through `getMinExposureOffset` and `getMaxExposureOffset` respectively. - /// Throws a `CameraException` when an illegal offset is supplied. - /// - /// When the supplied [offset] value does not align with the step size obtained - /// through `getExposureStepSize`, it will automatically be rounded to the nearest step. - /// - /// Returns the (rounded) offset value that was set. Future setExposureOffset(double offset) async { // Check if offset is in range - final List range = await Future.wait( - >[getMinExposureOffset(), getMaxExposureOffset()]); - if (offset < range[0] || offset > range[1]) { - throw CameraException( - 'exposureOffsetOutOfBounds', - 'The provided exposure offset was outside the supported range for this device.', - ); - } + final List range = await Future.wait(>[ + CameraPlatform.instance.getMinExposureOffset(_cameraId), + CameraPlatform.instance.getMaxExposureOffset(_cameraId) + ]); // Round to the closest step if needed - final double stepSize = await getExposureOffsetStepSize(); + final double stepSize = + await CameraPlatform.instance.getExposureOffsetStepSize(_cameraId); if (stepSize > 0) { final double inv = 1.0 / stepSize; double roundedOffset = (offset * inv).roundToDouble() / inv; @@ -525,18 +390,12 @@ class CameraController extends ValueNotifier { /// Locks the capture orientation. /// /// If [orientation] is omitted, the current device orientation is used. - Future lockCaptureOrientation([DeviceOrientation? orientation]) async { - await CameraPlatform.instance.lockCaptureOrientation( - _cameraId, orientation ?? value.deviceOrientation); + Future lockCaptureOrientation() async { + await CameraPlatform.instance + .lockCaptureOrientation(_cameraId, value.deviceOrientation); value = value.copyWith( - lockedCaptureOrientation: Optional.of( - orientation ?? value.deviceOrientation)); - } - - /// Sets the focus mode for taking pictures. - Future setFocusMode(FocusMode mode) async { - await CameraPlatform.instance.setFocusMode(_cameraId, mode); - value = value.copyWith(focusMode: mode); + lockedCaptureOrientation: + Optional.of(value.deviceOrientation)); } /// Unlocks the capture orientation. @@ -546,25 +405,10 @@ class CameraController extends ValueNotifier { lockedCaptureOrientation: const Optional.absent()); } - /// Sets the focus point for automatically determining the focus value. - /// - /// Supplying a `null` value will reset the focus point to it's default - /// value. - Future setFocusPoint(Offset? point) async { - if (point != null && - (point.dx < 0 || point.dx > 1 || point.dy < 0 || point.dy > 1)) { - throw ArgumentError( - 'The values of point should be anywhere between (0,0) and (1,1).'); - } - await CameraPlatform.instance.setFocusPoint( - _cameraId, - point == null - ? null - : Point( - point.dx, - point.dy, - ), - ); + /// Sets the focus mode for taking pictures. + Future setFocusMode(FocusMode mode) async { + await CameraPlatform.instance.setFocusMode(_cameraId, mode); + value = value.copyWith(focusMode: mode); } /// Releases the resources of this camera. @@ -573,7 +417,7 @@ class CameraController extends ValueNotifier { if (_isDisposed) { return; } - _unawaited(_deviceOrientationSubscription?.cancel()); + _deviceOrientationSubscription?.cancel(); _isDisposed = true; super.dispose(); if (_initCalled != null) { diff --git a/packages/camera/camera_android/example/lib/camera_preview_lite.dart b/packages/camera/camera_android/example/lib/camera_preview.dart similarity index 89% rename from packages/camera/camera_android/example/lib/camera_preview_lite.dart rename to packages/camera/camera_android/example/lib/camera_preview.dart index acacaeef5599..5e8f64cb2fbd 100644 --- a/packages/camera/camera_android/example/lib/camera_preview_lite.dart +++ b/packages/camera/camera_android/example/lib/camera_preview.dart @@ -2,12 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'camera_controller_lite.dart'; +import 'camera_controller.dart'; /// A widget showing a live camera preview. class CameraPreview extends StatelessWidget { @@ -27,10 +26,13 @@ class CameraPreview extends StatelessWidget { ? ValueListenableBuilder( valueListenable: controller, builder: (BuildContext context, Object? value, Widget? child) { + final double cameraAspectRatio = + controller.value.previewSize!.width / + controller.value.previewSize!.height; return AspectRatio( aspectRatio: _isLandscape() - ? controller.value.aspectRatio - : (1 / controller.value.aspectRatio), + ? cameraAspectRatio + : (1 / cameraAspectRatio), child: Stack( fit: StackFit.expand, children: [ diff --git a/packages/camera/camera_android/example/lib/main.dart b/packages/camera/camera_android/example/lib/main.dart index 5fc6cd6d35ef..1f3e06d33b9b 100644 --- a/packages/camera/camera_android/example/lib/main.dart +++ b/packages/camera/camera_android/example/lib/main.dart @@ -4,6 +4,7 @@ import 'dart:async'; import 'dart:io'; +import 'dart:math'; import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/foundation.dart'; @@ -11,8 +12,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:video_player/video_player.dart'; -import 'camera_controller_lite.dart'; -import 'camera_preview_lite.dart'; +import 'camera_controller.dart'; +import 'camera_preview.dart'; /// Camera example home widget. class CameraExampleHome extends StatefulWidget { @@ -219,7 +220,8 @@ class _CameraExampleHomeState extends State _currentScale = (_baseScale * details.scale) .clamp(_minAvailableZoom, _maxAvailableZoom); - await controller!.setZoomLevel(_currentScale); + await CameraPlatform.instance + .setZoomLevel(controller!.cameraId, _currentScale); } /// Display the thumbnail of the captured image or video. @@ -404,7 +406,8 @@ class _CameraExampleHomeState extends State : null, onLongPress: () { if (controller != null) { - controller!.setExposurePoint(null); + CameraPlatform.instance + .setExposurePoint(controller!.cameraId, null); showInSnackBar('Resetting exposure point'); } }, @@ -488,7 +491,8 @@ class _CameraExampleHomeState extends State : null, onLongPress: () { if (controller != null) { - controller!.setFocusPoint(null); + CameraPlatform.instance + .setFocusPoint(controller!.cameraId, null); } showInSnackBar('Resetting focus point'); }, @@ -538,7 +542,8 @@ class _CameraExampleHomeState extends State ), IconButton( icon: cameraController != null && - cameraController.value.isRecordingPaused + (!cameraController.value.isRecordingVideo || + cameraController.value.isRecordingPaused) ? const Icon(Icons.play_arrow) : const Icon(Icons.pause), color: Colors.blue, @@ -625,12 +630,12 @@ class _CameraExampleHomeState extends State final CameraController cameraController = controller!; - final Offset offset = Offset( + final Point point = Point( details.localPosition.dx / constraints.maxWidth, details.localPosition.dy / constraints.maxHeight, ); - cameraController.setExposurePoint(offset); - cameraController.setFocusPoint(offset); + CameraPlatform.instance.setExposurePoint(cameraController.cameraId, point); + CameraPlatform.instance.setFocusPoint(cameraController.cameraId, point); } Future onNewCameraSelected(CameraDescription cameraDescription) async { @@ -659,10 +664,6 @@ class _CameraExampleHomeState extends State if (mounted) { setState(() {}); } - if (cameraController.value.hasError) { - showInSnackBar( - 'Camera error ${cameraController.value.errorDescription}'); - } }); try { @@ -671,18 +672,20 @@ class _CameraExampleHomeState extends State // The exposure mode is currently not supported on the web. ...!kIsWeb ? >[ - cameraController.getMinExposureOffset().then( - (double value) => _minAvailableExposureOffset = value), - cameraController - .getMaxExposureOffset() + CameraPlatform.instance + .getMinExposureOffset(cameraController.cameraId) + .then( + (double value) => _minAvailableExposureOffset = value), + CameraPlatform.instance + .getMaxExposureOffset(cameraController.cameraId) .then((double value) => _maxAvailableExposureOffset = value) ] : >[], - cameraController - .getMaxZoomLevel() + CameraPlatform.instance + .getMaxZoomLevel(cameraController.cameraId) .then((double value) => _maxAvailableZoom = value), - cameraController - .getMinZoomLevel() + CameraPlatform.instance + .getMinZoomLevel(cameraController.cameraId) .then((double value) => _minAvailableZoom = value), ]); } on CameraException catch (e) { @@ -1077,7 +1080,7 @@ Future main() async { // Fetch the available cameras before initializing the app. try { WidgetsFlutterBinding.ensureInitialized(); - _cameras = await availableCameras(); + _cameras = await CameraPlatform.instance.availableCameras(); } on CameraException catch (e) { _logError(e.code, e.description); } diff --git a/packages/camera/camera_android/example/pubspec.yaml b/packages/camera/camera_android/example/pubspec.yaml index 8b319b099e2e..64e69405041c 100644 --- a/packages/camera/camera_android/example/pubspec.yaml +++ b/packages/camera/camera_android/example/pubspec.yaml @@ -17,6 +17,7 @@ dependencies: flutter: sdk: flutter path_provider: ^2.0.0 + quiver: ^3.0.0 video_player: ^2.1.4 dev_dependencies: diff --git a/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart index 557f4858acab..2043a162b9cc 100644 --- a/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart +++ b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart @@ -6,7 +6,8 @@ import 'dart:async'; import 'dart:io'; import 'dart:ui'; -import 'package:camera/camera.dart'; +import 'package:camera_example/camera_controller.dart'; +import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/painting.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; @@ -74,7 +75,8 @@ void main() { testWidgets( 'Capture specific image resolutions', (WidgetTester tester) async { - final List cameras = await availableCameras(); + final List cameras = + await CameraPlatform.instance.availableCameras(); if (cameras.isEmpty) { return; } @@ -128,7 +130,8 @@ void main() { testWidgets( 'Capture specific video resolutions', (WidgetTester tester) async { - final List cameras = await availableCameras(); + final List cameras = + await CameraPlatform.instance.availableCameras(); if (cameras.isEmpty) { return; } @@ -154,7 +157,8 @@ void main() { ); testWidgets('Pause and resume video recording', (WidgetTester tester) async { - final List cameras = await availableCameras(); + final List cameras = + await CameraPlatform.instance.availableCameras(); if (cameras.isEmpty) { return; } @@ -209,7 +213,8 @@ void main() { testWidgets( 'Android image streaming', (WidgetTester tester) async { - final List cameras = await availableCameras(); + final List cameras = + await CameraPlatform.instance.availableCameras(); if (cameras.isEmpty) { return; } @@ -223,7 +228,7 @@ void main() { await controller.initialize(); bool _isDetecting = false; - await controller.startImageStream((CameraImage image) { + await controller.startImageStream((CameraImageData image) { if (_isDetecting) { return; } @@ -244,7 +249,7 @@ void main() { ); /// Start streaming with specifying the ImageFormatGroup. - Future startStreaming(List cameras, + Future startStreaming(List cameras, ImageFormatGroup? imageFormatGroup) async { final CameraController controller = CameraController( cameras.first, @@ -254,9 +259,9 @@ void main() { ); await controller.initialize(); - final Completer _completer = Completer(); + final Completer _completer = Completer(); - await controller.startImageStream((CameraImage image) { + await controller.startImageStream((CameraImageData image) { if (!_completer.isCompleted) { Future(() async { await controller.stopImageStream(); @@ -272,12 +277,13 @@ void main() { testWidgets( 'iOS image streaming with imageFormatGroup', (WidgetTester tester) async { - final List cameras = await availableCameras(); + final List cameras = + await CameraPlatform.instance.availableCameras(); if (cameras.isEmpty) { return; } - CameraImage _image = await startStreaming(cameras, null); + CameraImageData _image = await startStreaming(cameras, null); expect(_image, isNotNull); expect(_image.format.group, ImageFormatGroup.bgra8888); expect(_image.planes.length, 1); diff --git a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart new file mode 100644 index 000000000000..2015b1f0ad3a --- /dev/null +++ b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart @@ -0,0 +1,438 @@ +// 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 'dart:async'; +import 'dart:math'; + +import 'package:camera_platform_interface/camera_platform_interface.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:quiver/core.dart'; + +/// The state of a [CameraController]. +class CameraValue { + /// Creates a new camera controller state. + const CameraValue({ + required this.isInitialized, + this.previewSize, + required this.isRecordingVideo, + required this.isTakingPicture, + required this.isStreamingImages, + required this.isRecordingPaused, + required this.flashMode, + required this.exposureMode, + required this.focusMode, + required this.deviceOrientation, + this.lockedCaptureOrientation, + this.recordingOrientation, + this.isPreviewPaused = false, + this.previewPauseOrientation, + }); + + /// Creates a new camera controller state for an uninitialized controller. + const CameraValue.uninitialized() + : this( + isInitialized: false, + isRecordingVideo: false, + isTakingPicture: false, + isStreamingImages: false, + isRecordingPaused: false, + flashMode: FlashMode.auto, + exposureMode: ExposureMode.auto, + focusMode: FocusMode.auto, + deviceOrientation: DeviceOrientation.portraitUp, + isPreviewPaused: false, + ); + + /// True after [CameraController.initialize] has completed successfully. + final bool isInitialized; + + /// True when a picture capture request has been sent but as not yet returned. + final bool isTakingPicture; + + /// True when the camera is recording (not the same as previewing). + final bool isRecordingVideo; + + /// True when images from the camera are being streamed. + final bool isStreamingImages; + + /// True when video recording is paused. + final bool isRecordingPaused; + + /// True when the preview widget has been paused manually. + final bool isPreviewPaused; + + /// Set to the orientation the preview was paused in, if it is currently paused. + final DeviceOrientation? previewPauseOrientation; + + /// The size of the preview in pixels. + /// + /// Is `null` until [isInitialized] is `true`. + final Size? previewSize; + + /// The flash mode the camera is currently set to. + final FlashMode flashMode; + + /// The exposure mode the camera is currently set to. + final ExposureMode exposureMode; + + /// The focus mode the camera is currently set to. + final FocusMode focusMode; + + /// The current device UI orientation. + final DeviceOrientation deviceOrientation; + + /// The currently locked capture orientation. + final DeviceOrientation? lockedCaptureOrientation; + + /// Whether the capture orientation is currently locked. + bool get isCaptureOrientationLocked => lockedCaptureOrientation != null; + + /// The orientation of the currently running video recording. + final DeviceOrientation? recordingOrientation; + + /// Creates a modified copy of the object. + /// + /// Explicitly specified fields get the specified value, all other fields get + /// the same value of the current object. + CameraValue copyWith({ + bool? isInitialized, + bool? isRecordingVideo, + bool? isTakingPicture, + bool? isStreamingImages, + Size? previewSize, + bool? isRecordingPaused, + FlashMode? flashMode, + ExposureMode? exposureMode, + FocusMode? focusMode, + bool? exposurePointSupported, + bool? focusPointSupported, + DeviceOrientation? deviceOrientation, + Optional? lockedCaptureOrientation, + Optional? recordingOrientation, + bool? isPreviewPaused, + Optional? previewPauseOrientation, + }) { + return CameraValue( + isInitialized: isInitialized ?? this.isInitialized, + previewSize: previewSize ?? this.previewSize, + isRecordingVideo: isRecordingVideo ?? this.isRecordingVideo, + isTakingPicture: isTakingPicture ?? this.isTakingPicture, + isStreamingImages: isStreamingImages ?? this.isStreamingImages, + isRecordingPaused: isRecordingPaused ?? this.isRecordingPaused, + flashMode: flashMode ?? this.flashMode, + exposureMode: exposureMode ?? this.exposureMode, + focusMode: focusMode ?? this.focusMode, + deviceOrientation: deviceOrientation ?? this.deviceOrientation, + lockedCaptureOrientation: lockedCaptureOrientation == null + ? this.lockedCaptureOrientation + : lockedCaptureOrientation.orNull, + recordingOrientation: recordingOrientation == null + ? this.recordingOrientation + : recordingOrientation.orNull, + isPreviewPaused: isPreviewPaused ?? this.isPreviewPaused, + previewPauseOrientation: previewPauseOrientation == null + ? this.previewPauseOrientation + : previewPauseOrientation.orNull, + ); + } + + @override + String toString() { + return '${objectRuntimeType(this, 'CameraValue')}(' + 'isRecordingVideo: $isRecordingVideo, ' + 'isInitialized: $isInitialized, ' + 'previewSize: $previewSize, ' + 'isStreamingImages: $isStreamingImages, ' + 'flashMode: $flashMode, ' + 'exposureMode: $exposureMode, ' + 'focusMode: $focusMode, ' + 'deviceOrientation: $deviceOrientation, ' + 'lockedCaptureOrientation: $lockedCaptureOrientation, ' + 'recordingOrientation: $recordingOrientation, ' + 'isPreviewPaused: $isPreviewPaused, ' + 'previewPausedOrientation: $previewPauseOrientation)'; + } +} + +/// Controls a device camera. +/// +/// This is a stripped-down version of the app-facing controller to serve as a +/// utility for the example and integration tests. It wraps only the calls that +/// have state associated with them, to consolidate tracking of camera state +/// outside of the overall example code. +class CameraController extends ValueNotifier { + /// Creates a new camera controller in an uninitialized state. + CameraController( + this.description, + this.resolutionPreset, { + this.enableAudio = true, + this.imageFormatGroup, + }) : super(const CameraValue.uninitialized()); + + /// The properties of the camera device controlled by this controller. + final CameraDescription description; + + /// The resolution this controller is targeting. + /// + /// This resolution preset is not guaranteed to be available on the device, + /// if unavailable a lower resolution will be used. + /// + /// See also: [ResolutionPreset]. + final ResolutionPreset resolutionPreset; + + /// Whether to include audio when recording a video. + final bool enableAudio; + + /// The [ImageFormatGroup] describes the output of the raw image format. + /// + /// When null the imageFormat will fallback to the platforms default. + final ImageFormatGroup? imageFormatGroup; + + late int _cameraId; + + bool _isDisposed = false; + StreamSubscription? _imageStreamSubscription; + FutureOr? _initCalled; + StreamSubscription? + _deviceOrientationSubscription; + + /// The camera identifier with which the controller is associated. + int get cameraId => _cameraId; + + /// Initializes the camera on the device. + Future initialize() async { + final Completer _initializeCompleter = + Completer(); + + _deviceOrientationSubscription = CameraPlatform.instance + .onDeviceOrientationChanged() + .listen((DeviceOrientationChangedEvent event) { + value = value.copyWith( + deviceOrientation: event.orientation, + ); + }); + + _cameraId = await CameraPlatform.instance.createCamera( + description, + resolutionPreset, + enableAudio: enableAudio, + ); + + CameraPlatform.instance + .onCameraInitialized(_cameraId) + .first + .then((CameraInitializedEvent event) { + _initializeCompleter.complete(event); + }); + + await CameraPlatform.instance.initializeCamera( + _cameraId, + imageFormatGroup: imageFormatGroup ?? ImageFormatGroup.unknown, + ); + + value = value.copyWith( + isInitialized: true, + previewSize: await _initializeCompleter.future + .then((CameraInitializedEvent event) => Size( + event.previewWidth, + event.previewHeight, + )), + exposureMode: await _initializeCompleter.future + .then((CameraInitializedEvent event) => event.exposureMode), + focusMode: await _initializeCompleter.future + .then((CameraInitializedEvent event) => event.focusMode), + exposurePointSupported: await _initializeCompleter.future + .then((CameraInitializedEvent event) => event.exposurePointSupported), + focusPointSupported: await _initializeCompleter.future + .then((CameraInitializedEvent event) => event.focusPointSupported), + ); + + _initCalled = true; + } + + /// Prepare the capture session for video recording. + Future prepareForVideoRecording() async { + await CameraPlatform.instance.prepareForVideoRecording(); + } + + /// Pauses the current camera preview + Future pausePreview() async { + await CameraPlatform.instance.pausePreview(_cameraId); + value = value.copyWith( + isPreviewPaused: true, + previewPauseOrientation: Optional.of( + value.lockedCaptureOrientation ?? value.deviceOrientation)); + } + + /// Resumes the current camera preview + Future resumePreview() async { + await CameraPlatform.instance.resumePreview(_cameraId); + value = value.copyWith( + isPreviewPaused: false, + previewPauseOrientation: const Optional.absent()); + } + + /// Captures an image and returns the file where it was saved. + /// + /// Throws a [CameraException] if the capture fails. + Future takePicture() async { + value = value.copyWith(isTakingPicture: true); + final XFile file = await CameraPlatform.instance.takePicture(_cameraId); + value = value.copyWith(isTakingPicture: false); + return file; + } + + /// Start streaming images from platform camera. + Future startImageStream( + Function(CameraImageData image) onAvailable) async { + _imageStreamSubscription = CameraPlatform.instance + .onStreamedFrameAvailable(_cameraId) + .listen((CameraImageData imageData) { + onAvailable(imageData); + }); + value = value.copyWith(isStreamingImages: true); + } + + /// Stop streaming images from platform camera. + Future stopImageStream() async { + value = value.copyWith(isStreamingImages: false); + await _imageStreamSubscription?.cancel(); + _imageStreamSubscription = null; + } + + /// Start a video recording. + /// + /// The video is returned as a [XFile] after calling [stopVideoRecording]. + /// Throws a [CameraException] if the capture fails. + Future startVideoRecording() async { + await CameraPlatform.instance.startVideoRecording(_cameraId); + value = value.copyWith( + isRecordingVideo: true, + isRecordingPaused: false, + recordingOrientation: Optional.of( + value.lockedCaptureOrientation ?? value.deviceOrientation)); + } + + /// Stops the video recording and returns the file where it was saved. + /// + /// Throws a [CameraException] if the capture failed. + Future stopVideoRecording() async { + final XFile file = + await CameraPlatform.instance.stopVideoRecording(_cameraId); + value = value.copyWith( + isRecordingVideo: false, + recordingOrientation: const Optional.absent(), + ); + return file; + } + + /// Pause video recording. + /// + /// This feature is only available on iOS and Android sdk 24+. + Future pauseVideoRecording() async { + await CameraPlatform.instance.pauseVideoRecording(_cameraId); + value = value.copyWith(isRecordingPaused: true); + } + + /// Resume video recording after pausing. + /// + /// This feature is only available on iOS and Android sdk 24+. + Future resumeVideoRecording() async { + await CameraPlatform.instance.resumeVideoRecording(_cameraId); + value = value.copyWith(isRecordingPaused: false); + } + + /// Returns a widget showing a live camera preview. + Widget buildPreview() { + return CameraPlatform.instance.buildPreview(_cameraId); + } + + /// Sets the flash mode for taking pictures. + Future setFlashMode(FlashMode mode) async { + await CameraPlatform.instance.setFlashMode(_cameraId, mode); + value = value.copyWith(flashMode: mode); + } + + /// Sets the exposure mode for taking pictures. + Future setExposureMode(ExposureMode mode) async { + await CameraPlatform.instance.setExposureMode(_cameraId, mode); + value = value.copyWith(exposureMode: mode); + } + + /// Sets the exposure offset for the selected camera. + Future setExposureOffset(double offset) async { + // Check if offset is in range + final List range = await Future.wait(>[ + CameraPlatform.instance.getMinExposureOffset(_cameraId), + CameraPlatform.instance.getMaxExposureOffset(_cameraId) + ]); + + // Round to the closest step if needed + final double stepSize = + await CameraPlatform.instance.getExposureOffsetStepSize(_cameraId); + if (stepSize > 0) { + final double inv = 1.0 / stepSize; + double roundedOffset = (offset * inv).roundToDouble() / inv; + if (roundedOffset > range[1]) { + roundedOffset = (offset * inv).floorToDouble() / inv; + } else if (roundedOffset < range[0]) { + roundedOffset = (offset * inv).ceilToDouble() / inv; + } + offset = roundedOffset; + } + + return CameraPlatform.instance.setExposureOffset(_cameraId, offset); + } + + /// Locks the capture orientation. + /// + /// If [orientation] is omitted, the current device orientation is used. + Future lockCaptureOrientation() async { + await CameraPlatform.instance + .lockCaptureOrientation(_cameraId, value.deviceOrientation); + value = value.copyWith( + lockedCaptureOrientation: + Optional.of(value.deviceOrientation)); + } + + /// Unlocks the capture orientation. + Future unlockCaptureOrientation() async { + await CameraPlatform.instance.unlockCaptureOrientation(_cameraId); + value = value.copyWith( + lockedCaptureOrientation: const Optional.absent()); + } + + /// Sets the focus mode for taking pictures. + Future setFocusMode(FocusMode mode) async { + await CameraPlatform.instance.setFocusMode(_cameraId, mode); + value = value.copyWith(focusMode: mode); + } + + /// Releases the resources of this camera. + @override + Future dispose() async { + if (_isDisposed) { + return; + } + _deviceOrientationSubscription?.cancel(); + _isDisposed = true; + super.dispose(); + if (_initCalled != null) { + await _initCalled; + await CameraPlatform.instance.dispose(_cameraId); + } + } + + @override + void removeListener(VoidCallback listener) { + // Prevent ValueListenableBuilder in CameraPreview widget from causing an + // exception to be thrown by attempting to remove its own listener after + // the controller has already been disposed. + if (!_isDisposed) { + super.removeListener(listener); + } + } +} diff --git a/packages/camera/camera_avfoundation/example/lib/camera_preview.dart b/packages/camera/camera_avfoundation/example/lib/camera_preview.dart new file mode 100644 index 000000000000..5e8f64cb2fbd --- /dev/null +++ b/packages/camera/camera_avfoundation/example/lib/camera_preview.dart @@ -0,0 +1,85 @@ +// 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/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import 'camera_controller.dart'; + +/// A widget showing a live camera preview. +class CameraPreview extends StatelessWidget { + /// Creates a preview widget for the given camera controller. + const CameraPreview(this.controller, {Key? key, this.child}) + : super(key: key); + + /// The controller for the camera that the preview is shown for. + final CameraController controller; + + /// A widget to overlay on top of the camera preview + final Widget? child; + + @override + Widget build(BuildContext context) { + return controller.value.isInitialized + ? ValueListenableBuilder( + valueListenable: controller, + builder: (BuildContext context, Object? value, Widget? child) { + final double cameraAspectRatio = + controller.value.previewSize!.width / + controller.value.previewSize!.height; + return AspectRatio( + aspectRatio: _isLandscape() + ? cameraAspectRatio + : (1 / cameraAspectRatio), + child: Stack( + fit: StackFit.expand, + children: [ + _wrapInRotatedBox(child: controller.buildPreview()), + child ?? Container(), + ], + ), + ); + }, + child: child, + ) + : Container(); + } + + Widget _wrapInRotatedBox({required Widget child}) { + if (kIsWeb || defaultTargetPlatform != TargetPlatform.android) { + return child; + } + + return RotatedBox( + quarterTurns: _getQuarterTurns(), + child: child, + ); + } + + bool _isLandscape() { + return [ + DeviceOrientation.landscapeLeft, + DeviceOrientation.landscapeRight + ].contains(_getApplicableOrientation()); + } + + int _getQuarterTurns() { + final Map turns = { + DeviceOrientation.portraitUp: 0, + DeviceOrientation.landscapeRight: 1, + DeviceOrientation.portraitDown: 2, + DeviceOrientation.landscapeLeft: 3, + }; + return turns[_getApplicableOrientation()]!; + } + + DeviceOrientation _getApplicableOrientation() { + return controller.value.isRecordingVideo + ? controller.value.recordingOrientation! + : (controller.value.previewPauseOrientation ?? + controller.value.lockedCaptureOrientation ?? + controller.value.deviceOrientation); + } +} diff --git a/packages/camera/camera_avfoundation/example/lib/main.dart b/packages/camera/camera_avfoundation/example/lib/main.dart index c0181a5d36a1..1f3e06d33b9b 100644 --- a/packages/camera/camera_avfoundation/example/lib/main.dart +++ b/packages/camera/camera_avfoundation/example/lib/main.dart @@ -4,13 +4,17 @@ import 'dart:async'; import 'dart:io'; +import 'dart:math'; -import 'package:camera/camera.dart'; +import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:video_player/video_player.dart'; +import 'camera_controller.dart'; +import 'camera_preview.dart'; + /// Camera example home widget. class CameraExampleHome extends StatefulWidget { /// Default Constructor @@ -108,7 +112,6 @@ class _CameraExampleHomeState extends State super.dispose(); } - // #docregion AppLifecycle @override void didChangeAppLifecycleState(AppLifecycleState state) { final CameraController? cameraController = controller; @@ -124,7 +127,6 @@ class _CameraExampleHomeState extends State onNewCameraSelected(cameraController.description); } } - // #enddocregion AppLifecycle @override Widget build(BuildContext context) { @@ -218,7 +220,8 @@ class _CameraExampleHomeState extends State _currentScale = (_baseScale * details.scale) .clamp(_minAvailableZoom, _maxAvailableZoom); - await controller!.setZoomLevel(_currentScale); + await CameraPlatform.instance + .setZoomLevel(controller!.cameraId, _currentScale); } /// Display the thumbnail of the captured image or video. @@ -403,7 +406,8 @@ class _CameraExampleHomeState extends State : null, onLongPress: () { if (controller != null) { - controller!.setExposurePoint(null); + CameraPlatform.instance + .setExposurePoint(controller!.cameraId, null); showInSnackBar('Resetting exposure point'); } }, @@ -487,7 +491,8 @@ class _CameraExampleHomeState extends State : null, onLongPress: () { if (controller != null) { - controller!.setFocusPoint(null); + CameraPlatform.instance + .setFocusPoint(controller!.cameraId, null); } showInSnackBar('Resetting focus point'); }, @@ -537,7 +542,8 @@ class _CameraExampleHomeState extends State ), IconButton( icon: cameraController != null && - cameraController.value.isRecordingPaused + (!cameraController.value.isRecordingVideo || + cameraController.value.isRecordingPaused) ? const Icon(Icons.play_arrow) : const Icon(Icons.pause), color: Colors.blue, @@ -624,12 +630,12 @@ class _CameraExampleHomeState extends State final CameraController cameraController = controller!; - final Offset offset = Offset( + final Point point = Point( details.localPosition.dx / constraints.maxWidth, details.localPosition.dy / constraints.maxHeight, ); - cameraController.setExposurePoint(offset); - cameraController.setFocusPoint(offset); + CameraPlatform.instance.setExposurePoint(cameraController.cameraId, point); + CameraPlatform.instance.setFocusPoint(cameraController.cameraId, point); } Future onNewCameraSelected(CameraDescription cameraDescription) async { @@ -658,10 +664,6 @@ class _CameraExampleHomeState extends State if (mounted) { setState(() {}); } - if (cameraController.value.hasError) { - showInSnackBar( - 'Camera error ${cameraController.value.errorDescription}'); - } }); try { @@ -670,18 +672,20 @@ class _CameraExampleHomeState extends State // The exposure mode is currently not supported on the web. ...!kIsWeb ? >[ - cameraController.getMinExposureOffset().then( - (double value) => _minAvailableExposureOffset = value), - cameraController - .getMaxExposureOffset() + CameraPlatform.instance + .getMinExposureOffset(cameraController.cameraId) + .then( + (double value) => _minAvailableExposureOffset = value), + CameraPlatform.instance + .getMaxExposureOffset(cameraController.cameraId) .then((double value) => _maxAvailableExposureOffset = value) ] : >[], - cameraController - .getMaxZoomLevel() + CameraPlatform.instance + .getMaxZoomLevel(cameraController.cameraId) .then((double value) => _maxAvailableZoom = value), - cameraController - .getMinZoomLevel() + CameraPlatform.instance + .getMinZoomLevel(cameraController.cameraId) .then((double value) => _minAvailableZoom = value), ]); } on CameraException catch (e) { @@ -1076,7 +1080,7 @@ Future main() async { // Fetch the available cameras before initializing the app. try { WidgetsFlutterBinding.ensureInitialized(); - _cameras = await availableCameras(); + _cameras = await CameraPlatform.instance.availableCameras(); } on CameraException catch (e) { _logError(e.code, e.description); } diff --git a/packages/camera/camera_avfoundation/example/pubspec.yaml b/packages/camera/camera_avfoundation/example/pubspec.yaml index 8449e7c5acd0..78927fc70d76 100644 --- a/packages/camera/camera_avfoundation/example/pubspec.yaml +++ b/packages/camera/camera_avfoundation/example/pubspec.yaml @@ -17,6 +17,7 @@ dependencies: flutter: sdk: flutter path_provider: ^2.0.0 + quiver: ^3.0.0 video_player: ^2.1.4 dev_dependencies: From 1432eaa4e8231d72cb885aa01826b893ae0209a8 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Wed, 8 Jun 2022 16:00:43 -0400 Subject: [PATCH 08/13] Clean up platform checks, try enabling tests on iOS --- .../example/integration_test/camera_test.dart | 64 +------- .../example/integration_test/camera_test.dart | 147 ++++++------------ 2 files changed, 53 insertions(+), 158 deletions(-) diff --git a/packages/camera/camera_android/example/integration_test/camera_test.dart b/packages/camera/camera_android/example/integration_test/camera_test.dart index 2043a162b9cc..7349a4ed7f76 100644 --- a/packages/camera/camera_android/example/integration_test/camera_test.dart +++ b/packages/camera/camera_android/example/integration_test/camera_test.dart @@ -30,10 +30,8 @@ void main() { final Map presetExpectedSizes = { - ResolutionPreset.low: - Platform.isAndroid ? const Size(240, 320) : const Size(288, 352), - ResolutionPreset.medium: - Platform.isAndroid ? const Size(480, 720) : const Size(480, 640), + ResolutionPreset.low: const Size(240, 320), + ResolutionPreset.medium: const Size(480, 720), ResolutionPreset.high: const Size(720, 1280), ResolutionPreset.veryHigh: const Size(1080, 1920), ResolutionPreset.ultraHigh: const Size(2160, 3840), @@ -208,10 +206,10 @@ void main() { await videoController.dispose(); expect(duration, lessThan(recordingTime - timePaused)); - }, skip: !Platform.isAndroid); + }); testWidgets( - 'Android image streaming', + 'image streaming', (WidgetTester tester) async { final List cameras = await CameraPlatform.instance.availableCameras(); @@ -245,59 +243,5 @@ void main() { await controller.stopImageStream(); await controller.dispose(); }, - skip: !Platform.isAndroid, - ); - - /// Start streaming with specifying the ImageFormatGroup. - Future startStreaming(List cameras, - ImageFormatGroup? imageFormatGroup) async { - final CameraController controller = CameraController( - cameras.first, - ResolutionPreset.low, - enableAudio: false, - imageFormatGroup: imageFormatGroup, - ); - - await controller.initialize(); - final Completer _completer = Completer(); - - await controller.startImageStream((CameraImageData image) { - if (!_completer.isCompleted) { - Future(() async { - await controller.stopImageStream(); - await controller.dispose(); - }).then((Object? value) { - _completer.complete(image); - }); - } - }); - return _completer.future; - } - - testWidgets( - 'iOS image streaming with imageFormatGroup', - (WidgetTester tester) async { - final List cameras = - await CameraPlatform.instance.availableCameras(); - if (cameras.isEmpty) { - return; - } - - CameraImageData _image = await startStreaming(cameras, null); - expect(_image, isNotNull); - expect(_image.format.group, ImageFormatGroup.bgra8888); - expect(_image.planes.length, 1); - - _image = await startStreaming(cameras, ImageFormatGroup.yuv420); - expect(_image, isNotNull); - expect(_image.format.group, ImageFormatGroup.yuv420); - expect(_image.planes.length, 2); - - _image = await startStreaming(cameras, ImageFormatGroup.bgra8888); - expect(_image, isNotNull); - expect(_image.format.group, ImageFormatGroup.bgra8888); - expect(_image.planes.length, 1); - }, - skip: !Platform.isIOS, ); } diff --git a/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart index 2043a162b9cc..6ac7a6860dd9 100644 --- a/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart +++ b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart @@ -30,10 +30,8 @@ void main() { final Map presetExpectedSizes = { - ResolutionPreset.low: - Platform.isAndroid ? const Size(240, 320) : const Size(288, 352), - ResolutionPreset.medium: - Platform.isAndroid ? const Size(480, 720) : const Size(480, 640), + ResolutionPreset.low: const Size(288, 352), + ResolutionPreset.medium: const Size(480, 640), ResolutionPreset.high: const Size(720, 1280), ResolutionPreset.veryHigh: const Size(1080, 1920), ResolutionPreset.ultraHigh: const Size(2160, 3840), @@ -72,33 +70,29 @@ void main() { expectedSize, Size(image.height.toDouble(), image.width.toDouble())); } - testWidgets( - 'Capture specific image resolutions', - (WidgetTester tester) async { - final List cameras = - await CameraPlatform.instance.availableCameras(); - if (cameras.isEmpty) { - return; - } - for (final CameraDescription cameraDescription in cameras) { - bool previousPresetExactlySupported = true; - for (final MapEntry preset - in presetExpectedSizes.entries) { - final CameraController controller = - CameraController(cameraDescription, preset.key); - await controller.initialize(); - final bool presetExactlySupported = - await testCaptureImageResolution(controller, preset.key); - assert(!(!previousPresetExactlySupported && presetExactlySupported), - 'The camera took higher resolution pictures at a lower resolution.'); - previousPresetExactlySupported = presetExactlySupported; - await controller.dispose(); - } + testWidgets('Capture specific image resolutions', + (WidgetTester tester) async { + final List cameras = + await CameraPlatform.instance.availableCameras(); + if (cameras.isEmpty) { + return; + } + for (final CameraDescription cameraDescription in cameras) { + bool previousPresetExactlySupported = true; + for (final MapEntry preset + in presetExpectedSizes.entries) { + final CameraController controller = + CameraController(cameraDescription, preset.key); + await controller.initialize(); + final bool presetExactlySupported = + await testCaptureImageResolution(controller, preset.key); + assert(!(!previousPresetExactlySupported && presetExactlySupported), + 'The camera took higher resolution pictures at a lower resolution.'); + previousPresetExactlySupported = presetExactlySupported; + await controller.dispose(); } - }, - // TODO(egarciad): Fix https://github.com/flutter/flutter/issues/93686. - skip: true, - ); + } + }); // This tests that the capture is no bigger than the preset, since we have // automatic code to fall back to smaller sizes when we need to. Returns @@ -127,34 +121,30 @@ void main() { expectedSize, Size(video.height, video.width)); } - testWidgets( - 'Capture specific video resolutions', - (WidgetTester tester) async { - final List cameras = - await CameraPlatform.instance.availableCameras(); - if (cameras.isEmpty) { - return; - } - for (final CameraDescription cameraDescription in cameras) { - bool previousPresetExactlySupported = true; - for (final MapEntry preset - in presetExpectedSizes.entries) { - final CameraController controller = - CameraController(cameraDescription, preset.key); - await controller.initialize(); - await controller.prepareForVideoRecording(); - final bool presetExactlySupported = - await testCaptureVideoResolution(controller, preset.key); - assert(!(!previousPresetExactlySupported && presetExactlySupported), - 'The camera took higher resolution pictures at a lower resolution.'); - previousPresetExactlySupported = presetExactlySupported; - await controller.dispose(); - } + testWidgets('Capture specific video resolutions', + (WidgetTester tester) async { + final List cameras = + await CameraPlatform.instance.availableCameras(); + if (cameras.isEmpty) { + return; + } + for (final CameraDescription cameraDescription in cameras) { + bool previousPresetExactlySupported = true; + for (final MapEntry preset + in presetExpectedSizes.entries) { + final CameraController controller = + CameraController(cameraDescription, preset.key); + await controller.initialize(); + await controller.prepareForVideoRecording(); + final bool presetExactlySupported = + await testCaptureVideoResolution(controller, preset.key); + assert(!(!previousPresetExactlySupported && presetExactlySupported), + 'The camera took higher resolution pictures at a lower resolution.'); + previousPresetExactlySupported = presetExactlySupported; + await controller.dispose(); } - }, - // TODO(egarciad): Fix https://github.com/flutter/flutter/issues/93686. - skip: true, - ); + } + }); testWidgets('Pause and resume video recording', (WidgetTester tester) async { final List cameras = @@ -208,45 +198,7 @@ void main() { await videoController.dispose(); expect(duration, lessThan(recordingTime - timePaused)); - }, skip: !Platform.isAndroid); - - testWidgets( - 'Android image streaming', - (WidgetTester tester) async { - final List cameras = - await CameraPlatform.instance.availableCameras(); - if (cameras.isEmpty) { - return; - } - - final CameraController controller = CameraController( - cameras[0], - ResolutionPreset.low, - enableAudio: false, - ); - - await controller.initialize(); - bool _isDetecting = false; - - await controller.startImageStream((CameraImageData image) { - if (_isDetecting) { - return; - } - - _isDetecting = true; - - expectLater(image, isNotNull).whenComplete(() => _isDetecting = false); - }); - - expect(controller.value.isStreamingImages, true); - - sleep(const Duration(milliseconds: 500)); - - await controller.stopImageStream(); - await controller.dispose(); - }, - skip: !Platform.isAndroid, - ); + }); /// Start streaming with specifying the ImageFormatGroup. Future startStreaming(List cameras, @@ -275,7 +227,7 @@ void main() { } testWidgets( - 'iOS image streaming with imageFormatGroup', + 'image streaming with imageFormatGroup', (WidgetTester tester) async { final List cameras = await CameraPlatform.instance.availableCameras(); @@ -298,6 +250,5 @@ void main() { expect(_image.format.group, ImageFormatGroup.bgra8888); expect(_image.planes.length, 1); }, - skip: !Platform.isIOS, ); } From 4c176f36beb82531ab1e0b559effbf4075b0ecc0 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Wed, 8 Jun 2022 16:03:58 -0400 Subject: [PATCH 09/13] Fix app-facing podfile --- packages/camera/camera/example/ios/Podfile | 7 ------- 1 file changed, 7 deletions(-) diff --git a/packages/camera/camera/example/ios/Podfile b/packages/camera/camera/example/ios/Podfile index 5bc7b7e85717..f7d6a5e68c3a 100644 --- a/packages/camera/camera/example/ios/Podfile +++ b/packages/camera/camera/example/ios/Podfile @@ -29,13 +29,6 @@ flutter_ios_podfile_setup target 'Runner' do flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) - - target 'RunnerTests' do - platform :ios, '9.0' - inherit! :search_paths - # Pods for testing - pod 'OCMock', '~> 3.8.1' - end end post_install do |installer| From 63d73660361076e65596ffc63dbb3ac2ebc43320 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Wed, 8 Jun 2022 16:17:00 -0400 Subject: [PATCH 10/13] Add implements to pubspecs --- packages/camera/camera_android/pubspec.yaml | 1 + packages/camera/camera_avfoundation/pubspec.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index a4c6470a4ab3..908d55f9c50e 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -10,6 +10,7 @@ environment: flutter: plugin: + implements: camera platforms: android: package: io.flutter.plugins.camera diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index cac33b33c2f9..237f5ad329c5 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -10,6 +10,7 @@ environment: flutter: plugin: + implements: camera platforms: ios: pluginClass: CameraPlugin From 9075507daf97ce2f4ed111abcb63ba589a40e497 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Wed, 8 Jun 2022 16:24:14 -0400 Subject: [PATCH 11/13] iOS import fix --- .../ios/Classes/camera_avfoundation-umbrella.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_avfoundation/ios/Classes/camera_avfoundation-umbrella.h b/packages/camera/camera_avfoundation/ios/Classes/camera_avfoundation-umbrella.h index 5c39401e6261..f8464aaae3dc 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/camera_avfoundation-umbrella.h +++ b/packages/camera/camera_avfoundation/ios/Classes/camera_avfoundation-umbrella.h @@ -3,7 +3,7 @@ // found in the LICENSE file. #import -#import +#import FOUNDATION_EXPORT double cameraVersionNumber; FOUNDATION_EXPORT const unsigned char cameraVersionString[]; From f8af7e69d7c372f2a14e4dfcc1420c35d247f8d1 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Wed, 8 Jun 2022 16:24:34 -0400 Subject: [PATCH 12/13] Analysis fixes --- .../camera/camera_android/example/lib/camera_controller.dart | 1 - .../camera_avfoundation/example/lib/camera_controller.dart | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/camera/camera_android/example/lib/camera_controller.dart b/packages/camera/camera_android/example/lib/camera_controller.dart index 2015b1f0ad3a..5a7a79c8d96c 100644 --- a/packages/camera/camera_android/example/lib/camera_controller.dart +++ b/packages/camera/camera_android/example/lib/camera_controller.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:math'; import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/foundation.dart'; diff --git a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart index 2015b1f0ad3a..5a7a79c8d96c 100644 --- a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart +++ b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:math'; import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/foundation.dart'; From 509d5d43e9080da304deaab3bbf15776540fac04 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Wed, 8 Jun 2022 16:41:07 -0400 Subject: [PATCH 13/13] Missed analysis fix --- .../camera_android/example/integration_test/camera_test.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/camera/camera_android/example/integration_test/camera_test.dart b/packages/camera/camera_android/example/integration_test/camera_test.dart index 7349a4ed7f76..05c669307dcc 100644 --- a/packages/camera/camera_android/example/integration_test/camera_test.dart +++ b/packages/camera/camera_android/example/integration_test/camera_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; import 'dart:io'; import 'dart:ui';