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
5 changes: 4 additions & 1 deletion Space_Mapper/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,8 @@
"select_age_group": "Select age group",
"submit": "Submit",
"reset": "Reset",
"gender": "gender"
"gender": "gender",
"about_the_project": "About the project",
"consent_form" : "Consent Form",
"do_you_agree_to_share_your_anonymous_location_with" : "Do you agree to share your anonymous locations to "
}
5 changes: 4 additions & 1 deletion Space_Mapper/lang/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,8 @@
"select_age_group": "Selecciona grupo de edad",
"submit": "Enviar",
"reset": "Restablecer",
"gender": "género"
"gender": "género",
"about_the_project": "Sobre el proyecto",
"consent_form" : "Consentimiento",
"do_you_agree_to_share_your_anonymous_location_with" : "¿Estás de acuerdo en compartir tu historial de ubicaciones anónimo con "
}
11 changes: 6 additions & 5 deletions Space_Mapper/lib/mocks/mock_survey.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,24 @@ import '../models/survey.dart';
mixin MockSurvey implements Survey {
static final List<Survey> items = [
Survey(
1,
0,
"Mosquito Alert",
"https://play.google.com/store/apps/details?id=ceab.movelab.tigatrapp&hl=es&gl=US",
"https://www.periodismociudadano.com/wp-content/uploads/2020/11/mosquito-49141_640.jpg",
"Mosquito Alert is a citizen science platform for studying and controlling the tiger mosquito (Aedes albopictus) and the yellow fever mosquito (Aedes aegypti).",
),
Survey(
2,
1,
"Max Planck Institute",
"https://www.mpg.de/institutes",
"https://upload.wikimedia.org/wikipedia/commons/thumb/f/f3/Max_Planck_Institute_for_the_Science_of_Light%2C_new_building%2C_July_2015.jpg/800px-Max_Planck_Institute_for_the_Science_of_Light%2C_new_building%2C_July_2015.jpg",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus a dui leo. Integer volutpat ipsum sed nulla luctus porttitor. Vivamus tincidunt iaculis purus. Sed lacinia faucibus dignissim.",
),
Survey(
3,
2,
"Space Mapper Form Test",
"https://ee.kobotoolbox.org/single/asCwpCjZ",
//"https://ee.kobotoolbox.org/single/asCwpCjZ",
"https://ee.kobotoolbox.org/x/AG5j1vFN",
"https://raw.githubusercontent.com/ActivitySpaceProject/space_mapper/master/Assets/images/3.0.2%2B18_screenshots.png",
"Nullam ac est non ante lobortis cursus. Sed nulla leo, venenatis at enim a, iaculis venenatis purus.Nullam ac est non ante lobortis cursus. Sed nulla leo, venenatis at enim a, iaculis venenatis purus.Nullam ac est non ante lobortis cursus. Sed nulla leo, venenatis at enim a, iaculis venenatis purus.Nullam ac est non ante lobortis cursus. Sed nulla leo, venenatis at enim a, iaculis venenatis purus.Nullam ac est non ante lobortis cursus. Sed nulla leo, venenatis at enim a, iaculis venenatis purus.",
)
Expand All @@ -33,7 +34,7 @@ mixin MockSurvey implements Survey {
return items;
}

static Survey fetch(int index) {
static Survey fetchByID(int index) {
return items[index];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ class CustomLocationsManager {
continue; //CustomLocationsManager.customLocations[j].getUUID()) continue;
}
// Match not found, we add the location
CustomLocation newLocation = await CustomLocationsManager.createCustomLocation(
recordedLocations[i]
);
CustomLocation newLocation =
await CustomLocationsManager.createCustomLocation(
recordedLocations[i]);
customLocations.add(newLocation);
}
return customLocations;
Expand All @@ -54,7 +54,7 @@ class CustomLocationsManager {

static Future<CustomLocation> createCustomLocation(
var recordedLocation) async {
CustomLocation location = new CustomLocation();
CustomLocation location = new CustomLocation();

//Save data from flutter_background_geolocation library
location.setUUID(recordedLocation['uuid']);
Expand All @@ -65,8 +65,9 @@ class CustomLocationsManager {
location.setAltitude(recordedLocation['coords']['altitude'],
recordedLocation['coords']['altitude_accuracy']);

Placemark? placemark = await getLocationData(recordedLocation['coords']['latitude'],
recordedLocation['coords']['longitude']);
Placemark? placemark = await getLocationData(
recordedLocation['coords']['latitude'],
recordedLocation['coords']['longitude']);

//Add our custom data
if (placemark != null) {
Expand All @@ -84,6 +85,7 @@ class CustomLocationsManager {
}
return location;
}

///Get data such as city, province, postal code, street name, country...
static Future<Placemark?> getLocationData(double lat, double long) async {
try {
Expand All @@ -98,6 +100,23 @@ class CustomLocationsManager {
}
}

/// This class should be used to share your location history to other people
class ShareLocation {
late final String _timestamp;
final double _lat;
final double _long;

ShareLocation(this._timestamp, this._lat, this._long);

Map<String, dynamic> toJson() => {
'timestamp': _timestamp,
'coords': {
'latitude': _lat,
'longitude': _long,
}
};
}

class CustomLocation {
late final String _uuid;
late String _locality = "";
Expand Down Expand Up @@ -228,4 +247,4 @@ class CustomLocation {
num getAltitudeAcc() {
return _altitude;
}
}
}
3 changes: 2 additions & 1 deletion Space_Mapper/lib/models/survey.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ class Survey {
: id = 0,
name = ' ',
webUrl = ' ',
imageUrl = ' ',
imageUrl =
'', // Leave this without space ('' instead of ' ') to avoid an exception
summary = ' ';

static Future<List<Survey>> fetchAll() async {
Expand Down
5 changes: 2 additions & 3 deletions Space_Mapper/lib/ui/list_view.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import '../app_localizations.dart';
import '../models/list_view.dart';
import '../models/custom_locations.dart';
import 'package:flutter/material.dart';

class STOListView extends StatefulWidget {
Expand All @@ -9,8 +9,7 @@ class STOListView extends StatefulWidget {
_STOListViewState createState() => _STOListViewState();
}

class _STOListViewState extends State<STOListView> {

class _STOListViewState extends State<STOListView> {
@override
void initState() {
super.initState();
Expand Down
22 changes: 2 additions & 20 deletions Space_Mapper/lib/ui/side_drawer.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'dart:convert';
import 'package:asm/app_localizations.dart';
import 'package:asm/models/list_view.dart';
import 'package:asm/models/custom_locations.dart';
import 'package:asm/ui/list_view.dart';
import 'package:asm/ui/report_an_issue.dart';
import 'package:flutter/material.dart';
Expand All @@ -9,25 +9,7 @@ import 'package:share/share.dart';
import 'package:flutter_background_geolocation/flutter_background_geolocation.dart'
as bg;

import 'available_surveys.dart';

class ShareLocation {
late final String _timestamp;
final double _lat;
final double _long;

ShareLocation(timestamp, this._lat, this._long) {
_timestamp = CustomLocationsManager.formatTimestamp(timestamp);
}

Map<String, dynamic> toJson() => {
'timestamp': _timestamp,
'coords': {
'latitude': _lat,
'longitude': _long,
}
};
}
import 'surveys_list.dart';

class SpaceMapperSideDrawer extends StatelessWidget {
_shareLocations() async {
Expand Down
198 changes: 198 additions & 0 deletions Space_Mapper/lib/ui/survey_detail.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
import 'dart:convert';

import 'package:flutter_background_geolocation/flutter_background_geolocation.dart'
as bg;
import 'package:flutter/material.dart';

import '../app_localizations.dart';
import '../components/banner_image.dart';
import '../components/survey_tile.dart';
import '../mocks/mock_survey.dart';
import '../models/survey.dart';
import '../models/custom_locations.dart';
import '../ui/web_view.dart';
import '../styles.dart';

const BannerImageHeight = 300.0;
const BodyVerticalPadding = 20.0;
const FooterHeight = 100.0;

class SurveyDetail extends StatefulWidget {
final int surveyID;

SurveyDetail(this.surveyID);

@override
_SurveyDetailState createState() => _SurveyDetailState(surveyID);
}

class _SurveyDetailState extends State<SurveyDetail> {
final int surveyID;
Survey survey = Survey.blank();
bool consent = false;

_SurveyDetailState(this.surveyID);

@override
void initState() {
super.initState();
loadData();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
AppLocalizations.of(context)!.translate("about_the_project"))),
body: Stack(
children: [
_renderBody(context, survey),
_renderFooter(context),
//_renderConsentForm(),
],
),
);
}

loadData() {
final survey = MockSurvey.fetchByID(this.surveyID);

if (mounted) {
setState(() {
this.survey = survey;
});
}
}

Widget _renderBody(BuildContext context, Survey survey) {
var result = <Widget>[];
result.add(BannerImage(url: survey.imageUrl, height: BannerImageHeight));
result.add(_renderHeader());
result.add(_renderConsentForm());
result.add(_renderBottomSpacer());
return SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: result));
}

Widget _renderHeader() {
return Container(
padding: EdgeInsets.symmetric(
vertical: BodyVerticalPadding,
horizontal: Styles.horizontalPaddingDefault),
child: SurveyTile(survey: survey, darkTheme: false),
);
}

Widget _renderFooter(BuildContext contexty) {
return Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
decoration: BoxDecoration(color: Colors.white.withOpacity(0.5)),
height: FooterHeight,
child: Container(
padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 30.0),
child: _renderTakeSurveyButton(),
),
)
],
);
}

Widget _renderConsentForm() {
String title = AppLocalizations.of(context)!.translate("consent_form");
String text = AppLocalizations.of(context)!.translate("do_you_agree_to_share_your_anonymous_location_with") + "${survey.name}?";

return Container(
height: SurveyTileHeight,
padding: EdgeInsets.symmetric(
//vertical: BodyVerticalPadding,
horizontal: Styles.horizontalPaddingDefault),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('$title',
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: Styles.surveyTileTitleLight),
Row(
children: [
Checkbox(
value: consent,
onChanged: (bool? newValue) {
setState(() {
consent = newValue!;
});
},
),
Expanded(
child: Text('$text',
overflow: TextOverflow.ellipsis,
maxLines: 3,
style: Styles.surveyTileCaption),
)
],
),
],
),
);
}

Widget _renderTakeSurveyButton() {
return TextButton(
//color: Styles.accentColor,
//textColor: Styles.textColorBright,
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.blue),
),
onPressed: () => {
_navigationToSurvey(context),
},
child: Text(
'Take Survey'.toUpperCase(),
style: Styles.textCTAButton,
),
);
}

Future<void> _navigationToSurvey(BuildContext context) async {
String locationHistoryJSON = "";

// If we have consent, send location history. Otherwise, send empty string
if (consent) {
List allLocations = await bg.BackgroundGeolocation.locations;
List<ShareLocation> customLocation = [];

// We get only timestamp and coordinates into our custom class
for (var thisLocation in allLocations) {
ShareLocation _loc = new ShareLocation(
bg.Location(thisLocation).timestamp,
bg.Location(thisLocation).coords.latitude,
bg.Location(thisLocation).coords.longitude);
customLocation.add(_loc);
}

locationHistoryJSON = jsonEncode(customLocation);
locationHistoryJSON = locationHistoryJSON.replaceAll("\"",
"'"); //We replace " into ' to avoid a javascript exception when we post it in the webview's form
} else {
locationHistoryJSON = "I do not agree to share my location history.";
}

Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
MyWebView(survey.webUrl, locationHistoryJSON)));
}

Widget _renderBottomSpacer() {
return Container(height: FooterHeight);
}
}
Loading