Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 28 additions & 16 deletions example/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
## 1.2.0
* Improved scan button visibility - converted to prominent filled button with text label
* Enhanced "no devices found" state with explicit "Start Scan" call-to-action button
* Moved search field to app bar header for better accessibility
* Moved queue type settings to drawer menu as expandable section
* Added tooltip to Bluetooth availability icon (tap to view on mobile)
* Display company name based on company identifier from manufacturer data
* Enhanced search functionality - now supports searching by company name
* Improved overall UI layout and navigation flow

## 1.1.0
* Add support for `autoConnect` parameter
* Display RSSI values in device details
* Persist filters
* Fix clear log button
* Move "Copy Services" button to Services panel header
* Enhance services format to be more detailed and human-readable
* **Services & Characteristics:**
* Add property filtering for characteristics with visual filter chips
* Add navigation buttons to navigate between characteristics (previous/next)
* Improve service sorting (favorites first, system services last)
* Enhance services list UI with better filtering and navigation
* Improve format for discovered services to be more detailed and human-readable
* Move "Copy Services" button to Services panel header

* **Company & Manufacturer Data:**
* Display company name based on company identifier from manufacturer data
* Show and filter by company name in device list
* Enhanced search functionality - now supports searching by company name

* **Scanning & Device Discovery:**
* Improved scan button visibility - converted to prominent filled button with text label
* Enhanced "no devices found" state with explicit "Start Scan" call-to-action button
* Display RSSI values in device details

* **UI & Navigation:**
* Moved search field to app bar header for better accessibility
* Moved queue type settings to drawer menu as expandable section
* Added tooltip to Bluetooth availability icon (tap to view on mobile)
* Improved overall UI layout and navigation flow

* **Functionality:**
* Add support for `autoConnect` parameter
* Persist filters across app sessions
* Fix clear log button functionality

## 1.0.0
* Initial release
97 changes: 97 additions & 0 deletions example/lib/data/utils.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,104 @@
import 'package:universal_ble/universal_ble.dart';

bool isSystemService(String uuid) {
final normalized = uuid.toUpperCase().replaceAll('-', '');
return normalized == '00001800' ||
normalized == '00001801' ||
normalized == '0000180A' ||
normalized.startsWith('000018');
}

/// Sorts BLE services with the following priority:
/// 1. Favorite services first
/// 2. System services last
/// 3. Other services in between
List<BleService> sortBleServices(
List<BleService> services, {
Set<String>? favoriteServices,
}) {
final sortedServices = List<BleService>.from(services);
sortedServices.sort((a, b) {
final aIsFavorite = favoriteServices?.contains(a.uuid) ?? false;
final bIsFavorite = favoriteServices?.contains(b.uuid) ?? false;
if (aIsFavorite != bIsFavorite) {
return aIsFavorite ? -1 : 1;
}
final aIsSystem = isSystemService(a.uuid);
final bIsSystem = isSystemService(b.uuid);
if (aIsSystem != bIsSystem) {
return aIsSystem ? 1 : -1;
}
return 0;
});
return sortedServices;
}

/// Returns a list of all filtered characteristics with their parent services.
/// Services are sorted (favorites first, system services last).
/// Characteristics are filtered by property filters if provided.
List<({BleService service, BleCharacteristic characteristic})>
getFilteredBleCharacteristics(
List<BleService> services, {
Set<String>? favoriteServices,
Set<CharacteristicProperty>? propertyFilters,
}) {
final List<({BleService service, BleCharacteristic characteristic})> result =
[];

// Sort services: favorites first, then system services, then others
final sortedServices = sortBleServices(
services,
favoriteServices: favoriteServices,
);

for (var service in sortedServices) {
for (var char in service.characteristics) {
// Filter by properties if filters are selected
if (propertyFilters != null && propertyFilters.isNotEmpty) {
if (char.properties.any((prop) => propertyFilters.contains(prop))) {
result.add((service: service, characteristic: char));
}
} else {
result.add((service: service, characteristic: char));
}
}
}
return result;
}

/// Finds the next or previous characteristic in a filtered list.
///
/// [filtered] - The filtered list of (service, characteristic) tuples
/// [currentCharacteristicUuid] - The UUID of the currently selected characteristic
/// [next] - If true, finds the next item; if false, finds the previous item
///
/// Returns the next/previous item, or the first item if current is not found,
/// or null if the list is empty.
({BleService service, BleCharacteristic characteristic})?
navigateToAdjacentCharacteristic(
List<({BleService service, BleCharacteristic characteristic})> filtered,
String currentCharacteristicUuid,
bool next,
) {
if (filtered.isEmpty) return null;

final currentIndex = filtered.indexWhere(
(item) => item.characteristic.uuid == currentCharacteristicUuid,
);

if (currentIndex == -1) {
// Current selection not in filtered list, return first
return filtered.first;
}

if (next) {
// Navigate to next (with wrapping)
final nextIndex = (currentIndex + 1) % filtered.length;
return filtered[nextIndex];
} else {
// Navigate to previous (with wrapping)
final previousIndex =
currentIndex > 0 ? currentIndex - 1 : filtered.length - 1;
return filtered[previousIndex];
}
}
27 changes: 26 additions & 1 deletion example/lib/home/scanner_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,17 @@ class _ScannerScreenState extends State<ScannerScreen> {
(isScanning) => setState(() => _isScanning = isScanning),
);

_loadScanFilters();
// Get initial Bluetooth availability state
UniversalBle.getBluetoothAvailabilityState().then((state) {
if (mounted) {
setState(() => bleAvailabilityState = state);
}
});

_loadScanFilters().then((_) {
// Auto-start scanning after filters are loaded
_tryAutoStartScan();
});

// Load company identifiers in the background
CompanyIdentifierService.instance.load();
Expand Down Expand Up @@ -305,6 +315,17 @@ class _ScannerScreenState extends State<ScannerScreen> {
bool get _isBluetoothAvailable =>
bleAvailabilityState == AvailabilityState.poweredOn;

Future<void> _tryAutoStartScan() async {
// Only auto-start if Bluetooth is available and not already scanning
if (_isBluetoothAvailable && !_isScanning) {
// Check again to make sure we're not already scanning
final isScanning = await UniversalBle.isScanning();
if (!isScanning && mounted) {
await _startScan();
}
}
}

String _getBluetoothAvailabilityTooltip() {
switch (bleAvailabilityState) {
case AvailabilityState.poweredOn:
Expand Down Expand Up @@ -410,6 +431,10 @@ class _ScannerScreenState extends State<ScannerScreen> {
triggerMode: TooltipTriggerMode.tap,
child: BleAvailabilityIcon(onAvailabilityStateChanged: (state) {
setState(() => bleAvailabilityState = state);
// Auto-start scanning when Bluetooth becomes available
if (state == AvailabilityState.poweredOn) {
_tryAutoStartScan();
}
}),
),
],
Expand Down
Loading