From 39228cc347f739ba3c3bd92bb827e0fa36edfe6d Mon Sep 17 00:00:00 2001 From: bardram Date: Thu, 11 Jan 2024 13:31:19 +0100 Subject: [PATCH] fix of bug in data viz #204 --- android/gradle.properties | 3 +- lib/blocs/app_bloc.dart | 12 ++- lib/carp_study_app.dart | 12 +-- lib/ui/pages/data_visualization_page.dart | 99 ++++++++----------- lib/ui/widgets/details_banner.dart | 3 +- .../cards/activity_data_model.dart | 4 +- .../cards/heart_rate_data_model.dart | 4 +- .../cards/mobility_data_model.dart | 4 +- lib/view_models/cards/steps_data_model.dart | 4 +- .../data_visualization_page_model.dart | 23 +++-- lib/view_models/view_model.dart | 26 ++--- 11 files changed, 92 insertions(+), 102 deletions(-) diff --git a/android/gradle.properties b/android/gradle.properties index 94adc3a3..d2032bce 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,3 +1,4 @@ org.gradle.jvmargs=-Xmx1536M -android.useAndroidX=true android.enableJetifier=true +android.useAndroidX=true +android.enableR8=true diff --git a/lib/blocs/app_bloc.dart b/lib/blocs/app_bloc.dart index 9f76338e..db9b3647 100644 --- a/lib/blocs/app_bloc.dart +++ b/lib/blocs/app_bloc.dart @@ -17,7 +17,7 @@ enum StudyAppState { class StudyAppBLoC { StudyAppState _state = StudyAppState.created; final CarpBackend _backend = CarpBackend(); - final CarpStudyAppViewModel _data = CarpStudyAppViewModel(); + final CarpStudyAppViewModel _appViewModel = CarpStudyAppViewModel(); StudyDeploymentStatus? _status; final StreamController _stateStream = StreamController.broadcast(); @@ -102,7 +102,7 @@ class StudyAppBLoC { DateTime? get studyStartTimestamp => deployment?.deployed; /// The overall data model for this app - CarpStudyAppViewModel get data => _data; + CarpStudyAppViewModel get appViewModel => _appViewModel; // Future? dataPageInitialization; /// Initialize this BLOC. Called before being used for anything. @@ -149,7 +149,6 @@ class StudyAppBLoC { if (isConfiguring) return; stateStream.sink.add(StudiesAppState.configuring); - info('$runtimeType configuring...'); // set up and initialize sensing await Sensing().initialize(); @@ -158,7 +157,9 @@ class StudyAppBLoC { await Sensing().addStudy(); // initialize the UI data models - await data.init(Sensing().controller!); + appViewModel.init(Sensing().controller!); + + debug('$runtimeType - done init() of view model'); // set up the messaging part messageManager.initialize().then( @@ -241,7 +242,8 @@ class StudyAppBLoC { ? user!.username : Sensing().controller!.deployment!.userId!; - /// The name used for friendly greeting - '' if no user logged in. + /// The name used for friendly greeting. + /// Returns an empty string if no user logged in. String? get friendlyUsername => (user != null) ? user!.firstName : ''; /// Does this [deployment] have any measures? diff --git a/lib/carp_study_app.dart b/lib/carp_study_app.dart index 9a0d65af..50b9fd2b 100644 --- a/lib/carp_study_app.dart +++ b/lib/carp_study_app.dart @@ -48,7 +48,7 @@ class CarpStudyAppState extends State { parentNavigatorKey: _shellNavigatorKey, pageBuilder: (context, state) => CustomTransitionPage( child: TaskListPage( - bloc.data.taskListPageViewModel, + bloc.appViewModel.taskListPageViewModel, ), transitionsBuilder: bottomNavigationBarAnimation, ), @@ -58,7 +58,7 @@ class CarpStudyAppState extends State { parentNavigatorKey: _shellNavigatorKey, pageBuilder: (context, state) => CustomTransitionPage( child: StudyPage( - bloc.data.studyPageViewModel, + bloc.appViewModel.studyPageViewModel, ), transitionsBuilder: bottomNavigationBarAnimation, ), @@ -68,7 +68,7 @@ class CarpStudyAppState extends State { parentNavigatorKey: _shellNavigatorKey, pageBuilder: (context, state) => CustomTransitionPage( child: DataVisualizationPage( - bloc.data.dataVisualizationPageViewModel), + bloc.appViewModel.dataVisualizationPageViewModel), transitionsBuilder: bottomNavigationBarAnimation, ), ), @@ -84,7 +84,7 @@ class CarpStudyAppState extends State { path: '/profile', parentNavigatorKey: _shellNavigatorKey, pageBuilder: (context, state) => CustomTransitionPage( - child: ProfilePage(bloc.data.profilePageViewModel), + child: ProfilePage(bloc.appViewModel.profilePageViewModel), transitionsBuilder: bottomNavigationBarAnimation, ), ), @@ -111,7 +111,7 @@ class CarpStudyAppState extends State { ? firstRoute : (bloc.studyId == null ? '/invitations' : null), builder: (context, state) => InformedConsentPage( - bloc.data.informedConsentViewModel, + bloc.appViewModel.informedConsentViewModel, ), ), GoRoute( @@ -139,7 +139,7 @@ class CarpStudyAppState extends State { ? '/consent' : (bloc.user == null ? '/login' : null), builder: (context, state) => - InvitationListPage(bloc.data.invitationsListViewModel), + InvitationListPage(bloc.appViewModel.invitationsListViewModel), ), ], debugLogDiagnostics: true, diff --git a/lib/ui/pages/data_visualization_page.dart b/lib/ui/pages/data_visualization_page.dart index b603b4c0..9b33c484 100644 --- a/lib/ui/pages/data_visualization_page.dart +++ b/lib/ui/pages/data_visualization_page.dart @@ -14,61 +14,49 @@ class _DataVisualizationPageState extends State { Widget build(BuildContext context) { RPLocalizations locale = RPLocalizations.of(context)!; return Scaffold( - backgroundColor: Theme.of(context).colorScheme.secondary, - body: SafeArea( - child: FutureBuilder( - future: bloc.data._dataVisualizationPageViewModel - .init(Sensing().controller!), - builder: (context, data) { - if (data.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); - } else { - return Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const CarpAppBar(), - Container( - color: Theme.of(context).colorScheme.secondary, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: Align( - alignment: Alignment.centerLeft, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '${locale.translate('pages.data_viz.hello')} ${bloc.friendlyUsername}' - .toUpperCase(), - style: dataCardTitleStyle.copyWith( - color: Theme.of(context).primaryColor), - ), - Text(locale.translate('pages.data_viz.thanks'), - style: aboutCardSubtitleStyle), - const SizedBox(height: 15), - ], - ), + backgroundColor: Theme.of(context).colorScheme.secondary, + body: SafeArea( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const CarpAppBar(), + Container( + color: Theme.of(context).colorScheme.secondary, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: Align( + alignment: Alignment.centerLeft, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '${locale.translate('pages.data_viz.hello')} ${bloc.friendlyUsername}' + .toUpperCase(), + style: dataCardTitleStyle.copyWith( + color: Theme.of(context).primaryColor), ), - ), + Text(locale.translate('pages.data_viz.thanks'), + style: aboutCardSubtitleStyle), + const SizedBox(height: 15), + ], ), - Expanded( - flex: 4, - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: _dataVizCards, - ), - ), - ) - ], - ); - } - }, - ), - ), - ); + ), + ), + ), + Expanded( + flex: 4, + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: _dataVizCards, + ), + ), + ) + ], + ))); } // The list of cards, depending on what measures are defined in the study. @@ -109,11 +97,6 @@ class _DataVisualizationPageState extends State { widgets.add(MediaCardWidget(mediaModelsList)); } - // check to show device data visualizations - if (bloc.hasDevices()) { - //TODO ADD DEVICES VIZ? - } - if (bloc.hasMeasure(SensorSamplingPackage.STEP_COUNT)) { widgets.add(StepsCardWidget(widget.model.stepsCardDataModel)); } diff --git a/lib/ui/widgets/details_banner.dart b/lib/ui/widgets/details_banner.dart index bde0294d..20f42ddb 100644 --- a/lib/ui/widgets/details_banner.dart +++ b/lib/ui/widgets/details_banner.dart @@ -17,7 +17,8 @@ class DetailsBanner extends StatelessWidget { if (imagePath != null && imagePath!.isNotEmpty) SizedBox( height: 300, - child: bloc.data.studyPageViewModel.getMessageImage(imagePath), + child: + bloc.appViewModel.studyPageViewModel.getMessageImage(imagePath), ), Padding( padding: const EdgeInsets.all(16), diff --git a/lib/view_models/cards/activity_data_model.dart b/lib/view_models/cards/activity_data_model.dart index d791f944..d99c3614 100644 --- a/lib/view_models/cards/activity_data_model.dart +++ b/lib/view_models/cards/activity_data_model.dart @@ -15,8 +15,8 @@ class ActivityCardViewModel extends SerializableViewModel { .where((measurement) => measurement.data is Activity); @override - Future init(SmartphoneDeploymentController ctrl) async { - await super.init(ctrl); + void init(SmartphoneDeploymentController ctrl) { + super.init(ctrl); // listen for activity events and count the minutes activityEvents?.listen((measurement) { diff --git a/lib/view_models/cards/heart_rate_data_model.dart b/lib/view_models/cards/heart_rate_data_model.dart index a10d1b6d..6f73692a 100644 --- a/lib/view_models/cards/heart_rate_data_model.dart +++ b/lib/view_models/cards/heart_rate_data_model.dart @@ -25,8 +25,8 @@ class HeartRateCardViewModel extends SerializableViewModel { .where((measurement) => measurement.data is PolarHR); @override - Future init(SmartphoneDeploymentController ctrl) async { - await super.init(ctrl); + void init(SmartphoneDeploymentController ctrl) { + super.init(ctrl); heartRateEvents?.listen( (heartRateDataPoint) { diff --git a/lib/view_models/cards/mobility_data_model.dart b/lib/view_models/cards/mobility_data_model.dart index 8a7b4b29..138ae7d0 100644 --- a/lib/view_models/cards/mobility_data_model.dart +++ b/lib/view_models/cards/mobility_data_model.dart @@ -12,8 +12,8 @@ class MobilityCardViewModel extends SerializableViewModel { MobilityCardViewModel(); @override - Future init(SmartphoneDeploymentController ctrl) async { - await super.init(ctrl); + void init(SmartphoneDeploymentController ctrl) { + super.init(ctrl); // listen for mobility events and update the features mobilityEvents?.listen((measurement) { diff --git a/lib/view_models/cards/steps_data_model.dart b/lib/view_models/cards/steps_data_model.dart index 2e5d8c6b..8ae1aa98 100644 --- a/lib/view_models/cards/steps_data_model.dart +++ b/lib/view_models/cards/steps_data_model.dart @@ -17,8 +17,8 @@ class StepsCardViewModel extends SerializableViewModel { .where((dataPoint) => dataPoint.data is StepCount); @override - Future init(SmartphoneDeploymentController ctrl) async { - await super.init(ctrl); + void init(SmartphoneDeploymentController ctrl) { + super.init(ctrl); // listen for pedometer events and count them pedometerEvents?.listen((pedometerDataPoint) { diff --git a/lib/view_models/data_visualization_page_model.dart b/lib/view_models/data_visualization_page_model.dart index dcd84693..4c9f11dc 100644 --- a/lib/view_models/data_visualization_page_model.dart +++ b/lib/view_models/data_visualization_page_model.dart @@ -49,19 +49,18 @@ class DataVisualizationPageViewModel extends ViewModel { DataVisualizationPageViewModel(); @override - Future init(SmartphoneDeploymentController ctrl) async { + void init(SmartphoneDeploymentController ctrl) { super.init(ctrl); - await _activityCardDataModel.init(ctrl); - await _stepsCardDataModel.init(ctrl); - await _heartRateCardDataModel.init(ctrl); - await _mobilityCardDataModel.init(ctrl); - await _measuresCardDataModel.init(ctrl); - await _surveysCardDataModel.init(ctrl); - await _audioCardDataModel.init(ctrl); - await _videoCardDataModel.init(ctrl); - await _imageCardDataModel.init(ctrl); - await _studyProgressCardDataModel.init(ctrl); - return 1; + _activityCardDataModel.init(ctrl); + _stepsCardDataModel.init(ctrl); + _heartRateCardDataModel.init(ctrl); + _mobilityCardDataModel.init(ctrl); + _measuresCardDataModel.init(ctrl); + _surveysCardDataModel.init(ctrl); + _audioCardDataModel.init(ctrl); + _videoCardDataModel.init(ctrl); + _imageCardDataModel.init(ctrl); + _studyProgressCardDataModel.init(ctrl); } /// Delete all persistent view models. diff --git a/lib/view_models/view_model.dart b/lib/view_models/view_model.dart index 99f4007d..4a20679e 100644 --- a/lib/view_models/view_model.dart +++ b/lib/view_models/view_model.dart @@ -8,7 +8,9 @@ abstract class ViewModel { /// Initialize this view model before use. @mustCallSuper - Future init(SmartphoneDeploymentController ctrl) async { + void init(SmartphoneDeploymentController ctrl) { + debug('$runtimeType - init()'); + _controller = ctrl; } } @@ -43,12 +45,12 @@ abstract class SerializableViewModel extends ViewModel { @override @mustCallSuper - Future init(SmartphoneDeploymentController ctrl) async { + void init(SmartphoneDeploymentController ctrl) { super.init(ctrl); _model = createModel(); // restore the data model (if any saved) - await restore().then((savedModel) => _model = savedModel ?? _model); + restore().then((savedModel) => _model = savedModel ?? _model); // save the data model on a regular basis. Timer.periodic(const Duration(minutes: 3), (_) => save(model.toJson())); @@ -170,13 +172,15 @@ class CarpStudyAppViewModel extends ViewModel { _informedConsentViewModel; @override - Future init(SmartphoneDeploymentController ctrl) async { - await super.init(ctrl); - await _studyPageViewModel.init(ctrl); - await _taskListPageViewModel.init(ctrl); - await _profilePageViewModel.init(ctrl); - await _devicesPageViewModel.init(ctrl); - await _invitationsListViewModel.init(ctrl); - await _informedConsentViewModel.init(ctrl); + // Future init(SmartphoneDeploymentController ctrl) async { + void init(SmartphoneDeploymentController ctrl) { + super.init(ctrl); + _studyPageViewModel.init(ctrl); + _taskListPageViewModel.init(ctrl); + _profilePageViewModel.init(ctrl); + _devicesPageViewModel.init(ctrl); + _invitationsListViewModel.init(ctrl); + _informedConsentViewModel.init(ctrl); + _dataVisualizationPageViewModel.init(ctrl); } }