import 'dart:io'; import 'package:esense_flutter/esense.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:sense_the_rhythm/esense_connect_dialog.dart'; import 'package:sense_the_rhythm/simfile.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'level.dart'; class LevelSelection extends StatefulWidget { const LevelSelection({super.key}); @override State createState() => _LevelSelectionState(); } class _LevelSelectionState extends State { String? stepmaniaCoursesPath; List stepmaniaCoursesFolders = []; String eSenseDeviceName = ''; ESenseManager? eSenseManager; ValueNotifier _deviceStatus = ValueNotifier(''); // String _deviceStatus = ''; bool connected = false; bool sampling = false; @override void initState() { super.initState(); _listenToESense(); loadFolderPath(); } Future _askForPermissions() async { if (!(await Permission.bluetoothScan.request().isGranted && await Permission.bluetoothConnect.request().isGranted)) { print( 'WARNING - no permission to use Bluetooth granted. Cannot access eSense device.'); } // for some strange reason, Android requires permission to location for Bluetooth to work.....? if (Platform.isAndroid) { if (!(await Permission.locationWhenInUse.request().isGranted)) { print( 'WARNING - no permission to access location granted. Cannot access eSense device.'); } } } Future _listenToESense() async { await _askForPermissions(); // if you want to get the connection events when connecting, // set up the listener BEFORE connecting... eSenseManager!.connectionEvents.listen((event) { print('CONNECTION event: $event'); // when we're connected to the eSense device, we can start listening to events from it // if (event.type == ConnectionType.connected) _listenToESenseEvents(); setState(() { connected = false; switch (event.type) { case ConnectionType.connected: _deviceStatus.value = 'connected'; connected = true; break; case ConnectionType.unknown: _deviceStatus.value = 'unknown'; break; case ConnectionType.disconnected: _deviceStatus.value = 'disconnected'; sampling = false; break; case ConnectionType.device_found: _deviceStatus.value = 'device_found'; break; case ConnectionType.device_not_found: _deviceStatus.value = 'device_not_found'; break; } }); }); } Future _connectToESense(String deviceName) async { if (!connected) { await _askForPermissions(); print('Trying to connect to eSense device...'); setState(() { eSenseDeviceName = deviceName; }); print(eSenseDeviceName); eSenseManager = ESenseManager(eSenseDeviceName); connected = await eSenseManager!.connect(); print('success!'); setState(() { _deviceStatus.value = connected ? 'connecting...' : 'connection failed'; }); } } Future loadFolderPath() async { SharedPreferences prefs = await SharedPreferences.getInstance(); final String? stepmaniaCoursesPathSetting = prefs.getString('stepmania_courses'); if (stepmaniaCoursesPathSetting == null) return; List stepmaniaCoursesFoldersFuture = await listFilesAndFolders(stepmaniaCoursesPathSetting); setState(() { stepmaniaCoursesPath = stepmaniaCoursesPathSetting; stepmaniaCoursesFolders = stepmaniaCoursesFoldersFuture; }); } Future selectFolder() async { String? selectedFolder = await FilePicker.platform.getDirectoryPath(); if (selectedFolder != null) { // Save the selected folder path SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.setString('stepmania_courses', selectedFolder); loadFolderPath(); } } Future> listFilesAndFolders(String directoryPath) async { final directory = Directory(directoryPath); try { // List all files and folders in the directory return directory .listSync() .where((entity) => FileSystemEntity.isDirectorySync(entity.path)) .map((entity) { Simfile simfile = Simfile(entity.path); simfile.load(); return simfile; }).toList(); } catch (e) { print("Error reading directory: $e"); return []; } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Sense the Rhythm'), actions: [ IconButton( onPressed: () => showDialog( context: context, builder: (BuildContext context) { return ESenseConnectDialog( deviceStatus: _deviceStatus, connect: (String name) { _connectToESense(name); }); }, ), icon: const Icon(Icons.bluetooth)) ], ), body: Builder(builder: (context) { if (stepmaniaCoursesPath == null) { return Text('Add a Directory with Stepmania Songs on \'+\''); } else if (stepmaniaCoursesFolders.isEmpty) { return Text( 'Folder empty. Add Stepmania Songs to Folder or select a different folder on \'+\''); } else { return ListView.separated( itemCount: stepmaniaCoursesFolders.length, separatorBuilder: (BuildContext context, int index) => const Divider(), itemBuilder: (context, index) { return ListTile( leading: Image.file( File(stepmaniaCoursesFolders[index].bannerPath!)), trailing: Icon(Icons.play_arrow), title: Text(stepmaniaCoursesFolders[index].tags["TITLE"]!), subtitle: Text('3:45'), onTap: () => Navigator.push( context, MaterialPageRoute( builder: (BuildContext context) => Level(stepmaniaCoursesFolders[index]))), ); }, ); } }), floatingActionButton: FloatingActionButton( onPressed: () => {selectFolder()}, child: Icon(Icons.add)), ); } }