Skip to content

Commit

Permalink
🥅 Wrap controller methods
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexV525 committed Sep 25, 2023
1 parent f9f5a89 commit 20efd55
Showing 1 changed file with 137 additions and 40 deletions.
177 changes: 137 additions & 40 deletions lib/src/states/camera_picker_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,13 @@ class CameraPickerState extends State<CameraPicker>

CameraPickerTextDelegate get textDelegate => Constants.textDelegate;

/// If controller methods were failed to called for camera descriptions,
/// it will be recorded as invalid and never gets called again.
///
/// 如果相机实例的某个方法调用失败,该方法会被记录并且不会再被调用。
final invalidControllerMethods = <CameraDescription, Set<String>>{};
bool retriedAfterInvalidInitialize = false;

@override
void initState() {
super.initState();
Expand Down Expand Up @@ -265,6 +272,31 @@ class CameraPickerState extends State<CameraPicker>
return scale;
}

/// Wraps [CameraController] methods with invalid controls.
/// Returns the [fallback] value if invalid and [T] is non-void.
///
/// 对于 [CameraController] 的方法增加是否无效的控制。
/// 如果 [T] 是非 void 且方法无效,返回 [fallback]
Future<T> wrapControllerMethod<T>(
String key,
Future<T> Function() method, {
CameraDescription? description,
VoidCallback? onError,
T? fallback,
}) async {
description ??= currentCamera;
if (invalidControllerMethods[description]!.contains(key)) {
return fallback!;
}
try {
return await method();
} catch (e) {
invalidControllerMethods[description]!.add(key);
onError?.call();
rethrow;
}
}

