diff --git a/packages/camera/camera_platform_interface/CHANGELOG.md b/packages/camera/camera_platform_interface/CHANGELOG.md index 6126e519c516..630da2489109 100644 --- a/packages/camera/camera_platform_interface/CHANGELOG.md +++ b/packages/camera/camera_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.4.0 + +- Added interface methods to support auto focus. + ## 1.3.0 - Introduces an option to set the image format when initializing. diff --git a/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart b/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart index 797999f9441d..9bd4d58781cc 100644 --- a/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart +++ b/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart @@ -2,6 +2,8 @@ // 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/src/types/focus_mode.dart'; + import '../../camera_platform_interface.dart'; /// Generic Event coming from the native side of Camera. @@ -50,11 +52,19 @@ class CameraInitializedEvent extends CameraEvent { /// The default exposure mode final ExposureMode exposureMode; + + /// The default focus mode + final FocusMode focusMode; + + final IsoMode isoMode; final WbMode wbMode; /// Whether setting exposure points is supported. final bool exposurePointSupported; + /// Whether setting focus points is supported. + final bool focusPointSupported; + /// Build a CameraInitialized event triggered from the camera represented by /// `cameraId`. /// @@ -63,8 +73,9 @@ class CameraInitializedEvent extends CameraEvent { CameraInitializedEvent( int cameraId, this.previewWidth, - this.previewHeight, + this.previewHeight, [ this.exposureMode, + this.focusMode, this.exposurePointSupported, this.isoMode, this.wbMode, ) : super(cameraId); @@ -74,9 +85,11 @@ class CameraInitializedEvent extends CameraEvent { : previewWidth = json['previewWidth'], previewHeight = json['previewHeight'], exposureMode = deserializeExposureMode(json['exposureMode']), + exposurePointSupported = json['exposurePointSupported'] ?? false, + focusMode = deserializeFocusMode(json['focusMode']), + focusPointSupported = json['focusPointSupported'] ?? false, isoMode = deserializeIsoMode(json['isoMode']), wbMode = deserializeWbMode(json['wbMode']), - exposurePointSupported = json['exposurePointSupported'], super(json['cameraId']); /// Converts the [CameraInitializedEvent] instance into a [Map] instance that @@ -89,6 +102,8 @@ class CameraInitializedEvent extends CameraEvent { 'isoMode' : serializeIsoMode(isoMode), 'wbMode' : serializeWbMode(wbMode), 'exposurePointSupported': exposurePointSupported, + 'focusMode': serializeFocusMode(focusMode), + 'focusPointSupported': focusPointSupported, }; @override @@ -100,6 +115,8 @@ class CameraInitializedEvent extends CameraEvent { previewWidth == other.previewWidth && previewHeight == other.previewHeight && exposureMode == other.exposureMode && + focusMode == other.focusMode && + focusPointSupported == other.focusPointSupported; isoMode == other.isoMode && wbMode == other.wbMode && exposurePointSupported == other.exposurePointSupported; @@ -110,6 +127,8 @@ class CameraInitializedEvent extends CameraEvent { previewWidth.hashCode ^ previewHeight.hashCode ^ exposureMode.hashCode ^ + focusMode.hashCode ^ + focusPointSupported.hashCode; isoMode.hashCode ^ wbMode.hashCode ^ exposurePointSupported.hashCode; diff --git a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart index fae5b0c4ee3d..6807c07a12cd 100644 --- a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart +++ b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:math'; import 'package:camera_platform_interface/camera_platform_interface.dart'; +import 'package:camera_platform_interface/src/types/focus_mode.dart'; import 'package:camera_platform_interface/src/types/image_format_group.dart'; import 'package:camera_platform_interface/src/utils/utils.dart'; import 'package:cross_file/cross_file.dart'; @@ -259,6 +260,31 @@ class MethodChannelCamera extends CameraPlatform { }, ); + @override + Future setFocusMode(int cameraId, FocusMode mode) => + _channel.invokeMethod( + 'setFocusMode', + { + 'cameraId': cameraId, + 'mode': serializeFocusMode(mode), + }, + ); + + @override + Future setFocusPoint(int cameraId, Point point) { + assert(point == null || point.x >= 0 && point.x <= 1); + assert(point == null || point.y >= 0 && point.y <= 1); + return _channel.invokeMethod( + 'setFocusPoint', + { + 'cameraId': cameraId, + 'reset': point == null, + 'x': point?.x, + 'y': point?.y, + }, + ); + } + @override Future setIsoMode(int cameraId, IsoMode mode) => _channel.invokeMethod( @@ -378,6 +404,8 @@ class MethodChannelCamera extends CameraPlatform { call.arguments['previewHeight'], deserializeExposureMode(call.arguments['exposureMode']), call.arguments['exposurePointSupported'], + deserializeFocusMode(call.arguments['focusMode']), + call.arguments['focusPointSupported'], deserializeIsoMode(call.arguments['isoMode']), deserializeWbMode(call.arguments['wbMode']), )); diff --git a/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart b/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart index a2c3fef44978..3215cbb2bd07 100644 --- a/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart +++ b/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart @@ -8,6 +8,7 @@ import 'dart:math'; import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:camera_platform_interface/src/method_channel/method_channel_camera.dart'; import 'package:camera_platform_interface/src/types/exposure_mode.dart'; +import 'package:camera_platform_interface/src/types/focus_mode.dart'; import 'package:camera_platform_interface/src/types/image_format_group.dart'; import 'package:cross_file/cross_file.dart'; import 'package:flutter/widgets.dart'; @@ -133,7 +134,7 @@ abstract class CameraPlatform extends PlatformInterface { throw UnimplementedError('setExposureMode() is not implemented.'); } - /// Sets the exposure point for automatically determining the exposure value. + /// Sets the exposure point for automatically determining the exposure values. Future setExposurePoint(int cameraId, Point point) { throw UnimplementedError('setExposurePoint() is not implemented.'); } @@ -170,6 +171,16 @@ abstract class CameraPlatform extends PlatformInterface { throw UnimplementedError('setExposureOffset() is not implemented.'); } + /// Sets the focus mode for taking pictures. + Future setFocusMode(int cameraId, FocusMode mode) { + throw UnimplementedError('setFocusMode() is not implemented.'); + } + + /// Sets the focus point for automatically determining the focus values. + Future setFocusPoint(int cameraId, Point point) { + throw UnimplementedError('setFocusPoint() is not implemented.'); + } + Future setIsoMode(int cameraId, IsoMode mode) { throw UnimplementedError('setIsoMode() is not implemented.'); } diff --git a/packages/camera/camera_platform_interface/lib/src/types/exposure_mode.dart b/packages/camera/camera_platform_interface/lib/src/types/exposure_mode.dart index 836f53826479..7fbfbaf5f4a9 100644 --- a/packages/camera/camera_platform_interface/lib/src/types/exposure_mode.dart +++ b/packages/camera/camera_platform_interface/lib/src/types/exposure_mode.dart @@ -13,6 +13,7 @@ enum ExposureMode { /// Returns the exposure mode as a String. String serializeExposureMode(ExposureMode exposureMode) { + if (exposureMode == null) return null; switch (exposureMode) { case ExposureMode.locked: return 'locked'; @@ -25,6 +26,7 @@ String serializeExposureMode(ExposureMode exposureMode) { /// Returns the exposure mode for a given String. ExposureMode deserializeExposureMode(String str) { + if (str == null) return null; switch (str) { case "locked": return ExposureMode.locked; diff --git a/packages/camera/camera_platform_interface/lib/src/types/focus_mode.dart b/packages/camera/camera_platform_interface/lib/src/types/focus_mode.dart new file mode 100644 index 000000000000..ad5e9a2d46f1 --- /dev/null +++ b/packages/camera/camera_platform_interface/lib/src/types/focus_mode.dart @@ -0,0 +1,38 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/// The possible focus modes that can be set for a camera. +enum FocusMode { + /// Automatically determine focus settings. + auto, + + /// Lock the currently determined focus settings. + locked, +} + +/// Returns the focus mode as a String. +String serializeFocusMode(FocusMode focusMode) { + if (focusMode == null) return null; + switch (focusMode) { + case FocusMode.locked: + return 'locked'; + case FocusMode.auto: + return 'auto'; + default: + throw ArgumentError('Unknown FocusMode value'); + } +} + +/// Returns the focus mode for a given String. +FocusMode deserializeFocusMode(String str) { + if (str == null) return null; + switch (str) { + case "locked": + return FocusMode.locked; + case "auto": + return FocusMode.auto; + default: + throw ArgumentError('"$str" is not a valid FocusMode value'); + } +} diff --git a/packages/camera/camera_platform_interface/lib/src/types/types.dart b/packages/camera/camera_platform_interface/lib/src/types/types.dart index c43dea40c3a8..025e7bec81d0 100644 --- a/packages/camera/camera_platform_interface/lib/src/types/types.dart +++ b/packages/camera/camera_platform_interface/lib/src/types/types.dart @@ -10,3 +10,4 @@ export 'image_format_group.dart'; export 'wb_mode.dart'; export 'iso_mode.dart'; export 'exposure_mode.dart'; +export 'focus_mode.dart'; diff --git a/packages/camera/camera_platform_interface/pubspec.yaml b/packages/camera/camera_platform_interface/pubspec.yaml index 40f164debfdf..436bd4a18e95 100644 --- a/packages/camera/camera_platform_interface/pubspec.yaml +++ b/packages/camera/camera_platform_interface/pubspec.yaml @@ -3,7 +3,7 @@ description: A common platform interface for the camera plugin. homepage: https://github.com/flutter/plugins/tree/master/packages/camera/camera_platform_interface # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.3.0 +version: 1.4.0 dependencies: diff --git a/packages/camera/camera_platform_interface/test/camera_platform_interface_test.dart b/packages/camera/camera_platform_interface/test/camera_platform_interface_test.dart index 574fa45e7b81..80316317e698 100644 --- a/packages/camera/camera_platform_interface/test/camera_platform_interface_test.dart +++ b/packages/camera/camera_platform_interface/test/camera_platform_interface_test.dart @@ -264,6 +264,32 @@ void main() { ); }); + test( + 'Default implementation of setFocusMode() should throw unimplemented error', + () { + // Arrange + final cameraPlatform = ExtendsCameraPlatform(); + + // Act & Assert + expect( + () => cameraPlatform.setFocusMode(1, null), + throwsUnimplementedError, + ); + }); + + test( + 'Default implementation of setFocusPoint() should throw unimplemented error', + () { + // Arrange + final cameraPlatform = ExtendsCameraPlatform(); + + // Act & Assert + expect( + () => cameraPlatform.setFocusPoint(1, null), + throwsUnimplementedError, + ); + }); + test( 'Default implementation of startVideoRecording() should throw unimplemented error', () { diff --git a/packages/camera/camera_platform_interface/test/events/camera_event_test.dart b/packages/camera/camera_platform_interface/test/events/camera_event_test.dart index 50a6ded2c84f..ca96355ce3c8 100644 --- a/packages/camera/camera_platform_interface/test/events/camera_event_test.dart +++ b/packages/camera/camera_platform_interface/test/events/camera_event_test.dart @@ -4,6 +4,7 @@ import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:camera_platform_interface/src/types/exposure_mode.dart'; +import 'package:camera_platform_interface/src/types/focus_mode.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { @@ -12,13 +13,15 @@ void main() { group('CameraInitializedEvent tests', () { test('Constructor should initialize all properties', () { final event = CameraInitializedEvent( - 1, 1024, 640, ExposureMode.auto, true, IsoMode.auto, WbMode.auto); + 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true,IsoMode.auto, WbMode.auto); expect(event.cameraId, 1); expect(event.previewWidth, 1024); expect(event.previewHeight, 640); expect(event.exposureMode, ExposureMode.auto); + expect(event.focusMode, FocusMode.auto); expect(event.exposurePointSupported, true); + expect(event.focusPointSupported, true); }); test('fromJson should initialize all properties', () { @@ -26,36 +29,45 @@ void main() { 'cameraId': 1, 'previewWidth': 1024.0, 'previewHeight': 640.0, - 'exposureMode': 'auto' + 'exposureMode': 'auto', + 'exposurePointSupported': true, + 'focusMode': 'auto', + 'focusPointSupported': true }); expect(event.cameraId, 1); expect(event.previewWidth, 1024); expect(event.previewHeight, 640); expect(event.exposureMode, ExposureMode.auto); + expect(event.exposurePointSupported, true); + expect(event.focusMode, FocusMode.auto); + expect(event.focusPointSupported, true); }); test('toJson should return a map with all fields', () { final event = CameraInitializedEvent( - 1, 1024, 640, ExposureMode.auto, true, IsoMode.auto, WbMode.auto); + 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true,IsoMode.auto, WbMode.auto); final jsonMap = event.toJson(); - expect(jsonMap.length, 5); + expect(jsonMap.length, 7); expect(jsonMap['cameraId'], 1); expect(jsonMap['previewWidth'], 1024); expect(jsonMap['previewHeight'], 640); expect(jsonMap['exposureMode'], 'auto'); expect(jsonMap['exposurePointSupported'], true); + expect(jsonMap['focusMode'], 'auto'); + expect(jsonMap['focusPointSupported'], true); }); test('equals should return true if objects are the same', () { + final firstEvent = - CameraInitializedEvent(1, 1024, 640, ExposureMode.auto, true, IsoMode.auto, WbMode.auto); + CameraInitializedEvent(1, 1024, 640, ExposureMode.auto, true,FocusMode.auto, true, IsoMode.auto, WbMode.auto); final secondEvent = - CameraInitializedEvent(1, 1024, 640, ExposureMode.auto, true, IsoMode.auto, WbMode.auto); + CameraInitializedEvent(1, 1024, 640, ExposureMode.auto, true,FocusMode.auto, true, IsoMode.auto, WbMode.auto); expect(firstEvent == secondEvent, true); }); @@ -63,7 +75,7 @@ void main() { test('equals should return false if cameraId is different', () { final firstEvent = - CameraInitializedEvent(1, 1024, 640, ExposureMode.auto, true, IsoMode.auto, WbMode.auto); + CameraInitializedEvent(1, 1024, 640, ExposureMode.auto, true,FocusMode.auto, true, IsoMode.auto, WbMode.auto); final secondEvent = CameraInitializedEvent(2, 1024, 640, ExposureMode.auto, true, IsoMode.auto, WbMode.auto); @@ -72,41 +84,70 @@ void main() { test('equals should return false if previewWidth is different', () { + final firstEvent = - CameraInitializedEvent(1, 1024, 640, ExposureMode.auto, true, IsoMode.auto, WbMode.auto); + CameraInitializedEvent(1, 1024, 640, ExposureMode.auto, true,FocusMode.auto, true, IsoMode.auto, WbMode.auto); final secondEvent = - CameraInitializedEvent(1, 2048, 640, ExposureMode.auto, true, IsoMode.auto, WbMode.auto); + CameraInitializedEvent(1, 2048, 640, ExposureMode.auto, true,FocusMode.auto, true, IsoMode.auto, WbMode.auto); expect(firstEvent == secondEvent, false); }); test('equals should return false if previewHeight is different', () { + final firstEvent = - CameraInitializedEvent(1, 1024, 640, ExposureMode.auto, true, IsoMode.auto, WbMode.auto); + CameraInitializedEvent(1, 1024, 640, ExposureMode.auto, true,FocusMode.auto, true, IsoMode.auto, WbMode.auto); final secondEvent = - CameraInitializedEvent(1, 1024, 980, ExposureMode.auto, true, IsoMode.auto, WbMode.auto); + CameraInitializedEvent(1, 1024, 980, ExposureMode.auto, true,FocusMode.auto, true, IsoMode.auto, WbMode.auto); expect(firstEvent == secondEvent, false); }); test('equals should return false if exposureMode is different', () { + final firstEvent = CameraInitializedEvent( + 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); + final secondEvent = CameraInitializedEvent( + 1, 1024, 640, ExposureMode.locked, true, FocusMode.auto, true); + final firstEvent = + CameraInitializedEvent(1, 1024, 640, ExposureMode.auto, true); + final secondEvent = + CameraInitializedEvent(1, 1024, 640, ExposureMode.locked, true); test('equals should return false if exposureMode is different', () { final firstEvent = - CameraInitializedEvent(1, 1024, 640, ExposureMode.auto, true, IsoMode.auto, WbMode.auto); + CameraInitializedEvent(1, 1024, 640, ExposureMode.auto, true,FocusMode.auto, true, IsoMode.auto, WbMode.auto); final secondEvent = - CameraInitializedEvent(1, 1024, 640, ExposureMode.locked, true, IsoMode.auto, WbMode.auto); + CameraInitializedEvent(1, 1024, 640, ExposureMode.locked, true,FocusMode.auto, true, IsoMode.auto, WbMode.auto); expect(firstEvent == secondEvent, false); }); test('equals should return false if exposurePointSupported is different', () { + final firstEvent = - CameraInitializedEvent(1, 1024, 640, ExposureMode.auto, true, IsoMode.auto, WbMode.auto); + CameraInitializedEvent(1, 1024, 640, ExposureMode.auto, true,FocusMode.auto, true, IsoMode.auto, WbMode.auto); final secondEvent = - CameraInitializedEvent(1, 1024, 640, ExposureMode.auto, false, IsoMode.auto, WbMode.auto); + CameraInitializedEvent(1, 1024, 640, ExposureMode.auto, false,FocusMode.auto, true, IsoMode.auto, WbMode.auto); + + expect(firstEvent == secondEvent, false); + }); + + test('equals should return false if focusMode is different', () { + final firstEvent = CameraInitializedEvent( + 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); + final secondEvent = CameraInitializedEvent( + 1, 1024, 640, ExposureMode.auto, true, FocusMode.locked, true); + + expect(firstEvent == secondEvent, false); + }); + + test('equals should return false if focusPointSupported is different', () { + final firstEvent = CameraInitializedEvent( + 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); + final secondEvent = CameraInitializedEvent( + 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, false); expect(firstEvent == secondEvent, false); }); @@ -114,12 +155,14 @@ void main() { test('hashCode should match hashCode of all properties', () { final event = - CameraInitializedEvent(1, 1024, 640, ExposureMode.auto, true, IsoMode.auto, WbMode.auto); + CameraInitializedEvent(1, 1024, 640, ExposureMode.auto, true,FocusMode.auto, true, IsoMode.auto, WbMode.auto); final expectedHashCode = event.cameraId ^ event.previewWidth.hashCode ^ event.previewHeight.hashCode ^ event.exposureMode.hashCode ^ - event.exposurePointSupported.hashCode; + event.exposurePointSupported.hashCode ^ + event.focusMode.hashCode ^ + event.focusPointSupported.hashCode; expect(event.hashCode, expectedHashCode); }); diff --git a/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart b/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart index bfcffb959efc..e0fb3254bb8f 100644 --- a/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart +++ b/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart @@ -8,6 +8,7 @@ import 'dart:math'; import 'package:async/async.dart'; import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:camera_platform_interface/src/method_channel/method_channel_camera.dart'; +import 'package:camera_platform_interface/src/types/focus_mode.dart'; import 'package:camera_platform_interface/src/utils/utils.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; @@ -131,6 +132,8 @@ void main() { 1080, ExposureMode.auto, true, + FocusMode.auto, + true, IsoMode.auto, WbMode.auto )); @@ -172,6 +175,8 @@ void main() { 1080, ExposureMode.auto, true, + FocusMode.auto, + true, IsoMode.auto, WbMode.auto )); @@ -216,6 +221,8 @@ void main() { 1080, ExposureMode.auto, true, + FocusMode.auto, + true, IsoMode.auto, WbMode.auto )); @@ -235,6 +242,8 @@ void main() { 2160, ExposureMode.auto, true, + FocusMode.auto, + true, IsoMode.auto, WbMode.auto ); @@ -348,6 +357,8 @@ void main() { 1080, ExposureMode.auto, true, + FocusMode.auto, + true, IsoMode.auto, WbMode.auto ), @@ -689,6 +700,54 @@ void main() { ]); }); + test('Should set the focus mode', () async { + // Arrange + MethodChannelMock channel = MethodChannelMock( + channelName: 'plugins.flutter.io/camera', + methods: {'setFocusMode': null}, + ); + + // Act + await camera.setFocusMode(cameraId, FocusMode.auto); + await camera.setFocusMode(cameraId, FocusMode.locked); + + // Assert + expect(channel.log, [ + isMethodCall('setFocusMode', + arguments: {'cameraId': cameraId, 'mode': 'auto'}), + isMethodCall('setFocusMode', + arguments: {'cameraId': cameraId, 'mode': 'locked'}), + ]); + }); + + test('Should set the exposure point', () async { + // Arrange + MethodChannelMock channel = MethodChannelMock( + channelName: 'plugins.flutter.io/camera', + methods: {'setFocusPoint': null}, + ); + + // Act + await camera.setFocusPoint(cameraId, Point(0.5, 0.5)); + await camera.setFocusPoint(cameraId, null); + + // Assert + expect(channel.log, [ + isMethodCall('setFocusPoint', arguments: { + 'cameraId': cameraId, + 'x': 0.5, + 'y': 0.5, + 'reset': false + }), + isMethodCall('setFocusPoint', arguments: { + 'cameraId': cameraId, + 'x': null, + 'y': null, + 'reset': true + }), + ]); + }); + test('Should build a texture widget as preview widget', () async { // Act Widget widget = camera.buildPreview(cameraId); diff --git a/packages/camera/camera_platform_interface/test/types/focus_mode_test.dart b/packages/camera/camera_platform_interface/test/types/focus_mode_test.dart new file mode 100644 index 000000000000..ca7ad902820a --- /dev/null +++ b/packages/camera/camera_platform_interface/test/types/focus_mode_test.dart @@ -0,0 +1,31 @@ +// Copyright 2019 The Chromium 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/src/types/focus_mode.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test('FocusMode should contain 2 options', () { + final values = FocusMode.values; + + expect(values.length, 2); + }); + + test("FocusMode enum should have items in correct index", () { + final values = FocusMode.values; + + expect(values[0], FocusMode.auto); + expect(values[1], FocusMode.locked); + }); + + test("serializeFocusMode() should serialize correctly", () { + expect(serializeFocusMode(FocusMode.auto), "auto"); + expect(serializeFocusMode(FocusMode.locked), "locked"); + }); + + test("deserializeFocusMode() should deserialize correctly", () { + expect(deserializeFocusMode('auto'), FocusMode.auto); + expect(deserializeFocusMode('locked'), FocusMode.locked); + }); +}