import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:beacon_scanner/beacon_scanner.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart'; import 'package:manager_api_new/api.dart'; import 'package:mymuseum_visitapp/Components/AssistantChatSheet.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/ConfigurationPage/beaconArticleFound.dart'; import 'package:mymuseum_visitapp/Screens/Sections/Article/article_page.dart'; import 'package:mymuseum_visitapp/Services/pushNotificationService.dart'; import 'package:mymuseum_visitapp/Screens/Sections/Quiz/quizz_page.dart'; import 'package:mymuseum_visitapp/Screens/section_page.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 ConfigurationPage extends StatefulWidget { const ConfigurationPage({Key? key,required this.configuration, required this.isAlreadyAllowed}) : super(key: key); final ConfigurationDTO configuration; final bool isAlreadyAllowed; @override State createState() => _ConfigurationPageState(); } class _ConfigurationPageState extends State with WidgetsBindingObserver { //ConfigurationDTO? configuration; int timeBetweenBeaconPopUp = 20000; // 20 sec int meterToBeacon = 100; // 15 meters bool modeDebugBeacon = false; // Beacon specific final controller = Get.find(); StreamSubscription? _streamRanging; /*final _regionBeacons = >{}; final _beacons = [];*/ bool _isDialogShowing = false; DateTime? lastTimePopUpWasClosed; //bool _isArticleOpened = false; StreamSubscription? listener; //final List regions = []; late final VoidCallback _notificationListener; @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: '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(widget.isAlreadyAllowed) { listeningState(); } _notificationListener = () { final message = PushNotificationService.tappedMessage.value; if (message == null) return; if (!mounted) return; final title = message.notification?.title ?? ''; final body = message.notification?.body ?? ''; ScaffoldMessenger.of(context).showMaterialBanner( MaterialBanner( content: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ if (title.isNotEmpty) Text(title, style: const TextStyle(fontWeight: FontWeight.bold)), if (body.isNotEmpty) Text(body), ], ), actions: [ TextButton( onPressed: () => ScaffoldMessenger.of(context).hideCurrentMaterialBanner(), child: const Text('OK'), ), ], ), ); }; PushNotificationService.tappedMessage.addListener(_notificationListener); WidgetsBinding.instance.addPostFrameCallback((_) { final appContext = Provider.of(context, listen: false); VisitAppContext visitAppContext = appContext.getContext(); visitAppContext.configuration = widget.configuration; visitAppContext.sectionIds = widget.configuration.sectionIds; appContext.setContext(visitAppContext); listener = controller.startStream.listen((flag) async { if (flag == true && mounted) { final ctx = Provider.of(context, listen: false).getContext(); await initScanBeacon(ctx); } }); }); //listeningState(); } listeningState() async { await BeaconScanner.instance.initialize(true); } 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 { if (_streamRanging != null && !_streamRanging!.isPaused) return; if (_streamRanging != null && _streamRanging!.isPaused) { _streamRanging!.resume(); return; } final regions = [ if (Platform.isIOS) Region( identifier: 'MyMuseumB', beaconId: IBeaconId(proximityUUID: 'FDA50693-A4E2-4FB1-AFCF-C6EB07647825'), ) else Region(identifier: 'MyMuseumB'), ]; _streamRanging = BeaconScanner.instance.ranging(regions).listen((ScanResult result) { if (!mounted || result.beacons.isEmpty) return; final beaconSections = visitAppContext.beaconSections; if (beaconSections == null) return; var matches = beaconSections.where((bs) => result.beacons.any((b) => b.id.minorId == bs!.minorBeaconId && b.accuracy < meterToBeacon ) ); if (matches.isNotEmpty) { matches = matches.where((bs) => bs!.configurationId == visitAppContext.configuration!.id!); } if (matches.isNotEmpty && !modeDebugBeacon) { matches = matches.where((bs) => !visitAppContext.readSections.any((ra) => ra.id == bs!.sectionId) ); } if (matches.isEmpty) return; final milliLastTime = lastTimePopUpWasClosed?.millisecondsSinceEpoch ?? 0; final cooldownOk = (DateTime.now().millisecondsSinceEpoch - milliLastTime) > timeBetweenBeaconPopUp; if (!_isDialogShowing && !visitAppContext.isContentCurrentlyShown && cooldownOk && visitAppContext.isScanningBeacons) { final sorted = matches.toList()..sort((a, b) => a!.orderInConfig!.compareTo(b!.orderInConfig!)); _onBeaconFound(visitAppContext, sorted.first); } }); } /*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; PushNotificationService.showBeaconDiscoveryNotification( title: TranslationHelper.getFromLocale('beaconFound', visitAppContext), body: TranslationHelper.getFromLocale('beaconFoundBody', visitAppContext), ); 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: const TextStyle(color: kSecondGrey)), 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: const TextStyle(color: kSecondGrey)), 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) => SectionPage( configuration: widget.configuration, rawSection: null, visitAppContextIn: visitAppContext, sectionId: 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() { PushNotificationService.tappedMessage.removeListener(_notificationListener); listener?.cancel(); _streamRanging?.cancel(); controller.pauseScanning(); super.dispose(); } @override Widget build(BuildContext context) { final appContext = Provider.of(context); VisitAppContext visitAppContext = appContext.getContext(); return PopScope( canPop: true, child: Scaffold( /*appBar: CustomAppBar( title: TranslationHelper.get(configuration!.title, visitAppContext), isHomeButton: true, ),*/ backgroundColor: kBackgroundGrey, body: Body(configuration: widget.configuration), floatingActionButton: Stack( children: [ if (visitAppContext.applicationInstanceDTO?.isAssistant == true) Align( alignment: Alignment.bottomRight, child: Padding( padding: const EdgeInsets.only(right: 90, bottom: 1), child: FloatingActionButton( heroTag: 'assistant_config', backgroundColor: kMainColor1, onPressed: () { AssistantChatSheet.show( context, visitAppContext: visitAppContext, configurationId: widget.configuration.id, onNavigateToSection: (sectionId, sectionTitle) { Navigator.push(context, MaterialPageRoute( builder: (_) => SectionPage( configuration: widget.configuration, rawSection: null, visitAppContextIn: visitAppContext, sectionId: sectionId, ), )); }, ); }, child: const Icon(Icons.chat_bubble_outline, color: Colors.white), ), ), ), visitAppContext.beaconSections != null && 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: InkWell( onTap: () async { if (!visitAppContext.isScanningBeacons) { await [ Permission.bluetoothScan, Permission.bluetoothConnect, Permission.location, ].request(); controller.startScanning(); visitAppContext.isScanningBeacons = true; visitAppContext.isScanBeaconAlreadyAllowed = true; appContext.setContext(visitAppContext); } else { _streamRanging?.pause(); controller.pauseScanning(); visitAppContext.isScanningBeacons = false; appContext.setContext(visitAppContext); } }, child: Container( decoration: BoxDecoration( shape: BoxShape.circle, color: visitAppContext.isScanningBeacons ? kMainColor1 : Colors.grey, ), height: 75.0, width: 65.0, child: const Icon(Icons.my_location, color: Colors.white), ), ), ), ) : const SizedBox(), Align( alignment: Alignment.bottomRight, child: ScannerBouton(appContext: appContext), ), ], ), ), ); } 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'), ), ], ); }, ); } } }