// Singleton import 'dart:async'; import 'dart:convert'; import 'package:appwrite/appwrite.dart' as appwrite; import 'package:appwrite/models.dart' as models; import 'package:bus_infotainment/audio_cache.dart'; import 'package:bus_infotainment/auth/api_constants.dart'; import 'package:bus_infotainment/auth/auth_api.dart'; import 'package:bus_infotainment/backend/modules/announcement.dart'; import 'package:bus_infotainment/backend/modules/commands.dart'; import 'package:bus_infotainment/backend/modules/synced_time.dart'; import 'package:bus_infotainment/backend/modules/tracker.dart'; import 'package:bus_infotainment/backend/modules/tube_info.dart'; import 'package:bus_infotainment/tfl_datasets.dart'; import 'package:bus_infotainment/utils/audio%20wrapper.dart'; import 'package:bus_infotainment/utils/delegates.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:http/http.dart' as http; import 'package:ntp/ntp.dart'; import 'package:permission_handler/permission_handler.dart'; class LiveInformation { static final LiveInformation _singleton = LiveInformation._internal(); factory LiveInformation() { return _singleton; } LiveInformation._internal(); Future initialize() async { { // By default, load the bus sequences from the assets print("Loading bus sequences from assets"); busSequences = BusSequences.fromCSV( await rootBundle.loadString("assets/datasets/bus-blinds.csv"), await rootBundle.loadString("assets/datasets/bus-sequences.csv") ); print("Loaded bus sequences from assets"); try { http.Response response = await http.get(Uri.parse('https://tfl.gov.uk/bus-sequences.csv')); busSequences = BusSequences.fromCSV( await rootBundle.loadString("assets/datasets/bus-blinds.csv"), response.body ); print("Loaded bus sequences from TFL"); } catch (e) { print("Failed to load bus sequences from TFL. Using local copy."); } // Load tube stations print("Loading tube stations from assets"); tubeStations = TubeStations.fromJson(json.decode(await rootBundle.loadString("assets/datasets/tube_stations.json"))); print("Loaded tube stations from assets"); String sessionID = "test"; commandModule = CommandModule(sessionID); } // Initialise modules syncedTimeModule = SyncedTimeModule(); announcementModule = AnnouncementModule(); initTrackerModule(); print("Initialised LiveInformation"); } Future initTrackerModule() async { if (await Permission.location.isGranted) { trackerModule = TrackerModule(); } } // Auth AuthAPI auth = AuthAPI(); // Modules late CommandModule commandModule; late BusSequences busSequences; late AnnouncementModule announcementModule; late SyncedTimeModule syncedTimeModule; late TrackerModule trackerModule; late TubeStations tubeStations; // Important variables BusRouteVariant? _currentRouteVariant; // Events EventDelegate routeVariantDelegate = EventDelegate(); // Internal methods Future setRouteVariant_Internal(BusRouteVariant routeVariant) async { // Set the current route variant _currentRouteVariant = routeVariant; // Let everyone know that the route variant has been set/changed routeVariantDelegate.trigger(routeVariant); // Get all of the files that need to be cached List audioFiles = []; for (BusRouteStop stop in routeVariant.busStops) { audioFiles.add(stop.getAudioFileName()); } // Cache/Load the audio files await announcementModule .announcementCache .loadAnnouncementsFromBytes(await LiveInformation().announcementModule.getBundleBytes(), [ ...audioFiles, if (!routeVariant.busRoute.routeNumber.toLowerCase().startsWith("ul")) "R_${routeVariant.busRoute.routeNumber}_001.mp3" else "R_RAIL_REPLACEMENT_SERVICE_001.mp3", ] ); } // Public methods BusRouteVariant? getRouteVariant() { return _currentRouteVariant; } Future setRouteVariant(BusRouteVariant routeVariant) async { await commandModule.executeCommand( "setroute ${routeVariant.busRoute.routeNumber} ${routeVariant.busRoute.routeVariants.values.toList().indexOf(routeVariant)}" ); } /* Everything under this will be considered legacy code */ } class AnnouncementQueueEntry { final String displayText; final List audioSources; bool sendToServer = true; DateTime? scheduledTime; DateTime? timestamp; AnnouncementQueueEntry({required this.displayText, required this.audioSources, this.sendToServer = true, this.scheduledTime, this.timestamp}); } class NamedAnnouncementQueueEntry extends AnnouncementQueueEntry { final String shortName; NamedAnnouncementQueueEntry({ required this.shortName, required String displayText, required List audioSources, DateTime? scheduledTime, DateTime? timestamp, bool sendToServer = true, }) : super( displayText: displayText, audioSources: audioSources, sendToServer: sendToServer, scheduledTime: scheduledTime, timestamp: timestamp, ); } var abs = (int value) => value < 0 ? -value : value;