import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_beacon/flutter_beacon.dart'; import 'package:get/get.dart'; import 'package:manager_api/api.dart'; import 'package:mymuseum_visitapp/Components/CustomAppBar.dart'; import 'package:mymuseum_visitapp/Components/ScannerBouton.dart'; import 'package:mymuseum_visitapp/Helpers/requirement_state_controller.dart'; import 'package:mymuseum_visitapp/Helpers/translationHelper.dart'; import 'package:mymuseum_visitapp/Models/beaconSection.dart'; import 'package:mymuseum_visitapp/Models/visitContext.dart'; import 'package:mymuseum_visitapp/Screens/Article/article.dart'; import 'package:mymuseum_visitapp/Screens/Visit/beaconArticleFound.dart'; import 'package:mymuseum_visitapp/app_context.dart'; import 'package:mymuseum_visitapp/constants.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:provider/provider.dart'; import 'components/body.dart'; class VisitPage extends StatefulWidget { const VisitPage({Key? key, required this.configurationId}) : super(key: key); final String configurationId; @override State createState() => _VisitPageState(); } class _VisitPageState extends State with WidgetsBindingObserver { ConfigurationDTO? configuration; int timeBetweenBeaconPopUp = 20000; // 20 sec int meterToBeacon = 15; // 15 meters bool modeDebugBeacon = true; // Beacon specific final controller = Get.find(); StreamSubscription? _streamBluetooth; StreamSubscription? _streamRanging; /*final _regionBeacons = >{}; final _beacons = [];*/ bool _isDialogShowing = false; DateTime? lastTimePopUpWasClosed; //bool _isArticleOpened = false; StreamSubscription? listener; final List regions = []; @override void initState() { WidgetsBinding.instance.addObserver(this); if (Platform.isIOS) { // iOS platform, at least set identifier and proximityUUID for region scanning regions.add(Region( identifier: 'MyMuseumB', proximityUUID: '01122334-4556-6778-899A-ABBCCDDEEFF0')); regions.add(Region( identifier: 'MyMuseumB', proximityUUID: 'FDA50693-A4E2-4FB1-AFCF-C6EB07647825')); } else { // Android platform, it can ranging out of beacon that filter all of Proximity UUID regions.add(Region(identifier: 'MyMuseumB')); } super.initState(); if(controller.authorizationStatusOk) { listeningState(); } //listeningState(); } listeningState() async { print('Listening to bluetooth state'); _streamBluetooth = flutterBeacon .bluetoothStateChanged() .listen((BluetoothState state) async { controller.updateBluetoothState(state); await checkAllRequirements(); }); } checkAllRequirements() async { final bluetoothState = await flutterBeacon.bluetoothState; controller.updateBluetoothState(bluetoothState); print('BLUETOOTH $bluetoothState'); final authorizationStatus = await flutterBeacon.authorizationStatus; controller.updateAuthorizationStatus(authorizationStatus); print('AUTHORIZATION $authorizationStatus'); final locationServiceEnabled = await flutterBeacon.checkLocationServicesIfEnabled; controller.updateLocationService(locationServiceEnabled); print('LOCATION SERVICE $locationServiceEnabled'); var status = await Permission.bluetoothScan.status; if (status.isDenied) { print("IS DENIIIED"); // We didn't ask for permission yet or the permission has been denied before but not permanently. } // You can request multiple permissions at once. Map statuses = await [ Permission.bluetoothScan, Permission.bluetoothConnect, ].request(); print(statuses[Permission.bluetoothScan]); print(statuses[Permission.bluetoothConnect]); print(status); if (controller.bluetoothEnabled && controller.authorizationStatusOk && controller.locationServiceEnabled) { print('STATE READY'); //if (currentIndex == 0) { print('SCANNING'); //controller.startScanning(); /*controller.startStream.listen((flag) { if (flag == true) { initScanBeacon(); } });*/ /*} else { print('BROADCASTING'); controller.startBroadcasting(); }*/ } else { print('STATE NOT READY'); controller.pauseScanning(); } } initScanBeacon(VisitAppContext visitAppContext) async { await flutterBeacon.initializeScanning; if (!controller.authorizationStatusOk || !controller.locationServiceEnabled || !controller.bluetoothEnabled) { print( 'RETURNED, authorizationStatusOk=${controller.authorizationStatusOk}, ' 'locationServiceEnabled=${controller.locationServiceEnabled}, ' 'bluetoothEnabled=${controller.bluetoothEnabled}'); return; } if (_streamRanging != null) { if (_streamRanging!.isPaused) { _streamRanging?.resume(); return; } } _streamRanging = flutterBeacon.ranging(regions).listen((RangingResult result) { //print(result); if (mounted) { //print("visitAppContext"); //print(visitAppContext); //print(visitAppContext!.beaconSections); if(result.beacons.isNotEmpty) { print(result); print(result.beacons.map((b) => b.macAddress)); print(visitAppContext.beaconSections!.map((bb) => bb!.minorBeaconId)); var beaconList = visitAppContext.beaconSections!.where((bs) => result.beacons.any((element) => element.minor == bs!.minorBeaconId && element.accuracy < meterToBeacon)); if(beaconList.isNotEmpty) { // FILTER CONFIG beaconList = beaconList.where((beacon) => beacon!.configurationId == visitAppContext.configuration!.id!); } if(beaconList.isNotEmpty && !modeDebugBeacon) { // FILTER ALREADY READ beaconList = beaconList.where((b) => !visitAppContext.readArticles.any((ra) => ra.id == b!.sectionId)); } if(beaconList.isNotEmpty) { var milliLastTime = lastTimePopUpWasClosed == null ? 0 : lastTimePopUpWasClosed!.millisecondsSinceEpoch; var checkIfMoreThanSec = (DateTime.now().millisecondsSinceEpoch - milliLastTime) > timeBetweenBeaconPopUp; if(!_isDialogShowing && !visitAppContext.isArticleCurrentlyShown && checkIfMoreThanSec && visitAppContext.isScanningBeacons) { print("Before sorting"); print(beaconList); beaconList.toList().sort((a, b) => a!.orderInConfig!.compareTo(b!.orderInConfig!)); print("after storting"); print(beaconList); _onBeaconFound(visitAppContext, beaconList.first); } else { print("Non pas possible d'afficher pour le moment"); } /*ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('BEACON - ${result.beacons.first.macAddress} - ${result.beacons.first.accuracy} - ${result.beacons.first.proximity.name}'), backgroundColor: kBlue2), );*/ } } //setState(() { /*_regionBeacons[result.region] = result.beacons; _beacons.clear(); _regionBeacons.values.forEach((list) { _beacons.addAll(list); }); _beacons.sort(_compareParameters);*/ //}); } }); } /*pauseScanBeacon() async { _streamRanging?.pause(); if (_beacons.isNotEmpty) { setState(() { _beacons.clear(); }); } }*/ /*int _compareParameters(Beacon a, Beacon b) { int compare = a.proximityUUID.compareTo(b.proximityUUID); if (compare == 0) { compare = a.major.compareTo(b.major); } if (compare == 0) { compare = a.minor.compareTo(b.minor); } return compare; }*/ @override void didChangeAppLifecycleState(AppLifecycleState state) async { print('AppLifecycleState = $state'); if (state == AppLifecycleState.resumed) { if (_streamBluetooth != null) { if (_streamBluetooth!.isPaused) { _streamBluetooth?.resume(); } } await checkAllRequirements(); } else if (state == AppLifecycleState.paused) { _streamBluetooth?.pause(); } } void _onBeaconFound(VisitAppContext visitAppContext, BeaconSection? beaconSection) { _isDialogShowing = true; showDialog( barrierDismissible: false, builder: (BuildContext context) => AlertDialog( shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(10.0)) ), content: BeaconArticleFound(beaconSection: beaconSection), actions: [ TextButton( child: Text(TranslationHelper.getFromLocale("close", visitAppContext), style: TextStyle(color: kMainColor)), onPressed: () { _isDialogShowing = false; // set it `false` since dialog is closed Navigator.of(context).pop(); lastTimePopUpWasClosed = DateTime.now(); }, ), TextButton( child: Text(TranslationHelper.getFromLocale("open", visitAppContext), style: TextStyle(color: kMainColor)), onPressed: () { _isDialogShowing = false; // set it `false` since dialog is closed Navigator.of(context).pop(); //visitAppContext.isArticleCurrentlyShown = true; lastTimePopUpWasClosed = DateTime.now(); Navigator.push( context, MaterialPageRoute( builder: (context) => ArticlePage( visitAppContextIn: visitAppContext, articleId: beaconSection!.sectionId!, ), ), ); }, ) ], actionsAlignment: MainAxisAlignment.spaceAround, contentPadding: EdgeInsets.zero, ), context: context ); /*Future.delayed(const Duration(seconds: 6), () { Navigator.pop(context); //pop dialog _isDialogShowing = false; });*/ } @override void dispose() { controller.pauseScanning(); super.dispose(); } @override Widget build(BuildContext context) { final appContext = Provider.of(context); VisitAppContext visitAppContext = appContext.getContext(); configuration = visitAppContext.configuration; listener = controller.startStream.listen((flag) async { print(flag); if (flag == true) { print("FIIIIIIREEEE ---------------"); await initScanBeacon(visitAppContext); controller.startScanning(); } }); return WillPopScope( child: Scaffold( appBar: CustomAppBar( title: TranslationHelper.get(configuration!.title, visitAppContext), isHomeButton: true, ), backgroundColor: kBackgroundGrey, body: Body(configurationId: configuration!.id), // TODO handle error.. floatingActionButton: Stack( children: [ visitAppContext.beaconSections!.where((bs) => bs!.configurationId == visitAppContext.configuration!.id).isNotEmpty ? Align( alignment: Alignment.bottomRight, child: Padding( padding: const EdgeInsets.only(right: 90, bottom: 1), child: Container( height: 65.0, width: 65.0, child: FittedBox( child: FloatingActionButton( heroTag: "beacon", onPressed: () async { bool isCancel = false; if(!controller.authorizationStatusOk) { //await handleOpenLocationSettings(); print("STATUUSSSS ? "); await showDialog( context: context, barrierDismissible: false, builder: (_) { return AlertDialog( backgroundColor: Colors.white, content: Padding( padding: const EdgeInsets.symmetric(vertical: 20), child: SizedBox( height: 150, child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, crossAxisAlignment: CrossAxisAlignment.center, children: [ Icon(Icons.my_location, color: kMainColor), Text(TranslationHelper.getFromLocale("locationWarning", visitAppContext), style: TextStyle(color: kMainColor), textAlign: TextAlign.center), ], ), ), ), actions: [ TextButton( child: Text(TranslationHelper.getFromLocale("close", visitAppContext), style: TextStyle(color: kMainColor)), onPressed: () { isCancel = true; Navigator.of(context).pop(); }, ), TextButton( child: Text(TranslationHelper.getFromLocale("ok", visitAppContext), style: TextStyle(color: kMainColor)), onPressed: () async { Navigator.of(context).pop(); }, ) ], actionsAlignment: MainAxisAlignment.spaceAround, contentPadding: EdgeInsets.zero, ); }); if(!isCancel) { if(Platform.isIOS) { Map statuses0 = await [ Permission.bluetooth, ].request(); Map statuses1 = await [ Permission.bluetoothScan, ].request(); Map statuses2 = await [ Permission.bluetoothConnect, ].request(); Map statuses3 = await [ Permission.locationWhenInUse, ].request(); Map statuses4 = await [ Permission.location, ].request(); Map statuses5 = await [ Permission.locationAlways, ].request(); print(statuses0[Permission.bluetooth]); print(statuses1[Permission.bluetoothScan]); print(statuses2[Permission.bluetoothConnect]); print(statuses3[Permission.locationWhenInUse]); print(statuses4[Permission.location]); print(statuses5[Permission.locationAlways]); } else { Map statuses = await [ Permission.bluetoothScan, Permission.bluetoothConnect, Permission.location, ].request(); print(statuses[Permission.bluetoothScan]); print(statuses[Permission.bluetoothConnect]); print(statuses[Permission.location]); print(statuses[Permission.locationWhenInUse]); var status = await Permission.bluetoothScan.status; print(status); } listeningState(); } } if(!isCancel) { if(!visitAppContext.isScanningBeacons) { print("Start Scan"); print(_streamRanging); if (_streamRanging != null) { _streamRanging?.resume(); } else { await initScanBeacon(visitAppContext); } controller.startScanning(); visitAppContext.isScanningBeacons = true; appContext.setContext(visitAppContext); } else { print("Pause Scan"); controller.pauseScanning(); // PAUSE OR DISPOSE ? visitAppContext.isScanningBeacons = false; appContext.setContext(visitAppContext); } } }, tooltip: 'Beacon', backgroundColor: visitAppContext.isScanningBeacons ? kBlue1 : Colors.grey, child: const Icon(Icons.my_location), ), ), ), ), ) : const SizedBox(), Align( alignment: Alignment.bottomRight, child: ScannerBouton(appContext: appContext), ), ], ), ), onWillPop: () async => false, ); } handleOpenLocationSettings() async { if (Platform.isAndroid) { await flutterBeacon.openLocationSettings; } else if (Platform.isIOS) { await showDialog( context: context, builder: (context) { return AlertDialog( title: Text('Location Services Off'), content: Text( 'Please enable Location Services on Settings > Privacy > Location Services.', ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text('OK'), ), ], ); }, ); } } handleOpenBluetooth() async { if (Platform.isAndroid) { try { await flutterBeacon.openBluetoothSettings; } on PlatformException catch (e) { print(e); } } else if (Platform.isIOS) { await showDialog( context: context, builder: (context) { return AlertDialog( title: Text('Bluetooth is Off'), content: Text('Please enable Bluetooth on Settings > Bluetooth.'), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text('OK'), ), ], ); }, ); } } }