7 Commits
8 changed files with 186 additions and 44 deletions
+1
View File
@@ -1,4 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:label="oeffimaster"
android:name="${applicationName}"
+64
View File
@@ -0,0 +1,64 @@
erDiagram
journey||--|{routesegments: "consists of"
routesegments||--||segment: "sorts"
segment||--|{departure: "consists of"
departure}|--|{station: "originates from"
departure}|--||vehicle: uses
vehicle||..|{line: "shared data"
segment}|--||vehicle: uses
stationlines}|..||station: "general connections"
stationlines}|..||line: groups
journey {
int id PK
string startstation
string endstation
}
routesegments {
int id PK
int journey FK
int segment FK
int segmentorder
}
segment {
int id PK
string ref
int startid FK
int endid FK
string vehicleid FK
}
departure {
int id PK
string ref
string direction
string vehicle FK
string vehiclename
string station FK
string stationname
string arrivaltime
string departuretime
}
station {
string id PK
string name
}
stationlines {
int id PK
string stationid FK
string lineid FK
}
vehicle {
string vehicleid PK
string name
string line FK
}
line {
string lineid PK
string name
string mode
string destination
string direction
string fgcolor
string bgcolor
}
+8
View File
@@ -0,0 +1,8 @@
flowchart TD
Start-->stat[Search Station]-->selstat[Select station]
selstat-->seldep["`Select departure:
vehicle, direction & time`"]
seldep-->selarr["`Select arrival station:
when & where to exit`"] --> sw{switch?} --yes-->seldep
sw--"no"-->fin([journey details])
+19
View File
@@ -0,0 +1,19 @@
sequenceDiagram
participant api@{ "type": "entity"} as public transport API
participant ah as api_handler
participant ui as UI
participant th as transport_helper
participant db@{ "type":"database"} as database
ui-->>ah: find Station
ah-->>api: GET request w/ stationname
api-->>ah: return json
ah-->>th: provide json Map
th-->>db: store object
db-->>th: return id
th-->>ah: return object
ah-->>ui: display data
+10 -10
View File
@@ -1,10 +1,10 @@
- running mobile app
- users should be able to
- plan public transport in the VBB transit network
- create and customise routes quickly and on-the-fly
- edit routes from anywhere, where respective route information can be seen
- compare routes easily, especially between different endpoints
- the app should offer up the information most relevant to current route planning
- between route steps, there should be no more than 3 taps required, optimally even just one(1), scrolling excluded
- e.g. only lines that connect to a station should be shown
- stations that a line goes through should be available as next node/ step for planning
- [x] running mobile app
- [ ] users should be able to
- [~] plan public transport in the VBB transit network
- [~] create and customise routes quickly and on-the-fly
- [ ] edit routes from anywhere, where respective route information can be seen
- [ ] compare routes easily, especially between different endpoints
- [~] the app should offer up the information most relevant to current route planning
- [x] between route steps, there should be no more than 3 taps required, optimally even just one(1), scrolling excluded
- [x] e.g. only lines that connect to a station should be shown
- [x] stations that a line goes through should be available as next node/ step for planning
+12 -9
View File
@@ -6,19 +6,19 @@ import 'dart:convert';
import 'transport_helper.dart';
String timeOf(DateTime fullDT) {
// doing it this way so we always get two digits on the hour
String full = fullDT.toString();
List<String> splits = full.split(" ")[1].split(":");
String time = '${splits[0]}:${splits[1]}';
return time;
}
class ApiHandler {
// TODO: Split next line for better Error Handling
static final String baseUrl = dotenv.get("VBB_API_URL");
static final String accessID = dotenv.get("VBB_API_KEY");
// TODO: function calls for the following API calls
// - [x] location.name: findStations
// - [ ] location.nearbystops
// - [ ] location.details
// - [ ] departureBoard
// - [ ] multiDepartureBoard
// - [ ] journeyDetails
//static Future<Map<String, dynamic>> findStations(String locationName) async {
static Future<List<Station>> findStations(
String locationName, {
@@ -79,6 +79,7 @@ class ApiHandler {
static Future<List<Departure>> departures(
String stationId, {
int? maxDepartures = -1,
DateTime? time,
int? duration = 0,
}) async {
String urlPath = '/api/fahrinfo/latest/departureBoard';
@@ -87,7 +88,9 @@ class ApiHandler {
Uri url = Uri.https(baseUrl, urlPath, {
'accessId': accessID,
'id': stationString,
////'time': /current time //time from which departures will be shown
'time': timeOf(
time ?? DateTime.now(),
), //time from which departures will be shown
'duration': duration.toString(), //interval time in minutes
'maxJourneys': maxDepartures.toString(),
//'type': 'S', // Station/Stops only
+66 -17
View File
@@ -273,9 +273,11 @@ class StationList extends StatelessWidget {
class ChooseDeparturePage extends StatefulWidget {
final Station startStation;
final int? journeyId;
final Departure? arrival;
const ChooseDeparturePage({
super.key,
this.journeyId,
this.arrival,
required this.startStation,
});
@@ -288,7 +290,10 @@ class _ChooseDeparturePageState extends State<ChooseDeparturePage> {
Journey? singleJourney;
void loadDepartures(Station startStation) async {
departureList = await ApiHandler.departures(startStation.id);
departureList = await ApiHandler.departures(
startStation.id,
time: widget.arrival?.arrivalTime ?? DateTime.now(),
);
setState(() {});
}
@@ -330,16 +335,25 @@ class _ChooseDeparturePageState extends State<ChooseDeparturePage> {
),
),
),
ElevatedButton(
// ElevatedButton(
// onPressed: () {
// if (widget.journeyId != null) {
// loadJourney(widget.journeyId as int);
// }
// },
// child: const Text('Finish Route'),
// ),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
if (widget.journeyId != null) {
loadJourney(widget.journeyId as int);
}
},
child: const Text('Finish Route'),
),
],
),
tooltip: 'Finish route',
child: const Icon(Icons.check),
),
);
}
@@ -470,6 +484,7 @@ class _ChooseArrivalPageState extends State<ChooseArrivalPage> {
builder: (context) => ChooseDeparturePage(
startStation: startStation,
journeyId: journeyId,
arrival: arrival,
),
),
);
@@ -528,8 +543,32 @@ class _JourneyDetailPageState extends State<JourneyDetailPage> {
return Scaffold(
appBar: AppBar(title: Text('Connection Details \n$duration')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: SizedBox(
height: 400,
child: SegmentList(journey: widget.singleJourney),
),
),
Container(
margin: const EdgeInsets.all(20.0),
child: ElevatedButton(
onPressed: () {
Navigator.pop(
context,
MaterialPageRoute(
builder: (context) => JourneyOverviewPage(),
),
);
},
child: const Text('View all Journeys'),
),
),
],
),
),
);
}
}
@@ -596,6 +635,16 @@ class _JourneyOverviewPageState extends State<JourneyOverviewPage> {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: EdgeInsets.only(left: 30, top: 50),
width: 500.0,
height: 100.0,
child: FittedBox(
fit: BoxFit.contain,
alignment: Alignment.centerLeft,
child: Text("Your Journeys"),
),
),
FutureBuilder(
future: loadJourneys(),
builder: (context, snapshot) {
@@ -648,17 +697,17 @@ class _JourneyOverviewPageState extends State<JourneyOverviewPage> {
);
},
),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ChooseStationPage(),
),
);
},
child: const Text('Start a route'),
),
// ElevatedButton(
// onPressed: () {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => ChooseStationPage(),
// ),
// );
// },
// child: const Text('Start a route'),
// ),
],
),
),
+1 -3
View File
@@ -187,9 +187,7 @@ class Journey {
database.execute("""CREATE TABLE IF NOT EXISTS '$tableName'(
id INTEGER PRIMARY KEY AUTOINCREMENT,
startstation TEXT,
endstation TEXT,
FOREIGN KEY (startstation) REFERENCES station(name)
FOREIGN KEY (endstation) REFERENCES station(name)
endstation TEXT
);
""");
}