From 90d433aac59503b5e0633e6d8741f6ea97cac1ff Mon Sep 17 00:00:00 2001 From: Thomas Harris Date: Tue, 30 Aug 2022 14:38:51 +0200 Subject: [PATCH 01/11] Add a button to the settings page --- .gitignore | 1 + app/android/app/src/main/AndroidManifest.xml | 1 + app/lib/l10n/app_en.arb | 1 + app/lib/reports/pages/cubit.dart | 2 + app/lib/settings/pages/settings.dart | 18 ++- app/pubspec.lock | 126 ++++++++++++++++++- 6 files changed, 144 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index eb03e3e1e..0867c505d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules *.log +.idea/ \ No newline at end of file diff --git a/app/android/app/src/main/AndroidManifest.xml b/app/android/app/src/main/AndroidManifest.xml index af3e70696..ec9b6a130 100644 --- a/app/android/app/src/main/AndroidManifest.xml +++ b/app/android/app/src/main/AndroidManifest.xml @@ -7,6 +7,7 @@ android:label="PharMe" android:icon="@mipmap/launcher_icon" android:usesCleartextTraffic="false" + android:name="${applicationName}" > { class ReportsState with _$ReportsState { const factory ReportsState.initial() = _InitialState; const factory ReportsState.loading() = _LoadingState; + // add a member here that indicates showing the s4h button, load in the cubit by asking android + // button then talks to android again const factory ReportsState.loaded( List medications, ) = _LoadedState; diff --git a/app/lib/settings/pages/settings.dart b/app/lib/settings/pages/settings.dart index 023f41dd1..472e3005c 100644 --- a/app/lib/settings/pages/settings.dart +++ b/app/lib/settings/pages/settings.dart @@ -1,3 +1,6 @@ +import 'dart:io'; +import 'dart:math'; + import '../../common/module.dart'; import '../utils.dart'; @@ -6,7 +9,8 @@ class SettingsPage extends StatelessWidget { @override Widget build(BuildContext context) { - return ListView(children: [ + return ListView( + children: [ ListTile( title: Text( context.l10n.settings_page_account_settings, @@ -21,6 +25,16 @@ class SettingsPage extends StatelessWidget { builder: (_) => _deleteDataDialog(context), ), ), + if (Platform.isAndroid) + ListTile( + title: Text(context.l10n.settings_page_login_smart4health), + trailing: Icon(Icons.chevron_right), + onTap: () => { + debugPrint("logging in") + }, + ) + else + null, Divider(), ListTile( title: Text( @@ -48,7 +62,7 @@ class SettingsPage extends StatelessWidget { trailing: Icon(Icons.chevron_right), onTap: () => context.router.push(TermsAndConditionsRoute()), ), - ]); + ].whereType().toList()); } Widget _deleteDataDialog(BuildContext context) { diff --git a/app/pubspec.lock b/app/pubspec.lock index e013a01e7..76d333f10 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -21,7 +21,7 @@ packages: name: archive url: "https://pub.dartlang.org" source: hosted - version: "3.3.0" + version: "3.1.11" args: dependency: transitive description: @@ -78,6 +78,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "8.0.3" + bloc_test: + dependency: "direct dev" + description: + name: bloc_test + url: "https://pub.dartlang.org" + source: hosted + version: "9.0.3" boolean_selector: dependency: transitive description: @@ -190,13 +197,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.2" + coverage: + dependency: transitive + description: + name: coverage + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" crypto: dependency: transitive description: name: crypto url: "https://pub.dartlang.org" source: hosted - version: "3.0.2" + version: "3.0.1" csslib: dependency: transitive description: @@ -281,6 +295,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.1" + diff_match_patch: + dependency: transitive + description: + name: diff_match_patch + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.1" dropdown_button2: dependency: "direct main" description: @@ -335,6 +356,11 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "8.0.1" + flutter_driver: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" flutter_hooks: dependency: "direct main" description: @@ -462,6 +488,11 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.3" + fuchsia_remote_debug_protocol: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" functions_client: dependency: transitive description: @@ -560,6 +591,11 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + integration_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" intl: dependency: "direct main" description: @@ -651,6 +687,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.2" + mocktail: + dependency: "direct dev" + description: + name: mocktail + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.0" nested: dependency: transitive description: @@ -658,6 +701,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.0" + node_preamble: + dependency: transitive + description: + name: node_preamble + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" package_config: dependency: transitive description: @@ -959,6 +1009,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.1" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + shelf_static: + dependency: transitive + description: + name: shelf_static + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" shelf_web_socket: dependency: transitive description: @@ -985,6 +1049,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.2" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + source_maps: + dependency: transitive + description: + name: source_maps + url: "https://pub.dartlang.org" + source: hosted + version: "0.10.10" source_span: dependency: transitive description: @@ -1034,6 +1112,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.3.6" + sync_http: + dependency: transitive + description: + name: sync_http + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.0" term_glyph: dependency: transitive description: @@ -1041,6 +1126,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + test: + dependency: transitive + description: + name: test + url: "https://pub.dartlang.org" + source: hosted + version: "1.21.1" test_api: dependency: transitive description: @@ -1048,6 +1140,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.4.9" + test_core: + dependency: transitive + description: + name: test_core + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.13" time: dependency: transitive description: @@ -1068,7 +1167,7 @@ packages: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.3.1" + version: "1.3.0" universal_html: dependency: transitive description: @@ -1146,6 +1245,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.2" + vm_service: + dependency: transitive + description: + name: vm_service + url: "https://pub.dartlang.org" + source: hosted + version: "8.2.2" watcher: dependency: transitive description: @@ -1160,6 +1266,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.0" + webdriver: + dependency: transitive + description: + name: webdriver + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" win32: dependency: transitive description: From da26c08f4555d4c5447bee9060e401579ffe9e58 Mon Sep 17 00:00:00 2001 From: Thomas Harris Date: Tue, 30 Aug 2022 15:41:41 +0200 Subject: [PATCH 02/11] Import the CHDP SDK --- app/CONTRIBUTING.md | 19 +++++++++++++++++++ app/android/.gitignore | 1 + app/android/app/build.gradle | 23 ++++++++++++++++++++++- app/android/build.gradle | 29 +++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 1 deletion(-) diff --git a/app/CONTRIBUTING.md b/app/CONTRIBUTING.md index 93c6e12ce..4a1e71f17 100644 --- a/app/CONTRIBUTING.md +++ b/app/CONTRIBUTING.md @@ -16,6 +16,25 @@ Please also see the [contribution guide in the root folder](../CONTRIBUTING.md). You should now be able to run the app by opening the debug panel on the left and pressing the green triangle at the top (or using the shortcut F5). +### `secrets.properties` + +The CHDP libraries require a few secrets to build. In +[`android/secrets.properties`](android/secrets.properties) (gitignored) add the following keys +and appropriate values: + +```properties +gpr.user= +gpr.token= + +d4l.clientId= +d4l.clientSecret= +``` + +The first two are a Github username and Personal Access Token with the `read:packages` scope. This +is used by gradle to download the SDK from D4L's private repositories. + +The next two are secrets given by D4L. Ask Thomas at Thomas.Harris (at) hpi.de for them. + ## Architecture The app consists of multiple so-called modules. Our main modules correspond to diff --git a/app/android/.gitignore b/app/android/.gitignore index 6f568019d..77131d6a4 100644 --- a/app/android/.gitignore +++ b/app/android/.gitignore @@ -11,3 +11,4 @@ GeneratedPluginRegistrant.java key.properties **/*.keystore **/*.jks +secrets.properties \ No newline at end of file diff --git a/app/android/app/build.gradle b/app/android/app/build.gradle index 2cc5d04b6..fecb48114 100644 --- a/app/android/app/build.gradle +++ b/app/android/app/build.gradle @@ -25,6 +25,10 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" +def secretsPropertiesFile = new File(rootProject.projectDir, "secrets.properties") +def secrets = new Properties() +secretsPropertiesFile.withReader("UTF-8") { reader -> secrets.load(reader) } + android { compileSdkVersion 31 @@ -44,13 +48,27 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "de.hpi.pharme" - minSdkVersion 19 + minSdkVersion 23 targetSdkVersion 30 versionCode flutterVersionCode.toInteger() versionName flutterVersionName + + manifestPlaceholders += [ + platform : "s4h", + environment : "staging", + clientId : secrets.getProperty("d4l.clientId"), + clientSecret : secrets.getProperty("d4l.clientSecret"), + redirectScheme: "eu.smart4health.7cf2befc-a010-4290-996c-a94426b5ce65", + debug : "false" + ] } buildTypes { + + debug { + matchingFallbacks = ["release", "debug"] + } + release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. @@ -65,4 +83,7 @@ flutter { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + + implementation "care.data4life.hc-sdk-kmp:sdk-android:1.15.1" + implementation "care.data4life.hc-fhir-sdk-java:fhir-java:1.6.2" } diff --git a/app/android/build.gradle b/app/android/build.gradle index 76374445e..f42aa7de7 100644 --- a/app/android/build.gradle +++ b/app/android/build.gradle @@ -11,10 +11,39 @@ buildscript { } } +def secretsPropertiesFile = new File(rootProject.projectDir, "secrets.properties") +def secrets = new Properties() +secretsPropertiesFile.withReader("UTF-8") { reader -> secrets.load(reader) } + allprojects { repositories { google() mavenCentral() + + // tried to loop over a list of urls like in kotlin but no dice + maven { + url "https://maven.pkg.github.com/d4l-data4life/hc-util-sdk-kmp" + credentials { + username secrets.getProperty("gpr.user") + password secrets.getProperty("gpr.token") + } + } + + maven { + url "https://maven.pkg.github.com/d4l-data4life/hc-fhir-sdk-java" + credentials { + username secrets.getProperty("gpr.user") + password secrets.getProperty("gpr.token") + } + } + + maven { + url "https://maven.pkg.github.com/d4l-data4life/hc-fhir-helper-sdk-kmp" + credentials { + username secrets.getProperty("gpr.user") + password secrets.getProperty("gpr.token") + } + } } } From db687255c9ba1090421a712f64af1a60c03974e9 Mon Sep 17 00:00:00 2001 From: Thomas Harris Date: Tue, 30 Aug 2022 17:13:37 +0200 Subject: [PATCH 03/11] Set up the first method channel, and init the d4l sdk --- app/android/app/build.gradle | 1 + .../kotlin/de/hpi/frasecys/MainActivity.kt | 27 +++++++++++- app/lib/common/utilities/hive_utils.dart | 28 +++++++------ app/lib/settings/chdp_state.dart | 42 +++++++++++++++++++ app/lib/settings/pages/settings.dart | 20 +++++---- app/pubspec.lock | 42 ------------------- app/pubspec.yaml | 2 +- 7 files changed, 99 insertions(+), 63 deletions(-) create mode 100644 app/lib/settings/chdp_state.dart diff --git a/app/android/app/build.gradle b/app/android/app/build.gradle index fecb48114..435a7d892 100644 --- a/app/android/app/build.gradle +++ b/app/android/app/build.gradle @@ -86,4 +86,5 @@ dependencies { implementation "care.data4life.hc-sdk-kmp:sdk-android:1.15.1" implementation "care.data4life.hc-fhir-sdk-java:fhir-java:1.6.2" + implementation "com.jakewharton.threetenabp:threetenabp:1.4.0" } 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 f48fa7311..91f5af7c4 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 @@ -1,6 +1,31 @@ package de.hpi.pharme import io.flutter.embedding.android.FlutterActivity +import com.jakewharton.threetenabp.AndroidThreeTen +import care.data4life.sdk.Data4LifeClient +import io.flutter.embedding.engine.FlutterEngine +import io.flutter.plugin.common.MethodChannel +import android.os.Bundle -class MainActivity: FlutterActivity() { +class MainActivity : FlutterActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + AndroidThreeTen.init(this.applicationContext) + Data4LifeClient.init(applicationContext) + } + + + override fun configureFlutterEngine(flutterEngine: FlutterEngine) { + super.configureFlutterEngine(flutterEngine) + + MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "chdp") + .setMethodCallHandler { call, result -> + when (call.method) { + "isLoggedIn" -> result.success(true) + else -> result.notImplemented() + } + } + } } diff --git a/app/lib/common/utilities/hive_utils.dart b/app/lib/common/utilities/hive_utils.dart index 9abc3a5f8..cd619afb0 100644 --- a/app/lib/common/utilities/hive_utils.dart +++ b/app/lib/common/utilities/hive_utils.dart @@ -1,19 +1,23 @@ import 'dart:convert'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +// import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:hive/hive.dart'; +// TODO The D4L SDK depends on an older version of tink (1.2, but up to 1.4 in testing) +// while flutter_secure_storage depends on 1.5. This causes problems, so +// use a dummy key here until someone figures out what to do Future> retrieveExistingOrGenerateKey() async { - const secureStorage = FlutterSecureStorage(); + return List.filled(32, 0); + // const secureStorage = FlutterSecureStorage(); // if key not exists return null - final encryprionKey = await secureStorage.read(key: 'key'); - if (encryprionKey == null) { - final key = Hive.generateSecureKey(); - await secureStorage.write( - key: 'key', - value: base64UrlEncode(key), - ); - } - final key = await secureStorage.read(key: 'key'); - return base64Url.decode(key!); + // final encryprionKey = await secureStorage.read(key: 'key'); + // if (encryprionKey == null) { + // final key = Hive.generateSecureKey(); + // await secureStorage.write( + // key: 'key', + // value: base64UrlEncode(key), + // ); + // } + // final key = Hive.generateSecureKey();// await secureStorage.read(key: 'key'); + // return key; // base64Url.decode(key); } diff --git a/app/lib/settings/chdp_state.dart b/app/lib/settings/chdp_state.dart new file mode 100644 index 000000000..67f1385fc --- /dev/null +++ b/app/lib/settings/chdp_state.dart @@ -0,0 +1,42 @@ +import 'package:flutter/services.dart'; + +import '../common/module.dart'; + +class ChdpListTile extends StatefulWidget { + const ChdpListTile({Key? key}) : super(key: key); + + @override + State createState() => _ChdpState(); +} + +class _ChdpState extends State { + static const platform = MethodChannel('chdp'); + + bool _isLoggedIn = false; + + @override + void initState() { + super.initState(); + getIsLoggedIn().whenComplete(() => null); + } + + @override + Widget build(BuildContext context) { + return ListTile( + title: Text('Is logged in? $_isLoggedIn'), + ); + } + + Future getIsLoggedIn() async { + var isLoggedIn = false; + try { + isLoggedIn = await platform.invokeMethod('isLoggedIn'); + } on PlatformException catch (e) { + debugPrint('platform exception in getIsLoggedIn'); + } + + setState(() { + _isLoggedIn = isLoggedIn; + }); + } +} diff --git a/app/lib/settings/pages/settings.dart b/app/lib/settings/pages/settings.dart index 472e3005c..25acf8c45 100644 --- a/app/lib/settings/pages/settings.dart +++ b/app/lib/settings/pages/settings.dart @@ -2,11 +2,17 @@ import 'dart:io'; import 'dart:math'; import '../../common/module.dart'; +import '../chdp_state.dart'; import '../utils.dart'; -class SettingsPage extends StatelessWidget { +class SettingsPage extends StatefulWidget { const SettingsPage({Key? key}) : super(key: key); + @override + State createState() => _SettingsPageState(); +} + +class _SettingsPageState extends State { @override Widget build(BuildContext context) { return ListView( @@ -26,12 +32,12 @@ class SettingsPage extends StatelessWidget { ), ), if (Platform.isAndroid) - ListTile( - title: Text(context.l10n.settings_page_login_smart4health), - trailing: Icon(Icons.chevron_right), - onTap: () => { - debugPrint("logging in") - }, + ChdpListTile( + // title: Text(context.l10n.settings_page_login_smart4health), + // trailing: Icon(Icons.chevron_right), + // onTap: () => { + // debugPrint("logging in") + // }, ) else null, diff --git a/app/pubspec.lock b/app/pubspec.lock index 76d333f10..4c91c92de 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -387,48 +387,6 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_secure_storage: - dependency: "direct main" - description: - name: flutter_secure_storage - url: "https://pub.dartlang.org" - source: hosted - version: "5.0.2" - flutter_secure_storage_linux: - dependency: transitive - description: - name: flutter_secure_storage_linux - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - flutter_secure_storage_macos: - dependency: transitive - description: - name: flutter_secure_storage_macos - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - flutter_secure_storage_platform_interface: - dependency: transitive - description: - name: flutter_secure_storage_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.0" - flutter_secure_storage_web: - dependency: transitive - description: - name: flutter_secure_storage_web - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.2" - flutter_secure_storage_windows: - dependency: transitive - description: - name: flutter_secure_storage_windows - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.2" flutter_share: dependency: "direct main" description: diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 10a429803..3745dba47 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -23,7 +23,7 @@ dependencies: flutter_hooks: ^0.18.5+1 flutter_localizations: sdk: flutter - flutter_secure_storage: ^5.0.2 +# flutter_secure_storage: ^5.0.2 flutter_share: ^2.0.0 flutter_sliding_up_panel: ^2.0.1 flutter_svg: ^1.1.1 From b627a54af553c6b18baad2af72c5e7795a700f85 Mon Sep 17 00:00:00 2001 From: Thomas Harris Date: Wed, 31 Aug 2022 11:25:48 +0200 Subject: [PATCH 04/11] Log out and log in work --- app/android/app/src/debug/AndroidManifest.xml | 7 --- app/android/app/src/main/AndroidManifest.xml | 1 + .../kotlin/de/hpi/frasecys/MainActivity.kt | 63 ++++++++++++++++++- .../app/src/main/res/values-night/styles.xml | 4 +- .../app/src/main/res/values/styles.xml | 4 +- app/lib/settings/chdp_state.dart | 48 +++++++++++++- 6 files changed, 112 insertions(+), 15 deletions(-) delete mode 100644 app/android/app/src/debug/AndroidManifest.xml diff --git a/app/android/app/src/debug/AndroidManifest.xml b/app/android/app/src/debug/AndroidManifest.xml deleted file mode 100644 index d1df7c5b4..000000000 --- a/app/android/app/src/debug/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/app/android/app/src/main/AndroidManifest.xml b/app/android/app/src/main/AndroidManifest.xml index ec9b6a130..acc0b29d3 100644 --- a/app/android/app/src/main/AndroidManifest.xml +++ b/app/android/app/src/main/AndroidManifest.xml @@ -6,6 +6,7 @@ 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 91f5af7c4..ae9d70a5c 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 @@ -6,6 +6,8 @@ import care.data4life.sdk.Data4LifeClient import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel import android.os.Bundle +import care.data4life.sdk.listener.ResultListener +import care.data4life.sdk.lang.D4LException class MainActivity : FlutterActivity() { @@ -23,9 +25,68 @@ class MainActivity : FlutterActivity() { MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "chdp") .setMethodCallHandler { call, result -> when (call.method) { - "isLoggedIn" -> result.success(true) + "isLoggedIn" -> Data4LifeClient.getInstance().isUserLoggedIn( + object : ResultListener { + override fun onSuccess(t: Boolean) = result.success(t) + + override fun onError(exception: D4LException) = + result.error( + "D4LException", + "Failed to check login status", + exception + ) + } + ) + "login" -> { + val intent = Data4LifeClient.getInstance().getLoginIntent( + this@MainActivity, + setOf( + "perm:r", + "rec:r", + "rec:w", + "attachment:r", + "attachment:w", + "user:r", + "user:w", + "user:q", + ) + ) + + this@MainActivity.startActivityForResult(intent, Data4LifeClient.D4L_AUTH) + + result.success(0) + } + "logout" -> { + Data4LifeClient.getInstance().logout( + object : care.data4life.sdk.listener.Callback { + override fun onSuccess() = result.success(0) + + override fun onError(exception: D4LException) = + result.error( + "D4LException", + "Failed to logout", + exception + ) + } + ) + + } else -> result.notImplemented() } } } + + override fun onActivityResult( + requestCode: Int, + resultCode: Int, + data: android.content.Intent?, + ) { + super.onActivityResult(requestCode, resultCode, data) + + if (requestCode == Data4LifeClient.D4L_AUTH) { + if (resultCode == android.app.Activity.RESULT_OK) { + android.util.Log.i("PHARME", "login successful") + } + } + } } diff --git a/app/android/app/src/main/res/values-night/styles.xml b/app/android/app/src/main/res/values-night/styles.xml index 449a9f930..343700c66 100644 --- a/app/android/app/src/main/res/values-night/styles.xml +++ b/app/android/app/src/main/res/values-night/styles.xml @@ -1,7 +1,7 @@ - diff --git a/app/android/app/src/main/res/values/styles.xml b/app/android/app/src/main/res/values/styles.xml index d74aa35c2..0ad2b7ea7 100644 --- a/app/android/app/src/main/res/values/styles.xml +++ b/app/android/app/src/main/res/values/styles.xml @@ -1,7 +1,7 @@ - diff --git a/app/lib/settings/chdp_state.dart b/app/lib/settings/chdp_state.dart index 67f1385fc..d13c7b6e0 100644 --- a/app/lib/settings/chdp_state.dart +++ b/app/lib/settings/chdp_state.dart @@ -9,7 +9,7 @@ class ChdpListTile extends StatefulWidget { State createState() => _ChdpState(); } -class _ChdpState extends State { +class _ChdpState extends State with WidgetsBindingObserver { static const platform = MethodChannel('chdp'); bool _isLoggedIn = false; @@ -17,14 +17,40 @@ class _ChdpState extends State { @override void initState() { super.initState(); + WidgetsBinding.instance.addObserver(this); getIsLoggedIn().whenComplete(() => null); } + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (state == AppLifecycleState.resumed) { + getIsLoggedIn().whenComplete(() => null); + } + } + @override Widget build(BuildContext context) { + final title = _isLoggedIn + ? Text('Log out of Smart4Health') + : Text('Log in to Smart4Health'); + return ListTile( - title: Text('Is logged in? $_isLoggedIn'), - ); + title: title, + trailing: _isLoggedIn ? null : Icon(Icons.chevron_right), + onTap: () => { + // are you okay intellij autoformatter???? + if (_isLoggedIn) + {logout().whenComplete(getIsLoggedIn)} + else + {login().whenComplete(() => null)} + }, + ); // ListTile } Future getIsLoggedIn() async { @@ -39,4 +65,20 @@ class _ChdpState extends State { _isLoggedIn = isLoggedIn; }); } + + Future login() async { + try { + await platform.invokeMethod('login'); + } on PlatformException catch (e) { + debugPrint('platform exception in login'); + } + } + + Future logout() async { + try { + await platform.invokeMethod('logout'); + } on PlatformException catch (e) { + debugPrint('platform exception in logout'); + } + } } From a9572f53fbd83c9f2364880b3f4f30d272c79b66 Mon Sep 17 00:00:00 2001 From: Thomas Harris Date: Wed, 31 Aug 2022 14:31:02 +0200 Subject: [PATCH 05/11] Uploading works --- .../kotlin/de/hpi/frasecys/MainActivity.kt | 59 ++++++++++++ .../common/pages/medications/medication.dart | 93 +++++++++++++++++-- app/lib/common/utilities/pdf_utils.dart | 11 +++ app/lib/reports/pages/reports.dart | 2 + app/lib/settings/pages/settings.dart | 8 +- 5 files changed, 157 insertions(+), 16 deletions(-) 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(), From 4b18edae1741e019cd008d091559989f1988aa1b Mon Sep 17 00:00:00 2001 From: Jannis Baum Date: Tue, 27 Sep 2022 08:54:09 +0200 Subject: [PATCH 06/11] chore: fix markdown lint --- app/CONTRIBUTING.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/CONTRIBUTING.md b/app/CONTRIBUTING.md index 4a1e71f17..4635d8c4a 100644 --- a/app/CONTRIBUTING.md +++ b/app/CONTRIBUTING.md @@ -18,9 +18,9 @@ pressing the green triangle at the top (or using the shortcut F5). ### `secrets.properties` -The CHDP libraries require a few secrets to build. In -[`android/secrets.properties`](android/secrets.properties) (gitignored) add the following keys -and appropriate values: +The CHDP libraries require a few secrets to build. In +[`android/secrets.properties`](android/secrets.properties) (gitignored) add the +following keys and appropriate values: ```properties gpr.user= @@ -30,10 +30,12 @@ d4l.clientId= d4l.clientSecret= ``` -The first two are a Github username and Personal Access Token with the `read:packages` scope. This -is used by gradle to download the SDK from D4L's private repositories. +The first two are a Github username and Personal Access Token with the +`read:packages` scope. This is used by gradle to download the SDK from D4L's +private repositories. -The next two are secrets given by D4L. Ask Thomas at Thomas.Harris (at) hpi.de for them. +The next two are secrets given by D4L. Ask Thomas at Thomas.Harris (at) hpi.de +for them. ## Architecture From 98075a680ef7b063dd3c3571062492b232ff7d6f Mon Sep 17 00:00:00 2001 From: Jannis Baum Date: Tue, 27 Sep 2022 09:11:35 +0200 Subject: [PATCH 07/11] chore: fix app linting --- app/lib/common/models/medication/guideline.dart | 6 ++---- app/lib/common/models/medication/medication.dart | 2 +- app/lib/common/pages/medications/medication.dart | 8 +------- app/lib/common/utilities/hive_utils.dart | 6 ------ app/lib/reports/pages/reports.dart | 2 -- app/lib/settings/chdp_state.dart | 6 +++--- app/lib/settings/pages/settings.dart | 1 - 7 files changed, 7 insertions(+), 24 deletions(-) diff --git a/app/lib/common/models/medication/guideline.dart b/app/lib/common/models/medication/guideline.dart index 75415227c..c7d6c34f9 100644 --- a/app/lib/common/models/medication/guideline.dart +++ b/app/lib/common/models/medication/guideline.dart @@ -1,8 +1,6 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hive/hive.dart'; -import '../../module.dart'; - part 'guideline.g.dart'; @HiveType(typeId: 9) @@ -70,7 +68,7 @@ class Guideline { @override int get hashCode { - return hashValues( + return Object.hash( implication, recommendation, warningLevel, @@ -115,7 +113,7 @@ class Phenotype { } @override - int get hashCode => hashValues(geneResult.name, geneSymbol.name); + int get hashCode => Object.hash(geneResult.name, geneSymbol.name); } @HiveType(typeId: 11) diff --git a/app/lib/common/models/medication/medication.dart b/app/lib/common/models/medication/medication.dart index 3cae5d3f6..5092f4abd 100644 --- a/app/lib/common/models/medication/medication.dart +++ b/app/lib/common/models/medication/medication.dart @@ -77,7 +77,7 @@ class MedicationWithGuidelines { guidelines.contentEquals(other.guidelines); @override - int get hashCode => hashValues(name, guidelines); + int get hashCode => Object.hash(name, guidelines); } List medicationsFromHTTPResponse(Response resp) { diff --git a/app/lib/common/pages/medications/medication.dart b/app/lib/common/pages/medications/medication.dart index 8e2b2b405..94b4e384a 100644 --- a/app/lib/common/pages/medications/medication.dart +++ b/app/lib/common/pages/medications/medication.dart @@ -2,18 +2,12 @@ import 'dart:io'; -import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import '../../l10n.dart'; -import '../../models/module.dart'; -import '../../theme.dart'; +import '../../../common/module.dart'; 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( diff --git a/app/lib/common/utilities/hive_utils.dart b/app/lib/common/utilities/hive_utils.dart index cd619afb0..fbb3433fc 100644 --- a/app/lib/common/utilities/hive_utils.dart +++ b/app/lib/common/utilities/hive_utils.dart @@ -1,11 +1,5 @@ -import 'dart:convert'; - // import 'package:flutter_secure_storage/flutter_secure_storage.dart'; -import 'package:hive/hive.dart'; -// TODO The D4L SDK depends on an older version of tink (1.2, but up to 1.4 in testing) -// while flutter_secure_storage depends on 1.5. This causes problems, so -// use a dummy key here until someone figures out what to do Future> retrieveExistingOrGenerateKey() async { return List.filled(32, 0); // const secureStorage = FlutterSecureStorage(); diff --git a/app/lib/reports/pages/reports.dart b/app/lib/reports/pages/reports.dart index d35299ade..c471cdd2a 100644 --- a/app/lib/reports/pages/reports.dart +++ b/app/lib/reports/pages/reports.dart @@ -1,5 +1,3 @@ -import 'package:flutter/services.dart'; - import '../../common/module.dart'; import 'cubit.dart'; diff --git a/app/lib/settings/chdp_state.dart b/app/lib/settings/chdp_state.dart index d13c7b6e0..8f4833829 100644 --- a/app/lib/settings/chdp_state.dart +++ b/app/lib/settings/chdp_state.dart @@ -58,7 +58,7 @@ class _ChdpState extends State with WidgetsBindingObserver { try { isLoggedIn = await platform.invokeMethod('isLoggedIn'); } on PlatformException catch (e) { - debugPrint('platform exception in getIsLoggedIn'); + debugPrint('platform exception in getIsLoggedIn: ${e.toString()}'); } setState(() { @@ -70,7 +70,7 @@ class _ChdpState extends State with WidgetsBindingObserver { try { await platform.invokeMethod('login'); } on PlatformException catch (e) { - debugPrint('platform exception in login'); + debugPrint('platform exception in login: ${e.toString()}'); } } @@ -78,7 +78,7 @@ class _ChdpState extends State with WidgetsBindingObserver { try { await platform.invokeMethod('logout'); } on PlatformException catch (e) { - debugPrint('platform exception in logout'); + debugPrint('platform exception in logout: ${e.toString()}'); } } } diff --git a/app/lib/settings/pages/settings.dart b/app/lib/settings/pages/settings.dart index 3edf430b6..241ef2a9b 100644 --- a/app/lib/settings/pages/settings.dart +++ b/app/lib/settings/pages/settings.dart @@ -1,5 +1,4 @@ import 'dart:io'; -import 'dart:math'; import '../../common/module.dart'; import '../chdp_state.dart'; From 7f8725980a8eaa62d0d0747818c65d0b784adac8 Mon Sep 17 00:00:00 2001 From: Jannis Baum Date: Tue, 27 Sep 2022 12:21:28 +0200 Subject: [PATCH 08/11] feat(app): make chdp build work secrets from env --- app/android/app/build.gradle | 15 +++++++++++++-- app/android/build.gradle | 15 +++++++++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/app/android/app/build.gradle b/app/android/app/build.gradle index 435a7d892..ea7f7c3c2 100644 --- a/app/android/app/build.gradle +++ b/app/android/app/build.gradle @@ -25,9 +25,20 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" -def secretsPropertiesFile = new File(rootProject.projectDir, "secrets.properties") def secrets = new Properties() -secretsPropertiesFile.withReader("UTF-8") { reader -> secrets.load(reader) } +def secretsPropertiesFile = new File(rootProject.projectDir, "secrets.properties") +if (secretsPropertiesFile.isFile()) { + secretsPropertiesFile.withReader("UTF-8") { reader -> secrets.load(reader) } +} else { + assert System.getenv('GPR_USER') + assert System.getenv('GPR_TOKEN') + assert System.getenv('D4L_CLIENT_ID') + assert System.getenv('D4L_CLIENT_SECRET') + secrets.setProperty("gpr.user", System.getenv('GPR_USER')) + secrets.setProperty("gpr.token", System.getenv('GPR_TOKEN')) + secrets.setProperty("d4l.clientId", System.getenv('D4L_CLIENT_ID')) + secrets.setProperty("d4l.clientSecret", System.getenv('D4L_CLIENT_SECRET')) +} android { compileSdkVersion 31 diff --git a/app/android/build.gradle b/app/android/build.gradle index f42aa7de7..a481163f5 100644 --- a/app/android/build.gradle +++ b/app/android/build.gradle @@ -11,9 +11,20 @@ buildscript { } } -def secretsPropertiesFile = new File(rootProject.projectDir, "secrets.properties") def secrets = new Properties() -secretsPropertiesFile.withReader("UTF-8") { reader -> secrets.load(reader) } +def secretsPropertiesFile = new File(rootProject.projectDir, "secrets.properties") +if (secretsPropertiesFile.isFile()) { + secretsPropertiesFile.withReader("UTF-8") { reader -> secrets.load(reader) } +} else { + assert System.getenv('GPR_USER') + assert System.getenv('GPR_TOKEN') + assert System.getenv('D4L_CLIENT_ID') + assert System.getenv('D4L_CLIENT_SECRET') + secrets.setProperty("gpr.user", System.getenv('GPR_USER')) + secrets.setProperty("gpr.token", System.getenv('GPR_TOKEN')) + secrets.setProperty("d4l.clientId", System.getenv('D4L_CLIENT_ID')) + secrets.setProperty("d4l.clientSecret", System.getenv('D4L_CLIENT_SECRET')) +} allprojects { repositories { From 19780e2eb069a61c79d5f8e7ee146b06f9e85182 Mon Sep 17 00:00:00 2001 From: Jannis Baum Date: Tue, 27 Sep 2022 12:28:34 +0200 Subject: [PATCH 09/11] ci(app): use GH secrets for chdp build --- .github/workflows/app.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/app.yml b/.github/workflows/app.yml index c8466b860..8c9e68109 100644 --- a/.github/workflows/app.yml +++ b/.github/workflows/app.yml @@ -48,6 +48,11 @@ jobs: - run: flutter pub get - run: flutter pub run build_runner build - run: flutter build apk + env: + GPR_USER: ${{ secrets.GPR_USER }} + GPR_TOKEN: ${{ secrets.GPR_TOKEN }} + D4L_CLIENT_ID: ${{ secrets.D4L_CLIENT_ID }} + D4L_CLIENT_SECRET: ${{ secrets.D4L_CLIENT_SECRET }} build-ios: name: Build iOS Package From fceef9869608e7fb96a6f1529542e9532ab392be Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Mon, 24 Oct 2022 11:33:21 +0200 Subject: [PATCH 10/11] style: add Sinai colored logos --- app/assets/icon/pharme_sinai.png | Bin 0 -> 26189 bytes app/assets/images/logo_sinai.svg | 385 +++++++++++++++++++++++++++++++ 2 files changed, 385 insertions(+) create mode 100644 app/assets/icon/pharme_sinai.png create mode 100644 app/assets/images/logo_sinai.svg diff --git a/app/assets/icon/pharme_sinai.png b/app/assets/icon/pharme_sinai.png new file mode 100644 index 0000000000000000000000000000000000000000..57fccfff1126eb2088022ab74116f88b8a263395 GIT binary patch literal 26189 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PharMe + From 41fc2a228bb1af33ab9a17ac9e98fc5c5637eba9 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Mon, 24 Oct 2022 13:38:40 +0200 Subject: [PATCH 11/11] Revert "style: add Sinai colored logos" This reverts commit fceef9869608e7fb96a6f1529542e9532ab392be. --- app/assets/icon/pharme_sinai.png | Bin 26189 -> 0 bytes app/assets/images/logo_sinai.svg | 385 ------------------------------- 2 files changed, 385 deletions(-) delete mode 100644 app/assets/icon/pharme_sinai.png delete mode 100644 app/assets/images/logo_sinai.svg diff --git a/app/assets/icon/pharme_sinai.png b/app/assets/icon/pharme_sinai.png deleted file mode 100644 index 57fccfff1126eb2088022ab74116f88b8a263395..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26189 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PharMe -