ToLaptop
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
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.
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
Binary file not shown.
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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+21
-19
@@ -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
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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"),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
),
|
||||
)
|
||||
|
||||
],
|
||||
)
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class pages_Settings extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
child: Column(
|
||||
children: [
|
||||
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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"))
|
||||
}
|
||||
|
||||
+81
-1
@@ -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:
|
||||
|
||||
+19
-1
@@ -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