ToLaptop
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -41,3 +41,4 @@ app.*.map.json
|
||||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
||||
/assets/ibus_recordings.zip
|
||||
|
||||
@@ -41,6 +41,7 @@ android {
|
||||
targetSdkVersion flutter.targetSdkVersion
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
multiDexEnabled = true
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
<application
|
||||
android:label="bus_infotainment"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:networkSecurityConfig="@xml/network_security_config">
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
|
||||
6
android/app/src/main/res/xml/network_security_config.xml
Normal file
6
android/app/src/main/res/xml/network_security_config.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<domain-config cleartextTrafficPermitted="true">
|
||||
<domain includeSubdomains="false">127.0.0.1</domain>
|
||||
</domain-config>
|
||||
</network-security-config>
|
||||
BIN
assets/audio/manual_announcements/buggysafety.mp3
Normal file
BIN
assets/audio/manual_announcements/buggysafety.mp3
Normal file
Binary file not shown.
BIN
assets/audio/manual_announcements/busondiversion.mp3
Normal file
BIN
assets/audio/manual_announcements/busondiversion.mp3
Normal file
Binary file not shown.
BIN
assets/audio/manual_announcements/busterminateshere.mp3
Normal file
BIN
assets/audio/manual_announcements/busterminateshere.mp3
Normal file
Binary file not shown.
BIN
assets/audio/manual_announcements/cctvoperation.mp3
Normal file
BIN
assets/audio/manual_announcements/cctvoperation.mp3
Normal file
Binary file not shown.
BIN
assets/audio/manual_announcements/destinationchange.mp3
Normal file
BIN
assets/audio/manual_announcements/destinationchange.mp3
Normal file
Binary file not shown.
BIN
assets/audio/manual_announcements/driverchange.mp3
Normal file
BIN
assets/audio/manual_announcements/driverchange.mp3
Normal file
Binary file not shown.
BIN
assets/audio/manual_announcements/envirobell.mp3
Normal file
BIN
assets/audio/manual_announcements/envirobell.mp3
Normal file
Binary file not shown.
BIN
assets/audio/manual_announcements/facecovering.mp3
Normal file
BIN
assets/audio/manual_announcements/facecovering.mp3
Normal file
Binary file not shown.
BIN
assets/audio/manual_announcements/movedownthebus.mp3
Normal file
BIN
assets/audio/manual_announcements/movedownthebus.mp3
Normal file
Binary file not shown.
BIN
assets/audio/manual_announcements/nextstopclosed.wav
Normal file
BIN
assets/audio/manual_announcements/nextstopclosed.wav
Normal file
Binary file not shown.
BIN
assets/audio/manual_announcements/nostanding.mp3
Normal file
BIN
assets/audio/manual_announcements/nostanding.mp3
Normal file
Binary file not shown.
BIN
assets/audio/manual_announcements/readytodepart.mp3
Normal file
BIN
assets/audio/manual_announcements/readytodepart.mp3
Normal file
Binary file not shown.
BIN
assets/audio/manual_announcements/safedooropening.mp3
Normal file
BIN
assets/audio/manual_announcements/safedooropening.mp3
Normal file
Binary file not shown.
BIN
assets/audio/manual_announcements/seatsupstairs.mp3
Normal file
BIN
assets/audio/manual_announcements/seatsupstairs.mp3
Normal file
Binary file not shown.
BIN
assets/audio/manual_announcements/serviceregulation.mp3
Normal file
BIN
assets/audio/manual_announcements/serviceregulation.mp3
Normal file
Binary file not shown.
BIN
assets/audio/manual_announcements/wheelchairspace1.mp3
Normal file
BIN
assets/audio/manual_announcements/wheelchairspace1.mp3
Normal file
Binary file not shown.
BIN
assets/audio/manual_announcements/wheelchairspace2.mp3
Normal file
BIN
assets/audio/manual_announcements/wheelchairspace2.mp3
Normal file
Binary file not shown.
BIN
assets/audio/to_destination.wav
Normal file
BIN
assets/audio/to_destination.wav
Normal file
Binary file not shown.
69343
assets/datasets/bus-sequences.csv
Normal file
69343
assets/datasets/bus-sequences.csv
Normal file
File diff suppressed because it is too large
Load Diff
BIN
assets/fonts/ibus/london-buses-ibus.ttf
Normal file
BIN
assets/fonts/ibus/london-buses-ibus.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/lcd/lcddot.ttf
Normal file
BIN
assets/fonts/lcd/lcddot.ttf
Normal file
Binary file not shown.
@@ -28,8 +28,15 @@ class AnnouncementCache extends AudioCache {
|
||||
final archive = ZipDecoder().decodeBytes(bytes.buffer.asUint8List());
|
||||
|
||||
for (final file in archive) {
|
||||
if (Announcements.contains(file.name)) {
|
||||
_audioCache[file.name] = file.content;
|
||||
|
||||
String filename = file.name;
|
||||
if (filename.contains("/")) {
|
||||
filename = filename.split("/").last;
|
||||
}
|
||||
|
||||
if (Announcements.contains(filename)) {
|
||||
_audioCache[filename] = file.content;
|
||||
print("Loaded announcement: ${filename}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,21 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:bus_infotainment/pages/audio_cache_test.dart';
|
||||
import 'package:bus_infotainment/pages/tfl_dataset_test.dart';
|
||||
import 'package:bus_infotainment/singletons/live_information.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
void main() {
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
LiveInformation liveInformation = LiveInformation();
|
||||
await liveInformation.LoadDatasets();
|
||||
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
|
||||
const MyApp({super.key});
|
||||
|
||||
// This widget is the root of your application.
|
||||
@@ -14,27 +24,19 @@ class MyApp extends StatelessWidget {
|
||||
return MaterialApp(
|
||||
title: 'Flutter Demo',
|
||||
theme: ThemeData(
|
||||
// This is the theme of your application.
|
||||
//
|
||||
// TRY THIS: Try running your application with "flutter run". You'll see
|
||||
// the application has a purple toolbar. Then, without quitting the app,
|
||||
// try changing the seedColor in the colorScheme below to Colors.green
|
||||
// and then invoke "hot reload" (save your changes or press the "hot
|
||||
// reload" button in a Flutter-supported IDE, or press "r" if you used
|
||||
// the command line to start the app).
|
||||
//
|
||||
// Notice that the counter didn't reset back to zero; the application
|
||||
// state is not lost during the reload. To reset the state, use hot
|
||||
// restart instead.
|
||||
//
|
||||
// This works for code too, not just values: Most code changes can be
|
||||
// tested with just a hot reload.
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
||||
useMaterial3: true,
|
||||
brightness: Brightness.light,
|
||||
/* light theme settings */
|
||||
),
|
||||
darkTheme: ThemeData(
|
||||
brightness: Brightness.dark,
|
||||
/* dark theme settings */
|
||||
),
|
||||
themeMode: ThemeMode.dark,
|
||||
|
||||
routes: {
|
||||
'/home': (context) => const MyHomePage(title: 'Flutter Demo Home Page'),
|
||||
'/': (context) => AudioCacheTest(),
|
||||
'/audiocachetest': (context) => AudioCacheTest(),
|
||||
'/': (context) => TfL_Dataset_Test(),
|
||||
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
|
||||
import 'package:audioplayers/audioplayers.dart';
|
||||
|
||||
import 'package:bus_infotainment/audio_cache.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -38,8 +38,8 @@ class AudioCacheTest extends StatelessWidget {
|
||||
announcements.add(
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
AudioPlayer player = AudioPlayer();
|
||||
player.play(BytesSource(_announcementCache[key]));
|
||||
// AudioPlayer player = AudioPlayer();
|
||||
// player.play(BytesSource(_announcementCache[key]));
|
||||
},
|
||||
child: Text(
|
||||
key
|
||||
|
||||
156
lib/pages/components/ibus_display.dart
Normal file
156
lib/pages/components/ibus_display.dart
Normal file
@@ -0,0 +1,156 @@
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bus_infotainment/singletons/live_information.dart';
|
||||
import 'package:bus_infotainment/utils/delegates.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:text_scroll/text_scroll.dart';
|
||||
|
||||
class ibus_display extends StatefulWidget {
|
||||
|
||||
bool hasBorder = true;
|
||||
|
||||
ibus_display({
|
||||
this.hasBorder = true
|
||||
});
|
||||
|
||||
@override
|
||||
State<ibus_display> createState() => _ibus_displayState();
|
||||
}
|
||||
|
||||
class _ibus_displayState extends State<ibus_display> {
|
||||
|
||||
String topLine = "*** NO MESSAGE ***";
|
||||
|
||||
late final ListenerReceipt<AnnouncementQueueEntry> _receipt;
|
||||
|
||||
_ibus_displayState(){
|
||||
|
||||
LiveInformation liveInformation = LiveInformation();
|
||||
_receipt = liveInformation.announcementDelegate.addListener((value) {
|
||||
topLine = value.displayText;
|
||||
setState(() {
|
||||
|
||||
});
|
||||
});
|
||||
topLine = liveInformation.CurrentAnnouncement;
|
||||
|
||||
}
|
||||
|
||||
Timer _timer() => Timer.periodic(Duration(seconds: 1), (timer) {
|
||||
setState(() {
|
||||
topLine = LiveInformation().CurrentAnnouncement;
|
||||
});
|
||||
});
|
||||
|
||||
String _padString(String input){
|
||||
|
||||
if (input.length < 40){
|
||||
print("Input is too short");
|
||||
return input;
|
||||
}
|
||||
|
||||
String prefix = "";
|
||||
String suffix = "";
|
||||
for (int i = 0; i < 80; i++){
|
||||
prefix += " ";
|
||||
}
|
||||
|
||||
return prefix + input + suffix;
|
||||
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
|
||||
LiveInformation().announcementDelegate.removeListener(_receipt);
|
||||
|
||||
super.dispose();
|
||||
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
|
||||
return Container(
|
||||
|
||||
width: double.infinity,
|
||||
|
||||
child: FittedBox(
|
||||
alignment: Alignment.center,
|
||||
|
||||
child: Stack(
|
||||
children: [
|
||||
Container(
|
||||
|
||||
// width: double.infinity,
|
||||
// height: 100,
|
||||
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black,
|
||||
border: widget.hasBorder ? Border.all(color: Colors.grey.shade900, width: 2) : null,
|
||||
),
|
||||
|
||||
clipBehavior: Clip.hardEdge,
|
||||
|
||||
child: Transform.scale(
|
||||
scale: 1.3,
|
||||
transformHitTests: false,
|
||||
child: Transform.translate(
|
||||
|
||||
offset: Offset(0, 4),
|
||||
|
||||
child: Column(
|
||||
|
||||
children: [
|
||||
Transform.translate(
|
||||
offset: Offset(0, 5),
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
width: 32*4*3,
|
||||
child: TextScroll(
|
||||
_padString(topLine),
|
||||
velocity: Velocity(pixelsPerSecond: Offset(120, 0)),
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
color: Colors.orange,
|
||||
fontFamily: "ibus"
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Transform.translate(
|
||||
offset: Offset(0, -7),
|
||||
child: Text(
|
||||
"Bus Stopping",
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
color: Colors.orange,
|
||||
fontFamily: "ibus",
|
||||
height: 1.5
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
),
|
||||
|
||||
Positioned.fill(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
border: widget.hasBorder ? Border.all(color: Colors.grey.shade900, width: 2) : null,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
85
lib/pages/display.dart
Normal file
85
lib/pages/display.dart
Normal file
@@ -0,0 +1,85 @@
|
||||
|
||||
import 'package:bus_infotainment/pages/components/ibus_display.dart';
|
||||
import 'package:bus_infotainment/pages/tfl_dataset_test.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class pages_Display extends StatefulWidget {
|
||||
|
||||
final TfL_Dataset_TestState _tfL_Dataset_TestState;
|
||||
|
||||
pages_Display(this._tfL_Dataset_TestState, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<pages_Display> createState() => _pages_DisplayState();
|
||||
}
|
||||
|
||||
class _pages_DisplayState extends State<pages_Display> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
|
||||
decoration: BoxDecoration(
|
||||
color: widget._tfL_Dataset_TestState.hideUI ? Colors.black : Theme.of(context).colorScheme.background
|
||||
),
|
||||
|
||||
width: double.infinity,
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
|
||||
if (MediaQuery.of(context).size.width < 600)
|
||||
Expanded(
|
||||
child: RotatedBox(
|
||||
quarterTurns: 1,
|
||||
child: ibus_display(
|
||||
hasBorder: false,
|
||||
),
|
||||
),
|
||||
)
|
||||
else
|
||||
Expanded(
|
||||
child: ibus_display(
|
||||
hasBorder: false,
|
||||
),
|
||||
),
|
||||
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Positioned.fill(
|
||||
child: Container(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.fullscreen),
|
||||
onPressed: () {
|
||||
|
||||
// Hide the app bar and nav bar
|
||||
widget._tfL_Dataset_TestState.setState(() {
|
||||
widget._tfL_Dataset_TestState.hideUI = !widget._tfL_Dataset_TestState.hideUI;
|
||||
});
|
||||
setState(() {
|
||||
|
||||
});
|
||||
|
||||
// Hide the notification bar and make the app full screen and display over notch
|
||||
if (widget._tfL_Dataset_TestState.hideUI) {
|
||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
|
||||
} else {
|
||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
|
||||
}
|
||||
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
410
lib/pages/home.dart
Normal file
410
lib/pages/home.dart
Normal file
@@ -0,0 +1,410 @@
|
||||
|
||||
|
||||
import 'package:bus_infotainment/pages/components/ibus_display.dart';
|
||||
import 'package:bus_infotainment/singletons/live_information.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class pages_Home extends StatelessWidget {
|
||||
const pages_Home({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
return Container(
|
||||
|
||||
width: double.infinity,
|
||||
|
||||
margin: const EdgeInsets.all(10),
|
||||
|
||||
child: Column(
|
||||
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
||||
children: [
|
||||
|
||||
// Text("Home Page"),
|
||||
|
||||
ibus_display(),
|
||||
|
||||
SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
|
||||
_QuickAnnouncements(),
|
||||
|
||||
],
|
||||
)
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _QuickAnnouncements extends StatefulWidget {
|
||||
|
||||
|
||||
_QuickAnnouncements({super.key});
|
||||
|
||||
@override
|
||||
State<_QuickAnnouncements> createState() => _QuickAnnouncementsState();
|
||||
}
|
||||
|
||||
class _QuickAnnouncementsState extends State<_QuickAnnouncements> {
|
||||
|
||||
List<Widget> announcements = [];
|
||||
|
||||
int _currentIndex = 0;
|
||||
|
||||
_QuickAnnouncementsState() {
|
||||
LiveInformation liveInformation = LiveInformation();
|
||||
|
||||
for (ManualAnnouncementEntry announcement in liveInformation.manualAnnouncements) {
|
||||
announcements.add(
|
||||
_QuickAnnouncement(announcement: announcement, index: liveInformation.manualAnnouncements.indexOf(announcement))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
|
||||
return Container(
|
||||
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.lightGreen.shade100,
|
||||
border: Border.all(
|
||||
color: Colors.black,
|
||||
width: 2
|
||||
),
|
||||
|
||||
|
||||
|
||||
),
|
||||
|
||||
padding: const EdgeInsets.all(2),
|
||||
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
height: 2,
|
||||
color: Colors.black,
|
||||
),
|
||||
|
||||
if (_currentIndex < announcements.length)
|
||||
announcements[_currentIndex + 0]
|
||||
else
|
||||
Container(
|
||||
height: 50,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.lightGreen.shade100,
|
||||
border: const Border.symmetric(
|
||||
vertical: BorderSide(
|
||||
color: Colors.black,
|
||||
width: 2
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Container(
|
||||
height: 2,
|
||||
color: Colors.black,
|
||||
),
|
||||
|
||||
if (_currentIndex + 1 < announcements.length)
|
||||
announcements[_currentIndex + 1]
|
||||
else
|
||||
Container(
|
||||
height: 50,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.lightGreen.shade100,
|
||||
border: const Border.symmetric(
|
||||
vertical: BorderSide(
|
||||
color: Colors.black,
|
||||
width: 2
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Container(
|
||||
height: 2,
|
||||
color: Colors.black,
|
||||
),
|
||||
|
||||
if (_currentIndex + 2 < announcements.length)
|
||||
announcements[_currentIndex + 2]
|
||||
else
|
||||
Container(
|
||||
height: 50,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.lightGreen.shade100,
|
||||
border: const Border.symmetric(
|
||||
vertical: BorderSide(
|
||||
color: Colors.black,
|
||||
width: 2
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Container(
|
||||
height: 2,
|
||||
color: Colors.black,
|
||||
),
|
||||
|
||||
if (_currentIndex + 3 < announcements.length)
|
||||
announcements[_currentIndex + 3]
|
||||
else
|
||||
Container(
|
||||
height: 50,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.lightGreen.shade100,
|
||||
border: const Border.symmetric(
|
||||
vertical: BorderSide(
|
||||
color: Colors.black,
|
||||
width: 2
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Container(
|
||||
height: 2,
|
||||
color: Colors.black,
|
||||
),
|
||||
|
||||
Container(
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.lightGreen.shade100,
|
||||
border: const Border.symmetric(
|
||||
vertical: BorderSide(
|
||||
color: Colors.black,
|
||||
width: 2
|
||||
)
|
||||
),
|
||||
),
|
||||
|
||||
alignment: Alignment.centerRight,
|
||||
|
||||
child: Row(
|
||||
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
||||
children: [
|
||||
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.lightGreen.shade100,
|
||||
border: const Border.symmetric(
|
||||
vertical: BorderSide(
|
||||
color: Colors.black,
|
||||
width: 2
|
||||
)
|
||||
),
|
||||
),
|
||||
|
||||
margin: const EdgeInsets.symmetric(
|
||||
horizontal: 4
|
||||
),
|
||||
|
||||
child: Container(
|
||||
child: Stack(
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
child: Icon(
|
||||
Icons.arrow_upward,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
Positioned.fill(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
_currentIndex = wrap(_currentIndex - 4, 0, announcements.length);
|
||||
setState(() {});
|
||||
print(_currentIndex);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.transparent,
|
||||
shadowColor: Colors.transparent,
|
||||
surfaceTintColor: Colors.transparent,
|
||||
foregroundColor: Colors.transparent,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(0),
|
||||
),
|
||||
),
|
||||
child: const Text(""),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
),
|
||||
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.lightGreen.shade100,
|
||||
border: const Border.symmetric(
|
||||
vertical: BorderSide(
|
||||
color: Colors.black,
|
||||
width: 2
|
||||
)
|
||||
),
|
||||
),
|
||||
|
||||
margin: const EdgeInsets.symmetric(
|
||||
horizontal: 4
|
||||
),
|
||||
|
||||
child: Container(
|
||||
child: Stack(
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
child: Icon(
|
||||
Icons.arrow_downward,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
Positioned.fill(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
_currentIndex = wrap(_currentIndex + 4, 0, announcements.length);
|
||||
setState(() {});
|
||||
print(_currentIndex);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.transparent,
|
||||
shadowColor: Colors.transparent,
|
||||
surfaceTintColor: Colors.transparent,
|
||||
foregroundColor: Colors.transparent,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(0),
|
||||
),
|
||||
),
|
||||
child: const Text(""),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
),
|
||||
|
||||
]
|
||||
|
||||
),
|
||||
|
||||
),
|
||||
Container(
|
||||
height: 2,
|
||||
color: Colors.black,
|
||||
),
|
||||
|
||||
]
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
int wrap(int i, int j, int length) {
|
||||
return ((i - j) % length + length) % length;
|
||||
}
|
||||
|
||||
class _QuickAnnouncement extends StatelessWidget {
|
||||
|
||||
final ManualAnnouncementEntry announcement;
|
||||
final int index;
|
||||
|
||||
const _QuickAnnouncement({super.key, required this.announcement, required this.index});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
Container(
|
||||
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.lightGreen.shade100,
|
||||
border: const Border.symmetric(
|
||||
vertical: BorderSide(
|
||||
color: Colors.black,
|
||||
width: 2
|
||||
)
|
||||
),
|
||||
),
|
||||
|
||||
padding: const EdgeInsets.all(5),
|
||||
|
||||
width: double.infinity,
|
||||
height: 50,
|
||||
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
announcement.shortName,
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
color: Colors.black,
|
||||
fontFamily: "lcd",
|
||||
height: 1,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
|
||||
alignment: Alignment.centerRight,
|
||||
|
||||
child: Text(
|
||||
(index+1).toString(),
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
color: Colors.black,
|
||||
fontFamily: "lcd",
|
||||
height: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
),
|
||||
|
||||
Positioned.fill(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
LiveInformation liveInformation = LiveInformation();
|
||||
liveInformation.queueAnnouncement(announcement);
|
||||
},
|
||||
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.transparent,
|
||||
shadowColor: Colors.transparent,
|
||||
surfaceTintColor: Colors.transparent,
|
||||
foregroundColor: Colors.transparent,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(0),
|
||||
),
|
||||
),
|
||||
|
||||
child: const Text("More"),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
397
lib/pages/routes.dart
Normal file
397
lib/pages/routes.dart
Normal file
@@ -0,0 +1,397 @@
|
||||
|
||||
import 'package:bus_infotainment/singletons/live_information.dart';
|
||||
import 'package:bus_infotainment/tfl_datasets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:text_scroll/text_scroll.dart';
|
||||
|
||||
class pages_Routes extends StatefulWidget {
|
||||
|
||||
@override
|
||||
State<pages_Routes> createState() => _pages_RoutesState();
|
||||
}
|
||||
|
||||
class _pages_RoutesState extends State<pages_Routes> {
|
||||
final TextEditingController _controller = TextEditingController(text: "");
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
LiveInformation liveInformation = LiveInformation();
|
||||
|
||||
List<Widget> routes = [];
|
||||
routes.add(SizedBox(height: 10));
|
||||
for (BusRoute route in liveInformation.busSequences!.routes.values) {
|
||||
|
||||
if (!route.routeNumber.toLowerCase().contains(_controller.text.toLowerCase())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
routes.add(_Route(route, this));
|
||||
routes.add(SizedBox(height: 10));
|
||||
}
|
||||
|
||||
return Container(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
width: double.infinity,
|
||||
// padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Column(
|
||||
|
||||
children: [
|
||||
|
||||
|
||||
|
||||
Container(
|
||||
|
||||
padding: const EdgeInsets.all(10),
|
||||
|
||||
|
||||
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
spreadRadius: 2,
|
||||
blurRadius: 4,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
child: TextField(
|
||||
controller: _controller,
|
||||
onChanged: (String value) {
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
Expanded(
|
||||
child: ListView(
|
||||
children: routes,
|
||||
),
|
||||
),
|
||||
|
||||
],
|
||||
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class _Route extends StatelessWidget {
|
||||
|
||||
final BusRoute route;
|
||||
final _pages_RoutesState tfL_Dataset_TestState;
|
||||
|
||||
const _Route(this.route, this.tfL_Dataset_TestState);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
List<Widget> Variants = [];
|
||||
|
||||
for (BusRouteVariant variant in route.routeVariants.values) {
|
||||
Variants.add(const SizedBox(height: 10));
|
||||
Variants.add(_Variant(route, variant, tfL_Dataset_TestState));
|
||||
}
|
||||
|
||||
return Container(
|
||||
|
||||
alignment: Alignment.center,
|
||||
|
||||
decoration: BoxDecoration(
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
spreadRadius: 2,
|
||||
blurRadius: 4,
|
||||
),
|
||||
],
|
||||
color: Colors.grey.shade900,
|
||||
),
|
||||
|
||||
margin: const EdgeInsets.symmetric(horizontal: 10),
|
||||
padding: const EdgeInsets.all(10),
|
||||
|
||||
width: 100,
|
||||
|
||||
child: Column(
|
||||
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
||||
children: [
|
||||
|
||||
Container(
|
||||
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade800,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
spreadRadius: 2,
|
||||
blurRadius: 2,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
padding: const EdgeInsets.all(5),
|
||||
|
||||
child: Text(
|
||||
"Route: ${route.routeNumber}",
|
||||
style: GoogleFonts.montserrat(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
height: 1
|
||||
),
|
||||
|
||||
),
|
||||
|
||||
),
|
||||
|
||||
ListView(
|
||||
children: Variants,
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
),
|
||||
|
||||
],
|
||||
|
||||
),
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _Variant extends StatelessWidget {
|
||||
|
||||
final BusRoute route;
|
||||
final BusRouteVariant variant;
|
||||
|
||||
final _pages_RoutesState tfL_Dataset_TestState;
|
||||
|
||||
const _Variant(this.route, this.variant, this.tfL_Dataset_TestState);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
return Container(
|
||||
|
||||
child: Stack(
|
||||
children: [
|
||||
|
||||
Container(
|
||||
|
||||
decoration: BoxDecoration(
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
spreadRadius: 2,
|
||||
blurRadius: 2,
|
||||
),
|
||||
],
|
||||
color: Colors.grey.shade800,
|
||||
),
|
||||
|
||||
padding: const EdgeInsets.all(5),
|
||||
|
||||
child: Column(
|
||||
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
||||
children: [
|
||||
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
"Start:",
|
||||
style: GoogleFonts.montserrat(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(width: 5),
|
||||
|
||||
Expanded(
|
||||
child: TextScroll(
|
||||
"${variant.busStops.first.formattedStopName}",
|
||||
mode: TextScrollMode.bouncing,
|
||||
pauseBetween: const Duration(seconds: 2),
|
||||
pauseOnBounce: const Duration(seconds: 2),
|
||||
style: GoogleFonts.montserrat(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.normal,
|
||||
color: Colors.white,
|
||||
height: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
],
|
||||
),
|
||||
|
||||
SizedBox(height: 5),
|
||||
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
"End:",
|
||||
style: GoogleFonts.montserrat(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(width: 5),
|
||||
|
||||
Expanded(
|
||||
child: TextScroll(
|
||||
"${variant.busStops.last.formattedStopName}",
|
||||
mode: TextScrollMode.bouncing,
|
||||
pauseBetween: const Duration(seconds: 2),
|
||||
pauseOnBounce: const Duration(seconds: 2),
|
||||
style: GoogleFonts.montserrat(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.normal,
|
||||
color: Colors.white,
|
||||
height: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
],
|
||||
),
|
||||
|
||||
|
||||
|
||||
],
|
||||
)
|
||||
),
|
||||
|
||||
Positioned.fill(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
print("Variant: ${variant.routeVariant}");
|
||||
|
||||
// Open dialog
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
contentPadding: const EdgeInsets.only(
|
||||
top: 15,
|
||||
left: 15,
|
||||
right: 15,
|
||||
bottom: 8,
|
||||
|
||||
),
|
||||
content: Column(
|
||||
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
||||
Text(
|
||||
"Select the following route?",
|
||||
style: GoogleFonts.montserrat(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
|
||||
Text(
|
||||
"${route.routeNumber} to ${variant.busStops.last.formattedStopName}",
|
||||
style: GoogleFonts.montserrat(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
],
|
||||
|
||||
),
|
||||
|
||||
actionsPadding: const EdgeInsets.only(
|
||||
left: 15,
|
||||
right: 15,
|
||||
bottom: 15,
|
||||
),
|
||||
actions: [
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
),
|
||||
|
||||
child: const Text("Cancel"),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
|
||||
LiveInformation liveInformation = LiveInformation();
|
||||
liveInformation.setRouteVariant(variant);
|
||||
tfL_Dataset_TestState.setState(() {});
|
||||
},
|
||||
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
),
|
||||
|
||||
child: const Text("Confirm"),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
},
|
||||
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.transparent,
|
||||
shadowColor: Colors.transparent,
|
||||
surfaceTintColor: Colors.transparent,
|
||||
foregroundColor: Colors.transparent,
|
||||
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(0),
|
||||
),
|
||||
|
||||
),
|
||||
|
||||
|
||||
child: Container()
|
||||
),
|
||||
)
|
||||
|
||||
],
|
||||
)
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
16
lib/pages/settings.dart
Normal file
16
lib/pages/settings.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class pages_Settings extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
child: Column(
|
||||
children: [
|
||||
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
148
lib/pages/tfl_dataset_test.dart
Normal file
148
lib/pages/tfl_dataset_test.dart
Normal file
@@ -0,0 +1,148 @@
|
||||
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:bus_infotainment/pages/display.dart';
|
||||
import 'package:bus_infotainment/pages/home.dart';
|
||||
import 'package:bus_infotainment/pages/routes.dart';
|
||||
import 'package:bus_infotainment/pages/settings.dart';
|
||||
import 'package:bus_infotainment/singletons/live_information.dart';
|
||||
import 'package:bus_infotainment/tfl_datasets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:text_scroll/text_scroll.dart';
|
||||
|
||||
class TfL_Dataset_Test extends StatefulWidget {
|
||||
|
||||
@override
|
||||
State<TfL_Dataset_Test> createState() => TfL_Dataset_TestState();
|
||||
}
|
||||
|
||||
class TfL_Dataset_TestState extends State<TfL_Dataset_Test> {
|
||||
|
||||
int _selectedIndex = 0;
|
||||
|
||||
bool hideUI = false;
|
||||
|
||||
late final List<Widget> Pages;
|
||||
|
||||
TfL_Dataset_TestState() {
|
||||
Pages = [
|
||||
pages_Home(),
|
||||
pages_Routes(),
|
||||
pages_Display(this),
|
||||
pages_Settings(),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
LiveInformation liveInformation = LiveInformation();
|
||||
|
||||
_selectedIndex = min(_selectedIndex, Pages.length - 1);
|
||||
_selectedIndex = max(_selectedIndex, 0);
|
||||
|
||||
return Scaffold(
|
||||
|
||||
appBar: !hideUI ? AppBar(
|
||||
|
||||
surfaceTintColor: Colors.transparent,
|
||||
|
||||
title: Column(
|
||||
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
||||
children: [
|
||||
|
||||
Text(
|
||||
"Bus Infotainment",
|
||||
style: GoogleFonts.montserrat(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
|
||||
Row(
|
||||
|
||||
children: [
|
||||
|
||||
Text(
|
||||
"Selected: ",
|
||||
style: GoogleFonts.montserrat(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
|
||||
if (liveInformation.getRouteVariant() != null)
|
||||
Container(
|
||||
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black,
|
||||
),
|
||||
|
||||
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 2),
|
||||
|
||||
child: Text(
|
||||
"${liveInformation.getRouteVariant()!.busRoute.routeNumber} to ${liveInformation.getRouteVariant()!.busStops.last.formattedStopName}",
|
||||
style: GoogleFonts.montserrat(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.orange.shade900,
|
||||
),
|
||||
),
|
||||
|
||||
)
|
||||
else
|
||||
Text(
|
||||
"None",
|
||||
style: GoogleFonts.montserrat(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
)
|
||||
|
||||
],
|
||||
|
||||
),
|
||||
) : null,
|
||||
body: Pages[_selectedIndex],
|
||||
|
||||
|
||||
bottomNavigationBar: !hideUI ? NavigationBar(
|
||||
selectedIndex: _selectedIndex,
|
||||
destinations: const [
|
||||
NavigationDestination(
|
||||
icon: Icon(Icons.home),
|
||||
label: "Home",
|
||||
),
|
||||
NavigationDestination(
|
||||
icon: Icon(Icons.bus_alert),
|
||||
label: "Routes",
|
||||
),
|
||||
NavigationDestination(
|
||||
icon: Icon(Icons.tv),
|
||||
label: "Display",
|
||||
),
|
||||
NavigationDestination(
|
||||
icon: Icon(Icons.settings),
|
||||
label: "Settings",
|
||||
),
|
||||
],
|
||||
onDestinationSelected: (int index) {
|
||||
setState(() {
|
||||
_selectedIndex = index;
|
||||
});
|
||||
},
|
||||
|
||||
) : null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,221 @@
|
||||
|
||||
// Singleton
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bus_infotainment/audio_cache.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/services.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:just_audio/just_audio.dart';
|
||||
|
||||
class LiveInformation {
|
||||
|
||||
static final LiveInformation _singleton = LiveInformation._internal();
|
||||
|
||||
factory LiveInformation() {
|
||||
return _singleton;
|
||||
}
|
||||
|
||||
LiveInformation._internal();
|
||||
|
||||
Future<void> LoadDatasets() async {
|
||||
|
||||
{
|
||||
// Load the bus sequences
|
||||
|
||||
try {
|
||||
|
||||
http.Response response = await http.get(Uri.parse('https://tfl.gov.uk/bus-sequences.csv'));
|
||||
|
||||
busSequences = BusSequences.fromCSV(response.body);
|
||||
|
||||
print("Loaded bus sequences from TFL");
|
||||
|
||||
} catch (e) {
|
||||
|
||||
String csv = await rootBundle.loadString("assets/datasets/bus-sequences.csv");
|
||||
|
||||
busSequences = BusSequences.fromCSV(csv);
|
||||
|
||||
print("Loaded bus sequences from assets");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
refreshTimer();
|
||||
}
|
||||
|
||||
Timer refreshTimer() => Timer.periodic(Duration(seconds: 1), (timer) {
|
||||
_handleAnnouncementQueue();
|
||||
});
|
||||
|
||||
|
||||
AudioWrapper audioPlayer = AudioWrapper();
|
||||
AnnouncementCache announcementCache = AnnouncementCache();
|
||||
List<AnnouncementQueueEntry> announcementQueue = [];
|
||||
EventDelegate<AnnouncementQueueEntry> announcementDelegate = EventDelegate();
|
||||
String CurrentAnnouncement = "*** NO MESSAGE ***";
|
||||
void _handleAnnouncementQueue() async {
|
||||
print("Handling announcement queue");
|
||||
if (audioPlayer.state != AudioWrapper_State.Playing) {
|
||||
if (announcementQueue.isNotEmpty) {
|
||||
AnnouncementQueueEntry announcement = announcementQueue.first;
|
||||
announcementDelegate.trigger(announcement);
|
||||
CurrentAnnouncement = announcement.displayText;
|
||||
|
||||
|
||||
|
||||
for (AudioWrapperSource source in announcement.audioSources) {
|
||||
|
||||
Duration? duration = await audioPlayer.play(source);
|
||||
if (source == announcement.audioSources.last) {
|
||||
announcementQueue.removeAt(0);
|
||||
}
|
||||
await Future.delayed(duration!);
|
||||
await Future.delayed(Duration(milliseconds: 150));
|
||||
}
|
||||
audioPlayer.stop();
|
||||
print("Popped announcement queue");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void announceRouteVariant(BusRouteVariant routeVariant) async {
|
||||
if (routeVariant == null) {
|
||||
return;
|
||||
}
|
||||
String display = "${routeVariant.busRoute.routeNumber} to ${routeVariant.busStops.last.formattedStopName}";
|
||||
|
||||
String audio_route = "R_${routeVariant.busRoute.routeNumber}_001.mp3";
|
||||
String audio_destination = routeVariant.busStops.last.getAudioFileName();
|
||||
|
||||
print("Audio file: $audio_route");
|
||||
|
||||
await announcementCache.loadAnnouncements([audio_route, audio_destination]);
|
||||
|
||||
AudioWrapperSource source_route = AudioWrapperByteSource(announcementCache[audio_route]);
|
||||
AudioWrapperSource source_destination = AudioWrapperByteSource(announcementCache[audio_destination]);
|
||||
|
||||
queueAnnouncement(AnnouncementQueueEntry(
|
||||
displayText: display,
|
||||
audioSources: [source_route, AudioWrapperAssetSource("audio/to_destination.wav"), source_destination]
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
late BusSequences busSequences;
|
||||
|
||||
BusRouteVariant? _currentRouteVariant;
|
||||
|
||||
void setRouteVariant(BusRouteVariant routeVariant) {
|
||||
_currentRouteVariant = routeVariant;
|
||||
announceRouteVariant(routeVariant);
|
||||
}
|
||||
|
||||
BusRouteVariant? getRouteVariant() {
|
||||
return _currentRouteVariant;
|
||||
}
|
||||
|
||||
void queueAnnouncement(AnnouncementQueueEntry announcement) {
|
||||
announcementQueue.add(announcement);
|
||||
}
|
||||
|
||||
List<ManualAnnouncementEntry> manualAnnouncements = [
|
||||
ManualAnnouncementEntry(
|
||||
shortName: "Driver Change",
|
||||
informationText: "Driver Change",
|
||||
audioSources: [AudioWrapperAssetSource("audio/manual_announcements/driverchange.mp3")],
|
||||
),
|
||||
ManualAnnouncementEntry(
|
||||
shortName: "No Standing Upr Deck",
|
||||
informationText: "No standing on the upper deck",
|
||||
audioSources: [AudioWrapperAssetSource("audio/manual_announcements/nostanding.mp3")],
|
||||
),
|
||||
ManualAnnouncementEntry(
|
||||
shortName: "Face Covering",
|
||||
informationText: "Please wear a face covering!",
|
||||
audioSources: [AudioWrapperAssetSource("audio/manual_announcements/facecovering.mp3")],
|
||||
),
|
||||
ManualAnnouncementEntry(
|
||||
shortName: "Seats Upstairs",
|
||||
informationText: "Seats are available upstairs",
|
||||
audioSources: [AudioWrapperAssetSource("audio/manual_announcements/seatsupstairs.mp3")],
|
||||
),
|
||||
ManualAnnouncementEntry(
|
||||
shortName: "Bus Terminates Here",
|
||||
informationText: "Bus terminates here. Please take your belongings with you",
|
||||
audioSources: [AudioWrapperAssetSource("audio/manual_announcements/busterminateshere.mp3")],
|
||||
),
|
||||
ManualAnnouncementEntry(
|
||||
shortName: "Bus On Diversion",
|
||||
informationText: "Bus on diversion. Please listen for further announcements",
|
||||
audioSources: [AudioWrapperAssetSource("audio/manual_announcements/busondiversion.mp3")],
|
||||
),
|
||||
ManualAnnouncementEntry(
|
||||
shortName: "Destination Change",
|
||||
informationText: "Destination Changed - please listen for further instructions",
|
||||
audioSources: [AudioWrapperAssetSource("audio/manual_announcements/destinationchange.mp3")],
|
||||
),
|
||||
ManualAnnouncementEntry(
|
||||
shortName: "Wheelchair Space",
|
||||
informationText: "Wheelchair space requested",
|
||||
audioSources: [AudioWrapperAssetSource("audio/manual_announcements/wheelchairspace1.mp3")],
|
||||
),
|
||||
ManualAnnouncementEntry(
|
||||
shortName: "Move Down The Bus",
|
||||
informationText: "Please move down the bus",
|
||||
audioSources: [AudioWrapperAssetSource("audio/manual_announcements/movedownthebus.mp3")],
|
||||
),
|
||||
ManualAnnouncementEntry(
|
||||
shortName: "Next Stop Closed",
|
||||
informationText: "The next bus stop is closed",
|
||||
audioSources: [AudioWrapperAssetSource("audio/manual_announcements/nextstopclosed.wav")],
|
||||
),
|
||||
ManualAnnouncementEntry(
|
||||
shortName: "CCTV In Operation",
|
||||
informationText: "CCTV is in operation on this bus",
|
||||
audioSources: [AudioWrapperAssetSource("audio/manual_announcements/cctvoperation.mp3")],
|
||||
),
|
||||
ManualAnnouncementEntry(
|
||||
shortName: "Safe Door Opening",
|
||||
informationText: "Driver will open the doors when it is safe to do so",
|
||||
audioSources: [AudioWrapperAssetSource("audio/manual_announcements/safedooropening.mp3")],
|
||||
),
|
||||
ManualAnnouncementEntry(
|
||||
shortName: "Buggy Safety",
|
||||
informationText: "For your child's safety, please remain with your buggy",
|
||||
audioSources: [AudioWrapperAssetSource("audio/manual_announcements/buggysafety.mp3")],
|
||||
),
|
||||
ManualAnnouncementEntry(
|
||||
shortName: "Wheelchair Space 2",
|
||||
informationText: "Wheelchair priority space required",
|
||||
audioSources: [AudioWrapperAssetSource("audio/manual_announcements/wheelchairspace2.mp3")],
|
||||
),
|
||||
ManualAnnouncementEntry(
|
||||
shortName: "Service Regulation",
|
||||
informationText: "Regulating service - please listen for further information",
|
||||
audioSources: [AudioWrapperAssetSource("audio/manual_announcements/serviceregulation.mp3")],
|
||||
),
|
||||
ManualAnnouncementEntry(
|
||||
shortName: "Bus Ready To Depart",
|
||||
informationText: "This bus is ready to depart",
|
||||
audioSources: [AudioWrapperAssetSource("audio/manual_announcements/readytodepart.mp3")],
|
||||
),
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
class AnnouncementQueueEntry {
|
||||
final String displayText;
|
||||
final List<AudioWrapperSource> audioSources;
|
||||
|
||||
AnnouncementQueueEntry({required this.displayText, required this.audioSources});
|
||||
}
|
||||
|
||||
class ManualAnnouncementEntry extends AnnouncementQueueEntry {
|
||||
final String shortName;
|
||||
|
||||
ManualAnnouncementEntry({required this.shortName, required String informationText, required List<AudioWrapperSource> audioSources}) : super(displayText: informationText, audioSources: audioSources);
|
||||
}
|
||||
125
lib/tfl_datasets.dart
Normal file
125
lib/tfl_datasets.dart
Normal file
@@ -0,0 +1,125 @@
|
||||
|
||||
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:bus_infotainment/audio_cache.dart';
|
||||
import 'package:bus_infotainment/utils/NameBeautify.dart';
|
||||
import 'package:csv/csv.dart';
|
||||
|
||||
class BusSequences {
|
||||
|
||||
Map<String, BusRoute> routes = {};
|
||||
|
||||
BusSequences.fromCSV(String csv) {
|
||||
|
||||
List<List<dynamic>> rowsAsListOfValues = const CsvToListConverter().convert(csv);
|
||||
|
||||
rowsAsListOfValues.removeAt(0);
|
||||
|
||||
for (int i = 0; i < rowsAsListOfValues.length; i++) {
|
||||
|
||||
try {
|
||||
|
||||
List<dynamic> entries = rowsAsListOfValues[i];
|
||||
|
||||
String routeNumber = entries[0].toString();
|
||||
|
||||
BusRoute route = routes.containsKey(routeNumber) ? routes[routeNumber]! : BusRoute(routeNumber: routeNumber);
|
||||
|
||||
int routeVariant = entries[1];
|
||||
|
||||
BusRouteVariant variant = route.routeVariants.containsKey(routeVariant) ? route.routeVariants[routeVariant]! : BusRouteVariant(routeVariant: routeVariant, busRoute: route);
|
||||
|
||||
BusRouteStops stop = BusRouteStops();
|
||||
|
||||
stop.stopName = entries[6].toString();
|
||||
stop.stopCode = entries[4].toString();
|
||||
stop.easting = entries[7];
|
||||
stop.northing = entries[8];
|
||||
|
||||
variant.busStops.add(stop);
|
||||
|
||||
route.routeVariants[routeVariant] = variant;
|
||||
routes[routeNumber] = route;
|
||||
|
||||
} catch (e) {
|
||||
// print("Error parsing bus sequence: $e");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class BusRoute {
|
||||
|
||||
String routeNumber = "";
|
||||
|
||||
AnnouncementCache? announcementCache;
|
||||
|
||||
Map<int, BusRouteVariant> routeVariants = {};
|
||||
|
||||
BusRoute({
|
||||
this.routeNumber = "-1",
|
||||
this.announcementCache,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
class BusRouteVariant {
|
||||
|
||||
int routeVariant = -1;
|
||||
|
||||
List<BusRouteStops> busStops = [];
|
||||
|
||||
late BusRoute busRoute;
|
||||
|
||||
BusRouteVariant({
|
||||
this.routeVariant = -1,
|
||||
required this.busRoute,
|
||||
});
|
||||
}
|
||||
|
||||
class BusRouteStops {
|
||||
|
||||
String stopName = "";
|
||||
|
||||
int easting = -1;
|
||||
int northing = -1;
|
||||
|
||||
String stopCode = "";
|
||||
|
||||
String get formattedStopName {
|
||||
return NameBeautify.beautifyStopName(stopName);
|
||||
}
|
||||
|
||||
String getAudioFileName() {
|
||||
|
||||
// Convert the stop name to all caps
|
||||
String stopName = this.stopName.toUpperCase();
|
||||
|
||||
stopName = NameBeautify.beautifyStopName(stopName);
|
||||
|
||||
// replace & with N
|
||||
stopName = stopName.replaceAll('&', 'N');
|
||||
|
||||
stopName = stopName.replaceAll('/', '');
|
||||
|
||||
stopName = stopName.replaceAll('\'', '');
|
||||
|
||||
stopName = stopName.replaceAll(' ', ' ');
|
||||
|
||||
// Replace space with underscore
|
||||
stopName = stopName.replaceAll(' ', '_');
|
||||
|
||||
// convert to all caps
|
||||
stopName = stopName.toUpperCase();
|
||||
|
||||
stopName = "S_${stopName}_001.mp3";
|
||||
|
||||
return stopName;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
69
lib/utils/NameBeautify.dart
Normal file
69
lib/utils/NameBeautify.dart
Normal file
@@ -0,0 +1,69 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class NameBeautify {
|
||||
|
||||
static final Map<String, String> Longify = {
|
||||
|
||||
"ctr": "Centre",
|
||||
"stn": "Station",
|
||||
"tn": "Town",
|
||||
|
||||
};
|
||||
|
||||
static String beautifyStopName(String label) {
|
||||
|
||||
String stopName = label.toUpperCase();
|
||||
|
||||
// remove <>
|
||||
stopName = stopName.replaceAll("<>", "");
|
||||
|
||||
// remove any parathesese pairs as well as the contents (), [], {}, <>, ><
|
||||
stopName = stopName.replaceAll(RegExp(r'\(.*\)'), '');
|
||||
stopName = stopName.replaceAll(RegExp(r'\[.*\]'), '');
|
||||
stopName = stopName.replaceAll(RegExp(r'\{.*\}'), '');
|
||||
// stopName = stopName.replaceAll(RegExp(r'\<.*\>'), '');
|
||||
stopName = stopName.replaceAll(RegExp(r'\>.*\<'), '');
|
||||
|
||||
|
||||
// remove any special characters except & and /
|
||||
stopName = stopName.replaceAll(RegExp(r'[^a-zA-Z0-9&/ ]'), '');
|
||||
|
||||
// remove any double spaces
|
||||
stopName = stopName.replaceAll(RegExp(r' '), ' ');
|
||||
|
||||
// remove any spaces at the start or end of the string
|
||||
stopName = stopName.trim();
|
||||
|
||||
// replace any short words with their long form
|
||||
for (String phrase in Longify.keys) {
|
||||
stopName = stopName.replaceAll(RegExp(phrase, caseSensitive: false), Longify[phrase]!);
|
||||
}
|
||||
|
||||
|
||||
stopName = stopName.toLowerCase();
|
||||
// Capitalify the first letter of each word
|
||||
try {
|
||||
stopName = stopName.split(' ').map((word) => word[0].toUpperCase() + word.substring(1)).join(' ');
|
||||
} catch (e) {}
|
||||
|
||||
return stopName;
|
||||
|
||||
}
|
||||
|
||||
static String getShortTime(){
|
||||
|
||||
// return the HH:MM with AM and PM and make sure that the hour is 12 hour format and it always double digits. IE 01, 02 etc
|
||||
DateTime now = DateTime.now();
|
||||
String formatted = DateFormat('hh:mm a').format(now);
|
||||
return formatted;
|
||||
}
|
||||
|
||||
static String getLongTime() {
|
||||
DateTime now = DateTime.now();
|
||||
String formattedTime = DateFormat('HH:mm:ss dd.MM.yyyy').format(now);
|
||||
return formattedTime;
|
||||
}
|
||||
|
||||
}
|
||||
125
lib/utils/audio wrapper.dart
Normal file
125
lib/utils/audio wrapper.dart
Normal file
@@ -0,0 +1,125 @@
|
||||
|
||||
import 'package:audioplayers/audioplayers.dart' as audioplayers;
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:just_audio/just_audio.dart' as justaudio;
|
||||
|
||||
enum AudioWrapper_State {
|
||||
Playing,
|
||||
NotPlaying
|
||||
}
|
||||
|
||||
class AudioWrapper {
|
||||
|
||||
audioplayers.AudioPlayer _audioPlayer_AudioPlayer = audioplayers.AudioPlayer();
|
||||
justaudio.AudioPlayer _justAudio_AudioPlayer = justaudio.AudioPlayer();
|
||||
|
||||
justaudio.AudioSource _convertSource_JustAudio(AudioWrapperSource source){
|
||||
if (source is AudioWrapperByteSource){
|
||||
return _ByteSource(source.bytes);
|
||||
} else if (source is AudioWrapperAssetSource){
|
||||
return justaudio.AudioSource.asset(source.assetPath);
|
||||
} else {
|
||||
throw Exception("Unknown source type");
|
||||
}
|
||||
}
|
||||
|
||||
audioplayers.Source _convertSource_AudioPlayers(AudioWrapperSource source){
|
||||
if (source is AudioWrapperByteSource){
|
||||
return audioplayers.BytesSource(source.bytes);
|
||||
} else if (source is AudioWrapperAssetSource){
|
||||
return audioplayers.AssetSource(source.assetPath);
|
||||
} else {
|
||||
throw Exception("Unknown source type");
|
||||
}
|
||||
}
|
||||
|
||||
Future<Duration?> play(AudioWrapperSource source) async {
|
||||
if (kIsWeb) {
|
||||
// Use just_audio
|
||||
|
||||
justaudio.AudioSource audioSource = _convertSource_JustAudio(source);
|
||||
|
||||
Duration? duration = await _justAudio_AudioPlayer.setAudioSource(audioSource);
|
||||
|
||||
_justAudio_AudioPlayer.play();
|
||||
|
||||
return duration;
|
||||
|
||||
} else {
|
||||
// Use audioplayers
|
||||
|
||||
audioplayers.Source audioSource = _convertSource_AudioPlayers(source);
|
||||
|
||||
await _audioPlayer_AudioPlayer.play(audioSource);
|
||||
|
||||
return await _audioPlayer_AudioPlayer.getDuration();
|
||||
}
|
||||
}
|
||||
|
||||
void stop(){
|
||||
if (kIsWeb) {
|
||||
_justAudio_AudioPlayer.stop();
|
||||
} else {
|
||||
_audioPlayer_AudioPlayer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
AudioWrapper_State get state {
|
||||
if (kIsWeb) {
|
||||
if (_justAudio_AudioPlayer.playing){
|
||||
return AudioWrapper_State.Playing;
|
||||
} else {
|
||||
return AudioWrapper_State.NotPlaying;
|
||||
}
|
||||
} else {
|
||||
if (_audioPlayer_AudioPlayer.state == audioplayers.PlayerState.playing){
|
||||
return AudioWrapper_State.Playing;
|
||||
} else {
|
||||
return AudioWrapper_State.NotPlaying;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class AudioWrapperSource {
|
||||
|
||||
}
|
||||
|
||||
class AudioWrapperByteSource extends AudioWrapperSource {
|
||||
|
||||
Uint8List bytes = Uint8List(0);
|
||||
|
||||
AudioWrapperByteSource(Uint8List bytes){
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class AudioWrapperAssetSource extends AudioWrapperSource {
|
||||
|
||||
String assetPath = "";
|
||||
|
||||
AudioWrapperAssetSource(String assetPath){
|
||||
this.assetPath = assetPath;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class _ByteSource extends justaudio.StreamAudioSource {
|
||||
final List<int> bytes;
|
||||
_ByteSource(this.bytes);
|
||||
|
||||
@override
|
||||
Future<justaudio.StreamAudioResponse> request([int? start, int? end]) async {
|
||||
start ??= 0;
|
||||
end ??= bytes.length;
|
||||
return justaudio.StreamAudioResponse(
|
||||
sourceLength: bytes.length,
|
||||
contentLength: end - start,
|
||||
offset: start,
|
||||
stream: Stream.value(bytes.sublist(start, end)),
|
||||
contentType: 'audio/mpeg',
|
||||
);
|
||||
}
|
||||
}
|
||||
90
lib/utils/delegates.dart
Normal file
90
lib/utils/delegates.dart
Normal file
@@ -0,0 +1,90 @@
|
||||
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
/// Event system
|
||||
|
||||
class ListenerReceipt<T> {
|
||||
Function(T) listener;
|
||||
|
||||
ListenerReceipt(this.listener);
|
||||
}
|
||||
|
||||
class EventDelegate<T> {
|
||||
final List<ListenerReceipt<T>> _receipts = [];
|
||||
|
||||
ListenerReceipt<T> addListener(Function(T) listener) {
|
||||
final receipt = ListenerReceipt(listener);
|
||||
_receipts.add(receipt);
|
||||
return receipt;
|
||||
}
|
||||
|
||||
void removeListener(ListenerReceipt<T> receipt) {
|
||||
_receipts.remove(receipt);
|
||||
print("removed listener");
|
||||
}
|
||||
|
||||
void trigger(T event) {
|
||||
print("triggering event");
|
||||
for (var receipt in _receipts) {
|
||||
print("triggering listener");
|
||||
try {
|
||||
receipt.listener(event);
|
||||
} catch (e) {
|
||||
print("Error in listener: $e");
|
||||
removeListener(receipt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// flutter integration
|
||||
|
||||
class DelegateBuilder<T> extends StatefulWidget {
|
||||
final EventDelegate<T> delegate;
|
||||
final Widget Function(BuildContext, T) builder;
|
||||
final Widget Function(BuildContext)? defaultBuilder;
|
||||
|
||||
DelegateBuilder({required this.delegate, required this.builder, this.defaultBuilder}) : super(key: UniqueKey())
|
||||
{
|
||||
print("created delegate builder widget");
|
||||
}
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _DelegateBuilderState<T>();
|
||||
|
||||
|
||||
}
|
||||
|
||||
class _DelegateBuilderState<T> extends State<DelegateBuilder<T>> {
|
||||
late ListenerReceipt<T> _receipt;
|
||||
|
||||
T? lastEvent;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
print("init delegate builder widget");
|
||||
_receipt = widget.delegate.addListener((event) {
|
||||
lastEvent = event;
|
||||
print("triggered");
|
||||
setState(() {
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
widget.delegate.removeListener(_receipt);
|
||||
print("disposed");
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
print("rebuilt");
|
||||
print("Valid: ${lastEvent != null}");
|
||||
return lastEvent == null ? widget.defaultBuilder == null ? Container() : widget.defaultBuilder!(context) : widget.builder(context, lastEvent!);
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,14 @@
|
||||
import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import audio_session
|
||||
import audioplayers_darwin
|
||||
import just_audio
|
||||
import path_provider_foundation
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
|
||||
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
|
||||
JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
}
|
||||
|
||||
82
pubspec.lock
82
pubspec.lock
@@ -17,6 +17,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.11.0"
|
||||
audio_session:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: audio_session
|
||||
sha256: "6fdf255ed3af86535c96452c33ecff1245990bb25a605bfb1958661ccc3d467f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.18"
|
||||
audioplayers:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -121,6 +129,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
csv:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: csv
|
||||
sha256: "63ed2871dd6471193dffc52c0e6c76fb86269c00244d244297abbb355c84a86e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.1.1"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -184,8 +200,16 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
google_fonts:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: google_fonts
|
||||
sha256: f0b8d115a13ecf827013ec9fc883390ccc0e87a96ed5347a3114cac177ef18e8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.0"
|
||||
http:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: http
|
||||
sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba
|
||||
@@ -200,6 +224,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: intl
|
||||
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.19.0"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -208,6 +240,38 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.7"
|
||||
just_audio:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: just_audio
|
||||
sha256: b607cd1a43bac03d85c3aaee00448ff4a589ef2a77104e3d409889ff079bf823
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.9.36"
|
||||
just_audio_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: just_audio_platform_interface
|
||||
sha256: c3dee0014248c97c91fe6299edb73dc4d6c6930a2f4f713579cd692d9e47f4a1
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.2"
|
||||
just_audio_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: just_audio_web
|
||||
sha256: "134356b0fe3d898293102b33b5fd618831ffdc72bb7a1b726140abdf22772b70"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.9"
|
||||
just_audio_windows:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: just_audio_windows
|
||||
sha256: "7b8801f3987e98a2002cd23b5600b2daf162248ff1413266fb44c84448c1c0d3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -320,6 +384,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.7.4"
|
||||
rxdart:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: rxdart
|
||||
sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.27.7"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
@@ -389,6 +461,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.1"
|
||||
text_scroll:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: text_scroll
|
||||
sha256: "7869d86a6fdd725dee56bdd150216a99f0372b82fbfcac319214dbd5f36e1908"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
20
pubspec.yaml
20
pubspec.yaml
@@ -31,7 +31,14 @@ dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
archive: ^3.1.2
|
||||
just_audio: ^0.9.36
|
||||
just_audio_windows: ^0.2.0
|
||||
audioplayers: ^5.2.1
|
||||
csv: ^5.1.1
|
||||
http: any
|
||||
google_fonts: ^6.1.0
|
||||
intl: any
|
||||
text_scroll: ^0.2.0
|
||||
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
@@ -63,6 +70,10 @@ flutter:
|
||||
# To add assets to your application, add an assets section, like this:
|
||||
assets:
|
||||
- assets/ibus_recordings.zip
|
||||
- assets/datasets/bus-sequences.csv
|
||||
- assets/fonts/ibus/london-buses-ibus.ttf
|
||||
- assets/audio/manual_announcements/
|
||||
- assets/audio/to_destination.wav
|
||||
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
@@ -78,7 +89,14 @@ flutter:
|
||||
# "family" key with the font family name, and a "fonts" key with a
|
||||
# list giving the asset and other descriptors for the font. For
|
||||
# example:
|
||||
# fonts:
|
||||
fonts:
|
||||
- family: "ibus"
|
||||
fonts:
|
||||
- asset: assets/fonts/ibus/london-buses-ibus.ttf
|
||||
- family: "lcd"
|
||||
fonts:
|
||||
- asset: assets/fonts/lcd/lcddot.ttf
|
||||
|
||||
# - family: Schyler
|
||||
# fonts:
|
||||
# - asset: fonts/Schyler-Regular.ttf
|
||||
|
||||
@@ -7,8 +7,11 @@
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <audioplayers_windows/audioplayers_windows_plugin.h>
|
||||
#include <just_audio_windows/just_audio_windows_plugin.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
AudioplayersWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin"));
|
||||
JustAudioWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("JustAudioWindowsPlugin"));
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
audioplayers_windows
|
||||
just_audio_windows
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
||||
Reference in New Issue
Block a user