Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

[camera] Fix initialization error in camera example on iOS #3406

Merged
merged 4 commits into from
Jan 14, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/camera/camera/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
14 changes: 10 additions & 4 deletions packages/camera/camera/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -571,10 +571,16 @@ class _CameraExampleHomeState extends State<CameraExampleHome>

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);
}
Expand Down
129 changes: 30 additions & 99 deletions packages/camera/camera/lib/src/camera_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -285,12 +285,7 @@ class CameraController extends ValueNotifier<CameraValue> {
///
/// Throws a [CameraException] if the capture fails.
Future<XFile> 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.',
Expand Down Expand Up @@ -328,13 +323,7 @@ class CameraController extends ValueNotifier<CameraValue> {
Future<void> 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.',
Expand Down Expand Up @@ -374,13 +363,7 @@ class CameraController extends ValueNotifier<CameraValue> {
Future<void> 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.',
Expand Down Expand Up @@ -410,12 +393,7 @@ class CameraController extends ValueNotifier<CameraValue> {
/// The video is returned as a [XFile] after calling [stopVideoRecording].
/// Throws a [CameraException] if the capture fails.
Future<void> 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.',
Expand All @@ -441,12 +419,7 @@ class CameraController extends ValueNotifier<CameraValue> {
///
/// Throws a [CameraException] if the capture failed.
Future<XFile> 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',
Expand All @@ -466,12 +439,7 @@ class CameraController extends ValueNotifier<CameraValue> {
///
/// This feature is only available on iOS and Android sdk 24+.
Future<void> 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',
Expand All @@ -490,12 +458,7 @@ class CameraController extends ValueNotifier<CameraValue> {
///
/// This feature is only available on iOS and Android sdk 24+.
Future<void> 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',
Expand All @@ -512,12 +475,7 @@ class CameraController extends ValueNotifier<CameraValue> {

/// 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) {
Expand All @@ -527,13 +485,7 @@ class CameraController extends ValueNotifier<CameraValue> {

/// Gets the maximum supported zoom level for the selected camera.
Future<double> 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) {
Expand All @@ -543,13 +495,7 @@ class CameraController extends ValueNotifier<CameraValue> {

/// Gets the minimum supported zoom level for the selected camera.
Future<double> 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) {
Expand All @@ -563,13 +509,7 @@ class CameraController extends ValueNotifier<CameraValue> {
/// zoom level returned by the `getMaxZoomLevel`. Throws an `CameraException`
/// when an illegal zoom level is suplied.
Future<void> 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) {
Expand Down Expand Up @@ -621,13 +561,7 @@ class CameraController extends ValueNotifier<CameraValue> {

/// Gets the minimum supported exposure offset for the selected camera in EV units.
Future<double> 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) {
Expand All @@ -637,13 +571,7 @@ class CameraController extends ValueNotifier<CameraValue> {

/// Gets the maximum supported exposure offset for the selected camera in EV units.
Future<double> 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) {
Expand All @@ -655,13 +583,7 @@ class CameraController extends ValueNotifier<CameraValue> {
///
/// Returns 0 when the camera supports using a free value without stepping.
Future<double> 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) {
Expand All @@ -681,13 +603,7 @@ class CameraController extends ValueNotifier<CameraValue> {
///
/// Returns the (rounded) offset value that was set.
Future<double> 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<double> range =
await Future.wait([getMinExposureOffset(), getMaxExposureOffset()]);
Expand Down Expand Up @@ -763,4 +679,19 @@ class CameraController extends ValueNotifier<CameraValue> {
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.',
);
}
}
}
2 changes: 1 addition & 1 deletion packages/camera/camera/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
42 changes: 30 additions & 12 deletions packages/camera/camera/test/camera_image_stream_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,21 @@ void main() {
ResolutionPreset.max);

expect(
() => cameraController.startImageStream((image) => null),
throwsA(isA<CameraException>().having(
(error) => error.description,
'Uninitialized CameraController.',
'startImageStream was called on uninitialized CameraController.',
)));
() => cameraController.startImageStream((image) => null),
throwsA(
isA<CameraException>()
.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',
Expand Down Expand Up @@ -107,12 +116,21 @@ void main() {
ResolutionPreset.max);

expect(
cameraController.stopImageStream,
throwsA(isA<CameraException>().having(
(error) => error.description,
'Uninitialized CameraController.',
'stopImageStream was called on uninitialized CameraController.',
)));
cameraController.stopImageStream,
throwsA(
isA<CameraException>()
.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',
Expand Down
Loading