From f8a1c3854074e68dc6ad0fb61d742f3754fbdac9 Mon Sep 17 00:00:00 2001 From: YamiDoesDev Date: Sun, 8 Jan 2023 02:50:52 +0100 Subject: [PATCH] finishing touches for a working version --- android/build.gradle | 2 +- lib/objects/bluetooth_device_entry.dart | 4 +- lib/objects/bluetooth_object.dart | 150 ++++++--------------- lib/objects/cloud_service_api.dart | 2 +- lib/screens/bluetooth_device_settings.dart | 70 ++++++---- lib/screens/bluetooth_screen.dart | 66 ++++----- lib/screens/registered_devices_screen.dart | 16 +-- lib/screens/settings.dart | 2 +- lib/widgets/custom_list_tile.dart | 4 +- 9 files changed, 127 insertions(+), 189 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 83ae220..0582685 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.6.10' + ext.kotlin_version = '1.7.20' repositories { google() mavenCentral() diff --git a/lib/objects/bluetooth_device_entry.dart b/lib/objects/bluetooth_device_entry.dart index 3a3f11e..d1727bd 100644 --- a/lib/objects/bluetooth_device_entry.dart +++ b/lib/objects/bluetooth_device_entry.dart @@ -17,7 +17,9 @@ class BluetoothDeviceEntry extends ListTile { onTap: onTap, onLongPress: onLongPress, enabled: enabled, - leading: Icon(bluetoothObject.isConnected ? Icons.bluetooth_connected : Icons.bluetooth), + leading: bluetoothObject.isConnected + ? const Icon(Icons.bluetooth_connected, color: Colors.blue) + : const Icon(Icons.bluetooth), title: Text(bluetoothObject.name), subtitle: Text(bluetoothObject.address.toString()), trailing: Row( diff --git a/lib/objects/bluetooth_object.dart b/lib/objects/bluetooth_object.dart index ccd2693..9eef610 100644 --- a/lib/objects/bluetooth_object.dart +++ b/lib/objects/bluetooth_object.dart @@ -21,6 +21,7 @@ class BluetoothObject { late BluetoothConnection? _connection; late Stream _connectionStream; late StreamSubscription _connectionStreamSubscription; + late BuildContext _context; final CloudServiceAPI _cloudServiceAPI = CloudServiceAPI(); @@ -69,7 +70,7 @@ class BluetoothObject { _connection = null; } - Future bondDevice() async { + Future bondDevice(BuildContext context) async { try { bool bonded = false; if (_device.isBonded) { @@ -81,22 +82,7 @@ class BluetoothObject { bonded = (await FlutterBluetoothSerial.instance.bondDeviceAtAddress(_address))!; debugPrint('Bonding with $_address has ${bonded ? 'succed' : 'failed'}.'); } - /*setState(() { - _discoveryResults[_discoveryResults.indexOf( - result)] = - BluetoothDiscoveryResult( - device: BluetoothDevice( - name: device.name ?? '', - address: address, - type: device.type, - bondState: bonded - ? BluetoothBondState.bonded - : BluetoothBondState.none, - ), - rssi: result.rssi); - });*/ } catch (ex) { - /* showDialog( context: context, builder: (BuildContext context) { @@ -114,7 +100,7 @@ class BluetoothObject { ], ); }, - );*/ + ); } } @@ -133,12 +119,15 @@ class BluetoothObject { _connection = null; } - _connection = await BluetoothConnection.toAddress(_address); - debugPrint("Connected to the device"); - - _connectionStream = _connection!.input!; - _connectionStreamSubscription = _connectionStream.listen(_connectionOnListen); - _connectionStreamSubscription.onDone(_connectionOnDone); + try { + _connection = await BluetoothConnection.toAddress(_address); + debugPrint("Connected to the device"); + _connectionStream = _connection!.input!; + _connectionStreamSubscription = _connectionStream.listen(_connectionOnListen); + _connectionStreamSubscription.onDone(_connectionOnDone); + } catch (ex) { + debugPrint("$ex"); + } } void _connectionOnDone() { @@ -151,111 +140,58 @@ class BluetoothObject { Future _connectionOnListen(Uint8List data) async { final String dataDecoded = const AsciiDecoder().convert(data); - - debugPrint("received: $data"); debugPrint("received decoded: $dataDecoded"); - _messageBufferChars += dataDecoded; - if (data[data.length - 1] == 10) { - debugPrint("received buffer: $_messageBufferChars"); - int spaceIndex = _messageBufferChars.indexOf(" "); - String firstParameter = ""; - String secondParameter = ""; - try { - firstParameter = _messageBufferChars.substring(0, spaceIndex); - secondParameter = _messageBufferChars.substring(spaceIndex + 1, _messageBufferChars.length - 2); - } catch (ex) { - debugPrint(ex.toString()); - } - debugPrint("we still go on!"); + if (!(data[data.length - 1] == 10)) { + return; + } + if (!_messageBufferChars.contains(" ")) { _messageBufferChars = ""; - debugPrint("first: $firstParameter"); - debugPrint("second: $secondParameter"); - if (firstParameter == "fingerprint") { - debugPrint("received final buffer: $secondParameter"); - _primaryThumbprint = secondParameter; - debugPrint("{ _secondaryThumbprint: $_secondaryThumbprint }"); - debugPrint("{ id: $id, _primaryThumbprint: $_primaryThumbprint, _secondaryThumbprint: $_secondaryThumbprint }"); - await _registerDevice(); - } + return; } - /* - // Allocate buffer for parsed data - int backspacesCounter = 0; - for (var byte in data) { - if (byte == 8 || byte == 127) { - backspacesCounter++; - } + List input = _messageBufferChars.split(" "); + if (input[0] == "fingerprint") { + _primaryThumbprint = input[1].trim(); + debugPrint("_primaryThumbprint: ${const AsciiEncoder().convert(_primaryThumbprint)}"); + debugPrint("id: ${const AsciiEncoder().convert(id)}"); + debugPrint("_secondaryThumbprint: $_secondaryThumbprint"); + await _registerDevice(); } - Uint8List buffer = Uint8List(data.length - backspacesCounter); - int bufferIndex = buffer.length; - - // Apply backspace control character - backspacesCounter = 0; - for (int i = data.length - 1; i >= 0; i--) { - if (data[i] == 8 || data[i] == 127) { - backspacesCounter++; - } else { - if (backspacesCounter > 0) { - backspacesCounter--; - } else { - buffer[--bufferIndex] = data[i]; - } - } - } - - // Create message if there is new line character - String dataString = String.fromCharCodes(buffer); - int index = buffer.indexOf(13); - if (~index != 0) { - _messageBuffer = dataString.substring(index); - } else { - _messageBuffer = (backspacesCounter > 0 - ? _messageBuffer.substring(0, _messageBuffer.length - backspacesCounter) - : _messageBuffer + dataString); - } - */ + _messageBufferChars = ""; } - Future sendData(String output) async { + Future sendData(BuildContext context, String output) async { if (_connection == null) return; bool nameAvailable = await _cloudServiceAPI.checkNameAvailability(output); if (!nameAvailable) return; id = output; _connection!.output.add(Uint8List.fromList(const AsciiEncoder().convert("$output \r\n"))); await _connection!.output.allSent; + _context = context; debugPrint("sent: $output"); } Future _registerDevice() async { bool registered = false; registered = await _cloudServiceAPI.createDevice(id, _primaryThumbprint, ""); + _confirmRegistration(registered); + } - String statusText = - registered ? "das Gerät wurde erfolgreich registriert" : "das Gerät konnte nicht registriert werden"; - Fluttertoast.showToast( - msg: statusText, - toastLength: Toast.LENGTH_SHORT, - gravity: ToastGravity.BOTTOM, - timeInSecForIosWeb: 2, - backgroundColor: Colors.grey[200], - textColor: Colors.black, - fontSize: 16.0); + void _confirmRegistration(bool registered) { + showDialog( + context: _context, + builder: (BuildContext context) => AlertDialog( + title: const Text('Device Info'), + content: Text(registered ? "das Gerät wurde erfolgreich registriert" : "das Gerät konnte nicht registriert werden"), + backgroundColor: Colors.white, + actions: [ + TextButton(onPressed: () => Navigator.pop(context, 'Cancel'), + child: const Text('OK'), + ), + ], + ), + ); } } - -/*setState(() { - _discoveryResults[_discoveryResults.indexOf( - result)] = - BluetoothDiscoveryResult( - device: BluetoothDevice( - name: device.name ?? '', - address: address, - type: device.type, - isConnected: connected, - ), - rssi: result.rssi); - }); - */ diff --git a/lib/objects/cloud_service_api.dart b/lib/objects/cloud_service_api.dart index 61282e2..579d200 100644 --- a/lib/objects/cloud_service_api.dart +++ b/lib/objects/cloud_service_api.dart @@ -133,7 +133,7 @@ class CloudServiceAPI { String statusText = status ? "die eingegebene ID ist verfügbar" : "die eingegebene ID ist nicht verfügbar"; Fluttertoast.showToast( msg: statusText, - toastLength: Toast.LENGTH_SHORT, + toastLength: Toast.LENGTH_LONG, gravity: ToastGravity.BOTTOM, timeInSecForIosWeb: 2, backgroundColor: Colors.grey[200], diff --git a/lib/screens/bluetooth_device_settings.dart b/lib/screens/bluetooth_device_settings.dart index 6306d64..2896357 100644 --- a/lib/screens/bluetooth_device_settings.dart +++ b/lib/screens/bluetooth_device_settings.dart @@ -14,6 +14,7 @@ class BluetoothDeviceSettings extends StatefulWidget { class BluetoothDeviceSettingsState extends State { late BluetoothObject _bluetoothObject; + bool _isRegistering = false; String _textInput = ""; // 0123456789 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ @override @@ -49,8 +50,8 @@ class BluetoothDeviceSettingsState extends State { ), ListTile( title: const Text("Device Connection State"), - subtitle: - Text(_bluetoothObject.isConnected ? "ConnectionState.DISCONECTED" : "ConnectionState.CONNECED"), + subtitle: Text( + _bluetoothObject.isConnected ? "ConnectionState.DISCONNECTED" : "ConnectionState.CONNECTED"), ), ], ), @@ -81,33 +82,48 @@ class BluetoothDeviceSettingsState extends State { )) ])), const Divider(), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 0.0), - child: - SizedBox( - width: double.infinity, - child: - ElevatedButton( - onPressed: () async { - _bluetoothObject.sendData(_textInput); - }, - style: buttonStyle, - child: const Text("Send Data"), - ), - ) - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), - child: TextField( - decoration: const InputDecoration( - border: OutlineInputBorder(), - hintText: 'Enter the device name', + SingleSection(title: "Cloud Registration", children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 0.0), + child: TextFormField( + obscureText: false, + decoration: InputDecoration( + border: const OutlineInputBorder(), + hintText: 'Enter the device name', + labelText: "Device Name", + filled: false, + suffixIcon: _isRegistering + ? FittedBox( + child: Container( + height: 10, + width: 10, + margin: const EdgeInsets.all(8.0), + child: const CircularProgressIndicator( + strokeWidth: 2.0, + ), + ), + ) // : const I + : IconButton( + icon: const Icon(Icons.login), + onPressed: () async { + FocusScopeNode currentFocus = FocusScope.of(context); + currentFocus.unfocus(); + setState(() { + _isRegistering = true; + }); + await _bluetoothObject.sendData(context, _textInput); + setState(() { + _isRegistering = false; + }); + }, + )), + keyboardType: TextInputType.text, + onChanged: (String newValue) { + _textInput = newValue; + }, ), - onChanged: (String newValue) { - _textInput = newValue; - }, ), - ), + ]), ]), ]), )); diff --git a/lib/screens/bluetooth_screen.dart b/lib/screens/bluetooth_screen.dart index 469f5dc..7b00614 100644 --- a/lib/screens/bluetooth_screen.dart +++ b/lib/screens/bluetooth_screen.dart @@ -54,21 +54,31 @@ class _BluetoothScreen extends State { } } - @override - void dispose() { - // Avoid memory leak (`setState` after dispose) and cancel discovery - _streamSubscription?.cancel(); - _activeObject?.disconnectDevice(); - super.dispose(); - debugPrint("called dispose"); - } - Future _initAsync() async { await _enablePermissions(); await _enableBluetooth(); await _startDiscovery(); } + @override + void dispose() { + // Avoid memory leak (`setState` after dispose) and cancel discovery + debugPrint("called dispose"); + _disposeAsync(); + super.dispose(); + } + + Future _disposeAsync() async { + await _streamSubscription?.cancel(); + await _activeObject?.disconnectDevice(); + List bondedDevices = await FlutterBluetoothSerial.instance.getBondedDevices(); + debugPrint(bondedDevices.toString()); + super.dispose(); + debugPrint("called dispose"); + } + + + Future _enablePermissions() async { PermissionStatus bluetoothScan = await Permission.bluetoothScan.request(); PermissionStatus bluetoothConnect = await Permission.bluetoothConnect.request(); @@ -183,6 +193,7 @@ class _BluetoothScreen extends State { CustomListTile( title: _enabledBluetooth ? "Bluetooth Enabled" : "Please Enable Bluetooth (click me)", icon: _enabledBluetooth ? Icons.check_circle_outline_rounded : Icons.info_outline_rounded, + iconColor: _enabledPermissions ? Colors.green : Colors.red, onTap: () async { await _enableBluetooth(); }, @@ -190,6 +201,7 @@ class _BluetoothScreen extends State { CustomListTile( title: _enabledPermissions ? "Permissions Granted" : "Please Grant Permissions (click me)", icon: _enabledPermissions ? Icons.check_circle_outline_rounded : Icons.info_outline_rounded, + iconColor: _enabledPermissions ? Colors.green : Colors.red, onTap: () async { await _enablePermissions(); }, @@ -203,38 +215,6 @@ class _BluetoothScreen extends State { ), ], ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - // const EdgeInsets defaultContentPadding = EdgeInsets.symmetric(horizontal: 16.0); - Expanded( - child: ElevatedButton( - onPressed: () async { - /*await Permission.bluetoothConnect.request(); - await Permission.bluetoothScan.request(); - if (await Permission.bluetoothScan.request().isGranted) { - // Either the permission was already granted before or the user just granted it. - debugPrint("Location Permission is granted"); - } else { - debugPrint("Location Permission is denied."); - }*/ - }, - style: buttonStyle, - child: const Text("Get BT Permissions"), - ), - ), - const SizedBox(width: 16.0), - Expanded( - child: ElevatedButton( - onPressed: () async { - _restartDiscovery(); - }, - style: buttonStyle, - child: const Text("Restart Scan"), - ), - ) - ]), - ), const Divider(), SingleSection( title: "Bluetooth Devices", @@ -262,9 +242,11 @@ class _BluetoothScreen extends State { bluetoothObject: bluetoothObject, onTap: () async { await _toggleConnection(bluetoothObject); + await _restartDiscovery(); }, onLongPress: () async { - await bluetoothObject.bondDevice(); + await bluetoothObject.bondDevice(context); + await _restartDiscovery(); }, ); }, diff --git a/lib/screens/registered_devices_screen.dart b/lib/screens/registered_devices_screen.dart index 6672062..7037b17 100644 --- a/lib/screens/registered_devices_screen.dart +++ b/lib/screens/registered_devices_screen.dart @@ -124,7 +124,7 @@ class _RegisteredDevicesScreen extends State { }); }, ), - filled: true, + filled: false, ), keyboardType: TextInputType.text, onChanged: (String newValue) { @@ -151,13 +151,13 @@ class _RegisteredDevicesScreen extends State { context: context, builder: (BuildContext context) => AlertDialog( title: const Text('Device Info'), - content: Text("${entry.id} " - "\nentrypoint: ${entry.endpoint} " - "\nstatus: ${entry.status} " - "\nconnection: ${entry.connectionState} " - "\nlast activity: ${entry.lastActivityTime}" - "\nprimaryThumbprint: ${entry.primaryThumbprint}"), - backgroundColor: Colors.white70, + content: Text("ID: ${entry.id} " + "\n\nentrypoint: ${entry.endpoint} " + "\n\nstatus: ${entry.status} " + "\n\nconnection: ${entry.connectionState} " + "\n\nlast activity: ${entry.lastActivityTime}" + "\n\nprimaryThumbprint: ${entry.primaryThumbprint}"), + backgroundColor: Colors.white, actions: [ TextButton(onPressed: () => Navigator.pop(context, 'Cancel'), child: const Text('Cancel'), diff --git a/lib/screens/settings.dart b/lib/screens/settings.dart index ccf9f23..86ed486 100644 --- a/lib/screens/settings.dart +++ b/lib/screens/settings.dart @@ -85,6 +85,7 @@ class _SettingsState extends State { border: const OutlineInputBorder(), hintText: 'Enter the server password', labelText: "Password", + filled: false, suffixIcon: IconButton( icon: Icon(_passwordVisible ? Icons.visibility : Icons.visibility_off), onPressed: () { @@ -93,7 +94,6 @@ class _SettingsState extends State { }); }, ), - filled: true, ), initialValue: Settings.password, keyboardType: TextInputType.text, diff --git a/lib/widgets/custom_list_tile.dart b/lib/widgets/custom_list_tile.dart index ec38f58..a79c49c 100644 --- a/lib/widgets/custom_list_tile.dart +++ b/lib/widgets/custom_list_tile.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; class CustomListTile extends StatelessWidget { final String title; final IconData icon; + final Color? iconColor; final VoidCallback? onTap; //final Widget? trailing; @@ -11,6 +12,7 @@ class CustomListTile extends StatelessWidget { Key? key, required this.title, required this.icon, + this.iconColor, this.onTap, //this.trailing, }) : super(key: key); @@ -19,7 +21,7 @@ class CustomListTile extends StatelessWidget { Widget build(BuildContext context) { return ListTile( title: Text(title), - leading: Icon(icon), + leading: Icon(icon, color: iconColor), onTap: onTap, //trailing: trailing, );