import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'datamodel.dart'; import 'sensorconnector.dart'; import 'databaseconnector.dart'; import 'package:shared_preferences/shared_preferences.dart'; class Configuration extends StatefulWidget { const Configuration({Key? key}) : super(key: key); @override State<Configuration> createState() => _MyHomePageState(); } class _MyHomePageState extends State<Configuration> { late Future<Collection> futureCollection; late Future<List<Collection>> futureCollections; late Future<List<EventType>> futureEventTypes; late Future<List<Device>> futureDevices; late Future<String> futureAuthToken; SensorConnector connector = SensorConnector(); final ConfigurationStoreInstance configuration = ConfigurationStoreInstance(); Future<void> updateConfiguration() async { final EventStoreInstance event = EventStoreInstance(); futureAuthToken.then((value){ //NOTE: Counterintuitive async callback function debugPrint("Login credentials correct! Auth Token: " + value); futureDevices.then((value) { debugPrint("Device List complete"); configuration.devices = value; futureEventTypes.then((value) async { debugPrint("Event Types List complete"); configuration.eventTypes = value; debugPrint("EventType Json: " + jsonEncode( configuration.eventTypes[0])); debugPrint("Device Json: " + jsonEncode( configuration.devices[0])); debugPrint("Current Collection Json: " + jsonEncode( configuration.currentCollection)); debugPrint("Sensor Login Json: " + jsonEncode( configuration.loginInformation)); try { //Update id and urn for the add event widget with some initial data event.currentEvent.id = 0; event.currentEvent.urnId = configuration.devices[0] .id; //TODO: fix if devices are an empty list. event.currentEvent.urn = configuration.devices[0].urn; event.currentEvent.description = ''; event.currentEvent.label = ''; event.currentEvent.type = configuration.eventTypes[0] .name; var date = DateTime.now().toUtc(); var isoDate = date.toIso8601String(); event.currentEvent.startDate = isoDate; event.currentEvent.endDate = isoDate; configuration.initialized = true; HapticFeedback.vibrate(); //TODO: display success user feedback //TODO: store complete "configuration" } catch (e) { throw Exception('Something went wrong try again'); } final prefs = await SharedPreferences.getInstance(); prefs.setString('configuration', jsonEncode(configuration.toMap())); prefs.setString('currentEvent', jsonEncode(event.toMap())); debugPrint('Configuration stored!'); }); }); }); //TODO: display failed as user feedback somehow } @override void initState() { super.initState(); //Start fetching all possible sensor data as soon as possible: futureCollections = connector.fetchCollections(); futureEventTypes = connector.fetchEventTypes(); if (configuration.currentCollection.id != -1) { futureDevices = connector.fetchCollectionDevices(configuration.currentCollection.id); } futureAuthToken = connector.getAuthToken(configuration.loginInformation.mail, configuration.loginInformation.password); } late Future<String> eventTypes; @override Widget build(BuildContext context) { final ConfigurationStoreInstance configuration = ConfigurationStoreInstance(); final EventStoreInstance events = EventStoreInstance(); return Scaffold( appBar: AppBar( title: const Text('Configuration'), ), body: Container( margin: const EdgeInsets.symmetric(horizontal: 5.0), child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const Text( 'You must be online to do something here!', style: TextStyle(fontSize: 14) ), const SizedBox(height: 50), TextFormField( keyboardType: TextInputType.emailAddress, autofocus: false, initialValue: configuration.loginInformation.mail, decoration: const InputDecoration( labelText: 'Sensor E-Mail', hintText: 'User for writing events', ), onChanged: (value) { configuration.loginInformation.mail = value; }, onFieldSubmitted: (value) { futureAuthToken = connector.getAuthToken(configuration.loginInformation.mail, configuration.loginInformation.password); }, ), TextFormField( autofocus: false, initialValue: configuration.loginInformation.password, decoration: const InputDecoration( labelText: 'Sensor Password', hintText: 'Password for writing events', ), onChanged: (value){ configuration.loginInformation.password = value; }, onFieldSubmitted: (value) { futureAuthToken = connector.getAuthToken(configuration.loginInformation.mail, configuration.loginInformation.password); }, ), const SizedBox(height: 50), FutureBuilder<List<Collection>>( future: futureCollections, builder: (context, snapshot){ if (snapshot.hasData) { configuration.collections = []; snapshot.data?.forEach((element) { configuration.collections.add(element); }); /*Initialize active collection with first received collection if not initialized yet*/ if(configuration.currentCollection.id == -1){ configuration.currentCollection = configuration.collections[0]; futureDevices = connector.fetchCollectionDevices(configuration.currentCollection.id); } return DropdownButtonFormField( value: configuration.currentCollection.collectionName, decoration: const InputDecoration( labelText: 'Chose Collection', ), items: configuration.collections.map((Collection collection) { return DropdownMenuItem( value: collection.collectionName, child: Text(collection.collectionName), ); }).toList(), onChanged: (value) { configuration.currentCollection = configuration.getCollectionFromName(value.toString()); //Fetch new selected collection devices futureDevices = connector.fetchCollectionDevices(configuration.currentCollection.id); } ); } else{ return const CircularProgressIndicator(); } } ), ], ), ), ), bottomNavigationBar: Container( margin: const EdgeInsets.symmetric(vertical: 10.0), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ const SizedBox(width: 10.0), FloatingActionButton.extended( heroTag: null, tooltip: 'Reset all data', icon: const Icon(Icons.cancel), label: const Text('Reset all'), onPressed: () { //events.reset(); //TODO: remove before release! var db = DatabaseInstance(); db.delete(); configuration.reset(); //TODO: remove before release this is not a real use case! }, ), const SizedBox(width: 50), FloatingActionButton.extended( heroTag: null, tooltip: 'Select Collection and download corresponding devices', icon: const Icon(Icons.save), label: const Text('Store'), onPressed: () { updateConfiguration(); //TODO: display failed as user feedback somehow }, ), const SizedBox(width: 10.0), ], ), ) ); } } //TODO: add scrowlable view here for landscape compatibility. //TODO: write configuration on app dispose!