Skip to content

Commit

Permalink
[camera] Add back Optional type for nullable CameraController orienta…
Browse files Browse the repository at this point in the history
…tions (flutter#6911)

* Add flag

* Add missing comment

* Add tests

* Bump versions

* Stage changelog changes

* Revert "Fix examples analyze"

This reverts commit 4db1858a29136f3fb07a223d94d7e68b6b8d4b7d.

* Revert "[camera] Remove deprecated Optional type (flutter#6870)"

This reverts commit 3d8b73b.

* Add back optional

* Edit changelog

* Fix semicolon

* Add )
  • Loading branch information
camsim99 committed Jan 26, 2023
1 parent af065a6 commit ff84c44
Show file tree
Hide file tree
Showing 11 changed files with 436 additions and 57 deletions.
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.10.3

* Adds back use of Optional type.

## 0.10.2+1

* Updates code for stricter lint checks.
Expand Down
155 changes: 138 additions & 17 deletions packages/camera/camera/lib/src/camera_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// found in the LICENSE file.

import 'dart:async';
import 'dart:collection';
import 'dart:math';

import 'package:camera_platform_interface/camera_platform_interface.dart';
Expand Down Expand Up @@ -160,10 +161,10 @@ class CameraValue {
bool? exposurePointSupported,
bool? focusPointSupported,
DeviceOrientation? deviceOrientation,
DeviceOrientation? lockedCaptureOrientation,
DeviceOrientation? recordingOrientation,
Optional<DeviceOrientation>? lockedCaptureOrientation,
Optional<DeviceOrientation>? recordingOrientation,
bool? isPreviewPaused,
DeviceOrientation? previewPauseOrientation,
Optional<DeviceOrientation>? previewPauseOrientation,
}) {
return CameraValue(
isInitialized: isInitialized ?? this.isInitialized,
Expand All @@ -180,12 +181,16 @@ class CameraValue {
exposurePointSupported ?? this.exposurePointSupported,
focusPointSupported: focusPointSupported ?? this.focusPointSupported,
deviceOrientation: deviceOrientation ?? this.deviceOrientation,
lockedCaptureOrientation:
lockedCaptureOrientation ?? this.lockedCaptureOrientation,
recordingOrientation: recordingOrientation ?? this.recordingOrientation,
lockedCaptureOrientation: lockedCaptureOrientation == null
? this.lockedCaptureOrientation
: lockedCaptureOrientation.orNull,
recordingOrientation: recordingOrientation == null
? this.recordingOrientation
: recordingOrientation.orNull,
isPreviewPaused: isPreviewPaused ?? this.isPreviewPaused,
previewPauseOrientation:
previewPauseOrientation ?? this.previewPauseOrientation,
previewPauseOrientation: previewPauseOrientation == null
? this.previewPauseOrientation
: previewPauseOrientation.orNull,
);
}

Expand Down Expand Up @@ -353,8 +358,8 @@ class CameraController extends ValueNotifier<CameraValue> {
await CameraPlatform.instance.pausePreview(_cameraId);
value = value.copyWith(
isPreviewPaused: true,
previewPauseOrientation:
value.lockedCaptureOrientation ?? value.deviceOrientation);
previewPauseOrientation: Optional<DeviceOrientation>.of(
value.lockedCaptureOrientation ?? value.deviceOrientation));
} on PlatformException catch (e) {
throw CameraException(e.code, e.message);
}
Expand All @@ -367,7 +372,9 @@ class CameraController extends ValueNotifier<CameraValue> {
}
try {
await CameraPlatform.instance.resumePreview(_cameraId);
value = value.copyWith(isPreviewPaused: false);
value = value.copyWith(
isPreviewPaused: false,
previewPauseOrientation: const Optional<DeviceOrientation>.absent());
} on PlatformException catch (e) {
throw CameraException(e.code, e.message);
}
Expand Down Expand Up @@ -498,9 +505,9 @@ class CameraController extends ValueNotifier<CameraValue> {
value = value.copyWith(
isRecordingVideo: true,
isRecordingPaused: false,
isStreamingImages: onAvailable != null,
recordingOrientation:
value.lockedCaptureOrientation ?? value.deviceOrientation);
recordingOrientation: Optional<DeviceOrientation>.of(
value.lockedCaptureOrientation ?? value.deviceOrientation),
isStreamingImages: onAvailable != null);
} on PlatformException catch (e) {
throw CameraException(e.code, e.message);
}
Expand All @@ -525,7 +532,10 @@ class CameraController extends ValueNotifier<CameraValue> {
try {
final XFile file =
await CameraPlatform.instance.stopVideoRecording(_cameraId);
value = value.copyWith(isRecordingVideo: false);
value = value.copyWith(
isRecordingVideo: false,
recordingOrientation: const Optional<DeviceOrientation>.absent(),
);
return file;
} on PlatformException catch (e) {
throw CameraException(e.code, e.message);
Expand Down Expand Up @@ -743,7 +753,8 @@ class CameraController extends ValueNotifier<CameraValue> {
await CameraPlatform.instance.lockCaptureOrientation(
_cameraId, orientation ?? value.deviceOrientation);
value = value.copyWith(
lockedCaptureOrientation: orientation ?? value.deviceOrientation);
lockedCaptureOrientation: Optional<DeviceOrientation>.of(
orientation ?? value.deviceOrientation));
} on PlatformException catch (e) {
throw CameraException(e.code, e.message);
}
Expand All @@ -763,7 +774,8 @@ class CameraController extends ValueNotifier<CameraValue> {
Future<void> unlockCaptureOrientation() async {
try {
await CameraPlatform.instance.unlockCaptureOrientation(_cameraId);
value = value.copyWith();
value = value.copyWith(
lockedCaptureOrientation: const Optional<DeviceOrientation>.absent());
} on PlatformException catch (e) {
throw CameraException(e.code, e.message);
}
Expand Down Expand Up @@ -834,3 +846,112 @@ class CameraController extends ValueNotifier<CameraValue> {
}
}
}

/// A value that might be absent.
///
/// Used to represent [DeviceOrientation]s that are optional but also able
/// to be cleared.
@immutable
class Optional<T> extends IterableBase<T> {
/// Constructs an empty Optional.
const Optional.absent() : _value = null;

/// Constructs an Optional of the given [value].
///
/// Throws [ArgumentError] if [value] is null.
Optional.of(T value) : _value = value {
// TODO(cbracken): Delete and make this ctor const once mixed-mode
// execution is no longer around.
ArgumentError.checkNotNull(value);
}

/// Constructs an Optional of the given [value].
///
/// If [value] is null, returns [absent()].
const Optional.fromNullable(T? value) : _value = value;

final T? _value;

/// True when this optional contains a value.
bool get isPresent => _value != null;

/// True when this optional contains no value.
bool get isNotPresent => _value == null;

/// Gets the Optional value.
///
/// Throws [StateError] if [value] is null.
T get value {
if (_value == null) {
throw StateError('value called on absent Optional.');
}
return _value!;
}

/// Executes a function if the Optional value is present.
void ifPresent(void Function(T value) ifPresent) {
if (isPresent) {
ifPresent(_value as T);
}
}

/// Execution a function if the Optional value is absent.
void ifAbsent(void Function() ifAbsent) {
if (!isPresent) {
ifAbsent();
}
}

/// Gets the Optional value with a default.
///
/// The default is returned if the Optional is [absent()].
///
/// Throws [ArgumentError] if [defaultValue] is null.
T or(T defaultValue) {
return _value ?? defaultValue;
}

/// Gets the Optional value, or `null` if there is none.
T? get orNull => _value;

/// Transforms the Optional value.
///
/// If the Optional is [absent()], returns [absent()] without applying the transformer.
///
/// The transformer must not return `null`. If it does, an [ArgumentError] is thrown.
Optional<S> transform<S>(S Function(T value) transformer) {
return _value == null
? Optional<S>.absent()
: Optional<S>.of(transformer(_value as T));
}

/// Transforms the Optional value.
///
/// If the Optional is [absent()], returns [absent()] without applying the transformer.
///
/// Returns [absent()] if the transformer returns `null`.
Optional<S> transformNullable<S>(S? Function(T value) transformer) {
return _value == null
? Optional<S>.absent()
: Optional<S>.fromNullable(transformer(_value as T));
}

@override
Iterator<T> get iterator =>
isPresent ? <T>[_value as T].iterator : Iterable<T>.empty().iterator;

/// Delegates to the underlying [value] hashCode.
@override
int get hashCode => _value.hashCode;

/// Delegates to the underlying [value] operator==.
@override
bool operator ==(Object o) => o is Optional<T> && o._value == _value;

@override
String toString() {
return _value == null
? 'Optional { absent }'
: 'Optional { value: $_value }';
}
}
2 changes: 1 addition & 1 deletion packages/camera/camera/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing
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.10.2+1
version: 0.10.3

environment:
sdk: ">=2.14.0 <3.0.0"
Expand Down
17 changes: 12 additions & 5 deletions packages/camera/camera/test/camera_preview_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,11 @@ void main() {
isInitialized: true,
isRecordingVideo: true,
deviceOrientation: DeviceOrientation.portraitUp,
lockedCaptureOrientation: DeviceOrientation.landscapeRight,
recordingOrientation: DeviceOrientation.landscapeLeft,
lockedCaptureOrientation:
const Optional<DeviceOrientation>.fromNullable(
DeviceOrientation.landscapeRight),
recordingOrientation: const Optional<DeviceOrientation>.fromNullable(
DeviceOrientation.landscapeLeft),
previewSize: const Size(480, 640),
);

Expand Down Expand Up @@ -164,8 +167,11 @@ void main() {
controller.value = controller.value.copyWith(
isInitialized: true,
deviceOrientation: DeviceOrientation.portraitUp,
lockedCaptureOrientation: DeviceOrientation.landscapeRight,
recordingOrientation: DeviceOrientation.landscapeLeft,
lockedCaptureOrientation:
const Optional<DeviceOrientation>.fromNullable(
DeviceOrientation.landscapeRight),
recordingOrientation: const Optional<DeviceOrientation>.fromNullable(
DeviceOrientation.landscapeLeft),
previewSize: const Size(480, 640),
);

Expand Down Expand Up @@ -195,7 +201,8 @@ void main() {
controller.value = controller.value.copyWith(
isInitialized: true,
deviceOrientation: DeviceOrientation.portraitUp,
recordingOrientation: DeviceOrientation.landscapeLeft,
recordingOrientation: const Optional<DeviceOrientation>.fromNullable(
DeviceOrientation.landscapeLeft),
previewSize: const Size(480, 640),
);

Expand Down
3 changes: 2 additions & 1 deletion packages/camera/camera/test/camera_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1166,7 +1166,8 @@ void main() {
cameraController.value = cameraController.value.copyWith(
isPreviewPaused: false,
deviceOrientation: DeviceOrientation.portraitUp,
lockedCaptureOrientation: DeviceOrientation.landscapeRight);
lockedCaptureOrientation:
Optional<DeviceOrientation>.of(DeviceOrientation.landscapeRight));

await cameraController.pausePreview();

Expand Down
3 changes: 2 additions & 1 deletion packages/camera/camera_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## NEXT
## 0.10.3

* Adds back use of Optional type.
* Updates minimum Flutter version to 3.0.

## 0.10.2+3
Expand Down

0 comments on commit ff84c44

Please sign in to comment.