Files
AtomMatrixApp/lib/connectPage.dart
2024-08-29 21:16:30 +02:00

369 lines
12 KiB
Dart

import 'dart:async';
import 'package:bluetooth_classic/bluetooth_classic.dart';
import 'package:bluetooth_classic/models/device.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:provider/provider.dart';
import 'package:xapk_installer/AppStateModel.dart';
import 'package:xapk_installer/BluetoothUtils.dart';
import 'package:xapk_installer/ConnectionScreenDialogs.dart';
import 'package:xapk_installer/CustomBluetoothService.dart';
import 'NoDeviceScreen.dart';
class ConnectPage extends StatefulWidget {
const ConnectPage({super.key});
@override
State<ConnectPage> createState() => _ConnectPageState();
}
class _ConnectPageState extends State<ConnectPage> {
final _bluetoothClassicPlugin = Custombluetoothservice();
//a bool to hide the no devices found screen when the user presses the scan button
bool _hideNoDevicesScreen = false;
bool _scanning = false;
Icon _statusIcon = const Icon(Icons.restart_alt);
int _deviceStatus = Device.disconnected;
Device _connectedDevice = Device(name: 'Unknown', address: 'Unknown');
StreamSubscription? deviceListen;
StreamSubscription? deviceChangeListen;
//Init
@override
void initState() {
super.initState();
_bluetoothClassicPlugin.initPermissions();
_bluetoothClassicPlugin.onDeviceStatusChanged().listen((event) {
setState(() {
Provider.of<AppStateModel>(context, listen: false).setDeviceStatus(event);
_deviceStatus = event;
});
});
_bluetoothClassicPlugin.onDeviceDiscovered().listen(
(event) {
if (kDebugMode) {
print('Device discovered: ${event.name}');
}
if (_scanning) {
Provider.of<AppStateModel>(context, listen: false)
.addDiscoveredDevice(event);
//Sort list put Unknowns at the end
Provider.of<AppStateModel>(context, listen: false)
.discoveredDevices
.sort((a, b) {
if (a.name == null) {
return 1;
}
if (b.name == null) {
return -1;
}
return a.name!.compareTo(b.name!);
});
}
},
);
}
//Dispose
@override
void dispose() {
super.dispose();
// Provider.of<AppStateModel>(context, listen: false).listen?.cancel();
}
Future<void> _scan() async {
if (_scanning) {
await _bluetoothClassicPlugin.stopScan();
if (kDebugMode) {
print('Scanning stopped');
}
setState(() {
_scanning = false;
_statusIcon = const Icon(Icons.restart_alt);
});
} else {
setState(() {
Provider.of<AppStateModel>(context, listen: false).clearDiscoveredDevices();
_hideNoDevicesScreen = true;
_statusIcon = const Icon(Icons.cancel);
});
await _bluetoothClassicPlugin.startScan();
if (kDebugMode) {
print('Scanning started');
}
setState(() {
_scanning = true;
});
}
}
Future<void> _stopScan() async {
await _bluetoothClassicPlugin.stopScan();
if (kDebugMode) {
print('Scanning stopped');
}
await _bluetoothClassicPlugin.disconnect();
setState(() {
_scanning = false;
_statusIcon = const Icon(Icons.restart_alt);
});
}
Future<bool?> _showConnectConfirmDialog(
BuildContext context, String deviceName, String deviceAddress) {
return showDialog<bool?>(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Connect'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text('Do you want to connect to this device?'),
Container(height: 10),
Text(deviceName,
style: const TextStyle(
fontWeight: FontWeight.bold,
decoration: TextDecoration.underline,
fontSize: 20)),
Text(deviceAddress, style: const TextStyle(fontSize: 12)),
],
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop(false);
},
child: const Text('No')),
TextButton(
onPressed: () {
Navigator.of(context).pop(true);
},
child: const Text('Yes')),
],
);
});
}
Future<void> _connect(Device device) async {
final connect = await _showConnectConfirmDialog(
context, device.name ?? 'Unknown', device.address);
if (connect == null || !connect) {
return;
}
bool connected = false;
bool error = false;
List<Device> alreadyConnectedDevices = [];
try {
alreadyConnectedDevices =
await _bluetoothClassicPlugin.getPairedDevices();
} catch (e) {
error = true;
}
if(error){
if (context.mounted) {
if (kDebugMode) {
print('Failed to get paired devices');
}
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Failed to get paired devices'),
content: const Text('Failed to get paired devices'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('OK'))
],
);
});
return;
}
}
// //If the device is already connected, tell the user
// if (alreadyConnectedDevices
// .any((element) => element.address == device.address)) {
// if (context.mounted) {
// if (kDebugMode) {
// print('Device already connected');
// }
// showAlreadyConnectedDialog(context);
// return;
// }
// }
if(_deviceStatus == Device.connected){
if (context.mounted) {
if (kDebugMode) {
print('Device already connected');
}
showAlreadyConnectedDialog(context);
return;
}
}
try {
connected = await _bluetoothClassicPlugin.connect(
device.address, "00001101-0000-1000-8000-00805f9b34fb");
} catch (e) {
error = true;
}
if (error || !connected) {
if (context.mounted) {
if (kDebugMode) {
print('Failed to connect');
}
}
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Failed to connect'),
content: const Text('Failed to connect to the device'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('OK'))
],
);
});
return;
}
setState(() {
_statusIcon = const Icon(Icons.restart_alt);
_connectedDevice = device;
});
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Connected'),
content: const Text('You are now connected to the device'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('OK'))
],
);
});
await _bluetoothClassicPlugin.write("setPix 1 1 255 0 0\n"); //TODO: remove this
}
Future<void> _disconnect() async {
await _bluetoothClassicPlugin.disconnect();
setState(() {
_statusIcon = const Icon(Icons.restart_alt);
_connectedDevice = Device(name: 'Unknown', address: 'Unknown');
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Connect Page'),
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
Navigator.of(context).pop();
},
),
actions: [
IconButton(
icon: _statusIcon,
onPressed: () {
_scan();
},
),
IconButton(
onPressed: () {
_stopScan();
Provider.of<AppStateModel>(context, listen: false)
.clearDiscoveredDevices();
},
icon: const Icon(Icons.star_purple500_outlined))
],
),
body: !_hideNoDevicesScreen
? NoDevicesScreen(onScanPressed: () {
setState(() {
_hideNoDevicesScreen = true;
});
_scan();
})
: Padding(
padding: const EdgeInsets.all(4.0),
child: Consumer<AppStateModel>(
builder: (context, appstate, child) {
final List<Device> _discoveredDevices =
appstate.discoveredDevices;
return ListView.builder(itemBuilder: (context, index) {
if (index >= _discoveredDevices.length) {
return null;
}
return ListTile(
title: Text(_discoveredDevices[index].name ?? 'Unknown', style: TextStyle(
fontWeight: _discoveredDevices[index].name == _connectedDevice.name ? FontWeight.bold : FontWeight.normal,
color: _discoveredDevices[index].name == _connectedDevice.name ? Colors.green : Colors.white
),),
subtitle: Text(_discoveredDevices[index].address),
leading: _discoveredDevices[index].name == null ? const Icon(Icons.question_mark) : const Icon(
Icons.bluetooth,
color: Colors.blueAccent,
),
style: ListTileStyle.list,
onTap: () async {
if(_discoveredDevices[index].name == _connectedDevice.name){
await _disconnect();
} else {
await _connect(_discoveredDevices[index]);
}
},
onLongPress: () async {
// if (isAlreadyConnectedToDevice(_discoveredDevices[index],
// await _bluetoothClassicPlugin.getPairedDevices())) {
// _bluetoothClassicPlugin.disconnect();
// }
Fluttertoast.showToast(
msg: Provider.of<AppStateModel>(context, listen: false).deviceStatus.toString(),
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 1,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 16.0
);
},
);
});
}
),
),
);
}
}