diff --git a/CHANGELOG.md b/CHANGELOG.md index 193ca69..14d7df7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 1.0.5 + +1. scanPortsForSingleDevice and customDiscover supports async mode now. + ## 1.0.4 1. Use network_tools v4.0.1 to fix path issue in flutter. diff --git a/README.md b/README.md index 358f9f0..725bca2 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ## Features -This package will add support for flutter features which is out of the scope of [network_tools](https://github.com/osociety/network_tools) because of platform limitations but network_tools must also be added to pubspec.yml +This package will add support for flutter features which is out of the scope of [network_tools](https://github.com/osociety/network_tools) because of platform limitations. ## Getting started @@ -16,8 +16,7 @@ dependencies: flutter: sdk: flutter - network_tools_flutter: ^1.0.1 - network_tools: ^3.2.4 + network_tools_flutter: ^1.0.4 ``` Import package in your project @@ -29,4 +28,4 @@ Use HostScannerFlutter and PortScannerFlutter for your flutter projects. See exa ## Additional information -You can use same methods but need to import from network_tools_flutter. \ No newline at end of file +You can use same APIs but need to import from network_tools_flutter. All APIs from network_tools are automatically imported by network_tools_flutter. So just import network_tools_flutter in your flutter app. \ No newline at end of file diff --git a/analysis_options.yaml b/analysis_options.yaml index a5744c1..28e868b 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,4 +1,36 @@ +# Defines a default set of lint rules enforced for +# projects at Google. For details and rationale, +# see https://github.com/dart-lang/pedantic#enabled-lints. +# include: package:pedantic/analysis_options.yaml + +# lint analysis include: package:flutter_lints/flutter.yaml -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options + +analyzer: + errors: + missing_required_param: error + missing_return: error + must_be_immutable: error + exclude: + - "**/*.g.dart" + - "**/*.freezed.dart" + - "**/*.config.dart" + - "**/*.pb.dart" + - "**/*.pbenum.dart" + - "**/*.pbgrpc.dart" + - "**/*.pbjson.dart" + - "**/*.gr.dart" + - "**/*.md" + - "example/**" + +linter: + rules: + # Use parameter order as in json response + always_put_required_named_parameters_first: true + + avoid_classes_with_only_static_members: false + + sort_constructors_first: true + + avoid_relative_lib_imports: false diff --git a/example/lib/main.dart b/example/lib/main.dart index 1d959b0..31fb282 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,3 +1,5 @@ +import 'package:example/pages/pingable_devices.dart'; +import 'package:example/pages/port_scanner_page.dart'; import 'package:flutter/material.dart'; import 'package:network_tools_flutter/network_tools_flutter.dart'; import 'package:path_provider/path_provider.dart'; @@ -36,75 +38,48 @@ class MyApp extends StatelessWidget { colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), - home: const MyHomePage(title: 'Network Tools Flutter'), + home: const MyHomePage(), ); } } -class MyHomePage extends StatefulWidget { - const MyHomePage({super.key, required this.title}); - - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - - final String title; - - @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - List activeHosts = []; - - @override - void initState() { - super.initState(); - NetInterface.localInterface().then((value) { - final netInt = value; - if (netInt != null) { - HostScannerFlutter.getAllPingableDevices(netInt.networkId) - .listen((host) { - setState(() { - activeHosts.add(host); - }); - }); - } - }); - } +class MyHomePage extends StatelessWidget { + const MyHomePage({super.key}); @override Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. return Scaffold( appBar: AppBar( - // TRY THIS: Try changing the color here to a specific color (to - // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar - // change color while the other colors stay the same. - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget.title), + title: const Text('Home'), ), - body: Center( - child: ListView.builder( - itemCount: activeHosts.length, - itemBuilder: (context, index) { - return ListTile( - title: Text(activeHosts[index].address), - ); - }, - ), + body: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const PingableDevices(), + ), + ); + }, + child: const Text('Pingable Devices'), + ), + const SizedBox(height: 20, width: double.infinity), + TextButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const PortScannerPage(), + ), + ); + }, + child: const Text('Port Scanner'), + ), + ], ), ); } diff --git a/example/lib/pages/pingable_devices.dart b/example/lib/pages/pingable_devices.dart new file mode 100644 index 0000000..4d2b9a9 --- /dev/null +++ b/example/lib/pages/pingable_devices.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:network_tools_flutter/network_tools_flutter.dart'; + +class PingableDevices extends StatefulWidget { + const PingableDevices({super.key}); + + @override + State createState() => _PingableDevicesState(); +} + +class _PingableDevicesState extends State { + List activeHosts = []; + + @override + void initState() { + super.initState(); + NetInterface.localInterface().then((value) { + final NetInterface? netInt = value; + if (netInt == null) { + return; + } + HostScannerFlutter.getAllPingableDevices(netInt.networkId).listen((host) { + setState(() { + activeHosts.add(host); + }); + }).onError((e) { + print('Error $e'); + }); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + title: const Text('Pingable Devices'), + ), + body: Center( + child: activeHosts.isEmpty + ? const CircularProgressIndicator() + : ListView.builder( + itemCount: activeHosts.length, + itemBuilder: (context, index) { + return ListTile( + title: Text(activeHosts[index].address), + ); + }, + ), + ), + ); + } +} diff --git a/example/lib/pages/port_scanner_page.dart b/example/lib/pages/port_scanner_page.dart new file mode 100644 index 0000000..d094c0b --- /dev/null +++ b/example/lib/pages/port_scanner_page.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; +import 'package:network_tools_flutter/network_tools_flutter.dart'; + +class PortScannerPage extends StatefulWidget { + const PortScannerPage({super.key}); + + @override + State createState() => _PortScannerPageState(); +} + +class _PortScannerPageState extends State { + List activeHosts = []; + + @override + void initState() { + super.initState(); + NetInterface.localInterface().then((value) { + final NetInterface? netInt = value; + if (netInt == null) { + return; + } + String subnet = + netInt.ipAddress.substring(0, netInt.ipAddress.lastIndexOf('.')); + HostScanner.scanDevicesForSinglePort(subnet, 53).listen((host) { + setState(() { + activeHosts.add(host); + }); + }).onError((e) { + print('Error $e'); + }); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + title: const Text('Port Scanner'), + ), + body: Center( + child: activeHosts.isEmpty + ? const CircularProgressIndicator() + : ListView.builder( + itemCount: activeHosts.length, + itemBuilder: (context, index) { + return ListTile( + title: Text(activeHosts[index].address), + ); + }, + ), + ), + ); + } +} diff --git a/example/pubspec.lock b/example/pubspec.lock index 170a25b..edbdc1c 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -53,10 +53,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" crypto: dependency: transitive description: @@ -239,10 +239,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" multicast_dns: dependency: transitive description: @@ -265,7 +265,7 @@ packages: path: ".." relative: true source: path - version: "1.0.3" + version: "1.0.4" path: dependency: transitive description: @@ -379,18 +379,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -419,10 +419,10 @@ packages: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" typed_data: dependency: transitive description: @@ -459,10 +459,10 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" win32: dependency: transitive description: @@ -488,5 +488,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.1.0 <4.0.0" + dart: ">=3.2.0-194.0.dev <4.0.0" flutter: ">=3.7.0" diff --git a/lib/src/host_scanner_flutter.dart b/lib/src/host_scanner_flutter.dart index bc1e124..849fc70 100644 --- a/lib/src/host_scanner_flutter.dart +++ b/lib/src/host_scanner_flutter.dart @@ -58,8 +58,8 @@ class HostScannerFlutter { progressCallback ?.call((i - firstHostId) * 100 / (lastValidSubnet - firstHostId)); final activeHostFound = ActiveHost.fromSendableActiveHost( - sendableActiveHost: SendableActiveHost( - message[0], PingData.fromJson(message[1]))); + sendableActiveHost: SendableActiveHost(message[0], + pingData: PingData.fromJson(message[1]))); await activeHostFound.resolveInfo(); yield activeHostFound; } else if (message is String && message == 'Done') { @@ -104,8 +104,8 @@ class HostScannerFlutter { await for (final SendableActiveHost activeHostFound in hostsDiscoveredInNetwork) { - sendPort - .send([activeHostFound.address, activeHostFound.pingData.toJson()]); + sendPort.send( + [activeHostFound.address, activeHostFound.pingData!.toJson()]); } sendPort.send('Done'); } diff --git a/lib/src/port_scanner_flutter.dart b/lib/src/port_scanner_flutter.dart index 607bfc6..b68bcd9 100644 --- a/lib/src/port_scanner_flutter.dart +++ b/lib/src/port_scanner_flutter.dart @@ -29,6 +29,7 @@ class PortScannerFlutter { ProgressCallback? progressCallback, Duration timeout = const Duration(milliseconds: 2000), bool resultsInAddressAscendingOrder = true, + bool async = false, }) { if (Platform.isIOS) { DartPingIOS.register(); @@ -37,7 +38,8 @@ class PortScannerFlutter { portList: portList, progressCallback: progressCallback, timeout: timeout, - resultsInAddressAscendingOrder: resultsInAddressAscendingOrder); + resultsInAddressAscendingOrder: resultsInAddressAscendingOrder, + async: async); } /// Scans port from [startPort] to [endPort] of [target]. Progress can be @@ -50,6 +52,7 @@ class PortScannerFlutter { ProgressCallback? progressCallback, Duration timeout = const Duration(milliseconds: 2000), bool resultsInAddressAscendingOrder = true, + bool async = false, }) { if (Platform.isIOS) { DartPingIOS.register(); @@ -59,7 +62,8 @@ class PortScannerFlutter { endPort: endPort, progressCallback: progressCallback, timeout: timeout, - resultsInAddressAscendingOrder: resultsInAddressAscendingOrder); + resultsInAddressAscendingOrder: resultsInAddressAscendingOrder, + async: async); } static Future connectToPort({ diff --git a/pubspec.yaml b/pubspec.yaml index d5c44ba..81891da 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: network_tools_flutter description: Extended features of network_tools package for flutter framework supporting iOS and Android -version: 1.0.4 +version: 1.0.5 issue_tracker: https://github.com/osociety/network_tools_flutter/issues repository: https://github.com/osociety/network_tools_flutter @@ -39,7 +39,7 @@ dependencies: logging: ^1.2.0 # Deal with internationalized/localized messages and more. intl: ^0.18.0 - network_tools: ^4.0.1 + network_tools: ^4.0.3 path_provider: ^2.1.1 universal_io: ^2.2.0 diff --git a/test/host_scan_flutter_test.dart b/test/host_scan_flutter_test.dart index 70cc9dc..19ee817 100644 --- a/test/host_scan_flutter_test.dart +++ b/test/host_scan_flutter_test.dart @@ -35,7 +35,7 @@ void main() { }); group('Testing Host Scanner emits', () { - test('Running getAllPingableDevices emits tests', () async { + test('Running getAllPingableDevices emits tests', () async* { expectLater( //There should be at least one device pingable in network HostScannerFlutter.getAllPingableDevices( @@ -46,7 +46,7 @@ void main() { emits(isA()), ); }); - test('Running getAllPingableDevices emitsThrough tests', () async { + test('Running getAllPingableDevices emitsThrough tests', () async* { expectLater( //Should emit at least our own local machine when pinging all hosts. HostScannerFlutter.getAllPingableDevices( diff --git a/test/port_scan_flutter_test.dart b/test/port_scan_flutter_test.dart index fa96030..7af5034 100644 --- a/test/port_scan_flutter_test.dart +++ b/test/port_scan_flutter_test.dart @@ -49,6 +49,27 @@ void main() { } }); + test('Running scanPortsForSingleDevice Async tests', () { + for (final activeHost in hostsWithOpenPort) { + final port = activeHost.openPorts.elementAt(0).port; + expectLater( + PortScannerFlutter.scanPortsForSingleDevice( + activeHost.address, + startPort: port - 1, + endPort: port + 1, + async: true, + ), + emitsThrough( + isA().having( + (p0) => p0.openPorts.contains(OpenPort(port)), + "Should match host having same open port", + equals(true), + ), + ), + ); + } + }); + test('Running connectToPort tests', () { for (final activeHost in hostsWithOpenPort) { expectLater( @@ -78,6 +99,19 @@ void main() { } }); + test('Running customDiscover Async tests', () { + for (final activeHost in hostsWithOpenPort) { + expectLater( + PortScannerFlutter.customDiscover( + activeHost.address, + portList: [port - 1, port, port + 1], + async: true, + ), + emits(isA()), + ); + } + }); + test('Running isOpen tests', () { for (final activeHost in hostsWithOpenPort) { expectLater(