diff --git a/app/android/app/src/main/kotlin/de/hpi/frasecys/MainActivity.kt b/app/android/app/src/main/kotlin/de/hpi/frasecys/MainActivity.kt index ae9d70a5c..7d69c665a 100644 --- a/app/android/app/src/main/kotlin/de/hpi/frasecys/MainActivity.kt +++ b/app/android/app/src/main/kotlin/de/hpi/frasecys/MainActivity.kt @@ -8,6 +8,15 @@ import io.flutter.plugin.common.MethodChannel import android.os.Bundle import care.data4life.sdk.listener.ResultListener import care.data4life.sdk.lang.D4LException +import care.data4life.fhir.r4.model.Attachment +import care.data4life.fhir.r4.model.CodeSystemDocumentReferenceStatus +import care.data4life.fhir.r4.model.DocumentReference +import android.util.Base64 +import java.io.File +import java.security.MessageDigest +import care.data4life.sdk.call.Fhir4Record +import java.util.Calendar +import care.data4life.fhir.r4.util.FhirDateTimeConverter class MainActivity : FlutterActivity() { @@ -71,6 +80,31 @@ class MainActivity : FlutterActivity() { ) } + "upload" -> { + val path: String = call.argument("path")!! + val title: String = call.argument("title")!! + val docRef = makeDocumentReference(path, title) + + Data4LifeClient.getInstance().fhir4.create( + resource = docRef, + annotations = listOf("pharme"), + callback = object : + care.data4life.sdk.call.Callback> { + override fun onSuccess(_result: Fhir4Record) { + result.success(true) + } + + override fun onError(exception: D4LException) { + result.success(false) + } + } + ) + } + "toast" -> { + val msg: String = call.argument("msg")!! + + android.widget.Toast.makeText(this, msg, android.widget.Toast.LENGTH_SHORT).show() + } else -> result.notImplemented() } } @@ -90,3 +124,28 @@ class MainActivity : FlutterActivity() { } } } + +fun makeDocumentReference(path: String, title: String): DocumentReference { + val bytes = File(path).readBytes() + return DocumentReference( + CodeSystemDocumentReferenceStatus.CURRENT, + listOf( + DocumentReference.DocumentReferenceContent( + Attachment().apply { + contentType = "application/pdf" + data = Base64.encodeToString( + bytes, + Base64.NO_WRAP, + ) + size = bytes.size + hash = with(MessageDigest.getInstance("SHA-1")) { + Base64.encodeToString(digest(bytes), Base64.NO_WRAP) + } + }, + ), + ), + ).apply { + description = title + date = FhirDateTimeConverter.toFhirInstant(Calendar.getInstance().time) + } +} \ No newline at end of file diff --git a/app/lib/common/pages/medications/medication.dart b/app/lib/common/pages/medications/medication.dart index 236942aef..8e2b2b405 100644 --- a/app/lib/common/pages/medications/medication.dart +++ b/app/lib/common/pages/medications/medication.dart @@ -1,6 +1,9 @@ // ignore_for_file: avoid_returning_null_for_void +import 'dart:io'; + import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../../l10n.dart'; @@ -10,6 +13,7 @@ import '../../utilities/pdf_utils.dart'; import '../../widgets/module.dart'; import 'cubit.dart'; import 'widgets/module.dart'; +import '../../../common/module.dart'; class MedicationPage extends StatelessWidget { const MedicationPage( @@ -50,7 +54,7 @@ class MedicationPage extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - _buildHeader(medication), + _buildHeader(medication, context), SizedBox(height: 20), Disclaimer(), SizedBox(height: 20), @@ -67,7 +71,8 @@ class MedicationPage extends StatelessWidget { ); } - Widget _buildHeader(MedicationWithGuidelines medication) { + Widget _buildHeader( + MedicationWithGuidelines medication, BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -75,13 +80,51 @@ class MedicationPage extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(medication.name, style: PharMeTheme.textTheme.displaySmall), - IconButton( - onPressed: () => sharePdf(medication), - icon: Icon( - Icons.ios_share, - size: 32, - color: PharMeTheme.primaryColor, - ), + Row( + children: [ + if (Platform.isAndroid) + IconButton( + onPressed: () async { + final isLoggedIn = await MethodChannel('chdp') + .invokeMethod('isLoggedIn'); + + if (isLoggedIn) { + await showDialog( + context: context, + builder: (_) => + _shouldUploadDialog(context, () async { + final isSuccess = + await sharePdfSmart4Health(medication); + if (isSuccess) { + await MethodChannel('chdp').invokeMethod( + 'toast', + {'msg': 'Upload successful'}); + } else { + await MethodChannel('chdp').invokeMethod( + 'toast', {'msg': 'Upload failed'}); + } + })); + } else { + await showDialog( + context: context, + builder: (_) => _pleaseLogInDialog(context)); + } + }, + icon: Icon( + Icons.upload_file, + size: 32, + color: PharMeTheme.primaryColor, + )) + else + null, + IconButton( + onPressed: () => sharePdf(medication), + icon: Icon( + Platform.isAndroid ? Icons.share : Icons.ios_share, + size: 32, + color: PharMeTheme.primaryColor, + )), + ].whereType().toList(), ), ], ), @@ -102,4 +145,36 @@ class MedicationPage extends StatelessWidget { ], ); } + + Widget _pleaseLogInDialog(BuildContext context) { + return AlertDialog( + title: Text('Please log in'), + content: Text( + 'To upload your report, please log in to Smart4Health under More > Account Settings'), + actions: [ + TextButton( + onPressed: context.router.root.pop, + child: Text('Okay'), + ), + ], + ); + } + + // intellij autoformatter having a normal one + Widget _shouldUploadDialog( + BuildContext context, Future Function() onPositivePressed) { + return AlertDialog( + title: Text('Upload report'), + content: Text('Would you like to upload your report to Smart4Health?'), + actions: [ + TextButton(onPressed: context.router.root.pop, child: Text('Cancel')), + TextButton( + onPressed: () async { + await context.router.root.pop(); + await onPositivePressed(); + }, + child: Text('Upload'), + ) + ]); + } } diff --git a/app/lib/common/utilities/pdf_utils.dart b/app/lib/common/utilities/pdf_utils.dart index 4c11cfdd2..3dd18cd2e 100644 --- a/app/lib/common/utilities/pdf_utils.dart +++ b/app/lib/common/utilities/pdf_utils.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:flutter/services.dart'; import 'package:flutter_share/flutter_share.dart'; import 'package:path_provider/path_provider.dart'; import 'package:pdf/pdf.dart'; @@ -30,6 +31,14 @@ Future sharePdf(MedicationWithGuidelines medication) async { await FlutterShare.shareFile(title: medication.name, filePath: path); } +Future sharePdfSmart4Health(MedicationWithGuidelines medication) async { + final path = await createPdf(medication); + return await MethodChannel('chdp').invokeMethod('upload', { + 'path': path, + 'title': 'PharMe Report: ${medication.name}' + }); +} + pw.Widget buildPdfPage( pw.Context context, MedicationWithGuidelines medication, @@ -140,6 +149,7 @@ class _PdfSegment extends pw.StatelessWidget { _PdfSegment({required this.child}); final pw.Widget child; + @override pw.Widget build(Context context) { return pw.SizedBox( @@ -154,6 +164,7 @@ class _PdfText extends pw.StatelessWidget { final String title; final String? text; + @override pw.Widget build(Context context) { return pw.RichText( diff --git a/app/lib/reports/pages/reports.dart b/app/lib/reports/pages/reports.dart index c471cdd2a..d35299ade 100644 --- a/app/lib/reports/pages/reports.dart +++ b/app/lib/reports/pages/reports.dart @@ -1,3 +1,5 @@ +import 'package:flutter/services.dart'; + import '../../common/module.dart'; import 'cubit.dart'; diff --git a/app/lib/settings/pages/settings.dart b/app/lib/settings/pages/settings.dart index 25acf8c45..3edf430b6 100644 --- a/app/lib/settings/pages/settings.dart +++ b/app/lib/settings/pages/settings.dart @@ -32,13 +32,7 @@ class _SettingsPageState extends State { ), ), if (Platform.isAndroid) - ChdpListTile( - // title: Text(context.l10n.settings_page_login_smart4health), - // trailing: Icon(Icons.chevron_right), - // onTap: () => { - // debugPrint("logging in") - // }, - ) + ChdpListTile() else null, Divider(),