brux88_beacon 0.1.5
brux88_beacon: ^0.1.5 copied to clipboard
A Flutter plugin for handling BLE beacons with background detection and reliable monitoring.
// example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:brux88_beacon/brux88_beacon.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Brux88 Beacon Example',
theme: ThemeData(
primarySwatch: Colors.blue,
useMaterial3: true,
),
home: BeaconControlPage(),
);
}
}
class BeaconControlPage extends StatefulWidget {
@override
_BeaconControlPageState createState() => _BeaconControlPageState();
}
class _BeaconControlPageState extends State<BeaconControlPage> {
final BeaconManager _beaconManager = BeaconManager();
bool _isInitialized = false;
bool _isForegroundMonitoringRunning = false;
bool _isBackgroundServiceRunning = false;
bool _isBackgroundServiceEnabled = false;
List<Beacon> _detectedBeacons = [];
MonitoringState _monitoringState = MonitoringState.unknown;
List<String> _logs = [];
bool _autoRestartEnabled = false;
@override
void initState() {
super.initState();
_initializeBeaconManager();
_setupListeners();
}
Future<void> _initializeBeaconManager() async {
try {
final initialized = await _beaconManager.initialize();
setState(() {
_isInitialized = initialized;
});
if (initialized) {
await _beaconManager.disableAllAutoRestart();
// Controlla lo stato attuale
final autoRestartStatus = await _beaconManager.isAutoRestartEnabled();
setState(() {
_isInitialized = initialized;
_autoRestartEnabled = autoRestartStatus;
});
await _updateStatus();
_showSnackBar(
'Beacon Manager inizializzato con successo', Colors.green);
} else {
_showSnackBar(
'Errore nell\'inizializzazione del Beacon Manager', Colors.red);
}
} catch (e) {
_showSnackBar('Errore: $e', Colors.red);
}
}
void _setupListeners() {
// Listener per i beacon rilevati
_beaconManager.beacons.listen((beacons) {
setState(() {
_detectedBeacons = beacons;
});
});
// Listener per lo stato di monitoraggio
_beaconManager.monitoringState.listen((state) {
setState(() {
_monitoringState = state;
});
});
}
Future<void> _toggleAutoRestart() async {
final newValue = !_autoRestartEnabled;
final success = await _beaconManager.setAutoRestartEnabled(newValue);
if (success) {
setState(() {
_autoRestartEnabled = newValue;
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content:
Text('Auto-restart ${newValue ? "abilitato" : "disabilitato"}'),
),
);
}
}
Future<void> _updateStatus() async {
try {
final isForegroundRunning =
await _beaconManager.isForegroundMonitoringRunning();
final isBackgroundRunning =
await _beaconManager.isBackgroundServiceRunning();
final isBackgroundEnabled =
await _beaconManager.isBackgroundServiceEnabled();
setState(() {
_isForegroundMonitoringRunning = isForegroundRunning;
_isBackgroundServiceRunning = isBackgroundRunning;
_isBackgroundServiceEnabled = isBackgroundEnabled;
});
// Log dello stato per debug
print('Status Update:');
print(' Foreground Monitoring: $isForegroundRunning');
print(' Background Service Running: $isBackgroundRunning');
print(' Background Service Enabled: $isBackgroundEnabled');
} catch (e) {
_showSnackBar('Errore nell\'aggiornamento dello stato: $e', Colors.red);
}
}
// ===== CONTROLLI FOREGROUND =====
Future<void> _startForegroundMonitoring() async {
try {
// Controlla i permessi prima
final permissionsMap = await _beaconManager.checkPermissions();
final locationGranted = permissionsMap['location'] ?? false;
if (!locationGranted) {
await _beaconManager.requestPermissions();
}
final success = await _beaconManager.startForegroundMonitoringOnly();
//_setupListeners();
if (success) {
_showSnackBar('Monitoraggio foreground avviato', Colors.green);
await _updateStatus();
} else {
_showSnackBar(
'Errore nell\'avvio del monitoraggio foreground', Colors.red);
}
} catch (e) {
print('Error starting foreground monitoring: $e');
_showSnackBar('Errore: $e', Colors.red);
}
}
Future<void> _stopForegroundMonitoring() async {
try {
final success = await _beaconManager.stopForegroundMonitoringOnly();
if (success) {
_showSnackBar('Monitoraggio foreground arrestato', Colors.orange);
await _updateStatus();
} else {
_showSnackBar(
'Errore nell\'arresto del monitoraggio foreground', Colors.red);
}
} catch (e) {
_showSnackBar('Errore: $e', Colors.red);
}
}
// ===== CONTROLLI BACKGROUND =====
Future<void> _startBackgroundService() async {
try {
// Opzionalmente, abilita il watchdog solo quando l'utente avvia il servizio
// await _beaconManager.setWatchdogEnabled(true);
// Prima controlla i permessi attuali
final permissionsMap = await _beaconManager.checkPermissions();
print('Current permissions: $permissionsMap');
// Verifica se i permessi essenziali sono concessi
final locationGranted = permissionsMap['location'] ?? false;
final bluetoothScanGranted = permissionsMap['bluetoothScan'] ?? false;
final bluetoothConnectGranted =
permissionsMap['bluetoothConnect'] ?? false;
if (!locationGranted ||
!bluetoothScanGranted ||
!bluetoothConnectGranted) {
_showSnackBar(
'Permessi mancanti. Concedi i permessi nelle impostazioni dell\'app.',
Colors.orange);
// Prova comunque a richiedere i permessi
try {
final permissionsGranted = await _beaconManager.requestPermissions();
if (!permissionsGranted) {
_showSnackBar(
'Permessi non concessi. Impossibile avviare il servizio.',
Colors.red);
return;
}
} catch (e) {
print('Error requesting permissions: $e');
_showSnackBar('Errore nella richiesta dei permessi: $e', Colors.red);
return;
}
}
// Verifica se il Bluetooth è abilitato
final bluetoothEnabled = await _beaconManager.isBluetoothEnabled();
if (!bluetoothEnabled) {
_showSnackBar(
'Bluetooth non abilitato. Abilita il Bluetooth per continuare.',
Colors.red);
return;
}
// Verifica se la localizzazione è abilitata
final locationEnabled = await _beaconManager.isLocationEnabled();
if (!locationEnabled) {
_showSnackBar(
'Localizzazione non abilitata. Abilita la localizzazione per continuare.',
Colors.red);
return;
}
// Verifica se la batteria è ottimizzata
final isBatteryOptimized =
await _beaconManager.isBatteryOptimizationIgnored();
if (!isBatteryOptimized) {
_showSnackBar(
'Raccomandato: disabilita l\'ottimizzazione batteria per prestazioni migliori',
Colors.orange);
await _beaconManager.requestIgnoreBatteryOptimization();
}
// Avvia il servizio background
final success = await _beaconManager.startBackgroundService();
if (success) {
_showSnackBar('Servizio background avviato', Colors.green);
await _updateStatus();
} else {
_showSnackBar('Errore nell\'avvio del servizio background', Colors.red);
}
} catch (e) {
print('Error starting background service: $e');
_showSnackBar('Errore: $e', Colors.red);
}
}
Future<void> _stopBackgroundService() async {
try {
await _beaconManager.setWatchdogEnabled(false);
final success = await _beaconManager.stopBackgroundService();
if (success) {
_showSnackBar('Servizio background arrestato', Colors.orange);
await _updateStatus();
} else {
_showSnackBar(
'Errore nell\'arresto del servizio background', Colors.red);
}
} catch (e) {
_showSnackBar('Errore: $e', Colors.red);
}
}
Future<void> _restartBackgroundService() async {
try {
final success = await _beaconManager.restartBackgroundService();
if (success) {
_showSnackBar('Servizio background riavviato', Colors.blue);
await _updateStatus();
} else {
_showSnackBar('Errore nel riavvio del servizio background', Colors.red);
}
} catch (e) {
_showSnackBar('Errore: $e', Colors.red);
}
}
// ===== CONTROLLI COMPLETI =====
Future<void> _startCompleteMonitoring() async {
try {
await _beaconManager.requestPermissions();
final success = await _beaconManager.startCompleteMonitoring();
if (success) {
_showSnackBar('Monitoraggio completo avviato (foreground + background)',
Colors.blue);
await _updateStatus();
} else {
_showSnackBar(
'Errore nell\'avvio del monitoraggio completo', Colors.red);
}
} catch (e) {
_showSnackBar('Errore: $e', Colors.red);
}
}
Future<void> _stopCompleteMonitoring() async {
try {
final success = await _beaconManager.stopCompleteMonitoring();
if (success) {
_showSnackBar('Monitoraggio completo arrestato', Colors.orange);
await _updateStatus();
} else {
_showSnackBar(
'Errore nell\'arresto del monitoraggio completo', Colors.red);
}
} catch (e) {
_showSnackBar('Errore: $e', Colors.red);
}
}
// ===== UTILITY E DIAGNOSTICA =====
Future<void> _checkSystemRequirements() async {
try {
// Controlla i requisiti di sistema usando il nuovo metodo dettagliato
final bluetoothEnabled = await _beaconManager.isBluetoothEnabled();
final locationEnabled = await _beaconManager.isLocationEnabled();
final detailedPermissions = await _beaconManager.getDetailedPermissions();
String message = 'Stato Sistema Completo:\n\n';
// Stato servizi di sistema
message += '📶 Sistema:\n';
message +=
'Bluetooth: ${bluetoothEnabled ? "✅ Attivo" : "❌ Disattivo"}\n';
message +=
'Localizzazione: ${locationEnabled ? "✅ Attiva" : "❌ Disattiva"}\n';
message +=
'Android: ${detailedPermissions['androidVersion'] ?? "N/A"}\n\n';
// Permessi
message += '🔐 Permessi:\n';
final locationPerm = detailedPermissions['location'] as bool? ?? false;
final bluetoothScan =
detailedPermissions['bluetoothScan'] as bool? ?? false;
final bluetoothConnect =
detailedPermissions['bluetoothConnect'] as bool? ?? false;
final notifications =
detailedPermissions['notifications'] as bool? ?? false;
final backgroundLocation =
detailedPermissions['backgroundLocation'] as bool? ?? false;
message += 'Localizzazione: ${locationPerm ? "✅" : "❌"}\n';
message +=
'Localizzazione Background: ${backgroundLocation ? "✅" : "❌"}\n';
message += 'Bluetooth Scan: ${bluetoothScan ? "✅" : "❌"}\n';
message += 'Bluetooth Connect: ${bluetoothConnect ? "✅" : "❌"}\n';
message += 'Notifiche: ${notifications ? "✅" : "❌"}\n\n';
// Ottimizzazioni
message += '🔋 Ottimizzazioni:\n';
final batteryOptIgnored =
detailedPermissions['batteryOptimizationIgnored'] as bool? ?? false;
final canScheduleAlarms =
detailedPermissions['canScheduleExactAlarms'] as bool? ?? false;
message += 'Batteria non ottimizzata: ${batteryOptIgnored ? "✅" : "❌"}\n';
message += 'Allarmi esatti: ${canScheduleAlarms ? "✅" : "❌"}\n\n';
// Stato servizi
message += '⚙️ Servizi:\n';
message +=
'Background Service: ${_isBackgroundServiceRunning ? "✅ Attivo" : "❌ Inattivo"}\n';
message +=
'Auto-start Background: ${_isBackgroundServiceEnabled ? "✅ Abilitato" : "❌ Disabilitato"}\n';
message +=
'Monitoring Foreground: ${_isForegroundMonitoringRunning ? "✅ Attivo" : "❌ Inattivo"}\n';
// Raccomandazioni
if (!batteryOptIgnored || !backgroundLocation || !locationPerm) {
message += '\n⚠️ Raccomandazioni:\n';
if (!batteryOptIgnored) {
message += '• Disabilita ottimizzazione batteria\n';
}
if (!backgroundLocation) {
message += '• Concedi permesso localizzazione background\n';
}
if (!locationPerm) {
message += '• Concedi permesso localizzazione\n';
}
}
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Row(
children: [
Icon(Icons.info_outline, color: Colors.blue),
SizedBox(width: 8),
Text('Diagnostica Sistema'),
],
),
content: SingleChildScrollView(
child: Text(
message,
style: TextStyle(fontFamily: 'monospace', fontSize: 12),
),
),
actions: [
if (!batteryOptIgnored)
TextButton(
onPressed: () async {
Navigator.pop(context);
await _beaconManager.requestIgnoreBatteryOptimization();
},
child: Text('Ottimizza Batteria'),
),
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('Chiudi'),
),
],
),
);
} catch (e) {
_showSnackBar('Errore nel controllo requisiti: $e', Colors.red);
}
}
Future<void> _getLogs() async {
try {
final logs = await _beaconManager.getLogs();
setState(() {
_logs = logs;
});
_showLogsDialog();
} catch (e) {
_showSnackBar('Errore nel recupero dei log: $e', Colors.red);
}
}
void _showLogsDialog() {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Log del sistema'),
content: Container(
width: double.maxFinite,
height: 400,
child: ListView.builder(
itemCount: _logs.length,
itemBuilder: (context, index) {
return Card(
margin: EdgeInsets.symmetric(vertical: 2),
child: Padding(
padding: EdgeInsets.all(8),
child: Text(
_logs[index],
style: TextStyle(fontSize: 12, fontFamily: 'monospace'),
),
),
);
},
),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('Chiudi'),
),
],
);
},
);
}
void _showSnackBar(String message, Color color) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: color,
duration: Duration(seconds: 3),
),
);
}
// ===== UI COMPONENTS =====
Widget _buildStatusCard() {
return Card(
elevation: 4,
margin: EdgeInsets.all(16),
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Stato del Sistema',
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 16),
_buildStatusRow('Inizializzato', _isInitialized),
Divider(),
Text('Monitoraggio Foreground (Ranging):',
style: TextStyle(fontWeight: FontWeight.bold)),
_buildStatusRow(
'Foreground Monitoring', _isForegroundMonitoringRunning),
SizedBox(height: 8),
Text('Servizio Background:',
style: TextStyle(fontWeight: FontWeight.bold)),
_buildStatusRow(
'Background Service Running', _isBackgroundServiceRunning),
_buildStatusRow(
'Background Service Abilitato', _isBackgroundServiceEnabled),
Divider(),
Text(
'Stato Regione: ${_monitoringState.toString().split('.').last.toUpperCase()}'),
Text('Beacon Rilevati: ${_detectedBeacons.length}'),
if (_isForegroundMonitoringRunning && _isBackgroundServiceRunning)
Container(
padding: EdgeInsets.all(8),
margin: EdgeInsets.only(top: 8),
decoration: BoxDecoration(
color: Colors.blue.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
Icon(Icons.info, color: Colors.blue, size: 16),
SizedBox(width: 8),
Expanded(
child: Text('Monitoraggio completo attivo',
style: TextStyle(color: Colors.blue))),
],
),
),
],
),
),
);
}
Widget _buildStatusRow(String label, bool status) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 4),
child: Row(
children: [
Icon(
status ? Icons.check_circle : Icons.cancel,
color: status ? Colors.green : Colors.red,
size: 20,
),
SizedBox(width: 8),
Text(label),
],
),
);
}
Widget _buildForegroundControls() {
return Card(
elevation: 4,
margin: EdgeInsets.all(16),
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Controlli Monitoraggio Foreground',
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 8),
Text(
'Il monitoraggio foreground rileva beacon in tempo reale quando l\'app è aperta',
style: TextStyle(color: Colors.grey[600], fontSize: 12),
),
SizedBox(height: 16),
Row(
children: [
Expanded(
child: ElevatedButton.icon(
onPressed: _isInitialized && !_isForegroundMonitoringRunning
? _startForegroundMonitoring
: null,
icon: Icon(Icons.radar),
label: Text('Avvia Foreground'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.purple,
foregroundColor: Colors.white,
),
),
),
SizedBox(width: 8),
Expanded(
child: ElevatedButton.icon(
onPressed: _isInitialized && _isForegroundMonitoringRunning
? _stopForegroundMonitoring
: null,
icon: Icon(Icons.stop),
label: Text('Ferma Foreground'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.orange,
foregroundColor: Colors.white,
),
),
),
],
),
],
),
),
);
}
Widget _buildBackgroundControls() {
return Card(
elevation: 4,
margin: EdgeInsets.all(16),
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Controlli Servizio Background',
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 8),
Text(
'Il servizio background rileva beacon anche quando l\'app è chiusa',
style: TextStyle(color: Colors.grey[600], fontSize: 12),
),
SizedBox(height: 16),
Row(
children: [
Expanded(
child: ElevatedButton.icon(
onPressed: _isInitialized && !_isBackgroundServiceRunning
? _startBackgroundService
: null,
icon: Icon(Icons.play_arrow),
label: Text('Avvia Background'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
foregroundColor: Colors.white,
),
),
),
SizedBox(width: 8),
Expanded(
child: ElevatedButton.icon(
onPressed: _isInitialized && _isBackgroundServiceRunning
? _stopBackgroundService
: null,
icon: Icon(Icons.stop),
label: Text('Arresta Background'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
),
),
),
],
),
SizedBox(height: 8),
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: _isInitialized ? _restartBackgroundService : null,
icon: Icon(Icons.refresh),
label: Text('Riavvia Servizio Background'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
),
),
),
],
),
),
);
}
Widget _buildCompleteControls() {
return Card(
elevation: 4,
margin: EdgeInsets.all(16),
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Controlli Monitoraggio Completo',
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 8),
Text(
'Avvia/ferma sia il monitoraggio foreground che il servizio background insieme',
style: TextStyle(color: Colors.grey[600], fontSize: 12),
),
SizedBox(height: 16),
Row(
children: [
Expanded(
child: ElevatedButton.icon(
onPressed: _isInitialized &&
!(_isForegroundMonitoringRunning &&
_isBackgroundServiceRunning)
? _startCompleteMonitoring
: null,
icon: Icon(Icons.play_circle_fill),
label: Text('Avvia Tutto'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.indigo,
foregroundColor: Colors.white,
),
),
),
SizedBox(width: 8),
Expanded(
child: ElevatedButton.icon(
onPressed: _isInitialized &&
(_isForegroundMonitoringRunning ||
_isBackgroundServiceRunning)
? _stopCompleteMonitoring
: null,
icon: Icon(Icons.stop_circle),
label: Text('Ferma Tutto'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.brown,
foregroundColor: Colors.white,
),
),
),
],
),
],
),
),
);
}
Widget _buildBeaconsList() {
return Card(
elevation: 4,
margin: EdgeInsets.all(16),
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Beacon Rilevati (${_detectedBeacons.length})',
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 16),
if (_detectedBeacons.isEmpty)
Container(
padding: EdgeInsets.all(20),
child: Center(
child: Column(
children: [
Icon(Icons.bluetooth_searching,
size: 50, color: Colors.grey),
SizedBox(height: 8),
Text(
'Nessun beacon rilevato',
style: TextStyle(color: Colors.grey),
),
if (!_isForegroundMonitoringRunning &&
!_isBackgroundServiceRunning)
Text(
'Avvia il monitoraggio per rilevare beacon',
style: TextStyle(color: Colors.grey, fontSize: 12),
),
],
),
),
)
else
...(_detectedBeacons
.map((beacon) => _buildBeaconCard(beacon))
.toList()),
],
),
),
);
}
Widget _buildBeaconCard(Beacon beacon) {
return Card(
margin: EdgeInsets.symmetric(vertical: 4),
child: ListTile(
leading: Icon(Icons.bluetooth, color: Colors.blue),
title: Text('UUID: ${beacon.uuid}'),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (beacon.major != null) Text('Major: ${beacon.major}'),
if (beacon.minor != null) Text('Minor: ${beacon.minor}'),
Text('Distanza: ${beacon.distance.toStringAsFixed(2)}m'),
Text('RSSI: ${beacon.rssi} dBm'),
],
),
trailing: Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: _getDistanceColor(beacon.distance),
borderRadius: BorderRadius.circular(12),
),
child: Text(
_getDistanceText(beacon.distance),
style: TextStyle(color: Colors.white, fontSize: 12),
),
),
),
);
}
Color _getDistanceColor(double distance) {
if (distance < 1.0) return Colors.green;
if (distance < 3.0) return Colors.orange;
return Colors.red;
}
String _getDistanceText(double distance) {
if (distance < 1.0) return 'VICINO';
if (distance < 3.0) return 'MEDIO';
return 'LONTANO';
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Brux88 Beacon Control'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
actions: [
IconButton(
onPressed: _updateStatus,
icon: Icon(Icons.refresh),
tooltip: 'Aggiorna stato',
),
IconButton(
onPressed: _checkSystemRequirements,
icon: Icon(Icons.settings),
tooltip: 'Controlla requisiti sistema',
),
IconButton(
onPressed: _getLogs,
icon: Icon(Icons.list),
tooltip: 'Visualizza log',
),
],
),
body: SingleChildScrollView(
child: Column(
children: [
_buildStatusCard(),
// Controllo Auto-restart
Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Auto-restart Settings',
style: Theme.of(context).textTheme.headlineSmall),
SizedBox(height: 8),
SwitchListTile(
title: Text('Auto-restart abilitato'),
subtitle: Text(_autoRestartEnabled
? 'Il servizio si riavvierà automaticamente se terminato'
: 'Il servizio NON si riavvierà automaticamente'),
value: _autoRestartEnabled,
onChanged: _isInitialized
? (value) => _toggleAutoRestart()
: null,
),
],
),
),
),
_buildForegroundControls(),
_buildBackgroundControls(),
_buildCompleteControls(),
_buildBeaconsList(),
],
),
),
);
}
@override
void dispose() {
_beaconManager.dispose();
super.dispose();
}
}
/*import 'package:brux88_beacon_example/permission_screen.dart';
import 'package:flutter/material.dart';
import 'package:brux88_beacon/brux88_beacon.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:permission_handler/permission_handler.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Beacon Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const BeaconHomePage(),
);
}
}
class BeaconHomePage extends StatefulWidget {
const BeaconHomePage({Key? key}) : super(key: key);
@override
State<BeaconHomePage> createState() => _BeaconHomePageState();
}
class _BeaconHomePageState extends State<BeaconHomePage>
with SingleTickerProviderStateMixin {
// Chiave globale per lo Scaffold, che ci fornisce un contesto valido
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
final BeaconManager _beaconManager = BeaconManager();
bool _isMonitoring = false;
List<Beacon> _detectedBeacons = [];
List<String> _logs = [];
SelectedBeacon? _selectedBeacon;
late TabController _tabController;
bool _showDetectionNotifications = false;
@override
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this);
_initBeaconManager().then((_) {
_checkServiceStatus();
_checkBatteryOptimization();
});
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
Future<void> _checkBatteryOptimization() async {
try {
final isIgnored = await _beaconManager.isBatteryOptimizationIgnored();
if (!isIgnored) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Ottimizzazione Batteria'),
content: Text(
'Per garantire il corretto funzionamento del monitoraggio beacon in background, ' +
'è necessario disattivare l\'ottimizzazione della batteria per questa app.'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('Non ora'),
),
TextButton(
onPressed: () async {
Navigator.of(context).pop();
await _beaconManager.requestIgnoreBatteryOptimization();
},
child: Text('Disattiva'),
),
],
),
);
}
} catch (e) {
_addLog("Errore nel controllo dell'ottimizzazione batteria: $e");
}
}
Future<void> _checkServiceStatus() async {
try {
// Controlla se il monitoraggio è attivo
final isRunning = await _beaconManager.isMonitoringRunning();
setState(() {
_isMonitoring = isRunning;
});
if (isRunning) {
_addLog("Monitoraggio già attivo");
} else if (_selectedBeacon != null && _selectedBeacon!.enabled) {
// Se abbiamo un beacon selezionato ma il monitoraggio non è attivo, riavviamolo
_addLog("Riavvio automatico del monitoraggio");
await _beaconManager.startMonitoring();
setState(() {
_isMonitoring = true;
});
}
} catch (e) {
_addLog("Errore nel controllo dello stato del servizio: $e");
}
}
Future<void> _initBeaconManager() async {
try {
_addLog("Inizializzazione beacon manager...");
// Se il monitoraggio è attivo, fermalo prima di reinizializzare
if (_isMonitoring) {
_addLog(
"Monitoraggio attivo rilevato, lo fermo prima di reinizializzare");
try {
await _beaconManager.stopMonitoring();
await _beaconManager.cancelRecurringAlarm();
} catch (e) {
_addLog("Errore nel fermare il monitoraggio precedente: $e");
// Continua comunque con l'inizializzazione
}
}
// Prima inizializza in modo basico
final initialized = await _beaconManager.initialize();
if (!initialized) {
_addLog("Errore durante l'inizializzazione del BeaconManager");
return;
}
// Verifica i permessi dopo l'inizializzazione di base
final permissionsStatus = await _beaconManager.checkPermissions();
final allPermissionsGranted = !permissionsStatus.values.contains(false);
if (!allPermissionsGranted) {
_addLog("Permessi mancanti, richiedo i permessi necessari");
// Richiedi permessi di posizione
var locationStatus = await Permission.locationWhenInUse.request();
_addLog("Permesso posizione: $locationStatus");
if (locationStatus.isGranted) {
var backgroundLocationStatus =
await Permission.locationAlways.request();
_addLog(
"Permesso posizione in background: $backgroundLocationStatus");
}
// Richiedi permessi Bluetooth
var bluetoothScanStatus = await Permission.bluetoothScan.request();
_addLog("Permesso Bluetooth scan: $bluetoothScanStatus");
var bluetoothConnectStatus =
await Permission.bluetoothConnect.request();
_addLog("Permesso Bluetooth connect: $bluetoothConnectStatus");
// Richiedi permesso notifiche
var notificationStatus = await Permission.notification.request();
_addLog("Permesso notifiche: $notificationStatus");
}
// Ascolto dei beacon rilevati
_beaconManager.beacons.listen((beacons) {
if (mounted) {
setState(() {
_detectedBeacons = beacons;
});
}
// Mostra toast per ogni beacon rilevato
if (beacons.isNotEmpty) {
for (var beacon in beacons) {
Fluttertoast.showToast(
msg: "Beacon rilevato: ${beacon.uuid}",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM,
);
}
}
});
// Ascolto dello stato di monitoraggio
_beaconManager.monitoringState.listen((state) {
_addLog("Stato monitoraggio: $state");
});
// Carica i log iniziali
_loadLogs();
// Carica il beacon selezionato
_loadSelectedBeacon();
// Verifica se il monitoraggio era attivo in precedenza
final isMonitoring = await _beaconManager.isMonitoringRunning();
setState(() {
_isMonitoring = isMonitoring;
});
_addLog(
"Inizializzazione beacon manager completata. Monitoraggio attivo: $_isMonitoring");
// Se il monitoraggio era attivo, riavvialo
if (_isMonitoring) {
_addLog("Riavvio monitoraggio automaticamente...");
try {
await _beaconManager.startMonitoring();
await _beaconManager.setupRecurringAlarm();
_addLog("Monitoraggio riavviato con successo");
} catch (e) {
_addLog("Errore nel riavvio del monitoraggio: $e");
setState(() {
_isMonitoring = false;
});
}
}
} catch (e) {
_addLog("Errore nell'inizializzazione: $e");
}
}
Future<void> _loadSelectedBeacon() async {
try {
final beacon = await _beaconManager.getSelectedBeacon();
setState(() {
_selectedBeacon = beacon;
});
if (beacon != null) {
_addLog("Beacon selezionato caricato: ${beacon.uuid}");
}
} catch (e) {
_addLog("Errore nel caricamento del beacon selezionato: $e");
}
}
Future<void> _loadLogs() async {
try {
final logs = await _beaconManager.getLogs();
setState(() {
_logs = logs;
});
} catch (e) {
_addLog("Errore nel caricamento dei log: $e");
}
}
void _addLog(String message) {
setState(() {
final timestamp = DateTime.now().toString().substring(0, 19);
_logs.insert(0, "[$timestamp] $message");
});
}
Future<void> _toggleMonitoring() async {
try {
if (_isMonitoring) {
_addLog("Arresto monitoraggio...");
// Imposta _isMonitoring = false prima di fermare il servizio
// per evitare che i callback possano riattivarlo
setState(() {
_isMonitoring = false;
});
await _beaconManager.stopMonitoring();
await _beaconManager.cancelRecurringAlarm(); // Cancella l'allarme
_addLog("Monitoraggio fermato");
} else {
// Verifica nuovamente i permessi prima di avviare il monitoraggio
final permissionsStatus = await _beaconManager.checkPermissions();
final allPermissionsGranted = !permissionsStatus.values.contains(false);
if (!allPermissionsGranted) {
_addLog(
"Richiesta di permessi mancanti prima di avviare il monitoraggio");
// Richiedi i permessi di base
await Permission.locationWhenInUse.request();
if (await Permission.locationWhenInUse.isGranted) {
await Permission.locationAlways.request();
}
await Permission.bluetoothScan.request();
await Permission.bluetoothConnect.request();
await Permission.notification.request();
// Richiedi di ignorare l'ottimizzazione della batteria
await _beaconManager.requestIgnoreBatteryOptimization();
}
// Richiedi il permesso per gli allarmi esatti
await _beaconManager.requestExactAlarmPermission();
_addLog("Avvio monitoraggio...");
// Verifica che il BeaconManager sia inizializzato
bool isInitialized = await _beaconManager.isInitialized();
if (!isInitialized) {
_addLog("BeaconManager non inizializzato, lo reinizializzo...");
isInitialized = await _beaconManager.initialize();
if (!isInitialized) {
_addLog("Impossibile inizializzare il BeaconManager");
throw Exception("Impossibile inizializzare il BeaconManager");
}
}
// Avvia il monitoraggio
await _beaconManager.startMonitoring();
await _beaconManager.setupRecurringAlarm(); // Configura l'allarme
_addLog("Monitoraggio avviato");
setState(() {
_isMonitoring = true;
});
// Verifica che il servizio sia effettivamente attivo
await _checkServiceStatus();
}
} catch (e) {
_addLog("Errore: $e");
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Si è verificato un errore: $e'),
backgroundColor: Colors.red,
),
);
}
}
}
// Estrai i dialoghi in metodi separati
Future<bool> showPermissionDialog(BuildContext context) async {
return await showDialog<bool>(
context: context,
barrierDismissible: false,
builder: (BuildContext dialogContext) {
return AlertDialog(
title: const Text('Permessi necessari'),
content: const Text(
'Per monitorare i beacon, l\'app ha bisogno di tutti i permessi richiesti. '
'Vuoi concedere tutti i permessi necessari per continuare?'),
actions: [
TextButton(
onPressed: () {
Navigator.of(dialogContext).pop(false);
},
child: const Text('Annulla'),
),
TextButton(
onPressed: () {
Navigator.of(dialogContext).pop(true);
},
child: const Text('Concedi permessi'),
),
],
);
},
) ??
false; // Default a false se il dialogo viene chiuso in modo imprevisto
}
Future<bool> showContinueAnywayDialog(BuildContext context) async {
return await showDialog<bool>(
context: context,
barrierDismissible: false,
builder: (BuildContext dialogContext) {
return AlertDialog(
title: const Text('Permessi mancanti'),
content: const Text(
'Alcuni permessi necessari non sono stati concessi. '
'Il monitoraggio potrebbe non funzionare correttamente. '
'Vuoi avviare comunque il monitoraggio?'),
actions: [
TextButton(
onPressed: () {
Navigator.of(dialogContext).pop(false);
},
child: const Text('No'),
),
TextButton(
onPressed: () {
Navigator.of(dialogContext).pop(true);
},
child: const Text('Sì, avvia comunque'),
),
],
);
},
) ??
false; // Default a false se il dialogo viene chiuso in modo imprevisto
}
Future<void> requestAllPermissions() async {
// Prima richiedi i permessi di base
await Permission.locationWhenInUse.request();
if (await Permission.locationWhenInUse.isGranted) {
await Permission.locationAlways.request();
}
// Permessi Bluetooth (per Android 12+)
await Permission.bluetoothScan.request();
await Permission.bluetoothConnect.request();
// Permessi notifiche (per Android 13+)
await Permission.notification.request();
// Richiedi di ignorare l'ottimizzazione della batteria
await _beaconManager.requestIgnoreBatteryOptimization();
}
Future<void> _saveSelectedBeacon(Beacon beacon) async {
try {
final result = await _beaconManager.setBeaconToMonitor(
uuid: beacon.uuid,
major: beacon.major,
minor: beacon.minor,
enabled: true,
);
if (result) {
_addLog("Beacon selezionato: ${beacon.uuid}");
Fluttertoast.showToast(
msg: "Beacon selezionato con successo",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM,
);
// Ricarica il beacon selezionato
_loadSelectedBeacon();
} else {
_addLog("Errore nella selezione del beacon");
}
} catch (e) {
_addLog("Errore: $e");
}
}
Future<void> _clearSelectedBeacon() async {
try {
final result = await _beaconManager.clearSelectedBeacon();
if (result) {
setState(() {
_selectedBeacon = null;
});
_addLog("Selezione beacon cancellata");
Fluttertoast.showToast(
msg: "Selezione beacon cancellata",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM,
);
} else {
_addLog("Errore nella cancellazione del beacon selezionato");
}
} catch (e) {
_addLog("Errore: $e");
}
}
Future<void> _refreshLogs() async {
_addLog("Aggiornamento log...");
await _loadLogs();
}
@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text('Plugin Beacon Demo'),
bottom: TabBar(
controller: _tabController,
tabs: [
Tab(text: 'Beacon'),
Tab(text: 'Permessi', icon: Icon(Icons.security)),
Tab(text: 'Log'),
],
),
),
body: TabBarView(
controller: _tabController,
children: [
// Tab Beacon
Column(
children: [
SwitchListTile(
title: const Text('Notifiche di rilevamento beacon'),
subtitle: const Text(
'Mostra notifiche quando vengono rilevati nuovi beacon'),
value: _showDetectionNotifications,
onChanged: (bool value) async {
// Chiama il metodo del plugin
await _beaconManager.setShowDetectionNotifications(value);
setState(() {
_showDetectionNotifications = value;
// Se hai un log, aggiungi anche un messaggio
if (mounted && _logs != null) {
_logs.add(
"Notifiche di rilevamento ${value ? 'abilitate' : 'disabilitate'}");
}
});
},
),
Padding(
padding: EdgeInsets.all(16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: _toggleMonitoring,
child: Text(_isMonitoring
? 'Ferma monitoraggio'
: 'Avvia monitoraggio'),
),
],
),
),
// Beacon selezionato
if (_selectedBeacon != null)
Card(
margin: EdgeInsets.all(16.0),
color: Colors.lightBlue[50],
child: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Beacon Selezionato',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
SizedBox(height: 8),
Text('UUID: ${_selectedBeacon!.uuid}'),
if (_selectedBeacon!.major != null)
Text('Major: ${_selectedBeacon!.major}'),
if (_selectedBeacon!.minor != null)
Text('Minor: ${_selectedBeacon!.minor}'),
SizedBox(height: 8),
Text(
'Stato: ${_selectedBeacon!.enabled ? 'Attivo' : 'Inattivo'}'),
SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: _clearSelectedBeacon,
child: Text('Cancella'),
),
],
),
],
),
),
),
Expanded(
child: _detectedBeacons.isEmpty
? Center(child: Text('Nessun beacon rilevato'))
: ListView.builder(
itemCount: _detectedBeacons.length,
itemBuilder: (context, index) {
final beacon = _detectedBeacons[index];
return Card(
margin: EdgeInsets.symmetric(
horizontal: 16.0, vertical: 8.0),
child: ListTile(
title: Text('UUID: ${beacon.uuid}'),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (beacon.major != null)
Text('Major: ${beacon.major}'),
if (beacon.minor != null)
Text('Minor: ${beacon.minor}'),
Text(
'Distanza: ${beacon.distance.toStringAsFixed(2)}m'),
Text('RSSI: ${beacon.rssi} dBm'),
],
),
trailing: IconButton(
icon: Icon(Icons.save),
onPressed: () => _saveSelectedBeacon(beacon),
tooltip: 'Seleziona beacon',
),
),
);
},
),
),
],
),
// Scheda Permessi
const PermissionScreen(),
// Tab Log
Column(
children: [
Padding(
padding: EdgeInsets.all(16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: _refreshLogs,
child: Text('Aggiorna Log'),
),
ElevatedButton(
onPressed: () {
setState(() {
_logs.clear();
_addLog("Log cancellati");
});
},
child: Text('Cancella Log'),
),
],
),
),
Expanded(
child: _logs.isEmpty
? Center(child: Text('Nessun log disponibile'))
: ListView.builder(
itemCount: _logs.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 4.0,
),
child: Text(
_logs[index],
style: TextStyle(
fontFamily: 'monospace',
fontSize: 12,
),
),
);
},
),
),
],
),
],
),
);
}
}
*/