From 8cdb530ef0b9058b6b264ddeba32de2f28018f33 Mon Sep 17 00:00:00 2001 From: "Bodhi Mulders (BeMacized)" Date: Tue, 12 Jan 2021 12:05:23 +0100 Subject: [PATCH 1/3] Fix error on first camera initialize in example app --- packages/camera/camera/example/lib/main.dart | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index 681a45172816..efc0c904f507 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -571,10 +571,12 @@ class _CameraExampleHomeState extends State try { await controller.initialize(); - _minAvailableExposureOffset = await controller.getMinExposureOffset(); - _maxAvailableExposureOffset = await controller.getMaxExposureOffset(); - _maxAvailableZoom = await controller.getMaxZoomLevel(); - _minAvailableZoom = await controller.getMinZoomLevel(); + await Future.wait([ + controller.getMinExposureOffset().then((value) => _minAvailableExposureOffset = value), + controller.getMaxExposureOffset().then((value) => _maxAvailableExposureOffset = value), + controller.getMaxZoomLevel().then((value) => _maxAvailableZoom = value), + controller.getMinZoomLevel().then((value) => _minAvailableZoom = value), + ]); } on CameraException catch (e) { _showCameraException(e); } From f478a5d48af2fbe46a756823665f682d24f0855a Mon Sep 17 00:00:00 2001 From: "Bodhi Mulders (BeMacized)" Date: Tue, 12 Jan 2021 12:36:50 +0100 Subject: [PATCH 2/3] Improved error handling a bit & updated tests. --- packages/camera/camera/example/lib/main.dart | 8 +- .../camera/lib/src/camera_controller.dart | 129 ++++---------- .../camera/test/camera_image_stream_test.dart | 42 +++-- packages/camera/camera/test/camera_test.dart | 168 +++++++++++++----- 4 files changed, 186 insertions(+), 161 deletions(-) diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index efc0c904f507..0b5684301c97 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -572,8 +572,12 @@ class _CameraExampleHomeState extends State try { await controller.initialize(); await Future.wait([ - controller.getMinExposureOffset().then((value) => _minAvailableExposureOffset = value), - controller.getMaxExposureOffset().then((value) => _maxAvailableExposureOffset = value), + controller + .getMinExposureOffset() + .then((value) => _minAvailableExposureOffset = value), + controller + .getMaxExposureOffset() + .then((value) => _maxAvailableExposureOffset = value), controller.getMaxZoomLevel().then((value) => _maxAvailableZoom = value), controller.getMinZoomLevel().then((value) => _minAvailableZoom = value), ]); diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart index ae79cc4ad367..aa7aa3bb834f 100644 --- a/packages/camera/camera/lib/src/camera_controller.dart +++ b/packages/camera/camera/lib/src/camera_controller.dart @@ -285,12 +285,7 @@ class CameraController extends ValueNotifier { /// /// Throws a [CameraException] if the capture fails. Future takePicture() async { - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController.', - 'takePicture was called on uninitialized CameraController', - ); - } + _throwIfNotInitialized("takePicture"); if (value.isTakingPicture) { throw CameraException( 'Previous capture has not returned yet.', @@ -328,13 +323,7 @@ class CameraController extends ValueNotifier { Future startImageStream(onLatestImageAvailable onAvailable) async { assert(defaultTargetPlatform == TargetPlatform.android || defaultTargetPlatform == TargetPlatform.iOS); - - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'startImageStream was called on uninitialized CameraController.', - ); - } + _throwIfNotInitialized("startImageStream"); if (value.isRecordingVideo) { throw CameraException( 'A video recording is already started.', @@ -374,13 +363,7 @@ class CameraController extends ValueNotifier { Future stopImageStream() async { assert(defaultTargetPlatform == TargetPlatform.android || defaultTargetPlatform == TargetPlatform.iOS); - - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'stopImageStream was called on uninitialized CameraController.', - ); - } + _throwIfNotInitialized("stopImageStream"); if (value.isRecordingVideo) { throw CameraException( 'A video recording is already started.', @@ -410,12 +393,7 @@ class CameraController extends ValueNotifier { /// The video is returned as a [XFile] after calling [stopVideoRecording]. /// Throws a [CameraException] if the capture fails. Future startVideoRecording() async { - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'startVideoRecording was called on uninitialized CameraController', - ); - } + _throwIfNotInitialized("startVideoRecording"); if (value.isRecordingVideo) { throw CameraException( 'A video recording is already started.', @@ -441,12 +419,7 @@ class CameraController extends ValueNotifier { /// /// Throws a [CameraException] if the capture failed. Future stopVideoRecording() async { - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'stopVideoRecording was called on uninitialized CameraController', - ); - } + _throwIfNotInitialized("stopVideoRecording"); if (!value.isRecordingVideo) { throw CameraException( 'No video is recording', @@ -466,12 +439,7 @@ class CameraController extends ValueNotifier { /// /// This feature is only available on iOS and Android sdk 24+. Future pauseVideoRecording() async { - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'pauseVideoRecording was called on uninitialized CameraController', - ); - } + _throwIfNotInitialized("pauseVideoRecording"); if (!value.isRecordingVideo) { throw CameraException( 'No video is recording', @@ -490,12 +458,7 @@ class CameraController extends ValueNotifier { /// /// This feature is only available on iOS and Android sdk 24+. Future resumeVideoRecording() async { - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'resumeVideoRecording was called on uninitialized CameraController', - ); - } + _throwIfNotInitialized("resumeVideoRecording"); if (!value.isRecordingVideo) { throw CameraException( 'No video is recording', @@ -512,12 +475,7 @@ class CameraController extends ValueNotifier { /// Returns a widget showing a live camera preview. Widget buildPreview() { - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'buildView() was called on uninitialized CameraController.', - ); - } + _throwIfNotInitialized("buildPreview"); try { return CameraPlatform.instance.buildPreview(_cameraId); } on PlatformException catch (e) { @@ -527,13 +485,7 @@ class CameraController extends ValueNotifier { /// Gets the maximum supported zoom level for the selected camera. Future getMaxZoomLevel() { - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'getMaxZoomLevel was called on uninitialized CameraController', - ); - } - + _throwIfNotInitialized("getMaxZoomLevel"); try { return CameraPlatform.instance.getMaxZoomLevel(_cameraId); } on PlatformException catch (e) { @@ -543,13 +495,7 @@ class CameraController extends ValueNotifier { /// Gets the minimum supported zoom level for the selected camera. Future getMinZoomLevel() { - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'getMinZoomLevel was called on uninitialized CameraController', - ); - } - + _throwIfNotInitialized("getMinZoomLevel"); try { return CameraPlatform.instance.getMinZoomLevel(_cameraId); } on PlatformException catch (e) { @@ -563,13 +509,7 @@ class CameraController extends ValueNotifier { /// zoom level returned by the `getMaxZoomLevel`. Throws an `CameraException` /// when an illegal zoom level is suplied. Future setZoomLevel(double zoom) { - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'setZoomLevel was called on uninitialized CameraController', - ); - } - + _throwIfNotInitialized("setZoomLevel"); try { return CameraPlatform.instance.setZoomLevel(_cameraId, zoom); } on PlatformException catch (e) { @@ -621,13 +561,7 @@ class CameraController extends ValueNotifier { /// Gets the minimum supported exposure offset for the selected camera in EV units. Future getMinExposureOffset() async { - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'getMinExposureOffset was called on uninitialized CameraController', - ); - } - + _throwIfNotInitialized("getMinExposureOffset"); try { return CameraPlatform.instance.getMinExposureOffset(_cameraId); } on PlatformException catch (e) { @@ -637,13 +571,7 @@ class CameraController extends ValueNotifier { /// Gets the maximum supported exposure offset for the selected camera in EV units. Future getMaxExposureOffset() async { - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'getMaxExposureOffset was called on uninitialized CameraController', - ); - } - + _throwIfNotInitialized("getMaxExposureOffset"); try { return CameraPlatform.instance.getMaxExposureOffset(_cameraId); } on PlatformException catch (e) { @@ -655,13 +583,7 @@ class CameraController extends ValueNotifier { /// /// Returns 0 when the camera supports using a free value without stepping. Future getExposureOffsetStepSize() async { - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'getExposureOffsetStepSize was called on uninitialized CameraController', - ); - } - + _throwIfNotInitialized("getExposureOffsetStepSize"); try { return CameraPlatform.instance.getExposureOffsetStepSize(_cameraId); } on PlatformException catch (e) { @@ -681,13 +603,7 @@ class CameraController extends ValueNotifier { /// /// Returns the (rounded) offset value that was set. Future setExposureOffset(double offset) async { - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'setExposureOffset was called on uninitialized CameraController', - ); - } - + _throwIfNotInitialized("setExposureOffset"); // Check if offset is in range List range = await Future.wait([getMinExposureOffset(), getMaxExposureOffset()]); @@ -763,4 +679,19 @@ class CameraController extends ValueNotifier { await CameraPlatform.instance.dispose(_cameraId); } } + + void _throwIfNotInitialized(String functionName) { + if (!value.isInitialized) { + throw CameraException( + 'Uninitialized CameraController', + '$functionName() was called on an uninitialized CameraController.', + ); + } + if (_isDisposed) { + throw CameraException( + 'Disposed CameraController', + '$functionName() was called on a disposed CameraController.', + ); + } + } } diff --git a/packages/camera/camera/test/camera_image_stream_test.dart b/packages/camera/camera/test/camera_image_stream_test.dart index be7047f2220f..57e3aeb36f3f 100644 --- a/packages/camera/camera/test/camera_image_stream_test.dart +++ b/packages/camera/camera/test/camera_image_stream_test.dart @@ -22,12 +22,21 @@ void main() { ResolutionPreset.max); expect( - () => cameraController.startImageStream((image) => null), - throwsA(isA().having( - (error) => error.description, - 'Uninitialized CameraController.', - 'startImageStream was called on uninitialized CameraController.', - ))); + () => cameraController.startImageStream((image) => null), + throwsA( + isA() + .having( + (error) => error.code, + 'code', + 'Uninitialized CameraController', + ) + .having( + (error) => error.description, + 'description', + 'startImageStream() was called on an uninitialized CameraController.', + ), + ), + ); }); test('startImageStream() throws $CameraException when recording videos', @@ -107,12 +116,21 @@ void main() { ResolutionPreset.max); expect( - cameraController.stopImageStream, - throwsA(isA().having( - (error) => error.description, - 'Uninitialized CameraController.', - 'stopImageStream was called on uninitialized CameraController.', - ))); + cameraController.stopImageStream, + throwsA( + isA() + .having( + (error) => error.code, + 'code', + 'Uninitialized CameraController', + ) + .having( + (error) => error.description, + 'description', + 'stopImageStream() was called on an uninitialized CameraController.', + ), + ), + ); }); test('stopImageStream() throws $CameraException when recording videos', diff --git a/packages/camera/camera/test/camera_test.dart b/packages/camera/camera/test/camera_test.dart index 1cea609d1741..2636cd141e81 100644 --- a/packages/camera/camera/test/camera_test.dart +++ b/packages/camera/camera/test/camera_test.dart @@ -210,12 +210,21 @@ void main() { sensorOrientation: 90), ResolutionPreset.max); expect( - cameraController.takePicture(), - throwsA(isA().having( - (error) => error.description, - 'Uninitialized CameraController.', - 'takePicture was called on uninitialized CameraController', - ))); + cameraController.takePicture(), + throwsA( + isA() + .having( + (error) => error.code, + 'code', + 'Uninitialized CameraController', + ) + .having( + (error) => error.description, + 'description', + 'takePicture() was called on an uninitialized CameraController.', + ), + ), + ); }); test('takePicture() throws $CameraException when takePicture is true', @@ -283,12 +292,21 @@ void main() { ResolutionPreset.max); expect( - cameraController.startVideoRecording(), - throwsA(isA().having( - (error) => error.description, - 'Uninitialized CameraController', - 'startVideoRecording was called on uninitialized CameraController', - ))); + cameraController.startVideoRecording(), + throwsA( + isA() + .having( + (error) => error.code, + 'code', + 'Uninitialized CameraController', + ) + .having( + (error) => error.description, + 'description', + 'startVideoRecording() was called on an uninitialized CameraController.', + ), + ), + ); }); test('startVideoRecording() throws $CameraException when recording videos', () async { @@ -347,12 +365,21 @@ void main() { ResolutionPreset.max); expect( - cameraController.getMaxZoomLevel, - throwsA(isA().having( - (error) => error.description, - 'Uninitialized CameraController', - 'getMaxZoomLevel was called on uninitialized CameraController', - ))); + cameraController.getMaxZoomLevel, + throwsA( + isA() + .having( + (error) => error.code, + 'code', + 'Uninitialized CameraController', + ) + .having( + (error) => error.description, + 'description', + 'getMaxZoomLevel() was called on an uninitialized CameraController.', + ), + ), + ); }); test('getMaxZoomLevel() throws $CameraException when disposed', () async { @@ -367,12 +394,21 @@ void main() { await cameraController.dispose(); expect( - cameraController.getMaxZoomLevel, - throwsA(isA().having( - (error) => error.description, - 'Uninitialized CameraController', - 'getMaxZoomLevel was called on uninitialized CameraController', - ))); + cameraController.getMaxZoomLevel, + throwsA( + isA() + .having( + (error) => error.code, + 'code', + 'Disposed CameraController', + ) + .having( + (error) => error.description, + 'description', + 'getMaxZoomLevel() was called on a disposed CameraController.', + ), + ), + ); }); test( @@ -429,12 +465,21 @@ void main() { ResolutionPreset.max); expect( - cameraController.getMinZoomLevel, - throwsA(isA().having( - (error) => error.description, - 'Uninitialized CameraController', - 'getMinZoomLevel was called on uninitialized CameraController', - ))); + cameraController.getMinZoomLevel, + throwsA( + isA() + .having( + (error) => error.code, + 'code', + 'Uninitialized CameraController', + ) + .having( + (error) => error.description, + 'description', + 'getMinZoomLevel() was called on an uninitialized CameraController.', + ), + ), + ); }); test('getMinZoomLevel() throws $CameraException when disposed', () async { @@ -449,12 +494,21 @@ void main() { await cameraController.dispose(); expect( - cameraController.getMinZoomLevel, - throwsA(isA().having( - (error) => error.description, - 'Uninitialized CameraController', - 'getMinZoomLevel was called on uninitialized CameraController', - ))); + cameraController.getMinZoomLevel, + throwsA( + isA() + .having( + (error) => error.code, + 'code', + 'Disposed CameraController', + ) + .having( + (error) => error.description, + 'description', + 'getMinZoomLevel() was called on a disposed CameraController.', + ), + ), + ); }); test( @@ -510,12 +564,21 @@ void main() { ResolutionPreset.max); expect( - () => cameraController.setZoomLevel(42.0), - throwsA(isA().having( - (error) => error.description, - 'Uninitialized CameraController', - 'setZoomLevel was called on uninitialized CameraController', - ))); + () => cameraController.setZoomLevel(42.0), + throwsA( + isA() + .having( + (error) => error.code, + 'code', + 'Uninitialized CameraController', + ) + .having( + (error) => error.description, + 'description', + 'setZoomLevel() was called on an uninitialized CameraController.', + ), + ), + ); }); test('setZoomLevel() throws $CameraException when disposed', () async { @@ -530,12 +593,21 @@ void main() { await cameraController.dispose(); expect( - () => cameraController.setZoomLevel(42.0), - throwsA(isA().having( - (error) => error.description, - 'Uninitialized CameraController', - 'setZoomLevel was called on uninitialized CameraController', - ))); + () => cameraController.setZoomLevel(42.0), + throwsA( + isA() + .having( + (error) => error.code, + 'code', + 'Disposed CameraController', + ) + .having( + (error) => error.description, + 'description', + 'setZoomLevel() was called on a disposed CameraController.', + ), + ), + ); }); test( From f53f116b12d1266e9e843b8e3acfb1cbc7ede9fe Mon Sep 17 00:00:00 2001 From: "Bodhi Mulders (BeMacized)" Date: Tue, 12 Jan 2021 12:40:17 +0100 Subject: [PATCH 3/3] Updated pubspec and changelog --- packages/camera/camera/CHANGELOG.md | 4 ++++ packages/camera/camera/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 1a2b03d93a6a..ef6089255b20 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.6+1 + +* Improved error feedback by differentiating between uninitialized and disposed camera controllers. + ## 0.6.6 * Adds auto focus support for Android and iOS implementations. diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 0b21497b5462..9581aba73d1c 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -2,7 +2,7 @@ name: camera description: A Flutter plugin for getting information about and controlling the camera on Android and iOS. Supports previewing the camera feed, capturing images, capturing video, and streaming image buffers to dart. -version: 0.6.6 +version: 0.6.6+1 homepage: https://github.com/flutter/plugins/tree/master/packages/camera/camera dependencies: