diff --git a/open_earable/lib/sensor_data_tab/earable_3d_model.dart b/open_earable/lib/sensor_data_tab/earable_3d_model.dart index e1e8d1d..723aea0 100644 --- a/open_earable/lib/sensor_data_tab/earable_3d_model.dart +++ b/open_earable/lib/sensor_data_tab/earable_3d_model.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:open_earable/sensor_data_tab/earable_3d_view.dart' if (dart.library.html) 'package:open_earable/sensor_data_tab/earable_3d_view_web.dart'; import 'dart:async'; import 'dart:math'; import 'package:open_earable_flutter/open_earable_flutter.dart'; -import 'package:model_viewer_plus/model_viewer_plus.dart'; -import 'package:webview_flutter/webview_flutter.dart'; class Earable3DModel extends StatefulWidget { final OpenEarable openEarable; @@ -14,15 +13,17 @@ class Earable3DModel extends StatefulWidget { } class _Earable3DModelState extends State { - WebViewController? _controller; StreamSubscription? _imuSubscription; double _pitch = 0; double _yaw = 0; double _roll = 0; - final String fileName = "assets/OpenEarable.obj"; + final GlobalKey _modelViewerKey = GlobalKey(); + + final String fileName = 'assets/OpenEarableV1.glb'; dynamic sourceTexture; + @override void initState() { super.initState(); @@ -62,8 +63,11 @@ class _Earable3DModelState extends State { _pitch = data["EULER"]["PITCH"]; _roll = data["EULER"]["ROLL"]; }); - _controller?.runJavaScript( - "document.querySelector('model-viewer').setAttribute('orientation', '${-_pitch} $_roll ${-_yaw}');",); + if (_modelViewerKey.currentState == null) { + print("ModelViewerKey.currentState is null"); + return; + } + _modelViewerKey.currentState?.updateOrientation(_pitch, _yaw, _roll); }); } @@ -71,20 +75,12 @@ class _Earable3DModelState extends State { Widget build(BuildContext context) { return Column(mainAxisAlignment: MainAxisAlignment.center, children: [ Expanded( - child: ModelViewer( - cameraControls: false, - backgroundColor: Theme.of(context).colorScheme.surface, - src: 'assets/OpenEarableV1.glb', - alt: 'A 3D model of an astronaut', - interactionPrompt: InteractionPrompt.none, - autoRotate: false, - disableZoom: true, - disablePan: true, - onWebViewCreated: (controller) { - _controller = controller; - controller.runJavaScript( - "document.body.style.overflow = 'hidden';document.documentElement.style.overflow = 'hidden';document.addEventListener('touchmove', function(e) { e.preventDefault(); }, { passive: false });",); - },),), + child: ModelViewerWidget( + key: _modelViewerKey, + modelSrc: fileName, + backgroundColor: Theme.of(context).colorScheme.surface, + ), + ), Padding( padding: EdgeInsets.only(bottom: 16), child: Text( diff --git a/open_earable/lib/sensor_data_tab/earable_3d_view.dart b/open_earable/lib/sensor_data_tab/earable_3d_view.dart new file mode 100644 index 0000000..2c5046c --- /dev/null +++ b/open_earable/lib/sensor_data_tab/earable_3d_view.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; +import 'package:model_viewer_plus/model_viewer_plus.dart'; +import 'package:webview_flutter/webview_flutter.dart'; + +class ModelViewerWidget extends StatefulWidget { + final String modelSrc; + final Color backgroundColor; + final bool cameraControls; + final bool autoRotate; + final bool disableZoom; + final bool disablePan; + + const ModelViewerWidget({ + required this.modelSrc, + required this.backgroundColor, + this.cameraControls = false, + this.autoRotate = false, + this.disableZoom = true, + this.disablePan = true, + super.key, + }); + + @override + State createState() => ModelViewerWidgetState(); +} + +class ModelViewerWidgetState extends State { + WebViewController? _controller; + + void updateOrientation(double pitch, double yaw, double roll) { + if (_controller != null) { + final jsCommand = "document.querySelector('model-viewer').setAttribute('orientation', '${-pitch} $roll ${-yaw}');"; + _controller?.runJavaScript(jsCommand); + } else { + print("WebViewController is not initialized yet."); + } + } + + @override + Widget build(BuildContext context) { + return ModelViewer( + cameraControls: widget.cameraControls, + backgroundColor: widget.backgroundColor, + src: widget.modelSrc, + alt: 'A 3D model of the OpenEarable', + interactionPrompt: InteractionPrompt.none, + autoRotate: widget.autoRotate, + disableZoom: widget.disableZoom, + disablePan: widget.disablePan, + onWebViewCreated: (controller) { + _controller = controller; + controller.runJavaScript( + "document.body.style.overflow = 'hidden';document.documentElement.style.overflow = 'hidden';document.addEventListener('touchmove', function(e) { e.preventDefault(); }, { passive: false });", + ); + }, + ); + } +} diff --git a/open_earable/lib/sensor_data_tab/earable_3d_view_web.dart b/open_earable/lib/sensor_data_tab/earable_3d_view_web.dart new file mode 100644 index 0000000..752d068 --- /dev/null +++ b/open_earable/lib/sensor_data_tab/earable_3d_view_web.dart @@ -0,0 +1,75 @@ +import 'dart:ui_web'; + +import 'package:universal_html/html.dart' as html; +import 'package:flutter/material.dart'; + +class ModelViewerWidget extends StatefulWidget { + final String modelSrc; + final Color backgroundColor; + final bool cameraControls; + final bool autoRotate; + final bool disableZoom; + final bool disablePan; + + const ModelViewerWidget({ + required this.modelSrc, + required this.backgroundColor, + this.cameraControls = false, + this.autoRotate = false, + this.disableZoom = true, + this.disablePan = true, + super.key, + }); + + @override + State createState() => ModelViewerWidgetState(); +} + +class ModelViewerWidgetState extends State { + final String _viewType = 'model-viewer'; + + @override + void initState() { + super.initState(); + + platformViewRegistry.registerViewFactory( + _viewType, + (int viewId) { + final element = html.Element.tag('model-viewer') + ..setAttribute('src', widget.modelSrc) + ..setAttribute('alt', 'A 3D model of the OpenEarable') + ..style.width = '100%' + ..style.height = '100%'; + + if (widget.cameraControls) { + element.setAttribute('camera-controls', ''); + } + if (widget.autoRotate) { + element.setAttribute('auto-rotate', ''); + } + if (widget.disableZoom) { + element.setAttribute('disable-zoom', ''); + } + if (widget.disablePan) { + element.setAttribute('disable-pan', ''); + } + + return element; + }, + ); + } + + void updateOrientation(double pitch, double yaw, double roll) { + // Find the registered element and update its orientation + print("Updating orientation: $pitch, $yaw, $roll"); + final element = html.document.querySelector(_viewType); + if (element != null) { + element.setAttribute('orientation', '${-pitch} $roll ${-yaw}'); + } + } + + @override + Widget build(BuildContext context) { + return HtmlElementView(viewType: _viewType); + } +} diff --git a/open_earable/lib/sensor_data_tab/sensor_html_chart.dart b/open_earable/lib/sensor_data_tab/sensor_html_chart.dart index 8722e60..797b5f4 100644 --- a/open_earable/lib/sensor_data_tab/sensor_html_chart.dart +++ b/open_earable/lib/sensor_data_tab/sensor_html_chart.dart @@ -1,14 +1,10 @@ import 'dart:convert'; -// ignore: avoid_web_libraries_in_flutter -import 'dart:js' as js; -// ignore: avoid_web_libraries_in_flutter -import 'dart:html' as html; +import 'package:universal_html/html.dart' as html; +import 'package:universal_html/js.dart' as js; import 'dart:ui_web'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:open_earable/sensor_data_tab/sensor_chart.dart'; -// Conditional imports -// For web-specific code class ChartSeries { final String id; diff --git a/open_earable/lib/sensor_data_tab/sensor_html_chart_stub.dart b/open_earable/lib/sensor_data_tab/sensor_html_chart_stub.dart index e013caa..1c9f3a2 100644 --- a/open_earable/lib/sensor_data_tab/sensor_html_chart_stub.dart +++ b/open_earable/lib/sensor_data_tab/sensor_html_chart_stub.dart @@ -20,13 +20,14 @@ class ChartSeries { }); } + class ChartJsWidget extends StatelessWidget { final String chartType; final List seriesList; + final String title; - const ChartJsWidget({ - super.key, + const ChartJsWidget({super.key, required this.chartType, required this.seriesList, required this.title, diff --git a/open_earable/macos/Podfile.lock b/open_earable/macos/Podfile.lock index cd30fd8..896b869 100644 --- a/open_earable/macos/Podfile.lock +++ b/open_earable/macos/Podfile.lock @@ -1,24 +1,35 @@ PODS: - flutter_inappwebview_macos (0.0.1): - FlutterMacOS - - OrderedSet (~> 5.0) + - OrderedSet (~> 6.0.3) - FlutterMacOS (1.0.0) - - OrderedSet (5.0.0) + - open_file_mac (0.0.1): + - FlutterMacOS + - OrderedSet (6.0.3) - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS + - universal_ble (0.0.1): + - Flutter + - FlutterMacOS - url_launcher_macos (0.0.1): - FlutterMacOS + - webview_flutter_wkwebview (0.0.1): + - Flutter + - FlutterMacOS DEPENDENCIES: - flutter_inappwebview_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos`) - FlutterMacOS (from `Flutter/ephemeral`) + - open_file_mac (from `Flutter/ephemeral/.symlinks/plugins/open_file_mac/macos`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) + - universal_ble (from `Flutter/ephemeral/.symlinks/plugins/universal_ble/darwin`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) + - webview_flutter_wkwebview (from `Flutter/ephemeral/.symlinks/plugins/webview_flutter_wkwebview/darwin`) SPEC REPOS: trunk: @@ -29,21 +40,30 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos FlutterMacOS: :path: Flutter/ephemeral + open_file_mac: + :path: Flutter/ephemeral/.symlinks/plugins/open_file_mac/macos path_provider_foundation: :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin shared_preferences_foundation: :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin + universal_ble: + :path: Flutter/ephemeral/.symlinks/plugins/universal_ble/darwin url_launcher_macos: :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos + webview_flutter_wkwebview: + :path: Flutter/ephemeral/.symlinks/plugins/webview_flutter_wkwebview/darwin SPEC CHECKSUMS: - flutter_inappwebview_macos: 9600c9df9fdb346aaa8933812009f8d94304203d + flutter_inappwebview_macos: bdf207b8f4ebd58e86ae06cd96b147de99a67c9b FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 - OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c + open_file_mac: 0e554648e2a87ce59e9438e3e5ca3e552e90d89a + OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94 path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 - url_launcher_macos: 5f437abeda8c85500ceb03f5c1938a8c5a705399 + universal_ble: cf52a7b3fd2e7c14d6d7262e9fdadb72ab6b88a6 + url_launcher_macos: c82c93949963e55b228a30115bd219499a6fe404 + webview_flutter_wkwebview: 0982481e3d9c78fd5c6f62a002fcd24fc791f1e4 PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.2 diff --git a/open_earable/pubspec.lock b/open_earable/pubspec.lock index 859ab6f..7078016 100644 --- a/open_earable/pubspec.lock +++ b/open_earable/pubspec.lock @@ -73,6 +73,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" + charcode: + dependency: transitive + description: + name: charcode + sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 + url: "https://pub.dev" + source: hosted + version: "1.3.1" checked_yaml: dependency: transitive description: @@ -787,6 +795,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.12.0" + universal_html: + dependency: "direct main" + description: + name: universal_html + sha256: "56536254004e24d9d8cfdb7dbbf09b74cf8df96729f38a2f5c238163e3d58971" + url: "https://pub.dev" + source: hosted + version: "2.2.4" universal_io: dependency: transitive description: diff --git a/open_earable/pubspec.yaml b/open_earable/pubspec.yaml index d9516af..ad01e42 100644 --- a/open_earable/pubspec.yaml +++ b/open_earable/pubspec.yaml @@ -58,6 +58,7 @@ dependencies: collection: ^1.18.0 webview_flutter: ^4.9.0 intl: ^0.18.1 + universal_html: ^2.2.4 dependency_overrides: intl: ^0.18.1 diff --git a/open_earable/web/index.html b/open_earable/web/index.html index df89cb7..cb99851 100644 --- a/open_earable/web/index.html +++ b/open_earable/web/index.html @@ -19,7 +19,7 @@ - + @@ -40,6 +40,7 @@ +