shtuff
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,7 @@ import 'package:bus_infotainment/backend/modules/announcement.dart';
|
|||||||
import 'package:bus_infotainment/backend/modules/commands.dart';
|
import 'package:bus_infotainment/backend/modules/commands.dart';
|
||||||
import 'package:bus_infotainment/backend/modules/synced_time.dart';
|
import 'package:bus_infotainment/backend/modules/synced_time.dart';
|
||||||
import 'package:bus_infotainment/backend/modules/tracker.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/tfl_datasets.dart';
|
||||||
import 'package:bus_infotainment/utils/audio%20wrapper.dart';
|
import 'package:bus_infotainment/utils/audio%20wrapper.dart';
|
||||||
import 'package:bus_infotainment/utils/delegates.dart';
|
import 'package:bus_infotainment/utils/delegates.dart';
|
||||||
@@ -57,6 +58,12 @@ class LiveInformation {
|
|||||||
print("Failed to load bus sequences from TFL. Using local copy.");
|
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";
|
String sessionID = "test";
|
||||||
|
|
||||||
commandModule = CommandModule(sessionID);
|
commandModule = CommandModule(sessionID);
|
||||||
@@ -87,6 +94,7 @@ class LiveInformation {
|
|||||||
late AnnouncementModule announcementModule;
|
late AnnouncementModule announcementModule;
|
||||||
late SyncedTimeModule syncedTimeModule;
|
late SyncedTimeModule syncedTimeModule;
|
||||||
late TrackerModule trackerModule;
|
late TrackerModule trackerModule;
|
||||||
|
late TubeStations tubeStations;
|
||||||
|
|
||||||
// Important variables
|
// Important variables
|
||||||
BusRouteVariant? _currentRouteVariant;
|
BusRouteVariant? _currentRouteVariant;
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:bus_infotainment/audio_cache.dart';
|
import 'package:bus_infotainment/audio_cache.dart';
|
||||||
import 'package:bus_infotainment/backend/live_information.dart';
|
import 'package:bus_infotainment/backend/live_information.dart';
|
||||||
|
import 'package:bus_infotainment/backend/modules/tube_info.dart';
|
||||||
import 'package:bus_infotainment/tfl_datasets.dart';
|
import 'package:bus_infotainment/tfl_datasets.dart';
|
||||||
import 'package:bus_infotainment/utils/audio%20wrapper.dart';
|
import 'package:bus_infotainment/utils/audio%20wrapper.dart';
|
||||||
import 'package:bus_infotainment/utils/delegates.dart';
|
import 'package:bus_infotainment/utils/delegates.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
import 'info_module.dart';
|
import 'info_module.dart';
|
||||||
|
|
||||||
@@ -33,12 +36,26 @@ class AnnouncementModule extends InfoModule {
|
|||||||
return _bundleBytes!;
|
return _bundleBytes!;
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if (kIsWeb) {
|
// Try to load them from shared preferences
|
||||||
throw Exception("Cannot load bundle bytes on web");
|
try {
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
String fileLocation = prefs.getString("AnnouncementsFileLocation")!;
|
||||||
|
|
||||||
|
File file = File(fileLocation);
|
||||||
|
setBundleBytes(file.readAsBytesSync());
|
||||||
|
return _bundleBytes!;
|
||||||
|
} catch (e) {
|
||||||
|
throw Exception("Loading announcements from assets has been deprecated.");
|
||||||
}
|
}
|
||||||
|
|
||||||
final bytes = await rootBundle.load(_bundleLocation);
|
|
||||||
return bytes.buffer.asUint8List();
|
|
||||||
|
// if (kIsWeb) {
|
||||||
|
// throw Exception("Cannot load bundle bytes on web");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// final bytes = await rootBundle.load(_bundleLocation);
|
||||||
|
// return bytes.buffer.asUint8List();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -274,11 +291,21 @@ class AnnouncementModule extends InfoModule {
|
|||||||
|
|
||||||
String audioRoute = "R_${routeVariant.busRoute.routeNumber}_001.mp3";
|
String audioRoute = "R_${routeVariant.busRoute.routeNumber}_001.mp3";
|
||||||
print("Checkpoint 5");
|
print("Checkpoint 5");
|
||||||
await announcementCache.loadAnnouncementsFromBytes(await getBundleBytes(), [audioRoute, "R_RAIL_REPLACEMENT_SERVICE_001.mp3"]);
|
await announcementCache.loadAnnouncementsFromBytes(await getBundleBytes(), [audioRoute]);
|
||||||
print("Checkpoint 6");
|
print("Checkpoint 6");
|
||||||
AudioWrapperSource sourceRoute = !routeNumber.toLowerCase().startsWith("ul") ?
|
AudioWrapperSource sourceRoute = !routeNumber.toLowerCase().startsWith("ul") ?
|
||||||
AudioWrapperByteSource(announcementCache[audioRoute]!) :
|
AudioWrapperByteSource(announcementCache[audioRoute]!) :
|
||||||
AudioWrapperByteSource(announcementCache["R_RAIL_REPLACEMENT_SERVICE_001.mp3"]!);
|
// AudioWrapperByteSource(announcementCache["R_RAIL_REPLACEMENT_SERVICE_001.mp3"]!);
|
||||||
|
AudioWrapperAssetSource("audio/R_RAIL_REPLACEMENT_SERVICE_001.mp3");
|
||||||
|
|
||||||
|
if (routeNumber.toLowerCase().startsWith("ul")) {
|
||||||
|
|
||||||
|
TubeLine? closestLine = liveInformation.tubeStations.getClosestLine(routeVariant);
|
||||||
|
|
||||||
|
sourceRoute = closestLine?.getAudio() ?? sourceRoute;
|
||||||
|
routeNumber = closestLine?.getShortName() ?? routeNumber;
|
||||||
|
}
|
||||||
|
|
||||||
print("Checkpoint 6.1");
|
print("Checkpoint 6.1");
|
||||||
AudioWrapperSource sourceDestination = AudioWrapperByteSource(await routeVariant.destination!.getAudioBytes());
|
AudioWrapperSource sourceDestination = AudioWrapperByteSource(await routeVariant.destination!.getAudioBytes());
|
||||||
print("Checkpoint 7");
|
print("Checkpoint 7");
|
||||||
|
|||||||
@@ -90,7 +90,9 @@ class TrackerModule extends InfoModule {
|
|||||||
|
|
||||||
int stopIndex = liveInformation.getRouteVariant()!.busStops.indexOf(closestStop);
|
int stopIndex = liveInformation.getRouteVariant()!.busStops.indexOf(closestStop);
|
||||||
|
|
||||||
closestStop = liveInformation.getRouteVariant()!.busStops[stopIndex + 1];
|
int maxStops = liveInformation.getRouteVariant()!.busStops.length;
|
||||||
|
|
||||||
|
closestStop = liveInformation.getRouteVariant()!.busStops[min(stopIndex + 1, maxStops)];
|
||||||
|
|
||||||
print("Closest stop is now: ${closestStop.formattedStopName}");
|
print("Closest stop is now: ${closestStop.formattedStopName}");
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -0,0 +1,133 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import 'package:bus_infotainment/utils/OrdinanceSurveyUtils.dart';
|
||||||
|
import 'package:bus_infotainment/utils/audio%20wrapper.dart';
|
||||||
|
import 'package:vector_math/vector_math.dart';
|
||||||
|
|
||||||
|
import '../../tfl_datasets.dart';
|
||||||
|
|
||||||
|
class TubeStations {
|
||||||
|
|
||||||
|
List<TubeStation> stations = [];
|
||||||
|
List<TubeLine> lines = [];
|
||||||
|
|
||||||
|
TubeStations.fromJson(Map<String, dynamic> json) {
|
||||||
|
/*
|
||||||
|
Example:
|
||||||
|
"Acton Town": {
|
||||||
|
"lines": [
|
||||||
|
"District",
|
||||||
|
"Piccadilly"
|
||||||
|
],
|
||||||
|
"location": {
|
||||||
|
"lat": 51.5029984,
|
||||||
|
"lng": -0.2803455
|
||||||
|
}
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
|
||||||
|
for (String stationName in json.keys) {
|
||||||
|
TubeStation station = TubeStation();
|
||||||
|
station.name = stationName;
|
||||||
|
|
||||||
|
Map<String, dynamic> stationData = json[stationName];
|
||||||
|
|
||||||
|
station.latitude = stationData["location"]["lat"];
|
||||||
|
station.longitude = stationData["location"]["lng"];
|
||||||
|
|
||||||
|
for (String lineName in stationData["lines"]) {
|
||||||
|
|
||||||
|
// check if the line already exists
|
||||||
|
TubeLine line = lines.firstWhere((element) => element.name == lineName, orElse: () => TubeLine()..name = lineName);
|
||||||
|
|
||||||
|
if (!lines.contains(line)) {
|
||||||
|
lines.add(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
station.lines.add(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
stations.add(station);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the london underground line that is a close match to a given bus route variant
|
||||||
|
TubeLine? getClosestLine(BusRouteVariant variant) {
|
||||||
|
|
||||||
|
Map<TubeLine, int> lineMatches = {};
|
||||||
|
|
||||||
|
for (TubeLine line in lines) {
|
||||||
|
lineMatches[line] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (BusRouteStop stop in variant.busStops) {
|
||||||
|
for (TubeStation station in stations) {
|
||||||
|
|
||||||
|
// get the distance between the bus stop and the tube station
|
||||||
|
double distance = Vector2(stop.easting.toDouble(), stop.northing.toDouble()).distanceTo(OSGrid.toNorthingEasting(station.latitude, station.longitude));
|
||||||
|
|
||||||
|
// if the distance is less than 100m, then we can assume that the bus stop is near the tube station
|
||||||
|
if (distance < 200) {
|
||||||
|
for (TubeLine line in station.lines) {
|
||||||
|
lineMatches[line] = lineMatches[line]! + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TubeLine? closestLine;
|
||||||
|
int closestMatch = 0;
|
||||||
|
for (TubeLine line in lineMatches.keys) {
|
||||||
|
if (lineMatches[line]! > closestMatch) {
|
||||||
|
closestLine = line;
|
||||||
|
closestMatch = lineMatches[line]!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return closestLine;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class TubeStation {
|
||||||
|
|
||||||
|
late final String name;
|
||||||
|
|
||||||
|
late final double latitude;
|
||||||
|
late final double longitude;
|
||||||
|
|
||||||
|
List<TubeLine> lines = [];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class TubeLine {
|
||||||
|
|
||||||
|
late final String name;
|
||||||
|
|
||||||
|
AudioWrapperSource? getAudio() {
|
||||||
|
|
||||||
|
String lineName = name.toLowerCase().replaceAll(" ", "_");
|
||||||
|
lineName = lineName.replaceAll("and", "N");
|
||||||
|
lineName = lineName.toUpperCase();
|
||||||
|
|
||||||
|
return AudioWrapperAssetSource("audio/rail_replacement/$lineName.mp3");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get short name
|
||||||
|
// example: "District" -> "DIS", "London Overground" -> "LO", "TfL Rail" -> "TFL", "Waterloo and City" -> "W&C"
|
||||||
|
String getShortName() {
|
||||||
|
String shortName = name;
|
||||||
|
|
||||||
|
List<String> words = shortName.split(" ");
|
||||||
|
shortName = words.map((e) => e[0]).join("");
|
||||||
|
|
||||||
|
if (shortName.length < 3) {
|
||||||
|
shortName = name.substring(0, 3).toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
return shortName;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -88,7 +88,6 @@ class MyApp extends StatelessWidget {
|
|||||||
// '/': (context) => TfL_Dataset_Test(),
|
// '/': (context) => TfL_Dataset_Test(),
|
||||||
'/': (context) => remaster.InitialStartup(),
|
'/': (context) => remaster.InitialStartup(),
|
||||||
'/application': (context) => TfL_Dataset_Test(),
|
'/application': (context) => TfL_Dataset_Test(),
|
||||||
'/dashboard': (context) => Dashboard(),
|
|
||||||
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import 'package:flutter/widgets.dart';
|
|||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
class InitialStartup extends StatefulWidget {
|
class InitialStartup extends StatefulWidget {
|
||||||
|
|
||||||
@@ -324,6 +325,8 @@ class _page3 extends InitialStartupPage {
|
|||||||
|
|
||||||
class _page3State extends State<_page3> {
|
class _page3State extends State<_page3> {
|
||||||
|
|
||||||
|
bool _loadingAudio = false;
|
||||||
|
|
||||||
Future<bool> _announcementsUploaded() async {
|
Future<bool> _announcementsUploaded() async {
|
||||||
try {
|
try {
|
||||||
Uint8List bytes = await LiveInformation().announcementModule.getBundleBytes();
|
Uint8List bytes = await LiveInformation().announcementModule.getBundleBytes();
|
||||||
@@ -393,6 +396,7 @@ class _page3State extends State<_page3> {
|
|||||||
height: 4,
|
height: 4,
|
||||||
),
|
),
|
||||||
|
|
||||||
|
if (!_loadingAudio)
|
||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
future: _announcementsUploaded(),
|
future: _announcementsUploaded(),
|
||||||
builder: (context, val) {
|
builder: (context, val) {
|
||||||
@@ -412,6 +416,10 @@ class _page3State extends State<_page3> {
|
|||||||
text: Text(text),
|
text: Text(text),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_loadingAudio = true;
|
||||||
|
});
|
||||||
|
|
||||||
FilePickerResult? result = await FilePicker.platform.pickFiles(
|
FilePickerResult? result = await FilePicker.platform.pickFiles(
|
||||||
type: FileType.custom,
|
type: FileType.custom,
|
||||||
allowedExtensions: [
|
allowedExtensions: [
|
||||||
@@ -419,6 +427,8 @@ class _page3State extends State<_page3> {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
late Uint8List bytes;
|
late Uint8List bytes;
|
||||||
|
|
||||||
@@ -434,13 +444,25 @@ class _page3State extends State<_page3> {
|
|||||||
|
|
||||||
AnnouncementCache cache = LiveInformation().announcementModule.announcementCache;
|
AnnouncementCache cache = LiveInformation().announcementModule.announcementCache;
|
||||||
|
|
||||||
|
if (!kIsWeb) {
|
||||||
|
|
||||||
|
// Use shared preferences to store the file location
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
|
prefs.setString("AnnouncementsFileLocation", result.files.single.path!);
|
||||||
|
}
|
||||||
|
|
||||||
// load a random announcement to ensure that the file is usable
|
// load a random announcement to ensure that the file is usable
|
||||||
await cache.loadAnnouncementsFromBytes(bytes, ["S_WALTHAMSTOW_CENTRAL_001.mp3"]);
|
await cache.loadAnnouncementsFromBytes(bytes, ["S_WALTHAMSTOW_CENTRAL_001.mp3"]);
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
|
_loadingAudio = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
setState(() {
|
||||||
|
_loadingAudio = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -450,11 +472,62 @@ class _page3State extends State<_page3> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
else
|
||||||
|
CircularProgressIndicator(),
|
||||||
|
|
||||||
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
|
||||||
|
FutureBuilder(
|
||||||
|
future: _announcementsUploaded(),
|
||||||
|
builder: (context, val) {
|
||||||
|
bool isEnabled = true;
|
||||||
|
String text = "Continue";
|
||||||
|
Color color = Colors.white;
|
||||||
|
|
||||||
|
if (val.hasData) {
|
||||||
|
isEnabled = val.data!;
|
||||||
|
}
|
||||||
|
if (!isEnabled) {
|
||||||
|
text = "Complete the prerequisites to continue";
|
||||||
|
}
|
||||||
|
|
||||||
|
return ShadButton(
|
||||||
|
text: Text(text),
|
||||||
|
onPressed: () async {
|
||||||
|
|
||||||
|
showShadDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => ShadDialog.alert(
|
||||||
|
title: Text("You're all setup"),
|
||||||
|
description: Text("You can now continue to the application. \nTry not to annoy anyone on the bus! ;)"),
|
||||||
|
actions: [
|
||||||
|
ShadButton(
|
||||||
|
text: Text("Continue to application"),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pushNamed(context, "/");
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
barrierDismissible: false
|
||||||
|
);
|
||||||
|
|
||||||
|
},
|
||||||
|
enabled: isEnabled,
|
||||||
|
backgroundColor: color,
|
||||||
|
width: double.infinity,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
|
|
||||||
import 'package:bus_infotainment/remaster/InitialStartup.dart';
|
import 'package:bus_infotainment/remaster/InitialStartup.dart';
|
||||||
|
import 'package:bus_infotainment/remaster/dashboard.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||||
|
|
||||||
@@ -17,7 +18,11 @@ class RemasteredApp extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
|
|
||||||
routes: {
|
routes: {
|
||||||
'/': (context) => InitialStartup()
|
'/setup': (context) => InitialStartup(),
|
||||||
|
'/': (context) => HomePage_Re(),
|
||||||
|
'/routes': (context) => RoutePage(),
|
||||||
|
'/enroute': (context) => EnRoutePage(),
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|||||||
+456
-311
@@ -1,365 +1,510 @@
|
|||||||
|
|
||||||
|
import 'package:bus_infotainment/backend/live_information.dart';
|
||||||
import 'package:bus_infotainment/pages/components/ibus_display.dart';
|
import 'package:bus_infotainment/pages/components/ibus_display.dart';
|
||||||
|
import 'package:bus_infotainment/tfl_datasets.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'dart:io' show Platform;
|
import 'dart:io' show Platform;
|
||||||
|
|
||||||
|
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_carousel_widget/flutter_carousel_widget.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||||
import 'package:text_scroll/text_scroll.dart';
|
import 'package:text_scroll/text_scroll.dart';
|
||||||
|
|
||||||
|
import '../backend/modules/tube_info.dart';
|
||||||
|
|
||||||
Color rgb(int r, int g, int b) {
|
Color rgb(int r, int g, int b) {
|
||||||
return Color.fromRGBO(r, g, b, 1);
|
return Color.fromRGBO(r, g, b, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
class Dashboard extends StatelessWidget {
|
class HomePage_Re extends StatefulWidget {
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<HomePage_Re> createState() => _HomePage_ReState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _HomePage_ReState extends State<HomePage_Re> {
|
||||||
|
Future<bool> _shouldRedirectToSetup() async {
|
||||||
|
|
||||||
|
List<bool> perms = [];
|
||||||
|
|
||||||
|
perms.addAll([
|
||||||
|
await Permission.manageExternalStorage.isGranted,
|
||||||
|
await Permission.location.isGranted
|
||||||
|
]);
|
||||||
|
|
||||||
|
bool shouldRedirectA = !perms.contains(false);
|
||||||
|
bool shouldRedirectB = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Uint8List bytes = await LiveInformation().announcementModule.getBundleBytes();
|
||||||
|
shouldRedirectB = true;
|
||||||
|
} catch (e) {
|
||||||
|
print("Failed to load bundle");
|
||||||
|
shouldRedirectB = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Should redirect to setup: ${shouldRedirectA || shouldRedirectB}");
|
||||||
|
print("Permissions: $shouldRedirectA");
|
||||||
|
print("Bundle: $shouldRedirectB");
|
||||||
|
print("Permissions_indv: $perms");
|
||||||
|
|
||||||
|
return !shouldRedirectA || !shouldRedirectB;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
|
||||||
|
_shouldRedirectToSetup().then((value) {
|
||||||
|
if (value) {
|
||||||
|
Navigator.pushNamed(context, "/setup");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// TODO: implement build
|
// TODO: implement build
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
body: Container(
|
||||||
|
|
||||||
backgroundColor: Colors.grey.shade900,
|
padding: EdgeInsets.all(16),
|
||||||
|
|
||||||
bottomNavigationBar: ((defaultTargetPlatform == TargetPlatform.android || defaultTargetPlatform == TargetPlatform.iOS)) ? null : BottomAppBar(
|
alignment: Alignment.center,
|
||||||
|
|
||||||
color: Colors.grey.shade800,
|
child: SizedBox(
|
||||||
|
|
||||||
child: Row(
|
width: double.infinity,
|
||||||
children: [
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Icons.home),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pushNamed(context, "/dashboard");
|
|
||||||
},
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Icons.search),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pushNamed(context, "/search");
|
|
||||||
},
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Icons.settings),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pushNamed(context, "/settings");
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
body: Column(
|
child: Column(
|
||||||
children: [
|
|
||||||
|
|
||||||
Container(
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
|
||||||
margin: EdgeInsets.all(16),
|
children: [
|
||||||
padding: EdgeInsets.all(8),
|
|
||||||
|
|
||||||
decoration: BoxDecoration(
|
Text(
|
||||||
|
"Choose mode:",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 32,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
|
||||||
color: Colors.grey.shade800
|
ShadCard(
|
||||||
|
title: Text("Solo mode"),
|
||||||
|
width: double.infinity,
|
||||||
|
description: Text(
|
||||||
|
"Choose this mode if you are only using this device. (No internet required)"
|
||||||
|
),
|
||||||
|
content: Column(
|
||||||
|
|
||||||
),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
|
||||||
// height: 100,
|
children: [
|
||||||
|
|
||||||
child: ibus_display()
|
SizedBox(
|
||||||
),
|
height: 4,
|
||||||
|
|
||||||
Expanded(
|
|
||||||
child: Row(
|
|
||||||
|
|
||||||
children: [
|
|
||||||
|
|
||||||
if (false)
|
|
||||||
if (!(defaultTargetPlatform == TargetPlatform.android || defaultTargetPlatform == TargetPlatform.iOS))
|
|
||||||
NavigationRail(
|
|
||||||
|
|
||||||
selectedIndex: 0,
|
|
||||||
|
|
||||||
groupAlignment: 1,
|
|
||||||
|
|
||||||
destinations: [
|
|
||||||
|
|
||||||
NavigationRailDestination(
|
|
||||||
icon: Icon(Icons.home),
|
|
||||||
label: Text("Dashboard"),
|
|
||||||
),
|
),
|
||||||
|
|
||||||
NavigationRailDestination(
|
ShadButton.secondary(
|
||||||
icon: Icon(Icons.search),
|
onPressed: () {
|
||||||
label: Text("Routes")
|
Navigator.pushNamed(context, "/routes");
|
||||||
),
|
},
|
||||||
|
text: Text("Continue"),
|
||||||
NavigationRailDestination(
|
|
||||||
icon: Icon(Icons.settings),
|
|
||||||
label: Text("Settings")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
|
||||||
Expanded(
|
SizedBox(
|
||||||
child: Container(
|
height: 16,
|
||||||
|
),
|
||||||
|
|
||||||
child: Column(
|
ShadCard(
|
||||||
|
title: Text("Multi mode"),
|
||||||
|
width: double.infinity,
|
||||||
|
description: Text(
|
||||||
|
"Choose this mode if you are using multiple devices. (Internet required)"
|
||||||
|
),
|
||||||
|
content: Column(
|
||||||
|
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
|
||||||
Container(
|
children: [
|
||||||
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
horizontal: 16
|
|
||||||
),
|
|
||||||
|
|
||||||
child: IntrinsicHeight(
|
|
||||||
child: Row(
|
|
||||||
|
|
||||||
children: [
|
|
||||||
|
|
||||||
Expanded(
|
|
||||||
child: Container(
|
|
||||||
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
|
|
||||||
color: Colors.grey.shade800
|
|
||||||
|
|
||||||
),
|
|
||||||
|
|
||||||
padding: EdgeInsets.all(16),
|
|
||||||
|
|
||||||
child: IntrinsicWidth(
|
|
||||||
child: Column(
|
|
||||||
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
|
|
||||||
children: [
|
|
||||||
|
|
||||||
Row(
|
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"Bus Route:",
|
|
||||||
style: GoogleFonts.interTight(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.white
|
|
||||||
)
|
|
||||||
),
|
|
||||||
|
|
||||||
SizedBox(
|
|
||||||
width: 16,
|
|
||||||
),
|
|
||||||
|
|
||||||
Text(
|
|
||||||
"11",
|
|
||||||
style: GoogleFonts.interTight(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.normal,
|
|
||||||
color: Colors.white
|
|
||||||
)
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
// mainAxisSize: MainAxisSize.min,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"Destination:",
|
|
||||||
style: GoogleFonts.interTight(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.white
|
|
||||||
)
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(
|
|
||||||
width: 16,
|
|
||||||
),
|
|
||||||
|
|
||||||
Text(
|
|
||||||
"Fullham Broadway",
|
|
||||||
style: GoogleFonts.interTight(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.normal,
|
|
||||||
color: Colors.white
|
|
||||||
)
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(
|
|
||||||
height: 8,
|
|
||||||
),
|
|
||||||
|
|
||||||
Row(
|
|
||||||
// mainAxisSize: MainAxisSize.min,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"Next Stop:",
|
|
||||||
style: GoogleFonts.interTight(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.white
|
|
||||||
)
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(
|
|
||||||
width: 16,
|
|
||||||
),
|
|
||||||
|
|
||||||
Text(
|
|
||||||
"St Thomas Hospital / County Hall",
|
|
||||||
style: GoogleFonts.interTight(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.normal,
|
|
||||||
color: Colors.white
|
|
||||||
),
|
|
||||||
overflow: TextOverflow.fade,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
Row(
|
|
||||||
// mainAxisSize: MainAxisSize.min,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"Last Stop:",
|
|
||||||
style: GoogleFonts.interTight(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.white
|
|
||||||
),
|
|
||||||
overflow: TextOverflow.fade,
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(
|
|
||||||
width: 16,
|
|
||||||
),
|
|
||||||
|
|
||||||
Text(
|
|
||||||
"Fullham Town Hall",
|
|
||||||
style: GoogleFonts.interTight(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.normal,
|
|
||||||
color: Colors.white
|
|
||||||
),
|
|
||||||
overflow: TextOverflow.fade,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
],
|
|
||||||
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// SizedBox(
|
|
||||||
// width: 16,
|
|
||||||
// ),
|
|
||||||
//
|
|
||||||
// Column(
|
|
||||||
//
|
|
||||||
// children: [
|
|
||||||
//
|
|
||||||
// SizedBox(
|
|
||||||
//
|
|
||||||
// width: 150,
|
|
||||||
//
|
|
||||||
// child: FloatingActionButton(
|
|
||||||
// onPressed: () {
|
|
||||||
//
|
|
||||||
// },
|
|
||||||
//
|
|
||||||
// backgroundColor: Colors.red,
|
|
||||||
//
|
|
||||||
// child: Text(
|
|
||||||
// "Bus Stopping",
|
|
||||||
// style: GoogleFonts.interTight(
|
|
||||||
// fontSize: 18,
|
|
||||||
// fontWeight: FontWeight.bold,
|
|
||||||
// color: Colors.white
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
//
|
|
||||||
// SizedBox(
|
|
||||||
// height: 16
|
|
||||||
// ),
|
|
||||||
//
|
|
||||||
// SizedBox(
|
|
||||||
//
|
|
||||||
// width: 150,
|
|
||||||
// height: 100,
|
|
||||||
//
|
|
||||||
// child: FloatingActionButton(
|
|
||||||
// onPressed: () {
|
|
||||||
//
|
|
||||||
// },
|
|
||||||
//
|
|
||||||
// backgroundColor: Colors.grey.shade600,
|
|
||||||
//
|
|
||||||
// child: Text(
|
|
||||||
// "Acknowledge Bus Stop",
|
|
||||||
// style: GoogleFonts.interTight(
|
|
||||||
// fontSize: 18,
|
|
||||||
// fontWeight: FontWeight.bold,
|
|
||||||
// color: Colors.white
|
|
||||||
// ),
|
|
||||||
// textAlign: TextAlign.center,
|
|
||||||
//
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// ],
|
|
||||||
//
|
|
||||||
// )
|
|
||||||
|
|
||||||
],
|
|
||||||
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
],
|
|
||||||
|
|
||||||
|
SizedBox(
|
||||||
|
height: 4,
|
||||||
),
|
),
|
||||||
|
|
||||||
),
|
ShadButton.secondary(
|
||||||
)
|
onPressed: () {
|
||||||
|
Navigator.pushNamed(context, "/application");
|
||||||
|
},
|
||||||
|
text: Text("Continue"),
|
||||||
|
)
|
||||||
|
|
||||||
],
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
)
|
||||||
)
|
|
||||||
|
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class RoutePage extends StatelessWidget {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
body: Container(
|
||||||
|
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
|
||||||
|
alignment: Alignment.center,
|
||||||
|
|
||||||
|
child: SizedBox(
|
||||||
|
|
||||||
|
width: double.infinity,
|
||||||
|
|
||||||
|
child: Column(
|
||||||
|
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
|
||||||
|
children: [
|
||||||
|
|
||||||
|
Text(
|
||||||
|
"Routes",
|
||||||
|
style: ShadTheme.of(context).textTheme.h1,
|
||||||
|
),
|
||||||
|
|
||||||
|
Text(
|
||||||
|
"Nearby routes",
|
||||||
|
style: ShadTheme.of(context).textTheme.h4,
|
||||||
|
),
|
||||||
|
|
||||||
|
ExpandableCarousel(
|
||||||
|
options: CarouselOptions(
|
||||||
|
|
||||||
|
),
|
||||||
|
items: [
|
||||||
|
ShadCard(
|
||||||
|
title: Text("Route 34"),
|
||||||
|
content: ShadSelect(
|
||||||
|
options: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Text("Choose a variant"),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
selectedOptionBuilder: (BuildContext context, value) {
|
||||||
|
return Text("Choose a variant");
|
||||||
|
},
|
||||||
|
),
|
||||||
|
padding: EdgeInsets.all(10),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
Divider(),
|
||||||
|
|
||||||
|
RouteSearch()
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Widget> _getNearbyRoutes() {
|
||||||
|
|
||||||
|
final variants = [
|
||||||
|
"Walthamstow Central to Barnet Church",
|
||||||
|
"Walthamstow Central to Barnet Church",
|
||||||
|
];
|
||||||
|
|
||||||
|
List<Widget> widgets = [];
|
||||||
|
|
||||||
|
for (int i = 0; i < variants.length; i++) {
|
||||||
|
widgets.add(
|
||||||
|
ShadOption(
|
||||||
|
value: i,
|
||||||
|
child: Text(variants[i]!),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return widgets;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class RouteSearch extends StatefulWidget {
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<RouteSearch> createState() => _RouteSearchState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RouteSearchState extends State<RouteSearch> {
|
||||||
|
TextEditingController controller = TextEditingController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
|
List<Widget> routes = [];
|
||||||
|
|
||||||
|
for (BusRoute route in LiveInformation().busSequences.routes.values.toList()) {
|
||||||
|
|
||||||
|
if (controller.text.isNotEmpty && !route.routeNumber.toLowerCase().contains(controller.text.toLowerCase())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
routes.add(RouteCard(route: route));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Expanded(
|
||||||
|
child: Column(
|
||||||
|
|
||||||
|
children: [
|
||||||
|
|
||||||
|
ShadInput(
|
||||||
|
placeholder: Text("Search for a route..."),
|
||||||
|
controller: controller,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(
|
||||||
|
height: 4,
|
||||||
|
),
|
||||||
|
|
||||||
|
Expanded(
|
||||||
|
child: GridView.count(
|
||||||
|
crossAxisCount: 3,
|
||||||
|
children: [
|
||||||
|
...routes
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RouteCard extends StatelessWidget {
|
||||||
|
|
||||||
|
BusRoute route;
|
||||||
|
|
||||||
|
RouteCard({required this.route});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
// TODO: implement build
|
||||||
|
|
||||||
|
Map<String, Widget> variants = {};
|
||||||
|
|
||||||
|
for (BusRouteVariant variant in route.routeVariants.values) {
|
||||||
|
String variantLabel = "${variant.busStops.first.formattedStopName} -> ${variant.busStops.last.formattedStopName}";
|
||||||
|
|
||||||
|
variants[variantLabel] = ShadOption(
|
||||||
|
value: variant.routeVariant.toString(),
|
||||||
|
child: Text(variantLabel),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return AspectRatio(
|
||||||
|
aspectRatio: 1,
|
||||||
|
child: Container(
|
||||||
|
child: ShadButton.secondary(
|
||||||
|
text: Text(
|
||||||
|
"Route \n ${route.routeNumber}",
|
||||||
|
style: ShadTheme.of(context).textTheme.h3
|
||||||
|
),
|
||||||
|
padding: EdgeInsets.all(8),
|
||||||
|
width: 105,
|
||||||
|
height: 105,
|
||||||
|
|
||||||
|
onPressed: () {
|
||||||
|
showShadSheet(
|
||||||
|
side: ShadSheetSide.bottom,
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
|
||||||
|
List<Widget> variantWidgets = [];
|
||||||
|
|
||||||
|
for (BusRouteVariant variant in route.routeVariants.values) {
|
||||||
|
String variantLabel = "${variant.busStops.first.formattedStopName} -> ${variant.busStops.last.formattedStopName}";
|
||||||
|
|
||||||
|
variantWidgets.add(
|
||||||
|
ShadButton.outline(
|
||||||
|
text: SizedBox(
|
||||||
|
width: 800-490,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text("${variant.busStops.first.formattedStopName} ->"),
|
||||||
|
SizedBox(
|
||||||
|
height: 2,
|
||||||
|
),
|
||||||
|
Text(variant.busStops.last.formattedStopName)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
width: double.infinity,
|
||||||
|
height: 50,
|
||||||
|
padding: EdgeInsets.all(8),
|
||||||
|
onPressed: () async {
|
||||||
|
LiveInformation liveInformation = LiveInformation();
|
||||||
|
await liveInformation.setRouteVariant(variant);
|
||||||
|
|
||||||
|
liveInformation.announcementModule.queueAnnouncementByRouteVariant(routeVariant: variant);
|
||||||
|
|
||||||
|
Navigator.pushNamed(context, "/enroute");
|
||||||
|
},
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
variantWidgets.add(SizedBox(
|
||||||
|
height: 4,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ShadSheet(
|
||||||
|
title: Text("Route ${route.routeNumber} - Variants"),
|
||||||
|
|
||||||
|
content: Container(
|
||||||
|
width: 2000,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
...variantWidgets
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
padding: EdgeInsets.all(8),
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class EnRoutePage extends StatelessWidget {
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
// TODO: implement build
|
||||||
|
return Scaffold(
|
||||||
|
body: Column(
|
||||||
|
|
||||||
|
children: [
|
||||||
|
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.all(8),
|
||||||
|
child: ibus_display()
|
||||||
|
),
|
||||||
|
|
||||||
|
Divider(
|
||||||
|
height: 1,
|
||||||
|
),
|
||||||
|
|
||||||
|
ShadButton(
|
||||||
|
text: Text("Route scanner"),
|
||||||
|
onPressed: () {
|
||||||
|
|
||||||
|
LiveInformation liveInformation = LiveInformation();
|
||||||
|
|
||||||
|
TubeLine? line = liveInformation.tubeStations.getClosestLine(liveInformation.getRouteVariant()!);
|
||||||
|
|
||||||
|
ShadToaster.of(context).show(
|
||||||
|
ShadToast(
|
||||||
|
title: Text("Closest line"),
|
||||||
|
description: Text(line == null ? "No line found" : line.name),
|
||||||
|
duration: Duration(seconds: 5),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
ShadButton(
|
||||||
|
text: Text("dest"),
|
||||||
|
onPressed: () {
|
||||||
|
LiveInformation liveInformation = LiveInformation();
|
||||||
|
liveInformation.announcementModule.queueAnnouncementByRouteVariant(routeVariant: liveInformation.getRouteVariant()!);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
ShadSelectFormField<String>(
|
||||||
|
options: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Text("Choose a variant"),
|
||||||
|
),
|
||||||
|
...variants.values
|
||||||
|
],
|
||||||
|
selectedOptionBuilder: (context, value) => value ==
|
||||||
|
'none'
|
||||||
|
? const Text('Select a verified email to display')
|
||||||
|
: Text(variants.keys.toList()[int.parse(value)-1]!),
|
||||||
|
placeholder: Text("Choose a variant"),
|
||||||
|
|
||||||
|
),
|
||||||
|
*/
|
||||||
@@ -98,6 +98,18 @@ class BusSequences extends InfoModule {
|
|||||||
|
|
||||||
print("Loaded ${routes.length} routes");
|
print("Loaded ${routes.length} routes");
|
||||||
|
|
||||||
|
// Remove any empty routes
|
||||||
|
List<String> emptyRoutes = [];
|
||||||
|
for (String routeNumber in routes.keys) {
|
||||||
|
if (routes[routeNumber]!.routeVariants.isEmpty) {
|
||||||
|
emptyRoutes.add(routeNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String routeNumber in emptyRoutes) {
|
||||||
|
routes.remove(routeNumber);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -225,6 +237,7 @@ class BusRouteStop {
|
|||||||
|
|
||||||
// Replace space with underscore
|
// Replace space with underscore
|
||||||
stopName = stopName.replaceAll(' ', '_');
|
stopName = stopName.replaceAll(' ', '_');
|
||||||
|
stopName = stopName.replaceAll('-', '_');
|
||||||
|
|
||||||
// convert to all caps
|
// convert to all caps
|
||||||
stopName = stopName.toUpperCase();
|
stopName = stopName.toUpperCase();
|
||||||
@@ -263,6 +276,7 @@ class BusDestination {
|
|||||||
|
|
||||||
// Replace space with underscore
|
// Replace space with underscore
|
||||||
name = name.replaceAll(' ', '_');
|
name = name.replaceAll(' ', '_');
|
||||||
|
name = name.replaceAll('-', '_');
|
||||||
|
|
||||||
// convert to all caps
|
// convert to all caps
|
||||||
name = name.toUpperCase();
|
name = name.toUpperCase();
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ class NameBeautify {
|
|||||||
stopName = stopName.replaceAll(RegExp(r'\>.*\<'), '');
|
stopName = stopName.replaceAll(RegExp(r'\>.*\<'), '');
|
||||||
|
|
||||||
|
|
||||||
// remove any special characters except & and /
|
// remove any special characters except & and / and -
|
||||||
stopName = stopName.replaceAll(RegExp(r'[^a-zA-Z0-9&/ ]'), '');
|
stopName = stopName.replaceAll(RegExp(r'[^a-zA-Z0-9&\/\- ]'), '');
|
||||||
|
|
||||||
// remove any double spaces
|
// remove any double spaces
|
||||||
stopName = stopName.replaceAll(RegExp(r' '), ' ');
|
stopName = stopName.replaceAll(RegExp(r' '), ' ');
|
||||||
@@ -41,12 +41,17 @@ class NameBeautify {
|
|||||||
stopName = stopName.replaceAll(RegExp(phrase, caseSensitive: false), Longify[phrase]!);
|
stopName = stopName.replaceAll(RegExp(phrase, caseSensitive: false), Longify[phrase]!);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove any spaces at the start or end of the string
|
||||||
|
stopName = stopName.trim();
|
||||||
|
stopName = stopName.replaceAll(' ', ' ');
|
||||||
|
|
||||||
stopName = stopName.toLowerCase();
|
stopName = stopName.toLowerCase();
|
||||||
// Capitalify the first letter of each word
|
// Capitalify the first letter of each word
|
||||||
try {
|
try {
|
||||||
stopName = stopName.split(' ').map((word) => word[0].toUpperCase() + word.substring(1)).join(' ');
|
stopName = stopName.split(' ').map((word) => word[0].toUpperCase() + word.substring(1)).join(' ');
|
||||||
} catch (e) {}
|
} catch (e) {
|
||||||
|
print("Error capitalifying stop name: $stopName");
|
||||||
|
}
|
||||||
|
|
||||||
return stopName;
|
return stopName;
|
||||||
|
|
||||||
|
|||||||
@@ -25,14 +25,14 @@ class AudioWrapper {
|
|||||||
|
|
||||||
print("AudioWrapper mode: $mode");
|
print("AudioWrapper mode: $mode");
|
||||||
|
|
||||||
mode = AudioWrapper_Mode.Mobile;
|
mode = AudioWrapper_Mode.Web;
|
||||||
}
|
}
|
||||||
|
|
||||||
justaudio.AudioSource _convertSource_JustAudio(AudioWrapperSource source){
|
justaudio.AudioSource _convertSource_JustAudio(AudioWrapperSource source){
|
||||||
if (source is AudioWrapperByteSource){
|
if (source is AudioWrapperByteSource){
|
||||||
return _ByteSource(source.bytes);
|
return _ByteSource(source.bytes);
|
||||||
} else if (source is AudioWrapperAssetSource){
|
} else if (source is AudioWrapperAssetSource){
|
||||||
return justaudio.AudioSource.asset(source.assetPath);
|
return justaudio.AudioSource.asset("assets/" + source.assetPath);
|
||||||
} else {
|
} else {
|
||||||
throw Exception("Unknown source type");
|
throw Exception("Unknown source type");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <audioplayers_linux/audioplayers_linux_plugin.h>
|
#include <audioplayers_linux/audioplayers_linux_plugin.h>
|
||||||
|
#include <rive_common/rive_plugin.h>
|
||||||
#include <screen_retriever/screen_retriever_plugin.h>
|
#include <screen_retriever/screen_retriever_plugin.h>
|
||||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||||
#include <window_manager/window_manager_plugin.h>
|
#include <window_manager/window_manager_plugin.h>
|
||||||
@@ -16,6 +17,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
|
|||||||
g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
|
g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin");
|
||||||
audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar);
|
audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar);
|
||||||
|
g_autoptr(FlPluginRegistrar) rive_common_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "RivePlugin");
|
||||||
|
rive_plugin_register_with_registrar(rive_common_registrar);
|
||||||
g_autoptr(FlPluginRegistrar) screen_retriever_registrar =
|
g_autoptr(FlPluginRegistrar) screen_retriever_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
|
||||||
screen_retriever_plugin_register_with_registrar(screen_retriever_registrar);
|
screen_retriever_plugin_register_with_registrar(screen_retriever_registrar);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
audioplayers_linux
|
audioplayers_linux
|
||||||
|
rive_common
|
||||||
screen_retriever
|
screen_retriever
|
||||||
url_launcher_linux
|
url_launcher_linux
|
||||||
window_manager
|
window_manager
|
||||||
|
|||||||
+30
-54
@@ -5,18 +5,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: appwrite
|
name: appwrite
|
||||||
sha256: "335faac24642aaf66627c21ce26a5c64bcbc3911e624c61b9dfbe0eec7be1342"
|
sha256: d95476adb1bbb72ae9eb31adffb9205b5cfe24abc4f483982eadceb396d433fb
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "12.0.1"
|
version: "12.0.3"
|
||||||
archive:
|
archive:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: archive
|
name: archive
|
||||||
sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d"
|
sha256: "0763b45fa9294197a2885c8567927e2830ade852e5c896fd4ab7e0e348d0f373"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.4.10"
|
version: "3.5.0"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -145,14 +145,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.18.0"
|
version: "1.18.0"
|
||||||
convert:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: convert
|
|
||||||
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.1.1"
|
|
||||||
cookie_jar:
|
cookie_jar:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -197,10 +189,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: device_info_plus
|
name: device_info_plus
|
||||||
sha256: "77f757b789ff68e4eaf9c56d1752309bd9f7ad557cb105b938a7f8eb89e59110"
|
sha256: eead12d1a1ed83d8283ab4c2f3fca23ac4082f29f25f29dff0f758f57d06ec91
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "9.1.2"
|
version: "10.1.0"
|
||||||
device_info_plus_platform_interface:
|
device_info_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -245,10 +237,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: file_picker
|
name: file_picker
|
||||||
sha256: b6283d7387310ad83bc4f3bc245b75d223a032ae6eba275afcd585de2b9a1476
|
sha256: "29c90806ac5f5fb896547720b73b17ee9aed9bba540dc5d91fe29f8c5745b10a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.0.1"
|
version: "8.0.3"
|
||||||
fixnum:
|
fixnum:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -270,6 +262,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.5.0"
|
version: "4.5.0"
|
||||||
|
flutter_carousel_widget:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_carousel_widget
|
||||||
|
sha256: "37b9e55e4cafffe358152b016db24153e756152aa07c4214cfe6ee902cd69a01"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.0"
|
||||||
flutter_lints:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@@ -437,14 +437,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.18.1"
|
version: "0.18.1"
|
||||||
js:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: js
|
|
||||||
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.7.1"
|
|
||||||
just_audio:
|
just_audio:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -473,10 +465,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: just_audio_windows
|
name: just_audio_windows
|
||||||
sha256: "7b8801f3987e98a2002cd23b5600b2daf162248ff1413266fb44c84448c1c0d3"
|
sha256: "48ab2dec79cf6097550602fe07b1a644f341450e138dc8fdc23e42ce0ed2d928"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.0"
|
version: "0.2.1"
|
||||||
latlong2:
|
latlong2:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -573,14 +565,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
version: "2.0.0"
|
||||||
modular_ui:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: modular_ui
|
|
||||||
sha256: c7bfa2d509f6b55f45833ea5a46c7b40030c2a3c8fb26f5211eab440235ee99f
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.0.5"
|
|
||||||
ntp:
|
ntp:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -593,18 +577,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: package_info_plus
|
name: package_info_plus
|
||||||
sha256: "7e76fad405b3e4016cd39d08f455a4eb5199723cf594cd1b8916d47140d93017"
|
sha256: b93d8b4d624b4ea19b0a5a208b2d6eff06004bc3ce74c06040b120eeadd00ce0
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.2.0"
|
version: "8.0.0"
|
||||||
package_info_plus_platform_interface:
|
package_info_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: package_info_plus_platform_interface
|
name: package_info_plus_platform_interface
|
||||||
sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6"
|
sha256: f49918f3433a3146047372f9d4f1f847511f2acd5cd030e1f44fe5a50036b70e
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "3.0.0"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -741,14 +725,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.8"
|
version: "2.1.8"
|
||||||
pointycastle:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: pointycastle
|
|
||||||
sha256: "79fbafed02cfdbe85ef3fd06c7f4bc2cbcba0177e61b765264853d4253b21744"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.9.0"
|
|
||||||
polylabel:
|
polylabel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -769,18 +745,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: rive
|
name: rive
|
||||||
sha256: ec44b6cf7341e21727c4b0e762f4ac82f9a45f7e52df3ebad2d1289a726fbaaf
|
sha256: "95690a0fb4f6e195c53b217ab3cc0e0b0f443c670adbdee9d57d636a36b82b18"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.13.1"
|
version: "0.13.2"
|
||||||
rive_common:
|
rive_common:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: rive_common
|
name: rive_common
|
||||||
sha256: "0f070bc0e764c570abd8b34d744ef30d1292bd4051f2e0951bbda755875fce6a"
|
sha256: "3eee68fcab3e0882090cea5a8cf7acea7967f469a34a2580322575603b094435"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.3"
|
version: "0.4.5"
|
||||||
rxdart:
|
rxdart:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -801,10 +777,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: shadcn_ui
|
name: shadcn_ui
|
||||||
sha256: "06c25555efbabe6d1d6acd6397bff4940e931d53ac3448e3a4f6a2287ab6798d"
|
sha256: df9aedbd18bd60160c1c54717eef47fe4f90422f843070ccbc32786193803f7d
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.3"
|
version: "0.4.1"
|
||||||
shared_preferences:
|
shared_preferences:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1110,10 +1086,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: win32
|
name: win32
|
||||||
sha256: "0a989dc7ca2bb51eac91e8fd00851297cfffd641aa7538b165c62637ca0eaa4a"
|
sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.4.0"
|
version: "5.5.0"
|
||||||
win32_registry:
|
win32_registry:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
+5
-2
@@ -50,8 +50,8 @@ dependencies:
|
|||||||
vector_math: ^2.1.4
|
vector_math: ^2.1.4
|
||||||
permission_handler: ^11.3.0
|
permission_handler: ^11.3.0
|
||||||
file_picker: ^8.0.0+1
|
file_picker: ^8.0.0+1
|
||||||
shadcn_ui: ^0.3.3
|
shadcn_ui: ^0.4.1
|
||||||
modular_ui: ^0.0.5
|
flutter_carousel_widget: ^2.2.0
|
||||||
|
|
||||||
|
|
||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
@@ -90,6 +90,9 @@ flutter:
|
|||||||
- assets/datasets/bus-blinds.csv
|
- assets/datasets/bus-blinds.csv
|
||||||
- assets/audio/5-seconds-of-silence.mp3
|
- assets/audio/5-seconds-of-silence.mp3
|
||||||
- assets/version.txt
|
- assets/version.txt
|
||||||
|
- assets/audio/R_RAIL_REPLACEMENT_SERVICE_001.mp3
|
||||||
|
- assets/datasets/tube_stations.json
|
||||||
|
- assets/audio/rail_replacement/
|
||||||
|
|
||||||
# - images/a_dot_burr.jpeg
|
# - images/a_dot_burr.jpeg
|
||||||
# - images/a_dot_ham.jpeg
|
# - images/a_dot_ham.jpeg
|
||||||
|
|||||||
Reference in New Issue
Block a user