Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 112 additions & 9 deletions Space_Mapper/lib/ui/list_view.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,93 @@
import 'package:flutter/material.dart';
import 'package:flutter_background_geolocation/flutter_background_geolocation.dart'
as bg;
import 'package:geocoding/geocoding.dart';

Future<String?> getLocationData(
String dataType, double lat, double long) async {
try {
List<Placemark> placemarks = await placemarkFromCoordinates(
lat,
long,
);
switch (dataType) {
case "locality":
return placemarks[0].locality;
case "subAdministrativeArea":
return placemarks[0].subAdministrativeArea;
case "ISOCountry":
return placemarks[0].isoCountryCode;
default:
return "";
}
} catch (err) {}
}

Future<List<dynamic>>? buildLocationsList() async {
List locations = await bg.BackgroundGeolocation.locations;
List ret = [];

for (int i = 0; i < locations.length; ++i) {
String? locality = await getLocationData(
"locality",
locations[i]['coords']['latitude'],
locations[i]['coords']['longitude']);
String? administrativeArea = await getLocationData(
"subAdministrativeArea",
locations[i]['coords']['latitude'],
locations[i]['coords']['longitude']);
// ignore: non_constant_identifier_names
String? ISOCountry = await getLocationData(
"ISOCountry",
locations[i]['coords']['latitude'],
locations[i]['coords']['longitude']);
String timestamp = locations[i]['timestamp'];
String activity = locations[i]['activity']['type'];
num speed = locations[i]['coords']['speed'];
num altitude = locations[i]['coords']['altitude'];
var add = new DisplayLocation(locality!, administrativeArea!, ISOCountry!,
timestamp, activity, speed, altitude);
ret.add(add);
}
return ret;
}

class DisplayLocation {
DisplayLocation(this.locality, this.subAdministrativeArea, this.ISOCountry,
timestamp, this.activity, this.speed, this.altitude) {
this.timestamp = formatTimestamp(timestamp);
}
late String locality;
late String subAdministrativeArea;
// ignore: non_constant_identifier_names
late String ISOCountry;
late String timestamp;
late String activity;
late num speed;
late num altitude;

String formatTimestamp(String timestamp) {
//2021-10-25T21:25:08.210Z <- This is the original format
//2021-10-25 | 21:25:08 <- This is the result
String result = "";
for (int i = 0; i < timestamp.length; ++i) {
if (timestamp[i] != "T" && timestamp[i] != ".")
result += timestamp[i];
else if (timestamp[i] == "T")
result += " | ";
else if (timestamp[i] == ".") break;
}
return result;
}
}

class STOListView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Location List")),
appBar: AppBar(title: Text("Location History")),
body: FutureBuilder<List>(
future: bg.BackgroundGeolocation.locations,
future: buildLocationsList(),
builder: (context, snapshot) {
if (snapshot.hasData) {
List? data = snapshot.data;
Expand All @@ -25,23 +104,47 @@ class STOListView extends StatelessWidget {
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
var thisLocation = data[index];
DisplayLocation thisLocation = data[index];
return _tile(
thisLocation['timestamp'] +
" activity: " +
thisLocation['activity']['type'].toString(),
thisLocation['coords'].toString(),
thisLocation.locality +
", " +
thisLocation.subAdministrativeArea +
", " +
thisLocation.ISOCountry,
thisLocation.timestamp,
" \nActivity: " +
thisLocation.activity +
" \nSpeed: " +
thisLocation.speed.toString() +
" \nAltitude: " +
thisLocation.altitude.toString(),
Icons.gps_fixed);
});
}

ListTile _tile(String title, String subtitle, IconData icon) => ListTile(
ListTile _tile(String title, String subtitle, String text, IconData icon) =>
ListTile(
title: Text(title,
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 20,
)),
subtitle: Text(subtitle),
subtitle: new RichText(
text: new TextSpan(
// Note: Styles for TextSpans must be explicitly defined.
// Child text spans will inherit styles from parent
style: new TextStyle(
fontSize: 14.0,
color: Colors.black,
),
children: <TextSpan>[
new TextSpan(
text: subtitle,
style: new TextStyle(fontWeight: FontWeight.bold)),
new TextSpan(text: text),
],
),
),
leading: Icon(
icon,
color: Colors.blue[500],
Expand Down
159 changes: 159 additions & 0 deletions Space_Mapper/lib/ui/report_an_issue.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import 'package:asm/ui_style/report_an_issue_style.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:mailto/mailto.dart';

class ReportAnIssue extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Report an Issue")),
body: reportIssueBody(context),
);
}
}

