@@ -10,17 +10,21 @@ import 'package:flutter/services.dart';
10
10
import 'package:flutter/widgets.dart' ;
11
11
import 'package:stream_transform/stream_transform.dart' ;
12
12
13
+ import 'src/messages.g.dart' ;
14
+
13
15
/// An implementation of [CameraPlatform] for Windows.
14
16
class CameraWindows extends CameraPlatform {
17
+ /// Creates a new Windows [CameraPlatform] implementation instance.
18
+ CameraWindows ({@visibleForTesting CameraApi ? api})
19
+ : _hostApi = api ?? CameraApi ();
20
+
15
21
/// Registers the Windows implementation of CameraPlatform.
16
22
static void registerWith () {
17
23
CameraPlatform .instance = CameraWindows ();
18
24
}
19
25
20
- /// The method channel used to interact with the native platform.
21
- @visibleForTesting
22
- final MethodChannel pluginChannel =
23
- const MethodChannel ('plugins.flutter.io/camera_windows' );
26
+ /// Interface for calling host-side code.
27
+ final CameraApi _hostApi;
24
28
25
29
/// Camera specific method channels to allow communicating with specific cameras.
26
30
final Map <int , MethodChannel > _cameraChannels = < int , MethodChannel > {};
@@ -43,19 +47,18 @@ class CameraWindows extends CameraPlatform {
43
47
@override
44
48
Future <List <CameraDescription >> availableCameras () async {
45
49
try {
46
- final List <Map <dynamic , dynamic >>? cameras = await pluginChannel
47
- .invokeListMethod <Map <dynamic , dynamic >>('availableCameras' );
50
+ final List <String ?> cameras = await _hostApi.getAvailableCameras ();
48
51
49
- if (cameras == null ) {
50
- return < CameraDescription > [];
51
- }
52
-
53
- return cameras.map ((Map <dynamic , dynamic > camera) {
52
+ return cameras.map ((String ? cameraName) {
54
53
return CameraDescription (
55
- name: camera['name' ] as String ,
56
- lensDirection:
57
- parseCameraLensDirection (camera['lensFacing' ] as String ),
58
- sensorOrientation: camera['sensorOrientation' ] as int ,
54
+ // This type is only nullable due to Pigeon limitations, see
55
+ // https://github.com/flutter/flutter/issues/97848. The native code
56
+ // will never return null.
57
+ name: cameraName! ,
58
+ // TODO(stuartmorgan): Implement these; see
59
+ // https://github.com/flutter/flutter/issues/97540.
60
+ lensDirection: CameraLensDirection .front,
61
+ sensorOrientation: 0 ,
59
62
);
60
63
}).toList ();
61
64
} on PlatformException catch (e) {
@@ -83,23 +86,8 @@ class CameraWindows extends CameraPlatform {
83
86
) async {
84
87
try {
85
88
// If resolutionPreset is not specified, plugin selects the highest resolution possible.
86
- final Map <String , dynamic >? reply = await pluginChannel
87
- .invokeMapMethod <String , dynamic >('create' , < String , dynamic > {
88
- 'cameraName' : cameraDescription.name,
89
- 'resolutionPreset' : null != mediaSettings? .resolutionPreset
90
- ? _serializeResolutionPreset (mediaSettings! .resolutionPreset)
91
- : null ,
92
- 'fps' : mediaSettings? .fps,
93
- 'videoBitrate' : mediaSettings? .videoBitrate,
94
- 'audioBitrate' : mediaSettings? .audioBitrate,
95
- 'enableAudio' : mediaSettings? .enableAudio ?? true ,
96
- });
97
-
98
- if (reply == null ) {
99
- throw CameraException ('System' , 'Cannot create camera' );
100
- }
101
-
102
- return reply['cameraId' ]! as int ;
89
+ return await _hostApi.create (
90
+ cameraDescription.name, _pigeonMediaSettings (mediaSettings));
103
91
} on PlatformException catch (e) {
104
92
throw CameraException (e.code, e.message);
105
93
}
@@ -110,35 +98,28 @@ class CameraWindows extends CameraPlatform {
110
98
int cameraId, {
111
99
ImageFormatGroup imageFormatGroup = ImageFormatGroup .unknown,
112
100
}) async {
113
- final int requestedCameraId = cameraId;
114
-
115
101
/// Creates channel for camera events.
116
- _cameraChannels.putIfAbsent (requestedCameraId , () {
117
- final MethodChannel channel = MethodChannel (
118
- 'plugins.flutter.io/camera_windows/camera$requestedCameraId ' );
102
+ _cameraChannels.putIfAbsent (cameraId , () {
103
+ final MethodChannel channel =
104
+ MethodChannel ( 'plugins.flutter.io/camera_windows/camera$cameraId ' );
119
105
channel.setMethodCallHandler (
120
- (MethodCall call) => handleCameraMethodCall (call, requestedCameraId ),
106
+ (MethodCall call) => handleCameraMethodCall (call, cameraId ),
121
107
);
122
108
return channel;
123
109
});
124
110
125
- final Map < String , double > ? reply;
111
+ final PlatformSize reply;
126
112
try {
127
- reply = await pluginChannel.invokeMapMethod <String , double >(
128
- 'initialize' ,
129
- < String , dynamic > {
130
- 'cameraId' : requestedCameraId,
131
- },
132
- );
113
+ reply = await _hostApi.initialize (cameraId);
133
114
} on PlatformException catch (e) {
134
115
throw CameraException (e.code, e.message);
135
116
}
136
117
137
118
cameraEventStreamController.add (
138
119
CameraInitializedEvent (
139
- requestedCameraId ,
140
- reply! [ 'previewWidth' ] ! ,
141
- reply[ 'previewHeight' ] ! ,
120
+ cameraId ,
121
+ reply.width ,
122
+ reply.height ,
142
123
ExposureMode .auto,
143
124
false ,
144
125
FocusMode .auto,
@@ -149,10 +130,7 @@ class CameraWindows extends CameraPlatform {
149
130
150
131
@override
151
132
Future <void > dispose (int cameraId) async {
152
- await pluginChannel.invokeMethod <void >(
153
- 'dispose' ,
154
- < String , dynamic > {'cameraId' : cameraId},
155
- );
133
+ await _hostApi.dispose (cameraId);
156
134
157
135
// Destroy method channel after camera is disposed to be able to handle last messages.
158
136
if (_cameraChannels.containsKey (cameraId)) {
@@ -217,18 +195,15 @@ class CameraWindows extends CameraPlatform {
217
195
218
196
@override
219
197
Future <XFile > takePicture (int cameraId) async {
220
- final String ? path;
221
- path = await pluginChannel.invokeMethod <String >(
222
- 'takePicture' ,
223
- < String , dynamic > {'cameraId' : cameraId},
224
- );
198
+ final String path = await _hostApi.takePicture (cameraId);
225
199
226
- return XFile (path! );
200
+ return XFile (path);
227
201
}
228
202
229
203
@override
230
- Future <void > prepareForVideoRecording () =>
231
- pluginChannel.invokeMethod <void >('prepareForVideoRecording' );
204
+ Future <void > prepareForVideoRecording () async {
205
+ // No-op.
206
+ }
232
207
233
208
@override
234
209
Future <void > startVideoRecording (int cameraId,
@@ -244,25 +219,15 @@ class CameraWindows extends CameraPlatform {
244
219
'Streaming is not currently supported on Windows' );
245
220
}
246
221
247
- await pluginChannel.invokeMethod <void >(
248
- 'startVideoRecording' ,
249
- < String , dynamic > {
250
- 'cameraId' : options.cameraId,
251
- 'maxVideoDuration' : options.maxDuration? .inMilliseconds,
252
- },
253
- );
222
+ await _hostApi.startVideoRecording (
223
+ options.cameraId, _pigeonVideoCaptureOptions (options));
254
224
}
255
225
256
226
@override
257
227
Future <XFile > stopVideoRecording (int cameraId) async {
258
- final String ? path;
259
-
260
- path = await pluginChannel.invokeMethod <String >(
261
- 'stopVideoRecording' ,
262
- < String , dynamic > {'cameraId' : cameraId},
263
- );
228
+ final String path = await _hostApi.stopVideoRecording (cameraId);
264
229
265
- return XFile (path! );
230
+ return XFile (path);
266
231
}
267
232
268
233
@override
@@ -362,45 +327,19 @@ class CameraWindows extends CameraPlatform {
362
327
363
328
@override
364
329
Future <void > pausePreview (int cameraId) async {
365
- await pluginChannel.invokeMethod <double >(
366
- 'pausePreview' ,
367
- < String , dynamic > {'cameraId' : cameraId},
368
- );
330
+ await _hostApi.pausePreview (cameraId);
369
331
}
370
332
371
333
@override
372
334
Future <void > resumePreview (int cameraId) async {
373
- await pluginChannel.invokeMethod <double >(
374
- 'resumePreview' ,
375
- < String , dynamic > {'cameraId' : cameraId},
376
- );
335
+ await _hostApi.resumePreview (cameraId);
377
336
}
378
337
379
338
@override
380
339
Widget buildPreview (int cameraId) {
381
340
return Texture (textureId: cameraId);
382
341
}
383
342
384
- /// Returns the resolution preset as a nullable String.
385
- String ? _serializeResolutionPreset (ResolutionPreset ? resolutionPreset) {
386
- switch (resolutionPreset) {
387
- case null :
388
- return null ;
389
- case ResolutionPreset .max:
390
- return 'max' ;
391
- case ResolutionPreset .ultraHigh:
392
- return 'ultraHigh' ;
393
- case ResolutionPreset .veryHigh:
394
- return 'veryHigh' ;
395
- case ResolutionPreset .high:
396
- return 'high' ;
397
- case ResolutionPreset .medium:
398
- return 'medium' ;
399
- case ResolutionPreset .low:
400
- return 'low' ;
401
- }
402
- }
403
-
404
343
/// Converts messages received from the native platform into camera events.
405
344
///
406
345
/// This is only exposed for test purposes. It shouldn't be used by clients
@@ -440,17 +379,52 @@ class CameraWindows extends CameraPlatform {
440
379
}
441
380
}
442
381
443
- /// Parses string presentation of the camera lens direction and returns enum value.
444
- @visibleForTesting
445
- CameraLensDirection parseCameraLensDirection (String string) {
446
- switch (string) {
447
- case 'front' :
448
- return CameraLensDirection .front;
449
- case 'back' :
450
- return CameraLensDirection .back;
451
- case 'external' :
452
- return CameraLensDirection .external ;
382
+ /// Returns a [MediaSettings] 's Pigeon representation.
383
+ PlatformMediaSettings _pigeonMediaSettings (MediaSettings ? settings) {
384
+ return PlatformMediaSettings (
385
+ resolutionPreset: _pigeonResolutionPreset (settings? .resolutionPreset),
386
+ enableAudio: settings? .enableAudio ?? true ,
387
+ framesPerSecond: settings? .fps,
388
+ videoBitrate: settings? .videoBitrate,
389
+ audioBitrate: settings? .audioBitrate,
390
+ );
391
+ }
392
+
393
+ /// Returns a [ResolutionPreset] 's Pigeon representation.
394
+ PlatformResolutionPreset _pigeonResolutionPreset (
395
+ ResolutionPreset ? resolutionPreset) {
396
+ if (resolutionPreset == null ) {
397
+ // Provide a default if one isn't provided, since the native side needs
398
+ // to set something.
399
+ return PlatformResolutionPreset .max;
400
+ }
401
+ switch (resolutionPreset) {
402
+ case ResolutionPreset .max:
403
+ return PlatformResolutionPreset .max;
404
+ case ResolutionPreset .ultraHigh:
405
+ return PlatformResolutionPreset .ultraHigh;
406
+ case ResolutionPreset .veryHigh:
407
+ return PlatformResolutionPreset .veryHigh;
408
+ case ResolutionPreset .high:
409
+ return PlatformResolutionPreset .high;
410
+ case ResolutionPreset .medium:
411
+ return PlatformResolutionPreset .medium;
412
+ case ResolutionPreset .low:
413
+ return PlatformResolutionPreset .low;
453
414
}
454
- throw ArgumentError ('Unknown CameraLensDirection value' );
415
+ // The enum comes from a different package, which could get a new value at
416
+ // any time, so provide a fallback that ensures this won't break when used
417
+ // with a version that contains new values. This is deliberately outside
418
+ // the switch rather than a `default` so that the linter will flag the
419
+ // switch as needing an update.
420
+ // ignore: dead_code
421
+ return PlatformResolutionPreset .max;
422
+ }
423
+
424
+ /// Returns a [VideoCamptureOptions] 's Pigeon representation.
425
+ PlatformVideoCaptureOptions _pigeonVideoCaptureOptions (
426
+ VideoCaptureOptions options) {
427
+ return PlatformVideoCaptureOptions (
428
+ maxDurationMilliseconds: options.maxDuration? .inMilliseconds);
455
429
}
456
430
}
0 commit comments