paradigm shift
This commit is contained in:
@@ -37,8 +37,8 @@ android {
|
||||
applicationId "com.imbenji.bus_infotainment"
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
||||
minSdkVersion flutter.minSdkVersion
|
||||
targetSdkVersion flutter.targetSdkVersion
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 33
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
multiDexEnabled = true
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
@@ -32,6 +34,9 @@
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
<meta-data
|
||||
android:name="com.google.mlkit.vision.DEPENDENCIES"
|
||||
android:value="barcode_ui"/>
|
||||
</application>
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
|
||||
@@ -58,7 +58,7 @@ Route,Blind,51.4309209,-0.0936496
|
||||
5,"Upton Park, Boleyn",51.5305005,0.0384915
|
||||
5,"Plaistow, Balaam Street",51.5226426,0.0223686
|
||||
5,"Canning Town, Barking Road",51.53044569999999,0.038323
|
||||
5,Canning Town,51.5189494,0.0132
|
||||
5,Canning Town,51.51395705923875, 0.00827546234713445
|
||||
6,"Willesden, Bus Garage",51.5474482,-0.2394372
|
||||
6,Kensal Rise,51.5345071,-0.2250186
|
||||
6,Queen's Park,51.5345448,-0.2043853
|
||||
@@ -216,8 +216,8 @@ Route,Blind,51.4309209,-0.0936496
|
||||
20,Whipps Cross,51.581499,0.0001219
|
||||
20,Woodford Green,51.6092549,0.0405521
|
||||
20,Woodford Wells,51.6148287,0.0282889
|
||||
20,Loughton,51.655942,0.068161
|
||||
20,Debden,51.6042352,0.04159529999999999
|
||||
20,Loughton,51.64195093723499, 0.05498357119784937
|
||||
20,Debden,51.645776846753535, 0.08200009471482733
|
||||
21,"Lewisham, Shopping Centre",51.46115090000001,-0.0073177
|
||||
21,"Lewisham, Jerrard Street",51.4650045,-0.0164722
|
||||
21,New Cross Gate,51.4749904,-0.0403466
|
||||
@@ -719,7 +719,7 @@ Route,Blind,51.4309209,-0.0936496
|
||||
69,Stratford,51.5426313,-0.0010369
|
||||
69,Plaistow,51.5268317,0.0308143
|
||||
69,"Canning Town, Hermit Road",51.5209368,0.0125057
|
||||
69,Canning Town,51.5189494,0.0132
|
||||
69,Canning Town,51.51395705923875, 0.00827546234713445
|
||||
70,"Chiswick, Business Park",51.4930604,-0.2748696
|
||||
70,"Acton, High Street",51.5068505,-0.2673267
|
||||
70,East Acton Lane,51.5123715,-0.2543317
|
||||
@@ -1066,7 +1066,7 @@ Route,Blind,51.4309209,-0.0936496
|
||||
108,Stratford International Bus Station,51.5453665,-0.0099022
|
||||
108,Bow Church,51.5287753,-0.0167013
|
||||
108,"Poplar, All Saints",51.5105521,-0.0118612
|
||||
108,Canning Town,51.5189494,0.0132
|
||||
108,Canning Town,51.51395705923875, 0.00827546234713445
|
||||
108,North Greenwich,51.4859576,0.007494900000000001
|
||||
108,East Greenwich,51.4309209,-0.0936496
|
||||
108,"Blackheath, Royal Standard",51.4779946,0.0202001
|
||||
@@ -1128,7 +1128,7 @@ Route,Blind,51.4309209,-0.0936496
|
||||
115,"Upton Park, Boleyn",51.5305005,0.0384915
|
||||
115,"Plaistow, Balaam street",51.5260131,0.0238468
|
||||
115,"Canning Town, Barking Road",51.5175196,0.0115068
|
||||
115,Canning Town,51.5189494,0.0132
|
||||
115,Canning Town,51.51395705923875, 0.00827546234713445
|
||||
115,"Poplar, All Saints",51.5105521,-0.0118612
|
||||
115,"Limehouse, Burdett Road",51.5209123,-0.0327503
|
||||
115,"Stepney, Arbour Square",51.5140186,-0.0481747
|
||||
@@ -1412,7 +1412,7 @@ Route,Blind,51.4309209,-0.0936496
|
||||
147,"East Ham, Newham Town Hall",51.53280239999999,0.0551608
|
||||
147,"Upton Park, Boleyn",51.5305005,0.0384915
|
||||
147,Prince Regent,51.5178274,0.0321034
|
||||
147,Canning Town,51.5189494,0.0132
|
||||
147,Canning Town,51.51395705923875, 0.00827546234713445
|
||||
148,Camberwell Green,51.4756016,-0.0928896
|
||||
148,Elephant & Castle,51.4938058,-0.0977932
|
||||
148,Parliament Square,51.5010421,-0.1268514
|
||||
@@ -1580,7 +1580,7 @@ Route,Blind,51.4309209,-0.0936496
|
||||
167,"Barkingside, Fullwell Cross",51.59451199999999,0.08571999999999999
|
||||
167,Gants Hill,51.5767812,0.0661732
|
||||
167,Buckhurst Hill,51.627572,0.034513
|
||||
167,Loughton,51.655942,0.068161
|
||||
167,Loughton,51.64195093723499, 0.05498357119784937
|
||||
168,Hampstead Heath,51.5608294,-0.1629416
|
||||
168,Chalk Farm,51.5422732,-0.1466907
|
||||
168,Camden Town,51.5390261,-0.1425516
|
||||
@@ -1984,7 +1984,7 @@ Route,Blind,51.4309209,-0.0936496
|
||||
212,Highams Park,51.6083754,0.0014712
|
||||
212,"Walthamstow, Beacontree Avenue",51.595736,0.0045318
|
||||
212,Walthamstow Central,51.5830128,-0.019886
|
||||
212,St James Street,51.5064993,-0.1393328
|
||||
212,St James Street,51.581118791593234, -0.0328839757764101
|
||||
213,Kingston,51.4116616,-0.2080648
|
||||
213,Kingston,51.4116616,-0.2080648
|
||||
213,"New Malden, Coombe Road",51.4094312,-0.2586718
|
||||
@@ -2202,7 +2202,7 @@ Route,Blind,51.4309209,-0.0936496
|
||||
241,Plaistow,51.5268317,0.0308143
|
||||
241,Plaistow Abbey Arms,51.5222296,0.0225853
|
||||
241,Keir Hardie Estate,51.5678758,-0.0607466
|
||||
241,Canning Town,51.5189494,0.0132
|
||||
241,Canning Town,51.51395705923875, 0.00827546234713445
|
||||
241,"Canning Town, Barking Road",51.5198077,0.0168462
|
||||
242,Homerton Hospital,51.5478609,-0.0425903
|
||||
242,"Clapton Park, Millfields",51.5575011,-0.0424329
|
||||
@@ -2483,7 +2483,7 @@ Route,Blind,51.4309209,-0.0936496
|
||||
274,Baker Street Station,51.5231548,-0.156863
|
||||
274,Portman Square,51.5161534,-0.1560125
|
||||
274,Marble Arch,51.5132225,-0.1588937
|
||||
275,St James Street,51.5064993,-0.1393328
|
||||
275,St James Street,51.581118791593234, -0.0328839757764101
|
||||
275,Walthamstow Central,51.5830128,-0.019886
|
||||
275,"Walthamstow, Beacontree Avenue",51.595736,0.0045318
|
||||
275,Mill lane,51.5521985,-0.1932197
|
||||
@@ -2685,7 +2685,7 @@ Route,Blind,51.4309209,-0.0936496
|
||||
300,"Plaistow, Greengate Street",51.527444,0.027621
|
||||
300,"Plaistow, Balaam Street",51.529212,0.0243676
|
||||
300,"Canning Town, Barking Road",51.5208364,0.0191093
|
||||
300,Canning Town,51.5189494,0.0132
|
||||
300,Canning Town,51.51395705923875, 0.00827546234713445
|
||||
302,Mill Hill Broadway,51.6129292,-0.2487871
|
||||
302,Burnt Oak,51.602809,-0.266965
|
||||
302,Burnt Oak,51.602809,-0.266965
|
||||
@@ -2716,7 +2716,7 @@ Route,Blind,51.4309209,-0.0936496
|
||||
308,Stratford City,51.5440354,-0.0053088
|
||||
308,Homerton Hospital,51.5478609,-0.0425903
|
||||
308,Clapton Pond,51.5561061,-0.05490830000000001
|
||||
309,Canning Town,51.5189494,0.0132
|
||||
309,Canning Town,51.51395705923875, 0.00827546234713445
|
||||
309,Aberfeldy Estate,51.4309209,-0.0936496
|
||||
309,"Poplar, All Saints",51.5105521,-0.0118612
|
||||
309,"Poplar, Cordelia Street",51.5137347,-0.0174897
|
||||
@@ -2805,7 +2805,7 @@ Route,Blind,51.4309209,-0.0936496
|
||||
322,Brixton,51.4612794,-0.1156148
|
||||
322,Clapham North,51.4658813,-0.1413263
|
||||
322,Clapham Common,51.4589252,-0.1493071
|
||||
323,Canning Town,51.5189494,0.0132
|
||||
323,Canning Town,51.51395705923875, 0.00827546234713445
|
||||
323,East London Mail Centre,51.55633,0.0655092
|
||||
323,Mile End,51.52354529999999,-0.0330122
|
||||
324,Stanmore Station,51.617676,-0.311451
|
||||
@@ -2862,7 +2862,7 @@ Route,Blind,51.4309209,-0.0936496
|
||||
330,"Upton Park, Boleyn",51.5305005,0.0384915
|
||||
330,"Plaistow, Balaam Street",51.529212,0.0243676
|
||||
330,"Canning Town, Barking Road",51.521274,0.0207211
|
||||
330,Canning Town,51.5189494,0.0132
|
||||
330,Canning Town,51.51395705923875, 0.00827546234713445
|
||||
331,Ruislip,51.5758719,-0.421236
|
||||
331,Ruislip Lido,51.59114049999999,-0.4304918
|
||||
331,Northwood Station,51.6112297,-0.423889
|
||||
@@ -3206,8 +3206,8 @@ Route,Blind,51.4309209,-0.0936496
|
||||
397,"Crooked Billet, Sainsbury's",51.601088,-0.0159737
|
||||
397,Chingford Mount,51.6185735,-0.0180318
|
||||
397,Chingford Station,51.6331421,0.0098588
|
||||
397,Loughton,51.655942,0.068161
|
||||
397,Debden,51.5417031,0.2034589
|
||||
397,Loughton,51.64195093723499, 0.05498357119784937
|
||||
397,Debden,51.645776846753535, 0.08200009471482733
|
||||
398,Ruislip,51.5758719,-0.421236
|
||||
398,Rayners Lane Station,51.5751034,-0.3708618
|
||||
398,South Harrow,51.5683717,-0.3553483
|
||||
@@ -3521,7 +3521,7 @@ Route,Blind,51.4309209,-0.0936496
|
||||
473,Plaistow,51.5268317,0.0308143
|
||||
473,Stratford,51.5426313,-0.0010369
|
||||
474,"Canning Town, Barking Road",51.52364720000001,0.0258453
|
||||
474,Canning Town,51.5189494,0.0132
|
||||
474,Canning Town,51.51395705923875, 0.00827546234713445
|
||||
474,London City Airport,51.5048437,0.049518
|
||||
474,North Woolwich,51.5008658,0.0626916
|
||||
474,Cyprus,51.5091192,0.0633823
|
||||
@@ -3658,7 +3658,7 @@ Route,Blind,51.4309209,-0.0936496
|
||||
541,Prince Regent,51.4309209,-0.0936496
|
||||
549,South Woodford,51.5912671,0.0264721
|
||||
549,Buckhurst Hill,51.627572,0.034513
|
||||
549,Loughton,51.655942,0.068161
|
||||
549,Loughton,51.64195093723499, 0.05498357119784937
|
||||
601,Thamesmead,51.50575809999999,0.1100586
|
||||
601,Bexley,51.439933,0.154327
|
||||
601,Wilmington Schools,51.4309209,-0.0936496
|
||||
@@ -3803,7 +3803,7 @@ Route,Blind,51.4309209,-0.0936496
|
||||
673,Beckton Station,51.5144016,0.06153319999999999
|
||||
674,"Harold Hill, Dagnam Park Square",51.6049149,0.2445527
|
||||
674,Romford Station,51.57472449999999,0.1826519
|
||||
675,St James Street,51.5064993,-0.1393328
|
||||
675,St James Street,51.581118791593234, -0.0328839757764101
|
||||
675,Woodbridge School,51.4309209,-0.0936496
|
||||
678,Stratford,51.5426313,-0.0010369
|
||||
678,NO BLIND DESCRIPTION (Departs only),51.4309209,-0.0936496
|
||||
@@ -4016,7 +4016,7 @@ EL1,Barking,51.536563,0.075766
|
||||
EL1,Barking,51.536563,0.075766
|
||||
EL1,"River Road, Waverley Gardens",51.5343317,-0.2891878
|
||||
EL1,"River Road, Waverley Gardens",51.5343317,-0.2891878
|
||||
EL1,Barking Riverside,51.536563,0.075766
|
||||
EL1,Barking Riverside,51.519303731473705, 0.11590257503355633
|
||||
EL2,Becontree Heath,51.5609465,0.1488995
|
||||
EL2,Five Elms,51.3697855,0.0259964
|
||||
EL2,Bennett's Castle Lane,51.5562949,0.1276031
|
||||
@@ -4031,7 +4031,7 @@ EL3,"Goodmayes, Goodmayes Lane",51.5620979,0.1095257
|
||||
EL3,Barking,51.536563,0.075766
|
||||
EL3,Creekmouth,51.517381,0.102234
|
||||
EL3,Barking,51.536563,0.075766
|
||||
EL3,Barking Riverside,51.536563,0.075766
|
||||
EL3,Barking Riverside,51.519303731473705, 0.11590257503355633
|
||||
G1,"Streatham, Green Lane",51.4147104,-0.1158693
|
||||
G1,"Streatham, St Leonard's Church",51.4307467,-0.1294977
|
||||
G1,Tooting Broadway,51.427867,-0.1678142
|
||||
@@ -4411,7 +4411,7 @@ UL10,Hackney Downs,51.5553095,-0.06908249999999999
|
||||
UL10,Liverpool Street,51.5175001,-0.0826966
|
||||
UL11,Canary Wharf,51.5054306,-0.0235333
|
||||
UL11,Stratford,51.5426313,-0.0010369
|
||||
UL12,Loughton,51.655942,0.068161
|
||||
UL12,Loughton,51.64195093723499, 0.05498357119784937
|
||||
UL12,Snaresbrook,51.58567859999999,0.0084531
|
||||
UL12,Leyton,51.5702225,-0.0146938
|
||||
UL12,Stratford,51.5426313,-0.0010369
|
||||
@@ -4438,7 +4438,7 @@ UL19,Wembley Central,51.550501,-0.3048409
|
||||
UL19,Stonebridge Park,51.5445824,-0.2608244
|
||||
UL19,Queen's Park,51.5345448,-0.2043853
|
||||
UL20,Tower Hill,51.5095757,-0.0760083
|
||||
UL20,Canning Town,51.5189494,0.0132
|
||||
UL20,Canning Town,51.51395705923875, 0.00827546234713445
|
||||
UL20,Stratford,51.5426313,-0.0010369
|
||||
UL20,Stratford,51.5426313,-0.0010369
|
||||
UL20,East Ham,51.5333972,0.04991139999999999
|
||||
@@ -4540,7 +4540,7 @@ W11,"Walthamstow, Crooked Billet",51.601088,-0.0159737
|
||||
W11,Walthamstow Central,51.5830128,-0.019886
|
||||
W12,"Walthamstow, Coppermill Lane",51.5803038,-0.0403725
|
||||
W12,Walthamstow Central,51.5830128,-0.019886
|
||||
W12,St James Street,51.5064993,-0.1393328
|
||||
W12,St James Street,51.581118791593234, -0.0328839757764101
|
||||
W12,Whipps Cross,51.5095281,-0.229236
|
||||
W12,South Woodford,51.5095281,-0.229236
|
||||
W12,Wanstead,51.5767971,0.0249881
|
||||
@@ -4559,7 +4559,7 @@ W14,"Leytonstone, Harrow Green",51.5582955,0.007356
|
||||
W14,Leyton,51.4964278,-0.2085211
|
||||
W14,"Leyton, Superstores",51.5558965,-0.0093311
|
||||
W15,"Higham Hill, Cogan Avenue",51.6012336,-0.0363719
|
||||
W15,"Forest Road, Palmerston Road",51.5858953,-0.0292291
|
||||
W15,"Forest Road, Palmerston Road",51.588898351903815, -0.030623848707334166
|
||||
W15,Walthamstow Central,51.5830128,-0.019886
|
||||
W15,"Leyton, Bakers Arms",51.5749185,-0.013549
|
||||
W15,Whipps Cross,51.581499,0.0001219
|
||||
@@ -4576,7 +4576,7 @@ W16,"Walthamstow, Wood Street",51.5864747,-0.0026968
|
||||
W16,"Leyton, Bakers Arms",51.5749185,-0.013549
|
||||
W16,Leytonstone,51.5649624,0.0088141
|
||||
W19,"Walthamstow, Argall Avenue",51.5701165,-0.0387872
|
||||
W19,St James Street,51.5064993,-0.1393328
|
||||
W19,St James Street,51.581118791593234, -0.0328839757764101
|
||||
W19,Walthamstow Central,51.5830128,-0.019886
|
||||
W19,Whipps Cross,51.4255297,-0.2050566
|
||||
W19,Leytonstone,51.5649624,0.0088141
|
||||
@@ -4704,7 +4704,7 @@ N15,Barking,51.5833519,-0.07649299999999999
|
||||
N15,"East Ham, Newham Town Hall",51.53280239999999,0.0551608
|
||||
N15,Upton Park,51.53471750000001,0.0337596
|
||||
N15,"Canning Town, Barking Road",51.5180017,0.0130984
|
||||
N15,Canning Town,51.5189494,0.0132
|
||||
N15,Canning Town,51.51395705923875, 0.00827546234713445
|
||||
N15,"Poplar, All Saints",51.5105521,-0.0118612
|
||||
N15,"Limehouse, Burdett Road",51.5150014,-0.0285662
|
||||
N15,Aldgate,51.5134365,-0.0772463
|
||||
@@ -5151,13 +5151,13 @@ N550,Aldgate,51.5134365,-0.0772463
|
||||
N550,Cannon Street,51.5119949,-0.091962
|
||||
N550,"Limehouse, Burdett Road",51.5150014,-0.0285662
|
||||
N550,"Poplar, All Saints",51.5105521,-0.0118612
|
||||
N550,Canning Town,51.5189494,0.0132
|
||||
N550,Canning Town,51.51395705923875, 0.00827546234713445
|
||||
N551,Trafalgar Square,51.508039,-0.128069
|
||||
N551,Aldwych,51.5132441,-0.1172819
|
||||
N551,Aldgate,51.5134365,-0.0772463
|
||||
N551,Limehouse,51.5110598,-0.0366652
|
||||
N551,"Poplar, All Saints",51.5105521,-0.0118612
|
||||
N551,Canning Town,51.5189494,0.0132
|
||||
N551,Canning Town,51.51395705923875, 0.00827546234713445
|
||||
N551,Keir Hardie Estate,51.5678758,-0.0607466
|
||||
N551,Prince Regent,51.4309209,-0.0936496
|
||||
N551,Beckton Station,51.5144016,0.06153319999999999
|
||||
|
||||
|
@@ -1,8 +1,8 @@
|
||||
|
||||
class ApiConstants {
|
||||
|
||||
static const String APPWRITE_ENDPOINT = "https://cloud.imbenji.net/v1";
|
||||
static const String APPWRITE_PROJECT_ID = "65de530c1c0a7ffc0c3f";
|
||||
static const String APPWRITE_ENDPOINT = "https://cloud.appwrite.io/v1";
|
||||
static const String APPWRITE_PROJECT_ID = "6633d0e00023502890ed";
|
||||
|
||||
static const String INFO_Q_DATABASE_ID = "65de5cab16717444527b";
|
||||
static const String MANUAL_Q_COLLECTION_ID = "65de9f2f925562a2eda8";
|
||||
|
||||
@@ -21,6 +21,7 @@ class AuthAPI extends ChangeNotifier {
|
||||
late final appwrite.Account account;
|
||||
|
||||
late models.User _currentUser;
|
||||
late models.Session _currentSession;
|
||||
|
||||
AuthStatus _status = AuthStatus.UNINITIALIZED;
|
||||
|
||||
@@ -32,11 +33,16 @@ class AuthAPI extends ChangeNotifier {
|
||||
AuthStatus get status => _status;
|
||||
String? get username => _currentUser.name;
|
||||
String? get email => _currentUser.email;
|
||||
String? get userID => _currentUser.$id;
|
||||
String? get userID {
|
||||
try {
|
||||
return _currentUser.$id;
|
||||
} catch (e) {
|
||||
return _currentSession.$id;
|
||||
}
|
||||
}
|
||||
|
||||
AuthAPI() {
|
||||
AuthAPI({bool autoLoad = true}) {
|
||||
init();
|
||||
loadUser();
|
||||
}
|
||||
|
||||
init() {
|
||||
@@ -45,6 +51,29 @@ class AuthAPI extends ChangeNotifier {
|
||||
.setProject(ApiConstants.APPWRITE_PROJECT_ID)
|
||||
.setSelfSigned();
|
||||
account = appwrite.Account(client);
|
||||
try {
|
||||
account.get().then((value) {
|
||||
_currentUser = value;
|
||||
_status = AuthStatus.AUTHENTICATED;
|
||||
print("Auto loaded user: ${_currentUser.name}");
|
||||
print("Auth status: $_status");
|
||||
});
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
loadAnonymousUser() async {
|
||||
try {
|
||||
final user = await account.createAnonymousSession();
|
||||
_currentSession = user;
|
||||
_status = AuthStatus.AUTHENTICATED;
|
||||
|
||||
} catch (e) {
|
||||
_status = AuthStatus.UNAUTHENTICATED;
|
||||
} finally {
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
loadUser() async {
|
||||
|
||||
@@ -16,6 +16,7 @@ import 'package:bus_infotainment/backend/modules/tube_info.dart';
|
||||
import 'package:bus_infotainment/tfl_datasets.dart';
|
||||
import 'package:bus_infotainment/utils/audio%20wrapper.dart';
|
||||
import 'package:bus_infotainment/utils/delegates.dart';
|
||||
import 'package:bus_infotainment/workaround/keepalive_realtime.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
@@ -62,12 +63,6 @@ class LiveInformation {
|
||||
print("Loading tube stations from assets");
|
||||
tubeStations = TubeStations.fromJson(json.decode(await rootBundle.loadString("assets/datasets/tube_stations.json")));
|
||||
print("Loaded tube stations from assets");
|
||||
|
||||
|
||||
String sessionID = "test";
|
||||
|
||||
commandModule = CommandModule(sessionID);
|
||||
|
||||
}
|
||||
|
||||
// Initialise modules
|
||||
@@ -77,6 +72,9 @@ class LiveInformation {
|
||||
initTrackerModule();
|
||||
|
||||
print("Initialised LiveInformation");
|
||||
if (!auth.isAuthenticated()) {
|
||||
auth.loadAnonymousUser();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> initTrackerModule() async {
|
||||
@@ -86,10 +84,18 @@ class LiveInformation {
|
||||
}
|
||||
|
||||
// Auth
|
||||
AuthAPI auth = AuthAPI();
|
||||
AuthAPI auth = AuthAPI(
|
||||
autoLoad: false,
|
||||
);
|
||||
String? roomCode;
|
||||
String? roomDocumentID;
|
||||
bool isHost = false;
|
||||
appwrite.RealtimeSubscription? _subscription;
|
||||
RealtimeKeepAliveConnection? _keepAliveConnection; // This is a workaround for a bug in the appwrite SDK
|
||||
|
||||
|
||||
// Modules
|
||||
late CommandModule commandModule;
|
||||
// late CommandModule commandModule; This needs to be deprecated
|
||||
late BusSequences busSequences;
|
||||
late AnnouncementModule announcementModule;
|
||||
late SyncedTimeModule syncedTimeModule;
|
||||
@@ -100,7 +106,7 @@ class LiveInformation {
|
||||
BusRouteVariant? _currentRouteVariant;
|
||||
|
||||
// Events
|
||||
EventDelegate<BusRouteVariant> routeVariantDelegate = EventDelegate();
|
||||
EventDelegate<BusRouteVariant?> routeVariantDelegate = EventDelegate();
|
||||
|
||||
// Internal methods
|
||||
|
||||
@@ -108,7 +114,54 @@ class LiveInformation {
|
||||
|
||||
|
||||
|
||||
Future<void> setRouteVariant_Internal(BusRouteVariant routeVariant) async {
|
||||
Future<void> setRouteVariant(BusRouteVariant? routeVariant) async {
|
||||
|
||||
if (routeVariant == null) {
|
||||
_currentRouteVariant = null;
|
||||
|
||||
await announcementModule.queueAnnounceByAudioName(
|
||||
displayText: "*** NO MESSAGE ***",
|
||||
);
|
||||
|
||||
routeVariantDelegate.trigger(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (roomCode != null) {
|
||||
try {
|
||||
final client = auth.client;
|
||||
final databases = appwrite.Databases(client);
|
||||
|
||||
final response = await databases.listDocuments(
|
||||
databaseId: "6633e85400036415ab0f",
|
||||
collectionId: "6633e85d0020f52f3771",
|
||||
queries: [
|
||||
appwrite.Query.search("SessionID", roomCode!)
|
||||
]
|
||||
);
|
||||
|
||||
final document = response.documents.first;
|
||||
|
||||
// Check if the route is not the same
|
||||
if (document.data["CurrentRoute"] != routeVariant.busRoute.routeNumber || document.data["CurrentRouteVariant"] != routeVariant.busRoute.routeVariants.values.toList().indexOf(routeVariant)) {
|
||||
final updatedDocument = await databases.updateDocument(
|
||||
databaseId: "6633e85400036415ab0f",
|
||||
collectionId: "6633e85d0020f52f3771",
|
||||
documentId: document.$id,
|
||||
data: {
|
||||
"CurrentRoute": routeVariant.busRoute.routeNumber,
|
||||
"CurrentRouteVariant": routeVariant.busRoute.routeVariants.values.toList().indexOf(routeVariant),
|
||||
"LastUpdater": auth.userID,
|
||||
}
|
||||
);
|
||||
print("Updated route on server");
|
||||
}
|
||||
} catch (e) {
|
||||
print("Failed to update route on server");
|
||||
}
|
||||
|
||||
}
|
||||
Continuation:
|
||||
|
||||
// Set the current route variant
|
||||
_currentRouteVariant = routeVariant;
|
||||
@@ -135,6 +188,10 @@ class LiveInformation {
|
||||
|
||||
]
|
||||
);
|
||||
|
||||
// Display the route variant
|
||||
announcementModule.queueAnnouncementByRouteVariant(routeVariant: routeVariant);
|
||||
|
||||
}
|
||||
|
||||
// Public methods
|
||||
@@ -143,16 +200,329 @@ class LiveInformation {
|
||||
return _currentRouteVariant;
|
||||
}
|
||||
|
||||
Future<void> setRouteVariant(BusRouteVariant routeVariant) async {
|
||||
await commandModule.executeCommand(
|
||||
"setroute ${routeVariant.busRoute.routeNumber} ${routeVariant.busRoute.routeVariants.values.toList().indexOf(routeVariant)}"
|
||||
Future<void> setRouteVariantQuery(String routeNumber, int routeVariantIndex) async {
|
||||
BusRoute route = busSequences.routes[routeNumber]!;
|
||||
BusRouteVariant routeVariant = route.routeVariants.values.toList()[routeVariantIndex];
|
||||
|
||||
await setRouteVariant(
|
||||
routeVariant
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Multi device support
|
||||
|
||||
Future<void> createRoom(String roomCode) async {
|
||||
print("Creating room with code $roomCode");
|
||||
|
||||
// Update the room code
|
||||
this.roomCode = roomCode;
|
||||
|
||||
// Enable host mode
|
||||
isHost = true;
|
||||
|
||||
// Access the database
|
||||
final client = auth.client;
|
||||
final databases = appwrite.Databases(client);
|
||||
|
||||
// Remove any existing documents
|
||||
final existingDocuments = await databases.listDocuments(
|
||||
databaseId: "6633e85400036415ab0f",
|
||||
collectionId: "6633e85d0020f52f3771",
|
||||
queries: [
|
||||
appwrite.Query.search("SessionID", roomCode)
|
||||
]
|
||||
);
|
||||
for (var document in existingDocuments.documents) {
|
||||
await databases.deleteDocument(
|
||||
databaseId: "6633e85400036415ab0f",
|
||||
collectionId: "6633e85d0020f52f3771",
|
||||
documentId: document.$id
|
||||
);
|
||||
}
|
||||
|
||||
// Create the document
|
||||
final document = await databases.createDocument(
|
||||
databaseId: "6633e85400036415ab0f",
|
||||
collectionId: "6633e85d0020f52f3771",
|
||||
documentId: appwrite.ID.unique(),
|
||||
data: {
|
||||
"SessionID": roomCode,
|
||||
"LastUpdater": auth.userID,
|
||||
}
|
||||
);
|
||||
|
||||
// Listen for changes
|
||||
// { Breaks due to bug in appwrite
|
||||
// final realtime = appwrite.Realtime(client);
|
||||
//
|
||||
// if (_subscription != null) {
|
||||
// _subscription!.close();
|
||||
// }
|
||||
//
|
||||
// _subscription = realtime.subscribe(
|
||||
// ['databases.6633e85400036415ab0f.collections.6633e85d0020f52f3771.documents.${document.$id}']
|
||||
// );
|
||||
// _subscription!.stream.listen(ServerListener);
|
||||
// }
|
||||
// Listen for changes
|
||||
if (_keepAliveConnection != null) {
|
||||
try {
|
||||
_keepAliveConnection!.close();
|
||||
} catch (e) {
|
||||
print("Failed to close connection... oh well");
|
||||
}
|
||||
}
|
||||
|
||||
String APPWRITE_ENDPOINT_URL = "https://cloud.appwrite.io/v1";
|
||||
String domain = APPWRITE_ENDPOINT_URL.replaceAll("https://", "").trim();
|
||||
_keepAliveConnection = RealtimeKeepAliveConnection(
|
||||
channels: ['databases.6633e85400036415ab0f.collections.6633e85d0020f52f3771.documents.${document.$id}'],
|
||||
onData: ServerListener,
|
||||
domain: domain,
|
||||
client: auth.client,
|
||||
onError: (e) {
|
||||
print("Workarround Error: $e");
|
||||
},
|
||||
);
|
||||
_keepAliveConnection!.initialize();
|
||||
|
||||
|
||||
// Update the room document ID
|
||||
roomDocumentID = document.$id;
|
||||
|
||||
print("Created room with code $roomCode");
|
||||
}
|
||||
|
||||
Future<void> JoinRoom(String roomCode) async {
|
||||
print("Joining room with code $roomCode");
|
||||
|
||||
// Disable host mode
|
||||
isHost = false;
|
||||
|
||||
// Update the room code
|
||||
this.roomCode = roomCode;
|
||||
|
||||
// Access the database
|
||||
final client = auth.client;
|
||||
final databases = appwrite.Databases(client);
|
||||
|
||||
// Get the document
|
||||
final response = await databases.listDocuments(
|
||||
databaseId: "6633e85400036415ab0f",
|
||||
collectionId: "6633e85d0020f52f3771",
|
||||
queries: [
|
||||
appwrite.Query.search("SessionID", roomCode)
|
||||
]
|
||||
);
|
||||
|
||||
if (response.documents.isEmpty) {
|
||||
throw Exception("Room not found");
|
||||
}
|
||||
|
||||
final document = response.documents.first;
|
||||
|
||||
// Listen for changes
|
||||
// {
|
||||
// final realtime = appwrite.Realtime(client);
|
||||
//
|
||||
// if (_subscription != null) {
|
||||
// _subscription!.close();
|
||||
// }
|
||||
//
|
||||
// _subscription = realtime.subscribe([
|
||||
// 'databases.6633e85400036415ab0f.collections.6633e85d0020f52f3771.documents.${document.$id}'
|
||||
// ]);
|
||||
//
|
||||
// _subscription!.stream.listen(ServerListener);
|
||||
// }
|
||||
// Listen for changes
|
||||
if (_keepAliveConnection != null) {
|
||||
try {
|
||||
_keepAliveConnection!.close();
|
||||
} catch (e) {
|
||||
print("Failed to close connection... oh well");
|
||||
}
|
||||
}
|
||||
|
||||
String APPWRITE_ENDPOINT_URL = "https://cloud.appwrite.io/v1";
|
||||
String domain = APPWRITE_ENDPOINT_URL.replaceAll("https://", "").trim();
|
||||
_keepAliveConnection = RealtimeKeepAliveConnection(
|
||||
channels: ['databases.6633e85400036415ab0f.collections.6633e85d0020f52f3771.documents.${document.$id}'],
|
||||
onData: ServerListener,
|
||||
domain: domain,
|
||||
client: auth.client,
|
||||
onError: (e) {
|
||||
print("Workarround Error: $e");
|
||||
},
|
||||
);
|
||||
_keepAliveConnection!.initialize();
|
||||
|
||||
// Update the room document ID
|
||||
roomDocumentID = document.$id;
|
||||
|
||||
// Get the current route
|
||||
try {
|
||||
String routeNumber = document.data["CurrentRoute"];
|
||||
int routeVariantIndex = document.data["CurrentRouteVariant"];
|
||||
|
||||
await setRouteVariantQuery(routeNumber, routeVariantIndex);
|
||||
print("Set route to $routeNumber $routeVariantIndex");
|
||||
} catch (e) {
|
||||
print("Failed to set route");
|
||||
}
|
||||
|
||||
print("Joined room with code $roomCode");
|
||||
}
|
||||
|
||||
String? lastCommand;
|
||||
Future<void> ServerListener(appwrite.RealtimeMessage response) async {
|
||||
print("Session update");
|
||||
|
||||
// Only do something if the document was created or updated
|
||||
if (!(response.events.first.contains("create") || response.events.first.contains("update"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the user that caused the event
|
||||
|
||||
String senderID = response.payload["LastUpdater"];
|
||||
// If the sender is the same as the client, then ignore the event
|
||||
if (senderID == auth.userID) {
|
||||
print("Ignoring event");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check to see if the commands are updated
|
||||
|
||||
try {
|
||||
// Get the new route
|
||||
String routeNumber = response.payload["CurrentRoute"];
|
||||
int routeVariantIndex = response.payload["CurrentRouteVariant"];
|
||||
|
||||
// If the route arent the same, then update the route
|
||||
if (routeNumber != _currentRouteVariant!.busRoute.routeNumber || routeVariantIndex != _currentRouteVariant!.busRoute.routeVariants.values.toList().indexOf(_currentRouteVariant!)) {
|
||||
// Set the route
|
||||
await setRouteVariantQuery(routeNumber, routeVariantIndex);
|
||||
|
||||
// announce the route
|
||||
// announcementModule.queueAnnouncementByRouteVariant(routeVariant: _currentRouteVariant!);
|
||||
}
|
||||
} catch (e) {
|
||||
print("Failed to set route");
|
||||
}
|
||||
|
||||
// Execute the command
|
||||
List<String> commands = response.payload["Commands"].cast<String>();
|
||||
|
||||
String? command = commands.last;
|
||||
|
||||
if (command == lastCommand) {
|
||||
return;
|
||||
}
|
||||
lastCommand = command;
|
||||
|
||||
List<String> commandParts = _splitCommand(command);
|
||||
String commandName = commandParts[0];
|
||||
|
||||
if (commandName == "announce") {
|
||||
print("Announce command received");
|
||||
String mode = commandParts[1];
|
||||
|
||||
print ("Command: $command");
|
||||
|
||||
if (mode == "info") {
|
||||
print("Announce info command received");
|
||||
announcementModule.queueAnnounementByInfoIndex(
|
||||
sendToServer: false,
|
||||
infoIndex: int.parse(commandParts[2]),
|
||||
scheduledTime: DateTime.fromMillisecondsSinceEpoch(int.parse(commandParts[3])),
|
||||
);
|
||||
} else if (mode == "dest") {
|
||||
print("Announce dest command received");
|
||||
|
||||
String routeNumber = commandParts[2];
|
||||
int routeVariantIndex = int.parse(commandParts[3]);
|
||||
|
||||
announcementModule.queueAnnouncementByRouteVariant(
|
||||
sendToServer: false,
|
||||
routeVariant: busSequences.routes[routeNumber]!.routeVariants.values.toList()[routeVariantIndex],
|
||||
scheduledTime: DateTime.fromMillisecondsSinceEpoch(int.parse(commandParts[4])),
|
||||
);
|
||||
|
||||
} else if (mode == "manual") {
|
||||
print("Announce manual command received");
|
||||
|
||||
final displayText = commandParts[2];
|
||||
|
||||
List<String> audioFileNames = commandParts.sublist(3);
|
||||
try {
|
||||
if (int.parse(audioFileNames.last) != null) {
|
||||
audioFileNames.removeLast();
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
DateTime scheduledTime = LiveInformation().syncedTimeModule.Now().add(Duration(seconds: 1));
|
||||
try {
|
||||
if (int.parse(commandParts.last) != null) {
|
||||
scheduledTime = DateTime.fromMillisecondsSinceEpoch(int.parse(commandParts.last));
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
announcementModule.queueAnnounceByAudioName(
|
||||
displayText: displayText,
|
||||
audioNames: audioFileNames,
|
||||
scheduledTime: scheduledTime,
|
||||
sendToServer: false
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String _extractId(String input) {
|
||||
RegExp regExp = RegExp(r'\("user:(.*)"\)');
|
||||
Match match = regExp.firstMatch(input)!;
|
||||
return match.group(1)!;
|
||||
}
|
||||
|
||||
Future<void> SendCommand(String command) async {
|
||||
|
||||
final client = auth.client;
|
||||
final databases = appwrite.Databases(client);
|
||||
|
||||
final response = await databases.listDocuments(
|
||||
databaseId: "6633e85400036415ab0f",
|
||||
collectionId: "6633e85d0020f52f3771",
|
||||
queries: [
|
||||
appwrite.Query.search("SessionID", roomCode!)
|
||||
]
|
||||
);
|
||||
|
||||
List<String> pastCommands = [];
|
||||
|
||||
response.documents.first.data["Commands"].forEach((element) {
|
||||
pastCommands.add(element);
|
||||
});
|
||||
|
||||
pastCommands.add(command);
|
||||
|
||||
final document = await databases.updateDocument(
|
||||
databaseId: "6633e85400036415ab0f",
|
||||
collectionId: "6633e85d0020f52f3771",
|
||||
documentId: roomDocumentID!,
|
||||
data: {
|
||||
"Commands": pastCommands,
|
||||
"LastUpdater": auth.userID,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
List<String> _splitCommand(String command) {
|
||||
var regex = RegExp(r'([^\s"]+)|"([^"]*)"');
|
||||
var matches = regex.allMatches(command);
|
||||
return matches.map((match) => match.group(0)!.replaceAll('"', '')).toList();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -162,8 +532,6 @@ class LiveInformation {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
class AnnouncementQueueEntry {
|
||||
|
||||
@@ -74,7 +74,7 @@ class AnnouncementModule extends InfoModule {
|
||||
final EventDelegate<AnnouncementQueueEntry> onAnnouncement = EventDelegate();
|
||||
|
||||
// Timer
|
||||
Timer refreshTimer() => Timer.periodic(const Duration(milliseconds: 200), (timer) async {
|
||||
Timer refreshTimer() => Timer.periodic(const Duration(milliseconds: 10), (timer) async {
|
||||
|
||||
if (!isPlaying) {
|
||||
|
||||
@@ -84,7 +84,7 @@ class AnnouncementModule extends InfoModule {
|
||||
|
||||
bool proceeding = await _internalAccountForInconsistentTime(
|
||||
announcement: nextAnnouncement,
|
||||
timerInterval: const Duration(milliseconds: 200),
|
||||
timerInterval: const Duration(milliseconds: 10),
|
||||
callback: () {
|
||||
queue.removeAt(0);
|
||||
print("Announcement proceeding");
|
||||
@@ -105,35 +105,21 @@ class AnnouncementModule extends InfoModule {
|
||||
|
||||
if (currentAnnouncement!.audioSources.isNotEmpty) {
|
||||
|
||||
// audioPlayer.loadSource(AudioWrapperAssetSource("assets/audio/5-seconds-of-silence.mp3"));
|
||||
// audioPlayer.play();
|
||||
// await Future.delayed(const Duration(milliseconds: 300));
|
||||
// audioPlayer.stop();
|
||||
// Prime all of the audio sources to be ready to play
|
||||
for (AudioWrapperSource source in currentAnnouncement!.audioSources) {
|
||||
try {
|
||||
await audioPlayer.loadSource(source);
|
||||
await Future.delayed((await audioPlayer.play())!);
|
||||
audioPlayer.stop();
|
||||
|
||||
// try {
|
||||
for (AudioWrapperSource source in currentAnnouncement!.audioSources) {
|
||||
try {
|
||||
await audioPlayer.loadSource(source);
|
||||
|
||||
Duration? duration = await audioPlayer.play();
|
||||
await Future.delayed(duration!);
|
||||
audioPlayer.stop();
|
||||
// await Future.delayed(const Duration(milliseconds: 100));
|
||||
if (currentAnnouncement?.audioSources.last != source) {
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
}
|
||||
} catch (e) {
|
||||
// Do nothing
|
||||
// print("Error playing announcement: $e on ${currentAnnouncement?.displayText}");
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
if (currentAnnouncement?.audioSources.last != source) {
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
}
|
||||
} catch (e) {
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
}
|
||||
// audioPlayer.stop();
|
||||
}
|
||||
|
||||
// } catch (e) {
|
||||
// // Do nothing
|
||||
// print("Error playing announcement: $e on ${currentAnnouncement?.displayTex}");
|
||||
// }
|
||||
} else {
|
||||
if (queue.isNotEmpty) {
|
||||
await Future.delayed(const Duration(seconds: 5));
|
||||
@@ -179,7 +165,7 @@ class AnnouncementModule extends InfoModule {
|
||||
}
|
||||
|
||||
// Configuration
|
||||
int get defaultAnnouncementDelay => liveInformation.auth.isAuthenticated() ? 2 : 0;
|
||||
int get defaultAnnouncementDelay => liveInformation.auth.isAuthenticated() ? 1 : 0;
|
||||
|
||||
// Methods
|
||||
Future<void> queueAnnounceByAudioName({
|
||||
@@ -199,8 +185,12 @@ class AnnouncementModule extends InfoModule {
|
||||
audioNamesString += "\"$audioName\" ";
|
||||
}
|
||||
|
||||
liveInformation.commandModule.executeCommand(
|
||||
"announce manual \"$displayText\" ${audioNamesString} ${scheduledTime?.millisecondsSinceEpoch ?? ""}"
|
||||
liveInformation.SendCommand("announce manual \"$displayText\" $audioNamesString ${scheduledTime.millisecondsSinceEpoch}");
|
||||
queueAnnounceByAudioName(
|
||||
displayText: displayText,
|
||||
audioNames: audioNames,
|
||||
scheduledTime: scheduledTime,
|
||||
sendToServer: false
|
||||
);
|
||||
|
||||
return;
|
||||
@@ -244,9 +234,13 @@ class AnnouncementModule extends InfoModule {
|
||||
|
||||
scheduledTime ??= liveInformation.syncedTimeModule.Now().add(Duration(seconds: defaultAnnouncementDelay));
|
||||
|
||||
liveInformation.commandModule.executeCommand(
|
||||
"announce info $infoIndex ${scheduledTime?.millisecondsSinceEpoch ?? ""}"
|
||||
liveInformation.SendCommand("announce info $infoIndex ${scheduledTime.millisecondsSinceEpoch}");
|
||||
queueAnnounementByInfoIndex(
|
||||
infoIndex: infoIndex,
|
||||
scheduledTime: scheduledTime,
|
||||
sendToServer: false
|
||||
);
|
||||
print("Announcement sent to server");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -270,9 +264,16 @@ class AnnouncementModule extends InfoModule {
|
||||
|
||||
scheduledTime ??= liveInformation.syncedTimeModule.Now().add(Duration(seconds: defaultAnnouncementDelay));
|
||||
|
||||
liveInformation.commandModule.executeCommand(
|
||||
"announce dest \"${routeVariant.busRoute.routeNumber}\" ${routeVariant.busRoute.routeVariants.values.toList().indexOf(routeVariant)} ${scheduledTime?.millisecondsSinceEpoch ?? ""}"
|
||||
String routeNumber = routeVariant.busRoute.routeNumber;
|
||||
int routeVariantIndex = routeVariant.busRoute.routeVariants.values.toList().indexOf(routeVariant);
|
||||
|
||||
liveInformation.SendCommand("announce dest ${routeNumber} ${routeVariantIndex} ${scheduledTime.millisecondsSinceEpoch}");
|
||||
queueAnnouncementByRouteVariant(
|
||||
routeVariant: routeVariant,
|
||||
scheduledTime: scheduledTime,
|
||||
sendToServer: false
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
print("Checkpoint 4");
|
||||
|
||||
@@ -47,17 +47,37 @@ class CommandModule extends InfoModule {
|
||||
|
||||
final databases = appwrite.Databases(client);
|
||||
|
||||
if (liveInformation.auth.status == AuthStatus.AUTHENTICATED) {
|
||||
final document = await databases.createDocument(
|
||||
databaseId: ApiConstants.INFO_Q_DATABASE_ID,
|
||||
collectionId: ApiConstants.COMMANDS_COLLECTION_ID,
|
||||
documentId: appwrite.ID.unique(),
|
||||
if (true) {
|
||||
try {
|
||||
final response = await databases.listDocuments(
|
||||
databaseId: "6633e85400036415ab0f",
|
||||
collectionId: "6633e85d0020f52f3771",
|
||||
queries: [
|
||||
appwrite.Query.search("SessionID", liveInformation.roomCode!)
|
||||
]
|
||||
);
|
||||
|
||||
List<String> pastCommands = [];
|
||||
|
||||
response.documents.first.data["Commands"].forEach((element) {
|
||||
pastCommands.add(element);
|
||||
});
|
||||
|
||||
pastCommands.add(command);
|
||||
|
||||
final document = await databases.updateDocument(
|
||||
databaseId: "6633e85400036415ab0f",
|
||||
collectionId: "6633e85d0020f52f3771",
|
||||
documentId: liveInformation.roomDocumentID!,
|
||||
data: {
|
||||
"session_id": sessionID,
|
||||
"command": command,
|
||||
"client_id": clientID,
|
||||
"Commands": pastCommands,
|
||||
"LastUpdater": clientID,
|
||||
}
|
||||
);
|
||||
);
|
||||
} catch (e) {
|
||||
print("Failed to send command");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
_onCommandReceived(CommandInfo(command, clientID));
|
||||
@@ -78,6 +98,10 @@ class CommandModule extends InfoModule {
|
||||
|
||||
if (command == "Response:") {
|
||||
|
||||
}
|
||||
else if (command == "initroom") {
|
||||
// initroom <roomCode>
|
||||
|
||||
}
|
||||
else if (command == "announce") {
|
||||
|
||||
@@ -166,10 +190,41 @@ class CommandModule extends InfoModule {
|
||||
BusRoute route = liveInformation.busSequences.routes[routeNumber]!;
|
||||
BusRouteVariant routeVariant = route.routeVariants.values.toList()[routeVariantIndex];
|
||||
|
||||
liveInformation.setRouteVariant_Internal(
|
||||
liveInformation.setRouteVariant(
|
||||
routeVariant
|
||||
);
|
||||
executeCommand("Response: v \"Client $clientID set its route to ($routeNumber to ${routeVariant.busStops.last.formattedStopName})\"");
|
||||
|
||||
|
||||
// Update the server
|
||||
if (liveInformation.isHost) {
|
||||
print("Updating server");
|
||||
final client = liveInformation.auth.client;
|
||||
final databases = appwrite.Databases(client);
|
||||
|
||||
final response = await databases.listDocuments(
|
||||
databaseId: "6633e85400036415ab0f",
|
||||
collectionId: "6633e85d0020f52f3771",
|
||||
queries: [
|
||||
appwrite.Query.search("SessionID", liveInformation.roomCode!)
|
||||
]
|
||||
);
|
||||
|
||||
final document = await databases.updateDocument(
|
||||
databaseId: "6633e85400036415ab0f",
|
||||
collectionId: "6633e85d0020f52f3771",
|
||||
documentId: response.documents.first.$id,
|
||||
data: {
|
||||
"CurrentRoute": routeNumber,
|
||||
"CurrentRouteVariant": routeVariantIndex,
|
||||
}
|
||||
);
|
||||
try {
|
||||
|
||||
print("Updated server");
|
||||
} catch (e) {
|
||||
print("Failed to update server");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -181,26 +236,26 @@ class CommandModule extends InfoModule {
|
||||
return;
|
||||
}
|
||||
|
||||
final realtime = appwrite.Realtime(LiveInformation().auth.client);
|
||||
|
||||
_subscription = realtime.subscribe(
|
||||
['databases.${ApiConstants.INFO_Q_DATABASE_ID}.collections.${ApiConstants.COMMANDS_COLLECTION_ID}.documents']
|
||||
);
|
||||
_subscription!.stream.listen((event) {
|
||||
print(jsonEncode(event.payload));
|
||||
|
||||
// Only do something if the document was created or updated
|
||||
if (!(event.events.first.contains("create") || event.events.first.contains("update"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
final commandInfo = CommandInfo(event.payload['command'], event.payload['client_id']);
|
||||
|
||||
if (commandInfo.clientID != clientID) {
|
||||
_onCommandReceived(commandInfo);
|
||||
}
|
||||
|
||||
});
|
||||
// final realtime = appwrite.Realtime(LiveInformation().auth.client);
|
||||
//
|
||||
// _subscription = realtime.subscribe(
|
||||
// ['databases.${ApiConstants.INFO_Q_DATABASE_ID}.collections.${ApiConstants.COMMANDS_COLLECTION_ID}.documents']
|
||||
// );
|
||||
// _subscription!.stream.listen((event) {
|
||||
// print(jsonEncode(event.payload));
|
||||
//
|
||||
// // Only do something if the document was created or updated
|
||||
// if (!(event.events.first.contains("create") || event.events.first.contains("update"))) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// final commandInfo = CommandInfo(event.payload['command'], event.payload['client_id']);
|
||||
//
|
||||
// if (commandInfo.clientID != clientID) {
|
||||
// _onCommandReceived(commandInfo);
|
||||
// }
|
||||
//
|
||||
// });
|
||||
|
||||
print("Listening for commands");
|
||||
|
||||
|
||||
@@ -183,7 +183,6 @@ class TrackerModule extends InfoModule {
|
||||
print("Closest stop: ${closestStop.formattedStopName} in ${closestDistance.round()} meters");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
double _calculateRelativeDistance(BusRouteStop stop, double latitude, double longitude) {
|
||||
|
||||
@@ -68,7 +68,7 @@ class TubeStations {
|
||||
double distance = Vector2(stop.easting.toDouble(), stop.northing.toDouble()).distanceTo(OSGrid.toNorthingEasting(station.latitude, station.longitude));
|
||||
|
||||
// if the distance is less than 100m, then we can assume that the bus stop is near the tube station
|
||||
if (distance < 200) {
|
||||
if (distance < 400) {
|
||||
for (TubeLine line in station.lines) {
|
||||
lineMatches[line] = lineMatches[line]! + 1;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import 'package:bus_infotainment/utils/delegates.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
|
||||
class pages_Home extends StatelessWidget {
|
||||
const pages_Home({super.key});
|
||||
@@ -60,7 +61,7 @@ class pages_Home extends StatelessWidget {
|
||||
outlineColor: Colors.white70,
|
||||
announcements: [
|
||||
for (NamedAnnouncementQueueEntry announcement in LiveInformation().announcementModule.manualAnnouncements)
|
||||
_AnnouncementEntry(
|
||||
AnnouncementEntry(
|
||||
label: announcement.shortName,
|
||||
index: LiveInformation().announcementModule.manualAnnouncements.indexOf(announcement),
|
||||
outlineColor: Colors.white70,
|
||||
@@ -92,12 +93,12 @@ class pages_Home extends StatelessWidget {
|
||||
color: Colors.grey.shade900,
|
||||
),
|
||||
|
||||
child: DelegateBuilder<BusRouteVariant>(
|
||||
child: DelegateBuilder<BusRouteVariant?>(
|
||||
delegate: LiveInformation().routeVariantDelegate,
|
||||
builder: (context, routeVariant) {
|
||||
print("rebuilt stop announcement picker");
|
||||
return StopAnnouncementPicker(
|
||||
routeVariant: routeVariant,
|
||||
routeVariant: routeVariant!,
|
||||
backgroundColor: Colors.grey.shade900,
|
||||
outlineColor: Colors.white70,
|
||||
);
|
||||
@@ -133,11 +134,6 @@ class pages_Home extends StatelessWidget {
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
LiveInformation liveInformation = LiveInformation();
|
||||
final commandModule = liveInformation.commandModule;
|
||||
|
||||
// commandModule.executeCommand(
|
||||
// "announce dest"
|
||||
// );
|
||||
|
||||
liveInformation.announcementModule.queueAnnouncementByRouteVariant(
|
||||
routeVariant: liveInformation.getRouteVariant()!
|
||||
@@ -147,75 +143,7 @@ class pages_Home extends StatelessWidget {
|
||||
child: Text("Announce current destination"),
|
||||
),
|
||||
|
||||
|
||||
// Container(
|
||||
//
|
||||
// margin: EdgeInsets.all(20),
|
||||
//
|
||||
// height: 300-45,
|
||||
//
|
||||
// child: ListView(
|
||||
//
|
||||
// scrollDirection: Axis.vertical,
|
||||
//
|
||||
// children: [
|
||||
//
|
||||
// ElevatedButton(
|
||||
// onPressed: () async {
|
||||
// LiveInformation liveInformation = LiveInformation();
|
||||
// liveInformation.queueAnnouncement(await liveInformation.getDestinationAnnouncement(liveInformation.getRouteVariant()!, sendToServer: false));
|
||||
// },
|
||||
// child: Text("Test announcement"),
|
||||
// ),
|
||||
//
|
||||
// ElevatedButton(
|
||||
// onPressed: () {
|
||||
// LiveInformation liveInformation = LiveInformation();
|
||||
// liveInformation.updateServer();
|
||||
// },
|
||||
// child: Text("Update server"),
|
||||
// ),
|
||||
//
|
||||
// SizedBox(
|
||||
//
|
||||
// width: 100,
|
||||
//
|
||||
// child: TextField(
|
||||
// onChanged: (String value) {
|
||||
// LiveInformation liveInformation = LiveInformation();
|
||||
// // liveInformation.documentID = value;
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
//
|
||||
// SizedBox(
|
||||
//
|
||||
// width: 200,
|
||||
//
|
||||
// child: TextField(
|
||||
// onSubmitted: (String value) {
|
||||
// LiveInformation liveInformation = LiveInformation();
|
||||
// liveInformation.queueAnnouncement(AnnouncementQueueEntry(
|
||||
// displayText: value,
|
||||
// audioSources: []
|
||||
// ));
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
//
|
||||
// ElevatedButton(
|
||||
// onPressed: () {
|
||||
// LiveInformation liveInformation = LiveInformation();
|
||||
// liveInformation.pullServer();
|
||||
// },
|
||||
// child: Text("Pull server"),
|
||||
// ),
|
||||
//
|
||||
// ],
|
||||
//
|
||||
// ),
|
||||
//
|
||||
// ),
|
||||
|
||||
|
||||
],
|
||||
),
|
||||
@@ -366,8 +294,9 @@ class AnnouncementPicker extends StatefulWidget {
|
||||
final Color backgroundColor;
|
||||
final Color outlineColor;
|
||||
final List<Widget> announcements;
|
||||
final String label;
|
||||
|
||||
const AnnouncementPicker({super.key, required this.backgroundColor, required this.outlineColor, required this.announcements});
|
||||
const AnnouncementPicker({super.key, required this.backgroundColor, required this.outlineColor, required this.announcements, this.label = ""});
|
||||
|
||||
@override
|
||||
State<AnnouncementPicker> createState() => _AnnouncementPickerState();
|
||||
@@ -411,9 +340,9 @@ class _AnnouncementPickerState extends State<AnnouncementPicker> {
|
||||
color: widget.backgroundColor,
|
||||
border: Border.all(
|
||||
color: widget.outlineColor,
|
||||
width: 2
|
||||
width: 1
|
||||
),
|
||||
|
||||
borderRadius: BorderRadius.circular(8)
|
||||
|
||||
|
||||
),
|
||||
@@ -428,118 +357,26 @@ class _AnnouncementPickerState extends State<AnnouncementPicker> {
|
||||
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
height: 2,
|
||||
color: widget.outlineColor,
|
||||
),
|
||||
|
||||
if (_currentIndex < announcementWidgets.length)
|
||||
announcementWidgets[_currentIndex + 0]
|
||||
else
|
||||
Container(
|
||||
height: 50,
|
||||
decoration: BoxDecoration(
|
||||
color: widget.backgroundColor,
|
||||
border: Border.symmetric(
|
||||
vertical: BorderSide(
|
||||
color: widget.outlineColor,
|
||||
width: 2
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Container(
|
||||
height: 2,
|
||||
color: widget.outlineColor,
|
||||
),
|
||||
|
||||
if (_currentIndex + 1 < announcementWidgets.length)
|
||||
announcementWidgets[_currentIndex + 1]
|
||||
else
|
||||
Container(
|
||||
height: 50,
|
||||
decoration: BoxDecoration(
|
||||
color: widget.backgroundColor,
|
||||
border: Border.symmetric(
|
||||
vertical: BorderSide(
|
||||
color: widget.outlineColor,
|
||||
width: 2
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Container(
|
||||
height: 2,
|
||||
color: widget.outlineColor,
|
||||
),
|
||||
|
||||
if (_currentIndex + 2 < announcementWidgets.length)
|
||||
announcementWidgets[_currentIndex + 2]
|
||||
else
|
||||
Container(
|
||||
height: 50,
|
||||
decoration: BoxDecoration(
|
||||
color: widget.backgroundColor,
|
||||
border: Border.symmetric(
|
||||
vertical: BorderSide(
|
||||
color: widget.outlineColor,
|
||||
width: 2
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Container(
|
||||
height: 2,
|
||||
color: widget.outlineColor,
|
||||
),
|
||||
|
||||
if (_currentIndex + 3 < announcementWidgets.length)
|
||||
announcementWidgets[_currentIndex + 3]
|
||||
else
|
||||
Container(
|
||||
height: 50,
|
||||
decoration: BoxDecoration(
|
||||
color: widget.backgroundColor,
|
||||
border: Border.symmetric(
|
||||
vertical: BorderSide(
|
||||
color: widget.outlineColor,
|
||||
width: 2
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Container(
|
||||
height: 2,
|
||||
color: widget.outlineColor,
|
||||
),
|
||||
|
||||
Container(
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: widget.backgroundColor,
|
||||
border: Border.symmetric(
|
||||
vertical: BorderSide(
|
||||
color: widget.outlineColor,
|
||||
width: 2
|
||||
)
|
||||
border: Border.all(
|
||||
color: widget.outlineColor,
|
||||
width: 1
|
||||
),
|
||||
borderRadius: BorderRadius.circular(4)
|
||||
),
|
||||
|
||||
alignment: Alignment.centerRight,
|
||||
|
||||
child: Row(
|
||||
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
||||
// height: 100,
|
||||
child: Column(
|
||||
children: [
|
||||
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
|
||||
if (_currentIndex < announcementWidgets.length)
|
||||
announcementWidgets[_currentIndex + 0]
|
||||
else
|
||||
Container(
|
||||
height: 50,
|
||||
decoration: BoxDecoration(
|
||||
color: widget.backgroundColor,
|
||||
border: Border.symmetric(
|
||||
@@ -549,50 +386,18 @@ class _AnnouncementPickerState extends State<AnnouncementPicker> {
|
||||
)
|
||||
),
|
||||
),
|
||||
|
||||
margin: const EdgeInsets.symmetric(
|
||||
horizontal: 4
|
||||
),
|
||||
|
||||
child: Container(
|
||||
child: Stack(
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
child: Icon(
|
||||
Icons.arrow_upward,
|
||||
color: widget.outlineColor,
|
||||
),
|
||||
),
|
||||
Positioned.fill(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
_currentIndex = wrap(_currentIndex - 4, 0, announcementWidgets.length, increment: 4);
|
||||
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,
|
||||
height: 1,
|
||||
color: widget.outlineColor,
|
||||
),
|
||||
|
||||
if (_currentIndex + 1 < announcementWidgets.length)
|
||||
announcementWidgets[_currentIndex + 1]
|
||||
else
|
||||
Container(
|
||||
height: 50,
|
||||
decoration: BoxDecoration(
|
||||
color: widget.backgroundColor,
|
||||
border: Border.symmetric(
|
||||
@@ -602,55 +407,200 @@ class _AnnouncementPickerState extends State<AnnouncementPicker> {
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
margin: const EdgeInsets.symmetric(
|
||||
horizontal: 4
|
||||
),
|
||||
|
||||
child: Container(
|
||||
child: Stack(
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
child: Icon(
|
||||
Icons.arrow_downward,
|
||||
color: widget.outlineColor,
|
||||
),
|
||||
),
|
||||
Positioned.fill(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
_currentIndex = wrap(_currentIndex + 4, 0, announcementWidgets.length, increment: 4);
|
||||
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: 1,
|
||||
color: widget.outlineColor,
|
||||
),
|
||||
|
||||
]
|
||||
if (_currentIndex + 2 < announcementWidgets.length)
|
||||
announcementWidgets[_currentIndex + 2]
|
||||
else
|
||||
Container(
|
||||
height: 50,
|
||||
decoration: BoxDecoration(
|
||||
color: widget.backgroundColor,
|
||||
border: Border.symmetric(
|
||||
vertical: BorderSide(
|
||||
color: widget.outlineColor,
|
||||
width: 2
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Container(
|
||||
height: 1,
|
||||
color: widget.outlineColor,
|
||||
),
|
||||
|
||||
if (_currentIndex + 3 < announcementWidgets.length)
|
||||
announcementWidgets[_currentIndex + 3]
|
||||
else
|
||||
Container(
|
||||
height: 50,
|
||||
decoration: BoxDecoration(
|
||||
color: widget.backgroundColor,
|
||||
border: Border.symmetric(
|
||||
vertical: BorderSide(
|
||||
color: widget.outlineColor,
|
||||
width: 2
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Container(
|
||||
height: 1,
|
||||
color: widget.outlineColor,
|
||||
),
|
||||
|
||||
Container(
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: widget.backgroundColor,
|
||||
),
|
||||
|
||||
child: Row(
|
||||
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
||||
children: [
|
||||
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
),
|
||||
child: Text(
|
||||
widget.label,
|
||||
style: ShadTheme.of(context).textTheme.h4.copyWith(
|
||||
shadows: [
|
||||
Shadow(
|
||||
color: Colors.blueAccent.shade700,
|
||||
blurRadius: 8
|
||||
)
|
||||
],
|
||||
color: Colors.blueAccent.shade700
|
||||
)
|
||||
),
|
||||
),
|
||||
|
||||
Expanded(child: Container()),
|
||||
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: widget.backgroundColor,
|
||||
border: Border.symmetric(
|
||||
vertical: BorderSide(
|
||||
color: widget.outlineColor,
|
||||
width: 1
|
||||
)
|
||||
),
|
||||
),
|
||||
|
||||
margin: const EdgeInsets.symmetric(
|
||||
horizontal: 4
|
||||
),
|
||||
|
||||
child: Container(
|
||||
child: Stack(
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
child: Icon(
|
||||
Icons.arrow_upward,
|
||||
color: widget.outlineColor,
|
||||
),
|
||||
),
|
||||
Positioned.fill(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
_currentIndex = wrap(_currentIndex - 4, 0, announcementWidgets.length, increment: 4);
|
||||
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: widget.backgroundColor,
|
||||
border: Border.symmetric(
|
||||
vertical: BorderSide(
|
||||
color: widget.outlineColor,
|
||||
width: 1
|
||||
)
|
||||
),
|
||||
),
|
||||
|
||||
margin: const EdgeInsets.symmetric(
|
||||
horizontal: 4
|
||||
),
|
||||
|
||||
child: Container(
|
||||
child: Stack(
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
child: Icon(
|
||||
Icons.arrow_downward,
|
||||
color: widget.outlineColor,
|
||||
),
|
||||
),
|
||||
Positioned.fill(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
_currentIndex = wrap(_currentIndex + 4, 0, announcementWidgets.length, increment: 4);
|
||||
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: widget.outlineColor,
|
||||
),
|
||||
|
||||
]
|
||||
@@ -669,13 +619,14 @@ class StopAnnouncementPicker extends AnnouncementPicker {
|
||||
required this.routeVariant,
|
||||
required Color backgroundColor,
|
||||
required Color outlineColor,
|
||||
String label = "Stops"
|
||||
}) : super(
|
||||
key: key,
|
||||
backgroundColor: backgroundColor,
|
||||
outlineColor: outlineColor,
|
||||
announcements: [
|
||||
for (BusRouteStop stop in routeVariant.busStops)
|
||||
_AnnouncementEntry(
|
||||
AnnouncementEntry(
|
||||
label: stop.formattedStopName,
|
||||
onPressed: () {
|
||||
LiveInformation liveInformation = LiveInformation();
|
||||
@@ -688,7 +639,8 @@ class StopAnnouncementPicker extends AnnouncementPicker {
|
||||
outlineColor: outlineColor,
|
||||
alert: LiveInformation().announcementModule.announcementCache[stop.getAudioFileName()] == null,
|
||||
)
|
||||
]
|
||||
],
|
||||
label: label
|
||||
);
|
||||
}
|
||||
|
||||
@@ -709,7 +661,7 @@ int wrap(int i, int j, int length, {int increment = -1}) {
|
||||
}
|
||||
}
|
||||
|
||||
class _AnnouncementEntry extends StatelessWidget {
|
||||
class AnnouncementEntry extends StatelessWidget {
|
||||
|
||||
final String label;
|
||||
|
||||
@@ -719,7 +671,7 @@ class _AnnouncementEntry extends StatelessWidget {
|
||||
|
||||
bool alert = false;
|
||||
|
||||
_AnnouncementEntry({super.key, required this.label, required this.onPressed, required this.index, required this.outlineColor, this.alert = false});
|
||||
AnnouncementEntry({super.key, required this.label, required this.onPressed, required this.index, required this.outlineColor, this.alert = false});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -730,12 +682,6 @@ class _AnnouncementEntry extends StatelessWidget {
|
||||
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
border: Border.symmetric(
|
||||
vertical: BorderSide(
|
||||
color: outlineColor,
|
||||
width: 2
|
||||
)
|
||||
),
|
||||
),
|
||||
|
||||
padding: const EdgeInsets.symmetric(
|
||||
@@ -760,7 +706,7 @@ class _AnnouncementEntry extends StatelessWidget {
|
||||
label,
|
||||
style: GoogleFonts.teko(
|
||||
fontSize: 25,
|
||||
color: outlineColor,
|
||||
color: Colors.white,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
|
||||
@@ -1228,11 +1228,11 @@ class _ConsoleState extends State<Console> {
|
||||
// TODO: implement initState
|
||||
super.initState();
|
||||
|
||||
_listenerReceipt = LiveInformation().commandModule.onCommandReceived.addListener((p0) {
|
||||
/*_listenerReceipt = LiveInformation().commandModule.onCommandReceived.addListener((p0) {
|
||||
print("Command received, updating console");
|
||||
|
||||
setState(() {});
|
||||
});
|
||||
});*/
|
||||
|
||||
}
|
||||
|
||||
@@ -1253,7 +1253,7 @@ class _ConsoleState extends State<Console> {
|
||||
Text("Command History:")
|
||||
);
|
||||
|
||||
for (int i = 0; i < LiveInformation().commandModule.commandHistory.length; i++){
|
||||
/*for (int i = 0; i < LiveInformation().commandModule.commandHistory.length; i++){
|
||||
CommandInfo command = LiveInformation().commandModule.commandHistory[i];
|
||||
|
||||
commands.add(
|
||||
@@ -1271,7 +1271,7 @@ class _ConsoleState extends State<Console> {
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
*/
|
||||
return Container(
|
||||
|
||||
decoration: BoxDecoration(
|
||||
@@ -1299,7 +1299,7 @@ class _ConsoleState extends State<Console> {
|
||||
color: Colors.white70,
|
||||
),
|
||||
|
||||
Container(
|
||||
/*Container(
|
||||
height: 50,
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: TextField(
|
||||
@@ -1315,7 +1315,7 @@ class _ConsoleState extends State<Console> {
|
||||
LiveInformation().commandModule.executeCommand(value);
|
||||
},
|
||||
),
|
||||
)
|
||||
)*/
|
||||
|
||||
],
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
|
||||
|
||||
import 'package:bus_infotainment/pages/tfl_dataset_test.dart';
|
||||
import 'package:bus_infotainment/remaster/InitialStartup.dart';
|
||||
import 'package:bus_infotainment/remaster/dashboard.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -15,13 +16,22 @@ class RemasteredApp extends StatelessWidget {
|
||||
darkTheme: ShadThemeData(
|
||||
brightness: Brightness.dark,
|
||||
colorScheme: ShadSlateColorScheme.dark(),
|
||||
// force dark mode
|
||||
),
|
||||
themeMode: ThemeMode.dark,
|
||||
|
||||
routes: {
|
||||
'/setup': (context) => InitialStartup(),
|
||||
'/': (context) => HomePage_Re(),
|
||||
'/routes': (context) => RoutePage(),
|
||||
'/enroute': (context) => EnRoutePage(),
|
||||
'/legacy': (context) => TfL_Dataset_Test(),
|
||||
'/multi': (context) => MultiModeSetup(),
|
||||
'/multi/enroute': (context) => MultiModeEnroute(),
|
||||
'/multi/login': (context) => MultiModeLogin(),
|
||||
'/multi/register': (context) => MultiModeRegister(),
|
||||
'/display': (context) => FullscreenDisplay(),
|
||||
'/multi/join': (context) => MultiModeJoin(),
|
||||
|
||||
},
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
112
lib/workaround/keepalive_realtime.dart
Normal file
112
lib/workaround/keepalive_realtime.dart
Normal file
@@ -0,0 +1,112 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:appwrite/appwrite.dart';
|
||||
import 'package:web_socket_channel/web_socket_channel.dart';
|
||||
// import this package https://pub.dev/packages/web_socket_channel
|
||||
|
||||
class RealtimeKeepAliveConnection {
|
||||
RealtimeKeepAliveConnection({
|
||||
required this.channels,
|
||||
required this.domain,
|
||||
required this.client,
|
||||
this.keepAlivePingDuration = const Duration(seconds: 90),
|
||||
required this.onData,
|
||||
required this.onError,
|
||||
});
|
||||
|
||||
final List<String> channels;
|
||||
final String domain;
|
||||
final Duration keepAlivePingDuration;
|
||||
final Client client;
|
||||
final Function(RealtimeMessage) onData;
|
||||
final Function(dynamic) onError;
|
||||
|
||||
// ignore: unused_field
|
||||
StreamSubscription<dynamic>? _subscription;
|
||||
WebSocketChannel? _webSocket;
|
||||
final Stopwatch _stopwatch = Stopwatch();
|
||||
bool _keepAlive = true;
|
||||
bool _sentKeepAlivePing = false;
|
||||
int reconnectCount = 0;
|
||||
|
||||
Future initialize() async {
|
||||
await _initRealtime(
|
||||
onData: _realtimeOnData,
|
||||
onDone: _realtimeOnDone,
|
||||
onError: _realtimeOnError,
|
||||
);
|
||||
_heartbeat();
|
||||
}
|
||||
|
||||
void close() {
|
||||
_keepAlive = false;
|
||||
_subscription!.cancel();
|
||||
}
|
||||
|
||||
void _heartbeat() async {
|
||||
while (_keepAlive) {
|
||||
await Future.delayed(keepAlivePingDuration);
|
||||
if (_webSocket != null) {
|
||||
_sentKeepAlivePing = true;
|
||||
_webSocket!.sink.add("ping");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _realtimeOnData(RealtimeMessage data) {
|
||||
log("[$reconnectCount][${_stopwatch.elapsed}] onData");
|
||||
onData(data);
|
||||
}
|
||||
|
||||
void _realtimeOnDone() async {
|
||||
reconnectCount++;
|
||||
log("[$reconnectCount][${_stopwatch.elapsed}] onDone");
|
||||
if (_keepAlive) {
|
||||
if (_subscription != null) _subscription!.cancel();
|
||||
|
||||
_subscription = _subscription = await _initRealtime(
|
||||
onData: _realtimeOnData,
|
||||
onDone: _realtimeOnDone,
|
||||
onError: _realtimeOnError,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _realtimeOnError(dynamic e) {
|
||||
log("[$reconnectCount][${_stopwatch.elapsed}] onError:$e");
|
||||
onError(onError);
|
||||
}
|
||||
|
||||
Future _initRealtime({
|
||||
required Function(RealtimeMessage) onData,
|
||||
required Function() onDone,
|
||||
required Function(dynamic) onError,
|
||||
}) async {
|
||||
_stopwatch.reset();
|
||||
_stopwatch.start();
|
||||
String channelParams = channels.map((c) => "channels[]=$c").join('&');
|
||||
|
||||
String? projectId = client.config['project'];
|
||||
|
||||
final wssUrl = Uri.parse('wss://$domain/realtime?project=$projectId&$channelParams');
|
||||
_webSocket = WebSocketChannel.connect(wssUrl);
|
||||
|
||||
Realtime realtime = Realtime(client);
|
||||
RealtimeSubscription subscriptionRealTime = realtime.subscribe(channels);
|
||||
|
||||
subscriptionRealTime.stream.listen(onData, onDone: onDone, onError: onError);
|
||||
_subscription = _webSocket!.stream.listen(_handlePingMsg);
|
||||
}
|
||||
|
||||
void _handlePingMsg(dynamic response) {
|
||||
var json = jsonDecode(response);
|
||||
|
||||
if (json["type"] == "error" && _sentKeepAlivePing) {
|
||||
_sentKeepAlivePing = false;
|
||||
log("Web socket keep-alive heartbeat successful (Reconnect Count: $reconnectCount, Time alive: ${_stopwatch.elapsed})");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
32
pubspec.lock
32
pubspec.lock
@@ -185,6 +185,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.8"
|
||||
dart_ping:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dart_ping
|
||||
sha256: "2f5418d0a5c64e53486caaac78677b25725b1e13c33c5be834ce874ea18bd24f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.0.1"
|
||||
device_info_plus:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -565,6 +573,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
native_qr:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: native_qr
|
||||
sha256: "0928754b92305eb101a3359014a60ac5e90126534406b4dd6fb7550433978420"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.0.3"
|
||||
ntp:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -741,6 +757,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
qr:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: qr
|
||||
sha256: "64957a3930367bf97cc211a5af99551d630f2f4625e38af10edd6b19131b64b3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
qr_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: qr_flutter
|
||||
sha256: "5095f0fc6e3f71d08adef8feccc8cea4f12eec18a2e31c2e8d82cb6019f4b097"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.0"
|
||||
rive:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -52,6 +52,9 @@ dependencies:
|
||||
file_picker: ^8.0.0+1
|
||||
shadcn_ui: ^0.4.1
|
||||
flutter_carousel_widget: ^2.2.0
|
||||
dart_ping: ^9.0.1
|
||||
native_qr: ^0.0.3
|
||||
qr_flutter: ^4.1.0
|
||||
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
|
||||
Reference in New Issue
Block a user