From f8724b4d5997f37fddd17ae5db006a7d6c220cb1 Mon Sep 17 00:00:00 2001 From: Pablo Galve Date: Sat, 23 Oct 2021 18:40:47 +0200 Subject: [PATCH 01/11] Report an Issue tab and screen: initial draft --- Space_Mapper/lib/ui/list_view.dart | 2 +- Space_Mapper/lib/ui/report_an_issue.dart | 81 +++++++++++++++++++ Space_Mapper/lib/ui/side_drawer.dart | 27 +++++-- .../lib/ui_style/report_an_issue_style.dart | 3 + Space_Mapper/pubspec.lock | 7 ++ Space_Mapper/pubspec.yaml | 3 +- 6 files changed, 113 insertions(+), 10 deletions(-) create mode 100644 Space_Mapper/lib/ui/report_an_issue.dart create mode 100644 Space_Mapper/lib/ui_style/report_an_issue_style.dart diff --git a/Space_Mapper/lib/ui/list_view.dart b/Space_Mapper/lib/ui/list_view.dart index 2f065db6..54e53177 100644 --- a/Space_Mapper/lib/ui/list_view.dart +++ b/Space_Mapper/lib/ui/list_view.dart @@ -6,7 +6,7 @@ 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( future: bg.BackgroundGeolocation.locations, builder: (context, snapshot) { diff --git a/Space_Mapper/lib/ui/report_an_issue.dart b/Space_Mapper/lib/ui/report_an_issue.dart new file mode 100644 index 00000000..9ac65e4b --- /dev/null +++ b/Space_Mapper/lib/ui/report_an_issue.dart @@ -0,0 +1,81 @@ +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'; + +class ReportAnIssue extends StatelessWidget { + _launchGithubIssues() async { + const url = 'https://github.com/ActivitySpaceProject/space_mapper/issues'; + if (await canLaunch(url)) { + await launch(url); + } else { + throw 'Could not launch $url'; + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text("Report an Issue")), + body: reportIssueBody(), + ); + } +} + +Widget reportIssueBody() { + return Padding( + padding: EdgeInsets.all(20.0), + child: Column( + children: [ + Container( + padding: EdgeInsets.only(bottom: 15.0), + child: Text( + "Help us improve by either reporting an issue or requesting a useful feature.", + style: TextStyle(fontSize: 17.0), + ), + ), + DisplayService( + "Github", + Icon( + AntDesign.github, + size: ReportAnIssueStyle.iconSize, + )), + Container( + //padding: EdgeInsets.all(10.0), + child: Text("Report issues on github to get the fastest solution."), + ), + Text("Here goes a button"), + DisplayService( + "Email", + Icon( + Icons.email_outlined, + size: ReportAnIssueStyle.iconSize, + )), + Container( + //padding: EdgeInsets.all(10.0), + child: Text("As an alternative, you can send us an email."), + ), + Text("Here goes a button"), + Text("Here goes a button") + ], + )); +} + +//Display +Widget DisplayService(String name, Icon icon) { + return Container( + margin: EdgeInsets.only(bottom: 20.0), + child: Row( + children: [ + icon, + Container( + margin: EdgeInsets.only(right: 25.0), + ), + Text( + "Github", + style: TextStyle(fontSize: 25.0), + ), + ], + ), + ); +} diff --git a/Space_Mapper/lib/ui/side_drawer.dart b/Space_Mapper/lib/ui/side_drawer.dart index 95481ea5..4b0b6377 100644 --- a/Space_Mapper/lib/ui/side_drawer.dart +++ b/Space_Mapper/lib/ui/side_drawer.dart @@ -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'; @@ -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)) { @@ -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( @@ -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())); @@ -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())); + }, + ), ) ], ), diff --git a/Space_Mapper/lib/ui_style/report_an_issue_style.dart b/Space_Mapper/lib/ui_style/report_an_issue_style.dart new file mode 100644 index 00000000..99ee7fb4 --- /dev/null +++ b/Space_Mapper/lib/ui_style/report_an_issue_style.dart @@ -0,0 +1,3 @@ +class ReportAnIssueStyle { + static const iconSize = 100.0; +} diff --git a/Space_Mapper/pubspec.lock b/Space_Mapper/pubspec.lock index 960992e5..6eccfccb 100644 --- a/Space_Mapper/pubspec.lock +++ b/Space_Mapper/pubspec.lock @@ -205,6 +205,13 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_vector_icons: + dependency: "direct main" + description: + name: flutter_vector_icons + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" flutter_web_plugins: dependency: transitive description: flutter diff --git a/Space_Mapper/pubspec.yaml b/Space_Mapper/pubspec.yaml index f15305c3..3cc1c2eb 100644 --- a/Space_Mapper/pubspec.yaml +++ b/Space_Mapper/pubspec.yaml @@ -37,7 +37,8 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.3 - + flutter_vector_icons: ^1.0.0 + dev_dependencies: flutter_test: sdk: flutter From 290b7d302dd964fac83113223dc94daad4ffe8dd Mon Sep 17 00:00:00 2001 From: Pablo Galve Date: Sat, 23 Oct 2021 19:50:31 +0200 Subject: [PATCH 02/11] Report an Issue Screen: User Interface (UI) finished --- Space_Mapper/lib/ui/report_an_issue.dart | 95 ++++++++++++++++-------- 1 file changed, 66 insertions(+), 29 deletions(-) diff --git a/Space_Mapper/lib/ui/report_an_issue.dart b/Space_Mapper/lib/ui/report_an_issue.dart index 9ac65e4b..9b4feb1c 100644 --- a/Space_Mapper/lib/ui/report_an_issue.dart +++ b/Space_Mapper/lib/ui/report_an_issue.dart @@ -4,35 +4,23 @@ import 'package:url_launcher/url_launcher.dart'; import 'package:flutter_vector_icons/flutter_vector_icons.dart'; class ReportAnIssue extends StatelessWidget { - _launchGithubIssues() async { - const url = 'https://github.com/ActivitySpaceProject/space_mapper/issues'; - if (await canLaunch(url)) { - await launch(url); - } else { - throw 'Could not launch $url'; - } - } - @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: Text("Report an Issue")), - body: reportIssueBody(), + appBar: AppBar(title: Text("Report an Issue/Feature Request")), + body: reportIssueBody(context), ); } } -Widget reportIssueBody() { +Widget reportIssueBody(BuildContext context) { return Padding( padding: EdgeInsets.all(20.0), child: Column( children: [ - Container( - padding: EdgeInsets.only(bottom: 15.0), - child: Text( - "Help us improve by either reporting an issue or requesting a useful feature.", - style: TextStyle(fontSize: 17.0), - ), + Text( + "Help us improve by either reporting an issue or requesting a useful feature.", + style: TextStyle(fontSize: 17.0), ), DisplayService( "Github", @@ -41,10 +29,21 @@ Widget reportIssueBody() { size: ReportAnIssueStyle.iconSize, )), Container( - //padding: EdgeInsets.all(10.0), - child: Text("Report issues on github to get the fastest solution."), + margin: EdgeInsets.only(bottom: 10.0), + child: Text( + "Report issues on github to get the fastest solution.", + style: TextStyle(fontSize: 17.0), + ), + ), + CustomButtonWithUrl( + "Go to Github Issues", + "https://github.com/ActivitySpaceProject/space_mapper/issues", + MaterialStateProperty.all(Colors.lightBlue[100]), + context), + Container( + //Container only to add more margin + margin: EdgeInsets.only(bottom: 10.0), ), - Text("Here goes a button"), DisplayService( "Email", Icon( @@ -52,19 +51,36 @@ Widget reportIssueBody() { size: ReportAnIssueStyle.iconSize, )), Container( - //padding: EdgeInsets.all(10.0), - child: Text("As an alternative, you can send us an email."), - ), - Text("Here goes a button"), - Text("Here goes a button") + margin: EdgeInsets.only(bottom: 10.0), + child: Text( + "As an alternative, you can send us an email.", + style: TextStyle(fontSize: 17.0), + )), + CustomButtonWithUrl( + "Report an issue by email", + "https://github.com/ActivitySpaceProject/space_mapper/issues", + MaterialStateProperty.all(Colors.red[100]), + context), + CustomButtonWithUrl( + "Request a feature by email", + "https://github.com/ActivitySpaceProject/space_mapper/issues", + MaterialStateProperty.all(Colors.lightBlue[100]), + context), ], )); } -//Display +_launchUrl(String url) async { + if (await canLaunch(url)) { + await launch(url); + } else { + throw 'Could not launch $url'; + } +} + Widget DisplayService(String name, Icon icon) { return Container( - margin: EdgeInsets.only(bottom: 20.0), + margin: EdgeInsets.fromLTRB(0.0, 20.0, 0.0, 20.0), child: Row( children: [ icon, @@ -72,10 +88,31 @@ Widget DisplayService(String name, Icon icon) { margin: EdgeInsets.only(right: 25.0), ), Text( - "Github", + name, style: TextStyle(fontSize: 25.0), ), ], ), ); } + +Widget CustomButtonWithUrl(String text, String openUrl, + MaterialStateProperty backgroundColor, BuildContext context) { + return Container( + width: MediaQuery.of(context).size.width * 0.6, + child: TextButton( + style: ButtonStyle( + backgroundColor: backgroundColor, + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18.0), + side: BorderSide(color: Colors.black)))), + onPressed: () { + _launchUrl(openUrl); + }, + child: Text( + text, + style: TextStyle(color: Colors.black), + ), + )); +} From 8c2123eaacb7fc79be7ef809c4beacb21f8a2eb0 Mon Sep 17 00:00:00 2001 From: Pablo Galve Date: Sat, 23 Oct 2021 20:22:17 +0200 Subject: [PATCH 03/11] fix CI issues to pass the tests --- Space_Mapper/lib/ui/report_an_issue.dart | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Space_Mapper/lib/ui/report_an_issue.dart b/Space_Mapper/lib/ui/report_an_issue.dart index 9b4feb1c..79989aa0 100644 --- a/Space_Mapper/lib/ui/report_an_issue.dart +++ b/Space_Mapper/lib/ui/report_an_issue.dart @@ -22,7 +22,7 @@ Widget reportIssueBody(BuildContext context) { "Help us improve by either reporting an issue or requesting a useful feature.", style: TextStyle(fontSize: 17.0), ), - DisplayService( + displayService( "Github", Icon( AntDesign.github, @@ -35,7 +35,7 @@ Widget reportIssueBody(BuildContext context) { style: TextStyle(fontSize: 17.0), ), ), - CustomButtonWithUrl( + customButtonWithUrl( "Go to Github Issues", "https://github.com/ActivitySpaceProject/space_mapper/issues", MaterialStateProperty.all(Colors.lightBlue[100]), @@ -44,7 +44,7 @@ Widget reportIssueBody(BuildContext context) { //Container only to add more margin margin: EdgeInsets.only(bottom: 10.0), ), - DisplayService( + displayService( "Email", Icon( Icons.email_outlined, @@ -56,12 +56,12 @@ Widget reportIssueBody(BuildContext context) { "As an alternative, you can send us an email.", style: TextStyle(fontSize: 17.0), )), - CustomButtonWithUrl( + customButtonWithUrl( "Report an issue by email", "https://github.com/ActivitySpaceProject/space_mapper/issues", MaterialStateProperty.all(Colors.red[100]), context), - CustomButtonWithUrl( + customButtonWithUrl( "Request a feature by email", "https://github.com/ActivitySpaceProject/space_mapper/issues", MaterialStateProperty.all(Colors.lightBlue[100]), @@ -78,7 +78,7 @@ _launchUrl(String url) async { } } -Widget DisplayService(String name, Icon icon) { +Widget displayService(String name, Icon icon) { return Container( margin: EdgeInsets.fromLTRB(0.0, 20.0, 0.0, 20.0), child: Row( @@ -96,7 +96,7 @@ Widget DisplayService(String name, Icon icon) { ); } -Widget CustomButtonWithUrl(String text, String openUrl, +Widget customButtonWithUrl(String text, String openUrl, MaterialStateProperty backgroundColor, BuildContext context) { return Container( width: MediaQuery.of(context).size.width * 0.6, From df879dc7995ab11ac70fdf7569b23e4446e13abb Mon Sep 17 00:00:00 2001 From: Pablo Galve Date: Sun, 24 Oct 2021 12:42:54 +0200 Subject: [PATCH 04/11] [Report an Issue Screen] Add mailto + templates for reports by email --- Space_Mapper/lib/ui/report_an_issue.dart | 35 ++++++++++++++++++++---- Space_Mapper/pubspec.lock | 7 +++++ Space_Mapper/pubspec.yaml | 5 ++-- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/Space_Mapper/lib/ui/report_an_issue.dart b/Space_Mapper/lib/ui/report_an_issue.dart index 79989aa0..fa4ecee4 100644 --- a/Space_Mapper/lib/ui/report_an_issue.dart +++ b/Space_Mapper/lib/ui/report_an_issue.dart @@ -2,12 +2,13 @@ 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/Feature Request")), + appBar: AppBar(title: Text("Report an Issue")), body: reportIssueBody(context), ); } @@ -60,12 +61,20 @@ Widget reportIssueBody(BuildContext context) { "Report an issue by email", "https://github.com/ActivitySpaceProject/space_mapper/issues", MaterialStateProperty.all(Colors.red[100]), - context), + context, + emails: ['john.palmer@upf.edu', 'pablogalve100@gmail.com'], + 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", "https://github.com/ActivitySpaceProject/space_mapper/issues", MaterialStateProperty.all(Colors.lightBlue[100]), - context), + context, + emails: ['john.palmer@upf.edu', 'pablogalve100@gmail.com'], + subject: 'Space Mapper: Feature Request', + body: + 'Dear Space Mapper support, \n\n I want to request the following feature:'), ], )); } @@ -78,6 +87,18 @@ _launchUrl(String url) async { } } +_launchMailto(List emails, String? subject, String? body) async { + 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'); +} + Widget displayService(String name, Icon icon) { return Container( margin: EdgeInsets.fromLTRB(0.0, 20.0, 0.0, 20.0), @@ -97,7 +118,8 @@ Widget displayService(String name, Icon icon) { } Widget customButtonWithUrl(String text, String openUrl, - MaterialStateProperty backgroundColor, BuildContext context) { + MaterialStateProperty backgroundColor, BuildContext context, + {List? emails, String? subject, String? body}) { return Container( width: MediaQuery.of(context).size.width * 0.6, child: TextButton( @@ -108,7 +130,10 @@ Widget customButtonWithUrl(String text, String openUrl, borderRadius: BorderRadius.circular(18.0), side: BorderSide(color: Colors.black)))), onPressed: () { - _launchUrl(openUrl); + //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, diff --git a/Space_Mapper/pubspec.lock b/Space_Mapper/pubspec.lock index 6eccfccb..77894f5f 100644 --- a/Space_Mapper/pubspec.lock +++ b/Space_Mapper/pubspec.lock @@ -294,6 +294,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.2" + mailto: + dependency: "direct main" + description: + name: mailto + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" matcher: dependency: transitive description: diff --git a/Space_Mapper/pubspec.yaml b/Space_Mapper/pubspec.yaml index 3cc1c2eb..01012b57 100644 --- a/Space_Mapper/pubspec.yaml +++ b/Space_Mapper/pubspec.yaml @@ -21,8 +21,7 @@ dependencies: sdk: flutter flutter_background_geolocation: ^4.3.0 shared_preferences: ^2.0.8 - background_fetch: ^1.0.1 - url_launcher: ^6.0.12 + background_fetch: ^1.0.1 http: ^0.13.4 flutter_map: ^0.14.0 # flutter_webview_plugin: ^0.4.0 @@ -33,6 +32,8 @@ dependencies: uuid: 3.0.5 flutter_form_builder: ^6.1.0+1 latlong2: ^0.8.1 + url_launcher: ^6.0.12 + mailto: ^2.0.0 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. From 7de01dc7a9d8e2ee4ad7098be771e8cf4195bb44 Mon Sep 17 00:00:00 2001 From: Pablo Galve Date: Sun, 24 Oct 2021 13:36:46 +0200 Subject: [PATCH 05/11] [Report an Issue Screen] Styles configurations moved to different file --- Space_Mapper/lib/ui/report_an_issue.dart | 57 ++++++++++--------- .../lib/ui_style/report_an_issue_style.dart | 21 +++++++ 2 files changed, 51 insertions(+), 27 deletions(-) diff --git a/Space_Mapper/lib/ui/report_an_issue.dart b/Space_Mapper/lib/ui/report_an_issue.dart index fa4ecee4..66c65e79 100644 --- a/Space_Mapper/lib/ui/report_an_issue.dart +++ b/Space_Mapper/lib/ui/report_an_issue.dart @@ -15,13 +15,15 @@ class ReportAnIssue extends StatelessWidget { } Widget reportIssueBody(BuildContext context) { + List emails = ['john.palmer@upf.edu', 'pablogalve100@gmail.com']; + return Padding( - padding: EdgeInsets.all(20.0), + 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: 17.0), + style: TextStyle(fontSize: ReportAnIssueStyle.normalTextSize), ), displayService( "Github", @@ -30,20 +32,22 @@ Widget reportIssueBody(BuildContext context) { size: ReportAnIssueStyle.iconSize, )), Container( - margin: EdgeInsets.only(bottom: 10.0), + margin: EdgeInsets.only( + bottom: ReportAnIssueStyle.marginBetweenTextAndButtons), child: Text( "Report issues on github to get the fastest solution.", - style: TextStyle(fontSize: 17.0), + style: TextStyle(fontSize: ReportAnIssueStyle.normalTextSize), ), ), customButtonWithUrl( "Go to Github Issues", "https://github.com/ActivitySpaceProject/space_mapper/issues", - MaterialStateProperty.all(Colors.lightBlue[100]), + ReportAnIssueStyle.requestFeatureColor, context), Container( //Container only to add more margin - margin: EdgeInsets.only(bottom: 10.0), + margin: EdgeInsets.only( + bottom: ReportAnIssueStyle.marginBetweenTextAndButtons), ), displayService( "Email", @@ -52,26 +56,21 @@ Widget reportIssueBody(BuildContext context) { size: ReportAnIssueStyle.iconSize, )), Container( - margin: EdgeInsets.only(bottom: 10.0), + margin: EdgeInsets.only( + bottom: ReportAnIssueStyle.marginBetweenTextAndButtons), child: Text( "As an alternative, you can send us an email.", - style: TextStyle(fontSize: 17.0), + style: TextStyle(fontSize: ReportAnIssueStyle.normalTextSize), )), - customButtonWithUrl( - "Report an issue by email", - "https://github.com/ActivitySpaceProject/space_mapper/issues", - MaterialStateProperty.all(Colors.red[100]), - context, - emails: ['john.palmer@upf.edu', 'pablogalve100@gmail.com'], + 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", - "https://github.com/ActivitySpaceProject/space_mapper/issues", - MaterialStateProperty.all(Colors.lightBlue[100]), - context, - emails: ['john.palmer@upf.edu', 'pablogalve100@gmail.com'], + 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:'), @@ -101,38 +100,42 @@ _launchMailto(List emails, String? subject, String? body) async { Widget displayService(String name, Icon icon) { return Container( - margin: EdgeInsets.fromLTRB(0.0, 20.0, 0.0, 20.0), + margin: EdgeInsets.fromLTRB(0.0, ReportAnIssueStyle.marginIconTopAndBottom, + 0.0, ReportAnIssueStyle.marginIconTopAndBottom), child: Row( children: [ icon, Container( - margin: EdgeInsets.only(right: 25.0), + margin: EdgeInsets.only( + right: ReportAnIssueStyle.marginBetweenIconAndTitle), ), Text( name, - style: TextStyle(fontSize: 25.0), + style: TextStyle(fontSize: ReportAnIssueStyle.titleSize), ), ], ), ); } -Widget customButtonWithUrl(String text, String openUrl, +Widget customButtonWithUrl(String text, String? openUrl, MaterialStateProperty backgroundColor, BuildContext context, {List? emails, String? subject, String? body}) { return Container( - width: MediaQuery.of(context).size.width * 0.6, + width: MediaQuery.of(context).size.width * + ReportAnIssueStyle.buttonWidthPercentage, child: TextButton( style: ButtonStyle( backgroundColor: backgroundColor, shape: MaterialStateProperty.all( RoundedRectangleBorder( - borderRadius: BorderRadius.circular(18.0), + 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) + ? _launchUrl(openUrl!) : _launchMailto(emails, subject!, body!); }, child: Text( diff --git a/Space_Mapper/lib/ui_style/report_an_issue_style.dart b/Space_Mapper/lib/ui_style/report_an_issue_style.dart index 99ee7fb4..39113690 100644 --- a/Space_Mapper/lib/ui_style/report_an_issue_style.dart +++ b/Space_Mapper/lib/ui_style/report_an_issue_style.dart @@ -1,3 +1,24 @@ +import 'package:flutter/material.dart'; + class ReportAnIssueStyle { + //General + static const screenPadding = 20.0; static const iconSize = 100.0; + + //Font sizes + static const titleSize = 25.0; + static const normalTextSize = 17.0; + + //Margins + static const marginBetweenTextAndButtons = 10.0; + static const marginIconTopAndBottom = 20.0; + static const marginBetweenIconAndTitle = 25.0; + + //Buttons + static const buttonBorderRadius = 18.0; + static MaterialStateProperty reportIssueColor = + MaterialStateProperty.all(Colors.red[100]); + static MaterialStateProperty requestFeatureColor = + MaterialStateProperty.all(Colors.lightBlue[100]); + static const buttonWidthPercentage = 0.6; //Double from 0 to 1 } From d0ca3f660be595817c803bfd225ed333e664a3b2 Mon Sep 17 00:00:00 2001 From: Pablo Galve Date: Sun, 24 Oct 2021 19:03:21 +0200 Subject: [PATCH 06/11] [Report an Issue Screen] Write a unit test --- Space_Mapper/lib/ui/report_an_issue.dart | 17 ++++++++++++-- Space_Mapper/test/integration/app_test.dart | 20 ---------------- .../test/unit/report_an_issue_test.dart | 23 +++++++++++++++++++ .../test/unit/shared_preferences_test.dart | 9 -------- .../test/unit/spacemapper_auth_test.dart | 9 -------- 5 files changed, 38 insertions(+), 40 deletions(-) delete mode 100644 Space_Mapper/test/integration/app_test.dart create mode 100644 Space_Mapper/test/unit/report_an_issue_test.dart delete mode 100644 Space_Mapper/test/unit/shared_preferences_test.dart delete mode 100644 Space_Mapper/test/unit/spacemapper_auth_test.dart diff --git a/Space_Mapper/lib/ui/report_an_issue.dart b/Space_Mapper/lib/ui/report_an_issue.dart index 66c65e79..7df3e856 100644 --- a/Space_Mapper/lib/ui/report_an_issue.dart +++ b/Space_Mapper/lib/ui/report_an_issue.dart @@ -79,6 +79,8 @@ Widget reportIssueBody(BuildContext context) { } _launchUrl(String url) async { + //The url must be valid + if (await canLaunch(url)) { await launch(url); } else { @@ -86,7 +88,17 @@ _launchUrl(String url) async { } } -_launchMailto(List emails, String? subject, String? body) async { +Future launchMailto( + List 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, @@ -96,6 +108,7 @@ _launchMailto(List emails, String? subject, String? body) async { // Use either Dart's string interpolation // or the toString() method. await launch('$mailtoLink'); + return true; } Widget displayService(String name, Icon icon) { @@ -136,7 +149,7 @@ Widget customButtonWithUrl(String text, String? openUrl, //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!); + : launchMailto(emails, subject!, body!); }, child: Text( text, diff --git a/Space_Mapper/test/integration/app_test.dart b/Space_Mapper/test/integration/app_test.dart deleted file mode 100644 index 573fd058..00000000 --- a/Space_Mapper/test/integration/app_test.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:asm/main.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -Widget makeTestableWidget() => MaterialApp(home: Image.network('')); - -void main() { - testWidgets('MyApp() has an appName that equals Space Mapper', - (WidgetTester tester) async { - // Test code goes here. - await tester.pumpWidget(MyApp()); - - // Create the Finders. - final titleFinder = find.text("Space Mapper"); - - // Use the `findsOneWidget` matcher provided by flutter_test to - // verify that the Text widgets appear exactly once in the widget tree. - expect(titleFinder, findsOneWidget); - }); -} diff --git a/Space_Mapper/test/unit/report_an_issue_test.dart b/Space_Mapper/test/unit/report_an_issue_test.dart new file mode 100644 index 00000000..93d800b3 --- /dev/null +++ b/Space_Mapper/test/unit/report_an_issue_test.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import '../../lib/ui/report_an_issue.dart'; +import 'package:test/test.dart'; +import 'package:mockito/mockito.dart'; + +class MockBuildContext extends Mock implements BuildContext {} + +void main() { + group('Report an Issue Screen - Unit Tests', () { + group('launchMailto', () { + test('launchMailto: Return false if there is at least on invalid email', + () { + List emails = [ + 'test@test.com', + 'test2@gmail.com', + 'thisfails.com', //This email will make the function return false + ]; + Future ret = launchMailto(emails, '', ''); + expect(ret, completion(equals(false))); + }); + }); + }); +} diff --git a/Space_Mapper/test/unit/shared_preferences_test.dart b/Space_Mapper/test/unit/shared_preferences_test.dart deleted file mode 100644 index 49d4f81f..00000000 --- a/Space_Mapper/test/unit/shared_preferences_test.dart +++ /dev/null @@ -1,9 +0,0 @@ -//import 'package:shared_preferences/shared_preferences.dart'; -//import 'package:test/test.dart'; - -void main() { - /*test('SharedPreferences.getInstance()', () { - final _prefs = SharedPreferences.getInstance(); - expect(_prefs, isNotNull); - });*/ -} diff --git a/Space_Mapper/test/unit/spacemapper_auth_test.dart b/Space_Mapper/test/unit/spacemapper_auth_test.dart deleted file mode 100644 index 45804aa8..00000000 --- a/Space_Mapper/test/unit/spacemapper_auth_test.dart +++ /dev/null @@ -1,9 +0,0 @@ -//import 'package:test/test.dart'; -//import '../../lib/util/spacemapper_auth.dart'; - -void main() { - /*test('TransistorAuth.register()', () { - final user = TransistorAuth.register(); - expect(user, isNotNull); - });*/ -} From 109e4f972800bd3f2a1470abed9996c1bc2dd120 Mon Sep 17 00:00:00 2001 From: Pablo Galve Date: Mon, 25 Oct 2021 00:14:48 +0200 Subject: [PATCH 07/11] [Web View] Remove unnecessary code --- Space_Mapper/lib/ui/web_view.dart | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/Space_Mapper/lib/ui/web_view.dart b/Space_Mapper/lib/ui/web_view.dart index c70f9a46..0667d40f 100644 --- a/Space_Mapper/lib/ui/web_view.dart +++ b/Space_Mapper/lib/ui/web_view.dart @@ -3,21 +3,6 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; -const String kNavigationExamplePage = ''' - -Navigation Delegate Example - -

-The navigation delegate is set to block navigation to the youtube website. -

- - - -'''; - class MyWebView extends StatefulWidget { @override _MyWebViewState createState() => _MyWebViewState(); @@ -47,7 +32,9 @@ class _MyWebViewState extends State { // to allow calling Scaffold.of(context) so we can show a snackbar. body: Builder(builder: (BuildContext context) { return WebView( - initialUrl: 'https://ee.kobotoolbox.org/single/asCwpCjZ', + //initialUrl: 'https://ee.kobotoolbox.org/single/asCwpCjZ', //Original form for beta-testing + initialUrl: + 'https://ee.kobotoolbox.org/x/8528dfMs', //Form to upload a json file javascriptMode: JavascriptMode.unrestricted, onWebViewCreated: (WebViewController webViewController) { _controller.complete(webViewController); @@ -58,14 +45,6 @@ class _MyWebViewState extends State { javascriptChannels: { _toasterJavascriptChannel(context), }, - navigationDelegate: (NavigationRequest request) { - if (request.url.startsWith('https://www.youtube.com/')) { - print('blocking navigation to $request}'); - return NavigationDecision.prevent; - } - print('allowing navigation to $request'); - return NavigationDecision.navigate; - }, onPageStarted: (String url) { print('Page started loading: $url'); }, From b1ecf111256f231a2b90b51f77f2cd230a517505 Mon Sep 17 00:00:00 2001 From: Pablo Galve Date: Tue, 26 Oct 2021 00:12:52 +0200 Subject: [PATCH 08/11] Make Location History more user-friendly fix #14 --- Space_Mapper/lib/ui/list_view.dart | 81 +++++++++++++++++++++++++++--- Space_Mapper/pubspec.lock | 14 ++++++ Space_Mapper/pubspec.yaml | 3 +- 3 files changed, 91 insertions(+), 7 deletions(-) diff --git a/Space_Mapper/lib/ui/list_view.dart b/Space_Mapper/lib/ui/list_view.dart index 54e53177..9a556883 100644 --- a/Space_Mapper/lib/ui/list_view.dart +++ b/Space_Mapper/lib/ui/list_view.dart @@ -1,6 +1,67 @@ import 'package:flutter/material.dart'; import 'package:flutter_background_geolocation/flutter_background_geolocation.dart' as bg; +import 'package:geocoding/geocoding.dart'; + +Future getLocationData( + String dataType, double lat, double long) async { + try { + List 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>? 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']); + 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, + this.timestamp, this.activity, this.speed, this.altitude); + late String locality; + late String subAdministrativeArea; + late String ISOCountry; + late String timestamp; + late String activity; + late num speed; + late num altitude; +} class STOListView extends StatelessWidget { @override @@ -8,7 +69,7 @@ class STOListView extends StatelessWidget { return Scaffold( appBar: AppBar(title: Text("Location History")), body: FutureBuilder( - future: bg.BackgroundGeolocation.locations, + future: buildLocationsList(), builder: (context, snapshot) { if (snapshot.hasData) { List? data = snapshot.data; @@ -25,12 +86,20 @@ 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 + + " Activity: " + + thisLocation.activity + + " Speed: " + + thisLocation.speed.toString() + + " Altitude: " + + thisLocation.altitude.toString(), Icons.gps_fixed); }); } diff --git a/Space_Mapper/pubspec.lock b/Space_Mapper/pubspec.lock index 77894f5f..cd907b31 100644 --- a/Space_Mapper/pubspec.lock +++ b/Space_Mapper/pubspec.lock @@ -224,6 +224,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.2" + geocoding: + dependency: "direct main" + description: + name: geocoding + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + geocoding_platform_interface: + dependency: transitive + description: + name: geocoding_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" glob: dependency: transitive description: diff --git a/Space_Mapper/pubspec.yaml b/Space_Mapper/pubspec.yaml index 01012b57..d962915b 100644 --- a/Space_Mapper/pubspec.yaml +++ b/Space_Mapper/pubspec.yaml @@ -34,7 +34,8 @@ dependencies: latlong2: ^0.8.1 url_launcher: ^6.0.12 mailto: ^2.0.0 - + geocoding: ^2.0.1 + # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.3 From 30319406d890d6a81530c5e392dccb1de0ca43b1 Mon Sep 17 00:00:00 2001 From: Pablo Galve Date: Tue, 26 Oct 2021 00:19:12 +0200 Subject: [PATCH 09/11] pass all "analyze" tests --- Space_Mapper/lib/ui/list_view.dart | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Space_Mapper/lib/ui/list_view.dart b/Space_Mapper/lib/ui/list_view.dart index 9a556883..e71b745a 100644 --- a/Space_Mapper/lib/ui/list_view.dart +++ b/Space_Mapper/lib/ui/list_view.dart @@ -36,6 +36,7 @@ Future>? buildLocationsList() async { "subAdministrativeArea", locations[i]['coords']['latitude'], locations[i]['coords']['longitude']); + // ignore: non_constant_identifier_names String? ISOCountry = await getLocationData( "ISOCountry", locations[i]['coords']['latitude'], @@ -44,18 +45,19 @@ Future>? buildLocationsList() async { 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!, + 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, +class DisplayLocation { + DisplayLocation(this.locality, this.subAdministrativeArea, this.ISOCountry, this.timestamp, this.activity, this.speed, this.altitude); late String locality; late String subAdministrativeArea; + // ignore: non_constant_identifier_names late String ISOCountry; late String timestamp; late String activity; @@ -86,7 +88,7 @@ class STOListView extends StatelessWidget { return ListView.builder( itemCount: data.length, itemBuilder: (context, index) { - displayLocation thisLocation = data[index]; + DisplayLocation thisLocation = data[index]; return _tile( thisLocation.locality + ", " + From f76ddfb232bd470180243aee703324c1af73746f Mon Sep 17 00:00:00 2001 From: Pablo Galve Date: Tue, 26 Oct 2021 00:38:05 +0200 Subject: [PATCH 10/11] [Location History] Formatted timestamp + improved readability --- Space_Mapper/lib/ui/list_view.dart | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/Space_Mapper/lib/ui/list_view.dart b/Space_Mapper/lib/ui/list_view.dart index e71b745a..78884f37 100644 --- a/Space_Mapper/lib/ui/list_view.dart +++ b/Space_Mapper/lib/ui/list_view.dart @@ -54,7 +54,9 @@ Future>? buildLocationsList() async { class DisplayLocation { DisplayLocation(this.locality, this.subAdministrativeArea, this.ISOCountry, - this.timestamp, this.activity, this.speed, this.altitude); + timestamp, this.activity, this.speed, this.altitude) { + this.timestamp = formatTimestamp(timestamp); + } late String locality; late String subAdministrativeArea; // ignore: non_constant_identifier_names @@ -63,6 +65,20 @@ class DisplayLocation { 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 { @@ -96,11 +112,11 @@ class STOListView extends StatelessWidget { ", " + thisLocation.ISOCountry, thisLocation.timestamp + - " Activity: " + + " \nActivity: " + thisLocation.activity + - " Speed: " + + " \nSpeed: " + thisLocation.speed.toString() + - " Altitude: " + + " \nAltitude: " + thisLocation.altitude.toString(), Icons.gps_fixed); }); From 7d711321bc1b1e5304d82c532aeb5173c1d39a3f Mon Sep 17 00:00:00 2001 From: Pablo Galve Date: Tue, 26 Oct 2021 00:47:45 +0200 Subject: [PATCH 11/11] [Location History] Timestamp in bold to highlight it --- Space_Mapper/lib/ui/list_view.dart | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/Space_Mapper/lib/ui/list_view.dart b/Space_Mapper/lib/ui/list_view.dart index 78884f37..2e4b714f 100644 --- a/Space_Mapper/lib/ui/list_view.dart +++ b/Space_Mapper/lib/ui/list_view.dart @@ -111,8 +111,8 @@ class STOListView extends StatelessWidget { thisLocation.subAdministrativeArea + ", " + thisLocation.ISOCountry, - thisLocation.timestamp + - " \nActivity: " + + thisLocation.timestamp, + " \nActivity: " + thisLocation.activity + " \nSpeed: " + thisLocation.speed.toString() + @@ -122,13 +122,29 @@ class STOListView extends StatelessWidget { }); } - 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: [ + new TextSpan( + text: subtitle, + style: new TextStyle(fontWeight: FontWeight.bold)), + new TextSpan(text: text), + ], + ), + ), leading: Icon( icon, color: Colors.blue[500],