Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding health connect warning message. #320

Merged
merged 13 commits into from
Aug 30, 2024
3 changes: 3 additions & 0 deletions assets/lang/da.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,12 @@
"expired": "udløbet",
"completed": "færdig",
"cancel": "Anuller",
"install": "Installer",
"pages.about.message.read_more": "Tryk for at læse mere",
"pages.about.study.website": "Hjemmeside",
"pages.about.study.privacy": "Fortrolighedspolitik",
"pages.about.install_health_connect.title": "Health Connect påkrævet",
"pages.about.install_health_connect.description": "Denne app kræver, at Health Connect fungerer korrekt. Vil du installere det?",
"dialog.location.permission": "Lokaliseringstjeneste",
"dialog.location.allow": "Tillad",
"pages.ic.need_accept": "Du skal acceptere denne informerede samtykke for at kunne deltage i dette studie.",
Expand Down
3 changes: 3 additions & 0 deletions assets/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,12 @@
"expired": "expired",
"completed": "completed",
"cancel": "Cancel",
"install": "Install",
"pages.about.message.read_more": "Tap to read more",
"pages.about.study.website": "Website",
"pages.about.study.privacy": "Privacy policy",
"pages.about.install_health_connect.title": "Health Connect Required",
"pages.about.install_health_connect.description": "This app requires Health Connect to function properly. Would you like to install it?",
"dialog.location.permission": "Permission Required",
"dialog.location.allow": "Allow",
"pages.ic.need_accept": "You need to accept the informed consent to participate in this study.",
Expand Down
20 changes: 20 additions & 0 deletions lib/blocs/app_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class StudyAppBLoC extends ChangeNotifier {
List<Message> _messages = [];
final StreamController<int> _messageStreamController =
StreamController.broadcast();
final String _healthConnectPackageName = 'com.google.android.apps.healthdata';

/// The state of this BloC.
StudyAppState get state => _state;
Expand Down Expand Up @@ -84,6 +85,8 @@ class StudyAppBLoC extends ChangeNotifier {
GlobalKey<ScaffoldMessengerState> get scaffoldKey => _scaffoldKey;
State? get scaffoldMessengerState => scaffoldKey.currentState;

String get healthConnectPackageName => _healthConnectPackageName;

/// Create the BLoC for the app.
StudyAppBLoC() : super() {
const dep =
Expand Down Expand Up @@ -151,6 +154,10 @@ class StudyAppBLoC extends ChangeNotifier {
/// The overall data model for this app
CarpStudyAppViewModel get appViewModel => _appViewModel;

final appCheck = AppCheck();

List<AppInfo>? installedApps;

/// Initialize this BLOC. Called before being used for anything.
Future<void> initialize() async {
if (isInitialized) return;
Expand Down Expand Up @@ -178,6 +185,19 @@ class StudyAppBLoC extends ChangeNotifier {
element == ConnectivityResult.wifi);
}

Future<bool> _isHealthConnectInstalled() async {
try {
final apps = await appCheck.getInstalledApps();
if (apps != null) {
return apps.any((app) => app.packageName == _healthConnectPackageName);
}
} catch (e) {
debug("Error checking Health Connect installation: $e");
return false;
}
return false;
}

/// Set the active study in the app based on an [invitation].
///
/// If a [context] is provided, the translation for this study is re-loaded
Expand Down
2 changes: 2 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import 'package:permission_handler/permission_handler.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:open_settings_plus/open_settings_plus.dart';
import 'package:open_settings_plus/core/open_settings_plus.dart';
import 'package:appcheck/appcheck.dart';

// the CARP packages
import 'package:carp_serializable/carp_serializable.dart';
Expand Down Expand Up @@ -77,6 +78,7 @@ part 'view_models/user_tasks.dart';
part 'carp_study_app.dart';
part 'ui/pages/informed_consent_page.dart';
part 'ui/pages/home_page.dart';
part 'ui/pages/home_page.install_health_connect_dialog.dart';
part 'ui/carp_study_style.dart';
part 'ui/colors.dart';
part 'ui/pages/data_visualization_page.dart';
Expand Down
16 changes: 16 additions & 0 deletions lib/ui/pages/home_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,22 @@ class HomePageState extends State<HomePage> {
// - starting sensing
askForLocationPermissions(context)
.then((_) => bloc.configureStudy().then((_) => bloc.start()));

if (Platform.isAndroid) {
// Check if HealthConnect is installed
_checkHealthConnectInstallation();
}
}

Future<void> _checkHealthConnectInstallation() async {
bool isInstalled = await bloc._isHealthConnectInstalled();
if (!isInstalled) {
showDialog<void>(
context: context,
barrierDismissible: true,
builder: (context) => InstallHealthConnectDialog(context),
);
}
}

@override
Expand Down
46 changes: 46 additions & 0 deletions lib/ui/pages/home_page.install_health_connect_dialog.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
part of carp_study_app;

class InstallHealthConnectDialog extends StatelessWidget {
const InstallHealthConnectDialog(BuildContext context, {super.key});

@override
Widget build(BuildContext context) {
RPLocalizations locale = RPLocalizations.of(context)!;
return AlertDialog(
titlePadding: const EdgeInsets.symmetric(vertical: 4),
insetPadding: const EdgeInsets.symmetric(vertical: 24, horizontal: 40),
title: const DialogTitle(
title: "pages.about.install_health_connect.title",
),
content: Text(
locale.translate('pages.about.install_health_connect.description'),
style: aboutCardContentStyle,
textAlign: TextAlign.justify,
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(locale.translate('cancel')),
),
TextButton(
child: Text(locale.translate('install')),
onPressed: () async {
_redirectToHealthConnectPlayStore();
Navigator.of(context).pop();
},
),
],
);
}

void _redirectToHealthConnectPlayStore() async {
final Uri url = Uri.parse(
'https://play.google.com/store/apps/details?id=${bloc.healthConnectPackageName}');
var canLaunch = await canLaunchUrl(url);
if (canLaunch) {
await launchUrl(url);
} else {
throw 'Could not launch $url';
}
}
}
54 changes: 31 additions & 23 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.7.0"
appcheck:
dependency: "direct main"
description:
name: appcheck
sha256: "0fcbe15b6bba67027c6f94bd177b9b319e019b51e0b49e9cd80486448c4a1909"
url: "https://pub.dev"
source: hosted
version: "1.5.2"
archive:
dependency: transitive
description:
Expand Down Expand Up @@ -186,10 +194,10 @@ packages:
dependency: transitive
description:
name: camera_android_camerax
sha256: "8bd9cab67551642eb33ceb33ece7acc0890014fc90ddfae637c7e2b683657e65"
sha256: "7cd93578ad201dcc6bb5810451fb00d76a86bab9b68dceb68b8cbd7038ac5846"
url: "https://pub.dev"
source: hosted
version: "0.6.7+2"
version: "0.6.8+3"
camera_avfoundation:
dependency: transitive
description:
Expand Down Expand Up @@ -668,10 +676,10 @@ packages:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: "9d98bd47ef9d34e803d438f17fd32b116d31009f534a6fa5ce3a1167f189a6de"
sha256: "9ee02950848f61c4129af3d6ec84a1cfc0e47931abc746b03e7a3bc3e8ff6eda"
url: "https://pub.dev"
source: hosted
version: "2.0.21"
version: "2.0.22"
flutter_secure_storage:
dependency: transitive
description:
Expand Down Expand Up @@ -806,10 +814,10 @@ packages:
dependency: "direct main"
description:
name: go_router
sha256: ddc16d34b0d74cb313986918c0f0885a7ba2fc24d8fb8419de75f0015144ccfe
sha256: "2ddb88e9ad56ae15ee144ed10e33886777eb5ca2509a914850a5faa7b52ff459"
url: "https://pub.dev"
source: hosted
version: "14.2.3"
version: "14.2.7"
google_fonts:
dependency: "direct main"
description:
Expand Down Expand Up @@ -934,10 +942,10 @@ packages:
dependency: transitive
description:
name: just_audio
sha256: ee50602364ba83fa6308f5512dd560c713ec3e1f2bc75f0db43618f0d82ef71a
sha256: d8e8aaf417d33e345299c17f6457f72bd4ba0c549dc34607abb5183a354edc4d
url: "https://pub.dev"
source: hosted
version: "0.9.39"
version: "0.9.40"
just_audio_platform_interface:
dependency: transitive
description:
Expand All @@ -950,10 +958,10 @@ packages:
dependency: transitive
description:
name: just_audio_web
sha256: "0edb481ad4aa1ff38f8c40f1a3576013c3420bf6669b686fe661627d49bc606c"
sha256: b163878529d9b028c53a6972fcd58cae2405bcd11cbfcea620b6fb9f151429d6
url: "https://pub.dev"
source: hosted
version: "0.4.11"
version: "0.4.12"
jwt_decoder:
dependency: transitive
description:
Expand Down Expand Up @@ -1110,10 +1118,10 @@ packages:
dependency: transitive
description:
name: mime
sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2"
sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a"
url: "https://pub.dev"
source: hosted
version: "1.0.5"
version: "1.0.6"
mobility_features:
dependency: transitive
description:
Expand Down Expand Up @@ -1438,10 +1446,10 @@ packages:
dependency: transitive
description:
name: permission_handler_html
sha256: d220eb8476b466d58b161e10b3001d93999010a26228a3fb89c4280db1249546
sha256: af26edbbb1f2674af65a8f4b56e1a6f526156bc273d0e65dd8075fab51c78851
url: "https://pub.dev"
source: hosted
version: "0.1.3+1"
version: "0.1.3+2"
permission_handler_platform_interface:
dependency: transitive
description:
Expand Down Expand Up @@ -1622,10 +1630,10 @@ packages:
dependency: transitive
description:
name: shared_preferences_android
sha256: a7e8467e9181cef109f601e3f65765685786c1a738a83d7fbbde377589c0d974
sha256: "480ba4345773f56acda9abf5f50bd966f581dac5d514e5fc4a18c62976bbba7e"
url: "https://pub.dev"
source: hosted
version: "2.3.1"
version: "2.3.2"
shared_preferences_foundation:
dependency: transitive
description:
Expand Down Expand Up @@ -1747,10 +1755,10 @@ packages:
dependency: transitive
description:
name: source_map_stack_trace
sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae"
sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "2.1.2"
source_maps:
dependency: transitive
description:
Expand Down Expand Up @@ -1955,10 +1963,10 @@ packages:
dependency: transitive
description:
name: url_launcher_android
sha256: f0c73347dfcfa5b3db8bc06e1502668265d39c08f310c29bff4e28eea9699f79
sha256: e35a698ac302dd68e41f73250bd9517fe3ab5fa4f18fe4647a0872db61bacbab
url: "https://pub.dev"
source: hosted
version: "6.3.9"
version: "6.3.10"
url_launcher_ios:
dependency: transitive
description:
Expand Down Expand Up @@ -2059,10 +2067,10 @@ packages:
dependency: transitive
description:
name: video_player_android
sha256: "4de50df9ee786f5891d3281e1e633d7b142ef1acf47392592eb91cba5d355849"
sha256: "101028b643a3b43ced72107aacdbc4d30d55365487751de001a36cf0d86038d1"
url: "https://pub.dev"
source: hosted
version: "2.6.0"
version: "2.7.2"
video_player_avfoundation:
dependency: transitive
description:
Expand Down Expand Up @@ -2201,4 +2209,4 @@ packages:
version: "3.1.2"
sdks:
dart: ">=3.5.0 <4.0.0"
flutter: ">=3.22.0"
flutter: ">=3.24.0"
3 changes: 2 additions & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: carp_study_app
description: The generic CARP study app.
publish_to: "none"
version: 2.1.1+30
version: 2.2.1+32

environment:
sdk: ">=3.2.0 <4.0.0"
Expand Down Expand Up @@ -51,6 +51,7 @@ dependencies:
permission_handler: ^11.0.1
connectivity_plus: ^6.0.0
open_settings_plus: ^0.3.0
appcheck: ^1.5.2

# Overriding carp libraries to use the local copy
dependency_overrides:
Expand Down
Loading