finishing touches for a working version

This commit is contained in:
YamiDoesDev 2023-01-08 02:50:52 +01:00
parent 079bc1bdad
commit f8a1c38540
9 changed files with 127 additions and 189 deletions

View File

@ -1,5 +1,5 @@
buildscript {
ext.kotlin_version = '1.6.10'
ext.kotlin_version = '1.7.20'
repositories {
google()
mavenCentral()

View File

@ -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(

View File

@ -21,6 +21,7 @@ class BluetoothObject {
late BluetoothConnection? _connection;
late Stream<Uint8List> _connectionStream;
late StreamSubscription<Uint8List> _connectionStreamSubscription;
late BuildContext _context;
final CloudServiceAPI _cloudServiceAPI = CloudServiceAPI();
@ -69,7 +70,7 @@ class BluetoothObject {
_connection = null;
}
Future<void> bondDevice() async {
Future<void> 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<void> _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<String> 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<void> sendData(String output) async {
Future<void> 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<void> _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<String>(
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: <Widget>[
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);
});
*/

View File

@ -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],

View File

@ -14,6 +14,7 @@ class BluetoothDeviceSettings extends StatefulWidget {
class BluetoothDeviceSettingsState extends State<BluetoothDeviceSettings> {
late BluetoothObject _bluetoothObject;
bool _isRegistering = false;
String _textInput = ""; // 0123456789 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ
@override
@ -49,8 +50,8 @@ class BluetoothDeviceSettingsState extends State<BluetoothDeviceSettings> {
),
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<BluetoothDeviceSettings> {
))
])),
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;
},
),
),
]),
]),
]),
));

View File

@ -54,21 +54,31 @@ class _BluetoothScreen extends State<BluetoothScreen> {
}
}
@override
void dispose() {
// Avoid memory leak (`setState` after dispose) and cancel discovery
_streamSubscription?.cancel();
_activeObject?.disconnectDevice();
super.dispose();
debugPrint("called dispose");
}
Future<void> _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<void> _disposeAsync() async {
await _streamSubscription?.cancel();
await _activeObject?.disconnectDevice();
List<BluetoothDevice> bondedDevices = await FlutterBluetoothSerial.instance.getBondedDevices();
debugPrint(bondedDevices.toString());
super.dispose();
debugPrint("called dispose");
}
Future<void> _enablePermissions() async {
PermissionStatus bluetoothScan = await Permission.bluetoothScan.request();
PermissionStatus bluetoothConnect = await Permission.bluetoothConnect.request();
@ -183,6 +193,7 @@ class _BluetoothScreen extends State<BluetoothScreen> {
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<BluetoothScreen> {
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<BluetoothScreen> {
),
],
),
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<BluetoothScreen> {
bluetoothObject: bluetoothObject,
onTap: () async {
await _toggleConnection(bluetoothObject);
await _restartDiscovery();
},
onLongPress: () async {
await bluetoothObject.bondDevice();
await bluetoothObject.bondDevice(context);
await _restartDiscovery();
},
);
},

View File

@ -124,7 +124,7 @@ class _RegisteredDevicesScreen extends State<RegisteredDevicesScreen> {
});
},
),
filled: true,
filled: false,
),
keyboardType: TextInputType.text,
onChanged: (String newValue) {
@ -151,13 +151,13 @@ class _RegisteredDevicesScreen extends State<RegisteredDevicesScreen> {
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: <Widget>[
TextButton(onPressed: () => Navigator.pop(context, 'Cancel'),
child: const Text('Cancel'),

View File

@ -85,6 +85,7 @@ class _SettingsState extends State<Settings> {
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<Settings> {
});
},
),
filled: true,
),
initialValue: Settings.password,
keyboardType: TextInputType.text,

View File

@ -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,
);