From 3ff6c978fc5df8cd343eb988e895277785787e16 Mon Sep 17 00:00:00 2001 From: Koji Wakamiya Date: Thu, 14 Mar 2024 20:50:32 +0900 Subject: [PATCH] feat(sensors_plus)!: Migrate to dart:js_interop --- .../sensors_plus/example/pubspec.yaml | 3 +- .../sensors_plus/lib/src/web_sensors.dart | 222 +++++++++--------- .../lib/src/web_sensors_interop.dart | 96 ++++++++ .../sensors_plus/sensors_plus/pubspec.yaml | 4 +- 4 files changed, 214 insertions(+), 111 deletions(-) create mode 100644 packages/sensors_plus/sensors_plus/lib/src/web_sensors_interop.dart diff --git a/packages/sensors_plus/sensors_plus/example/pubspec.yaml b/packages/sensors_plus/sensors_plus/example/pubspec.yaml index 42d89fbd02..fbfaa6c04b 100644 --- a/packages/sensors_plus/sensors_plus/example/pubspec.yaml +++ b/packages/sensors_plus/sensors_plus/example/pubspec.yaml @@ -19,4 +19,5 @@ flutter: uses-material-design: true environment: - sdk: '>=2.18.0 <4.0.0' + sdk: '>=3.3.0 <4.0.0' + flutter: '>=3.19.0' diff --git a/packages/sensors_plus/sensors_plus/lib/src/web_sensors.dart b/packages/sensors_plus/sensors_plus/lib/src/web_sensors.dart index 80affefdf9..f863ea004b 100644 --- a/packages/sensors_plus/sensors_plus/lib/src/web_sensors.dart +++ b/packages/sensors_plus/sensors_plus/lib/src/web_sensors.dart @@ -1,10 +1,9 @@ import 'dart:async'; import 'dart:developer' as developer; -import 'dart:html' as html - show LinearAccelerationSensor, Accelerometer, Gyroscope, Magnetometer; -import 'dart:js_util'; +import 'dart:js_interop'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; +import 'package:sensors_plus/src/web_sensors_interop.dart'; import 'package:sensors_plus_platform_interface/sensors_plus_platform_interface.dart'; /// The sensors plugin. @@ -23,31 +22,34 @@ class WebSensorsPlugin extends SensorsPlatform { }) { try { initSensor(); - } catch (error) { + } on DOMException catch (e) { if (onError != null) { onError(); } - /// Handle construction errors. - /// - /// If a feature policy blocks use of a feature it is because your code - /// is inconsistent with the policies set on your server. - /// This is not something that would ever be shown to a user. - /// See Feature-Policy for implementation instructions in the browsers. - if (error.toString().contains('SecurityError')) { - /// See the note above about feature policy. - developer.log('$apiName construction was blocked by a feature policy.', - error: error); - - /// if this feature is not supported or Flag is not enabled yet! - } else if (error.toString().contains('ReferenceError')) { - developer.log('$apiName is not supported by the User Agent.', - error: error); - - /// if this is unknown error, rethrow it - } else { - developer.log('Unknown error happened, rethrowing.'); - rethrow; + // Handle construction errors. + // + // If a feature policy blocks use of a feature it is because your code + // is inconsistent with the policies set on your server. + // This is not something that would ever be shown to a user. + // See Feature-Policy for implementation instructions in the browsers. + switch (e.name) { + case 'TypeError': + // if this feature is not supported or Flag is not enabled yet! + developer.log( + '$apiName is not supported by the User Agent.', + error: '${e.name}: ${e.message}', + ); + case 'SecurityError': + // See the note above about feature policy. + developer.log( + '$apiName construction was blocked by a feature policy.', + error: '${e.name}: ${e.message}', + ); + default: + // if this is unknown error, convert DOMException to Exception + developer.log('Unknown error happened, rethrowing.'); + throw Exception('${e.name}: ${e.message}'); } } } @@ -55,7 +57,6 @@ class WebSensorsPlugin extends SensorsPlatform { StreamController? _accelerometerStreamController; late Stream _accelerometerResultStream; - // todo: make web also support setting samplingPeriod @override Stream accelerometerEventStream({ Duration samplingPeriod = SensorInterval.normalInterval, @@ -64,31 +65,30 @@ class WebSensorsPlugin extends SensorsPlatform { _accelerometerStreamController = StreamController(); _featureDetected( () { - final accelerometer = html.Accelerometer(); - - setProperty( - accelerometer, - 'onreading', - allowInterop( - (_) { - _accelerometerStreamController!.add( - AccelerometerEvent( - accelerometer.x as double, - accelerometer.y as double, - accelerometer.z as double, - ), - ); - }, + final accelerometer = Accelerometer( + SensorOptions( + frequency: samplingPeriod.frequency, ), ); accelerometer.start(); - accelerometer.onError.forEach( - (e) => developer.log( - 'The accelerometer API is supported but something is wrong!', - error: e), - ); + accelerometer.onreading = (Event _) { + _accelerometerStreamController!.add( + AccelerometerEvent( + accelerometer.x, + accelerometer.y, + accelerometer.z, + ), + ); + }.toJS; + + accelerometer.onerror = (SensorErrorEvent e) { + developer.log( + 'The accelerometer API is supported but something is wrong!', + error: e.error.message, + ); + }.toJS; }, apiName: 'Accelerometer()', permissionName: 'accelerometer', @@ -110,7 +110,6 @@ class WebSensorsPlugin extends SensorsPlatform { StreamController? _gyroscopeEventStreamController; late Stream _gyroscopeEventResultStream; - // todo: make web also support setting samplingPeriod @override Stream gyroscopeEventStream({ Duration samplingPeriod = SensorInterval.normalInterval, @@ -119,31 +118,30 @@ class WebSensorsPlugin extends SensorsPlatform { _gyroscopeEventStreamController = StreamController(); _featureDetected( () { - final gyroscope = html.Gyroscope(); - - setProperty( - gyroscope, - 'onreading', - allowInterop( - (_) { - _gyroscopeEventStreamController!.add( - GyroscopeEvent( - gyroscope.x as double, - gyroscope.y as double, - gyroscope.z as double, - ), - ); - }, + final gyroscope = Gyroscope( + SensorOptions( + frequency: samplingPeriod.frequency, ), ); gyroscope.start(); - gyroscope.onError.forEach( - (e) => developer.log( - 'The gyroscope API is supported but something is wrong!', - error: e), - ); + gyroscope.onreading = (Event _) { + _gyroscopeEventStreamController!.add( + GyroscopeEvent( + gyroscope.x, + gyroscope.y, + gyroscope.z, + ), + ); + }.toJS; + + gyroscope.onerror = (SensorErrorEvent e) { + developer.log( + 'The gyroscope API is supported but something is wrong!', + error: e.error.message, + ); + }.toJS; }, apiName: 'Gyroscope()', permissionName: 'gyroscope', @@ -165,7 +163,6 @@ class WebSensorsPlugin extends SensorsPlatform { StreamController? _userAccelerometerStreamController; late Stream _userAccelerometerResultStream; - // todo: make web also support setting samplingPeriod @override Stream userAccelerometerEventStream({ Duration samplingPeriod = SensorInterval.normalInterval, @@ -175,31 +172,30 @@ class WebSensorsPlugin extends SensorsPlatform { StreamController(); _featureDetected( () { - final linearAccelerationSensor = html.LinearAccelerationSensor(); - - setProperty( - linearAccelerationSensor, - 'onreading', - allowInterop( - (_) { - _userAccelerometerStreamController!.add( - UserAccelerometerEvent( - linearAccelerationSensor.x as double, - linearAccelerationSensor.y as double, - linearAccelerationSensor.z as double, - ), - ); - }, + final linearAccelerationSensor = LinearAccelerationSensor( + SensorOptions( + frequency: samplingPeriod.frequency, ), ); linearAccelerationSensor.start(); - linearAccelerationSensor.onError.forEach( - (e) => developer.log( - 'The linear acceleration API is supported but something is wrong!', - error: e), - ); + linearAccelerationSensor.onreading = (Event _) { + _gyroscopeEventStreamController!.add( + GyroscopeEvent( + linearAccelerationSensor.x, + linearAccelerationSensor.y, + linearAccelerationSensor.z, + ), + ); + }.toJS; + + linearAccelerationSensor.onerror = (SensorErrorEvent e) { + developer.log( + 'The linear acceleration API is supported but something is wrong!', + error: e.error.message, + ); + }.toJS; }, apiName: 'LinearAccelerationSensor()', permissionName: 'accelerometer', @@ -222,40 +218,39 @@ class WebSensorsPlugin extends SensorsPlatform { StreamController? _magnetometerStreamController; late Stream _magnetometerResultStream; - // todo: make web also support setting samplingPeriod @override Stream magnetometerEventStream({ Duration samplingPeriod = SensorInterval.normalInterval, }) { + // The Magnetometer API is not supported by any modern browser. if (_magnetometerStreamController == null) { _magnetometerStreamController = StreamController(); _featureDetected( () { - final magnetometerSensor = html.Magnetometer(); - - setProperty( - magnetometerSensor, - 'onreading', - allowInterop( - (_) { - _magnetometerStreamController!.add( - MagnetometerEvent( - magnetometerSensor.x as double, - magnetometerSensor.y as double, - magnetometerSensor.z as double, - ), - ); - }, + final magnetometerSensor = Magnetometer( + SensorOptions( + frequency: samplingPeriod.frequency, ), ); magnetometerSensor.start(); - magnetometerSensor.onError.forEach( - (e) => developer.log( - 'The magnetometer API is supported but something is wrong!', - error: e), - ); + magnetometerSensor.onreading = (Event _) { + _gyroscopeEventStreamController!.add( + GyroscopeEvent( + magnetometerSensor.x, + magnetometerSensor.y, + magnetometerSensor.z, + ), + ); + }.toJS; + + magnetometerSensor.onerror = (SensorErrorEvent e) { + developer.log( + 'The magnetometer API is supported but something is wrong!', + error: e, + ); + }.toJS; }, apiName: 'Magnetometer()', permissionName: 'magnetometer', @@ -274,3 +269,14 @@ class WebSensorsPlugin extends SensorsPlatform { return _magnetometerResultStream; } } + +extension on Duration { + /// Converts the duration to a frequency in Hz. + int get frequency { + if (inMicroseconds <= 10) { + return 100; + } + + return 1000 ~/ inMilliseconds; + } +} diff --git a/packages/sensors_plus/sensors_plus/lib/src/web_sensors_interop.dart b/packages/sensors_plus/sensors_plus/lib/src/web_sensors_interop.dart new file mode 100644 index 0000000000..73fad6d897 --- /dev/null +++ b/packages/sensors_plus/sensors_plus/lib/src/web_sensors_interop.dart @@ -0,0 +1,96 @@ +import 'dart:js_interop'; + +/// Accelerometer API +/// https://developer.mozilla.org/en-US/docs/Web/API/Accelerometer +@JS('Accelerometer') +extension type Accelerometer._(JSObject _) implements JSObject { + external factory Accelerometer([ + SensorOptions options, + ]); + + external double get x; + external double get y; + external double get z; + + external set onreading(JSFunction callback); + external set onerror(JSFunction callback); + + external void start(); +} + +/// Gyroscope API +/// https://developer.mozilla.org/en-US/docs/Web/API/Gyroscope +@JS('Gyroscope') +extension type Gyroscope._(JSObject _) implements JSObject { + external factory Gyroscope([ + SensorOptions options, + ]); + + external double get x; + external double get y; + external double get z; + + external set onreading(JSFunction callback); + external set onerror(JSFunction callback); + + external void start(); +} + +/// LinearAccelerationSensor API +/// https://developer.mozilla.org/en-US/docs/Web/API/LinearAccelerationSensor +@JS('LinearAccelerationSensor') +extension type LinearAccelerationSensor._(JSObject _) implements JSObject { + external factory LinearAccelerationSensor([ + SensorOptions options, + ]); + + external double get x; + external double get y; + external double get z; + + external set onreading(JSFunction callback); + external set onerror(JSFunction callback); + + external void start(); +} + +/// Magnetometer API +/// https://developer.mozilla.org/en-US/docs/Web/API/Magnetometer +@JS('Magnetometer') +extension type Magnetometer._(JSObject _) implements JSObject { + external factory Magnetometer([ + SensorOptions options, + ]); + + external double get x; + external double get y; + external double get z; + + external set onreading(JSFunction callback); + external set onerror(JSFunction callback); + + external void start(); +} + +extension type SensorOptions._(JSObject _) implements JSObject { + external factory SensorOptions({ + int frequency, + }); +} + +/// Event +/// https://developer.mozilla.org/en-US/docs/Web/API/Event +extension type Event(JSObject _) implements JSObject {} + +/// SensorErrorEvent +/// https://developer.mozilla.org/en-US/docs/Web/API/SensorErrorEvent +extension type SensorErrorEvent(JSObject _) implements JSObject { + external DOMException get error; +} + +/// DOMException +/// https://developer.mozilla.org/en-US/docs/Web/API/DOMException +extension type DOMException(JSObject _) implements JSObject { + external String? get name; + external String? get message; +} diff --git a/packages/sensors_plus/sensors_plus/pubspec.yaml b/packages/sensors_plus/sensors_plus/pubspec.yaml index 0b1f4289d4..6ddd5eee24 100644 --- a/packages/sensors_plus/sensors_plus/pubspec.yaml +++ b/packages/sensors_plus/sensors_plus/pubspec.yaml @@ -31,5 +31,5 @@ dev_dependencies: flutter_lints: ">=2.0.1 <4.0.0" environment: - sdk: ">=2.18.0 <4.0.0" - flutter: ">=3.3.0" + sdk: ">=3.3.0 <4.0.0" + flutter: ">=3.19.0"