Compare commits

...

35 Commits

Author SHA1 Message Date
remrem ece19ef7d2 translate common_widget
continuous-integration/drone/push Build is passing Details
2 years ago
remrem cd6c43431e translate view/volumes
continuous-integration/drone/push Build was killed Details
2 years ago
remrem 8ee5055ffc translate view/profile
continuous-integration/drone/push Build was killed Details
2 years ago
remrem a2657b2bba translate view/on_boarding
continuous-integration/drone/push Build was killed Details
2 years ago
remrem 8b65bccf9f translate view/map
continuous-integration/drone/push Build was killed Details
2 years ago
remrem 3e00d74e97 fix ci
continuous-integration/drone/push Build is passing Details
2 years ago
remrem 6e2b6c057e conflicts with stats_activities_view.dart
continuous-integration/drone/push Build was killed Details
2 years ago
remrem 359cbfeed8 translate view/login
2 years ago
remrem d15eb2bffc rename strings fr to en in view/{activity,home}
2 years ago
remrem a10439d1bc rewrite string message in request_api
continuous-integration/drone/push Build is failing Details
2 years ago
Enzo 4a40f1bbd6 Merge branch 'Enzo'
continuous-integration/drone/push Build is failing Details
2 years ago
Enzo 866a0a7381 change convertisseur
2 years ago
Othmane BENJELLOUN 0784a95f3a Merge branch 'master' of https://codefirst.iut.uca.fr/git/SmartFit/SmartFit_Mobile
continuous-integration/drone/push Build is failing Details
2 years ago
Othmane BENJELLOUN 147c53e1ae regle affichage
2 years ago
Enzo c5b182411e Merge branch 'master' into Enzo
2 years ago
remrem acdb0ce770 add files for github actions
continuous-integration/drone/push Build is passing Details
2 years ago
Enzo dcd2198e79 correction affichage predict
2 years ago
Enzo 404c5f344a coorectif web import
2 years ago
Enzo ad706516df Merge branch 'Enzo'
continuous-integration/drone/push Build is passing Details
2 years ago
Enzo e2e5f86e3c remove print
2 years ago
Enzo 5f46882c18 correction import sur generic
2 years ago
Enzo 97e915d31f bug file généric
2 years ago
remrem de3a4f0cce Update 'README.md'
continuous-integration/drone/push Build is passing Details
2 years ago
Enzo JOLYS 5b81ff7b17 Mise à jour de 'lib/modele/api/request_api.dart'
continuous-integration/drone/push Build is passing Details
2 years ago
Enzo 6ded7e7eb0 correction prediction
continuous-integration/drone/push Build is passing Details
2 years ago
Enzo 7a3a707c1c debut test
2 years ago
Othmane BENJELLOUN d5efc26e4e correction orthographe
continuous-integration/drone/push Build is passing Details
2 years ago
Enzo f23ff5da02 logOff en commentaire
continuous-integration/drone/push Build is passing Details
2 years ago
Enzo 218179b9df commencement de logOff et deleteUser
2 years ago
Enzo 30ff3a0c3c modif prédiction for json incoming
2 years ago
Enzo abbb92b564 print retirer
2 years ago
Enzo 0ed7bd0cd7 Merge branch 'master' into Enzo
2 years ago
remrem 01c71f7dc6 Merge pull request 'offline' (#8) from offline into master
continuous-integration/drone/push Build is passing Details
2 years ago
Enzo 6fbde68b73 modif
2 years ago
Enzo 6027af0e18 modif prediction
2 years ago

@ -19,10 +19,6 @@ steps:
- dart run build_runner clean
- dart run build_runner build --delete-conflicting-outputs
- flutter build apk
- sfm_apk=sfm_$(date +"%Y_%m_%d_%H_%M_%S").apk
- cp ./build/app/outputs/flutter-apk/app-release.apk $sfm_apk
- curl -F "file=@$sfm_apk" https://anonfiles.me/api/v1/upload > upload.json
- cat upload.json | cut -d '"' -f 12
- name: build-web
image: ghcr.io/cirruslabs/flutter:3.16.3

@ -0,0 +1,38 @@
name: Flutter CI
on:
push:
branches: [ master ]
jobs:
build-apk-web-sonarqube:
runs-on: ubuntu-latest
container: ghcr.io/cirruslabs/flutter:3.16.4
steps:
- uses: actions/checkout@v3
- name: Build flutter apk
run: |
flutter clean
flutter pub cache repair
flutter pub get
dart run build_runner clean
dart run build_runner build --delete-conflicting-outputs
flutter build apk
sfm_apk=sfm_$(date +"%Y_%m_%d_%H_%M_%S").apk
cp ./build/app/outputs/flutter-apk/app-release.apk $sfm_apk
curl -F "file=@$sfm_apk" https://anonfiles.me/api/v1/upload > upload.json
cat upload.json | cut -d '"' -f 12
- name: Build flutter web
run: |
flutter build web --web-renderer canvaskit
curl -sL https://firebase.tools | bash
- name: Code analysis
run: |
export SONAR_SCANNER_VERSION=5.0.1.3006
export SONAR_SCANNER_HOME=$HOME/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-linux
curl --create-dirs -sSLo $HOME/.sonar/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$SONAR_SCANNER_VERSION-linux.zip
unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/
export PATH=$SONAR_SCANNER_HOME/bin:$PATH
export SONAR_SCANNER_OPTS="-server"

@ -1,5 +1,5 @@
# SmartFit Web and Mobile!
TODO: Description
TODO: Description + badge
## Getting Started

@ -12,7 +12,7 @@ class VolumesList extends StatelessWidget {
// TODO: True message with variables and context aware
if (volume["nbActivity"] == 0) {
return const Text("Aucune activité ces x jours/mois/années");
return const Text("No activity the last x days/month/year");
}
return SingleChildScrollView(
@ -21,32 +21,32 @@ class VolumesList extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
children: [
ContainerStatsActivities(volume["nbActivity"].toString(),
"Nombre Activitée(s)", Icons.numbers),
"Number of activities", Icons.numbers),
SizedBox(
width: media.width * 0.03,
),
ContainerStatsActivities(
"${Convertisseur.secondeIntoMinute(volume["durationActiviy"]).toStringAsFixed(2)} m",
"Temps Total",
"${Convertisseur.secondeIntoMinute(volume["durationActiviy"]).toStringAsFixed(0)} min",
"Total time",
Icons.timer),
SizedBox(
width: media.width * 0.03,
),
ContainerStatsActivities(
volume["bpmAvg"].toString(), "Bpm Moyens", Icons.favorite),
volume["bpmAvg"].toString(), "Average bpm", Icons.favorite),
SizedBox(
width: media.width * 0.03,
),
ContainerStatsActivities(
" ${Convertisseur.msIntoKmh(volume["speedAvg"]).toStringAsFixed(2)} km/h",
"Vitesse Moyenne",
"Average speed",
Icons.bolt),
SizedBox(
width: media.width * 0.03,
),
ContainerStatsActivities(
"${volume["denivelePositif"].toStringAsFixed(2)} m",
"Dénivelé Positif",
"Positive height difference",
Icons.hiking),
],
),