Widget reportIssueBody(BuildContext context) {
List<String> emails = ['john.palmer@upf.edu', 'pablogalve100@gmail.com'];

return Padding(
padding: EdgeInsets.all(ReportAnIssueStyle.screenPadding),
child: Column(
children: [
Text(
"Help us improve by either reporting an issue or requesting a useful feature.",
style: TextStyle(fontSize: ReportAnIssueStyle.normalTextSize),
),
displayService(
"Github",
Icon(
AntDesign.github,
size: ReportAnIssueStyle.iconSize,
)),
Container(
margin: EdgeInsets.only(
bottom: ReportAnIssueStyle.marginBetweenTextAndButtons),
child: Text(
"Report issues on github to get the fastest solution.",
style: TextStyle(fontSize: ReportAnIssueStyle.normalTextSize),
),
),
customButtonWithUrl(
"Go to Github Issues",
"https://github.com/ActivitySpaceProject/space_mapper/issues",
ReportAnIssueStyle.requestFeatureColor,
context),
Container(
//Container only to add more margin
margin: EdgeInsets.only(
bottom: ReportAnIssueStyle.marginBetweenTextAndButtons),
),
displayService(
"Email",
Icon(
Icons.email_outlined,
size: ReportAnIssueStyle.iconSize,
)),
Container(
margin: EdgeInsets.only(
bottom: ReportAnIssueStyle.marginBetweenTextAndButtons),
child: Text(
"As an alternative, you can send us an email.",
style: TextStyle(fontSize: ReportAnIssueStyle.normalTextSize),
)),
customButtonWithUrl("Report an issue by email", null,
ReportAnIssueStyle.reportIssueColor, context,
emails: emails,
subject: 'Space Mapper: Report Issue',
body:
'Dear Space Mapper support, \n\n I want to report the following issue:'),
customButtonWithUrl("Request a feature by email", null,
ReportAnIssueStyle.requestFeatureColor, context,
emails: emails,
subject: 'Space Mapper: Feature Request',
body:
'Dear Space Mapper support, \n\n I want to request the following feature:'),
],
));
}

_launchUrl(String url) async {
//The url must be valid

if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}

Future<bool> launchMailto(
List<String> emails, String? subject, String? body) async {
//All emails must be valid
for (int i = 0; i < emails.length; i++) {
if (RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+")
.hasMatch(emails[i]) ==
false) {
return false;
}
}

final mailtoLink = Mailto(
to: emails,
subject: subject,
body: body,
);
// Convert the Mailto instance into a string.
// Use either Dart's string interpolation
// or the toString() method.
await launch('$mailtoLink');
return true;
}

Widget displayService(String name, Icon icon) {
return Container(
margin: EdgeInsets.fromLTRB(0.0, ReportAnIssueStyle.marginIconTopAndBottom,
0.0, ReportAnIssueStyle.marginIconTopAndBottom),
child: Row(
children: [
icon,
Container(
margin: EdgeInsets.only(
right: ReportAnIssueStyle.marginBetweenIconAndTitle),
),
Text(
name,
style: TextStyle(fontSize: ReportAnIssueStyle.titleSize),
),
],
),
);
}

Widget customButtonWithUrl(String text, String? openUrl,
MaterialStateProperty<Color?> backgroundColor, BuildContext context,
{List<String>? emails, String? subject, String? body}) {
return Container(
width: MediaQuery.of(context).size.width *
ReportAnIssueStyle.buttonWidthPercentage,
child: TextButton(
style: ButtonStyle(
backgroundColor: backgroundColor,
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
ReportAnIssueStyle.buttonBorderRadius),
side: BorderSide(color: Colors.black)))),
onPressed: () {
//If emails list is null, this buttons opens a link on click, otherwise it sends an email with introduced data
emails == null
? _launchUrl(openUrl!)
: launchMailto(emails, subject!, body!);
},
child: Text(
text,
style: TextStyle(color: Colors.black),
),
));
}
27 changes: 19 additions & 8 deletions Space_Mapper/lib/ui/side_drawer.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:asm/ui/list_view.dart';
import 'package:asm/ui/report_an_issue.dart';
import 'package:asm/ui/web_view.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
Expand All @@ -7,6 +8,13 @@ import 'package:flutter_background_geolocation/flutter_background_geolocation.da
as bg;

class SpaceMapperSideDrawer extends StatelessWidget {
_shareLocations() async {
var now = new DateTime.now();
List allLocations = await bg.BackgroundGeolocation.locations;
Share.share(allLocations.toString(),
subject: "space_mapper_trajectory_" + now.toIso8601String() + ".json");
}

_launchProjectURL() async {
const url = 'http://activityspaceproject.com/';
if (await canLaunch(url)) {
Expand All @@ -16,13 +24,6 @@ class SpaceMapperSideDrawer extends StatelessWidget {
}
}

_shareLocations() async {
var now = new DateTime.now();
List allLocations = await bg.BackgroundGeolocation.locations;
Share.share(allLocations.toString(),
subject: "space_mapper_trajectory_" + now.toIso8601String() + ".json");
}

@override
Widget build(BuildContext context) {
return new Drawer(
Expand Down Expand Up @@ -51,7 +52,7 @@ class SpaceMapperSideDrawer extends StatelessWidget {
Card(
child: ListTile(
leading: const Icon(Icons.list),
title: Text('List Locations'),
title: Text('Locations History'),
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => STOListView()));
Expand All @@ -75,6 +76,16 @@ class SpaceMapperSideDrawer extends StatelessWidget {
_launchProjectURL();
},
),
),
Card(
child: ListTile(
leading: const Icon(Icons.report_problem_outlined),
title: Text('Report an Issue'),
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => ReportAnIssue()));
},
),
)
],
),
Expand Down
Loading