diff --git a/README.md b/README.md index e957dd6..d1eb4c8 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,9 @@ ## 🧐 What is it ? -Gaimon is a **very simple** & **easy to use** plugin to include **Haptic feedback** in your app. It support custom pattern with `.ahap` file support. +Gaimon is a **very simple** & **easy to use** plugin to include **Haptic feedback** in your app. It supports custom patterns with `.ahap` file support. + +While the custom haptic patterns and native actions are implemented for **iOS** and **Android**, Gaimon is safe to compile and use on **any platform** (Web, macOS, Windows, Linux, etc.). On unsupported platforms, it degrades gracefully by failing silently (no-op) and returning `false` for `canSupportsHaptic`.

diff --git a/example/pubspec.lock b/example/pubspec.lock index 119f022..dc8c263 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -134,10 +134,10 @@ packages: dependency: transitive description: name: meta - sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.18.0" path: dependency: transitive description: @@ -195,10 +195,10 @@ packages: dependency: transitive description: name: test_api - sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" + sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" url: "https://pub.dev" source: hosted - version: "0.7.10" + version: "0.7.11" vector_math: dependency: transitive description: diff --git a/lib/gaimon.dart b/lib/gaimon.dart index b254aa5..187d3a2 100644 --- a/lib/gaimon.dart +++ b/lib/gaimon.dart @@ -1,28 +1,47 @@ import 'dart:async'; import 'dart:io'; +import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter/services.dart'; import 'package:gaimon/ahap_to_waveform_converter.dart'; class Gaimon { static const MethodChannel _channel = MethodChannel('gaimon'); + static bool get _isAndroid => !kIsWeb && Platform.isAndroid; + static bool get _isIOS => !kIsWeb && Platform.isIOS; + static bool get _supportsMethodChannel => _isAndroid || _isIOS; + /// check if the device can vibrate or not static Future get canSupportsHaptic async { - return await _channel.invokeMethod('canSupportsHaptic'); + if (!_supportsMethodChannel) return false; + try { + return await _channel.invokeMethod('canSupportsHaptic') ?? false; + } catch (_) { + return false; + } } /// generate a selection impact vibration static void selection() => HapticFeedback.selectionClick(); /// generate an error impact vibration - static void error() => _channel.invokeMethod('error'); + static void error() { + if (!_supportsMethodChannel) return; + _channel.invokeMethod('error'); + } /// generate a success impact vibration - static void success() => _channel.invokeMethod('success'); + static void success() { + if (!_supportsMethodChannel) return; + _channel.invokeMethod('success'); + } /// generate a warning impact vibration - static void warning() => _channel.invokeMethod('warning'); + static void warning() { + if (!_supportsMethodChannel) return; + _channel.invokeMethod('warning'); + } /// generate a heavy impact vibration static void heavy() => HapticFeedback.heavyImpact(); @@ -34,15 +53,26 @@ class Gaimon { static void light() => HapticFeedback.lightImpact(); /// generate a rigid impact vibration - static void rigid() => _channel.invokeMethod('rigid'); + static void rigid() { + if (!_supportsMethodChannel) return; + _channel.invokeMethod('rigid'); + } /// generate a soft impact vibration - static void soft() => _channel.invokeMethod('soft'); + static void soft() { + if (!_supportsMethodChannel) return; + _channel.invokeMethod('soft'); + } /// generate a custom pattern impact vibration - static void patternFromData(String data) => Platform.isAndroid - ? _patternFromAhapToWaveform(data) - : _channel.invokeMethod('pattern', {'data': data}); + static void patternFromData(String data) { + if (!_supportsMethodChannel) return; + if (_isAndroid) { + _patternFromAhapToWaveform(data); + } else { + _channel.invokeMethod('pattern', {'data': data}); + } + } static void _patternFromAhapToWaveform(String data) { final waveform = ahapToWaveform(data); @@ -50,16 +80,23 @@ class Gaimon { } /// stop any ongoing vibration - static void stop() => _channel.invokeMethod('stop'); + static void stop() { + if (!_supportsMethodChannel) return; + _channel.invokeMethod('stop'); + } /// generate a custom pattern impact vibration from waveform (android only) static void patternFromWaveForm( List timings, List amplitudes, bool repeat, - ) => _channel.invokeMethod('pattern', { - 'timings': timings, - 'amplitudes': amplitudes, - 'repeat': repeat, - }); + ) { + if (!_supportsMethodChannel) return; + _channel.invokeMethod('pattern', { + 'timings': timings, + 'amplitudes': amplitudes, + 'repeat': repeat, + }); + } } + diff --git a/test/gaimon_test.dart b/test/gaimon_test.dart new file mode 100644 index 0000000..528c8f4 --- /dev/null +++ b/test/gaimon_test.dart @@ -0,0 +1,28 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:gaimon/gaimon.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('Gaimon Multi-platform Safety Tests', () { + test('canSupportsHaptic returns false on unsupported platform (macOS/test environment)', () async { + final supportsHaptic = await Gaimon.canSupportsHaptic; + expect(supportsHaptic, isFalse); + }); + + test('calling haptic actions does not throw on unsupported platforms', () { + expect(() => Gaimon.selection(), returnsNormally); + expect(() => Gaimon.error(), returnsNormally); + expect(() => Gaimon.success(), returnsNormally); + expect(() => Gaimon.warning(), returnsNormally); + expect(() => Gaimon.heavy(), returnsNormally); + expect(() => Gaimon.medium(), returnsNormally); + expect(() => Gaimon.light(), returnsNormally); + expect(() => Gaimon.rigid(), returnsNormally); + expect(() => Gaimon.soft(), returnsNormally); + expect(() => Gaimon.patternFromData('{}'), returnsNormally); + expect(() => Gaimon.patternFromWaveForm([100], [128], false), returnsNormally); + expect(() => Gaimon.stop(), returnsNormally); + }); + }); +}