@ -38,7 +38,7 @@ class MobileLigneContainerStats extends StatelessWidget {
child: Row(
children: [
const Text(
'Statistiques',
'Stats',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w800,

@ -22,7 +22,7 @@ class ProfileCompte extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Compte",
"Account",
style: TextStyle(
color: TColor.black,
fontSize: 16,

@ -35,7 +35,7 @@ class ProfileEntete extends StatelessWidget {
),
),
Text(
"Course à pied",
"Running",
style: TextStyle(
color: TColor.gray,
fontSize: 12,

@ -13,7 +13,7 @@ class ProfileInfoUser extends StatelessWidget {
Expanded(
child: TitleSubtitleCell(
title: context.watch<User>().listActivity.length.toString(),
subtitle: "Nombre d'activité",
subtitle: "Number of activities",
),
),
const SizedBox(
@ -21,8 +21,11 @@ class ProfileInfoUser extends StatelessWidget {
),
Expanded(
child: TitleSubtitleCell(
title: context.watch<User>().getTotalTimeAllActivity().toStringAsFixed(2),
subtitle: "Temps en activité",
title: context
.watch<User>()
.getTotalTimeAllActivity()
.toStringAsFixed(2),
subtitle: "Total activity time",
),
),
const SizedBox(
@ -32,7 +35,7 @@ class ProfileInfoUser extends StatelessWidget {
child: TitleSubtitleCell(
title:
"${context.watch<User>().getTotalDenivelePositifAllActivity().toStringAsFixed(2)} + m",
subtitle: "Total dénivelé positif",
subtitle: "Total positive height difference",
),
),
const SizedBox(
@ -42,7 +45,7 @@ class ProfileInfoUser extends StatelessWidget {
child: TitleSubtitleCell(
title:
"${context.watch<User>().getTotalDeniveleNegatifAllActivity().toStringAsFixed(2)} - m",
subtitle: "Total dénivelé négatif",
subtitle: "Total negative height difference",
),
),
],

@ -21,7 +21,7 @@ class ProfileOther extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Autre",
"Others",
style: TextStyle(
color: TColor.black,
fontSize: 16,

@ -37,7 +37,7 @@ class WebLigneContainerStats extends StatelessWidget {
child: Column(
children: [
const Text(
'Statistiques',
'Stats',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w800,

@ -27,32 +27,33 @@ class WorkoutRow extends StatelessWidget {
fit: BoxFit.cover,
),
),
const SizedBox(width: 15,),
const SizedBox(
width: 15,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
Text(
wObj["name"].toString(),
style: TextStyle(
color: TColor.black,
fontSize: 15,
fontWeight: FontWeight.bold),
),
Text(
"${ wObj["value"].toString()}",
wObj["value"].toString(),
style: TextStyle(
color: TColor.gray,
fontSize: 12,),
color: TColor.gray,
fontSize: 12,
),
),
const SizedBox(
height: 4,
),
const SizedBox(height: 4,),
],
)),
],
));
}
}
}

@ -75,7 +75,7 @@ class WorkoutRowGeneric extends StatelessWidget {
),
),
Text(
"Temps : ${Convertisseur.secondeIntoMinute(wObj["time"]).toStringAsFixed(2)} m",
"Time : ${Convertisseur.secondeIntoMinute(wObj["time"]).toStringAsFixed(2)} m",
style: TextStyle(
color: TColor.black,
fontSize: 12,

@ -75,14 +75,14 @@ class WorkoutRowWalking extends StatelessWidget {
),
),
Text(
"Temps : ${Convertisseur.secondeIntoMinute(wObj["time"]).toStringAsFixed(2)} m",
"Time : ${Convertisseur.secondeIntoMinute(wObj["time"]).toStringAsFixed(0)} minutes",
style: TextStyle(
color: TColor.black,
fontSize: 12,
),
),
Text(
"Vitesse moyenne : ${Convertisseur.msIntoKmh(wObj["VitesseAvg"]).toStringAsFixed(2)} km/h",
"Average speed : ${Convertisseur.msIntoKmh(wObj["VitesseAvg"]).toStringAsFixed(2)} km/h",
style: TextStyle(
color: TColor.black,
fontSize: 12,

@ -36,75 +36,8 @@ class _MobileGraphBpmAndSpeedByTime
width: double.maxFinite,
child: LineChart(
LineChartData(
showingTooltipIndicators:
widget.func.showingTooltipOnSpots.map((index) {
return ShowingTooltipIndicators([
LineBarSpot(
widget.func.tooltipsOnBar,
widget.func.lineBarsData.indexOf(widget.func.tooltipsOnBar),
widget.func.tooltipsOnBar.spots[index],
),
]);
}).toList(),
lineTouchData: LineTouchData(
enabled: true,
handleBuiltInTouches: false,
touchCallback: (FlTouchEvent event, LineTouchResponse? response) {
if (response == null || response.lineBarSpots == null) {
return;
}
if (event is FlTapUpEvent) {
final spotIndex = response.lineBarSpots!.first.spotIndex;
widget.func.showingTooltipOnSpots.clear();
setState(() {
widget.func.showingTooltipOnSpots.add(spotIndex);
});
}
},
mouseCursorResolver:
(FlTouchEvent event, LineTouchResponse? response) {
if (response == null || response.lineBarSpots == null) {
return SystemMouseCursors.basic;
}
return SystemMouseCursors.click;
},
getTouchedSpotIndicator:
(LineChartBarData barData, List<int> spotIndexes) {
return spotIndexes.map((index) {
return TouchedSpotIndicatorData(
const FlLine(
color: Colors.transparent,
),
FlDotData(
show: true,
getDotPainter: (spot, percent, barData, index) =>
FlDotCirclePainter(
radius: 3,
color: Colors.white,
strokeWidth: 3,
strokeColor: TColor.secondaryColor1,
),
),
);
}).toList();
},
touchTooltipData: LineTouchTooltipData(
tooltipBgColor: TColor.secondaryColor1,
tooltipRoundedRadius: 20,
getTooltipItems: (List<LineBarSpot> lineBarsSpot) {
return lineBarsSpot.map((lineBarSpot) {
return LineTooltipItem(
"Seconde ${lineBarSpot.x.toInt() / 10} ",
const TextStyle(
color: Colors.white,
fontSize: 10,
fontWeight: FontWeight.bold,
),
);
}).toList();
},
),
),
lineBarsData: widget.func.lineBarsData1,
minY: 0,
maxY: 110,
@ -113,7 +46,7 @@ class _MobileGraphBpmAndSpeedByTime
leftTitles: AxisTitles(
sideTitles: widget.func.leftTitles,
),
topTitles: const AxisTitles(),
topTitles: const AxisTitles(),
bottomTitles: AxisTitles(
sideTitles: widget.func.bottomTitles,
),

@ -33,75 +33,7 @@ class _WebGraphBpmAndSpeedByTime extends State<WebGraphBpmAndSpeedByTime> {
width: widget.media.width * 0.35,
child: LineChart(
LineChartData(
showingTooltipIndicators:
widget.func.showingTooltipOnSpots.map((index) {
return ShowingTooltipIndicators([
LineBarSpot(
widget.func.tooltipsOnBar,
widget.func.lineBarsData.indexOf(widget.func.tooltipsOnBar),
widget.func.tooltipsOnBar.spots[index],
),
]);
}).toList(),
lineTouchData: LineTouchData(
enabled: true,
handleBuiltInTouches: false,
touchCallback: (FlTouchEvent event, LineTouchResponse? response) {
if (response == null || response.lineBarSpots == null) {
return;
}
if (event is FlTapUpEvent) {
final spotIndex = response.lineBarSpots!.first.spotIndex;
widget.func.showingTooltipOnSpots.clear();
setState(() {
widget.func.showingTooltipOnSpots.add(spotIndex);
});
}
},
mouseCursorResolver:
(FlTouchEvent event, LineTouchResponse? response) {
if (response == null || response.lineBarSpots == null) {
return SystemMouseCursors.basic;
}
return SystemMouseCursors.click;
},
getTouchedSpotIndicator:
(LineChartBarData barData, List<int> spotIndexes) {
return spotIndexes.map((index) {
return TouchedSpotIndicatorData(
const FlLine(
color: Colors.transparent,
),
FlDotData(
show: true,
getDotPainter: (spot, percent, barData, index) =>
FlDotCirclePainter(
radius: 3,
color: Colors.white,
strokeWidth: 3,
strokeColor: TColor.secondaryColor1,
),
),
);
}).toList();
},
touchTooltipData: LineTouchTooltipData(
tooltipBgColor: TColor.secondaryColor1,
tooltipRoundedRadius: 20,
getTooltipItems: (List<LineBarSpot> lineBarsSpot) {
return lineBarsSpot.map((lineBarSpot) {
return LineTooltipItem(
"Seconde ${lineBarSpot.x.toInt() / 10} ",
const TextStyle(
color: Colors.white,
fontSize: 10,
fontWeight: FontWeight.bold,
),
);
}).toList();
},
),
),
lineBarsData: widget.func.lineBarsData1,
minY: -10,
maxY: 110,

@ -16,7 +16,7 @@ class EnteteHomeView extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Bienvenue,",
"Welcome,",
style: TextStyle(color: TColor.gray, fontSize: 12),
),
Text(

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:smartfit_app_mobile/modele/convertisseur.dart';
import 'package:smartfit_app_mobile/modele/user.dart';
import 'package:smartfit_app_mobile/common/colo_extension.dart';
@ -19,10 +20,11 @@ class Stats extends StatelessWidget {
.activityInfo
.bpmAvg
.toString();
String time = Provider.of<User>(context, listen: false)
.managerSelectedActivity
.getTimeAllActivitySelected()
.toString();
String time = Convertisseur.secondeIntoMinute(
Provider.of<User>(context, listen: false)
.managerSelectedActivity
.getTimeAllActivitySelected())
.toStringAsFixed(0);
return Column(
children: [
Padding(
@ -30,7 +32,7 @@ class Stats extends StatelessWidget {
child: Row(
children: [
const Text(
'Statistiques',
'Stats',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w800,
@ -57,7 +59,7 @@ class Stats extends StatelessWidget {
iconBackground: const Color(0xff6131AD),
time: '+5s',
label: 'Time',
value: '$time s',
value: '$time min',
),
const SizedBox(width: 15),
InfoStat(

@ -1,4 +1,3 @@
import 'package:flutter_svg/flutter_svg.dart';
import 'package:smartfit_app_mobile/common/colo_extension.dart';
import 'package:flutter/material.dart';
@ -7,7 +6,11 @@ class TodayTargetCell extends StatelessWidget {
final String icon;
final String value;
final String title;
const TodayTargetCell({super.key, required this.icon, required this.value, required this.title});
const TodayTargetCell(
{super.key,
required this.icon,
required this.value,
required this.title});
@override
Widget build(BuildContext context) {
@ -21,7 +24,7 @@ class TodayTargetCell extends StatelessWidget {
child: Row(
children: [
SvgPicture.asset(
icon,
icon,
width: 40,
height: 40,
),
@ -64,4 +67,5 @@ class TodayTargetCell extends StatelessWidget {
),
);
}
}
}

@ -270,7 +270,18 @@ class ActivityInfo {
// -- BPM -- //
int bpmSomme = 0;
int bpmNb = 0;
bool bpmNotZero = false;
// -- Denivelé -- //
double lastDenivele = 0.0;
// -- Altitude -- //
double altitudeSomme = 0;
int alititudeNb = 0;
// -- Température -- //
int temperatureSomme = 0;
int temperatureNb = 0;
// -- Vitesse -- //
double vitesseSomme = 0.0;
int vitesseNb = 0;
// --- Boucle -- //
for (int i = 1; i < csv.length; i++) {
//
@ -288,12 +299,78 @@ class ActivityInfo {
bpmMin = value;
}
}
/// ------------------ Denivele et Altitude --------------- //
if (!isNull(enteteCSV["Value_${managerFile.fieldAltitude}"]!, csv[i])) {
double value = double.parse(
csv[i][enteteCSV["Value_${managerFile.fieldAltitude}"]!]);
// -- Denivelé -- //
if (value > lastDenivele) {
denivelePositif += value - lastDenivele;
} else {
deniveleNegatif += (value - lastDenivele) * -1;
}
lastDenivele = value;
// -- Altitude -- //
if (value > altitudeMax) {
altitudeMax = value;
}
if (value < altitudeMin) {
altitudeMin = value;
}
altitudeSomme += value;
alititudeNb += 1;
altitudeNotZero = true;
}
// ------------------------ Température ----------------------- //
if (!isNull(
enteteCSV["Value_${managerFile.fieldTemperature}"]!, csv[i])) {
int value = int.parse(
csv[i][enteteCSV["Value_${managerFile.fieldTemperature}"]!]);
temperatureSomme += value;
temperatureNb += 1;
temperatureNotZero = true;
if (value > temperatureMax) {
temperatureMax = value;
}
if (value < temperatureMin) {
temperatureMin = value;
}
}
// ------------------------ Vitesse -----------------------------//
if (!isNull(enteteCSV["Value_${managerFile.fieldSpeed}"]!, csv[i])) {
double value =
double.parse(csv[i][enteteCSV["Value_${managerFile.fieldSpeed}"]!]);
vitesseSomme += value;
vitesseNb += 1;
vitesseNotZero = true;
if (value > vitesseMax) {
vitesseMax = value;
}
if (value < vitesseMin) {
vitesseMin = value;
}
}
}
// -- BPM -- //
if (bpmNotZero) {
bpmAvg = bpmSomme ~/ bpmNb;
}
// -- Atitude -- //
if (altitudeNotZero) {
altitudeAvg = altitudeSomme / alititudeNb;
}
// -- Température -- //
if (temperatureNotZero) {
temperatureAvg = temperatureSomme ~/ temperatureNb;
}
// -- Vitesse -- //
if (vitesseNotZero) {
vitesseAvg = vitesseSomme / vitesseNb;
}
return this;
}

@ -193,6 +193,16 @@ class ApiWrapper {
return res;
}
Future<Tuple2> getModeleAI(
String token, String category, InfoMessage infoManager) async {
await init();
if (handleOffline(infoManager)) return const Tuple2(false, "offline");
Tuple2 res = await api.getModeleAI(token, category);
if (!res.item1) infoManager.displayMessage(noConnectionMessage, true);
return res;
}
}
extension StringExtension on String {

@ -42,4 +42,7 @@ abstract class IDataStrategy {
// Update email, password, username
Future<Tuple2<bool, String>> modifAttribut(
String token, String nameAttribut, String newValue);
//
Future<Tuple2> getModeleAI(String token, String category);
}

@ -111,10 +111,12 @@ class RequestApi implements IDataStrategy {
return Tuple2<bool, String>(true, json['token'].toString());
}
if (response.statusCode == 401) {
return const Tuple2<bool, String>(false, "UNAUTHORIZED");
return const Tuple2<bool, String>(
false, "Wrong Password, enter it carrefully !");
}
if (response.statusCode == 404) {
return const Tuple2<bool, String>(false, "Not found the email");
return const Tuple2<bool, String>(
false, "Account not found, please verify your credentials.");
}
} on SocketException catch (_) {
return const Tuple2(false, "No connection");
@ -136,11 +138,12 @@ class RequestApi implements IDataStrategy {
return Tuple2(true, json['token'].toString());
}
if (response.statusCode == 400) {
return const Tuple2(false, "400 BAD REQUEST - Json mal formaté");
return const Tuple2(false,
"L'application rendontre une erreur inconnue, veuillez réessayer.");
}
if (response.statusCode == 409) {
return const Tuple2(
false, "409 CONFLICT - Déja un compte avec cet email");
false, "Un compte est déjà enregistré avec cet email!");
}
} on SocketException catch (_) {
return const Tuple2(false, "No connection");
@ -268,6 +271,7 @@ class RequestApi implements IDataStrategy {
headers: <String, String>{'Authorization': token});
if (response.statusCode == 200) {
Map<String, dynamic> json = jsonDecode(response.body);
return Tuple2(true, json);
}
if (response.statusCode == 400) {
@ -281,4 +285,21 @@ class RequestApi implements IDataStrategy {
}
return const Tuple2(false, "Fail");
}
@override
Future<Tuple2> getModeleAI(String token, String category) async {
try {
final response = await http.get(Uri.parse('$urlApi/user/ai/$category'),
headers: <String, String>{'Authorization': token});
if (response.statusCode == 200) {
Map<String, dynamic> json = jsonDecode(response.body);
return Tuple2(true, json);
} else {
return const Tuple2(false, "Fail");
}
} on SocketException catch (_) {
return const Tuple2(false, "No connection");
}
}
}

@ -2,10 +2,18 @@ class Convertisseur {
// Mettre que des trucs static
static double secondeIntoMinute(double seconde) {
return seconde / 60;
return (seconde / 60);
}
static double milisecondeIntoMinute(double miliseconde) {
return (miliseconde / 60000);
}
static double msIntoKmh(double metreSeconde) {
return metreSeconde * 3.6;
}
static double millisecondeIntoSeconde(double milliseconde) {
return (milliseconde / 1000);
}
}

@ -86,4 +86,9 @@ class RequestLocal implements IDataStrategy {
Future<bool> deleteFile(String token, String fileUuid) async {
throw Exception("Not Implemented");
}
@override
Future<Tuple2> getModeleAI(String token, String category) async {
return const Tuple2(false, "Not implemented");
}
}

@ -43,6 +43,7 @@ class ManagerFile {
// -- Getter categorie
String get marche => _marche;
String get velo => _velo;
String get generic => _generic;
List<String> allowedFieldWalking = List.empty(growable: true);
@ -61,7 +62,16 @@ class ManagerFile {
_fieldTemperature
];
allowedFieldGeneric = [_fieldTimestamp, _fieldBPM];
allowedFieldGeneric = [
_fieldTimestamp,
_fieldPositionLatitue,
_fieldPositionLongitude,
_fieldDistance,
_fieldBPM,
_fieldSpeed,
_fieldAltitude,
_fieldTemperature
];
allowedFieldCycling = [
_fieldTimestamp,
@ -149,6 +159,8 @@ class ManagerFile {
info.getDataWalking(csvData);
case (_velo):
info.getDataCycling(csvData);
case (_generic):
info.getDataGeneric(csvData);
default:
info.getDataGeneric(csvData);
}

@ -3,7 +3,10 @@ import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:smartfit_app_mobile/modele/activity.dart';
import 'package:smartfit_app_mobile/modele/activity_info/activity_info.dart';
import 'package:smartfit_app_mobile/modele/api/api_wrapper.dart';
import 'package:smartfit_app_mobile/modele/manager_selected_activity.dart';
import 'package:smartfit_app_mobile/modele/utile/info_message.dart';
import 'package:tuple/tuple.dart';
class User extends ChangeNotifier {
String username = "VOID";
@ -91,26 +94,27 @@ class User extends ChangeNotifier {
return map;
}
ActivityInfo predictActivity(DateTime date) {
// Appel pour avoir le model
String jsonString =
'{"coef": [270.63861280635473, 74.69699263779908, 1.9946527172333637, 0.03215810401413792, 0.3256805192289063], "intercept": [-335635.9890148213, -91874.0527070619, -2065.450392327813, -38.79838022998388, -291.590235396687]}';
Map<String, dynamic> jsonMap = json.decode(jsonString);
Future<Tuple2<bool, ActivityInfo>> predictActivity(
DateTime date, String category, InfoMessage infoManager) async {
ApiWrapper wrapper = ApiWrapper();
Tuple2 result = await wrapper.getModeleAI(token, category, infoManager);
if (!result.item1) return Tuple2(false, ActivityInfo());
String model = result.item2["model"];
Map<String, dynamic> jsonMap = json.decode(model);
// Transformer la date
int dateMilli = date.millisecondsSinceEpoch;
ActivityInfo activityInfo = ActivityInfo();
activityInfo.distance =
jsonMap["coef"][0] * dateMilli + jsonMap["intercept"][0];
activityInfo.bpmAvg =
(jsonMap["coef"][0] * dateMilli + jsonMap["intercept"][0]).toInt();
activityInfo.timeOfActivity =
jsonMap["coef"][1] * dateMilli + jsonMap["intercept"][1];
activityInfo.denivelePositif =
jsonMap["coef"][2] * dateMilli + jsonMap["intercept"][2];
activityInfo.vitesseAvg =
jsonMap["coef"][2] * dateMilli + jsonMap["intercept"][2];
activityInfo.distance =
jsonMap["coef"][3] * dateMilli + jsonMap["intercept"][3];
activityInfo.bpmAvg =
jsonMap["coef"][4] * dateMilli + jsonMap["intercept"][4];
return activityInfo;
return Tuple2(true, activityInfo);
}
}

@ -20,9 +20,11 @@ class HomeViewUtil {
.getXWithTime(managerFile.fieldAltitude);
List<FlSpot> bpmSecondes2 = List.from(bpmSecondes);
return DataHomeView(normaliserPremierElement(bpmSecondes), normaliserPremierElement(normaliserDeuxiemeElement(bpmSecondes2)),
normaliserPremierElement(normaliserDeuxiemeElement(vitesseSecondes)), normaliserPremierElement(altitudeSeconde));
return DataHomeView(
normaliserPremierElement(bpmSecondes),
normaliserPremierElement(normaliserDeuxiemeElement(bpmSecondes2)),
normaliserPremierElement(normaliserDeuxiemeElement(vitesseSecondes)),
normaliserPremierElement(altitudeSeconde));
}
List<FlSpot> normaliserDeuxiemeElement(List<FlSpot> liste) {
@ -41,21 +43,21 @@ class HomeViewUtil {
}
return liste;
}
List<FlSpot> normaliserPremierElement(List<FlSpot> liste) {
// Trouver le plus grand élément dans le premier élément de chaque FlSpot
double maxElement = 0.0;
for (var spot in liste) {
if (spot.x > maxElement) {
maxElement = spot.x;
// Trouver le plus grand élément dans le premier élément de chaque FlSpot
double maxElement = 0.0;
for (var spot in liste) {
if (spot.x > maxElement) {
maxElement = spot.x;
}
}
// Calculer le facteur de normalisation
double normalisationFactor = maxElement != 0.0 ? 100 / maxElement : 1.0;
// Mettre à jour tous les premiers éléments de la liste
for (int i = 0; i < liste.length; i++) {
liste[i] = FlSpot(liste[i].x * normalisationFactor, liste[i].y);
}
return liste;
}
// Calculer le facteur de normalisation
double normalisationFactor = maxElement != 0.0 ? 100 / maxElement : 1.0;
// Mettre à jour tous les premiers éléments de la liste
for (int i = 0; i < liste.length; i++) {
liste[i] = FlSpot(liste[i].x * normalisationFactor, liste[i].y);
}
return liste;
}
}

@ -5,7 +5,6 @@ import 'package:smartfit_app_mobile/modele/activity_saver.dart';
import 'package:smartfit_app_mobile/modele/helper.dart';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:csv/csv.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
@ -91,6 +90,10 @@ class ListActivityUtile {
String csvString = const ListToCsvConverter().convert(resultData.item2);
Uint8List byteCSV = Uint8List.fromList(utf8.encode(csvString));
/* -- Ne pas décomenter sinon web ne marche plus (Utilisé pour déboguer)
File x = await File("${await _managerFile.localPath}\\what")
.writeAsString(csvString);*/
Tuple2<bool, String> result = await api.uploadFileByte(
token,
byteCSV,

@ -17,8 +17,8 @@ class Activity extends StatelessWidget {
.activitySelected
.isEmpty
? ScreenTypeLayout.builder(
mobile: (_) => const NoActivityView("Pas d'activité sélectionnée"),
desktop: (_) => const NoActivityView("Pas d'activité sélectionnée"),
mobile: (_) => const NoActivityView("No activities selected"),
desktop: (_) => const NoActivityView("No activities selected"),
)
: ScreenTypeLayout.builder(
mobile: (_) => const MobileActivity(),

@ -36,7 +36,7 @@ class _MobileListActivity extends State<MobileListActivity> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"List Activités",
"Activity List",
style: TextStyle(
color: TColor.black,
fontSize: 16,
@ -74,7 +74,7 @@ class _MobileListActivity extends State<MobileListActivity> {
setState(() {});
},
child: Text(
"Ajouter",
"Add",
style: TextStyle(
color: TColor.gray,
fontSize: 14,
@ -93,7 +93,7 @@ class _MobileListActivity extends State<MobileListActivity> {
children: [
const SizedBox(height: 20),
Text(
"Vous n'avez pas d'activités pour le moment, veuillez en ajouter.",
"You don't have any activity at the moment, please add an activity by cliking the 'Add' button",
style: TextStyle(
color: TColor.gray,
fontSize: 11,

@ -36,7 +36,7 @@ class _WebListActivityState extends State<WebListActivity> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"List Activités",
"Activity List",
style: TextStyle(
color: TColor.black,
fontSize: 16,
@ -73,7 +73,7 @@ class _WebListActivityState extends State<WebListActivity> {
}
},
child: Text(
"Ajouter",
"Add",
style: TextStyle(
color: TColor.gray,
fontSize: 14,
@ -92,7 +92,7 @@ class _WebListActivityState extends State<WebListActivity> {
children: [
const SizedBox(height: 20),
Text(
"Vous n'avez pas d'activités pour le moment, veuillez en ajouter.",
"You don't have any activity at the moment, please add an activity by cliking the 'Add' button",
style: TextStyle(
color: TColor.gray,
fontSize: 11,

@ -28,10 +28,8 @@ class _HomeViewState extends State<HomeView> {
: selectedActivitiesCount > 1
? const StatAtivities()
: ScreenTypeLayout.builder(
mobile: (_) =>
const NoActivityView("Pas d'activité sélectionnée"),
desktop: (_) =>
const NoActivityView("Pas d'activité sélectionnée"),
mobile: (_) => const NoActivityView("No activity selected"),
desktop: (_) => const NoActivityView("No activity selected"),
);
}
}

@ -29,6 +29,7 @@ class _MobileHomeView extends State<MobileHomeView> {
context.watch<User>().managerSelectedActivity;
data = HomeViewUtil().initData(context);
// -- BPM -- //
data.maxBPM =
managerSelectedActivity.activitySelected.first.activityInfo.bpmMax;
@ -36,7 +37,6 @@ class _MobileHomeView extends State<MobileHomeView> {
managerSelectedActivity.activitySelected.first.activityInfo.bpmMin;
int avgBpm =
managerSelectedActivity.activitySelected.first.activityInfo.bpmAvg;
// -- Altitude -- //
double minAltitude =
managerSelectedActivity.activitySelected.first.activityInfo.altitudeMin;
@ -48,12 +48,12 @@ class _MobileHomeView extends State<MobileHomeView> {
double maxSpeed = managerSelectedActivity.getMaxSpeedAllActivitySelected();
double avgSpeed = managerSelectedActivity.getAvgSpeedAllActivitySelected();
double minSpeed = managerSelectedActivity.getMinSpeedAllActivitySelected();
data.maxSpeed = maxSpeed;
data.time = context
.watch<User>()
.managerSelectedActivity
.getTimeAllActivitySelected();
return Scaffold(
backgroundColor: TColor.white,
body: SingleChildScrollView(
@ -68,7 +68,7 @@ class _MobileHomeView extends State<MobileHomeView> {
height: media.width * 0.05,
),
Text(
"Status d'activité",
"Activity Status",
style: TextStyle(
color: TColor.black,
fontSize: 16,
@ -87,7 +87,7 @@ class _MobileHomeView extends State<MobileHomeView> {
"${avgBpm.toString()} BPM",
"Minimum",
"Maximum",
"Moyenne",
"Average",
Icons.trending_down,
Icons.trending_up,
Icons.favorite_outline),
@ -95,7 +95,7 @@ class _MobileHomeView extends State<MobileHomeView> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Rythme cardique et vitesse",
"Heart rate and speed",
style: TextStyle(
color: TColor.black,
fontSize: 16,
@ -114,9 +114,9 @@ class _MobileHomeView extends State<MobileHomeView> {
"${double.parse(maxSpeed.toStringAsFixed(2))} m/s",
"${double.parse(minSpeed.toStringAsFixed(2))} m/s",
"${double.parse(avgSpeed.toStringAsFixed(2))} m/s",
"Max vitesse",
"Min vitesse",
"Moyenne vitesse",
"Max speed",
"Min speed",
"Avg speed",
Icons.trending_down,
Icons.trending_up,
Icons.trending_up),
@ -140,9 +140,9 @@ class _MobileHomeView extends State<MobileHomeView> {
"${minAltitude.toInt()} M",
"${maxAltitude.toInt()} M",
"${avgAltitude.toInt()} M",
"Altitude minimum",
"Altitude maximum",
"Altitude moyenne",
"Minimum altitude",
"Maximum altitude",
"Average altitude",
Icons.trending_down,
Icons.trending_up,
Icons.favorite_outline),

@ -49,7 +49,7 @@ class _NoActivityViewState extends State<NoActivityView> {
),
),
Text(
"Veuillez sélectionner une activité en cliquant sur la loupe ci-dessous",
"Please select an activity by clicking the magnifying glass below",
textAlign: TextAlign.center,
style: TextStyle(color: TColor.gray, fontSize: 12),
),

@ -1,8 +1,15 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart';
import 'package:smartfit_app_mobile/common/colo_extension.dart';
import 'package:smartfit_app_mobile/common_widget/button/round_button.dart';
import 'package:smartfit_app_mobile/common_widget/container/workout_row/workout_row.dart';
import 'package:smartfit_app_mobile/modele/activity_info/activity_info.dart';
import 'package:smartfit_app_mobile/modele/convertisseur.dart';
import 'package:smartfit_app_mobile/modele/manager_file.dart';
import 'package:smartfit_app_mobile/modele/user.dart';
import 'package:smartfit_app_mobile/modele/utile/info_message.dart';
import 'package:tuple/tuple.dart';
class Prediction extends StatefulWidget {
const Prediction({Key? key}) : super(key: key);
@ -14,61 +21,85 @@ class Prediction extends StatefulWidget {
class _PredictionState extends State<Prediction> {
List<Map<String, dynamic>> lastWorkoutArr = [
{
"name": "Temps",
"name": "Time",
"image": "assets/img/time-icon2.svg",
"value": "200 s",
"value": "..",
},
{
"name": "Rythme cardiaque",
"name": "Heart rate",
"image": "assets/img/bpm2-icon.svg",
"value": "120 BPM",
"value": "..",
},
{
"name": "Vitesse",
"name": "Speed",
"image": "assets/img/vitesse2-icon.svg",
"value": "3 m/s",
"value": "..",
},
{
"name": "Distance",
"image": "assets/img/distance2-icon.svg",
"value": "300 m",
"value": "..",
}
];
final ManagerFile _managerFile = ManagerFile();
String selectedCategory = "Select an activity category";
@override
Widget build(BuildContext context) {
var media = MediaQuery.of(context).size;
List<String> listCategory = [_managerFile.marche, _managerFile.velo];
void prediction() async {
InfoMessage tmp = InfoMessage();
/*
if (selectedCategory != _managerFile.marche ||
selectedCategory != _managerFile.velo) return;*/
Tuple2<bool, ActivityInfo> resultat =
await Provider.of<User>(context, listen: false)
.predictActivity(DateTime.now(), selectedCategory, tmp);
if (!resultat.item1) return;
setState(() {
lastWorkoutArr[0]["value"] =
"${Convertisseur.secondeIntoMinute(resultat.item2.timeOfActivity).toStringAsFixed(0)} min";
lastWorkoutArr[1]["value"] =
"${resultat.item2.bpmAvg.toStringAsFixed(2)} bpm";
lastWorkoutArr[2]["value"] =
"${Convertisseur.msIntoKmh(resultat.item2.vitesseAvg).toStringAsFixed(1)} km/h ";
lastWorkoutArr[3]["value"] =
"${resultat.item2.distance.toStringAsFixed(2)} m";
});
}
return Scaffold(
body: Padding(
padding: EdgeInsets.symmetric(horizontal: 20.0),
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 40),
const SizedBox(height: 40),
Text(
"Prédiction",
"Prediction",
style: TextStyle(
color: TColor.black,
fontSize: 22,
fontWeight: FontWeight.w700,
),
),
SizedBox(height: 20),
Row(
children: [
],
const SizedBox(height: 20),
const Row(
children: [],
),
Container(
decoration: BoxDecoration(
color: TColor.lightGray,
boxShadow: [
BoxShadow(
color: Color.fromARGB(255, 234, 234, 234).withOpacity(0.9),
color: const Color.fromARGB(255, 234, 234, 234)
.withOpacity(0.9),
spreadRadius: 2,
blurRadius: 5,
offset: Offset(0, 2),
offset: const Offset(0, 2),
),
],
borderRadius: BorderRadius.circular(15),
@ -90,7 +121,7 @@ class _PredictionState extends State<Prediction> {
Expanded(
child: DropdownButtonHideUnderline(
child: DropdownButton(
items: ["Walking", "Cycling"]
items: listCategory
.map((name) => DropdownMenuItem(
value: name,
child: Text(
@ -102,10 +133,14 @@ class _PredictionState extends State<Prediction> {
),
))
.toList(),
onChanged: (value) {},
onChanged: (value) {
setState(() {
selectedCategory = value!;
});
},
isExpanded: true,
hint: Text(
"Choisir type d'activité",
selectedCategory,
style: TextStyle(
color: TColor.gray,
fontSize: 12,
@ -115,25 +150,23 @@ class _PredictionState extends State<Prediction> {
),
),
// Bouton "Valider" prenant 30% de la largeur du parent
],
),
),
SizedBox(height: 20),
RoundButton(
title: "Valider",
const SizedBox(height: 20),
RoundButton(
title: "Save",
onPressed: () async {
setState(() {});
prediction();
}),
SizedBox(height: 20),
const SizedBox(height: 20),
ListView.builder(
padding: EdgeInsets.zero,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: lastWorkoutArr.length,
itemBuilder: (context, index) {
var wObj =
lastWorkoutArr[index] as Map<String, dynamic> ?? {};
var wObj = lastWorkoutArr[index];
return InkWell(
child: WorkoutRow(wObj: wObj),
);

@ -3,6 +3,7 @@ import 'package:provider/provider.dart';
import 'package:smartfit_app_mobile/common_widget/container/container_stats_activities.dart';
import 'package:smartfit_app_mobile/common_widget/other/entete_home_view.dart';
import 'package:smartfit_app_mobile/common/colo_extension.dart';
import 'package:smartfit_app_mobile/modele/convertisseur.dart';
import 'package:smartfit_app_mobile/modele/manager_selected_activity.dart';
import 'package:smartfit_app_mobile/modele/user.dart';
import 'package:smartfit_app_mobile/modele/utile/home_view/data_home_view.dart';
@ -83,11 +84,11 @@ class _StatAtivities extends State<StatAtivities> {
double maxSpeed = context
.watch<User>()
.managerSelectedActivity
.getMaxAltitudeAllActivitySelected();
.getMaxSpeedAllActivitySelected();
double minSpeed = context
.watch<User>()
.managerSelectedActivity
.getMinAltitudeAllActivitySelected();
.getMinSpeedAllActivitySelected();
return Scaffold(
backgroundColor: TColor.white,
@ -106,7 +107,7 @@ class _StatAtivities extends State<StatAtivities> {
height: media.width * 0.05,
),
Text(
"Status d'activité",
"Activity Status",
style: TextStyle(
color: TColor.black,
fontSize: 16,
@ -119,17 +120,17 @@ class _StatAtivities extends State<StatAtivities> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
ContainerStatsActivities(
"$avgBpm BPM", "Moyenne Bpm", Icons.favorite),
"$avgBpm BPM", "Average bpm", Icons.favorite),
SizedBox(
width: media.width * 0.03,
),
ContainerStatsActivities(
"$maxBpm BPM", "Maximum Bpm", Icons.trending_up),
"$maxBpm BPM", "Maximum bpm", Icons.trending_up),
SizedBox(
width: media.width * 0.03,
),
ContainerStatsActivities(
"$minBpm BPM", "Minimum Bpm", Icons.trending_down)
"$minBpm BPM", "Minimum bpm", Icons.trending_down)
],
),
SizedBox(
@ -139,17 +140,23 @@ class _StatAtivities extends State<StatAtivities> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
ContainerStatsActivities(
"${avgSpeed.toStringAsFixed(2)} m/s", "Moyenne vitesse", Icons.bolt),
"${Convertisseur.msIntoKmh(avgSpeed).toStringAsFixed(0)} km/h",
"Average speed",
Icons.bolt),
SizedBox(
width: media.width * 0.03,
),
ContainerStatsActivities(
"$maxSpeed m/s", "Maximum vitesse", Icons.trending_up),
"${Convertisseur.msIntoKmh(maxSpeed).toStringAsFixed(0)} km/h",
"Maximum speed",
Icons.trending_up),
SizedBox(
width: media.width * 0.03,
),
ContainerStatsActivities(
"$minSpeed m/s", "Minimum vitesse", Icons.trending_down)
"${Convertisseur.msIntoKmh(minSpeed).toStringAsFixed(0)} km/h",
"Minimum speed",
Icons.trending_down)
],
),
SizedBox(
@ -159,7 +166,7 @@ class _StatAtivities extends State<StatAtivities> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
ContainerStatsActivities("$avgTemperature °C",
"Moyenne Temperature", Icons.thermostat),
"Average Temperature", Icons.thermostat),
SizedBox(
width: media.width * 0.03,
),
@ -179,17 +186,19 @@ class _StatAtivities extends State<StatAtivities> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
ContainerStatsActivities(
"${avgAltitude.toStringAsFixed(2)} m", "Moyenne Altitude", Icons.landscape),
"${avgAltitude.toStringAsFixed(2)} m",
"Average altitude",
Icons.landscape),
SizedBox(
width: media.width * 0.03,
),
ContainerStatsActivities("$maxAltitude m",
"Maximum Altitude", Icons.trending_up),
"Maximum altitude", Icons.trending_up),
SizedBox(
width: media.width * 0.03,
),
ContainerStatsActivities("$minAltitude m",
"Minimum Altitude", Icons.trending_down)
"Minimum altitude", Icons.trending_down)
],
),
SizedBox(
@ -199,12 +208,12 @@ class _StatAtivities extends State<StatAtivities> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
ContainerStatsActivities("$getTotalDistance m",
"Distance Totale", Icons.double_arrow),
"Total distance", Icons.double_arrow),
SizedBox(
width: media.width * 0.03,
),
ContainerStatsActivities(
"$totalSteps", "Total Pas", Icons.do_not_step_rounded),
ContainerStatsActivities("$totalSteps", "Total steps",
Icons.do_not_step_rounded),
],
),
SizedBox(
@ -214,12 +223,14 @@ class _StatAtivities extends State<StatAtivities> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
ContainerStatsActivities(
"$totalTime s", "Temps Total", Icons.timer),
"${Convertisseur.secondeIntoMinute(totalTime)} min",
"Total time",
Icons.timer),
SizedBox(
width: media.width * 0.03,
),
ContainerStatsActivities("$totalCalories kCal",
"Calories Dépensées", Icons.local_fire_department),
"Burned calories", Icons.local_fire_department),
],
),
SizedBox(

@ -63,117 +63,109 @@ class _WebHomeView extends State<WebHomeView> {
return Scaffold(
backgroundColor: TColor.white,
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
height: media.width * 0.03,
),
const EnteteHomeView(),
SizedBox(
height: media.width * 0.03,
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
height: media.width * 0.03,
),
const EnteteHomeView(),
SizedBox(
height: media.width * 0.03,
),
Column(mainAxisAlignment: MainAxisAlignment.center, children: [
Column(mainAxisAlignment: MainAxisAlignment.center, children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Status d'activité",
style: TextStyle(
color: TColor.black,
fontSize: 16,
fontWeight: FontWeight.w700),
),
SizedBox(
height: media.width * 0.02,
),
Row(mainAxisAlignment: MainAxisAlignment.center,
children: [
BpmByTime(media, data),
SizedBox(
width: media.width * 0.01,
),
LigneContainerStats(
"${minBpm.toString()} BPM",
"${maxBpm.toString()} BPM",
"${avgBpm.toString()} BPM",
"Minimum",
"Maximum",
"Moyenne",
Icons.trending_down,
Icons.trending_up,
Icons.favorite_outline),
]),
]),
Text(
"Activity status",
style: TextStyle(
color: TColor.black,
fontSize: 16,
fontWeight: FontWeight.w700),
),
SizedBox(
height: media.width * 0.05,
height: media.width * 0.02,
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Rythme cardique et vitesse",
style: TextStyle(
color: TColor.black,
fontSize: 16,
fontWeight: FontWeight.w700),
),
SizedBox(
height: media.width * 0.03,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GraphBpmAndSpeedByTime(media, data),
SizedBox(
height: media.width * 0.05,
),
LigneContainerStats(
"${double.parse(maxSpeed.toStringAsFixed(2))} m/s",
"${double.parse(avgSpeed.toStringAsFixed(2))} m/s",
"${avgBpm.toString()} BPM",
"Max vitesse",
"Moy vitesse",
"Moy Bpm",
Icons.trending_up,
Icons.bolt,
Icons.favorite_outline),
SizedBox(
height: media.width * 0.05,
),
]),
]),
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
BpmByTime(media, data),
SizedBox(
width: media.width * 0.01,
),
LigneContainerStats(
"${minBpm.toString()} BPM",
"${maxBpm.toString()} BPM",
"${avgBpm.toString()} BPM",
"Minimum",
"Maximum",
"Average",
Icons.trending_down,
Icons.trending_up,
Icons.favorite_outline),
]),
]),
SizedBox(
height: media.width * 0.02,
height: media.width * 0.05,
),
Text(
"Altitude",
style: TextStyle(
color: TColor.black,
fontSize: 16,
fontWeight: FontWeight.w700),
),
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
GraphAltitudeByTime(media, data),
LigneContainerStats(
"${minAltitude.toInt()} m",
"${maxAltitude.toInt()} m",
"${avgAltitude.toInt()} m",
"Minimum",
"Maximum",
"Moyenne",
Icons.trending_down,
Icons.trending_up,
Icons.favorite_outline),
Column(mainAxisAlignment: MainAxisAlignment.center, children: [
Text(
"Heart rate and speed",
style: TextStyle(
color: TColor.black,
fontSize: 16,
fontWeight: FontWeight.w700),
),
SizedBox(
height: media.width * 0.03,
),
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
GraphBpmAndSpeedByTime(media, data),
SizedBox(
height: media.width * 0.05,
),
LigneContainerStats(
"${double.parse(maxSpeed.toStringAsFixed(2))} m/s",
"${double.parse(avgSpeed.toStringAsFixed(2))} m/s",
"${avgBpm.toString()} BPM",
"Max speed",
"Moy speed",
"Avg bpm",
Icons.trending_up,
Icons.bolt,
Icons.favorite_outline),
SizedBox(
height: media.width * 0.05,
),
]),
]),
],
),
]),
SizedBox(
height: media.width * 0.02,
),
Text(
"Altitude",
style: TextStyle(
color: TColor.black,
fontSize: 16,
fontWeight: FontWeight.w700),
),
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
GraphAltitudeByTime(media, data),
LigneContainerStats(
"${minAltitude.toInt()} m",
"${maxAltitude.toInt()} m",
"${avgAltitude.toInt()} m",
"Minimum",
"Maximum",
"Average",
Icons.trending_down,
Icons.trending_up,
Icons.favorite_outline),
]),
],
),
),
),
);
}
}

@ -82,11 +82,11 @@ class _MobileLoginView extends State<MobileLoginView> {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"Bienvenue",
"Welcome",
style: TextStyle(color: TColor.gray, fontSize: 16),
),
Text(
"Se connecter",
"Log in",
style: TextStyle(
color: TColor.black,
fontSize: 20,
@ -109,7 +109,7 @@ class _MobileLoginView extends State<MobileLoginView> {
),
RoundTextField(
controller: controllerTextPassword,
hitText: "Mot de passe",
hitText: "Password",
icon: "assets/img/lock.svg",
obscureText: _obscureText,
rigtIcon: TextButton(
@ -129,7 +129,7 @@ class _MobileLoginView extends State<MobileLoginView> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Mot de passe oublié ?",
"Password Lost ?",
style: TextStyle(
color: TColor.gray,
fontSize: 15,
@ -146,10 +146,10 @@ class _MobileLoginView extends State<MobileLoginView> {
style: TextStyle(color: TColor.red))),
const Spacer(),
RoundButton(
title: "Se connecter",
title: "Log in",
onPressed: () async {
if (!emailValidate || !passwordValidate) {
_printMsgError("Les champs renseigné ne sont pas valide");
_printMsgError("Fields are not valid!");
return;
}
Tuple2<bool, String> result =
@ -163,7 +163,7 @@ class _MobileLoginView extends State<MobileLoginView> {
if (infoUser.item1 == false) {
//print("Erreur - Impossible de récupéré les données de l'utilisateur");
_printMsgError(
"Impossible de récupéré les données de l'utilisateur - {$infoUser.item2}");
"Can't find user data! - {$infoUser.item2}");
} else {
util.fillUser(context, infoUser.item2, result.item2);
if (!kIsWeb)
@ -175,7 +175,7 @@ class _MobileLoginView extends State<MobileLoginView> {
builder: (context) => const MainTabView()));
}
} else {
_printMsgError("Connexion refuser - ${result.item2}");
_printMsgError("Connection refused - ${result.item2}");
}
}),
SizedBox(
@ -264,14 +264,14 @@ class _MobileLoginView extends State<MobileLoginView> {
mainAxisSize: MainAxisSize.min,
children: [
Text(
"Vous n'avez pas toujours pas de compte ? ",
"Don't have an account yet ?",
style: TextStyle(
color: TColor.black,
fontSize: 14,
),
),
Text(
"Créer un compte",
"Register",
style: TextStyle(
color: TColor.black,
fontSize: 14,

@ -98,11 +98,11 @@ class _MobileSignUpView extends State<MobileSignUpView> {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"Bienvenue,",
"Welcome,",
style: TextStyle(color: TColor.gray, fontSize: 16),
),
Text(
"Créer un compte",
"Register",
style: TextStyle(
color: TColor.black,
fontSize: 20,
@ -112,7 +112,7 @@ class _MobileSignUpView extends State<MobileSignUpView> {
height: media.width * 0.05,
),
RoundTextField(
hitText: "Prénom",
hitText: "Username",
icon: "assets/img/user_text.svg",
controller: controllerUsername,
),
@ -129,7 +129,7 @@ class _MobileSignUpView extends State<MobileSignUpView> {
height: media.width * 0.04,
),
RoundTextField(
hitText: "Mot de passe",
hitText: "Password",
icon: "assets/img/lock.svg",
obscureText: _obscureText,
controller: controllerTextPassword,
@ -164,7 +164,7 @@ class _MobileSignUpView extends State<MobileSignUpView> {
Padding(
padding: const EdgeInsets.only(top: 8),
child: Text(
"En continuant, vous acceptez notre Politique de\nconfidentialité et nos Conditions d'utilisation.",
"I accept to not break the app",
style: TextStyle(color: TColor.gray, fontSize: 10),
),
)
@ -181,12 +181,12 @@ class _MobileSignUpView extends State<MobileSignUpView> {
height: media.width * 0.4,
),
RoundButton(
title: "Créer un compte",
title: "Register",
onPressed: () async {
if (!emailValidate ||
!passwordValidate ||
!usernameValidate) {
_printMsgError("Les champs renseigné ne sont pas valide");
_printMsgError("Fields are not valid!");
return;
}
@ -215,7 +215,7 @@ class _MobileSignUpView extends State<MobileSignUpView> {
color: TColor.gray.withOpacity(0.5),
)),
Text(
" Ou ",
" Or ",
style: TextStyle(color: TColor.black, fontSize: 12),
),
Expanded(
@ -239,14 +239,14 @@ class _MobileSignUpView extends State<MobileSignUpView> {
mainAxisSize: MainAxisSize.min,
children: [
Text(
"Vous avez déjà un compte ? ",
"Already have an account ?",
style: TextStyle(
color: TColor.black,
fontSize: 14,
),
),
Text(
"Se connecter",
"Log in",
style: TextStyle(
color: TColor.black,
fontSize: 14,

@ -86,11 +86,11 @@ class _WebLoginView extends State<WebLoginView> {
height: media.width * 0.03,
),
Text(
"Bienvenue sur SmartFit",
"Welcome on SmartFit",
style: TextStyle(color: TColor.gray, fontSize: 16),
),
Text(
"Se connecter",
"Log in",
style: TextStyle(
color: TColor.black,
fontSize: 20,
@ -110,7 +110,7 @@ class _WebLoginView extends State<WebLoginView> {
),
RoundTextField(
controller: controllerTextPassword,
hitText: "Mot de passe",
hitText: "Password",
icon: "assets/img/lock.svg",
obscureText: _obscureText,
rigtIcon: TextButton(
@ -133,7 +133,7 @@ class _WebLoginView extends State<WebLoginView> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Mot de passe oublié ?",
"Password lost ?",
style: TextStyle(
color: TColor.gray,
fontSize: 15,
@ -150,11 +150,11 @@ class _WebLoginView extends State<WebLoginView> {
style: TextStyle(color: TColor.red))),
const Spacer(),
RoundButton(
title: "Se connecter",
title: "Log in",
onPressed: () async {
// TODO: utiliser la vrai validation
if (!emailValidate || !passwordValidate) {
_printMsgError("Les champs renseigné ne sont pas valide");
_printMsgError("Fields are not valid!");
return;
}
Tuple2<bool, String> result =
@ -168,7 +168,7 @@ class _WebLoginView extends State<WebLoginView> {
if (infoUser.item1 == false) {
//print("Erreur - Impossible de récupéré les données de l'utilisateur");
_printMsgError(
"Impossible de récupéré les données de l'utilisateur - {$infoUser.item2}");
"Can't find user data - {$infoUser.item2}");
} else {
util.fillUser(context, infoUser.item2, result.item2);
if (!kIsWeb)
@ -180,7 +180,7 @@ class _WebLoginView extends State<WebLoginView> {
builder: (context) => const MainTabView()));
}
} else {
_printMsgError("Connexion refuser - ${result.item2}");
_printMsgError("Connection refused - ${result.item2}");
}
}),
SizedBox(
@ -197,14 +197,14 @@ class _WebLoginView extends State<WebLoginView> {
mainAxisSize: MainAxisSize.min,
children: [
Text(
"Vous n'avez pas toujours pas de compte ? ",
"Don't have an account yet ? ",
style: TextStyle(
color: TColor.black,
fontSize: 14,
),
),
Text(
"Créer un compte",
"Register",
style: TextStyle(
color: TColor.black,
fontSize: 14,

@ -100,11 +100,11 @@ class _WebSignUpView extends State<WebSignUpView> {
height: media.width * 0.04,
),
Text(
"Bienvenue,",
"Welcome,",
style: TextStyle(color: TColor.gray, fontSize: 16),
),
Text(
"Créer un compte",
"Register",
style: TextStyle(
color: TColor.black,
fontSize: 20,
@ -114,7 +114,7 @@ class _WebSignUpView extends State<WebSignUpView> {
height: media.width * 0.05,
),
RoundTextField(
hitText: "Prénom",
hitText: "Username",
icon: "assets/img/user_text.svg",
controller: controllerUsername,
),
@ -131,7 +131,7 @@ class _WebSignUpView extends State<WebSignUpView> {
height: media.width * 0.04,
),
RoundTextField(
hitText: "Mot de passe",
hitText: "Password",
icon: "assets/img/lock.svg",
obscureText: _obscureText,
controller: controllerTextPassword,
@ -166,7 +166,7 @@ class _WebSignUpView extends State<WebSignUpView> {
Padding(
padding: const EdgeInsets.only(top: 8),
child: Text(
"En continuant, vous acceptez notre Politique de\nconfidentialité et nos Conditions d'utilisation.",
"Please don't break the app",
style: TextStyle(color: TColor.gray, fontSize: 10),
),
)
@ -180,12 +180,12 @@ class _WebSignUpView extends State<WebSignUpView> {
height: media.width * 0.05,
),
RoundButton(
title: "Créer un compte",
title: "Register",
onPressed: () async {
if (!emailValidate ||
!passwordValidate ||
!usernameValidate) {
_printMsgError("Les champs renseigné ne sont pas valide");
_printMsgError("Fields are not valid!");
return;
}
Tuple2<bool, String> result = await util.createUser(
@ -215,14 +215,14 @@ class _WebSignUpView extends State<WebSignUpView> {
mainAxisSize: MainAxisSize.min,
children: [
Text(
"Vous avez déjà un compte ? ",
"Already have an account ?",
style: TextStyle(
color: TColor.black,
fontSize: 14,
),
),
Text(
"Se connecter",
"Log in",
style: TextStyle(
color: TColor.black,
fontSize: 14,

@ -24,7 +24,7 @@ class _MobileMyMaps extends State<MobileMyMaps> {
return Scaffold(
appBar: AppBar(
title: const Text("Carte Google Map "),
title: const Text("Google Map "),
backgroundColor: TColor.secondaryColor1,
),
body: _getMap());

@ -21,16 +21,16 @@ class _MyMapState extends State<MyMap> {
if (listSelected.isEmpty) {
return ScreenTypeLayout.builder(
mobile: (_) => const NoActivityView("Pas d'activité sélectionnée"),
desktop: (_) => const NoActivityView("Pas d'activité sélectionnée"),
mobile: (_) => const NoActivityView("No activity selected"),
desktop: (_) => const NoActivityView("No activity selected"),
);
}
if (listSelected.length > 1) {
return ScreenTypeLayout.builder(
mobile: (_) => const NoActivityView(
"Une seule activité doit être sélectionnée"),
desktop: (_) => const NoActivityView(
"Une seule activité doit être sélectionnée"),
mobile: (_) =>
const NoActivityView("Only one activity must be selected"),
desktop: (_) =>
const NoActivityView("Only one activity must be selected"),
);
}
return ScreenTypeLayout.builder(

@ -22,7 +22,7 @@ class _MyMapOSM extends State<MyMapOSM> {
return Scaffold(
appBar: AppBar(
title: const Text("Carte Open Street Map "),
title: const Text("Open Street Map "),
backgroundColor: TColor.secondaryColor1,
),
body: FlutterMap(

@ -25,7 +25,7 @@ class _WebMyMaps extends State<WebMyMaps> {
return Scaffold(
appBar: AppBar(
title: const Text("Carte Google Map "),
title: const Text("Google Map "),
backgroundColor: TColor.primaryColor1,
),
body: _getMap());

@ -27,21 +27,21 @@ class _OnBoardingViewState extends State<OnBoardingView> {
List pageArr = [
{
"title": "Atteignez vos objectifs",
"title": "Achieve your goals",
"subtitle":
"Ne vous inquiétez pas si vous avez du mal à déterminer vos objectifs. Grâce à l'analyse concrète de vos performances, nous vous aiderons à les atteindre.",
"Don't worry if you have trouble determining your goals. Thanks to the concrete analysis of your performances, we will help you achieve them.",
"image": "assets/img/on_1.svg"
},
{
"title": "Persévérez",
"subtitle":
"Continuez à persévérer pour atteindre vos objectifs. La douleur n'est que temporaire. Si vous abandonnez maintenant, vous souffrirez éternellement.",
"Continue to persevere to achieve your goals. The pain is only temporary. If you give up now, you will suffer forever.",
"image": "assets/img/on_2.svg"
},
{
"title": "Laissez-nous piloter, mettez simplement votre Suunto",
"title": "Let us pilot, just put on your Suunto",
"subtitle":
"Détendez-vous, nous prenons les commandes en analysant performances et statistiques pour vous aider à atteindre vos objectifs.",
"Relax, we take charge by analyzing performance and statistics to help you achieve your goals.",
"image": "assets/img/on_3.svg"
},
];

@ -41,7 +41,7 @@ class _StartedViewState extends State<StartedView> {
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: RoundButton(
title: "Commencer",
title: "Start",
type: isChangeColor
? RoundButtonType.textGradient
: RoundButtonType.bgGradient,

@ -7,7 +7,11 @@ import 'package:smartfit_app_mobile/common_widget/container/profile/profile_comp
import 'package:smartfit_app_mobile/common_widget/container/profile/profile_entete.dart';
import 'package:smartfit_app_mobile/common_widget/container/profile/profile_info_user.dart';
import 'package:smartfit_app_mobile/common_widget/container/profile/profile_other.dart';
import 'package:smartfit_app_mobile/modele/api/api_wrapper.dart';
import 'package:smartfit_app_mobile/modele/user.dart';
import 'package:smartfit_app_mobile/modele/utile/info_message.dart';
import 'package:smartfit_app_mobile/view/login/signup_view.dart';
import 'package:tuple/tuple.dart';
class ProfileViewAllPlatforme extends StatefulWidget {
final bool offlineSave;
@ -26,7 +30,32 @@ class _ProfileViewAllPlatforme extends State<ProfileViewAllPlatforme> {
@override
Widget build(BuildContext context) {
ApiWrapper wrapper = ApiWrapper();
String username = context.watch<User>().username;
InfoMessage infoManager = InfoMessage();
void logOff() {
// Appel ici pour logOff
/*
if (!result.item1) {
// Affiché erreur
} else {
Navigator.push(context,
MaterialPageRoute(builder: (context) => const LoginView()));
}*/
}
void deleteUser() async {
// Ne marche pas !!
Tuple2 result = await wrapper.deleteUser(
Provider.of<User>(context, listen: false).token, infoManager);
if (!result.item1) {
// Affiché erreur
} else {
Navigator.push(context,
MaterialPageRoute(builder: (context) => const SignUpView()));
}
}
return Scaffold(
appBar: AppBar(
@ -59,8 +88,6 @@ class _ProfileViewAllPlatforme extends State<ProfileViewAllPlatforme> {
const SizedBox(
height: 25,
),
// TODO: Download/Delete (local) all users files on toggle ?
// TODO: Display size of download in Mo
Visibility(
visible: isNative,
child: const Column(
@ -72,7 +99,39 @@ class _ProfileViewAllPlatforme extends State<ProfileViewAllPlatforme> {
)
],
)),
ProfileOther(widget.otherArr)
ProfileOther(widget.otherArr),
const SizedBox(
height: 25,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextButton(
style: TextButton.styleFrom(
backgroundColor: TColor.primaryColor1,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8))),
onPressed: logOff,
child: const Text('Log out',
style: TextStyle(color: Colors.black)),
),
const SizedBox(
width: 25,
),
TextButton(
style: TextButton.styleFrom(
backgroundColor: TColor.primaryColor1,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8))),
onPressed: deleteUser,
child: const Text('Delete your account',
style: TextStyle(color: Colors.black)),
),
],
),
const SizedBox(
height: 25,
)
],
),
),

@ -35,7 +35,7 @@ class ContactUsView extends StatelessWidget {
),
),
title: Text(
"Nous Contacter",
"Contact us",
style: TextStyle(
color: TColor.black,
fontSize: 16,
@ -50,17 +50,16 @@ class ContactUsView extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SizedBox(height: media.width * 0.02),
const Text(
"Vous pouvez nous contacter pour toute question, suggestion ou problème concernant notre application SmartFit.",
"You can contact us with any questions, suggestions or issues regarding our SmartFit app.",
style: TextStyle(
fontSize: 16,
),
),
SizedBox(height: media.width * 0.05),
const Text(
"Adresse e-mail",
"Email address",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
@ -74,7 +73,7 @@ class ContactUsView extends StatelessWidget {
),
SizedBox(height: media.width * 0.02),
const Text(
"Téléphone",
"Phone",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
@ -88,7 +87,7 @@ class ContactUsView extends StatelessWidget {
),
SizedBox(height: media.width * 0.02),
const Text(
"Adresse",
"Address",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
@ -102,14 +101,14 @@ class ContactUsView extends StatelessWidget {
),
SizedBox(height: media.width * 0.02),
const Text(
"Heures de bureau",
"Schedules",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const Text(
"Lundi - Vendredi : 9h00 - 18h00",
"Monday - Friday : 9h00 - 18h00",
style: TextStyle(
fontSize: 16,
),

@ -52,7 +52,7 @@ class _MobileChangeEmailViewState extends State<MobileChangeEmailView> {
),
),
title: Text(
"Changer son e-mail",
"Change your email",
style: TextStyle(
color: TColor.black, fontSize: 16, fontWeight: FontWeight.w700),
),
@ -70,7 +70,7 @@ class _MobileChangeEmailViewState extends State<MobileChangeEmailView> {
Row(
children: [
Text(
"Ancien e-mail : ",
"Current email : ",
style: TextStyle(
color: TColor.black,
fontSize: 16,
@ -95,7 +95,7 @@ class _MobileChangeEmailViewState extends State<MobileChangeEmailView> {
child: Column(
children: [
RoundTextField(
hitText: "Nouveau e-mail",
hitText: "New email",
icon: "assets/img/user_text.svg",
keyboardType: TextInputType.text,
controller: controllerTextEmail,
@ -110,7 +110,7 @@ class _MobileChangeEmailViewState extends State<MobileChangeEmailView> {
height: media.width * 0.01,
),
RoundButton(
title: "Confirmer",
title: "Confirm",
onPressed: () async {
bool res = await api.updateUserInfo(
'email',

@ -56,7 +56,7 @@ class _MobileChangePasswordViewState extends State<MobileChangePasswordView> {
),
),
title: Text(
"Changer son Mot de passe",
"Change your password",
style: TextStyle(
color: TColor.black, fontSize: 16, fontWeight: FontWeight.w700),
),
@ -80,7 +80,7 @@ class _MobileChangePasswordViewState extends State<MobileChangePasswordView> {
children: [
SizedBox(height: media.width * 0.05),
RoundTextField(
hitText: "Ancien mot de passe",
hitText: "Current password",
obscureText: true,
icon: "assets/img/lock.svg",
keyboardType: TextInputType.text,
@ -89,20 +89,20 @@ class _MobileChangePasswordViewState extends State<MobileChangePasswordView> {
SizedBox(height: media.width * 0.07),
RoundTextField(
controller: controllerNewPasswd,
hitText: "Nouveau mot de passe",
hitText: "New password",
icon: "assets/img/lock.svg",
obscureText: true,
),
SizedBox(height: media.width * 0.07),
RoundTextField(
controller: controllerNewPasswd2,
hitText: "Confirmer nouveau mot de passe",
hitText: "Confirm new password",
icon: "assets/img/lock.svg",
obscureText: true,
),
SizedBox(height: media.width * 0.07),
RoundButton(
title: "Confirmer",
title: "Confirm",
onPressed: () async {
Tuple2<bool, String> res = await api.login(
controllerActualPasswd.text,

@ -53,7 +53,7 @@ class _MobileChangeUsernameViewState extends State<MobileChangeUsernameView> {
),
),
title: Text(
"Changer son pseudo",
"Change your username",
style: TextStyle(
color: TColor.black, fontSize: 16, fontWeight: FontWeight.w700),
),
@ -71,7 +71,7 @@ class _MobileChangeUsernameViewState extends State<MobileChangeUsernameView> {
Row(
children: [
Text(
"Ancien pseudo : ",
"Current username: ",
style: TextStyle(
color: TColor.black,
fontSize: 16,
@ -96,7 +96,7 @@ class _MobileChangeUsernameViewState extends State<MobileChangeUsernameView> {
child: Column(
children: [
RoundTextField(
hitText: "Nouveau pseudo",
hitText: "New username",
icon: "assets/img/user_text.svg",
keyboardType: TextInputType.text,
controller: controllerTextUsername,
@ -109,7 +109,7 @@ class _MobileChangeUsernameViewState extends State<MobileChangeUsernameView> {
TextStyle(color: infoManager.messageColor))),
SizedBox(height: media.width * 0.02),
RoundButton(
title: "Confirmer",
title: "Confirm",
onPressed: () async {
bool res = await api.updateUserInfo(
'username',

@ -14,28 +14,24 @@ class _MobileProfileView extends State<MobileProfileView> {
List accountArr = [
{
"image": "assets/img/p_personal.png",
"name": "Changer son pseudo",
"name": "Change your username",
"tag": "1"
},
{
"image": "assets/img/p_personal.png",
"name": "Changer son email",
"name": "Change your email",
"tag": "3"
},
{
"image": "assets/img/p_personal.png",
"name": "Changer son mot de passe",
"name": "Change your password",
"tag": "2"
},
];
List otherArr = [
{"image": "assets/img/p_contact.png", "name": "Nous contacter", "tag": "5"},
{
"image": "assets/img/p_privacy.png",
"name": "Politique de confidentialité",
"tag": "6"
},
{"image": "assets/img/p_contact.png", "name": "Contact us", "tag": "5"},
{"image": "assets/img/p_privacy.png", "name": "Privacy policy", "tag": "6"},
];
@override
Widget build(BuildContext context) {

@ -35,7 +35,7 @@ class PrivacyPolicyView extends StatelessWidget {
),
),
title: Text(
"Politique de confidentialité",
"Privacy policy",
style: TextStyle(
color: TColor.black,
fontSize: 16,
@ -51,7 +51,7 @@ class PrivacyPolicyView extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text(
"Politique de confidentialité de SmartFit",
"SmartFit privacy policy",
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
@ -59,14 +59,14 @@ class PrivacyPolicyView extends StatelessWidget {
),
SizedBox(height: media.width * 0.02),
const Text(
"Cette Politique de confidentialité explique comment SmartFit collecte, utilise, protège et partage vos informations lorsque vous utilisez notre application mobile SmartFit.",
"This Privacy Policy explains how SmartFit collects, uses, protects and shares your information when you use our SmartFit mobile application.",
style: TextStyle(
fontSize: 16,
),
),
SizedBox(height: media.width * 0.05),
const Text(
"Collecte et Utilisation des Informations",
"Collect and usage of your data",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
@ -77,7 +77,7 @@ class PrivacyPolicyView extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"SmartFit recueille des données uniquement dans le but d'analyser et d'afficher des informations relatives à vos activités physiques à partir des données collectées par votre montre connectée. Ces informations peuvent inclure, sans toutefois s'y limiter :",
"SmartFit collects data solely for the purpose of analyzing and displaying information about your physical activities based on data collected by your smartwatch. This information may include, but is not limited to:",
style: TextStyle(
fontSize: 16,
),
@ -87,31 +87,31 @@ class PrivacyPolicyView extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"• Données d'activité physique (comme la fréquence cardiaque, le nombre de pas, la distance parcourue, etc.)",
" Physical activity data (such as heart rate, number of steps, distance traveled, etc.",
style: TextStyle(
fontSize: 16,
),
),
Text(
"Données sur les habitudes de sommeil",
"Sleep data",
style: TextStyle(
fontSize: 16,
),
),
Text(
"Informations de localisation (si l'option est activée par l'utilisateur)",
"GPS data",
style: TextStyle(
fontSize: 16,
),
),
Text(
"Préférences de l'utilisateur concernant les paramètres de l'application",
"Application parameters",
style: TextStyle(
fontSize: 16,
),
),
Text(
"Ces informations sont utilisées pour générer des graphiques, des statistiques et des recommandations personnalisées afin de vous aider dans vos objectifs de remise en forme.",
"This information is used to generate graphs, statistics and personalized recommendations to help you with your fitness goals.",
style: TextStyle(
fontSize: 16,
),
@ -122,7 +122,7 @@ class PrivacyPolicyView extends StatelessWidget {
),
SizedBox(height: media.width * 0.05),
const Text(
"Protection des Informations",
"Information protection",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,

@ -52,7 +52,7 @@ class _WebChangeEmailViewState extends State<WebChangeEmailView> {
),
),
title: Text(
"Changer son email",
"Change you email",
style: TextStyle(
color: TColor.black, fontSize: 16, fontWeight: FontWeight.w700),
),
@ -70,7 +70,7 @@ class _WebChangeEmailViewState extends State<WebChangeEmailView> {
Row(
children: [
Text(
"Email actuel : ",
"Current email: ",
style: TextStyle(
color: TColor.black,
fontSize: 16,
@ -95,7 +95,7 @@ class _WebChangeEmailViewState extends State<WebChangeEmailView> {
child: Column(
children: [
RoundTextField(
hitText: "Nouveau email",
hitText: "New email",
icon: "assets/img/user_text.svg",
keyboardType: TextInputType.text,
controller: controllerTextEmail,
@ -108,7 +108,7 @@ class _WebChangeEmailViewState extends State<WebChangeEmailView> {
TextStyle(color: infoManager.messageColor))),
SizedBox(height: media.width * 0.03),
RoundButton(
title: "Confirmer",
title: "Confirm",
onPressed: () async {
bool res = await apiWrapper.updateUserInfo(
'email',

@ -55,7 +55,7 @@ class _WebChangePasswordViewState extends State<WebChangePasswordView> {
),
),
title: Text(
"Changer son Mot de passe",
"Change password",
style: TextStyle(
color: TColor.black, fontSize: 16, fontWeight: FontWeight.w700),
),
@ -78,7 +78,7 @@ class _WebChangePasswordViewState extends State<WebChangePasswordView> {
child: Column(
children: [
RoundTextField(
hitText: "Ancien mot de passe",
hitText: "Current password",
obscureText: true,
icon: "assets/img/lock.svg",
keyboardType: TextInputType.text,
@ -87,14 +87,14 @@ class _WebChangePasswordViewState extends State<WebChangePasswordView> {
SizedBox(height: media.width * 0.02),
RoundTextField(
controller: controllerNewPasswd,
hitText: "Nouveau mot de passe",
hitText: "New password",
icon: "assets/img/lock.svg",
obscureText: true,
),
SizedBox(height: media.width * 0.02),
RoundTextField(
controller: controllerNewPasswd2,
hitText: "Confirmer nouveau mot de passe",
hitText: "Confirm new password",
icon: "assets/img/lock.svg",
obscureText: true,
),
@ -105,7 +105,7 @@ class _WebChangePasswordViewState extends State<WebChangePasswordView> {
TextStyle(color: infoManager.messageColor))),
SizedBox(height: media.width * 0.04),
RoundButton(
title: "Confirmer",
title: "Confirm",
onPressed: () async {
Tuple2<bool, String> res = await api.login(
controllerActualPasswd.text,

@ -52,7 +52,7 @@ class _WebChangeUsernameViewState extends State<WebChangeUsernameView> {
),
),
title: Text(
"Changer son pseudo",
"Change your username",
style: TextStyle(
color: TColor.black, fontSize: 16, fontWeight: FontWeight.w700),
),
@ -70,7 +70,7 @@ class _WebChangeUsernameViewState extends State<WebChangeUsernameView> {
Row(
children: [
Text(
"Ancien pseudo : ",
"Current username: ",
style: TextStyle(
color: TColor.black,
fontSize: 16,
@ -95,7 +95,7 @@ class _WebChangeUsernameViewState extends State<WebChangeUsernameView> {
child: Column(
children: [
RoundTextField(
hitText: "Nouveau pseudo",
hitText: "New username",
icon: "assets/img/user_text.svg",
keyboardType: TextInputType.text,
controller: controllerTextUsername,
@ -110,7 +110,7 @@ class _WebChangeUsernameViewState extends State<WebChangeUsernameView> {
TextStyle(color: infoManager.messageColor))),
SizedBox(height: media.width * 0.04),
RoundButton(
title: "Confirmer",
title: "Confirm",
onPressed: () async {
bool res = await api.updateUserInfo(
'username',

@ -14,32 +14,24 @@ class _WebProfileView extends State<WebProfileView> {
List accountArr = [
{
"image": "assets/img/p_personal.png",
"name": "Changer son pseudo ",
"name": "Change your username",
"tag": "1"
},
{
"image": "assets/img/p_personal.png",
"name": "Changer son email ",
"name": "Change your email",
"tag": "3"
},
{
"image": "assets/img/p_personal.png",
"name": "Changer son mot de passe ",
"name": "Changer your password",
"tag": "2"
},
];
List otherArr = [
{
"image": "assets/img/p_contact.png",
"name": "Nous contacter !!",
"tag": "5"
},
{
"image": "assets/img/p_privacy.png",
"name": "Politique de confidentialité",
"tag": "6"
},
{"image": "assets/img/p_contact.png", "name": "Contact us !", "tag": "5"},
{"image": "assets/img/p_privacy.png", "name": "Privacy policy", "tag": "6"},
];
@override
Widget build(BuildContext context) {

@ -48,7 +48,7 @@ class _VolumesViews extends State<VolumesView> {
height: media.width * 0.05,
),
Text(
"Derniere semaine : ${date.day}/${date.month}/${date.year} - ${date.subtract(const Duration(days: 7)).day}/${date.subtract(const Duration(days: 7)).month}/${date.subtract(const Duration(days: 7)).year}",
"Last week: ${date.day}/${date.month}/${date.year} - ${date.subtract(const Duration(days: 7)).day}/${date.subtract(const Duration(days: 7)).month}/${date.subtract(const Duration(days: 7)).year}",
style: TextStyle(
color: TColor.black,
fontSize: 16,
@ -62,7 +62,7 @@ class _VolumesViews extends State<VolumesView> {
height: media.width * 0.03,
),
Text(
"Dernier Mois : ${date.day}/${date.month}/${date.year} - ${date.subtract(const Duration(days: 30)).day}/${date.subtract(const Duration(days: 30)).month}/${date.subtract(const Duration(days: 30)).year}",
"Last month: ${date.day}/${date.month}/${date.year} - ${date.subtract(const Duration(days: 30)).day}/${date.subtract(const Duration(days: 30)).month}/${date.subtract(const Duration(days: 30)).year}",
style: TextStyle(
color: TColor.black,
fontSize: 16,
@ -73,7 +73,7 @@ class _VolumesViews extends State<VolumesView> {
height: media.width * 0.03,
),
Text(
"Dernière année : ${date.day}/${date.month}/${date.year} - ${date.subtract(const Duration(days: 366)).day}/${date.subtract(const Duration(days: 366)).month}/${date.subtract(const Duration(days: 366)).year}",
"Last year: ${date.day}/${date.month}/${date.year} - ${date.subtract(const Duration(days: 366)).day}/${date.subtract(const Duration(days: 366)).month}/${date.subtract(const Duration(days: 366)).year}",
style: TextStyle(
color: TColor.black,
fontSize: 16,

@ -5,17 +5,25 @@
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:smartfit_app_mobile/main.dart';
import 'package:smartfit_app_mobile/modele/activity.dart';
import 'package:smartfit_app_mobile/modele/activity_info/activity_info.dart';
import 'package:smartfit_app_mobile/modele/api/api_wrapper.dart';
import 'package:smartfit_app_mobile/modele/api/request_api.dart';
import 'package:smartfit_app_mobile/modele/user.dart';
import 'package:smartfit_app_mobile/modele/utile/signup_user.dart';
import 'package:tuple/tuple.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
testWidgets('Test login', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(const MyApp());
//await tester.pumpWidget(const MyApp());
// Verify that our counter starts at 0.
//expect(find.text('Se connecter'), findsOneWidget);
/*
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
@ -25,6 +33,40 @@ void main() {
// Verify that our counter has incremented.
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
expect(find.text('1'), findsOneWidget);*/
});
// Ce n'est pas possible de faire des test http =>
//To test code that needs an HttpClient, provide your own HttpClient
//implementation to the code under test, so that your test can
//consistently provide a testable response to the code under test.
/*
test("Unit Test -- Create and delete User", () async {
final SignUp utilSignUp = SignUp();
final RequestApi requestApi = RequestApi();
Tuple2 result =
await utilSignUp.createUser("test@gmail.com", "test", "test");
expect(result.item1, true);
Tuple2 delete = await requestApi.deleteUser(result.item2);
expect(delete.item1, true);
});*/
test("User", () {
User user = User();
ActivityOfUser activityOfUser =
ActivityOfUser(ActivityInfo(), "CATEGORIE", "0000", "NAMEFILE");
expect(user.listActivity.length, 0);
user.addActivity(activityOfUser);
expect(user.listActivity.length, 1);
user.removeActivity(activityOfUser);
expect(user.listActivity.length, 0);
});
}

Loading…
Cancel
Save