/// Initialize cameras instances.
/// 初始化相机实例
Future<void> initCameras([CameraDescription? cameraDescription]) async {
Expand Down Expand Up @@ -325,8 +357,10 @@ class CameraPickerState extends State<CameraPicker>
index = currentCameraIndex;
}
// Initialize the controller with the given resolution preset.
final description = cameraDescription ?? cameras[index];
invalidControllerMethods[description] ??= <String>{};
final CameraController newController = CameraController(
cameraDescription ?? cameras[index],
description,
pickerConfig.resolutionPreset,
enableAudio: enableAudio,
imageFormatGroup: pickerConfig.imageFormatGroup,
Expand All @@ -352,44 +386,79 @@ class CameraPickerState extends State<CameraPicker>
..start();
await Future.wait(
<Future<void>>[
newController
.getExposureOffsetStepSize()
.then((double value) => exposureStep = value)
.catchError((_) => exposureStep),
newController
.getMaxExposureOffset()
.then((double value) => maxAvailableExposureOffset = value)
.catchError((_) => maxAvailableExposureOffset),
newController
.getMinExposureOffset()
.then((double value) => minAvailableExposureOffset = value)
.catchError((_) => minAvailableExposureOffset),
newController
.getMaxZoomLevel()
.then((double value) => maxAvailableZoom = value)
.catchError((_) => maxAvailableZoom),
newController
.getMinZoomLevel()
.then((double value) => minAvailableZoom = value)
.catchError((_) => minAvailableZoom),
wrapControllerMethod(
'getExposureOffsetStepSize',
() => newController.getExposureOffsetStepSize(),
description: description,
fallback: exposureStep,
).then((value) => exposureStep = value),
wrapControllerMethod(
'getMaxExposureOffset',
() => newController.getMaxExposureOffset(),
description: description,
fallback: maxAvailableExposureOffset,
).then((value) => maxAvailableExposureOffset = value),
wrapControllerMethod(
'getMinExposureOffset',
() => newController.getMinExposureOffset(),
description: description,
fallback: minAvailableExposureOffset,
).then((value) => minAvailableExposureOffset = value),
wrapControllerMethod(
'getMaxZoomLevel',
() => newController.getMaxZoomLevel(),
description: description,
fallback: maxAvailableZoom,
).then((value) => maxAvailableZoom = value),
wrapControllerMethod(
'getMinZoomLevel',
() => newController.getMinZoomLevel(),
description: description,
fallback: minAvailableZoom,
).then((value) => minAvailableZoom = value),
wrapControllerMethod(
'getMinZoomLevel',
() => newController.getMinZoomLevel(),
description: description,
fallback: minAvailableZoom,
).then((value) => minAvailableZoom = value),
if (pickerConfig.lockCaptureOrientation != null)
newController
.lockCaptureOrientation(pickerConfig.lockCaptureOrientation)
.catchError((_) {}),
if (pickerConfig.preferredFlashMode != FlashMode.auto)
newController
.setFlashMode(pickerConfig.preferredFlashMode)
.catchError((_) {
validFlashModes[currentCamera]
?.remove(pickerConfig.preferredFlashMode);
}),
wrapControllerMethod<void>(
'lockCaptureOrientation',
() => newController.lockCaptureOrientation(
pickerConfig.lockCaptureOrientation,
),
description: description,
),
// Do not set flash modes for the front camera.
if (description.lensDirection != CameraLensDirection.front &&
pickerConfig.preferredFlashMode != FlashMode.auto)
wrapControllerMethod<void>(
'setFlashMode',
() => newController.setFlashMode(
pickerConfig.preferredFlashMode,
),
description: description,
onError: () {
validFlashModes[description]?.remove(
pickerConfig.preferredFlashMode,
);
},
),
],
eagerError: false,
);
stopwatch.stop();
realDebugPrint("${stopwatch.elapsed} for config's update.");
innerController = newController;
} catch (e, s) {
handleErrorWithHandler(e, pickerConfig.onError, s: s);
if (!retriedAfterInvalidInitialize) {
retriedAfterInvalidInitialize = true;
Future.delayed(Duration.zero, initCameras);
} else {
retriedAfterInvalidInitialize = false;
}
} finally {
safeSetState(() {});
}
Expand Down Expand Up @@ -582,9 +651,16 @@ class CameraPickerState extends State<CameraPicker>
isFocusPointFadeOut.value = false;
try {
await Future.wait(<Future<void>>[
wrapControllerMethod<void>(
'setExposureOffset',
() => controller.setExposureOffset(0),
),
controller.setExposureOffset(0),
if (controller.value.exposureMode == ExposureMode.locked)
controller.setExposureMode(ExposureMode.auto),
wrapControllerMethod<void>(
'setExposureMode',
() => controller.setExposureMode(ExposureMode.auto),
),
]);
final Offset newPoint = lastExposurePoint.value!.scale(
1 / constraints.maxWidth,
Expand All @@ -604,6 +680,7 @@ class CameraPickerState extends State<CameraPicker>
/// Update the exposure offset using the exposure controller.
/// 使用曝光控制器更新曝光值
Future<void> updateExposureOffset(double value) async {
final previousSliderOffsetValue = currentExposureSliderOffset.value;
currentExposureSliderOffset.value = value;
// Normalize the new exposure value if exposures have steps.
if (exposureStep > 0) {
Expand All @@ -621,18 +698,26 @@ class CameraPickerState extends State<CameraPicker>
value > maxAvailableExposureOffset) {
return;
}
final previousOffsetValue = currentExposureOffset.value;
currentExposureOffset.value = value;
bool hasError = false;
try {
realDebugPrint('Updating the exposure offset value: $value');
// Use [CameraPlatform] explicitly to reduce channel calls.
await CameraPlatform.instance.setExposureOffset(
controller.cameraId,
value,
await wrapControllerMethod(
'setExposureOffset',
() => CameraPlatform.instance.setExposureOffset(
controller.cameraId,
value,
),
);
} catch (e, s) {
handleErrorWithHandler(e, pickerConfig.onError, s: s);
hasError = true;
currentExposureSliderOffset.value = previousSliderOffsetValue;
currentExposureOffset.value = previousOffsetValue;
}
if (!isFocusPointDisplays.value) {
if (!hasError && !isFocusPointDisplays.value) {
isFocusPointDisplays.value = true;
}
restartExposurePointDisplayTimer();
Expand Down Expand Up @@ -693,11 +778,17 @@ class CameraPickerState extends State<CameraPicker>
final ExposureMode previousExposureMode = controller.value.exposureMode;
try {
await Future.wait(<Future<void>>[
controller.setFocusMode(FocusMode.locked).catchError((e, s) {
wrapControllerMethod<void>(
'setFocusMode',
() => controller.setFocusMode(FocusMode.locked),
).catchError((e, s) {
handleErrorWithHandler(e, pickerConfig.onError, s: s);
}),
if (previousExposureMode != ExposureMode.locked)
controller.setExposureMode(ExposureMode.locked).catchError((e, s) {
wrapControllerMethod<void>(
'setExposureMode',
() => controller.setExposureMode(ExposureMode.locked),
).catchError((e, s) {
handleErrorWithHandler(e, pickerConfig.onError, s: s);
}),
]);
Expand All @@ -719,9 +810,15 @@ class CameraPickerState extends State<CameraPicker>
return;
}
await Future.wait(<Future<void>>[
controller.setFocusMode(FocusMode.auto),
wrapControllerMethod<void>(
'setFocusMode',
() => controller.setFocusMode(FocusMode.auto),
),
if (previousExposureMode != ExposureMode.locked)
controller.setExposureMode(previousExposureMode),
wrapControllerMethod<void>(
'setExposureMode',
() => controller.setExposureMode(previousExposureMode),
),
]);
await controller.resumePreview();
} catch (e, s) {
Expand Down

0 comments on commit 20efd55

Please sign in to comment.