From 44412508e7307a1ddde8ce955b034d79892484c4 Mon Sep 17 00:00:00 2001 From: Rohit Sangwan Date: Fri, 12 Jul 2024 20:54:00 +0530 Subject: [PATCH] Implement scan filter in example app (#61) * Implement scan filter in example app * Remove Fixed FlutterVersion from Workflow and improve ScanFilterWidget * Update example/lib/home/widgets/scan_filter_widget.dart * Update example/lib/home/widgets/scan_filter_widget.dart * Update example/lib/peripheral_details/peripheral_detail_page.dart * Improve text --------- Co-authored-by: Foti Dim Co-authored-by: Foti Dim --- .github/workflows/build_web_app.yml | 1 - example/.gitignore | 1 + example/.metadata | 30 --- example/lib/home/home.dart | 53 +++--- .../lib/home/widgets/scan_filter_widget.dart | 172 ++++++++++++++++++ .../peripheral_detail_page.dart | 4 +- example/pubspec.lock | 12 +- example/pubspec.yaml | 3 +- example/web/index.html | 23 +-- 9 files changed, 208 insertions(+), 91 deletions(-) delete mode 100644 example/.metadata create mode 100644 example/lib/home/widgets/scan_filter_widget.dart diff --git a/.github/workflows/build_web_app.yml b/.github/workflows/build_web_app.yml index 8626cc2..8a73d69 100644 --- a/.github/workflows/build_web_app.yml +++ b/.github/workflows/build_web_app.yml @@ -14,7 +14,6 @@ jobs: - name: Setup Flutter uses: subosito/flutter-action@v2 with: - flutter-version: '3.16.0' channel: 'stable' - run: flutter --version diff --git a/example/.gitignore b/example/.gitignore index 24476c5..8fe6ed9 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -42,3 +42,4 @@ app.*.map.json /android/app/debug /android/app/profile /android/app/release +.metadata \ No newline at end of file diff --git a/example/.metadata b/example/.metadata deleted file mode 100644 index 56b8ab2..0000000 --- a/example/.metadata +++ /dev/null @@ -1,30 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: "ead455963c12b453cdb2358cad34969c76daf180" - channel: "stable" - -project_type: app - -# Tracks metadata for the flutter migrate command -migration: - platforms: - - platform: root - create_revision: ead455963c12b453cdb2358cad34969c76daf180 - base_revision: ead455963c12b453cdb2358cad34969c76daf180 - - platform: web - create_revision: ead455963c12b453cdb2358cad34969c76daf180 - base_revision: ead455963c12b453cdb2358cad34969c76daf180 - - # User provided section - - # List of Local paths (relative to this file) that should be - # ignored by the migrate tool. - # - # Files that are not part of the templates will be ignored by default. - unmanaged_files: - - 'lib/main.dart' - - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/example/lib/home/home.dart b/example/lib/home/home.dart index 269d6cf..24d6330 100644 --- a/example/lib/home/home.dart +++ b/example/lib/home/home.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:universal_ble/universal_ble.dart'; import 'package:universal_ble_example/data/capabilities.dart'; import 'package:universal_ble_example/data/mock_universal_ble.dart'; +import 'package:universal_ble_example/home/widgets/scan_filter_widget.dart'; import 'package:universal_ble_example/home/widgets/scanned_devices_placeholder_widget.dart'; import 'package:universal_ble_example/home/widgets/scanned_item_widget.dart'; import 'package:universal_ble_example/data/permission_handler.dart'; @@ -23,17 +24,12 @@ class _MyAppState extends State { final _bleDevices = []; bool _isScanning = false; QueueType _queueType = QueueType.global; + TextEditingController servicesFilterController = TextEditingController(); + TextEditingController namePrefixController = TextEditingController(); + TextEditingController manufacturerDataController = TextEditingController(); AvailabilityState? bleAvailabilityState; - final List _services = [ - "00001800-0000-1000-8000-00805f9b34fb", - "0000180f-0000-1000-8000-00805f9b34fb", - "00002a00-0000-1000-8000-00805f9b34fb", - "00002a01-0000-1000-8000-00805f9b34fb", - "00002a19-0000-1000-8000-00805f9b34fb", - "8000cc00-cc00-ffff-ffff-ffffffffffff", - "8000dd00-dd00-ffff-ffff-ffffffffffff", - ]; + ScanFilter? scanFilter; @override void initState() { @@ -76,17 +72,26 @@ class _MyAppState extends State { Future startScan() async { await UniversalBle.startScan( - scanFilter: ScanFilter( - withServices: kIsWeb ? _services : [], - withManufacturerData: [ - // ManufacturerDataFilter( - // companyIdentifier: 0x012D, - // data: Uint8List.fromList( - // [0x03, 0x00, 0x64, 0x00], - // ), - // ), - ], - ), + scanFilter: scanFilter, + ); + } + + void _showScanFilterBottomSheet() { + showModalBottomSheet( + isScrollControlled: true, + context: context, + builder: (context) { + return ScanFilterWidget( + servicesFilterController: servicesFilterController, + namePrefixController: namePrefixController, + manufacturerDataController: manufacturerDataController, + onScanFilter: (ScanFilter? filter) { + setState(() { + scanFilter = filter; + }); + }, + ); + }, ); } @@ -172,9 +177,7 @@ class _MyAppState extends State { text: 'Connected Devices', onPressed: () async { List devices = - await UniversalBle.getSystemDevices( - withServices: _services, - ); + await UniversalBle.getSystemDevices(); if (devices.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( @@ -201,6 +204,10 @@ class _MyAppState extends State { }); }, ), + PlatformButton( + text: 'Scan Filters', + onPressed: _showScanFilterBottomSheet, + ), if (_bleDevices.isNotEmpty) PlatformButton( text: 'Clear List', diff --git a/example/lib/home/widgets/scan_filter_widget.dart b/example/lib/home/widgets/scan_filter_widget.dart new file mode 100644 index 0000000..898c5af --- /dev/null +++ b/example/lib/home/widgets/scan_filter_widget.dart @@ -0,0 +1,172 @@ +import 'package:flutter/material.dart'; +import 'package:universal_ble/universal_ble.dart'; +import 'package:universal_ble_example/widgets/platform_button.dart'; + +class ScanFilterWidget extends StatefulWidget { + final Function(ScanFilter? filter) onScanFilter; + final TextEditingController servicesFilterController; + final TextEditingController namePrefixController; + final TextEditingController manufacturerDataController; + + const ScanFilterWidget({ + super.key, + required this.onScanFilter, + required this.servicesFilterController, + required this.namePrefixController, + required this.manufacturerDataController, + }); + + @override + State createState() => _ScanFilterWidgetState(); +} + +class _ScanFilterWidgetState extends State { + String? error; + + void applyFilter() { + setState(() { + error = null; + }); + try { + List serviceUUids = []; + List namePrefixes = []; + List manufacturerDataFilters = []; + + // Parse Services + if (widget.servicesFilterController.text.isNotEmpty) { + List services = widget.servicesFilterController.text.split(','); + for (String service in services) { + try { + serviceUUids.add(BleUuid.parse(service.trim())); + } on FormatException catch (_) { + throw Exception("Invalid Service UUID $service"); + } + } + } + + // Parse Name Prefix + String namePrefix = widget.namePrefixController.text; + if (namePrefix.isNotEmpty) { + namePrefixes = namePrefix.split(',').map((e) => e.trim()).toList(); + } + + // Parse Manufacturer Data + String manufacturerDataText = widget.manufacturerDataController.text; + if (manufacturerDataText.isNotEmpty) { + List manufacturerData = manufacturerDataText.split(','); + for (String manufacturer in manufacturerData) { + int? companyIdentifier = int.tryParse(manufacturer); + if (companyIdentifier == null) { + throw Exception("Invalid Manufacturer Data $manufacturer"); + } + manufacturerDataFilters.add( + ManufacturerDataFilter(companyIdentifier: companyIdentifier), + ); + } + } + + if (serviceUUids.isEmpty && + namePrefixes.isEmpty && + manufacturerDataFilters.isEmpty) { + widget.onScanFilter(null); + } else { + widget.onScanFilter( + ScanFilter( + withServices: serviceUUids, + withNamePrefix: namePrefixes, + ), + ); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text("Filters Applied")), + ); + } + Navigator.pop(context); + } catch (e) { + setState(() { + error = e.toString(); + }); + } + } + + void clearFilter() { + widget.servicesFilterController.clear(); + widget.namePrefixController.clear(); + widget.manufacturerDataController.clear(); + widget.onScanFilter(null); + Navigator.pop(context); + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: MediaQuery.of(context).viewInsets.copyWith(left: 20, right: 20), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: 20), + Center( + child: Text( + "Scan Filters", + style: Theme.of(context).textTheme.titleLarge, + ), + ), + const Text("Use comma to add multiple values"), + const Divider(), + const SizedBox(height: 10), + TextFormField( + controller: widget.servicesFilterController, + maxLines: 2, + decoration: const InputDecoration( + labelText: "Services", + border: OutlineInputBorder(), + ), + ), + const SizedBox(height: 10), + TextFormField( + controller: widget.namePrefixController, + maxLines: 2, + decoration: const InputDecoration( + labelText: "Name Prefixes", + border: OutlineInputBorder(), + ), + ), + const SizedBox(height: 10), + TextFormField( + controller: widget.manufacturerDataController, + maxLines: 2, + decoration: const InputDecoration( + labelText: "Manufacturer Data Company IDs", + border: OutlineInputBorder(), + ), + ), + const SizedBox(height: 10), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Expanded( + child: PlatformButton( + text: 'Apply', + onPressed: applyFilter, + ), + ), + const SizedBox(width: 10), + Expanded( + child: PlatformButton( + text: 'Clear', + onPressed: clearFilter, + ), + ), + ], + ), + const SizedBox(height: 10), + if (error != null) + Text( + error!, + style: const TextStyle(color: Colors.red), + ), + const SizedBox(height: 10), + ], + ), + ); + } +} diff --git a/example/lib/peripheral_details/peripheral_detail_page.dart b/example/lib/peripheral_details/peripheral_detail_page.dart index 28b9644..58f0646 100644 --- a/example/lib/peripheral_details/peripheral_detail_page.dart +++ b/example/lib/peripheral_details/peripheral_detail_page.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:convert/convert.dart'; -import 'package:expandable/expandable.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:universal_ble/universal_ble.dart'; @@ -30,7 +29,6 @@ class _PeripheralDetailPageState extends State { bool isConnected = false; GlobalKey valueFormKey = GlobalKey(); List discoveredServices = []; - ExpandableController expandableController = ExpandableController(); final List _logs = []; final binaryCode = TextEditingController(); @@ -104,7 +102,7 @@ class _PeripheralDetailPageState extends State { if (kIsWeb) { _addLog("DiscoverServices", - '${services.length} services discovered,\nNote: Only services added in WebRequestOptionsBuilder will be discoverd'); + '${services.length} services discovered,\nNote: Only services added in ScanFilter will be discovered'); } } diff --git a/example/pubspec.lock b/example/pubspec.lock index 8efa046..52fde30 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -134,14 +134,6 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_adaptive_scaffold: - dependency: "direct main" - description: - name: flutter_adaptive_scaffold - sha256: "794791e6fc0cc23e375d07ea987e76cf7f0eb7cd753532e45dc35b7e90575e2d" - url: "https://pub.dev" - source: hosted - version: "0.1.11" flutter_driver: dependency: transitive description: flutter @@ -410,7 +402,7 @@ packages: path: ".." relative: true source: path - version: "0.10.0" + version: "0.11.0" vector_math: dependency: transitive description: @@ -461,4 +453,4 @@ packages: version: "6.5.0" sdks: dart: ">=3.4.0 <4.0.0" - flutter: ">=3.19.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index eb96111..8bb7697 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -9,12 +9,11 @@ environment: dependencies: flutter: sdk: flutter + convert: ^3.1.1 expandable: ^5.0.1 cupertino_icons: ^1.0.2 permission_handler: ^10.4.3 device_info_plus: ^9.0.2 - flutter_adaptive_scaffold: ^0.1.7+2 - convert: ^3.1.1 universal_ble: path: ../ diff --git a/example/web/index.html b/example/web/index.html index 312aac1..26f5da2 100644 --- a/example/web/index.html +++ b/example/web/index.html @@ -31,29 +31,8 @@ example - - - - - +