diff --git a/assets/img/icon_map.svg b/assets/img/icon_map.svg new file mode 100644 index 0000000..bc75d9b --- /dev/null +++ b/assets/img/icon_map.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/lib/common/colo_extension.dart b/lib/common/colo_extension.dart index aeb52ae..2b5dcde 100644 --- a/lib/common/colo_extension.dart +++ b/lib/common/colo_extension.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; class TColor { - static Color get primaryColor1 => Color(0xffF09932); - static Color get primaryColor2 => Color(0xffFFDCB2); + static Color get primaryColor1 => const Color(0xffF09932); + static Color get primaryColor2 => const Color(0xffFFDCB2); - static Color get secondaryColor1 => Color(0xff6131AD); - static Color get secondaryColor2 => Color(0xffD4B9FF); + static Color get secondaryColor1 => const Color(0xff6131AD); + static Color get secondaryColor2 => const Color(0xffD4B9FF); static List get primaryG => [primaryColor2, primaryColor1]; static List get secondaryG => [secondaryColor2, secondaryColor1]; diff --git a/lib/common_widget/container/container_stats.dart b/lib/common_widget/container/container_stats.dart index e4e5548..5c2d657 100644 --- a/lib/common_widget/container/container_stats.dart +++ b/lib/common_widget/container/container_stats.dart @@ -11,11 +11,11 @@ class ContainerStats extends StatelessWidget { final String designation; final IconData icon; - @override + @override Widget build(BuildContext context) { return ScreenTypeLayout.builder( - mobile: (_) => MobileContainerStats(this.value, this.designation, this.icon), - desktop: (_) => WebContainerStats(this.value, this.designation, this.icon), + mobile: (_) => MobileContainerStats(value, designation, icon), + desktop: (_) => WebContainerStats(value, designation, icon), ); } } diff --git a/lib/common_widget/container/container_stats_activities.dart b/lib/common_widget/container/container_stats_activities.dart new file mode 100644 index 0000000..a595673 --- /dev/null +++ b/lib/common_widget/container/container_stats_activities.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; +import 'package:responsive_builder/responsive_builder.dart'; +import 'package:smartfit_app_mobile/common_widget/container/mobile/mobile_container_stats_activities.dart'; +import 'package:smartfit_app_mobile/common_widget/container/web/web_container_stats_activities.dart'; + +class ContainerStatsActivities extends StatelessWidget { + const ContainerStatsActivities( + this.value, + this.designation, + this.icon, { + Key? key, + }) : super(key: key); + + final String value; + final String designation; + final IconData icon; + + @override + Widget build(BuildContext context) { + return ScreenTypeLayout.builder( + mobile: (_) => MobileContainerStatsActivities(value, designation, icon), + desktop: (_) => WebContainerStatsActivities(value, designation, icon), + ); + } +} diff --git a/lib/common_widget/container/list/list_activity.dart b/lib/common_widget/container/list/list_activity_widget.dart similarity index 79% rename from lib/common_widget/container/list/list_activity.dart rename to lib/common_widget/container/list/list_activity_widget.dart index 9ac84f8..9c6c82d 100644 --- a/lib/common_widget/container/list/list_activity.dart +++ b/lib/common_widget/container/list/list_activity_widget.dart @@ -2,22 +2,25 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:smartfit_app_mobile/common_widget/container/workout_row.dart'; import 'package:smartfit_app_mobile/modele/api/api_wrapper.dart'; +import 'package:smartfit_app_mobile/modele/activity.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:smartfit_app_mobile/modele/utile/list_activity/list_activity_utile.dart'; import 'package:tuple/tuple.dart'; -class ListActivity extends StatefulWidget { - const ListActivity({Key? key}) : super(key: key); +class ListActivityWidget extends StatefulWidget { + const ListActivityWidget({Key? key}) : super(key: key); @override - State createState() => _ListActivity(); + State createState() => _ListActivityWidget(); } -class _ListActivity extends State { +class _ListActivityWidget extends State { final ApiWrapper api = ApiWrapper(); final InfoMessage infoManager = InfoMessage(); final ListActivityUtile _utile = ListActivityUtile(); + final ManagerFile managerFile = ManagerFile(); @override Widget build(BuildContext context) { @@ -29,9 +32,16 @@ class _ListActivity extends State { shrinkWrap: true, itemCount: Provider.of(context, listen: true).listActivity.length, itemBuilder: (context, index) { - var activityObj = + ActivityOfUser activityObj = Provider.of(context, listen: true).listActivity[index]; - var activityMap = activityObj.toMap(); + Map activityMap; + // -- Si categorie == marche + if (activityObj.category == managerFile.marche) { + activityMap = activityObj.toMapWalking(); + } else { + // -- Default -- // + activityMap = activityObj.toMapGeneric(); + } return InkWell( onTap: () {}, @@ -60,6 +70,7 @@ class _ListActivity extends State { Provider.of(context, listen: false) .managerSelectedActivity .removeSelectedActivity(activityObj.fileUuid); + setState(() {}); return; } diff --git a/lib/common_widget/container/mobile/mobile_container_stats.dart b/lib/common_widget/container/mobile/mobile_container_stats.dart index 355f624..e8350d8 100644 --- a/lib/common_widget/container/mobile/mobile_container_stats.dart +++ b/lib/common_widget/container/mobile/mobile_container_stats.dart @@ -36,6 +36,7 @@ class MobileContainerStats extends StatelessWidget { icon: icon, iconColor: TColor.white, iconBackground: TColor.secondaryColor1, + sizeIcon: 12, ), Align( alignment: Alignment.bottomLeft, diff --git a/lib/common_widget/container/mobile/mobile_container_stats_activities.dart b/lib/common_widget/container/mobile/mobile_container_stats_activities.dart new file mode 100644 index 0000000..96d7559 --- /dev/null +++ b/lib/common_widget/container/mobile/mobile_container_stats_activities.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:smartfit_app_mobile/common/colo_extension.dart'; +import 'package:smartfit_app_mobile/common_widget/stats.dart'; + +class MobileContainerStatsActivities extends StatelessWidget { + const MobileContainerStatsActivities( + this.value, + this.designation, + this.icon, { + Key? key, + }) : super(key: key); + + final String value; + final String designation; + final IconData icon; + + @override + Widget build(BuildContext context) { + var media = MediaQuery.of(context).size; + + return Container( + height: media.width * 0.4, + width: media.width * 0.27, + padding: const EdgeInsets.all(8), + margin: const EdgeInsets.symmetric(vertical: 5), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(15), + border: Border.all( + color: const Color(0xffe1e1e1), + ), + boxShadow: const [ + BoxShadow( + color: Colors.black12, + offset: Offset(3, 3), + blurRadius: 3, + ), + ], + ), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.center, // Centrer horizontalement + mainAxisAlignment: MainAxisAlignment.center, + + children: [ + StatIcon( + icon: icon, + iconColor: TColor.white, + iconBackground: TColor.secondaryColor1, + sizeIcon: 30.0, + ), + const SizedBox(height: 20), // Espacement entre l'icône et le texte + Text( + designation, + style: const TextStyle(fontSize: 12), + textAlign: TextAlign.center, + ), + Text( + value, + style: const TextStyle( + fontSize: 17, + fontWeight: FontWeight.w800, + ), + textAlign: TextAlign.center, + ), + ], + ), + ); + } +} diff --git a/lib/common_widget/container/mobile/mobile_ligne_container_stats.dart b/lib/common_widget/container/mobile/mobile_ligne_container_stats.dart index 157016a..2228112 100644 --- a/lib/common_widget/container/mobile/mobile_ligne_container_stats.dart +++ b/lib/common_widget/container/mobile/mobile_ligne_container_stats.dart @@ -3,9 +3,16 @@ import 'package:smartfit_app_mobile/common/colo_extension.dart'; import 'package:smartfit_app_mobile/common_widget/container/container_stats.dart'; class MobileLigneContainerStats extends StatelessWidget { - const MobileLigneContainerStats(this.value1, this.value2, this.value3, - this.designation1, this.designation2, this.designation3, - this.icon1, this.icon2, this.icon3, + const MobileLigneContainerStats( + this.value1, + this.value2, + this.value3, + this.designation1, + this.designation2, + this.designation3, + this.icon1, + this.icon2, + this.icon3, {Key? key}) : super(key: key); @@ -21,13 +28,11 @@ class MobileLigneContainerStats extends StatelessWidget { final IconData icon2; final IconData icon3; - @override Widget build(BuildContext context) { return Column( children: [ - Divider(height: 30), - + const Divider(height: 30), Padding( padding: const EdgeInsets.symmetric(horizontal: 30), child: Row( @@ -39,7 +44,7 @@ class MobileLigneContainerStats extends StatelessWidget { fontWeight: FontWeight.w800, ), ), - SizedBox(width: 8), + const SizedBox(width: 8), Icon( Icons.pie_chart_rounded, size: 15, @@ -48,24 +53,21 @@ class MobileLigneContainerStats extends StatelessWidget { ], ), ), - - SizedBox(height: 20), - SingleChildScrollView( - scrollDirection: Axis.horizontal, - child:Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ContainerStats(value1, designation1, icon1), - const SizedBox(width: 20), - ContainerStats(value2, designation2, icon2), - const SizedBox(width: 20), - ContainerStats(value3, designation3, icon3), - ],) - ), - Divider(height: 30), + const SizedBox(height: 20), + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ContainerStats(value1, designation1, icon1), + const SizedBox(width: 20), + ContainerStats(value2, designation2, icon2), + const SizedBox(width: 20), + ContainerStats(value3, designation3, icon3), + ], + )), + const Divider(height: 30), ], ); - - } } diff --git a/lib/common_widget/container/profile/profile_info_user.dart b/lib/common_widget/container/profile/profile_info_user.dart index 05f7f9a..b404b30 100644 --- a/lib/common_widget/container/profile/profile_info_user.dart +++ b/lib/common_widget/container/profile/profile_info_user.dart @@ -1,35 +1,48 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import 'package:smartfit_app_mobile/common_widget/title_subtitle_cell.dart'; +import 'package:smartfit_app_mobile/modele/user.dart'; class ProfileInfoUser extends StatelessWidget { const ProfileInfoUser({super.key}); @override Widget build(BuildContext context) { - return const Row( + return Row( children: [ Expanded( child: TitleSubtitleCell( - title: "??? cm", - subtitle: "Taille", + title: context.watch().listActivity.length.toString(), + subtitle: "Nombre d'activité", ), ), - SizedBox( + const SizedBox( width: 15, ), Expanded( child: TitleSubtitleCell( - title: "?? kg", - subtitle: "Poids", + title: context.watch().getTotalTimeAllActivity().toString(), + subtitle: "Temps en activité", ), ), - SizedBox( + const SizedBox( width: 15, ), Expanded( child: TitleSubtitleCell( - title: "?? ans", - subtitle: "Age", + title: + "${context.watch().getTotalDenivelePositif().toString()} + m", + subtitle: "Total dénivelé positif", + ), + ), + const SizedBox( + width: 15, + ), + Expanded( + child: TitleSubtitleCell( + title: + "${context.watch().getTotalDeniveleNegatif().toString()} - m", + subtitle: "Total dénivelé négatif", ), ), ], diff --git a/lib/common_widget/container/web/web_container_stats.dart b/lib/common_widget/container/web/web_container_stats.dart index 89aa241..638940b 100644 --- a/lib/common_widget/container/web/web_container_stats.dart +++ b/lib/common_widget/container/web/web_container_stats.dart @@ -13,7 +13,7 @@ class WebContainerStats extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - height: 70, + height: 80, width: 70, padding: const EdgeInsets.all(8), margin: const EdgeInsets.symmetric(vertical: 5), @@ -36,6 +36,7 @@ class WebContainerStats extends StatelessWidget { icon: icon, iconColor: TColor.white, iconBackground: TColor.secondaryColor1, + sizeIcon: 8.0, ), Align( alignment: Alignment.bottomLeft, diff --git a/lib/common_widget/container/web/web_container_stats_activities.dart b/lib/common_widget/container/web/web_container_stats_activities.dart new file mode 100644 index 0000000..e771c6c --- /dev/null +++ b/lib/common_widget/container/web/web_container_stats_activities.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:smartfit_app_mobile/common/colo_extension.dart'; +import 'package:smartfit_app_mobile/common_widget/stats.dart'; + +class WebContainerStatsActivities extends StatelessWidget { + const WebContainerStatsActivities( + this.value, + this.designation, + this.icon, { + Key? key, + }) : super(key: key); + + final String value; + final String designation; + final IconData icon; + + @override + Widget build(BuildContext context) { + var media = MediaQuery.of(context).size; + + return Container( + height: media.width * 0.2, + width: media.width * 0.3, + padding: const EdgeInsets.all(8), + margin: const EdgeInsets.symmetric(vertical: 5), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(15), + border: Border.all( + color: const Color(0xffe1e1e1), + ), + boxShadow: const [ + BoxShadow( + color: Colors.black12, + offset: Offset(3, 3), + blurRadius: 3, + ), + ], + ), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.center, // Centrer horizontalement + mainAxisAlignment: MainAxisAlignment.center, + + children: [ + StatIcon( + icon: icon, + iconColor: TColor.white, + iconBackground: TColor.secondaryColor1, + sizeIcon: 40.0, + ), + const SizedBox(height: 40), // Espacement entre l'icône et le texte + Text( + designation, + style: const TextStyle(fontSize: 15), + textAlign: TextAlign.center, + ), + Text( + value, + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.w800, + ), + textAlign: TextAlign.center, + ), + ], + ), + ); + } +} diff --git a/lib/common_widget/container/web/web_ligne_container_stats.dart b/lib/common_widget/container/web/web_ligne_container_stats.dart index c0e9836..3558681 100644 --- a/lib/common_widget/container/web/web_ligne_container_stats.dart +++ b/lib/common_widget/container/web/web_ligne_container_stats.dart @@ -3,9 +3,16 @@ import 'package:smartfit_app_mobile/common/colo_extension.dart'; import 'package:smartfit_app_mobile/common_widget/container/container_stats.dart'; class WebLigneContainerStats extends StatelessWidget { - const WebLigneContainerStats(this.value1, this.value2, this.value3, - this.designation1, this.designation2, this.designation3, - this.icon1, this.icon2, this.icon3, + const WebLigneContainerStats( + this.value1, + this.value2, + this.value3, + this.designation1, + this.designation2, + this.designation3, + this.icon1, + this.icon2, + this.icon3, {Key? key}) : super(key: key); @@ -21,11 +28,10 @@ class WebLigneContainerStats extends StatelessWidget { final IconData icon2; final IconData icon3; - @override Widget build(BuildContext context) { return Column( - children: [ + children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 5), child: Column( @@ -37,7 +43,7 @@ class WebLigneContainerStats extends StatelessWidget { fontWeight: FontWeight.w800, ), ), - SizedBox(width: 8), + const SizedBox(width: 8), Icon( Icons.pie_chart_rounded, size: 15, @@ -46,24 +52,21 @@ class WebLigneContainerStats extends StatelessWidget { ], ), ), - - SizedBox(height: 20), - SingleChildScrollView( - scrollDirection: Axis.horizontal, - child:Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ContainerStats(value1, designation1, icon1), - const SizedBox(width: 20), - ContainerStats(value2, designation2, icon2), - const SizedBox(width: 20), - ContainerStats(value3, designation3, icon3), - ],) - ), - Divider(height: 30), + const SizedBox(height: 20), + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ContainerStats(value1, designation1, icon1), + const SizedBox(width: 20), + ContainerStats(value2, designation2, icon2), + const SizedBox(width: 20), + ContainerStats(value3, designation3, icon3), + ], + )), + const Divider(height: 30), ], ); - - } } diff --git a/lib/common_widget/container/workout_row.dart b/lib/common_widget/container/workout_row.dart index 9137ee6..d4d499a 100644 --- a/lib/common_widget/container/workout_row.dart +++ b/lib/common_widget/container/workout_row.dart @@ -25,7 +25,7 @@ class WorkoutRow extends StatelessWidget { decoration: BoxDecoration( border: Border.all( color: isSelected - ? Color.fromARGB(255, 144, 252, 148) + ? const Color.fromARGB(255, 144, 252, 148) : Colors.transparent, width: 2.0, ), @@ -33,12 +33,12 @@ class WorkoutRow extends StatelessWidget { ), child: Material( color: isSelected - ? Color.fromARGB(255, 240, 255, 240) + ? const Color.fromARGB(255, 240, 255, 240) : Colors.transparent, child: InkWell( borderRadius: BorderRadius.circular(10), // Utiliser le même borderRadius - splashColor: Color.fromARGB(255, 42, 94, 44) + splashColor: const Color.fromARGB(255, 42, 94, 44) .withOpacity(0.3), // Couleur du fond au survol onTap: onClick, child: Padding( @@ -60,14 +60,28 @@ class WorkoutRow extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - wObj["categorie"].toString(), + "Type : ${wObj["categorie"].toString()}", style: TextStyle( color: TColor.black, fontSize: 12, ), ), Text( - wObj["date"].toString(), + "Date : ${wObj["date"].toString()}", + style: TextStyle( + color: TColor.black, + fontSize: 12, + ), + ), + Text( + "Temps : ${wObj["time"].toString()}", + style: TextStyle( + color: TColor.black, + fontSize: 12, + ), + ), + Text( + "Dénivelé positif : ${wObj["DenivelePositif"].toString()}", style: TextStyle( color: TColor.black, fontSize: 12, diff --git a/lib/common_widget/graph/bpm_by_time.dart b/lib/common_widget/graph/bpm_by_time.dart index 968d0e1..bf0a09f 100644 --- a/lib/common_widget/graph/bpm_by_time.dart +++ b/lib/common_widget/graph/bpm_by_time.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:responsive_builder/responsive_builder.dart'; +import 'package:smartfit_app_mobile/common_widget/graph/data_for_graph/func_bpm_by_time.dart'; import 'package:smartfit_app_mobile/common_widget/graph/mobile/mobile_bpm_by_time.dart'; import 'package:smartfit_app_mobile/common_widget/graph/web/web_bpm_by_time.dart'; import 'package:smartfit_app_mobile/modele/utile/home_view/data_home_view.dart'; @@ -8,19 +9,20 @@ class BpmByTime extends StatefulWidget { final Size media; final DataHomeView data; - const BpmByTime(this.media, this.data, {Key? key}) - : super(key: key); + const BpmByTime(this.media, this.data, {Key? key}) : super(key: key); @override State createState() => _BpmByTime(); } class _BpmByTime extends State { - @override + @override Widget build(BuildContext context) { + final FuncBpmByTime funcBpm = FuncBpmByTime(widget.data); + return ScreenTypeLayout.builder( - mobile: (_) => MobileBpmByTime(widget.media, widget.data), - desktop: (_) => WebBpmByTime(widget.media, widget.data), + mobile: (_) => MobileBpmByTime(widget.media, widget.data), + desktop: (_) => WebBpmByTime(widget.media, widget.data, funcBpm), ); } -} \ No newline at end of file +} diff --git a/lib/common_widget/graph/data_for_graph/func_bpm_and_speed_by_time.dart b/lib/common_widget/graph/data_for_graph/func_bpm_and_speed_by_time.dart index ae9f413..92fafdb 100644 --- a/lib/common_widget/graph/data_for_graph/func_bpm_and_speed_by_time.dart +++ b/lib/common_widget/graph/data_for_graph/func_bpm_and_speed_by_time.dart @@ -17,6 +17,19 @@ class FuncBpmAndSpeedByTime { reservedSize: 40, ); + SideTitles get leftTitles => SideTitles( + getTitlesWidget: leftTitleWidgets, + showTitles: true, + interval: 20, + reservedSize: 40, + ); + SideTitles get bottomTitles => SideTitles( + getTitlesWidget: bottomTitleWidgets, + showTitles: true, + interval: 20, + reservedSize: 20, + ); + late final lineBarsData = [ LineChartBarData( spots: data.bpmSecondes, @@ -38,25 +51,95 @@ class FuncBpmAndSpeedByTime { late final tooltipsOnBar = lineBarsData[0]; Widget rightTitleWidgets(double value, TitleMeta meta) { + double interval = data.maxBPM / 5; + String text; + switch (value.toInt()) { + case 0: + text = '0 BPM'; + break; + case 20: + text = "${(interval).toStringAsFixed(2)} BPM"; + break; + case 40: + text = "${(interval * 2).toStringAsFixed(2)} BPM"; + break; + case 60: + text = "${(interval * 3).toStringAsFixed(2)} BPM"; + break; + case 80: + text = "${(interval * 4).toStringAsFixed(2)} BPM"; + break; + case 100: + text = "${interval * 5} BPM"; + break; + default: + return Container(); + } + + return Text(text, + style: TextStyle( + color: TColor.gray, + fontSize: 12, + ), + textAlign: TextAlign.center); + } + + Widget bottomTitleWidgets(double value, TitleMeta meta) { + double interval = data.time / 5; + String text; + switch (value) { + case 0: + text = '0 s'; + break; + case 20: + text = "${(interval).toStringAsFixed(2)} s"; + break; + case 40: + text = "${(interval * 2).toStringAsFixed(2)} s"; + break; + case 60: + text = "${(interval * 3).toStringAsFixed(2)} s"; + break; + case 80: + text = "${(interval * 4).toStringAsFixed(2)} s"; + break; + case 100: + text = "${(interval * 5).toStringAsFixed(2)} s"; + break; + default: + return Container(); + } + + return Text(text, + style: TextStyle( + color: TColor.gray, + fontSize: 12, + ), + textAlign: TextAlign.center); + } + + Widget leftTitleWidgets(double value, TitleMeta meta) { + double interval = data.maxSpeed / 5; + String text; switch (value.toInt()) { case 0: - text = '0%'; + text = '0 m/s'; break; case 20: - text = '20%'; + text = "${(interval / 5).toStringAsFixed(2)} m/s"; break; case 40: - text = '40%'; + text = "${(interval * 2).toStringAsFixed(2)} m/s"; break; case 60: - text = '60%'; + text = "${(interval * 3).toStringAsFixed(2)} m/s"; break; case 80: - text = '80%'; + text = "${(interval * 4).toStringAsFixed(2)} m/s"; break; case 100: - text = '100%'; + text = "${(interval * 5).toStringAsFixed(2)} m/s"; break; default: return Container(); diff --git a/lib/common_widget/graph/data_for_graph/func_bpm_by_time.dart b/lib/common_widget/graph/data_for_graph/func_bpm_by_time.dart new file mode 100644 index 0000000..afbed1b --- /dev/null +++ b/lib/common_widget/graph/data_for_graph/func_bpm_by_time.dart @@ -0,0 +1,95 @@ +import 'package:fl_chart/fl_chart.dart'; +import 'package:flutter/material.dart'; +import 'package:smartfit_app_mobile/common/colo_extension.dart'; +import 'package:smartfit_app_mobile/modele/utile/home_view/data_home_view.dart'; + +class FuncBpmByTime { + final DataHomeView data; + + FuncBpmByTime(this.data); + + SideTitles get rightTitles => SideTitles( + getTitlesWidget: rightTitleWidgets, + showTitles: true, + interval: 20, + reservedSize: 40, + ); + + SideTitles get bottomTitles => SideTitles( + getTitlesWidget: bottomTitleWidgets, + showTitles: true, + interval: 20, + reservedSize: 20, + ); + + Widget rightTitleWidgets(double value, TitleMeta meta) { + int minBpm = data.minBPM; + int maxBpm = data.maxBPM; + double interval = (maxBpm - minBpm) / 5; + + String text; + switch (value.toInt()) { + case 0: + text = "${(minBpm).toStringAsFixed(2)} BPM"; + break; + case 20: + text = "${(minBpm + interval).toStringAsFixed(2)} BPM"; + break; + case 40: + text = "${(minBpm + interval * 2).toStringAsFixed(2)} BPM"; + break; + case 60: + text = "${(minBpm + interval * 3).toStringAsFixed(2)} BPM"; + break; + case 80: + text = "${(minBpm + interval * 4).toStringAsFixed(2)} BPM"; + break; + case 100: + text = "${(maxBpm).toStringAsFixed(2)} BPM"; + break; + default: + return Container(); + } + + return Text(text, + style: TextStyle( + color: TColor.gray, + fontSize: 12, + ), + textAlign: TextAlign.center); + } + + Widget bottomTitleWidgets(double value, TitleMeta meta) { + double interval = data.time / 5; + String text; + switch (value) { + case 0: + text = '0 s'; + break; + case 20: + text = "${(interval).toStringAsFixed(2)} s"; + break; + case 40: + text = "${(interval * 2).toStringAsFixed(2)} s"; + break; + case 60: + text = "${(interval * 3).toStringAsFixed(2)} s"; + break; + case 80: + text = "${(interval * 4).toStringAsFixed(2)} s"; + break; + case 100: + text = "${(interval * 5).toStringAsFixed(2)} s"; + break; + default: + return Container(); + } + + return Text(text, + style: TextStyle( + color: TColor.gray, + fontSize: 12, + ), + textAlign: TextAlign.center); + } +} diff --git a/lib/common_widget/graph/graph.dart b/lib/common_widget/graph/graph.dart index ee34f2f..b09aca3 100644 --- a/lib/common_widget/graph/graph.dart +++ b/lib/common_widget/graph/graph.dart @@ -127,7 +127,7 @@ class GraphPainter extends CustomPainter { ..style = PaintingStyle.fill; Paint dotOutlinePaint = Paint() - ..color = ui.Color.fromARGB(255, 236, 236, 236).withAlpha(200) + ..color = const ui.Color.fromARGB(255, 236, 236, 236).withAlpha(200) ..strokeWidth = 8; Paint dotCenter = Paint() diff --git a/lib/common_widget/graph/mobile/mobile_altitude_by_time.dart b/lib/common_widget/graph/mobile/mobile_altitude_by_time.dart index da7691c..e37bee9 100644 --- a/lib/common_widget/graph/mobile/mobile_altitude_by_time.dart +++ b/lib/common_widget/graph/mobile/mobile_altitude_by_time.dart @@ -20,10 +20,22 @@ class MobileGraphAltitudeByTime extends StatefulWidget { class _MobileGraphAltitudeByTime extends State { @override Widget build(BuildContext context) { - final double maxY = - context.watch().managerSelectedActivity.getMaxAltitude() + 2; - final double minY = - context.watch().managerSelectedActivity.getMinAltitude() - 2; + final double maxY = context + .watch() + .managerSelectedActivity + .activitySelected + .first + .activityInfo + .altitudeMax + + 2; + final double minY = context + .watch() + .managerSelectedActivity + .activitySelected + .first + .activityInfo + .altitudeMin - + 2; final lineBarsData = [ LineChartBarData( @@ -57,7 +69,15 @@ class _MobileGraphAltitudeByTime extends State { titlesData: FlTitlesData( leftTitles: const AxisTitles(), topTitles: const AxisTitles(), - bottomTitles: const AxisTitles(), + bottomTitles: AxisTitles( + sideTitles: SideTitles( + reservedSize: 20, + showTitles: true, + getTitlesWidget: (value, meta) { + return Text( + "${double.parse((value / 10).toStringAsFixed(2))}s"); + }, + )), rightTitles: AxisTitles( sideTitles: SideTitles( reservedSize: 60, diff --git a/lib/common_widget/graph/mobile/mobile_bpm_and_speed_by_time.dart b/lib/common_widget/graph/mobile/mobile_bpm_and_speed_by_time.dart index a7ad501..3dc8afc 100644 --- a/lib/common_widget/graph/mobile/mobile_bpm_and_speed_by_time.dart +++ b/lib/common_widget/graph/mobile/mobile_bpm_and_speed_by_time.dart @@ -94,7 +94,7 @@ class _MobileGraphBpmAndSpeedByTime getTooltipItems: (List lineBarsSpot) { return lineBarsSpot.map((lineBarSpot) { return LineTooltipItem( - "Seconde ${lineBarSpot.x.toInt()} ", + "Seconde ${lineBarSpot.x.toInt() / 10} ", const TextStyle( color: Colors.white, fontSize: 10, @@ -110,9 +110,19 @@ class _MobileGraphBpmAndSpeedByTime maxY: 110, titlesData: FlTitlesData( show: true, - leftTitles: const AxisTitles(), + leftTitles: AxisTitles( + sideTitles: widget.func.rightTitles, + ), topTitles: const AxisTitles(), - bottomTitles: const AxisTitles(), + bottomTitles: AxisTitles( + sideTitles: SideTitles( + reservedSize: 20, + showTitles: true, + getTitlesWidget: (value, meta) { + return Text( + "${double.parse((value / 10).toStringAsFixed(2))}s"); + }, + )), rightTitles: AxisTitles( sideTitles: SideTitles( reservedSize: 70, diff --git a/lib/common_widget/graph/mobile/mobile_bpm_by_time.dart b/lib/common_widget/graph/mobile/mobile_bpm_by_time.dart index d14962c..9190259 100644 --- a/lib/common_widget/graph/mobile/mobile_bpm_by_time.dart +++ b/lib/common_widget/graph/mobile/mobile_bpm_by_time.dart @@ -61,7 +61,7 @@ class _MobileBpmByTime extends State { reservedSize: 20, showTitles: true, getTitlesWidget: (value, meta) { - return Text("${double.parse(value.toStringAsFixed(2))}s"); + return Text("${double.parse((value/10).toStringAsFixed(2))}s"); }, )), rightTitles: AxisTitles( diff --git a/lib/common_widget/graph/web/web_altitude_by_time.dart b/lib/common_widget/graph/web/web_altitude_by_time.dart index 0368c88..8868894 100644 --- a/lib/common_widget/graph/web/web_altitude_by_time.dart +++ b/lib/common_widget/graph/web/web_altitude_by_time.dart @@ -19,10 +19,22 @@ class WebGraphAltitudeByTime extends StatefulWidget { class _WebGraphAltitudeByTime extends State { @override Widget build(BuildContext context) { - final double maxY = - context.watch().managerSelectedActivity.getMaxAltitude() + 2; - final double minY = - context.watch().managerSelectedActivity.getMinAltitude() - 2; + final double maxY = context + .watch() + .managerSelectedActivity + .activitySelected + .first + .activityInfo + .altitudeMax + + 2; + final double minY = context + .watch() + .managerSelectedActivity + .activitySelected + .first + .activityInfo + .altitudeMin - + 2; final lineBarsData = [ LineChartBarData( diff --git a/lib/common_widget/graph/web/web_bpm_and_speed_by_time.dart b/lib/common_widget/graph/web/web_bpm_and_speed_by_time.dart index 1b9203d..ec8ad66 100644 --- a/lib/common_widget/graph/web/web_bpm_and_speed_by_time.dart +++ b/lib/common_widget/graph/web/web_bpm_and_speed_by_time.dart @@ -24,10 +24,9 @@ class _WebGraphBpmAndSpeedByTime extends State { Widget build(BuildContext context) { final double maxY = widget.data.maxBPM + 2; final double minY = widget.data.minBPM - 2; - final double maxX = + final double maxX = widget.data.bpmSecondes[widget.data.bpmSecondes.length - 1].x; const double minX = 0.0; - return Container( padding: const EdgeInsets.only(left: 15), height: widget.media.width * 0.20, @@ -104,37 +103,25 @@ class _WebGraphBpmAndSpeedByTime extends State { ), ), lineBarsData: widget.func.lineBarsData1, - minY: 0, + minY: -10, maxY: 110, titlesData: FlTitlesData( show: true, leftTitles: AxisTitles( - sideTitles: widget.func.rightTitles, + sideTitles: widget.func.leftTitles, ), topTitles: const AxisTitles(), - bottomTitles: AxisTitles( - sideTitles: SideTitles( - reservedSize: 20, - showTitles: true, - getTitlesWidget: (value, meta) { - return Text( - "${double.parse((value / 10).toStringAsFixed(2))}s"); - }, - )), + bottomTitles:AxisTitles( + sideTitles: widget.func.bottomTitles, + ), rightTitles: AxisTitles( - sideTitles: SideTitles( - reservedSize: 70, - showTitles: true, - getTitlesWidget: (value, meta) { - return Text( - "${double.parse(value.toStringAsFixed(2))} BPM"); - }, - ))), + sideTitles: widget.func.rightTitles, + ),), gridData: FlGridData( drawVerticalLine: true, drawHorizontalLine: true, horizontalInterval: (maxY - minY) / 5, - verticalInterval: (maxX - minX) / 4, + verticalInterval: (maxX - minX) / 5 , getDrawingHorizontalLine: (value) { return FlLine( color: TColor.gray.withOpacity(0.15), diff --git a/lib/common_widget/graph/web/web_bpm_by_time.dart b/lib/common_widget/graph/web/web_bpm_by_time.dart index c9d202b..bf9a67f 100644 --- a/lib/common_widget/graph/web/web_bpm_by_time.dart +++ b/lib/common_widget/graph/web/web_bpm_by_time.dart @@ -1,15 +1,16 @@ import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; import 'package:smartfit_app_mobile/common/colo_extension.dart'; -import 'package:smartfit_app_mobile/modele/user.dart'; +import 'package:smartfit_app_mobile/common_widget/graph/data_for_graph/func_bpm_by_time.dart'; import 'package:smartfit_app_mobile/modele/utile/home_view/data_home_view.dart'; class WebBpmByTime extends StatefulWidget { final Size media; final DataHomeView data; + final FuncBpmByTime func; - const WebBpmByTime(this.media, this.data, {Key? key}) : super(key: key); + const WebBpmByTime(this.media, this.data, this.func, {Key? key}) + : super(key: key); @override State createState() => _WebBpmByTime(); @@ -18,13 +19,6 @@ class WebBpmByTime extends StatefulWidget { class _WebBpmByTime extends State { @override Widget build(BuildContext context) { - final double maxY = - context.watch().managerSelectedActivity.getMaxBpm() + 2; - final double minY = - context.watch().managerSelectedActivity.getMinBpm() - 2; - final double maxX = - widget.data.bpmSecondes[widget.data.bpmSecondes.length - 1].x; - const double minX = 0.0; final lineBarsData = [ LineChartBarData( spots: widget.data.bpmSecondes, @@ -41,16 +35,11 @@ class _WebBpmByTime extends State { width: widget.media.width * 0.35, child: LineChart(LineChartData( lineBarsData: lineBarsData, + minY: widget.data.minBPM.toDouble() * 0.95, borderData: FlBorderData(show: false), - maxY: maxY, - minY: minY, - maxX: maxX, - minX: minX, gridData: FlGridData( drawVerticalLine: true, drawHorizontalLine: true, - horizontalInterval: (maxY - minY) / 5, - verticalInterval: (maxX - minX) / 4, getDrawingHorizontalLine: (value) { return FlLine( color: TColor.gray.withOpacity(0.15), @@ -61,19 +50,19 @@ class _WebBpmByTime extends State { leftTitles: const AxisTitles(), topTitles: const AxisTitles(), bottomTitles: AxisTitles( - sideTitles: SideTitles( - reservedSize: 20, - showTitles: true, - getTitlesWidget: (value, meta) { - return Text("${double.parse(value.toStringAsFixed(2))}s"); - }, - )), + sideTitles: widget.func.bottomTitles, + ), rightTitles: AxisTitles( sideTitles: SideTitles( reservedSize: 70, showTitles: true, getTitlesWidget: (value, meta) { - return Text("${double.parse(value.toStringAsFixed(2))} BPM"); + return Text("${double.parse(value.toStringAsFixed(2))} BPM", + style: TextStyle( + color: TColor.gray, + fontSize: 12, + ), + textAlign: TextAlign.center); }, )), )))); diff --git a/lib/common_widget/info.dart b/lib/common_widget/info.dart index f28d9f1..c75c775 100644 --- a/lib/common_widget/info.dart +++ b/lib/common_widget/info.dart @@ -9,7 +9,7 @@ class Info extends StatelessWidget { Widget build(BuildContext context) { String distance = Provider.of(context, listen: false) .managerSelectedActivity - .getTotalDistance() + .getDistanceAllActivitySelected() .toString(); return Row( diff --git a/lib/common_widget/other/entete_home_view.dart b/lib/common_widget/other/entete_home_view.dart index b5ab1e8..1797faa 100644 --- a/lib/common_widget/other/entete_home_view.dart +++ b/lib/common_widget/other/entete_home_view.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import 'package:smartfit_app_mobile/common/colo_extension.dart'; +import 'package:smartfit_app_mobile/modele/user.dart'; import 'package:smartfit_app_mobile/view/home/notification_view.dart'; class EnteteHomeView extends StatelessWidget { @@ -18,7 +20,7 @@ class EnteteHomeView extends StatelessWidget { style: TextStyle(color: TColor.gray, fontSize: 12), ), Text( - "Benjelloun Othmane", + context.watch().username, style: TextStyle( color: TColor.black, fontSize: 20, diff --git a/lib/common_widget/stats.dart b/lib/common_widget/stats.dart index dc6f72c..affbc98 100644 --- a/lib/common_widget/stats.dart +++ b/lib/common_widget/stats.dart @@ -10,15 +10,18 @@ class Stats extends StatelessWidget { Widget build(BuildContext context) { String calories = Provider.of(context, listen: false) .managerSelectedActivity - .getCalorie() + .getCalorieAllActivitySelected() .toString(); String heartrate = Provider.of(context, listen: false) .managerSelectedActivity - .getAvgBpm() + .activitySelected + .first + .activityInfo + .bpmAvg .toString(); String time = Provider.of(context, listen: false) .managerSelectedActivity - .getTotalTime() + .getTimeAllActivitySelected() .toString(); return Column( children: [ @@ -127,6 +130,7 @@ class InfoStat extends StatelessWidget { icon: icon, iconColor: iconColor, iconBackground: iconBackground, + sizeIcon: 8.0, ), Align( alignment: Alignment.bottomLeft, @@ -160,11 +164,13 @@ class StatIcon extends StatelessWidget { required this.icon, required this.iconColor, required this.iconBackground, + required this.sizeIcon, }) : super(key: key); final IconData icon; final Color iconColor; final Color iconBackground; + final double? sizeIcon; @override Widget build(BuildContext context) { @@ -174,7 +180,7 @@ class StatIcon extends StatelessWidget { color: iconBackground, borderRadius: BorderRadius.circular(9), ), - child: Icon(icon, size: 8, color: iconColor), + child: Icon(icon, size: sizeIcon, color: iconColor), ); } } diff --git a/lib/common_widget/steps.dart b/lib/common_widget/steps.dart index 6bcb530..03acc68 100644 --- a/lib/common_widget/steps.dart +++ b/lib/common_widget/steps.dart @@ -9,7 +9,7 @@ class Steps extends StatelessWidget { Widget build(BuildContext context) { String steps = Provider.of(context, listen: false) .managerSelectedActivity - .getTotalSteps() + .getTimeAllActivitySelected() .toString(); return Padding( diff --git a/lib/modele/activity.dart b/lib/modele/activity.dart index 525f5ae..57b36f1 100644 --- a/lib/modele/activity.dart +++ b/lib/modele/activity.dart @@ -1,11 +1,11 @@ -import 'package:flutter/foundation.dart'; +import 'package:smartfit_app_mobile/modele/activity_info/activity_info.dart'; class ActivityOfUser { + final ActivityInfo _activityInfo; // A afficher - late String _categorie; - late String _date; - late String _fileUuid; - late String _nameFile; + final String _categorie; + final String _fileUuid; + final String _nameFile; // ------------ // late String _imageName; @@ -14,8 +14,8 @@ class ActivityOfUser { String get fileUuid => _fileUuid; String get nameFile => _nameFile; - String get categorie => _categorie; - String get date => _date; + String get category => _categorie; + ActivityInfo get activityInfo => _activityInfo; Map get enteteCSV => _enteteCSV; // -- Getter/Setter -- Ancien // @@ -29,14 +29,9 @@ class ActivityOfUser { } ActivityOfUser( - String date, String categorie, String fileUuid, String nameFile) { - _categorie = categorie; - _date = date; - _fileUuid = fileUuid; - _nameFile = nameFile; - + this._activityInfo, this._categorie, this._fileUuid, this._nameFile) { // Mettre dans une fonction appart - if (categorie == "Walking") { + if (_categorie == "Walking") { _imageName = "assets/img/workout1.svg"; } else { // Mettre des conditions pour d'autre type d'activité @@ -46,7 +41,19 @@ class ActivityOfUser { // -------------------------- FIN Localisation ---------------------- // - Map toMap() { - return {'categorie': _categorie, 'image': _imageName, 'date': _date}; + Map toMapGeneric() { + Map map = { + 'categorie': _categorie, + 'image': _imageName, + 'date': _activityInfo.startTime, + 'time': _activityInfo.timeOfActivity, + }; + return map; + } + + Map toMapWalking() { + Map map = toMapGeneric(); + map.addAll(activityInfo.toMapWalking()); + return map; } } diff --git a/lib/modele/activity_info/activity_info.dart b/lib/modele/activity_info/activity_info.dart new file mode 100644 index 0000000..2617846 --- /dev/null +++ b/lib/modele/activity_info/activity_info.dart @@ -0,0 +1,227 @@ +import 'dart:convert'; + +import 'package:smartfit_app_mobile/modele/manager_file.dart'; + +class ActivityInfo { + ManagerFile managerFile = ManagerFile(); + ActivityInfo(); + + // -- Time -- // Ne pas calculer (Ligne session) + DateTime startTime = DateTime.now(); + double timeOfActivity = 0.0; + double distance = 0.0; + int calories = 0; + int steps = 0; + // ----------- BPM ------------ // + int bpmMax = 0; + int bpmMin = 300; + int bpmAvg = 0; + // ----------- Denivelé ------------ // + double denivelePositif = 0.0; + double deniveleNegatif = 0.0; + // ----------- Altitude ------------ // + double altitudeMax = 0.0; + double altitudeMin = 30000.0; + double altitudeAvg = 0.0; + // ----------- Température --------- // + int temperatureMax = 0; + int temperatureMin = 3000; + int temperatureAvg = 0; + // ----------- Vitesse ------------- // + double vitesseMax = 0.0; + double vitesseMin = 999999.0; + double vitesseAvg = 0.0; + + // ---------------------------------------------------------------------- // + + // -- Fonction pour lire le csv et remplir la classe -- // + ActivityInfo getData(List> csv) { + // - Entete - // + Map enteteCSV = getEntete(csv.first); + // ------------- Var tmp ---------- // + // -- BPM -- // + int bpmSomme = 0; + int bpmNb = 0; + // -- 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++) { + // + // ---------------------- BPM ---------------------- // + if (!isNull(enteteCSV["Value_${managerFile.fielBPM}"]!, csv[i])) { + int value = + int.parse(csv[i][enteteCSV["Value_${managerFile.fielBPM}"]!]); + bpmSomme += value; + bpmNb += 1; + if (value > bpmMax) { + bpmMax = value; + } + if (value < bpmMin) { + 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; + } + + // ------------------------ Température ----------------------- // + if (!isNull( + enteteCSV["Value_${managerFile.fieldTemperature}"]!, csv[i])) { + int value = int.parse( + csv[i][enteteCSV["Value_${managerFile.fieldTemperature}"]!]); + temperatureSomme += value; + temperatureNb += 1; + 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; + if (value > vitesseMax) { + vitesseMax = value; + } + if (value < vitesseMin) { + vitesseMin = value; + } + } + } + + // -- BPM -- // + bpmAvg = bpmSomme ~/ bpmNb; + // -- Atitude -- // + altitudeAvg = altitudeSomme / alititudeNb; + // -- Température -- // + temperatureAvg = temperatureSomme ~/ temperatureNb; + // -- Vitesse -- // + vitesseAvg = vitesseSomme / vitesseNb; + return this; + } + + // ------------ Fonction utile ------------------- // + Map getEntete(List content) { + Map enteteCSV = {}; + for (int i = 0; i < content.length; i++) { + enteteCSV.addAll({content[i]: i}); + } + return enteteCSV; + } + + bool isNull(int colonne, List ligne) { + return ligne[colonne] == "null"; + } + + // ------------- Pour print ----------------- // + Map toMapWalking() { + return { + // -- Denivelé -- // + "DenivelePositif": denivelePositif, + "DeniveleNegatif": denivelePositif, + // -- Altitude -- // + "AltitudeMax": altitudeMax, + "AltitudeMin": altitudeMin, + "AltitudeAvg": altitudeAvg + }; + } + + // --------------- JSON --------- // + // -- Lecture -- // + ActivityInfo.fromJson(Map? map) { + if (map == null) { + return; + } + // -- Ligne session -- // + startTime = DateTime.parse(map["startTime"]); + timeOfActivity = map["timeOfActivity"].toDouble(); + distance = map["distance"].toDouble(); + calories = map["calories"].toInt(); + steps = map["steps"].toInt(); + // -- BPM -- // + bpmAvg = map["bpmAvg"]; + bpmMax = map["bpmMax"]; + bpmMin = map["bpmMin"]; + // -- Denivelé -- // + deniveleNegatif = map["deniveleNegatif"].toDouble(); + denivelePositif = map["denivelePositif"].toDouble(); + // -- Altitude -- // + altitudeMax = map["altitudeMax"].toDouble(); + altitudeMin = map["altitudeMin"].toDouble(); + altitudeAvg = map["altitudeAvg"].toDouble(); + // -- Température -- // + temperatureMax = map["temperatureMax"].toInt(); + temperatureMin = map["temperatureMin"].toInt(); + temperatureAvg = map["temperatureAvg"].toInt(); + // -- Vitesse -- // + vitesseMax = map["vitesseMax"].toDouble(); + vitesseMin = map["vitesseMin"].toDouble(); + vitesseAvg = map["vitesseAvg"].toDouble(); + } + + // -- Ecriture -- // + String toJson() { + Map jsonMap = { + // -- BPM -- // + 'bpmAvg': bpmAvg, + 'bpmMax': bpmMax, + 'bpmMin': bpmMin, + // -- Denivelé -- // + 'denivelePositif': denivelePositif, + 'deniveleNegatif': deniveleNegatif, + // -- Altitude -- // + 'altitudeMax': altitudeMax, + 'altitudeMin': altitudeMin, + 'altitudeAvg': altitudeAvg, + // -- Température -- // + 'temperatureMax': temperatureMax, + 'temperatureMin': temperatureMin, + 'temperatureAvg': temperatureAvg, + // -- Vitesse -- // + 'vitesseMax': vitesseMax, + 'vitesseMin': vitesseMin, + 'vitesseAvg': vitesseAvg, + // Ligne session + 'startTime': startTime.toString(), + 'timeOfActivity': timeOfActivity, + 'distance': distance, + 'calories': calories, + 'steps': steps + }; + return jsonEncode(jsonMap); + } +} diff --git a/lib/modele/api/api_wrapper.dart b/lib/modele/api/api_wrapper.dart index bc6c49e..fc3ddd0 100644 --- a/lib/modele/api/api_wrapper.dart +++ b/lib/modele/api/api_wrapper.dart @@ -1,5 +1,6 @@ import 'dart:typed_data'; +import 'package:smartfit_app_mobile/modele/activity_info/activity_info.dart'; import 'package:smartfit_app_mobile/modele/api/i_data_strategy.dart'; import 'package:smartfit_app_mobile/modele/api/request_api.dart'; import 'package:smartfit_app_mobile/modele/local_db/request_local.dart'; @@ -157,13 +158,14 @@ class ApiWrapper { Uint8List contentFile, String filename, String category, - String date, + DateTime date, + ActivityInfo activityInfo, InfoMessage infoManager) async { await init(); if (handleOffline(infoManager)) return const Tuple2(false, "offline"); - Tuple2 res = - await api.uploadFileByte(token, contentFile, filename, category, date); + Tuple2 res = await api.uploadFileByte( + token, contentFile, filename, category, date, activityInfo); stdout.write("uploadFileByte: ${res.item1}"); return res; } diff --git a/lib/modele/api/i_data_strategy.dart b/lib/modele/api/i_data_strategy.dart index 7435b6a..54ea65d 100644 --- a/lib/modele/api/i_data_strategy.dart +++ b/lib/modele/api/i_data_strategy.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'dart:typed_data'; +import 'package:smartfit_app_mobile/modele/activity_info/activity_info.dart'; import 'package:tuple/tuple.dart'; abstract class IDataStrategy { @@ -21,8 +22,13 @@ abstract class IDataStrategy { Future> uploadFile(String token, File file); // Upload file as bytes - Future> uploadFileByte(String token, - Uint8List contentFile, String nameFile, String category, String date); + Future> uploadFileByte( + String token, + Uint8List contentFile, + String nameFile, + String category, + DateTime date, + ActivityInfo activityInfo); // Get one file by id (LOCAL OK) Future getFile(String token, String fileUuid); diff --git a/lib/modele/api/request_api.dart b/lib/modele/api/request_api.dart index dd64366..03af1c8 100644 --- a/lib/modele/api/request_api.dart +++ b/lib/modele/api/request_api.dart @@ -2,11 +2,12 @@ import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; +import 'package:smartfit_app_mobile/modele/activity_info/activity_info.dart'; import 'package:smartfit_app_mobile/modele/api/i_data_strategy.dart'; import 'package:http/http.dart' as http; import 'package:tuple/tuple.dart'; -class RequestApi extends IDataStrategy { +class RequestApi implements IDataStrategy { // Faire attention au URL String urlApi = "https://codefirst.iut.uca.fr/containers/SmartFit-smartfit_api"; @@ -191,7 +192,8 @@ class RequestApi extends IDataStrategy { Uint8List contentFile, String nameFile, String category, - String date) async { + DateTime date, + ActivityInfo activityInfo) async { final uri = Uri.parse('$urlApi/user/files'); Map headers = {'Authorization': token}; @@ -204,7 +206,8 @@ class RequestApi extends IDataStrategy { request.files.add(httpImage); request.headers.addAll(headers); request.fields["SmartFit_Category"] = category; - request.fields["SmartFit_Date"] = date; + request.fields["SmartFit_Date"] = date.toString(); + request.fields["info"] = activityInfo.toJson(); final response = await request.send(); diff --git a/lib/modele/local_db/model.dart b/lib/modele/local_db/model.dart index 0598328..a9f06dc 100644 --- a/lib/modele/local_db/model.dart +++ b/lib/modele/local_db/model.dart @@ -19,9 +19,7 @@ class Activity { String uuid; String filename; String category; - DateTime date; String info; - Activity( - this.id, this.uuid, this.filename, this.category, this.date, this.info); + Activity(this.id, this.uuid, this.filename, this.category, this.info); } diff --git a/lib/modele/local_db/objectbox.dart b/lib/modele/local_db/objectbox.dart index c600cb7..7b64eca 100644 --- a/lib/modele/local_db/objectbox.dart +++ b/lib/modele/local_db/objectbox.dart @@ -4,6 +4,7 @@ import 'dart:typed_data'; import 'package:csv/csv.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/objectbox.g.dart'; import 'package:path/path.dart' as p; import 'package:path_provider/path_provider.dart'; @@ -106,8 +107,9 @@ class ObjectBox { List userActivityList = List.empty(growable: true); for (Activity act in activityDBList) { - userActivityList.add(ActivityOfUser( - act.date.toString(), act.category, act.uuid, act.filename)); + ActivityInfo actInfo = ActivityInfo.fromJson(jsonDecode(act.info)); + userActivityList + .add(ActivityOfUser(actInfo, act.category, act.uuid, act.filename)); } return userActivityList; diff --git a/lib/modele/local_db/request_local.dart b/lib/modele/local_db/request_local.dart index 755eada..4179244 100644 --- a/lib/modele/local_db/request_local.dart +++ b/lib/modele/local_db/request_local.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'package:smartfit_app_mobile/modele/activity_info/activity_info.dart'; import 'package:smartfit_app_mobile/modele/api/i_data_strategy.dart'; import 'package:smartfit_app_mobile/modele/local_db/model.dart'; import 'package:tuple/tuple.dart'; @@ -30,7 +31,6 @@ class RequestLocal implements IDataStrategy { "uuid": act.uuid, "filename": act.filename, "category": act.category, - "creation_date": act.date, "info": act.info }; jsonList.add(json); @@ -72,7 +72,8 @@ class RequestLocal implements IDataStrategy { Uint8List contentFile, String nameFile, String category, - String date) async { + DateTime date, + ActivityInfo activityInfo) async { return const Tuple2(false, "not implemented"); } diff --git a/lib/modele/manager_file.dart b/lib/modele/manager_file.dart index a4178d2..ede5e0f 100644 --- a/lib/modele/manager_file.dart +++ b/lib/modele/manager_file.dart @@ -2,6 +2,9 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:csv/csv.dart'; import 'package:fit_tool/fit_tool.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:smartfit_app_mobile/modele/activity_info/activity_info.dart'; +import 'package:tuple/tuple.dart'; class ManagerFile { // -- Field @@ -12,8 +15,17 @@ class ManagerFile { final String _fieldBPM = "heart_rate"; final String _fieldSpeed = "speed"; final String _fieldAltitude = "altitude"; - final String _fieldTotalStep = "total_strides"; - final String _fieldTotalCalorie = "total_calories"; + final String _fieldTemperature = "temperature"; + + // -- Not in CSV (Ligne session) -- // + static const String _session = "session"; + static const String _startTime = "start_time"; + static const String _sport = "sport"; + static const String _timeActivity = "total_elapsed_time"; + static const String _totalDistance = "total_distance"; + static const String _totalCalories = "total_calories"; + static const String _totalStep = "total_strides"; + // -- Getter field String get fieldTimeStamp => _fieldTimestamp; String get fieldPositionLatitude => _fieldPositionLatitue; @@ -22,10 +34,20 @@ class ManagerFile { String get fielBPM => _fieldBPM; String get fieldSpeed => _fieldSpeed; String get fieldAltitude => _fieldAltitude; - String get fieldTotalStep => _fieldTotalStep; - String get fieldTotalCalories => _fieldTotalCalorie; + String get fieldTemperature => _fieldTemperature; + + // -- Categorie -- // + static const String _generic = "generic"; + static const String _velo = "cycling"; + static const String _marche = "walking"; + + // -- Getter categorie + String get marche => _marche; + String get generic => _generic; List allowedFieldWalking = List.empty(growable: true); + List allowedFieldGeneric = List.empty(growable: true); + List allowedFieldCycling = List.empty(growable: true); ManagerFile() { allowedFieldWalking = [ @@ -36,29 +58,132 @@ class ManagerFile { _fieldBPM, _fieldSpeed, _fieldAltitude, - _fieldTotalStep, - _fieldTotalCalorie + _fieldTemperature ]; + + allowedFieldGeneric = [_fieldTimestamp, _fieldBPM]; + + allowedFieldCycling = [ + _fieldTimestamp, + _fieldPositionLatitue, + _fieldPositionLongitude, + _fieldDistance, + _fieldBPM, + _fieldSpeed, + _fieldAltitude, + _fieldTemperature + ]; + } + + // -- Read the byte of file CSV -- // + List> convertByteIntoCSV(Uint8List bytes) { + return const CsvToListConverter().convert(utf8.decode(bytes)); + } + + String _getCategoryById(int id) { + switch (id) { + case 0: + return _generic; + case 2: + return _velo; + case 11: + return _marche; + default: + return _generic; + } + } + + // ------------- Get The path of application --- // + Future get localPath async { + final directory = await getApplicationDocumentsDirectory(); + return directory.path; + } + + Tuple4>, ActivityInfo, String> + convertBytesFitFileIntoCSVListAndGetInfo(Uint8List bytes) { + List fitFile = FitFile.fromBytes(bytes).records; + String categorie; + List fieldAllowed = []; + ActivityInfo info = ActivityInfo(); + // -- Chercher ligne session -- // + List ligneSession = _getLigneSession(fitFile); + if (ligneSession.isEmpty) { + return Tuple4(false, List.empty(), ActivityInfo(), ""); + } + categorie = + _getCategoryById(int.parse(_getXfromListe(_sport, ligneSession))); + + // -- Si la catégorie est pas prévu est est généric -- // + switch (categorie) { + case (_marche): + fieldAllowed = allowedFieldWalking; + break; + case (_generic): + fieldAllowed = allowedFieldGeneric; + break; + default: + // A REMETRE EN GENERIC + //fieldAllowed = allowedFieldGeneric; + //info = ActivityInfoGeneric(); + //categorie = _generic; + fieldAllowed = allowedFieldWalking; + break; + } + + // -------- Transformation en CSV ----------- // + List> csvData = transformDataMapIntoCSV( + getDataOfListeOfRecord(fitFile, fieldAllowed), fieldAllowed); + + // ------ Remplir info avec la ligne session --------- // + info.startTime = DateTime.fromMillisecondsSinceEpoch( + int.parse(_getXfromListe(_startTime, ligneSession))); + + info.timeOfActivity = + double.parse(_getXfromListe(_timeActivity, ligneSession)); + info.distance = double.parse(_getXfromListe(_totalDistance, ligneSession)); + info.calories = int.parse(_getXfromListe(_totalCalories, ligneSession)); + info.steps = int.parse(_getXfromListe(_totalStep, ligneSession)); + // ----------------------------------------------------- // + + return Tuple4(true, csvData, info.getData(csvData), categorie); } - List> convertBytesFitFileIntoCSVList(Uint8List bytes) { - FitFile fitFile = FitFile.fromBytes(bytes); + List _getLigneSession(List listRecord) { + for (int i = listRecord.length - 1; i != listRecord.length - 5; i--) { + List tmpListe = listRecord[i].toRow(); + if (tmpListe[0] == "Data" && tmpListe[2] == _session) { + return tmpListe; + } + } + return List.empty(); + } + + String _getXfromListe(String x, List liste) { + for (int i = 0; i < liste.length; i++) { + if (liste[i] == x) { + return liste[i + 1].toString(); + } + } + return "null"; + } - // ----------- Lire le fit et extarire les données qu'on choisi ----------- // + List>> getDataOfListeOfRecord( + List listeRecord, List allowedField) { List>> dataResult = List.empty(growable: true); - for (Record element in fitFile.records) { + for (Record element in listeRecord) { List listeField = element.toRow(); Map> ligneDataResult = {}; + // -- Skip ligne whith no data -- // bool skip = true; + // -- Si ce n'est pas de la data on pass -- // if (listeField[0] != "Data") { continue; } - for (int i = 0; i < listeField.length;) { - if (allowedFieldWalking.contains(listeField[i])) { + if (allowedField.contains(listeField[i])) { Map tmp = {}; tmp["Value"] = listeField[i + 1].toString(); tmp["Unite"] = listeField[i + 2].toString(); @@ -66,27 +191,33 @@ class ManagerFile { i += 2; skip = false; } + + // -- Pour boucler -- // i += 1; } if (!skip) { dataResult.add(ligneDataResult); } } - // -------- FIN ---------- // + return dataResult; + } + List> transformDataMapIntoCSV( + List>> listeMap, + List fieldAllowed) { // ------- Création du csv ----- // // --- Création de l'entête -- // List enteteCSV = []; - for (String field in allowedFieldWalking) { + for (String field in fieldAllowed) { enteteCSV.add("Value_$field"); enteteCSV.add("Unite_$field"); } List> csvData = List.empty(growable: true); // - for (Map> ligne in dataResult) { + for (Map> ligne in listeMap) { List tmpLigne = List.empty(growable: true); - for (String field in allowedFieldWalking) { + for (String field in fieldAllowed) { if (!ligne.containsKey(field)) { tmpLigne.add("null"); tmpLigne.add("null"); @@ -101,82 +232,4 @@ class ManagerFile { // ------- FIN --------------- // return csvData; } - - // -- Read the byte of file CSV -- // - List> convertByteIntoCSV(Uint8List bytes) { - return const CsvToListConverter().convert(utf8.decode(bytes)); - } - - /* - // ------------- Get The path of application --- // - Future get localPath async { - final directory = await getApplicationDocumentsDirectory(); - return directory.path; - }*/ - - /* - // ----- Read csv File ------- // - Future> readCSVFile(String path) async { - if (File(path).exists() == false) return List.empty(); - final input = File(path).openRead(); - final fields = await input - .transform(utf8.decoder) - .transform(const CsvToListConverter()) - .toList(); - return fields; - } - - // ----- Read a file FIT --- // - Future> readFitFile(String path) async { - if (File(path).existsSync() == false) return List.empty(); - - final file = File(path); - final bytes = await file.readAsBytes(); - final fitFile = FitFile.fromBytes(bytes); - - return fitFile.toRows(); - } - - Future> readFitFileWhithFile(File file) async { - final bytes = await file.readAsBytes(); - final fitFile = FitFile.fromBytes(bytes); - return fitFile.toRows(); - } - - List readFitFileWeb(Uint8List bytes) { - final fitFile = FitFile.fromBytes(bytes); - return fitFile.toRows(); - }*/ - - /* - // --- A modifier si utilisé --- // - Future saveFileLocal(String nameFileWithExtension, String path) async { - /* - final outFile = File("${await localPath}\\Files\\$nameFileWithExtension"); - if (outFile.existsSync() == false) { - outFile.createSync(recursive: true); - } - await outFile.writeAsString(await file.readAsString()); - return true;*/ - }*/ - /* - // -- Check si le fichier existe localement -- // - Future fileExist(String filname) async { - Directory directory = Directory("${await localPath}\\Files\\"); - if (!directory.existsSync()) { - print("Le dossier n'existe pas !"); - return false; - } - List files = directory.listSync(); - for (FileSystemEntity file in files) { - if (file.path.split("\\").last == filname) { - return true; - } - } - return false; - }*/ - - // --- Ligne utile --- // - //final csv = const ListToCsvConverter().convert(fitFile.toRows()); - //await outFile.writeAsString(csv);*/ } diff --git a/lib/modele/manager_selected_activity.dart b/lib/modele/manager_selected_activity.dart index 7fbe4e7..6aa944a 100644 --- a/lib/modele/manager_selected_activity.dart +++ b/lib/modele/manager_selected_activity.dart @@ -3,19 +3,17 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:smartfit_app_mobile/common_widget/graph/graph.dart'; import 'package:smartfit_app_mobile/modele/activity.dart'; import 'package:smartfit_app_mobile/modele/manager_file.dart'; +import 'package:latlong2/latlong.dart' as osm; class ManagerSelectedActivity { final ManagerFile _managerFile = ManagerFile(); List activitySelected = List.empty(growable: true); bool addSelectedActivity(ActivityOfUser activityOfUser) { - // Regarder si l'entete est la même - // C'est de la merde!! - /* if (activitySelected.isNotEmpty && - activitySelected.first.enteteCSV != activityOfUser.enteteCSV) { + activityOfUser.category != activitySelected.first.category) { return false; - }*/ + } activitySelected.add(activityOfUser); return true; } @@ -54,7 +52,6 @@ class ManagerSelectedActivity { List getXWithTime(String field) { List result = List.empty(growable: true); - int firstTimestamp = 0; for (int c = 0; c < activitySelected.length; c++) { @@ -82,208 +79,141 @@ class ManagerSelectedActivity { return result; } -// ----------------- BPM ------------------ // +//-------------------------------------------------------------------------------------------// - // Retourne le BPM Max (Fichier CSV) - int getMaxBpm() { +// ----------------- BPM ------------------ // + int getBpmMaxAllActivitieSelected() { int max = 0; - for (int c = 0; c < activitySelected.length; c++) { - for (int i = 0; i < activitySelected[c].contentActivity.length; i++) { - if (_notNull(c, i, - activitySelected[c].enteteCSV["Value_${_managerFile.fielBPM}"]!)) { - int valueTmp = activitySelected[c].contentActivity[i] - [activitySelected[c].enteteCSV["Value_${_managerFile.fielBPM}"]!]; - if (valueTmp > max) { - max = valueTmp; - } - } + for (ActivityOfUser activityOfUser in activitySelected) { + if (activityOfUser.activityInfo.bpmMax > max) { + max = activityOfUser.activityInfo.bpmMax; } } - return max; } -// Retourne le BPM Min (Fichier CSV) - int getMinBpm() { - int min = 300; - - for (int c = 0; c < activitySelected.length; c++) { - for (int i = 0; i < activitySelected[c].contentActivity.length; i++) { - if (_notNull(c, i, - activitySelected[c].enteteCSV["Value_${_managerFile.fielBPM}"]!)) { - int valueTmp = activitySelected[c].contentActivity[i] - [activitySelected[c].enteteCSV["Value_${_managerFile.fielBPM}"]!]; - if (valueTmp < min) { - min = valueTmp; - } - } + int getBpmMinAllActivitieSelected() { + int min = 999; + for (ActivityOfUser activityOfUser in activitySelected) { + if (activityOfUser.activityInfo.bpmMax < min) { + min = activityOfUser.activityInfo.bpmMin; } } return min; } - // Retourne le BPM avg (Fichier CSV) - int getAvgBpm() { + int getBpmAvgAllActivitieSelected() { int somme = 0; - int nb = 0; - for (int c = 0; c < activitySelected.length; c++) { - for (int i = 0; i < activitySelected[c].contentActivity.length; i++) { - if (_notNull(c, i, - activitySelected[c].enteteCSV["Value_${_managerFile.fielBPM}"]!)) { - somme += activitySelected[c].contentActivity[i][activitySelected[c] - .enteteCSV["Value_${_managerFile.fielBPM}"]!] as int; - nb++; - } + for (ActivityOfUser activityOfUser in activitySelected) { + somme += activityOfUser.activityInfo.bpmAvg; + } + return somme ~/ activitySelected.length; + } + + // ------------------ Fin BPM ------------------- // + // ------------------ Altitude ------------------ // + double getMaxAltitudeAllActivitySelected() { + double max = 0.0; + for (ActivityOfUser activityOfUser in activitySelected) { + if (activityOfUser.activityInfo.altitudeMax > max) { + max = activityOfUser.activityInfo.altitudeMax; } } - return somme ~/ nb; + return max; } - // -------------------------- FIN BPM ---------------------- // + double getMinAltitudeAllActivitySelected() { + double min = 99999.0; + for (ActivityOfUser activityOfUser in activitySelected) { + if (activityOfUser.activityInfo.altitudeMax < min) { + min = activityOfUser.activityInfo.altitudeMin; + } + } + return min; + } - // ---------------------- Distance ---------------------- // + double getAvgAltitudeAllActivitySelected() { + double somme = 0; + for (ActivityOfUser activityOfUser in activitySelected) { + somme += activityOfUser.activityInfo.altitudeAvg; + } + return somme / activitySelected.length; + } + // ------------------ Fin Altitude ------------------- // - double getTotalDistance() { - double max = 0; + // ------------------ Température -------------------- // + int getAvgTemperatureAllActivitySelected() { + int somme = 0; + for (ActivityOfUser activityOfUser in activitySelected) { + somme += activityOfUser.activityInfo.temperatureAvg; + } + return somme ~/ activitySelected.length; + } - for (int c = 0; c < activitySelected.length; c++) { - for (int i = activitySelected[c].contentActivity.length - 1; - i != 0; - i--) { - if (_notNull( - c, - i, - activitySelected[c] - .enteteCSV["Value_${_managerFile.fieldDistance}"]!)) { - double valueTmp = activitySelected[c] - .contentActivity[i][activitySelected[c] - .enteteCSV["Value_${_managerFile.fieldDistance}"]!] - .toDouble(); - if (valueTmp > max) { - max = valueTmp; - } - } + int getMaxTemperatureAllActivitySelected() { + int max = 0; + for (ActivityOfUser activityOfUser in activitySelected) { + if (activityOfUser.activityInfo.temperatureMax > max) { + max = activityOfUser.activityInfo.temperatureMax; } } - return max; } - // ---------------------- FIN Distance ---------------------- // - - // ---------------------- Calories ---------------------- // - int getCalorie() { - for (int c = 0; c < activitySelected.length; c++) { - for (int i = activitySelected[c].contentActivity.length - 1; - i != 0; - i--) { - if (_notNull( - c, - i, - activitySelected[c] - .enteteCSV["Value_${_managerFile.fieldTotalCalories}"]!)) { - return activitySelected[c].contentActivity[i][activitySelected[c] - .enteteCSV["Value_${_managerFile.fieldTotalCalories}"]!] as int; - } + int getMinTemperatureAllActivitySelected() { + int min = 0; + for (ActivityOfUser activityOfUser in activitySelected) { + if (activityOfUser.activityInfo.temperatureMin > min) { + min = activityOfUser.activityInfo.temperatureMin; } } - return 0; + return min; } + // -------------------------- FIN Température ---------------------- // - // ---------------------- FIN Calories ---------------------- // - // ---------------------- Step ------------------------------// + // ---------------------- Distance ---------------------- // - int getTotalSteps() { - for (int c = 0; c < activitySelected.length; c++) { - for (int i = activitySelected[c].contentActivity.length - 1; - i != 0; - i--) { - if (_notNull( - c, - i, - activitySelected[c] - .enteteCSV["Value_${_managerFile.fieldTotalStep}"]!)) { - return activitySelected[c] - .contentActivity[i][activitySelected[c] - .enteteCSV["Value_${_managerFile.fieldTotalStep}"]!] - .toInt(); - } - } + double getDistanceAllActivitySelected() { + double somme = 0; + for (ActivityOfUser activityOfUser in activitySelected) { + somme += activityOfUser.activityInfo.distance; } - return 0; + return somme; } - // ----------------------- FIN Step ------------------------ // - // ------------------------- Time ----------------------------- // + // ---------------------- FIN Distance ---------------------- // - int getTotalTime() { - for (int c = 0; c < activitySelected.length; c++) { - for (int i = activitySelected[c].contentActivity.length - 1; - i != 0; - i--) { - if (_notNull( - c, - i, - activitySelected[c] - .enteteCSV["Value_${_managerFile.fieldTimeStamp}"]!)) { - return activitySelected[c].contentActivity[i][activitySelected[c] - .enteteCSV["Value_${_managerFile.fieldTimeStamp}"]!]; - } - } + // ---------------------- Calories ---------------------- // + int getCalorieAllActivitySelected() { + int somme = 0; + for (ActivityOfUser activityOfUser in activitySelected) { + somme += activityOfUser.activityInfo.calories; } - return 0; + return somme; } - // ---------------------------- FIN time -------------------- // - // ---------------------------------------- Altitude -------------------- // + // ---------------------- FIN Calories ---------------------- // + // ---------------------- Step ------------------------------// - // --- Fichier CSV --- // - double getMaxAltitude() { - double max = 0; - for (int c = 0; c < activitySelected.length; c++) { - for (int i = 0; i < activitySelected[c].contentActivity.length; i++) { - if (_notNull( - c, - i, - activitySelected[c] - .enteteCSV["Value_${_managerFile.fieldAltitude}"]!)) { - double valueTmp = activitySelected[c] - .contentActivity[i][activitySelected[c] - .enteteCSV["Value_${_managerFile.fieldAltitude}"]!] - .toDouble(); - if (valueTmp > max) { - max = valueTmp; - } - } - } + int getStepsAllActivitySelected() { + int somme = 0; + for (ActivityOfUser activityOfUser in activitySelected) { + somme += activityOfUser.activityInfo.steps; } - return max; + return somme; } + // ----------------------- FIN Step ------------------------ // - // --- Fichier CSV --- // - double getMinAltitude() { - double min = 5000; - for (int c = 0; c < activitySelected.length; c++) { - for (int i = 0; i < activitySelected[c].contentActivity.length; i++) { - if (_notNull( - c, - i, - activitySelected[c] - .enteteCSV["Value_${_managerFile.fieldAltitude}"]!)) { - double valueTmp = activitySelected[c] - .contentActivity[i][activitySelected[c] - .enteteCSV["Value_${_managerFile.fieldAltitude}"]!] - .toDouble(); - if (valueTmp < min) { - min = valueTmp; - } - } - } + // ------------------------- Time ----------------------------- // + + double getTimeAllActivitySelected() { + double somme = 0; + for (ActivityOfUser activityOfUser in activitySelected) { + somme += activityOfUser.activityInfo.timeOfActivity; } - return min; + return somme; } - - // -------------------------- FIN altitude ---------------------- // + // ---------------------------- FIN time -------------------- // // -------------------------- Speed ---------------------- // @@ -326,50 +256,32 @@ class ManagerSelectedActivity { return result; } - // Retourne la Speed Max (Fichier CSV) - double getMaxSpeed() { + double getMaxSpeedAllActivitySelected() { double max = 0.00; - - for (int c = 0; c < activitySelected.length; c++) { - for (int i = 0; i < activitySelected[c].contentActivity.length; i++) { - if (_notNull( - c, - i, - activitySelected[c] - .enteteCSV["Value_${_managerFile.fieldSpeed}"]!)) { - double valueTmp = activitySelected[c] - .contentActivity[i][activitySelected[c] - .enteteCSV["Value_${_managerFile.fieldSpeed}"]!] - .toDouble(); - if (valueTmp > max) { - max = valueTmp; - } - } + for (ActivityOfUser activityOfUser in activitySelected) { + if (activityOfUser.activityInfo.vitesseMax > max) { + max = activityOfUser.activityInfo.vitesseMax; } } return max; } - // Retourne avg Max (Fichier CSV) - double getAvgSpeed() { - double somme = 0; - int nb = 0; - - for (int c = 0; c < activitySelected.length; c++) { - for (int i = 0; i < activitySelected[c].contentActivity.length; i++) { - if (_notNull( - c, - i, - activitySelected[c] - .enteteCSV["Value_${_managerFile.fieldSpeed}"]!)) { - somme += activitySelected[c].contentActivity[i][activitySelected[c] - .enteteCSV["Value_${_managerFile.fieldSpeed}"]!]; - nb++; - } + double getMinSpeedAllActivitySelected() { + double min = 99999.9; + for (ActivityOfUser activityOfUser in activitySelected) { + if (activityOfUser.activityInfo.vitesseMin < min) { + min = activityOfUser.activityInfo.vitesseMin; } } + return min; + } - return somme / nb; + double getAvgSpeedAllActivitySelected() { + double somme = 0.0; + for (ActivityOfUser activityOfUser in activitySelected) { + somme += activityOfUser.activityInfo.vitesseAvg; + } + return somme / activitySelected.length; } // -------------------------- FIN Speed ---------------------- // @@ -401,4 +313,28 @@ class ManagerSelectedActivity { } return list; } + + List getPositionOSM() { + List list = List.empty(growable: true); + + for (int i = 0; i < activitySelected[0].contentActivity.length; i++) { + if (_notNull( + 0, + i, + activitySelected[0] + .enteteCSV["Value_${_managerFile.fieldPositionLatitude}"]!) && + _notNull( + 0, + i, + activitySelected[0].enteteCSV[ + "Value_${_managerFile.fieldPositionLongitude}"]!)) { + list.add(osm.LatLng( + activitySelected[0].contentActivity[i][activitySelected[0] + .enteteCSV["Value_${_managerFile.fieldPositionLatitude}"]!], + activitySelected[0].contentActivity[i][activitySelected[0] + .enteteCSV["Value_${_managerFile.fieldPositionLongitude}"]!])); + } + } + return list; + } } diff --git a/lib/modele/user.dart b/lib/modele/user.dart index e866ebc..5320f97 100644 --- a/lib/modele/user.dart +++ b/lib/modele/user.dart @@ -24,4 +24,32 @@ class User extends ChangeNotifier { listActivity.insert(index, activity); notifyListeners(); } + + // ------------ Fonction Calcul -------- // + // --- Time --- // + double getTotalTimeAllActivity() { + double totalTime = 0.0; + for (ActivityOfUser activity in listActivity) { + totalTime += activity.activityInfo.timeOfActivity; + } + return totalTime; + } + + // ------------ Walking -------------- // + // ---- Denivelé ---- // + double getTotalDenivelePositif() { + double totalDevPos = 0.0; + for (ActivityOfUser activity in listActivity) { + totalDevPos += activity.activityInfo.denivelePositif; + } + return totalDevPos; + } + + double getTotalDeniveleNegatif() { + double totalDevNeg = 0.0; + for (ActivityOfUser activity in listActivity) { + totalDevNeg += activity.activityInfo.deniveleNegatif; + } + return totalDevNeg; + } } diff --git a/lib/modele/utile/home_view/data_home_view.dart b/lib/modele/utile/home_view/data_home_view.dart index 6c3220f..089b37f 100644 --- a/lib/modele/utile/home_view/data_home_view.dart +++ b/lib/modele/utile/home_view/data_home_view.dart @@ -7,6 +7,8 @@ class DataHomeView { late List altitudeSeconde; int minBPM = 0; int maxBPM = 0; + double maxSpeed = 0; + double time = 0; DataHomeView(this.bpmSecondes, this.bpmSecondes2, this.vitesseSecondes, this.altitudeSeconde); diff --git a/lib/modele/utile/home_view/home_view_util.dart b/lib/modele/utile/home_view/home_view_util.dart index b1e2a94..36bec08 100644 --- a/lib/modele/utile/home_view/home_view_util.dart +++ b/lib/modele/utile/home_view/home_view_util.dart @@ -21,8 +21,8 @@ class HomeViewUtil { List bpmSecondes2 = List.from(bpmSecondes); - return DataHomeView(bpmSecondes, normaliserDeuxiemeElement(bpmSecondes2), - normaliserDeuxiemeElement(vitesseSecondes), altitudeSeconde); + return DataHomeView(normaliserPremierElement(bpmSecondes), normaliserPremierElement(normaliserDeuxiemeElement(bpmSecondes2)), + normaliserPremierElement(normaliserDeuxiemeElement(vitesseSecondes)), altitudeSeconde); } List normaliserDeuxiemeElement(List liste) { @@ -41,4 +41,21 @@ class HomeViewUtil { } return liste; } + List normaliserPremierElement(List 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; + } + } + // 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; +} + } diff --git a/lib/modele/utile/list_activity/list_activity_utile.dart b/lib/modele/utile/list_activity/list_activity_utile.dart index 6ab88e4..976f06c 100644 --- a/lib/modele/utile/list_activity/list_activity_utile.dart +++ b/lib/modele/utile/list_activity/list_activity_utile.dart @@ -10,6 +10,9 @@ import 'package:csv/csv.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.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/i_data_strategy.dart'; +import 'package:smartfit_app_mobile/modele/api/request_api.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'; @@ -34,9 +37,11 @@ class ListActivityUtile { // TODO: Not sure this line as an utility // localDB.saveActivityFile(activityOfUser.contentActivity); - Provider.of(context, listen: false) + if (!Provider.of(context, listen: false) .managerSelectedActivity - .addSelectedActivity(activityOfUser); + .addSelectedActivity(activityOfUser)) { + return const Tuple2(false, "Pas de même categorie"); + } return const Tuple2(true, "Yeah"); } @@ -54,8 +59,10 @@ class ListActivityUtile { Provider.of(context, listen: false).listActivity.clear(); notZero = true; } + // -- connaitre le type de categorie pour changer le type d'info -- // + Provider.of(context, listen: false).addActivity(ActivityOfUser( - element["creation_date"].toString(), + ActivityInfo.fromJson(element["info"]), element["category"].toString(), element["uuid"].toString(), element["filename"].toString())); @@ -66,22 +73,19 @@ class ListActivityUtile { element["uuid"], element["filename"], element["category"], - DateTime.parse(element["creation_date"]), element["info"] - .toString())); // Do not remove toString(), it do not work w/o it, idk why + .toString())); // TODO: Do not remove toString(), it do not work w/o it, idk why } - /* - if (notZero) { - await getContentActivity(context); - }*/ return const Tuple2(true, "Yeah"); } - Future> addFile(Uint8List bytes, String filename, + Future> _addFile(Uint8List bytes, String filename, String token, InfoMessage infoManager) async { // -- Transormer le fit en CSV - List> csv = _managerFile.convertBytesFitFileIntoCSVList(bytes); - String csvString = const ListToCsvConverter().convert(csv); + Tuple4>, ActivityInfo, String> resultData = + _managerFile.convertBytesFitFileIntoCSVListAndGetInfo(bytes); + + String csvString = const ListToCsvConverter().convert(resultData.item2); Uint8List byteCSV = Uint8List.fromList(utf8.encode(csvString)); // Save on local storage if plateform not browser @@ -90,20 +94,42 @@ class ListActivityUtile { actSaver.saveActivity(byteCSV, filename); } - String categoryActivity = filename.split("_").first.toLowerCase(); - String dateActivity = filename.split("_")[1].split("T").first; - Tuple2 result = await api.uploadFileByte( - token, byteCSV, filename, categoryActivity, dateActivity, infoManager); + token, + byteCSV, + filename, + resultData.item4, + resultData.item3.startTime, + resultData.item3, + infoManager); if (result.item1 == false) { return Tuple2(false, result.item2); } return const Tuple2(true, "Yeah"); } + // --- Ne marche pas sous window !! Jsp linux (mettre en format mobile) -- // + void addFileWeb(Uint8List? bytes, String token, String filename, + BuildContext context, InfoMessage infoManager) async { + if (bytes == null) { + return; + } + Tuple2 resultAdd = + await _addFile(bytes, filename, token, infoManager); + if (!resultAdd.item1) { + //print("Message error"); + return; + } + Tuple2 resultGet = await getFiles(token, context); + if (!resultGet.item1) { + //print("Message error"); + return; + } + } + void addFileMobile(String path, String token, String filename, BuildContext context, InfoMessage infoManager) async { - Tuple2 resultAdd = await addFile( + Tuple2 resultAdd = await _addFile( await File(path).readAsBytes(), filename, token, infoManager); if (!resultAdd.item1) { //print("Message error"); diff --git a/lib/view/activity/mobile/mobile_list_activity.dart b/lib/view/activity/mobile/mobile_list_activity.dart index d1e5e6b..3a5d3be 100644 --- a/lib/view/activity/mobile/mobile_list_activity.dart +++ b/lib/view/activity/mobile/mobile_list_activity.dart @@ -2,7 +2,7 @@ import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:smartfit_app_mobile/common/colo_extension.dart'; -import 'package:smartfit_app_mobile/common_widget/container/list/list_activity.dart'; +import 'package:smartfit_app_mobile/common_widget/container/list/list_activity_widget.dart'; import 'package:smartfit_app_mobile/modele/user.dart'; import 'package:smartfit_app_mobile/modele/utile/info_message.dart'; import 'package:smartfit_app_mobile/modele/utile/list_activity/list_activity_utile.dart'; @@ -15,7 +15,6 @@ class MobileListActivity extends StatefulWidget { } class _MobileListActivity extends State { - FilePickerResult? result; final ListActivityUtile _utile = ListActivityUtile(); final InfoMessage infoManager = InfoMessage(); @@ -56,12 +55,12 @@ class _MobileListActivity extends State { onPressed: () async { FilePickerResult? result = await FilePicker.platform.pickFiles(); - if (result != null) { + if (result != null && result.files.isNotEmpty) { // ignore: use_build_context_synchronously _utile.addFileMobile( result.files.single.path!, Provider.of(context, listen: false).token, - result.files.single.name, + result.files.first.name, context, infoManager); } else { @@ -98,7 +97,7 @@ class _MobileListActivity extends State { ), ) ]) - : const ListActivity(), + : const ListActivityWidget(), SizedBox( height: media.width * 0.1, ), diff --git a/lib/view/activity/web/web_list_activity.dart b/lib/view/activity/web/web_list_activity.dart index d072362..b8a9f47 100644 --- a/lib/view/activity/web/web_list_activity.dart +++ b/lib/view/activity/web/web_list_activity.dart @@ -1,10 +1,7 @@ -import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:smartfit_app_mobile/modele/utile/info_message.dart'; import 'package:smartfit_app_mobile/modele/utile/list_activity/list_activity_utile.dart'; -import 'package:smartfit_app_mobile/view/activity/list_activity.dart'; -import 'package:tuple/tuple.dart'; -import 'package:universal_html/html.dart' as html; +import 'package:smartfit_app_mobile/common_widget/container/list/list_activity_widget.dart'; import 'package:file_picker/file_picker.dart'; import 'package:provider/provider.dart'; import 'package:smartfit_app_mobile/common/colo_extension.dart'; @@ -22,27 +19,6 @@ class _WebListActivityState extends State { final ListActivityUtile _utile = ListActivityUtile(); final InfoMessage infoManager = InfoMessage(); - void addFileWeb(html.File file, String token) async { - final reader = html.FileReader(); - reader.readAsArrayBuffer(file); - - reader.onLoadEnd.listen((event) async { - if (reader.readyState == html.FileReader.DONE) { - Uint8List bytes = reader.result as Uint8List; - Tuple2 resultAdd = - await _utile.addFile(bytes, file.name, token, infoManager); - if (!resultAdd.item1) { - return; - } - Tuple2 resultGet = await _utile.getFiles(token, context); - if (!resultGet.item1) { - //print("MessageError"); - return; - } - } - }); - } - @override Widget build(BuildContext context) { var media = MediaQuery.of(context).size; @@ -77,19 +53,20 @@ class _WebListActivityState extends State { fontWeight: FontWeight.w700))), TextButton( onPressed: () async { - html.FileUploadInputElement uploadInput = - html.FileUploadInputElement(); - uploadInput.click(); - - uploadInput.onChange.listen((e) { - final files = uploadInput.files; - if (files != null && files.isNotEmpty) { - addFileWeb( - files[0], - Provider.of(context, listen: false) - .token); // Lecture du fichier sélectionné - } - }); + FilePickerResult? result = + await FilePicker.platform.pickFiles(); + if (result != null && result.files.isNotEmpty) { + _utile.addFileWeb( + result.files.first.bytes, + Provider.of(context, listen: false).token, + result.files.first.name, + context, + infoManager); + } else { + print("Picker"); + // msg d'erreur + // User canceled the picker + } }, child: Text( "Ajouter", @@ -116,7 +93,7 @@ class _WebListActivityState extends State { ), ) ]) - : const ListActivity(), + : const ListActivityWidget(), SizedBox( height: media.width * 0.1, ), diff --git a/lib/view/home/home_view.dart b/lib/view/home/home_view.dart index 637a004..773d2df 100644 --- a/lib/view/home/home_view.dart +++ b/lib/view/home/home_view.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:responsive_builder/responsive_builder.dart'; -import 'package:smartfit_app_mobile/modele/activity.dart'; import 'package:smartfit_app_mobile/modele/user.dart'; import 'package:smartfit_app_mobile/view/home/mobile/mobile_homeview.dart'; import 'package:smartfit_app_mobile/view/home/no_activity_view.dart'; +import 'package:smartfit_app_mobile/view/home/stats_activities_view.dart'; import 'package:smartfit_app_mobile/view/home/web/web_homeview.dart'; class HomeView extends StatefulWidget { @@ -17,25 +17,21 @@ class HomeView extends StatefulWidget { class _HomeViewState extends State { @override Widget build(BuildContext context) { - List listSelected = - context.watch().managerSelectedActivity.activitySelected; - if (listSelected.isEmpty) { - return ScreenTypeLayout.builder( - mobile: (_) => const NoActivityView("Pas d'activité sélectionnée"), - desktop: (_) => const NoActivityView("Pas d'activité sélectionnée"), - ); - } - if (listSelected.length > 1) { - return ScreenTypeLayout.builder( - mobile: (_) => const NoActivityView( - "Qu'une seule activité doit être sélectionner"), - desktop: (_) => const NoActivityView( - "Qu'une seule activité doit être sélectionner"), - ); - } - return ScreenTypeLayout.builder( - mobile: (_) => const MobileHomeView(), - desktop: (_) => const WebHomeView(), - ); + final selectedActivitiesCount = + context.watch().managerSelectedActivity.activitySelected.length; + + return selectedActivitiesCount == 1 + ? ScreenTypeLayout.builder( + mobile: (_) => const MobileHomeView(), + desktop: (_) => const WebHomeView(), + ) + : selectedActivitiesCount > 1 + ? const StatAtivities() + : ScreenTypeLayout.builder( + mobile: (_) => + const NoActivityView("Pas d'activité sélectionnée"), + desktop: (_) => + const NoActivityView("Pas d'activité sélectionnée"), + ); } } diff --git a/lib/view/home/mobile/mobile_homeview.dart b/lib/view/home/mobile/mobile_homeview.dart index 3c7439c..9794461 100644 --- a/lib/view/home/mobile/mobile_homeview.dart +++ b/lib/view/home/mobile/mobile_homeview.dart @@ -30,17 +30,23 @@ class _MobileHomeView extends State { data = HomeViewUtil().initData(context); // -- BPM -- // - data.maxBPM = managerSelectedActivity.getMaxBpm(); - data.minBPM = managerSelectedActivity.getMinBpm(); - int avgBpm = managerSelectedActivity.getAvgBpm(); + data.maxBPM = + managerSelectedActivity.activitySelected.first.activityInfo.bpmMax; + data.minBPM = + managerSelectedActivity.activitySelected.first.activityInfo.bpmMin; + int avgBpm = + managerSelectedActivity.activitySelected.first.activityInfo.bpmAvg; // -- Altitude -- // - double minAltitude = managerSelectedActivity.getMinAltitude(); - double maxAltitude = managerSelectedActivity.getMaxAltitude(); - double avgAltitude = (maxAltitude + minAltitude) / 2; + double minAltitude = + managerSelectedActivity.activitySelected.first.activityInfo.altitudeMin; + double maxAltitude = + managerSelectedActivity.activitySelected.first.activityInfo.altitudeMax; + double avgAltitude = + managerSelectedActivity.activitySelected.first.activityInfo.altitudeAvg; // -- Speed -- // - double maxSpeed = managerSelectedActivity.getMaxSpeed(); - double avgSpeed = managerSelectedActivity.getAvgSpeed(); + double maxSpeed = managerSelectedActivity.getMaxSpeedAllActivitySelected(); + double avgSpeed = managerSelectedActivity.getAvgSpeedAllActivitySelected(); return Scaffold( backgroundColor: TColor.white, diff --git a/lib/view/home/stats_activities_view.dart b/lib/view/home/stats_activities_view.dart new file mode 100644 index 0000000..a0c5d0a --- /dev/null +++ b/lib/view/home/stats_activities_view.dart @@ -0,0 +1,235 @@ +import 'package:flutter/material.dart'; +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/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'; +import 'package:smartfit_app_mobile/modele/utile/home_view/home_view_util.dart'; + +class StatAtivities extends StatefulWidget { + const StatAtivities({super.key}); + + @override + State createState() => _StatAtivities(); +} + +class _StatAtivities extends State { + late DataHomeView data; + TextEditingController bpmController = TextEditingController(); + + @override + Widget build(BuildContext context) { + var media = MediaQuery.of(context).size; + data = HomeViewUtil().initData(context); + ManagerSelectedActivity managerSelectedActivity = + context.watch().managerSelectedActivity; + + // -- BPM -- // + int maxBpm = managerSelectedActivity.getBpmMaxAllActivitieSelected(); + int minBpm = managerSelectedActivity.getBpmMinAllActivitieSelected(); + int avgBpm = managerSelectedActivity.getBpmAvgAllActivitieSelected(); + // -- Altitude -- // + double maxAltitude = + managerSelectedActivity.getMaxAltitudeAllActivitySelected(); + double minAltitude = + managerSelectedActivity.getMinAltitudeAllActivitySelected(); + double avgAltitude = + managerSelectedActivity.getAvgAltitudeAllActivitySelected(); + + // -- Température -- // + double avgTemperature = context + .watch() + .managerSelectedActivity + .getAvgTemperatureAllActivitySelected() + .toDouble(); + double maxTemperature = context + .watch() + .managerSelectedActivity + .getMaxTemperatureAllActivitySelected() + .toDouble(); + double minTemperature = context + .watch() + .managerSelectedActivity + .getMinTemperatureAllActivitySelected() + .toDouble(); + // ----- Distance ---- // + double getTotalDistance = context + .watch() + .managerSelectedActivity + .getDistanceAllActivitySelected(); + // ---- Calories --- // + int totalCalories = context + .watch() + .managerSelectedActivity + .getCalorieAllActivitySelected(); + // --- Steps --- // + int totalSteps = context + .watch() + .managerSelectedActivity + .getStepsAllActivitySelected(); + // -- Time -- // + double totalTime = context + .watch() + .managerSelectedActivity + .getTimeAllActivitySelected(); + + // -- Speed -- // + double avgSpeed = context + .watch() + .managerSelectedActivity + .getAvgSpeedAllActivitySelected(); + double maxSpeed = context + .watch() + .managerSelectedActivity + .getMaxAltitudeAllActivitySelected(); + double minSpeed = context + .watch() + .managerSelectedActivity + .getMinAltitudeAllActivitySelected(); + + return Scaffold( + backgroundColor: TColor.white, + body: SingleChildScrollView( + child: SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: media.width * 0.03, + ), + const EnteteHomeView(), + SizedBox( + height: media.width * 0.05, + ), + Text( + "Status d'activité", + style: TextStyle( + color: TColor.black, + fontSize: 16, + fontWeight: FontWeight.w700), + ), + SizedBox( + height: media.width * 0.03, + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ContainerStatsActivities( + "$avgBpm BPM", "Moyenne Bpm", Icons.favorite), + SizedBox( + width: media.width * 0.03, + ), + ContainerStatsActivities( + "$maxBpm BPM", "Maximum Bpm", Icons.trending_up), + SizedBox( + width: media.width * 0.03, + ), + ContainerStatsActivities( + "$minBpm BPM", "Minimum Bpm", Icons.trending_down) + ], + ), + SizedBox( + height: media.width * 0.03, + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ContainerStatsActivities( + "$avgSpeed m/s", "Moyenne vitesse", Icons.bolt), + SizedBox( + width: media.width * 0.03, + ), + ContainerStatsActivities( + "$maxSpeed m/s", "Maximum vitesse", Icons.trending_up), + SizedBox( + width: media.width * 0.03, + ), + ContainerStatsActivities( + "$minSpeed m/s", "Minimum vitesse", Icons.trending_down) + ], + ), + SizedBox( + height: media.width * 0.03, + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ContainerStatsActivities("$avgTemperature °C", + "Moyenne Temperature", Icons.thermostat), + SizedBox( + width: media.width * 0.03, + ), + ContainerStatsActivities("$maxTemperature °C", + "Maximum Temperature", Icons.trending_up), + SizedBox( + width: media.width * 0.03, + ), + ContainerStatsActivities("$minTemperature °C", + "Minimum Temperature", Icons.trending_down) + ], + ), + SizedBox( + height: media.width * 0.03, + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ContainerStatsActivities( + "$avgAltitude m", "Moyenne Altitude", Icons.landscape), + SizedBox( + width: media.width * 0.03, + ), + ContainerStatsActivities("$maxAltitude m", + "Maximum Altitude", Icons.trending_up), + SizedBox( + width: media.width * 0.03, + ), + ContainerStatsActivities("$minAltitude m", + "Minimum Altitude", Icons.trending_down) + ], + ), + SizedBox( + height: media.width * 0.03, + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ContainerStatsActivities("$getTotalDistance m", + "Distance Totale", Icons.double_arrow), + SizedBox( + width: media.width * 0.03, + ), + ContainerStatsActivities( + "$totalSteps", "Total Pas", Icons.do_not_step_rounded), + ], + ), + SizedBox( + height: media.width * 0.03, + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ContainerStatsActivities( + "$totalTime s", "Temps Total", Icons.timer), + SizedBox( + width: media.width * 0.03, + ), + ContainerStatsActivities("$totalCalories kCal", + "Calories Dépensées", Icons.local_fire_department), + ], + ), + SizedBox( + height: media.width * 0.03, + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/view/home/web/web_homeview.dart b/lib/view/home/web/web_homeview.dart index 74ccf41..2498fd0 100644 --- a/lib/view/home/web/web_homeview.dart +++ b/lib/view/home/web/web_homeview.dart @@ -6,6 +6,7 @@ import 'package:smartfit_app_mobile/common_widget/graph/bpm_and_speed_by_time.da import 'package:smartfit_app_mobile/common_widget/graph/bpm_by_time.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/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'; import 'package:smartfit_app_mobile/modele/utile/home_view/home_view_util.dart'; @@ -24,24 +25,41 @@ class _WebHomeView extends State { @override Widget build(BuildContext context) { var media = MediaQuery.of(context).size; + ManagerSelectedActivity managerSelectedActivity = + context.watch().managerSelectedActivity; + // -- BPM -- // - int maxBpm = context.watch().managerSelectedActivity.getMaxBpm(); - int minBpm = context.watch().managerSelectedActivity.getMinBpm(); - int avgBpm = context.watch().managerSelectedActivity.getAvgBpm(); + int maxBpm = + managerSelectedActivity.activitySelected.first.activityInfo.bpmMax; + int minBpm = + managerSelectedActivity.activitySelected.first.activityInfo.bpmMin; + int avgBpm = + managerSelectedActivity.activitySelected.first.activityInfo.bpmAvg; // -- Altitude -- // double minAltitude = - context.watch().managerSelectedActivity.getMinAltitude(); + managerSelectedActivity.activitySelected.first.activityInfo.altitudeMin; double maxAltitude = - context.watch().managerSelectedActivity.getMaxAltitude(); - double avgAltitude = (maxAltitude + minAltitude) / 2; + managerSelectedActivity.activitySelected.first.activityInfo.altitudeMax; + double avgAltitude = + managerSelectedActivity.activitySelected.first.activityInfo.altitudeAvg; // -- Speed -- // - double maxSpeed = - context.watch().managerSelectedActivity.getMaxSpeed(); - double avgSpeed = - context.watch().managerSelectedActivity.getAvgSpeed(); + double maxSpeed = context + .watch() + .managerSelectedActivity + .getMaxSpeedAllActivitySelected(); + double avgSpeed = context + .watch() + .managerSelectedActivity + .getAvgSpeedAllActivitySelected(); data = HomeViewUtil().initData(context); - + data.maxBPM = maxBpm; + data.minBPM = minBpm; + data.maxSpeed = maxSpeed; + data.time = context + .watch() + .managerSelectedActivity + .getTimeAllActivitySelected(); return Scaffold( backgroundColor: TColor.white, body: SingleChildScrollView( @@ -103,7 +121,7 @@ class _WebHomeView extends State { fontWeight: FontWeight.w700), ), SizedBox( - height: media.width * 0.02, + height: media.width * 0.03, ), Row( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/view/main_tab/web/web_main_tab_view.dart b/lib/view/main_tab/web/web_main_tab_view.dart index 271fed6..3a077d1 100644 --- a/lib/view/main_tab/web/web_main_tab_view.dart +++ b/lib/view/main_tab/web/web_main_tab_view.dart @@ -50,34 +50,34 @@ class _WebMainTabViewState extends State { onTap: () => updateTab(1, const Activity()), ), InkWell( - onTap: () { - updateTab(4, const ListActivity()); - }, - child: Container( - width: 65, - height: 65, - decoration: BoxDecoration( - gradient: LinearGradient( - colors: TColor.primaryG, - ), - borderRadius: BorderRadius.circular(35), - boxShadow: const [ - BoxShadow( - color: Colors.black12, - blurRadius: 2, - ), - ], - ), - child: Icon( - Icons.search, - color: TColor.white, - size: 35, + onTap: () { + updateTab(4, const ListActivity()); + }, + child: Container( + width: 65, + height: 65, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: TColor.primaryG, ), + borderRadius: BorderRadius.circular(35), + boxShadow: const [ + BoxShadow( + color: Colors.black12, + blurRadius: 2, + ), + ], + ), + child: Icon( + Icons.search, + color: TColor.white, + size: 35, ), ), + ), sideBarButton( - icon: "assets/img/Camera_tab.svg", - selectIcon: "assets/img/Camera_tab_select.svg", + icon: "assets/img/icon_map.svg", + selectIcon: "assets/img/icon_map.svg", index: 2, onTap: () => updateTab(2, const MyMap()), ), @@ -94,19 +94,20 @@ class _WebMainTabViewState extends State { child: Container( decoration: BoxDecoration( color: TColor.white, - boxShadow: [ + boxShadow: const [ BoxShadow( color: Colors.black12, blurRadius: 2, - offset: const Offset(-2, 0), // Ajout d'une ombre sur le côté + offset: Offset(-2, 0), // Ajout d'une ombre sur le côté ), ], ), child: Scaffold( backgroundColor: TColor.white, body: currentTab, - floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, - floatingActionButton: SizedBox( + floatingActionButtonLocation: + FloatingActionButtonLocation.centerDocked, + floatingActionButton: const SizedBox( width: 70, height: 70, ), diff --git a/lib/view/map/chose_map.dart b/lib/view/map/chose_map.dart new file mode 100644 index 0000000..1ddd3bd --- /dev/null +++ b/lib/view/map/chose_map.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:smartfit_app_mobile/common/colo_extension.dart'; +import 'package:smartfit_app_mobile/view/map/mobile/mobile_my_map.dart'; +import 'package:smartfit_app_mobile/view/map/my_map_osm.dart'; + +class ChoseMap extends StatefulWidget { + const ChoseMap({Key? key}) : super(key: key); + + @override + State createState() => _ChoseMap(); +} + +class _ChoseMap extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: TColor.white, + body: SafeArea( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + TextButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const MobileMyMaps())); + }, + child: const Text("Use map with google map")), + TextButton( + onPressed: () { + Navigator.push(context, + MaterialPageRoute(builder: (context) => const MyMapOSM())); + }, + child: const Text("Use map with Open Street Map")), + const Text( + "Mettre une image la en mode une personne avec des jumelles") + ], + )), + ); + } +} diff --git a/lib/view/map/mobile/mobile_my_map.dart b/lib/view/map/mobile/mobile_my_map.dart index a3b35dc..4657afe 100644 --- a/lib/view/map/mobile/mobile_my_map.dart +++ b/lib/view/map/mobile/mobile_my_map.dart @@ -20,11 +20,14 @@ class _MobileMyMaps extends State { Widget build(BuildContext context) { _polylines = MapUtil().initPolines(context, 10, TColor.primaryColor1); _cameraPosition = - CameraPosition(target: _polylines.first.points.first, zoom: 18); + CameraPosition(target: _polylines.first.points.first, zoom: 15); return Scaffold( - body: _getMap(), - ); + appBar: AppBar( + title: const Text("Carte Google Map "), + backgroundColor: TColor.primaryColor1, + ), + body: _getMap()); } Widget _getMap() { diff --git a/lib/view/map/my_map.dart b/lib/view/map/my_map.dart index a234d6f..07782af 100644 --- a/lib/view/map/my_map.dart +++ b/lib/view/map/my_map.dart @@ -4,8 +4,7 @@ import 'package:responsive_builder/responsive_builder.dart'; import 'package:smartfit_app_mobile/modele/activity.dart'; import 'package:smartfit_app_mobile/modele/user.dart'; import 'package:smartfit_app_mobile/view/home/no_activity_view.dart'; -import 'package:smartfit_app_mobile/view/map/mobile/mobile_my_map.dart'; -import 'package:smartfit_app_mobile/view/map/web/web_my_map.dart'; +import 'package:smartfit_app_mobile/view/map/chose_map.dart'; class MyMap extends StatefulWidget { const MyMap({Key? key}) : super(key: key); @@ -35,8 +34,8 @@ class _MyMapState extends State { ); } return ScreenTypeLayout.builder( - mobile: (_) => const MobileMyMaps(), - desktop: (_) => const WebMyMaps(), + mobile: (_) => const ChoseMap(), + desktop: (_) => const ChoseMap(), ); } } diff --git a/lib/view/map/my_map_osm.dart b/lib/view/map/my_map_osm.dart new file mode 100644 index 0000000..3d5a074 --- /dev/null +++ b/lib/view/map/my_map_osm.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:latlong2/latlong.dart' as osm; +import 'package:smartfit_app_mobile/common/colo_extension.dart'; +import 'package:smartfit_app_mobile/modele/user.dart'; + +class MyMapOSM extends StatefulWidget { + const MyMapOSM({Key? key}) : super(key: key); + + @override + State createState() => _MyMapOSM(); +} + +class _MyMapOSM extends State { + final controller = MapController(); + + @override + Widget build(BuildContext context) { + List listPolynines = + context.watch().managerSelectedActivity.getPositionOSM(); + + return Scaffold( + appBar: AppBar( + title: const Text("Carte Open Street Map "), + backgroundColor: TColor.primaryColor1, + ), + body: FlutterMap( + options: MapOptions(center: listPolynines.first), + children: [ + TileLayer( + urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', + ), + PolylineLayer( + polylines: [ + Polyline( + points: listPolynines, + color: TColor.primaryColor1, + strokeWidth: 5.0, + ), + ], + ), + ], + ), + ); + } +} diff --git a/lib/view/map/test.dart b/lib/view/map/test.dart deleted file mode 100644 index 717dfbb..0000000 --- a/lib/view/map/test.dart +++ /dev/null @@ -1,117 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:google_maps_flutter/google_maps_flutter.dart'; -import 'package:location/location.dart'; - -class MyMap extends StatefulWidget { - const MyMap({ Key? key }) : super(key: key); - - @override - State createState() => _MyMapState(); -} - -class _MyMapState extends State { - - Completer _googleMapController = Completer(); - CameraPosition? _cameraPosition; - Location? _location; - LocationData? _currentLocation; - - - - @override - void initState() { - _init(); - super.initState(); - } - - _init() async { - _location = Location(); - _cameraPosition = CameraPosition( - target: LatLng(0, 0), // this is just the example lat and lng for initializing - zoom: 15 - ); - _initLocation(); - } - - //function to listen when we move position - _initLocation() { - //use this to go to current location instead - _location?.getLocation().then((location) { - _currentLocation = location; - }); - _location?.onLocationChanged.listen((newLocation) { - _currentLocation = newLocation; - moveToPosition(LatLng(_currentLocation?.latitude ?? 0, _currentLocation?.longitude ?? 0)); - }); - } - - moveToPosition(LatLng latLng) async { - GoogleMapController mapController = await _googleMapController.future; - mapController.animateCamera( - CameraUpdate.newCameraPosition( - CameraPosition( - target: latLng, - zoom: 15 - ) - ) - ); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - body: _buildBody(), - ); - } - - Widget _buildBody() { - return _getMap(); - } - - Widget _getMarker() { - return Container( - width: 40, - height: 40, - padding: EdgeInsets.all(2), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(100), - boxShadow: [ - BoxShadow( - color: Colors.grey, - offset: Offset(0,3), - spreadRadius: 4, - blurRadius: 6 - ) - ] - ), - child: ClipOval(child: Image.asset("assets/img/u1.png")), - ); - } - - Widget _getMap() { - return Stack( - children: [ - GoogleMap( - initialCameraPosition: _cameraPosition!, - mapType: MapType.normal, - onMapCreated: (GoogleMapController controller) { - // now we need a variable to get the controller of google map - if (!_googleMapController.isCompleted) { - _googleMapController.complete(controller); - } - }, - ), - - Positioned.fill( - child: Align( - alignment: Alignment.center, - child: _getMarker() - ) - ) - ], - ); - } -} \ No newline at end of file diff --git a/lib/view/map/web/web_my_map.dart b/lib/view/map/web/web_my_map.dart index 6c4517a..8b6167a 100644 --- a/lib/view/map/web/web_my_map.dart +++ b/lib/view/map/web/web_my_map.dart @@ -21,11 +21,14 @@ class _WebMyMaps extends State { Widget build(BuildContext context) { _polylines = MapUtil().initPolines(context, 10, TColor.primaryColor1); _cameraPosition = - CameraPosition(target: _polylines.first.points.first, zoom: 18); + CameraPosition(target: _polylines.first.points.first, zoom: 5); return Scaffold( - body: _getMap(), - ); + appBar: AppBar( + title: const Text("Carte Google Map "), + backgroundColor: TColor.primaryColor1, + ), + body: _getMap()); } Widget _getMap() { diff --git a/lib/view/test/page_test.dart b/lib/view/test/page_test.dart index 0ccb04c..83fca6f 100644 --- a/lib/view/test/page_test.dart +++ b/lib/view/test/page_test.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:crypto/crypto.dart'; +import 'package:csv/csv.dart'; import 'package:fit_tool/fit_tool.dart'; import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter/material.dart'; @@ -8,6 +9,7 @@ import 'package:file_picker/file_picker.dart'; import 'package:provider/provider.dart'; import 'dart:io'; import 'package:smartfit_app_mobile/modele/api/i_data_strategy.dart'; +import 'package:smartfit_app_mobile/modele/manager_file.dart'; import 'package:smartfit_app_mobile/modele/user.dart'; import 'package:smartfit_app_mobile/modele/api/request_api.dart'; import 'package:tuple/tuple.dart'; @@ -82,6 +84,8 @@ class _TestPage extends State { //late File x = File(file.path); Future readFile() async { + ManagerFile managerFile = ManagerFile(); + PlatformFile t = result!.files.single; String? y = t.path; if (t.path == null) { @@ -91,15 +95,13 @@ class _TestPage extends State { final content = await file.readAsBytes(); FitFile fitFile = FitFile.fromBytes(content); //print(fitFile.toRows()); - print("--------------"); - print("--------------"); - print("--------------"); //print("${await _managerFile.localPath}\\test.csv"); - //final outFile = File("${await _managerFile.localPath}\\test.csv"); - //final csv = const ListToCsvConverter().convert(fitFile.toRows()); - //await outFile.writeAsString(csv); + final outFile = File("${await managerFile.localPath}\\test.csv"); + final csv = const ListToCsvConverter().convert(fitFile.toRows()); + await outFile.writeAsString(csv); + /* // ----------- Lire le fit et extarire les données qu'on choisi ----------- // List liste = fitFile.records; List allowedField = [ @@ -180,7 +182,7 @@ class _TestPage extends State { //print(x.getDistanceWithTime(ActivityOfUser(result))); //print(x.getDistance(ActivityOfUser(result))); //print(x.getAltitudeWithTime(ActivityOfUser(result))); - //print(x.getSpeedWithTime(ActivityOfUser(result))); + //print(x.getSpeedWithTime(ActivityOfUser(result)));*/ } } diff --git a/pubspec.yaml b/pubspec.yaml index c1a5239..275c447 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -60,6 +60,8 @@ dependencies: objectbox: ^2.3.1 objectbox_flutter_libs: any path: ^1.8.3 + flutter_map: ^5.0.0 + latlong2: ^0.9.0 dev_dependencies: flutter_test: