Files
Bus-Infotainment--IBus-/lib/remaster/InitialStartup.dart
2024-05-20 09:06:38 +01:00

603 lines
17 KiB
Dart

import 'dart:io';
import 'package:bus_infotainment/audio_cache.dart';
import 'package:bus_infotainment/backend/live_information.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import 'package:shared_preferences/shared_preferences.dart';
class InitialStartup extends StatefulWidget {
@override
State<InitialStartup> createState() => _InitialStartupState();
}
class _InitialStartupState extends State<InitialStartup> {
int _page = 0;
@override
Widget build(BuildContext context) {
if (_page == 0 && !kIsWeb) {
_page = 1;
}
return Scaffold(
body: [
_page1(this),
_page2(this),
_page3(this)
][_page],
);
}
void setPage(int page) {
setState(() {
_page = page;
});
}
}
abstract class InitialStartupPage extends StatefulWidget {
_InitialStartupState parent;
InitialStartupPage(this.parent);
}
// Cookies page - only for web
class _page1 extends InitialStartupPage {
_page1(super.parent);
@override
State<_page1> createState() => _page1State();
}
class _page1State extends State<_page1> {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
padding: EdgeInsets.all(32),
alignment: Alignment.center,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"Cookies",
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.w600,
)
),
Text(
"This website uses first-party cookies for the storage of user data. These cookies are necessary for the functioning of the site and help us provide you with a better browsing experience. By using this website, you consent to the use of these cookies.",
textAlign: TextAlign.center,
),
SizedBox(
height: 8,
),
ShadButton(
onPressed: () {
widget.parent.setPage(1);
},
text: Text(
"I understand and agree"
),
)
],
)
);
}
}
// Permission request page
class _page2 extends InitialStartupPage {
_page2(super.parent);
@override
State<_page2> createState() => _page2State();
}
class _page2State extends State<_page2> {
Future<bool> _allPermissionsGranted() async {
List<bool> perms = [];
perms.addAll([
await Permission.manageExternalStorage.isGranted,
await Permission.location.isGranted
]);
return !perms.contains(false);
}
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(16),
alignment: Alignment.center,
child: SizedBox(
// width: double.infinity,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Permissions",
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.w600,
)
),
SizedBox(
height: 16,
),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
// mainAxisSize: MainAxisSize.min,
// crossAxisAlignment: CrossAxisAlignment.start,
children: [
ShadCard(
width: 300,
height: 200,
title: Text(
"Location",
),
description: Text(
"Your location is required for automatically updating your nearest bus stop."
),
content: Container(
child: Column(
children: [
SizedBox(
height: 4,
),
FutureBuilder(
future: Permission.location.isGranted,
builder: (context, val) {
bool isEnabled = true;
String text = "Request permission";
Color color = Colors.white;
if (val.hasData) {
isEnabled = !val.data!;
}
if (!isEnabled) {
text = "Permission granted!";
color = Colors.green.shade400;
}
return ShadButton(
text: Text(text),
onPressed: () async {
await Permission.location.request();
setState(() {
});
},
enabled: isEnabled,
backgroundColor: color,
);
},
),
],
),
),
),
SizedBox(
width: 16,
),
ShadCard(
width: 300,
height: 200,
title: Text(
"Storage",
),
description: Text(
"Storage access is required to access recorded announcements."
),
content: Container(
child: Column(
children: [
SizedBox(
height: 4,
),
FutureBuilder(
future: Permission.manageExternalStorage.isGranted,
builder: (context, val) {
bool isEnabled = true;
String text = "Request permission";
Color color = Colors.white;
if (val.hasData) {
isEnabled = !val.data!;
}
if (!isEnabled) {
text = "Permission granted!";
color = Colors.green.shade400;
}
return ShadButton(
text: Text(text),
onPressed: () async {
await Permission.manageExternalStorage.request();
setState(() {
});
},
enabled: isEnabled,
backgroundColor: color,
);
},
)
],
),
),
),
SizedBox(
width: 16,
),
ShadCard(
width: 300,
height: 200,
title: Text(
"Network",
),
description: Text(
"Network access is required for commincation between devices for multi mode."
),
content: Container(
child: Column(
children: [
SizedBox(
height: 4,
),
FutureBuilder(
future: Permission.nearbyWifiDevices.isGranted,
builder: (context, val) {
bool isEnabled = true;
String text = "Request permission";
Color color = Colors.white;
if (val.hasData) {
isEnabled = !val.data!;
}
if (!isEnabled) {
text = "Permission granted!";
color = Colors.green.shade400;
}
return ShadButton(
text: Text(text),
onPressed: () async {
await Permission.manageExternalStorage.request();
setState(() {
});
},
enabled: isEnabled,
backgroundColor: color,
);
},
)
],
),
),
),
],
),
),
SizedBox(
height: 16,
),
FutureBuilder(
future: _allPermissionsGranted(),
builder: (context, val) {
bool isEnabled = true;
String text = "Continue";
Color color = Colors.white;
if (val.hasData) {
isEnabled = val.data!;
}
if (!isEnabled) {
text = "Grant all permissions before continuing";
}
return ShadButton(
text: Text(text),
onPressed: () async {
widget.parent.setPage(2);
},
enabled: isEnabled,
backgroundColor: color,
width: double.infinity,
);
},
)
],
),
)
);
}
}
class _page3 extends InitialStartupPage {
_page3(super.parent);
@override
State<_page3> createState() => _page3State();
}
class _page3State extends State<_page3> {
bool _loadingAudio = false;
Future<bool> _announcementsUploaded() async {
try {
Uint8List bytes = await LiveInformation().announcementModule.getBundleBytes();
return true;
} catch (e) {
return false;
}
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
padding: EdgeInsets.all(16),
alignment: Alignment.center,
child: SizedBox(
width: double.infinity,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Prerequisites",
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.w600,
)
),
SizedBox(
height: 16,
),
ShadCard(
width: double.infinity,
title: Text(
"Announcement files",
),
description: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"You are required to upload the London iBus announcement files. These files can be acquire by submitting a request to TfL under the Freedom of Information Act (2000)"
),
SizedBox(
height: 4,
),
Text(
"Please upload a zip file."
)
],
),
content: Container(
child: Column(
children: [
SizedBox(
height: 4,
),
if (!_loadingAudio)
FutureBuilder(
future: _announcementsUploaded(),
builder: (context, val) {
bool isEnabled = true;
String text = "Upload file";
Color color = Colors.white;
if (val.hasData) {
isEnabled = !val.data!;
}
if (!isEnabled) {
text = "File uploaded!";
color = Colors.green.shade400;
}
return ShadButton(
text: Text(text),
onPressed: () async {
setState(() {
_loadingAudio = true;
});
FilePickerResult? result = await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: [
"zip"
]
);
if (result != null) {
late Uint8List bytes;
if (kIsWeb) {
bytes = result.files.single.bytes!;
} else {
File file = File(result.files.single.path!);
bytes = file.readAsBytesSync();
}
LiveInformation().announcementModule.setBundleBytes(bytes);
AnnouncementCache cache = LiveInformation().announcementModule.announcementCache;
if (!kIsWeb) {
// Use shared preferences to store the file location
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString("AnnouncementsFileLocation", result.files.single.path!);
}
// load a random announcement to ensure that the file is usable
await cache.loadAnnouncementsFromBytes(bytes, ["S_WALTHAMSTOW_CENTRAL_001.mp3"]);
setState(() {
_loadingAudio = false;
});
} else {
setState(() {
_loadingAudio = false;
});
}
},
enabled: isEnabled,
backgroundColor: color,
);
},
)
else
CircularProgressIndicator(),
],
),
),
),
SizedBox(
height: 16,
),
FutureBuilder(
future: _announcementsUploaded(),
builder: (context, val) {
bool isEnabled = true;
String text = "Continue";
Color color = Colors.white;
if (val.hasData) {
isEnabled = val.data!;
}
if (!isEnabled) {
text = "Complete the prerequisites to continue";
}
return ShadButton(
text: Text(text),
onPressed: () async {
showShadDialog(
context: context,
builder: (context) => ShadDialog.alert(
title: Text("You're all setup"),
description: Text("You can now continue to the application. \nTry not to annoy anyone on the bus! ;)"),
actions: [
ShadButton(
text: Text("Continue to application"),
onPressed: () {
Navigator.pushNamed(context, "/");
},
)
],
),
barrierDismissible: false
);
},
enabled: isEnabled,
backgroundColor: color,
width: double.infinity,
);
},
)
],
),
)
);
}
}