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
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/CONTRIBUTING.md b/app/CONTRIBUTING.md
index 93c6e12ce..4635d8c4a 100644
--- a/app/CONTRIBUTING.md
+++ b/app/CONTRIBUTING.md
@@ -16,6 +16,27 @@ 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..ea7f7c3c2 100644
--- a/app/android/app/build.gradle
+++ b/app/android/app/build.gradle
@@ -25,6 +25,21 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+def secrets = new Properties()
+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
@@ -44,13 +59,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 +94,8 @@ 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"
+ implementation "com.jakewharton.threetenabp:threetenabp:1.4.0"
}
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 af3e70696..acc0b29d3 100644
--- a/app/android/app/src/main/AndroidManifest.xml
+++ b/app/android/app/src/main/AndroidManifest.xml
@@ -6,7 +6,9 @@
+ when (call.method) {
+ "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
+ )
+ }
+ )
+
+ }
+ "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()
+ }
+ }
+ }
+
+ 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")
+ }
+ }
+ }
}
+
+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/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/android/build.gradle b/app/android/build.gradle
index 76374445e..a481163f5 100644
--- a/app/android/build.gradle
+++ b/app/android/build.gradle
@@ -11,10 +11,50 @@ buildscript {
}
}
+def secrets = new Properties()
+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 {
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")
+ }
+ }
}
}
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 236942aef..94b4e384a 100644
--- a/app/lib/common/pages/medications/medication.dart
+++ b/app/lib/common/pages/medications/medication.dart
@@ -1,13 +1,11 @@
// ignore_for_file: avoid_returning_null_for_void
-import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
+import 'dart:io';
-import '../../l10n.dart';
-import '../../models/module.dart';
-import '../../theme.dart';
+import 'package:flutter/services.dart';
+
+import '../../../common/module.dart';
import '../../utilities/pdf_utils.dart';
-import '../../widgets/module.dart';
import 'cubit.dart';
import 'widgets/module.dart';
@@ -50,7 +48,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 +65,8 @@ class MedicationPage extends StatelessWidget {
);
}
- Widget _buildHeader(MedicationWithGuidelines medication) {
+ Widget _buildHeader(
+ MedicationWithGuidelines medication, BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -75,13 +74,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 +139,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/hive_utils.dart b/app/lib/common/utilities/hive_utils.dart
index 9abc3a5f8..fbb3433fc 100644
--- a/app/lib/common/utilities/hive_utils.dart
+++ b/app/lib/common/utilities/hive_utils.dart
@@ -1,19 +1,17 @@
-import 'dart:convert';
-
-import 'package:flutter_secure_storage/flutter_secure_storage.dart';
-import 'package:hive/hive.dart';
+// import 'package:flutter_secure_storage/flutter_secure_storage.dart';
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/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/l10n/app_en.arb b/app/lib/l10n/app_en.arb
index bc1326ee5..d4c89456f 100644
--- a/app/lib/l10n/app_en.arb
+++ b/app/lib/l10n/app_en.arb
@@ -68,6 +68,7 @@
"settings_page_continue": "Continue",
"settings_page_delete_data": "Delete App Data",
"settings_page_delete_data_text": "Are you sure that you want to delete all app data? This also includes your genomic data.",
+ "settings_page_login_smart4health": "Log in to Smart4Health",
"settings_page_more": "More",
"settings_page_onboarding": "Onboarding",
"settings_page_about_us": "About us",
diff --git a/app/lib/reports/pages/cubit.dart b/app/lib/reports/pages/cubit.dart
index d6fbfce42..558862654 100644
--- a/app/lib/reports/pages/cubit.dart
+++ b/app/lib/reports/pages/cubit.dart
@@ -72,6 +72,8 @@ class ReportsCubit extends Cubit {
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/chdp_state.dart b/app/lib/settings/chdp_state.dart
new file mode 100644
index 000000000..8f4833829
--- /dev/null
+++ b/app/lib/settings/chdp_state.dart
@@ -0,0 +1,84 @@
+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 with WidgetsBindingObserver {
+ static const platform = MethodChannel('chdp');
+
+ bool _isLoggedIn = false;
+
+ @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: 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 {
+ var isLoggedIn = false;
+ try {
+ isLoggedIn = await platform.invokeMethod('isLoggedIn');
+ } on PlatformException catch (e) {
+ debugPrint('platform exception in getIsLoggedIn: ${e.toString()}');
+ }
+
+ setState(() {
+ _isLoggedIn = isLoggedIn;
+ });
+ }
+
+ Future login() async {
+ try {
+ await platform.invokeMethod('login');
+ } on PlatformException catch (e) {
+ debugPrint('platform exception in login: ${e.toString()}');
+ }
+ }
+
+ Future logout() async {
+ try {
+ await platform.invokeMethod('logout');
+ } on PlatformException catch (e) {
+ debugPrint('platform exception in logout: ${e.toString()}');
+ }
+ }
+}
diff --git a/app/lib/settings/pages/settings.dart b/app/lib/settings/pages/settings.dart
index 023f41dd1..241ef2a9b 100644
--- a/app/lib/settings/pages/settings.dart
+++ b/app/lib/settings/pages/settings.dart
@@ -1,12 +1,21 @@
+import 'dart:io';
+
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(children: [
+ return ListView(
+ children: [
ListTile(
title: Text(
context.l10n.settings_page_account_settings,
@@ -21,6 +30,10 @@ class SettingsPage extends StatelessWidget {
builder: (_) => _deleteDataDialog(context),
),
),
+ if (Platform.isAndroid)
+ ChdpListTile()
+ else
+ null,
Divider(),
ListTile(
title: Text(
@@ -48,7 +61,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..4c91c92de 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:
@@ -361,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:
@@ -462,6 +446,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 +549,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 +645,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 +659,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 +967,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 +1007,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 +1070,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 +1084,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 +1098,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 +1125,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 +1203,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 +1224,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:
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