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 createState() => _ConnectPageState(); } class _ConnectPageState extends State { 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(context, listen: false).setDeviceStatus(event); _deviceStatus = event; }); }); _bluetoothClassicPlugin.onDeviceDiscovered().listen( (event) { if (kDebugMode) { print('Device discovered: ${event.name}'); } if (_scanning) { Provider.of(context, listen: false) .addDiscoveredDevice(event); //Sort list put Unknowns at the end Provider.of(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(context, listen: false).listen?.cancel(); } Future _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(context, listen: false).clearDiscoveredDevices(); _hideNoDevicesScreen = true; _statusIcon = const Icon(Icons.cancel); }); await _bluetoothClassicPlugin.startScan(); if (kDebugMode) { print('Scanning started'); } setState(() { _scanning = true; }); } } Future _stopScan() async { await _bluetoothClassicPlugin.stopScan(); if (kDebugMode) { print('Scanning stopped'); } await _bluetoothClassicPlugin.disconnect(); setState(() { _scanning = false; _statusIcon = const Icon(Icons.restart_alt); }); } Future _showConnectConfirmDialog( BuildContext context, String deviceName, String deviceAddress) { return showDialog( 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 _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 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 _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(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( builder: (context, appstate, child) { final List _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(context, listen: false).deviceStatus.toString(), toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.CENTER, timeInSecForIosWeb: 1, backgroundColor: Colors.red, textColor: Colors.white, fontSize: 16.0 ); }, ); }); } ), ), ); } }